zoukankan      html  css  js  c++  java
  • spring-boot-starter-amqp踩坑记

    踩坑记录

    近日在用spring boot架构一个微服务框架,服务发现与治理、发布REST接口各种轻松惬意。但是服务当设计MQ入口时,就发现遇到无数地雷,现在整理成下文,供各路大侠围观与嘲笑。

    版本

    当前使用的spring-boot-starter-amqp版本为2016.5发布的1.3.5.RELEASE

    也许若干年后,你们版本都不会有这些问题了。:(

    RabbitMQ

    当需要用到MQ的时候,我的第一反映就是使用RabbitMQ,猫了一眼spring boot的官方说明,上面说spring boot为rabbit准备了spring-boot-starter-amqp,并且为RabbitTemplate和RabbitMQ提供了自动配置选项。暗自窃喜~~

    瞅瞅[官方文档]http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-rabbitmq和例子,SO EASY,再看一眼GITHUB上的官方例了,也有例子。

    心情愉悦的照着例子,开干~~。

    踩坑

    十五分钟后的代码类似这样:

    @Service
    @RabbitListener(queues = "merchant")
    public class MQReceiver  {
        protected Logger logger = Logger.getLogger(MQReceiver.class
                .getName()); 
      
        @RabbitHandler
        public void process(@Payload UpdateMerchant request) {
            UpdateMerchantResponse response = new UpdateMerchantResponse();
            logger.info(request.getMerchantId() + "->" + response.getReturnCode());
        }
    }
    

    消费信息后,应该记录一条日志。
    结果得到只有org.springframework.amqp.AmqpException: No method found for class [B 这个异常,并且还无限循环抛出这个异常。。。

    记得刚才官方文档好像说了异常什么的,转身去猫一眼,果然有:

    If retries are not enabled and the listener throws an exception, by default the delivery will be retried indefinitely. You can modify this behavior in two ways; set the defaultRequeueRejected
     property to false
     and zero re-deliveries will be attempted; or, throw an AmqpRejectAndDontRequeueException
     to signal the message should be rejected. This is the mechanism used when retries are enabled and the maximum delivery attempts are reached.

    知道了为啥会无限重试了,下面来看看为啥会抛出这个异常,google搜一下,貌似还有一个倒霉鬼遇到了这个问题

    进去看完问题和大神的解答,豁然开朗。

    There are two conversions in the @RabbitListener pipeline.
    The first converts from a Spring AMQP Message to a spring-messaging Message.
    There is currently no way to change the first converter from SimpleMessageConverter which handles String, Serializable and passes everything else as byte[].
    The second converter converts the message payload to the method parameter type (if necessary).
    With method-level @RabbitListeners there is a tight binding between the handler and the method.
    With class-level @RabbitListener s, the message payload from the first conversion is used to select which method to invoke. Only then, is the argument conversion attempted.
    This mechanism works fine with Java Serializable objects since the payload has already been converted before the method is selected.
    However, with JSON, the first conversion returns a byte[] and hence we find no matching @RabbitHandler.
    We need a mechanism such that the first converter is settable so that the payload is converted early enough in the pipeline to select the appropriate handler method.
    A ContentTypeDelegatingMessageConverter is probably most appropriate.
    And, as stated in AMQP-574, we need to clearly document the conversion needs for a @RabbitListener, especially when using JSON or a custom conversion.

    得嘞,官方示例果然是坑,试试大神的解决方案,手动新增下转换。

      @Bean
        public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
            RabbitTemplate template = new RabbitTemplate(connectionFactory);
            template.setMessageConverter(new Jackson2JsonMessageConverter());
            return template;
        }
    
        @Bean
        public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setConnectionFactory(connectionFactory);
            factory.setMessageConverter(new Jackson2JsonMessageConverter());
            return factory;
        }
    

    然后在生产和消费信息的地方使用他们:

    @RabbitListener(queues = "merchant", containerFactory="rabbitListenerContainerFactory")
    public void process(@Payload UpdateMerchant request) { 
         UpdateMerchantResponse response = new UpdateMerchantResponse();
        logger.info(request.getMerchantId() + "->" + response.getReturnCode());
     }
    

    再来一次,果然可以了

    c.l.s.m.service.MQReceiver : 00000001->null

    总结

    看起来很简单,可是掉坑里面之后怎么也得折腾个几个小时才能爬出来,此文献给掉进同一个坑的童鞋,希望你能满意。

  • 相关阅读:
    .net序列化和反序列化(一)——自动序列化
    在Sql Server 2005使用公用表表达式CTE简化复杂的查询语句
    使用JQuery与iframe交互
    FCKeditor自定义工具栏和定义多个工具栏
    FCKeditor自定义非空验证
    PHP5.3.6的IIS配置
    Linux下缓存服务器的应用
    PHP采集程序中常用的函数
    关于PHP5.3.x和Zend Optimizer(Zend Guard Loader),以及shopex4.8.5安装的问题
    SQLserver数据库还原出现错误112(磁盘空间不足)的解决办法
  • 原文地址:https://www.cnblogs.com/lazio10000/p/5559999.html
Copyright © 2011-2022 走看看