来源https://javaguide.net

RabbitMQ 1-工作模型与Java编程

课前准备

预习资料

Windows安装步骤

Linux安装步骤

官网文章中文翻译系列

环境说明

操作系统:CentOS 7 JDK:1.8

Erlang:19.0.4或最新版

RabbitMQ:3.6.12或最新版 版本对应关系

典型应用场景

  1. 跨系统的异步通信。人民银行二代支付系统,使用重量级消息队列 IBM MQ,异步,解耦,削峰都有体现。
  2. 应用内的同步变成异步。秒杀:自己发送给自己
  3. 基于Pub/Sub模型实现的事件驱动。放款失败通知、提货通知、购买碎屏保、系统间同步数据、摒弃ELT(比如全量同步商户数据); 摒弃API(比如定时增量获取用户、获取产品,变成增量广播)。
  4. 利用RabbitMQ实现事务的最终一致性

基本介绍

AMQP协议

​ AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品、不同的开发语言等条件的限制。

​ AMQP的实现有:RabbitMQ、OpenAMQ、Apache Qpid、Redhat Enterprise MRG、AMQP Infrastructure、MQ、Zyre等。

RabbitMQ的特性

RabbitMQ使用Erlang语言编写,使用Mnesia数据库存储消息。

(1)可靠性(Reliability) RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认、发布确认。

(2)灵活的路由(Flexible Routing) 在消息进入队列之前,通过 Exchange 来路由消息的。对于典型的路由功能,RabbitMQ 已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能,可以将多个 Exchange 绑定在一起,也通过插件机制实现自己的 Exchange 。

(3)消息集群(Clustering) 多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker 。

(4)高可用(Highly Available Queues) 队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。

(5)多种协议(Multi-protocol) RabbitMQ 支持多种消息队列协议,比如 AMQP、STOMP、MQTT 等等。

(6)多语言客户端(Many Clients) RabbitMQ 几乎支持所有常用语言,比如 Java、.NET、Ruby、PHP、C#、JavaScript 等等。

(7)管理界面(Management UI) RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息、集群中的节点。

(8)插件机制(Plugin System)

RabbitMQ提供了许多插件,以实现从多方面扩展,当然也可以编写自己的插件。

工作模型

概念 解释
Broker 即RabbitMQ的实体服务器。提供一种传输服务,维护一条从生产者到消费者的传输线路, 保证消息数据能按照指定的方式传输。
Exchange 消息交换机。指定消息按照什么规则路由到哪个队列Queue。
Queue 消息队列。消息的载体,每条消息都会被投送到一个或多个队列中。
Binding 绑定。作用就是将Exchange和Queue按照某种路由规则绑定起来。
Routing Key 路由关键字。Exchange根据Routing Key进行消息投递。定义绑定时指定的关键字称为Binding Key。
Vhost 虚拟主机。一个Broker可以有多个虚拟主机,用作不同用户的权限分离。一个虚拟主机持有一组Exchange、Queue和Binding。
Producer 消息生产者。主要将消息投递到对应的Exchange上面。一般是独立的程序。
Consumer 消息消费者。消息的接收者,一般是独立的程序。
Connection Producer 和 Consumer 与Broker之间的TCP长连接。
Channel 消息通道,也称信道。在客户端的每个连接里可以建立多个Channel,每个Channel代表一 个会话任务。在RabbitMQ Java Client API中,channel上定义了大量的编程接口。

三种主要的交换机

Direct Exchange 直连交换机

定义:直连类型的交换机与一个队列绑定时,需要指定一个明确的binding key。

路由规则:发送消息到直连类型的交换机时,只有routing key跟binding key完全匹配时,绑定的队列才能收到消息。

例如:

// 只有队列1能收到消息
channel.basicPublish("MY_DIRECT_EXCHANGE", "key1", null, msg.getBytes());

Topic Exchange 主题交换机

定义:主题类型的交换机与一个队列绑定时,可以指定按模式匹配的routing key。

通配符有两个,*代表匹配一个单词。#代表匹配零个或者多个单词。单词与单词之间用 . 隔开。

路由规则:发送消息到主题类型的交换机时,routing key符合binding key的模式时,绑定的队列才能收到消息。

例如:

// 只有队列1能收到消息
channel.basicPublish("MY_TOPIC_EXCHANGE", "sh.abc", null, msg.getBytes());
// 队列2和队列3能收到消息
channel.basicPublish("MY_TOPIC_EXCHANGE", "bj.book", null, msg.getBytes());
// 只有队列4能收到消息
channel.basicPublish("MY_TOPIC_EXCHANGE", "abc.def.food", null, msg.getBytes());

Fanout Exchange 广播交换机

定义:广播类型的交换机与一个队列绑定时,不需要指定binding key。

路由规则:当消息发送到广播类型的交换机时,不需要指定routing key,所有与之绑定的队列都能收到消息。

例如:

// 3个队列都会收到消息
channel.basicPublish("MY_FANOUT_EXCHANGE", "", null, msg.getBytes());

Java API 编程

创建Maven工程,pom.xml引入依赖

<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.1.0</version>
</dependency>

生产者

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class MyProducer {
private final static String QUEUE_NAME = "ORIGIN_QUEUE";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
// 连接IP
factory.setHost("127.0.0.1");
// 连接端口
factory.setPort(5672);
// 虚拟机
factory.setVirtualHost("/");
// 用户
factory.setUsername("guest");
factory.setPassword("guest");
// 建立连接
Connection conn = factory.newConnection();
// 创建消息通道
Channel channel = conn.createChannel();
String msg = "Hello world, Rabbit MQ";
// 声明队列
// String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 发送消息(发送到默认交换机AMQP Default,Direct)
// 如果有一个队列名称跟Routing Key相等,那么消息会路由到这个队列
// String exchange, String routingKey, BasicProperties props, byte[] body
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
channel.close();
conn.close();
}
}

消费者

import com.rabbitmq.client.*;
import java.io.IOException;
public class MyConsumer {
private final static String QUEUE_NAME = "ORIGIN_QUEUE";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
// 连接IP
factory.setHost("127.0.0.1");
// 默认监听端口
factory.setPort(5672);
// 虚拟机
factory.setVirtualHost("/");
// 设置访问的用户
factory.setUsername("guest");
factory.setPassword("guest");
// 建立连接
Connection conn = factory.newConnection();
// 创建消息通道
Channel channel = conn.createChannel();
// 声明队列
// String queue, boolean durable, boolean exclusive, boolean autoDelete,
Map<String, Object> arguments
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
System.out.println(" Waiting for message....");
// 创建消费者
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
String msg = new String(body, "UTF-8");
System.out.println("Received message : '" + msg + "'");
}
};
// 开始获取消息
// String queue, boolean autoAck, Consumer callback
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}

参数说明

声明交换机的参数

  • String type:交换机的类型,direct, topic, fanout中的一种。
  • boolean durable:是否持久化,代表交换机在服务器重启后是否还存在。

声明队列的参数

  • boolean durable:是否持久化,代表队列在服务器重启后是否还存在。
  • boolean exclusive:是否排他性队列。排他性队列只能在声明它的Connection中使用,连接断开时自动删除。
  • boolean autoDelete:是否自动删除。如果为true,至少有一个消费者连接到这个队列,之后所有与这个队列连接的消费者都断开时,队列会自动删除。
  • Map<String, Object> arguments:队列的其他属性,例如
    • x-message-ttl、x-expires、x-max-length、x-max- length-bytes、x-dead-letter-exchange、x-dead-letter-routing-key、x-max-priority。

消息属性BasicProperties

消息的全部属性有14个,以下列举了一些主要的参数:

参数 释义
Map<String,Object> headers 消息的其他自定义参数
Integer deliveryMode 2持久化,其他:瞬态
Integer priority 消息的优先级
String correlationId 关联ID,方便RPC响应与请求关联
String replyTo 回调队列
String expiration TTL,消息过期时间,单位毫秒

进阶知识

1、TTL(Time To Live)

a、消息的过期时间

有两种设置方式:

通过队列属性设置消息过期时间:

Map<String, Object> argss = new HashMap<String, Object>();
argss.put("x-message-ttl",6000);
channel.queueDeclare("TEST_TTL_QUEUE", false, false, false, argss);

设置单条消息的过期时间:

AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2) // 持久化消息
.contentEncoding("UTF-8")
.expiration("10000") // TTL
.build();
channel.basicPublish("", "TEST_TTL_QUEUE", properties, msg.getBytes());

b、队列的过期时间

Map<String, Object> argss = new HashMap<String, Object>();
argss.put("x-message-ttl",6000); channel.queueDeclare("TEST_TTL_QUEUE", false, false, false, argss);

队列的过期时间决定了在没有任何消费者以后,队列可以存活多久。

参考:

com.gupaoedu.ttl

2、死信队列

有三种情况消息会进入DLX(Dead Letter Exchange)死信交换机。

  1. (NACK || Reject ) && requeue == false
  2. 消息过期
  3. 队列达到最大长度(先入队的消息会被发送到DLX)

可以设置一个死信队列(Dead Letter Queue)与DLX绑定,即可以存储Dead Letter,消费者可以监听这个队列取走消息。

Map<String,Object> arguments = new HashMap<String,Object>();
arguments.put("x-dead-letter-exchange","DLX_EXCHANGE");
// 指定了这个队列的死信交换机
channel.queueDeclare("TEST_DLX_QUEUE", false, false, false, arguments);
// 声明死信交换机
channel.exchangeDeclare("DLX_EXCHANGE","topic", false, false, false, null);
// 声明死信队列
channel.queueDeclare("DLX_QUEUE", false, false, false, null);
// 绑定
channel.queueBind("DLX_QUEUE","DLX_EXCHANGE","#");

参考:

com.gupaoedu.dlx

3、优先级队列

设置一个队列的最大优先级:

Map<String, Object> argss = new HashMap<String, Object>();
argss.put("x-max-priority",10); // 队列最大优先级 channel.queueDeclare("ORIGIN_QUEUE", false, false, false, argss);

发送消息时指定消息当前的优先级:

AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.priority(5) // 消息优先级
.build();
channel.basicPublish("", "ORIGIN_QUEUE", properties, msg.getBytes());

优先级高的消息可以优先被消费。但是:只有消息堆积(消息的发送速度大于消费者的消费速度)的情况下优先级才有意义。

参考:

com.gupaoedu.message

4、延迟队列

RabbitMQ本身不支持延迟队列。可以使用TTL结合DLX的方式来实现消息的延迟投递,即把DLX跟某个队列绑定, 到了指定时间,消息过期后,就会从DLX路由到这个队列,消费者可以从这个队列取走消息。

另一种方式是使用rabbitmq-delayed-message-exchange插件。

当然,将需要发送的信息保存在数据库,使用任务调度系统扫描然后发送也是可以实现的。

参考:

com.gupaoedu.dlx

5、RPC

RabbitMQ实现RPC的原理:服务端处理消息后,把响应消息发送到一个响应队列,客户端再从响应队列获取到结果。

其中的问题:Client收到消息后,怎么知道应答消息是回复哪一条消息的?所以必须有一个唯一ID来关联,就是 correlationId。

参考:

com.gupaoedu.rpc

6、服务端流控(Flow Control)

RabbitMQ 会在启动时检测机器的物理内存数值。默认当 MQ 占用 40% 以上内存时,MQ 会主动抛出一个内存警告并阻塞所有连接(Connections)。可以通过修改 rabbitmq.config 文件来调整内存阈值,默认值是 0.4,如下所示: [{rabbit, [{vm_memory_high_watermark, 0.4}]}].

默认情况,如果剩余磁盘空间在 1GB 以下,RabbitMQ 主动阻塞所有的生产者。这个阈值也是可调的。注意队列长度只在消息堆积的情况下有意义,而且会删除先入队的消息,不能实现服务端限流。

7、消费端限流

在AutoACK为false的情况下,如果一定数目的消息(通过基于consumer或者channel设置Qos的值)未被确认前,不进行消费新的消息。

channel.basicQos(2); // 如果超过2条消息没有发送ACK,当前消费者不再接收队列消息
channel.basicConsume(QUEUE_NAME, false, consumer);

参考:com.gupaoedu.limit

UI管理界面的使用

管理插件提供了更简单的管理方式。

启 用 管 理 插 件

Windows启用管理插件

cd C:\Program Files\RabbitMQ Server\rabbitmq_server-3.6.6\sbin
rabbitmq-plugins.bat enable rabbitmq_management

Linux启用管理插件

cd /usr/lib/rabbitmq/bin
./rabbitmq-plugins enable rabbitmq_management

管理界面访问端口

默认端口是15672,默认用户guest,密码guest。guest用户默认只能在本机访问。

Linux 创建 RabbitMQ 用户

例如:创建用户admin,密码admin,授权访问所有的Vhost

firewall-cmd --permanent --add-port=15672/tcp
firewall-cmd --reload
rabbitmqctl add_user admin admin
rabbitmqctl set_user_tags admin administrator
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

Spring配置方式集成RabbitMQ

参考gitlab工程步骤:

1、创建Maven工程,pom.xml引入依赖

2、src/main/resouces目录,创建rabbitMQ.xml

3、配置applicationContext.xml

4、src/main/resouces目录,log4j.properties

5、编写生产者

6、编写4个消费者

7、编写单元测试类

Spring Boot集成RabbitMQ

参考gitlab工程

来源于: https://javaguide.net

微信公众号:不止极客

百万架构师第四十课:RabbitMq:RabbitMq-工作模型与JAVA编程|JavaGuide的更多相关文章

  1. NeHe OpenGL教程 第四十课:绳子的模拟

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  2. 第四十课:CSS3 transition详解

    W3C中对transition是这样描述的:允许css的属性值在一定的时间内平滑的过渡,也就是说,以动画的效果改变css的属性值. transition主要包含4个属性值:transition-pro ...

  3. 阿里架构师花近十年时间整理出来的Java核心知识pdf(Java岗)

    由于细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容! 整理了一份Java核心知识点.覆盖了JVM.锁.并发.Java反射.Spring原理.微服务.Zooke ...

  4. 潭州课堂25班:Ph201805201 django 项目 第四十课 后台 文章发布,更新实现,热门新闻管理,轮播图管理(课堂笔记)

    把图片上传到 七牛云,必须经过后台的许可, 在虚拟机中安装七牛云所需模块pip install qiniu # 创建utils/secrets/qiniu_secret_info.py文件 # 从七牛 ...

  5. python第四十课——构造函数

    1.动态给对象添加属性: 在对象创建完毕后,单独为其添加需要的属性:可以理解为:私人定制 [注意]: 添加的属性只有此对象能够使用,别的对象如果用了,直接报错; 2.构造函数/构造方法/构造器: 格式 ...

  6. Jmeter(四十二)Jmeter工作原理

    “千举万变,其道一也.不离于宗,谓之天人” ----<荀子·儒效>和<庄子·天下> 作为接口测试工具 Jmeter只是作为发起请求的客户端(可以理解为前端),Jmeter是作为 ...

  7. 最新咕咆+鲁班+图灵+享学+蚂蚁+硅谷+源码 Java架构师资料《Java架构师VIP课程》

    最新的Java架构师完整资料,完整视频+源码+文档. 每一套都是一百多个G的资料,无密. JAVA架构师全套课程 咕泡学院互联网架构师第一期 咕泡学院互联网架构师第二期 咕泡学院互联网架构师第三期 博 ...

  8. 十年阿里顶级架构师教你怎么使用Java来搭建微服务

    微服务背后的大理念是将大型.复杂且历时长久的应用在架构上设计为内聚的服务,这些服务能够随着时间的流逝而演化.本文主要介绍了利用 Java 生态系统构建微服务的多种方法,并分析了每种方法的利弊. 快速预 ...

  9. 15套java架构师、集群、高可用、高可扩展、高性能、高并发、性能优化、Spring boot、Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分布式项目实战视频教程

    * { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩展. ...

  10. 15套java互联网架构师、高并发、集群、负载均衡、高可用、数据库设计、缓存、性能优化、大型分布式 项目实战视频教程

    * { font-family: "Microsoft YaHei" !important } h1 { color: #FF0 } 15套java架构师.集群.高可用.高可扩 展 ...

随机推荐

  1. 一款.NET开源的Windows资源管理器标签页工具

    前言 今天大姚给大家分享一款基于.NET开发的可以让你在Windows资源管理器中使用Tab多标签功能的小工具:QTTabBar. 工具介绍 QTTabBar是一款基于.NET开发的可以让你在Wind ...

  2. 一个关于CountDownLatch的并发需求

    需求 A,B,C可并发运行,全部成功才算成功,一个失败全员回滚. 思考 使用CountDownLatch,可以保证三个线程结束后,才进行提交成功状态.但是怎么才能判断某个任务失败了呢? 捕获子线程异常 ...

  3. Nuxt.js 应用中的 close 事件钩子

    title: Nuxt.js 应用中的 close 事件钩子 date: 2024/12/2 updated: 2024/12/2 author: cmdragon excerpt: close 钩子 ...

  4. Vue.js 其他指令

    1.ref标识 通过给HTML标签或者组件标签设定ref属性,根据他的属性值来访问这个HTML元素或者组件 语法:this.$refs.属性值 //为p标签设置ref属性 <div id=&qu ...

  5. IOS热重载工具InjectionIII

    IOS热重载工具InjectionIII 支持 OC.Swift 以及 Swift 和 OC 混编项目的 UI 热重载工具,采取在模拟器(真机不支持)注入方式实现 UI 热重载,修改完 UI 直接 c ...

  6. 构建模块化 CLI:Lerna + Commander 打造灵活的基础脚手架

    在现代软件开发中,创建 定制化的命令行工具(CLI) 已成为满足公司业务需求的关键一环.这类工具可以辅助执行诸如代码检查.项目初始化等任务.为了提高开发效率并简化维护过程,我们将功能模块化,并通过多个 ...

  7. 【C#】【Cookie】Cookie设置与读取

    依赖 using System.Web; 设置Cookie 1.新建Cookie对象 HttpCookie cookie = new HttpCookie("UserInfo"); ...

  8. Qt/C++音视频开发54-视频监控控件的极致设计

    一.前言 跌跌撞撞摸爬滚打一步步迭代完善到今天,这个视频监控控件的设计,在现阶段水平上个人认为是做的最棒的(稍微自恋一下),理论上来说应该可以用5年不用推翻重写,推翻重写当然也是程序员爱干的事情,这个 ...

  9. 阿里IM技术分享(三):闲鱼亿级IM消息系统的架构演进之路

    本文由阿里闲鱼技术团队今朝.有攸分享,本次有修订. 1.引言 闲鱼即时消息系统历经数代迭代,目前已能稳定的支撑亿级消息体量. 在此消息系统的建设过程中,我们经历了从简单到复杂.从困扰到破局,每一次的技 ...

  10. Mac_使用allure 生成测试报告

    在mac下使用allure 生成测试报告,需要安装软件为: 1. pytest 2. allure-pytest 或者 pytest-allure-adaptor (记住二者只能选其一) 3. all ...