go ants学习
golang ants 学习
既然Go调度器已经这么优秀了,我们为什么还要使用ants呢?优秀不代表完美,基于G-P-M的Go调度器背后,go程序的并发编程中,可以任性地起大规模的goroutine来执行任务,官方也宣称用golang写并发程序的时候随便起个成千上万的goroutine毫无压力。
然而,你起1000个goroutine没有问题,10000也没有问题,10w个可能也没问题;那,100w个呢?1000w个呢?(这里只是举个极端的例子,实际编程起这么大规模的goroutine的例子极少)这里就会出问题,什么问题呢?
- 首先,即便每个goroutine只分配2KB的内存,但如果是恐怖如斯的数量,聚少成多,内存暴涨,就会对GC造成极大的负担,写过java的同学应该知道jvm GC那万恶的STW(Stop The World)机制,也就是GC的时候会挂起用户程序直到垃圾回收完,虽然Go1.8之后的GC已经去掉了STW以及优化成了并行GC,性能上有了不小的提升,但是,如果太过于频繁地进行GC,依然会有性能瓶颈;
- 其次,还记得前面我们说的runtime和GC也都是goroutine吗?是的,如果goroutine规模太大,内存吃紧,runtime调度和垃圾回收同样会出问题,虽然G-P-M模型足够优秀,韩信点兵,多多益善,但你不能不给士兵发口粮(内存)吧?巧妇难为无米之炊,没有内存,Go调度器就会阻塞goroutine,结果就是P的Local队列积压,又导致内存溢出,这就是个死循环...,甚至极有可能程序直接Crash掉,本来是想享受golang并发带来的效益,结果却得不偿失。
ants是一个高性能的 goroutine 池,实现了对大规模 goroutine 的调度管理、goroutine 复用,允许使用者在开发并发程序的时候限制 goroutine 数量,复用资源,达到更高效执行任务的效果。
在大规模批量并发任务场景下比原生 goroutine 并发具有更高的性能
ants流程图,逻辑还是比较简单的
ants用做端口扫描(原生的goroutine 扫描结果不稳定不可复现)
package main
import (
"fmt"
"github.com/panjf2000/ants/v2"
"net"
"sync"
"time"
)
func scan(port interface{}) {
address := fmt.Sprintf("176.36.129.182:%d", port)
con, err := net.DialTimeout("tcp", address, 3*time.Second)
if err != nil {
return
}
fmt.Println(port)
con.Close()
}
func main() {
start:=time.Now().Unix()
var wg sync.WaitGroup
p, _ := ants.NewPoolWithFunc(10000, func(i interface{}) { // 开启大小为10000的goroutine池同时绑定需要并发执行的函数 相比ants.NewPool,由于是执行单一的函数效率更高
scan(i)
wg.Done()
})
defer p.Release() //释放goroutine池
for i := 0; i < 65535; i++ {
wg.Add(1)
_ = p.Invoke(i) //提交任务 i作为参数传上去
}
wg.Wait()
fmt.Println(time.Now().Unix()-start)
}
ants项目网站:https://github.com/panjf2000/ants
ants作者博客页:https://strikefreedom.top/high-performance-implementation-of-goroutine-pool
建议将作者的博客全部读完(几千字而已,作者写了一定的设计理念和源码结构解析)