├── README.md ├── monkeyQ.go ├── monkeyQTools └── sdk └── monkeyQ.php /README.md: -------------------------------------------------------------------------------- 1 | MonkeyQ-队列服务 2 | ============ 3 | 4 | 以redis作为持久存储. golang实现的 消息队列。 支持 延迟队列 多实例等。项目急要,刚写完~~ 边上线边填坑吧。。 5 | 6 | monkeyQTools 是一个管理队列的小工具~~ 主要方便我临时管理下队列。 7 | 8 | 运行即可 9 | 10 | 11 | $ ./monkeyQ -host 8.8.8.8 -port 9394 -redis 127.0.0.1:6379 -auth 2016 12 | 13 | 14 | ###支持的参数: 15 | 16 | Usage of ./monkeyQ: 17 | 18 | -host string 19 | 20 | Bound IP. default:localhost (default "localhost") 21 | 22 | -port string 23 | 24 | port. default:9394 (default "9394") 25 | 26 | -redis string 27 | 28 | redis server. default:127.0.0.1:6379 (default "127.0.0.1:6379") 29 | 30 | -auth string 31 | 32 | redis server auth password 33 | -------------------------------------------------------------------------------- /monkeyQ.go: -------------------------------------------------------------------------------- 1 | //author 逆雪寒 2 | //version 0.7.1 3 | //消息队列 4 | package main 5 | 6 | import ( 7 | "encoding/json" 8 | "flag" 9 | "fmt" 10 | "github.com/garyburd/redigo/redis" 11 | "github.com/hjr265/redsync.go/redsync" 12 | "log" 13 | "net/http" 14 | "runtime" 15 | "strconv" 16 | "time" 17 | ) 18 | 19 | var ( 20 | Host string 21 | Port string 22 | Redis string 23 | Auth string 24 | ) 25 | 26 | var ( 27 | Pool *redis.Pool 28 | Qlock *redsync.Mutex 29 | ) 30 | 31 | func must(e error) { 32 | if e != nil { 33 | panic(e) 34 | } 35 | } 36 | 37 | func theMoment() int64{ 38 | return time.Now().Unix() 39 | } 40 | 41 | func toInt64(parameters string) int64 { 42 | if parameter, err := strconv.ParseInt(parameters, 10, 32); err != nil { 43 | return 0 44 | } else { 45 | return parameter 46 | } 47 | } 48 | 49 | func toString(parameters int64) string{ 50 | return strconv.FormatInt(parameters,10) 51 | } 52 | 53 | func init() { 54 | 55 | log.SetFlags(log.LstdFlags) 56 | flag.StringVar(&Host, "host", "localhost", "Bound IP. default:localhost") 57 | flag.StringVar(&Port, "port", "9394", "port. default:9394") 58 | flag.StringVar(&Redis, "redis", "127.0.0.1:6379", "redis server. default:127.0.0.1:6379") 59 | flag.StringVar(&Auth, "auth", "", "redis server auth password") 60 | 61 | flag.Parse() 62 | Pool = newPool(Redis) 63 | log.Printf("Success:redis have connected") 64 | 65 | var err error 66 | redisPool := []*redis.Pool{Pool} 67 | Qlock, err = redsync.NewMutexWithPool("redsync", redisPool) 68 | if err != nil{ 69 | log.Fatalf("redsync error: %v", err) 70 | } 71 | 72 | MonkeyQ = NewMonkey() 73 | ReadyQ = NewReadyQueue() 74 | DelayQ = NewDelayQueue() 75 | Queue = NewQueues() 76 | 77 | if err := Queue.init(); err != nil { 78 | log.Fatalf("queue init error: %s", err.Error()) 79 | } 80 | 81 | DelayQ.Trigger() 82 | MonkeyQ.FunWork() //暂去掉 83 | } 84 | 85 | func newPool(server string) *redis.Pool { 86 | return &redis.Pool{ 87 | MaxIdle: 8, 88 | MaxActive: 0, 89 | IdleTimeout: 240 * time.Second, 90 | Dial: func() (redis.Conn, error) { 91 | conn, err := redis.Dial("tcp", server) 92 | if err != nil { 93 | log.Printf("Fail:redis connection failed") 94 | return nil, err 95 | } 96 | 97 | if Auth != "" { 98 | if _, err := conn.Do("AUTH", Auth); err != nil { 99 | conn.Close() 100 | log.Printf("Fail:redis auth failed") 101 | return nil, err 102 | } 103 | } 104 | return conn, err 105 | }, 106 | TestOnBorrow: func(c redis.Conn, t time.Time) error { 107 | _, err := c.Do("PING") 108 | return err 109 | }, 110 | } 111 | } 112 | 113 | var ( 114 | MonkeyQ *Monkey 115 | ReadyQ *ReadyQueue 116 | DelayQ *DelayQueue 117 | Queue *Queues 118 | ) 119 | 120 | const ( 121 | OptQueueNames = "SysInfo_queue_names" 122 | ) 123 | 124 | type OptionQueue struct { 125 | QueueName string 126 | VisibilityTimeout string 127 | MessageRetentionPeriod string 128 | DelaySeconds string 129 | } 130 | 131 | //队列配置 132 | type Queues struct { 133 | Option map[string]OptionQueue 134 | UpdateQueue chan map[string]string 135 | QueueNameCache map[string]string 136 | } 137 | 138 | func NewQueues() *Queues { 139 | return &Queues{make(map[string]OptionQueue), make(chan map[string]string), make(map[string]string)} 140 | } 141 | 142 | func (this *Queues) init() (err error) { 143 | 144 | //动态更新队列配置 145 | go func() { 146 | for { 147 | select { 148 | case uo := <-this.UpdateQueue: 149 | this.AddQueueInOpt(uo["queueName"]) //记录新添加的queue 150 | this.SaveOptCache(uo["queueName"], uo) 151 | go DelayQ.Monitor(uo["queueName"]) 152 | } 153 | } 154 | }() 155 | 156 | items, _ := this.GetAllQueuesInfo() 157 | 158 | if len(items) == 0 { 159 | return 160 | } 161 | 162 | var optMap map[string]string 163 | for _, qname := range items { 164 | if optMap, err = this.GetOptions(qname); err != nil { 165 | return 166 | } 167 | //更新到内存 168 | this.SaveOptCache(qname, optMap) 169 | } 170 | return 171 | } 172 | 173 | func (this *Queues) SaveOptCache(qname string, opt map[string]string) { 174 | this.Option[qname] = OptionQueue{opt["queueName"], opt["visibilityTimeout"], opt["messageRetentionPeriod"], opt["delaySeconds"]} 175 | } 176 | 177 | func (this *Queues) Get(queueName string) (qn OptionQueue, ok bool) { 178 | qn, ok = this.Option[queueName] 179 | return 180 | } 181 | 182 | //系统配置 183 | func (this *Queues) AddQueueInOpt(qname string) (err error) { 184 | rdg := Pool.Get() 185 | defer rdg.Close() 186 | //记录所有的队列名 187 | _, err = rdg.Do("SADD", OptQueueNames, qname) 188 | 189 | this.QueueNameCache[qname] = "" 190 | 191 | return 192 | } 193 | 194 | func (this *Queues) DelQueueInOpt(k string) (err error) { 195 | rdg := Pool.Get() 196 | defer rdg.Close() 197 | 198 | _, err = rdg.Do("SREM", OptQueueNames, k) 199 | 200 | if _, ok := this.QueueNameCache[k]; ok { 201 | delete(this.QueueNameCache, k) 202 | } 203 | return 204 | } 205 | 206 | func (this *Queues) ExistsQueueInOpt(qname string) (ok bool, err error) { 207 | rdg := Pool.Get() 208 | defer rdg.Close() 209 | 210 | if _, ok = this.QueueNameCache[qname]; !ok { 211 | ok, err = redis.Bool(rdg.Do("SISMEMBER", OptQueueNames, qname)) 212 | } 213 | 214 | return 215 | } 216 | 217 | //获取系统配置 218 | func (this *Queues) GetAllQueuesInfo() ([]string, error) { 219 | rdg := Pool.Get() 220 | defer rdg.Close() 221 | 222 | queues, err := redis.Strings(rdg.Do("SMEMBERS", OptQueueNames)) 223 | 224 | for _, q := range queues { 225 | this.QueueNameCache[q] = "" //cache 226 | } 227 | return queues, err 228 | } 229 | 230 | func (this *Queues) GetAllQueuesInfoByCache() (map[string]string, error) { 231 | 232 | if len(this.QueueNameCache) == 0 { 233 | return nil,fmt.Errorf("queues info cache not exists") 234 | } 235 | return this.QueueNameCache,nil 236 | } 237 | 238 | func (this *Queues) DelQueue(queueName string) (err error) { 239 | rdg := Pool.Get() 240 | defer rdg.Close() 241 | 242 | if _, err = rdg.Do("DEL", this.Table(queueName)); err == nil { 243 | err = this.DelQueueInOpt(queueName) 244 | } 245 | 246 | return 247 | } 248 | 249 | func (this *Queues) Table(queueName string) string { 250 | return "configureQueue_" + queueName 251 | } 252 | 253 | func (this *Queues) GetOptions(queueName string) (opt map[string]string, err error) { 254 | rdg := Pool.Get() 255 | defer rdg.Close() 256 | 257 | queueName = this.Table(queueName) 258 | 259 | if opt, err = redis.StringMap(rdg.Do("HGETALL", queueName)); err != nil { 260 | return 261 | } 262 | 263 | return 264 | } 265 | 266 | func (this *Queues) queueExists(queueName string) (bool, error) { 267 | rdg := Pool.Get() 268 | defer rdg.Close() 269 | 270 | if result, _ := redis.Bool(rdg.Do("EXISTS", this.Table(queueName))); result { 271 | return true, fmt.Errorf("queue exists:%s", queueName) 272 | } 273 | 274 | return false, nil 275 | } 276 | 277 | func (this *Queues) build(opt OptionQueue) (err error) { 278 | rdg := Pool.Get() 279 | defer rdg.Close() 280 | 281 | queueName := this.Table(opt.QueueName) 282 | 283 | visibilityTimeout, messageRetentionPeriod, delaySeconds := toInt64(opt.VisibilityTimeout), toInt64(opt.MessageRetentionPeriod), toInt64(opt.DelaySeconds) 284 | 285 | if visibilityTimeout == 0 { 286 | return fmt.Errorf("VisibilityTimeout must be greater than zero!") 287 | } 288 | 289 | //隐藏时间 290 | if _, err = rdg.Do("HSET", queueName, "visibilityTimeout", visibilityTimeout); err != nil { 291 | return 292 | } 293 | //信息最大保存时间 294 | if _, err = rdg.Do("HSET", queueName, "messageRetentionPeriod", messageRetentionPeriod); err != nil { 295 | return 296 | } 297 | //延迟队列 298 | if _, err = rdg.Do("HSET", queueName, "delaySeconds", delaySeconds); err != nil { 299 | return 300 | } 301 | return 302 | } 303 | 304 | func (this *Queues) Create(opt OptionQueue) (err error) { 305 | if result, _ := this.queueExists(opt.QueueName); result { 306 | return fmt.Errorf("Queue %s exist", opt.QueueName) 307 | } 308 | return this.build(opt) 309 | } 310 | 311 | func (this *Queues) Update(opt OptionQueue) (err error) { 312 | if result, _ := this.queueExists(opt.QueueName); !result { 313 | return fmt.Errorf("Queue %s doesn't exist", opt.QueueName) 314 | } 315 | return this.build(opt) 316 | } 317 | 318 | type Monkey struct { 319 | } 320 | 321 | func NewMonkey() *Monkey { 322 | return &Monkey{} 323 | } 324 | 325 | //定时清理 326 | func (this *Monkey) FunWork() { 327 | go func(){ 328 | ticker := time.NewTicker(1 * time.Second) 329 | for { 330 | select { 331 | case <-ticker.C: 332 | queues, _ := Queue.GetAllQueuesInfoByCache() 333 | for qname,_ := range queues { 334 | this.CleanQueue(qname) 335 | } 336 | } 337 | } 338 | }() 339 | } 340 | 341 | //创建队列 342 | func (this *Monkey) Create(optionQueue OptionQueue) (err error) { 343 | 344 | if err = Queue.Create(optionQueue); err == nil { 345 | opt := make(map[string]string) 346 | opt["queueName"] = optionQueue.QueueName 347 | opt["visibilityTimeout"] = optionQueue.VisibilityTimeout 348 | opt["messageRetentionPeriod"] = optionQueue.MessageRetentionPeriod 349 | opt["delaySeconds"] = optionQueue.DelaySeconds 350 | 351 | Queue.UpdateQueue <- opt //创建 352 | } 353 | return 354 | } 355 | 356 | func (this *Monkey) Update(optionQueue OptionQueue) (err error) { 357 | 358 | if err = Queue.Update(optionQueue); err == nil { 359 | opt := make(map[string]string) 360 | opt["queueName"] = optionQueue.QueueName 361 | opt["visibilityTimeout"] = optionQueue.VisibilityTimeout 362 | opt["messageRetentionPeriod"] = optionQueue.MessageRetentionPeriod 363 | opt["delaySeconds"] = optionQueue.DelaySeconds 364 | 365 | Queue.UpdateQueue <- opt //更新 366 | } 367 | return 368 | } 369 | 370 | //插入队列 371 | func (this *Monkey) Push(queueName string, body string, delaySeconds string) (err error) { 372 | optionQueue, ok := Queue.Get(queueName) 373 | 374 | if !ok { 375 | return fmt.Errorf("Queue %s exception", queueName) 376 | } 377 | 378 | var delaySecondsInt, queueDelaySeconds int64 379 | 380 | if delaySeconds != "" { 381 | delaySecondsInt = toInt64(delaySeconds) 382 | } 383 | 384 | if optionQueue.DelaySeconds != "" { 385 | queueDelaySeconds = toInt64(optionQueue.DelaySeconds) 386 | } 387 | 388 | if delaySecondsInt != 0 { 389 | err = this.delayPush(queueName, body, delaySecondsInt) 390 | } else if queueDelaySeconds != 0 { 391 | err = this.delayPush(queueName, body, queueDelaySeconds) 392 | } else { 393 | err = this.readyPush(queueName, body) 394 | } 395 | return 396 | } 397 | 398 | //插入延迟队列 399 | func (this *Monkey) delayPush(queueName string, body string, delaySeconds int64) (err error) { 400 | if err = DelayQ.Add(queueName, body, delaySeconds); err != nil { 401 | return 402 | } 403 | return 404 | } 405 | 406 | //插入准备队列 407 | func (this *Monkey) readyPush(queueName string, body string) (err error) { 408 | if err = ReadyQ.Push(queueName, body); err != nil { 409 | return 410 | } 411 | return 412 | } 413 | 414 | //弹出队列 415 | func (this *Monkey) Pop(queueName string, waitSeconds int) (string, error) { 416 | body,err := ReadyQ.Pop(queueName, waitSeconds) 417 | if err != nil{ 418 | return "",err 419 | } 420 | //出列后入延迟列 421 | optionQueue, ok := Queue.Get(queueName) 422 | if ok { 423 | DelayQ.Add(queueName, body, toInt64(optionQueue.VisibilityTimeout)); 424 | } 425 | return body,err 426 | } 427 | 428 | //删除 429 | func (this *Monkey) Del(queueName string, body string) (err error) { 430 | return DelayQ.Del(queueName, body) 431 | 432 | } 433 | 434 | // 435 | func (this *Monkey) SetVisibilityTime(queueName string, body string, visibilityTime int64) (err error) { 436 | err = DelayQ.SetVisibilityTime(queueName, body, visibilityTime) 437 | return 438 | } 439 | 440 | func (this *Monkey) CleanQueue(queueName string) (err error) { 441 | rdg := Pool.Get() 442 | defer rdg.Close() 443 | 444 | optionQueue,ok := Queue.Get(queueName) 445 | delayQueueName := DelayQ.Table(queueName) 446 | 447 | if !ok { 448 | return fmt.Errorf("Queue does not exist") 449 | } 450 | 451 | holdSecond := toInt64(optionQueue.MessageRetentionPeriod) 452 | 453 | validBySecond := theMoment() - holdSecond 454 | 455 | if holdSecond != 0{ 456 | _,err = rdg.Do("ZREMRANGEBYSCORE", delayQueueName, 0, validBySecond) 457 | } 458 | return 459 | } 460 | 461 | //删除队列 462 | func (this *Monkey) DelQueue(queueName string) (err error) { 463 | if err = ReadyQ.DelQueue(queueName); err != nil { 464 | return 465 | } 466 | 467 | if err = DelayQ.DelQueue(queueName); err != nil { 468 | return 469 | } 470 | if err = Queue.DelQueue(queueName); err != nil { 471 | return 472 | } 473 | return 474 | } 475 | 476 | func (this *Monkey) Write(res http.ResponseWriter, message interface{}) { 477 | result, err := json.Marshal(message) 478 | must(err) 479 | res.Write(result) 480 | } 481 | 482 | //准备队列 483 | type ReadyQueue struct { 484 | } 485 | 486 | func NewReadyQueue() *ReadyQueue { 487 | return &ReadyQueue{} 488 | } 489 | 490 | func (this *ReadyQueue) Table(queueName string) string { 491 | return "readyQueue_" + queueName 492 | } 493 | 494 | //添加到准备队列 495 | func (this *ReadyQueue) Push(queueName string, id string) (err error) { 496 | rdg := Pool.Get() 497 | defer rdg.Close() 498 | 499 | _, err = rdg.Do("LPUSH", this.Table(queueName), id) 500 | return 501 | } 502 | 503 | //出队列 504 | func (this *ReadyQueue) Pop(queueName string, waitSeconds int) (string, error) { 505 | rdg := Pool.Get() 506 | defer rdg.Close() 507 | 508 | qn := this.Table(queueName) 509 | values, err := redis.Values(rdg.Do("BRPOP", qn, waitSeconds)) 510 | 511 | var nul, body string 512 | redis.Scan(values, &nul, &body) 513 | 514 | return body, err 515 | } 516 | 517 | func (this *ReadyQueue) DelQueue(queueName string) (err error) { 518 | rdg := Pool.Get() 519 | defer rdg.Close() 520 | 521 | _, err = rdg.Do("DEL", this.Table(queueName)) 522 | return err 523 | } 524 | 525 | func (this *ReadyQueue) MultiPush(queueName string, items []string) { 526 | rdg := Pool.Get() 527 | defer rdg.Close() 528 | 529 | queueName = this.Table(queueName) 530 | for _, v := range items { 531 | rdg.Send("LPUSH", queueName, v) 532 | } 533 | rdg.Flush() 534 | } 535 | 536 | type DelayQueue struct { 537 | } 538 | 539 | func NewDelayQueue() *DelayQueue { 540 | return &DelayQueue{} 541 | } 542 | 543 | func (this *DelayQueue) Table(queueName string) string { 544 | return "delayQueue_" + queueName 545 | } 546 | 547 | //添加 548 | func (this *DelayQueue) Add(queueName string, id string, delaySeconds int64) (err error) { 549 | redis := Pool.Get() 550 | defer redis.Close() 551 | 552 | delaySeconds = theMoment() + delaySeconds 553 | _, err = redis.Do("ZADD", this.Table(queueName), delaySeconds, id) 554 | return 555 | } 556 | 557 | //删除 558 | func (this *DelayQueue) Del(queueName string, id string) (err error) { 559 | redis := Pool.Get() 560 | defer redis.Close() 561 | 562 | _, err = redis.Do("ZREM", this.Table(queueName), id) 563 | return 564 | } 565 | 566 | func (this *DelayQueue) SetVisibilityTime(queueName string, body string, visibilityTime int64) (err error) { 567 | redis := Pool.Get() 568 | defer redis.Close() 569 | 570 | optionQueue, _ := Queue.Get(queueName) 571 | 572 | if visibilityTime == 0 { 573 | visibilityTime = toInt64(optionQueue.VisibilityTimeout) 574 | } 575 | 576 | visibilityTime = theMoment() + visibilityTime 577 | _, err = redis.Do("ZADD", this.Table(queueName), visibilityTime, body) 578 | 579 | return 580 | } 581 | 582 | func (this *DelayQueue) DelQueue(queueName string) (err error) { 583 | rdg := Pool.Get() 584 | defer rdg.Close() 585 | 586 | _, err = rdg.Do("DEL", this.Table(queueName)) 587 | return err 588 | } 589 | 590 | //移去 准备队列 591 | func (this *DelayQueue) ToReadyQueue(queueName string, exit chan bool) { 592 | rdg := Pool.Get() 593 | defer rdg.Close() 594 | 595 | if ok, _ := Queue.ExistsQueueInOpt(queueName); !ok { 596 | close(exit) 597 | log.Printf("%s queue exit", queueName) 598 | } 599 | 600 | nowBySecond := theMoment() 601 | delayQueueName := this.Table(queueName) 602 | 603 | items, err := redis.Strings(rdg.Do("ZRANGEBYSCORE", delayQueueName, 0, nowBySecond)) 604 | must(err) 605 | 606 | if len(items) != 0 { 607 | _, err = redis.Int(rdg.Do("ZREMRANGEBYSCORE", delayQueueName, 0, nowBySecond)) 608 | must(err) 609 | ReadyQ.MultiPush(queueName, items) 610 | } 611 | } 612 | 613 | //监控队列 614 | func (this *DelayQueue) Monitor(queueName string) { 615 | redis := Pool.Get() 616 | ticker := time.NewTicker(1 * time.Second) 617 | 618 | defer func() { 619 | redis.Close() 620 | ticker.Stop() 621 | Qlock.Unlock() 622 | }() 623 | 624 | exit := make(chan bool) 625 | 626 | for { 627 | select { 628 | case <-ticker.C: 629 | if err := Qlock.Lock(); err == nil { 630 | this.ToReadyQueue(queueName, exit) 631 | } 632 | case <-exit: 633 | return 634 | } 635 | } 636 | } 637 | 638 | //延迟队列定时 639 | func (this *DelayQueue) Trigger() { 640 | rdg := Pool.Get() 641 | defer rdg.Close() 642 | 643 | items, _ := Queue.GetAllQueuesInfoByCache() 644 | for qname,_ := range items { 645 | go this.Monitor(qname) 646 | } 647 | } 648 | 649 | type CreateResult struct { 650 | Success bool `json:"success"` 651 | QueueName string `json:"queueName"` 652 | VisibilityTimeout string `json:"visibilityTimeout"` 653 | MessageRetentionPeriod string `json:"messageRetentionPeriod"` 654 | DelaySeconds string `json:"delaySeconds"` 655 | Error string `json:"error"` 656 | } 657 | 658 | type UpdateResult struct { 659 | Success bool `json:"success"` 660 | QueueName string `json:"queueName"` 661 | VisibilityTimeout string `json:"visibilityTimeout"` 662 | MessageRetentionPeriod string `json:"messageRetentionPeriod"` 663 | DelaySeconds string `json:"delaySeconds"` 664 | Error string `json:"error"` 665 | } 666 | 667 | type PushResult struct { 668 | Success bool `json:"success"` 669 | QueueName string `json:"queueName"` 670 | Body string `json:"body"` 671 | DelaySeconds string `json:"delaySeconds"` 672 | Error string `json:"error"` 673 | } 674 | 675 | type PopResult struct { 676 | Success bool `json:"success"` 677 | Body string `json:"body"` 678 | Error string `json:"error"` 679 | } 680 | 681 | type DelResult struct { 682 | Success bool `json:"success"` 683 | Body string `json:"body"` 684 | Error string `json:"error"` 685 | } 686 | 687 | type DelQueueResult struct { 688 | Success bool `json:"success"` 689 | QueueName string `json:"queueName"` 690 | Error string `json:"error"` 691 | } 692 | 693 | type SetVisibilityTimeResult struct { 694 | Success bool `json:"success"` 695 | QueueName string `json:"queueName"` 696 | VisibilityTimeout string `json:"visibilityTimeout"` 697 | Error string `json:"error"` 698 | } 699 | 700 | func CreateQueue(res http.ResponseWriter, req *http.Request) { 701 | req.ParseForm() 702 | queueName := req.PostFormValue("QueueName") 703 | visibilityTimeout := req.PostFormValue("VisibilityTimeout") 704 | messageRetentionPeriod := req.PostFormValue("MessageRetentionPeriod") 705 | delaySeconds := req.PostFormValue("DelaySeconds") 706 | 707 | if queueName == "" { 708 | MonkeyQ.Write(res, CreateResult{false, queueName, visibilityTimeout, messageRetentionPeriod, delaySeconds, "QueueName must not be null"}) 709 | return 710 | } 711 | 712 | var optionQueue OptionQueue 713 | optionQueue.QueueName = queueName 714 | optionQueue.VisibilityTimeout = visibilityTimeout 715 | optionQueue.MessageRetentionPeriod = messageRetentionPeriod //最大存储时间 716 | optionQueue.DelaySeconds = delaySeconds 717 | 718 | if err := MonkeyQ.Create(optionQueue); err != nil { 719 | MonkeyQ.Write(res, CreateResult{false, queueName, visibilityTimeout, messageRetentionPeriod, delaySeconds, err.Error()}) 720 | } else { 721 | MonkeyQ.Write(res, CreateResult{true, queueName, visibilityTimeout, messageRetentionPeriod, delaySeconds, ""}) 722 | } 723 | } 724 | 725 | func UpdateQueue(res http.ResponseWriter, req *http.Request) { 726 | req.ParseForm() 727 | queueName := req.PostFormValue("QueueName") 728 | visibilityTimeout := req.PostFormValue("VisibilityTimeout") //变成活跃时间 729 | messageRetentionPeriod := req.PostFormValue("MessageRetentionPeriod") //信息最大保存时间 730 | delaySeconds := req.PostFormValue("DelaySeconds") //延迟时间 731 | 732 | if queueName == "" { 733 | MonkeyQ.Write(res, UpdateResult{false, queueName, visibilityTimeout, messageRetentionPeriod, delaySeconds, "QueueName must not be null"}) 734 | return 735 | } 736 | 737 | var optionQueue OptionQueue 738 | optionQueue.QueueName = queueName 739 | optionQueue.VisibilityTimeout = visibilityTimeout 740 | optionQueue.MessageRetentionPeriod = messageRetentionPeriod 741 | optionQueue.DelaySeconds = delaySeconds 742 | 743 | if err := MonkeyQ.Update(optionQueue); err != nil { 744 | MonkeyQ.Write(res, UpdateResult{false, queueName, visibilityTimeout, messageRetentionPeriod, delaySeconds, err.Error()}) 745 | } else { 746 | MonkeyQ.Write(res, UpdateResult{true, queueName, visibilityTimeout, messageRetentionPeriod, delaySeconds, ""}) 747 | } 748 | } 749 | 750 | func Push(res http.ResponseWriter, req *http.Request) { 751 | req.ParseForm() 752 | queueName := req.PostFormValue("queueName") 753 | body := req.PostFormValue("body") 754 | delaySeconds := req.PostFormValue("delaySeconds") //延迟时间 755 | 756 | if queueName == "" || body == "" { 757 | MonkeyQ.Write(res, PushResult{false, queueName, body, delaySeconds, "queueName and body must not be null"}) 758 | return 759 | } 760 | 761 | if err := MonkeyQ.Push(queueName, body, delaySeconds); err != nil { 762 | MonkeyQ.Write(res, PushResult{false, queueName, body, delaySeconds, err.Error()}) 763 | } else { 764 | MonkeyQ.Write(res, PushResult{true, queueName, body, delaySeconds, ""}) 765 | } 766 | } 767 | 768 | func Pop(res http.ResponseWriter, req *http.Request) { 769 | req.ParseForm() 770 | 771 | queueName := req.Form["queueName"] 772 | waitSeconds := req.Form["waitSeconds"] 773 | 774 | if len(waitSeconds) == 0 || len(queueName) == 0 { 775 | MonkeyQ.Write(res, PopResult{false, "", "queueName or waitSeconds lose"}) 776 | return 777 | } 778 | 779 | second := toInt64(waitSeconds[0]) 780 | 781 | body, err := MonkeyQ.Pop(queueName[0], int(second)) 782 | 783 | if err != nil { 784 | MonkeyQ.Write(res, PopResult{false, "", "no news"}) 785 | } else { 786 | MonkeyQ.Write(res, PopResult{true, body, ""}) 787 | } 788 | } 789 | 790 | func DelMessage(res http.ResponseWriter, req *http.Request) { 791 | req.ParseForm() 792 | queueName := req.PostFormValue("queueName") 793 | body := req.PostFormValue("body") 794 | 795 | if queueName == "" || body == "" { 796 | MonkeyQ.Write(res, DelResult{false, "", "queueName and body must not be null"}) 797 | return 798 | } 799 | 800 | err := MonkeyQ.Del(queueName, body) 801 | if err != nil { 802 | MonkeyQ.Write(res, DelResult{false, "", err.Error()}) 803 | 804 | } else { 805 | MonkeyQ.Write(res, DelResult{true, body, ""}) 806 | } 807 | } 808 | 809 | func SetVisibilityTime(res http.ResponseWriter, req *http.Request) { 810 | req.ParseForm() 811 | 812 | queueName := req.PostFormValue("queueName") 813 | body := req.PostFormValue("body") 814 | visibilityTime := req.PostFormValue("visibilityTime") 815 | 816 | if queueName == "" || body == "" { 817 | MonkeyQ.Write(res, SetVisibilityTimeResult{false, queueName, visibilityTime, "queueName and body must not be null"}) 818 | return 819 | } 820 | 821 | visibilitySecond := toInt64(visibilityTime) 822 | err := MonkeyQ.SetVisibilityTime(queueName, body, visibilitySecond) 823 | if err != nil { 824 | MonkeyQ.Write(res, SetVisibilityTimeResult{false, queueName, visibilityTime, err.Error()}) 825 | } else { 826 | MonkeyQ.Write(res, SetVisibilityTimeResult{true, queueName, visibilityTime, ""}) 827 | } 828 | } 829 | 830 | func DelQueue(res http.ResponseWriter, req *http.Request) { 831 | req.ParseForm() 832 | queueName := req.PostFormValue("queueName") 833 | 834 | if queueName == "" { 835 | MonkeyQ.Write(res, DelQueueResult{false, queueName, "queueName and body must not be null"}) 836 | return 837 | } 838 | 839 | err := MonkeyQ.DelQueue(queueName) 840 | if err != nil { 841 | MonkeyQ.Write(res, DelQueueResult{false, queueName, err.Error()}) 842 | } else { 843 | MonkeyQ.Write(res, DelQueueResult{true, queueName, ""}) 844 | } 845 | } 846 | 847 | type WaitForYou struct{} 848 | 849 | func (this *WaitForYou) ServeHTTP(res http.ResponseWriter, req *http.Request) { 850 | defer func() { 851 | if err := recover(); err != nil { 852 | log.Printf("error:%s", err) 853 | } 854 | }() 855 | 856 | ac := req.URL.Path 857 | if ac == "/createQueue" { 858 | CreateQueue(res, req) 859 | return 860 | } else if ac == "/updateQueue" { 861 | UpdateQueue(res, req) 862 | return 863 | } else if ac == "/push" { 864 | Push(res, req) 865 | return 866 | } else if ac == "/pop" { 867 | Pop(res, req) 868 | return 869 | } else if ac == "/setVisibilityTime" { 870 | SetVisibilityTime(res, req) 871 | return 872 | } else if ac == "/delMessage" { 873 | DelMessage(res, req) 874 | return 875 | } else if ac == "/delQueue" { 876 | DelQueue(res, req) 877 | return 878 | } else if ac == "/ping" { 879 | res.Write([]byte("pong")) 880 | return 881 | } 882 | 883 | http.NotFound(res, req) 884 | } 885 | 886 | func main() { 887 | 888 | runtime.GOMAXPROCS(runtime.NumCPU()) 889 | 890 | s := &http.Server{ 891 | Addr: Host + ":" + Port, 892 | Handler: &WaitForYou{}, 893 | ReadTimeout: 30 * time.Second, 894 | WriteTimeout: 30 * time.Second, 895 | MaxHeaderBytes: 1 << 20, 896 | } 897 | 898 | log.Printf("Success:HTTP has been started") 899 | log.Fatal(s.ListenAndServe()) 900 | 901 | } 902 | -------------------------------------------------------------------------------- /monkeyQTools: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | setQueueName($o['qName']); 39 | 40 | $result = $q->setVisibilityTimeout($o['visibilityTimeout']) //设置 41 | ->setDelaySeconds($o['delaySeconds']) 42 | ->create(); 43 | 44 | var_dump($result); 45 | exit; 46 | //修改队列 47 | }else if($o['do'] == "updateQ"){ 48 | 49 | if($o['delaySeconds'] == ''){ 50 | $o['delaySeconds'] = 0; 51 | } 52 | 53 | if($o['visibilityTimeout'] == ''){ 54 | $o['visibilityTimeout'] = 10; 55 | } 56 | 57 | if($o['qName'] == ''){ 58 | Usage($o['do']); 59 | } 60 | 61 | $q = $monkey->setQueueName($o['qName']); 62 | $result = $q->setVisibilityTimeout($o['visibilityTimeout']) 63 | ->setDelaySeconds($o['delaySeconds']) 64 | ->update(); 65 | 66 | //删除队列 67 | }else if($o['do'] == "deleteQ"){ 68 | 69 | if($o['qName'] == ''){ 70 | Usage($o['do']); 71 | } 72 | 73 | $q = $monkey->setQueueName($o['qName']); 74 | $result = $q->deleteQueue(); 75 | 76 | }else if($o['do'] == "push"){ 77 | 78 | if($o['qName'] == ''){ 79 | Usage($o['do']); 80 | } 81 | 82 | if($o['message'] == ''){ 83 | Qexit('message Null'); 84 | } 85 | 86 | $result = $monkey->setQueueName($o['qName']) 87 | ->message($o['message']) 88 | ->push(); 89 | 90 | }else if($o['do'] == "pop"){ 91 | 92 | if($o['qName'] == ''){ 93 | Usage($o['do']); 94 | } 95 | 96 | if($o['waitSeconds'] == ''){ 97 | Qexit('waitSeconds Null'); 98 | } 99 | 100 | $q = $monkey->setQueueName($o['qName']); 101 | $q->setWaitSeconds($o['waitSeconds']); //设置long polling 阻塞时间.默认:30秒 102 | 103 | 104 | $q->watch(function(){ 105 | $message = $q->pop(); 106 | 107 | if($message->OK){ 108 | var_dump($message->value()); //获取信息 109 | // var_dump($message->deleteMessage());//删除此信息 110 | var_dump($message->setVisibilityTime(0));//重新放入队列,设置变active时间 111 | } 112 | 113 | }); 114 | 115 | }else{ goto MonkeyQ; } 116 | 117 | function Qexit($msg){ 118 | exit($msg.PHP_EOL); 119 | } 120 | 121 | function Usage($do=''){ 122 | if($do == 'createQ'){ 123 | Qexit("qName | visibilityTimeout | delaySeconds"); 124 | }else if($do == 'updateQ'){ 125 | Qexit("qName | visibilityTimeout | delaySeconds"); 126 | }else if($do == 'push'){ 127 | Qexit("qName | message"); 128 | }else if($do == 'pop'){ 129 | Qexit("qName | waitSeconds"); 130 | }else if($do == 'deleteQ'){ 131 | Qexit("qName"); 132 | }else{ 133 | Qexit("do | qName | visibilityTimeout | delaySeconds | waitSeconds"); 134 | } 135 | } 136 | 137 | if(!$result){ Usage(); } 138 | 139 | var_dump($result); 140 | 141 | MonkeyQ: 142 | Usage(); 143 | -------------------------------------------------------------------------------- /sdk/monkeyQ.php: -------------------------------------------------------------------------------- 1 | host($host,$port); 14 | } 15 | 16 | public function __clone(){ 17 | trigger_error('Clone is not allow!',E_USER_ERROR); 18 | } 19 | 20 | protected function decode($data) { 21 | return @json_decode($data,true); 22 | } 23 | 24 | private function host($host,$port) { 25 | $this->host = "http://" . $host . ":" . $port."/"; 26 | } 27 | 28 | public static function factory($host = 'localhost',$port = 9394) { 29 | if(is_null(self::$instance)) { 30 | self::$instance = new self($host,$port); 31 | } 32 | return self::$instance; 33 | } 34 | 35 | protected function request($do,$parameter = '',$method = 'GET') { 36 | $query = ''; 37 | $ch = curl_init(); 38 | $query = http_build_query($parameter); 39 | 40 | if($method != 'GET') { 41 | curl_setopt($ch, CURLOPT_POSTFIELDS,$query); 42 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); 43 | curl_setopt($ch, CURLOPT_URL, $this->host.$do); 44 | }else{ 45 | $query = "?" . $query; 46 | curl_setopt($ch, CURLOPT_URL, $this->host.$do.$query); 47 | } 48 | curl_setopt($ch, CURLOPT_TIMEOUT,$this->waitSeconds); 49 | curl_setopt($ch, CURLOPT_RETURNTRANSFER,true); 50 | 51 | $data = curl_exec($ch); 52 | if(curl_errno($ch)){ 53 | return false; 54 | } 55 | curl_close($ch); 56 | return $data; 57 | } 58 | 59 | private $queueName = ''; 60 | private $visibilityTimeout = 10; 61 | private $messageRetentionPeriod = 0; 62 | private $delaySeconds = 0; 63 | 64 | /** 65 | * 设置队列名 66 | * 67 | * @param string $queueName 队列名 68 | */ 69 | public function setQueueName($queueName){ 70 | $this->queueName = $queueName; 71 | return $this; 72 | } 73 | 74 | /** 75 | * 设置消息可见时间 76 | * 77 | * @param int $visibilityTimeout 单位秒 78 | */ 79 | public function setVisibilityTimeout($visibilityTimeout){ 80 | $this->visibilityTimeout = $visibilityTimeout; 81 | return $this; 82 | } 83 | 84 | /** 85 | * 设置队列最大存储时间 86 | * 87 | * @param int $messageRetentionPeriod 单位秒 88 | */ 89 | public function setMessageRetentionPeriod($messageRetentionPeriod) { 90 | $this->messageRetentionPeriod = $messageRetentionPeriod; 91 | return $this; 92 | } 93 | 94 | /** 95 | * 设置队列延迟时间 96 | * 97 | * @param int $delaySeconds 单位秒 98 | */ 99 | public function setDelaySeconds($delaySeconds){ 100 | $this->delaySeconds = $delaySeconds; 101 | return $this; 102 | } 103 | 104 | /** 105 | * 创建队列 106 | */ 107 | public function create() { 108 | if($this->queueName == ""){ 109 | return false; 110 | } 111 | 112 | $result = $this->request('createQueue',[ 113 | 'QueueName' => $this->queueName, 114 | 'VisibilityTimeout' => $this->visibilityTimeout, 115 | 'MessageRetentionPeriod' => $this->messageRetentionPeriod, 116 | 'DelaySeconds' => $this->delaySeconds 117 | ],'POST'); 118 | 119 | return $this->decode($result); 120 | } 121 | 122 | /** 123 | * 更新队列设置 124 | */ 125 | public function update() { 126 | if($this->queueName == ""){ 127 | return false; 128 | } 129 | 130 | $result = $this->request('updateQueue',[ 131 | 'QueueName' => $this->queueName, 132 | 'VisibilityTimeout' => $this->visibilityTimeout, 133 | 'MessageRetentionPeriod' => $this->messageRetentionPeriod, 134 | 'DelaySeconds' => $this->delaySeconds 135 | ],'POST'); 136 | 137 | return $this->decode($result); 138 | } 139 | 140 | private $body = ''; 141 | 142 | /** 143 | * 消息主体 144 | * @param string $body 消息内容 145 | */ 146 | public function message($body){ 147 | $this->body = $body; 148 | return $this; 149 | } 150 | 151 | /** 152 | * 发布消息 153 | */ 154 | public function push() { 155 | if($this->queueName == '' || $this->body == ''){ 156 | return false; 157 | } 158 | 159 | $result = $this->request('push',[ 160 | 'queueName' => $this->queueName, 161 | 'body' => $this->body 162 | ],'POST'); 163 | 164 | return $this->decode($result); 165 | } 166 | 167 | private $waitSeconds = 30; //默认30秒 168 | 169 | /** 170 | * long polling时间 171 | * @param int $second 单位秒 172 | */ 173 | public function setWaitSeconds($second) { 174 | $this->waitSeconds = $second; 175 | } 176 | 177 | /** 178 | * long polling 179 | */ 180 | public function pop() { 181 | if($this->queueName == ''){ 182 | return false; 183 | } 184 | 185 | $item = $this->request('pop',[ 186 | 'queueName' => $this->queueName, 187 | 'waitSeconds' => $this->waitSeconds 188 | ],'GET'); 189 | 190 | 191 | $message = Message::instance(); 192 | $message->monkeyQ = $this; 193 | $message->in($this->decode($item)); 194 | return $message; 195 | } 196 | 197 | /** 198 | * long polling 守护 199 | */ 200 | public function watch(Array $fn) { 201 | 202 | if(!is_callable($fn['success']) || !is_callable($fn['fail'])){ 203 | return false; 204 | } 205 | while(true){ 206 | $message = $this->pop(); 207 | if($message->OK){ 208 | $success = $fn['success']; 209 | $success($message); 210 | }else{ 211 | $fail = $fn['fail']; 212 | $fail($message); 213 | } 214 | } 215 | } 216 | 217 | /** 218 | * 长轮训后获得到消息,可以进行:删除消息 219 | */ 220 | public function deleteMessage() { 221 | if($this->queueName == '' || $this->body == ''){ 222 | return false; 223 | } 224 | $result = $this->request('delMessage',[ 225 | 'queueName' => $this->queueName, 226 | 'body' => $this->body 227 | ],'POST'); 228 | 229 | return $this->decode($result); 230 | } 231 | 232 | /** 233 | * 长轮训后获得到消息,可以进行:重入消息队列 234 | */ 235 | public function setVisibilityTime($visibilityTime) { 236 | 237 | if($this->queueName == '' || $this->body == ''){ 238 | return false; 239 | } 240 | 241 | $result = $this->request('setVisibilityTime',[ 242 | 'queueName' => $this->queueName, 243 | 'body' => $this->body, 244 | 'visibilityTime' => (int)$visibilityTime 245 | ],'POST'); 246 | 247 | return $this->decode($result); 248 | } 249 | 250 | /** 251 | * 删除队列 252 | */ 253 | public function deleteQueue() { 254 | if($this->queueName == ''){ 255 | return false; 256 | } 257 | 258 | $result = $this->request('delQueue',[ 259 | 'queueName' => $this->queueName 260 | ],'POST'); 261 | 262 | return $this->decode($result); 263 | } 264 | } 265 | 266 | class Message{ 267 | 268 | public $OK = false; 269 | public $queueName = ''; 270 | public $monkeyQ = NULL; 271 | private $message = NULL; 272 | private static $instance = NULL; 273 | 274 | private function __construct(){ } 275 | 276 | public function __clone(){ 277 | trigger_error('Clone is not allow!',E_USER_ERROR); 278 | } 279 | 280 | public static function instance() { 281 | if(is_null(self::$instance)) { 282 | self::$instance = new self(); 283 | } 284 | return self::$instance; 285 | } 286 | 287 | public function setVisibilityTime($visibilityTime = 0){ 288 | return $this->monkeyQ->setVisibilityTime($visibilityTime); 289 | } 290 | 291 | public function deleteMessage(){ 292 | return $this->monkeyQ->deleteMessage(); 293 | } 294 | 295 | public function value(){ 296 | return $this->message; 297 | } 298 | 299 | public function in($message){ 300 | if(!$message){ 301 | $this->OK = false; 302 | }else{ 303 | $this->message = $message; 304 | if($this->message['success']){ 305 | $this->monkeyQ->message($this->message['body']); 306 | $this->OK = true; 307 | }else{ 308 | $this->OK = false; 309 | } 310 | } 311 | } 312 | } 313 | 314 | 315 | 316 | --------------------------------------------------------------------------------