Spring boot integrates rabbitmq

Posted by animedls on Wed, 22 Apr 2020 16:33:46 +0200

Before integration, rabbitMqJAR package should be introduced in springboot. The version number can be customized for you. This project follows the version of springboot

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

Then start to build configuration items, and add rabbitMQ configuration in application.properties of springboot project

# rabbitMQ configuration item
# rabbitmq access domain name
spring.rabbitmq.host=127.0.0.1
# rabbitmq port number
spring.rabbitmq.port=5672
# rabbitMq account
spring.rabbitmq.username=
# rabbitMq password
spring.rabbitmq.password=
# Turn on confirm callback p - > exchange
spring.rabbitmq.publisher-confirms=true
#Enable returnedMessage callback exchange - > queue
spring.rabbitmq.publisher-returns=true
#Set ack queue - > C
spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.prefetch=100
spring.rabbitmq.template.mandatory=true
#Open consumer retry
spring.rabbitmq.listener.simple.retry.enabled=true
#Maximum number of retries (if 5 retries are not enough, the message will be deleted. By default, there is no limit to the number of retries. It is recommended to control the number of retries within 10)
spring.rabbitmq.listener.simple.retry.max-attempts=5
#Retry interval
spring.rabbitmq.listener.simple.retry.initial-interval=3000
spring.rabbitmq.virtual-host=/

Then build rabbitMQ configuration RabbitMQConfig

@Configuration
public class RabbitMQConfig {

    private Logger logger = LoggerFactory.getLogger(RabbitMQConfig.class);

    @Autowired
    private CachingConnectionFactory connectionFactory;

    /**
     * Accept data automatically converted to Json
     */
    @Bean("messageConverter")
    public MessageConverter messageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    @Bean("rabbitTemplate")
    public RabbitTemplate rabbitTemplate() {
        final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(messageConverter());

        connectionFactory.setPublisherConfirms(true);
        connectionFactory.setPublisherReturns(true);
        rabbitTemplate.setMandatory(true);
        rabbitTemplate.setMessageConverter(messageConverter());
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                if(!ack) {
                    logger.info("Message sending failed:correlationData({}),ack({}),cause({})", correlationData, ack, cause);
                }
            }
        });
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange,
                    String routingKey) {
                logger.info("Message lost:exchange({}),route({}),replyCode({}),replyText({}),message:{}", exchange, routingKey,
                        replyCode, replyText, message);
            }
        });
        return rabbitTemplate;
    }

    @Bean("rabbitAdmin")
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        System.err.println("RabbitAdmin Started...");
        // Set to load this class automatically when starting the spring container (this parameter is now true by default, which can be ignored)
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }

}

Then define the initialization listener method MQListenerConfig

@Configuration
public class MQListenerConfig {

    @Bean
    public MessageListenerConfig messageListenerConfig(RabbitAdmin admin,
            CachingConnectionFactory rabbitConnectionFactory)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
        MessageListenerConfig messageListenerConfig = new MessageListenerConfig();
        messageListenerConfig.init(admin, rabbitConnectionFactory);
        return messageListenerConfig;
    }
}

Initializing the listening method to obtain the queue and listener of consumers in the form of annotation

@Component
public class MessageListenerConfig {

    public void init(RabbitAdmin admin, CachingConnectionFactory rabbitConnectionFactory)
            throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException {

        Map<String, AbstractConsumer> map = SpringUtil.getBeansOfType(AbstractConsumer.class);//Query subclass under AbstractConsumer parent
        List<AbstractConsumer> abstractConsumerList = new ArrayList<AbstractConsumer>(map.values());//Convert the above subclass to List collection
        SendMQService sendMQService = SpringUtil.getBean(RabbitServiceImpl.class);//Get rabbitMqService interface
        this.init(abstractConsumerList, 0, admin, rabbitConnectionFactory,sendMQService);//Initialization parameters
    }

    private void init(List<AbstractConsumer> clazzList, int index, RabbitAdmin admin,
            CachingConnectionFactory rabbitConnectionFactory,SendMQService sendMQService) {

        if (EmptyUtils.isEmpty(clazzList) || clazzList.size() <= index) {
            return;
        }

        AbstractConsumer abstractConsumer = clazzList.get(index);

        RabbitMq rabbitMq = abstractConsumer.getClass().getAnnotation(RabbitMq.class);// Obtain rabbitMQ annotation information according to reflection

        if (rabbitMq == null) {
            this.init(clazzList, index + 1, admin, rabbitConnectionFactory,sendMQService);
        }

        String queueString = rabbitMq.queues(); // queue
        String routingKeyString = rabbitMq.routingKey(); // exchanger
        String exchangeString = rabbitMq.exchange(); // Routing rules
        int count = rabbitMq.consumersPerQueue(); // Number of consumers per queue

        DirectMessageListenerContainer container = new DirectMessageListenerContainer(rabbitConnectionFactory);
        Queue queue = new Queue(queueString);// Declaration queue
        admin.declareQueue(queue);// Initialize queue

        if (EmptyUtils.isNotEmpty(exchangeString) && EmptyUtils.isNotEmpty(routingKeyString)) {
            AbstractMQService mqService = (AbstractMQService) SpringUtil.getBean(rabbitMq.exchangeTypes() + AbstractMQService.SERVICE_NAME);
            AbstractExchange exchange = mqService.initExchange(exchangeString);
            admin.declareExchange(exchange);

            Binding binding = mqService.initBinding(queue, exchange, routingKeyString);// Initialize data from different queues
            admin.declareBinding(binding);
        }

        MessageListenerAdapter adapter = new MessageListenerAdapter(abstractConsumer);
        adapter.setEncoding("utf-8");
        container.setConsumersPerQueue(rabbitMq.consumersPerQueue());
        container.setQueues(queue);// Listener configuration queue
        container.setMessageListener(adapter);
        container.setAutoDeclare(true);
        container.setAcknowledgeMode(rabbitMq.mode());
        container.setConsumersPerQueue(count);
        // Start the corresponding adapter
        container.start();
        sendMQService.addContainer(queueString, container);
        this.init(clazzList, index + 1, admin, rabbitConnectionFactory,sendMQService);
    }
}

Initial switch and binding relationship interface

public interface AbstractMQService {

    static final String SERVICE_NAME = "MQService";

    /**
     * Initialize switch
     * @return
     */
    public AbstractExchange initExchange(String exchangeName);

    /**
     * Initialize binding relationship
     * @param routeKey
     * @return
     */
    public Binding initBinding(Queue queue,AbstractExchange exchange,String routeKey);
}

The initial switch and binding relationship implementation classes are DirectMQServiceImpl, FanoutMQServiceImpl and TopicMQServiceImpl, respectively

@Service("directMQService")
public class DirectMQServiceImpl implements AbstractMQService {

    @Override
    public AbstractExchange initExchange(String exChangeName) {
        DirectExchange exchange = new DirectExchange(exChangeName);
        return exchange;
    }

    @Override
    public Binding initBinding(Queue queue, AbstractExchange exChange, String routeKey) {
        DirectExchange exchange = (DirectExchange) exChange;
        DestinationConfigurer bindConfigurer = BindingBuilder.bind(queue);
        DirectExchangeRoutingKeyConfigurer routKeyConfigurer = bindConfigurer.to(exchange);
        return routKeyConfigurer.with(routeKey);
    }

}
@Service("fanoutMQService")
public class FanoutMQServiceImpl implements AbstractMQService {

    @Override
    public AbstractExchange initExchange(String exChangeName) {
        FanoutExchange exchange = new FanoutExchange(exChangeName);
        return exchange;
    }

    @Override
    public Binding initBinding(Queue queue, AbstractExchange exChange, String routeKey) {
        FanoutExchange exchange = (FanoutExchange) exChange;
        DestinationConfigurer bindConfigurer = BindingBuilder.bind(queue);
        Binding binding = bindConfigurer.to(exchange);
        return binding;
    }

}
@Service("topicMQService")
public class TopicMQServiceImpl implements AbstractMQService {

    @Override
    public AbstractExchange initExchange(String exChangeName) {
        TopicExchange exchange = new TopicExchange(exChangeName);
        return exchange;
    }

    @Override
    public Binding initBinding(Queue queue, AbstractExchange exChange, String routeKey) {
        TopicExchange exchange = (TopicExchange) exChange;
        DestinationConfigurer bindConfigurer = BindingBuilder.bind(queue);
        TopicExchangeRoutingKeyConfigurer routKeyConfigurer = bindConfigurer.to(exchange);
        return routKeyConfigurer.with(routeKey);
    }

}

Custom annotation

@Target(value = { ElementType.FIELD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface RabbitMq {

    /**
     * queue
     * 
     * @return
     */
    public String queues() default "";

    /**
     * exchanger
     * 
     * @return
     */
    public String exchange() default "";

    /**
     * Routing rules
     * 
     * @return
     */
    public String routingKey() default "";

    /**
     * Persistence or not
     * 
     * @return
     */
    public boolean isPersistence() default true;

    /**
     * Confirmation mode
     * 
     * @return
     */
    public AcknowledgeMode mode() default AcknowledgeMode.MANUAL;

    /**
     * Number of consumers per queue
     * 
     * @return
     */
    public int consumersPerQueue() default 1;

    /**
     * Exchange type
     * 
     * @return
     */
    public String exchangeTypes() default ExchangeTypes.DIRECT;
}

User defined consumer AbstractConsumer, which is used for general purpose. Each additional consumer only needs to inherit and then process the business logic

public abstract class AbstractConsumer extends MessagingMessageListenerAdapter {

    protected static final String MQ_CORRELATIONDATA_KEY = "spring_returned_message_correlation";

    public static final String MQ_CACHE_MQ_KEY = "rabbitMQ.queues:";

    public static final Integer FAIL_MAX_COUNT = 5;

    private RedisService redisService = SpringUtil.getBean(RedisService.class);

    @Override
    public void onMessage(Message message, Channel channel) throws IOException {
        MessageProperties messageProperties = message.getMessageProperties();
        long deliveryTag = messageProperties.getDeliveryTag();

        String correlationId = (String) message.getMessageProperties().getHeaders().get(MQ_CORRELATIONDATA_KEY);
        String queues = messageProperties.getConsumerQueue();
        String cacheKey = new StringBuilder().append(MQ_CACHE_MQ_KEY).append(queues).append(":").append(correlationId).toString();
        Integer failCount = (Integer)redisService.get(cacheKey);
        try {
            this.handleMessage(new String(message.getBody(), "UTF-8"));
            channel.basicAck(deliveryTag, false);

            redisService.del(new StringBuilder().append(correlationId).toString());
        } catch (Exception e) {
            if(failCount > FAIL_MAX_COUNT) {
                return;
            }
            redisService.incr(cacheKey, 1, new Long(CacheTime.CACHE_EXP_THIRTY_SECONDS));
            channel.basicNack(deliveryTag, false, false);
        }
    }

    public abstract void handleMessage(String message);

}

What's not perfect? Please give me more advice! For the first time

Topics: Java RabbitMQ Spring SpringBoot JSON