如何返回频道

时间:2019-07-14 00:03:29

标签: go rabbitmq channel

我正在创建一个工作程序以使用RabitMQ队列中的消息。为此,我创建了一个名为queue.go的文件

    package ExternalServices

    import (
        "../domain"
        "encoding/json"
        "github.com/streadway/amqp"
        "os"
    )

    const (
        catalogQueue = "catalog-queue"
    )

    func EnqueueMessageCatalog(catalog *domain.Catalog) error {
        marshal, err := json.Marshal(*catalog)
        if err != nil {
            return err
        }
        jsonVal := string(marshal)
        err = enqueue(catalogQueue, jsonVal)
        return err
    }

    func DequeueMessageCatalog() ([]domain.Catalog, error) {
        msgs, err := dequeue(catalogQueue)
        if err != nil {
            return nil, err
        }

        allCatalogs := make([]domain.Catalog, len(msgs))
        for _, currMsg := range msgs {
            var currCatalog domain.Catalog
            err = json.Unmarshal([]byte(currMsg), &currCatalog)
            if err != nil {
                return nil, err
            }
        }

        return allCatalogs, nil
    }

    func openConnection() (*amqp.Connection, *amqp.Channel, error) {
        conn, err := amqp.Dial(os.Getenv("RabbitMQConStr"))
        if err != nil {
            return nil, nil, err
        }

        ch, err := conn.Channel()
        if err != nil {
            conn.Close()
            return nil, nil, err
        }
        return conn, ch, nil
    }

    func ensureQueueExists(queueName string, ch *amqp.Channel) (amqp.Queue, error) {
        q, err := ch.QueueDeclare(
            queueName, // name
            false,     // durable
            false,     // delete when unused
            false,     // exclusive
            false,     // no-wait
            nil,       // arguments
        )

        return q, err
    }

    func enqueue(queueName string, message string) error {
        con, ch, err := openConnection()
        if err != nil {
            return err
        }

        defer con.Close()
        defer ch.Close()

        q, err := ensureQueueExists(queueName, ch)
        if err != nil {
            return err
        }

        err = ch.Publish(
            "",     // exchange
            q.Name, // routing key
            false,  // mandatory
            false,  // immediate
            amqp.Publishing{
                ContentType: "application/json",
                Body:        []byte(message),
            })
        return err
    }

    func dequeue(queueName string) ([]string, error) {
        con, ch, err := openConnection()
        if err != nil {
            return nil, err
        }

        defer con.Close()
        defer ch.Close()

        q, err := ensureQueueExists(queueName, ch)
        if err != nil {
            return nil, err
        }

        msgs, err := ch.Consume(
            q.Name, // queue
            "",     // consumer
            true,   // auto-ack
            false,  // exclusive
            false,  // no-local
            false,  // no-wait
            nil,    // args
        )

        if err != nil {
            return nil, err
        }

        jsons := make([]string, len(msgs))
        i := 0
        for currMsg:= range msgs {
            jsons[i] = string(currMsg.Body)
            i += 1
        }

        return jsons, nil
    }

但是,我对dequeue函数有些困惑。我希望每次有消息到达队列时都通知我的工作人员,所以我想这样做的正确方法是为我的工作人员创建一个字符串chan,因为我不想公开{{ 1}}。

到目前为止,这是我的工人。

Consume
  1. 如何修改package worker import ( "../external-services" "log" ) func StartWorker() { go func() { messages, err := ExternalServices.DequeueMessageCatalog(); if err != nil { // todo log } for d := range messages { log.Printf("Received a message: %s", d) } }() } 函数,使其返回字符串chan?
  2. 修改此方法以返回字符串chan之后,是否需要从该方法中删除行dequeuedefer con.Close()

这是我在GoLang中的第一个项目,因此,您认为可以提高代码质量的任何事情都会受到赞赏:-D

2 个答案:

答案 0 :(得分:2)

也许是这样的:

msgs, err := ch.Consume(...)

/* handle error */

stringCh := make(chan string)
done := make(chan struct{})
go func() {
    defer con.Close()
    defer ch.Close()
    defer close(stringCh)
    for {
        select {
        case currMsg := <-msgs:
            stringCh <- string(currMsg.Body)
        case <-done:
            return
        }
    }
}()
return stringCh, done

这只是一个粗略的例子。基本思想是生成另一个goroutine来监听Consume返回的消息chan。其他详细信息,例如如何正常关机,出队界面等等,取决于您的需求。

答案 1 :(得分:0)

在阅读@YSTai回复后,我意识到我错过了go例程的创建。这就是我的代码结束的方式。

worker.go

package main

import (
    "../domain"
    "../externalservices"
    "log"
    "strings"
    "sync"
)

/*
StartWorker initializes a program that will wait for messages enqueued and process them
*/
func StartWorker() {
    var wg sync.WaitGroup

    wg.Add(1)

    go func() {
        defer wg.Done()

        catalogReceived := make(chan domain.Catalog)
        defer close(catalogReceived)
        for true {

            go func() {
                externalservices.DequeueMessageCatalog(catalogReceived)

                catalog := <-catalogReceived
                website := domain.Website{
                    Name: strings.ToUpper(catalog.Name),
                    Zip:  catalog.Zip}
                externalservices.InsertWebSite(&website)
            }()
        }

    }()

    log.Printf(" [*] Waiting for messages")
    wg.Wait()
}

func main() {
    StartWorker()
}

queue.go

package externalservices

import (
    "../domain"
    "encoding/json"
    "github.com/streadway/amqp"
    "os"
)

const (
    catalogQueue = "catalog-queue"
)

func EnqueueMessageCatalog(catalog *domain.Catalog) error {
    marshal, err := json.Marshal(*catalog)
    if err != nil {
        return err
    }
    jsonVal := string(marshal)
    err = enqueue(catalogQueue, jsonVal)
    return err
}

// DequeueMessageCatalog is nice
func DequeueMessageCatalog(messageChannel chan domain.Catalog) {

    message := make(chan []byte)
    defer close(message)

    for true {
        go func() {
            dequeue(catalogQueue, message)
        }()

        currCatalog := domain.Catalog{}
        json.Unmarshal([]byte(<-message), &currCatalog)
        messageChannel <- currCatalog
    }
}

func openConnection() (*amqp.Connection, *amqp.Channel, error) {
    connString := os.Getenv("RabbitMQConStr")
    conn, err := amqp.Dial(connString)
    if err != nil {
        return nil, nil, err
    }

    ch, err := conn.Channel()
    if err != nil {
        conn.Close()
        return nil, nil, err
    }
    return conn, ch, nil
}

func ensureQueueExists(queueName string, ch *amqp.Channel) (amqp.Queue, error) {
    q, err := ch.QueueDeclare(
        queueName, // name
        false,     // durable
        false,     // delete when unused
        false,     // exclusive
        false,     // no-wait
        nil,       // arguments
    )

    return q, err
}

func enqueue(queueName string, message string) error {
    con, ch, err := openConnection()
    if err != nil {
        return err
    }

    defer con.Close()
    defer ch.Close()

    q, err := ensureQueueExists(queueName, ch)
    if err != nil {
        return err
    }

    err = ch.Publish(
        "",     // exchange
        q.Name, // routing key
        false,  // mandatory
        false,  // immediate
        amqp.Publishing{
            ContentType: "application/json",
            Body:        []byte(message),
        })
    return err
}

func dequeue(queueName string, message chan []byte) error {
    con, ch, err := openConnection()
    if err != nil {
        return err
    }

    defer con.Close()
    defer ch.Close()

    q, err := ensureQueueExists(queueName, ch)
    if err != nil {
        return err
    }

    msgs, err := ch.Consume(
        q.Name, // queue
        "",     // consumer
        true,   // auto-ack
        false,  // exclusive
        false,  // no-local
        true,  // no-wait
        nil,    // args
    )

    if err != nil {
        return err
    }

    for currMsg := range msgs {
        message <- currMsg.Body
    }

    return nil
}