·在使用SpringCloud或Dubbo进行SOA架构后,不同的应用层模块(web)与业务层模块(service)要建立调用关系,也就是依赖/耦合
·当模块变多时,模块间的耦合度也会逐步上升,这就需要一个解耦工具:消息中间件
·另外,如果某个业务流程分为很多步,某一步特别耗时间且不稳定,整个业务的稳定性就会受很大影响,这时也需要用消息中间件来分离这些不稳定的业务过程
消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
在这里面,关键的部分是“消息传递”和“消息排队”,可以保证事件的顺序性,也可以在高并发下使用。
执行过程长,且不需要返回结果的功能,可以利用MQ传递(MQ的异步通信特征)
JMS(Java Message Service),是一套接口规范,在jdk中已定义好接口(类似于JDBC,只有JDBC无法操作数据库,需要具体的驱动来实现功能)。
·TextMessage(String)——普通文本(用得最多)
·MapMessage(Map)——键值对集合(用的次多)
·ObjectMessage(Serializable Object)——可序列化的对象
·BytesMessage(byte[])——字节数组
·StreamMessage(Stream)——流数据
JMS的传递模式非常像观察者模式的思路:
定义对象间的一种一对多的依赖关系,让多个观察者同时监听某一个主题现象,当一个对象的状态发生改变时,会通知所有观察者对象,所有依赖于它的对象都得到通知并被自动更新。
观察者模式——https://my.oschina.net/LinkedBear/blog/1791975
消息传递的方式有两种:
·Queue点对点(生产者与消费者的一对一关系)
·Topic发布-订阅(生产者与消费者的一对多关系)
引用文章图片:https://blog.csdn.net/jasonhui512/article/details/53231566
选用阿里巴巴的RocketMQ(现已被Apache接手),搭建Demo工程
参考文档:http://rocketmq.apache.org/docs/simple-example/
projectxmlns=http://maven.apache.org/POM/4.0.0xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xsi:schemaLocation=http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd
modelVersion4.0.0/modelVersion
groupIdcom.linkedbear/groupId
artifactIdRocketMQ-Demo/artifactId
version0.0.1-SNAPSHOT/version
properties
rocketmq.version4.3.0/rocketmq.version
/properties
parent
groupIdorg.springframework.boot/groupId
artifactIdspring-boot-starter-parent/artifactId
version2.0.0.RELEASE/version
/parent
dependencies
dependency
groupIdorg.springframework.boot/groupId
artifactIdspring-boot-starter-web/artifactId
/dependency
!--RocketMQ--
dependency
groupIdorg.apache.rocketmq/groupId
artifactIdrocketmq-client/artifactId
version${rocketmq.version}/version
/dependency
!--热部署--
dependency
groupIdorg.springframework.boot/groupId
artifactIdspring-boot-devtools/artifactId
/dependency
/dependencies
build
plugins
plugin
artifactIdmaven-compiler-plugin/artifactId
configuration
source1.8/source
target1.8/target
/configuration
/plugin
/plugins
/build
/project
/**
*生产者Controller
*@TitleProducerController
*@authorLinkedBear
*/
@Controller
publicclassProducerController{
//此分组名必须保证全局唯一(考虑到负载均衡等后续问题),故封装为静态常量
publicstaticfinalStringPRODUCE_GROUP_NAME=TestGroup;
//MQ的运行地址
publicstaticfinalStringMQ_IP=127.0.0.1:9876;
@RequestMapping(/produceMessage)
@ResponseBody
publicMapString,ObjectproduceMessage()throwsException{
//1.创建生产者连接(类似于JDBC中的Connection),要传入MQ的分组名
DefaultMQProducerproducer=newDefaultMQProducer(PRODUCE_GROUP_NAME);
//2.设置MQ的运行地址
producer.setNamesrvAddr(MQ_IP);
//3.开启连接
producer.start();
//4.构造消息(重载方法较多,此处选择topic,tag,message的三参数方法)
Messagemessage=newMessage(test_topic,test_tag,(test_message。。。+Math.random()).getBytes());
//5.发送消息,该方法会返回一个发送结果的对象
SendResultresult=producer.send(message);
System.out.println(result.getSendStatus());
//6.关闭连接
producer.shutdown();
//此处将发送结果显示在页面上,方便查看
MapString,Objectmap=newHashMap();
map.put(消息,result.getSendStatus());
returnmap;
}
}
/**
*消费者Controller
*@TitleConsumerController
*@authorLinkedBear
*/
@Controller
publicclassConsumerController{
@RequestMapping(/getMessage)
@ResponseBody
publicvoidgetMessage()throwsException{
//1.创建消费者连接,要传入MQ的分组名,该分组名在ProducerController中
//此处创建的是pushConsumer,它使用监听器,给人的感觉是消息被推送的
//pullConsumer,取消息的过程需要自己写
DefaultMQPushConsumerconsumer=newDefaultMQPushConsumer(ProducerController.PRODUCE_GROUP_NAME);
//2.设置MQ的运行地址
consumer.setNamesrvAddr(ProducerController.MQ_IP);
//3.设置消息的提取顺序
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//4.设置消费者接收消息的Topic和Tag,此处对Tag不作限制
consumer.subscribe(test_topic,*);
//5.使用监听器接收消息
consumer.registerMessageListener(newMessageListenerConcurrently(){
@Override
publicConsumeConcurrentlyStatusconsumeMessage(ListMessageExtmsgs,
ConsumeConcurrentlyContextcontext){
try{
for(MessageExtmessageExt:msgs){
Stringmessage=newString(messageExt.getBody(),utf-8);
System.out.println(收到消息【主题:+messageExt.getTopic()+,正文:+message+】);
}
returnConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}catch(Exceptione){
//转换出现问题,稍后重新发送
returnConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
});
//6.启动消费者
consumer.start();
}
}
执行http://localhost:8080/produceMessage:
执行http://localhost:8080/getMessage: