SpringBoot与RabbitMQ整合

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);
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值