Hello World

85 阅读4分钟

原文链接

介绍

RabbitMQ 是一个分布式的消息服务中间件: 它接收并转发消息。你可以将它理解成是一个邮局: 当信件放入邮箱,信件会先到邮局,再由邮局派邮递员发出去,,将信件送到你想送的人手里。RabbitMQ 就是这个邮局,邮箱,邮递员。

和邮局不同的是,RabbitMQ 并不会处理邮件。RabbitMQ 接收和存储的是信息的二进制数据。

基本概念

生产者: 发送消息的称之为消息的生产者。

图:

producer

队列: 在 RabbitMQ 中,邮箱的名字称之为队列。尽管消息会在 RabbitMQ 和你的应用之间传递,但在整个传递的过程中,消息只存储在队列中。队列是一个大型的消息缓冲区,它只收到主机内存和磁盘大小的限制。消息生产者们可以将消息发送到同一条队列中,而消息的消费者则可以从同一条队列中获取消息

图:

queue

消费者: 在 RabbitMQ 中,等待接收消息的被称为消费者。

图:

consumer

Note: 消费者、生成者和 RabbitMQ 不需要部署在同一台主机上,大部分情况也都是分开部署的。另外:一个应用可以即是生产者,也可以是消费者。

"Hello World"

demo 使用 Golang,Objective-CSwift 可以看这里, 更多版本请看 官网

这一小节,会使用 2 个程序进行扮演消息的生成者和消费者。生产者发送一条消息,消费者接收并打印消息。关于更多 API

下图中, "P" 表示生产者,"C" 表示消费者,而中间的箱子表示队列 - RabbitMQ 中消费者保留的消息缓冲区(产生了消息不一定要马上被消费)

python-one

Golang RabbitMQ Client

RabbitMQ 使用多种协议,本教程使用 AMQP 0-9-1 协议,开源且通用的消息传递协议。基本上每个语言都有其对应的 RabbtiMQ Client 。本教程使用的 Go amqp client

安装 ampq

go get github.com/rabbitmq/amqp091-go

Sending

sending

消息产生者 send.go 和消费者 receive.go, 产生者连接 RabbitMQ 并发送一个简单的文本消息。

send.go ,需要先导入第三方库

package main

import (
    "context"
    "log"
    "time"
    
    amqp "github.com/rabbitmq/amqp091-go"
)

创建一个检查 amqp 调用错误的函数

func failOnError(err error, msg string) {
  if err != nil {
    log.Panicf("%s:%s", msg, err);
  }
}

连接 RabbitMQ 服务

conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "Failed to connect to RabbtitMQ")
defer conn.Close()

conn 是对于套接字(封装了 ipport 的结构体)的抽象,并为我们处理协议版本和身份验证。接着创建 channelRabbitMQ 中大部分功能都基于此。

ch, err := conn.Channel()
failOnError(err, "Fail to open a channel")
defer ch.Close()

发送消息依赖队列,先创建一个队列

q, err := ch.QueueDeclare(
  "hello", 	// name
  false, 		// durable
  false, 		// delete when unused
  false,  	// exclusize
  false, 		// no-wait
  nil,			// arguments
)
failOnError(err, "Failed to declare a queue")

ctx, cancel := context.WithTimeout(context.Background(), 5 * time.Second)
defer cancel()

body := "Hello World!"
err = cn.PublishWithContext(
                ctx,
                "",	        // exchange
                q.Name,		// rounting key
                false,		// mandatory
                false,		// immediate
                amqp.Publish {
                    ContentType: "text/plain",
                    Body: []byte(Body),
                },
                )	
failOnError(err, "failed to publish a message")
log.Printf("[x] Sent %s\n", body)

声明队列是幂等(幂等: 调用一次或多次,产生的结果是一致的)的,不存在时才会创建。传递的消息是一个字节数组,所以可以传递任何数据

无法发送数据

如果你是第一次使用 RabbitMQ ,你可能会想为什么没有看到发送的消息。RabbitMQ 至少需要 200M 的空间,否则将无法接收数据。如果空间实在不够,可以通过修改 disk_free_limit 中的配置来修改其限制 - 配置文件参考

完整代码

Receiving

消费者会一直监听 RabbitMQ 中的新消息,接收并将其打印。

receiving

send.go 中一样,先添加 import 和 help 函数

package main

import (
  "log"

  amqp "github.com/rabbitmq/amqp091-go"
)

func failOnError(err error, msg string) {
  if err != nil {
    log.Panicf("%s: %s", msg, err)
  }
}

准备工作和 send.go 中一致,需要注意的是队列名称要和 send.go 中,保持一致。

// 1. 连接 RabbitMQ
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "Failed to connect RabbitMQ")
defer conn.Close()

// 2. 创建 channel
cn, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer cn.Close()

// 3. 声明队列
q, err := cn.QueueDeclare(
	"hello",
	false,
	false,
	false,
	false,
	nil)
failOnError(err, "Failed to declare a queue")

需要注意的是, 在 receive.go 中也声明了队列,因为在实际场景中,我们可能先执行消费者的相关代码,所以我们要确保队列是存在(因为创建队列是幂等的,不会有实际的影响)

RabbitMQ 会异步发送消息,我们通过协程的 channel 读取接收到的消息。

完整代码

Putting it all together

启动消息生产者

go run send.go

启动消息消费者

go run reveive.go

消费者会打印从 RabbitMQ 中接收到的消息,我们阻塞了消费者程序,所以它不会退出,而是一直接收 RabbitMQ 中的消息,可以尝试在另一个终端中启动另一个消息生产者