go ants学习

golang ants 学习

既然Go调度器已经这么优秀了,我们为什么还要使用ants呢?优秀不代表完美,基于G-P-M的Go调度器背后,go程序的并发编程中,可以任性地起大规模的goroutine来执行任务,官方也宣称用golang写并发程序的时候随便起个成千上万的goroutine毫无压力。

然而,你起1000个goroutine没有问题,10000也没有问题,10w个可能也没问题;那,100w个呢?1000w个呢?(这里只是举个极端的例子,实际编程起这么大规模的goroutine的例子极少)这里就会出问题,什么问题呢?

  1. 首先,即便每个goroutine只分配2KB的内存,但如果是恐怖如斯的数量,聚少成多,内存暴涨,就会对GC造成极大的负担,写过java的同学应该知道jvm GC那万恶的STW(Stop The World)机制,也就是GC的时候会挂起用户程序直到垃圾回收完,虽然Go1.8之后的GC已经去掉了STW以及优化成了并行GC,性能上有了不小的提升,但是,如果太过于频繁地进行GC,依然会有性能瓶颈;
  2. 其次,还记得前面我们说的runtime和GC也都是goroutine吗?是的,如果goroutine规模太大,内存吃紧,runtime调度和垃圾回收同样会出问题,虽然G-P-M模型足够优秀,韩信点兵,多多益善,但你不能不给士兵发口粮(内存)吧?巧妇难为无米之炊,没有内存,Go调度器就会阻塞goroutine,结果就是P的Local队列积压,又导致内存溢出,这就是个死循环...,甚至极有可能程序直接Crash掉,本来是想享受golang并发带来的效益,结果却得不偿失。

ants是一个高性能的 goroutine 池,实现了对大规模 goroutine 的调度管理、goroutine 复用,允许使用者在开发并发程序的时候限制 goroutine 数量,复用资源,达到更高效执行任务的效果。

在大规模批量并发任务场景下比原生 goroutine 并发具有更高的性能

ants流程图,逻辑还是比较简单的

ants-flowchart-cn

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

建议将作者的博客全部读完(几千字而已,作者写了一定的设计理念和源码结构解析)

posted @ 2022-05-04 14:11  beginner_z  阅读(1307)  评论(0编辑  收藏  举报