1、定义队列对象
@Data
public class QueueProperties {
/**
* 死信交换器
*/
public static final String ARGUMENTS_X_DEAD_LETTER_EXCHANGE = "x-dead-letter-exchange";
/**
* 死信路由键
*/
public static final String ARGUMENTS_X_DEAD_LETTER_ROUTING_KEY = "x-dead-letter-routing-key";
/**
* 队列名称
*/
private String queueName;
/**
* 交换器名称
*/
private String exchangeName;
/**
* 队列路由key
*/
private String routingKeyName;
/**
* 死信队列名称
*/
private String deadQueueName;
/**
* 死信交换器
*/
private String deadExchangeName;
/**
* 死信路由key
*/
private String deadRoutingKeyName;
/**
* @param queueName 队列名称
* @param exchangeName 交换器名称
* @param routingKeyName 队列路由key
* @param deadQueueName 死信队列名称
* @param deadExchangeName 死信交换器
* @param deadRoutingKeyName 死信路由key
*/
public QueueProperties(String queueName, String exchangeName, String routingKeyName, String deadQueueName, String deadExchangeName, String deadRoutingKeyName) {
this.queueName = queueName;
this.exchangeName = exchangeName;
this.routingKeyName = routingKeyName;
this.deadQueueName = deadQueueName;
this.deadExchangeName = deadExchangeName;
this.deadRoutingKeyName = deadRoutingKeyName;
}
}
2、mq配置辅助类
@Slf4j
public abstract class AbstractConnectionFactoryConfig {
@Resource
protected FastJsonMessageConverter fastJsonMessageConverter;
/**
* 初始化RabbitMQ 连接池
*
* @param addresses MQ地址信息 ip:port,ip:port 支持集群配置
* @param username MQ指定的VH 用户名
* @param password MQ指定的VH 密码
* @param virtualHost MQ指定的VH
* @return ConnectionFactory 连接池工厂对象
*/
CachingConnectionFactory getConnectionFactory(String addresses, String username, String password, String virtualHost) {
log.info("创建RabbitMQ 连接, 时间:{}", new Object[]{new Date()});
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(addresses);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
connectionFactory.setPublisherConfirms(true);
return connectionFactory;
}
/**
* 申明队列
*
* @param rabbitAdmin AMQP administrative
* @param properties 队列属性
*/
void declareQueue(RabbitAdmin rabbitAdmin, QueueProperties properties) {
// 代码规范约束,只要定义了死信exchange,那么需要定义死信的全部信息
declareQueue(rabbitAdmin, properties, StringUtils.isNotEmpty(properties.getDeadExchangeName()));
}
/**
* 申明队列
*
* @param rabbitAdmin AMQP administrative
* @param properties 队列属性
* @param supportDeadMessage 是否支持死信
*/
private void declareQueue(RabbitAdmin rabbitAdmin, QueueProperties properties, boolean supportDeadMessage) {
Map<String, Object> arguments = new HashMap<>(2);
if (supportDeadMessage) {
arguments.put(QueueProperties.ARGUMENTS_X_DEAD_LETTER_EXCHANGE, properties.getDeadExchangeName());
arguments.put(QueueProperties.ARGUMENTS_X_DEAD_LETTER_ROUTING_KEY, properties.getDeadRoutingKeyName());
}
// 申明exchange
DirectExchange exchange = new DirectExchange(properties.getExchangeName(),true,false);
DirectExchange deadExchange = new DirectExchange(properties.getDeadExchangeName(),true,false);
// 申明队列
Queue queue = new Queue(properties.getQueueName(), true, false, false, arguments);
rabbitAdmin.declareExchange(exchange);
rabbitAdmin.declareQueue(queue);
rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(exchange).with(properties.getRoutingKeyName()));
// 死信队列
if (!supportDeadMessage) {
return;
}
Queue deadQueue = new Queue(properties.getDeadQueueName(), true, false, false, null);
rabbitAdmin.declareQueue(deadQueue);
rabbitAdmin.declareExchange(deadExchange);
rabbitAdmin.declareBinding(BindingBuilder.bind(deadQueue).to(deadExchange).with(properties.getDeadRoutingKeyName()));
}
}
3、把频繁的json序列化和反序列化的过程,封装到MessageConverter里面去处理
最开始的时候,producer都是直接convertAndSend的json数据,consumer也是接收json数据,然后在转化为Bean去处理逻辑【麻烦】
@Component("fastJsonMessageConverter")
@Scope("prototype")
public class FastJsonMessageConverter extends AbstractMessageConverter {
@Override
protected Message createMessage(Object objectToConvert, MessageProperties messageProperties) throws MessageConversionException {
String jsonString = JSON.toJSONString(objectToConvert);
byte[] bytes = jsonString.getBytes(StandardCharsets.UTF_8);
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
messageProperties.setContentEncoding(StandardCharsets.UTF_8.name());
messageProperties.setContentLength(bytes.length);
return new Message(bytes, messageProperties);
}
@Override
public Object fromMessage(Message message) throws MessageConversionException {
return null;
}
/**
* @param message MQ 消息
* @param <T> 消息类型
* @return MQ消息转换后的对象
*/
public <T> T fromMessage(Message message, Class<T> clazz) {
return JSON.parseObject(new String(message.getBody(), StandardCharsets.UTF_8), clazz);
}
}
4、MQ生产者配置类
@Configuration
public class MQConfig extends AbstractConnectionFactoryConfig {
@Resource
private RabbitMqProperties rabbitMqProperties;
/**
* @return ConnectionFactory 连接池工厂对象
*/
@Bean(name = "xxRabbitMQConnectionFactory")
public ConnectionFactory xxRabbitMQConnectionFactory() {
return getConnectionFactory(rabbitMqProperties.getAddresses(), rabbitMqProperties.getUsername(),
rabbitMqProperties.getPassword(), rabbitMqProperties.getVirtualHost());
}
/**
* 消息模板,用于发送消息
*
* @param connectionFactory 连接工厂
* @return rabbit 模板
*/
@Bean(name = "xxRabbitTemplate")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public RabbitTemplate rabbitTemplate(@Qualifier("xxRabbitMQConnectionFactory") ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(fastJsonMessageConverter);
return template;
}
/**
* 定义RabbitAdmin
*
* @param rabbitTemplate 消息模板
* @return rabbit admin
*/
@Bean(name = "xxRabbitAdmin")
public RabbitAdmin xxRabbitAdmin(@Qualifier("xxRabbitTemplate") RabbitTemplate rabbitTemplate) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate);
// 任务
QueueProperties taskQueue = new QueueProperties(MQQueueConstants.QueueName.taskQueueName,
MQQueueConstants.ExchangeName.taskExchangeName,
MQQueueConstants.RoutingKey.taskRoutingKey,
MQQueueConstants.QueueName.taskDeadQueueName,
MQQueueConstants.ExchangeName.taskDeadExchangeName,
MQQueueConstants.RoutingKey.taskDeadRoutingKey);
declareQueue(rabbitAdmin, taskQueue);
return rabbitAdmin;
}
}
5、MQ消费者配置类
@Configuration
@EnableRabbit
public class XxRabbitMQListenerConfig implements RabbitListenerConfigurer {
/**
*
*/
@Resource
private ConnectionFactory xxRabbitMQConnectionFactory;
@Resource
private RabbitMqProperties rabbitMqProperties;
/**
* RabbitMq 消费者监听配置
*
* @param registrar
*/
@Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(xxRabbitMQHandlerMethodFactory());
}
/**
* @return
*/
@Bean(name = "xxRabbitMQHandlerMethodFactory")
public DefaultMessageHandlerMethodFactory xxRabbitMQHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(new MappingJackson2MessageConverter());
return factory;
}
/**
* @return
*/
@Bean(name = "xxRabbitListerContainerFactory")
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(xxRabbitMQConnectionFactory);
factory.setPrefetchCount(rabbitMqProperties.getPrefetchCount());
factory.setConcurrentConsumers(rabbitMqProperties.getConcurrentConsumers());
factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
return factory;
}
}
6、生产者
public interface IMqProvider {
/**
* 消息转发
*
* @param exchange 消息交换器
* @param routingKey 路由key
* @param message 消息体
*/
void send(String exchange, String routingKey, Object message);
}
@Slf4j
@Component
public class TaskProvider implements IMqProvider {
@Resource(name = "xxRabbitTemplate")
private AmqpTemplate amqpTemplate;
/**
* 发送消息到相应的队列
*
* @param exchange 调度任务MQ exchange key
* @param routingKey 调度任务MQ routing key
* @param message 调度任务MQ message
*/
@Override
public void send(String exchange, String routingKey, Object message) {
log.info("信息内容:{}", new Object[]{JSON.toJSONString(message)});
amqpTemplate.convertAndSend(exchange, routingKey, message);
}
}
7、消费者
@Slf4j
@Component
public class TaskRabbitListener {
@Resource
private FastJsonMessageConverter fastJsonMessageConverter;
@Resource
private TaskHelper taskHelper;
@Resource
private Validator validator;
@RabbitListener(queues = MQQueueConstants.QueueName.taskQueueName, containerFactory = "xxRabbitListerContainerFactory", admin = "xxRabbitAdmin")
public void process(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
boolean isReject = true;
TaskMessage TaskMessage;
try {
taskMessage = fastJsonMessageConverter.fromMessage(message, TaskMessage.class);
log.info("content: {}", new Object[]{taskMessage.toString()});
Set<ConstraintViolation<TaskMessage>> violationSet = validator.validate(taskMessage);
if (CollectionUtils.isNotEmpty(violationSet)) {
log.error("数据校验不通过,任务编号:{}", taskMessage.getTaskNo());
for (ConstraintViolation<TaskMessage> violation : violationSet) {
log.error("任务编号:{},错误字段:{},错误信息:{}", new Object[]{taskMessage.getTaskNo(), violation.getPropertyPath(), violation.getMessage()});
}
throw new BusinessRuntimeException(ExceptionCode.System.PARAM_VALIDA_ERROR, "参数校验异常,具体查看日志文件.");
}
taskHelper.addTask(taskMessage);
channel.basicAck(deliveryTag, false);
isReject = false;
} catch (Exception e) {
log.error("处理任务异常:", e);
} finally {
if (isReject) {
channel.basicReject(deliveryTag, false);
}
}
}
}