1.1 MQ的相关概念
1.1.1 什么是MQ
MQ,从字面意思上看,本质是个队列,FIFO先入先出,只不过队列中存放的内容是message而已,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ是一种非常常见的上下游“逻辑解耦+物理解耦”的消息通信机制。使用了MQ之后,消息发送上游只需要依赖MQ,不用依赖其他服务。
1.1.2 为什么要使用MQ
-
流量削峰
-
举个例子,如果订单系统最多能处理一万次订单,这个处理能力应付正常时段的下单绰绰有余,但是在高峰期,有两万次下单系统处理不了,只能限制不允许下单。使用消息队列做缓存,可以取消这个限制,把1秒内下的订单分散成一段时间处理。
-
-
应用解耦
-
以电商应用为例,应用中有订单系统、库存系统、物流系统、支付系统。用户创建订单后,如果耦合调用,任何一个子系统出了故障,都会造成下单操作异常。当转变成基于消息队列的方式后,系统间调用的问题会减少很多,比如物流系统发送故障,需要几分钟来修复。在这几分钟的时间,物流系统要处理的数据被缓存在消息队列中,用户的下单操作可以正常完成。当物流系统恢复后,继续处理消息。
-
-
异步处理
-
有些服务是异步的,例如A调用B,B需要花很长时间,但是A需要知道B什么时候执行完。以前一般有两种方式,a过一段时间调用b的api去查询,或者A提供一个callback方法。但是这样不太优雅。我们可以B处理完后,发送一条消息给A服务,A可以及时的得到异步处理的消息。
-

1.1.3 RabbitMQ基本介绍
2007年发布,是一个在AMQP(高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。
由于erlang语言的高并发特性,性能较好;吞吐量到万级,MQ功能比较完备,健壮、稳定、易用、跨平台、支持多种语言。社区活跃度高,更新频率相当高。
1.1.4 MQ的选择
-
Kafka
KafKa主要特点是基于Pull的模式来处理消息消费,追求高吞吐量,一开始的目的就是用于日志收集和传输,适合产生大量数据的互联网服务的数据收集业务。
-
RocketMQ
天生为金融互联网领域而生,对于可靠性要求很高的场景,尤其是电商里面的订单扣款,以及业务削峰,在大量的交易涌入时,后端可能无法及时处理的情况。
-
RabbitMQ
结合erlang语言本身的并发优势,性能好时效性微秒级,社区活跃度也比较高,管理界面用起来十分方便,如果你的数据量没那么大,中小型公司优先选择功能比较完备的RabbitMQ。
1.2 RabbitMQ
1.2.1 基本概念
RabbitMQ是一个消息中间件:它接受并转发消息。你可以把它当做一个快递站点,当你要发送一个包裹时,你把你的包裹放在快递站,快递员最终会把你的快递送到收件人那里,按照这种逻辑RabbitMQ是一个快递站,一个快递员帮你传递快件。RabbitMQ与快递站的主要区别在于,它不处理快件而是接收,存储和转发消息数据。

1.2.2 核心部分

1.2.3 各个名词解释

Broker:接收和分发消息的应用,RabbitMQ Server就是Message Broker
Virtual host:处于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQ server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建 exchange/queue等
Connection:publisher/consumer和broker之间的TCP连接
Channel:如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销是巨大的,效率也较低。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。
Exchange:message到达broker的第一站,根据分发规则,匹配查询表中的routing key,分发消息到queue中。常用的类型有:direct(point-to-point),topic(publish-subscribe)and fanout(multicast)
Queue:消息最终被送到这里等待consumer取走
Binding:exchange和queue之间的虚拟连接,binding可以包含routing key,Binding信息被保存在exchange中的查询表中,用户message的分发依据
1.2.4 安装
1)官网地址
https://rabbitmq.com/download.html
2)文件上传
上传到 /usr/local/software目录下
3)安装文件
rpm -ivh erlang-21.3-1.el7.x86_64.rpm yum install socat -y rpm -ivh rabbitmq-server-3.8.8-1.el7.noarch.rpm
4)常用命令
# 添加开机启动RabbitMQ服务 chkconfig rabbitmq-server on # 启动服务 /sbin/service rabbitmq-server start # 查看服务状态 /sbin/service rabbitmq-server status # 停止服务 /sbin/service rabbitmq-server stop # 开启web管理插件 rabbitmq-plugins enable rabbitmq_management
访问:http://xxx.xxx.xxx.xxx:15672/
初始化帐号和密码为:guest/guest
我们发现权限不足,无法访问

1.2.6 添加一个新的帐号
#当前用户和角色 rabbitmqctl list_users # 创建帐号 rabbitmqctl add_user admin 123 # 设置用户角色 rabbitmqctl set_user_tags admin administrator # 设置用户权限 set_permissions [-p <vhostpath>] <user> <conf> <write> <read> # 设置用户admin具有/vhost1这个virtual host中所有资源的配置、写、读权限 rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*"
此时我们用刚添加的admin/123登陆

2. Hello world
在下图中,“P”是我们的生产者,“C”是我们的消费者,中间的框是一个队列-RabbitMQ,代表使用者保留的消息缓冲区

2.1 引入依赖
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> </build> <dependencies> <!--rabbitmq 依赖客户端--> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.8.0</version> </dependency> <!--操作文件流的一个依赖--> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency> </dependencies>
2.2 生产者
public class Producer {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
//创建一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("xxx.xxx.xxx.xxx");
factory.setUsername("admin");
factory.setPassword("123");
//channel 实现了自动 close 接口 自动关闭 不需要显示关闭
try(Connection connection = factory.newConnection(); Channel channel =
connection.createChannel()) {
/**
* 生成一个队列
* 1.队列名称
* 2.队列里面的消息是否持久化 默认消息存储在内存中
* 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费
* 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除
* 5.其他参数
*/
channel.queueDeclare(QUEUE_NAME,false,false,false,null);
String message="hello world";
/**
* 发送一个消息
* 1.发送到那个交换机
* 2.路由的 key 是哪个
* 3.其他的参数信息
* 4.发送消息的消息体
*/
channel.basicPublish("",QUEUE_NAME,null,message.getBytes());
System.out.println("消息发送完毕");
}
}
}
我们执行以下生产者,发送一条消息。
可以发现rabbit管理页面对应的队列中有一条消息处于准备消费的状态。

2.3 消费者
public class Consumer {
private final static String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("xxx.xxx.xxx.xxx");
factory.setUsername("admin");
factory.setPassword("123");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
System.out.println("等待接收消息....");
//推送的消息如何进行消费的接口回调
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody());
System.out.println(message);
};
//取消消费的一个回调接口 如在消费的时候队列被删除掉了
CancelCallback cancelCallback = (consumerTag) -> {
System.out.println("消息消费被中断");
};
/**
* 消费者消费消息
* 1.消费哪个队列
* 2.消费成功之后是否要自动应答 true 代表自动应答 false 手动应答
* 3.消费者未成功消费的回调
* 4.消费者取消消息的回调
*/
channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
}
}
可以看到刚生产的消息被消费了。

本文介绍了RabbitMQ的基本概念,包括作为消息中间件的作用、使用MQ解决流量削峰、应用解耦和异步处理问题。还详细讲解了RabbitMQ的选择,以及如何通过RabbitMQ实现简单的生产者消费者模式。
725

被折叠的 条评论
为什么被折叠?



