由於太長了,所以分成兩篇寫,接上一篇講解了消息的定義和發送,這裏繼續講解消費者
由於可能每條消息所處理的邏輯可能不一樣,例如:常規訂單30分鐘不支付則取消訂單,團購訂單一天拼團不成功則取消等等,爲了避免在消費者監聽類中使用大量if else,這裏使用策略模式來處理(由於spring的bean的初始化的時候創建,如果用Java常規的反射獲取類,則在具體策略類用注入別的bean的時候,會拿不到值,所以需要通過applicationContext獲取類)
1.消息消費者類-MessageConsumer -- 使用註解的方式監聽,這裏包括消費確認
@Component
@RabbitListener(queues = QueueContent.MESSAGE_QUEUE_NAME)
public class MessageConsumer {
static Logger logger = LoggerFactory.getLogger(MessageConsumer.class);
@RabbitHandler
public void handler(String msg,Channel channel, Message message) throws IOException {
if (!StringUtils.isEmpty(msg)) {
MessagePojo messagePojo = JSONUtil.toBean(msg,MessagePojo.class);
Action action = Action.RETRY;
try {
//這裏使用策略模式和springboot的結合使用,
Strategy s = (Strategy)SpringContextUtil.getBean(messagePojo.getClassName());
s.doJob(messagePojo.getParams());
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
logger.info("[MessageConsumer延時消息消費時間]"+DateUtil.datetoString(new Date()) + JSON.toJSONString(messagePojo) + ",消息ID:" + messagePojo.getMessageId());
action = Action.ACCEPT;
} catch (Exception e) {
logger.error("確認消費異常",e);
//記錄下這條消息
redisService.hmSet("failedMsg",messagePojo.getMessageId(),msg);
action = Action.RETRY;
}finally {
// 通過finally塊來保證Ack/Nack會且只會執行一次
if (action == Action.ACCEPT) {
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
} else if (action == Action.RETRY) {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
} else {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
}
}
}
}
}
public enum Action {
ACCEPT, // 處理成功
RETRY, // 可以重試的錯誤
REJECT, // 無需重試的錯誤
}
2.定義策略接口-Strategy
public interface Strategy {
public void doJob(Map<String, Object> params) throws Exception;
}
3.定義具體策略實現類---A B
@Component("A")
public class A implements Strategy {
/**
*
* @param params 接口所需參數
*/
@Override
public void doJob(Map<String, Object> params) {
System.out.println("用A方法處理");
}
}
@Component("B")
public class B implements Strategy {
/**
*
* @param params 接口所需參數
*/
@Override
public void doJob(Map<String, Object> params) {
System.out.println("用B方法處理");
}
}
4.獲取上下文工具類,在spring容器中根據類名獲取具體類---SpringContextUtil
@Component
public class SpringContextUtil implements ApplicationContextAware {
// Spring應用上下文環境
private static ApplicationContext applicationContext;
/**
* 實現ApplicationContextAware接口的回調方法。設置上下文環境
*
* @param applicationContext
*/
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextUtil.applicationContext = applicationContext;
}
/**
* @return ApplicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 獲取對象
*
* @param name
* @return Object
* @throws BeansException
*/
public static Object getBean(String name) throws BeansException {
return applicationContext.getBean(name);
}
}
5.寫一個controller測試
@RestController
public class MessageController {
@Autowired
private MessageProvider provider;
Logger logger = LoggerFactory.getLogger(MessageController.class);
@RequestMapping(value="/send_message",produces = "text/json;charset=UTF-8")
@ResponseBody
public String send_message(MessagePojo pojo){
try {
provider.sendMessage(pojo);
return JSON.toJSONString(pojo);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
測試結果:延遲消費和具體方法處理具體業務也實現了