上一篇我们讲了 go-zero 中的并发工具包 core/syncx

从整体分析来看,并发组件主要通过 channel + mutex 控制程序中协程之间沟通。

Do not communicate by sharing memory; instead, share memory by communicating.

不要通过共享内存来通信,而应通过通信来共享内存。

本篇来聊 go-zero 对 Go 中 goroutine 支持的并发组件。

我们回顾一下,go原生支持的 goroutine 控制的工具有哪些?

  1. go func() 开启一个协程
  2. sync.WaitGroup 控制多个协程任务编排
  3. sync.Cond 协程唤醒或者是协程等待

那可能会问 go-zero 为什么还要拿出来讲这些?回到 go-zero 的设计理念:工具大于约定和文档

那么就来看看,go-zero 提供哪些工具?

threading

虽然 go func() 已经很方便,但是有几个问题:

  • 如果协程异常退出,无法追踪异常栈
  • 某个异常请求触发panic,应该做故障隔离,而不是整个进程退出,容易被攻击

我们看看 core/threading 包提供了哪些额外选择:

func GoSafe(fn func()) {
go RunSafe(fn)
} func RunSafe(fn func()) {
defer rescue.Recover()
fn()
} func Recover(cleanups ...func()) {
for _, cleanup := range cleanups {
cleanup()
} if p := recover(); p != nil {
logx.ErrorStack(p)
}
}

GoSafe

threading.GoSafe() 就帮你解决了这个问题。开发者可以将自己在协程中需要完成逻辑,以闭包的方式传入,由 GoSafe() 内部 go func()

当开发者的函数出现异常退出时,会在 Recover() 中打印异常栈,以便让开发者更快确定异常发生点和调用栈。

NewWorkerGroup

我们再看第二个:WaitGroup。日常开发,其实 WaitGroup 没什么好说的,你需要 N 个协程协作 :wg.Add(N) ,等待全部协程完成任务:wg.Wait(),同时完成一个任务需要手动 wg.Done()

可以看的出来,在任务开始 -> 结束 -> 等待,整个过程需要开发者关注任务的状态然后手动修改状态。

NewWorkerGroup 就帮开发者减轻了负担,开发者只需要关注:

  1. 任务逻辑【函数】
  2. 任务数【workers

然后启动 WorkerGroup.Start(),对应任务数就会启动:

func (wg WorkerGroup) Start() {
// 包装了sync.WaitGroup
group := NewRoutineGroup()
for i := 0; i < wg.workers; i++ {
// 内部维护了 wg.Add(1) wg.Done()
// 同时也是 goroutine 安全模式下进行的
group.RunSafe(wg.job)
}
group.Wait()
}

worker 的状态会自动管理,可以用来固定数量的 worker 来处理消息队列的任务,用法如下:

func main() {
group := NewWorkerGroup(func() {
// process tasks
}, runtime.NumCPU())
group.Start()
}

Pool

这里的 Pool 不是 sync.Poolsync.Pool 有个不方便的地方是它池化的对象可能会被垃圾回收掉,这个就让开发者疑惑了,不知道自己创建并存入的对象什么时候就没了。

go-zero 中的 pool

  1. pool 中的对象会根据使用时间做懒销毁;
  2. 使用 cond 做对象消费和生产的通知以及阻塞;
  3. 开发者可以自定义自己的生产函数,销毁函数;

那我来看看生产对象,和消费对象在 pool 中时怎么实现的:

func (p *Pool) Get() interface{} {
// 调用 cond.Wait 时必须要持有c.L的锁
p.lock.Lock()
defer p.lock.Unlock() for {
// 1. pool中对象池是一个用链表连接的nodelist
if p.head != nil {
head := p.head
p.head = head.next
// 1.1 如果当前节点:当前时间 >= 上次使用时间+对象最大存活时间
if p.maxAge > 0 && head.lastUsed+p.maxAge < timex.Now() {
p.created--
// 说明当前节点已经过期了 -> 销毁节点对应的对象,然后继续寻找下一个节点
// 【️:不是销毁节点,而是销毁节点对应的对象】
p.destroy(head.item)
continue
} else {
return head.item
}
}
// 2. 对象池是懒加载的,get的时候才去创建对象链表
if p.created < p.limit {
p.created++
// 由开发者自己传入:生产函数
return p.create()
} p.cond.Wait()
}
}
func (p *Pool) Put(x interface{}) {
if x == nil {
return
}
// 互斥访问 pool 中nodelist
p.lock.Lock()
defer p.lock.Unlock() p.head = &node{
item: x,
next: p.head,
lastUsed: timex.Now(),
}
// 放入head,通知其他正在get的协程【极为关键】
p.cond.Signal()
}

上述就是 go-zeroCond 的使用。可以类比 生产者-消费者模型,只是在这里没有使用 channel 做通信,而是用 Cond 。这里有几个特性:

  • Cond和一个Locker关联,可以利用这个Locker对相关的依赖条件更改提供保护。
  • Cond可以同时支持 SignalBroadcast 方法,而 Channel 只能同时支持其中一种。

总结

工具大于约定和文档,一直是 go-zero 设计主旨之一;也同时将平时业务沉淀到组件中,这才是框架和组件的意义。

关于 go-zero 更多的设计和实现文章,可以持续关注我们。欢迎大家去关注和使用。

项目地址

https://github.com/tal-tech/go-zero

欢迎使用 go-zero 并 star 支持我们!

微信交流群

关注『微服务实践』公众号并回复 进群 获取社区群二维码。

一文带你更方便的控制 goroutine的更多相关文章

  1. Istio是啥?一文带你彻底了解!

    原标题:Istio是啥?一文带你彻底了解! " 如果你比较关注新兴技术的话,那么很可能在不同的地方听说过 Istio,并且知道它和 Service Mesh 有着牵扯. 这篇文章可以作为了解 ...

  2. 一文带您了解5G的价值与应用

    一文带您了解5G的价值与应用 5G最有趣的一点是:大多数产品都是先有明确应用场景而后千呼万唤始出来.而5G则不同,即将到来的5G不仅再一次印证了科学技术是第一生产力还给不少用户带来了迷茫——我们为什么 ...

  3. 一文带你了解elasticsearch

    一文带你了解elasticsearch cxf2102100人评论160人阅读2019-07-02 21:31:36   elasticsearch es基本概念 es术语介绍 文档Document ...

  4. 【转帖】Istio是啥?一文带你彻底了解!

    Istio是啥?一文带你彻底了解! http://www.sohu.com/a/270131876_463994 原始位置来源: https://cizixs.com 如果你比较关注新兴技术的话,那么 ...

  5. 一文带你看清HTTP所有概念(转)

    一文带你看清HTTP所有概念   上一篇文章我们大致讲解了一下 HTTP 的基本特征和使用,大家反响很不错,那么本篇文章我们就来深究一下 HTTP 的特性.我们接着上篇文章没有说完的 HTTP 标头继 ...

  6. 【项目实践】一文带你搞定Spring Security + JWT

    以项目驱动学习,以实践检验真知 前言 关于认证和授权,R之前已经写了两篇文章: [项目实践]在用安全框架前,我想先让你手撸一个登陆认证 [项目实践]一文带你搞定页面权限.按钮权限以及数据权限 在这两篇 ...

  7. 编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程

    建议56:使用继承ISerializable接口更灵活地控制序列化过程 接口ISerializable的意义在于,如果特性Serializable,以及与其像配套的OnDeserializedAttr ...

  8. 【转】编写高质量代码改善C#程序的157个建议——建议56:使用继承ISerializable接口更灵活地控制序列化过程

    建议56:使用继承ISerializable接口更灵活地控制序列化过程 接口ISerializable的意义在于,如果特性Serializable,以及与其像配套的OnDeserializedAttr ...

  9. 一文带你了解 C# DLR 的世界

    一文带你了解 C# DLR 的世界 在很久之前,我写了一片文章dynamic结合匿名类型 匿名对象传参,里面我以为DLR内部是用反射实现的.因为那时候是心中想当然的认为只有反射能够在运行时解析对象的成 ...

随机推荐

  1. 从崩溃的选课系统,论为什么更安全的 HTTPS 协议没有被全面采用

    尽人事,听天命.博主东南大学研究生在读,热爱健身和篮球,正在为两年后的秋招准备中,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 C ...

  2. django学习-1.开始hello world!

    1.前言 当你想走上测试开发之路,用python开发出一个web页面的时候,需要找一个支持python语言的web框架.django框架有丰富的文档和学习资料,也是非常成熟的web开发框架,想学pyt ...

  3. Java自学第10期——File类与IO流(输入输出流、处理流、转换流、缓冲流、Properties集合、打印流)

    1.IO简介 IO(输入输出)通过java.io包下的类和接口来支持,包下包括输入.输出两种IO流,每种输入输出流又可分为字符流和字节流两大类. 2.File类 File类是io包下与平台无关的文件和 ...

  4. 模拟web服务器 (小项目) 搭建+部署

    模拟web服务器:可以从浏览器中访问到自己编写的服务器中的资源,将其资源显示在浏览器中. 技术选型: corejava 线程池 同任务并发执行 IO流 传递数据 客户端也会像服务端发送数据, 服务器像 ...

  5. WPF 关于ComboBox在前台绑定XML数据的一些方法,使用XML数据提供器 XmlDataProvider

    关于使用 数据提供器:XmlDataProvider 的一些问题,以及在WPF中是如何使用的一些介绍,还有踩到的一些坑,希望其他和我碰到一样问题的,可以更快的解决. 首先,要求是 在WPF 的前台代码 ...

  6. 翻译:《实用的Python编程》02_06_List_comprehension

    目录 | 上一节 (2.5 collections模块) | 下一节 (2.7 对象模型) 2.6 列表推导式 一个常见的任务是处理列表中的项(译注:元素).本节介绍列表推导式,完成此任务的强大工具. ...

  7. 高级FTP

      一.作业需求 1. 用户加密认证(已完成) 2. 多用户同时登陆(已完成) 3. 每个用户有自己的家目录且只能访问自己的家目录(已完成) 4. 对用户进行磁盘配额.不同用户配额可不同(已完成) 5 ...

  8. Get和Post请求方式

    Get和Post是两种不同的类型的请求. 它们主要有3点不同. 1.get请求通过浏览器地址栏传递表单数据.post请求通过form data 传递数据,不会通过地址栏. 2.get请求安全性较低,p ...

  9. 记录实践PC端微信防撤回实现过程(基于3.1.0.67版本)

    利用OD实现对PC端微信防撤回功能的实现 文章最后有一键补丁工具哦~ 准备工具 1.OD 2.PC微信客户端(3.1.0.67) 过程 1.运行微信客户端,不需要登录 2.运行OD,左上角选择附加进程 ...

  10. P2424 约数和 【整除分块】

    一.题目 P2424 约数和 二.分析 因为都是加法,那么肯定有的一个性质,即前缀和的思想,就是$$ { ans =\sum_{i=1}^y f(i)} - {\sum_{i=1}^x f(i)}   ...