基于springboot实现多消费者监听注解MultiJmsListener

前置说明

  • spring-boot-starter-activemq为例子来说明
  • springboot提供JmsListener用以支持对消息的监听。但有些时候为了提高消费效率,需要对同一个queue或者topic使用多个监听器进行消费。而通过JmsListener注解,只能支持单线程消费,如果要做多个消费者,需要多次使用JmsListener,代码如下:
/**
 * 单线程监听Queue
 * @param message
 */
@JmsListener(destination = "test.queue1")
public void consumer4(String message) {
    System.out.println("==queue消息:" + message);
}
/**
 * 单线程监听Queue
 * @param message
 */
@JmsListener(destination = "test.queue1")
public void consumer5(String message) {
    System.out.println("==queue消息:" + message);
}
  • 本文基于springboot-2.1.4自定义注解MultiJmsListener
  • 本文采用spring-boot-starter-activemq整合activeMQ

源码清单

MultiJmsListener注解

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@MessageMapping
public @interface MultiJmsListener {
    //用以定义监听器beanName的前缀(保留字段)
    String prefixId() default "";

    String destination();

    //定义监听线程的个数
    int num() default 1;

    //定义消息的ContainerFactory,实际值参考ActiveMQConstant
    String containerFactory() default "";
}

传统消息监听器

public class ActiveMQMessageListener implements MessageListener {
    private final Logger logger = LoggerFactory.getLogger(ActiveMQMessageListener.class);

    //消息监听ID
    private int id;

    //执行方法
    private Method executeMethod;

    //执行类
    private Object executeObject;

    //监听目标
    private String dest;

    private Class executeClass;

	/** 有参构造器 */
    public ActiveMQMessageListener(Class executeClass, Method executeMethod, int id, String dest) {
        this.executeMethod = executeMethod;
        this.dest = dest;
        this.id = id;
        init(executeClass);
        this.executeClass = executeClass;
    }

    /**
     * 初始化操作
     * @param executeClass
     */
    private void init(Class executeClass) {
        try {
            this.executeObject = executeClass.newInstance();
        } catch (Exception e) {
            throw new JmsException("方法反射异常:", e);
        }
    }

    public ActiveMQMessageListener() {}

    @Override
    public void onMessage(Message message) {
        if (logger.isDebugEnabled()) {
            logger.debug("监听器[{}]监听到消息", id);
        }
        TextMessage textMessage = (TextMessage) message;
        try {
            //反射的方式调用方法,用以执行实际消费流程
            executeMethod.invoke(executeObject, textMessage.getText());
        } catch (Exception e) {
            if (logger.isErrorEnabled()) {
                logger.error("消息监听异常:", e);
            }
            throw new JmsException("消息监听异常:", e);
        }
    }
 //省略掉get/set方法
 }

自动配置

  • 自动配置类,引入JmsListenerContainerFactory用以同时支持QueueTopic
/**
 * 配置topic模式下的JmsListenerContainerFactory
 * @param activeMQConnectionFactory
 * @return
 */
@Bean(name = ActiveMQConsumerConstant.CONTAINER_FACTORY_TOPIC)
public JmsListenerContainerFactory jmsListenerContainerTopic(ConnectionFactory activeMQConnectionFactory) {
    DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
    //支持Topic模式
    bean.setPubSubDomain(true);
    bean.setConnectionFactory(activeMQConnectionFactory);
    FrameworkAutoConfigureManager.addAutoConfigureBeanMap("jmsListenerContainerTopic", JmsListenerContainerFactory.class);
    return bean;
}
/**
 * 配置queue模式下的JmsListenerContainerFactory
 * @param activeMQConnectionFactory
 * @return
 */
@Bean(name = ActiveMQConsumerConstant.CONTAINER_FACTORY_QUEUE)
public JmsListenerContainerFactory jmsListenerContainerQueue(ConnectionFactory activeMQConnectionFactory) {
    DefaultJmsListenerContainerFactory bean = new DefaultJmsListenerContainerFactory();
    bean.setConnectionFactory(activeMQConnectionFactory);
    FrameworkAutoConfigureManager.addAutoConfigureBeanMap("jmsListenerContainerQueue", JmsListenerContainerFactory.class);
    return bean;
}

/**
 * 用以支持MultiJmsListener注解的后置处理器
 * @return
 */
@Bean
public MultiJmsListenerAnnotationBeanPostProcessor multiJmsListenerAnnotationBeanPostProcessor() {
    MultiJmsListenerAnnotationBeanPostProcessor multiJmsBeanPostProcessor =
            new MultiJmsListenerAnnotationBeanPostProcessor();
    FrameworkAutoConfigureManager.addAutoConfigureBeanMap("multiJmsListenerAnnotationBeanPostProcessor", MultiJmsListenerAnnotationBeanPostProcessor.class);
    return multiJmsBeanPostProcessor;
}

常量类

  • 定义ActiveMQConsumerConstant常量,用以配置ContainerFactory对应的BeanName
public interface ActiveMQConsumerConstant {
    /** Queue模式下的JmsListenerContainerFactory的Bean名称 */
    String CONTAINER_FACTORY_QUEUE = "jmsListenerContainerQueue";

    /** Topic模式下的JmsListenerContainerFactory的Bean名称 */
    String CONTAINER_FACTORY_TOPIC = "jmsListenerContainerTopic";
}

后置处理器

  • 定义一个Bean后置处理器处理MultiJmsListener对应的方法
public class MultiJmsListenerAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, BeanFactoryAware, SmartInitializingSingleton {

    //用以缓存方法中没有MultiJmsListener注解的
    private final Set> nonAnnotatedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64));

    //JMS 注册机
    private JmsListenerEndpointRegistrar jmsListenerEndpointRegistrar = new JmsListenerEndpointRegistrar();

    private BeanFactory beanFactory;

    @Autowired
    public void setEndpointRegistry(JmsListenerEndpointRegistry endpointRegistry) {
        jmsListenerEndpointRegistrar.setEndpointRegistry(endpointRegistry);
    }

    @Autowired
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        jmsListenerEndpointRegistrar.setBeanFactory(beanFactory);
    }


    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //特殊bean过滤
        if((bean instanceof AopInfrastructureBean)
                || (bean instanceof JmsListenerContainerFactory)
                || (bean instanceof JmsListenerEndpointRegistry)) {
            return bean;
        }

        //获取bean对象的最终类型
        Class targetClass = AopProxyUtils.ultimateTargetClass(bean);
        if (this.nonAnnotatedClasses.contains(targetClass)) {
            return bean;
        }

        //选出该bean中包含MultiJmsListener注解的方法
        Map annotatedMethods = MethodIntrospector.selectMethods(targetClass, new MethodIntrospector.MetadataLookup () {
            @Override
            public MultiJmsListener inspect(Method method) {
                return AnnotationUtils.getAnnotation(method, MultiJmsListener.class);
            }
        });

        // 若当前类中未找到含该注解的方法,直接return bean
        if (annotatedMethods.isEmpty()) {
            this.nonAnnotatedClasses.add(targetClass);
            return bean;
        }

        //对每个方法做处理,添加监听
        annotatedMethods.forEach((method, multiJmsListener) -> processMultiListener(multiJmsListener, method, targetClass));

        //返回bean对象
        return bean;
    }

    /*
     * 提交监听器到进程
     * @param multiJmsListener 注解
     * @param method 方法
     * @param targetClass bean对应的class类
     * @param 
     */
    private  void processMultiListener(MultiJmsListener multiJmsListener, Method method, Class targetClass) {
        //线程数
        int threadNum = multiJmsListener.num();
        if (threadNum <= 0) {
            numthreadNum= 1;
        }

        //for循环
        for (int i = 1; i <= threadNum; i ++) {
            //多少个线程数,开启多少个监听
            ActiveMQMessageListener activeMQMessageListener = new ActiveMQMessageListener<>(targetClass, method, i, multiJmsListener.destination());
            //将监听器注册到JMSEndpointRegistrar
            register(multiJmsListener, activeMQMessageListener);
        }
    }

    /*
     * 向JMSEndpointRegistrar注册Endpoint
     * @param activeMQMessageListener 消息监听器
     */
    private void register(MultiJmsListener multiJmsListener, ActiveMQMessageListener activeMQMessageListener) {
        SimpleJmsListenerEndpoint simpleJmsListenerEndpoint = new SimpleJmsListenerEndpoint();
        //endpointId = 类名+方法名+id
        simpleJmsListenerEndpoint.setId(buildEndpointId(activeMQMessageListener));
        simpleJmsListenerEndpoint.setDestination(activeMQMessageListener.getDest());
        simpleJmsListenerEndpoint.setMessageListener(activeMQMessageListener);
        jmsListenerEndpointRegistrar.setBeanFactory(this.beanFactory);

        //不同的模式,引用的containerFactory不同
        String containFactory = multiJmsListener.containerFactory();
        if (StringUtils.isBlank(containFactory)) {
            //默认使用Queue
            containFactory = ActiveMQConsumerConstant.CONTAINER_FACTORY_QUEUE;
        }
        JmsListenerContainerFactory factory = (JmsListenerContainerFactory)this.beanFactory.getBean(containFactory, JmsListenerContainerFactory.class);
        jmsListenerEndpointRegistrar.setContainerFactoryBeanName(ActiveMQConsumerConstant.CONTAINER_FACTORY_QUEUE);
        jmsListenerEndpointRegistrar.registerEndpoint(simpleJmsListenerEndpoint, factory);

    }

    /*
     * 生成端点Endpoint的ID
     * @param activeMQMessageListener
     * @return
     */
    private String buildEndpointId(ActiveMQMessageListener activeMQMessageListener) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(activeMQMessageListener.getExecuteClass().getName())
                .append(MarkConstant.DOT)
                .append(activeMQMessageListener.getExecuteMethod().getName())
                .append(activeMQMessageListener.getId());
        return stringBuilder.toString();
    }

    /**
     * 在类初始化后,执行后续操作
     */
    @Override
    public void afterSingletonsInstantiated() {
        //执行registrar的后置操作
        jmsListenerEndpointRegistrar.afterPropertiesSet();
    }

    @Override
    public int getOrder() {
        return Integer.MAX_VALUE;
    }
}

测试

注解示例代码

@MultiJmsListener(destination = "test.queue0", num = 4)
public void consumer(String message) {
    System.out.println("==============================");
    System.out.println(message);
    System.out.println("==============================");
}

输出结果

  • 输出片段示例
测试queue
==============================
id=2
==============================
测试queue
==============================
id=1
==============================
测试queue
==============================
id=3
==============================
测试queue
==============================
  • 说明
    • id不同表示监听线程不同

总结

  • 方法层级的注解实现,常见的有两个方式,一种是直接通过AOP切面去实现,还有一种是通过BeanPostProcessor,在Bean初始化后,做后置处理。

你可能感兴趣的:(Springboot扩展,多消费者,ActiveMQ,多消费者监听)