学习目标:
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量 削锋等问题实现高性能,高可用,可伸缩和终一致性[架构] 使用较多的消息队列有 ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ
以下介绍消息队列在实际应用中常用的使用场景:异步处理,应用解耦,流量削锋,日志处理和消息通讯五个场景
直接进入正题。
一.异步处理
场景:发送手机验证码,邮件
传统古老处理方式如下图
这个流程,全部在主线程完成,注册-》入库-》发送邮件-》发送短信,由于都在主线程,所以要等待每一步完成才能继续执行。由于每一步的操作时间响应时间不固定,所以主线程的请求耗时可能会非常长,如果请求过多,会导致IIS站点巨慢,排队请求,甚至宕机,严重影响用户体验。(在一个主线程中,代码时从上往下以此执行的)
现在大多数的处理方式如下图
这个做法是主线程只做耗时非常短的入库操作,发送邮件和发送短信,会开启2个异步线程,扔进去并行执行,主线程不管,继续执行后续的操作,这种处理方式要远远好过第一种处理方式,极大的增强了请求响应速度,用户体验良好。缺点是,由于异步线程里的操作都是很耗时间的操作,一个请求要开启2个线程,而一台标准配置的ECS服务器支撑的并发线程数大概在800左右,假设一个线程在10秒做完,这个单个服务器最多能支持400个请求的并发,后面的就要排队。出现这种情况,就要考虑增加服务器做负载,尴尬的增加成本。
消息队列RabbitMq的处理方式
这个流程是,主线程依旧处理耗时低的入库操作,然后把需要处理的消息写进消息队列中,这个写入耗时可以忽略不计,非常快,然后,独立的发邮件子系统,和独立的发短信子系统,同时订阅消息队列,进行单独处理。处理好之后,向队列发送ACK确认,消息队列整条数据删除。这个流程也是现在各大公司都在用的方式,以SOA服务化各个系统,把耗时操作,单独交给独立的业务系统,通过消息队列作为中间件,达到应用解耦的目的,并且消耗的资源很低,单台服务器能承受更大的并发请求。
二.应用解耦
场景:双11是购物狂节,用户下单后,订单系统需要通知库存系统,传统的做法就是订单系统调用库存系统的接口.
这种做法有一个缺点:
三.流量削峰
流量削峰一般在秒杀活动中应用广泛
场景:秒杀活动,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。
作用:
1.可以控制活动人数,超过此一定阀值的订单直接丢弃(我为什么秒杀一次都没有成功过呢^^)
2.可以缓解短时间的高流量压垮应用(应用程序按自己的最大处理能力获取订单)
1.用户的请求,服务器收到之后,首先写入消息队列,加入消息队列长度超过最大值,则直接抛弃用户请求或跳转到错误页面.
2.秒杀业务根据消息队列中的请求信息,再做后续处理.
四.日志处理
这个场景应该都很熟悉,一个系统有大量的业务需要各种日志来保证后续的分析工作,而且实时性要求不高,用队列处理再好不过了
五.消息通讯
现在上线的各大社交通讯项目中,实际上是没有用消息队列做即时通讯的,但是它确实可以用来做,有兴趣的不妨去试试吧
这个是点对点通信,消费者A和B同时订阅消息队列,又同时是制造者,就能实现点对点通信
群聊的做法,所有客户端同时订阅队列,又同时是发送,制造者。
-----------------------------------------------------------------------------------------------------------------------------------------------------------
上述大致的5种RabbitMq的应用场景,下面来介绍几个消息队列的区别
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
单机吞吐量 | 万级,比 RocketMQ、Kafka 低一个数量级 | 同 ActiveMQ | 10 万级,支撑高吞吐 | 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
topic 数量对吞吐量的影响 | topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic | topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源 | ||
时效性 | ms 级 | 微秒级,这是 RabbitMQ 的一大特点,延迟最低 | ms 级 | 延迟在 ms 级以内 |
可用性 | 高,基于主从架构实现高可用 | 同 ActiveMQ | 非常高,分布式架构 | 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
消息可靠性 | 有较低的概率丢失数据 | 基本不丢 | 经过参数优化配置,可以做到 0 丢失 | 同 RocketMQ |
功能支持 | MQ 领域的功能极其完备 | 基于 erlang 开发,并发能力很强,性能极好,延时很低 | MQ 功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 |
RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。
AMQP :Advanced Message Queue,高级消息队列协议。它是应用层协议的一个开放 标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不 受产品、开发语言等条件的限制。
RabbitMQ 初起源于金融系统,用于在分布式系统中存储转发消息,在易用性、扩展 性、高可用性等方面表现不俗。具体特点包括:
1.可靠性(Reliability)
RabbitMQ 使用一些机制来保证可靠性,如持久化、传输确认、发布确认。
2.灵活的路由(Flexible Routing)
在消息进入队列之前,通过 Exchange 来路由消息的。对于典型的路由功能,RabbitMQ 已经提供了一些内置的 Exchange 来实现。针对更复杂的路由功能,可以将多个 Exchange 绑定在一起,也通过插件机制实现自己的 Exchange 。
3.消息集群(Clustering)
多个 RabbitMQ 服务器可以组成一个集群,形成一个逻辑 Broker 。
4.高可用(Highly Available Queues)
队列可以在集群中的机器上进行镜像,使得在部分节点出问题的情况下队列仍然可用。
5.多种协议(Multi-protocol)
RabbitMQ 支持多种消息队列协议,比如 STOMP、MQTT 等等。
6.多语言客户端(Many Clients) RabbitMQ 几乎支持所有常用语言,比如 Java、.NET、Ruby 等等。
7.管理界面(Management UI)
RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker 的许多方 面。
8.跟踪机制(Tracing)
如果消息异常,RabbitMQ 提供了消息跟踪机制,使用者可以找出发生了什么。
9.插件机制(Plugin System)
RabbitMQ 提供了许多插件,来从多方面进行扩展,也可以编写自己的插件。
RabbitMQ的优点:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c9cglfBY-1574129862698)(D:\TinkingCat\RabbitMQ\assets\timg.jpg)]
RabbitMQ Server: 也叫broker server,它是一种传输服务。 他的角色就是维护一条 从Producer到Consumer的路线,保证数据能够按照指定的方式进行传输。
Producer: 消息生产者,如图A、B、C,数据的发送方。消息生产者连接RabbitMQ服 务器然后将消息投递到Exchange。
Consumer:消息消费者,如图1、2、3,数据的接收方。消息消费者订阅队列, RabbitMQ将Queue中的消息发送到消息消费者。 Exchange:生产者将消息发送到Exchange(交换器),由Exchange将消息路由到一个 或多个Queue中(或者丢弃)。Exchange并不存储消息。RabbitMQ中的Exchange有 direct、fanout、topic、headers四种类型,每种类型对应不同的路由规则。
Queue:(队列)是RabbitMQ的内部对象,用于存储消息。消息消费者就是通过订阅 队列来获取消息的,RabbitMQ中的消息都只能存储在Queue中,生产者生产消息并终 投递到Queue中,消费者可以从Queue中获取消息并消费。多个消费者可以订阅同一个 Queue,这时Queue中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者 都收到所有的消息并处理。
RoutingKey:生产者在将消息发送给Exchange的时候,一般会指定一个routing key, 来指定这个消息的路由规则,而这个routing key需要与Exchange Type及binding key联合使用才能终生效。在Exchange Type与binding key固定的情况下(在正常使用时一 般这些内容都是固定配置好的),我们的生产者就可以在发送消息给Exchange时,通过 指定routing key来决定消息流向哪里。RabbitMQ为routing key设定的长度限制为255 bytes。
Connection: (连接):Producer和Consumer都是通过TCP连接到RabbitMQ Server 的。以后我们可以看到,程序的起始处就是建立这个TCP连接。
Channels: (信道):它建立在上述的TCP连接中。数据流动都是在Channel中进行 的。也就是说,一般情况是程序起始建立TCP连接,第二步就是建立这个Channel。
VirtualHost:权限控制的基本单位,一个VirtualHost里面有若干Exchange和 MessageQueue,以及指定被哪些user使用
RabbitMQ的消息流转:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j7I6MIqa-1574129862698)(D:\TinkingCat\RabbitMQ\assets\1348730-20190606002655965-1977548174.png)]
AMQP全称: Advanced Message Queuing Protocol
AMQP翻译: 高级消息队列协议
AMQP定义: 是具有现代特征的二进制协议。是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bq077Cw6-1574129862699)(D:\TinkingCat\RabbitMQ\assets\1348730-20190606002906491-408602073.png)]
【rabbitmq下载】rabbitmq下载官网地址:http://www.rabbitmq.com/
具体的安装包的下载【这里安装的版本是3.7.5】:https://github.com/rabbitmq/rabbitmq-server/releases/tag/v3.7.5
提供一个百度网盘地址:链接:https://pan.baidu.com/s/1K_dWn2u-NqSnZ1r8xR-5bw 提取码:1c19
注意事项:
erlang的版本会影响到rabbitmq的安装,两者有个版本对照
查看对照的地址:http://www.rabbitmq.com/which-erlang.html
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9ksnfVZR-1574129862699)(D:\TinkingCat\RabbitMQ\assets\1574066880038.png)]
【erlang下载】
具体的安装包的下载【这里安装的版本是19.3】:http://www.erlang.org/downloads/19.3
提供一个百度网盘地址:链接:https://pan.baidu.com/s/1F6dEThHbf2jRmJdOL_OiXQ 提取码:378x
上传这三个要安装的文件:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bi2Sdh5L-1574129862700)(D:\TinkingCat\RabbitMQ\assets\1574067014487.png)]
一.配置jdk环境
JAVA_HOME=/usr/jdk8
CLASSPATH=%JAVA_HOME%/lib:%JAVA_HOME%/jre/lib
PATH= P A T H : PATH: PATH:JAVA_HOME/bin:$JAVA_HOME/jre/bin
export PATH CLASSPATH JAVA_HOME
二.安装erlang
方法一:
[root@localhost ~]# tar -zxvf otp_src_19.3.tar.gz
[root@localhost ~]# mv otp_src_19.3 /usr/local/src/java/
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3JPWv9eH-1574129862700)(D:\TinkingCat\RabbitMQ\assets\1574067567962.png)]
#切换到目录文件执行安装,报错
[root@localhost java]# cd otp_src_19.3/
[root@localhost otp_src_19.3]# ./configure --prefix=/opt/erlang
Ignoring the --cache-file argument since it can cause the system to be erroneously configured
Disabling caching
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking for gcc... no
checking for cc... no
checking for cc... no
checking for cl... no
configure: error: no acceptable C compiler found in $PATH
See `config.log' for more details.
#安装gcc
yum install -y gcc
#yum search libtool
#yum search libtool-ltdl-devel
#yum install libtool
#yum install libtool-ltdl-devel
#yum install gcc-c++
#yum install erlang-doc
#yum install erlang-jinterface
#执行make
[root@localhost otp_src_19.3]# make
Makefile:248: /usr/local/src/java/otp_src_19.3/make/x86_64-unknown-linux-gnu/otp_ded.mk: 没有那个文件或目录
make: *** 没有规则可以创建目标“/usr/local/src/java/otp_src_19.3/make/x86_64-unknown-linux-gnu/otp_ded.mk”。 停止。
#解决方案
sudo yum install ncurses-devel.x86_64
#执行相关配置
./configure --prefix=/opt/erlang
#安装
make
make install
#配置erlang环境变量
ERLANG_HOME=/opt/erlang
JAVA_HOME=/usr/local/src/java/jdk8
CLASSPATH=%JAVA_HOME%/lib:%JAVA_HOME%/jre/lib
PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$ERLANG_HOME/bin
export PATH CLASSPATH JAVA_HOME ERLANG_HOME
#刷新修改的资源文件
source /etc/profile
#测试是否配置成功
erl
方法二:
#下载Erlang
y
#报错
[root@localhost jdk8]# wget http://www.rabbitmq.com/releases/erlang/erlang-19.0.4-1.el7.centos.x86_64.rpm
-bash: wget: 未找到命令
#安装wget
yum install wget
#安装Erlang
rpm -ivh erlang-19.0.4-1.el7.centos.x86_64.rpm
#测试
erl
#成功
[root@localhost jdk8]# erl
Erlang/OTP 19 [erts-8.0.3] [source] [64-bit] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.0.3 (abort with ^G)
1>
三.安装RabbitMQ
注意: 我给的是以tar.zx结尾的文件,需要解压一次才能得到上面的
[root@localhost ~]# xz -d rabbitmq-server-generic-unix-3.7.5.tar.xz
[root@localhost ~]# tar -xvf rabbitmq-server-generic-unix-3.7.5.tar -C /opt
#我给的是以tar.zx结尾的文件,需要解压一次才能得到tar文件
xz -d rabbitmq-server-generic-unix-3.7.5.tar.xz
#解压文件并且指定加压位置 /opt
tar -xvf rabbitmq-server-generic-unix-3.7.5.tar -C /opt
#移动到java文件夹下
mv rabbitmq_server-3.7.5 /usr/local/src/java/
#添加环境变量
ERLANG_HOME=/opt/erlang
RABBITMQ_HOME=/opt/rabbitmq_server-3.7.5
JAVA_HOME=/usr/local/src/java/jdk8
CLASSPATH=%JAVA_HOME%/lib:%JAVA_HOME%/jre/lib
PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$ERLANG_HOME/bin:$RABBITMQ_HOME/sbin
export PATH CLASSPATH JAVA_HOME ERLANG_HOME RABBITMQ_HOME
方法二:
#下载RabbitMQ
wget http://www.rabbitmq.com/releases/rabbitmq-server/v3.6.15/rabbitmq-server-3.6.15-1.el7.noarch.rpm
#安装RabbitMQ
rpm -ivh rabbitmq-server-3.6.15-1.el7.noarch.rpm
#报错
[root@localhost ~]# rpm -ivh rabbitmq-server-3.6.15-1.el7.noarch.rpm
警告:rabbitmq-server-3.6.15-1.el7.noarch.rpm: 头V4 RSA/SHA1 Signature, 密钥 ID 6026dfca: NOKEY
错误:依赖检测失败:
socat 被 rabbitmq-server-3.6.15-1.el7.noarch 需要
#安装socat
yum install socat
#检查安装
[root@localhost jdk8]# rpm -qa | grep rabbitmq
rabbitmq-server-3.6.15-1.el7.noarch
#可以查到说明安装成功!
#启动rabbitmq-server
systemctl start rabbitmq-server
#关闭rabbitmq-server
systemctl stop rabbitmq-server
#查看rabbitmq状态
systemctl status rabbitmq-server
#出现running说明成功
● rabbitmq-server.service - RabbitMQ broker
Loaded: loaded (/usr/lib/systemd/system/rabbitmq-server.service; disabled; vendor preset: disabled)
Active: active (running) since 一 2019-11-18 21:31:53 CST; 4min 19s ago
Main PID: 1759 (beam)
Status: "Initialized"
CGroup: /system.slice/rabbitmq-server.service
├─1759 /usr/lib64/erlang/erts-8.0.3/bin/beam -W w -A 64 -P 1048576 -t 5000000 -stbt db -zdbbl 12...
├─1902 /usr/lib64/erlang/erts-8.0.3/bin/epmd -daemon
├─2000 erl_child_setup 1024
├─2008 inet_gethost 4
└─2009 inet_gethost 4
#列出角色
rabbitmqctl list_users
#虽启动服务,但不能连接。尚未配置维护插件和开启远程连接
查看端口:netstat -ntlp
安装:yum -y install net-tools
查看进程:ps -ef |grep rabbitmq
四. 配置网页插件
#创建目录
mkdir /etc/rabbitmq
#我的已经存在,无需创建!
#启动插件
rabbitmq-plugins enable rabbitmq_management
#开放15672端口
#添加指定需要开放的端口:
firewall-cmd --add-port=15672/tcp --permanent
#重载入添加的端口:
firewall-cmd --reload
#查询指定端口是否开启成功:
firewall-cmd --query-port=15672/tcp
#访问web管理界面(http://192.168.126.129:15672/)
#登录遇到问题:User can only log in via localhost
#找到这个文件rabbit.app /usr/lib/rabbitmq/lib/rabbitmq_server-3.7.7/ebin/rabbit.app
find / -name rabbit.app
#将:{loopback_users, [<<”guest”>>]},
#改为:{loopback_users, []},
#原因:rabbitmq从3.3.0开始禁止使用guest/guest权限通过除localhost外的访问
#重启服务
systemctl restart rabbitmq-server.service
#user:guest
#pwd:guest
下载镜像:
docker pull rabbitmq:management
创建容器,rabbitmq需要有映射以下端口: 5671 5672 4369 15671 15672 25672
启动容器
docker run -di --name=rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 15671:15671 -p15672:15672 -p 25672:25672 rabbitmq:management
访问:
http://192.168.126.129:15672/
1、Hello Word模式
Hello Word模式他会发送数据到空字符的交换机,这种交换机很特殊,它允许我们准确地指定消息应该去哪个队列。需要在routing_key参数中指定队列名称。
2、Work Queues模式
Work Queues模式其实就是在Hello Word模式变成了多个消费者,不会消费同一条消息。
3、Publish/Subscribe模式(fanout模式)
Publish/Subscribe模式其实就是将队列绑定到交换机,数据进入交换机然后在决定进入哪个队列,绑定2个就会进入2个队列,类似广播性质。此模式也就是Exchange模式中的fanout模式。
上图中,生产者(P)发送到Exchange(X)的所有消息都会路由到图中的两个Queue,并最终被两个消费者(C1与C2)消费。
4、Routing模式
Routing模式它会把消息路由到那些binding key与routing key完全匹配的Queue中,此模式也就是Exchange模式中的direct模式。
以上图的配置为例,我们以routingKey="error"发送消息到Exchange,则消息会路由到Queue1(amqp.gen-S9b…,这是由RabbitMQ自动生成的Queue名称)和Queue2(amqp.gen-Agl…)。如果我们以routingKey="info"或routingKey="warning"来发送消息,则消息只会路由到Queue2。如果我们以其他routingKey发送消息,则消息不会路由到这两个Queue中。
5、Topics模式
Topics模式与Routing模式比较相近,routing key为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),binding key与routing key一样也是句点号“. ”分隔的字符串。binding key中可以存在两种特殊字符“”与“#”,用于做模糊匹配,其中“”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)。
以上图中的配置为例,routingKey=”quick.orange.rabbit”的消息会同时路由到Q1与Q2,routingKey=”lazy.orange.fox”的消息会路由到Q1与Q2,routingKey=”lazy.brown.fox”的消息会路由到Q2,routingKey=”lazy.pink.rabbit”的消息会路由到Q2(只会投递给Q2一次,虽然这个routingKey与Q2的两个bindingKey都匹配);routingKey=”quick.brown.fox”、routingKey=”orange”、routingKey=”quick.orange.male.rabbit”的消息将会被丢弃,因为它们没有匹配任何bindingKey。
6、RPC模式
这个模式很少用,也不多做解释了,就是一个请求队列一个回调队列,具体参考官方网站或者其他博客吧。
交换机4种模式中还有一种模式没叙述,就是headers模式:
headers类型的Exchange不依赖于routing key与binding key的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。
在绑定Queue与Exchange时指定一组键值对;当消息发送到Exchange时,RabbitMQ会取到该消息的headers(也是一个键值对的形式),对比其中的键值对是否完全匹配Queue与Exchange绑定时指定的键值对;如果完全匹配则消息会路由到该Queue,否则不会路由到该Queue。
fanout,direct,topicexchange的routingKey都需要要字符串形式的,而headers exchange则没有这个要求,因为键值对的值可以是任何类型 ,匹配有两种方式all和any。这两种方式是在接收端必须要用键值"x-mactch"来定义。all代表定义的多个键值对都要满足,而any则代码只要满足一个就可以了
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-amqpartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.springframework.amqpgroupId>
<artifactId>spring-rabbit-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<scope>testscope>
dependency>
dependencies>
ProduceerTest.java
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProducerTest {
//springboot整合rabbitmq提供的一个类
//RedisTemplate
//JdbcTemplate...
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void sendMsg(){
rabbitTemplate.convertAndSend("ange","欢迎来到叮当猫教育进行学习");
}
}
Cunsumer.java
@Component
@RabbitListener(queues = "ange")
public class Customer1 {
/**
*
* @param message 获取的消息内容
*/
@RabbitHandler
public void getMsg(String message){
System.out.println("ange收到的消息是:"+message);
}
}
RabbitMQ生产消费快速入门:
环境: springboot+jdk1.7+rabbitmq3.6.5 (Maven依赖配置)
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.9.RELEASEversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.rabbitmqgroupId>
<artifactId>amqp-clientartifactId>
<version>3.6.5version>
dependency>
dependencies>
public class Procuder {
public static void main(String[] args) throws Exception {
//1.创建一个ConnectionFactory 并进行配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.11");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setHandshakeTimeout(20000);
//2.通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3.通过Connection 创建一个 Channel
Channel channel = connection.createChannel();
/**
* basicPublish(String exchange, String routingKey, BasicProperties props, byte[] body)
* exchange:指定交换机 不指定 则默认 (AMQP default交换机) 通过routingkey进行匹配
* props 消息属性
* body 消息体
*/
//4.通过Channel发送数据
for(int i = 0; i < 5; i++){
System.out.println("生产消息:" + i);
String msg = "Hello RabbitMQ" + i;
channel.basicPublish("", "test", null, msg.getBytes());
}
//5.记得关闭相关的连接
channel.close();
connection.close();
}
}
public class Consumer {
public static void main(String[] args) throws Exception{
//1.创建一个ConnectionFactory 并进行配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.11");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setHandshakeTimeout(20000);
//2.通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3.通过Connection 创建一个 Channel
Channel channel = connection.createChannel();
//4. 声明创建一个队列
String queueName = "test";
/**
* durable 是否持久化
* exclusive 独占的 相当于加了一把锁
*/
channel.queueDeclare(queueName,true,false,false,null);
//5.创建消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//6.设置channel
/**
* ACK: 当一条消息从生产端发到消费端,消费端接收到消息后会马上回送一个ACK信息给broker,告诉它这条消息收到了
* autoack:
* true 自动签收 当消费者一收到消息就表示消费者收到了消息,消费者收到了消息就会立即从队列中删除。
* false 手动签收 当消费者收到消息在合适的时候来显示的进行确认,说我已经接收到了该消息了,RabbitMQ可以从队列中删除该消息了
*
*/
channel.basicConsume(queueName, true, queueingConsumer);
//7.获取消息
while(true){
Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.err.println("消费端:" + msg);
//Envelope envelope = delivery.getEnvelope();
}
}
}
Exchange: 接收消息,并根据路由键转发消息所绑定的队列
交换机属性:
所有发送到Direct Exchange的消息被转发到RouteKey指定的Queue
**注意:**Direct模式可以使用RabbitMQ自带的Exchange: default Exchange,所以不需要将Exchange进行任何绑定(binding)操作,消息传递时,RoutingKey必须完全匹配才会被队列接收,否则该消息会被抛弃
public class ProducerDirectExchange {
public static void main(String[] args) throws Exception {
//1.创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.11");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//2.创建Connection
Connection connection = connectionFactory.newConnection();
//3.创建Channel
Channel channel = connection.createChannel();
//4.声明
String exchangeName = "test_direct_exchange";
String routingKey = "test.direct";
//5.发送
String msg = "Hello World RabbitMQ4 Direct Exchange Message";
channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
}
}
public class ConsumerDirectExchange {
public static void main(String[] args) throws Exception{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.11");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setHandshakeTimeout(20000);
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//声明
String exchangeName = "test_direct_exchange";
String exchangeType = "direct";
String queueName = "test_direct_queue";
String routingKey = "test.direct";
//表示声明了一个交换机
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的消息被转发到所有关心RouteKey中指定Topic的Queue上
Exchange将RouteKey和某Topic进行模糊匹配,此时队列需要绑定一个Topic
注意:可以使用通配符进行匹配
符号 # 匹配一个或多个词
符号 * 匹配不多不少一个词
例如: “log.#” 能够匹配到 “log.info.oa”
“log.*” 只会匹配到 “log.err”
public class ProducerTopicExchange {
public static void main(String[] args) throws Exception {
//1.创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.11");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setHandshakeTimeout(20000);
//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 RabbitMQ4 Direct Exchange Message";
channel.basicPublish(exchangeName, routingKey1, null, msg.getBytes());
channel.basicPublish(exchangeName, routingKey2, null, msg.getBytes());
channel.basicPublish(exchangeName, routingKey3, null, msg.getBytes());
}
}
public class ConsumerTopicExchange {
public static void main(String[] args) throws Exception{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.11");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setHandshakeTimeout(20000);
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//声明
String exchangeName = "test_topic_exchange";
String exchangeType = "topic";
String queueName = "test_topic_queue";
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);
//循环获取消息
while(true){
//获取消息,如果没有消息,这一步将会一直阻塞
Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println("收到消息:" + msg);
}
}
}
不处理路由键,只需要简单的将队列绑定到交换机上
发送到交换机的消息都会被转发到与该交换机绑定的所有队列上
所以Fanout交换机转发消息是最快的
public class ProducerFanoutExchange {
public static void main(String[] args) throws Exception {
//1.创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.11");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setHandshakeTimeout(20000);
//2.创建Connection
Connection connection = connectionFactory.newConnection();
//3.创建Channel
Channel channel = connection.createChannel();
//4.声明
String exchangeName = "test_fanout_exchange";
//5.发送
for(int i = 0; i < 10 ; i++){
String msg = "Hello World RabbitMQ4 Direct Exchange Message";
channel.basicPublish(exchangeName, "", null, msg.getBytes());
}
channel.close();
connection.close();
}
}
public class ConsumerFanoutExchange {
public static void main(String[] args) throws Exception{
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.11");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setHandshakeTimeout(20000);
connectionFactory.setAutomaticRecoveryEnabled(true);
connectionFactory.setNetworkRecoveryInterval(3000);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//声明
String exchangeName = "test_fanout_exchange";
String exchangeType = "fanout";
String queueName = "test_topic_queue";
//无需指定路由key
String 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);
}
}
}
服务器与应用程序之间传递的数据,本质上就是一段数据,由Properties和Body组成
常用属性:delivery mode、headers (自定义属性)
其他属性:content_type、content_encoding、priority、expiration
消息的properties属性用法示例:
public class Procuder {
public static void main(String[] args) throws Exception {
//1.创建一个ConnectionFactory 并进行配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.11");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setHandshakeTimeout(20000);
//2.通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3.通过Connection 创建一个 Channel
Channel channel = connection.createChannel();
Map headers = new HashMap<>();
headers.put("my1", "111");
headers.put("my2", "222");
//10秒不消费 消息过期移除消息队列
AMQP.BasicProperties properties = new AMQP.BasicProperties().builder()
.deliveryMode(2)
.contentEncoding("utf-8")
.expiration("10000")
.headers(headers)
.build();
//4.通过Channel发送数据
for(int i = 0; i < 5; i++){
System.out.println("生产消息:" + i);
String msg = "Hello RabbitMQ" + i;
channel.basicPublish("", "test", properties, msg.getBytes());
}
//5.记得关闭相关的连接
channel.close();
connection.close();
}
}
public class Consumer {
public static void main(String[] args) throws Exception{
//1.创建一个ConnectionFactory 并进行配置
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.244.11");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
connectionFactory.setHandshakeTimeout(20000);
//2.通过连接工厂创建连接
Connection connection = connectionFactory.newConnection();
//3.通过Connection 创建一个 Channel
Channel channel = connection.createChannel();
//4. 声明创建一个队列
String queueName = "test";
channel.queueDeclare(queueName,true,false,false,null);
//5.创建消费者
QueueingConsumer queueingConsumer = new QueueingConsumer(channel);
//6.设置channel
channel.basicConsume(queueName, true, queueingConsumer);
//7.获取消息
while(true){
Delivery delivery = queueingConsumer.nextDelivery();
String msg = new String(delivery.getBody());
System.err.println("消费端:" + msg);
Map<String, Object> headers = delivery.getProperties().getHeaders();
System.err.println("headers value:" + headers.get("my1"));
}
}
}