https://blog.csdn.net/hry2015/article/details/79016854

1. 概述

RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现。本文的包含的主要内容如下:

- RabbitMQ的安装和管理界面
- 第一个Hello World程序
- 详细介绍RabbitMQ中消息代理(message brokers)、连接(Connections)、通道(Channels)、队列(queue)、发送消息和消息属性、发送/接收消息及使用注意事项
  • 1
  • 2
  • 3

2. RabbitMQ 概述

RabbitMQ是一个消息代理(message brokers),它负责从发布者(publishers)亦称生产者(producers)那儿接收消息,并根据既定的路由规则把接收到的消息发送给处理消息的消费者(consumers)

如果要了解RabbitMQ,最好学习一下AMQP协议,会对理解RabbitMQ工作原理有很大帮助,详细见AMQP 0-9-1 协议简介

3. 安装RabbitMQ和管理界面

  1. 安装RabbitMQ服务并启动,参考这篇文章: Ubuntu 16.04 安装 RabbitMQ
  2. 安装管理界面插件

    #启用rabbitmq-management插件:
    sudo rabbitmq-plugins enable rabbitmq_management

    使用guest/guest帐号登录管理界面 http://127.0.0.1:15672
  3. 创建新的帐号 hry/hry,并设置类型为Administrator

4. 第一个HelloWold程序

第一个RabbitMQ的HelloWorld实现如下功能,生产者发送一个消息,消息者接收到此消息:

5. 发布者(publishers)亦称生产者(producers)

负责发布消息,本节介绍发布者的代码和相关的方法

业务逻辑如下:
1 配置连接工厂
2 建立TCP连接
3 在TCP连接的基础上创建通道
4 声明一个队列
5 发送消息

5.1. 连接(Connections)和通道(Channels)

生产者和消费者是使用TCP和RabbitMQ建立长连接。但是TCP是非常耗费资源,为了减少TCP连接的数据,RabbitMQ提出通道的概念。在同一个TCP连接上可以建立多个通道,即可以把通道理解成共享一个TCP连接的多个轻量化连接。通道不是线程安全的,不推荐多个线程共用一个通道。在多线程/进程应用中,为每个线程/进程开启一个通道(channel),并且这些通道不能被线程/进程共享。
一个特定通道上的通讯与其他通道上的通讯是完全隔离的,因此每个AMQP方法都需要携带一个通道号,这样客户端就可以指定此方法是为哪个通道准备的。

以下功能的代码如下:
1 配置连接工厂
2 建立TCP连接
3 在TCP连接的基础上创建通道

// 配置连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(host);
factory.setUsername(userName);
factory.setPassword(password);
Connection connection = null;
Channel channel = null;
// 建立TCP连接
connection = factory.newConnection();
// 在TCP连接的基础上创建通道
channel = connection.createChannel();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5.2. 队列(queue)

用于存储即将要发送的消息(message)。它的大小理论上是无限的,唯一限制大小是RabbitMQ的内存和磁盘大小.队列在声明(declare)后才能被使用。声明队列操作是幂等操作,即如果一个队列尚不存在,声明一个队列会创建它,如果声明的队列已经存在,并且属性完全相同,那么此次声明不会对原有队列产生任何影响,如果声明中的属性与已存在队列的属性有差异,那么一个错误代码为406的通道级异常就会被抛出。

创建队列方法:

Queue.DeclareOk queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments) throws IOException
  • 1
  • 2

队列配置参数说明:

第一个参数queue
定义队列的名称,最多255字节的一个utf-8字符串。如果此参数为空字符串,则RabbitMQ会自动生成一个唯一的队列名,在同一个通道的后续的方法中,我们可以使用空字符串来表示之前生成的队列名称,之所以之后的方法可以获取正确的队列名是因为通道可以默默地记住消息代理最后一次生成的队列名称
以”amq.”开始的队列名称被预留做消息代理内部使用,不能被应用使用,否则抛出403 (ACCESS_REFUSED)错误
第二个参数 durable
是否持久化,如果true,则此种队列叫持久化队列(Durable queues)。此队列会被存储在磁盘上,当消息代理(broker)重启的时候,它依旧存在。没有被持久化的队列称作暂存队列(Transient queues)。
持久化的队列并不会使得路由到它的消息也具有持久性。倘若消息代理挂掉了,重新启动,那么在重启的过程中持久化队列会被重新声明,无论怎样,只有经过持久化的消息才能被重新恢复。
第三个参数 execulusive
表示此对应只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参数的优先级高于durable
第四个参数 autoDelete
当没有生成者/消费者使用此队列时,此队列会被自动删除。
(即当最后一个消费者退订后即被删除)
第五个参数 arguments
其它的扩展属性,如一些消息代理用他来完成类似与TTL功能时相关的参数

声明一个队列代码如下:

channel.queueDeclare(QUEUE_NAME+4, false, false, false, null);
  • 1

备注:还有一些特殊队列

  • 临时队列:等价于建立一个durable=false,execulusive=true,autoDelete=true,名称为随机的队列
  • 内部队列:以”amq.”开始的队列名称被预留做消息代理内部使用。如果试图在队列声明时打破这一规则的话,一个通道级的403 (ACCESS_REFUSED)错误会被抛出

5.3. 发送消息和消息属性

发送消息方法

void basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body) throws IOException;
  • 1

详细参数说明:

第一个参数exchange:
执行队列使用的交换机(exchange),交换机拿到一个消息之后将它路由给一个或零个队列。关于这块的内容后面会详细说明。如果值为空字符串,则表示使用默认交换机。默认交换机(default exchange)实际上是一个由消息代理预先声明好的没有名字(名字为空字符串)的直连交换机(direct exchange)。它有一个特殊的属性使得它对于简单应用特别有用处:那就是每个新建队列(queue)都会自动绑定到默认交换机上,绑定的路由键(routing key)名称与队列名称相同。
第二个参数routingKey:
指定路由键,如果使用默认交换机,则此值和队列名称相同
第三个参数props:
除了Routing key外,消息还可以配置如下属性,常用属性如下:
○ Content type:内容类型
○ Content encoding:内容编码
○ headers; 消息的头信息,类似http协议中的header属性
○ Delivery mode (persistent or not) 投递模式(持久化 或 非持久化): 如果此值是persistent ,则此消息存储在磁盘上。如果服务器重启,系统会保证收到的持久化消息未丢失,将消息以持久化方式发布时,会对性能造成一定的影响
○ Message priority:消息优先级
○ String correlationId;
○ ReplyTo; 反馈队列
○ Expiration period: 消息有效期
○ String messageId;
○ Message publishing timestamp:消息发送的时间戳
○ String type;
○ String userId;
○ Publisher application id:发布应用的ID
○ String clusterId;
第四个参数body:
消息的有效负载pPayload(消息实际携带的数据),它被RabbitMQ当作不透明的字节数组来对待。消息代理不会检查或者修改有效载荷。消息可以只包含属性而不携带有效载荷。

发送消息代码如下

channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
  • 1

5.4. 交换机(Exchange)

虽然代码没有出现交换机的配置,但是实际我们使用了交换机。
交换机拿到一个消息之后将它路由给一个或零个队列。它使用哪种路由算法是由交换机类型和被称作绑定(bindings)的规则所决定的。RabbitMQ的提供了四种交换机

  1. Direct exchange(直连交换机)
  2. Fanout exchange(扇型交换机)
  3. Topic exchange(主题交换机)
  4. Headers exchange(头交换机)

我的例子里只使用了默认交换机:默认交换机(default exchange)实际上是一个由消息代理预先声明好的名字为空字符串的直连交换机(direct exchange)。它有一个特殊的属性使得它对于简单应用特别有用处:那就是每个新建队列(queue)都会自动绑定到默认交换机上,绑定的路由键(routing key)名称与队列名称相同。
其他的交换机,我们后续文章再介绍。

6. 消费者(consumers)

接收并处理消息,业务如下

  1. 配置连接工厂
  2. 建立TCP连接
  3. 在TCP连接的基础上创建通道
  4. 声明一个队列
  5. 接收消息并处理

以上代码和发送者部分类似,这是略,只列出不同的部分

6.1. 接收消息并消费

Consumer
消息的接收是异常的,通过com.rabbitmq.client.Consumer定义回调方法。回调接口用于处理订阅的消息。DefaultConsumer是此接口的默认,官方也推荐如果要实现自己的Consumer,则继承此类

// 默认消费者实现
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body)
throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [HelloworldRecv] Received '" + message + "'");
}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

接收消息方法

String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;
  • 1

详细参数说明:

第一个参数queue
要接收消息的队列名称
第二个参数autoAck
消费者在处理消息的时候偶尔会失败或者有时会直接崩溃掉,而且网络原因也有可能引起各种问题。为了解决这个问题,RabbitMQ给我们两种建议:
○ 自动确认模式(automatic acknowledgement model):当RabbitMQ将消息发送给消费者后,立即从内存中删除消息。
○ 显式确认模式(explicit acknowledgement model):只有消费者发送确认回执(acknowledgement),RabbitMQ才删除消息。如果一个消费者在尚未发送确认回执的情况下挂掉了,那RabbitMQ会将消息重新投递给另一个消费者。如果当时没有可用的消费者了,RabbitMQ会死等下一个注册到此队列的消费者,然后再次尝试投递。
第三个参数callback
指定处理消息的回调类

接收消息代码如下

// 接收消息
channel.basicConsume(QUEUE_NAME, true, consumer);
  • 1
  • 2

7. 完整代码和测试

生产者的完整代码
HelloworldSend

消费者完整代码
HelloworldRecv

测试代码完整代码
测试如下场景:一个消费者启动并接收消息,一个生产者启动并发送消息

public class BasicTest {
// 测试线程池
private ExecutorService executorService = Executors.newFixedThreadPool(10); // rabbitmq的IP地址
private final String rabbitmq_host = "10.240.80.147";
// rabbitmq的用户名称
private final String rabbitmq_user = "hry";
// rabbitmq的用户密码
private final String rabbitmq_pwd = "hry"; @Test
public void helloworld() throws InterruptedException {
// 接收端
executorService.submit(() -> {
HelloworldRecv.execute(rabbitmq_host, rabbitmq_user, rabbitmq_pwd);
});
Thread.sleep(5* 100);
// 发送端
executorService.submit(() -> {
HelloworldSend.execute(rabbitmq_host, rabbitmq_user, rabbitmq_pwd);
}); // sleep 10s
Thread.sleep(10 * 1000);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

测试
执行BasicTest 的helloworld()方法,结果如下

 [HelloworldRecv] Waiting for messages.
[HelloworldSend] Sent 'Hello World!1515574041536'
[HelloworldRecv] Received 'Hello World!1515574041536'
  • 1
  • 2
  • 3

8. 代码

中间件系列一 RabbitMQ之安装和Hello World Demo的更多相关文章

  1. RabbitMQ系列之RabbitMQ单机安装

    安装epel源 rpm -ivh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm #ht ...

  2. 中间件系列三 RabbitMQ之交换机的四种类型和属性

    概述本文介绍RabbitMQ中交换机类型和属性,主要内容如下: 交换机的作用交换机的类型:Direct exchange(直连交换机).Fanout exchange(扇型交换机).Topic exc ...

  3. 消息中间件系列二:RabbitMQ入门(基本概念、RabbitMQ的安装和运行)

    一.基本概念 1. AMQP AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议.支持不同语言和不同的产品 2. 生产者 ...

  4. SpringBoot系列之RabbitMQ使用实用教程

    SpringBoot系列之RabbitMQ使用实用教程 @ 目录 1. 消息队列概述 1.1 MQ的概述 1.2 MQ目的地形式 2. 消息队列实现方式 2.1 常见MQ框架 2.2 MQ实现方式 3 ...

  5. Linux下 RabbitMQ的安装与配置-3

    一  Erlang安装 1.RabbitMQ是基于Erlang的,所以首先必须配置Erlang环境. 从Erlang的官网http://www.erlang.org/download.html 下载最 ...

  6. rabbitmq的安装和使用

    一.RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件).RabbitMQ服务器是用Erlang语言编写的,而群集和故障转移是构建在开放电信平台框架上的.所有 ...

  7. RabbitMQ使用教程(一)RabbitMQ环境安装配置及Hello World示例

    你是否听说过或者使用过队列? 你是否听说过或者使用过消息队列? 你是否听说过或者使用过RabbitMQ? 提到这几个词,用过的人,也许觉得很简单,没用过的人,也许觉得很复杂,至少在我没使用消息队列之前 ...

  8. RabbitMQ 离线安装(带视频)

    疯狂创客圈 Java 高并发[ 亿级流量聊天室实战]实战系列 [博客园总入口 ] 架构师成长+面试必备之 高并发基础书籍 [Netty Zookeeper Redis 高并发实战 ] 疯狂创客圈 高并 ...

  9. RabbitMQ的安装与使用(Centos7,linux版本)

    1.主流的消息中间件简单介绍哦. 1).ActiveMQ是Apache出品,最流行的,能力强劲的开源消息总线,并且它一个完全支持jms(java message service)规范的消息中间件.其丰 ...

随机推荐

  1. 前端er必须知道的Git地址及常用工具地址

    商城篇(找工作必练) 开源商城 推荐指数:5星,掌握了它,可以说,今后工作中的各种需求都不是问题,工作1~2年的也可以学习其中的思路(建议收藏). 这是一个集小程序/公众号/app为一体的商城系统,包 ...

  2. java正则匹配${xxx} 排除单引号双引号内的内容,前提引号必须成对出现

    public static void main(String[] a) { String wpp = "select 1, ${mark} '``this is, `/message22` ...

  3. 开源一周岁,MindSpore新特性巨量来袭

    摘要:MindSpore很多新特性与大家见面了,无论是在效率提升.易用性,还是创新方面,都是干货满满. 最近,AI计算框架是业界的热点,各大厂商纷纷投身AI框架的自研发,究其原因:AI框架在整个人工智 ...

  4. 看过这篇剖析,你还不懂 Go sync.Map 吗?

    hi, 大家好,我是 haohongfan. 本篇文章会从使用方式和原码角度剖析 sync.Map.不过不管是日常开发还是开源项目中,好像 sync.Map 并没有得到很好的利用,大家还是习惯使用 M ...

  5. Docker工具的使用

    初识 Docker jdk的版本问题,环境造成的问题很常见,称为代码的水土不服 把环境和代码一起传过去 软件跨环境迁移的问题就解决了 Docker 是一个开源的应用容器引擎 诞生于 2013 年初,基 ...

  6. 6.3string用法

    string类型可以大大方便对字符串的处理 1.string的定义 string str; string str="abcd"; 2.string中内容的访问 (1)可以像字符数组 ...

  7. Oracle recover current redo ORA-00600:[4193] (oracle 故障恢复current redo日志ORA-00600:[4193]报错)

    背景:搭建了一套oracle 19c主备库(单实例非CDB,PDB),linux7.5在断电后(没有进行数据库关闭)重启数据库报错如下图,redo当前状态下进行不完全恢复主库后resetlogs 打开 ...

  8. ES6 第二天

    三点运算符 <script type="text/javascript"> function func(...params){ params.forEach(funct ...

  9. Flutter 状态管理- 使用 MobX

    文 / Paul Halliday, developer.school 创始人 众所周知,状态管理是每个软件项目都需要持续迭代更新的方向.它并不是一个「一次性」的工作, 而需要不断确保你遵循的最佳实践 ...

  10. 基本dos命令

    Dos命令 打开cmd方法 开始---windows系统---命令提示符 win键 + R键 输入cmd --- 回车 按住Shift键---右击任意文件夹-----单击在此处打开PowerShell ...