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