import (
"fmt"
"strconv"
"sync"
"time"
)
var Context = map[int]interface{}{}
type DependOnParams struct {
Times int
Channel chan struct{}
}
var Mutex sync.Mutex
//var MoreDependOn = map[int]DependOnParams{}
//var Group sync.WaitGroup
var MoreDependOn = sync.Map{}
//有向图
type Graph struct {
vertex []Plugin //顶点
list map[int][]Plugin //连接表边
}
type Plugin struct {
RelayOn map[int]interface{}
Name string
Index int
DependOn *DependOnParams
}
func (p Plugin)Do() interface{}{
fmt.Println(p)
return p.Index + 100000
}
//添加边
func (g *Graph) addVertex(t Plugin, s Plugin) {
g.list[t.Index] = Push(g.list[t.Index], s)
}
//取出切片第一个
func Pop(list []Plugin) (Plugin, []Plugin) {
if len(list) > 0 {
a := list[0]
b := list[1:]
return a, b
} else {
return Plugin{}, list
}
}
//推入切片
func Push(list []Plugin, value Plugin) []Plugin {
result := append(list, value)
return result
}
//添加边
func (g *Graph) KhanSort() {
var inDegree = make(map[int]int)
var queue []Plugin
for i := 0; i < len(g.vertex); i++ {
for _, m := range g.list[g.vertex[i].Index] {
inDegree[m.Index]++
}
}
for i := 0; i < len(g.vertex); i++ {
if inDegree[g.vertex[i].Index] == 0 {
queue = Push(queue, g.vertex[i])
}
}
for len(queue) > 0 {
var now Plugin
now, queue = Pop(queue)
// 模拟插件运行,并将结果进行缓存
// 同时去修改所有对该插件的依赖
go func() {
fmt.Println("---->" + strconv.Itoa(now.Do().(int)))
Context[now.Index] = now.Do()
for _,k:= range g.list[now.Index]{
inDegree[k.Index]--
}
}()
for _, k := range g.list[now.Index] {
inDegree[k.Index]--
// 对依赖进行注入
// DependencyInjection(k,now)
if inDegree[k.Index] == 0 {
queue = Push(queue, k)
}
}
}
time.Sleep(2* time.Minute)
}
// 采用另一种方案:每一个插件开启一个线程,同时进行阻塞等待,
// 当所有的依赖都执行完之后才执行方法
func (g *Graph) KhanExecute() {
for i := 1; i < len(g.vertex); i++ {
for _, m := range g.list[g.vertex[i].Index] {
m.DependOn.Times++
//result,_ := MoreDependOn.Load(m.Index)
//if result == nil {
// MoreDependOn.Store(m.Index,&DependOnParams{
// Times: 1,
// Channel: make(chan struct{}),
// })
//}else{
// depend := result.(*DependOnParams)
// depend.Times++
// MoreDependOn.Store(m.Index,depend)
//}
// 进行依赖的注册
m.RelayOn[g.vertex[i].Index] = 1
}
}
for i := 1;i < len(g.vertex);i++ {
i := i
go func() {
// 进行依赖的阻塞
//params,_ := MoreDependOn.Load(g.vertex[i].Index)
//if params == nil{
// params = &DependOnParams{
// Times: 0,
// Channel: nil,
// }
//}
//times := params.(*DependOnParams)
for j := 0;j < g.vertex[i].DependOn.Times;j++ {
select {
case <- g.vertex[i].DependOn.Channel:
case <- time.After(time.Minute * 40):fmt.Println("超时了")
}
}
// 进行依赖的注入
DependencyInjection(g.vertex[i])
result := g.vertex[i].Do()
// 进行结果的缓存
Context[g.vertex[i].Index] = result
// 通知依赖它的插件进行依赖的更新
for k := 0;k < len(g.list[g.vertex[i].Index]);k++ {
g.list[g.vertex[i].Index][k].DependOn.Channel <- struct{}{}
}
}()
}
time.Sleep(2* time.Minute)
}
func DependencyInjection(plugin Plugin) {
for index,_ := range plugin.RelayOn {
plugin.RelayOn[index] = Context[index]
}
}
// 下一步的目标就是改成多线程的
// 在执行的时候进行多线程
func main() {
g := NewGraph(9)
for i := 1;i < 9;i++ {
if i != 5{
g.vertex[i] = Plugin{Index: i, RelayOn: map[int]interface{}{},DependOn: &DependOnParams{Times: 0,Channel: make(chan struct{})}}
}else {
g.vertex[i] = Plugin{Index: 0, RelayOn: map[int]interface{}{},DependOn: &DependOnParams{Times: 0,Channel: make(chan struct{})}}
}
}
g.addVertex(g.vertex[2], g.vertex[1])
g.addVertex(g.vertex[3], g.vertex[1])
g.addVertex(g.vertex[7], g.vertex[1])
g.addVertex(g.vertex[4], g.vertex[2])
g.addVertex(g.vertex[5], g.vertex[2])
g.addVertex(g.vertex[8], g.vertex[7])
g.KhanExecute()
}
//创建图
func NewGraph(v int) *Graph {
g := new(Graph)
g.vertex = make([]Plugin,v)
g.list = map[int][]Plugin{}
i := 0
for i < v {
g.list[i] = make([]Plugin, 0)
i++
}
return g
}
本质就是进行拓扑排序,但是拓扑排序之后会将所有的入度为0的先放入队列,然后遍历这个队列,同时对会清理这个定点所有的依赖点的度;然后判断所依赖的点的入度是否为0,是的话就加入队列,直到队列为空。
插件调度的实质就是,在扫描完依赖关系之后,为每一个插件开启一个协程,该协程会根据他的依赖开启通道阻塞。在运行完成之后,首先将运行的结果缓存在上下文中,然后给依赖他的协程的通道中传入channel。
第二部分
在进行DAG的模拟实现之后,需要将这种技术运用于工作流的编排之中。首先根据业务需求对插件的操作进行抽象,插件的动作抽象包含:
-
依赖的个数
-
依赖通道(信号量)
-
依赖的数据
-
以来的数据类型
-
插件的定义
Do(ctx *static.OptionalParams, relyOnParams ...interface{}) (interface{}, error)
GetValue() chan *Value
GetIndex() int
On() *RelyOn
GetResultType() string
SetHandlerInfo(info *HandlerInfo)
GetHandlerInfo() *HandlerInfo
SetHandlerSystem(system *HandlerSystem)
GetHandlerSystem() *HandlerSystem
}
type DoValue func(ctx *static.OptionalParams, relyOnParams ...interface{}) (interface{}, error)
type HandlerSystem struct {
Executable bool
Token string
System func(value DoValue) DoValue
Recover func()
logrus.Logger
}
type HandlerInfo struct {
ID int
Rely *RelyOn
Value chan *Value
Type string
}
type RelyOn struct {
Times int
Channel chan struct{}
RelyOnParams map[int]*Value
}
type Value struct {
Type string
Result interface{}
Err chan error
}
扫描图
scheduler := graph.TasksScheduler{
PointTask: make([]graph.Handler, 0, 0),
AdjustTask: make(map[int][]graph.Handler),
HandlerContext: make(map[int]*graph.Value),
}
if a.Recall != nil {
for i := 0; i < len(a.Recall); i++ {
scheduler.PointTask = append(scheduler.PointTask, a.Recall[i])
}
}
if a.ReSorter != nil {
for i := 0; i < len(a.ReSorter); i++ {
scheduler.PointTask = append(scheduler.PointTask, a.ReSorter[i])
}
}
if a.Filter != nil {
for i := 0; i < len(a.Filter); i++ {
scheduler.PointTask = append(scheduler.PointTask, a.Filter[i])
}
}
if a.Sorter != nil {
for i := 0; i < len(a.Sorter); i++ {
scheduler.PointTask = append(scheduler.PointTask, a.Sorter[i])
}
}
if a.Extra != nil {
for i := 0; i < len(a.Extra); i++ {
scheduler.PointTask = append(scheduler.PointTask, a.Extra[i])
}
}
for index, slice := range a.Config {
RelyOns := make([]graph.Handler, 0, 0)
for k := 0; k < len(slice); k++ {
for j := 0; j < len(scheduler.PointTask); j++ {
if slice[k] == scheduler.PointTask[j].GetIndex() {
RelyOns = append(RelyOns, scheduler.PointTask[j])
}
}
}
scheduler.AdjustTask[index] = append(scheduler.AdjustTask[index], RelyOns...)
}
return &scheduler
}
执行DAG
multipleHandlerResult := make(map[int]chan interface{}, len(g.PointTask))
var ss sync.WaitGroup
ss.Add(len(g.PointTask))
for i := 0; i < len(g.PointTask); i++ {
for _, m := range g.AdjustTask[g.PointTask[i].GetIndex()] {
m.On().Times++
// 依赖值的初始化
if m.On().RelyOnParams == nil {
m.On().RelyOnParams = make(map[int]*Value)
}
m.On().RelyOnParams[g.PointTask[i].GetIndex()] = &Value{}
}
}
for i := 0; i < len(g.PointTask); i++ {
if g.PointTask[i].On().Times > 0 {
g.PointTask[i].On().Channel = make(chan struct{}, g.PointTask[i].On().Times)
}
}
for i := 0; i < len(g.PointTask); i++ {
i := i
go func() {
defer func() {
if rec := recover(); rec != nil {
SendSemaphoreToRelayed(g.AdjustTask, g.PointTask[i].GetIndex())
fmt.Println(string(debug.Stack()))
}
ss.Done()
}()
// 进行依赖的阻塞
DependencyBlocking(g.PointTask[i])
// 进行依赖的注入
relyOnParams := DependencyInjection(g.PointTask[i], g.HandlerContext)
result, _ := g.PointTask[i].Do(ctx, relyOnParams...)
if result != nil {
ctx.GoodsIDs = result.([]int64)
}
fmt.Println(result)
fmt.Println(g.PointTask[i].GetIndex())
value := &Value{
Type: g.PointTask[i].GetResultType(),
Result: result,
}
multipleHandlerResult[g.PointTask[i].GetIndex()] = make(chan interface{}, 1)
multipleHandlerResult[g.PointTask[i].GetIndex()] <- result
// 进行结果的缓存
CacheResultToContext(g.HandlerContext, value, g.PointTask[i].GetIndex())
// 给依赖它的插件发送信号量
SendSemaphoreToRelayed(g.AdjustTask, g.PointTask[i].GetIndex())
}()
}
ss.Wait()
return multipleHandlerResult
}
func DependencyInjection(plugin Handler, Context map[int]*Value) []interface{} {
relyOnParams := make([]interface{}, 0, 0)
for index, _ := range plugin.On().RelyOnParams {
if checkRelyOnParams(plugin.On().RelyOnParams[index].Type, Context[index].Type) {
relyOnParams = append(relyOnParams, Context[index])
}
}
return relyOnParams
}
func SendSemaphoreToRelayed(adjust map[int][]Handler, curPluginIndex int) {
for k := 0; k < len(adjust[curPluginIndex]); k++ {
adjust[curPluginIndex][k].On().Channel <- struct{}{}
}
}
func CacheResultToContext(ctx map[int]*Value, result *Value, index int) {
ctx[index] = result
}
func DependencyBlocking(curPlugin Handler) {
for j := 0; j < curPlugin.On().Times; j++ {
select {
case <-curPlugin.On().Channel:
case <-time.After(40 * time.Second):
fmt.Println("over the time:" + strconv.Itoa(curPlugin.GetIndex()))
}
}
fmt.Println("finish:" + strconv.Itoa(curPlugin.GetIndex()))
}
func checkRelyOnParams(args1, args2 string) bool {
return strings.EqualFold(args1, args2)
}