聊聊 ErrorGroup 的用法和拓展
聊聊 ErrorGroup 的用法和拓展
在写业务代码时经常碰到需要将一个通用的父任务拆成几个小任务并发执行的场景。此时需要将一个大的任务拆成几个小任务并发执行,来提高程序效率。
那我是怎么做的呢,我会在一个函数中启动我需要的协程来并发完成任务,每个协程构建一个管道进行结果传输,最后起一个for select 收集每个不同的管道任务。使用wait group来控制每个协程生命周期。
其实类似的业务场景已经有很多大神进行了抽象并形成了简单易用的代码框架,本文将以 go 官方的 ErrGroup 库为切入点对各路大佬的拓展库进行分析。
为什么要写这个文章呢,因为我在看这些库时发现,在这个基础上大佬们约拓展越丰富,把这些代码看下来感觉自己对业务场景的抽象能力以及处理能力增加了很多,但这些必须总结下来!因为一定会忘记,尤其是长时间不用,一旦出现相似的场景,那么很难直接回忆起来,所以提笔写了这篇文章。有各路大佬保驾护航那一定能快速形成没有坑的优秀代码。
官方 ErrorGroup 介绍ErrGroup 是 Go 官方提供的一个同步扩展库。接下来,我来给你介绍一下 ErrGroup 的基本用法和 ...
用 go 处理1分钟百万请求
用 go 处理1分钟百万请求
这是 Marcio Castilho 在 MBAM 时产生的一个需求,我在读后发现他们在处理百万请求时设计的并发池是非常巧妙的,在这里把这篇文章和代码做一个解析,原文在这里。
这篇文章不是单纯的翻译,主要是对他们在处理问题时不同版本的代码进行一个分析,原文中有很多作者的心路历程,大家感兴趣的可以去看原文。
背景Marcio Castilho 在处理他们的匿名数据统计和分析系统时遇到一个需求:我们的目标是能够处理来自数百万个端点的大量 POST 请求。Web 处理程序将接收一个 JSON 文档,该文档可能包含需要写入 Amazon S3 的许多有效负载的集合,以便我们的 map-reduce 系统稍后对这些数据进行操作。S3就是亚马逊的一个对象存储服务,其实他们的主要业务逻辑很简单,难就难在控制并发。最开始他们按照自己以往的经验想要建立一个worker层架构,使用:
Sidekiq
Resque
DelayedJob
Elasticbeanstalk Worker Tier
RabbitMQ
and so on…
建立两个集群,一个是前端集群进行请求消 ...
go语言设计模式(Map-Reduce)
go语言设计模式(Map-Reduce)我理解的设计模式说到设计模式,多数人想到的都是 1995 年 GoF 提出的 23 种设计模式 他通常被定义为:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。
从定义上看,设计模式其实是一种经验的总结,是针对特定问题的简洁而优雅的解决方案。我认为只要是优雅的,简洁的,可进行多业务场景问题解决的,易于理解使用的一套行之有效的代码编写模式都可以称为设计模式或是编程模式。
既然是经验总结的编程模式,那么学习它的最直接的好处就在于可以站在巨人的肩膀上解决软件开发过程中的一些特定问题。
我写编程模式这个模块也是为了将好的模式收集积累,在遇到相似的问题场景,可以拿来主义。哈哈哈,听起来有些不动脑子的愚蠢。但你想想我们这个世界不就是这样在前人的肩膀上一步步的发展进步的。你如果做什么都要自己思考,总结,设计都要从0到1这本身就不太现实,即使是你做到了,那也一定不够优雅或是错误百出。
设计模式这个大标签下,我将把我阅读他人总结 ...
go 语言中的 range 真的影响性能吗
go 语言中的 range 真的影响性能吗
是不是有人告诉你,range 每次循环都进行一次值拷贝,非常影响性能。今天来揭秘 range 到底有多”坑“
range 的简单介绍Go 语言中,range 可以用来很方便地遍历数组(array)、切片(slice)、字典(map)和信道(chan)
array/slice12345words := []string{"Go", "语言", “很”, "棒"}for i, s := range words { words = append(words, "test") fmt.Println(i, s)}
输出结果如下:
12340 Go1 语言2 很3 棒
变量 words 在循环开始前,仅会计算一次,如果在循环中修改切片的长度不会改变本次循环的次数。
迭代过程中,每次迭代的下标和值被赋值给变量 i 和 s,第二个参数 s 是可选的。
针对 nil 切片,迭代次数为 0。
range 还有另一种只遍历 ...
使用 Sync Pool 提升程序性能
使用 Sync Pool 提升程序性能
程序性能优化往往是一个程序提升的瓶颈,这不但需要你有意识的积累知识,而且还需工作上有场景的支持,对我来说工作上可能并没有太多的场景来支持我去提升,但不断的积累还是必要的,每块支持都能建立自己的体系的前提是你积累的足够多,这篇文章就是在看并发相关的博文时发现的一个提升性能的好东西:sync.pool
你有没有遇到过,当多个 goroutine 都需要创建同⼀个对象的时候,如果 goroutine 数过多,导致对象的创建数⽬剧增,进⽽导致 GC 压⼒增大。形成 “并发⼤-占⽤内存⼤-GC 缓慢-处理并发能⼒降低-并发更⼤”这样的恶性循环。当多个可以重复使用的数据都需要进行数据反序列化或是形成一个buffer。
在这个时候,就需要有⼀个对象池,每个 goroutine 不再⾃⼰单独创建对象,⽽是从对象池中获取出⼀个对象(如果池中已经有的话)。
go 语言的并发原语其实已经有了该功能:sync.pool:
sync.Pool 数据类型用来保存一组可独立访问的临时对象。请注意这里加粗的“临时”这两个字,它说明了 sync.Pool 这个数据类型的特点,也 ...
如何使用 benchmark 进行性能分析
如何使用 benchmark 进行性能分析
代码的性能是区分代码好坏的重要因素,你是否经常遇到你觉得这样写性能好,但当别人质疑你的时候你有没法给出好的证据?用go语言内置的基准测试工具becnmark给出证据吧!
稳定的测试环境当我们尝试去优化代码的性能时,首先得知道当前的性能怎么样。Go 语言标准库内置的 testing 测试框架提供了基准测试(benchmark)的能力,能让我们很容易地对某一段代码进行性能测试。
性能测试受环境的影响很大,为了保证测试的可重复性,在进行性能测试时,尽可能地保持测试环境的稳定。
机器处于闲置状态,测试时不要执行其他任务,也不要和其他人共享硬件资源。
机器是否关闭了节能模式,一般笔记本会默认打开这个模式,测试时关闭。
避免使用虚拟机和云主机进行测试,一般情况下,为了尽可能地提高资源的利用率,虚拟机和云主机 CPU 和内存一般会超分配,超分机器的性能表现会非常地不稳定。
超分配是针对硬件资源来说的,商业上对应的就是云主机的超卖。虚拟化技术带来的最大直接收益是服务器整合,通过 CPU、内存、存储、网络的超分配(Overcommitment)技术, ...
go并发模型(超时取消模型)
go 并发模型整理(一)
go 语言的并发是整个语言的精髓所在,在语言层面灵活的处理协程,可以有效提高程序性能。但是go并发虽然简单,处理不当依然会造成很大的问题,比如内存溢出,比如死锁等等头疼的问题,写模型整理这个模块主要就是想要把一些好的并发模式收集起来积累经验,同时也对容易出问题的地方深入了解。
超时取消模型网上看到的很好的流程图这是我在看别人博客时看到的一张流程图,但是他的内容并没有完整的代码实现,我感觉这个流程做的很棒,所以自己用代码去实现了一下,希望可以抛砖引玉。代码点击这里看
这个流程图之所以我觉好,是因为 :
考虑的十分细节,每个过程产生的并发的生命周期控制都有涉及,可以做到即使出现问题也可以通过超时的设置优雅退出所有并发。
流程图并不是简单的去实现一个超时退出,而是设计了一些比较通用的业务逻辑。请看下图:
代码实现先把整个流程拆分一下:发送者 —> 接收者(并发接收) —> 最终结果处理者
其中发送者再次拆分:工人1;工人2;工人3 —> 数据整合者 —> 整合后的最终可以发送的数据
整个流程涉及10个协程,其中三个工人的并发,三个接收 ...
聊聊代码耦合
代码耦合性说到代码的耦合性,我想大多数人应该都没有好好的了解过什么是耦合,大多数是在自己开发时遇到了一些问题,或是听主管说起就大概的有一个了解。
我也是这样,我从没有去了解代码耦合,我只是一个大概的认知,大概就是一个方法不要重复的出现在多个模块,模块之间不要有太多的相互依赖。
在这之后我遇到了一个声明某函数类型,但在另外一个文件里实现的使用方式,一个小哥告诉我这是为了减少代码耦合,我就觉得奇怪,你既然在这个包声明了该函数,就算你在另一个包实现那也是一样要调用该包的,如何解耦?后面我觉的是大家根本就不理解耦合,都是胡乱猜想的,我的代码重复的逻辑提出来了,或是清晰了那就是解耦。对于什么是“耦合”、什么是“乱”,他们并不知道有什么客观标准可以度量。
我就上网查了一下资料,有一个解释甚合我意,文章把代码耦合划分成三个可以度量的标准:
依赖依赖和耦合的最大区别在于,当我们说“A和B耦合”时,在字面含义中,A和B二者平等。然而,正确的模块关系根本不应该平等,而应该是单向依赖才对。所以我们应该说“A依赖B”,这样含义要清楚得多。A依赖B意味着,A模块可以调用B模块暴露的API,但B模块绝不允许调用A ...
Redis 分布式锁,你的使用正确吗
Redis 分布式锁,你的使用正确吗
Redis 在项目中使用的最多的就是分布式锁,但你的使用是否正确,会不会因为这个所给你的项目挖下深坑?
为什么要用分布式锁我们日常经常会遇到一些高并发的场景,例如电商 App 上经常出现的秒杀活动、限量优惠券抢购,还有我们去哪儿网的火车票抢票系统等,这些场景有一个共同特点就是访问量激增,虽然在系统设计时会通过限流、异步、排队等方式优化,但整体的并发还是平时的数倍以上,为了避免并发问题,防止库存超卖,给用户提供一个良好的购物体验,这些系统中都会用到锁的机制。
对于单进程的并发场景,可以使用编程语言及相应的类库提供的锁,避免并发问题。go 中常用的就是 sync 包的 Mutex,当然你也可以使用 atomic value 通过值的原子性来自制一把锁。
如果在分布式场景中,实现不同客户端的线程对代码和资源的同步访问,保证在多线程下处理共享数据的安全性,就需要用到分布式锁技术。
Redis 分布式锁使用 Redis 作为分布式锁,本质上要实现的目标就是一个进程在 Redis 里面占据了仅有的一个“茅坑”,当别的进程也想来占坑时,发现已经有人蹲在那里了, ...
如何使用 dlv 结合 Goland 进行程序 debug 调试
如何使用 dlv 结合 Goland 进行程序 debug 调试相信很多 Golang 的初级玩家不会进行程序的 Debug 定位问题单纯的靠脑子,或者效率很低的不断的添加日志打印,别问我为什么知道的因为我就是这样的,最好最快捷的问题定位方式一定是使用 Debug 打断点调试,这时就引出了本文的主角dlv。
实际上,delve 才是全称,dlv 只是启动命令,如果 VScode,Goland,默认使用的调试器就是基于 delve 的。
安装 dlv参考官方的安装方法,把 dlv 命令安装在 gopath 的 bin 目录下(需要你把go的bin目录添加到$PATH)
123git clone https://github.com/go-delve/delvecd delvego install github.com/go-delve/delve/cmd/dlv
成功安装执行
1dlv version
得到:
123Delve DebuggerVersion: 1.7.1Build: $Id: 3bde2354aafb5a4043fd59838842c4cd4a8b6f0b $
进 ...