GO全家桶测试驱动学习:
计算步骤:
分组=》并发计算=》合并结果
业务扩展点:
业务数据与业务计算逻辑。
代码开源:
代码位置:
测试用例
package app import ( "fmt" "gitee.com/ichub/gomini/mini/general/framework/coroutines" "gitee.com/ichub/gomini/mini/model" "github.com/emirpasic/gods/lists/arraylist" "github.com/sirupsen/logrus" "testing" "time" ) func Test001_runner(t *testing.T) { //Execute() var rulesRunner = NewRulesRunner() rulesRunner.GroupNumber = 2 rulesRunner.Tasks = arraylist.New() rulesRunner.Tasks.Add(model.NewRules()) rulesRunner.Tasks.Add(model.NewRules()) rulesRunner.Tasks.Add(model.NewRules()) var result = coroutines.FindBeanCoRoutines().Execute(rulesRunner) logrus.Error(result) }
测试结果
[2024-04-427 12:07:47]D:/go-ichub/go/gomini/mini/general/framework/app/rule_runner_test.go:24 gitee.com/ichub/gomini/mini/general/framework/app.Test001_runner() {
"code": 200,
"msg": "成功",
"data": [
{
"code": 200,
"msg": "成功",
"data": [
{
"rule_id": "0",
"rule_key": "",
"name": "NAME20000",
"domain": "",
"rule": "",
"param": "",
"result": "",
"remark": "",
"func_type": "",
"created_at": {},
"created_by": "0"
}
]
},
{
"code": 200,
"msg": "成功",
"data": [
{
"rule_id": "0",
"rule_key": "",
"name": "NAME10000",
"domain": "",
"rule": "",
"param": "",
"result": "",
"remark": "",
"func_type": "",
"created_at": {},
"created_by": "0"
},
{
"rule_id": "0",
"rule_key": "",
"name": "NAME10001",
"domain": "",
"rule": "",
"param": "",
"result": "",
"remark": "",
"func_type": "",
"created_at": {},
"created_by": "0"
}
]
}
]
}
--- PASS: Test001_runner (0.02s)
PASS
代码解析
接口
package iface import ( "gitee.com/ichub/goconfig/common/base/basedto" "gitee.com/ichub/gomini/mini/general/framework/coroutines/dto" ) type CoRunnerIface interface { Execute(data *dto.CoData) *basedto.IchubResult GetData() *dto.CoData } 计算基类
package coroutines import ( "gitee.com/ichub/goconfig/common/base/basedto" "gitee.com/ichub/gomini/mini/general/framework/coroutines/dto" ) type CoRunner struct { *dto.CoData } func NewCoRunner() *CoRunner { return &CoRunner{ CoData: dto.NewCoData(), } } func (g *CoRunner) Execute(data *dto.CoData) *basedto.IchubResult { return basedto.NewIchubResult().SuccessData(data.Tasks) } func (g *CoRunner) GetData() *dto.CoData { g.CoData.DivideGroup() return g.CoData } 应用计算:
package app import ( "gitee.com/ichub/goconfig/common/base/basedto" "gitee.com/ichub/goconfig/common/base/baseutils" "gitee.com/ichub/gomini/mini/general/framework/coroutines" "gitee.com/ichub/gomini/mini/general/framework/coroutines/dto" "gitee.com/ichub/gomini/mini/model" ) type RulesRunner struct { *coroutines.CoRunner } func NewRulesRunner() *RulesRunner { return &RulesRunner{ CoRunner: coroutines.NewCoRunner(), } } func (g *RulesRunner) Execute(data *dto.CoData) *basedto.IchubResult { for i := 0; i < data.Tasks.Size(); i++ { var task, ok = data.Tasks.Get(i) if ok { task.(*model.Rules).Name = "NAME" + baseutils.Any2Str(i+10000*data.GroupOrder) } else { return basedto.ResultFailMessage("处理类型不是Rules!") } } return basedto.NewIchubResult().SuccessData(data.Tasks) }
计算执行类 package coroutines import ( "gitee.com/ichub/goconfig/common/base/basedto" "gitee.com/ichub/gomini/mini/general/framework/coroutines/dto" "gitee.com/ichub/gomini/mini/general/framework/coroutines/iface" "github.com/emirpasic/gods/lists/arraylist" "sync" ) type CoRoutines struct { basedto.BaseEntitySingle } func NewCoRoutines() *CoRoutines { return &CoRoutines{} }
func (self *CoRoutines) Execute(iface iface.CoRunnerIface) *basedto.IchubResult { if iface.GetData() == nil { return basedto.ResultFailMsg("Data is nil") } var wg sync.WaitGroup done := make(chan *basedto.IchubResult, iface.GetData().GroupTasks.Size()) for i := 0; i < iface.GetData().GroupNumber; i++ { var task, ok = iface.GetData().GroupTasks.Get(i) if !ok { continue } var data = dto.NewCoData() data.GroupOrder = i + 1 data.Tasks = task.(*arraylist.List) wg.Add(1) go func() { defer wg.Done() result := iface.Execute(data) if result == nil { result = basedto.ResultFailMsg("Task execution resulted in nil result") } done <- result }() } resultList := arraylist.New() // 恢复并优化超时机制 timeout := time.After(time.Duration(iface.GetData().TimeoutSeconds)) for i := 0; i < iface.GetData().GroupNumber; i++ { select { case <-timeout: fmt.Println("操作超时,退出。") wg.Wait() close(done) return self.Merge(resultList) case result := <-done: resultList.Add(result) } } wg.Wait() close(done) return self.Merge(resultList) } func (this *CoRoutines) MergeResult(result *basedto.IchubResult) *basedto.IchubResult { var groups = result.Data.(*arraylist.List) for i := 0; i < groups.Size(); i++ { //var r, _ = groups.Get(i) //result.Data = r.(*basedto.IchubResult).Data } return result } func (this *CoRoutines) Merge(taskResult *arraylist.List) *basedto.IchubResult { if taskResult.Size() == 0 { return basedto.ResultFailMsg("result is empty!") } var result = basedto.NewIchubResult() result.Data = taskResult return result } func (this *CoRoutines) worker(done chan *basedto.IchubResult, iface iface.CoRunnerIface, data *dto.CoData, wg *sync.WaitGroup) { defer wg.Done() var result = iface.Execute(data) done <- result } 计算数据 package dto import ( "github.com/emirpasic/gods/lists/arraylist" "github.com/sirupsen/logrus" "time" ) const DATA_WAIT_TIMEOUT = 5 * 60 * time.Second const DATA_THEAD_NUMBER = 2 type CoData struct { //协程数 GroupNumber int `json:"group_number"` TimeoutSeconds time.Duration `json:"timeout_seconds"` GroupTasks *arraylist.List `json:"group_tasks"` Tasks *arraylist.List `json:"tasks"` GroupOrder int `json:"group_order"` } func NewCoData() *CoData { return &CoData{ GroupNumber: DATA_THEAD_NUMBER, TimeoutSeconds: DATA_WAIT_TIMEOUT, } } func (this *CoData) DivideGroup() *CoData { if this.GroupTasks != nil { return this } this.GroupTasks = arraylist.New() if this.Tasks.Size() == 0 { return this } else if this.Tasks.Size() <= this.GroupNumber { this.GroupTasks.Add(this.Tasks) } else { this.Split2Group() } return this } func (this *CoData) GroupSize() int { var groupSize = this.Tasks.Size() / this.GroupNumber if this.Tasks.Size()%this.GroupNumber > 0 { groupSize += 1 } logrus.Info(groupSize, this.Tasks) return groupSize } func (this *CoData) Split2Group() { var groupSize = this.GroupSize() var j = 0 var group = arraylist.New() for i := 0; i < this.Tasks.Size(); i++ { j++ var task, ok = this.Tasks.Get(i) if ok { group.Add(task) } if j%groupSize == 0 || i == this.Tasks.Size()-1 { this.GroupTasks.Add(group) j = 0 group = arraylist.New() } } }