RabbitMQ3.10.7入门与进阶实战

1 分布式消息队列认知提升

1.1 MQ的应用场景与MQ性能衡量指标

1.1.1 分布式消息队列MQ应用场景:

解耦; 服务解耦

消峰; 消峰填谷:流量高峰和低谷均衡;

异步:   异步化缓冲; (最终一致性)

1.1.2 应用思考点

1、生产端可靠性投递:消息不能丢失;

2、消费端幂等:避免消息消费多次;

3、高可用

4、低延迟

5、可靠性(一般都是副本方式)

6、堆积能力

7、扩展性(如简单的扩容)

1.2 主流的分布式消息队列MQ:

ActiveMQ :  古老,强大,Apach

RabbitMQ:

RocketMQ: 阿里巴巴---->Apach

Kafka:    高吞吐,海量数据的存储;

1.2.1 技术选型:

(集群架构模式需要考虑的几点: 分布式、可扩展、高可用、可维护性)

ActiveMQ: 适合中小型的传统行业,不适合高并发业务;

RabbitMQ:   横向扩展能力不好,可用性和可维护性很棒;

Kafka和RocketMQ: 可拓展性很强,具备高可用性,但是可维护性比较麻烦一点;

(Kafka可以在廉价服务器上有着非常高和吞吐性能,如果对消息可靠性要求不是很高,可以考虑使用Kafka,Kafka也可以保证消息一条也不丢失,但是对性能影响比较大。)

1.3 ActiveMQ集群架构与原理解析

1.3.1 初始JMS与其专业术语

        现在我们和大家了解一下古老而又神秘的消息中间件"ActiveMQ"。首先,说起ActiveMQ,就必须先聊聊JMS(Java Message Service)规范,也就是Java消息服务,它定义了java中访问消息中间件的接口规范。在这里注意哦,JMS只是接口,并没有给予实现,实现JMS接口的消息中间件为"JMS Provider",目前知名的开源MOM(Message Oriented Middleware,也就是消息中间件)系统包括Apache的ActiveMQ、RocketMQ、Kafka,以及RabbitMQ,可以说它们都是"基本遵循"或"参考"JMS规范,都有自己的特点和优势。

1.3.2 专业术语

  • JMS(Java Message Service):实现JMS接口的消息中间件;
  • Provider(MessageProvider):消息的生产者;
  • Consumer(MessageConsumer):消息的消费者;
  • PTP(Point to Point):即点对点的消息模型,这也是非常经典的模型;
  • Pub/Sub(Publish/Subscribe):即发布/订阅的消息模型;
  • Queue:队列目标,也就是我们常说的消息队列,一般都是会真正的进行物理存储;
  • Topic:主题目标;
  • ConnectionFactory:连接工厂,JMS用它创建连接;
  • Connection:JMS客户端到JMS Provider的连接;
  • Destination:消息的目的地;
  • Session:会话,一个发送或接收消息的线程。

1.3.3 JMS消息格式定义

  • StreamMessage原始值得数据流
  • MapMessage一套名称/值对
  • TextMessage一个字符串对象
  • BytesMessage一个未解释字节的数据流
  • ObjectMessage一个序列化的java对象

1.4 了解ActiveMQ

        ActiveMQ是一个完全支持JMS1.1和J2EE1.4规范的JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在早些年的"J2EE应用"时期扮演着特殊的地位,可以说那个年代ActiveMQ在业界应用最广泛,当然如果现在想要有更强大的性能和海量数据处理能力,ActiveMQ还需要不断地升级版本,不断地提升性能和架构设计的重构。

        就算现在我们80%以上的业务我们是用ActiveMQ已经足够满足需求,其丰富的API、多种集群构建模式使得它成为业界老牌消息中间件,在中小型企业中应用广泛。

        当然如果你想针对大规模、高并发应用服务做消息中间件技术选型,譬如淘宝、京东这种大型的电商网站,尤其是双11这种特殊时期,ActiveMQ可能就显得力不从心了,当然我们这里后续还会和大家介绍其他非常优秀的MOM。

1.5 消息投递模式

        我们首先要了解JMS规范里最经典的两种消息投递模式。即"点对点"与"发布订阅"。

  • 点对点:生产者向队列投递一条消息,只有一个消费者能够监听到这条消息(PTP),下图所示:

  •  发布订阅:生产者向队列投递一条消息,所有监听该队列的消费者都能得到这条消息(Pub/Sub)

 1.6 ActiveMQ各项指标

衡量一个MOM,我们主要从三方面考虑即可,即服务性能、存储堆积能力、可扩展性。

  • 服务性能

        ActiveMQ的性能一般,在早期传统行业为王的时代还是比较流行的,但现如今面对高并发、大数据的业务场景,往往力不从心!

  • 数据存储

        默认采用kahadb(索引文件形式存储),也可以使用高性能的google level db(内存数据库存储)。

  • 集群架构

        ActiveMQ可以与zookeeper进行构建 主备集群 模型,并且多套的主备模型直接采用Network的方式构建分布式集群。

1.7 ActiveMQ集群架构模式

        ActiveMQ最经典的两种集群架构模式,Master-Slave、Network集群模式!

  • Master-Slave

 Master-Slave:顾名思义,就是主从方式,当然这里要理解为主备的方式,也就是双机热备机制;Master-Slave背后的想法是,消息被复制到slave broker,因此即使master broker遇到了像硬件故障之类的错误,你也可以立即切换到slave broker而不丢失任何消息。Master-Slave是目前ActiveMQ推荐的高可靠性和容错的解决方案。

  • 架构思考:Master-Slave集群模型的关键点:

        上图(Master-Slave)绿色为主节点,灰色的则为备份节点,这两个节点都是运行状态。

        zookeeper的作用就是为了当绿色的主节点宕机时,进行及时切换到备份的灰色节点上去,使其进行主从角色的互换,用于实现高可用性的方案。

        Master-Slave集群模型的缺点也显而易见,就是不能做到分布式的topic、queue,当消息量巨大时,我们的MQ集群压力过大,没办法满足分布式的需求。

  • Network

  •  Network:这里可以理解为网络通信方式,也可以说叫Network of brokers。这种方式真正解决了分布式消息存储和故障转移、roker切换的问题。可以理解消息会进行均衡;从ActiveMQ1.1版本起,ActiveMQ支持networks of brokers。它支持分布式的topics和queues。一个broker会相同对待所有的订阅(subscription):不管它们是来自本地的客户端连接,还是来自远程broker,它都会递送有关的消息拷贝到每个订阅。远程broker得到这个消息拷贝后,会依次把它递送到其内部的本地连接上。
  • 架构思考:Network集群模型的关键点:

        首先,这种方案需要两套或多套(Master-Slave)的集群模型才可以搞定,部署非常麻烦,需要两套或多套集群直接相互交叉配置,相互间能够感知到彼此的存在。下面我给出一段XML配置,简单来说就是在ActiveMQ的配置文件里要进行多套(Master-Slave)之间的networkConnector配置工作:

<broker brokerName="receiver" persistent="false" useJmx="false">
     <transportConnectors>
         <transportConnector uri="tcp://localhost:62002"/>
     </transportConnectors>
     <networkConnectors>
        <networkConnector 
              uri="static:( tcp://localhost:61616,tcp://remotehost:61616)"/>
     </networkConnectors>
</broker>

        其次,Network虽然解决了分布式消息队列这个难题,但是还有很多潜在的问题,最典型的就是资源浪费问题,并且也可能达不到所预期的效果;通常采用Master-Slave模型是传统型互联网公司的首选,作为互联网公司往往会选择开箱即用的消息中间件,从运维、部署、使用各个方面都要优于ActiveMQ,当然ActiveMQ毕竟是"老牌传统强Q",Apache的顶级项目之一,目前正在进行新版本的重构(对于5.X版本)与落地,下一代"Artemis代理",也可以理解为"6.X";官网如下:https://activemq.apache.org/

1.8 RabbitMQ集群架构
1、主备模式:热备份(master-slave)
master 对外提供读写,salve作为一个备份,当出现异常的时候,master宕机,做一个切换,slave节点切换为master节点
2、远程模式:数据异地容灾,提升性能,单个节点处理不过来,可以转移到下游的集群中(架构简单、配置复杂)
3、镜像模式:业界使用最广泛(消息可靠传递)
4、多活模式:异地容灾、数据转储,类似于远程模式       

1.8.1  主备模式

        warren(兔子窝),一个主、备方案(主节点如果挂掉,从系欸但提供服务,和ActiveMQ利用Zookeeper做主/备一样)

主备模式-HaProxy配置

 主备模式-HaProxy配置

listen  rabbitmq_cluster

bind  0.0.0.0:5672    #配置TCP模式

mode  tcp    #简单的轮询

balance  roundrobin    #主节点

server  bhz76  192.168.11.76:5672  check  inter  5000 rise  2  fall 2

server  bhz77  192.168.11.77:5672  backup  check  5000 rise  2  fall 2    #备用节点

1.8.2  远程模式

        远程通信和复制,可以实现双活的一种模式,简称Shovel模式

        所谓Shovel就是我们可以把消息进行不同数据中心的复制工作,可以跨地域的让两个mq集群互联。

 

 

 

1.8.3  镜像模式

        集群模式非常经典的就是Mirror镜像模式,保证百分之100数据据不丢失。

        在实际工作中用的最多,并且实现集群非常简单,一般互联网大厂都会构建这种镜像集群模式。

 

 

1.8.4  多活模式

 

 

 

 

 

 

1.9 Kafka高性能核心pageCache与zeroCopy原理解析

1.9.1 Kafka有哪些特点?

特点:

1、分布式,支持消息分区

2、跨平台,java、python,php

3、实时性很好

4、伸缩性(上亿)

1.9.2 Kafka高性能原因:

顺序写:

        顺序写盘的过程,consumer顺序消费数据,但是不从盘中删除数据,避免随机写盘。阿里云支持rocket删除某条消息,有可能进行的是打标记转储的方式,而不是物理删除。

Page Cache空中接力:

        producer生产消息时,会使用pwrite()系统调用按偏移量写入数据,并且都会先写入page cache里。

        consumer消费消息时,会使用sendfile()系统调用,零拷贝地将数据从page cache传输到broker的Socket buffer,再通过网络传输。同时,page cache中的数据会随着内核中flusher线程的调度以及对sync()/fsync()的调用写回到磁盘,就算进程崩溃,也不用担心数据丢失。

        如果consumer要消费的消息不在page cache里,才会去磁盘读取,并且会顺便预读出一些相邻的块放入page cache,以方便下一次读取。

重要的结论:

        如果Kafka producer的生产速率与consumer的消费速率相差不大,那么就能几乎只靠对broker page cache的读写完成整个生产-消费过程,磁盘访问非常少。这个结论俗称为“读写空中接力”。 

3、后台异步、主动FLUSH

4、预读策略,IO调度

Page Cache空中接力过程:

普通程序,一次需要4次copy

        pageCache 是操作系统实现的一种主要的磁盘缓存机制/策略,以此减少磁盘IO操作应用程序寻找数据,先去pageCache中寻找数据,如果没有命中,再去磁盘中读。

        pageCache 就是把本应该从磁盘读取改为从内存读,把对磁盘的访问改为对内存的访问。

        kafka的零copy,如果有N个消费者进程,则只需要N+1次copy操作,1次copy到内核读取缓冲区,N次从缓冲区copy到网卡接口。

1.10 Kafka集群模式

        kafka 生产者  消费者 速率相当时候,甚至都用不到磁盘,磁盘只是做个异步的备份而已。

        kafka一定有可靠性的考量,相同的消息可以存在集群中每个节点,只要不是整个集群挂掉,消息不回丢失。

2.1 RabbitMQ进阶与实战

2.1.1 初识RabbitMQ核心概念

        RabbitMQ是一个开源的消息代理和队列服务器,用来通过普通协议在不同的应用之间共享数据,RabbitMQ是使用Erlang语言来编写的,并且RabbitMQ是基于AMQP协议的。

RabbitMQ,不太适合大量的消息堆积。

AMQP : Advanced Message Queuing Protocol(高级消息队列协议)

 定义:具有现代特征的二进制协议,是一个提供统一消息服务的应用层标准高级消息队列协议,

是应用层协议的一个开放标准,为面向消息中间件设计。

server:代表rabbitMQ

virtual host : 可以当成一个虚拟主机,划分模型域的概念。example: /order, /production

Message Queue:消息队列。

Exchange:主题的概念。

 

2.1.2 AMQP核心概念

  • Server:又称Broker,接受客户端的连接,实现AMQP实体服务。
  • Connection:连接,应用程序与Broker的网络连接。
  • Channel:网络信道,几乎所有的操作都在Channel中进行,Channel是进行消息读写的通道。客户端可建立多个Channel,每个Channel代表一个会话任务。
  • Message:消息,服务器和应用程序之间传送的数据,由Properties和Body组成。Properties可以对消息进行修饰,比如消息的优先级、过期时间、延迟等高级特性;Body侧则就是消息体内容。
  • Virtual host:虚拟地址,用于进行逻辑隔离,最上层的消息路由。

        一个Virtual Host里面可以有若干个Exchange和Queue,同一个Virtual Host里面不能有相同名称的Exchange或Queue。

  • Exchange:交换机,接收消息,根据路由键转发消息到绑定的队列。
  • Binding:Exchange和Queue之间的虚拟连接,binding中可以包含routing key。
  • Routing key:一个路由规则,虚拟机可用它来确定如何路由一个特定消息。
  • Queue:也称为Message Queue,消息队列,保存消息并将它们转发给消费者。

RabbitMQ的整体架构

        虽然Exchange与队列支持多对多,但是在实际业务中最好做到一个队列只对应一个Exchange,一个Exchange对应多个队列。设计复杂化了。

        一个消费者(Consumer)也可以消费多个队列(Queue)中的数据,但是实际业务中也不建议这么做。通常一个消费者只消费一个队列中的数据。

RabbitMQ消息转换

2.1.3 RabbitMQ急速安装与入门

2.1.3.1 安装Erlang

官网下载地址:Downloads - Erlang/OTP

因为最新的RabbitMQ3.10.7版本要求的Erlang版本为25.0,所以我们下载Erlang的25.0.4版本。

 

        下载之后是一个opt_src_25.0.4.tar.gz的压缩包。我上传到自己的虚拟机上,上传路径为:/home/software/6-Erlang25.0.4

        解压安装包,并配置安装目录,这里我们预安装到/usr/local/erlang目录下:

cd  /home/software/6-Erlang25.0.4/

tar -zxvf otp_src_25.0.4.tar.gz

        安装依赖:

yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c++ kernel-devel m4 ncurses-devel tk tc tz

cd otp_src_25.0.4

./configure --prefix=/usr/local/erlang

        执行./configure --prefix=/usr/local/erlang命令报错:configure: error: No curses library functions found

        

        重新执行:

./configure --prefix=/usr/local/erlang

        安装Erlang:

make

make install

        修改/etc/profile配置文件,添加下面的环境变量:

vim /etc/profile

# Erlang environment
ERLANG_HOME=/usr/local/erlang/otp_src_25.0.4/
export PATH=$PATH:$ERLANG_HOME/bin
export ERLANG_HOME

 

        最后执行如下命令让配置文件生效:

source /etc/profile

        可以输入erl命令来验证Erlang是否安装成功,如果出现类似以下的提示即表示安装成功:

2.1.3.2 RabbitMQ安装

        安装包下载地址:https://github.com/rabbitmq/rabbitmq-server/releases/tag/v3.10.7

        上传文件包rabbitmq-server-generic-unix-3.10.7.tar.xz到虚拟机/home/software/7-RabbitMQ/路径下,这个路径可以自己指定。

        解压tar.xz文件:先 xz -d xxx.tar.xz 将 rabbitmq-server-generic-unix-3.10.7.tar.xz解压成 rabbitmq-server-generic-unix-3.10.7.tar 然后,再用 tar xvf rabbitmq-server-generic-unix-3.10.7.tar来解包。

        将解压出来的文件夹移动到/usr/local/rabbitmq/路径下:

mv rabbitmq_server-3.10.7 /usr/local/rabbitmq/

        修改/etc/profile文件,添加下面的环境变量:

# RabbitMQ environment
export PATH=$PATH:/usr/local/rabbitmq/rabbitmq_server-3.10.7/sbin/
export RABBITMQ_HOME=/usr/local/rabbitmq/rabbitmq_server-3.10.7

        之后执行下面命令让配置文件生效:

source /etc/profile

        修改主机名称:

vim /etc/hostname

         修改/etc/hosts文件,添加如下配置:

192.168.110.130 centos130

2.1.3.3 RabbitMQ运行

        在修改完/etc/profile配置文件之后,可以任意打开Shell窗口,输入如下命令以运行RabbitMQ服务:

rabbitmq-server -detached

        在rabbitmq-server命令后面添加一个"-detached"参数是为了让RabbitMQ服务以守护进程的方式在后台运行,这样就不会因为当前Shell窗口的关闭而影响服务。

        运行如下命令查看RabbitMQ是否正常启动:

rabbitmqctl status

        如果RabbitMQ正常启动,会输出如下信息。当然也可以通过rabbitmqctl cluster_status命令来查看集群信息,目前只有一个RabbitMQ服务节点,可以看做单节点的集群:

Status of node rabbit@localhost ...
Runtime

OS PID: 3166
OS: Linux
Uptime (seconds): 17
Is under maintenance?: false
RabbitMQ version: 3.10.7
Node name: rabbit@localhost
Erlang configuration: Erlang/OTP 25 [erts-13.0.4] [source] [64-bit] [smp:1:1] [ds:1:1:10] [async-thread                                                 s:1]
Crypto library: OpenSSL 1.0.2k-fips  26 Jan 2017
Erlang processes: 265 used, 1048576 limit
Scheduler run queue: 1
Cluster heartbeat timeout (net_ticktime): 60

Plugins

Enabled plugin file: /usr/local/rabbitmq/rabbitmq_server-3.10.7/etc/rabbitmq/enabled_plugins
Enabled plugins:


Data directory

Node data directory: /usr/local/rabbitmq/rabbitmq_server-3.10.7/var/lib/rabbitmq/mnesia/rabbit@localhos                                                 t
Raft data directory: /usr/local/rabbitmq/rabbitmq_server-3.10.7/var/lib/rabbitmq/mnesia/rabbit@localhos                                                 t/quorum/rabbit@localhost

Config files


Log file(s)

 * /usr/local/rabbitmq/rabbitmq_server-3.10.7/var/log/rabbitmq/rabbit@localhost.log
 * /usr/local/rabbitmq/rabbitmq_server-3.10.7/var/log/rabbitmq/rabbit@localhost_upgrade.log
 * <stdout>

Alarms

(none)

Memory

Total memory used: 0.0808 gb
Calculation strategy: rss
Memory high watermark setting: 0.4 of available memory, computed to: 1.5816 gb

code: 0.0294 gb (36.33 %)
other_proc: 0.0192 gb (23.78 %)
other_system: 0.0119 gb (14.78 %)
allocated_unused: 0.0109 gb (13.48 %)
reserved_unallocated: 0.0046 gb (5.74 %)
other_ets: 0.0031 gb (3.78 %)
atom: 0.0013 gb (1.65 %)
binary: 0.0001 gb (0.18 %)
mnesia: 0.0001 gb (0.11 %)
metrics: 0.0001 gb (0.07 %)
plugins: 0.0 gb (0.05 %)
msg_index: 0.0 gb (0.04 %)
quorum_ets: 0.0 gb (0.01 %)
quorum_queue_dlx_procs: 0.0 gb (0.0 %)
stream_queue_procs: 0.0 gb (0.0 %)
stream_queue_replica_reader_procs: 0.0 gb (0.0 %)
connection_channels: 0.0 gb (0.0 %)
connection_other: 0.0 gb (0.0 %)
connection_readers: 0.0 gb (0.0 %)
connection_writers: 0.0 gb (0.0 %)
mgmt_db: 0.0 gb (0.0 %)
queue_procs: 0.0 gb (0.0 %)
queue_slave_procs: 0.0 gb (0.0 %)
quorum_queue_procs: 0.0 gb (0.0 %)
stream_queue_coordinator_procs: 0.0 gb (0.0 %)

File Descriptors

Total: 2, limit: 65439
Sockets: 0, limit: 58893

Free Disk Space

Low free disk space watermark: 0.05 gb
Free disk space: 30.7848 gb

Totals

Connection count: 0
Queue count: 0
Virtual host count: 1

Listeners

Interface: [::], port: 25672, protocol: clustering, purpose: inter-node and CLI tool communication
Interface: [::], port: 5672, protocol: amqp, purpose: AMQP 0-9-1 and AMQP 1.0

        默认情况下,访问RabbitMQ服务的用户名和密码都是"guest",这个账户有限制,默认只能通过本地网络(如localhost)访问,远程网络访问受限,所以在实现生产和消费消息之前,需要另外添加一个用户,并设置相应的访问权限。

        添加新用户,用户名为"root",密码为"1TdhblkFcdhx2a":

rabbitmqctl add_user root

        为root用户设置所有权限:

rabbitmqctl set_permissions -p / root ".*" ".*" ".*"

        设置root用户为管理员角色:

rabbitmqctl set_user_tags root administrator

        安装rabbitmq_management插件:

cd /usr/local/rabbitmq/rabbitmq_server-3.10.7 

rabbitmq-plugins enable rabbitmq_management

        通过浏览器访问RabbitMQ控制台:

 http://IP:15672

        安装成功!

2.1.3.4 新建exchanges

        点击Exchanges标签页->Add a new exchange

        Name:exchange-test

        Type:topic

        Durability:Durable

        Auto delete:No

        Internal:No

         最后点击"Add exchange",然后可以看到新建的exchange-test这个exchange。

2.1.3.4 新建Queues

        点击Exchanges标签页->Add a new queue

        Type:Classic

        Name:queue-test

        Durability:Durable

        Auto delete:No

         最后点击"Add queue",然后可以看到新建的queue-test这个queue。

2.1.3.5 建立exchange和Queue的关联

        点击 "exchanges"标签页,找到exchange-test后点进去。

         之后点击Bindings,填写如下:

        点击Bind。

 2.1.3.6 在exchange里发条消息到Queue

         然后到队列里面看到已经有一条消息了。

 

2.1.3.7 生产和消费消息

maven依赖

<!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
<dependency>
   <groupId>com.rabbitmq</groupId>
   <artifactId>amqp-client</artifactId>
   <version>5.15.0</version>
</dependency>

生产者客户端代码

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class RabbitProducer {
	private static final String EXCHANGE_NAME = "exchange_test";
	private static final String ROUTING_KEY = "text.*";
	private static final String QUEUE_NAME = "queue_test";
	private static final String IP_ADDRESS = "192.168.110.130";
	private static final int PORT = 5672; //RabbitMQ服务默认端口为5672

	public static void main(String[] args) throws IOException,
			TimeoutException, InterruptedException {
		ConnectionFactory factory = new ConnectionFactory();
		factory.setHost(IP_ADDRESS);
		factory.setPort(PORT);
		factory.setUsername("root");
		factory.setPassword("1TdhblkFcdhx2a");
		Connection connection = factory.newConnection();//创建连接
		Channel channel = connection.createChannel();//创建信道
		//创建一个type="topic"、持久化的、非自动删除的交换器。
		channel.exchangeDeclare(EXCHANGE_NAME, "topic", true, false, null);
		//创建一个持久化、非排他的、非自动删除的队列
		channel.queueDeclare(QUEUE_NAME, true, false, false, null);
		//将交换机与队列通过路由键绑定
		channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);
		//发送一条持久化消息:Hello World!
		String message = "Hello World!";
		channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY,
				MessageProperties.PERSISTENT_TEXT_PLAIN,
				message.getBytes());
		//关闭资源
		channel.close();
		connection.close();

	}
	
}

消费者客户端代码

import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class Receiver {
    private static final String QUEUE_NAME = "queue_test";
    private static final String IP_ADDRESS = "192.168.110.130";
    private static final int PORT = 5672;

	public static void main(String[] args) throws IOException, TimeoutException,
            InterruptedException {
        Address[] address = new Address[]{
          new Address(IP_ADDRESS, PORT)
        };

        ConnectionFactory factory = new ConnectionFactory();
        factory.setUsername("root");
        factory.setPassword("1TdhblkFcdhx2a");
        // 这里的连接方式与生产者的demo略有不同,注意区别。
        Connection connection = factory.newConnection(address); //创建连接
        final Channel channel = connection.createChannel();//创建信道
        channel.basicQos(64);//设置客户端最多接收未被ack的消息个数
        Consumer consumer = new DefaultConsumer(channel) {
          @Override
          public void handleDelivery(String consumerTag,
                                Envelope envelope,
                                AMQP.BasicProperties properties,
                                byte[] body)
                  throws IOException {
              System.out.println("recvive message:" + new String(body));
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              channel.basicAck(envelope.getDeliveryTag(), false);
          }
        };
        channel.basicConsume(QUEUE_NAME, consumer);
        //等待回调函数执行完毕之后,关闭资源。
        TimeUnit.SECONDS.sleep(5);
        channel.close();
        connection.close();
    }
}

2.1.4 RabbitMQ核心API

 

 

 

Direct Exchange消费者客户端代码

import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class RabbitMQ4DirectExchangeConsumer {

	public static void main(String[] args) throws Exception {
		
		
        ConnectionFactory connectionFactory = new ConnectionFactory() ;  
        
        connectionFactory.setHost("192.168.110.130");
        connectionFactory.setPort(5672);
		connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("1TdhblkFcdhx2a");
		
        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);

        channel.basicQos(64);//设置客户端最多接收未被ack的消息个数
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                    throws IOException {
                System.out.println("recvive message:" + new String(body));
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume(queueName, consumer);
        //等待回调函数执行完毕之后,关闭资源。
        TimeUnit.SECONDS.sleep(50);
        channel.close();
        connection.close();
	}
}

Direct Exchange生产者客户端代码

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class RabbitMQ4DirectExchangeProducer {

	
	public static void main(String[] args) throws Exception {
		
		//1 创建ConnectionFactory
		ConnectionFactory connectionFactory = new ConnectionFactory();
		connectionFactory.setHost("192.168.110.130");
		connectionFactory.setPort(5672);
		connectionFactory.setVirtualHost("/");
		connectionFactory.setUsername("root");
		connectionFactory.setPassword("1TdhblkFcdhx2a");
		
		//2 创建Connection
		Connection connection = connectionFactory.newConnection();
		//3 创建Channel
		Channel channel = connection.createChannel();  
		//4 声明
		String exchangeName = "test_direct_exchange";
		String routingKey = "test_direct_routingKey";
		//5 发送
		
		String msg = "Hello World RabbitMQ 4  Direct Exchange Message ... ";
		channel.basicPublish(exchangeName, routingKey , null , msg.getBytes()); 		
		
	}
	
}

 

 

可以通过模糊匹配进行队列和exchange多对多的链接。这种方式会乱。

Topic Exchange消费者客户端1代码

import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class Receiver4TopicExchange1 {

	public static void main(String[] args) throws Exception {


        ConnectionFactory connectionFactory = new ConnectionFactory() ;

        connectionFactory.setHost("192.168.110.130");
        connectionFactory.setPort(5672);
		connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("1TdhblkFcdhx2a");

        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";
		//String routingKey = "user.*";
		String routingKey = "user.#";
		channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
		channel.queueDeclare(queueName, false, false, false, null);
		channel.queueBind(queueName, exchangeName, routingKey);

        channel.basicQos(64);//设置客户端最多接收未被ack的消息个数
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                    throws IOException {
                System.err.println("consumer1 start.. ");
                System.out.println("recvive message:" + new String(body) + ", RoutingKey: " + envelope.getRoutingKey());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume(queueName, consumer);
        //等待回调函数执行完毕之后,关闭资源。
        TimeUnit.SECONDS.sleep(50);
        channel.close();
        connection.close();

	}
}

Topic Exchange消费者客户端2代码

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class Receiver4TopicExchange2 {

	public static void main(String[] args) throws Exception {


        ConnectionFactory connectionFactory = new ConnectionFactory() ;

        connectionFactory.setHost("192.168.110.130");
        connectionFactory.setPort(5672);
		connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("1TdhblkFcdhx2a");

        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";
		String routingKey = "user.*";
//		String routingKey = "user.#";
		channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
		channel.queueDeclare(queueName, false, false, false, null);
		channel.queueBind(queueName, exchangeName, routingKey);

        channel.basicQos(64);//设置客户端最多接收未被ack的消息个数
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                    throws IOException {
                System.err.println("consumer2 start.. ");
                System.out.println("recvive message:" + new String(body) + ", RoutingKey: " + envelope.getRoutingKey());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        channel.basicConsume(queueName, consumer);
        //等待回调函数执行完毕之后,关闭资源。
        TimeUnit.SECONDS.sleep(50);
        channel.close();
        connection.close();
	}
}

Topic Exchange生产者客户端代码

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.110.130");
		connectionFactory.setPort(5672);
		connectionFactory.setVirtualHost("/");
		connectionFactory.setUsername("root");
		connectionFactory.setPassword("1TdhblkFcdhx2a");
		
		//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代码

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class Receiver4FanoutExchange1 {

	public static void main(String[] args) throws Exception {


        ConnectionFactory connectionFactory = new ConnectionFactory() ;

        connectionFactory.setHost("192.168.110.130");
        connectionFactory.setPort(5672);
		connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("1TdhblkFcdhx2a");

        connectionFactory.setAutomaticRecoveryEnabled(true);
        connectionFactory.setNetworkRecoveryInterval(3000);
        Connection connection = connectionFactory.newConnection();

        Channel channel = connection.createChannel();
		//4 声明
		String exchangeName = "test_fanout_exchange";
		String exchangeType = "fanout";
		String queueName = "test_fanout_queue";
		String routingKey = "";	//	不设置路由键
		channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
		channel.queueDeclare(queueName, false, false, false, null);
		channel.queueBind(queueName, exchangeName, routingKey);

        channel.basicQos(64);//设置客户端最多接收未被ack的消息个数
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                    throws IOException {
                System.err.println("--------------- consumer 1 --------------");
                System.out.println("recvive message:" + new String(body) + ", RoutingKey: " + envelope.getRoutingKey());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        //参数:队列名称、是否自动ACK、Consumer
        channel.basicConsume(queueName, true, consumer);
        //等待回调函数执行完毕之后,关闭资源。
        TimeUnit.SECONDS.sleep(50);
        channel.close();
        connection.close();
	}
}

Fanout Exchange消费者客户端2代码

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

public class Receiver4FanoutExchange2 {

    public static void main(String[] args) throws Exception {


        ConnectionFactory connectionFactory = new ConnectionFactory();

        connectionFactory.setHost("192.168.110.130");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("root");
        connectionFactory.setPassword("1TdhblkFcdhx2a");

        connectionFactory.setAutomaticRecoveryEnabled(true);
        connectionFactory.setNetworkRecoveryInterval(3000);
        Connection connection = connectionFactory.newConnection();

        Channel channel = connection.createChannel();
        //4 声明
        String exchangeName = "test_fanout_exchange";
        String exchangeType = "fanout";
        String queueName = "test_fanout_queue";
        String routingKey = "";    //	不设置路由键
        channel.exchangeDeclare(exchangeName, exchangeType, true, false, false, null);
        channel.queueDeclare(queueName, false, false, false, null);
        channel.queueBind(queueName, exchangeName, routingKey);

        channel.basicQos(64);//设置客户端最多接收未被ack的消息个数
        Consumer consumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag,
                                       Envelope envelope,
                                       AMQP.BasicProperties properties,
                                       byte[] body)
                    throws IOException {
                System.err.println("--------------- consumer 2 --------------");
                System.out.println("recvive message:" + new String(body) + ", RoutingKey: " + envelope.getRoutingKey());
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                channel.basicAck(envelope.getDeliveryTag(), false);
            }
        };
        //参数:队列名称、是否自动ACK、Consumer
        channel.basicConsume(queueName, true, consumer);
        //等待回调函数执行完毕之后,关闭资源。
        TimeUnit.SECONDS.sleep(50);
        channel.close();
        connection.close();
    }
}

Fanout Exchange生产者客户端代码

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class Sender4FanoutExchange {

	
	public static void main(String[] args) throws Exception {
		
		//1 创建ConnectionFactory
		ConnectionFactory connectionFactory = new ConnectionFactory();
		connectionFactory.setHost("192.168.110.130");
		connectionFactory.setPort(5672);
		connectionFactory.setVirtualHost("/");
		connectionFactory.setUsername("root");
		connectionFactory.setPassword("1TdhblkFcdhx2a");
		
		//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 RabbitMQ 4 FANOUT Exchange Message ...";
			channel.basicPublish(exchangeName, "" , null , msg.getBytes());
		}
		channel.close();  
        connection.close();  
	}
	
}

2.1.5 RabbitMQ核心API-其它关键概念讲解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
回答: RabbitMQ是一个消息队列中间件,它可以被看作是一个服务节点或者服务实例,通常也可以将其视为一台服务器。\[1\]在生产环境中,由于一些不明原因导致RabbitMQ重启,可能会导致消息投递失败和消息丢失。为了确保消息的可靠投递,可以采用事务机制或者使用发送方确认机制。事务机制可以解决消息发送方和RabbitMQ之间消息确认的问题,只有消息成功被RabbitMQ接收,事务才能提交成功,否则可以进行事务回滚和消息重发。然而,使用事务机制会对RabbitMQ的性能产生一定的影响。另一种方法是使用发送方确认机制,通过该机制可以实现消息的可靠投递,即发送方在消息被RabbitMQ确认接收之后才认为消息已经成功投递。\[2\]\[3\]这样可以在RabbitMQ集群不可用的情况下,对无法投递的消息进行处理和恢复。 #### 引用[.reference_title] - *1* [Rabbitmq进阶](https://blog.csdn.net/weixin_46634416/article/details/124755747)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [RabbitMQ进阶](https://blog.csdn.net/weixin_73198745/article/details/130910185)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [RabbitMQ入门进阶高级详解(内容有点多)](https://blog.csdn.net/sinat_16658263/article/details/124211232)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lvdapiaoliang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值