├── NOTICE ├── .gitignore ├── rocketmq-spring-boot ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring.factories │ │ └── java │ │ │ └── org │ │ │ └── apache │ │ │ └── rocketmq │ │ │ └── spring │ │ │ ├── core │ │ │ ├── RocketMQListener.java │ │ │ ├── RocketMQLocalTransactionState.java │ │ │ ├── RocketMQPushConsumerLifecycleListener.java │ │ │ └── RocketMQLocalTransactionListener.java │ │ │ ├── support │ │ │ ├── RocketMQConsumerLifecycleListener.java │ │ │ ├── RocketMQListenerContainer.java │ │ │ ├── RocketMQHeaders.java │ │ │ └── RocketMQUtil.java │ │ │ ├── annotation │ │ │ ├── SelectorType.java │ │ │ ├── ConsumeMode.java │ │ │ ├── MessageModel.java │ │ │ ├── RocketMQMessageListener.java │ │ │ └── RocketMQTransactionListener.java │ │ │ ├── config │ │ │ ├── RocketMQConfigUtils.java │ │ │ ├── TransactionHandlerRegistry.java │ │ │ ├── TransactionHandler.java │ │ │ └── RocketMQTransactionAnnotationProcessor.java │ │ │ └── autoconfigure │ │ │ ├── JacksonFallbackConfiguration.java │ │ │ ├── RocketMQProperties.java │ │ │ ├── RocketMQAutoConfiguration.java │ │ │ └── ListenerContainerConfiguration.java │ └── test │ │ └── java │ │ └── org │ │ └── apache │ │ └── rocketmq │ │ └── spring │ │ ├── support │ │ ├── DefaultRocketMQListenerContainerTest.java │ │ └── RocketMQUtilTest.java │ │ └── autoconfigure │ │ └── RocketMQAutoConfigurationTest.java ├── README.md └── pom.xml ├── rocketmq-spring-boot-samples ├── rocketmq-produce-demo │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── application.properties │ │ │ └── java │ │ │ └── org │ │ │ └── apache │ │ │ └── rocketmq │ │ │ └── samples │ │ │ └── springboot │ │ │ ├── domain │ │ │ └── OrderPaidEvent.java │ │ │ └── ProducerApplication.java │ └── pom.xml ├── rocketmq-consume-demo │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── application.properties │ │ │ └── java │ │ │ └── org │ │ │ └── apache │ │ │ └── rocketmq │ │ │ └── samples │ │ │ └── springboot │ │ │ ├── ConsumerApplication.java │ │ │ ├── consumer │ │ │ ├── StringConsumer.java │ │ │ ├── StringTransactionalConsumer.java │ │ │ ├── OrderPaidEventConsumer.java │ │ │ ├── Checker.java │ │ │ └── MessageExtConsumer.java │ │ │ └── domain │ │ │ └── OrderPaidEvent.java │ └── pom.xml ├── README_zh_CN.md ├── README.md ├── style │ ├── copyright │ │ ├── Apache.xml │ │ └── profiles_settings.xml │ ├── rmq_checkstyle.xml │ └── rmq_codeStyle.xml ├── pom.xml └── LICENSE ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── style ├── copyright │ ├── Apache.xml │ └── profiles_settings.xml ├── rmq_checkstyle.xml └── rmq_codeStyle.xml ├── rocketmq-spring-boot-starter └── pom.xml ├── pom.xml ├── rocketmq-spring-boot-parent └── pom.xml ├── README_zh_CN.md ├── LICENSE └── README.md /NOTICE: -------------------------------------------------------------------------------- 1 | Apache RocketMQ 2 | Copyright 2016-2018 The Apache Software Foundation 3 | 4 | This product includes software developed at 5 | The Apache Software Foundation (http://www.apache.org/). 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .classpath 3 | .project 4 | .settings/ 5 | target/ 6 | *.log* 7 | *.iml 8 | .idea/ 9 | *.versionsBackup 10 | !NOTICE-BIN 11 | !LICENSE-BIN 12 | .DS_Store 13 | .vscode 14 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-produce-demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | rocketmq.name-server=localhost:9876 2 | rocketmq.producer.group=my-group1 3 | 4 | # properties used in the application 5 | demo.rocketmq.topic=string-topic 6 | demo.rocketmq.orderTopic=order-paid-topic 7 | demo.rocketmq.msgExtTopic=message-ext-topic 8 | demo.rocketmq.transTopic=spring-transaction-topic -------------------------------------------------------------------------------- /rocketmq-spring-boot/README.md: -------------------------------------------------------------------------------- 1 | # RocketMQ Spring Boot Support 2 | This project provides auto-configuration for the following RocketMQ client: 3 | 4 | - [Produce and Consume Spring Message](../rocketmq-spring-boot-starter) 5 | - [Send (halp) Message in Transaction](../rocketmq-spring-boot-starter) 6 | 7 | For details, please see sample code in the [rocketmq-spring-boot-samples](../rocketmq-spring-boot-samples) -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-consume-demo/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=rocketmq-consume-demo 2 | 3 | rocketmq.name-server=localhost:9876 4 | rocketmq.topic=string-topic 5 | 6 | # properties used in application code 7 | demo.rocketmq.orderTopic=order-paid-topic 8 | demo.rocketmq.msgExtTopic=message-ext-topic 9 | demo.rocketmq.transTopic=spring-transaction-topic 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | The issue tracker is **ONLY** used for bug report and feature request. 2 | 3 | Any question or RocketMQ proposal please use our [mailing lists](http://rocketmq.apache.org/about/contact/). 4 | 5 | **BUG REPORT** 6 | 7 | 1. Please describe the issue you observed: 8 | 9 | - What did you do (The steps to reproduce)? 10 | 11 | - What did you expect to see? 12 | 13 | - What did you see instead? 14 | 15 | 2. Please tell us about your environment: 16 | 17 | 3. Other information (e.g. detailed explanation, logs, related issues, suggestions how to fix, etc): 18 | 19 | **FEATURE REQUEST** 20 | 21 | 1. Please describe the feature you are requesting. 22 | 23 | 2. Provide any additional detail on your proposed use case for this feature. 24 | 25 | 3. Indicate the importance of this issue to you (blocker, must-have, should-have, nice-to-have). Are you currently using any workarounds to address this issue? 26 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/core/RocketMQListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.core; 19 | 20 | public interface RocketMQListener { // 泛型,声明消费的消息类型 21 | 22 | /** 23 | * 消费消息 24 | * 25 | * @param message 消息 26 | */ 27 | void onMessage(T message); 28 | 29 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/core/RocketMQLocalTransactionState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.core; 19 | 20 | public enum RocketMQLocalTransactionState { 21 | 22 | /** 23 | * 已提交 24 | */ 25 | COMMIT, 26 | /** 27 | * 已回滚 28 | */ 29 | ROLLBACK, 30 | /** 31 | * 未知 32 | */ 33 | UNKNOWN 34 | 35 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/support/RocketMQConsumerLifecycleListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.support; 19 | 20 | public interface RocketMQConsumerLifecycleListener { 21 | 22 | /** 23 | * Consumer 准备启动的回调。 24 | * 25 | * 通过实现该方法,可以对 Consumer 做一些其他方法的调用。 26 | * 27 | * @param consumer 消费者 28 | */ 29 | void prepareStart(final T consumer); 30 | 31 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/core/RocketMQPushConsumerLifecycleListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.core; 19 | 20 | import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; 21 | import org.apache.rocketmq.spring.support.RocketMQConsumerLifecycleListener; 22 | 23 | public interface RocketMQPushConsumerLifecycleListener extends RocketMQConsumerLifecycleListener { 24 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/README_zh_CN.md: -------------------------------------------------------------------------------- 1 | # rocketmq-spring-boot-samples 2 | 3 | [English](./README.md) 4 | 5 | [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) 6 | 7 | 这里是一个使用rocketmq-spring-boot-starter的列子。 [rocketmq-spring-boot](https://github.com/apache/rocketmq-spring) 8 | 9 | 10 | ## 在本地运行这个测试例子 11 | 12 | 1. 如上面注意项所述,需要开发者在本地build并安装rocketmq-spring-boot-starter 13 | 14 | 2. 根据RocketMQ官网的quick-start来启动NameServer和Broker,并验证是否启动正确。注意: 测试期间不要停止Broker或者NameServer 15 | http://rocketmq.apache.org/docs/quick-start/ 16 | 17 | 3. 创建测试例子所需要的Topic 18 | ``` 19 | cd YOUR_ROCKETMQ_HOME 20 | 21 | bash bin/mqadmin updateTopic -c DefaultCluster -t string-topic 22 | bash bin/mqadmin updateTopic -c DefaultCluster -t order-paid-topic 23 | bash bin/mqadmin updateTopic -c DefaultCluster -t message-ext-topic 24 | bash bin/mqadmin updateTopic -c DefaultCluster -t spring-transaction-topic 25 | ``` 26 | 27 | 4. 编译并运行测试例子 28 | 29 | ``` 30 | # 打开一个终端窗口,编译并启动发送端 31 | cd rocketmq-produce-demo 32 | mvn clean package 33 | java -jar target/rocketmq-produce-demo-0.0.1-SNAPSHOT.jar 34 | 35 | # 打开另一个终端窗口,编译并启动消费端 36 | cd rocketmq-consume-demo 37 | mvn clean package 38 | java -jar target/rocketmq-consume-demo-0.0.1-SNAPSHOT.jar 39 | ``` 40 | 结合测试代码,观察窗口中消息的发送和接收情况 41 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/README.md: -------------------------------------------------------------------------------- 1 | # rocketmq-spring-boot-samples 2 | 3 | [中文](./README_zh_CN.md) 4 | 5 | [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) 6 | 7 | It's a demo project for how to use [rocketmq-spring-boot](https://github.com/apache/rocketmq-spring) 8 | 9 | Run the test case locally 10 | 1. build and install the rocketmq-spring-boot-starter 11 | 12 | 2. startup rocketmq according to quick-start, verify the namesvr and broker startup correctly, Note: DON'T do "Shutdown Servers" step. 13 | http://rocketmq.apache.org/docs/quick-start/ 14 | 15 | 3. create topics for the demo test cases 16 | ``` 17 | bash bin/mqadmin updateTopic -c DefaultCluster -t string-topic 18 | bash bin/mqadmin updateTopic -c DefaultCluster -t order-paid-topic 19 | bash bin/mqadmin updateTopic -c DefaultCluster -t message-ext-topic 20 | bash bin/mqadmin updateTopic -c DefaultCluster -t spring-transaction-topic 21 | ``` 22 | 4. run tests 23 | 24 | ``` 25 | # open a terminal, run produce 26 | cd rocketmq-produce-demo 27 | mvn clean package 28 | java -jar target/rocketmq-produce-demo-0.0.1-SNAPSHOT.jar 29 | 30 | # open another terminal, run consume 31 | cd rocketmq-consume-demo 32 | mvn clean package 33 | java -jar target/rocketmq-consume-demo-0.0.1-SNAPSHOT.jar 34 | ``` 35 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/annotation/SelectorType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.annotation; 19 | 20 | import org.apache.rocketmq.common.filter.ExpressionType; 21 | 22 | /** 23 | * 选择消息的方式 24 | */ 25 | public enum SelectorType { 26 | 27 | /** 28 | * @see ExpressionType#TAG 29 | * 30 | * 标签 31 | */ 32 | TAG, 33 | 34 | /** 35 | * @see ExpressionType#SQL92 36 | * 37 | * SQL 38 | */ 39 | SQL92 40 | 41 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/annotation/ConsumeMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.annotation; 19 | 20 | /** 21 | * 消费模式 22 | */ 23 | public enum ConsumeMode { 24 | 25 | /** 26 | * Receive asynchronously delivered messages concurrently 27 | * 28 | * 并发消费 29 | */ 30 | CONCURRENTLY, 31 | 32 | /** 33 | * Receive asynchronously delivered messages orderly. one queue, one thread 34 | * 35 | * 顺序消费 36 | */ 37 | ORDERLY 38 | 39 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-consume-demo/src/main/java/org/apache/rocketmq/samples/springboot/ConsumerApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.samples.springboot; 19 | 20 | import org.springframework.boot.SpringApplication; 21 | import org.springframework.boot.autoconfigure.SpringBootApplication; 22 | 23 | /** 24 | * ConsumerApplication 25 | */ 26 | @SpringBootApplication 27 | public class ConsumerApplication { 28 | 29 | public static void main(String[] args) { 30 | SpringApplication.run(ConsumerApplication.class, args); 31 | } 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/annotation/MessageModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.annotation; 19 | 20 | /** 21 | * 消费模型 22 | */ 23 | public enum MessageModel { 24 | 25 | /** 26 | * 广播消费 27 | */ 28 | BROADCASTING("BROADCASTING"), 29 | /** 30 | * 集群消费 31 | */ 32 | CLUSTERING("CLUSTERING"); 33 | 34 | private final String modeCN; 35 | 36 | MessageModel(String modeCN) { 37 | this.modeCN = modeCN; 38 | } 39 | 40 | public String getModeCN() { 41 | return this.modeCN; 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/support/RocketMQListenerContainer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.support; 19 | 20 | import org.apache.rocketmq.spring.core.RocketMQListener; 21 | import org.springframework.beans.factory.DisposableBean; 22 | 23 | public interface RocketMQListenerContainer extends DisposableBean { 24 | 25 | /** 26 | * Setup the message listener to use. Throws an {@link IllegalArgumentException} if that message listener type is 27 | * not supported. 28 | */ 29 | void setupMessageListener(RocketMQListener messageListener); 30 | 31 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/config/RocketMQConfigUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.config; 19 | 20 | public class RocketMQConfigUtils { 21 | 22 | /** 23 | * The bean name of the internally managed RocketMQ transaction annotation processor. 24 | */ 25 | public static final String ROCKETMQ_TRANSACTION_ANNOTATION_PROCESSOR_BEAN_NAME = "org.springframework.rocketmq.spring.starter.internalRocketMQTransAnnotationProcessor"; 26 | 27 | public static final String ROCKETMQ_TRANSACTION_DEFAULT_GLOBAL_NAME = "rocketmq_transaction_default_global_name"; 28 | 29 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-produce-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 22 | 4.0.0 23 | 24 | org.apache.rocketmq 25 | rocketmq-spring-boot-samples 26 | 0.0.1-SNAPSHOT 27 | 28 | 29 | rocketmq-produce-demo 30 | 31 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-consume-demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 22 | 4.0.0 23 | 24 | org.apache.rocketmq 25 | rocketmq-spring-boot-samples 26 | 0.0.1-SNAPSHOT 27 | 28 | 29 | rocketmq-consume-demo 30 | 31 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/core/RocketMQLocalTransactionListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.apache.rocketmq.spring.core; 18 | 19 | import org.springframework.messaging.Message; 20 | 21 | public interface RocketMQLocalTransactionListener { 22 | 23 | /** 24 | * 执行本地事务 25 | * 26 | * @param msg 消息 27 | * @param arg 方法参数 28 | * @return 本地事务状态 29 | */ 30 | RocketMQLocalTransactionState executeLocalTransaction(final Message msg, final Object arg); 31 | 32 | /** 33 | * 检查本地事务状态 34 | * 35 | * @param msg 消息 36 | * @return 本地事务状态 37 | */ 38 | RocketMQLocalTransactionState checkLocalTransaction(final Message msg); 39 | 40 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/autoconfigure/JacksonFallbackConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.autoconfigure; 19 | 20 | import com.fasterxml.jackson.databind.ObjectMapper; 21 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 22 | import org.springframework.context.annotation.Bean; 23 | import org.springframework.context.annotation.Configuration; 24 | 25 | @Configuration 26 | @ConditionalOnMissingBean(ObjectMapper.class) // 不存在 ObjectMapper Bean 时 27 | class JacksonFallbackConfiguration { 28 | 29 | @Bean 30 | public ObjectMapper rocketMQMessageObjectMapper() { 31 | return new ObjectMapper(); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## What is the purpose of the change 2 | 3 | XXXXX 4 | 5 | ## Brief changelog 6 | 7 | XX 8 | 9 | ## Verifying this change 10 | 11 | XXXX 12 | 13 | Follow this checklist to help us incorporate your contribution quickly and easily. Notice, `it would be helpful if you could finish the following 5 checklist(the last one is not necessary)before request the community to review your PR`. 14 | 15 | - [x] Make sure there is a [Github issue](https://github.com/apache/rocketmq/issues) filed for the change (usually before you start working on it). Trivial changes like typos do not require a Github issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue. 16 | - [x] Format the pull request title like `[ISSUE #123] Fix UnknownException when host config not exist`. Each commit in the pull request should have a meaningful subject line and body. 17 | - [x] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. 18 | - [x] Write necessary unit-test(over 80% coverage) to verify your logic correction, more mock a little better when cross module dependency exist. 19 | - [x] Run `mvn -B clean apache-rat:check findbugs:findbugs checkstyle:checkstyle` to make sure basic checks pass. Run `mvn clean install -DskipITs` to make sure unit-test pass. Run `mvn clean test-compile failsafe:integration-test` to make sure integration-test pass. 20 | - [ ] If this contribution is large, please file an [Apache Individual Contributor License Agreement](http://www.apache.org/licenses/#clas). 21 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-consume-demo/src/main/java/org/apache/rocketmq/samples/springboot/consumer/StringConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.samples.springboot.consumer; 19 | 20 | import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; 21 | import org.apache.rocketmq.spring.core.RocketMQListener; 22 | import org.springframework.stereotype.Service; 23 | 24 | /** 25 | * RocketMQMessageListener 26 | */ 27 | @Service 28 | @RocketMQMessageListener(topic = "${demo.rocketmq.topic}", consumerGroup = "string_consumer") 29 | public class StringConsumer implements RocketMQListener { 30 | @Override 31 | public void onMessage(String message) { 32 | System.out.printf("------- StringConsumer received: %s \n", message); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-consume-demo/src/main/java/org/apache/rocketmq/samples/springboot/consumer/StringTransactionalConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.samples.springboot.consumer; 19 | 20 | import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; 21 | import org.apache.rocketmq.spring.core.RocketMQListener; 22 | import org.springframework.stereotype.Service; 23 | 24 | /** 25 | * StringTransactionalConsumer 26 | */ 27 | @Service 28 | @RocketMQMessageListener(topic = "${demo.rocketmq.transTopic}", consumerGroup = "string_trans_consumer") 29 | public class StringTransactionalConsumer implements RocketMQListener { 30 | @Override 31 | public void onMessage(String message) { 32 | System.out.printf("------- StringTransactionalConsumer received: %s \n", message); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/support/RocketMQHeaders.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.apache.rocketmq.spring.support; 18 | 19 | /** 20 | * Represents the RocketMQ message protocol that is used during the data exchange. 21 | */ 22 | public class RocketMQHeaders { 23 | 24 | public static final String KEYS = "KEYS"; 25 | public static final String TAGS = "TAGS"; 26 | public static final String TOPIC = "TOPIC"; 27 | public static final String MESSAGE_ID = "MESSAGE_ID"; 28 | public static final String BORN_TIMESTAMP = "BORN_TIMESTAMP"; 29 | public static final String BORN_HOST = "BORN_HOST"; 30 | public static final String FLAG = "FLAG"; 31 | public static final String QUEUE_ID = "QUEUE_ID"; 32 | public static final String SYS_FLAG = "SYS_FLAG"; 33 | public static final String TRANSACTION_ID = "TRANSACTION_ID"; 34 | public static final String PROPERTIES = "PROPERTIES"; 35 | 36 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-consume-demo/src/main/java/org/apache/rocketmq/samples/springboot/consumer/OrderPaidEventConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.samples.springboot.consumer; 19 | 20 | import org.apache.rocketmq.samples.springboot.domain.OrderPaidEvent; 21 | import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; 22 | import org.apache.rocketmq.spring.core.RocketMQListener; 23 | import org.springframework.stereotype.Service; 24 | 25 | /** 26 | * OrderPaidEventConsumer 27 | */ 28 | @Service // 标记是 Service 29 | @RocketMQMessageListener(topic = "${demo.rocketmq.orderTopic}", consumerGroup = "order-paid-consumer") // 标记是 RocketMQ MessageListener 30 | public class OrderPaidEventConsumer implements RocketMQListener { // 实现 RocketMQListener 接口 31 | 32 | @Override 33 | public void onMessage(OrderPaidEvent orderPaidEvent) { 34 | System.out.printf("------- OrderPaidEventConsumer received: %s \n", orderPaidEvent); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-produce-demo/src/main/java/org/apache/rocketmq/samples/springboot/domain/OrderPaidEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.apache.rocketmq.samples.springboot.domain; 18 | 19 | import java.io.Serializable; 20 | import java.math.BigDecimal; 21 | 22 | public class OrderPaidEvent implements Serializable { 23 | private String orderId; 24 | 25 | private BigDecimal paidMoney; 26 | 27 | public OrderPaidEvent() { 28 | } 29 | 30 | public OrderPaidEvent(String orderId, BigDecimal paidMoney) { 31 | this.orderId = orderId; 32 | this.paidMoney = paidMoney; 33 | } 34 | 35 | public String getOrderId() { 36 | return orderId; 37 | } 38 | 39 | public void setOrderId(String orderId) { 40 | this.orderId = orderId; 41 | } 42 | 43 | public BigDecimal getPaidMoney() { 44 | return paidMoney; 45 | } 46 | 47 | public void setPaidMoney(BigDecimal paidMoney) { 48 | this.paidMoney = paidMoney; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /style/copyright/Apache.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-consume-demo/src/main/java/org/apache/rocketmq/samples/springboot/domain/OrderPaidEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.samples.springboot.domain; 19 | 20 | import java.io.Serializable; 21 | import java.math.BigDecimal; 22 | 23 | /** 24 | * OrderPaidEvent 25 | */ 26 | public class OrderPaidEvent implements Serializable { 27 | private String orderId; 28 | 29 | private BigDecimal paidMoney; 30 | 31 | public OrderPaidEvent() { 32 | } 33 | 34 | public OrderPaidEvent(String orderId, BigDecimal paidMoney) { 35 | this.orderId = orderId; 36 | this.paidMoney = paidMoney; 37 | } 38 | 39 | public String getOrderId() { 40 | return orderId; 41 | } 42 | 43 | public void setOrderId(String orderId) { 44 | this.orderId = orderId; 45 | } 46 | 47 | public BigDecimal getPaidMoney() { 48 | return paidMoney; 49 | } 50 | 51 | public void setPaidMoney(BigDecimal paidMoney) { 52 | this.paidMoney = paidMoney; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/style/copyright/Apache.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-consume-demo/src/main/java/org/apache/rocketmq/samples/springboot/consumer/Checker.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.samples.springboot.consumer; 19 | 20 | import org.apache.rocketmq.client.producer.LocalTransactionState; 21 | import org.apache.rocketmq.client.producer.TransactionListener; 22 | import org.apache.rocketmq.common.message.Message; 23 | import org.apache.rocketmq.common.message.MessageExt; 24 | 25 | /** 26 | * Note: This is a nagitive testing. It aims to tell user the fact that 27 | * the @RocketMQTransactionListener can not be used on consumer side!!! 28 | * 29 | *

How to try it? just uncomment the annotation declaration, then compile 30 | * and run the consumer, it will fail to start. 31 | */ 32 | 33 | //@RocketMQTransactionListener 34 | public class Checker implements TransactionListener { 35 | @Override 36 | public LocalTransactionState executeLocalTransaction(Message message, Object o) { 37 | return null; 38 | } 39 | 40 | @Override 41 | public LocalTransactionState checkLocalTransaction(MessageExt messageExt) { 42 | return null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/test/java/org/apache/rocketmq/spring/support/DefaultRocketMQListenerContainerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.apache.rocketmq.spring.support; 18 | 19 | import org.apache.rocketmq.common.message.MessageExt; 20 | import org.apache.rocketmq.spring.core.RocketMQListener; 21 | import org.junit.Test; 22 | import java.lang.reflect.InvocationTargetException; 23 | import java.lang.reflect.Method; 24 | 25 | import static org.assertj.core.api.Assertions.assertThat; 26 | 27 | public class DefaultRocketMQListenerContainerTest { 28 | @Test 29 | public void testGetMessageType() throws Exception { 30 | DefaultRocketMQListenerContainer listenerContainer = new DefaultRocketMQListenerContainer(); 31 | Method getMessageType = DefaultRocketMQListenerContainer.class.getDeclaredMethod("getMessageType"); 32 | getMessageType.setAccessible(true); 33 | 34 | listenerContainer.setRocketMQListener(new RocketMQListener() { 35 | @Override 36 | public void onMessage(String message) { 37 | } 38 | }); 39 | Class result = (Class)getMessageType.invoke(listenerContainer); 40 | assertThat(result.getName().equals(String.class.getName())); 41 | 42 | listenerContainer.setRocketMQListener(new RocketMQListener() { 43 | @Override 44 | public void onMessage(MessageExt message) { 45 | } 46 | }); 47 | result = (Class)getMessageType.invoke(listenerContainer); 48 | assertThat(result.getName().equals(MessageExt.class.getName())); 49 | } 50 | } 51 | 52 | 53 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 4.0.0 21 | 22 | 23 | org.apache.rocketmq 24 | rocketmq-spring-boot-parent 25 | 2.0.2-SNAPSHOT 26 | ../rocketmq-spring-boot-parent/pom.xml 27 | 28 | 29 | rocketmq-spring-boot-starter 30 | jar 31 | 32 | RocketMQ Spring Boot Starter 33 | SRocketMQ Spring Boot Starter 34 | https://github.com/apache/rocketmq-spring 35 | 36 | 37 | 38 | 39 | org.apache.rocketmq 40 | rocketmq-spring-boot 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-starter-validation 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-consume-demo/src/main/java/org/apache/rocketmq/samples/springboot/consumer/MessageExtConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.samples.springboot.consumer; 19 | 20 | import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; 21 | import org.apache.rocketmq.common.UtilAll; 22 | import org.apache.rocketmq.common.consumer.ConsumeFromWhere; 23 | import org.apache.rocketmq.common.message.MessageExt; 24 | import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; 25 | import org.apache.rocketmq.spring.core.RocketMQListener; 26 | import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener; 27 | import org.springframework.stereotype.Service; 28 | 29 | /** 30 | * MessageExtConsumer, consume listener impl class. 31 | */ 32 | @Service 33 | @RocketMQMessageListener(topic = "message-ext-topic", selectorExpression = "tag1", consumerGroup = "${spring.application.name}-message-ext-consumer") 34 | public class MessageExtConsumer implements RocketMQListener, RocketMQPushConsumerLifecycleListener { 35 | 36 | @Override 37 | public void onMessage(MessageExt message) { 38 | System.out.printf("------- MessageExtConsumer received message, msgId: %s, body:%s \n", message.getMsgId(), new String(message.getBody())); 39 | } 40 | 41 | @Override 42 | public void prepareStart(DefaultMQPushConsumer consumer) { 43 | // set consumer consume message from now 44 | consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_TIMESTAMP); 45 | consumer.setConsumeTimestamp(UtilAll.timeMillisToHumanString3(System.currentTimeMillis())); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/config/TransactionHandlerRegistry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.config; 19 | 20 | import io.netty.util.internal.ConcurrentSet; 21 | import org.apache.rocketmq.client.exception.MQClientException; 22 | import org.apache.rocketmq.spring.core.RocketMQTemplate; 23 | import org.springframework.beans.factory.DisposableBean; 24 | 25 | import java.util.Set; 26 | 27 | public class TransactionHandlerRegistry implements DisposableBean { 28 | 29 | 30 | private RocketMQTemplate rocketMQTemplate; 31 | /** 32 | * {@link TransactionHandler#name} 的 集合 33 | */ 34 | private final Set listenerContainers = new ConcurrentSet<>(); 35 | 36 | public TransactionHandlerRegistry(RocketMQTemplate template) { 37 | this.rocketMQTemplate = template; 38 | } 39 | 40 | @Override 41 | public void destroy() throws Exception { 42 | listenerContainers.clear(); 43 | } 44 | 45 | public void registerTransactionHandler(TransactionHandler handler) throws MQClientException { 46 | // 不能声明重复的 TransactionHandler 47 | if (listenerContainers.contains(handler.getName())) { 48 | throw new MQClientException(-1, 49 | String.format("The transaction name [%s] has been defined in TransactionListener [%s]", handler.getName(), handler.getBeanName())); 50 | } 51 | // 添加到 listenerContainers 中 52 | listenerContainers.add(handler.getName()); 53 | 54 | // 创建并启动 TransactionMQProducer 55 | rocketMQTemplate.createAndStartTransactionMQProducer(handler.getName(), handler.getListener(), handler.getCheckExecutor()); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/test/java/org/apache/rocketmq/spring/support/RocketMQUtilTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.apache.rocketmq.spring.support; 18 | 19 | import java.util.Arrays; 20 | 21 | import com.fasterxml.jackson.databind.ObjectMapper; 22 | import org.junit.Test; 23 | import org.springframework.messaging.Message; 24 | import org.springframework.messaging.support.MessageBuilder; 25 | 26 | import static org.junit.Assert.assertTrue; 27 | 28 | public class RocketMQUtilTest { 29 | 30 | private ObjectMapper objectMapper = new ObjectMapper(); 31 | 32 | @Test 33 | public void testMessageBuilder() { 34 | Message msg = MessageBuilder.withPayload("test"). 35 | setHeader("A", "test1"). 36 | setHeader("B", "test2"). 37 | build(); 38 | System.out.printf("header size=%d %s %n", msg.getHeaders().size(), msg.getHeaders().toString()); 39 | assertTrue(msg.getHeaders().get("A") != null); 40 | assertTrue(msg.getHeaders().get("B") != null); 41 | } 42 | 43 | @Test 44 | public void testPayload() { 45 | String charset = "UTF-8"; 46 | String destination = "test-topic"; 47 | Message msgWithStringPayload = MessageBuilder.withPayload("test").build(); 48 | org.apache.rocketmq.common.message.Message rocketMsg1 = RocketMQUtil.convertToRocketMessage(objectMapper, 49 | charset, destination, msgWithStringPayload); 50 | 51 | Message msgWithBytePayload = MessageBuilder.withPayload("test".getBytes()).build(); 52 | org.apache.rocketmq.common.message.Message rocketMsg2 = RocketMQUtil.convertToRocketMessage(objectMapper, 53 | charset, destination, msgWithBytePayload); 54 | 55 | assertTrue(Arrays.equals(((String)msgWithStringPayload.getPayload()).getBytes(), rocketMsg1.getBody())); 56 | assertTrue(Arrays.equals((byte[])msgWithBytePayload.getPayload(), rocketMsg2.getBody())); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/annotation/RocketMQMessageListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.annotation; 19 | 20 | import java.lang.annotation.Documented; 21 | import java.lang.annotation.ElementType; 22 | import java.lang.annotation.Retention; 23 | import java.lang.annotation.RetentionPolicy; 24 | import java.lang.annotation.Target; 25 | 26 | @Target(ElementType.TYPE) // 表名,注解在类上 27 | @Retention(RetentionPolicy.RUNTIME) 28 | @Documented 29 | public @interface RocketMQMessageListener { 30 | 31 | /** 32 | * 消费分组 33 | * 34 | * Consumers of the same role is required to have exactly same subscriptions and consumerGroup to correctly achieve 35 | * load balance. It's required and needs to be globally unique. 36 | * 37 | * 38 | * See here for further discussion. 39 | */ 40 | String consumerGroup(); 41 | 42 | /** 43 | * 消费主体 44 | * 45 | * Topic name. 46 | */ 47 | String topic(); 48 | 49 | /** 50 | * 选择消息的方式 51 | * 52 | * Control how to selector message. 53 | * 54 | * @see SelectorType 55 | */ 56 | SelectorType selectorType() default SelectorType.TAG; 57 | 58 | /** 59 | * 选择消息的表达式 60 | * 61 | * Control which message can be select. Grammar please see {@link SelectorType#TAG} and {@link SelectorType#SQL92} 62 | */ 63 | String selectorExpression() default "*"; 64 | 65 | /** 66 | * 消费模式 67 | * 68 | * Control consume mode, you can choice receive message concurrently or orderly. 69 | */ 70 | ConsumeMode consumeMode() default ConsumeMode.CONCURRENTLY; 71 | 72 | /** 73 | * 消费模型 74 | * 75 | * Control message mode, if you want all subscribers receive message all message, broadcasting is a good choice. 76 | */ 77 | MessageModel messageModel() default MessageModel.CLUSTERING; 78 | 79 | /** 80 | * 消费线程数 81 | * 82 | * Max consumer thread number. 83 | */ 84 | int consumeThreadMax() default 64; 85 | 86 | } -------------------------------------------------------------------------------- /style/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 31 | 32 | 35 | 36 | 39 | 40 | 43 | 44 | 47 | 48 | 51 | 52 | 55 | 56 | 59 | 60 | 63 | 64 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/annotation/RocketMQTransactionListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.annotation; 19 | 20 | import org.apache.rocketmq.spring.config.RocketMQConfigUtils; 21 | import org.springframework.stereotype.Component; 22 | 23 | import java.lang.annotation.Documented; 24 | import java.lang.annotation.ElementType; 25 | import java.lang.annotation.Retention; 26 | import java.lang.annotation.RetentionPolicy; 27 | import java.lang.annotation.Target; 28 | 29 | /** 30 | * This annotation is used over a class which implements interface 31 | * org.apache.rocketmq.client.producer.TransactionListener. The class implements 32 | * two methods for process callback events after the txProducer sends a transactional message. 33 | *

Note: The annotation is used only on RocketMQ client producer side, it can not be used 34 | * on consumer side. 35 | */ 36 | @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) // 表名,注解在类上 37 | @Retention(RetentionPolicy.RUNTIME) 38 | @Documented 39 | @Component // 默认带了 @Component 注解,所以只要添加到了类上,就会注册成 Spring Bean 对象 40 | public @interface RocketMQTransactionListener { 41 | 42 | /** 43 | * 生产者分组 44 | * 45 | * Declare the txProducerGroup that is used to relate callback event to the listener, rocketMQTemplate must send a 46 | * transactional message with the declared txProducerGroup. 47 | *

48 | *

It is suggested to use the default txProducerGroup if your system only needs to define a TransactionListener class. 49 | */ 50 | String txProducerGroup() default RocketMQConfigUtils.ROCKETMQ_TRANSACTION_DEFAULT_GLOBAL_NAME; 51 | 52 | /** 53 | * Set ExecutorService params -- corePoolSize 54 | */ 55 | int corePoolSize() default 1; 56 | 57 | /** 58 | * Set ExecutorService params -- maximumPoolSize 59 | */ 60 | int maximumPoolSize() default 1; 61 | 62 | /** 63 | * Set ExecutorService params -- keepAliveTime 64 | */ 65 | long keepAliveTime() default 1000 * 60; //60ms 66 | 67 | /** 68 | * Set ExecutorService params -- blockingQueueSize 69 | */ 70 | int blockingQueueSize() default 2000; 71 | 72 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/style/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 31 | 32 | 35 | 36 | 39 | 40 | 43 | 44 | 47 | 48 | 51 | 52 | 55 | 56 | 59 | 60 | 63 | 64 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/config/TransactionHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.config; 19 | 20 | import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; 21 | import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; 22 | import org.springframework.beans.factory.BeanFactory; 23 | 24 | import java.util.concurrent.LinkedBlockingDeque; 25 | import java.util.concurrent.ThreadPoolExecutor; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | /** 29 | * 解析 {@link org.apache.rocketmq.spring.annotation.RocketMQTransactionListener} 注解后,封装的对象 30 | */ 31 | class TransactionHandler { 32 | 33 | /** 34 | * {@link RocketMQTransactionListener#txProducerGroup()} 35 | */ 36 | private String name; 37 | /** 38 | * {@link RocketMQTransactionListener#corePoolSize()} 39 | * {@link RocketMQTransactionListener#maximumPoolSize()} 40 | * {@link RocketMQTransactionListener#maximumPoolSize()} 41 | * {@link RocketMQTransactionListener#keepAliveTime()} 42 | * {@link RocketMQTransactionListener#blockingQueueSize()} 43 | */ 44 | private ThreadPoolExecutor checkExecutor; 45 | 46 | /** 47 | * Bean 的名字 48 | */ 49 | private String beanName; 50 | /** 51 | * 对应的 RocketMQLocalTransactionListener 对象 52 | */ 53 | private RocketMQLocalTransactionListener bean; 54 | private BeanFactory beanFactory; 55 | 56 | public String getBeanName() { 57 | return beanName; 58 | } 59 | 60 | public void setBeanName(String beanName) { 61 | this.beanName = beanName; 62 | } 63 | 64 | public String getName() { 65 | return name; 66 | } 67 | 68 | public void setName(String name) { 69 | this.name = name; 70 | } 71 | 72 | public BeanFactory getBeanFactory() { 73 | return beanFactory; 74 | } 75 | 76 | public void setBeanFactory(BeanFactory beanFactory) { 77 | this.beanFactory = beanFactory; 78 | } 79 | 80 | public void setListener(RocketMQLocalTransactionListener listener) { 81 | this.bean = listener; 82 | } 83 | 84 | public RocketMQLocalTransactionListener getListener() { 85 | return this.bean; 86 | } 87 | 88 | public void setCheckExecutor(int corePoolSize, int maxPoolSize, long keepAliveTime, int blockingQueueSize) { 89 | // 创建 ThreadPoolExecutor 对象 90 | this.checkExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, 91 | keepAliveTime, TimeUnit.MILLISECONDS, 92 | new LinkedBlockingDeque<>(blockingQueueSize)); 93 | } 94 | 95 | public ThreadPoolExecutor getCheckExecutor() { 96 | return checkExecutor; 97 | } 98 | 99 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 22 | 4.0.0 23 | 24 | org.apache.rocketmq 25 | rocketmq-spring-boot-samples 26 | pom 27 | 0.0.1-SNAPSHOT 28 | 29 | RocketMQ Spring Boot Samples 30 | Samples for RocketMQ Spring Boot 31 | https://github.com/apache/rocketmq-spring 32 | 33 | 34 | rocketmq-produce-demo 35 | rocketmq-consume-demo 36 | 37 | 38 | 39 | 2.0.2-SNAPSHOT 40 | 41 | 42 | 43 | 44 | org.apache.rocketmq 45 | rocketmq-spring-boot-starter 46 | ${rocketmq-spring-boot-starter-version} 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.apache.maven.plugins 54 | maven-checkstyle-plugin 55 | 2.17 56 | 57 | 58 | validate 59 | validate 60 | 61 | ${disable.checks} 62 | src/main/resources 63 | style/rmq_checkstyle.xml 64 | UTF-8 65 | true 66 | true 67 | 68 | 69 | check 70 | 71 | 72 | 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-maven-plugin 77 | 2.1.0.RELEASE 78 | 79 | 80 | 81 | repackage 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 4.0.0 21 | 22 | 23 | org.apache.rocketmq 24 | rocketmq-spring-boot-parent 25 | 2.0.2-SNAPSHOT 26 | ../rocketmq-spring-boot-parent/pom.xml 27 | 28 | 29 | rocketmq-spring-boot 30 | jar 31 | 32 | RocketMQ Spring Boot AutoConfigure 33 | SRocketMQ Spring Boot AutoConfigure 34 | https://github.com/apache/rocketmq-spring 35 | 36 | 37 | 38 | 39 | org.slf4j 40 | slf4j-api 41 | 42 | 43 | org.springframework.boot 44 | spring-boot 45 | true 46 | 47 | 48 | org.springframework.boot 49 | spring-boot-autoconfigure 50 | true 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-autoconfigure-processor 55 | true 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-starter-test 60 | test 61 | 62 | 63 | org.apache.rocketmq 64 | rocketmq-client 65 | 66 | 67 | org.slf4j 68 | slf4j-api 69 | 70 | 71 | 72 | 73 | org.springframework 74 | spring-messaging 75 | 76 | 77 | org.springframework 78 | spring-core 79 | 80 | 81 | org.springframework 82 | spring-context 83 | 84 | 85 | org.springframework 86 | spring-aop 87 | 88 | 89 | com.fasterxml.jackson.core 90 | jackson-databind 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 4.0.0 21 | 22 | 23 | org.apache 24 | apache 25 | 18 26 | 27 | 28 | org.apache.rocketmq 29 | rocketmq-spring-all 30 | 2.0.2-SNAPSHOT 31 | pom 32 | 33 | Apache RocketMQ Spring Boot ${project.version} 34 | Build for Apache RocketMQ Spring Boot Support 35 | https://github.com/apache/rocketmq-spring 36 | 37 | 38 | 39 | git@github.com:apache/rocketmq-spring.git 40 | scm:git:git@github.com:apache/rocketmq-spring.git 41 | scm:git:git@github.com:apache/rocketmq-spring.git 42 | HEAD 43 | 44 | 45 | 46 | 47 | Development List 48 | dev-subscribe@rocketmq.apache.org 49 | dev-unsubscribe@rocketmq.apache.org 50 | dev@rocketmq.apache.org 51 | 52 | 53 | User List 54 | users-subscribe@rocketmq.apache.org 55 | users-unsubscribe@rocketmq.apache.org 56 | users@rocketmq.apache.org 57 | 58 | 59 | Commits List 60 | commits-subscribe@rocketmq.apache.org 61 | commits-unsubscribe@rocketmq.apache.org 62 | commits@rocketmq.apache.org 63 | 64 | 65 | 66 | 67 | 68 | Apache Software Foundation 69 | http://www.apache.org 70 | 71 | 72 | 73 | 74 | Apache License, Version 2.0 75 | http://www.apache.org/licenses/LICENSE-2.0.txt 76 | repo 77 | A business-friendly OSS license 78 | 79 | 80 | 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-checkstyle-plugin 86 | 2.17 87 | 88 | 89 | validate 90 | validate 91 | 92 | src/main/resources 93 | style/rmq_checkstyle.xml 94 | UTF-8 95 | true 96 | true 97 | 98 | 99 | check 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | rocketmq-spring-boot-parent 109 | rocketmq-spring-boot 110 | rocketmq-spring-boot-starter 111 | 114 | rocketmq-spring-boot-samples 115 | 116 | 117 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/autoconfigure/RocketMQProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.autoconfigure; 19 | 20 | import org.springframework.boot.context.properties.ConfigurationProperties; 21 | 22 | @SuppressWarnings("WeakerAccess") 23 | @ConfigurationProperties(prefix = "rocketmq") // 配置文件中 rocketmq 前缀 24 | public class RocketMQProperties { 25 | 26 | /** 27 | * The name server for rocketMQ, formats: `host:port;host:port`. 28 | * 29 | * Namesrv 地址 30 | */ 31 | private String nameServer; 32 | 33 | /** 34 | * Producer 配置 35 | */ 36 | private Producer producer; 37 | 38 | public String getNameServer() { 39 | return nameServer; 40 | } 41 | 42 | public void setNameServer(String nameServer) { 43 | this.nameServer = nameServer; 44 | } 45 | 46 | public RocketMQProperties.Producer getProducer() { 47 | return producer; 48 | } 49 | 50 | public void setProducer(RocketMQProperties.Producer producer) { 51 | this.producer = producer; 52 | } 53 | 54 | public static class Producer { 55 | 56 | /** 57 | * Name of producer. 58 | */ 59 | private String group; 60 | 61 | /** 62 | * Millis of send message timeout. 63 | */ 64 | private int sendMessageTimeout = 3000; 65 | 66 | /** 67 | * Compress message body threshold, namely, message body larger than 4k will be compressed on default. 68 | */ 69 | private int compressMessageBodyThreshold = 1024 * 4; 70 | 71 | /** 72 | * Maximum number of retry to perform internally before claiming sending failure in synchronous mode. 73 | * This may potentially cause message duplication which is up to application developers to resolve. 74 | */ 75 | private int retryTimesWhenSendFailed = 2; 76 | 77 | /** 78 | *

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

79 | * This may potentially cause message duplication which is up to application developers to resolve. 80 | */ 81 | private int retryTimesWhenSendAsyncFailed = 2; 82 | 83 | /** 84 | * Indicate whether to retry another broker on sending failure internally. 85 | */ 86 | private boolean retryNextServer = false; 87 | 88 | /** 89 | * Maximum allowed message size in bytes. 90 | */ 91 | private int maxMessageSize = 1024 * 1024 * 4; 92 | 93 | public String getGroup() { 94 | return group; 95 | } 96 | 97 | public void setGroup(String group) { 98 | this.group = group; 99 | } 100 | 101 | public int getSendMessageTimeout() { 102 | return sendMessageTimeout; 103 | } 104 | 105 | public void setSendMessageTimeout(int sendMessageTimeout) { 106 | this.sendMessageTimeout = sendMessageTimeout; 107 | } 108 | 109 | public int getCompressMessageBodyThreshold() { 110 | return compressMessageBodyThreshold; 111 | } 112 | 113 | public void setCompressMessageBodyThreshold(int compressMessageBodyThreshold) { 114 | this.compressMessageBodyThreshold = compressMessageBodyThreshold; 115 | } 116 | 117 | public int getRetryTimesWhenSendFailed() { 118 | return retryTimesWhenSendFailed; 119 | } 120 | 121 | public void setRetryTimesWhenSendFailed(int retryTimesWhenSendFailed) { 122 | this.retryTimesWhenSendFailed = retryTimesWhenSendFailed; 123 | } 124 | 125 | public int getRetryTimesWhenSendAsyncFailed() { 126 | return retryTimesWhenSendAsyncFailed; 127 | } 128 | 129 | public void setRetryTimesWhenSendAsyncFailed(int retryTimesWhenSendAsyncFailed) { 130 | this.retryTimesWhenSendAsyncFailed = retryTimesWhenSendAsyncFailed; 131 | } 132 | 133 | public boolean isRetryNextServer() { 134 | return retryNextServer; 135 | } 136 | 137 | public void setRetryNextServer(boolean retryNextServer) { 138 | this.retryNextServer = retryNextServer; 139 | } 140 | 141 | public int getMaxMessageSize() { 142 | return maxMessageSize; 143 | } 144 | 145 | public void setMaxMessageSize(int maxMessageSize) { 146 | this.maxMessageSize = maxMessageSize; 147 | } 148 | } 149 | 150 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/config/RocketMQTransactionAnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.config; 19 | 20 | import org.apache.rocketmq.client.exception.MQClientException; 21 | import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; 22 | import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | import org.springframework.aop.support.AopUtils; 26 | import org.springframework.beans.BeansException; 27 | import org.springframework.beans.factory.BeanCreationException; 28 | import org.springframework.beans.factory.BeanFactory; 29 | import org.springframework.beans.factory.BeanFactoryAware; 30 | import org.springframework.beans.factory.config.BeanPostProcessor; 31 | import org.springframework.core.Ordered; 32 | import org.springframework.core.annotation.AnnotationUtils; 33 | 34 | import java.util.Collections; 35 | import java.util.Set; 36 | import java.util.concurrent.ConcurrentHashMap; 37 | 38 | public class RocketMQTransactionAnnotationProcessor implements BeanPostProcessor, Ordered, BeanFactoryAware { 39 | 40 | private static final Logger log = LoggerFactory.getLogger(RocketMQTransactionAnnotationProcessor.class); 41 | 42 | private BeanFactory beanFactory; 43 | /** 44 | * 不处理的类的集合 45 | */ 46 | private final Set> nonProcessedClasses = Collections.newSetFromMap(new ConcurrentHashMap<>(64)); 47 | 48 | private TransactionHandlerRegistry transactionHandlerRegistry; 49 | 50 | public RocketMQTransactionAnnotationProcessor(TransactionHandlerRegistry transactionHandlerRegistry) { 51 | this.transactionHandlerRegistry = transactionHandlerRegistry; 52 | } 53 | 54 | @Override // 实现自 BeanFactoryAware 接口 55 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 56 | this.beanFactory = beanFactory; 57 | } 58 | 59 | @Override // 实现自 BeanPostProcessor 接口 60 | public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 61 | return bean; 62 | } 63 | 64 | @Override // 实现自 BeanPostProcessor 接口 65 | public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { 66 | // 如果 nonProcessedClasses 不存在 67 | if (!this.nonProcessedClasses.contains(bean.getClass())) { 68 | // 获得 Bean 对应的 Class 类名。因为有可能被 AOP 代理过。 69 | Class targetClass = AopUtils.getTargetClass(bean); 70 | // 添加到 nonProcessedClasses 中,表示后面不处理。 71 | this.nonProcessedClasses.add(bean.getClass()); 72 | // 获得 @RocketMQTransactionListener 注解 73 | RocketMQTransactionListener listener = AnnotationUtils.findAnnotation(targetClass, RocketMQTransactionListener.class); 74 | // 如果无注解,则不进行任何逻辑 75 | if (listener == null) { // for quick search 76 | log.trace("No @RocketMQTransactionListener annotations found on bean type: {}", bean.getClass()); 77 | } else { 78 | // 如果有注解,则注册到 TransactionHandlerRegistry 中 79 | try { 80 | processTransactionListenerAnnotation(listener, bean); 81 | } catch (MQClientException e) { 82 | log.error("Failed to process annotation " + listener, e); 83 | throw new BeanCreationException("Failed to process annotation " + listener, e); 84 | } 85 | } 86 | } 87 | 88 | return bean; 89 | } 90 | 91 | private void processTransactionListenerAnnotation(RocketMQTransactionListener listener, Object bean) throws MQClientException { 92 | // 校验 @RocketMQTransactionListener 非空 93 | if (transactionHandlerRegistry == null) { 94 | throw new MQClientException("Bad usage of @RocketMQTransactionListener, " + "the class must work with RocketMQTemplate", null); 95 | } 96 | // 如果未实现 RocketMQLocalTransactionListener 接口,直接抛出 IllegalStateException 异常。 97 | if (!RocketMQLocalTransactionListener.class.isAssignableFrom(bean.getClass())) { 98 | throw new MQClientException("Bad usage of @RocketMQTransactionListener, " + "the class must implement interface RocketMQLocalTransactionListener", null); 99 | } 100 | 101 | // 将 @RocketMQTransactionListener 注解,创建成 TransactionHandler 对象 102 | TransactionHandler transactionHandler = new TransactionHandler(); 103 | transactionHandler.setBeanFactory(this.beanFactory); 104 | transactionHandler.setName(listener.txProducerGroup()); 105 | transactionHandler.setBeanName(bean.getClass().getName()); 106 | transactionHandler.setListener((RocketMQLocalTransactionListener) bean); 107 | transactionHandler.setCheckExecutor(listener.corePoolSize(), listener.maximumPoolSize(), listener.keepAliveTime(), listener.blockingQueueSize()); 108 | 109 | // 注册 TransactionHandler 到 transactionHandlerRegistry 中 110 | transactionHandlerRegistry.registerTransactionHandler(transactionHandler); 111 | } 112 | 113 | @Override // 实现自 Ordered 接口 114 | public int getOrder() { 115 | return LOWEST_PRECEDENCE; 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/autoconfigure/RocketMQAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.autoconfigure; 19 | 20 | import com.fasterxml.jackson.databind.ObjectMapper; 21 | import org.apache.rocketmq.client.MQAdmin; 22 | import org.apache.rocketmq.client.producer.DefaultMQProducer; 23 | import org.apache.rocketmq.spring.config.RocketMQConfigUtils; 24 | import org.apache.rocketmq.spring.config.RocketMQTransactionAnnotationProcessor; 25 | import org.apache.rocketmq.spring.config.TransactionHandlerRegistry; 26 | import org.apache.rocketmq.spring.core.RocketMQTemplate; 27 | import org.springframework.beans.factory.config.BeanDefinition; 28 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 29 | import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; 30 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 31 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 32 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 33 | import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; 34 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 35 | import org.springframework.context.annotation.Bean; 36 | import org.springframework.context.annotation.Configuration; 37 | import org.springframework.context.annotation.Import; 38 | import org.springframework.context.annotation.Role; 39 | import org.springframework.util.Assert; 40 | 41 | @Configuration // 标识是配置类 42 | @EnableConfigurationProperties(RocketMQProperties.class) // 指定 RocketMQProperties 自动配置 43 | @ConditionalOnClass({ MQAdmin.class, ObjectMapper.class }) // 要求有 MQAdmin、ObjectMapper 类 44 | @ConditionalOnProperty(prefix = "rocketmq", value = "name-server") // 要求有 rocketmq 开头,且 name-server 的配置 45 | @Import({ JacksonFallbackConfiguration.class, ListenerContainerConfiguration.class }) // 引入 JacksonFallbackConfiguration 和 ListenerContainerConfiguration 配置类 46 | @AutoConfigureAfter(JacksonAutoConfiguration.class) // 在 JacksonAutoConfiguration 之后初始化 47 | public class RocketMQAutoConfiguration { 48 | 49 | @Bean 50 | @ConditionalOnMissingBean(DefaultMQProducer.class) // 不存在 DefaultMQProducer Bean 对象 51 | @ConditionalOnProperty(prefix = "rocketmq", value = {"name-server", "producer.group"}) // 要求有 rocketmq 开头,且 name-server、producer.group 的配置 52 | public DefaultMQProducer defaultMQProducer(RocketMQProperties rocketMQProperties) { 53 | // 校验配置 54 | RocketMQProperties.Producer producerConfig = rocketMQProperties.getProducer(); 55 | String nameServer = rocketMQProperties.getNameServer(); 56 | String groupName = producerConfig.getGroup(); 57 | Assert.hasText(nameServer, "[rocketmq.name-server] must not be null"); 58 | Assert.hasText(groupName, "[rocketmq.producer.group] must not be null"); 59 | 60 | // 创建 DefaultMQProducer 对象 61 | DefaultMQProducer producer = new DefaultMQProducer(groupName); 62 | // 将 RocketMQProperties.Producer 配置,设置到 producer 中 63 | producer.setNamesrvAddr(nameServer); 64 | producer.setSendMsgTimeout(producerConfig.getSendMessageTimeout()); 65 | producer.setRetryTimesWhenSendFailed(producerConfig.getRetryTimesWhenSendFailed()); 66 | producer.setRetryTimesWhenSendAsyncFailed(producerConfig.getRetryTimesWhenSendAsyncFailed()); 67 | producer.setMaxMessageSize(producerConfig.getMaxMessageSize()); 68 | producer.setCompressMsgBodyOverHowmuch(producerConfig.getCompressMessageBodyThreshold()); 69 | producer.setRetryAnotherBrokerWhenNotStoreOK(producerConfig.isRetryNextServer()); 70 | 71 | return producer; 72 | } 73 | 74 | @Bean(destroyMethod = "destroy") // 声明了销毁时,调用 destroy 方法 75 | @ConditionalOnBean(DefaultMQProducer.class) // 有 DefaultMQProducer Bean 的情况下 76 | @ConditionalOnMissingBean(RocketMQTemplate.class) // 不存在 RocketMQTemplate Bean 对象 77 | public RocketMQTemplate rocketMQTemplate(DefaultMQProducer mqProducer, ObjectMapper rocketMQMessageObjectMapper) { 78 | // 创建 RocketMQTemplate 对象 79 | RocketMQTemplate rocketMQTemplate = new RocketMQTemplate(); 80 | // 设置其属性 81 | rocketMQTemplate.setProducer(mqProducer); 82 | rocketMQTemplate.setObjectMapper(rocketMQMessageObjectMapper); 83 | return rocketMQTemplate; 84 | } 85 | 86 | @Bean 87 | @ConditionalOnBean(RocketMQTemplate.class) // 有 RocketMQTemplate Bean 的情况下 88 | @ConditionalOnMissingBean(TransactionHandlerRegistry.class) // 不存在 TransactionHandlerRegistry Bean 对象 89 | public TransactionHandlerRegistry transactionHandlerRegistry(RocketMQTemplate template) { 90 | // 创建 TransactionHandlerRegistry 对象 91 | return new TransactionHandlerRegistry(template); 92 | } 93 | 94 | @Bean(name = RocketMQConfigUtils.ROCKETMQ_TRANSACTION_ANNOTATION_PROCESSOR_BEAN_NAME) // Bean 的名字 95 | @ConditionalOnBean(TransactionHandlerRegistry.class) // 有 TransactionHandlerRegistry Bean 的情况下 96 | @Role(BeanDefinition.ROLE_INFRASTRUCTURE) 97 | public static RocketMQTransactionAnnotationProcessor transactionAnnotationProcessor(TransactionHandlerRegistry transactionHandlerRegistry) { 98 | // 创建 RocketMQTransactionAnnotationProcessor 对象 99 | return new RocketMQTransactionAnnotationProcessor(transactionHandlerRegistry); 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/test/java/org/apache/rocketmq/spring/autoconfigure/RocketMQAutoConfigurationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.autoconfigure; 19 | 20 | import com.fasterxml.jackson.databind.ObjectMapper; 21 | import org.apache.rocketmq.client.producer.DefaultMQProducer; 22 | import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; 23 | import org.apache.rocketmq.spring.core.RocketMQListener; 24 | import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer; 25 | import org.junit.Test; 26 | import org.springframework.beans.factory.NoSuchBeanDefinitionException; 27 | import org.springframework.boot.autoconfigure.AutoConfigurations; 28 | import org.springframework.boot.test.context.runner.ApplicationContextRunner; 29 | import org.springframework.context.annotation.Bean; 30 | import org.springframework.context.annotation.Configuration; 31 | 32 | import static org.assertj.core.api.Assertions.assertThat; 33 | 34 | public class RocketMQAutoConfigurationTest { 35 | private ApplicationContextRunner runner = new ApplicationContextRunner() 36 | .withConfiguration(AutoConfigurations.of(RocketMQAutoConfiguration.class)); 37 | 38 | 39 | @Test(expected = NoSuchBeanDefinitionException.class) 40 | public void testRocketMQAutoConfigurationNotCreatedByDefault() { 41 | runner.run(context -> context.getBean(RocketMQAutoConfiguration.class)); 42 | } 43 | 44 | 45 | @Test(expected = NoSuchBeanDefinitionException.class) 46 | public void testDefaultMQProducerNotCreatedByDefault() { 47 | runner.run(context -> context.getBean(DefaultMQProducer.class)); 48 | } 49 | 50 | 51 | @Test 52 | public void testDefaultMQProducerWithRelaxPropertyName() { 53 | runner.withPropertyValues("rocketmq.nameServer=127.0.0.1:9876", 54 | "rocketmq.producer.group=spring_rocketmq"). 55 | run((context) -> { 56 | assertThat(context).hasSingleBean(DefaultMQProducer.class); 57 | assertThat(context).hasSingleBean(RocketMQProperties.class); 58 | }); 59 | 60 | } 61 | 62 | @Test 63 | public void testDefaultMQProducer() { 64 | runner.withPropertyValues("rocketmq.name-server=127.0.0.1:9876", 65 | "rocketmq.producer.group=spring_rocketmq"). 66 | run((context) -> { 67 | assertThat(context).hasSingleBean(DefaultMQProducer.class); 68 | }); 69 | 70 | } 71 | 72 | @Test 73 | public void testRocketMQListenerContainer() { 74 | runner.withPropertyValues("rocketmq.name-server=127.0.0.1:9876"). 75 | withUserConfiguration(TestConfig.class). 76 | run((context) -> { 77 | // No producer on consume side 78 | assertThat(context).doesNotHaveBean(DefaultMQProducer.class); 79 | // Auto-create consume container if existing Bean annotated with @RocketMQMessageListener 80 | assertThat(context).hasSingleBean(DefaultRocketMQListenerContainer.class); 81 | }); 82 | 83 | } 84 | 85 | @Test 86 | public void testRocketMQListenerWithCustomObjectMapper() { 87 | runner.withPropertyValues("rocketmq.name-server=127.0.0.1:9876"). 88 | withUserConfiguration(TestConfig.class, CustomObjectMapperConfig.class). 89 | run((context) -> { 90 | assertThat(context).hasSingleBean(DefaultRocketMQListenerContainer.class); 91 | assertThat(context.getBean(DefaultRocketMQListenerContainer.class).getObjectMapper()) 92 | .isSameAs(context.getBean(CustomObjectMapperConfig.class).testObjectMapper()); 93 | }); 94 | } 95 | 96 | @Test 97 | public void testRocketMQListenerWithSeveralObjectMappers() { 98 | runner.withPropertyValues("rocketmq.name-server=127.0.0.1:9876"). 99 | withUserConfiguration(TestConfig.class, CustomObjectMappersConfig.class). 100 | run((context) -> { 101 | assertThat(context).hasSingleBean(DefaultRocketMQListenerContainer.class); 102 | assertThat(context.getBean(DefaultRocketMQListenerContainer.class).getObjectMapper()) 103 | .isSameAs(context.getBean(CustomObjectMappersConfig.class).rocketMQMessageObjectMapper()); 104 | }); 105 | } 106 | 107 | @Configuration 108 | static class TestConfig { 109 | 110 | @Bean 111 | public Object consumeListener() { 112 | return new MyMessageListener(); 113 | } 114 | } 115 | 116 | @Configuration 117 | static class CustomObjectMapperConfig { 118 | 119 | @Bean 120 | public ObjectMapper testObjectMapper() { 121 | return new ObjectMapper(); 122 | } 123 | 124 | } 125 | 126 | @Configuration 127 | static class CustomObjectMappersConfig { 128 | 129 | @Bean 130 | public ObjectMapper testObjectMapper() { 131 | return new ObjectMapper(); 132 | } 133 | 134 | @Bean 135 | public ObjectMapper rocketMQMessageObjectMapper() { 136 | return new ObjectMapper(); 137 | } 138 | 139 | } 140 | 141 | @RocketMQMessageListener(consumerGroup = "abc", topic = "test") 142 | static class MyMessageListener implements RocketMQListener { 143 | 144 | @Override 145 | public void onMessage(Object message) { 146 | 147 | } 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /style/rmq_checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/style/rmq_checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/autoconfigure/ListenerContainerConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.spring.autoconfigure; 19 | 20 | import com.fasterxml.jackson.databind.ObjectMapper; 21 | import org.apache.rocketmq.spring.annotation.ConsumeMode; 22 | import org.apache.rocketmq.spring.annotation.MessageModel; 23 | import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; 24 | import org.apache.rocketmq.spring.core.RocketMQListener; 25 | import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer; 26 | import org.slf4j.Logger; 27 | import org.slf4j.LoggerFactory; 28 | import org.springframework.aop.framework.AopProxyUtils; 29 | import org.springframework.beans.BeansException; 30 | import org.springframework.beans.factory.SmartInitializingSingleton; 31 | import org.springframework.beans.factory.support.BeanDefinitionValidationException; 32 | import org.springframework.context.ApplicationContext; 33 | import org.springframework.context.ApplicationContextAware; 34 | import org.springframework.context.ConfigurableApplicationContext; 35 | import org.springframework.context.annotation.Configuration; 36 | import org.springframework.context.support.GenericApplicationContext; 37 | import org.springframework.core.env.StandardEnvironment; 38 | 39 | import java.util.Map; 40 | import java.util.Objects; 41 | import java.util.concurrent.atomic.AtomicLong; 42 | 43 | @Configuration 44 | public class ListenerContainerConfiguration implements ApplicationContextAware, SmartInitializingSingleton { 45 | 46 | private static final Logger log = LoggerFactory.getLogger(ListenerContainerConfiguration.class); 47 | 48 | private ConfigurableApplicationContext applicationContext; 49 | 50 | /** 51 | * 计数器,用于在 {@link #registerContainer(String, Object)} 方法中,创建 DefaultRocketMQListenerContainer Bean 时,生成 Bean 的名字。 52 | */ 53 | private AtomicLong counter = new AtomicLong(0); 54 | 55 | private StandardEnvironment environment; 56 | 57 | private RocketMQProperties rocketMQProperties; 58 | 59 | private ObjectMapper objectMapper; 60 | 61 | public ListenerContainerConfiguration(ObjectMapper rocketMQMessageObjectMapper, StandardEnvironment environment, RocketMQProperties rocketMQProperties) { 62 | this.objectMapper = rocketMQMessageObjectMapper; 63 | this.environment = environment; 64 | this.rocketMQProperties = rocketMQProperties; 65 | } 66 | 67 | @Override // 实现自 ApplicationContextAware 接口 68 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 69 | this.applicationContext = (ConfigurableApplicationContext) applicationContext; 70 | } 71 | 72 | @Override // 实现自 SmartInitializingSingleton 接口 73 | public void afterSingletonsInstantiated() { 74 | // 获得所有 @RocketMQMessageListener 注解的 Bean 们 75 | Map beans = this.applicationContext.getBeansWithAnnotation(RocketMQMessageListener.class); 76 | // 遍历 beans 数组,生成(注册)对应的 DefaultRocketMQListenerContainer Bean 对象。 77 | if (Objects.nonNull(beans)) { 78 | beans.forEach(this::registerContainer); 79 | } 80 | } 81 | 82 | private void registerContainer(String beanName, Object bean) { 83 | // 获得 Bean 对应的 Class 类名。因为有可能被 AOP 代理过。 84 | Class clazz = AopProxyUtils.ultimateTargetClass(bean); 85 | // 如果未实现 RocketMQListener 接口,直接抛出 IllegalStateException 异常。 86 | if (!RocketMQListener.class.isAssignableFrom(bean.getClass())) { 87 | throw new IllegalStateException(clazz + " is not instance of " + RocketMQListener.class.getName()); 88 | } 89 | // 获得 @RocketMQMessageListener 注解 90 | RocketMQMessageListener annotation = clazz.getAnnotation(RocketMQMessageListener.class); 91 | // 校验注解配置 92 | validate(annotation); 93 | 94 | // 生成 DefaultRocketMQListenerContainer Bean 的名字 95 | String containerBeanName = String.format("%s_%s", DefaultRocketMQListenerContainer.class.getName(), counter.incrementAndGet()); 96 | GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext; 97 | // 创建 DefaultRocketMQListenerContainer Bean 对象,并注册到 Spring 容器中。 98 | genericApplicationContext.registerBean(containerBeanName, DefaultRocketMQListenerContainer.class, 99 | () -> createRocketMQListenerContainer(bean, annotation)); 100 | 101 | // 从 Spring 容器中,获得刚注册的 DefaultRocketMQListenerContainer Bean 对象 102 | DefaultRocketMQListenerContainer container = genericApplicationContext.getBean(containerBeanName, DefaultRocketMQListenerContainer.class); 103 | // 如果未启动,则进行启动 104 | if (!container.isRunning()) { 105 | try { 106 | container.start(); 107 | } catch (Exception e) { 108 | log.error("Started container failed. {}", container, e); 109 | throw new RuntimeException(e); 110 | } 111 | } 112 | 113 | // 打印日志 114 | log.info("Register the listener to container, listenerBeanName:{}, containerBeanName:{}", beanName, containerBeanName); 115 | } 116 | 117 | private DefaultRocketMQListenerContainer createRocketMQListenerContainer(Object bean, RocketMQMessageListener annotation) { 118 | // 创建 DefaultRocketMQListenerContainer 对象 119 | DefaultRocketMQListenerContainer container = new DefaultRocketMQListenerContainer(); 120 | // 设置其属性 121 | container.setNameServer(rocketMQProperties.getNameServer()); 122 | container.setTopic(environment.resolvePlaceholders(annotation.topic())); 123 | container.setConsumerGroup(environment.resolvePlaceholders(annotation.consumerGroup())); 124 | container.setRocketMQMessageListener(annotation); 125 | container.setRocketMQListener((RocketMQListener) bean); 126 | container.setObjectMapper(objectMapper); 127 | return container; 128 | } 129 | 130 | private void validate(RocketMQMessageListener annotation) { 131 | // 禁止顺序消费 + 广播消费 132 | if (annotation.consumeMode() == ConsumeMode.ORDERLY && 133 | annotation.messageModel() == MessageModel.BROADCASTING) { 134 | throw new BeanDefinitionValidationException("Bad annotation definition in @RocketMQMessageListener, messageModel BROADCASTING does not support ORDERLY message!"); 135 | } 136 | } 137 | 138 | } -------------------------------------------------------------------------------- /rocketmq-spring-boot-parent/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 4.0.0 21 | 22 | 23 | org.apache.rocketmq 24 | rocketmq-spring-all 25 | 2.0.2-SNAPSHOT 26 | ../pom.xml 27 | 28 | 29 | rocketmq-spring-boot-parent 30 | pom 31 | 32 | RocketMQ Spring Boot Parent 33 | Parent package for Apache RocketMQ Spring Boot Support 34 | https://github.com/apache/rocketmq-spring 35 | 36 | 37 | ${project.basedir}/.. 38 | 2.0.5.RELEASE 39 | 5.1.0.RELEASE 40 | 41 | 2.0.2-SNAPSHOT 42 | 43 | 4.3.2 44 | 1.7.25 45 | 2.9.7 46 | 47 | 1.8 48 | @ 49 | 50 | UTF-8 51 | UTF-8 52 | ${java.version} 53 | ${java.version} 54 | -Xdoclint:none 55 | 56 | 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot 62 | ${spring.boot.version} 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-autoconfigure 68 | ${spring.boot.version} 69 | 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-autoconfigure-processor 74 | ${spring.boot.version} 75 | 76 | 77 | 78 | org.springframework.boot 79 | spring-boot-starter-test 80 | ${spring.boot.version} 81 | test 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-starter 87 | ${spring.boot.version} 88 | 89 | 90 | org.springframework.boot 91 | spring-boot-starter-validation 92 | ${spring.boot.version} 93 | 94 | 95 | 96 | org.apache.rocketmq 97 | rocketmq-spring-boot 98 | ${rocketmq.spring.boot.version} 99 | 100 | 101 | 102 | org.apache.rocketmq 103 | rocketmq-client 104 | ${rocketmq-version} 105 | 106 | 107 | org.slf4j 108 | slf4j-api 109 | 110 | 111 | 112 | 113 | 114 | org.springframework 115 | spring-messaging 116 | ${spring.version} 117 | 118 | 119 | 120 | org.springframework 121 | spring-core 122 | ${spring.version} 123 | 124 | 125 | 126 | org.springframework 127 | spring-context 128 | ${spring.version} 129 | 130 | 131 | 132 | org.springframework 133 | spring-aop 134 | ${spring.version} 135 | 136 | 137 | 138 | org.slf4j 139 | slf4j-api 140 | ${slf4j.version} 141 | 142 | 143 | 144 | com.fasterxml.jackson.core 145 | jackson-databind 146 | ${jackson.version} 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | org.apache.maven.plugins 155 | maven-checkstyle-plugin 156 | 2.17 157 | 158 | 159 | validate 160 | validate 161 | 162 | src/main/resources 163 | style/rmq_checkstyle.xml 164 | UTF-8 165 | true 166 | true 167 | 168 | 169 | check 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /style/rmq_codeStyle.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 33 | 39 | 46 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/style/rmq_codeStyle.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 33 | 39 | 46 | -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/rocketmq-produce-demo/src/main/java/org/apache/rocketmq/samples/springboot/ProducerApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package org.apache.rocketmq.samples.springboot; 19 | 20 | import org.apache.rocketmq.client.producer.SendCallback; 21 | import org.apache.rocketmq.client.producer.SendResult; 22 | import org.apache.rocketmq.samples.springboot.domain.OrderPaidEvent; 23 | import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; 24 | import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; 25 | import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; 26 | import org.apache.rocketmq.spring.core.RocketMQTemplate; 27 | import org.apache.rocketmq.spring.support.RocketMQHeaders; 28 | import org.springframework.beans.factory.annotation.Value; 29 | import org.springframework.boot.CommandLineRunner; 30 | import org.springframework.boot.SpringApplication; 31 | import org.springframework.boot.autoconfigure.SpringBootApplication; 32 | import org.springframework.messaging.Message; 33 | import org.springframework.messaging.MessagingException; 34 | import org.springframework.messaging.support.MessageBuilder; 35 | 36 | import javax.annotation.Resource; 37 | import java.math.BigDecimal; 38 | import java.util.concurrent.ConcurrentHashMap; 39 | import java.util.concurrent.atomic.AtomicInteger; 40 | 41 | /** 42 | * Producer, using RocketMQTemplate sends a variety of messages 43 | */ 44 | @SpringBootApplication 45 | public class ProducerApplication implements CommandLineRunner { 46 | private static final String TX_PGROUP_NAME = "myTxProducerGroup"; 47 | @Resource 48 | private RocketMQTemplate rocketMQTemplate; 49 | @Value("${demo.rocketmq.transTopic}") 50 | private String springTransTopic; 51 | @Value("${demo.rocketmq.topic}") 52 | private String springTopic; 53 | @Value("${demo.rocketmq.orderTopic}") 54 | private String orderPaidTopic; 55 | @Value("${demo.rocketmq.msgExtTopic}") 56 | private String msgExtTopic; 57 | 58 | public static void main(String[] args) { 59 | SpringApplication.run(ProducerApplication.class, args); 60 | } 61 | 62 | @Override 63 | public void run(String... args) throws Exception { 64 | // Send string 65 | SendResult sendResult = rocketMQTemplate.syncSend(springTopic, "Hello, World!"); 66 | System.out.printf("syncSend1 to topic %s sendResult=%s %n", springTopic, sendResult); 67 | 68 | // Send string with spring Message 69 | sendResult = rocketMQTemplate.syncSend(springTopic, MessageBuilder.withPayload("Hello, World! I'm from spring message").build()); 70 | System.out.printf("syncSend2 to topic %s sendResult=%s %n", springTopic, sendResult); 71 | 72 | // Send user-defined object 73 | rocketMQTemplate.asyncSend(orderPaidTopic, new OrderPaidEvent("T_001", new BigDecimal("88.00")), new SendCallback() { 74 | public void onSuccess(SendResult var1) { 75 | System.out.printf("async onSucess SendResult=%s %n", var1); 76 | } 77 | 78 | public void onException(Throwable var1) { 79 | System.out.printf("async onException Throwable=%s %n", var1); 80 | } 81 | 82 | }); 83 | 84 | // Send message with special tag 85 | rocketMQTemplate.convertAndSend(msgExtTopic + ":tag0", "I'm from tag0"); // tag0 will not be consumer-selected 86 | System.out.printf("syncSend topic %s tag %s %n", msgExtTopic, "tag0"); 87 | rocketMQTemplate.convertAndSend(msgExtTopic + ":tag1", "I'm from tag1"); 88 | System.out.printf("syncSend topic %s tag %s %n", msgExtTopic, "tag1"); 89 | 90 | // Send transactional messages 91 | testTransaction(); 92 | } 93 | 94 | 95 | private void testTransaction() throws MessagingException { 96 | String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"}; 97 | for (int i = 0; i < 10; i++) { 98 | try { 99 | 100 | Message msg = MessageBuilder.withPayload("Hello RocketMQ " + i). 101 | setHeader(RocketMQHeaders.KEYS, "KEY_" + i).build(); 102 | SendResult sendResult = rocketMQTemplate.sendMessageInTransaction(TX_PGROUP_NAME, 103 | springTransTopic + ":" + tags[i % tags.length], msg, null); 104 | System.out.printf("------ send Transactional msg body = %s , sendResult=%s %n", 105 | msg.getPayload(), sendResult.getSendStatus()); 106 | 107 | Thread.sleep(10); 108 | } catch (Exception e) { 109 | e.printStackTrace(); 110 | } 111 | } 112 | } 113 | 114 | @RocketMQTransactionListener(txProducerGroup = TX_PGROUP_NAME) 115 | class TransactionListenerImpl implements RocketMQLocalTransactionListener { 116 | 117 | private AtomicInteger transactionIndex = new AtomicInteger(0); 118 | 119 | private ConcurrentHashMap localTrans = new ConcurrentHashMap(); 120 | 121 | public TransactionListenerImpl() { 122 | System.out.println(""); 123 | } 124 | 125 | @Override 126 | public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { 127 | String transId = (String)msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID); 128 | System.out.printf("#### executeLocalTransaction is executed, msgTransactionId=%s %n", 129 | transId); 130 | int value = transactionIndex.getAndIncrement(); 131 | int status = value % 3; 132 | localTrans.put(transId, status); 133 | if (status == 0) { 134 | // Return local transaction with success(commit), in this case, 135 | // this message will not be checked in checkLocalTransaction() 136 | System.out.printf(" # COMMIT # Simulating msg %s related local transaction exec succeeded! ### %n", msg.getPayload()); 137 | return RocketMQLocalTransactionState.COMMIT; 138 | } 139 | 140 | if (status == 1) { 141 | // Return local transaction with failure(rollback) , in this case, 142 | // this message will not be checked in checkLocalTransaction() 143 | System.out.printf(" # ROLLBACK # Simulating %s related local transaction exec failed! %n", msg.getPayload()); 144 | return RocketMQLocalTransactionState.ROLLBACK; 145 | } 146 | 147 | System.out.printf(" # UNKNOW # Simulating %s related local transaction exec UNKNOWN! \n"); 148 | return RocketMQLocalTransactionState.UNKNOWN; 149 | } 150 | 151 | @Override 152 | public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { 153 | String transId = (String)msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID); 154 | RocketMQLocalTransactionState retState = RocketMQLocalTransactionState.COMMIT; 155 | Integer status = localTrans.get(transId); 156 | if (null != status) { 157 | switch (status) { 158 | case 0: 159 | retState = RocketMQLocalTransactionState.UNKNOWN; 160 | break; 161 | case 1: 162 | retState = RocketMQLocalTransactionState.COMMIT; 163 | break; 164 | case 2: 165 | retState = RocketMQLocalTransactionState.ROLLBACK; 166 | break; 167 | } 168 | } 169 | System.out.printf("------ !!! checkLocalTransaction is executed once," + 170 | " msgTransactionId=%s, TransactionState=%s status=%s %n", 171 | transId, retState, status); 172 | return retState; 173 | } 174 | } 175 | 176 | } 177 | -------------------------------------------------------------------------------- /rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/support/RocketMQUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | package org.apache.rocketmq.spring.support; 18 | 19 | import com.fasterxml.jackson.databind.ObjectMapper; 20 | import org.apache.rocketmq.client.exception.MQClientException; 21 | import org.apache.rocketmq.client.producer.LocalTransactionState; 22 | import org.apache.rocketmq.client.producer.TransactionListener; 23 | import org.apache.rocketmq.common.message.Message; 24 | import org.apache.rocketmq.common.message.MessageExt; 25 | import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; 26 | import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; 27 | import org.springframework.messaging.MessageHeaders; 28 | import org.springframework.messaging.MessagingException; 29 | import org.springframework.messaging.support.MessageBuilder; 30 | import org.springframework.util.StringUtils; 31 | import org.slf4j.Logger; 32 | import org.slf4j.LoggerFactory; 33 | import java.nio.charset.Charset; 34 | import java.util.Objects; 35 | 36 | public class RocketMQUtil { 37 | 38 | private final static Logger log = LoggerFactory.getLogger(RocketMQUtil.class); 39 | 40 | public static TransactionListener convert(RocketMQLocalTransactionListener listener) { 41 | return new TransactionListener() { 42 | 43 | @Override 44 | public LocalTransactionState executeLocalTransaction(Message message, Object obj) { 45 | // 转换 RocketMQ Message 转换成 Spring Message 对象 46 | // 回调 RocketMQLocalTransactionListener 监听器 47 | RocketMQLocalTransactionState state = listener.executeLocalTransaction(convertToSpringMessage(message), obj); 48 | // 转换 Spring Boot RocketMQ RocketMQLocalTransactionState 事务状态,成 RocketMQ LocalTransactionState 事务状态 49 | return convertLocalTransactionState(state); 50 | } 51 | 52 | @Override 53 | public LocalTransactionState checkLocalTransaction(MessageExt messageExt) { 54 | // 转换 RocketMQ Message 转换成 Spring Message 对象 55 | // 回调 RocketMQLocalTransactionListener 监听器 56 | RocketMQLocalTransactionState state = listener.checkLocalTransaction(convertToSpringMessage(messageExt)); 57 | // 转换 Spring Boot RocketMQ RocketMQLocalTransactionState 事务状态,成 RocketMQ LocalTransactionState 事务状态 58 | return convertLocalTransactionState(state); 59 | } 60 | 61 | }; 62 | } 63 | 64 | private static LocalTransactionState convertLocalTransactionState(RocketMQLocalTransactionState state) { 65 | switch (state) { 66 | case UNKNOWN: 67 | return LocalTransactionState.UNKNOW; 68 | case COMMIT: 69 | return LocalTransactionState.COMMIT_MESSAGE; 70 | case ROLLBACK: 71 | return LocalTransactionState.ROLLBACK_MESSAGE; 72 | } 73 | 74 | // Never happen 75 | log.warn("Failed to covert enum type RocketMQLocalTransactionState.%s", state); 76 | return LocalTransactionState.UNKNOW; 77 | } 78 | 79 | public static MessagingException convert(MQClientException e) { 80 | return new MessagingException(e.getErrorMessage(), e); 81 | } 82 | 83 | // checkLocalTransaction 调用 84 | public static org.springframework.messaging.Message convertToSpringMessage(org.apache.rocketmq.common.message.MessageExt message) { 85 | org.springframework.messaging.Message retMessage = 86 | MessageBuilder.withPayload(message.getBody()). 87 | setHeader(RocketMQHeaders.KEYS, message.getKeys()). 88 | setHeader(RocketMQHeaders.TAGS, message.getTags()). 89 | setHeader(RocketMQHeaders.TOPIC, message.getTopic()). 90 | setHeader(RocketMQHeaders.MESSAGE_ID, message.getMsgId()). 91 | setHeader(RocketMQHeaders.BORN_TIMESTAMP, message.getBornTimestamp()). 92 | setHeader(RocketMQHeaders.BORN_HOST, message.getBornHostString()). 93 | setHeader(RocketMQHeaders.FLAG, message.getFlag()). 94 | setHeader(RocketMQHeaders.QUEUE_ID, message.getQueueId()). 95 | setHeader(RocketMQHeaders.SYS_FLAG, message.getSysFlag()). 96 | setHeader(RocketMQHeaders.TRANSACTION_ID, message.getTransactionId()). 97 | setHeader(RocketMQHeaders.PROPERTIES, message.getProperties()). 98 | build(); 99 | 100 | return retMessage; 101 | } 102 | 103 | // executeLocalTransaction 调用 104 | public static org.springframework.messaging.Message convertToSpringMessage(org.apache.rocketmq.common.message.Message message) { 105 | org.springframework.messaging.Message retMessage = 106 | MessageBuilder.withPayload(message.getBody()). 107 | setHeader(RocketMQHeaders.KEYS, message.getKeys()). 108 | setHeader(RocketMQHeaders.TAGS, message.getTags()). 109 | setHeader(RocketMQHeaders.TOPIC, message.getTopic()). 110 | setHeader(RocketMQHeaders.FLAG, message.getFlag()). 111 | setHeader(RocketMQHeaders.TRANSACTION_ID, message.getTransactionId()). 112 | setHeader(RocketMQHeaders.PROPERTIES, message.getProperties()). 113 | build(); 114 | 115 | return retMessage; 116 | } 117 | 118 | public static org.apache.rocketmq.common.message.Message convertToRocketMessage(ObjectMapper objectMapper, String charset, 119 | String destination, org.springframework.messaging.Message message) { 120 | // 生成消息的 bytes 数组 121 | Object payloadObj = message.getPayload(); 122 | byte[] payloads; 123 | if (payloadObj instanceof String) { 124 | payloads = ((String) payloadObj).getBytes(Charset.forName(charset)); 125 | } else if (payloadObj instanceof byte[]) { 126 | payloads = (byte[]) message.getPayload(); 127 | } else { 128 | try { 129 | String jsonObj = objectMapper.writeValueAsString(payloadObj); 130 | payloads = jsonObj.getBytes(Charset.forName(charset)); 131 | } catch (Exception e) { 132 | throw new RuntimeException("convert to RocketMQ message failed.", e); 133 | } 134 | } 135 | 136 | // 获得 topic、tag 属性 137 | String[] tempArr = destination.split(":", 2); 138 | String topic = tempArr[0]; 139 | String tags = ""; 140 | if (tempArr.length > 1) { 141 | tags = tempArr[1]; 142 | } 143 | 144 | // 创建 Message 对象,传入上述变量到其构造方法 145 | org.apache.rocketmq.common.message.Message rocketMsg = new org.apache.rocketmq.common.message.Message(topic, tags, payloads); 146 | 147 | // 如果 MessageHeaders 非空,逐个处理 148 | MessageHeaders headers = message.getHeaders(); 149 | if (Objects.nonNull(headers) && !headers.isEmpty()) { 150 | // 设置 KEYS 属性 151 | Object keys = headers.get(RocketMQHeaders.KEYS); 152 | if (!StringUtils.isEmpty(keys)) { // if headers has 'KEYS', set rocketMQ message key 153 | rocketMsg.setKeys(keys.toString()); 154 | } 155 | 156 | // 设置 FLAG 属性 157 | Object flagObj = headers.getOrDefault("FLAG", "0"); 158 | int flag = 0; 159 | try { 160 | flag = Integer.parseInt(flagObj.toString()); 161 | } catch (NumberFormatException e) { 162 | // Ignore it 163 | log.info("flag must be integer, flagObj:{}", flagObj); 164 | } 165 | rocketMsg.setFlag(flag); 166 | 167 | // 设置 WAIT 属性 168 | Object waitStoreMsgOkObj = headers.getOrDefault("WAIT_STORE_MSG_OK", "true"); 169 | boolean waitStoreMsgOK = Boolean.TRUE.equals(waitStoreMsgOkObj); 170 | rocketMsg.setWaitStoreMsgOK(waitStoreMsgOK); 171 | 172 | // 设置 USERS_ 属性 173 | headers.entrySet().stream() 174 | .filter(entry -> !Objects.equals(entry.getKey(), RocketMQHeaders.KEYS) 175 | && !Objects.equals(entry.getKey(), "FLAG") 176 | && !Objects.equals(entry.getKey(), "WAIT_STORE_MSG_OK")) // exclude "KEYS", "FLAG", "WAIT_STORE_MSG_OK" 177 | .forEach(entry -> { 178 | rocketMsg.putUserProperty("USERS_" + entry.getKey(), String.valueOf(entry.getValue())); // add other properties with prefix "USERS_" 179 | }); 180 | 181 | } 182 | 183 | return rocketMsg; 184 | } 185 | 186 | } 187 | -------------------------------------------------------------------------------- /README_zh_CN.md: -------------------------------------------------------------------------------- 1 | # RocketMQ-Spring [![Build Status](https://travis-ci.org/apache/rocketmq-spring.svg?branch=master)](https://travis-ci.org/apache/rocketmq-spring) 2 | 3 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.rocketmq/rocketmq-spring-all/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg:org.apache.rocketmq) 4 | [![GitHub release](https://img.shields.io/badge/release-download-orange.svg)](https://github.com/apache/rocketmq-spring/releases) 5 | [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) 6 | 7 | [English](./README.md) 8 | 9 | 帮助开发者在[Spring Boot](http://projects.spring.io/spring-boot/)中快速集成[RocketMQ](http://rocketmq.apache.org/)。支持Spring Message规范,方便开发者从其它MQ快速切换到RocketMQ。 10 | 11 | ## 如何贡献和帮助社区 12 | 13 | 我们永远欢迎开发者的帮助来使这个项目更加完善,无论是小的文档还是大的功能新特性,请参考RocketMQ的主站了解[细节](http://rocketmq.apache.org/docs/how-to-contribute/) 14 | 15 | ## 前提条件 16 | - JDK 1.8 and above 17 | - [Maven](http://maven.apache.org/) 3.0 and above 18 | 19 | 功能特性: 20 | 21 | - [x] 同步发送 22 | - [x] 同步顺序发送 23 | - [x] 异步发送 24 | - [x] 异步顺序发送 25 | - [x] 顺序消费 26 | - [x] 并发消费(广播/集群) 27 | - [x] one-way方式发送 28 | - [x] 事务方式发送 29 | - [ ] pull消费 30 | 31 | ## Quick Start 32 | 33 | 下面列出来了一些关键点,完整的示例请参考: [rocketmq-spring-boot-samples](rocketmq-spring-boot-samples) 34 | 35 | 注意:当前的RELEASE.VERSION=2.0.1 36 | 37 | ```xml 38 | 39 | 40 | org.apache.rocketmq 41 | rocketmq-spring-boot-starter 42 | ${RELEASE.VERSION} 43 | 44 | ``` 45 | 46 | ### 发送消息 47 | 48 | ```properties 49 | ## application.properties 50 | rocketmq.name-server=127.0.0.1:9876 51 | rocketmq.producer.group=my-group 52 | ``` 53 | 54 | > 注意: 55 | > 56 | > 请将上述示例配置中的`127.0.0.1:9876`替换成真实RocketMQ的NameServer地址与端口 57 | 58 | ```java 59 | @SpringBootApplication 60 | public class ProducerApplication implements CommandLineRunner{ 61 | @Resource 62 | private RocketMQTemplate rocketMQTemplate; 63 | 64 | public static void main(String[] args){ 65 | SpringApplication.run(ProducerApplication.class, args); 66 | } 67 | 68 | public void run(String... args) throws Exception { 69 | rocketMQTemplate.convertAndSend("test-topic-1", "Hello, World!"); 70 | rocketMQTemplate.send("test-topic-1", MessageBuilder.withPayload("Hello, World! I'm from spring message").build()); 71 | rocketMQTemplate.convertAndSend("test-topic-2", new OrderPaidEvent("T_001", new BigDecimal("88.00"))); 72 | 73 | // rocketMQTemplate.destroy(); // notes: once rocketMQTemplate be destroyed, you can not send any message again with this rocketMQTemplate 74 | } 75 | 76 | @Data 77 | @AllArgsConstructor 78 | public class OrderPaidEvent implements Serializable{ 79 | private String orderId; 80 | 81 | private BigDecimal paidMoney; 82 | } 83 | } 84 | ``` 85 | 86 | ### 在发送客户端发送事务性消息并且实现回查Listener 87 | ```java 88 | @SpringBootApplication 89 | public class ProducerApplication implements CommandLineRunner{ 90 | @Resource 91 | private RocketMQTemplate rocketMQTemplate; 92 | 93 | public static void main(String[] args){ 94 | SpringApplication.run(ProducerApplication.class, args); 95 | } 96 | 97 | public void run(String... args) throws Exception { 98 | try { 99 | // Build a SpringMessage for sending in transaction 100 | Message msg = MessageBuilder.withPayload(..)... 101 | // In sendMessageInTransaction(), the first parameter transaction name ("test") 102 | // must be same with the @RocketMQTransactionListener's member field 'transName' 103 | rocketMQTemplate.sendMessageInTransaction("test", "test-topic" msg, null); 104 | } catch (MQClientException e) { 105 | e.printStackTrace(System.out); 106 | } 107 | } 108 | 109 | // Define transaction listener with the annotation @RocketMQTransactionListener 110 | @RocketMQTransactionListener(transName="test") 111 | class TransactionListenerImpl implements RocketMQLocalTransactionListener { 112 | @Override 113 | public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { 114 | // ... local transaction process, return bollback, commit or unknown 115 | return RocketMQLocalTransactionState.UNKNOWN; 116 | } 117 | 118 | @Override 119 | public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { 120 | // ... check transaction status and return bollback, commit or unknown 121 | return RocketMQLocalTransactionState.COMMIT; 122 | } 123 | } 124 | } 125 | ``` 126 | 127 | > 更多发送相关配置 128 | > 129 | > ```properties 130 | > rocketmq.producer.send-message-timeout=300000 131 | > rocketmq.producer.compress-message-body-threshold=4096 132 | > rocketmq.producer.max-message-size=4194304 133 | > rocketmq.producer.retry-times-when-send-async-failed=0 134 | > rocketmq.producer.retry-next-server=true 135 | > rocketmq.producer.retry-times-when-send-failed=2 136 | > ``` 137 | 138 | 139 | ### 接收消息 140 | 141 | ```properties 142 | ## application.properties 143 | rocketmq.name-server=127.0.0.1:9876 144 | ``` 145 | 146 | > 注意: 147 | > 148 | > 请将上述示例配置中的`127.0.0.1:9876`替换成真实RocketMQ的NameServer地址与端口 149 | 150 | ```java 151 | @SpringBootApplication 152 | public class ConsumerApplication{ 153 | 154 | public static void main(String[] args){ 155 | SpringApplication.run(ConsumerApplication.class, args); 156 | } 157 | 158 | @Slf4j 159 | @Service 160 | @RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") 161 | public class MyConsumer1 implements RocketMQListener{ 162 | public void onMessage(String message) { 163 | log.info("received message: {}", message); 164 | } 165 | } 166 | 167 | @Slf4j 168 | @Service 169 | @RocketMQMessageListener(topic = "test-topic-2", consumerGroup = "my-consumer_test-topic-2") 170 | public class MyConsumer2 implements RocketMQListener{ 171 | public void onMessage(OrderPaidEvent orderPaidEvent) { 172 | log.info("received orderPaidEvent: {}", orderPaidEvent); 173 | } 174 | } 175 | } 176 | ``` 177 | 178 | 179 | > 更多消费相关配置 180 | > 181 | > see: [RocketMQMessageListener](src/main/java/org/apache/rocketmq/spring/starter/annotation/RocketMQMessageListener.java) 182 | 183 | 184 | ## FAQ 185 | 186 | 1. 生产环境有多个`nameserver`该如何连接? 187 | 188 | `rocketmq.name-server`支持配置多个`nameserver`地址,采用`;`分隔即可。例如:`172.19.0.1:9876;172.19.0.2:9876` 189 | 190 | 1. `rocketMQTemplate`在什么时候被销毁? 191 | 192 | 开发者在项目中使用`rocketMQTemplate`发送消息时,不需要手动执行`rocketMQTemplate.destroy()`方法, `rocketMQTemplate`会在spring容器销毁时自动销毁。 193 | 194 | 1. 启动报错:`Caused by: org.apache.rocketmq.client.exception.MQClientException: The consumer group[xxx] has been created before, specify another name please` 195 | 196 | RocketMQ在设计时就不希望一个消费者同时处理多个类型的消息,因此同一个`consumerGroup`下的consumer职责应该是一样的,不要干不同的事情(即消费多个topic)。建议`consumerGroup`与`topic`一一对应。 197 | 198 | 1. 发送的消息内容体是如何被序列化与反序列化的? 199 | 200 | RocketMQ的消息体都是以`byte[]`方式存储。当业务系统的消息内容体如果是`java.lang.String`类型时,统一按照`utf-8`编码转成`byte[]`;如果业务系统的消息内容为非`java.lang.String`类型,则采用[jackson-databind](https://github.com/FasterXML/jackson-databind)序列化成`JSON`格式的字符串之后,再统一按照`utf-8`编码转成`byte[]`。 201 | 202 | 1. 如何指定topic的`tags`? 203 | 204 | RocketMQ的最佳实践中推荐:一个应用尽可能用一个Topic,消息子类型用`tags`来标识,`tags`可以由应用自由设置。 205 | 在使用`rocketMQTemplate`发送消息时,通过设置发送方法的`destination`参数来设置消息的目的地,`destination`的格式为`topicName:tagName`,`:`前面表示topic的名称,后面表示`tags`名称。 206 | 207 | > 注意: 208 | > 209 | > `tags`从命名来看像是一个复数,但发送消息时,目的地只能指定一个topic下的一个`tag`,不能指定多个。 210 | 211 | 1. 发送消息时如何设置消息的`key`? 212 | 213 | 可以通过重载的`xxxSend(String destination, Message msg, ...)`方法来发送消息,指定`msg`的`headers`来完成。示例: 214 | 215 | ```java 216 | Message message = MessageBuilder.withPayload(payload).setHeader(MessageConst.PROPERTY_KEYS, msgId).build(); 217 | rocketMQTemplate.send("topic-test", message); 218 | ``` 219 | 220 | 同理还可以根据上面的方式来设置消息的`FLAG`、`WAIT_STORE_MSG_OK`以及一些用户自定义的其它头信息。 221 | 222 | > 注意: 223 | > 224 | > 在将Spring的Message转化为RocketMQ的Message时,为防止`header`信息与RocketMQ的系统属性冲突,在所有`header`的名称前面都统一添加了前缀`USERS_`。因此在消费时如果想获取自定义的消息头信息,请遍历头信息中以`USERS_`开头的key即可。 225 | 226 | 1. 消费消息时,除了获取消息`payload`外,还想获取RocketMQ消息的其它系统属性,需要怎么做? 227 | 228 | 消费者在实现`RocketMQListener`接口时,只需要起泛型为`MessageExt`即可,这样在`onMessage`方法将接收到RocketMQ原生的`MessageExt`消息。 229 | 230 | ```java 231 | @Slf4j 232 | @Service 233 | @RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") 234 | public class MyConsumer2 implements RocketMQListener{ 235 | public void onMessage(MessageExt messageExt) { 236 | log.info("received messageExt: {}", messageExt); 237 | } 238 | } 239 | ``` 240 | 241 | 1. 如何指定消费者从哪开始消费消息,或开始消费的位置? 242 | 243 | 消费者默认开始消费的位置请参考:[RocketMQ FAQ](http://rocketmq.apache.org/docs/faq/)。 244 | 若想自定义消费者开始的消费位置,只需在消费者类添加一个`RocketMQPushConsumerLifecycleListener`接口的实现即可。 示例如下: 245 | 246 | ```java 247 | @Slf4j 248 | @Service 249 | @RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") 250 | public class MyConsumer1 implements RocketMQListener, RocketMQPushConsumerLifecycleListener { 251 | @Override 252 | public void onMessage(String message) { 253 | log.info("received message: {}", message); 254 | } 255 | 256 | @Override 257 | public void prepareStart(final DefaultMQPushConsumer consumer) { 258 | // set consumer consume message from now 259 | consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_TIMESTAMP); 260 | consumer.setConsumeTimestamp(UtilAll.timeMillisToHumanString3(System.currentTimeMillis())); 261 | } 262 | } 263 | ``` 264 | 265 | 同理,任何关于`DefaultMQPushConsumer`的更多其它其它配置,都可以采用上述方式来完成。 266 | 267 | 1. 如何发送事务消息(即半消息支持分布式事务)? 268 | 在客户端,首先用户需要实现RocketMQLocalTransactionListener接口,并在接口类上注解声明@RocketMQTransactionListener,实现确认和回查方法;然后再使用资源模板RocketMQTemplate, 269 | 调用方法sendMessageInTransaction()来进行消息的发布。 注意:这个方法通过指定发送者组名与具体的声明了txProducerGroup的TransactionListener进行关联,您也可以不指定这个值,从而使用默认的事务发送者组。 270 | 271 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /rocketmq-spring-boot-samples/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RocketMQ-Spring [![Build Status](https://travis-ci.org/apache/rocketmq-spring.svg?branch=master)](https://travis-ci.org/apache/rocketmq-spring) 2 | 3 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.rocketmq/rocketmq-spring-all/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg:org.apache.rocketmq) 4 | [![GitHub release](https://img.shields.io/badge/release-download-orange.svg)](https://github.com/apache/rocketmq-spring/releases) 5 | [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) 6 | 7 | [中文](./README_zh_CN.md) 8 | 9 | ## Introduction 10 | 11 | This project aims to help developers quickly integrate [RocketMQ](http://rocketmq.apache.org/) with [Spring Boot](http://projects.spring.io/spring-boot/). 12 | 13 | ## How To Contribute 14 | 15 | We are always very happy to have contributions, whether for trivial cleanups or big new features. Please see the RocketMQ main website to read [details](http://rocketmq.apache.org/docs/how-to-contribute/) 16 | 17 | 18 | ## Prerequisites 19 | - JDK 1.8 and above 20 | - [Maven](http://maven.apache.org/) 3.0 and above 21 | 22 | ## Build and Install with local maven repository 23 | 24 | ``` 25 | mvn clean install 26 | ``` 27 | 28 | ## Features: 29 | 30 | - [x] synchronous transmission 31 | - [x] synchronous ordered transmission 32 | - [x] asynchronous transmission 33 | - [x] asynchronous ordered transmission 34 | - [x] orderly consume 35 | - [x] concurrently consume(broadcasting/clustering) 36 | - [x] one-way transmission 37 | - [x] transaction transmission 38 | - [ ] pull consume 39 | 40 | ## Quick Start 41 | 42 | Please see the complete sample [rocketmq-spring-boot-samples](rocketmq-spring-boot-samples) 43 | 44 | Note: Current RELEASE.VERSION=2.0.1 45 | 46 | ```xml 47 | 48 | 49 | org.apache.rocketmq 50 | rocketmq-spring-boot-starter 51 | ${RELEASE.VERSION} 52 | 53 | ``` 54 | 55 | ### Produce Message 56 | 57 | ```properties 58 | ## application.properties 59 | rocketmq.name-server=127.0.0.1:9876 60 | rocketmq.producer.group=my-group 61 | ``` 62 | 63 | > Note: 64 | > 65 | > Maybe you need change `127.0.0.1:9876` with your real NameServer address for RocketMQ 66 | 67 | ```java 68 | @SpringBootApplication 69 | public class ProducerApplication implements CommandLineRunner{ 70 | @Resource 71 | private RocketMQTemplate rocketMQTemplate; 72 | 73 | public static void main(String[] args){ 74 | SpringApplication.run(ProducerApplication.class, args); 75 | } 76 | 77 | public void run(String... args) throws Exception { 78 | rocketMQTemplate.convertAndSend("test-topic-1", "Hello, World!"); 79 | rocketMQTemplate.send("test-topic-1", MessageBuilder.withPayload("Hello, World! I'm from spring message").build()); 80 | rocketMQTemplate.convertAndSend("test-topic-2", new OrderPaidEvent("T_001", new BigDecimal("88.00"))); 81 | 82 | // rocketMQTemplate.destroy(); // notes: once rocketMQTemplate be destroyed, you can not send any message again with this rocketMQTemplate 83 | } 84 | 85 | @Data 86 | @AllArgsConstructor 87 | public class OrderPaidEvent implements Serializable{ 88 | private String orderId; 89 | 90 | private BigDecimal paidMoney; 91 | } 92 | } 93 | ``` 94 | 95 | > More relevant configurations for producing: 96 | > 97 | > ```properties 98 | > rocketmq.producer.send-message-timeout=300000 99 | > rocketmq.producer.compress-message-body-threshold=4096 100 | > rocketmq.producer.max-message-size=4194304 101 | > rocketmq.producer.retry-times-when-send-async-failed=0 102 | > rocketmq.producer.retry-next-server=true 103 | > rocketmq.producer.retry-times-when-send-failed=2 104 | > ``` 105 | 106 | 107 | ### Send message in transaction and implement local check Listener 108 | ```java 109 | @SpringBootApplication 110 | public class ProducerApplication implements CommandLineRunner{ 111 | @Resource 112 | private RocketMQTemplate rocketMQTemplate; 113 | 114 | public static void main(String[] args){ 115 | SpringApplication.run(ProducerApplication.class, args); 116 | } 117 | 118 | public void run(String... args) throws Exception { 119 | try { 120 | // Build a SpringMessage for sending in transaction 121 | Message msg = MessageBuilder.withPayload(..)... 122 | // In sendMessageInTransaction(), the first parameter transaction name ("test") 123 | // must be same with the @RocketMQTransactionListener's member field 'transName' 124 | rocketMQTemplate.sendMessageInTransaction("test", "test-topic" msg, null); 125 | } catch (MQClientException e) { 126 | e.printStackTrace(System.out); 127 | } 128 | } 129 | 130 | // Define transaction listener with the annotation @RocketMQTransactionListener 131 | @RocketMQTransactionListener(transName="test") 132 | class TransactionListenerImpl implements RocketMQLocalTransactionListener { 133 | @Override 134 | public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { 135 | // ... local transaction process, return bollback, commit or unknown 136 | return RocketMQLocalTransactionState.UNKNOWN; 137 | } 138 | 139 | @Override 140 | public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { 141 | // ... check transaction status and return bollback, commit or unknown 142 | return RocketMQLocalTransactionState.COMMIT; 143 | } 144 | } 145 | } 146 | ``` 147 | 148 | ### Consume Message 149 | 150 | ```properties 151 | ## application.properties 152 | rocketmq.name-server=127.0.0.1:9876 153 | ``` 154 | 155 | > Note: 156 | > 157 | > Maybe you need change `127.0.0.1:9876` with your real NameServer address for RocketMQ 158 | 159 | ```java 160 | @SpringBootApplication 161 | public class ConsumerApplication{ 162 | 163 | public static void main(String[] args){ 164 | SpringApplication.run(ConsumerApplication.class, args); 165 | } 166 | 167 | @Slf4j 168 | @Service 169 | @RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") 170 | public class MyConsumer1 implements RocketMQListener{ 171 | public void onMessage(String message) { 172 | log.info("received message: {}", message); 173 | } 174 | } 175 | 176 | @Slf4j 177 | @Service 178 | @RocketMQMessageListener(topic = "test-topic-2", consumerGroup = "my-consumer_test-topic-2") 179 | public class MyConsumer2 implements RocketMQListener{ 180 | public void onMessage(OrderPaidEvent orderPaidEvent) { 181 | log.info("received orderPaidEvent: {}", orderPaidEvent); 182 | } 183 | } 184 | } 185 | ``` 186 | 187 | > More relevant configurations for consuming: 188 | > 189 | > see: [RocketMQMessageListener](src/main/java/org/apache/rocketmq/spring/starter/annotation/RocketMQMessageListener.java) 190 | 191 | 192 | ## FAQ 193 | 194 | 1. How to connected many `nameserver` on production environment? 195 | 196 | `rocketmq.name-server` support the configuration of multiple `nameserver`, separated by `;`. For example: `172.19.0.1: 9876; 172.19.0.2: 9876` 197 | 198 | 1. When was `rocketMQTemplate` destroyed? 199 | 200 | Developers do not need to manually execute the `rocketMQTemplate.destroy ()` method when using `rocketMQTemplate` to send a message in the project, and` rocketMQTemplate` will be destroyed automatically when the spring container is destroyed. 201 | 202 | 1. start exception:`Caused by: org.apache.rocketmq.client.exception.MQClientException: The consumer group[xxx] has been created before, specify another name please` 203 | 204 | RocketMQ in the design do not want a consumer to deal with multiple types of messages at the same time, so the same `consumerGroup` consumer responsibility should be the same, do not do different things (that is, consumption of multiple topics). Suggested `consumerGroup` and` topic` one correspondence. 205 | 206 | 1. How is the message content body being serialized and deserialized? 207 | 208 | RocketMQ's message body is stored as `byte []`. When the business system message content body if it is `java.lang.String` type, unified in accordance with` utf-8` code into `byte []`; If the business system message content is not `java.lang.String` Type, then use [jackson-databind](https://github.com/FasterXML/jackson-databind) serialized into the `JSON` format string, and then unified in accordance with` utf-8` code into `byte [] `. 209 | 210 | 1. How do I specify the `tags` for topic? 211 | 212 | RocketMQ best practice recommended: an application as much as possible with one Topic, the message sub-type with `tags` to identify,` tags` can be set by the application free. 213 | 214 | When you use `rocketMQTemplate` to send a message, set the destination of the message by setting the` destination` parameter of the send method. The `destination` format is `topicName:tagName`, `:` Precedes the name of the topic, followed by the `tags` name. 215 | 216 | > Note: 217 | > 218 | > `tags` looks a complex, but when sending a message , the destination can only specify one topic under a `tag`, can not specify multiple. 219 | 220 | 1. How do I set the message's `key` when sending a message? 221 | 222 | You can send a message by overloading method like `xxxSend(String destination, Message msg, ...)`, setting `headers` of `msg`. for example: 223 | 224 | ```java 225 | Message message = MessageBuilder.withPayload(payload).setHeader(MessageConst.PROPERTY_KEYS, msgId).build(); 226 | rocketMQTemplate.send("topic-test", message); 227 | ``` 228 | 229 | Similarly, you can also set the message `FLAG`,` WAIT_STORE_MSG_OK` and some other user-defined other header information according to the above method. 230 | 231 | > Note: 232 | > 233 | > In the case of converting Spring's Message to RocketMQ's Message, to prevent the `header` information from conflicting with RocketMQ's system properties, the prefix `USERS_` was added in front of all `header` names. So if you want to get a custom message header when consuming, please pass through the key at the beginning of `USERS_` in the header. 234 | 235 | 1. When consume message, in addition to get the message `payload`, but also want to get RocketMQ message of other system attributes, how to do? 236 | 237 | Consumers in the realization of `RocketMQListener` interface, only need to be generic for the` MessageExt` can, so in the `onMessage` method will receive RocketMQ native 'MessageExt` message. 238 | 239 | ```java 240 | @Slf4j 241 | @Service 242 | @RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") 243 | public class MyConsumer2 implements RocketMQListener{ 244 | public void onMessage(MessageExt messageExt) { 245 | log.info("received messageExt: {}", messageExt); 246 | } 247 | } 248 | ``` 249 | 250 | 1. How do I specify where consumers start consuming messages? 251 | 252 | The default consume offset please refer: [RocketMQ FAQ](http://rocketmq.apache.org/docs/faq/). 253 | To customize the consumer's starting location, simply add a `RocketMQPushConsumerLifecycleListener` interface implementation to the consumer class. Examples are as follows: 254 | 255 | ```java 256 | @Slf4j 257 | @Service 258 | @RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") 259 | public class MyConsumer1 implements RocketMQListener, RocketMQPushConsumerLifecycleListener { 260 | @Override 261 | public void onMessage(String message) { 262 | log.info("received message: {}", message); 263 | } 264 | 265 | @Override 266 | public void prepareStart(final DefaultMQPushConsumer consumer) { 267 | // set consumer consume message from now 268 | consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_TIMESTAMP); 269 | consumer.setConsumeTimestamp(UtilAll.timeMillisToHumanString3(System.currentTimeMillis())); 270 | } 271 | } 272 | ``` 273 | 274 | Similarly, any other configuration on `DefaultMQPushConsumer` can be done in the same way as above. 275 | 276 | 277 | 1. How do I send transactional messages? 278 | It needs two steps on client side: a) Define a class which is annotated with @RocketMQTransactionListener and implements RocketMQLocalTransactionListener interface, in which, the executeLocalTransaction() and checkLocalTransaction() methods are implemented; 279 | b) Invoke the sendMessageInTransaction() method with the RocketMQTemplate API. Note: The first parameter of this method is correlated with the txProducerGroup attribute of @RocketMQTransactionListener. It can be null if using the default transaction producer group. 280 | --------------------------------------------------------------------------------