消息中间件RocketMQ

Dubbo远程调用,属于同步调用,在调用后还在等待被调用方返回的结果。
在这里插入图片描述

同步:你走我不走,我走你不走
异步:你走你的,我走我的,互不打扰
在这里插入图片描述
redis缓存中间件

当然,我们也可以通过异步调用的方式,利用消息中间件。
例如订单服务向消息中间件中发送消息,而商品服务可以从消息中间件中获取消息。

不能说ConsumerGroupA中的consumerA消费完MsgTopicA后就把消息队列中的MsgTopicA干掉,应当给MsgTopicA做一个标记,表示ConsumerGroupA消费完了(短信发送完了)。
因为接下来可能ConsumerGroupB也会来消费消息队列中的MsgTopicA(来执行发邮件操作)。
ConsumerGroupA中的consumerB去消费消息队列中的MsgTopicA,这时他发现同组的consumerA已经消费过了,就不再重复消费了。

那消息2怎么保证自己一定会发送给broken2呢?
看RooketMQ的部署模型。


整合

  1. 导包
<dependency> 
	<groupId>org.apache.rocketmq</groupId> 
	<artifactId>rocketmq-client</artifactId> 
	<version>4.4.0</version>
</dependency>
  1. 消息⽣产者实现
package com.cskaoyan;

import java.nio.charset.Charset;

import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;

public class Producer {
    public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
        // 消息的发送者
        DefaultMQProducer producer = new DefaultMQProducer("producer_group");

        // 设置nameServer的地址
        producer.setNamesrvAddr("localhost:9876");
        producer.start();

        // 创建一个消息对象
        String bodyStr = "jiayoutiezi";
        Message message = new Message("topicA", bodyStr.getBytes(Charset.forName("utf-8")));

        // 设置消息的延迟级别(18个延迟级别)
        // messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
        message.setDelayTimeLevel(2);

        // sendResult消息发送的结果
        SendResult sendResult = producer.send(message);

        // 消息发送的状态
        // SEND_OK,  发送成功
        // FLUSH_DISK_TIMEOUT,  刷盘超时
        // FLUSH_SLAVE_TIMEOUT,  同步到Slave超时
        // SLAVE_NOT_AVAILABLE;  从节点不可用
        SendStatus sendStatus = sendResult.getSendStatus();
        if (SendStatus.SEND_OK.equals(sendStatus)) {
            System.out.println("send succeed!" + System.currentTimeMillis());
        } else {
            System.out.println("send failed!" + System.currentTimeMillis());
        }
    }
}

  1. 消息消费者实现
package com.cskaoyan;

import java.util.List;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;

public class Consumer {

    public static void main(String[] args) throws MQClientException {
        // DefaultMQPullConsumer表示主动拉取
        // DefaultMQPushConsumer表示推送
        // 新建一个消费者
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group");

        // 设置注册中心的地址
        consumer.setNamesrvAddr("localhost:9876");

        //订阅Topic,第二个参数表示过滤条件
        consumer.subscribe("topicA", "*");

        // 注册消息监听器
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            // 这个方法就是consumer收到了消息之后调用的方法
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                System.out.println("收到消息。" + System.currentTimeMillis());

                // 每次从消息队列中拿出一个消息对象
                MessageExt messageExt = list.get(0);
                byte[] body = messageExt.getBody();
                String bodyStr = new String(body);

                // 做一些我们需要的业务逻辑
                System.out.println("收到的消息内容是:" + bodyStr);

                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                // 假如在这里消费失败的话,rocketMQ默认会重试16次
            }
        });

        consumer.start();
    }
}


消息队列的知识用在哪呢?
订单超时取消中,和以上代码的思路差不多,但是需要使用spring来管理bean。

application.yml:

mq:
  addr: localhost:9876
  producer_group: producer_group
  topic: order_timeout

OrderProducer:

package com.mall.order.mq;

import com.alibaba.fastjson.JSON;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * 发送一个订单超时取消的消息
 * 创建订单时就发送
 */
@Component
public class OrderProducer {

    @Value("${mq.producerGroup}")
    private String producerGroup;

    @Value("${mq.addr}")
    private String addr;

    @Value("${mq.topic}")
    private String topic;

    DefaultMQProducer mqProducer;

    // 在对OrderProducer这个bean初始化的时候,就会执行@PostConstruct注解下的方法。
    @PostConstruct
    public void init(){
        // 实例化OrderProducer的时候执行
        mqProducer = new DefaultMQProducer(producerGroup);
        mqProducer.setNamesrvAddr(addr);
        try {
            mqProducer.start();
        } catch (MQClientException e) {
            e.printStackTrace();
        }
    }

    public void sendOrderOvertimeMsg(String orderId,Integer userId){
        Map map = new HashMap<String,Object>();
        map.put("orderId",orderId);
        map.put("userId",userId);
        String jsonString = JSON.toJSONString(map);
        Message message = new Message(topic, jsonString.getBytes(Charset.forName("utf-8")));
        // 延迟5分钟 开发时进行修改
        message.setDelayTimeLevel(9);
        try {
            mqProducer.send(message);
        } catch (MQClientException e) {
            e.printStackTrace();
        } catch (RemotingException e) {
            e.printStackTrace();
        } catch (MQBrokerException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

SendMessageHandler:

package com.mall.order.biz.handler;

import com.mall.order.biz.context.AbsTransHandlerContext;
import com.mall.order.biz.context.CreateOrderContext;
import com.mall.order.biz.context.TransHandlerContext;
import com.mall.order.mq.OrderProducer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;

/**
 * @Description: 利用mq发送延迟取消订单消息
 **/
@Component
@Slf4j
public class SendMessageHandler extends AbstractTransHandler {

	@Autowired
	private OrderProducer orderProducer;

	@Override
	public boolean isAsync() {
		return false;
	}

	@Override
	public boolean handle(TransHandlerContext context) {
		// 还需要修改
		CreateOrderContext createOrderContext = (CreateOrderContext) context;
		String userIdStr = createOrderContext.getUserId().toString();
		Integer userId = Integer.valueOf(userIdStr);
		String orderId = createOrderContext.getOrderId();
		orderProducer.sendOrderOvertimeMsg(orderId,userId);
        return true;
	}
}

OrderConsumer:

package com.mall.order.mq;

import com.alibaba.fastjson.JSON;
import com.mall.order.constants.OrderConstants;
import com.mall.order.dal.entitys.Order;
import com.mall.order.dal.persistence.OrderMapper;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;

/**
 *  order消费者  需要修改
 */
@Component
public class  OrderConsumer {

    private DefaultMQPushConsumer consumer;

    @Autowired
    OrderMapper orderMapper;

    @Value("${mq.topic}")
    private String topic;

    @Value("${mq.addr}")
    private String addr;

    @Value("${mq.consumerGroup}")
    private String consumerGroup;

    @PostConstruct
    public void init(){
        consumer = new DefaultMQPushConsumer(consumerGroup);
        consumer.setNamesrvAddr(addr);
        try {
            consumer.subscribe(topic,"*");
        } catch (MQClientException e) {
            e.printStackTrace();
        }
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
                MessageExt messageExt = list.get(0);
                byte[] body = messageExt.getBody();
                String bodyStr = new String(body);
                Map<String,Object> map = JSON.parseObject(bodyStr, Map.class);
                String orderId = (String) map.get("orderId");
                Integer userId = (Integer) map.get("userId");
                Order order = orderMapper.selectByPrimaryKey(orderId);
                // 如果订单还是一个初始化状态的话
                if (order.getStatus() == OrderConstants.ORDER_STATUS_INIT){
                    // 就将其修改为取消状态
                    order.setStatus(OrderConstants.ORDER_STATUS_TRANSACTION_CANCEL);
                    int effectedRows = orderMapper.updateByPrimaryKey(order);
                    // 进行判断
                    if (effectedRows < 1){
                        // 重试
                        return ConsumeConcurrentlyStatus.RECONSUME_LATER;
                    }else {
                        // 消费成功
                        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                    }
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        try {
            consumer.start();
        } catch (MQClientException e) {
            e.printStackTrace();
        }
    }
}

https://blog.csdn.net/qq_42933931/article/details/113790433

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

-玫瑰少年-

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

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

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

打赏作者

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

抵扣说明:

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

余额充值