From 4250eff7859ee908edb04b0b9f5d91fd80c25d81 Mon Sep 17 00:00:00 2001 From: 王彪总 Date: Tue, 1 Jul 2025 22:45:29 +0800 Subject: [PATCH] 1.master1.1.1 --- .classpath | 26 ++++++++++++++++++++++++++ .project | 11 +++++++++++ pom.xml | 10 +++++----- src/main/java/org/apache/rocketmq/spring/starter/AliyunRocketMQAutoConfiguration.java | 236 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/main/java/org/apache/rocketmq/spring/starter/RocketMQProperties.java | 86 -------------------------------------------------------------------------------------- src/main/java/org/apache/rocketmq/spring/starter/annotation/RocketMQMessageListener.java | 77 ----------------------------------------------------------------------------- src/main/java/org/apache/rocketmq/spring/starter/core/AliyunRocketMQListenerContainer.java | 440 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/main/java/org/apache/rocketmq/spring/starter/core/AliyunRocketMQPushConsumerLifecycleListener.java | 23 ----------------------- src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java | 355 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java | 52 ---------------------------------------------------- src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java | 22 ---------------------- src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListener.java | 27 --------------------------- src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListenerContainer.java | 29 ----------------------------- src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQTemplate.java | 302 -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/main/java/org/apache/rocketmq/spring/starter/enums/ConsumeMode.java | 35 ----------------------------------- src/main/java/org/apache/rocketmq/spring/starter/enums/SelectorType.java | 34 ---------------------------------- src/main/java/org/apache/rocketmq/spring/starter/exception/ConvertMsgException.java | 27 --------------------------- src/main/java/org/apache/rocketmq/spring/starter/msgvo/ConsumeFailedMsgVO.java | 149 ----------------------------------------------------------------------------------------------------------------------------------------------------- src/main/java/org/apache/rocketmq/spring/starter/utils/ExceptionUtil.java | 21 --------------------- src/main/java/org/apache/rocketmq/spring/starter/utils/IPUtil.java | 49 ------------------------------------------------- src/main/java/zteits/rocketmq/spring/starter/AliyunRocketMQAutoConfiguration.java | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/RocketMQProperties.java | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/annotation/RocketMQMessageListener.java | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/core/AliyunRocketMQListenerContainer.java | 441 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/core/AliyunRocketMQPushConsumerLifecycleListener.java | 23 +++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java | 355 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java | 22 ++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/core/RocketMQListener.java | 27 +++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/core/RocketMQListenerContainer.java | 29 +++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/core/RocketMQTemplate.java | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/enums/ConsumeMode.java | 35 +++++++++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/enums/SelectorType.java | 34 ++++++++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/exception/ConvertMsgException.java | 27 +++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/msgvo/ConsumeFailedMsgVO.java | 149 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/utils/ExceptionUtil.java | 21 +++++++++++++++++++++ src/main/java/zteits/rocketmq/spring/starter/utils/IPUtil.java | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main/resources/META-INF/spring.factories | 2 +- src/test/java/org/apache/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java | 184 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- src/test/java/zteits/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 40 files changed, 2219 insertions(+), 2154 deletions(-) delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/AliyunRocketMQAutoConfiguration.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/RocketMQProperties.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/annotation/RocketMQMessageListener.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/core/AliyunRocketMQListenerContainer.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/core/AliyunRocketMQPushConsumerLifecycleListener.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListener.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListenerContainer.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQTemplate.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/enums/ConsumeMode.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/enums/SelectorType.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/exception/ConvertMsgException.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/msgvo/ConsumeFailedMsgVO.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/utils/ExceptionUtil.java delete mode 100644 src/main/java/org/apache/rocketmq/spring/starter/utils/IPUtil.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/AliyunRocketMQAutoConfiguration.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/RocketMQProperties.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/annotation/RocketMQMessageListener.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/core/AliyunRocketMQListenerContainer.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/core/AliyunRocketMQPushConsumerLifecycleListener.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/core/RocketMQListener.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/core/RocketMQListenerContainer.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/core/RocketMQTemplate.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/enums/ConsumeMode.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/enums/SelectorType.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/exception/ConvertMsgException.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/msgvo/ConsumeFailedMsgVO.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/utils/ExceptionUtil.java create mode 100644 src/main/java/zteits/rocketmq/spring/starter/utils/IPUtil.java delete mode 100644 src/test/java/org/apache/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java create mode 100644 src/test/java/zteits/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java diff --git a/.classpath b/.classpath index 6d7587a..a97cb0e 100644 --- a/.classpath +++ b/.classpath @@ -9,12 +9,14 @@ + + @@ -27,5 +29,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.project b/.project index f84930e..4e30ca7 100644 --- a/.project +++ b/.project @@ -20,4 +20,15 @@ org.eclipse.jdt.core.javanature org.eclipse.m2e.core.maven2Nature + + + 1751124793989 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/pom.xml b/pom.xml index 6ce8363..6a298b0 100644 --- a/pom.xml +++ b/pom.xml @@ -20,9 +20,9 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.apache.rocketmq + com.zteits.rocketmq spring-boot-starter-rocketmq - 1.0.8-SNAPSHOT + 1.1.1 Spring Boot Rocket Starter Starter for messaging using Apache RocketMQ @@ -107,12 +107,12 @@ nexus_releases core Release Repository - http://maven.renniting.cn/repository/maven-releases/ + https://maven2.renniting.cn/repository/maven-releases/ nexus_snapshots core Snapshots Repository - http://maven.renniting.cn/repository/maven-snapshots/ + https://maven2.renniting.cn/repository/maven-snapshots/ @@ -169,4 +169,4 @@ - \ No newline at end of file + diff --git a/src/main/java/org/apache/rocketmq/spring/starter/AliyunRocketMQAutoConfiguration.java b/src/main/java/org/apache/rocketmq/spring/starter/AliyunRocketMQAutoConfiguration.java deleted file mode 100644 index 2d65d3d..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/AliyunRocketMQAutoConfiguration.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter; - -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.METHOD_DESTROY; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_CONSUMER_GROUP; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_CONSUME_MODE; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_CONSUME_THREAD_MAX; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_MESSAGE_MODEL; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_OBJECT_MAPPER; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_ROCKETMQ_LISTENER; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_ROCKETMQ_TEMPLATE; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_SELECTOR_EXPRESS; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_SELECTOR_TYPE; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.*; - -import java.util.Map; -import java.util.Objects; -import java.util.Properties; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicLong; - -import javax.annotation.Resource; - -import com.aliyun.openservices.shade.com.alibaba.fastjson.JSON; -import org.apache.rocketmq.spring.starter.annotation.RocketMQMessageListener; -import org.apache.rocketmq.spring.starter.core.AliyunRocketMQListenerContainer; -import org.apache.rocketmq.spring.starter.core.RocketMQListener; -import org.apache.rocketmq.spring.starter.core.RocketMQTemplate; -import org.json.JSONObject; -import org.springframework.aop.support.AopUtils; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.annotation.Order; -import org.springframework.core.env.StandardEnvironment; -import org.springframework.util.Assert; - -import com.aliyun.openservices.ons.api.ONSFactory; -import com.aliyun.openservices.ons.api.Producer; -import com.aliyun.openservices.ons.api.PropertyKeyConst; -import com.fasterxml.jackson.databind.ObjectMapper; - -import lombok.extern.slf4j.Slf4j; - -@Configuration -@EnableConfigurationProperties(RocketMQProperties.class) -@Order -@Slf4j -public class AliyunRocketMQAutoConfiguration { - - @Bean - @ConditionalOnClass(RocketMQProperties.Producer.class) - @ConditionalOnMissingBean(RocketMQProperties.Producer.class) - @ConditionalOnProperty(prefix = "spring.rocketmq", value = {"environmentPrefix", "producer.group"}) - public Producer mqProducer(RocketMQProperties rocketMQProperties) { - - RocketMQProperties.Producer producerConfig = rocketMQProperties.getProducer(); - String groupName = producerConfig.getGroup(); - Assert.hasText(groupName, "[spring.rocketmq.producer.group] must not be null"); - String accessKey = rocketMQProperties.getAccessKey(); - Assert.hasText(accessKey, "[spring.rocketmq.accessKey] must not be null"); - String secretKey = rocketMQProperties.getSecretKey(); - Assert.hasText(secretKey, "[spring.rocketmq.secretKey] must not be null"); - String onsAddr = rocketMQProperties.getOnsAddr(); - Assert.hasText(secretKey, "[spring.rocketmq.onsAddr] must not be null"); - String environmentPrefix = rocketMQProperties.getEnvironmentPrefix(); - Assert.hasText(secretKey, "[spring.rocketmq.environmentPrefix] must not be null"); - - Properties producerProperties = new Properties(); - //生成者ProducerId添加前缀:PID_+环境标识_+groupName - String pid = "PID_"+environmentPrefix+"_"+groupName; - log.info("注册生产者PID:"+pid); - producerProperties.setProperty(PropertyKeyConst.ProducerId, pid); - producerProperties.setProperty(PropertyKeyConst.AccessKey, accessKey); - producerProperties.setProperty(PropertyKeyConst.SecretKey, secretKey); - producerProperties.setProperty(PropertyKeyConst.NAMESRV_ADDR, onsAddr); - log.info("注册生产者producerProperties:"+ JSON.toJSONString(producerProperties)); - //producerProperties.setProperty(PropertyKeyConst.ONSAddr, onsAddr); - Producer producer = ONSFactory.createProducer(producerProperties); - log.info("注册生产者完成:"+ JSON.toJSONString(producer)); - return producer; - } - - @Bean - @ConditionalOnClass(ObjectMapper.class) - @ConditionalOnMissingBean(name = "rocketMQMessageObjectMapper") - public ObjectMapper rocketMQMessageObjectMapper() { - return new ObjectMapper(); - } - - @Bean(destroyMethod = "destroy") - @ConditionalOnBean(Producer.class) - @ConditionalOnMissingBean(name = "rocketMQTemplate") - public RocketMQTemplate rocketMQTemplate(Producer mqProducer,RocketMQProperties rocketMQProperties, - @Autowired(required = false) - @Qualifier("rocketMQMessageObjectMapper") - ObjectMapper objectMapper) { - RocketMQTemplate rocketMQTemplate = new RocketMQTemplate(); - rocketMQTemplate.setAliyunProducer(mqProducer); - rocketMQTemplate.setEnvironmentPrefix(rocketMQProperties.getEnvironmentPrefix()); - if (Objects.nonNull(objectMapper)) { - rocketMQTemplate.setObjectMapper(objectMapper); - } - return rocketMQTemplate; - } - - @Configuration - @EnableConfigurationProperties(RocketMQProperties.class) - @ConditionalOnProperty(prefix = "spring.rocketmq", value = {"environmentPrefix", "producer.group"}) - @Order - public static class ListenerContainerConfiguration implements ApplicationContextAware, InitializingBean { - private ConfigurableApplicationContext applicationContext; - - private AtomicLong counter = new AtomicLong(0); - - @Resource - private StandardEnvironment environment; - - @Resource - private RocketMQProperties rocketMQProperties; - - private ObjectMapper objectMapper; - - @Autowired - private RocketMQTemplate rocketMQTemplate; - - public ListenerContainerConfiguration() { - } - - @Autowired(required = false) - public ListenerContainerConfiguration( - @Qualifier("rocketMQMessageObjectMapper") ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = (ConfigurableApplicationContext) applicationContext; - } - - @Override - public void afterPropertiesSet() { - Map beans = this.applicationContext.getBeansWithAnnotation(RocketMQMessageListener.class); - - if (Objects.nonNull(beans)) { - beans.forEach(this::registerContainer); - } - } - - private void registerContainer(String beanName, Object bean) { - String uuid = UUID.randomUUID().toString(); - log.info(uuid+"开始注册消费者,beanName:"+beanName); - log.info(uuid+"开始注册消费者,rocketMQProperties:"+JSON.toJSONString(rocketMQProperties)); - - Class clazz = AopUtils.getTargetClass(bean); - - if (!RocketMQListener.class.isAssignableFrom(bean.getClass())) { - throw new IllegalStateException(clazz + " is not instance of " + RocketMQListener.class.getName()); - } - RocketMQListener rocketMQListener = (RocketMQListener) bean; - RocketMQMessageListener annotation = clazz.getAnnotation(RocketMQMessageListener.class); - BeanDefinitionBuilder beanBuilder = BeanDefinitionBuilder.rootBeanDefinition(AliyunRocketMQListenerContainer.class); - // beanBuilder.addPropertyValue(PropertyKeyConst.NAMESRV_ADDR, rocketMQProperties.getOnsAddr()); - beanBuilder.addPropertyValue(PROP_NAMESRV_ADDR, rocketMQProperties.getOnsAddr()); - String topic = rocketMQProperties.getEnvironmentPrefix()+"_"+environment.resolvePlaceholders(annotation.topic()); - log.info(uuid+"订阅的主题topic:"+topic); - beanBuilder.addPropertyValue(PROP_TOPIC, topic); - String cid = "GID_"+rocketMQProperties.getEnvironmentPrefix()+"_"+environment.resolvePlaceholders(annotation.consumerGroup()); - log.info(uuid+"消费者CID:"+cid); - //消费者ConsumerId添加前缀:PID_+环境标识_+groupName - beanBuilder.addPropertyValue(PROP_CONSUMER_GROUP, cid); - beanBuilder.addPropertyValue(PROP_CONSUME_MODE, annotation.consumeMode()); - beanBuilder.addPropertyValue(PROP_CONSUME_THREAD_MAX, annotation.consumeThreadMax()); - beanBuilder.addPropertyValue(PROP_MESSAGE_MODEL, annotation.messageModel()); - beanBuilder.addPropertyValue(PROP_SELECTOR_EXPRESS, environment.resolvePlaceholders(annotation.selectorExpress())); - beanBuilder.addPropertyValue(PROP_SELECTOR_TYPE, annotation.selectorType()); - beanBuilder.addPropertyValue(PROP_ROCKETMQ_LISTENER, rocketMQListener); - beanBuilder.addPropertyValue(PROP_ROCKETMQ_TEMPLATE, rocketMQTemplate); - beanBuilder.addPropertyValue(PROP_ENVIRONMENT_PREFIX, rocketMQProperties.getEnvironmentPrefix()); - if (Objects.nonNull(objectMapper)) { - beanBuilder.addPropertyValue(PROP_OBJECT_MAPPER, objectMapper); - } - beanBuilder.setDestroyMethodName(METHOD_DESTROY); - //增加阿里云key - beanBuilder.addPropertyValue(PROP_ACCESS_KEY, rocketMQProperties.getAccessKey()); - beanBuilder.addPropertyValue(PROP_SECRET_KEY, rocketMQProperties.getSecretKey()); - - String containerBeanName = String.format("%s_%s", AliyunRocketMQListenerContainer.class.getName(), counter.incrementAndGet()); - log.info("消费者容器beanName:"+containerBeanName); - DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory(); - beanFactory.registerBeanDefinition(containerBeanName, beanBuilder.getBeanDefinition()); - - AliyunRocketMQListenerContainer container = beanFactory.getBean(containerBeanName, AliyunRocketMQListenerContainer.class); - - if (!container.isStarted()) { - try { - container.start(); - } catch (Exception e) { - log.error("started container failed. {}", container, e); - throw new RuntimeException(e); - } - } - - log.info("register rocketMQ listener to container, listenerBeanName:{}, containerBeanName:{}", beanName, containerBeanName); - } - } -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/RocketMQProperties.java b/src/main/java/org/apache/rocketmq/spring/starter/RocketMQProperties.java deleted file mode 100644 index c422df1..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/RocketMQProperties.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter; - -import lombok.Data; -import org.springframework.boot.context.properties.ConfigurationProperties; - -@SuppressWarnings("WeakerAccess") -@ConfigurationProperties(prefix = "spring.rocketmq") -@Data -public class RocketMQProperties { - /** - * 环境前缀 - */ - private String environmentPrefix; - /** - * 消息队列服务接入点 - */ - private String onsAddr; - - /** - * AccessKey, 用于标识、校验用户身份 - */ - private String accessKey; - /** - * SecretKey, 用于标识、校验用户身份 - */ - private String secretKey; - - private Producer producer; - @Data - public static class Producer { - - /** - * name of producer - */ - private String group; - - /** - * millis of send message timeout - */ - private int sendMsgTimeout = 3000; - - /** - * Compress message body threshold, namely, message body larger than 4k will be compressed on default. - */ - private int compressMsgBodyOverHowmuch = 1024 * 4; - - /** - *

Maximum number of retry to perform internally before claiming sending failure in synchronous mode.

- * This may potentially cause message duplication which is up to application developers to resolve. - */ - private int retryTimesWhenSendFailed = 2; - - /** - *

Maximum number of retry to perform internally before claiming sending failure in asynchronous mode.

- * This may potentially cause message duplication which is up to application developers to resolve. - */ - private int retryTimesWhenSendAsyncFailed = 2; - - /** - * Indicate whether to retry another broker on sending failure internally. - */ - private boolean retryAnotherBrokerWhenNotStoreOk = false; - - /** - * Maximum allowed message size in bytes. - */ - private int maxMessageSize = 1024 * 1024 * 4; // 4M - } -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/annotation/RocketMQMessageListener.java b/src/main/java/org/apache/rocketmq/spring/starter/annotation/RocketMQMessageListener.java deleted file mode 100644 index 9e768a8..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/annotation/RocketMQMessageListener.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter.annotation; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.apache.rocketmq.spring.starter.enums.ConsumeMode; -import org.apache.rocketmq.spring.starter.enums.SelectorType; - -import com.aliyun.openservices.shade.com.alibaba.rocketmq.common.protocol.heartbeat.MessageModel; - -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface RocketMQMessageListener { - - /** - * Consumers of the same role is required to have exactly same subscriptions and consumerGroup to correctly achieve - * load balance. It's required and needs to be globally unique. - *

- *

- * See here for further discussion. - */ - String consumerGroup(); - - /** - * Topic name - */ - String topic(); - - /** - * Control how to selector message - * - * @see ExpressionType - */ - SelectorType selectorType() default SelectorType.TAG; - - /** - * Control which message can be select. Grammar please see {@link ExpressionType#TAG} and {@link ExpressionType#SQL92} - */ - String selectorExpress() default "*"; - - /** - * Control consume mode, you can choice receive message concurrently or orderly - */ - ConsumeMode consumeMode() default ConsumeMode.CONCURRENTLY; - - /** - * Control message mode, if you want all subscribers receive message all message, broadcasting is a good choice. - */ - MessageModel messageModel() default MessageModel.CLUSTERING; - - /** - * Max consumer thread number - */ - int consumeThreadMax() default 64; - -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/core/AliyunRocketMQListenerContainer.java b/src/main/java/org/apache/rocketmq/spring/starter/core/AliyunRocketMQListenerContainer.java deleted file mode 100644 index b379593..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/core/AliyunRocketMQListenerContainer.java +++ /dev/null @@ -1,440 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter.core; - -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.CONSUMEFAILED_TAG; -import static org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.CONSUMEFAILED_TOPIC; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.nio.charset.Charset; -import java.util.Date; -import java.util.List; -import java.util.Objects; -import java.util.Properties; - -import org.apache.rocketmq.spring.starter.enums.ConsumeMode; -import org.apache.rocketmq.spring.starter.enums.SelectorType; -import org.apache.rocketmq.spring.starter.exception.ConvertMsgException; -import org.apache.rocketmq.spring.starter.msgvo.ConsumeFailedMsgVO; -import org.apache.rocketmq.spring.starter.utils.ExceptionUtil; -import org.apache.rocketmq.spring.starter.utils.IPUtil; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -import com.aliyun.openservices.ons.api.Action; -import com.aliyun.openservices.ons.api.ConsumeContext; -import com.aliyun.openservices.ons.api.Consumer; -import com.aliyun.openservices.ons.api.Message; -import com.aliyun.openservices.ons.api.MessageListener; -import com.aliyun.openservices.ons.api.ONSFactory; -import com.aliyun.openservices.ons.api.PropertyKeyConst; -import com.aliyun.openservices.ons.api.batch.BatchConsumer; -import com.aliyun.openservices.ons.api.batch.BatchMessageListener; -import com.aliyun.openservices.ons.api.order.ConsumeOrderContext; -import com.aliyun.openservices.ons.api.order.MessageOrderListener; -import com.aliyun.openservices.ons.api.order.OrderAction; -import com.aliyun.openservices.ons.api.order.OrderConsumer; -import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.consumer.MessageSelector; -import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.exception.MQClientException; -import com.aliyun.openservices.shade.com.alibaba.rocketmq.common.protocol.heartbeat.MessageModel; -import com.fasterxml.jackson.databind.ObjectMapper; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -@SuppressWarnings("WeakerAccess") -@Slf4j -public class AliyunRocketMQListenerContainer implements InitializingBean, RocketMQListenerContainer { - /** - * 阿里云分配的accesskey - */ - @Setter - private String accessKey; - /** - * 阿里云分配的secretKey - */ - @Setter - private String secretKey; - - @Setter - @Getter - private String consumerGroup; - /** - * 消息队列服务接入点 - */ - @Setter - @Getter - private String onsAddr; - - @Setter - @Getter - private String nameServerAddr; - - @Setter - @Getter - private String topic; - - @Setter - @Getter - private ConsumeMode consumeMode = ConsumeMode.CONCURRENTLY; - - @Setter - @Getter - private SelectorType selectorType = SelectorType.TAG; - - @Setter - @Getter - private String selectorExpress = "*"; - - @Setter - @Getter - private MessageModel messageModel = MessageModel.CLUSTERING; - - @Setter - @Getter - private int consumeThreadMax = 64; - - @Getter - @Setter - private String charset = "UTF-8"; - - @Setter - @Getter - private ObjectMapper objectMapper = new ObjectMapper(); - - @Setter - @Getter - private boolean started; - - @Setter - private RocketMQListener rocketMQListener; - /**普通消息*/ - private Consumer consumer; - /**顺序消息*/ - private OrderConsumer orderConsumer; - /**批量消息*/ - private BatchConsumer batchConsumer; - - private Class messageType; - /** - * 环境前缀 - */ - @Setter - private String environmentPrefix; - - @Setter - private RocketMQTemplate rocketMQTemplate; - - public void setupMessageListener(RocketMQListener rocketMQListener) { - this.rocketMQListener = rocketMQListener; - } - - @Override - public void destroy() { - this.setStarted(false); - if (Objects.nonNull(consumer)) { - consumer.shutdown(); - } - if (Objects.nonNull(orderConsumer)) { - orderConsumer.shutdown(); - } - if (Objects.nonNull(batchConsumer)) { - batchConsumer.shutdown(); - } - log.info("container destroyed, {}", this.toString()); - } - - public synchronized void start() throws MQClientException { - - if (this.isStarted()) { - throw new IllegalStateException("container already started. " + this.toString()); - } - - initRocketMQPushConsumer(); - - // parse message type - this.messageType = getMessageType(); - log.debug("msgType: {}", messageType.getName()); - - if (Objects.nonNull(consumer)) { - consumer.start(); - } - if (Objects.nonNull(orderConsumer)) { - orderConsumer.start(); - } - if (Objects.nonNull(batchConsumer)) { - batchConsumer.start(); - } - this.setStarted(true); - - log.info("started container: {}", this.toString()); - } - - public class DefaultMessageListenerConcurrently implements MessageListener { - - @SuppressWarnings("unchecked") - public Action consume(final Message message, final ConsumeContext context){ - Date consumeBeginTime = new Date(); - log.debug("received msg: {}", message); - try { - long now = consumeBeginTime.getTime(); - rocketMQListener.onMessage(doConvertMessage(message)); - long costTime = System.currentTimeMillis() - now; - log.debug("consume {} cost: {} ms", message.getMsgID(), costTime); - } catch (Exception e) { - log.warn("consume message failed. message:{}", message, e); - if(message.getTopic().equals(environmentPrefix+"_"+CONSUMEFAILED_TOPIC) && CONSUMEFAILED_TAG.equals(message.getTag())){ - log.error("消费失败的消息为“保存消费失败日志消息”,不需要记录日志,不需要重新消费,直接返回成功"); - return Action.CommitMessage; - } - if(e instanceof ConvertMsgException){ - log.error("消费失败的原因为转换对象失败,需要记录日志,不需要重新消费,返回消费成功"); - //消息消费失败,发送失败消息 - this.sendConsumeMsgFailed(message,e,consumeBeginTime); - return Action.CommitMessage; - } - this.sendConsumeMsgFailed(message,e,consumeBeginTime); - return Action.ReconsumeLater; - } - - return Action.CommitMessage; - } - /** - * 发送消息消费失败消息 - * @param message - * @param e - * 2018年3月22日 zhaowg - */ - private void sendConsumeMsgFailed(Message message, Exception e,Date consumeBeginTime) { - log.info("消费消息失败,开始发送消费失败MQ"); - String topic = CONSUMEFAILED_TOPIC; - String tag = CONSUMEFAILED_TAG; - try{ - Date consumeEndTime = new Date(); - ConsumeFailedMsgVO consumeFailedMsgVO = new ConsumeFailedMsgVO(); - consumeFailedMsgVO.setConsumeBeginTime(consumeBeginTime); - consumeFailedMsgVO.setConsumeEndTime(consumeEndTime); - consumeFailedMsgVO.setConsumeGroup(consumerGroup); - consumeFailedMsgVO.setConsumeIp(IPUtil.getLocalHost()); - if(e!=null){ - String errMsg = ExceptionUtil.getTrace(e); - if(!StringUtils.isEmpty(errMsg)){ - //最多保存1024个字符 - consumeFailedMsgVO.setCunsumerErrMsg(errMsg.substring(0, 1024)); - } - } - consumeFailedMsgVO.setMsg(new String(message.getBody())); - consumeFailedMsgVO.setMsgId(message.getMsgID()); - consumeFailedMsgVO.setMsgKeys(message.getKey()); - consumeFailedMsgVO.setReconsumeTimes(message.getReconsumeTimes()); - consumeFailedMsgVO.setTag(message.getTag()); - consumeFailedMsgVO.setTopic(message.getTopic()); - rocketMQTemplate.sendOneWay(topic, tag, consumeFailedMsgVO); - log.info("发送消息消费失败MQ成功"); - }catch(Exception e1){ - log.info("发送消息消费失败MQ异常",e); - } - - } - } - - public class DefaultMessageListenerOrderly implements MessageOrderListener { - - @Override - public OrderAction consume(Message message, ConsumeOrderContext context) { - log.debug("received msg: {}", message); - try { - long now = System.currentTimeMillis(); - rocketMQListener.onMessage(doConvertMessage(message)); - long costTime = System.currentTimeMillis() - now; - log.info("consume {} cost: {} ms", message.getMsgID(), costTime); - } catch (Exception e) { - log.warn("consume message failed. message:{}", message, e); - return OrderAction.Suspend; - } - return OrderAction.Success; - } - } - - public class DefaultMessageListenerBatchs implements BatchMessageListener{ - - @Override - public Action consume(List messages, ConsumeContext context) { - for (Message message : messages) { - Date consumeBeginTime = new Date(); - log.debug("received msg: {}", message); - try { - long now = consumeBeginTime.getTime(); - rocketMQListener.onMessage(doConvertMessage(message)); - long costTime = System.currentTimeMillis() - now; - log.debug("consume {} cost: {} ms", message.getMsgID(), costTime); - } catch (Exception e) { - log.warn("consume message failed. message:{}", message, e); - if(message.getTopic().equals(environmentPrefix+"_"+CONSUMEFAILED_TOPIC) && CONSUMEFAILED_TAG.equals(message.getTag())){ - log.error("消费失败的消息为“保存消费失败日志消息”,不需要记录日志,不需要重新消费,直接返回成功"); - continue; - } - if(e instanceof ConvertMsgException){ - log.error("消费失败的原因为转换对象失败,需要记录日志,不需要重新消费,返回消费成功"); - //消息消费失败,发送失败消息 - this.sendConsumeMsgFailed(message,e,consumeBeginTime); - continue; - } - this.sendConsumeMsgFailed(message,e,consumeBeginTime); - return Action.ReconsumeLater; - } - } - return Action.CommitMessage; - } - - /** - * 发送消息消费失败消息 - * @param message - * @param e - * 2018年3月22日 zhaowg - */ - private void sendConsumeMsgFailed(Message message, Exception e,Date consumeBeginTime) { - log.info("消费消息失败,开始发送消费失败MQ"); - String topic = environmentPrefix+"_"+CONSUMEFAILED_TOPIC; - String tag = CONSUMEFAILED_TAG; - try{ - Date consumeEndTime = new Date(); - ConsumeFailedMsgVO consumeFailedMsgVO = new ConsumeFailedMsgVO(); - consumeFailedMsgVO.setConsumeBeginTime(consumeBeginTime); - consumeFailedMsgVO.setConsumeEndTime(consumeEndTime); - consumeFailedMsgVO.setConsumeGroup(consumerGroup); - consumeFailedMsgVO.setConsumeIp(IPUtil.getLocalHost()); - if(e!=null){ - String errMsg = ExceptionUtil.getTrace(e); - if(!StringUtils.isEmpty(errMsg)){ - //最多保存1024个字符 - consumeFailedMsgVO.setCunsumerErrMsg(errMsg.substring(0, 1024)); - } - } - consumeFailedMsgVO.setMsg(new String(message.getBody())); - consumeFailedMsgVO.setMsgId(message.getMsgID()); - consumeFailedMsgVO.setMsgKeys(message.getKey()); - consumeFailedMsgVO.setReconsumeTimes(message.getReconsumeTimes()); - consumeFailedMsgVO.setTag(message.getTag()); - consumeFailedMsgVO.setTopic(message.getTopic()); - rocketMQTemplate.sendOneWay(topic, tag, consumeFailedMsgVO); - log.info("发送消息消费失败MQ成功"); - }catch(Exception e1){ - log.info("发送消息消费失败MQ异常",e); - } - - } - } - @Override - public void afterPropertiesSet() throws Exception { - start(); - } - - - @SuppressWarnings("unchecked") - private Object doConvertMessage(Message message) { - if (Objects.equals(messageType, Message.class)) { - return message; - } else { - String str = new String(message.getBody(), Charset.forName(charset)); - if (Objects.equals(messageType, String.class)) { - return str; - } else { - // if msgType not string, use objectMapper change it. - try { - return objectMapper.readValue(str, messageType); - } catch (Exception e) { - log.info("convert failed. str:{}, msgType:{}", str, messageType); - throw new ConvertMsgException("cannot convert message to " + messageType, e); - } - } - } - } - - private Class getMessageType() { - Type[] interfaces = rocketMQListener.getClass().getGenericInterfaces(); - if (Objects.nonNull(interfaces)) { - for (Type type : interfaces) { - if (type instanceof ParameterizedType) { - ParameterizedType parameterizedType = (ParameterizedType) type; - if (Objects.equals(parameterizedType.getRawType(), RocketMQListener.class)) { - Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); - if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) { - return (Class) actualTypeArguments[0]; - } else { - return Object.class; - } - } - } - } - - return Object.class; - } else { - return Object.class; - } - } - - private void initRocketMQPushConsumer() throws MQClientException { - - Assert.notNull(rocketMQListener, "Property 'rocketMQListener' is required"); - Assert.notNull(consumerGroup, "Property 'consumerGroup' is required"); - Assert.notNull(nameServerAddr, "Property 'nameServer' is required"); - Assert.notNull(topic, "Property 'topic' is required"); - - Properties consumerProperties = new Properties(); - consumerProperties.setProperty(PropertyKeyConst.ConsumerId, consumerGroup); - consumerProperties.setProperty(PropertyKeyConst.AccessKey, accessKey); - consumerProperties.setProperty(PropertyKeyConst.SecretKey, secretKey); - consumerProperties.setProperty(PropertyKeyConst.NAMESRV_ADDR, nameServerAddr); - consumerProperties.setProperty(PropertyKeyConst.ConsumeThreadNums, consumeThreadMax+""); - consumerProperties.setProperty(PropertyKeyConst.MessageModel, messageModel.getModeCN()); - //允许用户自己设置该consumer的一些配置 - if (rocketMQListener instanceof AliyunRocketMQPushConsumerLifecycleListener) { - ((AliyunRocketMQPushConsumerLifecycleListener) rocketMQListener).prepareStart(consumerProperties); - } - switch (consumeMode) { - case ORDERLY://顺序消息 - orderConsumer = ONSFactory.createOrderedConsumer(consumerProperties); - if(selectorType == SelectorType.TAG){ - orderConsumer.subscribe(topic, selectorExpress, new DefaultMessageListenerOrderly()); -// }else if(selectorType == SelectorType.SQL92){ -// orderConsumer.subscribe(topic, MessageSelector.bySql(selectorExpress), new DefaultMessageListenerOrderly()); - } - break; - case CONCURRENTLY://普通消息 - consumer = ONSFactory.createConsumer(consumerProperties); - if(selectorType == SelectorType.TAG){ - consumer.subscribe(topic, selectorExpress, new DefaultMessageListenerConcurrently()); -// }else if(selectorType == SelectorType.SQL92){ -// consumer.subscribe(topic, MessageSelector.bySql(selectorExpress), new DefaultMessageListenerConcurrently()); - } - break; - case BATCH://批量消息 - batchConsumer = ONSFactory.createBatchConsumer(consumerProperties); - batchConsumer.subscribe(topic, selectorExpress, new DefaultMessageListenerBatchs()); - break; - default: - throw new IllegalArgumentException("Property 'consumeMode' was wrong."); - } - - } - -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/core/AliyunRocketMQPushConsumerLifecycleListener.java b/src/main/java/org/apache/rocketmq/spring/starter/core/AliyunRocketMQPushConsumerLifecycleListener.java deleted file mode 100644 index e0b1860..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/core/AliyunRocketMQPushConsumerLifecycleListener.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter.core; - -import java.util.Properties; - -public interface AliyunRocketMQPushConsumerLifecycleListener extends RocketMQConsumerLifecycleListener { -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java b/src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java deleted file mode 100644 index 9e9889c..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java +++ /dev/null @@ -1,355 +0,0 @@ -///* -// * Licensed to the Apache Software Foundation (ASF) under one or more -// * contributor license agreements. See the NOTICE file distributed with -// * this work for additional information regarding copyright ownership. -// * The ASF licenses this file to You under the Apache License, Version 2.0 -// * (the "License"); you may not use this file except in compliance with -// * the License. You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -// -//package org.apache.rocketmq.spring.starter.core; -// -//import java.lang.reflect.ParameterizedType; -//import java.lang.reflect.Type; -//import java.nio.charset.Charset; -//import java.util.Date; -//import java.util.List; -//import java.util.Objects; -// -//import org.apache.commons.lang3.StringUtils; -//import org.apache.commons.lang3.exception.ExceptionUtils; -//import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; -//import org.apache.rocketmq.client.consumer.MessageSelector; -//import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; -//import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; -//import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext; -//import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus; -//import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; -//import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly; -//import org.apache.rocketmq.client.exception.MQClientException; -//import org.apache.rocketmq.common.message.MessageExt; -//import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; -//import org.apache.rocketmq.spring.starter.enums.ConsumeMode; -//import org.apache.rocketmq.spring.starter.enums.SelectorType; -//import org.apache.rocketmq.spring.starter.exception.ConvertMsgException; -//import org.apache.rocketmq.spring.starter.msgvo.ConsumeFailedMsgVO; -//import org.apache.rocketmq.spring.starter.utils.IPUtil; -//import org.springframework.beans.factory.InitializingBean; -//import org.springframework.util.Assert; -// -//import com.fasterxml.jackson.databind.ObjectMapper; -// -//import lombok.Getter; -//import lombok.Setter; -//import lombok.extern.slf4j.Slf4j; -// -//@SuppressWarnings("WeakerAccess") -//@Slf4j -//public class DefaultRocketMQListenerContainer implements InitializingBean, RocketMQListenerContainer { -// -// @Setter -// @Getter -// private long suspendCurrentQueueTimeMillis = 1000; -// -// /** -// * Message consume retry strategy
-1,no retry,put into DLQ directly
0,broker control retry frequency
-// * >0,client control retry frequency -// */ -// @Setter -// @Getter -// private int delayLevelWhenNextConsume = 0; -// -// @Setter -// @Getter -// private String consumerGroup; -// -// @Setter -// @Getter -// private String nameServer; -// -// @Setter -// @Getter -// private String topic; -// -// @Setter -// @Getter -// private ConsumeMode consumeMode = ConsumeMode.CONCURRENTLY; -// -// @Setter -// @Getter -// private SelectorType selectorType = SelectorType.TAG; -// -// @Setter -// @Getter -// private String selectorExpress = "*"; -// -// @Setter -// @Getter -// private MessageModel messageModel = MessageModel.CLUSTERING; -// -// @Setter -// @Getter -// private int consumeThreadMax = 64; -// -// @Getter -// @Setter -// private String charset = "UTF-8"; -// -// @Setter -// @Getter -// private ObjectMapper objectMapper = new ObjectMapper(); -// -// @Setter -// @Getter -// private boolean started; -// -// @Setter -// private RocketMQListener rocketMQListener; -// -// private DefaultMQPushConsumer consumer; -// -// private Class messageType; -// -// @Setter -// private RocketMQTemplate rocketMQTemplate; -// -// public void setupMessageListener(RocketMQListener rocketMQListener) { -// this.rocketMQListener = rocketMQListener; -// } -// -// @Override -// public void destroy() { -// this.setStarted(false); -// if (Objects.nonNull(consumer)) { -// consumer.shutdown(); -// } -// log.info("container destroyed, {}", this.toString()); -// } -// -// public synchronized void start() throws MQClientException { -// -// if (this.isStarted()) { -// throw new IllegalStateException("container already started. " + this.toString()); -// } -// -// initRocketMQPushConsumer(); -// -// // parse message type -// this.messageType = getMessageType(); -// log.debug("msgType: {}", messageType.getName()); -// -// consumer.start(); -// this.setStarted(true); -// -// log.info("started container: {}", this.toString()); -// } -// -// public class DefaultMessageListenerConcurrently implements MessageListenerConcurrently { -// -// @SuppressWarnings("unchecked") -// public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) { -// for (MessageExt messageExt : msgs) { -// Date consumeBeginTime = new Date(); -// log.debug("received msg: {}", messageExt); -// try { -// long now = System.currentTimeMillis(); -// rocketMQListener.onMessage(doConvertMessage(messageExt)); -// long costTime = System.currentTimeMillis() - now; -// log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime); -// } catch (Exception e) { -// log.warn("consume message failed. messageExt:{}", messageExt, e); -// context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume); -// if(messageExt.getTopic().equals("DATA_COLLECTION_TOPIC") && "ConsumeMsgFailed".equals(messageExt.getTags())){ -// log.error("消费失败的消息为“保存消费失败日志消息”,不需要记录日志,不需要重新消费,直接返回成功"); -// return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; -// } -// if(e instanceof ConvertMsgException){ -// log.error("消费失败的原因为转换对象失败,需要记录日志,不需要重新消费,返回消费成功"); -// //消息消费失败,发送失败消息 -// this.sendConsumeMsgFailed(messageExt,e,consumeBeginTime); -// return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; -// } -// this.sendConsumeMsgFailed(messageExt,e,consumeBeginTime); -// return ConsumeConcurrentlyStatus.RECONSUME_LATER; -// } -// } -// -// return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; -// } -// /** -// * 发送消息消费失败消息 -// * @param messageExt -// * @param e -// * 2018年3月22日 zhaowg -// */ -// private void sendConsumeMsgFailed(MessageExt messageExt, Exception e,Date consumeBeginTime) { -// log.info("消费消息失败,开始发送消费失败MQ"); -// String topic = "DATA_COLLECTION_TOPIC"; -// String tag = "ConsumeMsgFailed"; -// try{ -// Date consumeEndTime = new Date(); -// String destination = topic+":"+tag; -// ConsumeFailedMsgVO consumeFailedMsgVO = new ConsumeFailedMsgVO(); -// consumeFailedMsgVO.setConsumeBeginTime(consumeBeginTime); -// consumeFailedMsgVO.setConsumeEndTime(consumeEndTime); -// consumeFailedMsgVO.setConsumeGroup(consumerGroup); -// consumeFailedMsgVO.setConsumeIp(IPUtil.getLocalHost()); -// if(e!=null){ -// String errMsg = ExceptionUtils.getStackTrace(e); -// if(StringUtils.isNotBlank(errMsg)){ -// //最多保存1024个字符 -// consumeFailedMsgVO.setCunsumerErrMsg(errMsg.substring(0, 1024)); -// } -// } -// consumeFailedMsgVO.setMsg(new String(messageExt.getBody())); -// consumeFailedMsgVO.setMsgId(messageExt.getMsgId()); -// consumeFailedMsgVO.setMsgKeys(messageExt.getKeys()); -// consumeFailedMsgVO.setReconsumeTimes(messageExt.getReconsumeTimes()); -// consumeFailedMsgVO.setTag(messageExt.getTags()); -// consumeFailedMsgVO.setTopic(messageExt.getTopic()); -// rocketMQTemplate.sendOneWay(destination, consumeFailedMsgVO); -// log.info("发送消息消费失败MQ成功"); -// }catch(Exception e1){ -// log.info("发送消息消费失败MQ异常",e); -// } -// -// } -// } -// -// public class DefaultMessageListenerOrderly implements MessageListenerOrderly { -// -// @SuppressWarnings("unchecked") -// public ConsumeOrderlyStatus consumeMessage(List msgs, ConsumeOrderlyContext context) { -// for (MessageExt messageExt : msgs) { -// log.debug("received msg: {}", messageExt); -// try { -// long now = System.currentTimeMillis(); -// rocketMQListener.onMessage(doConvertMessage(messageExt)); -// long costTime = System.currentTimeMillis() - now; -// log.info("consume {} cost: {} ms", messageExt.getMsgId(), costTime); -// } catch (Exception e) { -// log.warn("consume message failed. messageExt:{}", messageExt, e); -// context.setSuspendCurrentQueueTimeMillis(suspendCurrentQueueTimeMillis); -// return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT; -// } -// } -// -// return ConsumeOrderlyStatus.SUCCESS; -// } -// } -// -// @Override -// public void afterPropertiesSet() throws Exception { -// start(); -// } -// -// @Override -// public String toString() { -// return "DefaultRocketMQListenerContainer{" + -// "consumerGroup='" + consumerGroup + '\'' + -// ", nameServer='" + nameServer + '\'' + -// ", topic='" + topic + '\'' + -// ", consumeMode=" + consumeMode + -// ", selectorType=" + selectorType + -// ", selectorExpress='" + selectorExpress + '\'' + -// ", messageModel=" + messageModel + -// '}'; -// } -// -// @SuppressWarnings("unchecked") -// private Object doConvertMessage(MessageExt messageExt) { -// if (Objects.equals(messageType, MessageExt.class)) { -// return messageExt; -// } else { -// String str = new String(messageExt.getBody(), Charset.forName(charset)); -// if (Objects.equals(messageType, String.class)) { -// return str; -// } else { -// // if msgType not string, use objectMapper change it. -// try { -// return objectMapper.readValue(str, messageType); -// } catch (Exception e) { -// log.info("convert failed. str:{}, msgType:{}", str, messageType); -// throw new ConvertMsgException("cannot convert message to " + messageType, e); -// } -// } -// } -// } -// -// private Class getMessageType() { -// Type[] interfaces = rocketMQListener.getClass().getGenericInterfaces(); -// if (Objects.nonNull(interfaces)) { -// for (Type type : interfaces) { -// if (type instanceof ParameterizedType) { -// ParameterizedType parameterizedType = (ParameterizedType) type; -// if (Objects.equals(parameterizedType.getRawType(), RocketMQListener.class)) { -// Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); -// if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) { -// return (Class) actualTypeArguments[0]; -// } else { -// return Object.class; -// } -// } -// } -// } -// -// return Object.class; -// } else { -// return Object.class; -// } -// } -// -// private void initRocketMQPushConsumer() throws MQClientException { -// -// Assert.notNull(rocketMQListener, "Property 'rocketMQListener' is required"); -// Assert.notNull(consumerGroup, "Property 'consumerGroup' is required"); -// Assert.notNull(nameServer, "Property 'nameServer' is required"); -// Assert.notNull(topic, "Property 'topic' is required"); -// -// consumer = new DefaultMQPushConsumer(consumerGroup); -// consumer.setNamesrvAddr(nameServer); -// consumer.setConsumeThreadMax(consumeThreadMax); -// if (consumeThreadMax < consumer.getConsumeThreadMin()) { -// consumer.setConsumeThreadMin(consumeThreadMax); -// } -// -// consumer.setMessageModel(messageModel); -// -// switch (selectorType) { -// case TAG: -// consumer.subscribe(topic, selectorExpress); -// break; -// case SQL92: -// consumer.subscribe(topic, MessageSelector.bySql(selectorExpress)); -// break; -// default: -// throw new IllegalArgumentException("Property 'selectorType' was wrong."); -// } -// -// switch (consumeMode) { -// case ORDERLY: -// consumer.setMessageListener(new DefaultMessageListenerOrderly()); -// break; -// case CONCURRENTLY: -// consumer.setMessageListener(new DefaultMessageListenerConcurrently()); -// break; -// default: -// throw new IllegalArgumentException("Property 'consumeMode' was wrong."); -// } -// -// // provide an entryway to custom setting RocketMQ consumer -// if (rocketMQListener instanceof AliyunRocketMQPushConsumerLifecycleListener) { -// ((AliyunRocketMQPushConsumerLifecycleListener) rocketMQListener).prepareStart(consumer); -// } -// -// } -// -//} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java b/src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java deleted file mode 100644 index 777b951..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter.core; - -/** - * Constants Created by aqlu on 2017/11/16. - */ -public final class DefaultRocketMQListenerContainerConstants { - public static final String PROP_NAMESERVER = "nameServer"; - public static final String PROP_TOPIC = "topic"; - public static final String PROP_CONSUMER_GROUP = "consumerGroup"; - public static final String PROP_CONSUME_MODE = "consumeMode"; - public static final String PROP_CONSUME_THREAD_MAX = "consumeThreadMax"; - public static final String PROP_MESSAGE_MODEL = "messageModel"; - public static final String PROP_SELECTOR_EXPRESS = "selectorExpress"; - public static final String PROP_SELECTOR_TYPE = "selectorType"; - public static final String PROP_ROCKETMQ_LISTENER = "rocketMQListener"; - public static final String PROP_OBJECT_MAPPER = "objectMapper"; - public static final String METHOD_DESTROY = "destroy"; - public static final String PROP_ROCKETMQ_TEMPLATE = "rocketMQTemplate"; - public static final String PROP_ONS_Addr = "onsAddr"; - public static final String PROP_ACCESS_KEY = "accessKey"; - public static final String PROP_SECRET_KEY = "secretKey"; - public static final String PROP_NAMESRV_ADDR = "nameServerAddr"; - /** - * 环境前缀 - */ - public static final String PROP_ENVIRONMENT_PREFIX = "environmentPrefix"; - /** - * 消息消费失败发送的主题 - */ - public final static String CONSUMEFAILED_TOPIC = "ZTEITS_RNT_CLOUD"; - /** - * 消息消费失败发送的tag - */ - public final static String CONSUMEFAILED_TAG = "ConsumeMsgFailed"; -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java b/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java deleted file mode 100644 index 37ebedb..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter.core; - -public interface RocketMQConsumerLifecycleListener { - void prepareStart(final T consumer); -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListener.java b/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListener.java deleted file mode 100644 index 64daa5f..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListener.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter.core; - -public interface RocketMQListener { - /** - * 消费消息接口,由应用来实现
- * 网络抖动等不稳定的情形可能会带来消息重复,对重复消息敏感的业务可对消息做幂等处理
- * 没有异常则表示处理成功,否在处理失败,处理失败时阿里云MQ会重复调用,最多调用16次,如果16次依然失败,则不再调用。 - */ - void onMessage(T message); -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListenerContainer.java b/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListenerContainer.java deleted file mode 100644 index 7667eed..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQListenerContainer.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter.core; - -import org.springframework.beans.factory.DisposableBean; - -public interface RocketMQListenerContainer extends DisposableBean { - - /** - * Setup the message listener to use. Throws an {@link IllegalArgumentException} if that message listener type is - * not supported. - */ - void setupMessageListener(RocketMQListener messageListener); -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQTemplate.java b/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQTemplate.java deleted file mode 100644 index 5d88f62..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/core/RocketMQTemplate.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter.core; - -import java.nio.charset.Charset; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; - -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.messaging.MessagingException; - -import com.aliyun.openservices.ons.api.Message; -import com.aliyun.openservices.ons.api.Producer; -import com.aliyun.openservices.ons.api.SendCallback; -import com.aliyun.openservices.ons.api.SendResult; -import com.fasterxml.jackson.databind.ObjectMapper; - -import lombok.Getter; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class RocketMQTemplate implements InitializingBean, DisposableBean { - - @Getter - @Setter - private Producer aliyunProducer; - - @Setter - @Getter - private ObjectMapper objectMapper = new ObjectMapper(); - - @Getter - @Setter - private String charset = "UTF-8"; - - /** - * 环境前缀 - */ - @Setter - private String environmentPrefix; - - /** - * 同步发送消息 - * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. - * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 - * @param key 业务主键 - * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. - * @param userProperties 添加用户自定义属性键值对; 该键值对在消费消费时可被获取.也可用于做SQL属性过滤 - * @param startDeliverTime 设置消息的定时投递时间(绝对时间),最大延迟时间为7天. - *

- *
    - *
  1. 延迟投递: 延迟3s投递, 设置为: System.currentTimeMillis() + 3000;
  2. - *
  3. 定时投递: 2016-02-01 11:30:00投递, 设置为: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-02-01 11:30:00").getTime()
  4. - *
- * @return {@link SendResult} - * 2018年3月23日 zhaowg - */ - public SendResult syncSend(String topic,String tag,String keys,Object payload,Map userProperties,Long startDeliverTime) { - if (Objects.isNull(topic) || Objects.isNull(payload)) { - log.info("同步消息发送失败,主题和消息不能为空"); - throw new IllegalArgumentException("同步消息发送失败,主题和消息不能为空"); - } - - try { - long now = System.currentTimeMillis(); - - Message rocketMsg = new Message(environmentPrefix+"_"+topic, tag, keys, convertToRocketMsg(payload)); - if(userProperties!=null && !userProperties.isEmpty()){ - for (Entry userProp : userProperties.entrySet()) { - rocketMsg.putUserProperties(userProp.getKey(), userProp.getValue()); - } - } - if(startDeliverTime!=null){ - //设置定时发送时间 - rocketMsg.setStartDeliverTime(startDeliverTime); - } - //阿里云发送 - SendResult sendResult = aliyunProducer.send(rocketMsg); - long costTime = System.currentTimeMillis() - now; - log.debug("发送消息耗时: {} ms, msgId:{}", costTime, sendResult.getMessageId()); - return sendResult; - } catch (Exception e) { - log.info("同步发送失败. topic:{}, message:{} ", topic, payload); - throw new MessagingException(e.getMessage(), e); - } - } - - /** - * Same to {@link #syncSend(String, String, String, Object, Map, Long)}. - * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. - * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 - * @param key 业务主键 - * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. - * @return {@link SendResult} - * 2018年3月23日 zhaowg - */ - public SendResult syncSend(String topic,String tag,String keys, Object payload) { - return syncSend(topic, tag, keys, payload, null, null); - } - /** - * Same to {@link #syncSend(String, String, String, Object)}. - * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. - * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 - * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. - * @return {@link SendResult} - * 2018年3月23日 zhaowg - */ - public SendResult syncSend(String topic,String tag, Object payload) { - return syncSend(topic, tag,null, payload); - } - - /** - * 异步发送消息 - * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. - * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 - * @param key 业务主键 - * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. - * @param userProperties 添加用户自定义属性键值对; 该键值对在消费消费时可被获取.也可用于做SQL属性过滤 - * @param startDeliverTime 设置消息的定时投递时间(绝对时间),最大延迟时间为7天. - *

- *
    - *
  1. 延迟投递: 延迟3s投递, 设置为: System.currentTimeMillis() + 3000;
  2. - *
  3. 定时投递: 2016-02-01 11:30:00投递, 设置为: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-02-01 11:30:00").getTime()
  4. - *
- * @param sendCallback 发送完成要执行的回调函数 - * 2018年3月23日 zhaowg - */ - public void asyncSend(String topic,String tag,String keys,Object payload,Map userProperties, - Long startDeliverTime,SendCallback sendCallback) { - if (Objects.isNull(topic) || Objects.isNull(payload)) { - log.info("异步消息发送失败,主题和消息不能为空"); - throw new IllegalArgumentException("异步消息发送失败,主题和消息不能为空"); - } - try { - long now = System.currentTimeMillis(); - - Message rocketMsg = new Message(environmentPrefix+"_"+topic, tag, keys, convertToRocketMsg(payload)); - if(userProperties!=null && !userProperties.isEmpty()){ - for (Entry userProp : userProperties.entrySet()) { - rocketMsg.putUserProperties(userProp.getKey(), userProp.getValue()); - } - } - if(startDeliverTime!=null){ - //设置定时发送时间 - rocketMsg.setStartDeliverTime(startDeliverTime); - } - //阿里云发送 - aliyunProducer.sendAsync(rocketMsg, sendCallback); - long costTime = System.currentTimeMillis() - now; - log.debug("发送消息耗时: {} ms", costTime); - } catch (Exception e) { - log.info("异步发送失败. topic:{}, message:{} ", topic, payload); - throw new MessagingException(e.getMessage(), e); - } - } - /** - * Same to {@link #asyncSend(String, String, String, Object, Map, Long, SendCallback)}. - * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. - * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 - * @param key 业务主键 - * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. - * @param sendCallback 发送完成要执行的回调函数 - * @return {@link SendResult} - * 2018年3月23日 zhaowg - */ - public void asyncSend(String topic,String tag,String keys, Object payload,SendCallback sendCallback) { - asyncSend(topic, tag, keys, payload, null, null,sendCallback); - } - /** - * Same to {@link #asyncSend(String, String, String, Object,SendCallback)}. - * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. - * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 - * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. - * @param sendCallback 发送完成要执行的回调函数 - * @return {@link SendResult} - * 2018年3月23日 zhaowg - */ - public void asyncSend(String topic,String tag, Object payload,SendCallback sendCallback) { - asyncSend(topic, tag,null, payload,sendCallback); - } - /** - * 服务器不应答,无法保证消息是否成功到达服务器 - * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. - * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 - * @param key 业务主键 - * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. - * @param userProperties 添加用户自定义属性键值对; 该键值对在消费消费时可被获取.也可用于做SQL属性过滤 - * @param startDeliverTime 设置消息的定时投递时间(绝对时间),最大延迟时间为7天. - *

- *
    - *
  1. 延迟投递: 延迟3s投递, 设置为: System.currentTimeMillis() + 3000;
  2. - *
  3. 定时投递: 2016-02-01 11:30:00投递, 设置为: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-02-01 11:30:00").getTime()
  4. - *
- * 2018年3月23日 zhaowg - */ - public void sendOneWay(String topic,String tag,String keys,Object payload,Map userProperties, - Long startDeliverTime) { - if (Objects.isNull(topic) || Objects.isNull(payload)) { - log.info("sendOneWay消息发送失败,主题和消息不能为空"); - throw new IllegalArgumentException("sendOneWay消息发送失败,主题和消息不能为空"); - } - try { - long now = System.currentTimeMillis(); - - Message rocketMsg = new Message(environmentPrefix+"_"+topic, tag, keys, convertToRocketMsg(payload)); - if(userProperties!=null && !userProperties.isEmpty()){ - for (Entry userProp : userProperties.entrySet()) { - rocketMsg.putUserProperties(userProp.getKey(), userProp.getValue()); - } - } - if(startDeliverTime!=null){ - //设置定时发送时间 - rocketMsg.setStartDeliverTime(startDeliverTime); - } - //阿里云发送 - aliyunProducer.sendOneway(rocketMsg); - long costTime = System.currentTimeMillis() - now; - log.debug("发送消息耗时: {} ms", costTime); - } catch (Exception e) { - log.info("sendOneWay发送失败. topic:{}, message:{} ", topic, payload); - throw new MessagingException(e.getMessage(), e); - } - } - /** - * Same to {@link #sendOneWay(String, String, String, Object, Map, Long)}. - * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. - * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 - * @param key 业务主键 - * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. - * 2018年3月23日 zhaowg - */ - public void sendOneWay(String topic,String tag,String keys, Object payload) { - sendOneWay(topic, tag, keys, payload, null, null); - } - /** - * Same to {@link #sendOneWay(String, String, String, Object)}. - * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. - * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 - * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. - * 2018年3月23日 zhaowg - */ - public void sendOneWay(String topic,String tag, Object payload) { - sendOneWay(topic, tag,null, payload); - } - - @Override - public void afterPropertiesSet() throws Exception { - if(aliyunProducer != null){ - log.info("开始启动阿里云[环境标识:"+environmentPrefix+"]生产者"); - aliyunProducer.start(); - } - } - - /** - * 转换对象为字节 - * @param msgObj - * @return - * 2018年3月23日 zhaowg - */ - private byte[] convertToRocketMsg(Object msgObj) { - byte[] payloads; - - if (msgObj instanceof String) { - payloads = ((String) msgObj).getBytes(Charset.forName(charset)); - } else { - try { - String jsonObj = this.objectMapper.writeValueAsString(msgObj); - payloads = jsonObj.getBytes(Charset.forName(charset)); - } catch (Exception e) { - throw new RuntimeException("convert to RocketMQ message failed.", e); - } - } - return payloads; - } - - - @Override - public void destroy() { - if(Objects.nonNull(aliyunProducer)){ - log.info("开始关闭阿里云[环境标识:"+environmentPrefix+"]生产者"); - aliyunProducer.shutdown(); - } - } -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/enums/ConsumeMode.java b/src/main/java/org/apache/rocketmq/spring/starter/enums/ConsumeMode.java deleted file mode 100644 index 04a0349..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/enums/ConsumeMode.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter.enums; - -public enum ConsumeMode { - /** - * 同时接收异步发送的消息 - */ - CONCURRENTLY, - - /** - * 顺序接收消息,一个队列,一个线程 - */ - ORDERLY, - - /** - * 批量接收发送的消息,允许自定义范围为[1, 32], 实际消费数量可能小于该值 - */ - BATCH -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/enums/SelectorType.java b/src/main/java/org/apache/rocketmq/spring/starter/enums/SelectorType.java deleted file mode 100644 index 1923713..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/enums/SelectorType.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.rocketmq.spring.starter.enums; - -import com.aliyun.openservices.shade.com.alibaba.rocketmq.common.filter.ExpressionType; - -public enum SelectorType { - - /** - * @see ExpressionType#TAG - */ - TAG, - - /** - * @see ExpressionType#SQL92 - * 注释by zwg 暂时不支持,原因:阿里云提供的ons-client最新版本1.7.4支持,但是该包有问题,引入后工程无法打包,目前使用的1.7.1,该版本不支持SQL92过滤 - */ - //SQL92 -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/exception/ConvertMsgException.java b/src/main/java/org/apache/rocketmq/spring/starter/exception/ConvertMsgException.java deleted file mode 100644 index 503ba1a..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/exception/ConvertMsgException.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.apache.rocketmq.spring.starter.exception; - -public class ConvertMsgException extends RuntimeException{ - - private static final long serialVersionUID = 1L; - - public ConvertMsgException() { - super(); - } - - public ConvertMsgException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - - public ConvertMsgException(String message, Throwable cause) { - super(message, cause); - } - - public ConvertMsgException(String message) { - super(message); - } - - public ConvertMsgException(Throwable cause) { - super(cause); - } - -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/msgvo/ConsumeFailedMsgVO.java b/src/main/java/org/apache/rocketmq/spring/starter/msgvo/ConsumeFailedMsgVO.java deleted file mode 100644 index 8224133..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/msgvo/ConsumeFailedMsgVO.java +++ /dev/null @@ -1,149 +0,0 @@ -package org.apache.rocketmq.spring.starter.msgvo; - -import java.io.Serializable; -import java.util.Date; - -public class ConsumeFailedMsgVO implements Serializable{ - - private static final long serialVersionUID = 1L; - - /**消息ID*/ - private String msgId; - - /**消息主题*/ - private String topic; - - /**消息标签描述*/ - private String tag; - - /**消费组*/ - private String consumeGroup; - - /**消费者ip*/ - private String consumeIp; - - /**消费开始时间*/ - private Date consumeBeginTime; - - /**消费结束时间*/ - private Date consumeEndTime; - - /**消息关键字*/ - private String msgKeys; - - /**重复消费次数*/ - private Integer reconsumeTimes; - - /**消费失败错误信息*/ - private String cunsumerErrMsg; - - /**消息内容*/ - private String msg; - - public String getCunsumerErrMsg() { - return cunsumerErrMsg; - } - - public void setCunsumerErrMsg(String cunsumerErrMsg) { - this.cunsumerErrMsg = cunsumerErrMsg; - } - - public String getMsg() { - return msg; - } - - public void setMsg(String msg) { - this.msg = msg; - } - - /**获取消息ID*/ - public String getMsgId() { - return msgId; - } - - /**设置消息ID*/ - public void setMsgId(String msgId) { - this.msgId = msgId == null ? null : msgId.trim(); - } - - /**获取消息主题*/ - public String getTopic() { - return topic; - } - - /**设置消息主题*/ - public void setTopic(String topic) { - this.topic = topic == null ? null : topic.trim(); - } - - /**获取消息标签描述*/ - public String getTag() { - return tag; - } - - /**设置消息标签描述*/ - public void setTag(String tag) { - this.tag = tag == null ? null : tag.trim(); - } - - /**获取消费组*/ - public String getConsumeGroup() { - return consumeGroup; - } - - /**设置消费组*/ - public void setConsumeGroup(String consumeGroup) { - this.consumeGroup = consumeGroup == null ? null : consumeGroup.trim(); - } - - /**获取消费者ip*/ - public String getConsumeIp() { - return consumeIp; - } - - /**设置消费者ip*/ - public void setConsumeIp(String consumeIp) { - this.consumeIp = consumeIp == null ? null : consumeIp.trim(); - } - - /**获取消费开始时间*/ - public Date getConsumeBeginTime() { - return consumeBeginTime; - } - - /**设置消费开始时间*/ - public void setConsumeBeginTime(Date consumeBeginTime) { - this.consumeBeginTime = consumeBeginTime; - } - - /**获取消费结束时间*/ - public Date getConsumeEndTime() { - return consumeEndTime; - } - - /**设置消费结束时间*/ - public void setConsumeEndTime(Date consumeEndTime) { - this.consumeEndTime = consumeEndTime; - } - - /**获取消息关键字*/ - public String getMsgKeys() { - return msgKeys; - } - - /**设置消息关键字*/ - public void setMsgKeys(String msgKeys) { - this.msgKeys = msgKeys == null ? null : msgKeys.trim(); - } - - /**获取重复消费次数*/ - public Integer getReconsumeTimes() { - return reconsumeTimes; - } - - /**设置重复消费次数*/ - public void setReconsumeTimes(Integer reconsumeTimes) { - this.reconsumeTimes = reconsumeTimes; - } - -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/utils/ExceptionUtil.java b/src/main/java/org/apache/rocketmq/spring/starter/utils/ExceptionUtil.java deleted file mode 100644 index e97da1c..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/utils/ExceptionUtil.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.apache.rocketmq.spring.starter.utils; - -import java.io.PrintWriter; -import java.io.StringWriter; - -public class ExceptionUtil { - - public static String getTrace(Throwable t) { - StringBuffer buffer = new StringBuffer(); - if(t==null){ - return ""; - } - StringWriter stringWriter = new StringWriter(); - PrintWriter writer = new PrintWriter(stringWriter); - t.printStackTrace(writer); - //设置堆栈信息 - buffer.append("堆栈信息为:" + stringWriter.getBuffer().toString()); - return buffer.toString(); - } - -} diff --git a/src/main/java/org/apache/rocketmq/spring/starter/utils/IPUtil.java b/src/main/java/org/apache/rocketmq/spring/starter/utils/IPUtil.java deleted file mode 100644 index b40739c..0000000 --- a/src/main/java/org/apache/rocketmq/spring/starter/utils/IPUtil.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.apache.rocketmq.spring.starter.utils; - -import java.net.InetAddress; - -import org.springframework.util.StringUtils; - -/** - * Copyright: Copyright (c) 2017 zteits - * - * @ClassName: com.clouds.constants.utils - * @Description: IP地址工具类 - * @version: v1.0.0 - * @author: atao - * @date: 2017/4/26 上午9:25 - * Modification History: - * Date Author Version Description - * ---------------------------------------------------------* - * 2017/4/26 atao v1.0.0 创建 - */ -public class IPUtil { - private static String localHost; - private static String localHostName; - - public static String getLocalHost() { - if (StringUtils.isEmpty(localHost)) { - getLocalHostInfo(); - } - return localHost; - } - - public static String getLocalHostNome() { - if (StringUtils.isEmpty(localHostName)) { - getLocalHostInfo(); - } - return localHostName; - } - - private static void getLocalHostInfo() { - try { - InetAddress ia = InetAddress.getLocalHost(); - localHostName = ia.getHostName(); - localHost = ia.getHostAddress(); - } catch (Exception e) { - //获取当前地址失败 - e.printStackTrace(); - } - } - -} diff --git a/src/main/java/zteits/rocketmq/spring/starter/AliyunRocketMQAutoConfiguration.java b/src/main/java/zteits/rocketmq/spring/starter/AliyunRocketMQAutoConfiguration.java new file mode 100644 index 0000000..7b3052f --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/AliyunRocketMQAutoConfiguration.java @@ -0,0 +1,233 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter; + +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.METHOD_DESTROY; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_CONSUMER_GROUP; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_CONSUME_MODE; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_CONSUME_THREAD_MAX; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_MESSAGE_MODEL; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_OBJECT_MAPPER; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_ROCKETMQ_LISTENER; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_ROCKETMQ_TEMPLATE; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_SELECTOR_EXPRESS; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.PROP_SELECTOR_TYPE; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.*; + +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; + +import javax.annotation.Resource; + +import com.aliyun.openservices.shade.com.alibaba.fastjson.JSONObject; +import zteits.rocketmq.spring.starter.annotation.RocketMQMessageListener; +import zteits.rocketmq.spring.starter.core.AliyunRocketMQListenerContainer; +import zteits.rocketmq.spring.starter.core.RocketMQListener; +import zteits.rocketmq.spring.starter.core.RocketMQTemplate; +import org.springframework.aop.support.AopUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.util.Assert; + +import com.aliyun.openservices.ons.api.ONSFactory; +import com.aliyun.openservices.ons.api.Producer; +import com.aliyun.openservices.ons.api.PropertyKeyConst; +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.extern.slf4j.Slf4j; + +@Configuration +@EnableConfigurationProperties(RocketMQProperties.class) +@Order +@Slf4j +public class AliyunRocketMQAutoConfiguration { + + @Bean + @ConditionalOnClass(RocketMQProperties.Producer.class) + @ConditionalOnMissingBean(RocketMQProperties.Producer.class) + @ConditionalOnProperty(prefix = "spring.rocketmq", value = {"environmentPrefix", "producer.group"}) + public Producer mqProducer(RocketMQProperties rocketMQProperties) { + log.info("注册生产者mqProducer:"+ JSONObject.toJSON(rocketMQProperties)); + + RocketMQProperties.Producer producerConfig = rocketMQProperties.getProducer(); + String groupName = producerConfig.getGroup(); + Assert.hasText(groupName, "[spring.rocketmq.producer.group] must not be null"); + String accessKey = rocketMQProperties.getAccessKey(); + Assert.hasText(accessKey, "[spring.rocketmq.accessKey] must not be null"); + String secretKey = rocketMQProperties.getSecretKey(); + Assert.hasText(secretKey, "[spring.rocketmq.secretKey] must not be null"); + // String onsAddr = rocketMQProperties.getOnsAddr(); + String namesrvAddr = rocketMQProperties.getNameSrvAddr(); + Assert.hasText(namesrvAddr, "[spring.rocketmq.nameSrvAddr] must not be null"); + String environmentPrefix = rocketMQProperties.getEnvironmentPrefix(); + Assert.hasText(secretKey, "[spring.rocketmq.environmentPrefix] must not be null"); + + Properties producerProperties = new Properties(); + //生成者ProducerId添加前缀:PID_+环境标识_+groupName + String pid = "PID_"+environmentPrefix+"_"+groupName; + log.info("注册生产者PID:"+pid); + producerProperties.setProperty(PropertyKeyConst.ProducerId, pid); + producerProperties.setProperty(PropertyKeyConst.AccessKey, accessKey); + producerProperties.setProperty(PropertyKeyConst.SecretKey, secretKey); + producerProperties.setProperty(PropertyKeyConst.NAMESRV_ADDR, namesrvAddr); + //producerProperties.setProperty(PropertyKeyConst.ONSAddr, onsAddr); + Producer producer = ONSFactory.createProducer(producerProperties); + return producer; + } + + @Bean + @ConditionalOnClass(ObjectMapper.class) + @ConditionalOnMissingBean(name = "rocketMQMessageObjectMapper") + public ObjectMapper rocketMQMessageObjectMapper() { + return new ObjectMapper(); + } + + @Bean(destroyMethod = "destroy") + @ConditionalOnBean(Producer.class) + @ConditionalOnMissingBean(name = "rocketMQTemplate") + public RocketMQTemplate rocketMQTemplate(Producer mqProducer,RocketMQProperties rocketMQProperties, + @Autowired(required = false) + @Qualifier("rocketMQMessageObjectMapper") + ObjectMapper objectMapper) { + RocketMQTemplate rocketMQTemplate = new RocketMQTemplate(); + rocketMQTemplate.setAliyunProducer(mqProducer); + rocketMQTemplate.setEnvironmentPrefix(rocketMQProperties.getEnvironmentPrefix()); + if (Objects.nonNull(objectMapper)) { + rocketMQTemplate.setObjectMapper(objectMapper); + } + return rocketMQTemplate; + } + + @Configuration + @EnableConfigurationProperties(RocketMQProperties.class) + @ConditionalOnProperty(prefix = "spring.rocketmq", value = {"environmentPrefix", "producer.group"}) + @Order + public static class ListenerContainerConfiguration implements ApplicationContextAware, InitializingBean { + private ConfigurableApplicationContext applicationContext; + + private AtomicLong counter = new AtomicLong(0); + + @Resource + private StandardEnvironment environment; + + @Resource + private RocketMQProperties rocketMQProperties; + + private ObjectMapper objectMapper; + + @Autowired + private RocketMQTemplate rocketMQTemplate; + + public ListenerContainerConfiguration() { + } + + @Autowired(required = false) + public ListenerContainerConfiguration( + @Qualifier("rocketMQMessageObjectMapper") ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = (ConfigurableApplicationContext) applicationContext; + } + + @Override + public void afterPropertiesSet() { + Map beans = this.applicationContext.getBeansWithAnnotation(RocketMQMessageListener.class); + + if (Objects.nonNull(beans)) { + beans.forEach(this::registerContainer); + } + } + + private void registerContainer(String beanName, Object bean) { + String uuid = UUID.randomUUID().toString(); + log.info(uuid+"开始注册消费者,beanName:"+beanName); + Class clazz = AopUtils.getTargetClass(bean); + + if (!RocketMQListener.class.isAssignableFrom(bean.getClass())) { + throw new IllegalStateException(clazz + " is not instance of " + RocketMQListener.class.getName()); + } + RocketMQListener rocketMQListener = (RocketMQListener) bean; + RocketMQMessageListener annotation = clazz.getAnnotation(RocketMQMessageListener.class); + BeanDefinitionBuilder beanBuilder = BeanDefinitionBuilder.rootBeanDefinition(AliyunRocketMQListenerContainer.class); + beanBuilder.addPropertyValue(PROP_NAMESRV_Addr, rocketMQProperties.getNameSrvAddr()); + // beanBuilder.addPropertyValue(PROP_ONS_Addr, rocketMQProperties.getOnsAddr()); + String topic = rocketMQProperties.getEnvironmentPrefix()+"_"+environment.resolvePlaceholders(annotation.topic()); + log.info(uuid+"订阅的主题topic:"+topic); + beanBuilder.addPropertyValue(PROP_TOPIC, topic); + String cid = "GID_"+rocketMQProperties.getEnvironmentPrefix()+"_"+environment.resolvePlaceholders(annotation.consumerGroup()); + log.info(uuid+"消费者CID:"+cid); + //消费者ConsumerId添加前缀:PID_+环境标识_+groupName + beanBuilder.addPropertyValue(PROP_CONSUMER_GROUP, cid); + beanBuilder.addPropertyValue(PROP_CONSUME_MODE, annotation.consumeMode()); + beanBuilder.addPropertyValue(PROP_CONSUME_THREAD_MAX, annotation.consumeThreadMax()); + beanBuilder.addPropertyValue(PROP_MESSAGE_MODEL, annotation.messageModel()); + beanBuilder.addPropertyValue(PROP_SELECTOR_EXPRESS, environment.resolvePlaceholders(annotation.selectorExpress())); + beanBuilder.addPropertyValue(PROP_SELECTOR_TYPE, annotation.selectorType()); + beanBuilder.addPropertyValue(PROP_ROCKETMQ_LISTENER, rocketMQListener); + beanBuilder.addPropertyValue(PROP_ROCKETMQ_TEMPLATE, rocketMQTemplate); + beanBuilder.addPropertyValue(PROP_ENVIRONMENT_PREFIX, rocketMQProperties.getEnvironmentPrefix()); + if (Objects.nonNull(objectMapper)) { + beanBuilder.addPropertyValue(PROP_OBJECT_MAPPER, objectMapper); + } + beanBuilder.setDestroyMethodName(METHOD_DESTROY); + //增加阿里云key + beanBuilder.addPropertyValue(PROP_ACCESS_KEY, rocketMQProperties.getAccessKey()); + beanBuilder.addPropertyValue(PROP_SECRET_KEY, rocketMQProperties.getSecretKey()); + + String containerBeanName = String.format("%s_%s", AliyunRocketMQListenerContainer.class.getName(), counter.incrementAndGet()); + log.info("消费者容器beanName:"+containerBeanName); + DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getBeanFactory(); + beanFactory.registerBeanDefinition(containerBeanName, beanBuilder.getBeanDefinition()); + + AliyunRocketMQListenerContainer container = beanFactory.getBean(containerBeanName, AliyunRocketMQListenerContainer.class); + + if (!container.isStarted()) { + try { + container.start(); + } catch (Exception e) { + log.error("started container failed. {}", container, e); + throw new RuntimeException(e); + } + } + + log.info("register rocketMQ listener to container, listenerBeanName:{}, containerBeanName:{}", beanName, containerBeanName); + } + } +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/RocketMQProperties.java b/src/main/java/zteits/rocketmq/spring/starter/RocketMQProperties.java new file mode 100644 index 0000000..94fa432 --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/RocketMQProperties.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@SuppressWarnings("WeakerAccess") +@ConfigurationProperties(prefix = "spring.rocketmq") +@Data +public class RocketMQProperties { + /** + * 环境前缀 + */ + private String environmentPrefix; + /** + * 消息队列服务接入点 + */ + private String onsAddr; + + private String nameSrvAddr; + + /** + * AccessKey, 用于标识、校验用户身份 + */ + private String accessKey; + /** + * SecretKey, 用于标识、校验用户身份 + */ + private String secretKey; + + private Producer producer; + @Data + public static class Producer { + + /** + * name of producer + */ + private String group; + + /** + * millis of send message timeout + */ + private int sendMsgTimeout = 3000; + + /** + * Compress message body threshold, namely, message body larger than 4k will be compressed on default. + */ + private int compressMsgBodyOverHowmuch = 1024 * 4; + + /** + *

Maximum number of retry to perform internally before claiming sending failure in synchronous mode.

+ * This may potentially cause message duplication which is up to application developers to resolve. + */ + private int retryTimesWhenSendFailed = 2; + + /** + *

Maximum number of retry to perform internally before claiming sending failure in asynchronous mode.

+ * This may potentially cause message duplication which is up to application developers to resolve. + */ + private int retryTimesWhenSendAsyncFailed = 2; + + /** + * Indicate whether to retry another broker on sending failure internally. + */ + private boolean retryAnotherBrokerWhenNotStoreOk = false; + + /** + * Maximum allowed message size in bytes. + */ + private int maxMessageSize = 1024 * 1024 * 4; // 4M + + /** + * 消费失败消息主题 + */ + private String consumeFailedTopic = "ZTEITS_RNT_CLOUD"; + + /** + * 消费失败消息标签 + */ + private String consumeFailedTag = "ConsumeMsgFailed"; + + // 对应的getter和setter方法 + public String getConsumeFailedTopic() { + return consumeFailedTopic; + } + + public void setConsumeFailedTopic(String consumeFailedTopic) { + this.consumeFailedTopic = consumeFailedTopic; + } + + public String getConsumeFailedTag() { + return consumeFailedTag; + } + + public void setConsumeFailedTag(String consumeFailedTag) { + this.consumeFailedTag = consumeFailedTag; + } + } +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/annotation/RocketMQMessageListener.java b/src/main/java/zteits/rocketmq/spring/starter/annotation/RocketMQMessageListener.java new file mode 100644 index 0000000..79b4ea9 --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/annotation/RocketMQMessageListener.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import zteits.rocketmq.spring.starter.enums.ConsumeMode; +import zteits.rocketmq.spring.starter.enums.SelectorType; + +import com.aliyun.openservices.shade.com.alibaba.rocketmq.common.protocol.heartbeat.MessageModel; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RocketMQMessageListener { + + /** + * Consumers of the same role is required to have exactly same subscriptions and consumerGroup to correctly achieve + * load balance. It's required and needs to be globally unique. + *

+ *

+ * See here for further discussion. + */ + String consumerGroup(); + + /** + * Topic name + */ + String topic(); + + /** + * Control how to selector message + * + * @see ExpressionType + */ + SelectorType selectorType() default SelectorType.TAG; + + /** + * Control which message can be select. Grammar please see {@link ExpressionType#TAG} and {@link ExpressionType#SQL92} + */ + String selectorExpress() default "*"; + + /** + * Control consume mode, you can choice receive message concurrently or orderly + */ + ConsumeMode consumeMode() default ConsumeMode.CONCURRENTLY; + + /** + * Control message mode, if you want all subscribers receive message all message, broadcasting is a good choice. + */ + MessageModel messageModel() default MessageModel.CLUSTERING; + + /** + * Max consumer thread number + */ + int consumeThreadMax() default 64; + +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/core/AliyunRocketMQListenerContainer.java b/src/main/java/zteits/rocketmq/spring/starter/core/AliyunRocketMQListenerContainer.java new file mode 100644 index 0000000..39b761d --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/core/AliyunRocketMQListenerContainer.java @@ -0,0 +1,441 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter.core; + +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.CONSUMEFAILED_TAG; +import static zteits.rocketmq.spring.starter.core.DefaultRocketMQListenerContainerConstants.CONSUMEFAILED_TOPIC; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.nio.charset.Charset; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.Properties; + +import zteits.rocketmq.spring.starter.enums.ConsumeMode; +import zteits.rocketmq.spring.starter.enums.SelectorType; +import zteits.rocketmq.spring.starter.exception.ConvertMsgException; +import zteits.rocketmq.spring.starter.msgvo.ConsumeFailedMsgVO; +import zteits.rocketmq.spring.starter.utils.ExceptionUtil; +import zteits.rocketmq.spring.starter.utils.IPUtil; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import com.aliyun.openservices.ons.api.Action; +import com.aliyun.openservices.ons.api.ConsumeContext; +import com.aliyun.openservices.ons.api.Consumer; +import com.aliyun.openservices.ons.api.Message; +import com.aliyun.openservices.ons.api.MessageListener; +import com.aliyun.openservices.ons.api.ONSFactory; +import com.aliyun.openservices.ons.api.PropertyKeyConst; +import com.aliyun.openservices.ons.api.batch.BatchConsumer; +import com.aliyun.openservices.ons.api.batch.BatchMessageListener; +import com.aliyun.openservices.ons.api.order.ConsumeOrderContext; +import com.aliyun.openservices.ons.api.order.MessageOrderListener; +import com.aliyun.openservices.ons.api.order.OrderAction; +import com.aliyun.openservices.ons.api.order.OrderConsumer; +import com.aliyun.openservices.shade.com.alibaba.rocketmq.client.exception.MQClientException; +import com.aliyun.openservices.shade.com.alibaba.rocketmq.common.protocol.heartbeat.MessageModel; +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@SuppressWarnings("WeakerAccess") +@Slf4j +public class AliyunRocketMQListenerContainer implements InitializingBean, RocketMQListenerContainer { + /** + * 阿里云分配的accesskey + */ + @Setter + private String accessKey; + /** + * 阿里云分配的secretKey + */ + @Setter + private String secretKey; + + @Setter + @Getter + private String consumerGroup; + /** + * 消息队列服务接入点 + */ + @Setter + @Getter + private String onsAddr; + +// @Setter +// @Getter +// private String nameServerAddr; + + @Setter + @Getter + private String nameSrvAddr; + + @Setter + @Getter + private String topic; + + @Setter + @Getter + private ConsumeMode consumeMode = ConsumeMode.CONCURRENTLY; + + @Setter + @Getter + private SelectorType selectorType = SelectorType.TAG; + + @Setter + @Getter + private String selectorExpress = "*"; + + @Setter + @Getter + private MessageModel messageModel = MessageModel.CLUSTERING; + + @Setter + @Getter + private int consumeThreadMax = 64; + + @Getter + @Setter + private String charset = "UTF-8"; + + @Setter + @Getter + private ObjectMapper objectMapper = new ObjectMapper(); + + @Setter + @Getter + private boolean started; + + @Setter + private RocketMQListener rocketMQListener; + /**普通消息*/ + private Consumer consumer; + /**顺序消息*/ + private OrderConsumer orderConsumer; + /**批量消息*/ + private BatchConsumer batchConsumer; + + private Class messageType; + /** + * 环境前缀 + */ + @Setter + private String environmentPrefix; + + @Setter + private RocketMQTemplate rocketMQTemplate; + + public void setupMessageListener(RocketMQListener rocketMQListener) { + this.rocketMQListener = rocketMQListener; + } + + @Override + public void destroy() { + this.setStarted(false); + if (Objects.nonNull(consumer)) { + consumer.shutdown(); + } + if (Objects.nonNull(orderConsumer)) { + orderConsumer.shutdown(); + } + if (Objects.nonNull(batchConsumer)) { + batchConsumer.shutdown(); + } + log.info("container destroyed, {}", this.toString()); + } + + public synchronized void start() throws MQClientException { + + if (this.isStarted()) { + throw new IllegalStateException("container already started. " + this.toString()); + } + + initRocketMQPushConsumer(); + + // parse message type + this.messageType = getMessageType(); + log.debug("msgType: {}", messageType.getName()); + + if (Objects.nonNull(consumer)) { + consumer.start(); + } + if (Objects.nonNull(orderConsumer)) { + orderConsumer.start(); + } + if (Objects.nonNull(batchConsumer)) { + batchConsumer.start(); + } + this.setStarted(true); + + log.info("started container: {}", this.toString()); + } + + public class DefaultMessageListenerConcurrently implements MessageListener { + + @SuppressWarnings("unchecked") + public Action consume(final Message message, final ConsumeContext context){ + Date consumeBeginTime = new Date(); + log.debug("received msg: {}", message); + try { + long now = consumeBeginTime.getTime(); + rocketMQListener.onMessage(doConvertMessage(message)); + long costTime = System.currentTimeMillis() - now; + log.debug("consume {} cost: {} ms", message.getMsgID(), costTime); + } catch (Exception e) { + log.warn("consume message failed. message:{}", message, e); + if(message.getTopic().equals(environmentPrefix+"_"+CONSUMEFAILED_TOPIC) && CONSUMEFAILED_TAG.equals(message.getTag())){ + log.error("消费失败的消息为“保存消费失败日志消息”,不需要记录日志,不需要重新消费,直接返回成功"); + return Action.CommitMessage; + } + if(e instanceof ConvertMsgException){ + log.error("消费失败的原因为转换对象失败,需要记录日志,不需要重新消费,返回消费成功"); + //消息消费失败,发送失败消息 + this.sendConsumeMsgFailed(message,e,consumeBeginTime); + return Action.CommitMessage; + } + this.sendConsumeMsgFailed(message,e,consumeBeginTime); + return Action.ReconsumeLater; + } + + return Action.CommitMessage; + } + /** + * 发送消息消费失败消息 + * @param message + * @param e + * 2018年3月22日 zhaowg + */ + private void sendConsumeMsgFailed(Message message, Exception e,Date consumeBeginTime) { + log.info("消费消息失败,开始发送消费失败MQ"); + String topic = environmentPrefix+"_"+CONSUMEFAILED_TOPIC; + String tag = CONSUMEFAILED_TAG; + try{ + Date consumeEndTime = new Date(); + ConsumeFailedMsgVO consumeFailedMsgVO = new ConsumeFailedMsgVO(); + consumeFailedMsgVO.setConsumeBeginTime(consumeBeginTime); + consumeFailedMsgVO.setConsumeEndTime(consumeEndTime); + consumeFailedMsgVO.setConsumeGroup(consumerGroup); + consumeFailedMsgVO.setConsumeIp(IPUtil.getLocalHost()); + if(e!=null){ + String errMsg = ExceptionUtil.getTrace(e); + if(!StringUtils.isEmpty(errMsg)){ + //最多保存1024个字符 + consumeFailedMsgVO.setCunsumerErrMsg(errMsg.substring(0, 1024)); + } + } + consumeFailedMsgVO.setMsg(new String(message.getBody())); + consumeFailedMsgVO.setMsgId(message.getMsgID()); + consumeFailedMsgVO.setMsgKeys(message.getKey()); + consumeFailedMsgVO.setReconsumeTimes(message.getReconsumeTimes()); + consumeFailedMsgVO.setTag(message.getTag()); + consumeFailedMsgVO.setTopic(message.getTopic()); + rocketMQTemplate.sendOneWay(topic, tag, consumeFailedMsgVO); + log.info("发送消息消费失败MQ成功"); + }catch(Exception e1){ + log.error("发送消息消费失败MQ异常", e1); + } + } + } + + public class DefaultMessageListenerOrderly implements MessageOrderListener { + + @Override + public OrderAction consume(Message message, ConsumeOrderContext context) { + log.debug("received msg: {}", message); + try { + long now = System.currentTimeMillis(); + rocketMQListener.onMessage(doConvertMessage(message)); + long costTime = System.currentTimeMillis() - now; + log.info("consume {} cost: {} ms", message.getMsgID(), costTime); + } catch (Exception e) { + log.warn("consume message failed. message:{}", message, e); + return OrderAction.Suspend; + } + return OrderAction.Success; + } + } + + public class DefaultMessageListenerBatchs implements BatchMessageListener{ + + @Override + public Action consume(List messages, ConsumeContext context) { + for (Message message : messages) { + Date consumeBeginTime = new Date(); + log.debug("received msg: {}", message); + try { + long now = consumeBeginTime.getTime(); + rocketMQListener.onMessage(doConvertMessage(message)); + long costTime = System.currentTimeMillis() - now; + log.debug("consume {} cost: {} ms", message.getMsgID(), costTime); + } catch (Exception e) { + log.warn("consume message failed. message:{}", message, e); + if(message.getTopic().equals(environmentPrefix+"_"+CONSUMEFAILED_TOPIC) && CONSUMEFAILED_TAG.equals(message.getTag())){ + log.error("消费失败的消息为“保存消费失败日志消息”,不需要记录日志,不需要重新消费,直接返回成功"); + continue; + } + if(e instanceof ConvertMsgException){ + log.error("消费失败的原因为转换对象失败,需要记录日志,不需要重新消费,返回消费成功"); + //消息消费失败,发送失败消息 + this.sendConsumeMsgFailed(message,e,consumeBeginTime); + continue; + } + this.sendConsumeMsgFailed(message,e,consumeBeginTime); + return Action.ReconsumeLater; + } + } + return Action.CommitMessage; + } + + /** + * 发送消息消费失败消息 + * @param message + * @param e + * 2018年3月22日 zhaowg + */ + private void sendConsumeMsgFailed(Message message, Exception e,Date consumeBeginTime) { + log.info("消费消息失败,开始发送消费失败MQ"); + String topic = environmentPrefix+"_"+CONSUMEFAILED_TOPIC; + String tag = CONSUMEFAILED_TAG; + try{ + Date consumeEndTime = new Date(); + ConsumeFailedMsgVO consumeFailedMsgVO = new ConsumeFailedMsgVO(); + consumeFailedMsgVO.setConsumeBeginTime(consumeBeginTime); + consumeFailedMsgVO.setConsumeEndTime(consumeEndTime); + consumeFailedMsgVO.setConsumeGroup(consumerGroup); + consumeFailedMsgVO.setConsumeIp(IPUtil.getLocalHost()); + if(e!=null){ + String errMsg = ExceptionUtil.getTrace(e); + if(!StringUtils.isEmpty(errMsg)){ + //最多保存1024个字符 + consumeFailedMsgVO.setCunsumerErrMsg(errMsg.substring(0, 1024)); + } + } + consumeFailedMsgVO.setMsg(new String(message.getBody())); + consumeFailedMsgVO.setMsgId(message.getMsgID()); + consumeFailedMsgVO.setMsgKeys(message.getKey()); + consumeFailedMsgVO.setReconsumeTimes(message.getReconsumeTimes()); + consumeFailedMsgVO.setTag(message.getTag()); + consumeFailedMsgVO.setTopic(message.getTopic()); + rocketMQTemplate.sendOneWay(topic, tag, consumeFailedMsgVO); + log.info("发送消息消费失败MQ成功"); + }catch(Exception e1){ + log.error("发送消息消费失败MQ异常", e1); + } + } + } + @Override + public void afterPropertiesSet() throws Exception { + start(); + } + + + @SuppressWarnings("unchecked") + private Object doConvertMessage(Message message) { + if (Objects.equals(messageType, Message.class)) { + return message; + } else { + String str = new String(message.getBody(), Charset.forName(charset)); + if (Objects.equals(messageType, String.class)) { + return str; + } else { + // if msgType not string, use objectMapper change it. + try { + return objectMapper.readValue(str, messageType); + } catch (Exception e) { + log.info("convert failed. str:{}, msgType:{}", str, messageType); + throw new ConvertMsgException("cannot convert message to " + messageType, e); + } + } + } + } + + private Class getMessageType() { + Type[] interfaces = rocketMQListener.getClass().getGenericInterfaces(); + if (Objects.nonNull(interfaces)) { + for (Type type : interfaces) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + if (Objects.equals(parameterizedType.getRawType(), RocketMQListener.class)) { + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) { + return (Class) actualTypeArguments[0]; + } else { + return Object.class; + } + } + } + } + + return Object.class; + } else { + return Object.class; + } + } + + private void initRocketMQPushConsumer() throws MQClientException { + + Assert.notNull(rocketMQListener, "Property 'rocketMQListener' is required"); + Assert.notNull(consumerGroup, "Property 'consumerGroup' is required"); + Assert.notNull(nameSrvAddr, "Property 'nameServer' is required"); + Assert.notNull(topic, "Property 'topic' is required"); + + Properties consumerProperties = new Properties(); + consumerProperties.setProperty(PropertyKeyConst.ConsumerId, consumerGroup); + consumerProperties.setProperty(PropertyKeyConst.AccessKey, accessKey); + consumerProperties.setProperty(PropertyKeyConst.SecretKey, secretKey); + consumerProperties.setProperty(PropertyKeyConst.NAMESRV_ADDR, nameSrvAddr); + consumerProperties.setProperty(PropertyKeyConst.ConsumeThreadNums, consumeThreadMax+""); + consumerProperties.setProperty(PropertyKeyConst.MessageModel, messageModel.getModeCN()); + //允许用户自己设置该consumer的一些配置 + if (rocketMQListener instanceof AliyunRocketMQPushConsumerLifecycleListener) { + ((AliyunRocketMQPushConsumerLifecycleListener) rocketMQListener).prepareStart(consumerProperties); + } + switch (consumeMode) { + case ORDERLY://顺序消息 + orderConsumer = ONSFactory.createOrderedConsumer(consumerProperties); + if(selectorType == SelectorType.TAG){ + orderConsumer.subscribe(topic, selectorExpress, new DefaultMessageListenerOrderly()); +// }else if(selectorType == SelectorType.SQL92){ +// orderConsumer.subscribe(topic, MessageSelector.bySql(selectorExpress), new DefaultMessageListenerOrderly()); + } + break; + case CONCURRENTLY://普通消息 + consumer = ONSFactory.createConsumer(consumerProperties); + if(selectorType == SelectorType.TAG){ + consumer.subscribe(topic, selectorExpress, new DefaultMessageListenerConcurrently()); +// }else if(selectorType == SelectorType.SQL92){ +// consumer.subscribe(topic, MessageSelector.bySql(selectorExpress), new DefaultMessageListenerConcurrently()); + } + break; + case BATCH://批量消息 + batchConsumer = ONSFactory.createBatchConsumer(consumerProperties); + batchConsumer.subscribe(topic, selectorExpress, new DefaultMessageListenerBatchs()); + break; + default: + throw new IllegalArgumentException("Property 'consumeMode' was wrong."); + } + + } + +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/core/AliyunRocketMQPushConsumerLifecycleListener.java b/src/main/java/zteits/rocketmq/spring/starter/core/AliyunRocketMQPushConsumerLifecycleListener.java new file mode 100644 index 0000000..ebb5a23 --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/core/AliyunRocketMQPushConsumerLifecycleListener.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter.core; + +import java.util.Properties; + +public interface AliyunRocketMQPushConsumerLifecycleListener extends RocketMQConsumerLifecycleListener { +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java b/src/main/java/zteits/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java new file mode 100644 index 0000000..9e9889c --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/core/DefaultRocketMQListenerContainer.java @@ -0,0 +1,355 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package org.apache.rocketmq.spring.starter.core; +// +//import java.lang.reflect.ParameterizedType; +//import java.lang.reflect.Type; +//import java.nio.charset.Charset; +//import java.util.Date; +//import java.util.List; +//import java.util.Objects; +// +//import org.apache.commons.lang3.StringUtils; +//import org.apache.commons.lang3.exception.ExceptionUtils; +//import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; +//import org.apache.rocketmq.client.consumer.MessageSelector; +//import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; +//import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; +//import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext; +//import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus; +//import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; +//import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly; +//import org.apache.rocketmq.client.exception.MQClientException; +//import org.apache.rocketmq.common.message.MessageExt; +//import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; +//import org.apache.rocketmq.spring.starter.enums.ConsumeMode; +//import org.apache.rocketmq.spring.starter.enums.SelectorType; +//import org.apache.rocketmq.spring.starter.exception.ConvertMsgException; +//import org.apache.rocketmq.spring.starter.msgvo.ConsumeFailedMsgVO; +//import org.apache.rocketmq.spring.starter.utils.IPUtil; +//import org.springframework.beans.factory.InitializingBean; +//import org.springframework.util.Assert; +// +//import com.fasterxml.jackson.databind.ObjectMapper; +// +//import lombok.Getter; +//import lombok.Setter; +//import lombok.extern.slf4j.Slf4j; +// +//@SuppressWarnings("WeakerAccess") +//@Slf4j +//public class DefaultRocketMQListenerContainer implements InitializingBean, RocketMQListenerContainer { +// +// @Setter +// @Getter +// private long suspendCurrentQueueTimeMillis = 1000; +// +// /** +// * Message consume retry strategy
-1,no retry,put into DLQ directly
0,broker control retry frequency
+// * >0,client control retry frequency +// */ +// @Setter +// @Getter +// private int delayLevelWhenNextConsume = 0; +// +// @Setter +// @Getter +// private String consumerGroup; +// +// @Setter +// @Getter +// private String nameServer; +// +// @Setter +// @Getter +// private String topic; +// +// @Setter +// @Getter +// private ConsumeMode consumeMode = ConsumeMode.CONCURRENTLY; +// +// @Setter +// @Getter +// private SelectorType selectorType = SelectorType.TAG; +// +// @Setter +// @Getter +// private String selectorExpress = "*"; +// +// @Setter +// @Getter +// private MessageModel messageModel = MessageModel.CLUSTERING; +// +// @Setter +// @Getter +// private int consumeThreadMax = 64; +// +// @Getter +// @Setter +// private String charset = "UTF-8"; +// +// @Setter +// @Getter +// private ObjectMapper objectMapper = new ObjectMapper(); +// +// @Setter +// @Getter +// private boolean started; +// +// @Setter +// private RocketMQListener rocketMQListener; +// +// private DefaultMQPushConsumer consumer; +// +// private Class messageType; +// +// @Setter +// private RocketMQTemplate rocketMQTemplate; +// +// public void setupMessageListener(RocketMQListener rocketMQListener) { +// this.rocketMQListener = rocketMQListener; +// } +// +// @Override +// public void destroy() { +// this.setStarted(false); +// if (Objects.nonNull(consumer)) { +// consumer.shutdown(); +// } +// log.info("container destroyed, {}", this.toString()); +// } +// +// public synchronized void start() throws MQClientException { +// +// if (this.isStarted()) { +// throw new IllegalStateException("container already started. " + this.toString()); +// } +// +// initRocketMQPushConsumer(); +// +// // parse message type +// this.messageType = getMessageType(); +// log.debug("msgType: {}", messageType.getName()); +// +// consumer.start(); +// this.setStarted(true); +// +// log.info("started container: {}", this.toString()); +// } +// +// public class DefaultMessageListenerConcurrently implements MessageListenerConcurrently { +// +// @SuppressWarnings("unchecked") +// public ConsumeConcurrentlyStatus consumeMessage(List msgs, ConsumeConcurrentlyContext context) { +// for (MessageExt messageExt : msgs) { +// Date consumeBeginTime = new Date(); +// log.debug("received msg: {}", messageExt); +// try { +// long now = System.currentTimeMillis(); +// rocketMQListener.onMessage(doConvertMessage(messageExt)); +// long costTime = System.currentTimeMillis() - now; +// log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime); +// } catch (Exception e) { +// log.warn("consume message failed. messageExt:{}", messageExt, e); +// context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume); +// if(messageExt.getTopic().equals("DATA_COLLECTION_TOPIC") && "ConsumeMsgFailed".equals(messageExt.getTags())){ +// log.error("消费失败的消息为“保存消费失败日志消息”,不需要记录日志,不需要重新消费,直接返回成功"); +// return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; +// } +// if(e instanceof ConvertMsgException){ +// log.error("消费失败的原因为转换对象失败,需要记录日志,不需要重新消费,返回消费成功"); +// //消息消费失败,发送失败消息 +// this.sendConsumeMsgFailed(messageExt,e,consumeBeginTime); +// return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; +// } +// this.sendConsumeMsgFailed(messageExt,e,consumeBeginTime); +// return ConsumeConcurrentlyStatus.RECONSUME_LATER; +// } +// } +// +// return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; +// } +// /** +// * 发送消息消费失败消息 +// * @param messageExt +// * @param e +// * 2018年3月22日 zhaowg +// */ +// private void sendConsumeMsgFailed(MessageExt messageExt, Exception e,Date consumeBeginTime) { +// log.info("消费消息失败,开始发送消费失败MQ"); +// String topic = "DATA_COLLECTION_TOPIC"; +// String tag = "ConsumeMsgFailed"; +// try{ +// Date consumeEndTime = new Date(); +// String destination = topic+":"+tag; +// ConsumeFailedMsgVO consumeFailedMsgVO = new ConsumeFailedMsgVO(); +// consumeFailedMsgVO.setConsumeBeginTime(consumeBeginTime); +// consumeFailedMsgVO.setConsumeEndTime(consumeEndTime); +// consumeFailedMsgVO.setConsumeGroup(consumerGroup); +// consumeFailedMsgVO.setConsumeIp(IPUtil.getLocalHost()); +// if(e!=null){ +// String errMsg = ExceptionUtils.getStackTrace(e); +// if(StringUtils.isNotBlank(errMsg)){ +// //最多保存1024个字符 +// consumeFailedMsgVO.setCunsumerErrMsg(errMsg.substring(0, 1024)); +// } +// } +// consumeFailedMsgVO.setMsg(new String(messageExt.getBody())); +// consumeFailedMsgVO.setMsgId(messageExt.getMsgId()); +// consumeFailedMsgVO.setMsgKeys(messageExt.getKeys()); +// consumeFailedMsgVO.setReconsumeTimes(messageExt.getReconsumeTimes()); +// consumeFailedMsgVO.setTag(messageExt.getTags()); +// consumeFailedMsgVO.setTopic(messageExt.getTopic()); +// rocketMQTemplate.sendOneWay(destination, consumeFailedMsgVO); +// log.info("发送消息消费失败MQ成功"); +// }catch(Exception e1){ +// log.info("发送消息消费失败MQ异常",e); +// } +// +// } +// } +// +// public class DefaultMessageListenerOrderly implements MessageListenerOrderly { +// +// @SuppressWarnings("unchecked") +// public ConsumeOrderlyStatus consumeMessage(List msgs, ConsumeOrderlyContext context) { +// for (MessageExt messageExt : msgs) { +// log.debug("received msg: {}", messageExt); +// try { +// long now = System.currentTimeMillis(); +// rocketMQListener.onMessage(doConvertMessage(messageExt)); +// long costTime = System.currentTimeMillis() - now; +// log.info("consume {} cost: {} ms", messageExt.getMsgId(), costTime); +// } catch (Exception e) { +// log.warn("consume message failed. messageExt:{}", messageExt, e); +// context.setSuspendCurrentQueueTimeMillis(suspendCurrentQueueTimeMillis); +// return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT; +// } +// } +// +// return ConsumeOrderlyStatus.SUCCESS; +// } +// } +// +// @Override +// public void afterPropertiesSet() throws Exception { +// start(); +// } +// +// @Override +// public String toString() { +// return "DefaultRocketMQListenerContainer{" + +// "consumerGroup='" + consumerGroup + '\'' + +// ", nameServer='" + nameServer + '\'' + +// ", topic='" + topic + '\'' + +// ", consumeMode=" + consumeMode + +// ", selectorType=" + selectorType + +// ", selectorExpress='" + selectorExpress + '\'' + +// ", messageModel=" + messageModel + +// '}'; +// } +// +// @SuppressWarnings("unchecked") +// private Object doConvertMessage(MessageExt messageExt) { +// if (Objects.equals(messageType, MessageExt.class)) { +// return messageExt; +// } else { +// String str = new String(messageExt.getBody(), Charset.forName(charset)); +// if (Objects.equals(messageType, String.class)) { +// return str; +// } else { +// // if msgType not string, use objectMapper change it. +// try { +// return objectMapper.readValue(str, messageType); +// } catch (Exception e) { +// log.info("convert failed. str:{}, msgType:{}", str, messageType); +// throw new ConvertMsgException("cannot convert message to " + messageType, e); +// } +// } +// } +// } +// +// private Class getMessageType() { +// Type[] interfaces = rocketMQListener.getClass().getGenericInterfaces(); +// if (Objects.nonNull(interfaces)) { +// for (Type type : interfaces) { +// if (type instanceof ParameterizedType) { +// ParameterizedType parameterizedType = (ParameterizedType) type; +// if (Objects.equals(parameterizedType.getRawType(), RocketMQListener.class)) { +// Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); +// if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) { +// return (Class) actualTypeArguments[0]; +// } else { +// return Object.class; +// } +// } +// } +// } +// +// return Object.class; +// } else { +// return Object.class; +// } +// } +// +// private void initRocketMQPushConsumer() throws MQClientException { +// +// Assert.notNull(rocketMQListener, "Property 'rocketMQListener' is required"); +// Assert.notNull(consumerGroup, "Property 'consumerGroup' is required"); +// Assert.notNull(nameServer, "Property 'nameServer' is required"); +// Assert.notNull(topic, "Property 'topic' is required"); +// +// consumer = new DefaultMQPushConsumer(consumerGroup); +// consumer.setNamesrvAddr(nameServer); +// consumer.setConsumeThreadMax(consumeThreadMax); +// if (consumeThreadMax < consumer.getConsumeThreadMin()) { +// consumer.setConsumeThreadMin(consumeThreadMax); +// } +// +// consumer.setMessageModel(messageModel); +// +// switch (selectorType) { +// case TAG: +// consumer.subscribe(topic, selectorExpress); +// break; +// case SQL92: +// consumer.subscribe(topic, MessageSelector.bySql(selectorExpress)); +// break; +// default: +// throw new IllegalArgumentException("Property 'selectorType' was wrong."); +// } +// +// switch (consumeMode) { +// case ORDERLY: +// consumer.setMessageListener(new DefaultMessageListenerOrderly()); +// break; +// case CONCURRENTLY: +// consumer.setMessageListener(new DefaultMessageListenerConcurrently()); +// break; +// default: +// throw new IllegalArgumentException("Property 'consumeMode' was wrong."); +// } +// +// // provide an entryway to custom setting RocketMQ consumer +// if (rocketMQListener instanceof AliyunRocketMQPushConsumerLifecycleListener) { +// ((AliyunRocketMQPushConsumerLifecycleListener) rocketMQListener).prepareStart(consumer); +// } +// +// } +// +//} diff --git a/src/main/java/zteits/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java b/src/main/java/zteits/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java new file mode 100644 index 0000000..f535eff --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/core/DefaultRocketMQListenerContainerConstants.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter.core; + +/** + * Constants Created by aqlu on 2017/11/16. + */ +public final class DefaultRocketMQListenerContainerConstants { + public static final String PROP_NAMESERVER = "nameServer"; + public static final String PROP_TOPIC = "topic"; + public static final String PROP_CONSUMER_GROUP = "consumerGroup"; + public static final String PROP_CONSUME_MODE = "consumeMode"; + public static final String PROP_CONSUME_THREAD_MAX = "consumeThreadMax"; + public static final String PROP_MESSAGE_MODEL = "messageModel"; + public static final String PROP_SELECTOR_EXPRESS = "selectorExpress"; + public static final String PROP_SELECTOR_TYPE = "selectorType"; + public static final String PROP_ROCKETMQ_LISTENER = "rocketMQListener"; + public static final String PROP_OBJECT_MAPPER = "objectMapper"; + public static final String METHOD_DESTROY = "destroy"; + public static final String PROP_ROCKETMQ_TEMPLATE = "rocketMQTemplate"; + public static final String PROP_ONS_Addr = "onsAddr"; + public static final String PROP_ACCESS_KEY = "accessKey"; + public static final String PROP_SECRET_KEY = "secretKey"; + public static final String PROP_NAMESRV_Addr = "nameSrvAddr"; + + /** + * 环境前缀 + */ + public static final String PROP_ENVIRONMENT_PREFIX = "environmentPrefix"; + /** + * 消息消费失败发送的主题 + */ + public final static String CONSUMEFAILED_TOPIC = "ZTEITS_RNT_CLOUD"; + /** + * 消息消费失败发送的tag + */ + public final static String CONSUMEFAILED_TAG = "ConsumeMsgFailed"; +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java b/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java new file mode 100644 index 0000000..c2a4f54 --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQConsumerLifecycleListener.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter.core; + +public interface RocketMQConsumerLifecycleListener { + void prepareStart(final T consumer); +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQListener.java b/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQListener.java new file mode 100644 index 0000000..6e20c8b --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQListener.java @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter.core; + +public interface RocketMQListener { + /** + * 消费消息接口,由应用来实现
+ * 网络抖动等不稳定的情形可能会带来消息重复,对重复消息敏感的业务可对消息做幂等处理
+ * 没有异常则表示处理成功,否在处理失败,处理失败时阿里云MQ会重复调用,最多调用16次,如果16次依然失败,则不再调用。 + */ + void onMessage(T message); +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQListenerContainer.java b/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQListenerContainer.java new file mode 100644 index 0000000..a986167 --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQListenerContainer.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter.core; + +import org.springframework.beans.factory.DisposableBean; + +public interface RocketMQListenerContainer extends DisposableBean { + + /** + * Setup the message listener to use. Throws an {@link IllegalArgumentException} if that message listener type is + * not supported. + */ + void setupMessageListener(RocketMQListener messageListener); +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQTemplate.java b/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQTemplate.java new file mode 100644 index 0000000..6004d64 --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/core/RocketMQTemplate.java @@ -0,0 +1,302 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter.core; + +import java.nio.charset.Charset; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.messaging.MessagingException; + +import com.aliyun.openservices.ons.api.Message; +import com.aliyun.openservices.ons.api.Producer; +import com.aliyun.openservices.ons.api.SendCallback; +import com.aliyun.openservices.ons.api.SendResult; +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class RocketMQTemplate implements InitializingBean, DisposableBean { + + @Getter + @Setter + private Producer aliyunProducer; + + @Setter + @Getter + private ObjectMapper objectMapper = new ObjectMapper(); + + @Getter + @Setter + private String charset = "UTF-8"; + + /** + * 环境前缀 + */ + @Setter + private String environmentPrefix; + + /** + * 同步发送消息 + * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. + * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 + * @param key 业务主键 + * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. + * @param userProperties 添加用户自定义属性键值对; 该键值对在消费消费时可被获取.也可用于做SQL属性过滤 + * @param startDeliverTime 设置消息的定时投递时间(绝对时间),最大延迟时间为7天. + *

+ *
    + *
  1. 延迟投递: 延迟3s投递, 设置为: System.currentTimeMillis() + 3000;
  2. + *
  3. 定时投递: 2016-02-01 11:30:00投递, 设置为: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-02-01 11:30:00").getTime()
  4. + *
+ * @return {@link SendResult} + * 2018年3月23日 zhaowg + */ + public SendResult syncSend(String topic,String tag,String keys,Object payload,Map userProperties,Long startDeliverTime) { + if (Objects.isNull(topic) || Objects.isNull(payload)) { + log.info("同步消息发送失败,主题和消息不能为空"); + throw new IllegalArgumentException("同步消息发送失败,主题和消息不能为空"); + } + + try { + long now = System.currentTimeMillis(); + + Message rocketMsg = new Message(environmentPrefix+"_"+topic, tag, keys, convertToRocketMsg(payload)); + if(userProperties!=null && !userProperties.isEmpty()){ + for (Entry userProp : userProperties.entrySet()) { + rocketMsg.putUserProperties(userProp.getKey(), userProp.getValue()); + } + } + if(startDeliverTime!=null){ + //设置定时发送时间 + rocketMsg.setStartDeliverTime(startDeliverTime); + } + //阿里云发送 + SendResult sendResult = aliyunProducer.send(rocketMsg); + long costTime = System.currentTimeMillis() - now; + log.debug("发送消息耗时: {} ms, msgId:{}", costTime, sendResult.getMessageId()); + return sendResult; + } catch (Exception e) { + log.info("同步发送失败. topic:{}, message:{} ", topic, payload); + throw new MessagingException(e.getMessage(), e); + } + } + + /** + * Same to {@link #syncSend(String, String, String, Object, Map, Long)}. + * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. + * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 + * @param key 业务主键 + * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. + * @return {@link SendResult} + * 2018年3月23日 zhaowg + */ + public SendResult syncSend(String topic,String tag,String keys, Object payload) { + return syncSend(topic, tag, keys, payload, null, null); + } + /** + * Same to {@link #syncSend(String, String, String, Object)}. + * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. + * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 + * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. + * @return {@link SendResult} + * 2018年3月23日 zhaowg + */ + public SendResult syncSend(String topic,String tag, Object payload) { + return syncSend(topic, tag,null, payload); + } + + /** + * 异步发送消息 + * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. + * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 + * @param key 业务主键 + * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. + * @param userProperties 添加用户自定义属性键值对; 该键值对在消费消费时可被获取.也可用于做SQL属性过滤 + * @param startDeliverTime 设置消息的定时投递时间(绝对时间),最大延迟时间为7天. + *

+ *
    + *
  1. 延迟投递: 延迟3s投递, 设置为: System.currentTimeMillis() + 3000;
  2. + *
  3. 定时投递: 2016-02-01 11:30:00投递, 设置为: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-02-01 11:30:00").getTime()
  4. + *
+ * @param sendCallback 发送完成要执行的回调函数 + * 2018年3月23日 zhaowg + */ + public void asyncSend(String topic,String tag,String keys,Object payload,Map userProperties, + Long startDeliverTime,SendCallback sendCallback) { + if (Objects.isNull(topic) || Objects.isNull(payload)) { + log.info("异步消息发送失败,主题和消息不能为空"); + throw new IllegalArgumentException("异步消息发送失败,主题和消息不能为空"); + } + try { + long now = System.currentTimeMillis(); + + Message rocketMsg = new Message(environmentPrefix+"_"+topic, tag, keys, convertToRocketMsg(payload)); + if(userProperties!=null && !userProperties.isEmpty()){ + for (Entry userProp : userProperties.entrySet()) { + rocketMsg.putUserProperties(userProp.getKey(), userProp.getValue()); + } + } + if(startDeliverTime!=null){ + //设置定时发送时间 + rocketMsg.setStartDeliverTime(startDeliverTime); + } + //阿里云发送 + aliyunProducer.sendAsync(rocketMsg, sendCallback); + long costTime = System.currentTimeMillis() - now; + log.debug("发送消息耗时: {} ms", costTime); + } catch (Exception e) { + log.info("异步发送失败. topic:{}, message:{} ", topic, payload); + throw new MessagingException(e.getMessage(), e); + } + } + /** + * Same to {@link #asyncSend(String, String, String, Object, Map, Long, SendCallback)}. + * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. + * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 + * @param key 业务主键 + * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. + * @param sendCallback 发送完成要执行的回调函数 + * @return {@link SendResult} + * 2018年3月23日 zhaowg + */ + public void asyncSend(String topic,String tag,String keys, Object payload,SendCallback sendCallback) { + asyncSend(topic, tag, keys, payload, null, null,sendCallback); + } + /** + * Same to {@link #asyncSend(String, String, String, Object,SendCallback)}. + * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. + * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 + * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. + * @param sendCallback 发送完成要执行的回调函数 + * @return {@link SendResult} + * 2018年3月23日 zhaowg + */ + public void asyncSend(String topic,String tag, Object payload,SendCallback sendCallback) { + asyncSend(topic, tag,null, payload,sendCallback); + } + /** + * 服务器不应答,无法保证消息是否成功到达服务器 + * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. + * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 + * @param key 业务主键 + * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. + * @param userProperties 添加用户自定义属性键值对; 该键值对在消费消费时可被获取.也可用于做SQL属性过滤 + * @param startDeliverTime 设置消息的定时投递时间(绝对时间),最大延迟时间为7天. + *

+ *
    + *
  1. 延迟投递: 延迟3s投递, 设置为: System.currentTimeMillis() + 3000;
  2. + *
  3. 定时投递: 2016-02-01 11:30:00投递, 设置为: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-02-01 11:30:00").getTime()
  4. + *
+ * 2018年3月23日 zhaowg + */ + public void sendOneWay(String topic,String tag,String keys,Object payload,Map userProperties, + Long startDeliverTime) { + if (Objects.isNull(topic) || Objects.isNull(payload)) { + log.info("sendOneWay消息发送失败,主题和消息不能为空"); + throw new IllegalArgumentException("sendOneWay消息发送失败,主题和消息不能为空"); + } + try { + long now = System.currentTimeMillis(); + + Message rocketMsg = new Message(environmentPrefix+"_"+topic, tag, keys, convertToRocketMsg(payload)); + if(userProperties!=null && !userProperties.isEmpty()){ + for (Entry userProp : userProperties.entrySet()) { + rocketMsg.putUserProperties(userProp.getKey(), userProp.getValue()); + } + } + if(startDeliverTime!=null){ + //设置定时发送时间 + rocketMsg.setStartDeliverTime(startDeliverTime); + } + //阿里云发送 + aliyunProducer.sendOneway(rocketMsg); + long costTime = System.currentTimeMillis() - now; + log.debug("发送消息耗时: {} ms", costTime); + } catch (Exception e) { + log.info("sendOneWay发送失败. topic:{}, message:{} ", topic, payload); + throw new MessagingException(e.getMessage(), e); + } + } + /** + * Same to {@link #sendOneWay(String, String, String, Object, Map, Long)}. + * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. + * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 + * @param key 业务主键 + * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. + * 2018年3月23日 zhaowg + */ + public void sendOneWay(String topic,String tag,String keys, Object payload) { + sendOneWay(topic, tag, keys, payload, null, null); + } + /** + * Same to {@link #sendOneWay(String, String, String, Object)}. + * @param topic 消息主题, 最长不超过255个字符; 由a-z, A-Z, 0-9, 以及中划线"-"和下划线"_"构成. + * @param tag 消息标签, 请使用合法标识符, 尽量简短且见名知意 + * @param payload 消息体, 消息体长度默认不超过4M, 具体请参阅集群部署文档描述. + * 2018年3月23日 zhaowg + */ + public void sendOneWay(String topic,String tag, Object payload) { + sendOneWay(topic, tag,null, payload); + } + + @Override + public void afterPropertiesSet() throws Exception { + if(aliyunProducer != null){ + log.info("开始启动阿里云[环境标识:"+environmentPrefix+"]生产者"); + aliyunProducer.start(); + } + } + + /** + * 转换对象为字节 + * @param msgObj + * @return + * 2018年3月23日 zhaowg + */ + private byte[] convertToRocketMsg(Object msgObj) { + byte[] payloads; + + if (msgObj instanceof String) { + payloads = ((String) msgObj).getBytes(Charset.forName(charset)); + } else { + try { + String jsonObj = this.objectMapper.writeValueAsString(msgObj); + payloads = jsonObj.getBytes(Charset.forName(charset)); + } catch (Exception e) { + throw new RuntimeException("convert to RocketMQ message failed.", e); + } + } + return payloads; + } + + + @Override + public void destroy() { + if(Objects.nonNull(aliyunProducer)){ + log.info("开始关闭阿里云[环境标识:"+environmentPrefix+"]生产者"); + aliyunProducer.shutdown(); + } + } +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/enums/ConsumeMode.java b/src/main/java/zteits/rocketmq/spring/starter/enums/ConsumeMode.java new file mode 100644 index 0000000..3795b7d --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/enums/ConsumeMode.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter.enums; + +public enum ConsumeMode { + /** + * 同时接收异步发送的消息 + */ + CONCURRENTLY, + + /** + * 顺序接收消息,一个队列,一个线程 + */ + ORDERLY, + + /** + * 批量接收发送的消息,允许自定义范围为[1, 32], 实际消费数量可能小于该值 + */ + BATCH +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/enums/SelectorType.java b/src/main/java/zteits/rocketmq/spring/starter/enums/SelectorType.java new file mode 100644 index 0000000..0b5b4f4 --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/enums/SelectorType.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package zteits.rocketmq.spring.starter.enums; + +import com.aliyun.openservices.shade.com.alibaba.rocketmq.common.filter.ExpressionType; + +public enum SelectorType { + + /** + * @see ExpressionType#TAG + */ + TAG, + + /** + * @see ExpressionType#SQL92 + * 注释by zwg 暂时不支持,原因:阿里云提供的ons-client最新版本1.7.4支持,但是该包有问题,引入后工程无法打包,目前使用的1.7.1,该版本不支持SQL92过滤 + */ + //SQL92 +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/exception/ConvertMsgException.java b/src/main/java/zteits/rocketmq/spring/starter/exception/ConvertMsgException.java new file mode 100644 index 0000000..4e75358 --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/exception/ConvertMsgException.java @@ -0,0 +1,27 @@ +package zteits.rocketmq.spring.starter.exception; + +public class ConvertMsgException extends RuntimeException{ + + private static final long serialVersionUID = 1L; + + public ConvertMsgException() { + super(); + } + + public ConvertMsgException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public ConvertMsgException(String message, Throwable cause) { + super(message, cause); + } + + public ConvertMsgException(String message) { + super(message); + } + + public ConvertMsgException(Throwable cause) { + super(cause); + } + +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/msgvo/ConsumeFailedMsgVO.java b/src/main/java/zteits/rocketmq/spring/starter/msgvo/ConsumeFailedMsgVO.java new file mode 100644 index 0000000..ec22372 --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/msgvo/ConsumeFailedMsgVO.java @@ -0,0 +1,149 @@ +package zteits.rocketmq.spring.starter.msgvo; + +import java.io.Serializable; +import java.util.Date; + +public class ConsumeFailedMsgVO implements Serializable{ + + private static final long serialVersionUID = 1L; + + /**消息ID*/ + private String msgId; + + /**消息主题*/ + private String topic; + + /**消息标签描述*/ + private String tag; + + /**消费组*/ + private String consumeGroup; + + /**消费者ip*/ + private String consumeIp; + + /**消费开始时间*/ + private Date consumeBeginTime; + + /**消费结束时间*/ + private Date consumeEndTime; + + /**消息关键字*/ + private String msgKeys; + + /**重复消费次数*/ + private Integer reconsumeTimes; + + /**消费失败错误信息*/ + private String cunsumerErrMsg; + + /**消息内容*/ + private String msg; + + public String getCunsumerErrMsg() { + return cunsumerErrMsg; + } + + public void setCunsumerErrMsg(String cunsumerErrMsg) { + this.cunsumerErrMsg = cunsumerErrMsg; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + /**获取消息ID*/ + public String getMsgId() { + return msgId; + } + + /**设置消息ID*/ + public void setMsgId(String msgId) { + this.msgId = msgId == null ? null : msgId.trim(); + } + + /**获取消息主题*/ + public String getTopic() { + return topic; + } + + /**设置消息主题*/ + public void setTopic(String topic) { + this.topic = topic == null ? null : topic.trim(); + } + + /**获取消息标签描述*/ + public String getTag() { + return tag; + } + + /**设置消息标签描述*/ + public void setTag(String tag) { + this.tag = tag == null ? null : tag.trim(); + } + + /**获取消费组*/ + public String getConsumeGroup() { + return consumeGroup; + } + + /**设置消费组*/ + public void setConsumeGroup(String consumeGroup) { + this.consumeGroup = consumeGroup == null ? null : consumeGroup.trim(); + } + + /**获取消费者ip*/ + public String getConsumeIp() { + return consumeIp; + } + + /**设置消费者ip*/ + public void setConsumeIp(String consumeIp) { + this.consumeIp = consumeIp == null ? null : consumeIp.trim(); + } + + /**获取消费开始时间*/ + public Date getConsumeBeginTime() { + return consumeBeginTime; + } + + /**设置消费开始时间*/ + public void setConsumeBeginTime(Date consumeBeginTime) { + this.consumeBeginTime = consumeBeginTime; + } + + /**获取消费结束时间*/ + public Date getConsumeEndTime() { + return consumeEndTime; + } + + /**设置消费结束时间*/ + public void setConsumeEndTime(Date consumeEndTime) { + this.consumeEndTime = consumeEndTime; + } + + /**获取消息关键字*/ + public String getMsgKeys() { + return msgKeys; + } + + /**设置消息关键字*/ + public void setMsgKeys(String msgKeys) { + this.msgKeys = msgKeys == null ? null : msgKeys.trim(); + } + + /**获取重复消费次数*/ + public Integer getReconsumeTimes() { + return reconsumeTimes; + } + + /**设置重复消费次数*/ + public void setReconsumeTimes(Integer reconsumeTimes) { + this.reconsumeTimes = reconsumeTimes; + } + +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/utils/ExceptionUtil.java b/src/main/java/zteits/rocketmq/spring/starter/utils/ExceptionUtil.java new file mode 100644 index 0000000..70818ea --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/utils/ExceptionUtil.java @@ -0,0 +1,21 @@ +package zteits.rocketmq.spring.starter.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public class ExceptionUtil { + + public static String getTrace(Throwable t) { + StringBuffer buffer = new StringBuffer(); + if(t==null){ + return ""; + } + StringWriter stringWriter = new StringWriter(); + PrintWriter writer = new PrintWriter(stringWriter); + t.printStackTrace(writer); + //设置堆栈信息 + buffer.append("堆栈信息为:" + stringWriter.getBuffer().toString()); + return buffer.toString(); + } + +} diff --git a/src/main/java/zteits/rocketmq/spring/starter/utils/IPUtil.java b/src/main/java/zteits/rocketmq/spring/starter/utils/IPUtil.java new file mode 100644 index 0000000..f9b4e80 --- /dev/null +++ b/src/main/java/zteits/rocketmq/spring/starter/utils/IPUtil.java @@ -0,0 +1,49 @@ +package zteits.rocketmq.spring.starter.utils; + +import java.net.InetAddress; + +import org.springframework.util.StringUtils; + +/** + * Copyright: Copyright (c) 2017 zteits + * + * @ClassName: com.clouds.constants.utils + * @Description: IP地址工具类 + * @version: v1.0.0 + * @author: atao + * @date: 2017/4/26 上午9:25 + * Modification History: + * Date Author Version Description + * ---------------------------------------------------------* + * 2017/4/26 atao v1.0.0 创建 + */ +public class IPUtil { + private static String localHost; + private static String localHostName; + + public static String getLocalHost() { + if (StringUtils.isEmpty(localHost)) { + getLocalHostInfo(); + } + return localHost; + } + + public static String getLocalHostNome() { + if (StringUtils.isEmpty(localHostName)) { + getLocalHostInfo(); + } + return localHostName; + } + + private static void getLocalHostInfo() { + try { + InetAddress ia = InetAddress.getLocalHost(); + localHostName = ia.getHostName(); + localHost = ia.getHostAddress(); + } catch (Exception e) { + //获取当前地址失败 + e.printStackTrace(); + } + } + +} diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories index 6ab3023..7247f53 100644 --- a/src/main/resources/META-INF/spring.factories +++ b/src/main/resources/META-INF/spring.factories @@ -1,2 +1,2 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.apache.rocketmq.spring.starter.AliyunRocketMQAutoConfiguration \ No newline at end of file +zteits.rocketmq.spring.starter.AliyunRocketMQAutoConfiguration diff --git a/src/test/java/org/apache/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java b/src/test/java/org/apache/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java deleted file mode 100644 index e4e2c41..0000000 --- a/src/test/java/org/apache/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java +++ /dev/null @@ -1,184 +0,0 @@ -///* -// * Licensed to the Apache Software Foundation (ASF) under one or more -// * contributor license agreements. See the NOTICE file distributed with -// * this work for additional information regarding copyright ownership. -// * The ASF licenses this file to You under the Apache License, Version 2.0 -// * (the "License"); you may not use this file except in compliance with -// * the License. You may obtain a copy of the License at -// * -// * http://www.apache.org/licenses/LICENSE-2.0 -// * -// * Unless required by applicable law or agreed to in writing, software -// * distributed under the License is distributed on an "AS IS" BASIS, -// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// * See the License for the specific language governing permissions and -// * limitations under the License. -// */ -// -//package org.apache.rocketmq.spring.starter; -// -//import com.fasterxml.jackson.databind.ObjectMapper; -//import org.apache.rocketmq.spring.starter.annotation.RocketMQMessageListener; -//import org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainer; -//import org.apache.rocketmq.spring.starter.core.RocketMQListener; -//import org.apache.rocketmq.spring.starter.core.RocketMQTemplate; -//import org.apache.rocketmq.spring.starter.enums.ConsumeMode; -//import org.apache.rocketmq.spring.starter.enums.SelectorType; -//import org.apache.rocketmq.client.producer.DefaultMQProducer; -//import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; -//import org.junit.After; -//import org.junit.Test; -//import org.springframework.beans.factory.support.BeanDefinitionBuilder; -//import org.springframework.boot.test.util.EnvironmentTestUtils; -//import org.springframework.context.annotation.AnnotationConfigApplicationContext; -// -//import static org.assertj.core.api.Assertions.assertThat; -// -//public class RocketMQAutoConfigurationTests { -// -// private static final String TEST_CONSUMER_GROUP = "my_consumer"; -// -// private static final String TEST_TOPIC = "test-topic"; -// -// private AnnotationConfigApplicationContext context; -// -// @Test -// public void rocketMQTemplate() { -// -// load("spring.rocketmq.nameServer=127.0.0.1:9876", -// "spring.rocketmq.producer.group=my_group", -// "spring.rocketmq.producer.send-msg-timeout=30000", -// "spring.rocketmq.producer.retry-times-when-send-async-failed=1", -// "spring.rocketmq.producer.compress-msg-body-over-howmuch=1024", -// "spring.rocketmq.producer.max-message-size=10240", -// "spring.rocketmq.producer.retry-another-broker-when-not-store-ok=true", -// "spring.rocketmq.producer.retry-times-when-send-failed=1"); -// -// assertThat(this.context.containsBean("rocketMQMessageObjectMapper")).isTrue(); -// assertThat(this.context.containsBean("mqProducer")).isTrue(); -// assertThat(this.context.containsBean("rocketMQTemplate")).isTrue(); -// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); -// -// RocketMQTemplate rocketMQTemplate = this.context.getBean(RocketMQTemplate.class); -// ObjectMapper objectMapper = this.context.getBean("rocketMQMessageObjectMapper", ObjectMapper.class); -// assertThat(rocketMQTemplate.getObjectMapper()).isEqualTo(objectMapper); -// -// DefaultMQProducer defaultMQProducer = rocketMQTemplate.getProducer(); -// -// assertThat(defaultMQProducer.getNamesrvAddr()).isEqualTo("127.0.0.1:9876"); -// assertThat(defaultMQProducer.getProducerGroup()).isEqualTo("my_group"); -// assertThat(defaultMQProducer.getSendMsgTimeout()).isEqualTo(30000); -// assertThat(defaultMQProducer.getRetryTimesWhenSendAsyncFailed()).isEqualTo(1); -// assertThat(defaultMQProducer.getCompressMsgBodyOverHowmuch()).isEqualTo(1024); -// assertThat(defaultMQProducer.getMaxMessageSize()).isEqualTo(10240); -// assertThat(defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()).isTrue(); -// assertThat(defaultMQProducer.getRetryTimesWhenSendFailed()).isEqualTo(1); -// } -// -// @Test -// public void enableProducer() { -// load(); -// assertThat(this.context.containsBean("mqProducer")).isFalse(); -// assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); -// closeContext(); -// -// load("spring.rocketmq.nameServer=127.0.0.1:9876"); -// assertThat(this.context.containsBean("mqProducer")).isFalse(); -// assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); -// closeContext(); -// -// load("spring.rocketmq.producer.group=my_group"); -// assertThat(this.context.containsBean("mqProducer")).isFalse(); -// assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); -// closeContext(); -// -// load("spring.rocketmq.nameServer=127.0.0.1:9876", "spring.rocketmq.producer.group=my_group"); -// assertThat(this.context.containsBean("mqProducer")).isTrue(); -// assertThat(this.context.containsBean("rocketMQTemplate")).isEqualTo(true); -// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); -// } -// -// @Test -// public void enableConsumer() { -// load(); -// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); -// closeContext(); -// -// load("spring.rocketmq.nameServer=127.0.0.1:9876"); -// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); -// closeContext(); -// -// load(false); -// this.context.registerBeanDefinition("myListener", -// BeanDefinitionBuilder.rootBeanDefinition(MyListener.class).getBeanDefinition()); -// this.context.refresh(); -// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); -// closeContext(); -// -// load(false, "spring.rocketmq.nameServer=127.0.0.1:9876"); -// this.context.registerBeanDefinition("myListener", -// BeanDefinitionBuilder.rootBeanDefinition(MyListener.class).getBeanDefinition()); -// this.context.refresh(); -// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isNotEmpty(); -// assertThat(this.context.containsBean(DefaultRocketMQListenerContainer.class.getName() + "_1")).isTrue(); -// assertThat(this.context.containsBean("mqProducer")).isFalse(); -// assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); -// -// } -// -// @Test -// public void listenerContainer() { -// load(false, "spring.rocketmq.nameServer=127.0.0.1:9876"); -// BeanDefinitionBuilder beanBuilder = BeanDefinitionBuilder.rootBeanDefinition(MyListener.class); -// this.context.registerBeanDefinition("myListener", beanBuilder.getBeanDefinition()); -// this.context.refresh(); -// -// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isNotEmpty(); -// assertThat(this.context.containsBean(DefaultRocketMQListenerContainer.class.getName() + "_1")).isTrue(); -// -// DefaultRocketMQListenerContainer listenerContainer = -// this.context.getBean(DefaultRocketMQListenerContainer.class.getName() + "_1", -// DefaultRocketMQListenerContainer.class); -// ObjectMapper objectMapper = this.context.getBean("rocketMQMessageObjectMapper", ObjectMapper.class); -// assertThat(listenerContainer.getObjectMapper()).isEqualTo(objectMapper); -// assertThat(listenerContainer.getConsumeMode()).isEqualTo(ConsumeMode.CONCURRENTLY); -// assertThat(listenerContainer.getSelectorType()).isEqualTo(SelectorType.TAG); -// assertThat(listenerContainer.getSelectorExpress()).isEqualTo("*"); -// assertThat(listenerContainer.getConsumerGroup()).isEqualTo(TEST_CONSUMER_GROUP); -// assertThat(listenerContainer.getTopic()).isEqualTo(TEST_TOPIC); -// assertThat(listenerContainer.getNameServer()).isEqualTo("127.0.0.1:9876"); -// assertThat(listenerContainer.getMessageModel()).isEqualTo(MessageModel.CLUSTERING); -// assertThat(listenerContainer.getConsumeThreadMax()).isEqualTo(1); -// } -// -// @After -// public void closeContext() { -// if (this.context != null) { -// this.context.close(); -// } -// } -// -// @RocketMQMessageListener(consumerGroup = TEST_CONSUMER_GROUP, topic = TEST_TOPIC, consumeThreadMax = 1) -// private static class MyListener implements RocketMQListener { -// -// @Override -// public void onMessage(String message) { -// System.out.println(message); -// } -// } -// -// private void load(boolean refresh, String... environment) { -// AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); -// ctx.register(RocketMQAutoConfiguration.class); -// EnvironmentTestUtils.addEnvironment(ctx, environment); -// if (refresh) { -// ctx.refresh(); -// } -// this.context = ctx; -// } -// -// private void load(String... environment) { -// load(true, environment); -// } -//} -// diff --git a/src/test/java/zteits/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java b/src/test/java/zteits/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java new file mode 100644 index 0000000..e4e2c41 --- /dev/null +++ b/src/test/java/zteits/rocketmq/spring/starter/RocketMQAutoConfigurationTests.java @@ -0,0 +1,184 @@ +///* +// * Licensed to the Apache Software Foundation (ASF) under one or more +// * contributor license agreements. See the NOTICE file distributed with +// * this work for additional information regarding copyright ownership. +// * The ASF licenses this file to You under the Apache License, Version 2.0 +// * (the "License"); you may not use this file except in compliance with +// * the License. You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// */ +// +//package org.apache.rocketmq.spring.starter; +// +//import com.fasterxml.jackson.databind.ObjectMapper; +//import org.apache.rocketmq.spring.starter.annotation.RocketMQMessageListener; +//import org.apache.rocketmq.spring.starter.core.DefaultRocketMQListenerContainer; +//import org.apache.rocketmq.spring.starter.core.RocketMQListener; +//import org.apache.rocketmq.spring.starter.core.RocketMQTemplate; +//import org.apache.rocketmq.spring.starter.enums.ConsumeMode; +//import org.apache.rocketmq.spring.starter.enums.SelectorType; +//import org.apache.rocketmq.client.producer.DefaultMQProducer; +//import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; +//import org.junit.After; +//import org.junit.Test; +//import org.springframework.beans.factory.support.BeanDefinitionBuilder; +//import org.springframework.boot.test.util.EnvironmentTestUtils; +//import org.springframework.context.annotation.AnnotationConfigApplicationContext; +// +//import static org.assertj.core.api.Assertions.assertThat; +// +//public class RocketMQAutoConfigurationTests { +// +// private static final String TEST_CONSUMER_GROUP = "my_consumer"; +// +// private static final String TEST_TOPIC = "test-topic"; +// +// private AnnotationConfigApplicationContext context; +// +// @Test +// public void rocketMQTemplate() { +// +// load("spring.rocketmq.nameServer=127.0.0.1:9876", +// "spring.rocketmq.producer.group=my_group", +// "spring.rocketmq.producer.send-msg-timeout=30000", +// "spring.rocketmq.producer.retry-times-when-send-async-failed=1", +// "spring.rocketmq.producer.compress-msg-body-over-howmuch=1024", +// "spring.rocketmq.producer.max-message-size=10240", +// "spring.rocketmq.producer.retry-another-broker-when-not-store-ok=true", +// "spring.rocketmq.producer.retry-times-when-send-failed=1"); +// +// assertThat(this.context.containsBean("rocketMQMessageObjectMapper")).isTrue(); +// assertThat(this.context.containsBean("mqProducer")).isTrue(); +// assertThat(this.context.containsBean("rocketMQTemplate")).isTrue(); +// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); +// +// RocketMQTemplate rocketMQTemplate = this.context.getBean(RocketMQTemplate.class); +// ObjectMapper objectMapper = this.context.getBean("rocketMQMessageObjectMapper", ObjectMapper.class); +// assertThat(rocketMQTemplate.getObjectMapper()).isEqualTo(objectMapper); +// +// DefaultMQProducer defaultMQProducer = rocketMQTemplate.getProducer(); +// +// assertThat(defaultMQProducer.getNamesrvAddr()).isEqualTo("127.0.0.1:9876"); +// assertThat(defaultMQProducer.getProducerGroup()).isEqualTo("my_group"); +// assertThat(defaultMQProducer.getSendMsgTimeout()).isEqualTo(30000); +// assertThat(defaultMQProducer.getRetryTimesWhenSendAsyncFailed()).isEqualTo(1); +// assertThat(defaultMQProducer.getCompressMsgBodyOverHowmuch()).isEqualTo(1024); +// assertThat(defaultMQProducer.getMaxMessageSize()).isEqualTo(10240); +// assertThat(defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()).isTrue(); +// assertThat(defaultMQProducer.getRetryTimesWhenSendFailed()).isEqualTo(1); +// } +// +// @Test +// public void enableProducer() { +// load(); +// assertThat(this.context.containsBean("mqProducer")).isFalse(); +// assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); +// closeContext(); +// +// load("spring.rocketmq.nameServer=127.0.0.1:9876"); +// assertThat(this.context.containsBean("mqProducer")).isFalse(); +// assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); +// closeContext(); +// +// load("spring.rocketmq.producer.group=my_group"); +// assertThat(this.context.containsBean("mqProducer")).isFalse(); +// assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); +// closeContext(); +// +// load("spring.rocketmq.nameServer=127.0.0.1:9876", "spring.rocketmq.producer.group=my_group"); +// assertThat(this.context.containsBean("mqProducer")).isTrue(); +// assertThat(this.context.containsBean("rocketMQTemplate")).isEqualTo(true); +// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); +// } +// +// @Test +// public void enableConsumer() { +// load(); +// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); +// closeContext(); +// +// load("spring.rocketmq.nameServer=127.0.0.1:9876"); +// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); +// closeContext(); +// +// load(false); +// this.context.registerBeanDefinition("myListener", +// BeanDefinitionBuilder.rootBeanDefinition(MyListener.class).getBeanDefinition()); +// this.context.refresh(); +// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isEmpty(); +// closeContext(); +// +// load(false, "spring.rocketmq.nameServer=127.0.0.1:9876"); +// this.context.registerBeanDefinition("myListener", +// BeanDefinitionBuilder.rootBeanDefinition(MyListener.class).getBeanDefinition()); +// this.context.refresh(); +// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isNotEmpty(); +// assertThat(this.context.containsBean(DefaultRocketMQListenerContainer.class.getName() + "_1")).isTrue(); +// assertThat(this.context.containsBean("mqProducer")).isFalse(); +// assertThat(this.context.containsBean("rocketMQTemplate")).isFalse(); +// +// } +// +// @Test +// public void listenerContainer() { +// load(false, "spring.rocketmq.nameServer=127.0.0.1:9876"); +// BeanDefinitionBuilder beanBuilder = BeanDefinitionBuilder.rootBeanDefinition(MyListener.class); +// this.context.registerBeanDefinition("myListener", beanBuilder.getBeanDefinition()); +// this.context.refresh(); +// +// assertThat(this.context.getBeansOfType(DefaultRocketMQListenerContainer.class)).isNotEmpty(); +// assertThat(this.context.containsBean(DefaultRocketMQListenerContainer.class.getName() + "_1")).isTrue(); +// +// DefaultRocketMQListenerContainer listenerContainer = +// this.context.getBean(DefaultRocketMQListenerContainer.class.getName() + "_1", +// DefaultRocketMQListenerContainer.class); +// ObjectMapper objectMapper = this.context.getBean("rocketMQMessageObjectMapper", ObjectMapper.class); +// assertThat(listenerContainer.getObjectMapper()).isEqualTo(objectMapper); +// assertThat(listenerContainer.getConsumeMode()).isEqualTo(ConsumeMode.CONCURRENTLY); +// assertThat(listenerContainer.getSelectorType()).isEqualTo(SelectorType.TAG); +// assertThat(listenerContainer.getSelectorExpress()).isEqualTo("*"); +// assertThat(listenerContainer.getConsumerGroup()).isEqualTo(TEST_CONSUMER_GROUP); +// assertThat(listenerContainer.getTopic()).isEqualTo(TEST_TOPIC); +// assertThat(listenerContainer.getNameServer()).isEqualTo("127.0.0.1:9876"); +// assertThat(listenerContainer.getMessageModel()).isEqualTo(MessageModel.CLUSTERING); +// assertThat(listenerContainer.getConsumeThreadMax()).isEqualTo(1); +// } +// +// @After +// public void closeContext() { +// if (this.context != null) { +// this.context.close(); +// } +// } +// +// @RocketMQMessageListener(consumerGroup = TEST_CONSUMER_GROUP, topic = TEST_TOPIC, consumeThreadMax = 1) +// private static class MyListener implements RocketMQListener { +// +// @Override +// public void onMessage(String message) { +// System.out.println(message); +// } +// } +// +// private void load(boolean refresh, String... environment) { +// AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); +// ctx.register(RocketMQAutoConfiguration.class); +// EnvironmentTestUtils.addEnvironment(ctx, environment); +// if (refresh) { +// ctx.refresh(); +// } +// this.context = ctx; +// } +// +// private void load(String... environment) { +// load(true, environment); +// } +//} +// -- libgit2 0.21.4