200 changed files with 10330 additions and 19 deletions
@ -0,0 +1,37 @@ |
|||
package com.elink.esua.epdc.rocketmq.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* |
|||
* 组织机构信息修改-发送MQ消息DTO |
|||
* |
|||
* @Author:liuchuang |
|||
* @Date:2020/3/6 19:58 |
|||
*/ |
|||
@Data |
|||
public class OrganizationModifyDTO implements Serializable { |
|||
private static final long serialVersionUID = -1330090682508121169L; |
|||
|
|||
/** |
|||
* 部门ID |
|||
*/ |
|||
private Long deptId; |
|||
|
|||
/** |
|||
* 旧部门名称 |
|||
*/ |
|||
private String oldDeptName; |
|||
|
|||
/** |
|||
* 新部门名称 |
|||
*/ |
|||
private String newDeptName; |
|||
|
|||
/** |
|||
* 部门类型 |
|||
*/ |
|||
private String typeKey; |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
package com.elink.esua.epdc.rocketmq.producer; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.rocketmq.client.producer.SendResult; |
|||
import org.apache.rocketmq.common.message.Message; |
|||
import org.apache.rocketmq.spring.core.RocketMQTemplate; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
/** |
|||
* |
|||
* 组织机构信息修改-发送MQ消息 |
|||
* |
|||
* @Author:liuchuang |
|||
* @Date:2020/3/5 17:53 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
public class OrganizationModifyProducer { |
|||
|
|||
@Autowired |
|||
private RocketMQTemplate rocketMQTemplate; |
|||
|
|||
/** |
|||
* |
|||
* 发送消息 |
|||
* |
|||
* @params [topic, tag, keys, body] |
|||
* @return void |
|||
* @author liuchuang |
|||
* @since 2020/3/6 21:09 |
|||
*/ |
|||
public void sendMessage(String topic, String tag, String keys, String body) { |
|||
Message message = new Message(topic, tag, keys, body.getBytes()); |
|||
try { |
|||
SendResult sendResult = rocketMQTemplate.getProducer().send(message); |
|||
log.info("EPDC-ADMIN-SERVER发送消息结果:{sendStatus:{}, topic:{}, msgId:{}}", sendResult.getSendStatus(), topic, sendResult.getMsgId()); |
|||
} catch (Exception e) { |
|||
log.error("EPDC-ADMIN-SERVER发送消息异常:{topic:{}, tag:{}, keys:{}, body:{}}", topic, tag, keys, body); |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
package com.elink.esua.epdc.commons.tools.constant; |
|||
|
|||
/** |
|||
* |
|||
* RocketMq 常量类 |
|||
* |
|||
* @Author:liuchuang |
|||
* @Date:2020/3/6 20:52 |
|||
*/ |
|||
public interface RocketMqConstant { |
|||
|
|||
/** |
|||
* 组织机构信息修改-消息topic |
|||
*/ |
|||
String MQ_TOPIC_ORGANIZATION = "organizationTopic"; |
|||
|
|||
/** |
|||
* 组织机构信息修改-消息tag |
|||
*/ |
|||
String MQ_TAG_ORGANIZATION = "organizationTag"; |
|||
|
|||
/** |
|||
* 分类信息修改-消息topic |
|||
*/ |
|||
String MQ_TOPIC_CATEGORY = "categoryTopic"; |
|||
|
|||
/** |
|||
* 分类信息修改-消息tag |
|||
*/ |
|||
String MQ_TAG_CATEGORY = "categoryTag"; |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
The issue tracker is **ONLY** used for bug report and feature request. |
|||
|
|||
Any question or RocketMQ proposal please use our [mailing lists](http://rocketmq.apache.org/about/contact/). |
|||
|
|||
**BUG REPORT** |
|||
|
|||
1. Please describe the issue you observed: |
|||
|
|||
- What did you do (The steps to reproduce)? |
|||
|
|||
- What did you expect to see? |
|||
|
|||
- What did you see instead? |
|||
|
|||
2. Please tell us about your environment: |
|||
|
|||
3. Other information (e.g. detailed explanation, logs, related issues, suggestions how to fix, etc): |
|||
|
|||
**FEATURE REQUEST** |
|||
|
|||
1. Please describe the feature you are requesting. |
|||
|
|||
2. Provide any additional detail on your proposed use case for this feature. |
|||
|
|||
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? |
|||
@ -0,0 +1,20 @@ |
|||
## What is the purpose of the change |
|||
|
|||
XXXXX |
|||
|
|||
## Brief changelog |
|||
|
|||
XX |
|||
|
|||
## Verifying this change |
|||
|
|||
XXXX |
|||
|
|||
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`. |
|||
|
|||
- [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. |
|||
- [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. |
|||
- [x] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. |
|||
- [x] Write necessary unit-test(over 80% coverage) to verify your logic correction, more mock a little better when cross module dependency exist. |
|||
- [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. |
|||
- [ ] If this contribution is large, please file an [Apache Individual Contributor License Agreement](http://www.apache.org/licenses/#clas). |
|||
@ -0,0 +1,13 @@ |
|||
.idea |
|||
.classpath |
|||
.project |
|||
.settings/ |
|||
target/ |
|||
*.log* |
|||
*.iml |
|||
.idea/ |
|||
*.versionsBackup |
|||
!NOTICE-BIN |
|||
!LICENSE-BIN |
|||
.DS_Store |
|||
.vscode |
|||
@ -0,0 +1,30 @@ |
|||
notifications: |
|||
email: |
|||
recipients: |
|||
- dev@rocketmq.apache.org |
|||
on_success: change |
|||
on_failure: always |
|||
|
|||
language: java |
|||
|
|||
matrix: |
|||
include: |
|||
# On OSX, run with default JDK only. |
|||
# - os: osx |
|||
# On Linux, run with specific JDKs only. |
|||
- os: linux |
|||
env: CUSTOM_JDK="oraclejdk8" |
|||
|
|||
before_install: |
|||
- echo 'MAVEN_OPTS="$MAVEN_OPTS -Xmx1024m -XX:MaxPermSize=512m -XX:+BytecodeVerificationLocal"' >> ~/.mavenrc |
|||
- cat ~/.mavenrc |
|||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then export JAVA_HOME=$(/usr/libexec/java_home); fi |
|||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then jdk_switcher use "$CUSTOM_JDK"; fi |
|||
|
|||
script: |
|||
- travis_retry mvn -B clean apache-rat:check |
|||
- travis_retry mvn -B package jacoco:report coveralls:report |
|||
|
|||
after_success: |
|||
# - mvn clean install -Pit-test |
|||
- mvn sonar:sonar -Psonar-apache |
|||
@ -0,0 +1,202 @@ |
|||
|
|||
Apache License |
|||
Version 2.0, January 2004 |
|||
http://www.apache.org/licenses/ |
|||
|
|||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
|||
|
|||
1. Definitions. |
|||
|
|||
"License" shall mean the terms and conditions for use, reproduction, |
|||
and distribution as defined by Sections 1 through 9 of this document. |
|||
|
|||
"Licensor" shall mean the copyright owner or entity authorized by |
|||
the copyright owner that is granting the License. |
|||
|
|||
"Legal Entity" shall mean the union of the acting entity and all |
|||
other entities that control, are controlled by, or are under common |
|||
control with that entity. For the purposes of this definition, |
|||
"control" means (i) the power, direct or indirect, to cause the |
|||
direction or management of such entity, whether by contract or |
|||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
|||
outstanding shares, or (iii) beneficial ownership of such entity. |
|||
|
|||
"You" (or "Your") shall mean an individual or Legal Entity |
|||
exercising permissions granted by this License. |
|||
|
|||
"Source" form shall mean the preferred form for making modifications, |
|||
including but not limited to software source code, documentation |
|||
source, and configuration files. |
|||
|
|||
"Object" form shall mean any form resulting from mechanical |
|||
transformation or translation of a Source form, including but |
|||
not limited to compiled object code, generated documentation, |
|||
and conversions to other media types. |
|||
|
|||
"Work" shall mean the work of authorship, whether in Source or |
|||
Object form, made available under the License, as indicated by a |
|||
copyright notice that is included in or attached to the work |
|||
(an example is provided in the Appendix below). |
|||
|
|||
"Derivative Works" shall mean any work, whether in Source or Object |
|||
form, that is based on (or derived from) the Work and for which the |
|||
editorial revisions, annotations, elaborations, or other modifications |
|||
represent, as a whole, an original work of authorship. For the purposes |
|||
of this License, Derivative Works shall not include works that remain |
|||
separable from, or merely link (or bind by name) to the interfaces of, |
|||
the Work and Derivative Works thereof. |
|||
|
|||
"Contribution" shall mean any work of authorship, including |
|||
the original version of the Work and any modifications or additions |
|||
to that Work or Derivative Works thereof, that is intentionally |
|||
submitted to Licensor for inclusion in the Work by the copyright owner |
|||
or by an individual or Legal Entity authorized to submit on behalf of |
|||
the copyright owner. For the purposes of this definition, "submitted" |
|||
means any form of electronic, verbal, or written communication sent |
|||
to the Licensor or its representatives, including but not limited to |
|||
communication on electronic mailing lists, source code control systems, |
|||
and issue tracking systems that are managed by, or on behalf of, the |
|||
Licensor for the purpose of discussing and improving the Work, but |
|||
excluding communication that is conspicuously marked or otherwise |
|||
designated in writing by the copyright owner as "Not a Contribution." |
|||
|
|||
"Contributor" shall mean Licensor and any individual or Legal Entity |
|||
on behalf of whom a Contribution has been received by Licensor and |
|||
subsequently incorporated within the Work. |
|||
|
|||
2. Grant of Copyright License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
copyright license to reproduce, prepare Derivative Works of, |
|||
publicly display, publicly perform, sublicense, and distribute the |
|||
Work and such Derivative Works in Source or Object form. |
|||
|
|||
3. Grant of Patent License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
(except as stated in this section) patent license to make, have made, |
|||
use, offer to sell, sell, import, and otherwise transfer the Work, |
|||
where such license applies only to those patent claims licensable |
|||
by such Contributor that are necessarily infringed by their |
|||
Contribution(s) alone or by combination of their Contribution(s) |
|||
with the Work to which such Contribution(s) was submitted. If You |
|||
institute patent litigation against any entity (including a |
|||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
|||
or a Contribution incorporated within the Work constitutes direct |
|||
or contributory patent infringement, then any patent licenses |
|||
granted to You under this License for that Work shall terminate |
|||
as of the date such litigation is filed. |
|||
|
|||
4. Redistribution. You may reproduce and distribute copies of the |
|||
Work or Derivative Works thereof in any medium, with or without |
|||
modifications, and in Source or Object form, provided that You |
|||
meet the following conditions: |
|||
|
|||
(a) You must give any other recipients of the Work or |
|||
Derivative Works a copy of this License; and |
|||
|
|||
(b) You must cause any modified files to carry prominent notices |
|||
stating that You changed the files; and |
|||
|
|||
(c) You must retain, in the Source form of any Derivative Works |
|||
that You distribute, all copyright, patent, trademark, and |
|||
attribution notices from the Source form of the Work, |
|||
excluding those notices that do not pertain to any part of |
|||
the Derivative Works; and |
|||
|
|||
(d) If the Work includes a "NOTICE" text file as part of its |
|||
distribution, then any Derivative Works that You distribute must |
|||
include a readable copy of the attribution notices contained |
|||
within such NOTICE file, excluding those notices that do not |
|||
pertain to any part of the Derivative Works, in at least one |
|||
of the following places: within a NOTICE text file distributed |
|||
as part of the Derivative Works; within the Source form or |
|||
documentation, if provided along with the Derivative Works; or, |
|||
within a display generated by the Derivative Works, if and |
|||
wherever such third-party notices normally appear. The contents |
|||
of the NOTICE file are for informational purposes only and |
|||
do not modify the License. You may add Your own attribution |
|||
notices within Derivative Works that You distribute, alongside |
|||
or as an addendum to the NOTICE text from the Work, provided |
|||
that such additional attribution notices cannot be construed |
|||
as modifying the License. |
|||
|
|||
You may add Your own copyright statement to Your modifications and |
|||
may provide additional or different license terms and conditions |
|||
for use, reproduction, or distribution of Your modifications, or |
|||
for any such Derivative Works as a whole, provided Your use, |
|||
reproduction, and distribution of the Work otherwise complies with |
|||
the conditions stated in this License. |
|||
|
|||
5. Submission of Contributions. Unless You explicitly state otherwise, |
|||
any Contribution intentionally submitted for inclusion in the Work |
|||
by You to the Licensor shall be under the terms and conditions of |
|||
this License, without any additional terms or conditions. |
|||
Notwithstanding the above, nothing herein shall supersede or modify |
|||
the terms of any separate license agreement you may have executed |
|||
with Licensor regarding such Contributions. |
|||
|
|||
6. Trademarks. This License does not grant permission to use the trade |
|||
names, trademarks, service marks, or product names of the Licensor, |
|||
except as required for reasonable and customary use in describing the |
|||
origin of the Work and reproducing the content of the NOTICE file. |
|||
|
|||
7. Disclaimer of Warranty. Unless required by applicable law or |
|||
agreed to in writing, Licensor provides the Work (and each |
|||
Contributor provides its Contributions) on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
|||
implied, including, without limitation, any warranties or conditions |
|||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
|||
PARTICULAR PURPOSE. You are solely responsible for determining the |
|||
appropriateness of using or redistributing the Work and assume any |
|||
risks associated with Your exercise of permissions under this License. |
|||
|
|||
8. Limitation of Liability. In no event and under no legal theory, |
|||
whether in tort (including negligence), contract, or otherwise, |
|||
unless required by applicable law (such as deliberate and grossly |
|||
negligent acts) or agreed to in writing, shall any Contributor be |
|||
liable to You for damages, including any direct, indirect, special, |
|||
incidental, or consequential damages of any character arising as a |
|||
result of this License or out of the use or inability to use the |
|||
Work (including but not limited to damages for loss of goodwill, |
|||
work stoppage, computer failure or malfunction, or any and all |
|||
other commercial damages or losses), even if such Contributor |
|||
has been advised of the possibility of such damages. |
|||
|
|||
9. Accepting Warranty or Additional Liability. While redistributing |
|||
the Work or Derivative Works thereof, You may choose to offer, |
|||
and charge a fee for, acceptance of support, warranty, indemnity, |
|||
or other liability obligations and/or rights consistent with this |
|||
License. However, in accepting such obligations, You may act only |
|||
on Your own behalf and on Your sole responsibility, not on behalf |
|||
of any other Contributor, and only if You agree to indemnify, |
|||
defend, and hold each Contributor harmless for any liability |
|||
incurred by, or claims asserted against, such Contributor by reason |
|||
of your accepting any such warranty or additional liability. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
APPENDIX: How to apply the Apache License to your work. |
|||
|
|||
To apply the Apache License to your work, attach the following |
|||
boilerplate notice, with the fields enclosed by brackets "[]" |
|||
replaced with your own identifying information. (Don't include |
|||
the brackets!) The text should be enclosed in the appropriate |
|||
comment syntax for the file format. We also recommend that a |
|||
file or class name and description of purpose be included on the |
|||
same "printed page" as the copyright notice for easier |
|||
identification within third-party archives. |
|||
|
|||
Copyright [yyyy] [name of copyright owner] |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
@ -0,0 +1,5 @@ |
|||
Apache RocketMQ |
|||
Copyright 2016-2018 The Apache Software Foundation |
|||
|
|||
This product includes software developed at |
|||
The Apache Software Foundation (http://www.apache.org/). |
|||
@ -0,0 +1,406 @@ |
|||
# RocketMQ-Spring [](https://travis-ci.org/apache/rocketmq-spring) [](https://coveralls.io/github/apache/rocketmq-spring?branch=master) |
|||
|
|||
[](https://search.maven.org/search?q=g:org.apache.rocketmq%20AND%20a:rocketmq-spring-all) |
|||
[](https://github.com/apache/rocketmq-spring/releases) |
|||
[](https://www.apache.org/licenses/LICENSE-2.0.html) |
|||
|
|||
[中文](./README_zh_CN.md) |
|||
|
|||
## Introduction |
|||
|
|||
This project aims to help developers quickly integrate [RocketMQ](http://rocketmq.apache.org/) with [Spring Boot](http://projects.spring.io/spring-boot/). |
|||
|
|||
## How To Contribute |
|||
|
|||
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/) |
|||
|
|||
|
|||
## Prerequisites |
|||
- JDK 1.8 and above |
|||
- [Maven](http://maven.apache.org/) 3.0 and above |
|||
|
|||
## Build and Install with local maven repository |
|||
|
|||
``` |
|||
mvn clean install |
|||
``` |
|||
|
|||
## Features: |
|||
|
|||
- [x] synchronous transmission |
|||
- [x] synchronous ordered transmission |
|||
- [x] synchronous batch transmission |
|||
- [x] asynchronous transmission |
|||
- [x] asynchronous ordered transmission |
|||
- [x] orderly consume |
|||
- [x] concurrently consume(broadcasting/clustering) |
|||
- [x] one-way transmission |
|||
- [x] transaction transmission |
|||
- [x] message trace |
|||
- [x] ACL |
|||
- [ ] pull consume |
|||
|
|||
## Quick Start |
|||
|
|||
Please see the complete sample [rocketmq-spring-boot-samples](rocketmq-spring-boot-samples) |
|||
|
|||
Note: Current RELEASE.VERSION=2.0.3 |
|||
|
|||
```xml |
|||
<!--add dependency in pom.xml--> |
|||
<dependency> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-starter</artifactId> |
|||
<version>${RELEASE.VERSION}</version> |
|||
</dependency> |
|||
``` |
|||
|
|||
### Produce Message |
|||
|
|||
```properties |
|||
## application.properties |
|||
rocketmq.name-server=127.0.0.1:9876 |
|||
rocketmq.producer.group=my-group |
|||
``` |
|||
|
|||
> Note: |
|||
> |
|||
> Maybe you need change `127.0.0.1:9876` with your real NameServer address for RocketMQ |
|||
|
|||
```java |
|||
@SpringBootApplication |
|||
public class ProducerApplication implements CommandLineRunner{ |
|||
@Resource |
|||
private RocketMQTemplate rocketMQTemplate; |
|||
|
|||
public static void main(String[] args){ |
|||
SpringApplication.run(ProducerApplication.class, args); |
|||
} |
|||
|
|||
public void run(String... args) throws Exception { |
|||
rocketMQTemplate.convertAndSend("test-topic-1", "Hello, World!"); |
|||
rocketMQTemplate.send("test-topic-1", MessageBuilder.withPayload("Hello, World! I'm from spring message").build()); |
|||
rocketMQTemplate.convertAndSend("test-topic-2", new OrderPaidEvent("T_001", new BigDecimal("88.00"))); |
|||
|
|||
// rocketMQTemplate.destroy(); // notes: once rocketMQTemplate be destroyed, you can not send any message again with this rocketMQTemplate |
|||
} |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
public class OrderPaidEvent implements Serializable{ |
|||
private String orderId; |
|||
|
|||
private BigDecimal paidMoney; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> More relevant configurations for producing: |
|||
> |
|||
> ```properties |
|||
> rocketmq.producer.send-message-timeout=300000 |
|||
> rocketmq.producer.compress-message-body-threshold=4096 |
|||
> rocketmq.producer.max-message-size=4194304 |
|||
> rocketmq.producer.retry-times-when-send-async-failed=0 |
|||
> rocketmq.producer.retry-next-server=true |
|||
> rocketmq.producer.retry-times-when-send-failed=2 |
|||
> ``` |
|||
|
|||
|
|||
### Send message in transaction and implement local check Listener |
|||
```java |
|||
@SpringBootApplication |
|||
public class ProducerApplication implements CommandLineRunner{ |
|||
@Resource |
|||
private RocketMQTemplate rocketMQTemplate; |
|||
|
|||
public static void main(String[] args){ |
|||
SpringApplication.run(ProducerApplication.class, args); |
|||
} |
|||
|
|||
public void run(String... args) throws Exception { |
|||
try { |
|||
// Build a SpringMessage for sending in transaction |
|||
Message msg = MessageBuilder.withPayload(..)...; |
|||
// In sendMessageInTransaction(), the first parameter transaction name ("test") |
|||
// must be same with the @RocketMQTransactionListener's member field 'transName' |
|||
rocketMQTemplate.sendMessageInTransaction("test", "test-topic", msg, null); |
|||
} catch (MQClientException e) { |
|||
e.printStackTrace(System.out); |
|||
} |
|||
} |
|||
|
|||
// Define transaction listener with the annotation @RocketMQTransactionListener |
|||
@RocketMQTransactionListener(transName="test") |
|||
class TransactionListenerImpl implements RocketMQLocalTransactionListener { |
|||
@Override |
|||
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { |
|||
// ... local transaction process, return bollback, commit or unknown |
|||
return RocketMQLocalTransactionState.UNKNOWN; |
|||
} |
|||
|
|||
@Override |
|||
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { |
|||
// ... check transaction status and return bollback, commit or unknown |
|||
return RocketMQLocalTransactionState.COMMIT; |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### Consume Message |
|||
|
|||
```properties |
|||
## application.properties |
|||
rocketmq.name-server=127.0.0.1:9876 |
|||
``` |
|||
|
|||
> Note: |
|||
> |
|||
> Maybe you need change `127.0.0.1:9876` with your real NameServer address for RocketMQ |
|||
|
|||
```java |
|||
@SpringBootApplication |
|||
public class ConsumerApplication{ |
|||
|
|||
public static void main(String[] args){ |
|||
SpringApplication.run(ConsumerApplication.class, args); |
|||
} |
|||
|
|||
@Slf4j |
|||
@Service |
|||
@RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") |
|||
public class MyConsumer1 implements RocketMQListener<String>{ |
|||
public void onMessage(String message) { |
|||
log.info("received message: {}", message); |
|||
} |
|||
} |
|||
|
|||
@Slf4j |
|||
@Service |
|||
@RocketMQMessageListener(topic = "test-topic-2", consumerGroup = "my-consumer_test-topic-2") |
|||
public class MyConsumer2 implements RocketMQListener<OrderPaidEvent>{ |
|||
public void onMessage(OrderPaidEvent orderPaidEvent) { |
|||
log.info("received orderPaidEvent: {}", orderPaidEvent); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> More relevant configurations for consuming: |
|||
> |
|||
> see: [RocketMQMessageListener](rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/annotation/RocketMQMessageListener.java) |
|||
|
|||
### Message Trace |
|||
|
|||
We need 2 more configurations for support message trace in producer. |
|||
|
|||
```properties |
|||
## application.properties |
|||
rocketmq.name-server=127.0.0.1:9876 |
|||
rocketmq.producer.group=my-group |
|||
|
|||
rocketmq.producer.enable-msg-trace=true |
|||
rocketmq.producer.customized-trace-topic=my-trace-topic |
|||
``` |
|||
|
|||
The message trace in consumer should configure in `@RocketMQMessageListener`. |
|||
|
|||
``` |
|||
@Service |
|||
@RocketMQMessageListener( |
|||
topic = "test-topic-1", |
|||
consumerGroup = "my-consumer_test-topic-1", |
|||
enableMsgTrace = true, |
|||
customizedTraceTopic = "my-trace-topic" |
|||
) |
|||
public class MyConsumer implements RocketMQListener<String> { |
|||
... |
|||
} |
|||
``` |
|||
|
|||
|
|||
> Note: |
|||
> |
|||
> Maybe you need change `127.0.0.1:9876` with your real NameServer address for RocketMQ |
|||
|
|||
> By default, the message track feature of Producer and Consumer is turned on and the trace-topic is RMQ_SYS_TRACE_TOPIC |
|||
> The topic of message trace can be configured with `rocketmq.consumer.customized-trace-topic` configuration item, not required to be configured in each `@RocketMQTransactionListener` |
|||
|
|||
|
|||
### ACL |
|||
|
|||
We need 2 more configurations for support ACL in producer. |
|||
|
|||
```properties |
|||
## application.properties |
|||
rocketmq.name-server=127.0.0.1:9876 |
|||
rocketmq.producer.group=my-group |
|||
|
|||
rocketmq.producer.access-key=AK |
|||
rocketmq.producer.secret-key=SK |
|||
``` |
|||
Transaction Message should configure AK/SK in `@RocketMQTransactionListener`. |
|||
|
|||
``` |
|||
@RocketMQTransactionListener( |
|||
txProducerGroup = "test, |
|||
accessKey = "AK", |
|||
secretKey = "SK" |
|||
) |
|||
class TransactionListenerImpl implements RocketMQLocalTransactionListener { |
|||
... |
|||
} |
|||
``` |
|||
|
|||
> Note: |
|||
> |
|||
> You do not need to configure AK/SK for each `@RocketMQTransactionListener`, you could configure `rocketmq.producer.access-key` and `rocketmq.producer.secret-key` as default value |
|||
|
|||
The ACL feature in consumer should configure AK/SK in `@RocketMQMessageListener`. |
|||
|
|||
``` |
|||
@Service |
|||
@RocketMQMessageListener( |
|||
topic = "test-topic-1", |
|||
consumerGroup = "my-consumer_test-topic-1", |
|||
accessKey = "AK", |
|||
secretKey = "SK" |
|||
) |
|||
public class MyConsumer implements RocketMQListener<String> { |
|||
... |
|||
} |
|||
``` |
|||
|
|||
> Note: |
|||
> |
|||
> You do not need to configure AK/SK for each `@RocketMQMessageListener`, you could configure `rocketmq.consumer.access-key` and `rocketmq.consumer.secret-key` as default value |
|||
|
|||
## FAQ |
|||
|
|||
1. How to connected many `nameserver` on production environment? |
|||
|
|||
`rocketmq.name-server` support the configuration of multiple `nameserver`, separated by `;`. For example: `172.19.0.1: 9876; 172.19.0.2: 9876` |
|||
|
|||
1. When was `rocketMQTemplate` destroyed? |
|||
|
|||
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. |
|||
|
|||
1. start exception:`Caused by: org.apache.rocketmq.client.exception.MQClientException: The consumer group[xxx] has been created before, specify another name please` |
|||
|
|||
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. |
|||
|
|||
1. How is the message content body being serialized and deserialized? |
|||
|
|||
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 [] `. |
|||
|
|||
1. How do I specify the `tags` for topic? |
|||
|
|||
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. |
|||
|
|||
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. |
|||
|
|||
> Note: |
|||
> |
|||
> `tags` looks a complex, but when sending a message , the destination can only specify one topic under a `tag`, can not specify multiple. |
|||
|
|||
1. How do I set the message's `key` when sending a message? |
|||
|
|||
You can send a message by overloading method like `xxxSend(String destination, Message<?> msg, ...)`, setting `headers` of `msg`. for example: |
|||
|
|||
```java |
|||
Message<?> message = MessageBuilder.withPayload(payload).setHeader(MessageConst.PROPERTY_KEYS, msgId).build(); |
|||
rocketMQTemplate.send("topic-test", message); |
|||
``` |
|||
|
|||
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. |
|||
|
|||
> Note: |
|||
> |
|||
> 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. |
|||
|
|||
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? |
|||
|
|||
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. |
|||
|
|||
```java |
|||
@Slf4j |
|||
@Service |
|||
@RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") |
|||
public class MyConsumer2 implements RocketMQListener<MessageExt>{ |
|||
public void onMessage(MessageExt messageExt) { |
|||
log.info("received messageExt: {}", messageExt); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
1. How do I specify where consumers start consuming messages? |
|||
|
|||
The default consume offset please refer: [RocketMQ FAQ](http://rocketmq.apache.org/docs/faq/). |
|||
To customize the consumer's starting location, simply add a `RocketMQPushConsumerLifecycleListener` interface implementation to the consumer class. Examples are as follows: |
|||
|
|||
```java |
|||
@Slf4j |
|||
@Service |
|||
@RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") |
|||
public class MyConsumer1 implements RocketMQListener<String>, RocketMQPushConsumerLifecycleListener { |
|||
@Override |
|||
public void onMessage(String message) { |
|||
log.info("received message: {}", message); |
|||
} |
|||
|
|||
@Override |
|||
public void prepareStart(final DefaultMQPushConsumer consumer) { |
|||
// set consumer consume message from now |
|||
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_TIMESTAMP); |
|||
consumer.setConsumeTimestamp(UtilAll.timeMillisToHumanString3(System.currentTimeMillis())); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Similarly, any other configuration on `DefaultMQPushConsumer` can be done in the same way as above. |
|||
|
|||
|
|||
1. How do I send transactional messages? |
|||
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; |
|||
|
|||
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. |
|||
|
|||
1. How do I create more than one RocketMQTemplate with a different name-server or other specific properties? |
|||
```java |
|||
// Step1. Define an extra RocketMQTemplate with required properties, note, the 'nameServer' property must be different from the value of global |
|||
// Spring configuration 'rocketmq.name-server', other properties are optionally defined, they will use the global configuration |
|||
// definition by default. |
|||
|
|||
// The RocketMQTemplate's Spring Bean name is 'extRocketMQTemplate', same with the simplified class name (Initials lowercase) |
|||
@ExtRocketMQTemplateConfiguration(nameServer="127.0.0.1:9876" |
|||
, ... // override other specific properties if needed |
|||
) |
|||
public class ExtRocketMQTemplate extends RocketMQTemplate { |
|||
// keep the body empty |
|||
} |
|||
|
|||
|
|||
// Step2. Use the extra RocketMQTemplate. e.g. |
|||
@Resource(name = "extRocketMQTemplate") // Must define the name to qualify to extra-defined RocketMQTemplate bean. |
|||
private RocketMQTemplate extRocketMQTemplate; |
|||
// you can use the template as normal. |
|||
|
|||
``` |
|||
|
|||
1. How do I create a consumer Listener with different name-server other than the global Spring configuration 'rocketmq.name-server' ? |
|||
```java |
|||
@Service |
|||
@RocketMQMessageListener( |
|||
nameServer = "NEW-NAMESERVER-LIST", // define new nameServer list |
|||
topic = "test-topic-1", |
|||
consumerGroup = "my-consumer_test-topic-1", |
|||
enableMsgTrace = true, |
|||
customizedTraceTopic = "my-trace-topic" |
|||
) |
|||
public class MyNameServerConsumer implements RocketMQListener<String> { |
|||
... |
|||
} |
|||
``` |
|||
@ -0,0 +1,391 @@ |
|||
# RocketMQ-Spring [](https://travis-ci.org/apache/rocketmq-spring) |
|||
|
|||
[](https://search.maven.org/search?q=g:org.apache.rocketmq%20AND%20a:rocketmq-spring-all) |
|||
[](https://github.com/apache/rocketmq-spring/releases) |
|||
[](https://www.apache.org/licenses/LICENSE-2.0.html) |
|||
|
|||
[English](./README.md) |
|||
|
|||
帮助开发者在[Spring Boot](http://projects.spring.io/spring-boot/)中快速集成[RocketMQ](http://rocketmq.apache.org/)。支持Spring Message规范,方便开发者从其它MQ快速切换到RocketMQ。 |
|||
|
|||
## 如何贡献和帮助社区 |
|||
|
|||
我们永远欢迎开发者的帮助来使这个项目更加完善,无论是小的文档还是大的功能新特性,请参考RocketMQ的主站了解[细节](http://rocketmq.apache.org/docs/how-to-contribute/) |
|||
|
|||
## 前提条件 |
|||
- JDK 1.8 and above |
|||
- [Maven](http://maven.apache.org/) 3.0 and above |
|||
|
|||
功能特性: |
|||
|
|||
- [x] 同步发送 |
|||
- [x] 同步顺序发送 |
|||
- [x] 同步批量发送 |
|||
- [x] 异步发送 |
|||
- [x] 异步顺序发送 |
|||
- [x] 顺序消费 |
|||
- [x] 并发消费(广播/集群) |
|||
- [x] one-way方式发送 |
|||
- [x] 事务方式发送 |
|||
- [x] 消息轨迹 |
|||
- [x] ACL |
|||
- [ ] pull消费 |
|||
|
|||
## Quick Start |
|||
|
|||
下面列出来了一些关键点,完整的示例请参考: [rocketmq-spring-boot-samples](rocketmq-spring-boot-samples) |
|||
|
|||
注意:当前的RELEASE.VERSION=2.0.3 |
|||
|
|||
```xml |
|||
<!--在pom.xml中添加依赖--> |
|||
<dependency> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-starter</artifactId> |
|||
<version>${RELEASE.VERSION}</version> |
|||
</dependency> |
|||
``` |
|||
|
|||
### 发送消息 |
|||
|
|||
```properties |
|||
## application.properties |
|||
rocketmq.name-server=127.0.0.1:9876 |
|||
rocketmq.producer.group=my-group |
|||
``` |
|||
|
|||
> 注意: |
|||
> |
|||
> 请将上述示例配置中的`127.0.0.1:9876`替换成真实RocketMQ的NameServer地址与端口 |
|||
|
|||
```java |
|||
@SpringBootApplication |
|||
public class ProducerApplication implements CommandLineRunner{ |
|||
@Resource |
|||
private RocketMQTemplate rocketMQTemplate; |
|||
|
|||
public static void main(String[] args){ |
|||
SpringApplication.run(ProducerApplication.class, args); |
|||
} |
|||
|
|||
public void run(String... args) throws Exception { |
|||
rocketMQTemplate.convertAndSend("test-topic-1", "Hello, World!"); |
|||
rocketMQTemplate.send("test-topic-1", MessageBuilder.withPayload("Hello, World! I'm from spring message").build()); |
|||
rocketMQTemplate.convertAndSend("test-topic-2", new OrderPaidEvent("T_001", new BigDecimal("88.00"))); |
|||
|
|||
// rocketMQTemplate.destroy(); // notes: once rocketMQTemplate be destroyed, you can not send any message again with this rocketMQTemplate |
|||
} |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
public class OrderPaidEvent implements Serializable{ |
|||
private String orderId; |
|||
|
|||
private BigDecimal paidMoney; |
|||
} |
|||
} |
|||
``` |
|||
|
|||
### 在发送客户端发送事务性消息并且实现回查Listener |
|||
```java |
|||
@SpringBootApplication |
|||
public class ProducerApplication implements CommandLineRunner{ |
|||
@Resource |
|||
private RocketMQTemplate rocketMQTemplate; |
|||
|
|||
public static void main(String[] args){ |
|||
SpringApplication.run(ProducerApplication.class, args); |
|||
} |
|||
|
|||
public void run(String... args) throws Exception { |
|||
try { |
|||
// Build a SpringMessage for sending in transaction |
|||
Message msg = MessageBuilder.withPayload(..)... |
|||
// In sendMessageInTransaction(), the first parameter transaction name ("test") |
|||
// must be same with the @RocketMQTransactionListener's member field 'transName' |
|||
rocketMQTemplate.sendMessageInTransaction("test", "test-topic" msg, null); |
|||
} catch (MQClientException e) { |
|||
e.printStackTrace(System.out); |
|||
} |
|||
} |
|||
|
|||
// Define transaction listener with the annotation @RocketMQTransactionListener |
|||
@RocketMQTransactionListener(transName="test") |
|||
class TransactionListenerImpl implements RocketMQLocalTransactionListener { |
|||
@Override |
|||
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { |
|||
// ... local transaction process, return bollback, commit or unknown |
|||
return RocketMQLocalTransactionState.UNKNOWN; |
|||
} |
|||
|
|||
@Override |
|||
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { |
|||
// ... check transaction status and return bollback, commit or unknown |
|||
return RocketMQLocalTransactionState.COMMIT; |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> 更多发送相关配置 |
|||
> |
|||
> ```properties |
|||
> rocketmq.producer.send-message-timeout=300000 |
|||
> rocketmq.producer.compress-message-body-threshold=4096 |
|||
> rocketmq.producer.max-message-size=4194304 |
|||
> rocketmq.producer.retry-times-when-send-async-failed=0 |
|||
> rocketmq.producer.retry-next-server=true |
|||
> rocketmq.producer.retry-times-when-send-failed=2 |
|||
> ``` |
|||
|
|||
|
|||
### 接收消息 |
|||
|
|||
```properties |
|||
## application.properties |
|||
rocketmq.name-server=127.0.0.1:9876 |
|||
``` |
|||
|
|||
> 注意: |
|||
> |
|||
> 请将上述示例配置中的`127.0.0.1:9876`替换成真实RocketMQ的NameServer地址与端口 |
|||
|
|||
```java |
|||
@SpringBootApplication |
|||
public class ConsumerApplication{ |
|||
|
|||
public static void main(String[] args){ |
|||
SpringApplication.run(ConsumerApplication.class, args); |
|||
} |
|||
|
|||
@Slf4j |
|||
@Service |
|||
@RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") |
|||
public class MyConsumer1 implements RocketMQListener<String>{ |
|||
public void onMessage(String message) { |
|||
log.info("received message: {}", message); |
|||
} |
|||
} |
|||
|
|||
@Slf4j |
|||
@Service |
|||
@RocketMQMessageListener(topic = "test-topic-2", consumerGroup = "my-consumer_test-topic-2") |
|||
public class MyConsumer2 implements RocketMQListener<OrderPaidEvent>{ |
|||
public void onMessage(OrderPaidEvent orderPaidEvent) { |
|||
log.info("received orderPaidEvent: {}", orderPaidEvent); |
|||
} |
|||
} |
|||
} |
|||
``` |
|||
|
|||
|
|||
> 更多消费相关配置 |
|||
> |
|||
> see: [RocketMQMessageListener](rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/annotation/RocketMQMessageListener.java) |
|||
|
|||
### 消息轨迹 |
|||
|
|||
Producer 端要想使用消息轨迹,需要多配置两个配置项: |
|||
|
|||
```properties |
|||
## application.properties |
|||
rocketmq.name-server=127.0.0.1:9876 |
|||
rocketmq.producer.group=my-group |
|||
|
|||
rocketmq.producer.enable-msg-trace=true |
|||
rocketmq.producer.customized-trace-topic=my-trace-topic |
|||
``` |
|||
|
|||
Consumer 端消息轨迹的功能需要在 `@RocketMQMessageListener` 中进行配置对应的属性: |
|||
|
|||
``` |
|||
@Service |
|||
@RocketMQMessageListener( |
|||
topic = "test-topic-1", |
|||
consumerGroup = "my-consumer_test-topic-1", |
|||
enableMsgTrace = true, |
|||
customizedTraceTopic = "my-trace-topic" |
|||
) |
|||
public class MyConsumer implements RocketMQListener<String> { |
|||
... |
|||
} |
|||
``` |
|||
|
|||
> 注意: |
|||
> |
|||
> 默认情况下 Producer 和 Consumer 的消息轨迹功能是开启的且 trace-topic 为 RMQ_SYS_TRACE_TOPIC |
|||
> Consumer 端的消息轨迹 trace-topic 可以在配置文件中配置 `rocketmq.consumer.customized-trace-topic` 配置项,不需要为在每个 `@RocketMQTransactionListener` 配置 |
|||
|
|||
|
|||
### ACL |
|||
|
|||
Producer 端要想使用 ACL 功能,需要多配置两个配置项: |
|||
|
|||
```properties |
|||
## application.properties |
|||
rocketmq.name-server=127.0.0.1:9876 |
|||
rocketmq.producer.group=my-group |
|||
|
|||
rocketmq.producer.access-key=AK |
|||
rocketmq.producer.secret-key=SK |
|||
``` |
|||
|
|||
事务消息的发送需要在 `@RocketMQTransactionListener` 注解里配置上 AK/SK: |
|||
|
|||
``` |
|||
@RocketMQTransactionListener( |
|||
txProducerGroup = "test, |
|||
accessKey = "AK", |
|||
secretKey = "SK" |
|||
) |
|||
class TransactionListenerImpl implements RocketMQLocalTransactionListener { |
|||
... |
|||
} |
|||
``` |
|||
|
|||
> 注意: |
|||
> |
|||
> 可以不用为每个 `@RocketMQTransactionListener` 注解配置 AK/SK,在配置文件中配置 `rocketmq.producer.access-key` 和 `rocketmq.producer.secret-key` 配置项,这两个配置项的值就是默认值 |
|||
|
|||
Consumer 端 ACL 功能需要在 `@RocketMQMessageListener` 中进行配置 |
|||
|
|||
``` |
|||
@Service |
|||
@RocketMQMessageListener( |
|||
topic = "test-topic-1", |
|||
consumerGroup = "my-consumer_test-topic-1", |
|||
accessKey = "AK", |
|||
secretKey = "SK" |
|||
) |
|||
public class MyConsumer implements RocketMQListener<String> { |
|||
... |
|||
} |
|||
``` |
|||
|
|||
> 注意: |
|||
> |
|||
> 可以不用为每个 `@RocketMQMessageListener` 注解配置 AK/SK,在配置文件中配置 `rocketmq.consumer.access-key` 和 `rocketmq.consumer.secret-key` 配置项,这两个配置项的值就是默认值 |
|||
|
|||
## FAQ |
|||
|
|||
1. 生产环境有多个`nameserver`该如何连接? |
|||
|
|||
`rocketmq.name-server`支持配置多个`nameserver`地址,采用`;`分隔即可。例如:`172.19.0.1:9876;172.19.0.2:9876` |
|||
|
|||
1. `rocketMQTemplate`在什么时候被销毁? |
|||
|
|||
开发者在项目中使用`rocketMQTemplate`发送消息时,不需要手动执行`rocketMQTemplate.destroy()`方法, `rocketMQTemplate`会在spring容器销毁时自动销毁。 |
|||
|
|||
1. 启动报错:`Caused by: org.apache.rocketmq.client.exception.MQClientException: The consumer group[xxx] has been created before, specify another name please` |
|||
|
|||
RocketMQ在设计时就不希望一个消费者同时处理多个类型的消息,因此同一个`consumerGroup`下的consumer职责应该是一样的,不要干不同的事情(即消费多个topic)。建议`consumerGroup`与`topic`一一对应。 |
|||
|
|||
1. 发送的消息内容体是如何被序列化与反序列化的? |
|||
|
|||
RocketMQ的消息体都是以`byte[]`方式存储。当业务系统的消息内容体如果是`java.lang.String`类型时,统一按照`utf-8`编码转成`byte[]`;如果业务系统的消息内容为非`java.lang.String`类型,则采用[jackson-databind](https://github.com/FasterXML/jackson-databind)序列化成`JSON`格式的字符串之后,再统一按照`utf-8`编码转成`byte[]`。 |
|||
|
|||
1. 如何指定topic的`tags`? |
|||
|
|||
RocketMQ的最佳实践中推荐:一个应用尽可能用一个Topic,消息子类型用`tags`来标识,`tags`可以由应用自由设置。 |
|||
在使用`rocketMQTemplate`发送消息时,通过设置发送方法的`destination`参数来设置消息的目的地,`destination`的格式为`topicName:tagName`,`:`前面表示topic的名称,后面表示`tags`名称。 |
|||
|
|||
> 注意: |
|||
> |
|||
> `tags`从命名来看像是一个复数,但发送消息时,目的地只能指定一个topic下的一个`tag`,不能指定多个。 |
|||
|
|||
1. 发送消息时如何设置消息的`key`? |
|||
|
|||
可以通过重载的`xxxSend(String destination, Message<?> msg, ...)`方法来发送消息,指定`msg`的`headers`来完成。示例: |
|||
|
|||
```java |
|||
Message<?> message = MessageBuilder.withPayload(payload).setHeader(MessageConst.PROPERTY_KEYS, msgId).build(); |
|||
rocketMQTemplate.send("topic-test", message); |
|||
``` |
|||
|
|||
同理还可以根据上面的方式来设置消息的`FLAG`、`WAIT_STORE_MSG_OK`以及一些用户自定义的其它头信息。 |
|||
|
|||
> 注意: |
|||
> |
|||
> 在将Spring的Message转化为RocketMQ的Message时,为防止`header`信息与RocketMQ的系统属性冲突,在所有`header`的名称前面都统一添加了前缀`USERS_`。因此在消费时如果想获取自定义的消息头信息,请遍历头信息中以`USERS_`开头的key即可。 |
|||
|
|||
1. 消费消息时,除了获取消息`payload`外,还想获取RocketMQ消息的其它系统属性,需要怎么做? |
|||
|
|||
消费者在实现`RocketMQListener`接口时,只需要起泛型为`MessageExt`即可,这样在`onMessage`方法将接收到RocketMQ原生的`MessageExt`消息。 |
|||
|
|||
```java |
|||
@Slf4j |
|||
@Service |
|||
@RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") |
|||
public class MyConsumer2 implements RocketMQListener<MessageExt>{ |
|||
public void onMessage(MessageExt messageExt) { |
|||
log.info("received messageExt: {}", messageExt); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
1. 如何指定消费者从哪开始消费消息,或开始消费的位置? |
|||
|
|||
消费者默认开始消费的位置请参考:[RocketMQ FAQ](http://rocketmq.apache.org/docs/faq/)。 |
|||
若想自定义消费者开始的消费位置,只需在消费者类添加一个`RocketMQPushConsumerLifecycleListener`接口的实现即可。 示例如下: |
|||
|
|||
```java |
|||
@Slf4j |
|||
@Service |
|||
@RocketMQMessageListener(topic = "test-topic-1", consumerGroup = "my-consumer_test-topic-1") |
|||
public class MyConsumer1 implements RocketMQListener<String>, RocketMQPushConsumerLifecycleListener { |
|||
@Override |
|||
public void onMessage(String message) { |
|||
log.info("received message: {}", message); |
|||
} |
|||
|
|||
@Override |
|||
public void prepareStart(final DefaultMQPushConsumer consumer) { |
|||
// set consumer consume message from now |
|||
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_TIMESTAMP); |
|||
consumer.setConsumeTimestamp(UtilAll.timeMillisToHumanString3(System.currentTimeMillis())); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
同理,任何关于`DefaultMQPushConsumer`的更多其它其它配置,都可以采用上述方式来完成。 |
|||
|
|||
1. 如何发送事务消息(即半消息支持分布式事务)? |
|||
在客户端,首先用户需要实现RocketMQLocalTransactionListener接口,并在接口类上注解声明@RocketMQTransactionListener,实现确认和回查方法;然后再使用资源模板RocketMQTemplate, |
|||
调用方法sendMessageInTransaction()来进行消息的发布。 注意:这个方法通过指定发送者组名与具体的声明了txProducerGroup的TransactionListener进行关联,您也可以不指定这个值,从而使用默认的事务发送者组。 |
|||
|
|||
1. 如何声明不同name-server或者其他特定的属性来定义非标的RocketMQTemplate? |
|||
```java |
|||
// 第一步: 定义非标的RocketMQTemplate使用你需要的属性,注意,这里的'nameServer'属性必须要定义,并且其取值不能与全局配置属性'rocketmq.name-server'相同 |
|||
// 也可以定义其他属性,如果不定义,它们取全局的配置属性值或默认值。 |
|||
|
|||
// 这个RocketMQTemplate的Spring Bean名是'extRocketMQTemplate', 与所定义的类名相同(但首字母小写) |
|||
@ExtRocketMQTemplateConfiguration(nameServer="127.0.0.1:9876" |
|||
, ... // 定义其他属性,如果有必要。 |
|||
) |
|||
public class ExtRocketMQTemplate extends RocketMQTemplate { |
|||
//类里面不需要做任何修改 |
|||
} |
|||
|
|||
|
|||
// 第二步: 使用这个非标RocketMQTemplate |
|||
@Resource(name = "extRocketMQTemplate") // 这里必须定义name属性来指向上具体的Spring Bean. |
|||
private RocketMQTemplate extRocketMQTemplate; |
|||
// 接下来就可以正常使用这个extRocketMQTemplate了. |
|||
|
|||
``` |
|||
|
|||
1. MessageListener消费端,是否可以指定不同的name-server而不是使用全局定义的'rocketmq.name-server'属性值 ? |
|||
|
|||
```java |
|||
@Service |
|||
@RocketMQMessageListener( |
|||
nameServer = "NEW-NAMESERVER-LIST", // 可以使用这个optional属性来指定不同的name-server |
|||
topic = "test-topic-1", |
|||
consumerGroup = "my-consumer_test-topic-1", |
|||
enableMsgTrace = true, |
|||
customizedTraceTopic = "my-trace-topic" |
|||
) |
|||
public class MyNameServerConsumer implements RocketMQListener<String> { |
|||
... |
|||
} |
|||
``` |
|||
@ -0,0 +1,222 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
|||
~ contributor license agreements. See the NOTICE file distributed with |
|||
~ this work for additional information regarding copyright ownership. |
|||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
~ (the "License"); you may not use this file except in compliance with |
|||
~ the License. You may obtain a copy of the License at |
|||
~ |
|||
~ http://www.apache.org/licenses/LICENSE-2.0 |
|||
~ |
|||
~ Unless required by applicable law or agreed to in writing, software |
|||
~ distributed under the License is distributed on an "AS IS" BASIS, |
|||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
~ See the License for the specific language governing permissions and |
|||
~ limitations under the License. |
|||
--> |
|||
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<parent> |
|||
<groupId>org.apache</groupId> |
|||
<artifactId>apache</artifactId> |
|||
<version>18</version> |
|||
</parent> |
|||
|
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-all</artifactId> |
|||
<version>2.0.3</version> |
|||
<packaging>pom</packaging> |
|||
|
|||
<name>Apache RocketMQ Spring Boot ${project.version}</name> |
|||
<description>Build for Apache RocketMQ Spring Boot Support</description> |
|||
<url>https://github.com/apache/rocketmq-spring</url> |
|||
|
|||
|
|||
<scm> |
|||
<url>git@github.com:apache/rocketmq-spring.git</url> |
|||
<connection>scm:git:git@github.com:apache/rocketmq-spring.git</connection> |
|||
<developerConnection>scm:git:git@github.com:apache/rocketmq-spring.git</developerConnection> |
|||
<tag>rocketmq-spring-all-2.0.3</tag> |
|||
</scm> |
|||
|
|||
<mailingLists> |
|||
<mailingList> |
|||
<name>Development List</name> |
|||
<subscribe>dev-subscribe@rocketmq.apache.org</subscribe> |
|||
<unsubscribe>dev-unsubscribe@rocketmq.apache.org</unsubscribe> |
|||
<post>dev@rocketmq.apache.org</post> |
|||
</mailingList> |
|||
<mailingList> |
|||
<name>User List</name> |
|||
<subscribe>users-subscribe@rocketmq.apache.org</subscribe> |
|||
<unsubscribe>users-unsubscribe@rocketmq.apache.org</unsubscribe> |
|||
<post>users@rocketmq.apache.org</post> |
|||
</mailingList> |
|||
<mailingList> |
|||
<name>Commits List</name> |
|||
<subscribe>commits-subscribe@rocketmq.apache.org</subscribe> |
|||
<unsubscribe>commits-unsubscribe@rocketmq.apache.org</unsubscribe> |
|||
<post>commits@rocketmq.apache.org</post> |
|||
</mailingList> |
|||
</mailingLists> |
|||
|
|||
|
|||
<organization> |
|||
<name>Apache Software Foundation</name> |
|||
<url>http://www.apache.org</url> |
|||
</organization> |
|||
|
|||
<licenses> |
|||
<license> |
|||
<name>Apache License, Version 2.0</name> |
|||
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> |
|||
<distribution>repo</distribution> |
|||
<comments>A business-friendly OSS license</comments> |
|||
</license> |
|||
</licenses> |
|||
|
|||
<build> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>org.apache.maven.plugins</groupId> |
|||
<artifactId>maven-checkstyle-plugin</artifactId> |
|||
<version>2.17</version> |
|||
<executions> |
|||
<execution> |
|||
<id>validate</id> |
|||
<phase>validate</phase> |
|||
<configuration> |
|||
<excludes>src/main/resources</excludes> |
|||
<configLocation>style/rmq_checkstyle.xml</configLocation> |
|||
<encoding>UTF-8</encoding> |
|||
<consoleOutput>true</consoleOutput> |
|||
<failsOnError>true</failsOnError> |
|||
</configuration> |
|||
<goals> |
|||
<goal>check</goal> |
|||
</goals> |
|||
</execution> |
|||
</executions> |
|||
</plugin> |
|||
<plugin> |
|||
<groupId>org.jacoco</groupId> |
|||
<artifactId>jacoco-maven-plugin</artifactId> |
|||
<version>0.7.8</version> |
|||
<executions> |
|||
<execution> |
|||
<id>default-prepare-agent</id> |
|||
<goals> |
|||
<goal>prepare-agent</goal> |
|||
</goals> |
|||
<configuration> |
|||
<destFile>${project.build.directory}/jacoco.exec</destFile> |
|||
</configuration> |
|||
</execution> |
|||
<execution> |
|||
<id>default-prepare-agent-integration</id> |
|||
<phase>pre-integration-test</phase> |
|||
<goals> |
|||
<goal>prepare-agent-integration</goal> |
|||
</goals> |
|||
<configuration> |
|||
<destFile>${project.build.directory}/jacoco-it.exec</destFile> |
|||
<propertyName>failsafeArgLine</propertyName> |
|||
</configuration> |
|||
</execution> |
|||
<execution> |
|||
<id>default-report</id> |
|||
<goals> |
|||
<goal>report</goal> |
|||
</goals> |
|||
</execution> |
|||
<execution> |
|||
<id>default-report-integration</id> |
|||
<goals> |
|||
<goal>report-integration</goal> |
|||
</goals> |
|||
</execution> |
|||
</executions> |
|||
</plugin> |
|||
<plugin> |
|||
<artifactId>maven-surefire-plugin</artifactId> |
|||
<version>2.19.1</version> |
|||
<configuration> |
|||
<skipAfterFailureCount>1</skipAfterFailureCount> |
|||
<forkCount>1</forkCount> |
|||
<reuseForks>true</reuseForks> |
|||
<excludes> |
|||
<exclude>**/IT*.java</exclude> |
|||
</excludes> |
|||
</configuration> |
|||
</plugin> |
|||
<plugin> |
|||
<groupId>org.codehaus.mojo</groupId> |
|||
<artifactId>findbugs-maven-plugin</artifactId> |
|||
<version>3.0.4</version> |
|||
</plugin> |
|||
<plugin> |
|||
<groupId>org.sonarsource.scanner.maven</groupId> |
|||
<artifactId>sonar-maven-plugin</artifactId> |
|||
<version>3.0.2</version> |
|||
</plugin> |
|||
<plugin> |
|||
<groupId>org.apache.rat</groupId> |
|||
<artifactId>apache-rat-plugin</artifactId> |
|||
<version>0.12</version> |
|||
<configuration> |
|||
<excludes> |
|||
<exclude>.gitignore</exclude> |
|||
<exclude>.travis.yml</exclude> |
|||
<exclude>CONTRIBUTING.md</exclude> |
|||
<exclude>**/README.md</exclude> |
|||
<exclude>**/README_zh_CN.md</exclude> |
|||
<exclude>.github/**</exclude> |
|||
<exclude>src/test/resources/certs/*</exclude> |
|||
<exclude>src/test/**/*.log</exclude> |
|||
<exclude>src/test/resources/META-INF/service/*</exclude> |
|||
<exclude>**/target/**</exclude> |
|||
<exclude>*/*.iml</exclude> |
|||
<exclude>**/*/spring.factories</exclude> |
|||
<exclude>**/application.properties</exclude> |
|||
</excludes> |
|||
</configuration> |
|||
</plugin> |
|||
<plugin> |
|||
<groupId>org.eluder.coveralls</groupId> |
|||
<artifactId>coveralls-maven-plugin</artifactId> |
|||
<version>4.3.0</version> |
|||
</plugin> |
|||
</plugins> |
|||
|
|||
<pluginManagement> |
|||
<plugins> |
|||
<plugin> |
|||
<artifactId>maven-assembly-plugin</artifactId> |
|||
<version>3.0.0</version> |
|||
</plugin> |
|||
</plugins> |
|||
</pluginManagement> |
|||
</build> |
|||
|
|||
<profiles> |
|||
<profile> |
|||
<id>sonar-apache</id> |
|||
<properties> |
|||
<!-- URL of the ASF SonarQube server --> |
|||
<sonar.host.url>https://builds.apache.org/analysis</sonar.host.url> |
|||
</properties> |
|||
</profile> |
|||
</profiles> |
|||
|
|||
<modules> |
|||
<module>rocketmq-spring-boot-parent</module> |
|||
<module>rocketmq-spring-boot</module> |
|||
<module>rocketmq-spring-boot-starter</module> |
|||
<!-- Note: The samples need to mvn compiple in its own directory |
|||
<module>rocketmq-spring-boot-samples</module> |
|||
--> |
|||
</modules> |
|||
</project> |
|||
@ -0,0 +1,167 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
|||
~ contributor license agreements. See the NOTICE file distributed with |
|||
~ this work for additional information regarding copyright ownership. |
|||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
~ (the "License"); you may not use this file except in compliance with |
|||
~ the License. You may obtain a copy of the License at |
|||
~ |
|||
~ http://www.apache.org/licenses/LICENSE-2.0 |
|||
~ |
|||
~ Unless required by applicable law or agreed to in writing, software |
|||
~ distributed under the License is distributed on an "AS IS" BASIS, |
|||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
~ See the License for the specific language governing permissions and |
|||
~ limitations under the License. |
|||
--> |
|||
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<parent> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-all</artifactId> |
|||
<version>2.0.3</version> |
|||
<relativePath>../pom.xml</relativePath> |
|||
</parent> |
|||
|
|||
<artifactId>rocketmq-spring-boot-parent</artifactId> |
|||
<packaging>pom</packaging> |
|||
|
|||
<name>RocketMQ Spring Boot Parent</name> |
|||
<description>Parent package for Apache RocketMQ Spring Boot Support</description> |
|||
<url>https://github.com/apache/rocketmq-spring</url> |
|||
|
|||
<properties> |
|||
<project.rootdir>${project.basedir}/..</project.rootdir> |
|||
<spring.boot.version>2.0.5.RELEASE</spring.boot.version> |
|||
<spring.version>5.1.0.RELEASE</spring.version> |
|||
|
|||
<rocketmq.spring.boot.version>2.0.3</rocketmq.spring.boot.version> |
|||
|
|||
<rocketmq-version>4.5.1</rocketmq-version> |
|||
<slf4j.version>1.7.25</slf4j.version> |
|||
<jackson.version>2.9.7</jackson.version> |
|||
|
|||
<java.version>1.8</java.version> |
|||
<resource.delimiter>@</resource.delimiter> |
|||
<!-- Delimiter that doesn't clash with Spring ${} placeholders --> |
|||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
|||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> |
|||
<maven.compiler.source>${java.version}</maven.compiler.source> |
|||
<maven.compiler.target>${java.version}</maven.compiler.target> |
|||
<additionalparam>-Xdoclint:none</additionalparam> |
|||
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin> |
|||
<!-- Exclude all generated code --> |
|||
<sonar.jacoco.itReportPath>${project.basedir}/../test/target/jacoco-it.exec</sonar.jacoco.itReportPath> |
|||
<sonar.exclusions>file:**/generated-sources/**,**/test/**</sonar.exclusions> |
|||
</properties> |
|||
|
|||
<dependencyManagement> |
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-autoconfigure</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-autoconfigure-processor</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-configuration-processor</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-validation</artifactId> |
|||
<version>${spring.boot.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot</artifactId> |
|||
<version>${rocketmq.spring.boot.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-client</artifactId> |
|||
<version>${rocketmq-version}</version> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.slf4j</groupId> |
|||
<artifactId>slf4j-api</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-acl</artifactId> |
|||
<version>${rocketmq-version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-messaging</artifactId> |
|||
<version>${spring.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-core</artifactId> |
|||
<version>${spring.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-context</artifactId> |
|||
<version>${spring.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-aop</artifactId> |
|||
<version>${spring.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>org.slf4j</groupId> |
|||
<artifactId>slf4j-api</artifactId> |
|||
<version>${slf4j.version}</version> |
|||
</dependency> |
|||
|
|||
<dependency> |
|||
<groupId>com.fasterxml.jackson.core</groupId> |
|||
<artifactId>jackson-databind</artifactId> |
|||
<version>${jackson.version}</version> |
|||
</dependency> |
|||
</dependencies> |
|||
</dependencyManagement> |
|||
|
|||
</project> |
|||
@ -0,0 +1,202 @@ |
|||
|
|||
Apache License |
|||
Version 2.0, January 2004 |
|||
http://www.apache.org/licenses/ |
|||
|
|||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
|||
|
|||
1. Definitions. |
|||
|
|||
"License" shall mean the terms and conditions for use, reproduction, |
|||
and distribution as defined by Sections 1 through 9 of this document. |
|||
|
|||
"Licensor" shall mean the copyright owner or entity authorized by |
|||
the copyright owner that is granting the License. |
|||
|
|||
"Legal Entity" shall mean the union of the acting entity and all |
|||
other entities that control, are controlled by, or are under common |
|||
control with that entity. For the purposes of this definition, |
|||
"control" means (i) the power, direct or indirect, to cause the |
|||
direction or management of such entity, whether by contract or |
|||
otherwise, or (ii) ownership of fifty percent (50%) or more of the |
|||
outstanding shares, or (iii) beneficial ownership of such entity. |
|||
|
|||
"You" (or "Your") shall mean an individual or Legal Entity |
|||
exercising permissions granted by this License. |
|||
|
|||
"Source" form shall mean the preferred form for making modifications, |
|||
including but not limited to software source code, documentation |
|||
source, and configuration files. |
|||
|
|||
"Object" form shall mean any form resulting from mechanical |
|||
transformation or translation of a Source form, including but |
|||
not limited to compiled object code, generated documentation, |
|||
and conversions to other media types. |
|||
|
|||
"Work" shall mean the work of authorship, whether in Source or |
|||
Object form, made available under the License, as indicated by a |
|||
copyright notice that is included in or attached to the work |
|||
(an example is provided in the Appendix below). |
|||
|
|||
"Derivative Works" shall mean any work, whether in Source or Object |
|||
form, that is based on (or derived from) the Work and for which the |
|||
editorial revisions, annotations, elaborations, or other modifications |
|||
represent, as a whole, an original work of authorship. For the purposes |
|||
of this License, Derivative Works shall not include works that remain |
|||
separable from, or merely link (or bind by name) to the interfaces of, |
|||
the Work and Derivative Works thereof. |
|||
|
|||
"Contribution" shall mean any work of authorship, including |
|||
the original version of the Work and any modifications or additions |
|||
to that Work or Derivative Works thereof, that is intentionally |
|||
submitted to Licensor for inclusion in the Work by the copyright owner |
|||
or by an individual or Legal Entity authorized to submit on behalf of |
|||
the copyright owner. For the purposes of this definition, "submitted" |
|||
means any form of electronic, verbal, or written communication sent |
|||
to the Licensor or its representatives, including but not limited to |
|||
communication on electronic mailing lists, source code control systems, |
|||
and issue tracking systems that are managed by, or on behalf of, the |
|||
Licensor for the purpose of discussing and improving the Work, but |
|||
excluding communication that is conspicuously marked or otherwise |
|||
designated in writing by the copyright owner as "Not a Contribution." |
|||
|
|||
"Contributor" shall mean Licensor and any individual or Legal Entity |
|||
on behalf of whom a Contribution has been received by Licensor and |
|||
subsequently incorporated within the Work. |
|||
|
|||
2. Grant of Copyright License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
copyright license to reproduce, prepare Derivative Works of, |
|||
publicly display, publicly perform, sublicense, and distribute the |
|||
Work and such Derivative Works in Source or Object form. |
|||
|
|||
3. Grant of Patent License. Subject to the terms and conditions of |
|||
this License, each Contributor hereby grants to You a perpetual, |
|||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable |
|||
(except as stated in this section) patent license to make, have made, |
|||
use, offer to sell, sell, import, and otherwise transfer the Work, |
|||
where such license applies only to those patent claims licensable |
|||
by such Contributor that are necessarily infringed by their |
|||
Contribution(s) alone or by combination of their Contribution(s) |
|||
with the Work to which such Contribution(s) was submitted. If You |
|||
institute patent litigation against any entity (including a |
|||
cross-claim or counterclaim in a lawsuit) alleging that the Work |
|||
or a Contribution incorporated within the Work constitutes direct |
|||
or contributory patent infringement, then any patent licenses |
|||
granted to You under this License for that Work shall terminate |
|||
as of the date such litigation is filed. |
|||
|
|||
4. Redistribution. You may reproduce and distribute copies of the |
|||
Work or Derivative Works thereof in any medium, with or without |
|||
modifications, and in Source or Object form, provided that You |
|||
meet the following conditions: |
|||
|
|||
(a) You must give any other recipients of the Work or |
|||
Derivative Works a copy of this License; and |
|||
|
|||
(b) You must cause any modified files to carry prominent notices |
|||
stating that You changed the files; and |
|||
|
|||
(c) You must retain, in the Source form of any Derivative Works |
|||
that You distribute, all copyright, patent, trademark, and |
|||
attribution notices from the Source form of the Work, |
|||
excluding those notices that do not pertain to any part of |
|||
the Derivative Works; and |
|||
|
|||
(d) If the Work includes a "NOTICE" text file as part of its |
|||
distribution, then any Derivative Works that You distribute must |
|||
include a readable copy of the attribution notices contained |
|||
within such NOTICE file, excluding those notices that do not |
|||
pertain to any part of the Derivative Works, in at least one |
|||
of the following places: within a NOTICE text file distributed |
|||
as part of the Derivative Works; within the Source form or |
|||
documentation, if provided along with the Derivative Works; or, |
|||
within a display generated by the Derivative Works, if and |
|||
wherever such third-party notices normally appear. The contents |
|||
of the NOTICE file are for informational purposes only and |
|||
do not modify the License. You may add Your own attribution |
|||
notices within Derivative Works that You distribute, alongside |
|||
or as an addendum to the NOTICE text from the Work, provided |
|||
that such additional attribution notices cannot be construed |
|||
as modifying the License. |
|||
|
|||
You may add Your own copyright statement to Your modifications and |
|||
may provide additional or different license terms and conditions |
|||
for use, reproduction, or distribution of Your modifications, or |
|||
for any such Derivative Works as a whole, provided Your use, |
|||
reproduction, and distribution of the Work otherwise complies with |
|||
the conditions stated in this License. |
|||
|
|||
5. Submission of Contributions. Unless You explicitly state otherwise, |
|||
any Contribution intentionally submitted for inclusion in the Work |
|||
by You to the Licensor shall be under the terms and conditions of |
|||
this License, without any additional terms or conditions. |
|||
Notwithstanding the above, nothing herein shall supersede or modify |
|||
the terms of any separate license agreement you may have executed |
|||
with Licensor regarding such Contributions. |
|||
|
|||
6. Trademarks. This License does not grant permission to use the trade |
|||
names, trademarks, service marks, or product names of the Licensor, |
|||
except as required for reasonable and customary use in describing the |
|||
origin of the Work and reproducing the content of the NOTICE file. |
|||
|
|||
7. Disclaimer of Warranty. Unless required by applicable law or |
|||
agreed to in writing, Licensor provides the Work (and each |
|||
Contributor provides its Contributions) on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
|||
implied, including, without limitation, any warranties or conditions |
|||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A |
|||
PARTICULAR PURPOSE. You are solely responsible for determining the |
|||
appropriateness of using or redistributing the Work and assume any |
|||
risks associated with Your exercise of permissions under this License. |
|||
|
|||
8. Limitation of Liability. In no event and under no legal theory, |
|||
whether in tort (including negligence), contract, or otherwise, |
|||
unless required by applicable law (such as deliberate and grossly |
|||
negligent acts) or agreed to in writing, shall any Contributor be |
|||
liable to You for damages, including any direct, indirect, special, |
|||
incidental, or consequential damages of any character arising as a |
|||
result of this License or out of the use or inability to use the |
|||
Work (including but not limited to damages for loss of goodwill, |
|||
work stoppage, computer failure or malfunction, or any and all |
|||
other commercial damages or losses), even if such Contributor |
|||
has been advised of the possibility of such damages. |
|||
|
|||
9. Accepting Warranty or Additional Liability. While redistributing |
|||
the Work or Derivative Works thereof, You may choose to offer, |
|||
and charge a fee for, acceptance of support, warranty, indemnity, |
|||
or other liability obligations and/or rights consistent with this |
|||
License. However, in accepting such obligations, You may act only |
|||
on Your own behalf and on Your sole responsibility, not on behalf |
|||
of any other Contributor, and only if You agree to indemnify, |
|||
defend, and hold each Contributor harmless for any liability |
|||
incurred by, or claims asserted against, such Contributor by reason |
|||
of your accepting any such warranty or additional liability. |
|||
|
|||
END OF TERMS AND CONDITIONS |
|||
|
|||
APPENDIX: How to apply the Apache License to your work. |
|||
|
|||
To apply the Apache License to your work, attach the following |
|||
boilerplate notice, with the fields enclosed by brackets "[]" |
|||
replaced with your own identifying information. (Don't include |
|||
the brackets!) The text should be enclosed in the appropriate |
|||
comment syntax for the file format. We also recommend that a |
|||
file or class name and description of purpose be included on the |
|||
same "printed page" as the copyright notice for easier |
|||
identification within third-party archives. |
|||
|
|||
Copyright [yyyy] [name of copyright owner] |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
@ -0,0 +1,34 @@ |
|||
# rocketmq-spring-boot-samples |
|||
|
|||
[中文](./README_zh_CN.md) |
|||
|
|||
[](https://www.apache.org/licenses/LICENSE-2.0.html) |
|||
|
|||
It's a demo project for how to use [rocketmq-spring-boot](https://github.com/apache/rocketmq-spring) |
|||
|
|||
Run the test case locally |
|||
1. build and install the rocketmq-spring-boot-starter |
|||
|
|||
2. startup rocketmq according to quick-start, verify the namesvr and broker startup correctly, Note: DON'T do "Shutdown Servers" step. |
|||
http://rocketmq.apache.org/docs/quick-start/ |
|||
|
|||
3. create topics for the demo test cases |
|||
``` |
|||
bash bin/mqadmin updateTopic -c DefaultCluster -t string-topic |
|||
bash bin/mqadmin updateTopic -c DefaultCluster -t order-paid-topic |
|||
bash bin/mqadmin updateTopic -c DefaultCluster -t message-ext-topic |
|||
bash bin/mqadmin updateTopic -c DefaultCluster -t spring-transaction-topic |
|||
``` |
|||
4. run tests |
|||
|
|||
``` |
|||
# open a terminal, run produce |
|||
cd rocketmq-produce-demo |
|||
mvn clean package |
|||
java -jar target/rocketmq-produce-demo-0.0.1-SNAPSHOT.jar |
|||
|
|||
# open another terminal, run consume |
|||
cd rocketmq-consume-demo |
|||
mvn clean package |
|||
java -jar target/rocketmq-consume-demo-0.0.1-SNAPSHOT.jar |
|||
``` |
|||
@ -0,0 +1,40 @@ |
|||
# rocketmq-spring-boot-samples |
|||
|
|||
[English](./README.md) |
|||
|
|||
[](https://www.apache.org/licenses/LICENSE-2.0.html) |
|||
|
|||
这里是一个使用rocketmq-spring-boot-starter的例子。 [rocketmq-spring-boot](https://github.com/apache/rocketmq-spring) |
|||
|
|||
|
|||
## 在本地运行这个测试例子 |
|||
|
|||
1. 如上面注意项所述,需要开发者在本地build并安装rocketmq-spring-boot-starter |
|||
|
|||
2. 根据RocketMQ官网的quick-start来启动NameServer和Broker,并验证是否启动正确。注意: 测试期间不要停止Broker或者NameServer |
|||
http://rocketmq.apache.org/docs/quick-start/ |
|||
|
|||
3. 创建测试例子所需要的Topic |
|||
``` |
|||
cd YOUR_ROCKETMQ_HOME |
|||
|
|||
bash bin/mqadmin updateTopic -c DefaultCluster -t string-topic |
|||
bash bin/mqadmin updateTopic -c DefaultCluster -t order-paid-topic |
|||
bash bin/mqadmin updateTopic -c DefaultCluster -t message-ext-topic |
|||
bash bin/mqadmin updateTopic -c DefaultCluster -t spring-transaction-topic |
|||
``` |
|||
|
|||
4. 编译并运行测试例子 |
|||
|
|||
``` |
|||
# 打开一个终端窗口,编译并启动发送端 |
|||
cd rocketmq-produce-demo |
|||
mvn clean package |
|||
java -jar target/rocketmq-produce-demo-0.0.1-SNAPSHOT.jar |
|||
|
|||
# 打开另一个终端窗口,编译并启动消费端 |
|||
cd rocketmq-consume-demo |
|||
mvn clean package |
|||
java -jar target/rocketmq-consume-demo-0.0.1-SNAPSHOT.jar |
|||
``` |
|||
结合测试代码,观察窗口中消息的发送和接收情况 |
|||
@ -0,0 +1,91 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
|||
~ contributor license agreements. See the NOTICE file distributed with |
|||
~ this work for additional information regarding copyright ownership. |
|||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
~ (the "License"); you may not use this file except in compliance with |
|||
~ the License. You may obtain a copy of the License at |
|||
~ |
|||
~ http://www.apache.org/licenses/LICENSE-2.0 |
|||
~ |
|||
~ Unless required by applicable law or agreed to in writing, software |
|||
~ distributed under the License is distributed on an "AS IS" BASIS, |
|||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
~ See the License for the specific language governing permissions and |
|||
~ limitations under the License. |
|||
--> |
|||
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-samples</artifactId> |
|||
<packaging>pom</packaging> |
|||
<version>0.0.1-SNAPSHOT</version> |
|||
|
|||
<name>RocketMQ Spring Boot Samples</name> |
|||
<description>Samples for RocketMQ Spring Boot</description> |
|||
<url>https://github.com/apache/rocketmq-spring</url> |
|||
|
|||
<modules> |
|||
<module>rocketmq-produce-demo</module> |
|||
<module>rocketmq-consume-demo</module> |
|||
<module>rocketmq-produce-acl-demo</module> |
|||
<module>rocketmq-consume-acl-demo</module> |
|||
</modules> |
|||
|
|||
<properties> |
|||
<rocketmq-spring-boot-starter-version>2.0.3</rocketmq-spring-boot-starter-version> |
|||
</properties> |
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-starter</artifactId> |
|||
<version>${rocketmq-spring-boot-starter-version}</version> |
|||
</dependency> |
|||
</dependencies> |
|||
|
|||
<build> |
|||
<plugins> |
|||
<plugin> |
|||
<groupId>org.apache.maven.plugins</groupId> |
|||
<artifactId>maven-checkstyle-plugin</artifactId> |
|||
<version>2.17</version> |
|||
<executions> |
|||
<execution> |
|||
<id>validate</id> |
|||
<phase>validate</phase> |
|||
<configuration> |
|||
<skip>${disable.checks}</skip> |
|||
<excludes>src/main/resources</excludes> |
|||
<configLocation>style/rmq_checkstyle.xml</configLocation> |
|||
<encoding>UTF-8</encoding> |
|||
<consoleOutput>true</consoleOutput> |
|||
<failsOnError>true</failsOnError> |
|||
</configuration> |
|||
<goals> |
|||
<goal>check</goal> |
|||
</goals> |
|||
</execution> |
|||
</executions> |
|||
</plugin> |
|||
<plugin> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-maven-plugin</artifactId> |
|||
<version>2.1.0.RELEASE</version> |
|||
<executions> |
|||
<execution> |
|||
<goals> |
|||
<goal>repackage</goal> |
|||
</goals> |
|||
</execution> |
|||
</executions> |
|||
</plugin> |
|||
</plugins> |
|||
</build> |
|||
|
|||
</project> |
|||
@ -0,0 +1,31 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
|||
~ contributor license agreements. See the NOTICE file distributed with |
|||
~ this work for additional information regarding copyright ownership. |
|||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
~ (the "License"); you may not use this file except in compliance with |
|||
~ the License. You may obtain a copy of the License at |
|||
~ |
|||
~ http://www.apache.org/licenses/LICENSE-2.0 |
|||
~ |
|||
~ Unless required by applicable law or agreed to in writing, software |
|||
~ distributed under the License is distributed on an "AS IS" BASIS, |
|||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
~ See the License for the specific language governing permissions and |
|||
~ limitations under the License. |
|||
--> |
|||
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-samples</artifactId> |
|||
<version>0.0.1-SNAPSHOT</version> |
|||
</parent> |
|||
|
|||
<artifactId>rocketmq-consume-acl-demo</artifactId> |
|||
|
|||
</project> |
|||
@ -0,0 +1,39 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot; |
|||
|
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* RocketMQMessageListener |
|||
*/ |
|||
@Service |
|||
@RocketMQMessageListener( |
|||
topic = "normal_topic_define_in_Aliware_MQ", |
|||
consumerGroup = "group_define_in_Aliware_MQ" |
|||
//accessKey = "AK" // It will read by `rocketmq.consumer.access-key` key
|
|||
//secretKey = "SK" // It will read by `rocketmq.consumer.access-key` key
|
|||
) |
|||
public class ACLStringConsumer implements RocketMQListener<String> { |
|||
@Override |
|||
public void onMessage(String message) { |
|||
System.out.printf("------- ACL StringConsumer received: %s \n", message); |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot; |
|||
|
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* StringTransactionalConsumer |
|||
*/ |
|||
@Service |
|||
@RocketMQMessageListener( |
|||
topic = "${demo.rocketmq.transTopic}", |
|||
consumerGroup = "group_define_in_Aliware_MQ", |
|||
accessKey = "AK", // if accessKey is empty, it will read by `rocketmq.consumer.access-key` key
|
|||
secretKey = "SK" // if accessKey is empty, it will read by `rocketmq.consumer.secret-key` key
|
|||
) |
|||
public class ACLStringTransactionalConsumer implements RocketMQListener<String> { |
|||
@Override |
|||
public void onMessage(String message) { |
|||
System.out.printf("------- ACL StringTransactionalConsumer received: %s \n", message); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot; |
|||
|
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
|
|||
/** |
|||
* ConsumerApplication |
|||
*/ |
|||
@SpringBootApplication |
|||
public class ConsumerACLApplication { |
|||
|
|||
public static void main(String[] args) { |
|||
SpringApplication.run(ConsumerACLApplication.class, args); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,10 @@ |
|||
spring.application.name=rocketmq-consume-acl-demo |
|||
|
|||
rocketmq.name-server=Endpoint_of_Aliware_MQ |
|||
rocketmq.topic=normal_topic_define_in_Aliware_MQ |
|||
|
|||
# properties used in application code |
|||
demo.rocketmq.transTopic=transaction_topic_define_in_Aliware_MQ |
|||
|
|||
rocketmq.consumer.access-key=AK |
|||
rocketmq.consumer.secret-key=SK |
|||
@ -0,0 +1,31 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
|||
~ contributor license agreements. See the NOTICE file distributed with |
|||
~ this work for additional information regarding copyright ownership. |
|||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
~ (the "License"); you may not use this file except in compliance with |
|||
~ the License. You may obtain a copy of the License at |
|||
~ |
|||
~ http://www.apache.org/licenses/LICENSE-2.0 |
|||
~ |
|||
~ Unless required by applicable law or agreed to in writing, software |
|||
~ distributed under the License is distributed on an "AS IS" BASIS, |
|||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
~ See the License for the specific language governing permissions and |
|||
~ limitations under the License. |
|||
--> |
|||
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-samples</artifactId> |
|||
<version>0.0.1-SNAPSHOT</version> |
|||
</parent> |
|||
|
|||
<artifactId>rocketmq-consume-demo</artifactId> |
|||
|
|||
</project> |
|||
@ -0,0 +1,33 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot; |
|||
|
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
|
|||
/** |
|||
* ConsumerApplication |
|||
*/ |
|||
@SpringBootApplication |
|||
public class ConsumerApplication { |
|||
|
|||
public static void main(String[] args) { |
|||
SpringApplication.run(ConsumerApplication.class, args); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,44 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot.consumer; |
|||
|
|||
import org.apache.rocketmq.client.producer.LocalTransactionState; |
|||
import org.apache.rocketmq.client.producer.TransactionListener; |
|||
import org.apache.rocketmq.common.message.Message; |
|||
import org.apache.rocketmq.common.message.MessageExt; |
|||
|
|||
/** |
|||
* Note: This is a nagitive testing. It aims to tell user the fact that |
|||
* the @RocketMQTransactionListener can not be used on consumer side!!! |
|||
* |
|||
* <p>How to try it? just uncomment the annotation declaration, then compile |
|||
* and run the consumer, it will fail to start. |
|||
*/ |
|||
|
|||
//@RocketMQTransactionListener
|
|||
public class Checker implements TransactionListener { |
|||
@Override |
|||
public LocalTransactionState executeLocalTransaction(Message message, Object o) { |
|||
return null; |
|||
} |
|||
|
|||
@Override |
|||
public LocalTransactionState checkLocalTransaction(MessageExt messageExt) { |
|||
return null; |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot.consumer; |
|||
|
|||
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; |
|||
import org.apache.rocketmq.common.UtilAll; |
|||
import org.apache.rocketmq.common.consumer.ConsumeFromWhere; |
|||
import org.apache.rocketmq.common.message.MessageExt; |
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* MessageExtConsumer, consume listener impl class. |
|||
*/ |
|||
@Service |
|||
@RocketMQMessageListener(topic = "message-ext-topic", selectorExpression = "tag1", consumerGroup = "${spring.application.name}-message-ext-consumer") |
|||
public class MessageExtConsumer implements RocketMQListener<MessageExt>, RocketMQPushConsumerLifecycleListener { |
|||
@Override |
|||
public void onMessage(MessageExt message) { |
|||
System.out.printf("------- MessageExtConsumer received message, msgId: %s, body:%s \n", message.getMsgId(), new String(message.getBody())); |
|||
} |
|||
|
|||
@Override |
|||
public void prepareStart(DefaultMQPushConsumer consumer) { |
|||
// set consumer consume message from now
|
|||
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_TIMESTAMP); |
|||
consumer.setConsumeTimestamp(UtilAll.timeMillisToHumanString3(System.currentTimeMillis())); |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot.consumer; |
|||
|
|||
import org.apache.rocketmq.samples.springboot.domain.OrderPaidEvent; |
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* OrderPaidEventConsumer |
|||
*/ |
|||
@Service |
|||
@RocketMQMessageListener(topic = "${demo.rocketmq.orderTopic}", consumerGroup = "order-paid-consumer") |
|||
public class OrderPaidEventConsumer implements RocketMQListener<OrderPaidEvent> { |
|||
|
|||
@Override |
|||
public void onMessage(OrderPaidEvent orderPaidEvent) { |
|||
System.out.printf("------- OrderPaidEventConsumer received: %s \n", orderPaidEvent); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot.consumer; |
|||
|
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* RocketMQMessageListener |
|||
*/ |
|||
@Service |
|||
@RocketMQMessageListener(topic = "${demo.rocketmq.topic}", consumerGroup = "string_consumer") |
|||
public class StringConsumer implements RocketMQListener<String> { |
|||
@Override |
|||
public void onMessage(String message) { |
|||
System.out.printf("------- StringConsumer received: %s \n", message); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot.consumer; |
|||
|
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* RocketMQMessageListener |
|||
*/ |
|||
@Service |
|||
@RocketMQMessageListener(nameServer = "${demo.rocketmq.myNameServer}", topic = "${demo.rocketmq.topic}", consumerGroup = "string_consumer") |
|||
public class StringConsumerNewNS implements RocketMQListener<String> { |
|||
@Override |
|||
public void onMessage(String message) { |
|||
System.out.printf("------- StringConsumerNewNS received: %s \n", message); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot.consumer; |
|||
|
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* StringTransactionalConsumer |
|||
*/ |
|||
@Service |
|||
@RocketMQMessageListener(topic = "${demo.rocketmq.transTopic}", consumerGroup = "string_trans_consumer") |
|||
public class StringTransactionalConsumer implements RocketMQListener<String> { |
|||
@Override |
|||
public void onMessage(String message) { |
|||
System.out.printf("------- StringTransactionalConsumer received: %s \n", message); |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot.domain; |
|||
|
|||
import java.io.Serializable; |
|||
import java.math.BigDecimal; |
|||
|
|||
/** |
|||
* OrderPaidEvent |
|||
*/ |
|||
public class OrderPaidEvent implements Serializable { |
|||
private String orderId; |
|||
|
|||
private BigDecimal paidMoney; |
|||
|
|||
public OrderPaidEvent() { |
|||
} |
|||
|
|||
public OrderPaidEvent(String orderId, BigDecimal paidMoney) { |
|||
this.orderId = orderId; |
|||
this.paidMoney = paidMoney; |
|||
} |
|||
|
|||
public String getOrderId() { |
|||
return orderId; |
|||
} |
|||
|
|||
public void setOrderId(String orderId) { |
|||
this.orderId = orderId; |
|||
} |
|||
|
|||
public BigDecimal getPaidMoney() { |
|||
return paidMoney; |
|||
} |
|||
|
|||
public void setPaidMoney(BigDecimal paidMoney) { |
|||
this.paidMoney = paidMoney; |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
spring.application.name=rocketmq-consume-demo |
|||
|
|||
rocketmq.name-server=localhost:9876 |
|||
|
|||
# properties used in application code |
|||
demo.rocketmq.topic=string-topic |
|||
demo.rocketmq.orderTopic=order-paid-topic |
|||
demo.rocketmq.msgExtTopic=message-ext-topic |
|||
demo.rocketmq.transTopic=spring-transaction-topic |
|||
|
|||
# another nameserver different global |
|||
demo.rocketmq.myNameServer=127.0.0.1:9876 |
|||
@ -0,0 +1,32 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
|||
~ contributor license agreements. See the NOTICE file distributed with |
|||
~ this work for additional information regarding copyright ownership. |
|||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
~ (the "License"); you may not use this file except in compliance with |
|||
~ the License. You may obtain a copy of the License at |
|||
~ |
|||
~ http://www.apache.org/licenses/LICENSE-2.0 |
|||
~ |
|||
~ Unless required by applicable law or agreed to in writing, software |
|||
~ distributed under the License is distributed on an "AS IS" BASIS, |
|||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
~ See the License for the specific language governing permissions and |
|||
~ limitations under the License. |
|||
--> |
|||
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-samples</artifactId> |
|||
<version>0.0.1-SNAPSHOT</version> |
|||
</parent> |
|||
|
|||
<artifactId>rocketmq-produce-acl-demo</artifactId> |
|||
|
|||
|
|||
</project> |
|||
@ -0,0 +1,151 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot; |
|||
|
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
|
|||
import javax.annotation.Resource; |
|||
|
|||
import org.apache.rocketmq.client.producer.SendResult; |
|||
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; |
|||
import org.apache.rocketmq.spring.core.RocketMQTemplate; |
|||
import org.apache.rocketmq.spring.support.RocketMQHeaders; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.CommandLineRunner; |
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
import org.springframework.messaging.Message; |
|||
import org.springframework.messaging.MessagingException; |
|||
import org.springframework.messaging.support.MessageBuilder; |
|||
|
|||
/** |
|||
* Producer, using RocketMQTemplate sends a variety of messages |
|||
*/ |
|||
@SpringBootApplication |
|||
public class ProducerACLApplication implements CommandLineRunner { |
|||
private static final String TX_PGROUP_NAME = "myTxProducerGroup"; |
|||
@Resource |
|||
private RocketMQTemplate rocketMQTemplate; |
|||
@Value("${demo.rocketmq.transTopic}") |
|||
private String springTransTopic; |
|||
@Value("${demo.rocketmq.topic}") |
|||
private String springTopic; |
|||
|
|||
public static void main(String[] args) { |
|||
SpringApplication.run(ProducerACLApplication.class, args); |
|||
} |
|||
|
|||
@Override |
|||
public void run(String... args) throws Exception { |
|||
// Send string
|
|||
SendResult sendResult = rocketMQTemplate.syncSend(springTopic + ":acl", "Hello, ACL Msg!"); |
|||
System.out.printf("syncSend1 to topic %s sendResult=%s %n", springTopic, sendResult); |
|||
|
|||
// Send string with spring Message
|
|||
sendResult = rocketMQTemplate.syncSend(springTopic, MessageBuilder.withPayload("Hello, World! I'm from spring message & ACL Msg").build()); |
|||
System.out.printf("syncSend2 to topic %s sendResult=%s %n", springTopic, sendResult); |
|||
|
|||
//Send transactional messages
|
|||
testTransaction(); |
|||
} |
|||
|
|||
|
|||
private void testTransaction() throws MessagingException { |
|||
String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"}; |
|||
for (int i = 0; i < 10; i++) { |
|||
try { |
|||
|
|||
Message msg = MessageBuilder.withPayload("Hello RocketMQ " + i). |
|||
setHeader(RocketMQHeaders.KEYS, "KEY_" + i).build(); |
|||
SendResult sendResult = rocketMQTemplate.sendMessageInTransaction(TX_PGROUP_NAME, |
|||
springTransTopic + ":" + tags[i % tags.length], msg, null); |
|||
System.out.printf("------ send Transactional msg body = %s , sendResult=%s %n", |
|||
msg.getPayload(), sendResult.getSendStatus()); |
|||
|
|||
Thread.sleep(10); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@RocketMQTransactionListener( |
|||
txProducerGroup = TX_PGROUP_NAME, |
|||
accessKey = "AK", // if not setting, it will read by `rocketmq.producer.access-key` key
|
|||
secretKey = "SK" // if not setting, it will read by `rocketmq.producer.secret-key` key
|
|||
) |
|||
class TransactionListenerImpl implements RocketMQLocalTransactionListener { |
|||
private AtomicInteger transactionIndex = new AtomicInteger(0); |
|||
|
|||
private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<String, Integer>(); |
|||
|
|||
@Override |
|||
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { |
|||
String transId = (String)msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID); |
|||
System.out.printf("#### executeLocalTransaction is executed, msgTransactionId=%s %n", |
|||
transId); |
|||
int value = transactionIndex.getAndIncrement(); |
|||
int status = value % 3; |
|||
localTrans.put(transId, status); |
|||
if (status == 0) { |
|||
// Return local transaction with success(commit), in this case,
|
|||
// this message will not be checked in checkLocalTransaction()
|
|||
System.out.printf(" # COMMIT # Simulating msg %s related local transaction exec succeeded! ### %n", msg.getPayload()); |
|||
return RocketMQLocalTransactionState.COMMIT; |
|||
} |
|||
|
|||
if (status == 1) { |
|||
// Return local transaction with failure(rollback) , in this case,
|
|||
// this message will not be checked in checkLocalTransaction()
|
|||
System.out.printf(" # ROLLBACK # Simulating %s related local transaction exec failed! %n", msg.getPayload()); |
|||
return RocketMQLocalTransactionState.ROLLBACK; |
|||
} |
|||
|
|||
System.out.printf(" # UNKNOW # Simulating %s related local transaction exec UNKNOWN! \n"); |
|||
return RocketMQLocalTransactionState.UNKNOWN; |
|||
} |
|||
|
|||
@Override |
|||
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { |
|||
String transId = (String)msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID); |
|||
RocketMQLocalTransactionState retState = RocketMQLocalTransactionState.COMMIT; |
|||
Integer status = localTrans.get(transId); |
|||
if (null != status) { |
|||
switch (status) { |
|||
case 0: |
|||
retState = RocketMQLocalTransactionState.UNKNOWN; |
|||
break; |
|||
case 1: |
|||
retState = RocketMQLocalTransactionState.COMMIT; |
|||
break; |
|||
case 2: |
|||
retState = RocketMQLocalTransactionState.COMMIT; |
|||
break; |
|||
} |
|||
} |
|||
System.out.printf("------ !!! checkLocalTransaction is executed once," + |
|||
" msgTransactionId=%s, TransactionState=%s status=%s %n", |
|||
transId, retState, status); |
|||
return retState; |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
rocketmq.name-server=Endpoint_of_Aliware_MQ |
|||
rocketmq.producer.group=my-group1 |
|||
rocketmq.producer.access-key=AK |
|||
rocketmq.producer.secret-key=SK |
|||
|
|||
demo.rocketmq.topic=normal_topic_define_in_Aliware_MQ |
|||
demo.rocketmq.transTopic=transaction_topic_define_in_Aliware_MQ |
|||
@ -0,0 +1,32 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
|||
~ contributor license agreements. See the NOTICE file distributed with |
|||
~ this work for additional information regarding copyright ownership. |
|||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
~ (the "License"); you may not use this file except in compliance with |
|||
~ the License. You may obtain a copy of the License at |
|||
~ |
|||
~ http://www.apache.org/licenses/LICENSE-2.0 |
|||
~ |
|||
~ Unless required by applicable law or agreed to in writing, software |
|||
~ distributed under the License is distributed on an "AS IS" BASIS, |
|||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
~ See the License for the specific language governing permissions and |
|||
~ limitations under the License. |
|||
--> |
|||
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
|||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
<parent> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-samples</artifactId> |
|||
<version>0.0.1-SNAPSHOT</version> |
|||
</parent> |
|||
|
|||
<artifactId>rocketmq-produce-demo</artifactId> |
|||
|
|||
|
|||
</project> |
|||
@ -0,0 +1,24 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.apache.rocketmq.samples.springboot; |
|||
|
|||
import org.apache.rocketmq.spring.annotation.ExtRocketMQTemplateConfiguration; |
|||
import org.apache.rocketmq.spring.core.RocketMQTemplate; |
|||
|
|||
@ExtRocketMQTemplateConfiguration(nameServer = "${demo.rocketmq.extNameServer}") |
|||
public class ExtRocketMQTemplate extends RocketMQTemplate { |
|||
} |
|||
@ -0,0 +1,195 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.samples.springboot; |
|||
|
|||
import org.apache.rocketmq.client.producer.SendCallback; |
|||
import org.apache.rocketmq.client.producer.SendResult; |
|||
import org.apache.rocketmq.samples.springboot.domain.OrderPaidEvent; |
|||
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; |
|||
import org.apache.rocketmq.spring.core.RocketMQTemplate; |
|||
import org.apache.rocketmq.spring.support.RocketMQHeaders; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.boot.CommandLineRunner; |
|||
import org.springframework.boot.SpringApplication; |
|||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
|||
import org.springframework.messaging.Message; |
|||
import org.springframework.messaging.MessagingException; |
|||
import org.springframework.messaging.support.MessageBuilder; |
|||
|
|||
import javax.annotation.Resource; |
|||
import java.math.BigDecimal; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
|
|||
/** |
|||
* Producer, using RocketMQTemplate sends a variety of messages |
|||
*/ |
|||
@SpringBootApplication |
|||
public class ProducerApplication implements CommandLineRunner { |
|||
private static final String TX_PGROUP_NAME = "myTxProducerGroup"; |
|||
@Resource |
|||
private RocketMQTemplate rocketMQTemplate; |
|||
@Value("${demo.rocketmq.transTopic}") |
|||
private String springTransTopic; |
|||
@Value("${demo.rocketmq.topic}") |
|||
private String springTopic; |
|||
@Value("${demo.rocketmq.orderTopic}") |
|||
private String orderPaidTopic; |
|||
@Value("${demo.rocketmq.msgExtTopic}") |
|||
private String msgExtTopic; |
|||
@Resource(name = "extRocketMQTemplate") |
|||
private RocketMQTemplate extRocketMQTemplate; |
|||
|
|||
public static void main(String[] args) { |
|||
SpringApplication.run(ProducerApplication.class, args); |
|||
} |
|||
|
|||
@Override |
|||
public void run(String... args) throws Exception { |
|||
// Send string
|
|||
SendResult sendResult = rocketMQTemplate.syncSend(springTopic, "Hello, World!"); |
|||
System.out.printf("syncSend1 to topic %s sendResult=%s %n", springTopic, sendResult); |
|||
|
|||
// Use the extRocketMQTemplate
|
|||
sendResult = extRocketMQTemplate.syncSend(springTopic, "Hello, World!"); |
|||
System.out.printf("extRocketMQTemplate.syncSend1 to topic %s sendResult=%s %n", springTopic, sendResult); |
|||
|
|||
// Send string with spring Message
|
|||
sendResult = rocketMQTemplate.syncSend(springTopic, MessageBuilder.withPayload("Hello, World! I'm from spring message").build()); |
|||
System.out.printf("syncSend2 to topic %s sendResult=%s %n", springTopic, sendResult); |
|||
|
|||
// Send user-defined object
|
|||
rocketMQTemplate.asyncSend(orderPaidTopic, new OrderPaidEvent("T_001", new BigDecimal("88.00")), new SendCallback() { |
|||
public void onSuccess(SendResult var1) { |
|||
System.out.printf("async onSucess SendResult=%s %n", var1); |
|||
} |
|||
|
|||
public void onException(Throwable var1) { |
|||
System.out.printf("async onException Throwable=%s %n", var1); |
|||
} |
|||
|
|||
}); |
|||
|
|||
// Send message with special tag
|
|||
rocketMQTemplate.convertAndSend(msgExtTopic + ":tag0", "I'm from tag0"); // tag0 will not be consumer-selected
|
|||
System.out.printf("syncSend topic %s tag %s %n", msgExtTopic, "tag0"); |
|||
rocketMQTemplate.convertAndSend(msgExtTopic + ":tag1", "I'm from tag1"); |
|||
System.out.printf("syncSend topic %s tag %s %n", msgExtTopic, "tag1"); |
|||
|
|||
|
|||
// Send a batch of strings
|
|||
testBatchMessages(); |
|||
|
|||
// Send transactional messages
|
|||
testTransaction(); |
|||
} |
|||
|
|||
private void testBatchMessages() { |
|||
List<Message> msgs = new ArrayList<Message>(); |
|||
for (int i = 0; i < 10; i++) { |
|||
msgs.add(MessageBuilder.withPayload("Hello RocketMQ Batch Msg#" + i). |
|||
setHeader(RocketMQHeaders.KEYS, "KEY_" + i).build()); |
|||
} |
|||
|
|||
SendResult sr = rocketMQTemplate.syncSend(springTopic, msgs, 60000); |
|||
|
|||
System.out.printf("--- Batch messages send result :" + sr); |
|||
} |
|||
|
|||
|
|||
private void testTransaction() throws MessagingException { |
|||
String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"}; |
|||
for (int i = 0; i < 10; i++) { |
|||
try { |
|||
|
|||
Message msg = MessageBuilder.withPayload("Hello RocketMQ " + i). |
|||
setHeader(RocketMQHeaders.KEYS, "KEY_" + i).build(); |
|||
SendResult sendResult = rocketMQTemplate.sendMessageInTransaction(TX_PGROUP_NAME, |
|||
springTransTopic + ":" + tags[i % tags.length], msg, null); |
|||
System.out.printf("------ send Transactional msg body = %s , sendResult=%s %n", |
|||
msg.getPayload(), sendResult.getSendStatus()); |
|||
|
|||
Thread.sleep(10); |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
@RocketMQTransactionListener(txProducerGroup = TX_PGROUP_NAME) |
|||
class TransactionListenerImpl implements RocketMQLocalTransactionListener { |
|||
private AtomicInteger transactionIndex = new AtomicInteger(0); |
|||
|
|||
private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<String, Integer>(); |
|||
|
|||
@Override |
|||
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) { |
|||
String transId = (String)msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID); |
|||
System.out.printf("#### executeLocalTransaction is executed, msgTransactionId=%s %n", |
|||
transId); |
|||
int value = transactionIndex.getAndIncrement(); |
|||
int status = value % 3; |
|||
localTrans.put(transId, status); |
|||
if (status == 0) { |
|||
// Return local transaction with success(commit), in this case,
|
|||
// this message will not be checked in checkLocalTransaction()
|
|||
System.out.printf(" # COMMIT # Simulating msg %s related local transaction exec succeeded! ### %n", msg.getPayload()); |
|||
return RocketMQLocalTransactionState.COMMIT; |
|||
} |
|||
|
|||
if (status == 1) { |
|||
// Return local transaction with failure(rollback) , in this case,
|
|||
// this message will not be checked in checkLocalTransaction()
|
|||
System.out.printf(" # ROLLBACK # Simulating %s related local transaction exec failed! %n", msg.getPayload()); |
|||
return RocketMQLocalTransactionState.ROLLBACK; |
|||
} |
|||
|
|||
System.out.printf(" # UNKNOW # Simulating %s related local transaction exec UNKNOWN! \n"); |
|||
return RocketMQLocalTransactionState.UNKNOWN; |
|||
} |
|||
|
|||
@Override |
|||
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { |
|||
String transId = (String)msg.getHeaders().get(RocketMQHeaders.TRANSACTION_ID); |
|||
RocketMQLocalTransactionState retState = RocketMQLocalTransactionState.COMMIT; |
|||
Integer status = localTrans.get(transId); |
|||
if (null != status) { |
|||
switch (status) { |
|||
case 0: |
|||
retState = RocketMQLocalTransactionState.UNKNOWN; |
|||
break; |
|||
case 1: |
|||
retState = RocketMQLocalTransactionState.COMMIT; |
|||
break; |
|||
case 2: |
|||
retState = RocketMQLocalTransactionState.ROLLBACK; |
|||
break; |
|||
} |
|||
} |
|||
System.out.printf("------ !!! checkLocalTransaction is executed once," + |
|||
" msgTransactionId=%s, TransactionState=%s status=%s %n", |
|||
transId, retState, status); |
|||
return retState; |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.apache.rocketmq.samples.springboot.domain; |
|||
|
|||
import java.io.Serializable; |
|||
import java.math.BigDecimal; |
|||
|
|||
public class OrderPaidEvent implements Serializable { |
|||
private String orderId; |
|||
|
|||
private BigDecimal paidMoney; |
|||
|
|||
public OrderPaidEvent() { |
|||
} |
|||
|
|||
public OrderPaidEvent(String orderId, BigDecimal paidMoney) { |
|||
this.orderId = orderId; |
|||
this.paidMoney = paidMoney; |
|||
} |
|||
|
|||
public String getOrderId() { |
|||
return orderId; |
|||
} |
|||
|
|||
public void setOrderId(String orderId) { |
|||
this.orderId = orderId; |
|||
} |
|||
|
|||
public BigDecimal getPaidMoney() { |
|||
return paidMoney; |
|||
} |
|||
|
|||
public void setPaidMoney(BigDecimal paidMoney) { |
|||
this.paidMoney = paidMoney; |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
rocketmq.name-server=localhost:9876 |
|||
rocketmq.producer.group=my-group1 |
|||
rocketmq.producer.sendMessageTimeout=300000 |
|||
|
|||
# properties used in the application |
|||
demo.rocketmq.topic=string-topic |
|||
demo.rocketmq.orderTopic=order-paid-topic |
|||
demo.rocketmq.msgExtTopic=message-ext-topic |
|||
demo.rocketmq.transTopic=spring-transaction-topic |
|||
|
|||
demo.rocketmq.extNameServer=127.0.0.1:9876 |
|||
@ -0,0 +1,23 @@ |
|||
<!-- |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
--> |
|||
|
|||
<component name="CopyrightManager"> |
|||
<copyright> |
|||
<option name="myName" value="Apache" /> |
|||
<option name="notice" value="Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License." /> |
|||
</copyright> |
|||
</component> |
|||
@ -0,0 +1,64 @@ |
|||
<!-- |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
--> |
|||
|
|||
<component name="CopyrightManager"> |
|||
<settings default="Apache"> |
|||
<module2copyright> |
|||
<element module="All" copyright="Apache"/> |
|||
</module2copyright> |
|||
<LanguageOptions name="GSP"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="HTML"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="JAVA"> |
|||
<option name="fileTypeOverride" value="3" /> |
|||
<option name="addBlankAfter" value="false" /> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="JSP"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="JSPX"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="MXML"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="Properties"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="block" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="SPI"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="block" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="XML"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="__TEMPLATE__"> |
|||
<option name="separateBefore" value="true"/> |
|||
<option name="lenBefore" value="1"/> |
|||
</LanguageOptions> |
|||
</settings> |
|||
</component> |
|||
@ -0,0 +1,135 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
--> |
|||
|
|||
<!DOCTYPE module PUBLIC |
|||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN" |
|||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd"> |
|||
<!--Refer http://checkstyle.sourceforge.net/reports/google-java-style.html#s2.2-file-encoding --> |
|||
<module name="Checker"> |
|||
|
|||
<property name="localeLanguage" value="en"/> |
|||
|
|||
<!--To configure the check to report on the first instance in each file--> |
|||
<module name="FileTabCharacter"/> |
|||
|
|||
<!-- header --> |
|||
<module name="RegexpHeader"> |
|||
<property name="header" value="/\*\nLicensed to the Apache Software Foundation*"/> |
|||
<property name="fileExtensions" value="java" /> |
|||
</module> |
|||
|
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="System\.out\.println"/> |
|||
<property name="message" value="Prohibit invoking System.out.println in source code !"/> |
|||
</module> |
|||
|
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="//FIXME"/> |
|||
<property name="message" value="Recommended fix FIXME task !"/> |
|||
</module> |
|||
|
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="//TODO"/> |
|||
<property name="message" value="Recommended fix TODO task !"/> |
|||
</module> |
|||
|
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="@alibaba"/> |
|||
<property name="message" value="Recommended remove @alibaba keyword!"/> |
|||
</module> |
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="@taobao"/> |
|||
<property name="message" value="Recommended remove @taobao keyword!"/> |
|||
</module> |
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="@author"/> |
|||
<property name="message" value="Recommended remove @author tag in javadoc!"/> |
|||
</module> |
|||
|
|||
<module name="RegexpSingleline"> |
|||
<property name="format" |
|||
value=".*[\u3400-\u4DB5\u4E00-\u9FA5\u9FA6-\u9FBB\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFF00-\uFFEF\u2E80-\u2EFF\u3000-\u303F\u31C0-\u31EF]+.*"/> |
|||
<property name="message" value="Not allow chinese character !"/> |
|||
</module> |
|||
|
|||
<module name="FileLength"> |
|||
<property name="max" value="3000"/> |
|||
</module> |
|||
|
|||
<module name="TreeWalker"> |
|||
|
|||
<module name="UnusedImports"> |
|||
<property name="processJavadoc" value="true"/> |
|||
</module> |
|||
<module name="RedundantImport"/> |
|||
|
|||
<!--<module name="IllegalImport" />--> |
|||
|
|||
<!--Checks that classes that override equals() also override hashCode()--> |
|||
<module name="EqualsHashCode"/> |
|||
<!--Checks for over-complicated boolean expressions. Currently finds code like if (topic == true), topic || true, !false, etc.--> |
|||
<module name="SimplifyBooleanExpression"/> |
|||
<module name="OneStatementPerLine"/> |
|||
<module name="UnnecessaryParentheses"/> |
|||
<!--Checks for over-complicated boolean return statements. For example the following code--> |
|||
<module name="SimplifyBooleanReturn"/> |
|||
|
|||
<!--Check that the default is after all the cases in producerGroup switch statement--> |
|||
<module name="DefaultComesLast"/> |
|||
<!--Detects empty statements (standalone ";" semicolon)--> |
|||
<module name="EmptyStatement"/> |
|||
<!--Checks that long constants are defined with an upper ell--> |
|||
<module name="UpperEll"/> |
|||
<module name="ConstantName"> |
|||
<property name="format" value="(^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$)|(^log$)"/> |
|||
</module> |
|||
<!--Checks that local, non-final variable names conform to producerGroup format specified by the format property--> |
|||
<module name="LocalVariableName"/> |
|||
<!--Validates identifiers for local, final variables, including catch parameters--> |
|||
<module name="LocalFinalVariableName"/> |
|||
<!--Validates identifiers for non-static fields--> |
|||
<module name="MemberName"/> |
|||
<!--Validates identifiers for class type parameters--> |
|||
<module name="ClassTypeParameterName"> |
|||
<property name="format" value="^[A-Z0-9]*$"/> |
|||
</module> |
|||
<!--Validates identifiers for method type parameters--> |
|||
<module name="MethodTypeParameterName"> |
|||
<property name="format" value="^[A-Z0-9]*$"/> |
|||
</module> |
|||
<module name="PackageName"/> |
|||
<module name="ParameterName"/> |
|||
<module name="StaticVariableName"/> |
|||
<module name="TypeName"/> |
|||
<!--Checks that there are no import statements that use the * notation--> |
|||
<module name="AvoidStarImport"/> |
|||
|
|||
<!--whitespace--> |
|||
<module name="GenericWhitespace"/> |
|||
<module name="NoWhitespaceBefore"/> |
|||
<module name="NoWhitespaceAfter"/> |
|||
<module name="WhitespaceAround"> |
|||
<property name="allowEmptyConstructors" value="true"/> |
|||
<property name="allowEmptyMethods" value="true"/> |
|||
</module> |
|||
<module name="Indentation"/> |
|||
<module name="MethodParamPad"/> |
|||
<module name="ParenPad"/> |
|||
<module name="TypecastParenPad"/> |
|||
</module> |
|||
</module> |
|||
@ -0,0 +1,157 @@ |
|||
<!-- |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
--> |
|||
|
|||
<code_scheme name="rocketmq"> |
|||
<option name="USE_SAME_INDENTS" value="true"/> |
|||
<option name="IGNORE_SAME_INDENTS_FOR_LANGUAGES" value="true"/> |
|||
<option name="OTHER_INDENT_OPTIONS"> |
|||
<value> |
|||
<option name="INDENT_SIZE" value="4"/> |
|||
<option name="CONTINUATION_INDENT_SIZE" value="4"/> |
|||
<option name="TAB_SIZE" value="4"/> |
|||
<option name="USE_TAB_CHARACTER" value="false"/> |
|||
<option name="SMART_TABS" value="false"/> |
|||
<option name="LABEL_INDENT_SIZE" value="0"/> |
|||
<option name="LABEL_INDENT_ABSOLUTE" value="false"/> |
|||
<option name="USE_RELATIVE_INDENTS" value="false"/> |
|||
</value> |
|||
</option> |
|||
<option name="PREFER_LONGER_NAMES" value="false"/> |
|||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="1000"/> |
|||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="1000"/> |
|||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND"> |
|||
<value/> |
|||
</option> |
|||
<option name="IMPORT_LAYOUT_TABLE"> |
|||
<value> |
|||
<package name="" withSubpackages="true" static="false"/> |
|||
<emptyLine/> |
|||
<package name="" withSubpackages="true" static="true"/> |
|||
</value> |
|||
</option> |
|||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false"/> |
|||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false"/> |
|||
<option name="JD_P_AT_EMPTY_LINES" value="false"/> |
|||
<option name="JD_KEEP_INVALID_TAGS" value="false"/> |
|||
<option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true"/> |
|||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/> |
|||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/> |
|||
<option name="ELSE_ON_NEW_LINE" value="true"/> |
|||
<option name="WHILE_ON_NEW_LINE" value="true"/> |
|||
<option name="CATCH_ON_NEW_LINE" value="true"/> |
|||
<option name="FINALLY_ON_NEW_LINE" value="true"/> |
|||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/> |
|||
<option name="ALIGN_MULTILINE_FOR" value="false"/> |
|||
<option name="SPACE_AFTER_TYPE_CAST" value="false"/> |
|||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true"/> |
|||
<option name="METHOD_PARAMETERS_WRAP" value="1"/> |
|||
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true"/> |
|||
<option name="LABELED_STATEMENT_WRAP" value="1"/> |
|||
<option name="WRAP_COMMENTS" value="true"/> |
|||
<option name="METHOD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="CLASS_ANNOTATION_WRAP" value="1"/> |
|||
<option name="FIELD_ANNOTATION_WRAP" value="1"/> |
|||
<JavaCodeStyleSettings> |
|||
<option name="CLASS_NAMES_IN_JAVADOC" value="3"/> |
|||
</JavaCodeStyleSettings> |
|||
<XML> |
|||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true"/> |
|||
</XML> |
|||
<ADDITIONAL_INDENT_OPTIONS fileType="haml"> |
|||
<option name="INDENT_SIZE" value="2"/> |
|||
</ADDITIONAL_INDENT_OPTIONS> |
|||
<codeStyleSettings language="Groovy"> |
|||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/> |
|||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/> |
|||
<option name="ELSE_ON_NEW_LINE" value="true"/> |
|||
<option name="CATCH_ON_NEW_LINE" value="true"/> |
|||
<option name="FINALLY_ON_NEW_LINE" value="true"/> |
|||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/> |
|||
<option name="ALIGN_MULTILINE_FOR" value="false"/> |
|||
<option name="SPACE_AFTER_TYPE_CAST" value="false"/> |
|||
<option name="METHOD_PARAMETERS_WRAP" value="1"/> |
|||
<option name="METHOD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="CLASS_ANNOTATION_WRAP" value="1"/> |
|||
<option name="FIELD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/> |
|||
<indentOptions> |
|||
<option name="CONTINUATION_INDENT_SIZE" value="4"/> |
|||
</indentOptions> |
|||
</codeStyleSettings> |
|||
<codeStyleSettings language="HOCON"> |
|||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/> |
|||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/> |
|||
</codeStyleSettings> |
|||
<codeStyleSettings language="JAVA"> |
|||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/> |
|||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/> |
|||
<option name="ELSE_ON_NEW_LINE" value="true"/> |
|||
<option name="WHILE_ON_NEW_LINE" value="true"/> |
|||
<option name="CATCH_ON_NEW_LINE" value="true"/> |
|||
<option name="FINALLY_ON_NEW_LINE" value="true"/> |
|||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/> |
|||
<option name="ALIGN_MULTILINE_FOR" value="false"/> |
|||
<option name="SPACE_AFTER_TYPE_CAST" value="false"/> |
|||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true"/> |
|||
<option name="METHOD_PARAMETERS_WRAP" value="1"/> |
|||
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true"/> |
|||
<option name="LABELED_STATEMENT_WRAP" value="1"/> |
|||
<option name="METHOD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="CLASS_ANNOTATION_WRAP" value="1"/> |
|||
<option name="FIELD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/> |
|||
<indentOptions> |
|||
<option name="CONTINUATION_INDENT_SIZE" value="4"/> |
|||
</indentOptions> |
|||
</codeStyleSettings> |
|||
<codeStyleSettings language="JSON"> |
|||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/> |
|||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/> |
|||
</codeStyleSettings> |
|||
<codeStyleSettings language="Scala"> |
|||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/> |
|||
<option name="ELSE_ON_NEW_LINE" value="true"/> |
|||
<option name="WHILE_ON_NEW_LINE" value="true"/> |
|||
<option name="CATCH_ON_NEW_LINE" value="true"/> |
|||
<option name="FINALLY_ON_NEW_LINE" value="true"/> |
|||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/> |
|||
<option name="ALIGN_MULTILINE_FOR" value="false"/> |
|||
<option name="METHOD_PARAMETERS_WRAP" value="1"/> |
|||
<option name="METHOD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="CLASS_ANNOTATION_WRAP" value="1"/> |
|||
<option name="FIELD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/> |
|||
<indentOptions> |
|||
<option name="INDENT_SIZE" value="4"/> |
|||
<option name="CONTINUATION_INDENT_SIZE" value="4"/> |
|||
<option name="TAB_SIZE" value="4"/> |
|||
</indentOptions> |
|||
</codeStyleSettings> |
|||
<codeStyleSettings language="XML"> |
|||
<indentOptions> |
|||
<option name="CONTINUATION_INDENT_SIZE" value="4"/> |
|||
</indentOptions> |
|||
</codeStyleSettings> |
|||
</code_scheme> |
|||
@ -0,0 +1,7 @@ |
|||
# RocketMQ Spring Boot Support |
|||
This project provides auto-configuration for the following RocketMQ client: |
|||
|
|||
- [Produce and Consume Spring Message](../rocketmq-spring-boot-starter) |
|||
- [Send (halp) Message in Transaction](../rocketmq-spring-boot-starter) |
|||
|
|||
For details, please see sample code in the [rocketmq-spring-boot-samples](../rocketmq-spring-boot-samples) |
|||
@ -0,0 +1,102 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
~ Licensed to the Apache Software Foundation (ASF) under one or more |
|||
~ contributor license agreements. See the NOTICE file distributed with |
|||
~ this work for additional information regarding copyright ownership. |
|||
~ The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
~ (the "License"); you may not use this file except in compliance with |
|||
~ the License. You may obtain a copy of the License at |
|||
~ |
|||
~ http://www.apache.org/licenses/LICENSE-2.0 |
|||
~ |
|||
~ Unless required by applicable law or agreed to in writing, software |
|||
~ distributed under the License is distributed on an "AS IS" BASIS, |
|||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
~ See the License for the specific language governing permissions and |
|||
~ limitations under the License. |
|||
--> |
|||
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
|||
<modelVersion>4.0.0</modelVersion> |
|||
|
|||
<parent> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-spring-boot-parent</artifactId> |
|||
<version>2.0.3</version> |
|||
<relativePath>../rocketmq-spring-boot-parent/pom.xml</relativePath> |
|||
</parent> |
|||
|
|||
<artifactId>rocketmq-spring-boot</artifactId> |
|||
<packaging>jar</packaging> |
|||
|
|||
<name>RocketMQ Spring Boot AutoConfigure</name> |
|||
<description>SRocketMQ Spring Boot AutoConfigure</description> |
|||
<url>https://github.com/apache/rocketmq-spring</url> |
|||
|
|||
|
|||
<dependencies> |
|||
<dependency> |
|||
<groupId>org.slf4j</groupId> |
|||
<artifactId>slf4j-api</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot</artifactId> |
|||
<optional>true</optional> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-autoconfigure</artifactId> |
|||
<optional>true</optional> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-autoconfigure-processor</artifactId> |
|||
<optional>true</optional> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-configuration-processor</artifactId> |
|||
<optional>true</optional> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework.boot</groupId> |
|||
<artifactId>spring-boot-starter-test</artifactId> |
|||
<scope>test</scope> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-client</artifactId> |
|||
<exclusions> |
|||
<exclusion> |
|||
<groupId>org.slf4j</groupId> |
|||
<artifactId>slf4j-api</artifactId> |
|||
</exclusion> |
|||
</exclusions> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.apache.rocketmq</groupId> |
|||
<artifactId>rocketmq-acl</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-messaging</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-core</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-context</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>org.springframework</groupId> |
|||
<artifactId>spring-aop</artifactId> |
|||
</dependency> |
|||
<dependency> |
|||
<groupId>com.fasterxml.jackson.core</groupId> |
|||
<artifactId>jackson-databind</artifactId> |
|||
</dependency> |
|||
</dependencies> |
|||
</project> |
|||
@ -0,0 +1,30 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.annotation; |
|||
|
|||
public enum ConsumeMode { |
|||
/** |
|||
* Receive asynchronously delivered messages concurrently |
|||
*/ |
|||
CONCURRENTLY, |
|||
|
|||
/** |
|||
* Receive asynchronously delivered messages orderly. one queue, one thread |
|||
*/ |
|||
ORDERLY |
|||
} |
|||
@ -0,0 +1,89 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.annotation; |
|||
|
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.lang.annotation.Documented; |
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
@Target(ElementType.TYPE) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
@Component |
|||
public @interface ExtRocketMQTemplateConfiguration { |
|||
/** |
|||
* The component name of the Producer configuration. |
|||
*/ |
|||
String value() default ""; |
|||
|
|||
/** |
|||
* The property of "name-server". |
|||
*/ |
|||
String nameServer(); |
|||
|
|||
/** |
|||
* Name of producer. |
|||
*/ |
|||
String group() default "${rocketmq.producer.group:}"; |
|||
/** |
|||
* Millis of send message timeout. |
|||
*/ |
|||
int sendMessageTimeout() default -1; |
|||
/** |
|||
* Compress message body threshold, namely, message body larger than 4k will be compressed on default. |
|||
*/ |
|||
int compressMessageBodyThreshold() default -1; |
|||
/** |
|||
* Maximum number of retry to perform internally before claiming sending failure in synchronous mode. |
|||
* This may potentially cause message duplication which is up to application developers to resolve. |
|||
*/ |
|||
int retryTimesWhenSendFailed() default -1; |
|||
/** |
|||
* <p> Maximum number of retry to perform internally before claiming sending failure in asynchronous mode. </p> |
|||
* This may potentially cause message duplication which is up to application developers to resolve. |
|||
*/ |
|||
int retryTimesWhenSendAsyncFailed() default -1; |
|||
/** |
|||
* Indicate whether to retry another broker on sending failure internally. |
|||
*/ |
|||
boolean retryNextServer() default false; |
|||
/** |
|||
* Maximum allowed message size in bytes. |
|||
*/ |
|||
int maxMessageSize() default -1; |
|||
/** |
|||
* The property of "access-key". |
|||
*/ |
|||
String accessKey() default "${rocketmq.producer.accessKey:}"; |
|||
/** |
|||
* The property of "secret-key". |
|||
*/ |
|||
String secretKey() default "${rocketmq.producer.secretKey:}"; |
|||
/** |
|||
* Switch flag instance for message trace. |
|||
*/ |
|||
boolean enableMsgTrace() default true; |
|||
/** |
|||
* The name value of message trace topic.If you don't config,you can use the default trace topic name. |
|||
*/ |
|||
String customizedTraceTopic() default "${rocketmq.producer.customized-trace-topic:}"; |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.annotation; |
|||
|
|||
public enum MessageModel { |
|||
BROADCASTING("BROADCASTING"), |
|||
CLUSTERING("CLUSTERING"); |
|||
|
|||
private final String modeCN; |
|||
|
|||
MessageModel(String modeCN) { |
|||
this.modeCN = modeCN; |
|||
} |
|||
|
|||
public String getModeCN() { |
|||
return this.modeCN; |
|||
} |
|||
} |
|||
@ -0,0 +1,112 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.annotation; |
|||
|
|||
import java.lang.annotation.Documented; |
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
@Target(ElementType.TYPE) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
public @interface RocketMQMessageListener { |
|||
|
|||
String NAME_SERVER_PLACEHOLDER = "${rocketmq.name-server:}"; |
|||
String ACCESS_KEY_PLACEHOLDER = "${rocketmq.consumer.access-key:}"; |
|||
String SECRET_KEY_PLACEHOLDER = "${rocketmq.consumer.secret-key:}"; |
|||
String TRACE_TOPIC_PLACEHOLDER = "${rocketmq.consumer.customized-trace-topic:}"; |
|||
String ACCESS_CHANNEL_PLACEHOLDER = "${rocketmq.access-channel:}"; |
|||
|
|||
/** |
|||
* Consumers of the same role is required to have exactly same subscriptions and consumerGroup to correctly achieve |
|||
* load balance. It's required and needs to be globally unique. |
|||
* |
|||
* |
|||
* See <a href="http://rocketmq.apache.org/docs/core-concept/">here</a> for further discussion. |
|||
*/ |
|||
String consumerGroup(); |
|||
|
|||
/** |
|||
* Topic name. |
|||
*/ |
|||
String topic(); |
|||
|
|||
/** |
|||
* Control how to selector message. |
|||
* |
|||
* @see SelectorType |
|||
*/ |
|||
SelectorType selectorType() default SelectorType.TAG; |
|||
|
|||
/** |
|||
* Control which message can be select. Grammar please see {@link SelectorType#TAG} and {@link SelectorType#SQL92} |
|||
*/ |
|||
String selectorExpression() default "*"; |
|||
|
|||
/** |
|||
* Control consume mode, you can choice receive message concurrently or orderly. |
|||
*/ |
|||
ConsumeMode consumeMode() default ConsumeMode.CONCURRENTLY; |
|||
|
|||
/** |
|||
* Control message mode, if you want all subscribers receive message all message, broadcasting is a good choice. |
|||
*/ |
|||
MessageModel messageModel() default MessageModel.CLUSTERING; |
|||
|
|||
/** |
|||
* Max consumer thread number. |
|||
*/ |
|||
int consumeThreadMax() default 64; |
|||
|
|||
/** |
|||
* Max consumer timeout, default 30s. |
|||
*/ |
|||
long consumeTimeout() default 30000L; |
|||
|
|||
/** |
|||
* The property of "access-key". |
|||
*/ |
|||
String accessKey() default ACCESS_KEY_PLACEHOLDER; |
|||
|
|||
/** |
|||
* The property of "secret-key". |
|||
*/ |
|||
String secretKey() default SECRET_KEY_PLACEHOLDER; |
|||
|
|||
/** |
|||
* Switch flag instance for message trace. |
|||
*/ |
|||
boolean enableMsgTrace() default true; |
|||
|
|||
/** |
|||
* The name value of message trace topic.If you don't config,you can use the default trace topic name. |
|||
*/ |
|||
String customizedTraceTopic() default TRACE_TOPIC_PLACEHOLDER; |
|||
|
|||
/** |
|||
* The property of "name-server". |
|||
*/ |
|||
String nameServer() default NAME_SERVER_PLACEHOLDER; |
|||
|
|||
/** |
|||
* The property of "access-channel". |
|||
*/ |
|||
String accessChannel() default ACCESS_CHANNEL_PLACEHOLDER; |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.annotation; |
|||
|
|||
import org.apache.rocketmq.spring.config.RocketMQConfigUtils; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.lang.annotation.Documented; |
|||
import java.lang.annotation.ElementType; |
|||
import java.lang.annotation.Retention; |
|||
import java.lang.annotation.RetentionPolicy; |
|||
import java.lang.annotation.Target; |
|||
|
|||
/** |
|||
* This annotation is used over a class which implements interface |
|||
* org.apache.rocketmq.client.producer.TransactionListener. The class implements |
|||
* two methods for process callback events after the txProducer sends a transactional message. |
|||
* <p>Note: The annotation is used only on RocketMQ client producer side, it can not be used |
|||
* on consumer side. |
|||
*/ |
|||
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE}) |
|||
@Retention(RetentionPolicy.RUNTIME) |
|||
@Documented |
|||
@Component |
|||
public @interface RocketMQTransactionListener { |
|||
|
|||
/** |
|||
* Declare the txProducerGroup that is used to relate callback event to the listener, rocketMQTemplate must send a |
|||
* transactional message with the declared txProducerGroup. |
|||
* <p> |
|||
* <p>It is suggested to use the default txProducerGroup if your system only needs to define a TransactionListener class. |
|||
*/ |
|||
String txProducerGroup() default RocketMQConfigUtils.ROCKETMQ_TRANSACTION_DEFAULT_GLOBAL_NAME; |
|||
|
|||
/** |
|||
* Set ExecutorService params -- corePoolSize |
|||
*/ |
|||
int corePoolSize() default 1; |
|||
|
|||
/** |
|||
* Set ExecutorService params -- maximumPoolSize |
|||
*/ |
|||
int maximumPoolSize() default 1; |
|||
|
|||
/** |
|||
* Set ExecutorService params -- keepAliveTime |
|||
*/ |
|||
long keepAliveTime() default 1000 * 60; //60ms
|
|||
|
|||
/** |
|||
* Set ExecutorService params -- blockingQueueSize |
|||
*/ |
|||
int blockingQueueSize() default 2000; |
|||
|
|||
/** |
|||
* The property of "access-key" |
|||
*/ |
|||
String accessKey() default "${rocketmq.producer.access-key}"; |
|||
|
|||
/** |
|||
* The property of "secret-key" |
|||
*/ |
|||
String secretKey() default "${rocketmq.producer.secret-key}"; |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.annotation; |
|||
|
|||
import org.apache.rocketmq.common.filter.ExpressionType; |
|||
|
|||
public enum SelectorType { |
|||
|
|||
/** |
|||
* @see ExpressionType#TAG |
|||
*/ |
|||
TAG, |
|||
|
|||
/** |
|||
* @see ExpressionType#SQL92 |
|||
*/ |
|||
SQL92 |
|||
} |
|||
@ -0,0 +1,158 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.autoconfigure; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.apache.rocketmq.acl.common.AclClientRPCHook; |
|||
import org.apache.rocketmq.acl.common.SessionCredentials; |
|||
import org.apache.rocketmq.client.exception.MQClientException; |
|||
import org.apache.rocketmq.client.producer.DefaultMQProducer; |
|||
import org.apache.rocketmq.spring.annotation.ExtRocketMQTemplateConfiguration; |
|||
import org.apache.rocketmq.spring.core.RocketMQTemplate; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.aop.framework.AopProxyUtils; |
|||
import org.springframework.beans.BeansException; |
|||
import org.springframework.beans.factory.SmartInitializingSingleton; |
|||
import org.springframework.beans.factory.support.BeanDefinitionValidationException; |
|||
import org.springframework.context.ApplicationContext; |
|||
import org.springframework.context.ApplicationContextAware; |
|||
import org.springframework.context.ConfigurableApplicationContext; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.support.GenericApplicationContext; |
|||
import org.springframework.core.env.StandardEnvironment; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
|
|||
|
|||
@Configuration |
|||
public class ExtProducerResetConfiguration implements ApplicationContextAware, SmartInitializingSingleton { |
|||
private final static Logger log = LoggerFactory.getLogger(ExtProducerResetConfiguration.class); |
|||
|
|||
private ConfigurableApplicationContext applicationContext; |
|||
|
|||
private StandardEnvironment environment; |
|||
|
|||
private RocketMQProperties rocketMQProperties; |
|||
|
|||
private ObjectMapper objectMapper; |
|||
|
|||
public ExtProducerResetConfiguration(ObjectMapper rocketMQMessageObjectMapper, |
|||
StandardEnvironment environment, |
|||
RocketMQProperties rocketMQProperties) { |
|||
this.objectMapper = rocketMQMessageObjectMapper; |
|||
this.environment = environment; |
|||
this.rocketMQProperties = rocketMQProperties; |
|||
} |
|||
|
|||
@Override |
|||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
|||
this.applicationContext = (ConfigurableApplicationContext) applicationContext; |
|||
} |
|||
|
|||
@Override |
|||
public void afterSingletonsInstantiated() { |
|||
Map<String, Object> beans = this.applicationContext.getBeansWithAnnotation(ExtRocketMQTemplateConfiguration.class); |
|||
|
|||
if (Objects.nonNull(beans)) { |
|||
beans.forEach(this::registerTemplate); |
|||
} |
|||
} |
|||
|
|||
private void registerTemplate(String beanName, Object bean) { |
|||
Class<?> clazz = AopProxyUtils.ultimateTargetClass(bean); |
|||
|
|||
if (!RocketMQTemplate.class.isAssignableFrom(bean.getClass())) { |
|||
throw new IllegalStateException(clazz + " is not instance of " + RocketMQTemplate.class.getName()); |
|||
} |
|||
|
|||
ExtRocketMQTemplateConfiguration annotation = clazz.getAnnotation(ExtRocketMQTemplateConfiguration.class); |
|||
GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext; |
|||
validate(annotation, genericApplicationContext); |
|||
|
|||
DefaultMQProducer mqProducer = createProducer(annotation); |
|||
// Set instanceName same as the beanName
|
|||
mqProducer.setInstanceName(beanName); |
|||
try { |
|||
mqProducer.start(); |
|||
} catch (MQClientException e) { |
|||
throw new BeanDefinitionValidationException(String.format("Failed to startup MQProducer for RocketMQTemplate {}", |
|||
beanName), e); |
|||
} |
|||
RocketMQTemplate rocketMQTemplate = (RocketMQTemplate) bean; |
|||
rocketMQTemplate.setProducer(mqProducer); |
|||
rocketMQTemplate.setObjectMapper(objectMapper); |
|||
|
|||
|
|||
log.info("Set real producer to :{} {}", beanName, annotation.value()); |
|||
} |
|||
|
|||
private DefaultMQProducer createProducer(ExtRocketMQTemplateConfiguration annotation) { |
|||
DefaultMQProducer producer = null; |
|||
|
|||
RocketMQProperties.Producer producerConfig = rocketMQProperties.getProducer(); |
|||
if (producerConfig == null) { |
|||
producerConfig = new RocketMQProperties.Producer(); |
|||
} |
|||
String nameServer = environment.resolvePlaceholders(annotation.nameServer()); |
|||
String groupName = environment.resolvePlaceholders(annotation.group()); |
|||
groupName = StringUtils.isEmpty(groupName) ? producerConfig.getGroup() : groupName; |
|||
|
|||
String ak = environment.resolvePlaceholders(annotation.accessKey()); |
|||
ak = StringUtils.isEmpty(ak) ? producerConfig.getAccessKey() : annotation.accessKey(); |
|||
String sk = environment.resolvePlaceholders(annotation.secretKey()); |
|||
sk = StringUtils.isEmpty(sk) ? producerConfig.getSecretKey() : annotation.secretKey(); |
|||
String customizedTraceTopic = environment.resolvePlaceholders(annotation.customizedTraceTopic()); |
|||
customizedTraceTopic = StringUtils.isEmpty(customizedTraceTopic) ? producerConfig.getCustomizedTraceTopic() : customizedTraceTopic; |
|||
|
|||
if (!StringUtils.isEmpty(ak) && !StringUtils.isEmpty(sk)) { |
|||
producer = new DefaultMQProducer(groupName, new AclClientRPCHook(new SessionCredentials(ak, sk)), |
|||
annotation.enableMsgTrace(), customizedTraceTopic); |
|||
producer.setVipChannelEnabled(false); |
|||
} else { |
|||
producer = new DefaultMQProducer(groupName, annotation.enableMsgTrace(), customizedTraceTopic); |
|||
} |
|||
|
|||
producer.setNamesrvAddr(nameServer); |
|||
producer.setSendMsgTimeout(annotation.sendMessageTimeout() == -1 ? producerConfig.getSendMessageTimeout() : annotation.sendMessageTimeout()); |
|||
producer.setRetryTimesWhenSendFailed(annotation.retryTimesWhenSendAsyncFailed() == -1 ? producerConfig.getRetryTimesWhenSendFailed() : annotation.retryTimesWhenSendAsyncFailed()); |
|||
producer.setRetryTimesWhenSendAsyncFailed(annotation.retryTimesWhenSendAsyncFailed() == -1 ? producerConfig.getRetryTimesWhenSendAsyncFailed() : annotation.retryTimesWhenSendAsyncFailed()); |
|||
producer.setMaxMessageSize(annotation.maxMessageSize() == -1 ? producerConfig.getMaxMessageSize() : annotation.maxMessageSize()); |
|||
producer.setCompressMsgBodyOverHowmuch(annotation.compressMessageBodyThreshold() == -1 ? producerConfig.getCompressMessageBodyThreshold() : annotation.compressMessageBodyThreshold()); |
|||
producer.setRetryAnotherBrokerWhenNotStoreOK(annotation.retryNextServer()); |
|||
|
|||
return producer; |
|||
} |
|||
|
|||
private void validate(ExtRocketMQTemplateConfiguration annotation, GenericApplicationContext genericApplicationContext) { |
|||
if (genericApplicationContext.isBeanNameInUse(annotation.value())) { |
|||
throw new BeanDefinitionValidationException(String.format("Bean {} has been used in Spring Application Context, " + |
|||
"please check the @ExtRocketMQTemplateConfiguration", |
|||
annotation.value())); |
|||
} |
|||
|
|||
if (rocketMQProperties.getNameServer() == null || |
|||
rocketMQProperties.getNameServer().equals(environment.resolvePlaceholders(annotation.nameServer()))) { |
|||
throw new BeanDefinitionValidationException( |
|||
"Bad annotation definition in @ExtRocketMQTemplateConfiguration, nameServer property is same with " + |
|||
"global property, please use the default RocketMQTemplate!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.autoconfigure; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
@Configuration |
|||
@ConditionalOnMissingBean(ObjectMapper.class) |
|||
class JacksonFallbackConfiguration { |
|||
|
|||
@Bean |
|||
public ObjectMapper rocketMQMessageObjectMapper() { |
|||
return new ObjectMapper(); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,139 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.autoconfigure; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.apache.rocketmq.client.AccessChannel; |
|||
import org.apache.rocketmq.spring.annotation.ConsumeMode; |
|||
import org.apache.rocketmq.spring.annotation.MessageModel; |
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.aop.framework.AopProxyUtils; |
|||
import org.springframework.beans.BeansException; |
|||
import org.springframework.beans.factory.SmartInitializingSingleton; |
|||
import org.springframework.beans.factory.support.BeanDefinitionValidationException; |
|||
import org.springframework.context.ApplicationContext; |
|||
import org.springframework.context.ApplicationContextAware; |
|||
import org.springframework.context.ConfigurableApplicationContext; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.support.GenericApplicationContext; |
|||
import org.springframework.core.env.StandardEnvironment; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
import java.util.concurrent.atomic.AtomicLong; |
|||
|
|||
|
|||
@Configuration |
|||
public class ListenerContainerConfiguration implements ApplicationContextAware, SmartInitializingSingleton { |
|||
private final static Logger log = LoggerFactory.getLogger(ListenerContainerConfiguration.class); |
|||
|
|||
private ConfigurableApplicationContext applicationContext; |
|||
|
|||
private AtomicLong counter = new AtomicLong(0); |
|||
|
|||
private StandardEnvironment environment; |
|||
|
|||
private RocketMQProperties rocketMQProperties; |
|||
|
|||
private ObjectMapper objectMapper; |
|||
|
|||
public ListenerContainerConfiguration(ObjectMapper rocketMQMessageObjectMapper, |
|||
StandardEnvironment environment, |
|||
RocketMQProperties rocketMQProperties) { |
|||
this.objectMapper = rocketMQMessageObjectMapper; |
|||
this.environment = environment; |
|||
this.rocketMQProperties = rocketMQProperties; |
|||
} |
|||
|
|||
@Override |
|||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
|||
this.applicationContext = (ConfigurableApplicationContext) applicationContext; |
|||
} |
|||
|
|||
@Override |
|||
public void afterSingletonsInstantiated() { |
|||
Map<String, Object> beans = this.applicationContext.getBeansWithAnnotation(RocketMQMessageListener.class); |
|||
|
|||
if (Objects.nonNull(beans)) { |
|||
beans.forEach(this::registerContainer); |
|||
} |
|||
} |
|||
|
|||
private void registerContainer(String beanName, Object bean) { |
|||
Class<?> clazz = AopProxyUtils.ultimateTargetClass(bean); |
|||
|
|||
if (!RocketMQListener.class.isAssignableFrom(bean.getClass())) { |
|||
throw new IllegalStateException(clazz + " is not instance of " + RocketMQListener.class.getName()); |
|||
} |
|||
|
|||
RocketMQMessageListener annotation = clazz.getAnnotation(RocketMQMessageListener.class); |
|||
validate(annotation); |
|||
|
|||
String containerBeanName = String.format("%s_%s", DefaultRocketMQListenerContainer.class.getName(), |
|||
counter.incrementAndGet()); |
|||
GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext; |
|||
|
|||
genericApplicationContext.registerBean(containerBeanName, DefaultRocketMQListenerContainer.class, |
|||
() -> createRocketMQListenerContainer(containerBeanName, bean, annotation)); |
|||
DefaultRocketMQListenerContainer container = genericApplicationContext.getBean(containerBeanName, |
|||
DefaultRocketMQListenerContainer.class); |
|||
if (!container.isRunning()) { |
|||
try { |
|||
container.start(); |
|||
} catch (Exception e) { |
|||
log.error("Started container failed. {}", container, e); |
|||
throw new RuntimeException(e); |
|||
} |
|||
} |
|||
|
|||
log.info("Register the listener to container, listenerBeanName:{}, containerBeanName:{}", beanName, containerBeanName); |
|||
} |
|||
|
|||
private DefaultRocketMQListenerContainer createRocketMQListenerContainer(String name, Object bean, RocketMQMessageListener annotation) { |
|||
DefaultRocketMQListenerContainer container = new DefaultRocketMQListenerContainer(); |
|||
|
|||
String nameServer = environment.resolvePlaceholders(annotation.nameServer()); |
|||
nameServer = StringUtils.isEmpty(nameServer) ? rocketMQProperties.getNameServer() : nameServer; |
|||
String accessChannel = environment.resolvePlaceholders(annotation.accessChannel()); |
|||
container.setNameServer(nameServer); |
|||
if (!StringUtils.isEmpty(accessChannel)) { |
|||
container.setAccessChannel(AccessChannel.valueOf(accessChannel)); |
|||
} |
|||
container.setTopic(environment.resolvePlaceholders(annotation.topic())); |
|||
container.setConsumerGroup(environment.resolvePlaceholders(annotation.consumerGroup())); |
|||
container.setRocketMQMessageListener(annotation); |
|||
container.setRocketMQListener((RocketMQListener) bean); |
|||
container.setObjectMapper(objectMapper); |
|||
container.setName(name); // REVIEW ME, use the same clientId or multiple?
|
|||
|
|||
return container; |
|||
} |
|||
|
|||
private void validate(RocketMQMessageListener annotation) { |
|||
if (annotation.consumeMode() == ConsumeMode.ORDERLY && |
|||
annotation.messageModel() == MessageModel.BROADCASTING) { |
|||
throw new BeanDefinitionValidationException( |
|||
"Bad annotation definition in @RocketMQMessageListener, messageModel BROADCASTING does not support ORDERLY message!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,139 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.autoconfigure; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.apache.rocketmq.acl.common.AclClientRPCHook; |
|||
import org.apache.rocketmq.acl.common.SessionCredentials; |
|||
import org.apache.rocketmq.client.AccessChannel; |
|||
import org.apache.rocketmq.client.MQAdmin; |
|||
import org.apache.rocketmq.client.producer.DefaultMQProducer; |
|||
import org.apache.rocketmq.spring.config.RocketMQConfigUtils; |
|||
import org.apache.rocketmq.spring.config.RocketMQTransactionAnnotationProcessor; |
|||
import org.apache.rocketmq.spring.config.TransactionHandlerRegistry; |
|||
import org.apache.rocketmq.spring.core.RocketMQTemplate; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Qualifier; |
|||
import org.springframework.beans.factory.config.BeanDefinition; |
|||
import org.springframework.boot.autoconfigure.AutoConfigureAfter; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; |
|||
import org.springframework.boot.context.properties.EnableConfigurationProperties; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.context.annotation.Import; |
|||
import org.springframework.context.annotation.Role; |
|||
import org.springframework.core.env.Environment; |
|||
import org.springframework.util.Assert; |
|||
import org.springframework.util.StringUtils; |
|||
|
|||
import javax.annotation.PostConstruct; |
|||
|
|||
@Configuration |
|||
@EnableConfigurationProperties(RocketMQProperties.class) |
|||
@ConditionalOnClass({ MQAdmin.class, ObjectMapper.class }) |
|||
@ConditionalOnProperty(prefix = "rocketmq", value = "name-server", matchIfMissing = true) |
|||
@Import({ JacksonFallbackConfiguration.class, ListenerContainerConfiguration.class, ExtProducerResetConfiguration.class }) |
|||
@AutoConfigureAfter(JacksonAutoConfiguration.class) |
|||
public class RocketMQAutoConfiguration { |
|||
private static final Logger log = LoggerFactory.getLogger(RocketMQAutoConfiguration.class); |
|||
|
|||
@Autowired |
|||
private Environment environment; |
|||
|
|||
@PostConstruct |
|||
public void checkProperties() { |
|||
String nameServer = environment.getProperty("rocketmq.name-server", String.class); |
|||
log.debug("rocketmq.nameServer = {}", nameServer); |
|||
if (nameServer == null) { |
|||
log.warn("The necessary spring property 'rocketmq.name-server' is not defined, all rockertmq beans creation are skipped!"); |
|||
} |
|||
} |
|||
|
|||
|
|||
@Bean |
|||
@ConditionalOnMissingBean(DefaultMQProducer.class) |
|||
@ConditionalOnProperty(prefix = "rocketmq", value = {"name-server", "producer.group"}) |
|||
public DefaultMQProducer defaultMQProducer(RocketMQProperties rocketMQProperties) { |
|||
RocketMQProperties.Producer producerConfig = rocketMQProperties.getProducer(); |
|||
String nameServer = rocketMQProperties.getNameServer(); |
|||
String groupName = producerConfig.getGroup(); |
|||
Assert.hasText(nameServer, "[rocketmq.name-server] must not be null"); |
|||
Assert.hasText(groupName, "[rocketmq.producer.group] must not be null"); |
|||
|
|||
String accessChannel = rocketMQProperties.getAccessChannel(); |
|||
|
|||
DefaultMQProducer producer; |
|||
String ak = rocketMQProperties.getProducer().getAccessKey(); |
|||
String sk = rocketMQProperties.getProducer().getSecretKey(); |
|||
if (!StringUtils.isEmpty(ak) && !StringUtils.isEmpty(sk)) { |
|||
producer = new DefaultMQProducer(groupName, new AclClientRPCHook(new SessionCredentials(ak, sk)), |
|||
rocketMQProperties.getProducer().isEnableMsgTrace(), |
|||
rocketMQProperties.getProducer().getCustomizedTraceTopic()); |
|||
producer.setVipChannelEnabled(false); |
|||
} else { |
|||
producer = new DefaultMQProducer(groupName, rocketMQProperties.getProducer().isEnableMsgTrace(), |
|||
rocketMQProperties.getProducer().getCustomizedTraceTopic()); |
|||
} |
|||
|
|||
producer.setNamesrvAddr(nameServer); |
|||
if (!StringUtils.isEmpty(accessChannel)) { |
|||
producer.setAccessChannel(AccessChannel.valueOf(accessChannel)); |
|||
} |
|||
producer.setSendMsgTimeout(producerConfig.getSendMessageTimeout()); |
|||
producer.setRetryTimesWhenSendFailed(producerConfig.getRetryTimesWhenSendFailed()); |
|||
producer.setRetryTimesWhenSendAsyncFailed(producerConfig.getRetryTimesWhenSendAsyncFailed()); |
|||
producer.setMaxMessageSize(producerConfig.getMaxMessageSize()); |
|||
producer.setCompressMsgBodyOverHowmuch(producerConfig.getCompressMessageBodyThreshold()); |
|||
producer.setRetryAnotherBrokerWhenNotStoreOK(producerConfig.isRetryNextServer()); |
|||
|
|||
return producer; |
|||
} |
|||
|
|||
@Bean(destroyMethod = "destroy") |
|||
@ConditionalOnBean(DefaultMQProducer.class) |
|||
@ConditionalOnMissingBean(name = RocketMQConfigUtils.ROCKETMQ_TEMPLATE_DEFAULT_GLOBAL_NAME) |
|||
public RocketMQTemplate rocketMQTemplate(DefaultMQProducer mqProducer, ObjectMapper rocketMQMessageObjectMapper) { |
|||
RocketMQTemplate rocketMQTemplate = new RocketMQTemplate(); |
|||
rocketMQTemplate.setProducer(mqProducer); |
|||
rocketMQTemplate.setObjectMapper(rocketMQMessageObjectMapper); |
|||
return rocketMQTemplate; |
|||
} |
|||
|
|||
@Bean |
|||
@ConditionalOnBean(name = RocketMQConfigUtils.ROCKETMQ_TEMPLATE_DEFAULT_GLOBAL_NAME) |
|||
@ConditionalOnMissingBean(TransactionHandlerRegistry.class) |
|||
public TransactionHandlerRegistry transactionHandlerRegistry(@Qualifier(RocketMQConfigUtils.ROCKETMQ_TEMPLATE_DEFAULT_GLOBAL_NAME) |
|||
RocketMQTemplate template) { |
|||
return new TransactionHandlerRegistry(template); |
|||
} |
|||
|
|||
@Bean(name = RocketMQConfigUtils.ROCKETMQ_TRANSACTION_ANNOTATION_PROCESSOR_BEAN_NAME) |
|||
@ConditionalOnBean(TransactionHandlerRegistry.class) |
|||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE) |
|||
public static RocketMQTransactionAnnotationProcessor transactionAnnotationProcessor( |
|||
TransactionHandlerRegistry transactionHandlerRegistry) { |
|||
return new RocketMQTransactionAnnotationProcessor(transactionHandlerRegistry); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,210 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.autoconfigure; |
|||
|
|||
import org.apache.rocketmq.common.MixAll; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
|
|||
@SuppressWarnings("WeakerAccess") |
|||
@ConfigurationProperties(prefix = "rocketmq") |
|||
public class RocketMQProperties { |
|||
|
|||
/** |
|||
* The name server for rocketMQ, formats: `host:port;host:port`. |
|||
*/ |
|||
private String nameServer; |
|||
|
|||
/** |
|||
* Enum type for accesChannel, values: LOCAL, CLOUD |
|||
*/ |
|||
private String accessChannel; |
|||
|
|||
private Producer producer; |
|||
|
|||
public String getNameServer() { |
|||
return nameServer; |
|||
} |
|||
|
|||
public void setNameServer(String nameServer) { |
|||
this.nameServer = nameServer; |
|||
} |
|||
|
|||
public String getAccessChannel() { |
|||
return accessChannel; |
|||
} |
|||
|
|||
public void setAccessChannel(String accessChannel) { |
|||
this.accessChannel = accessChannel; |
|||
} |
|||
|
|||
public Producer getProducer() { |
|||
return producer; |
|||
} |
|||
|
|||
public void setProducer(Producer producer) { |
|||
this.producer = producer; |
|||
} |
|||
|
|||
public static class Producer { |
|||
|
|||
/** |
|||
* Name of producer. |
|||
*/ |
|||
private String group; |
|||
|
|||
/** |
|||
* Millis of send message timeout. |
|||
*/ |
|||
private int sendMessageTimeout = 3000; |
|||
|
|||
/** |
|||
* Compress message body threshold, namely, message body larger than 4k will be compressed on default. |
|||
*/ |
|||
private int compressMessageBodyThreshold = 1024 * 4; |
|||
|
|||
/** |
|||
* Maximum number of retry to perform internally before claiming sending failure in synchronous mode. |
|||
* This may potentially cause message duplication which is up to application developers to resolve. |
|||
*/ |
|||
private int retryTimesWhenSendFailed = 2; |
|||
|
|||
/** |
|||
* <p> Maximum number of retry to perform internally before claiming sending failure in asynchronous mode. </p> |
|||
* This may potentially cause message duplication which is up to application developers to resolve. |
|||
*/ |
|||
private int retryTimesWhenSendAsyncFailed = 2; |
|||
|
|||
/** |
|||
* Indicate whether to retry another broker on sending failure internally. |
|||
*/ |
|||
private boolean retryNextServer = false; |
|||
|
|||
/** |
|||
* Maximum allowed message size in bytes. |
|||
*/ |
|||
private int maxMessageSize = 1024 * 1024 * 4; |
|||
|
|||
/** |
|||
* The property of "access-key". |
|||
*/ |
|||
private String accessKey; |
|||
|
|||
/** |
|||
* The property of "secret-key". |
|||
*/ |
|||
private String secretKey; |
|||
|
|||
/** |
|||
* Switch flag instance for message trace. |
|||
*/ |
|||
private boolean enableMsgTrace = true; |
|||
|
|||
/** |
|||
* The name value of message trace topic.If you don't config,you can use the default trace topic name. |
|||
*/ |
|||
private String customizedTraceTopic = MixAll.RMQ_SYS_TRACE_TOPIC; |
|||
|
|||
public String getGroup() { |
|||
return group; |
|||
} |
|||
|
|||
public void setGroup(String group) { |
|||
this.group = group; |
|||
} |
|||
|
|||
public int getSendMessageTimeout() { |
|||
return sendMessageTimeout; |
|||
} |
|||
|
|||
public void setSendMessageTimeout(int sendMessageTimeout) { |
|||
this.sendMessageTimeout = sendMessageTimeout; |
|||
} |
|||
|
|||
public int getCompressMessageBodyThreshold() { |
|||
return compressMessageBodyThreshold; |
|||
} |
|||
|
|||
public void setCompressMessageBodyThreshold(int compressMessageBodyThreshold) { |
|||
this.compressMessageBodyThreshold = compressMessageBodyThreshold; |
|||
} |
|||
|
|||
public int getRetryTimesWhenSendFailed() { |
|||
return retryTimesWhenSendFailed; |
|||
} |
|||
|
|||
public void setRetryTimesWhenSendFailed(int retryTimesWhenSendFailed) { |
|||
this.retryTimesWhenSendFailed = retryTimesWhenSendFailed; |
|||
} |
|||
|
|||
public int getRetryTimesWhenSendAsyncFailed() { |
|||
return retryTimesWhenSendAsyncFailed; |
|||
} |
|||
|
|||
public void setRetryTimesWhenSendAsyncFailed(int retryTimesWhenSendAsyncFailed) { |
|||
this.retryTimesWhenSendAsyncFailed = retryTimesWhenSendAsyncFailed; |
|||
} |
|||
|
|||
public boolean isRetryNextServer() { |
|||
return retryNextServer; |
|||
} |
|||
|
|||
public void setRetryNextServer(boolean retryNextServer) { |
|||
this.retryNextServer = retryNextServer; |
|||
} |
|||
|
|||
public int getMaxMessageSize() { |
|||
return maxMessageSize; |
|||
} |
|||
|
|||
public void setMaxMessageSize(int maxMessageSize) { |
|||
this.maxMessageSize = maxMessageSize; |
|||
} |
|||
|
|||
public String getAccessKey() { |
|||
return accessKey; |
|||
} |
|||
|
|||
public void setAccessKey(String accessKey) { |
|||
this.accessKey = accessKey; |
|||
} |
|||
|
|||
public String getSecretKey() { |
|||
return secretKey; |
|||
} |
|||
|
|||
public void setSecretKey(String secretKey) { |
|||
this.secretKey = secretKey; |
|||
} |
|||
|
|||
public boolean isEnableMsgTrace() { |
|||
return enableMsgTrace; |
|||
} |
|||
|
|||
public void setEnableMsgTrace(boolean enableMsgTrace) { |
|||
this.enableMsgTrace = enableMsgTrace; |
|||
} |
|||
|
|||
public String getCustomizedTraceTopic() { |
|||
return customizedTraceTopic; |
|||
} |
|||
|
|||
public void setCustomizedTraceTopic(String customizedTraceTopic) { |
|||
this.customizedTraceTopic = customizedTraceTopic; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.config; |
|||
|
|||
public class RocketMQConfigUtils { |
|||
/** |
|||
* The bean name of the internally managed RocketMQ transaction annotation processor. |
|||
*/ |
|||
public static final String ROCKETMQ_TRANSACTION_ANNOTATION_PROCESSOR_BEAN_NAME = |
|||
"org.springframework.rocketmq.spring.starter.internalRocketMQTransAnnotationProcessor"; |
|||
|
|||
public static final String ROCKETMQ_TRANSACTION_DEFAULT_GLOBAL_NAME = |
|||
"rocketmq_transaction_default_global_name"; |
|||
|
|||
public static final String ROCKETMQ_TEMPLATE_DEFAULT_GLOBAL_NAME = |
|||
"rocketMQTemplate"; |
|||
} |
|||
@ -0,0 +1,122 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.config; |
|||
|
|||
import org.apache.rocketmq.client.exception.MQClientException; |
|||
import org.apache.rocketmq.remoting.RPCHook; |
|||
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; |
|||
import org.apache.rocketmq.spring.support.RocketMQUtil; |
|||
import org.springframework.aop.support.AopUtils; |
|||
import org.springframework.beans.BeansException; |
|||
import org.springframework.beans.factory.BeanCreationException; |
|||
import org.springframework.beans.factory.config.BeanPostProcessor; |
|||
import org.springframework.context.ApplicationContext; |
|||
import org.springframework.context.ApplicationContextAware; |
|||
import org.springframework.core.Ordered; |
|||
import org.springframework.core.annotation.AnnotationUtils; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.Objects; |
|||
import java.util.Set; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
public class RocketMQTransactionAnnotationProcessor |
|||
implements BeanPostProcessor, Ordered, ApplicationContextAware { |
|||
private final static Logger log = LoggerFactory.getLogger(RocketMQTransactionAnnotationProcessor.class); |
|||
|
|||
private ApplicationContext applicationContext; |
|||
private final Set<Class<?>> nonProcessedClasses = |
|||
Collections.newSetFromMap(new ConcurrentHashMap<Class<?>, Boolean>(64)); |
|||
|
|||
private TransactionHandlerRegistry transactionHandlerRegistry; |
|||
|
|||
public RocketMQTransactionAnnotationProcessor(TransactionHandlerRegistry transactionHandlerRegistry) { |
|||
this.transactionHandlerRegistry = transactionHandlerRegistry; |
|||
} |
|||
|
|||
@Override |
|||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
|||
this.applicationContext = applicationContext; |
|||
} |
|||
|
|||
@Override |
|||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { |
|||
return bean; |
|||
} |
|||
|
|||
@Override |
|||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { |
|||
if (!this.nonProcessedClasses.contains(bean.getClass())) { |
|||
Class<?> targetClass = AopUtils.getTargetClass(bean); |
|||
RocketMQTransactionListener listener = AnnotationUtils.findAnnotation(targetClass, RocketMQTransactionListener.class); |
|||
this.nonProcessedClasses.add(bean.getClass()); |
|||
if (listener == null) { // for quick search
|
|||
log.trace("No @RocketMQTransactionListener annotations found on bean type: {}", bean.getClass()); |
|||
} else { |
|||
try { |
|||
processTransactionListenerAnnotation(listener, bean); |
|||
} catch (MQClientException e) { |
|||
log.error("Failed to process annotation " + listener, e); |
|||
throw new BeanCreationException("Failed to process annotation " + listener, e); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return bean; |
|||
} |
|||
|
|||
private void processTransactionListenerAnnotation(RocketMQTransactionListener listener, Object bean) |
|||
throws MQClientException { |
|||
if (transactionHandlerRegistry == null) { |
|||
throw new MQClientException("Bad usage of @RocketMQTransactionListener, " + |
|||
"the class must work with RocketMQTemplate", null); |
|||
} |
|||
if (!RocketMQLocalTransactionListener.class.isAssignableFrom(bean.getClass())) { |
|||
throw new MQClientException("Bad usage of @RocketMQTransactionListener, " + |
|||
"the class must implement interface RocketMQLocalTransactionListener", |
|||
null); |
|||
} |
|||
TransactionHandler transactionHandler = new TransactionHandler(); |
|||
transactionHandler.setBeanFactory(this.applicationContext.getAutowireCapableBeanFactory()); |
|||
transactionHandler.setName(listener.txProducerGroup()); |
|||
transactionHandler.setBeanName(bean.getClass().getName()); |
|||
transactionHandler.setListener((RocketMQLocalTransactionListener) bean); |
|||
transactionHandler.setCheckExecutor(listener.corePoolSize(), listener.maximumPoolSize(), |
|||
listener.keepAliveTime(), listener.blockingQueueSize()); |
|||
|
|||
RPCHook rpcHook = RocketMQUtil.getRPCHookByAkSk(applicationContext.getEnvironment(), |
|||
listener.accessKey(), listener.secretKey()); |
|||
|
|||
if (Objects.nonNull(rpcHook)) { |
|||
transactionHandler.setRpcHook(rpcHook); |
|||
} else { |
|||
log.debug("Access-key or secret-key not configure in " + listener + "."); |
|||
} |
|||
|
|||
transactionHandlerRegistry.registerTransactionHandler(transactionHandler); |
|||
} |
|||
|
|||
@Override |
|||
public int getOrder() { |
|||
return LOWEST_PRECEDENCE; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,85 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.config; |
|||
|
|||
import org.apache.rocketmq.remoting.RPCHook; |
|||
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; |
|||
import org.springframework.beans.factory.BeanFactory; |
|||
|
|||
import java.util.concurrent.LinkedBlockingDeque; |
|||
import java.util.concurrent.ThreadPoolExecutor; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
class TransactionHandler { |
|||
private String name; |
|||
private String beanName; |
|||
private RocketMQLocalTransactionListener bean; |
|||
private BeanFactory beanFactory; |
|||
private ThreadPoolExecutor checkExecutor; |
|||
private RPCHook rpcHook; |
|||
|
|||
public String getBeanName() { |
|||
return beanName; |
|||
} |
|||
|
|||
public void setBeanName(String beanName) { |
|||
this.beanName = beanName; |
|||
} |
|||
|
|||
public String getName() { |
|||
return name; |
|||
} |
|||
|
|||
public void setName(String name) { |
|||
this.name = name; |
|||
} |
|||
|
|||
public RPCHook getRpcHook() { |
|||
return rpcHook; |
|||
} |
|||
|
|||
public void setRpcHook(RPCHook rpcHook) { |
|||
this.rpcHook = rpcHook; |
|||
} |
|||
|
|||
public BeanFactory getBeanFactory() { |
|||
return beanFactory; |
|||
} |
|||
|
|||
public void setBeanFactory(BeanFactory beanFactory) { |
|||
this.beanFactory = beanFactory; |
|||
} |
|||
|
|||
public void setListener(RocketMQLocalTransactionListener listener) { |
|||
this.bean = listener; |
|||
} |
|||
|
|||
public RocketMQLocalTransactionListener getListener() { |
|||
return this.bean; |
|||
} |
|||
|
|||
public void setCheckExecutor(int corePoolSize, int maxPoolSize, long keepAliveTime, int blockingQueueSize) { |
|||
this.checkExecutor = new ThreadPoolExecutor(corePoolSize, maxPoolSize, |
|||
keepAliveTime, TimeUnit.MILLISECONDS, |
|||
new LinkedBlockingDeque<>(blockingQueueSize)); |
|||
} |
|||
|
|||
public ThreadPoolExecutor getCheckExecutor() { |
|||
return checkExecutor; |
|||
} |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.config; |
|||
|
|||
import io.netty.util.internal.ConcurrentSet; |
|||
import org.apache.rocketmq.client.exception.MQClientException; |
|||
import org.apache.rocketmq.spring.core.RocketMQTemplate; |
|||
import org.springframework.beans.factory.DisposableBean; |
|||
|
|||
import java.util.Set; |
|||
|
|||
public class TransactionHandlerRegistry implements DisposableBean { |
|||
private RocketMQTemplate rocketMQTemplate; |
|||
|
|||
private final Set<String> listenerContainers = new ConcurrentSet<>(); |
|||
|
|||
public TransactionHandlerRegistry(RocketMQTemplate template) { |
|||
this.rocketMQTemplate = template; |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() throws Exception { |
|||
listenerContainers.clear(); |
|||
} |
|||
|
|||
public void registerTransactionHandler(TransactionHandler handler) throws MQClientException { |
|||
if (listenerContainers.contains(handler.getName())) { |
|||
throw new MQClientException(-1, |
|||
String |
|||
.format("The transaction name [%s] has been defined in TransactionListener [%s]", handler.getName(), |
|||
handler.getBeanName())); |
|||
} |
|||
listenerContainers.add(handler.getName()); |
|||
|
|||
rocketMQTemplate.createAndStartTransactionMQProducer(handler.getName(), handler.getListener(), handler.getCheckExecutor(), handler.getRpcHook()); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.core; |
|||
|
|||
public interface RocketMQListener<T> { |
|||
void onMessage(T message); |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.apache.rocketmq.spring.core; |
|||
|
|||
import org.springframework.messaging.Message; |
|||
|
|||
public interface RocketMQLocalTransactionListener { |
|||
RocketMQLocalTransactionState executeLocalTransaction(final Message msg, final Object arg); |
|||
|
|||
RocketMQLocalTransactionState checkLocalTransaction(final Message msg); |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.core; |
|||
|
|||
public enum RocketMQLocalTransactionState { |
|||
COMMIT, |
|||
ROLLBACK, |
|||
UNKNOWN |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.core; |
|||
|
|||
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; |
|||
import org.apache.rocketmq.spring.support.RocketMQConsumerLifecycleListener; |
|||
|
|||
public interface RocketMQPushConsumerLifecycleListener extends RocketMQConsumerLifecycleListener<DefaultMQPushConsumer> { |
|||
} |
|||
@ -0,0 +1,677 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.core; |
|||
|
|||
import com.fasterxml.jackson.core.JsonProcessingException; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.apache.rocketmq.client.exception.MQClientException; |
|||
import org.apache.rocketmq.client.producer.DefaultMQProducer; |
|||
import org.apache.rocketmq.client.producer.MessageQueueSelector; |
|||
import org.apache.rocketmq.client.producer.SendCallback; |
|||
import org.apache.rocketmq.client.producer.SendResult; |
|||
import org.apache.rocketmq.client.producer.TransactionMQProducer; |
|||
import org.apache.rocketmq.client.producer.TransactionSendResult; |
|||
import org.apache.rocketmq.client.producer.selector.SelectMessageQueueByHash; |
|||
import org.apache.rocketmq.remoting.RPCHook; |
|||
import org.apache.rocketmq.spring.config.RocketMQConfigUtils; |
|||
import org.apache.rocketmq.spring.support.RocketMQUtil; |
|||
import org.springframework.beans.factory.DisposableBean; |
|||
import org.springframework.beans.factory.InitializingBean; |
|||
import org.springframework.messaging.Message; |
|||
import org.springframework.messaging.MessageHeaders; |
|||
import org.springframework.messaging.MessagingException; |
|||
import org.springframework.messaging.core.AbstractMessageSendingTemplate; |
|||
import org.springframework.messaging.core.MessagePostProcessor; |
|||
import org.springframework.messaging.support.MessageBuilder; |
|||
import org.springframework.util.Assert; |
|||
import org.springframework.util.MimeTypeUtils; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collection; |
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
import java.util.concurrent.ConcurrentHashMap; |
|||
import java.util.concurrent.ExecutorService; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
|
|||
@SuppressWarnings({"WeakerAccess", "unused"}) |
|||
public class RocketMQTemplate extends AbstractMessageSendingTemplate<String> implements InitializingBean, DisposableBean { |
|||
private static final Logger log = LoggerFactory.getLogger(RocketMQTemplate.class); |
|||
|
|||
private DefaultMQProducer producer; |
|||
|
|||
private ObjectMapper objectMapper; |
|||
|
|||
private String charset = "UTF-8"; |
|||
|
|||
private MessageQueueSelector messageQueueSelector = new SelectMessageQueueByHash(); |
|||
|
|||
private final Map<String, TransactionMQProducer> cache = new ConcurrentHashMap<>(); //only put TransactionMQProducer by now!!!
|
|||
|
|||
public DefaultMQProducer getProducer() { |
|||
return producer; |
|||
} |
|||
|
|||
public void setProducer(DefaultMQProducer producer) { |
|||
this.producer = producer; |
|||
} |
|||
|
|||
public ObjectMapper getObjectMapper() { |
|||
return objectMapper; |
|||
} |
|||
|
|||
public void setObjectMapper(ObjectMapper objectMapper) { |
|||
this.objectMapper = objectMapper; |
|||
} |
|||
|
|||
public String getCharset() { |
|||
return charset; |
|||
} |
|||
|
|||
public void setCharset(String charset) { |
|||
this.charset = charset; |
|||
} |
|||
|
|||
public MessageQueueSelector getMessageQueueSelector() { |
|||
return messageQueueSelector; |
|||
} |
|||
|
|||
public void setMessageQueueSelector(MessageQueueSelector messageQueueSelector) { |
|||
this.messageQueueSelector = messageQueueSelector; |
|||
} |
|||
|
|||
/** |
|||
* <p> Send message in synchronous mode. This method returns only when the sending procedure totally completes. |
|||
* Reliable synchronous transmission is used in extensive scenes, such as important notification messages, SMS |
|||
* notification, SMS marketing system, etc.. </p> |
|||
* <p> |
|||
* <strong>Warn:</strong> this method has internal retry-mechanism, that is, internal implementation will retry |
|||
* {@link DefaultMQProducer#getRetryTimesWhenSendFailed} times before claiming failure. As a result, multiple |
|||
* messages may potentially delivered to broker(s). It's up to the application developers to resolve potential |
|||
* duplication issue. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @return {@link SendResult} |
|||
*/ |
|||
public SendResult syncSend(String destination, Message<?> message) { |
|||
return syncSend(destination, message, producer.getSendMsgTimeout()); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #syncSend(String, Message)} with send timeout specified in addition. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @param timeout send timeout with millis |
|||
* @return {@link SendResult} |
|||
*/ |
|||
public SendResult syncSend(String destination, Message<?> message, long timeout) { |
|||
return syncSend(destination, message, timeout, 0); |
|||
} |
|||
|
|||
/** |
|||
* syncSend batch messages in a given timeout. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param messages Collection of {@link Message} |
|||
* @param timeout send timeout with millis |
|||
* @return {@link SendResult} |
|||
*/ |
|||
public SendResult syncSend(String destination, Collection<Message<?>> messages, long timeout) { |
|||
if (Objects.isNull(messages) || messages.size() == 0) { |
|||
log.error("syncSend with batch failed. destination:{}, messages is empty ", destination); |
|||
throw new IllegalArgumentException("`messages` can not be empty"); |
|||
} |
|||
|
|||
try { |
|||
long now = System.currentTimeMillis(); |
|||
Collection<org.apache.rocketmq.common.message.Message> rmqMsgs = new ArrayList<>(); |
|||
org.apache.rocketmq.common.message.Message rocketMsg; |
|||
for (Message<?> msg:messages) { |
|||
if (Objects.isNull(msg) || Objects.isNull(msg.getPayload())) { |
|||
log.warn("Found a message empty in the batch, skip it"); |
|||
continue; |
|||
} |
|||
rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper, charset, destination, msg); |
|||
rmqMsgs.add(rocketMsg); |
|||
} |
|||
|
|||
SendResult sendResult = producer.send(rmqMsgs, timeout); |
|||
long costTime = System.currentTimeMillis() - now; |
|||
log.debug("send messages cost: {} ms, msgId:{}", costTime, sendResult.getMsgId()); |
|||
return sendResult; |
|||
} catch (Exception e) { |
|||
log.error("syncSend with batch failed. destination:{}, messages.size:{} ", destination, messages.size()); |
|||
throw new MessagingException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #syncSend(String, Message)} with send timeout specified in addition. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @param timeout send timeout with millis |
|||
* @param delayLevel level for the delay message |
|||
* @return {@link SendResult} |
|||
*/ |
|||
public SendResult syncSend(String destination, Message<?> message, long timeout, int delayLevel) { |
|||
if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { |
|||
log.error("syncSend failed. destination:{}, message is null ", destination); |
|||
throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); |
|||
} |
|||
|
|||
try { |
|||
long now = System.currentTimeMillis(); |
|||
org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
charset, destination, message); |
|||
if (delayLevel > 0) { |
|||
rocketMsg.setDelayTimeLevel(delayLevel); |
|||
} |
|||
SendResult sendResult = producer.send(rocketMsg, timeout); |
|||
long costTime = System.currentTimeMillis() - now; |
|||
log.debug("send message cost: {} ms, msgId:{}", costTime, sendResult.getMsgId()); |
|||
return sendResult; |
|||
} catch (Exception e) { |
|||
log.error("syncSend failed. destination:{}, message:{} ", destination, message); |
|||
throw new MessagingException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #syncSend(String, Message)}. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param payload the Object to use as payload |
|||
* @return {@link SendResult} |
|||
*/ |
|||
public SendResult syncSend(String destination, Object payload) { |
|||
return syncSend(destination, payload, producer.getSendMsgTimeout()); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #syncSend(String, Object)} with send timeout specified in addition. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param payload the Object to use as payload |
|||
* @param timeout send timeout with millis |
|||
* @return {@link SendResult} |
|||
*/ |
|||
public SendResult syncSend(String destination, Object payload, long timeout) { |
|||
Message<?> message = this.doConvert(payload, null, null); |
|||
return syncSend(destination, message, timeout); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #syncSend(String, Message)} with send orderly with hashKey by specified. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @param hashKey use this key to select queue. for example: orderId, productId ... |
|||
* @return {@link SendResult} |
|||
*/ |
|||
public SendResult syncSendOrderly(String destination, Message<?> message, String hashKey) { |
|||
return syncSendOrderly(destination, message, hashKey, producer.getSendMsgTimeout()); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #syncSendOrderly(String, Message, String)} with send timeout specified in addition. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @param hashKey use this key to select queue. for example: orderId, productId ... |
|||
* @param timeout send timeout with millis |
|||
* @return {@link SendResult} |
|||
*/ |
|||
public SendResult syncSendOrderly(String destination, Message<?> message, String hashKey, long timeout) { |
|||
if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { |
|||
log.error("syncSendOrderly failed. destination:{}, message is null ", destination); |
|||
throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); |
|||
} |
|||
|
|||
try { |
|||
long now = System.currentTimeMillis(); |
|||
org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
charset, destination, message); |
|||
SendResult sendResult = producer.send(rocketMsg, messageQueueSelector, hashKey, timeout); |
|||
long costTime = System.currentTimeMillis() - now; |
|||
log.debug("send message cost: {} ms, msgId:{}", costTime, sendResult.getMsgId()); |
|||
return sendResult; |
|||
} catch (Exception e) { |
|||
log.error("syncSendOrderly failed. destination:{}, message:{} ", destination, message); |
|||
throw new MessagingException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #syncSend(String, Object)} with send orderly with hashKey by specified. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param payload the Object to use as payload |
|||
* @param hashKey use this key to select queue. for example: orderId, productId ... |
|||
* @return {@link SendResult} |
|||
*/ |
|||
public SendResult syncSendOrderly(String destination, Object payload, String hashKey) { |
|||
return syncSendOrderly(destination, payload, hashKey, producer.getSendMsgTimeout()); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #syncSendOrderly(String, Object, String)} with send timeout specified in addition. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param payload the Object to use as payload |
|||
* @param hashKey use this key to select queue. for example: orderId, productId ... |
|||
* @param timeout send timeout with millis |
|||
* @return {@link SendResult} |
|||
*/ |
|||
public SendResult syncSendOrderly(String destination, Object payload, String hashKey, long timeout) { |
|||
Message<?> message = this.doConvert(payload, null, null); |
|||
return syncSendOrderly(destination, message, hashKey, producer.getSendMsgTimeout()); |
|||
} |
|||
/** |
|||
* Same to {@link #asyncSend(String, Message, SendCallback)} with send timeout and delay level specified in addition. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @param sendCallback {@link SendCallback} |
|||
* @param timeout send timeout with millis |
|||
* @param delayLevel level for the delay message |
|||
*/ |
|||
public void asyncSend(String destination, Message<?> message, SendCallback sendCallback, long timeout, int delayLevel) { |
|||
if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { |
|||
log.error("asyncSend failed. destination:{}, message is null ", destination); |
|||
throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); |
|||
} |
|||
|
|||
try { |
|||
org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
charset, destination, message); |
|||
if (delayLevel > 0) { |
|||
rocketMsg.setDelayTimeLevel(delayLevel); |
|||
} |
|||
producer.send(rocketMsg, sendCallback, timeout); |
|||
} catch (Exception e) { |
|||
log.info("asyncSend failed. destination:{}, message:{} ", destination, message); |
|||
throw new MessagingException(e.getMessage(), e); |
|||
} |
|||
} |
|||
/** |
|||
* Same to {@link #asyncSend(String, Message, SendCallback)} with send timeout specified in addition. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @param sendCallback {@link SendCallback} |
|||
* @param timeout send timeout with millis |
|||
*/ |
|||
public void asyncSend(String destination, Message<?> message, SendCallback sendCallback, long timeout) { |
|||
asyncSend(destination,message,sendCallback,timeout,0); |
|||
} |
|||
|
|||
/** |
|||
* <p> Send message to broker asynchronously. asynchronous transmission is generally used in response time sensitive |
|||
* business scenarios. </p> |
|||
* <p> |
|||
* This method returns immediately. On sending completion, <code>sendCallback</code> will be executed. |
|||
* <p> |
|||
* Similar to {@link #syncSend(String, Object)}, internal implementation would potentially retry up to {@link |
|||
* DefaultMQProducer#getRetryTimesWhenSendAsyncFailed} times before claiming sending failure, which may yield |
|||
* message duplication and application developers are the one to resolve this potential issue. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @param sendCallback {@link SendCallback} |
|||
*/ |
|||
public void asyncSend(String destination, Message<?> message, SendCallback sendCallback) { |
|||
asyncSend(destination, message, sendCallback, producer.getSendMsgTimeout()); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #asyncSend(String, Object, SendCallback)} with send timeout specified in addition. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param payload the Object to use as payload |
|||
* @param sendCallback {@link SendCallback} |
|||
* @param timeout send timeout with millis |
|||
*/ |
|||
public void asyncSend(String destination, Object payload, SendCallback sendCallback, long timeout) { |
|||
Message<?> message = this.doConvert(payload, null, null); |
|||
asyncSend(destination, message, sendCallback, timeout); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #asyncSend(String, Message, SendCallback)}. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param payload the Object to use as payload |
|||
* @param sendCallback {@link SendCallback} |
|||
*/ |
|||
public void asyncSend(String destination, Object payload, SendCallback sendCallback) { |
|||
asyncSend(destination, payload, sendCallback, producer.getSendMsgTimeout()); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #asyncSendOrderly(String, Message, String, SendCallback)} with send timeout specified in |
|||
* addition. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @param hashKey use this key to select queue. for example: orderId, productId ... |
|||
* @param sendCallback {@link SendCallback} |
|||
* @param timeout send timeout with millis |
|||
*/ |
|||
public void asyncSendOrderly(String destination, Message<?> message, String hashKey, SendCallback sendCallback, |
|||
long timeout) { |
|||
if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { |
|||
log.error("asyncSendOrderly failed. destination:{}, message is null ", destination); |
|||
throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); |
|||
} |
|||
|
|||
try { |
|||
org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
charset, destination, message); |
|||
producer.send(rocketMsg, messageQueueSelector, hashKey, sendCallback, timeout); |
|||
} catch (Exception e) { |
|||
log.error("asyncSendOrderly failed. destination:{}, message:{} ", destination, message); |
|||
throw new MessagingException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #asyncSend(String, Message, SendCallback)} with send orderly with hashKey by specified. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @param hashKey use this key to select queue. for example: orderId, productId ... |
|||
* @param sendCallback {@link SendCallback} |
|||
*/ |
|||
public void asyncSendOrderly(String destination, Message<?> message, String hashKey, SendCallback sendCallback) { |
|||
asyncSendOrderly(destination, message, hashKey, sendCallback, producer.getSendMsgTimeout()); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #asyncSendOrderly(String, Message, String, SendCallback)}. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param payload the Object to use as payload |
|||
* @param hashKey use this key to select queue. for example: orderId, productId ... |
|||
* @param sendCallback {@link SendCallback} |
|||
*/ |
|||
public void asyncSendOrderly(String destination, Object payload, String hashKey, SendCallback sendCallback) { |
|||
asyncSendOrderly(destination, payload, hashKey, sendCallback, producer.getSendMsgTimeout()); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #asyncSendOrderly(String, Object, String, SendCallback)} with send timeout specified in addition. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param payload the Object to use as payload |
|||
* @param hashKey use this key to select queue. for example: orderId, productId ... |
|||
* @param sendCallback {@link SendCallback} |
|||
* @param timeout send timeout with millis |
|||
*/ |
|||
public void asyncSendOrderly(String destination, Object payload, String hashKey, SendCallback sendCallback, |
|||
long timeout) { |
|||
Message<?> message = this.doConvert(payload, null, null); |
|||
asyncSendOrderly(destination, message, hashKey, sendCallback, timeout); |
|||
} |
|||
|
|||
/** |
|||
* Similar to <a href="https://en.wikipedia.org/wiki/User_Datagram_Protocol">UDP</a>, this method won't wait for |
|||
* acknowledgement from broker before return. Obviously, it has maximums throughput yet potentials of message loss. |
|||
* <p> |
|||
* One-way transmission is used for cases requiring moderate reliability, such as log collection. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
*/ |
|||
public void sendOneWay(String destination, Message<?> message) { |
|||
if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { |
|||
log.error("sendOneWay failed. destination:{}, message is null ", destination); |
|||
throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); |
|||
} |
|||
|
|||
try { |
|||
org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
charset, destination, message); |
|||
producer.sendOneway(rocketMsg); |
|||
} catch (Exception e) { |
|||
log.error("sendOneWay failed. destination:{}, message:{} ", destination, message); |
|||
throw new MessagingException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #sendOneWay(String, Message)} |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param payload the Object to use as payload |
|||
*/ |
|||
public void sendOneWay(String destination, Object payload) { |
|||
Message<?> message = this.doConvert(payload, null, null); |
|||
sendOneWay(destination, message); |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #sendOneWay(String, Message)} with send orderly with hashKey by specified. |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param message {@link Message} |
|||
* @param hashKey use this key to select queue. for example: orderId, productId ... |
|||
*/ |
|||
public void sendOneWayOrderly(String destination, Message<?> message, String hashKey) { |
|||
if (Objects.isNull(message) || Objects.isNull(message.getPayload())) { |
|||
log.error("sendOneWayOrderly failed. destination:{}, message is null ", destination); |
|||
throw new IllegalArgumentException("`message` and `message.payload` cannot be null"); |
|||
} |
|||
|
|||
try { |
|||
org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
charset, destination, message); |
|||
producer.sendOneway(rocketMsg, messageQueueSelector, hashKey); |
|||
} catch (Exception e) { |
|||
log.error("sendOneWayOrderly failed. destination:{}, message:{}", destination, message); |
|||
throw new MessagingException(e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Same to {@link #sendOneWayOrderly(String, Message, String)} |
|||
* |
|||
* @param destination formats: `topicName:tags` |
|||
* @param payload the Object to use as payload |
|||
*/ |
|||
public void sendOneWayOrderly(String destination, Object payload, String hashKey) { |
|||
Message<?> message = this.doConvert(payload, null, null); |
|||
sendOneWayOrderly(destination, message, hashKey); |
|||
} |
|||
|
|||
@Override |
|||
public void afterPropertiesSet() throws Exception { |
|||
if (producer != null) { |
|||
producer.start(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
protected void doSend(String destination, Message<?> message) { |
|||
SendResult sendResult = syncSend(destination, message); |
|||
log.debug("send message to `{}` finished. result:{}", destination, sendResult); |
|||
} |
|||
|
|||
|
|||
|
|||
@Override |
|||
protected Message<?> doConvert(Object payload, Map<String, Object> headers, MessagePostProcessor postProcessor) { |
|||
String content; |
|||
if (payload instanceof String) { |
|||
content = (String) payload; |
|||
} else { |
|||
// If payload not as string, use objectMapper change it.
|
|||
try { |
|||
content = objectMapper.writeValueAsString(payload); |
|||
} catch (JsonProcessingException e) { |
|||
log.error("convert payload to String failed. payload:{}", payload); |
|||
throw new RuntimeException("convert to payload to String failed.", e); |
|||
} |
|||
} |
|||
|
|||
MessageBuilder<?> builder = MessageBuilder.withPayload(content); |
|||
if (headers != null) { |
|||
builder.copyHeaders(headers); |
|||
} |
|||
builder.setHeaderIfAbsent(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.TEXT_PLAIN); |
|||
|
|||
Message<?> message = builder.build(); |
|||
if (postProcessor != null) { |
|||
message = postProcessor.postProcessMessage(message); |
|||
} |
|||
return message; |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
if (Objects.nonNull(producer)) { |
|||
producer.shutdown(); |
|||
} |
|||
|
|||
for (Map.Entry<String, TransactionMQProducer> kv : cache.entrySet()) { |
|||
if (Objects.nonNull(kv.getValue())) { |
|||
kv.getValue().shutdown(); |
|||
} |
|||
} |
|||
cache.clear(); |
|||
} |
|||
|
|||
private String getTxProducerGroupName(String name) { |
|||
return name == null ? RocketMQConfigUtils.ROCKETMQ_TRANSACTION_DEFAULT_GLOBAL_NAME : name; |
|||
} |
|||
|
|||
private TransactionMQProducer stageMQProducer(String name) throws MessagingException { |
|||
name = getTxProducerGroupName(name); |
|||
|
|||
TransactionMQProducer cachedProducer = cache.get(name); |
|||
if (cachedProducer == null) { |
|||
throw new MessagingException( |
|||
String.format("Can not found MQProducer '%s' in cache! please define @RocketMQLocalTransactionListener class or invoke createOrGetStartedTransactionMQProducer() to create it firstly", name)); |
|||
} |
|||
|
|||
return cachedProducer; |
|||
} |
|||
|
|||
/** |
|||
* Send Spring Message in Transaction |
|||
* |
|||
* @param txProducerGroup the validate txProducerGroup name, set null if using the default name |
|||
* @param destination destination formats: `topicName:tags` |
|||
* @param message message {@link Message} |
|||
* @param arg ext arg |
|||
* @return TransactionSendResult |
|||
* @throws MessagingException |
|||
*/ |
|||
public TransactionSendResult sendMessageInTransaction(final String txProducerGroup, final String destination, final Message<?> message, final Object arg) throws MessagingException { |
|||
try { |
|||
TransactionMQProducer txProducer = this.stageMQProducer(txProducerGroup); |
|||
org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
charset, destination, message); |
|||
return txProducer.sendMessageInTransaction(rocketMsg, arg); |
|||
} catch (MQClientException e) { |
|||
throw RocketMQUtil.convert(e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Remove a TransactionMQProducer from cache by manual. |
|||
* <p>Note: RocketMQTemplate can release all cached producers when bean destroying, it is not recommended to directly |
|||
* use this method by user. |
|||
* |
|||
* @param txProducerGroup |
|||
* @throws MessagingException |
|||
*/ |
|||
public void removeTransactionMQProducer(String txProducerGroup) throws MessagingException { |
|||
txProducerGroup = getTxProducerGroupName(txProducerGroup); |
|||
if (cache.containsKey(txProducerGroup)) { |
|||
DefaultMQProducer cachedProducer = cache.get(txProducerGroup); |
|||
cachedProducer.shutdown(); |
|||
cache.remove(txProducerGroup); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Create and start a transaction MQProducer, this new producer is cached in memory. |
|||
* <p>Note: This method is invoked internally when processing {@code @RocketMQLocalTransactionListener}, it is not |
|||
* recommended to directly use this method by user. |
|||
* |
|||
* @param txProducerGroup Producer (group) name, unique for each producer |
|||
* @param transactionListener TransactoinListener impl class |
|||
* @param executorService Nullable. |
|||
* @param rpcHook Nullable. |
|||
* @return true if producer is created and started; false if the named producer already exists in cache. |
|||
* @throws MessagingException |
|||
*/ |
|||
public boolean createAndStartTransactionMQProducer(String txProducerGroup, |
|||
RocketMQLocalTransactionListener transactionListener, |
|||
ExecutorService executorService, RPCHook rpcHook) throws MessagingException { |
|||
txProducerGroup = getTxProducerGroupName(txProducerGroup); |
|||
if (cache.containsKey(txProducerGroup)) { |
|||
log.info(String.format("get TransactionMQProducer '%s' from cache", txProducerGroup)); |
|||
return false; |
|||
} |
|||
|
|||
TransactionMQProducer txProducer = createTransactionMQProducer(txProducerGroup, transactionListener, executorService, rpcHook); |
|||
try { |
|||
txProducer.start(); |
|||
cache.put(txProducerGroup, txProducer); |
|||
} catch (MQClientException e) { |
|||
throw RocketMQUtil.convert(e); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
private TransactionMQProducer createTransactionMQProducer(String name, |
|||
RocketMQLocalTransactionListener transactionListener, |
|||
ExecutorService executorService, RPCHook rpcHook) { |
|||
Assert.notNull(producer, "Property 'producer' is required"); |
|||
Assert.notNull(transactionListener, "Parameter 'transactionListener' is required"); |
|||
TransactionMQProducer txProducer; |
|||
if (Objects.nonNull(rpcHook)) { |
|||
txProducer = new TransactionMQProducer(name, rpcHook); |
|||
txProducer.setVipChannelEnabled(false); |
|||
txProducer.setInstanceName(RocketMQUtil.getInstanceName(rpcHook, name)); |
|||
} else { |
|||
txProducer = new TransactionMQProducer(name); |
|||
} |
|||
txProducer.setTransactionListener(RocketMQUtil.convert(transactionListener)); |
|||
|
|||
txProducer.setNamesrvAddr(producer.getNamesrvAddr()); |
|||
if (executorService != null) { |
|||
txProducer.setExecutorService(executorService); |
|||
} |
|||
|
|||
txProducer.setSendMsgTimeout(producer.getSendMsgTimeout()); |
|||
txProducer.setRetryTimesWhenSendFailed(producer.getRetryTimesWhenSendFailed()); |
|||
txProducer.setRetryTimesWhenSendAsyncFailed(producer.getRetryTimesWhenSendAsyncFailed()); |
|||
txProducer.setMaxMessageSize(producer.getMaxMessageSize()); |
|||
txProducer.setCompressMsgBodyOverHowmuch(producer.getCompressMsgBodyOverHowmuch()); |
|||
txProducer.setRetryAnotherBrokerWhenNotStoreOK(producer.isRetryAnotherBrokerWhenNotStoreOK()); |
|||
|
|||
return txProducer; |
|||
} |
|||
} |
|||
@ -0,0 +1,494 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.support; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.apache.rocketmq.client.AccessChannel; |
|||
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; |
|||
import org.apache.rocketmq.client.consumer.MessageSelector; |
|||
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; |
|||
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; |
|||
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext; |
|||
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus; |
|||
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; |
|||
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly; |
|||
import org.apache.rocketmq.client.consumer.rebalance.AllocateMessageQueueAveragely; |
|||
import org.apache.rocketmq.client.exception.MQClientException; |
|||
import org.apache.rocketmq.common.message.MessageExt; |
|||
import org.apache.rocketmq.remoting.RPCHook; |
|||
import org.apache.rocketmq.spring.annotation.ConsumeMode; |
|||
import org.apache.rocketmq.spring.annotation.MessageModel; |
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.annotation.SelectorType; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.aop.framework.AopProxyUtils; |
|||
import org.springframework.beans.BeansException; |
|||
import org.springframework.beans.factory.InitializingBean; |
|||
import org.springframework.context.ApplicationContext; |
|||
import org.springframework.context.ApplicationContextAware; |
|||
import org.springframework.context.SmartLifecycle; |
|||
import org.springframework.util.Assert; |
|||
|
|||
import java.lang.reflect.ParameterizedType; |
|||
import java.lang.reflect.Type; |
|||
import java.nio.charset.Charset; |
|||
import java.util.List; |
|||
import java.util.Objects; |
|||
|
|||
@SuppressWarnings("WeakerAccess") |
|||
public class DefaultRocketMQListenerContainer implements InitializingBean, |
|||
RocketMQListenerContainer, SmartLifecycle, ApplicationContextAware { |
|||
private final static Logger log = LoggerFactory.getLogger(DefaultRocketMQListenerContainer.class); |
|||
|
|||
private ApplicationContext applicationContext; |
|||
|
|||
/** |
|||
* The name of the DefaultRocketMQListenerContainer instance |
|||
*/ |
|||
private String name; |
|||
|
|||
private long suspendCurrentQueueTimeMillis = 1000; |
|||
|
|||
/** |
|||
* Message consume retry strategy<br> -1,no retry,put into DLQ directly<br> 0,broker control retry frequency<br> |
|||
* >0,client control retry frequency. |
|||
*/ |
|||
private int delayLevelWhenNextConsume = 0; |
|||
|
|||
private String nameServer; |
|||
|
|||
private AccessChannel accessChannel = AccessChannel.LOCAL; |
|||
|
|||
private String consumerGroup; |
|||
|
|||
private String topic; |
|||
|
|||
private int consumeThreadMax = 64; |
|||
|
|||
private String charset = "UTF-8"; |
|||
|
|||
private ObjectMapper objectMapper; |
|||
|
|||
private RocketMQListener rocketMQListener; |
|||
|
|||
private RocketMQMessageListener rocketMQMessageListener; |
|||
|
|||
private DefaultMQPushConsumer consumer; |
|||
|
|||
private Class messageType; |
|||
|
|||
private boolean running; |
|||
|
|||
// The following properties came from @RocketMQMessageListener.
|
|||
private ConsumeMode consumeMode; |
|||
private SelectorType selectorType; |
|||
private String selectorExpression; |
|||
private MessageModel messageModel; |
|||
private long consumeTimeout; |
|||
|
|||
public long getSuspendCurrentQueueTimeMillis() { |
|||
return suspendCurrentQueueTimeMillis; |
|||
} |
|||
|
|||
public void setSuspendCurrentQueueTimeMillis(long suspendCurrentQueueTimeMillis) { |
|||
this.suspendCurrentQueueTimeMillis = suspendCurrentQueueTimeMillis; |
|||
} |
|||
|
|||
public int getDelayLevelWhenNextConsume() { |
|||
return delayLevelWhenNextConsume; |
|||
} |
|||
|
|||
public void setDelayLevelWhenNextConsume(int delayLevelWhenNextConsume) { |
|||
this.delayLevelWhenNextConsume = delayLevelWhenNextConsume; |
|||
} |
|||
|
|||
public String getNameServer() { |
|||
return nameServer; |
|||
} |
|||
|
|||
public void setNameServer(String nameServer) { |
|||
this.nameServer = nameServer; |
|||
} |
|||
|
|||
public AccessChannel getAccessChannel() { |
|||
return accessChannel; |
|||
} |
|||
|
|||
public void setAccessChannel(AccessChannel accessChannel) { |
|||
this.accessChannel = accessChannel; |
|||
} |
|||
|
|||
public String getConsumerGroup() { |
|||
return consumerGroup; |
|||
} |
|||
|
|||
public void setConsumerGroup(String consumerGroup) { |
|||
this.consumerGroup = consumerGroup; |
|||
} |
|||
|
|||
public String getTopic() { |
|||
return topic; |
|||
} |
|||
|
|||
public void setTopic(String topic) { |
|||
this.topic = topic; |
|||
} |
|||
|
|||
public int getConsumeThreadMax() { |
|||
return consumeThreadMax; |
|||
} |
|||
|
|||
public String getCharset() { |
|||
return charset; |
|||
} |
|||
|
|||
public void setCharset(String charset) { |
|||
this.charset = charset; |
|||
} |
|||
|
|||
public ObjectMapper getObjectMapper() { |
|||
return objectMapper; |
|||
} |
|||
|
|||
public void setObjectMapper(ObjectMapper objectMapper) { |
|||
this.objectMapper = objectMapper; |
|||
} |
|||
|
|||
|
|||
public RocketMQListener getRocketMQListener() { |
|||
return rocketMQListener; |
|||
} |
|||
|
|||
public void setRocketMQListener(RocketMQListener rocketMQListener) { |
|||
this.rocketMQListener = rocketMQListener; |
|||
} |
|||
|
|||
public RocketMQMessageListener getRocketMQMessageListener() { |
|||
return rocketMQMessageListener; |
|||
} |
|||
|
|||
public void setRocketMQMessageListener(RocketMQMessageListener anno) { |
|||
this.rocketMQMessageListener = anno; |
|||
|
|||
this.consumeMode = anno.consumeMode(); |
|||
this.consumeThreadMax = anno.consumeThreadMax(); |
|||
this.messageModel = anno.messageModel(); |
|||
this.selectorExpression = anno.selectorExpression(); |
|||
this.selectorType = anno.selectorType(); |
|||
this.consumeTimeout = anno.consumeTimeout(); |
|||
} |
|||
|
|||
public ConsumeMode getConsumeMode() { |
|||
return consumeMode; |
|||
} |
|||
|
|||
public SelectorType getSelectorType() { |
|||
return selectorType; |
|||
} |
|||
|
|||
public String getSelectorExpression() { |
|||
return selectorExpression; |
|||
} |
|||
|
|||
public MessageModel getMessageModel() { |
|||
return messageModel; |
|||
} |
|||
|
|||
public DefaultMQPushConsumer getConsumer() { |
|||
return consumer; |
|||
} |
|||
|
|||
public void setConsumer(DefaultMQPushConsumer consumer) { |
|||
this.consumer = consumer; |
|||
} |
|||
|
|||
@Override |
|||
public void setupMessageListener(RocketMQListener rocketMQListener) { |
|||
this.rocketMQListener = rocketMQListener; |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
this.setRunning(false); |
|||
if (Objects.nonNull(consumer)) { |
|||
consumer.shutdown(); |
|||
} |
|||
log.info("container destroyed, {}", this.toString()); |
|||
} |
|||
|
|||
@Override |
|||
public boolean isAutoStartup() { |
|||
return true; |
|||
} |
|||
|
|||
@Override |
|||
public void stop(Runnable callback) { |
|||
stop(); |
|||
callback.run(); |
|||
} |
|||
|
|||
@Override |
|||
public void start() { |
|||
if (this.isRunning()) { |
|||
throw new IllegalStateException("container already running. " + this.toString()); |
|||
} |
|||
|
|||
try { |
|||
consumer.start(); |
|||
} catch (MQClientException e) { |
|||
throw new IllegalStateException("Failed to start RocketMQ push consumer", e); |
|||
} |
|||
this.setRunning(true); |
|||
|
|||
log.info("running container: {}", this.toString()); |
|||
} |
|||
|
|||
@Override |
|||
public void stop() { |
|||
if (this.isRunning()) { |
|||
if (Objects.nonNull(consumer)) { |
|||
consumer.shutdown(); |
|||
} |
|||
setRunning(false); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public boolean isRunning() { |
|||
return running; |
|||
} |
|||
|
|||
private void setRunning(boolean running) { |
|||
this.running = running; |
|||
} |
|||
|
|||
@Override |
|||
public int getPhase() { |
|||
// Returning Integer.MAX_VALUE only suggests that
|
|||
// we will be the first bean to shutdown and last bean to start
|
|||
return Integer.MAX_VALUE; |
|||
} |
|||
|
|||
|
|||
@Override |
|||
public void afterPropertiesSet() throws Exception { |
|||
initRocketMQPushConsumer(); |
|||
|
|||
this.messageType = getMessageType(); |
|||
log.debug("RocketMQ messageType: {}", messageType.getName()); |
|||
} |
|||
|
|||
@Override |
|||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { |
|||
this.applicationContext = applicationContext; |
|||
} |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return "DefaultRocketMQListenerContainer{" + |
|||
"consumerGroup='" + consumerGroup + '\'' + |
|||
", nameServer='" + nameServer + '\'' + |
|||
", topic='" + topic + '\'' + |
|||
", consumeMode=" + consumeMode + |
|||
", selectorType=" + selectorType + |
|||
", selectorExpression='" + selectorExpression + '\'' + |
|||
", messageModel=" + messageModel + |
|||
'}'; |
|||
} |
|||
|
|||
public void setName(String name) { |
|||
this.name = name; |
|||
} |
|||
|
|||
public class DefaultMessageListenerConcurrently implements MessageListenerConcurrently { |
|||
|
|||
@SuppressWarnings("unchecked") |
|||
@Override |
|||
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { |
|||
for (MessageExt messageExt : msgs) { |
|||
log.debug("received msg: {}", messageExt); |
|||
try { |
|||
long now = System.currentTimeMillis(); |
|||
rocketMQListener.onMessage(doConvertMessage(messageExt)); |
|||
long costTime = System.currentTimeMillis() - now; |
|||
log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime); |
|||
} catch (Exception e) { |
|||
log.warn("consume message failed. messageExt:{}", messageExt, e); |
|||
context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume); |
|||
return ConsumeConcurrentlyStatus.RECONSUME_LATER; |
|||
} |
|||
} |
|||
|
|||
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; |
|||
} |
|||
} |
|||
|
|||
public class DefaultMessageListenerOrderly implements MessageListenerOrderly { |
|||
|
|||
@SuppressWarnings("unchecked") |
|||
@Override |
|||
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) { |
|||
for (MessageExt messageExt : msgs) { |
|||
log.debug("received msg: {}", messageExt); |
|||
try { |
|||
long now = System.currentTimeMillis(); |
|||
rocketMQListener.onMessage(doConvertMessage(messageExt)); |
|||
long costTime = System.currentTimeMillis() - now; |
|||
log.info("consume {} cost: {} ms", messageExt.getMsgId(), costTime); |
|||
} catch (Exception e) { |
|||
log.warn("consume message failed. messageExt:{}", messageExt, e); |
|||
context.setSuspendCurrentQueueTimeMillis(suspendCurrentQueueTimeMillis); |
|||
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT; |
|||
} |
|||
} |
|||
|
|||
return ConsumeOrderlyStatus.SUCCESS; |
|||
} |
|||
} |
|||
|
|||
|
|||
@SuppressWarnings("unchecked") |
|||
private Object doConvertMessage(MessageExt messageExt) { |
|||
if (Objects.equals(messageType, MessageExt.class)) { |
|||
return messageExt; |
|||
} else { |
|||
String str = new String(messageExt.getBody(), Charset.forName(charset)); |
|||
if (Objects.equals(messageType, String.class)) { |
|||
return str; |
|||
} else { |
|||
// If msgType not string, use objectMapper change it.
|
|||
try { |
|||
return objectMapper.readValue(str, messageType); |
|||
} catch (Exception e) { |
|||
log.info("convert failed. str:{}, msgType:{}", str, messageType); |
|||
throw new RuntimeException("cannot convert message to " + messageType, e); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
private Class getMessageType() { |
|||
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(rocketMQListener); |
|||
Type[] interfaces = targetClass.getGenericInterfaces(); |
|||
Class<?> superclass = targetClass.getSuperclass(); |
|||
while ((Objects.isNull(interfaces) || 0 == interfaces.length) && Objects.nonNull(superclass)) { |
|||
interfaces = superclass.getGenericInterfaces(); |
|||
superclass = targetClass.getSuperclass(); |
|||
} |
|||
if (Objects.nonNull(interfaces)) { |
|||
for (Type type : interfaces) { |
|||
if (type instanceof ParameterizedType) { |
|||
ParameterizedType parameterizedType = (ParameterizedType) type; |
|||
if (Objects.equals(parameterizedType.getRawType(), RocketMQListener.class)) { |
|||
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); |
|||
if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) { |
|||
return (Class) actualTypeArguments[0]; |
|||
} else { |
|||
return Object.class; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
return Object.class; |
|||
} else { |
|||
return Object.class; |
|||
} |
|||
} |
|||
|
|||
private void initRocketMQPushConsumer() throws MQClientException { |
|||
Assert.notNull(rocketMQListener, "Property 'rocketMQListener' is required"); |
|||
Assert.notNull(consumerGroup, "Property 'consumerGroup' is required"); |
|||
Assert.notNull(nameServer, "Property 'nameServer' is required"); |
|||
Assert.notNull(topic, "Property 'topic' is required"); |
|||
|
|||
RPCHook rpcHook = RocketMQUtil.getRPCHookByAkSk(applicationContext.getEnvironment(), |
|||
this.rocketMQMessageListener.accessKey(), this.rocketMQMessageListener.secretKey()); |
|||
boolean enableMsgTrace = rocketMQMessageListener.enableMsgTrace(); |
|||
if (Objects.nonNull(rpcHook)) { |
|||
consumer = new DefaultMQPushConsumer(consumerGroup, rpcHook, new AllocateMessageQueueAveragely(), |
|||
enableMsgTrace, this.applicationContext.getEnvironment(). |
|||
resolveRequiredPlaceholders(this.rocketMQMessageListener.customizedTraceTopic())); |
|||
consumer.setVipChannelEnabled(false); |
|||
consumer.setInstanceName(RocketMQUtil.getInstanceName(rpcHook, consumerGroup)); |
|||
} else { |
|||
log.debug("Access-key or secret-key not configure in " + this + "."); |
|||
consumer = new DefaultMQPushConsumer(consumerGroup, enableMsgTrace, |
|||
this.applicationContext.getEnvironment(). |
|||
resolveRequiredPlaceholders(this.rocketMQMessageListener.customizedTraceTopic())); |
|||
} |
|||
|
|||
String customizedNameServer = this.applicationContext.getEnvironment().resolveRequiredPlaceholders(this.rocketMQMessageListener.nameServer()); |
|||
if (customizedNameServer != null) { |
|||
consumer.setNamesrvAddr(customizedNameServer); |
|||
} else { |
|||
consumer.setNamesrvAddr(nameServer); |
|||
} |
|||
if (accessChannel != null) { |
|||
consumer.setAccessChannel(accessChannel); |
|||
} |
|||
consumer.setConsumeThreadMax(consumeThreadMax); |
|||
if (consumeThreadMax < consumer.getConsumeThreadMin()) { |
|||
consumer.setConsumeThreadMin(consumeThreadMax); |
|||
} |
|||
consumer.setConsumeTimeout(consumeTimeout); |
|||
consumer.setInstanceName(this.name); |
|||
|
|||
switch (messageModel) { |
|||
case BROADCASTING: |
|||
consumer.setMessageModel(org.apache.rocketmq.common.protocol.heartbeat.MessageModel.BROADCASTING); |
|||
break; |
|||
case CLUSTERING: |
|||
consumer.setMessageModel(org.apache.rocketmq.common.protocol.heartbeat.MessageModel.CLUSTERING); |
|||
break; |
|||
default: |
|||
throw new IllegalArgumentException("Property 'messageModel' was wrong."); |
|||
} |
|||
|
|||
switch (selectorType) { |
|||
case TAG: |
|||
consumer.subscribe(topic, selectorExpression); |
|||
break; |
|||
case SQL92: |
|||
consumer.subscribe(topic, MessageSelector.bySql(selectorExpression)); |
|||
break; |
|||
default: |
|||
throw new IllegalArgumentException("Property 'selectorType' was wrong."); |
|||
} |
|||
|
|||
switch (consumeMode) { |
|||
case ORDERLY: |
|||
consumer.setMessageListener(new DefaultMessageListenerOrderly()); |
|||
break; |
|||
case CONCURRENTLY: |
|||
consumer.setMessageListener(new DefaultMessageListenerConcurrently()); |
|||
break; |
|||
default: |
|||
throw new IllegalArgumentException("Property 'consumeMode' was wrong."); |
|||
} |
|||
|
|||
if (rocketMQListener instanceof RocketMQPushConsumerLifecycleListener) { |
|||
((RocketMQPushConsumerLifecycleListener) rocketMQListener).prepareStart(consumer); |
|||
} |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.support; |
|||
|
|||
public interface RocketMQConsumerLifecycleListener<T> { |
|||
void prepareStart(final T consumer); |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.apache.rocketmq.spring.support; |
|||
|
|||
/** |
|||
* Represents the RocketMQ message protocol that is used during the data exchange. |
|||
*/ |
|||
public class RocketMQHeaders { |
|||
public static final String PREFIX = "rocketmq_"; |
|||
public static final String KEYS = "KEYS"; |
|||
public static final String TAGS = "TAGS"; |
|||
public static final String TOPIC = "TOPIC"; |
|||
public static final String MESSAGE_ID = "MESSAGE_ID"; |
|||
public static final String BORN_TIMESTAMP = "BORN_TIMESTAMP"; |
|||
public static final String BORN_HOST = "BORN_HOST"; |
|||
public static final String FLAG = "FLAG"; |
|||
public static final String QUEUE_ID = "QUEUE_ID"; |
|||
public static final String SYS_FLAG = "SYS_FLAG"; |
|||
public static final String TRANSACTION_ID = "TRANSACTION_ID"; |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.support; |
|||
|
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.springframework.beans.factory.DisposableBean; |
|||
|
|||
public interface RocketMQListenerContainer extends DisposableBean { |
|||
|
|||
/** |
|||
* Setup the message listener to use. Throws an {@link IllegalArgumentException} if that message listener type is |
|||
* not supported. |
|||
*/ |
|||
void setupMessageListener(RocketMQListener<?> messageListener); |
|||
} |
|||
@ -0,0 +1,215 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.apache.rocketmq.spring.support; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.apache.rocketmq.acl.common.AclClientRPCHook; |
|||
import org.apache.rocketmq.acl.common.SessionCredentials; |
|||
import org.apache.rocketmq.client.exception.MQClientException; |
|||
import org.apache.rocketmq.client.producer.LocalTransactionState; |
|||
import org.apache.rocketmq.client.producer.TransactionListener; |
|||
import org.apache.rocketmq.common.message.Message; |
|||
import org.apache.rocketmq.common.message.MessageConst; |
|||
import org.apache.rocketmq.common.message.MessageExt; |
|||
import org.apache.rocketmq.remoting.RPCHook; |
|||
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; |
|||
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; |
|||
import org.springframework.core.env.Environment; |
|||
import org.springframework.messaging.MessageHeaders; |
|||
import org.springframework.messaging.MessagingException; |
|||
import org.springframework.messaging.support.MessageBuilder; |
|||
import org.springframework.util.CollectionUtils; |
|||
import org.springframework.util.StringUtils; |
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import java.nio.charset.Charset; |
|||
import java.util.Map; |
|||
import java.util.Objects; |
|||
|
|||
public class RocketMQUtil { |
|||
private final static Logger log = LoggerFactory.getLogger(RocketMQUtil.class); |
|||
|
|||
public static TransactionListener convert(RocketMQLocalTransactionListener listener) { |
|||
return new TransactionListener() { |
|||
@Override |
|||
public LocalTransactionState executeLocalTransaction(Message message, Object obj) { |
|||
RocketMQLocalTransactionState state = listener.executeLocalTransaction(convertToSpringMessage(message), obj); |
|||
return convertLocalTransactionState(state); |
|||
} |
|||
|
|||
@Override |
|||
public LocalTransactionState checkLocalTransaction(MessageExt messageExt) { |
|||
RocketMQLocalTransactionState state = listener.checkLocalTransaction(convertToSpringMessage(messageExt)); |
|||
return convertLocalTransactionState(state); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
private static LocalTransactionState convertLocalTransactionState(RocketMQLocalTransactionState state) { |
|||
switch (state) { |
|||
case UNKNOWN: |
|||
return LocalTransactionState.UNKNOW; |
|||
case COMMIT: |
|||
return LocalTransactionState.COMMIT_MESSAGE; |
|||
case ROLLBACK: |
|||
return LocalTransactionState.ROLLBACK_MESSAGE; |
|||
} |
|||
|
|||
// Never happen
|
|||
log.warn("Failed to covert enum type RocketMQLocalTransactionState.%s", state); |
|||
return LocalTransactionState.UNKNOW; |
|||
} |
|||
|
|||
public static MessagingException convert(MQClientException e) { |
|||
return new MessagingException(e.getErrorMessage(), e); |
|||
} |
|||
|
|||
public static org.springframework.messaging.Message convertToSpringMessage( |
|||
MessageExt message) { |
|||
MessageBuilder messageBuilder = |
|||
MessageBuilder.withPayload(message.getBody()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.KEYS), message.getKeys()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.TAGS), message.getTags()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.TOPIC), message.getTopic()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.MESSAGE_ID), message.getMsgId()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.BORN_TIMESTAMP), message.getBornTimestamp()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.BORN_HOST), message.getBornHostString()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.FLAG), message.getFlag()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.QUEUE_ID), message.getQueueId()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.SYS_FLAG), message.getSysFlag()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.TRANSACTION_ID), message.getTransactionId()); |
|||
addUserProperties(message.getProperties(), messageBuilder); |
|||
return messageBuilder.build(); |
|||
} |
|||
|
|||
public static String toRocketHeaderKey(String rawKey) { |
|||
return RocketMQHeaders.PREFIX + rawKey; |
|||
} |
|||
|
|||
private static void addUserProperties(Map<String, String> properties, MessageBuilder messageBuilder) { |
|||
if (!CollectionUtils.isEmpty(properties)) { |
|||
properties.forEach((key, val) -> { |
|||
if (!MessageConst.STRING_HASH_SET.contains(key) && !MessageHeaders.ID.equals(key) |
|||
&& !MessageHeaders.TIMESTAMP.equals(key)) { |
|||
messageBuilder.setHeader(key, val); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public static org.springframework.messaging.Message convertToSpringMessage( |
|||
Message message) { |
|||
MessageBuilder messageBuilder = |
|||
MessageBuilder.withPayload(message.getBody()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.KEYS), message.getKeys()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.TAGS), message.getTags()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.TOPIC), message.getTopic()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.FLAG), message.getFlag()). |
|||
setHeader(toRocketHeaderKey(RocketMQHeaders.TRANSACTION_ID), message.getTransactionId()); |
|||
addUserProperties(message.getProperties(), messageBuilder); |
|||
return messageBuilder.build(); |
|||
} |
|||
|
|||
public static Message convertToRocketMessage( |
|||
ObjectMapper objectMapper, String charset, |
|||
String destination, org.springframework.messaging.Message<?> message) { |
|||
Object payloadObj = message.getPayload(); |
|||
byte[] payloads; |
|||
|
|||
if (payloadObj instanceof String) { |
|||
payloads = ((String) payloadObj).getBytes(Charset.forName(charset)); |
|||
} else if (payloadObj instanceof byte[]) { |
|||
payloads = (byte[]) message.getPayload(); |
|||
} else { |
|||
try { |
|||
String jsonObj = objectMapper.writeValueAsString(payloadObj); |
|||
payloads = jsonObj.getBytes(Charset.forName(charset)); |
|||
} catch (Exception e) { |
|||
throw new RuntimeException("convert to RocketMQ message failed.", e); |
|||
} |
|||
} |
|||
|
|||
String[] tempArr = destination.split(":", 2); |
|||
String topic = tempArr[0]; |
|||
String tags = ""; |
|||
if (tempArr.length > 1) { |
|||
tags = tempArr[1]; |
|||
} |
|||
|
|||
Message rocketMsg = new Message(topic, tags, payloads); |
|||
|
|||
MessageHeaders headers = message.getHeaders(); |
|||
if (Objects.nonNull(headers) && !headers.isEmpty()) { |
|||
Object keys = headers.get(RocketMQHeaders.KEYS); |
|||
if (!StringUtils.isEmpty(keys)) { // if headers has 'KEYS', set rocketMQ message key
|
|||
rocketMsg.setKeys(keys.toString()); |
|||
} |
|||
|
|||
Object flagObj = headers.getOrDefault("FLAG", "0"); |
|||
int flag = 0; |
|||
try { |
|||
flag = Integer.parseInt(flagObj.toString()); |
|||
} catch (NumberFormatException e) { |
|||
// Ignore it
|
|||
log.info("flag must be integer, flagObj:{}", flagObj); |
|||
} |
|||
rocketMsg.setFlag(flag); |
|||
|
|||
Object waitStoreMsgOkObj = headers.getOrDefault("WAIT_STORE_MSG_OK", "true"); |
|||
boolean waitStoreMsgOK = Boolean.TRUE.equals(waitStoreMsgOkObj); |
|||
rocketMsg.setWaitStoreMsgOK(waitStoreMsgOK); |
|||
|
|||
headers.entrySet().stream() |
|||
.filter(entry -> !Objects.equals(entry.getKey(), "FLAG") |
|||
&& !Objects.equals(entry.getKey(), "WAIT_STORE_MSG_OK")) // exclude "FLAG", "WAIT_STORE_MSG_OK"
|
|||
.forEach(entry -> { |
|||
if (!MessageConst.STRING_HASH_SET.contains(entry.getKey())) { |
|||
rocketMsg.putUserProperty(entry.getKey(), String.valueOf(entry.getValue())); |
|||
} |
|||
}); |
|||
|
|||
} |
|||
|
|||
return rocketMsg; |
|||
} |
|||
|
|||
public static RPCHook getRPCHookByAkSk(Environment env, String accessKeyOrExpr, String secretKeyOrExpr) { |
|||
String ak, sk; |
|||
try { |
|||
ak = env.resolveRequiredPlaceholders(accessKeyOrExpr); |
|||
sk = env.resolveRequiredPlaceholders(secretKeyOrExpr); |
|||
} catch (Exception e) { |
|||
// Ignore it
|
|||
ak = null; |
|||
sk = null; |
|||
} |
|||
if (!StringUtils.isEmpty(ak) && !StringUtils.isEmpty(sk)) { |
|||
return new AclClientRPCHook(new SessionCredentials(ak, sk)); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public static String getInstanceName(RPCHook rpcHook, String identify) { |
|||
String separator = "|"; |
|||
StringBuilder instanceName = new StringBuilder(); |
|||
SessionCredentials sessionCredentials = ((AclClientRPCHook) rpcHook).getSessionCredentials(); |
|||
instanceName.append(sessionCredentials.getAccessKey()) |
|||
.append(separator).append(sessionCredentials.getSecretKey()) |
|||
.append(separator).append(identify); |
|||
return instanceName.toString(); |
|||
} |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ |
|||
org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration |
|||
@ -0,0 +1,221 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
package org.apache.rocketmq.spring.autoconfigure; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.apache.rocketmq.client.producer.DefaultMQProducer; |
|||
import org.apache.rocketmq.spring.annotation.ExtRocketMQTemplateConfiguration; |
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQTemplate; |
|||
import org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer; |
|||
import org.junit.Assert; |
|||
import org.junit.Test; |
|||
import org.springframework.beans.factory.NoSuchBeanDefinitionException; |
|||
import org.springframework.beans.factory.support.BeanDefinitionValidationException; |
|||
import org.springframework.boot.autoconfigure.AutoConfigurations; |
|||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext; |
|||
import org.springframework.boot.test.context.runner.ApplicationContextRunner; |
|||
import org.springframework.boot.test.context.runner.ContextConsumer; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
|
|||
public class RocketMQAutoConfigurationTest { |
|||
private ApplicationContextRunner runner = new ApplicationContextRunner() |
|||
.withConfiguration(AutoConfigurations.of(RocketMQAutoConfiguration.class)); |
|||
|
|||
|
|||
@Test(expected = NoSuchBeanDefinitionException.class) |
|||
public void testDefaultMQProducerNotCreatedByDefault() { |
|||
// You will see the WARN log message about missing rocketmq.name-server spring property when running this test case.
|
|||
runner.run(context -> context.getBean(DefaultMQProducer.class)); |
|||
} |
|||
|
|||
|
|||
@Test |
|||
public void testDefaultMQProducerWithRelaxPropertyName() { |
|||
runner.withPropertyValues("rocketmq.nameServer=127.0.0.1:9876", |
|||
"rocketmq.producer.group=spring_rocketmq", |
|||
"rocketmq.accessChannel=LOCAL"). |
|||
run((context) -> { |
|||
assertThat(context).hasSingleBean(DefaultMQProducer.class); |
|||
assertThat(context).hasSingleBean(RocketMQProperties.class); |
|||
}); |
|||
|
|||
} |
|||
|
|||
@Test |
|||
public void testBadAccessChannelProperty() { |
|||
runner.withPropertyValues("rocketmq.nameServer=127.0.0.1:9876", |
|||
"rocketmq.producer.group=spring_rocketmq", |
|||
"rocketmq.accessChannel=LOCAL123"). |
|||
run((context) -> { |
|||
//Should throw exception for bad accessChannel property
|
|||
assertThat(context).getFailure(); |
|||
}); |
|||
} |
|||
|
|||
@Test |
|||
public void testDefaultMQProducer() { |
|||
runner.withPropertyValues("rocketmq.name-server=127.0.0.1:9876", |
|||
"rocketmq.producer.group=spring_rocketmq"). |
|||
run((context) -> { |
|||
assertThat(context).hasSingleBean(DefaultMQProducer.class); |
|||
}); |
|||
|
|||
} |
|||
|
|||
@Test |
|||
public void testRocketMQListenerContainer() { |
|||
runner.withPropertyValues("rocketmq.name-server=127.0.0.1:9876"). |
|||
withUserConfiguration(TestConfig.class). |
|||
run((context) -> { |
|||
// No producer on consume side
|
|||
assertThat(context).doesNotHaveBean(DefaultMQProducer.class); |
|||
// Auto-create consume container if existing Bean annotated with @RocketMQMessageListener
|
|||
assertThat(context).hasBean("org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer_1"); |
|||
assertThat(context).hasBean("org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer_2"); |
|||
assertThat(context).getBean("org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer_1"). |
|||
hasFieldOrPropertyWithValue("nameServer", "127.0.0.1:9876"); |
|||
assertThat(context).getBean("org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer_2"). |
|||
hasFieldOrPropertyWithValue("nameServer", "127.0.1.1:9876"); |
|||
}); |
|||
|
|||
} |
|||
|
|||
@Test |
|||
public void testRocketMQListenerWithCustomObjectMapper() { |
|||
runner.withPropertyValues("rocketmq.name-server=127.0.0.1:9876"). |
|||
withUserConfiguration(TestConfig.class, CustomObjectMapperConfig.class). |
|||
run((context) -> { |
|||
assertThat(context.getBean("org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer_1", |
|||
DefaultRocketMQListenerContainer.class).getObjectMapper()) |
|||
.isSameAs(context.getBean(CustomObjectMapperConfig.class).testObjectMapper()); |
|||
}); |
|||
} |
|||
|
|||
@Test |
|||
public void testRocketMQListenerWithSeveralObjectMappers() { |
|||
runner.withPropertyValues("rocketmq.name-server=127.0.0.1:9876"). |
|||
withUserConfiguration(TestConfig.class, CustomObjectMappersConfig.class). |
|||
run((context) -> { |
|||
assertThat(context.getBean("org.apache.rocketmq.spring.support.DefaultRocketMQListenerContainer_1", |
|||
DefaultRocketMQListenerContainer.class).getObjectMapper()) |
|||
.isSameAs(context.getBean(CustomObjectMappersConfig.class).rocketMQMessageObjectMapper()); |
|||
}); |
|||
} |
|||
|
|||
|
|||
@Test |
|||
public void testExtRocketMQTemplate() { |
|||
runner.withPropertyValues("rocketmq.name-server=127.0.0.1:9876"). |
|||
withUserConfiguration(ExtRocketMQTemplateConfig.class, CustomObjectMappersConfig.class). |
|||
run(new ContextConsumer<AssertableApplicationContext>() { |
|||
@Override |
|||
public void accept(AssertableApplicationContext context) throws Throwable { |
|||
Throwable th = context.getStartupFailure(); |
|||
System.out.printf("th==" + th + "\n"); |
|||
Assert.assertTrue(th instanceof BeanDefinitionValidationException); |
|||
} |
|||
}); |
|||
|
|||
runner.withPropertyValues("rocketmq.name-server=127.0.1.1:9876"). |
|||
withUserConfiguration(ExtRocketMQTemplateConfig.class, CustomObjectMappersConfig.class). |
|||
run((context) -> { |
|||
// No producer on consume side
|
|||
assertThat(context).getBean("extRocketMQTemplate").hasFieldOrProperty("producer"); |
|||
// Auto-create consume container if existing Bean annotated with @RocketMQMessageListener
|
|||
}); |
|||
} |
|||
|
|||
@Configuration |
|||
static class TestConfig { |
|||
|
|||
@Bean |
|||
public Object consumeListener() { |
|||
return new MyMessageListener(); |
|||
} |
|||
|
|||
@Bean |
|||
public Object consumeListener1() { |
|||
return new MyMessageListener1(); |
|||
} |
|||
|
|||
} |
|||
|
|||
@Configuration |
|||
static class CustomObjectMapperConfig { |
|||
|
|||
@Bean |
|||
public ObjectMapper testObjectMapper() { |
|||
return new ObjectMapper(); |
|||
} |
|||
|
|||
} |
|||
|
|||
@Configuration |
|||
static class CustomObjectMappersConfig { |
|||
|
|||
@Bean |
|||
public ObjectMapper testObjectMapper() { |
|||
return new ObjectMapper(); |
|||
} |
|||
|
|||
@Bean |
|||
public ObjectMapper rocketMQMessageObjectMapper() { |
|||
return new ObjectMapper(); |
|||
} |
|||
|
|||
} |
|||
|
|||
@RocketMQMessageListener(consumerGroup = "abc", topic = "test") |
|||
static class MyMessageListener implements RocketMQListener { |
|||
|
|||
@Override |
|||
public void onMessage(Object message) { |
|||
|
|||
} |
|||
} |
|||
|
|||
@RocketMQMessageListener(nameServer = "127.0.1.1:9876", consumerGroup = "abc1", topic = "test") |
|||
static class MyMessageListener1 implements RocketMQListener { |
|||
|
|||
@Override |
|||
public void onMessage(Object message) { |
|||
|
|||
} |
|||
} |
|||
|
|||
@Configuration |
|||
static class ExtRocketMQTemplateConfig { |
|||
|
|||
@Bean |
|||
public RocketMQTemplate extRocketMQTemplate() { |
|||
return new MyExtRocketMQTemplate(); |
|||
} |
|||
|
|||
} |
|||
|
|||
@ExtRocketMQTemplateConfiguration(group = "test", nameServer = "127.0.0.1:9876") |
|||
static class MyExtRocketMQTemplate extends RocketMQTemplate { |
|||
|
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,52 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.apache.rocketmq.spring.support; |
|||
|
|||
import org.apache.rocketmq.common.message.MessageExt; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.junit.Test; |
|||
import java.lang.reflect.InvocationTargetException; |
|||
import java.lang.reflect.Method; |
|||
|
|||
import static org.assertj.core.api.Assertions.assertThat; |
|||
|
|||
public class DefaultRocketMQListenerContainerTest { |
|||
@Test |
|||
public void testGetMessageType() throws Exception { |
|||
DefaultRocketMQListenerContainer listenerContainer = new DefaultRocketMQListenerContainer(); |
|||
Method getMessageType = DefaultRocketMQListenerContainer.class.getDeclaredMethod("getMessageType"); |
|||
getMessageType.setAccessible(true); |
|||
|
|||
listenerContainer.setRocketMQListener(new RocketMQListener<String>() { |
|||
@Override |
|||
public void onMessage(String message) { |
|||
} |
|||
}); |
|||
Class result = (Class)getMessageType.invoke(listenerContainer); |
|||
assertThat(result.getName().equals(String.class.getName())); |
|||
|
|||
listenerContainer.setRocketMQListener(new RocketMQListener<MessageExt>() { |
|||
@Override |
|||
public void onMessage(MessageExt message) { |
|||
} |
|||
}); |
|||
result = (Class)getMessageType.invoke(listenerContainer); |
|||
assertThat(result.getName().equals(MessageExt.class.getName())); |
|||
} |
|||
} |
|||
|
|||
|
|||
@ -0,0 +1,93 @@ |
|||
/* |
|||
* Licensed to the Apache Software Foundation (ASF) under one or more |
|||
* contributor license agreements. See the NOTICE file distributed with |
|||
* this work for additional information regarding copyright ownership. |
|||
* The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
* (the "License"); you may not use this file except in compliance with |
|||
* the License. You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.apache.rocketmq.spring.support; |
|||
|
|||
import java.util.Arrays; |
|||
|
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.junit.Test; |
|||
import org.springframework.messaging.Message; |
|||
import org.springframework.messaging.support.MessageBuilder; |
|||
|
|||
import static org.junit.Assert.assertEquals; |
|||
import static org.junit.Assert.assertNull; |
|||
import static org.junit.Assert.assertTrue; |
|||
|
|||
public class RocketMQUtilTest { |
|||
|
|||
private ObjectMapper objectMapper = new ObjectMapper(); |
|||
|
|||
@Test |
|||
public void testMessageBuilder() { |
|||
Message msg = MessageBuilder.withPayload("test"). |
|||
setHeader("A", "test1"). |
|||
setHeader("B", "test2"). |
|||
build(); |
|||
System.out.printf("header size=%d %s %n", msg.getHeaders().size(), msg.getHeaders().toString()); |
|||
assertTrue(msg.getHeaders().get("A") != null); |
|||
assertTrue(msg.getHeaders().get("B") != null); |
|||
} |
|||
|
|||
@Test |
|||
public void testPayload() { |
|||
String charset = "UTF-8"; |
|||
String destination = "test-topic"; |
|||
Message msgWithStringPayload = MessageBuilder.withPayload("test").build(); |
|||
org.apache.rocketmq.common.message.Message rocketMsg1 = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
charset, destination, msgWithStringPayload); |
|||
|
|||
Message msgWithBytePayload = MessageBuilder.withPayload("test".getBytes()).build(); |
|||
org.apache.rocketmq.common.message.Message rocketMsg2 = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
charset, destination, msgWithBytePayload); |
|||
|
|||
assertTrue(Arrays.equals(((String)msgWithStringPayload.getPayload()).getBytes(), rocketMsg1.getBody())); |
|||
assertTrue(Arrays.equals((byte[])msgWithBytePayload.getPayload(), rocketMsg2.getBody())); |
|||
} |
|||
|
|||
@Test |
|||
public void testHeaderConvertToRMQMsg() { |
|||
Message msgWithStringPayload = MessageBuilder.withPayload("test body") |
|||
.setHeader("test", 1) |
|||
.setHeader(RocketMQHeaders.TAGS, "tags") |
|||
.setHeader(RocketMQHeaders.KEYS, "my_keys") |
|||
.build(); |
|||
org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
"UTF-8", "test-topic", msgWithStringPayload); |
|||
assertEquals(String.valueOf("1"), rocketMsg.getProperty("test")); |
|||
assertNull(rocketMsg.getProperty(RocketMQHeaders.TAGS)); |
|||
assertEquals("my_keys", rocketMsg.getProperty(RocketMQHeaders.KEYS)); |
|||
} |
|||
|
|||
@Test |
|||
public void testHeaderConvertToSpringMsg() { |
|||
org.apache.rocketmq.common.message.Message rmqMsg = new org.apache.rocketmq.common.message.Message(); |
|||
rmqMsg.setBody("test body".getBytes()); |
|||
rmqMsg.setTopic("test-topic"); |
|||
rmqMsg.putUserProperty("test", "1"); |
|||
rmqMsg.setTags("tags"); |
|||
Message springMsg = RocketMQUtil.convertToSpringMessage(rmqMsg); |
|||
assertEquals(String.valueOf("1"), springMsg.getHeaders().get("test")); |
|||
assertEquals("tags", springMsg.getHeaders().get(RocketMQHeaders.PREFIX + RocketMQHeaders.TAGS)); |
|||
|
|||
org.apache.rocketmq.common.message.Message rocketMsg = RocketMQUtil.convertToRocketMessage(objectMapper, |
|||
"UTF-8", "test-topic", springMsg); |
|||
assertEquals(String.valueOf("1"), rocketMsg.getProperty("test")); |
|||
assertEquals(String.valueOf("tags"), rocketMsg.getProperty(RocketMQHeaders.PREFIX + RocketMQHeaders.TAGS)); |
|||
assertNull(rocketMsg.getTags()); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
<!-- |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
--> |
|||
|
|||
<component name="CopyrightManager"> |
|||
<copyright> |
|||
<option name="myName" value="Apache" /> |
|||
<option name="notice" value="Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License." /> |
|||
</copyright> |
|||
</component> |
|||
@ -0,0 +1,64 @@ |
|||
<!-- |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
--> |
|||
|
|||
<component name="CopyrightManager"> |
|||
<settings default="Apache"> |
|||
<module2copyright> |
|||
<element module="All" copyright="Apache"/> |
|||
</module2copyright> |
|||
<LanguageOptions name="GSP"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="HTML"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="JAVA"> |
|||
<option name="fileTypeOverride" value="3" /> |
|||
<option name="addBlankAfter" value="false" /> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="JSP"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="JSPX"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="MXML"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="Properties"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="block" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="SPI"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="block" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="XML"> |
|||
<option name="fileTypeOverride" value="3"/> |
|||
<option name="prefixLines" value="false"/> |
|||
</LanguageOptions> |
|||
<LanguageOptions name="__TEMPLATE__"> |
|||
<option name="separateBefore" value="true"/> |
|||
<option name="lenBefore" value="1"/> |
|||
</LanguageOptions> |
|||
</settings> |
|||
</component> |
|||
@ -0,0 +1,135 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!-- |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
--> |
|||
|
|||
<!DOCTYPE module PUBLIC |
|||
"-//Puppy Crawl//DTD Check Configuration 1.3//EN" |
|||
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd"> |
|||
<!--Refer http://checkstyle.sourceforge.net/reports/google-java-style.html#s2.2-file-encoding --> |
|||
<module name="Checker"> |
|||
|
|||
<property name="localeLanguage" value="en"/> |
|||
|
|||
<!--To configure the check to report on the first instance in each file--> |
|||
<module name="FileTabCharacter"/> |
|||
|
|||
<!-- header --> |
|||
<module name="RegexpHeader"> |
|||
<property name="header" value="/\*\nLicensed to the Apache Software Foundation*"/> |
|||
<property name="fileExtensions" value="java" /> |
|||
</module> |
|||
|
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="System\.out\.println"/> |
|||
<property name="message" value="Prohibit invoking System.out.println in source code !"/> |
|||
</module> |
|||
|
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="//FIXME"/> |
|||
<property name="message" value="Recommended fix FIXME task !"/> |
|||
</module> |
|||
|
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="//TODO"/> |
|||
<property name="message" value="Recommended fix TODO task !"/> |
|||
</module> |
|||
|
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="@alibaba"/> |
|||
<property name="message" value="Recommended remove @alibaba keyword!"/> |
|||
</module> |
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="@taobao"/> |
|||
<property name="message" value="Recommended remove @taobao keyword!"/> |
|||
</module> |
|||
<module name="RegexpSingleline"> |
|||
<property name="format" value="@author"/> |
|||
<property name="message" value="Recommended remove @author tag in javadoc!"/> |
|||
</module> |
|||
|
|||
<module name="RegexpSingleline"> |
|||
<property name="format" |
|||
value=".*[\u3400-\u4DB5\u4E00-\u9FA5\u9FA6-\u9FBB\uF900-\uFA2D\uFA30-\uFA6A\uFA70-\uFAD9\uFF00-\uFFEF\u2E80-\u2EFF\u3000-\u303F\u31C0-\u31EF]+.*"/> |
|||
<property name="message" value="Not allow chinese character !"/> |
|||
</module> |
|||
|
|||
<module name="FileLength"> |
|||
<property name="max" value="3000"/> |
|||
</module> |
|||
|
|||
<module name="TreeWalker"> |
|||
|
|||
<module name="UnusedImports"> |
|||
<property name="processJavadoc" value="true"/> |
|||
</module> |
|||
<module name="RedundantImport"/> |
|||
|
|||
<!--<module name="IllegalImport" />--> |
|||
|
|||
<!--Checks that classes that override equals() also override hashCode()--> |
|||
<module name="EqualsHashCode"/> |
|||
<!--Checks for over-complicated boolean expressions. Currently finds code like if (topic == true), topic || true, !false, etc.--> |
|||
<module name="SimplifyBooleanExpression"/> |
|||
<module name="OneStatementPerLine"/> |
|||
<module name="UnnecessaryParentheses"/> |
|||
<!--Checks for over-complicated boolean return statements. For example the following code--> |
|||
<module name="SimplifyBooleanReturn"/> |
|||
|
|||
<!--Check that the default is after all the cases in producerGroup switch statement--> |
|||
<module name="DefaultComesLast"/> |
|||
<!--Detects empty statements (standalone ";" semicolon)--> |
|||
<module name="EmptyStatement"/> |
|||
<!--Checks that long constants are defined with an upper ell--> |
|||
<module name="UpperEll"/> |
|||
<module name="ConstantName"> |
|||
<property name="format" value="(^[A-Z][A-Z0-9]*(_[A-Z0-9]+)*$)|(^log$)"/> |
|||
</module> |
|||
<!--Checks that local, non-final variable names conform to producerGroup format specified by the format property--> |
|||
<module name="LocalVariableName"/> |
|||
<!--Validates identifiers for local, final variables, including catch parameters--> |
|||
<module name="LocalFinalVariableName"/> |
|||
<!--Validates identifiers for non-static fields--> |
|||
<module name="MemberName"/> |
|||
<!--Validates identifiers for class type parameters--> |
|||
<module name="ClassTypeParameterName"> |
|||
<property name="format" value="^[A-Z0-9]*$"/> |
|||
</module> |
|||
<!--Validates identifiers for method type parameters--> |
|||
<module name="MethodTypeParameterName"> |
|||
<property name="format" value="^[A-Z0-9]*$"/> |
|||
</module> |
|||
<module name="PackageName"/> |
|||
<module name="ParameterName"/> |
|||
<module name="StaticVariableName"/> |
|||
<module name="TypeName"/> |
|||
<!--Checks that there are no import statements that use the * notation--> |
|||
<module name="AvoidStarImport"/> |
|||
|
|||
<!--whitespace--> |
|||
<module name="GenericWhitespace"/> |
|||
<module name="NoWhitespaceBefore"/> |
|||
<module name="NoWhitespaceAfter"/> |
|||
<module name="WhitespaceAround"> |
|||
<property name="allowEmptyConstructors" value="true"/> |
|||
<property name="allowEmptyMethods" value="true"/> |
|||
</module> |
|||
<module name="Indentation"/> |
|||
<module name="MethodParamPad"/> |
|||
<module name="ParenPad"/> |
|||
<module name="TypecastParenPad"/> |
|||
</module> |
|||
</module> |
|||
@ -0,0 +1,157 @@ |
|||
<!-- |
|||
Licensed to the Apache Software Foundation (ASF) under one or more |
|||
contributor license agreements. See the NOTICE file distributed with |
|||
this work for additional information regarding copyright ownership. |
|||
The ASF licenses this file to You under the Apache License, Version 2.0 |
|||
(the "License"); you may not use this file except in compliance with |
|||
the License. You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
--> |
|||
|
|||
<code_scheme name="rocketmq"> |
|||
<option name="USE_SAME_INDENTS" value="true"/> |
|||
<option name="IGNORE_SAME_INDENTS_FOR_LANGUAGES" value="true"/> |
|||
<option name="OTHER_INDENT_OPTIONS"> |
|||
<value> |
|||
<option name="INDENT_SIZE" value="4"/> |
|||
<option name="CONTINUATION_INDENT_SIZE" value="4"/> |
|||
<option name="TAB_SIZE" value="4"/> |
|||
<option name="USE_TAB_CHARACTER" value="false"/> |
|||
<option name="SMART_TABS" value="false"/> |
|||
<option name="LABEL_INDENT_SIZE" value="0"/> |
|||
<option name="LABEL_INDENT_ABSOLUTE" value="false"/> |
|||
<option name="USE_RELATIVE_INDENTS" value="false"/> |
|||
</value> |
|||
</option> |
|||
<option name="PREFER_LONGER_NAMES" value="false"/> |
|||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="1000"/> |
|||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="1000"/> |
|||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND"> |
|||
<value/> |
|||
</option> |
|||
<option name="IMPORT_LAYOUT_TABLE"> |
|||
<value> |
|||
<package name="" withSubpackages="true" static="false"/> |
|||
<emptyLine/> |
|||
<package name="" withSubpackages="true" static="true"/> |
|||
</value> |
|||
</option> |
|||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false"/> |
|||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false"/> |
|||
<option name="JD_P_AT_EMPTY_LINES" value="false"/> |
|||
<option name="JD_KEEP_INVALID_TAGS" value="false"/> |
|||
<option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true"/> |
|||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/> |
|||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/> |
|||
<option name="ELSE_ON_NEW_LINE" value="true"/> |
|||
<option name="WHILE_ON_NEW_LINE" value="true"/> |
|||
<option name="CATCH_ON_NEW_LINE" value="true"/> |
|||
<option name="FINALLY_ON_NEW_LINE" value="true"/> |
|||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/> |
|||
<option name="ALIGN_MULTILINE_FOR" value="false"/> |
|||
<option name="SPACE_AFTER_TYPE_CAST" value="false"/> |
|||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true"/> |
|||
<option name="METHOD_PARAMETERS_WRAP" value="1"/> |
|||
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true"/> |
|||
<option name="LABELED_STATEMENT_WRAP" value="1"/> |
|||
<option name="WRAP_COMMENTS" value="true"/> |
|||
<option name="METHOD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="CLASS_ANNOTATION_WRAP" value="1"/> |
|||
<option name="FIELD_ANNOTATION_WRAP" value="1"/> |
|||
<JavaCodeStyleSettings> |
|||
<option name="CLASS_NAMES_IN_JAVADOC" value="3"/> |
|||
</JavaCodeStyleSettings> |
|||
<XML> |
|||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true"/> |
|||
</XML> |
|||
<ADDITIONAL_INDENT_OPTIONS fileType="haml"> |
|||
<option name="INDENT_SIZE" value="2"/> |
|||
</ADDITIONAL_INDENT_OPTIONS> |
|||
<codeStyleSettings language="Groovy"> |
|||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/> |
|||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/> |
|||
<option name="ELSE_ON_NEW_LINE" value="true"/> |
|||
<option name="CATCH_ON_NEW_LINE" value="true"/> |
|||
<option name="FINALLY_ON_NEW_LINE" value="true"/> |
|||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/> |
|||
<option name="ALIGN_MULTILINE_FOR" value="false"/> |
|||
<option name="SPACE_AFTER_TYPE_CAST" value="false"/> |
|||
<option name="METHOD_PARAMETERS_WRAP" value="1"/> |
|||
<option name="METHOD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="CLASS_ANNOTATION_WRAP" value="1"/> |
|||
<option name="FIELD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/> |
|||
<indentOptions> |
|||
<option name="CONTINUATION_INDENT_SIZE" value="4"/> |
|||
</indentOptions> |
|||
</codeStyleSettings> |
|||
<codeStyleSettings language="HOCON"> |
|||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/> |
|||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/> |
|||
</codeStyleSettings> |
|||
<codeStyleSettings language="JAVA"> |
|||
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false"/> |
|||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/> |
|||
<option name="ELSE_ON_NEW_LINE" value="true"/> |
|||
<option name="WHILE_ON_NEW_LINE" value="true"/> |
|||
<option name="CATCH_ON_NEW_LINE" value="true"/> |
|||
<option name="FINALLY_ON_NEW_LINE" value="true"/> |
|||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/> |
|||
<option name="ALIGN_MULTILINE_FOR" value="false"/> |
|||
<option name="SPACE_AFTER_TYPE_CAST" value="false"/> |
|||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true"/> |
|||
<option name="METHOD_PARAMETERS_WRAP" value="1"/> |
|||
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true"/> |
|||
<option name="LABELED_STATEMENT_WRAP" value="1"/> |
|||
<option name="METHOD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="CLASS_ANNOTATION_WRAP" value="1"/> |
|||
<option name="FIELD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/> |
|||
<indentOptions> |
|||
<option name="CONTINUATION_INDENT_SIZE" value="4"/> |
|||
</indentOptions> |
|||
</codeStyleSettings> |
|||
<codeStyleSettings language="JSON"> |
|||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/> |
|||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/> |
|||
</codeStyleSettings> |
|||
<codeStyleSettings language="Scala"> |
|||
<option name="KEEP_BLANK_LINES_IN_DECLARATIONS" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1"/> |
|||
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1"/> |
|||
<option name="ELSE_ON_NEW_LINE" value="true"/> |
|||
<option name="WHILE_ON_NEW_LINE" value="true"/> |
|||
<option name="CATCH_ON_NEW_LINE" value="true"/> |
|||
<option name="FINALLY_ON_NEW_LINE" value="true"/> |
|||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false"/> |
|||
<option name="ALIGN_MULTILINE_FOR" value="false"/> |
|||
<option name="METHOD_PARAMETERS_WRAP" value="1"/> |
|||
<option name="METHOD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="CLASS_ANNOTATION_WRAP" value="1"/> |
|||
<option name="FIELD_ANNOTATION_WRAP" value="1"/> |
|||
<option name="PARENT_SETTINGS_INSTALLED" value="true"/> |
|||
<indentOptions> |
|||
<option name="INDENT_SIZE" value="4"/> |
|||
<option name="CONTINUATION_INDENT_SIZE" value="4"/> |
|||
<option name="TAB_SIZE" value="4"/> |
|||
</indentOptions> |
|||
</codeStyleSettings> |
|||
<codeStyleSettings language="XML"> |
|||
<indentOptions> |
|||
<option name="CONTINUATION_INDENT_SIZE" value="4"/> |
|||
</indentOptions> |
|||
</codeStyleSettings> |
|||
</code_scheme> |
|||
@ -0,0 +1,59 @@ |
|||
package com.elink.esua.epdc.rocketmq.consumer; |
|||
|
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.elink.esua.epdc.commons.tools.constant.RocketMqConstant; |
|||
import com.elink.esua.epdc.modules.consult.service.GridOperatorInfoService; |
|||
import com.elink.esua.epdc.modules.enterprise.service.EnterpriseInfoService; |
|||
import com.elink.esua.epdc.modules.evaluate.service.EvaluateDeptService; |
|||
import com.elink.esua.epdc.rocketmq.dto.OrganizationModifyDTO; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.rocketmq.common.message.MessageExt; |
|||
import org.apache.rocketmq.spring.annotation.MessageModel; |
|||
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; |
|||
import org.apache.rocketmq.spring.core.RocketMQListener; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.xml.ws.Action; |
|||
|
|||
/** |
|||
* |
|||
* 组织机构信息修改-监听MQ消息 |
|||
* |
|||
* @Author:liuchuang |
|||
* @Date:2020/3/7 13:38 |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
@RocketMQMessageListener(topic = RocketMqConstant.MQ_TOPIC_ORGANIZATION, consumerGroup = "${rocketmq.consumer.group}", messageModel = MessageModel.BROADCASTING) |
|||
public class OrganizationModifyConsumer implements RocketMQListener<MessageExt> { |
|||
|
|||
@Autowired |
|||
private EnterpriseInfoService enterpriseInfoService; |
|||
|
|||
@Autowired |
|||
private EvaluateDeptService evaluateDeptService; |
|||
|
|||
@Autowired |
|||
private GridOperatorInfoService gridOperatorInfoService; |
|||
|
|||
@Override |
|||
public void onMessage(MessageExt messageExt) { |
|||
log.info("EPDC-CUSTOM-SERVER消费消息START:{topic:{}, msgId:{}}", RocketMqConstant.MQ_TOPIC_ORGANIZATION, messageExt.getMsgId()); |
|||
try { |
|||
String charset = "UTF-8"; |
|||
String body = new String(messageExt.getBody(), charset); |
|||
OrganizationModifyDTO dto = JSONObject.parseObject(body, OrganizationModifyDTO.class); |
|||
// 企业信息表修改组织机构信息
|
|||
enterpriseInfoService.modifyOrganizationInfo(dto); |
|||
// 评价部门表修改组织机构信息
|
|||
evaluateDeptService.modifyOrganizationInfo(dto); |
|||
// 网格员信息表修改组织机构信息
|
|||
gridOperatorInfoService.modifyOrganizationInfo(dto); |
|||
log.info("EPDC-CUSTOM-SERVER消费消息END:{topic:{}, msgId:{}, body:{}}", RocketMqConstant.MQ_TOPIC_ORGANIZATION, messageExt.getMsgId(), body); |
|||
} catch (Exception e) { |
|||
log.info("EPDC-CUSTOM-SERVER消费消息失败:msgId:{}", messageExt.getMsgId()); |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
package com.elink.esua.epdc.rocketmq.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* |
|||
* 组织机构信息修改-接收MQ消息DTO |
|||
* |
|||
* @Author:liuchuang |
|||
* @Date:2020/3/6 22:34 |
|||
*/ |
|||
@Data |
|||
public class OrganizationModifyDTO implements Serializable { |
|||
|
|||
private static final long serialVersionUID = -6534846437298229554L; |
|||
/** |
|||
* 部门ID |
|||
*/ |
|||
private Long deptId; |
|||
|
|||
/** |
|||
* 旧部门名称 |
|||
*/ |
|||
private String oldDeptName; |
|||
|
|||
/** |
|||
* 新部门名称 |
|||
*/ |
|||
private String newDeptName; |
|||
|
|||
/** |
|||
* 部门类型 |
|||
*/ |
|||
private String typeKey; |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue