新的阅读体验地址:http://www.zhouhong.icu/post/141

本篇文章所有的代码:https://github.com/Tom-shushu/Distributed-system-learning-notes/tree/master/rabbitmq-api-demo

一、初识RabbitMQ

是一个开源的消息代理和队列服务器,用来通过普通协议在完全不同的应用之间共享数据,RabbitMQ是使用Erlang语言来编写的,并且RabbitMQ是基于AMQP协议的。
AMQP协议Advanced Message Queuing Protocol(高级消息队列协议)
 定义:具有现代特征的二进制协议,是一个提供统一消息服务的应用层标准高级消息队列协议,
是应用层协议的一个开放标准,为面向消息中间件设计。
AMQP专业术语:
  • Server:又称broker,接受客户端的链接,实现AMQP实体服务
  • Connection:连接,应用程序与broker的网络连接
  • Channel:网络信道,几乎所有的操作都在channel中进行,Channel是进行消息读写的通道。客户端可以建立多个channel,每个channel代表一个会话任务。
  • Message:消息,服务器与应用程序之间传送的数据,由Properties和Body组成.Properties可以对消息进行修饰,必须消息的优先级、延迟等高级特性;Body则是消息体内容。
  • virtualhost: 虚拟地址,用于进行逻辑隔离,最上层的消息路由。一个virtual host里面可以有若干个Exchange和Queue,同一个Virtual Host 里面不能有相同名称的Exchange 或 Queue。
  • Exchange:交换机,接收消息,根据路由键转单消息到绑定队列
  • Binding:  Exchange和Queue之间的虚拟链接,binding中可以包换routing key
  • Routing key: 一个路由规则,虚拟机可用它来确定如何路由一个特定消息。(如负载均衡)
RabbitMQ整体架构

Exchange和队列是多对多关系,实际操作一般为1个exchange对多个队列,为避免设计过于复杂.

二、单机版快速安装

  • 1、首先在Linux上进行一些软件的准备工作
yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c++ kernel-devel m4 ncurses-devel tk tc xz
wget https://github.com/rabbitmq/erlang-rpm/releases/download/v23.0.4/erlang-23.0.4-1.el7.x86_64.rpm
wget http://repo.iotti.biz/CentOS/7/x86_64/socat-1.7.3.2-5.el7.lux.x86_64.rpm
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.5/rabbitmq-server-3.8.5-1.el7.noarch.rpm
  • 3、安装服务命令
rpm -ivh erlang-23.0.4-1.el7.x86_64.rpm
rpm -ivh socat-1.7.3.2-5.el7.lux.x86_64.rpm
rpm -ivh rabbitmq-server-3.8.5-1.el7.noarch.rpm​
  • 4、启动
启动服务
systemctl start rabbitmq-server
查看是否启动
lsof -i:5672
  • 5、启动、安装web管理插件(管控台)
rabbitmq-plugins enable rabbitmq_management
  • 6、查看管理端口有没有启动
lsof -i:15672
或者:
netstat -tnlp | grep 15672
  • 7、添加用户
#添加用户 用户名 admin 密码 admin web管理工具可用此用户登录
sudo rabbitmqctl add_user admin admin
#设置用户角色 管理员
sudo rabbitmqctl set_user_tags admin administrator
#设置用户权限(接受来自所有Host的所有操作)
sudo rabbitmqctl set_permissions -p / admin "." "." ".*"
#查看用户权限
sudo rabbitmqctl list_user_permissions admin
  • 重新启动
systemctl start rabbitmq-server
rabbitmq-plugins enable rabbitmq_management

  • 代码测试
  1. 引入依赖
        <dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.6.5</version>
</dependency>

2.发送端:

package com.zhouhong.rabbitmq.api.helloworld;
import java.util.HashMap;
import java.util.Map;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Sender {
public static void main(String[] args) throws Exception {
// 1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.2.121");
connectionFactory.setPort(5672);
connectionFactory.setUsername("admin");
connectionFactory.setPassword("admin");
connectionFactory.setVirtualHost("/");
// 2 创建Connection
Connection connection = connectionFactory.newConnection();
// 3 创建Channel
Channel channel = connection.createChannel();
// 4 声明
String queueName = "test001";
// 参数: queue名字,是否持久化,独占的queue(仅供此连接),不使用时是否自动删除, 其他参数
channel.queueDeclare(queueName, false, false, false, null);
Map<String, Object> headers = new HashMap<String, Object>();
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
.deliveryMode(2)
.contentEncoding("UTF-8")
.headers(headers).build();
for(int i = 0; i < 5;i++) {
String msg = "Hello World RabbitMQ " + i;
channel.basicPublish("", queueName , props , msg.getBytes());
}
}
}

3.接收端

package com.zhouhong.rabbitmq.api.helloworld;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
public class Receiver {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory() ;
connectionFactory.setHost("192.168.2.121");
connectionFactory.setPort(5672);
connectionFactory.setPassword("admin");
connectionFactory.setUsername("admin");
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
String queueName = "test001";
// durable 是否持久化消息
channel.queueDeclare(queueName, false, false, false, null);
QueueingConsumer consumer = new QueueingConsumer(channel);
// 参数:队列名称、是否自动ACK、Consumer
channel.basicConsume(queueName, true, consumer);
// 循环获取消息
while(true){
// 获取消息,如果没有消息,这一步将会一直阻塞
Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:" + msg);
}
}
}

4.结果(先启动接收端进行监控,再启动发送端)

收到消息:Hello World RabbitMQ 0
收到消息:Hello World RabbitMQ 1
收到消息:Hello World RabbitMQ 2
收到消息:Hello World RabbitMQ 3
收到消息:Hello World RabbitMQ 4

三、RabbitMQ----交换机

  1. Name:交换机名称。
  2. Type:交换机类型 direct、topic、fanout、headers。
  3. Durability:是否持久化,ture为持久化。
  4. Auto Delete :当最后一个绑定道Exchange上的队列删除后,自动删除该Exchange。
  5. Internal:当前Exchange是否用于RabbitMQ内部使用,默认为False。
  6. Arguments:扩展参数,用于扩展AMQP协议自制定化使用。
  7. DirectExchange的消息被转发道RouteKey中指定的Queue。
交换机-----Direct exchange
Direct模式可以使用RabbitMQ自带的Exchange:default Exchange,所以不需要将Exchange进行任何绑定操作,消息传递时,RouteKey必须完全匹配才会被队列接收,否则该消息会被抛弃。
代码:
  • 发送端
package com.zhouhong.rabbitmq.api.exchange.direct;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Sender4DirectExchange {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.2.121");
connectionFactory.setPort(5672);
connectionFactory.setPassword("admin");
connectionFactory.setUsername("admin");
connectionFactory.setVirtualHost("/");
//2 创建Connection
Connection connection = connectionFactory.newConnection();
//3 创建Channel
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_direct_exchange";
//必须要和接收端 routingKey 一一对应
String routingKey = "test_direct_routingKey";
//5 发送
String msg = "Hello World RabbitMQ 4 Direct Exchange Message ... ";
channel.basicPublish(exchangeName, routingKey , null , msg.getBytes());
}
}
  • 接收端
package com.zhouhong.rabbitmq.api.exchange.direct;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
public class Receiver4DirectExchange {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory() ;
connectionFactory.setHost("192.168.2.121");
connectionFactory.setPort(5672);
connectionFactory.setPassword("admin");
connectionFactory.setUsername("admin");
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_direct_exchange";
String exchangeType = "direct";
String queueName = "test_direct_queue";
String routingKey = "test_direct_routingKey";
channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
channel.queueDeclare(queueName, false, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
//durable 是否持久化消息
QueueingConsumer consumer = new QueueingConsumer(channel);
//参数:队列名称、是否自动ACK、Consumer
channel.basicConsume(queueName, true, consumer);
//循环获取消息
while(true){
//获取消息,如果没有消息,这一步将会一直阻塞
Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:" + msg);
}
}
}
交换机-----topic exchange
exchange 将Routekey和某个topic进行一个模糊匹配,发送给对应队列、可以用通配符进行匹配

比如下面例子
代码:
  • 接收端
package com.zhouhong.rabbitmq.api.exchange.topic;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import com.rabbitmq.client.QueueingConsumer.Delivery;
public class Receiver4TopicExchange1 {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory() ;
connectionFactory.setHost("192.168.2.121");
connectionFactory.setPort(5672);
connectionFactory.setPassword("admin");
connectionFactory.setUsername("admin");
connectionFactory.setVirtualHost("/");
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_topic_exchange";
String exchangeType = "topic";
String queueName = "test_topic_queue";
// 只能匹配一个 例如:user.txt、user.py都可以,但是user.txt.py 不行
//String routingKey = "user.*";
// user.txt、user.py 、user.txt.py 都可以匹配到
String routingKey = "user.#";
channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
channel.queueDeclare(queueName, false, false, false, null);
channel.queueBind(queueName, exchangeName, routingKey);
//durable 是否持久化消息
QueueingConsumer consumer = new QueueingConsumer(channel);
// 参数:队列名称、是否自动ACK、Consumer
channel.basicConsume(queueName, true, consumer);
System.err.println("consumer1 start.. ");
// 循环获取消息
while(true){
// 获取消息,如果没有消息,这一步将会一直阻塞
Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:" + msg + ", RoutingKey: " + delivery.getEnvelope().getRoutingKey());
}
}
}
  • 发送端
package com.zhouhong.rabbitmq.api.exchange.topic;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Sender4TopicExchange {
public static void main(String[] args) throws Exception {
//1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.2.121");
connectionFactory.setPort(5672);
connectionFactory.setPassword("admin");
connectionFactory.setUsername("admin");
connectionFactory.setVirtualHost("/");
//2 创建Connection
Connection connection = connectionFactory.newConnection();
//3 创建Channel
Channel channel = connection.createChannel();
//4 声明
String exchangeName = "test_topic_exchange";
String routingKey1 = "user.save";
String routingKey2 = "user.update";
String routingKey3 = "user.delete.abc";
//5 发送
String msg = "Hello World RabbitMQ 4 Topic Exchange Message ...";
channel.basicPublish(exchangeName, routingKey1 , null , msg.getBytes());
channel.basicPublish(exchangeName, routingKey2 , null , msg.getBytes());
channel.basicPublish(exchangeName, routingKey3 , null , msg.getBytes());
channel.close();
connection.close();
}
}
交换机-----Fanout exchange 广播模式
1.不处理路由键,只需要简单的将队列绑定到交换机上。
2.发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。
3.Fanout交换机转发消息是最快的。
代码:见示例文章开始GitHub地址

四、RabbitMQ高级特性

1、消息如何保障 100% 的投递成功
生产端的可靠性投递的标志:
1、消息成功发出
2、mq节点成功接收
3、发送端MQ节点确认应答
4、完善的消息补偿机制
解决:消息信息落库,对消息状态进行打标
幂等性
    1、 select count(1) from t_order where id = 唯一id(或)指纹码
    2、唯一id或指纹码机制,利用数据库主键去重
2、Confirm
第一步:再channel上开启确认模式:channel.confirmSelect();
第二步:再channel上添加监听:addConfirmListener,监听成功和失败的返回结果,根据具体的结果对消息进行重新发送、或记录日期等后续处理!
3、return消息机制
ReturnListener用于处理不可路由的消息
我们的消息生产者,通过指定一个Exchage和Routingkey,把消息送达某一个队列中去,然后我们的消费者监听队列,进行消费处理操作,如果没有合适的队列,则会由returnListener进行接受。
Mandatory:如果为true,则监听器会接收到路由不可达的消息,然后进行后续处理,如果为false,那么broker端自动删除该消息。
4、消费端ACK与重回队列
消费端ACK:
  • 在工作的时候一般不会选择自动ack
  • 消费端的手工ack分为两种ACK和NACK
  • 消费端进行消费的时候,如果由于业务异常我们可以进行日志的记录,然后进行补偿。这种建议回复NACK,不要重回队列
  • 如果由于服务器宕机等严重问题,那我们就需要手工进行ACK保障消费端消费成功
消费端的重回队列
  • 是为了对没有处理成功的消息,把消息重新会投递给broker。
  • 重回队列,会回到队列的尾部
  • 也会造成一条消息一直重复投递,死循环了
  • 在实际应用中,都会关闭重回队列,也就是设置为false
5、TTL队列和消息
TTL: time to live的缩写,也就是生存时间。
  • RabbitMQ 支持消息过期时间,在消息发送时可以进行指定
  • RabbitMQ支持队列过期时间,从消息入队列开始计算,只要超过了队列的超时间时间配置,那么消息会自动的清除
死队列: DLX,Dead-Letter-Exchange
  • 利用DLX,当消息在一个队列中变成死信(dead message)之后,它能被重新publish到另一个Exchange,这个Exchange就是DLX.
消息变成死信的几种情况
  • 消息被拒绝 并且requeue = false
  • 消息TTL过期
  • 队列达到最大长度
DLX也是一个正常的Exchange,实际上是一个属性控制
  • 当队列中有死信时,RabbitMQ就会自动的将这个消息重新发布到设置的Exchange上,进而被路由到另一个队列.
  • 可以监听这个队列中消息做相应的处理,这个特性可以弥补rabbitMQ3.0以前的immediate参数功能。
  • 在正常队列上添加参数:arguments.put("x-dead-letter-exchange","dlx.exchange");这样消息过期、requeue、队列达到最大长度时,就可以直接路由到死信队列。
 
 

RabbitMQ简介、安装、基本特性API--Java测试的更多相关文章

  1. RabbitMQ的安装和配置化可视界面

    RabbitMQ在windows下的安装 RabbitMQ 它依赖于Erlang,在window上安装时,需要先安装Erlang. 首先确定你的window电脑是32位还是64位,然后下载对应版本的E ...

  2. atitit.跨语言实现备份mysql数据库 为sql文件特性 api 兼容性java c#.net php js

    atitit.跨语言实现备份mysql数据库 为sql文件特性 api 兼容性java c#.net php js 1. 两个方法:: bat vs mysqldump(推荐)  vs   lang  ...

  3. (1)RabbitMQ简介与安装

    1.RabbitMQ简介 因为RabbitMQ是基于开源的AMQP协议来实现的,所以在了解MQ时候,首先我们来了解下AMQP协议.AMQP,即Advanced Message Queuing Prot ...

  4. RabbitMQ简介及安装

    AMQP简介 AMQP AMQP(Advanced Message Queuing Protocol,高级消息队列协议)是进程之间传递异步消息的网络协议. AMQP工作过程 发布者(Publisher ...

  5. 如何使用 Java 测试 IBM Systems Director 的 REST API

    转自: http://www.ibm.com/developerworks/cn/aix/library/au-aix-systemsdirector/section2.html 如何使用 Java ...

  6. Phoenix简介概述,Phoenix的Java API 相关操作优秀案例

    Phoenix简介概述,Phoenix的Java API 相关操作优秀案例 一.Phoenix概述简介 二.Phoenix实例一:Java API操作 2.1 phoenix.properties 2 ...

  7. Java之Date Time API (Java 8 新特性)

    Java 8 – Date Time API Java 8 comes with a much improved and much required change in the way date an ...

  8. RabbitMQ简介

    AMQP简介 在了解RabbitMQ之前,首先要了解AMQP协议.AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消 ...

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

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

  10. 01 . RabbitMQ简介及部署

    RabbitMQ简介 ​ MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它 ...

随机推荐

  1. Logstash 日志收集(补)

    收集 Tomcat 日志 安装 Tomcat # 安装 jdk [root@web01 ~]# rpm -ivh jdk-8u181-linux-x64.rpm # 下载 [root@web01 ~] ...

  2. python函数传参

    之前一直没有注意过该问题,在leetcode144中写递归发现该问题,不知道递归函数传参是指针还是引用. 参考:http://c.biancheng.net/view/2258.html 如果是不可变 ...

  3. PHP 弱类型 && CODE/COMMADN injection

    CODE/COMMAND INJECTION CODE INJECTION https://www.freebuf.com/sectool/168653.html EXAMPLE1 <?php ...

  4. 事件循环 EventLoop(Promise,setTimeOut,async/await执行顺序)

    什么是事件循环?想要了解什么是事件循环就要从js的工作原理开始说起: JS主要的特点就是单线程,所谓单线程就是进程中只有一个线程在运行. 为什么JS是单线程的而不是多线程的呢? JS的主要用途就是与用 ...

  5. js camelCase formatter

    js camelCase formatter 驼峰命名 转换器 'A'.charCodeAt(); // 65 'Z'.charCodeAt(); // 90 'a'.charCodeAt(); // ...

  6. How to implement an accurate countdown timer with js

    How to implement an accurate countdown timer with js 如何用 js 实现一个精确的倒计时器 原理剖析 web worker js custom ti ...

  7. web effects collection

    web effects collection typewriter effect js 打字机效果 http://www.mattboldt.com/demos/typed-js/ https://g ...

  8. HTML5 & custom element & template

    HTML5 & custom element & template template https://codepen.io/xgqfrms/pen/eYYExvp https://cs ...

  9. js navigator.wakeLock 保持屏幕唤醒状态

    let lock; btn.addEventListener("click", async () => { try { if (lock) { lock.release(); ...

  10. ng 基础

    文档 组件的工作只管用户体验,而不用顾及其它. 它应该提供用于数据绑定的属性和方法,以便作为视图和应用逻辑的中介者 组件应该把诸如从服务器获取数据.验证用户输入或直接往控制台中写日志等工作委托给各种服 ...