func (me *broker) _consumer_routine_( quit <-chan int, wait *sync.WaitGroup, events chan<- *dingo.Event, tasks chan<- []byte, receipts <-chan *dingo.TaskReceipt, name string, ) { defer wait.Done() conn := me.pool.Get() defer conn.Close() qn := fmt.Sprintf("%v.%v", _redisTaskQueue, name) for { select { case _, _ = <-quit: goto clean default: // blocking call on redis server reply, err := conn.Do("BRPOP", qn, me.cfg.GetListenTimeout()) if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, err) break } if reply == nil { // timeout, go for next round break } // the return value from BRPOP should be // an slice with length 2 v, ok := reply.([]interface{}) if !ok { events <- dingo.NewEventFromError( dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, errors.New(fmt.Sprintf("invalid reply: %v", reply)), ) break } if len(v) != 2 { events <- dingo.NewEventFromError( dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, errors.New(fmt.Sprintf("invalid reply: %v", reply)), ) break } b, ok := v[1].([]byte) if !ok { events <- dingo.NewEventFromError( dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, errors.New(fmt.Sprintf("invalid reply: %v", reply)), ) break } h, err := dingo.DecodeHeader(b) if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, err) break } tasks <- b rcpt, ok := <-receipts if !ok { goto clean } if rcpt.ID != h.ID() { events <- dingo.NewEventFromError( dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, errors.New(fmt.Sprintf("expected: %v, received: %v", h, rcpt)), ) break } } } clean: return }
func (me *broker) _consumer_routine_( quit <-chan int, wait *sync.WaitGroup, events chan<- *dingo.Event, tasks chan<- []byte, receipts <-chan *dingo.TaskReceipt, ci *AmqpChannel, queueName string, ) { defer wait.Done() defer me.receiver.ReleaseChannel(ci) // acquire an tag id := <-me.consumerTags tag := fmt.Sprintf("dingo.consumer.%d", id) // return id defer func(id int) { me.consumerTags <- id }(id) dv, err := ci.Channel.Consume( queueName, tag, // consumer Tag false, // autoAck false, // exclusive false, // noLocal false, // noWait nil, // args ) if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, err) return } for { select { case d, ok := <-dv: if !ok { goto clean } ok = func() (ok bool) { var ( reply *dingo.TaskReceipt err error ) defer func() { if err != nil || !ok { d.Nack(false, false) events <- dingo.NewEventFromError(dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, err) } else { d.Ack(false) } }() h, err := dingo.DecodeHeader(d.Body) if err != nil { return } tasks <- d.Body // block here for receipts reply, ok = <-receipts if !ok { return } if reply.ID != h.ID() { err = errors.New(fmt.Sprintf("expected: %v, received: %v", h, reply)) return } if reply.Status == dingo.ReceiptStatus.WorkerNotFound { err = errors.New(fmt.Sprintf("worker not found: %v", h)) return } return }() if !ok { goto clean } case _, _ = <-quit: goto clean } } clean: err_ := ci.Channel.Cancel(tag, false) if err_ != nil { events <- dingo.NewEventFromError(dingo.ObjT.Consumer|dingo.ObjT.NamedConsumer, err_) // should we return here?, // we still need to clean the delivery channel... } // conuming remaining deliveries, // and don't ack them. (make them requeue in amqp) for cleared := false; cleared == false; { select { case d, ok := <-dv: if !ok { cleared = true break } // requeue d.Nack(false, true) default: cleared = true } } // close output channel close(tasks) return }
func (me *backend) _store_routine_( quit <-chan int, done chan<- int, events chan<- *dingo.Event, reports chan<- []byte, ci *AmqpChannel, dv <-chan amqp.Delivery, id dingo.Meta) { var ( err error isChannelError bool = false ) defer func() { if isChannelError { // when error occurs, this channel is // automatically closed. me.receiver.ReleaseChannel(nil) } else { me.receiver.ReleaseChannel(ci) } done <- 1 close(done) close(reports) }() finished: for { select { case _, _ = <-quit: break finished case d, ok := <-dv: if !ok { break finished } d.Ack(false) reports <- d.Body } } // consuming remaining stuffs in queue done: for { select { case d, ok := <-dv: if !ok { break done } d.Ack(false) reports <- d.Body default: break done } } // cancel consuming err = ci.Channel.Cancel(getConsumerTag(id), false) if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Store, err) isChannelError = true return } // unbind queue from exchange qName, rKey := getQueueName(id), getRoutingKey(id) err = ci.Channel.QueueUnbind( qName, // name of queue rKey, // routing key "dingo.x.result", // name of exchange nil, // args ) if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Store, err) isChannelError = true return } // delete queue _, err = ci.Channel.QueueDelete( qName, // name of queue true, // ifUnused true, // ifEmpty false, // noWait ) if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Store, err) isChannelError = true return } }
func (me *backend) _reporter_routine_(quit <-chan int, done chan<- int, events chan<- *dingo.Event, reports <-chan *dingo.ReportEnvelope) { var ( err error ) defer func() { done <- 1 }() conn := me.pool.Get() defer conn.Close() for { select { case _, _ = <-quit: goto clean case e, ok := <-reports: if !ok { goto clean } _, err = conn.Do("LPUSH", getKey(e.ID), e.Body) if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Reporter, err) break } } } clean: } func (me *backend) _store_routine_(quit <-chan int, done chan<- int, events chan<- *dingo.Event, reports chan<- []byte, id dingo.Meta) { conn := me.pool.Get() defer func() { // delete key in redis _, err := conn.Do("DEL", getKey(id)) if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Store, err) } err = conn.Close() if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Store, err) } done <- 1 }() finished: for { select { case _, _ = <-quit: break finished default: // blocking call to redis reply, err := conn.Do("BRPOP", getKey(id), me.cfg.GetPollTimeout()) if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Store, err) break } if reply == nil { // timeout break } v, ok := reply.([]interface{}) if !ok { events <- dingo.NewEventFromError( dingo.ObjT.Store, errors.New(fmt.Sprintf("Unable to get array of interface{} from %v", reply)), ) break } if len(v) != 2 { events <- dingo.NewEventFromError( dingo.ObjT.Store, errors.New(fmt.Sprintf("length of reply is not 2, but %v", v)), ) break } b, ok := v[1].([]byte) if !ok { events <- dingo.NewEventFromError( dingo.ObjT.Store, errors.New(fmt.Sprintf("the first object of reply is not byte-array, but %v", v)), ) break } reports <- b } } } // // private function // func getKey(meta dingo.Meta) string { return fmt.Sprintf("%v.%s.%s", _redisResultQueue, meta.Name(), meta.ID()) }
func (me *backend) _reporter_routine_(quit <-chan int, done chan<- int, events chan<- *dingo.Event, reports <-chan *dingo.ReportEnvelope) { defer func() { done <- 1 }() out := func(e *dingo.ReportEnvelope) (err error) { // report an error event when leaving defer func() { if err != nil { events <- dingo.NewEventFromError(dingo.ObjT.Reporter, err) } }() // acquire a channel ci, err := me.sender.Channel() if err != nil { return } defer me.sender.ReleaseChannel(ci) // QueueDeclare and QueueBind should be done in Poll(...) err = ci.Channel.Publish( "dingo.x.result", // name of exchange getRoutingKey(e.ID), // routing key false, // madatory false, // immediate amqp.Publishing{ DeliveryMode: amqp.Persistent, ContentType: "text/json", Body: e.Body, }, ) if err != nil { return } // block until amqp.Channel.NotifyPublish cf := <-ci.Confirm if !cf.Ack { err = errors.New("Unable to publish to server") return } return } finished: for { select { case _, _ = <-quit: break finished case e, ok := <-reports: if !ok { // reports channel is closed break finished } out(e) } } done: // cosume all remaining reports for { select { case e, ok := <-reports: if !ok { break done } out(e) default: break done } } }