目录
一、消息中间件
1、消息中间件
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
2、消息中间件的使用场景
2.1 异步处理
场景说明:用户注册完成后,需要发送注册邮件和注册短信。
传统的做法有两种:
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信,以上三个任务全部完成后,返回给客户端。
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时发送注册短信,以上三个任务完成后,返回给客户端。与串行方式的差别是并行的方式可以提高处理的时间。
引入消息队列,改造后的架构如下:
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此架构改变后,系统的吞吐量大大提高了。
2.2 应用解耦
场景说明:用户下单后,订单系统需要通知库存系统,传统的做法是订单系统调用库存系统的接口。
解耦合后:
订单系统:假如在下单时库存系统不能正常使用,也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其它的后续操作了,实现订单系统与库存系统的应用解耦。
3、常见消息中间件比较
二、RocketMQ
1、RocketMQ
RocketMQ是阿里巴巴开源的分布式消息中间件,现在是Apache的一个顶级项目。在阿里内部使用非常 广泛,已经经过了"双11"这种万亿级的应用场景考验。
1.1 下载RocketMQ
(1)解压RocketMQ缩安装包(已上传);
(2)配置环境变量
变量名:ROCKETMQ_HOME 变量值:MQ解压缩路径(一直到bin目录那一级);
编辑:path %ROCKETMQ_HOME%\bin
1.2 启动RocketMQ
(1)在bin目录下打开cmd;
(2)启动NameServer,命令:start mqnamesrv.cmd;
(3)启动Broker,命令:start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true。
1.3 安装可视化插件
无需解压,在jar包位置打开cmd,输入命令java -jar rocketmq-console-ng-1.0.0.jar启动jar工程。启动完毕后,在浏览器输入:localhost:8085进入控制台。
2、RocketMQ的架构及概念
如上图所示,整体可以分成4个角色,分别是:NameServer,Broker,Producer,Consumer。
Broker(邮递员):Broker是RocketMQ的核心,负责消息的接收,存储,投递等功能; NameServer(邮局):消息队列的协调者,Broker向它注册路由信息,同时Producer和Consumer向其获取路由信息;
Producer(寄件人):消息的生产者,需要从NameServer获取Broker信息,然后与Broker建立连接,向Broker发 送消息;
Consumer(收件人):消息的消费者,需要从NameServer获取Broker信息,然后与Broker建立连接,从Broker获 取消息;
Topic(地区):用来区分不同类型的消息,发送和接收消息前都需要先创建Topic,针对Topic来发送和接收消息;
Message Queue(邮件):为了提高性能和吞吐量,引入了Message Queue,一个Topic可以设置一个或多个Message Queue,这样消息就可以并行往各个Message Queue发送消息,消费者也可以并行的从多个;
Message:Queue读取消息Message Message是消息的载体。
3、消息发送和接收演示
消息发送步骤:
(1)创建消息生产者, 指定生产者所属的组名;
(2)指定Nameserver地址;
(3)启动生产者;
(4)创建消息对象,指定主题、标签和消息体;
(5)发送消息;
(6)关闭生产者。
消息接收步骤:
(1)创建消息消费者, 指定消费者所属的组名;
(2)指定Nameserver地址;
(3)指定消费者订阅的主题和标签;
(4)设置回调函数,编写处理消息的方法;
(5)启动消息消费者
3.1 添加坐标
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>
3.2 发送同步消息
这种可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知,短信通知。
RocketMQSendTest1类:
//发送消息
public class RocketMQSendTest1 {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
//1. 创建消息生产者, 指定生产者所属的组名
DefaultMQProducer producer = new DefaultMQProducer("myProducer-group");
//2. 指定Nameserver地址
producer.setNamesrvAddr("127.0.0.1:9876");
//3. 启动生产者
producer.start();
for(int i=0;i<10;i++){
//4. 创建消息对象,指定主题、标签和消息体
Message msg = new Message("myTopic","myTag",("是大眼同学呦!"+i).getBytes());
//5. 发送消息
SendResult sendResult = producer.send(msg);
System.out.println(sendResult);
}
//6. 关闭生产者
producer.shutdown();
}
}
RocketMQSeceiveTest1类:
//接收消息
public class RocketMQReceiveTest1 {
public static void main(String[] args) throws MQClientException {
//1. 创建消息消费者, 指定消费者所属的组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("myConsumer-group");
//2. 指定Nameserver地址
consumer.setNamesrvAddr("127.0.0.1:9876");
//3. 指定消费者订阅的主题和标签
consumer.subscribe("myTopic","*");
//consumer.setMessageModel(MessageModel.BROADCASTING);
//4. 设置回调函数,编写处理消息的方法
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
System.out.println("获取到的消费数据:"+list);
System.out.println(new String(list.get(0).getBody()));
//返回消费状态
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5. 启动消息消费者
consumer.start();
System.out.println("Consumer start...");
}
}
3.3 发送异步消息
异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。
RocketMQSendTest2类:
//发送消息
public class RocketMQSendTest2 {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
//1. 创建消息生产者, 指定生产者所属的组名
DefaultMQProducer producer = new DefaultMQProducer("myProducer-group");
//2. 指定Nameserver地址
producer.setNamesrvAddr("127.0.0.1:9876");
//3. 启动生产者
producer.start();
for (int i = 0; i < 10; i++) {
//4. 创建消息对象,指定主题、标签和消息体
Message msg = new Message("myTopic", "myTag2", ("是大眼同学呦!" + i).getBytes());
//5. 发送消息
producer.send(msg, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("发送成功:" + sendResult);
}
@Override
public void onException(Throwable throwable) {
System.out.println("发送异常:" + throwable);
}
});
//每个两秒发送一条消息
TimeUnit.SECONDS.sleep(2);
}
//6. 关闭生产者
producer.shutdown();
}
}
3.4 单向发送消息
这种方式主要用在不特别关心发送结果的场景,例如日志发送。
RocketMQSendTest3类:
//发送消息
public class RocketMQSendTest3 {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
//1. 创建消息生产者, 指定生产者所属的组名
DefaultMQProducer producer = new DefaultMQProducer("myProducer-group");
//2. 指定Nameserver地址
producer.setNamesrvAddr("127.0.0.1:9876");
//3. 启动生产者
producer.start();
for(int i=0;i<10;i++){
//4. 创建消息对象,指定主题、标签和消息体
Message msg = new Message("myTopic","myTag3",("是大眼同学呦!"+i).getBytes());
//5. 发送消息
//发送单向消息,没有任何返回结果
producer.sendOneway(msg);
//每个3秒发送一条数据
TimeUnit.SECONDS.sleep(3);
}
//6. 关闭生产者
producer.shutdown();
}
}
3.5 消费消息
(1)负载均衡模式(默认方式):消费者采用负载均衡方式消费消息,多个消费者共同消费队列消息,每个消费者处理的消息不同;
(2)广播模式:消费者采用广播的方式消费消息,每个消费者消费的消息都是相同的。
在创建一个接收消息的RocketMQSeceiveTest2类,对这两个接收消息的类都加上下面的代码,再去测试时,我们会看到两个消费者消费的消息数量是对等的,都是5条。
//广播模式消费
consumer.setMessageModel(MessageModel.BROADCASTING);