一、基础概念
RocketMQ 是阿里开源的分布式消息中间件,RocketMQ 的特点是纯JAVA实现
消息中间件利用消息中间件利用高效可靠的消息传递机制进行平台无关的数据交流,并基于数据通
信来进行分布式系统 的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进
程间的通信。
二、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是消息的载体。
三、消息发送和接收
1.发送同步消息
这种可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知,短信通知。
消息发送步骤:
1. 创建消息生产者, 指定生产者所属的组名
2. 指定Nameserver地址
3. 启动生产者
4. 创建消息对象,指定主题、标签和消息体
5. 发送消息
6. 关闭生产者
//发送消息
public class RocketMQSendTest {
public static void main(String[] args) throws Exception {
//1. 创建消息生产者, 指定生产者所属的组名
DefaultMQProducer producer = new DefaultMQProducer("myproducer-group");
//2. 指定Nameserver地址
producer.setNamesrvAddr("192.168.109.131:9876");
//3. 启动生产者
producer.start();
//4. 创建消息对象,指定主题、标签和消息体
Message msg = new Message("myTopic", "myTag", ("RocketMQ Message").getBytes());
//5. 发送消息
SendResult sendResult = producer.send(msg);
System.out.println(sendResult);
//6. 关闭生产者
producer.shutdown();
}
}
2.接收消息
消息接收步骤:
1. 创建消息消费者, 指定消费者所属的组名
2. 指定Nameserver地址
3. 指定消费者订阅的主题和标签
4. 设置回调函数,编写处理消息的方法
5. 启动消息消费者
//接收消息
public class RocketMQReceiveTest {
public static void main(String[] args) throws MQClientException {
//1. 创建消息消费者, 指定消费者所属的组名
6.3 发送异步消息
异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("myconsumergroup");
//2. 指定Nameserver地址
consumer.setNamesrvAddr("192.168.109.131:9876");
//3. 指定消费者订阅的主题和标签
consumer.subscribe("myTopic", "*");
//4. 设置回调函数,编写处理消息的方法
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt>
msgs, ConsumeConcurrentlyContext context) {
System.out.println("Receive New Messages: " + msgs);
//返回消费状态
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5. 启动消息消费者
consumer.start();
System.out.println("Consumer Started.");
}
}
3. 发送异步消息
异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。
//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", ("消息内容~~~").getBytes());
//5. 发送消息
producer.send(msg, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("发送成功:"+sendResult);
}
@Override
public void onException(Throwable e) {
System.out.println("发送异常:"+e);
}
}
);
TimeUnit.SECONDS.sleep(3);
}
//6. 关闭生产者
producer.shutdown();
4.单向发送消息
这种方式主要用在不特别关心发送结果的场景,例如日志发送
//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", ("单向消息~~~").getBytes());
//5. 发送消息
// 发送单向消息,没有任何返回结果
producer.sendOneway(msg);
TimeUnit.SECONDS.sleep(3);
}
//6. 关闭生产者
producer.shutdown();
四、RocketMQ 消费模式
1. 广播模式
消费者采用广播的方式消费消息,每个消费者消费的消息都是相同的
//设置广播模式
consumer.setMessageModel(MessageModel.BROADCASTING);
2.负载均衡模式(默认方式)
消费者采用负载均衡方式消费消息,多个消费者共同消费队列消息,每个消费者处理的消息不同
//设置集群模式,也就是负载均衡模式
consumer.setMessageModel(MessageModel.CLUSTERING);
五、使用场景案例
场景: 下单成功之后,向下单用户发送短信
1. 订单微服务发送消息
1 .在 shop-order服务 中添加rocketmq的依赖
<!--rocketmq-->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>
2 添加配置
rocketmq:
name-server: 127.0.0.1:9876 #rocketMQ服务的地址
producer:
group: shop-order #生产者组
3 编写测试代码
@RestController
@Slf4j
public class OrderController2 {
@Autowired
private OrderService orderService;
@Autowired
private ProductService productService;
@Autowired
private RocketMQTemplate rocketMQTemplate;
//准备买1件商品
@GetMapping("/order/prod/{pid}")
public Order order(@PathVariable("pid") Integer pid) {
//通过fegin调用商品微服务
Product product = productService.findByPid(pid);
if (product == null){
Order order = new Order();
order.setPname("下单失败");
return order;
}
Order order = new Order();
order.setUid(1);
order.setUsername("测试用户");
order.setPid(product.getPid());
order.setPname(product.getPname());
order.setPprice(product.getPprice());
order.setNumber(1);
orderService.save(order);
//下单成功之后,将消息放到mq中
rocketMQTemplate.convertAndSend("order-topic", order);
return order;
}
}
2 用户微服务订阅消息
1 修改 shop-user 模块配置
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>
2 修改置文件
rocketmq:
name-server: 127.0.0.1:9876
3 编写消息接收服务
//发送短信的服务
@Slf4j
@Service
@RocketMQMessageListener(consumerGroup = "shop-user", topic = "order-topic")
public class SmsService implements RocketMQListener<Order> {
@Override
public void onMessage(Order order) {
log.info("收到一个订单信息{},接下来发送短信", JSON.toJSONString(order));
}
}
启动RocketMQ
切换到安装目录
rocketmq的bin目录下
启动NameServer
start mqnamesrv.cmd
启动Broker
start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true
如果弹出框提示‘错误: 找不到或无法加载主类 xxxxxx’。在bin下找到并打开 runbroker.cmd,然后将‘%CLASSPATH%’加上英文双引号