Go并发Sync.Once解析

在 go 语言中我们可以使用 sync.Once 对象来实现函数方法只执行一次的功能。 简单代码示例 package main import ( "fmt" "sync" ) func main() { var ( o sync.Once wg sync.WaitGroup ) for i := 0; i < 10; i++ { wg.Add(1) go func(i int) { defer wg.Done() o.Do(func() { fmt.Printf("hello %d\n", i) }) }(i) } wg.Wait() } 输出: hello 9 不使用 Sync.Once 的结果如下: hello 9 hello 4 hello 0 hello 1 hello 2 hello 3 hello 6 hello 5 hello 7 hello 8 可以看到, 在使用 sync.Once 的情况下, 只执行一次函数。 ...

六月 3, 2022 · overstarry

Go截取视频某一帧图片

前言 最近遇到一个需求,需要截取视频的某一帧图片作为视频封面。我搜寻了相关资料,在 go 语言端常见的有两种做法,1)使用 opencv 的 go 绑定库,2)使用 ffmpeg 的 go 绑定库。 这里我打算使用第二种方法,使用 ffmpeg 的 go 绑定库。 ffmpeg 介绍 FFmpeg是一个开源免费跨平台的视频和音频流方案,属于自由软件,采用LGPL或GPL许可证(依据你选择的组件)。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多codec都是从头开发的。 FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。 FFmpeg项目由以下几部分组成: FFMpeg 视频文件转换命令行工具,也支持经过实时电视卡抓取和编码成视频文件。 FFServer 基于HTTP(RTSP正在开发中)用于实时广播的多媒体服务器,也支持时间平移. FFplay 用SDL和FFmpeg库开发的一个简单的媒体播放器. libavcodec 一个包含了所有FFmpeg音视频编解码器的库.为了保证最优性能和高可复用性,大多数编解码器从头开发的. libavformat 一个包含了所有的普通音视格式的解析器和产生器的库 解决 1 安装 ffmpeg 浏览器访问 https://ffbinaries.com/downloads 根据你的系统安装 ffmpeg 2 安装 ffmpeg 的 go 绑定库 这里我使用了 github.com/u2takey/ffmpeg-go 这个项目,它是一个绑定库,可以在 go 语言端使用 ffmpeg 命令行工具。 3 编写代码 package main import ( "bytes" "fmt" "github.com/disintegration/imaging" ffmpeg "github.com/u2takey/ffmpeg-go" "log" "os" "strings" ) func GetSnapshot(videoPath, snapshotPath string, frameNum int) (snapshotName string, err error) { buf := bytes.NewBuffer(nil) err = ffmpeg.Input(videoPath). Filter("select", ffmpeg.Args{fmt.Sprintf("gte(n,%d)", frameNum)}). Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}). WithOutput(buf, os.Stdout). Run() if err != nil { log.Fatal("生成缩略图失败:", err) return "", err } img, err := imaging.Decode(buf) if err != nil { log.Fatal("生成缩略图失败:", err) return "", err } err = imaging.Save(img, snapshotPath+".png") if err != nil { log.Fatal("生成缩略图失败:", err) return "", err } names := strings.Split(snapshotPath, "\\") snapshotName = names[len(names)-1] + ".png" return } func main() { _, err := GetSnapshot("./test.mp4", "test", 1) if err != nil { return } } 函数里 ffmpeg 相关的代码主要是提取视频的某一帧以mjpeg编码为图片,然后保存到本地。 ...

五月 28, 2022 · overstarry

Go errgroup

我们知道在 go 语言中很容易开启携程进行并发任务, 但是如何处理并发过程中的错误是非常棘手的,接下来我就来介绍 errgroup 的用法。 errgroup errgroup 包里主要是一个结构体和相应的方法,它可以让你在并发任务中处理错误。 type Group struct { // context 的 cancel 方法 cancel func() // 复用 WaitGroup wg sync.WaitGroup sem chan token // 用来保证只会接受一次错误 errOnce sync.Once // 保存第一个返回的错误 err error } func WithContext(ctx context.Context) (*Group, context.Context) func (g *Group) done() func (g *Group) Wait() error func (g *Group) Go(f func() error) func (g *Group) TryGo(f func() error) bool func (g *Group) SetLimit(n int) 通过 WithContext 可以创建一个可以取消的 Group,当然除此之外也可以零值的 Group 也可以直接使用,但是出错之后就不会取消其他的 goroutine 了. Go方法 传入一个函数参数,会启动一个 goroutine 处理。 Wait 类似 WaitGroup 的 Wait 方法,等待所有的 goroutine 结束后退出,返回的错误是一个出错的 err。 TryGo 是和 SetLimit 配套的,只有当 group 中的 goroutines 数量小于配置的数量时,才会在 goroutine 中调用函数。 TryGo 用来判断 goroutine 是否启动。 ...

五月 21, 2022 · overstarry

Go统计代码测试覆盖率

今天来讲一讲如何统计 go 代码的测试覆盖率,主要是 cover 命令。 cover 基本用法 1 先简单写个函数和相应的测试,代码如下: func Max(a, b int) int { if a > b { return a } return b } 这个函数就是简单的比较大小,如果 a > b,返回 a,否则返回 b。 测试代码如下 package main import "testing" func TestMax(t *testing.T) { type args struct { a int b int } tests := []struct { name string args args want int }{ { name: "a is larger than b", args: args{ a: 2, b: 1, }, want: 2, }, { name: "b is larger than a", args: args{ a: 1, b: 2, }, want: 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := Max(tt.args.a, tt.args.b); got != tt.want { t.Errorf("Max() = %v, want %v", got, tt.want) } }) } } 2 go test -cover 能够统计出代码测试的覆盖率,这是一种比统计函数是否被调用更强悍的手法。我们执行这个命令。输出如下: ...

四月 16, 2022 · overstarry

Casbin学习1

从本篇文章开始,将由我来开始介绍访问控制框架的基础知识,本篇文章是 casbin 系列文章的第一篇,主要介绍一些 casbin 的概念和基础知识。 概述 casbin 是一个开源的访问控制框架,它的目标是让开发人员可以更加简单的控制访问控制,支持多种访问控制模型,支持多种编程语言(各种编程语言支持的程度可以查看官网的文档)。 casbin 可以 支持自定义请求的格式,默认的请求格式为{subject, object, action}。 具有访问控制模型model和策略policy两个核心概念。 支持RBAC中的多层角色继承,不止主体可以有角色,资源也可以具有角色。 支持内置的超级用户 例如:root 或 administrator。超级用户可以执行任何操作而无需显式的权限声明。 支持多种内置的操作符,如 keyMatch,方便对路径式的资源进行管理,如 /foo/bar 可以映射到 /foo* Casbin 不支持的是: 身份认证 authentication(即验证用户的用户名和密码),Casbin 只负责访问控制。应该有其他专门的组件负责身份认证,然后由 Casbin 进行访问控制,二者是相互配合的关系。 管理用户列表或角色列表。 Casbin 认为由项目自身来管理用户、角色列表更为合适, 用户通常有他们的密码,但是 Casbin 的设计思想并不是把它作为一个存储密码的容器。 而是存储RBAC方案中用户和角色之间的映射关系。 工作原理 在 Casbin 中, 访问控制模型被抽象为基于 PERM (Policy, Effect, Request, Matcher) 的一个文件。 因此,切换或升级项目的授权机制与修改配置一样简单。 您可以通过组合可用的模型来定制您自己的访问控制模型。 例如,您可以在一个model中结合RBAC角色和ABAC属性,并共享一组policy规则。 adapters 在Casbin中,策略存储作为adapter(Casbin的中间件) 实现。 Casbin用户可以使用adapter从存储中加载策略规则 (aka LoadPolicy()) 或者将策略规则保存到其中 (aka SavePolicy())。 为了保持代码轻量级,我们没有把adapter代码放在主库中。 casbin 目前支持的官方的和第三方适配器有许多,这里只截取 go 相关的一部分适配器: 可以看到支持的生态十分丰富。 简单使用 接下来就由我来简单演示下 casbin 的使用 ...

一月 12, 2022 · overstarry