策略模式写一个消息消费者RocketMq

文章介绍了如何设计一个消息处理系统,包括生产者如何发送消息到队列,通过`SendMQMessageService`接口和实现类。消费者端,使用RocketMQ监听器消费消息,根据消息标签匹配相应处理器执行业务代码。整个流程涉及消息类型枚举、消息处理器接口及其不同类型的实现,以及通过`MessageStrategyHandle`注解来关联消息类型和处理器。
摘要由CSDN通过智能技术生成

1.准备阶段(生产者)

1.1准备消息

消息生产者生成对应的消息, 发送到对应的消息队列中;
举个栗子:

//整个接口
public interface SendMQMessageService{
    /**
     * 发送消息
     * @param topic 主题
     * @param content 消息体
     * @param tag 标签
     * @param key 
     * @return
     */
    void sendMessage(String topic, String content , String tag, String key);
}
//整个实现类
public class SendMQMessageServiceImpl implements SendMQMessageService{
    @Autowired
    private MqTempleate mqTemplate;
    
    
    /**
     * 发送消息
     * @param topic 主题
     * @param content 消息体
     * @param tag 标签
     * @param key 
     * @return
     */
    @Override
    public void sendMessage(String topic, String content , String tag, String key){
        mqTemplate.send(getMessageBody(topic,content,tag,key) , new SendCallback(){
            @Override
            public void onSuccess(SendResult sendResult){
                //成功了咋滴
            }
            
            @Override
            public void onException(Throwable throwable){
                //失败了咋滴
            }
        }); 
    }
    
    //生成一个消息体
    private MQCPMessage getMessageBody(String topic, String content , String tag, String key){
        MQCPMessage msg = new MQCPMessage();
        msg.setTopic(topic);
        msg.setBody(content,getBytes(Charset.forName("UTF-8")));
        //过滤标签
        msg.setTag(tag);
        //消息MQCP只支持id 和 key查询消息;
        if(StringUtil.isNotBlank(key)){
            msg.setKey(key);
        }
    }
}

1.2准备消息处理器上下文

@Component
public class MessageContext{
    @Resource
    private ApplicationContext applicationContext;
    
    //定义一个适配器容器
    public static final Map<MqMessageTypeEnums,Class<MessageStrategy>> messageStrategyMap =Maps.newHashMap();
    
    //定义一个获取消息处理器的方法
    public MessageStrategy getMessageStrategy(MqMessageTypeEnums mqMessageTypeEnums){
        Class<MessageStrategy> messageStrategyClass = messageStrategyMap.get(mqMessageTypeEnums);
        if(messageStrategyClass == null){
            throw new BizException("木有对应的消息类型,爬");
        }
        
        //通过ClassType获取对应的bean;
        return applicationContext.getBean(messageStrategyClass);
    }
    
    
}

1.3准备消息处理器实例对象

1.3.1 定义消息处理器接口

public interface MessageStrategy<T>{
    
    /**
     *  消息处理方法
     * @param messageBody 消息实体
     */
    void consumerMessage(T messageBodyStr);
}

1.3.2 定义消息处理器接口的相关实现

//枚举三种类型
@Getter
@AllArgsConstructor
public enum MqMessageTypeEnums{
    EAT("EAT" , "吃"),
    SLEEP("SLEEP","睡"),
    PLAY("PLAY","玩");
    
    //类型
    String code;
    
    //提示
    String message;
    
    //通过code 获取对应的枚举实例
    public static MqMessageTypeEnums value(String code){
        //获取所有的枚举
        MqMessageTypeEnums[] values = MqMessageTypeEnums.values();
        for(MqMessageTypeEnums mqMessageEnums : values){
            //存在code对应枚举, 返回
            if(Object.equals(mqMessageEnums.getCode(),code)){
                return mqMessageEnums;
            }
        }
        
    }
    
}

这里只举三个实现的例子

@MessageStrategyHandle(MqMessageTypeEnums.EAT)
@Component
public class EatMessageStrategyHandle implements MessageStrategy <MessageBody<String>>{
    @Autowired
    private IEatService iEatService;
    
    @Override
    public void consumerMessage(MessageBody<String> messageBody){
        String body=messageBody.getData();
        MqEatVo mqEatVo=JSON.parseObject(body,MqEatVo.class);
        //执行业务代码
        ResultVo resultVo = iEatService.eatSomething(mqEatVo);
        if(!resultVo.isOk()){
            throw new BizException(ResultEnum.FAILED.getCode(),"没吃到东西");
        }
    }
    
}

@MessageStrategyHandle(MqMessageTypeEnums.SLEEP)
@Component
public class SleepMessageStrategyHandle implements MessageStrategy <MessageBody<String>>{
    @Autowired
    private ISleepService iSleepService;
    
    @Override
    public void consumerMessage(MessageBody<String> messageBody){
        String body=messageBody.getData();
        MqSleepVo mqSleepVo=JSON.parseObject(body,MqSleepVo.class);
        //执行业务代码
        ResultVo resultVo = iSleepService.sleepSomeBody(mqEatVo);
        if(!resultVo.isOk()){
            throw new BizException(ResultEnum.FAILED.getCode(),"睡不着");
        }
    }
    
}

@MessageStrategyHandle(MqMessageTypeEnums.PLAY)
@Component
public class PlayMessageStrategyHandle implements MessageStrategy <MessageBody<String>>{
    @Autowired
    private IPlayService iPlayService;
    
    @Override
    public void consumerMessage(MessageBody<String> messageBody){
        String body=messageBody.getData();
        MqPlayVo mqPlayVo=JSON.parseObject(body,MqPlayVo.class);
        //执行业务代码
        ResultVo resultVo = iSleepService.playGame(mqEatVo);
        if(!resultVo.isOk()){
            throw new BizException(ResultEnum.FAILED.getCode(),"掉星");
        }
    }
    
}

1.4 准备消息类型和消息处理的实例对象对应的Map

需要注意的是:

所有的实例对象都是存储在ApplicationContext的容器中;

messageStrategyMap只是存储了对应枚举的消息处理器Class对象; 要获取真实的对象实例还是需要在Bean的容器中获取;

实现逻辑是:

通过定义一个组件,

a.获取所有的使用了@MessageStrategyHandle 注解的Bean 的实例, (真实执行业务代码的实例对象),

​ 这里会得到一个Map<String,Object> beanNameAndBeanInstanceMap 意思是: bean的名称 和 bean的实例对应的Map

b.遍历这个Map, 将MqMessageTypeEnums枚举和Bean实例进行对应

@Component
public class MessageProcessor implements ApplicationContextAware {
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException{
        //获取所有的使用了@MessageStrategyHandle 注解的Bean 的实例
        Map<String,Object> beanNameAndBeanInstanceMap = mapplicationContext.getBeansWithAnnotation(MessageStrategyHandle.class);
        beanNameAndBeanInstanceMap.forEach(
            (key,value)->{
                //获取类对象
                Class<MessageStrategy> messageStrategyClass =(Class<MessageStrategy>)value.getClass();
                //获取类对象中@MessageStrategyHandle 中的值
                MqMessageTypeEnums mqMessageTypeEnums = messageStrategyClass.getAnnotation(MessageStrategyHandle.class).value();
                //存入消息处理器上下文 MessageContext
                MessageContext.messageStrategyMap.put(mqMessageTypeEnums,messageStrategyClass);
            }
        );
    }
}

2执行阶段(消费者)

2.1获取消息并匹配适配器

获取消息

鉴别消息类型 Tag的方式, 获取对应消息的枚举

消息策略中获取对应的消息处理实例

调用消费消息的方法

@RocketMQMessageListener(consumerGroup = "消费组", topic = "MQ_TOPIC",consumeMode = ConsumeMode.ORDERLY,messageModel = MessageModel.CLUSTERING)
public class MqConsumer implements RocketMQListener<MessageExt>, RocketMQPushConsumerLifecycleListener{
    
    //注入消息上下文
    @Resource
    private MessageContext messageContext;
    
    @Override
    public void onMessage(MessageExt message) {
        try{
            //现将数据转成实体类   这里需要根据具体情况判断: 消息传递的数据模型是什么样的?
            //这里假定传递的是Class实体类 ==> json字符串的信息存在 ==> MessageExt的byte[] 中
            //需要反过来
            String messageJsonStr = new String(message.getBody(), StandardCharsets.UTF_8);
            //这里构建一个消息体
            MessageBody messageBody=MessageBody.buildFrom(message,message.getBody());
            //将messageJsonStr实例化成对象实例的步骤, 在对应的适配器中执行; 这里只做传递;
            String messageTagsCode = message.getTags();
            //获取对应的枚举类
            MqMessageTypeEnums mqMessageTypeEnums=MqMessageTypeEnums.value(messageCode);

            //从消息处理器上下文中获取对应的消息处理器
            MessageStrategy messageStrategy=MessageContext.getMessageStrategy(mqMessageTypeEnums);
            //调用处理消息的方法
            messageStrategy.consumerMessage(messageBody);
        }catch(Exception exception){
            //记录消费
            mqLog.saveLog(message,e.getMessage());
            throws new BizException("消息处理失败");
        }

        
    }
}

以上代码, 已在生产验证;

- 全文完 -

参考:

https://www.cnblogs.com/shanml/p/16463964.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值