Golang Worker池Job队列
这个模板是考虑用作字段校验的,之前是通过维护两个channel woker和result来实现监听的字段校验,但是这种方式是需要先全部校验完才能去遍历结果,而且我这里还需要根据数据的总长度来申请channel的缓存大小,这个方式非常不好,所以后续更改为这个模板通过控制Goroutine复用来限制并行的数量。后续还考虑过将所有的接口的封装成这种形式,但是这样无疑会降低接口的并发量,和同事讨论觉得这种形式还是存在一些业务场景的。
package Plug
import (
"fmt"
"gorm.io/gorm"
"strings"
"sync"
)
// Job接口 Do表示业务逻辑,所有实现Do()方法的结构体都可以当作一个Job类型
type Job interface {
Do()
}
type JobChan chan Job
type WorkerChan chan JobChan
var (
// 全局的Job队列
JobQueue JobChan
// 全局的Worker池
WorkerPool WorkerChan
// 全局任务分发器
Dis Dispatcher
)
// Worker
type Worker struct {
JobChannel JobChan
quit chan bool
}
// 分发器
type Dispatcher struct {
Workers []*Worker
quit chan bool
}
var MaxWorkerPoolSize = 5
// 初始化模板
func init()(){
// Job队列长度为10
JobQueue = make(JobChan,10)
// Worker Pool的容量为MaxWorkerPoolSize
WorkerPool = make(WorkerChan,MaxWorkerPoolSize)
// 任务分发器
Dis = Dispatcher{}
go Dis.Run()
}
// Worker 启动监听
func (w *Worker) Start() {
go func() {
for {
// Worker 放入 WorkerPool中
WorkerPool <- w.JobChannel
select {
// 监听取到任务,并执行业务逻辑
case job := <-w.JobChannel:
job.Do()
case <-w.quit:
return
}
}
}()
}
func (w Worker) Stop() {
go func() {
w.quit <- true
}()
}
func NewWorker()(*Worker){
return &Worker{JobChannel: make(JobChan,1)}
}
// 启动分发器
func (d *Dispatcher) Run() {
// 循环创建一定数量的Worker
for i := 0; i < MaxWorkerPoolSize; i++ {
worker := NewWorker()
d.Workers = append(d.Workers, worker)
// 启动Worker监听
worker.Start()
}
for {
select {
// 从Job队列中取得任务
case job := <-JobQueue:
go func(job Job) {
// 从WorkerPool中取出一个JobChan
jobChan := <-WorkerPool
// 把任务放到JobChan中,监听的Worker取到任务会开始Do()
jobChan <- job
}(job)
// stop dispatcher
case <-d.quit:
return
}
}
}
func (d *Dispatcher) Stop() {
go func() {
for i := range d.Workers{
d.Workers[i].Stop()
}
d.quit <- true
}()
}
type dealRuleJobData struct {
error error
wg *sync.WaitGroup
}
func(jd *dealRuleJobData)Do(){
defer jd.wg.Done()
// 业务逻辑
jd.error = nil
return
}
这个模板的之前浏览到一篇博客后借鉴的,暂时找不到了