github.com/artisanhe/tools@v1.0.1-0.20210607022958-19a8fef2eb04/mq/agent/agent.go (about) 1 package agent 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "os" 9 "reflect" 10 "strings" 11 "time" 12 13 "github.com/gomodule/redigo/redis" 14 "github.com/google/uuid" 15 "gopkg.in/robfig/cron.v2" 16 17 "github.com/artisanhe/tools/conf" 18 "github.com/artisanhe/tools/conf/presets" 19 "github.com/artisanhe/tools/courier" 20 "github.com/artisanhe/tools/env" 21 "github.com/artisanhe/tools/mq" 22 mq_redis "github.com/artisanhe/tools/mq/redis" 23 "github.com/artisanhe/tools/reflectx" 24 "github.com/artisanhe/tools/timelib" 25 ) 26 27 var ( 28 InvalidDeferTaskTime = errors.New("延迟执行任务时间错误") 29 ) 30 31 type Agent struct { 32 Name string 33 34 CentreChannel string `conf:"env"` 35 36 Protocol string 37 Host string `conf:"env,upstream"` 38 Port int32 `conf:"env"` 39 Password presets.Password `conf:"env"` 40 ConnectTimeout time.Duration 41 ReadTimeout time.Duration 42 WriteTimeout time.Duration 43 IdleTimeout time.Duration 44 MaxActive int 45 MaxIdle int 46 Wait bool 47 DB int 48 49 NumWorkers int `conf:"env"` 50 51 Prefix string 52 53 pool *redis.Pool 54 queue *mq.JobQueue 55 } 56 57 func (Agent) DockerDefaults() conf.DockerDefaults { 58 return conf.DockerDefaults{ 59 "Host": "redis", //conf.RancherInternal("tool-deps", "redis"), 60 "Port": 36379, 61 } 62 } 63 64 func getDefaultName() string { 65 serviceName := "anonymous" 66 if projectName, exists := os.LookupEnv("PROJECT_NAME"); exists { 67 serviceName = projectName 68 } 69 return strings.ToLower(fmt.Sprintf("%s.%s", serviceName, env.GetRuntimeEnv())) 70 } 71 72 func (Agent) MarshalDefaults(v interface{}) { 73 if agent, ok := v.(*Agent); ok { 74 if agent.CentreChannel == "" { 75 agent.CentreChannel = "service-task-mgr.dev" 76 } 77 if agent.Name == "" { 78 agent.Name = getDefaultName() 79 } 80 if agent.Prefix == "" { 81 agent.Prefix = "mq-" 82 } 83 if agent.NumWorkers == 0 { 84 agent.NumWorkers = 1 85 } 86 if agent.Protocol == "" { 87 agent.Protocol = "tcp" 88 } 89 if agent.Port == 0 { 90 agent.Port = 6379 91 } 92 if agent.Password == "" { 93 agent.Password = "redis" 94 } 95 if agent.ConnectTimeout == 0 { 96 agent.ConnectTimeout = 10 * time.Second 97 } 98 if agent.ReadTimeout == 0 { 99 agent.ReadTimeout = 10 * time.Second 100 } 101 if agent.WriteTimeout == 0 { 102 agent.WriteTimeout = 10 * time.Second 103 } 104 if agent.IdleTimeout == 0 { 105 agent.IdleTimeout = 240 * time.Second 106 } 107 if agent.MaxActive == 0 { 108 agent.MaxActive = 5 109 } 110 if agent.MaxIdle == 0 { 111 agent.MaxIdle = 3 112 } 113 if !agent.Wait { 114 agent.Wait = true 115 } 116 if agent.DB == 0 { 117 agent.DB = 10 118 } 119 } 120 } 121 122 func (agent *Agent) GetPool() *redis.Pool { 123 return agent.pool 124 } 125 126 func (agent *Agent) initialPool() { 127 agent.pool = &redis.Pool{ 128 Dial: func() (redis.Conn, error) { 129 return redis.Dial( 130 agent.Protocol, 131 fmt.Sprintf("%s:%d", agent.Host, agent.Port), 132 redis.DialConnectTimeout(agent.ConnectTimeout), 133 redis.DialReadTimeout(agent.ReadTimeout), 134 redis.DialWriteTimeout(agent.WriteTimeout), 135 redis.DialPassword(agent.Password.String()), 136 redis.DialDatabase(agent.DB), 137 ) 138 }, 139 MaxIdle: agent.MaxIdle, 140 MaxActive: agent.MaxActive, 141 IdleTimeout: agent.IdleTimeout, 142 Wait: true, 143 } 144 } 145 146 func (agent *Agent) RegisterReceiver(receiver func(status *mq.TaskStatus) error) { 147 agent.queue.RegisterReceiver(receiver) 148 } 149 150 func (agent *Agent) StartReceiver() { 151 agent.queue.StartReceiver(agent.NumWorkers) 152 } 153 154 func (agent *Agent) StartWorker() { 155 agent.queue.StartWorker(agent.Name, agent.NumWorkers) 156 } 157 158 func (agent *Agent) Cancel(id string) error { 159 return agent.queue.Cancel(id) 160 } 161 162 func (agent *Agent) ListChannel() ([]string, error) { 163 return agent.queue.ListChannel() 164 } 165 166 func (agent *Agent) ListSubject(channel string) ([]string, error) { 167 return agent.queue.ListSubject(channel) 168 } 169 170 func SubjectAndDataFromValue(v interface{}) (string, []byte, error) { 171 subject := reflectx.IndirectType(reflect.TypeOf(v)).Name() 172 data, err := MarshalData(v) 173 return subject, data, err 174 } 175 176 func (agent *Agent) SendTask(task *mq.Task) error { 177 return agent.queue.SendTask(task) 178 } 179 180 type CronTableInfo struct { 181 CronTableID string `json:"cronTableID"` 182 Channel string `json:"channel"` 183 Subject string `json:"subject"` 184 Spec string `json:"spec"` 185 Args string `json:"args"` 186 NextTime timelib.MySQLTimestamp `json:"nextTime"` 187 Desc string `json:"desc"` 188 } 189 190 func (agent *Agent) RegisterCron(subject string, spec string) error { 191 cronTable := &CronTableInfo{} 192 cronTable.CronTableID = fmt.Sprintf("%s-%s", agent.Name, subject) 193 cronTable.Subject = subject 194 cronTable.Channel = agent.Name 195 cronTable.Spec = spec 196 cronTable.Desc = cronTable.CronTableID 197 198 bytes, _ := json.Marshal(cronTable) 199 _, err := agent.Publish(agent.CentreChannel, "SubjectCreateOrUpdateCron", bytes) 200 return err 201 } 202 203 func (agent *Agent) Next(subject string, args interface{}) (*mq.Task, error) { 204 var data []byte 205 if args != nil { 206 data, _ = json.Marshal(args) 207 } 208 return agent.queue.Next(agent.Name, subject, data) 209 } 210 211 func (agent *Agent) Defer(subject string, nextTime time.Time, args interface{}) (*mq.Task, error) { 212 cronTable := &CronTableInfo{} 213 cronTable.CronTableID = uuid.New().String() 214 cronTable.Channel = agent.Name 215 cronTable.Subject = subject 216 cronTable.NextTime = timelib.MySQLTimestamp(nextTime) 217 218 if args != nil { 219 data, _ := json.Marshal(args) 220 cronTable.Args = string(data) 221 } 222 223 bytes, _ := json.Marshal(cronTable) 224 return agent.Publish(agent.CentreChannel, "SubjectCreateOrUpdateCron", bytes) 225 } 226 227 func (agent *Agent) Publish(chanel, subject string, data []byte) (*mq.Task, error) { 228 return agent.queue.Publish(chanel, subject, data) 229 } 230 231 func (agent *Agent) Register(subject string, job mq.Job) { 232 agent.queue.Register(subject, job) 233 } 234 235 func (agent *Agent) RegisterRoutes(routes ...*courier.Route) { 236 for _, route := range routes { 237 if len(route.Operators) == 0 { 238 continue 239 } 240 241 operatorMetas := courier.ToOperatorMetaList(route.Operators...) 242 243 lastOpIndex := len(operatorMetas) - 1 244 lastOpMeta := operatorMetas[lastOpIndex] 245 subject := lastOpMeta.Type.Name() 246 247 if cronDescriber, ok := lastOpMeta.Operator.(mq.CronDescriber); ok { 248 spec := cronDescriber.CronSpec() 249 _, err := cron.Parse(spec) 250 if err != nil { 251 panic(err) 252 } 253 if agent.RegisterCron(subject, spec); err != nil { 254 panic(err) 255 } 256 } 257 258 agent.queue.Register(subject, func(task *mq.Task) (interface{}, error) { 259 ctx := context.Background() 260 261 for i, opMeta := range operatorMetas { 262 op := reflect.New(opMeta.Type).Interface().(courier.IOperator) 263 264 if err := UnmarshalData(task.Data, op); err != nil { 265 return nil, err 266 } 267 268 ret, err := op.Output(ctx) 269 if err != nil { 270 return nil, err 271 } 272 273 if i != lastOpIndex { 274 if ctxProvider, ok := op.(courier.IContextProvider); ok { 275 ctx = context.WithValue(ctx, ctxProvider.ContextKey(), ret) 276 } 277 continue 278 } 279 280 return ret, nil 281 } 282 283 return nil, nil 284 }) 285 } 286 } 287 288 func (agent *Agent) initialQueue() { 289 agent.queue = mq.NewJobQueue( 290 mq_redis.NewRedisBroker(agent.pool, agent.Prefix), 291 mq_redis.NewRedisBackend(agent.pool, agent.Prefix), 292 ) 293 } 294 295 func (agent *Agent) Init() { 296 agent.initialPool() 297 agent.initialQueue() 298 }