一、概览
1. 本文目的:
分析生产级别的电商项目,是怎么使用ActiveMQ的。
二、代码分析
1. Config:
① 基本参数
amq.brokerURL=tcp://127.0.0.1:61616
amq.userName=admin
amq.password=admin
amq.destination=weimei2.activemq
amq.sessionCacheSize=100
② Bean的配置:主要配置JMS相关的ActiveMQ连接的工厂类、消息转换器、消息队列、监听器、队列模板。
<bean id="jmsConnectionFactoryExtend" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="${amq.brokerURL}"/>
<property name="userName" value="${amq.userName}"/>
<property name="password" value="${amq.password}"/>
</bean>
</property>
<property name="sessionCacheSize" value="${amq.sessionCacheSize}"/>
</bean>
<!-- converter -->
<bean id="jmsMessageConverter"
class="org.springframework.jms.support.converter.SimpleMessageConverter"/>
<!-- queue -->
<bean id="jmsQueueTemplate" class="org.springframework.jms.core.JmsTemplate">
<constructor-arg ref="jmsConnectionFactoryExtend"/>
<property name="pubSubDomain" value="false"/>
<property name="messageConverter" ref="jmsMessageConverter"/>
</bean>
<!-- listener -->
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="jmsConnectionFactoryExtend"/>
</bean>
<!-- template -->
<bean id="jmsMessagingTemplate" class="org.springframework.jms.core.JmsMessagingTemplate">
<property name="jmsTemplate" ref="jmsQueueTemplate"/>
</bean>
2. Producer:
① 作用:把消息投送到注入的目的地。
/**
*生产者
*/
@Component
public class AMQProducer {
@Autowired
private JmsMessagingTemplate jmsMessagingTemplate;
@Value("${amq.destination}")
public String destination;
public void sendMessage(Object payload, Map<String, Object> header) {
jmsMessagingTemplate.convertAndSend(destination, payload, header);
}
public void sendMessage(String destination,Object payload, Map<String, Object> header) {
jmsMessagingTemplate.convertAndSend(destination, payload, header);
}
}
② 业务应用:举例,用户取消订单后重新计算佣金接口。在后续介绍消费者工作原理时,会说明怎么消费这条消息。
/**
* 取消订单重新计算佣金,发送消息
* @param orderId
*/
private void reCalculateCommision(Long orderId){
AMQProducer aMQProducer = (AMQProducer) ApplicationContextHelper.getBean("AMQProducer");
//发送消息重新计算推广佣金
Map<String,Object> header = new HashMap<>();
header.put("bizType","COMMISION_RECALC");
header.put("orderId",orderId);
aMQProducer.sendMessage("",header);
}
注:将消息生产投送到消息队列后,生产者的工作就算完成了。
3. Consumer:
① 利用JmsListener监听消息队列指定目的地的消息
/**
* 消费者
*/
@EnableJms
@Component
@Slf4j
public class AMQConsumer {
@Autowired
ApplicationContext ctx;
/**
* 消息处理
*
* @param message
*/
@JmsListener(containerFactory = "jmsListenerContainerFactory", destination = "${amq.destination}")
public void onMessage(Message message) {
log.error("========================进入消费者===========================>");
Map<String, Handler> handlers = ctx.getBeansOfType(Handler.class);
if (CollectionUtils.isEmpty(handlers)) {
log.debug(" No message handler,activemq message discard.");
return;
}
// 遍历所有的handlers,根据message类型匹配一个handler处理
for (Map.Entry<String, Handler> entry : handlers.entrySet()) {
Handler tHandler = entry.getValue();
if (tHandler == null) {
continue;
}
tHandler.handle(message);
}
}
}
② 消息处理接口,有多个实现类
/**
* 消息处理接口
*/
public interface Handler {
void handle(Message t) throws JMSException;
}
③ 消息处理具体业务方法(就是上文处理取消订单重新计算佣金消息的方法)
/**
* 消费处理退单计算佣金方法
*/
@Override
public void handle(Message t) throws JMSException {
//消息类型不为退单重新佣金计算退出
if (!HandlerConstants.MSG_BIZ_TYPE_COMMISION_RECALC.equals(t.getStringProperty(AMQConstants.BIZ_TYPE))) {
return;
}
log.error("===================>进入重新计算佣金消费者进行消费======");
//boss修改价格重新计算佣金
String changePriceOrderIdStr = t.getStringProperty("changePriceOrderId");
if(!Objects.isNull(changePriceOrderIdStr)){
log.error("===================>boos修改价格重新计算佣金======");
Long changePriceOrderId = t.getLongProperty("changePriceOrderId");
//改价
changePrice(changePriceOrderId);
}
}