微服務之間通過RabbitMQ通訊

來源:互聯網
上載者:User

微服務之間通過RabbitMQ通訊

微服務之間是相互獨立的,不像單個工程一樣各個模組之間可以直接通過方法調用實現通訊,相互獨立的服務直接一般的通訊方式是使用 HTTP協議rpc協議或者使用訊息中介軟體如RabbitMQ``Kafka

image

在這篇文章 使用Golang和MongoDB構建微服務 已經實現了一個微服務的應用,在文章中已經實現了各個服務直接的通訊,是使用的 HTTP的形式 ,那各個服務之間如何通過 RabbitMQ進行訊息通訊呢,我們現在要實現一個功能,就是一個使用者預訂電影票的介面,需要服務 User Service(port 8000) 和 服務 Booking Service(port 8003)之間通訊,使用者預訂之後,把預訂資訊寫入到 booking的資料庫中

安裝 RabbitMQ

安裝 RabbitMQ 之前需要先安裝 Erlang 的環境 ,然後下載安裝RabbitMQ ,請選擇對應的版本,安裝完成之後,RabbitMQ在Windows上是作為一個服務在後台運行,關於 RabbitMQ 的介面如何使用,請參考官網的 教程,有各個主流語言的實現我們使用的是Go版本,請下載對應的實現介面 go get github.com/streadway/amqp

RabbitMQ的介面做一下簡單的封裝

  • 定義一個介面

messaging/message.go

type IMessageClient interface {    ConnectToBroker(connectionStr string) error    PublishToQueue(data []byte, queueName string) error    SubscribeToQueue(queueName string, handlerFunc func(amqp.Delivery)) error    Close()}type MessageClient struct {    conn *amqp.Connection}
  • 串連介面
func (m *MessageClient) ConnectToBroker(connectionStr string) error {    if connectionStr == "" {        panic("the connection str mustnt be null")    }    var err error    m.conn, err = amqp.Dial(connectionStr)    return err}
  • 發布訊息介面
func (m *MessageClient) PublishToQueue(body []byte, queueName string) error {    if m.conn == nil {        panic("before publish you must connect the RabbitMQ first")    }    ch, err := m.conn.Channel()    defer ch.Close()    failOnError(err, "Failed to open a channel")    q, err := ch.QueueDeclare(        queueName,        false,        false,        false,        false,        nil,    )    failOnError(err, "Failed to declare a queue")    err = ch.Publish(        "",        q.Name,        false,        false,        amqp.Publishing{            ContentType: "application/json",            Body:        body,        },    )    failOnError(err, "Failed to publish a message")    return nil}
  • 訂閱訊息介面
func (m *MessageClient) SubscribeToQueue(queueName string, handlerFunc func(amqp.Delivery)) error {    ch, err := m.conn.Channel()    //defer ch.Close()    failOnError(err, "Failed to open a channel")    q, err := ch.QueueDeclare(        queueName,        false,        false,        false,        false,        nil,    )    failOnError(err, "Failed to declare a queue")    msgs, err := ch.Consume(        q.Name,        "",        true,        false,        false,        false,        nil,    )    failOnError(err, "Failed to register a consumer")    go consumeLoop(msgs, handlerFunc)    return nil}

實現通訊

User Service中定義一個新的POST介面 /user/{name}/booking,實現使用者的預訂功能,預訂之後,通過RabbitMQ發布一個訊息給
Booking Service,Booking Service接收到訊息之後,做相應的處理(寫入資料庫)

User Service

  • 初始化 MessageClient

users/controllers/user.go

var client messaging.IMessageClientfunc init() {    client = &messaging.MessageClient{}    err := client.ConnectToBroker("amqp://guest:guest@localhost:5672/")    if err != nil {        fmt.Println("connect to rabbitmq error", err)    }}
  • 添加新的路由和實現

routes.go

register("POST", "/user/{name}/booking", controllers.NewBooking, nil)

users/controllers/user.go

func NewBooking(w http.ResponseWriter, r *http.Request) {    params := mux.Vars(r)    user_name := params["name"]    defer r.Body.Close()    var bookings models.Booking    body, _ := ioutil.ReadAll(r.Body)    err := json.Unmarshal(body, &bookings)    if err != nil {        fmt.Println("the format body error ", err)    }    fmt.Println("user name:", user_name, bookings)    go notifyMsg(body)}
  • 用一個協程實現訊息的發布
func notifyMsg(body []byte) {    err := client.PublishToQueue(body, "new_booking")    if err != nil {        fmt.Println("Failed to publis message", err)    }}

Booking Service

  • 初始化MessageClient
var client messaging.IMessageClientfunc initMessage() {    client = &messaging.MessageClient{}    err := client.ConnectToBroker("amqp://guest:guest@localhost:5672/")    if err != nil {        fmt.Println("Failed to connect to RabbitMQ", err)    }    err = client.SubscribeToQueue("new_booking", getBooking)    if err != nil {        fmt.Println("Failed to comsuer the msg", err)    }}

在 web服務之前啟動

func main() {    initMessage()    r := routes.NewRouter()    http.ListenAndServe(":8003", r)}
  • 接收後的訊息處理
func getBooking(delivery amqp.Delivery) {  var booking models.Booking    json.Unmarshal(delivery.Body, &booking)  booking.Id = bson.NewObjectId().Hex()    dao.Insert("Booking", "BookModel", booking)    fmt.Println("the booking msg", booking)}

驗證,需要啟動 User ServiceBooking Service
使用 Postman 發送對應的資料

post 127.0.0.1:8000/user/kevin_woo/booking{    "name":"kevin_woo",    "books":[        {            "date":"20180727",            "movies":["5b4c45d49d5e3e33c4a5b97a"]        },        {            "date":"20180810",            "movies":["5b4c45ea9d5e3e33c4a5b97b"]        }    ]}

可以看到資料庫已經有了一條新的預訂資訊

說明,我這裡POST的資料就是booking資料庫中的結構,實際情況需要對資料進行封裝處理,在POST資料時,沒有對資料進行驗證,
在實際開發過程中需要對各個資料做相應的驗證,這裡主要是看一下 RabbitMQ的訊息傳遞處理的過程

源碼 Github

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.