diff --git a/.codecov.yml b/.codecov.yml index c871f854..20ecf1f1 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,7 +1,5 @@ -ignore: - - "spring-cloud-alibaba-dependencies/.*" - - "spring-cloud-alibaba-docs/.*" - - "spring-cloud-alibaba-examples/.*" - - "spring-cloud-alibaba-test/.*" - - "spring-cloud-starter-alibaba/.*" - - "spring-cloud-starter-alicloud/.*" \ No newline at end of file + +coverage: + status: + project: off + patch: off \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index dff5f3a5..00000000 --- a/.travis.yml +++ /dev/null @@ -1 +0,0 @@ -language: java diff --git a/README.md b/README.md index ad4672cd..680fbea8 100644 --- a/README.md +++ b/README.md @@ -122,7 +122,7 @@ Examples: [Alibaba Cloud OSS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme.md) ## Version control guidelines -The version number of the project is in the form of x.x.x, where x is a number, starting from 0, and is not limited to the range 0~9. When the project is in the incubator phase, the first version number is fixed to 0, that is, the version number is 0.x.x. +The version number of the project is in the form of x.x.x, where x is a number, starting from 0, and is not limited to the range 0~9. When the project is in the incubator phase, the version number is 0.x.x. As the interfaces and annotations of Spring Boot 1 and Spring Boot 2 have been changed significantly in the Actuator module, and spring-cloud-commons is also changed quite a lot from 1.x.x to 2.0.0, we maintain two different branches to support Spring Boot 1 and Spring Boot 2: * 0.1.x for Spring Boot 1 diff --git a/mvnw b/mvnw index a69491ac..675cb6b0 100755 --- a/mvnw +++ b/mvnw @@ -8,7 +8,7 @@ # "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 +# https://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 diff --git a/mvnw.cmd b/mvnw.cmd index b0dc0e7e..7ecd01d8 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -7,7 +7,7 @@ @REM "License"); you may not use this file except in compliance @REM with the License. You may obtain a copy of the License at @REM -@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM https://www.apache.org/licenses/LICENSE-2.0 @REM @REM Unless required by applicable law or agreed to in writing, @REM software distributed under the License is distributed on an diff --git a/pom.xml b/pom.xml index 6c8b35aa..c896c04e 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 @@ -22,7 +22,7 @@ Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + https://www.apache.org/licenses/LICENSE-2.0.txt repo @@ -74,6 +74,7 @@ 2.1.0.RELEASE 2.1.0.RELEASE 2.1.0.RELEASE + 2.1.0.RELEASE 4.12 3.0 @@ -171,6 +172,14 @@ pom import + + + org.springframework.cloud + spring-cloud-gateway-dependencies + ${spring-cloud-gateway.version} + pom + import + diff --git a/spring-cloud-alibaba-coverage/pom.xml b/spring-cloud-alibaba-coverage/pom.xml index 8b737ce9..e739c3e2 100644 --- a/spring-cloud-alibaba-coverage/pom.xml +++ b/spring-cloud-alibaba-coverage/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba org.springframework.cloud diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index df04b40c..bb54b3dd 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-dependencies-parent @@ -19,15 +19,15 @@ 1.4.2 3.1.0 - 0.1.3 - 0.8.2 + 0.4.0 + 1.0.0-RC2 0.8.0 1.0.8 1.0.1 4.0.1 1.0.5 2.16.0 - 4.3.1 + 2.0.2 2.1.6 2.6.5 0.2.1.RELEASE @@ -107,8 +107,8 @@ org.apache.rocketmq - rocketmq-client - ${rocketmq.version} + rocketmq-spring-boot-starter + ${rocketmq.starter.version} @@ -186,26 +186,11 @@ - - com.alibaba.fescar - fescar-core - ${fescar.version} - - - com.alibaba.fescar - fescar-common - ${fescar.version} - com.alibaba.fescar fescar-spring ${fescar.version} - - com.alibaba.fescar - fescar-rm-datasource - ${fescar.version} - @@ -393,6 +378,14 @@ ${project.version} + + + org.springframework.cloud + spring-cloud-starter-dubbo + ${project.version} + + + @@ -453,4 +446,4 @@ - \ No newline at end of file + diff --git a/spring-cloud-alibaba-docs/pom.xml b/spring-cloud-alibaba-docs/pom.xml index 87014d57..fe978b89 100644 --- a/spring-cloud-alibaba-docs/pom.xml +++ b/spring-cloud-alibaba-docs/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc index d5cc93a6..1de933a1 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc @@ -6,24 +6,7 @@ Spring Cloud AliCloud ACM 是 Config Server 和 Client 的替代方案,客户 === 如何引入 Spring Cloud AliCloud ACM -Spring Cloud Alibaba 已经发布了 0.2.2.BUILD-SNAPSHOT 版本,需要首先导入依赖管理 POM。 - -[source,xml] ----- - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - ----- - -并引入 Spring Cloud AliCloud ACM Starter 依赖。 +如果要在您的项目中引入 ACM,使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-acm` 的 starter。 [source,xml] ---- diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/ans.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/ans.adoc index 1258a757..53fbf28f 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/ans.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/ans.adoc @@ -4,24 +4,7 @@ ANS(Application Naming Service) 是隶属于阿里云 EDAS 产品的组件 === 如何引入 Spring Cloud AliCloud ANS -Spring Cloud Alibaba 已经发布了 0.2.2.BUILD-SNAPSHOT 版本,需要首先导入依赖管理 POM。 - -[source,xml] ----- - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - ----- - -接下来引入 Spring Cloud AliCloud ANS Starter 即可。 +如果要在您的项目中引入 ANS,使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-ans` 的 starter。 [source,xml] ---- diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc index d3f0d885..ef5d96a4 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc @@ -2,3 +2,41 @@ Spring Cloud Alibaba BOM 包含了它所使用的所有依赖的版本。 +### Spring Cloud Alibaba Bill of Materials (BOM) + +如果您是 Maven Central 用户,请将我们的 BOM 添加到您的 pom.xml 中的 部分。 这将允许您省略任何Maven依赖项的版本,而是将版本控制委派给BOM。 + +```xml + + + + org.springframework.cloud + spring-cloud-alibaba-dependencies + 2.1.0.RELEASE + pom + import + + + +``` + +### Spring Snapshots Maven Repository + +如果要使用最新的 BUILD-SNAPSHOT 版本,请在 pom.xml 中添加 Spring Snapshot Repository,注意:BUILD-SNAPSHOT随时可能更新: + +```xml + + + spring-snapshots + Spring SnapShots + https://repo.spring.io/libs-snapshot + + true + + + +``` + +举个例子, 0.2.0.BUILD-SNAPSHOT 版本就在这个仓库中可用。 + + diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc index 3280fae2..0cf53242 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc @@ -28,55 +28,14 @@ NOTE: 注意dataid是以 properties(默认的文件扩展名方式)为扩展名 ===== 客户端使用方式 -为了能够在应用程序中使用 Nacos 来实现应用的外部化配置,在构建应用的同时添加一个Spring Boot Starter org.springframework.cloud:spring-cloud-starter-alibaba-nacos-config。以下是一个基本的 maven 依赖配置: +如果要在您的项目中使用 Nacos 来实现应用的外部化配置,使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alibaba-nacos-config` 的 starter。 [source,xml] ---- - - org.springframework.boot - spring-boot-starter-parent - 2.0.5.RELEASE - - - - - - - org.springframework.cloud - spring-cloud-dependencies - Finchley.SR1 - pom - import - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - - - - - org.springframework.cloud - spring-cloud-starter-alibaba-nacos-config - - - org.springframework.boot - spring-boot-starter - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - + + org.springframework.cloud + spring-cloud-starter-alibaba-nacos-config + ---- 现在就可以创建一个标准的 Spring Boot 应用。 diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc index 57b3a656..92f40c4a 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc @@ -11,39 +11,18 @@ Discovery Starter 也将服务实例自身的一些元数据信息-例如 host ==== 如何引入 Nacos Discovery Starter -为了能够在你的工程下引入 Nacos Discovey Starter,使用group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alibaba-nacos-discovery`。 -pom.xml 示例如下所示: +如果要在您的项目中使用 Nacos 来实现服务发现,使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alibaba-nacos-discovery` 的 starter。 [source,xml,indent=0] ---- - - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - - - - - - org.springframework.cloud - spring-cloud-starter-alibaba-nacos-discovery - - + + org.springframework.cloud + spring-cloud-starter-alibaba-nacos-discovery + ---- ==== 启动一个 Provider 应用 -如果您使用的 Spring Cloud 版本是 Finchley.SR1 版本,那么这个时候您的Spring Boot版本的选择可需要额外的小心了,因为版本的不匹对,可能会导致许多意外的效果。 -Spring Cloud 的 Finchley.SR1 版本最佳实践的 Spring Boot 版本是 2.0.6.RELEASE。在启动您的一个 Provider 应用时请检查依赖的 Spring Boot 版本是否是 -1.X.Y.RELEASE 或者 2.1.0.RELEASE 的版本。如果不是,请更正到 2.0.6.RELEASE 版本。 - 以下步骤向您展示了如何将一个服务注册到 Nacos。 1. pom.xml的配置。一个完整的 pom.xml 配置如下所示: @@ -62,7 +41,7 @@ Spring Cloud 的 Finchley.SR1 版本最佳实践的 Spring Boot 版本是 2.0.6. org.springframework.boot spring-boot-starter-parent - 2.0.6.RELEASE + ${spring.boot.version} @@ -77,14 +56,14 @@ Spring Cloud 的 Finchley.SR1 版本最佳实践的 Spring Boot 版本是 2.0.6. org.springframework.cloud spring-cloud-dependencies - Finchley.SR1 + ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT + ${spring.cloud.alibaba.version} pom import diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/oss.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/oss.adoc index ee634d65..4d025df1 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/oss.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/oss.adoc @@ -4,24 +4,7 @@ OSS(Object Storage Service)是阿里云的一款对象存储服务产品, === 如何引入 Spring Cloud AliCloud OSS -Spring Cloud Alibaba 已经发布了0.2.0版本,需要首先导入依赖管理POM。 - -[source,xml] ----- - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - ----- - -接下来引入 Spring Cloud AliCloud OSS Starter 即可。 +如果要在您的项目中引入 OSS,使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-oss` 的 starter。 [source,xml] ---- diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/rocketmq.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/rocketmq.adoc index 3724fd41..557fe2e5 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/rocketmq.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/rocketmq.adoc @@ -112,139 +112,167 @@ messageChannel.send(MessageBuilder.withPayload("simple msg").build()); 这段代码所有的消息类都是 `spring-messaging` 模块里提供的。屏蔽具体消息中间件的底层实现,如果想用更换消息中间件,在配置文件里配置相关消息中间件信息以及修改 binder 依赖即可。 -**Spring Cloud Stream 底层也是基于这段代码去做了各种抽象。** - -### Spring Cloud Alibaba RocketMQ Binder 实现原理 - -.RocketMQ Binder处理流程 -image::https://cdn.nlark.com/lark/0/2018/png/64647/1543560843558-24525bf4-1d0e-4e10-be5f-bdde7127f6e6.png[] +**Spring Cloud Stream 底层基于这段代码去做了各种抽象。** -RocketMQ Binder 的核心主要就是这3个类:`RocketMQMessageChannelBinder`,`RocketMQInboundChannelAdapter` 和 `RocketMQMessageHandler`。 +### 如何使用 Spring Cloud Alibaba RocketMQ Binder ### -`RocketMQMessageChannelBinder` 是个标准的 Binder 实现,其内部构建 `RocketMQInboundChannelAdapter` 和 `RocketMQMessageHandler`。 +如果要在您的项目中引入 RocketMQ Binder,需要引入如下 maven 依赖: -`RocketMQMessageHandler` 用于 RocketMQ `Producer` 的启动以及消息的发送,其内部会根据 `spring-messaging` 模块内 `org.springframework.messaging.Message` 消息类,去创建 RocketMQ 的消息类 `org.apache.rocketmq.common.message.Message`。 +```xml + + org.springframework.cloud + spring-cloud-stream-binder-rocketmq + +``` -在构造 `org.apache.rocketmq.common.message.Message` 的过程中会根据 `org.springframework.messaging.Message` 的 Header 构造成 `RocketMQMessageHeaderAccessor`。然后再根据 `RocketMQMessageHeaderAccessor` 中的一些属性,比如 tags、keys、flag等属性设置到 RocketMQ 的消息类 `org.apache.rocketmq.common.message.Message` 中。 +或者可以使用 Spring Cloud Stream RocketMQ Starter: -`RocketMQInboundChannelAdapter` 用于 RocketMQ `Consumer` 的启动以及消息的接收。其内部还支持 https://github.com/spring-projects/spring-retry[spring-retry] 的使用。 +```xml + + org.springframework.cloud + spring-cloud-starter-stream-rocketmq + +``` -在消费消息的时候可以从 Header 中获取 `Acknowledgement` 并进行一些设置。 +### Spring Cloud Alibaba RocketMQ Binder 实现 -比如使用 `MessageListenerConcurrently` 进行异步消费的时候,可以设置延迟消费: +RocketMQ Binder 的实现依赖于 https://github.com/apache/rocketmq-spring[RocketMQ-Spring] 框架。 + +RocketMQ-Spring 框架是 RocketMQ 与 Spring Boot 的整合,RocketMQ Spring 主要提供了 3 个特性: + +1. 使用 `RocketMQTemplate` 用来统一发送消息,包括同步、异步发送消息和事务消息 +2. `@RocketMQTransactionListener` 注解用来处理事务消息的监听和回查 +3. `@RocketMQMessageListener` 注解用来消费消息 + +RocketMQ Binder 的核心类 RocketMQMessageChannelBinder 实现了 Spring Cloud Stream 规范,内部构建会 `RocketMQInboundChannelAdapter` 和 `RocketMQMessageHandler`。 + +`RocketMQMessageHandler` 会基于 Binding 配置构造 `RocketMQTemplate`,`RocketMQTemplate` 内部会把 `spring-messaging` 模块内 `org.springframework.messaging.Message` 消息类转换成 RocketMQ 的消息类 `org.apache.rocketmq.common.message.Message`,然后发送出去。 + +`RocketMQInboundChannelAdapter` 也会基于 Binding 配置构造 `RocketMQListenerBindingContainer`,`RocketMQListenerBindingContainer` 内部会启动 RocketMQ `Consumer` 接收消息。 + +NOTE: 在使用 RocketMQ Binder 的同时也可以配置 rocketmq.** 用于触发 RocketMQ Spring 相关的 AutoConfiguration + +目前 Binder 支持在 `Header` 中设置相关的 key 来进行 RocketMQ Message 消息的特性设置。 + +比如 `TAGS`、`DELAY`、`TRANSACTIONAL_ARG`、`KEYS`、`WAIT_STORE_MSG_OK`、`FLAG` 表示 RocketMQ 消息对应的标签, ```java -@StreamListener("input") -public void receive(Message message) { - RocketMQMessageHeaderAccessor headerAccessor = new RocketMQMessageHeaderAccessor(message); - Acknowledgement acknowledgement = headerAccessor.getAcknowledgement(message); - acknowledgement.setConsumeConcurrentlyStatus(ConsumeConcurrentlyStatus.RECONSUME_LATER); - acknowledgement.setConsumeConcurrentlyDelayLevel(1); -} +MessageBuilder builder = MessageBuilder.withPayload(msg) + .setHeader(RocketMQHeaders.TAGS, "binder") + .setHeader(RocketMQHeaders.KEYS, "my-key") + .setHeader("DELAY", "1"); +Message message = builder.build(); +output().send(message); ``` -比如使用 `MessageListenerOrderly` 进行顺序消费的时候,可以设置延迟消费: +### 配置选项 -```java -@StreamListener("input") -public void receive(Message message) { - RocketMQMessageHeaderAccessor headerAccessor = new RocketMQMessageHeaderAccessor(message); - Acknowledgement acknowledgement = headerAccessor.getAcknowledgement(message); - acknowledgement.setConsumeOrderlyStatus(ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT); - acknowledgement.setConsumeOrderlySuspendCurrentQueueTimeMill(5000); -} -``` +#### RocketMQ Binder Properties -Provider端支持的配置: +spring.cloud.stream.rocketmq.binder.name-server:: +RocketMQ NameServer 地址。 ++ +Default: `127.0.0.1:9876`. +spring.cloud.stream.rocketmq.binder.access-key:: +阿里云账号 AccessKey。 ++ +Default: null. +spring.cloud.stream.rocketmq.binder.secret-key:: +阿里云账号 SecretKey。 ++ +Default: null. +spring.cloud.stream.rocketmq.binder.enable-msg-trace:: +是否为 Producer 和 Consumer 开启消息轨迹功能 ++ +Default: `true`. +spring.cloud.stream.rocketmq.binder.customized-trace-topic:: +消息轨迹开启后存储的 topic 名称。 ++ +Default: `RMQ_SYS_TRACE_TOPIC`. -:frame: topbot -[width="60%",options="header"] -|==== -^|配置项 ^|含义 ^| 默认值 -|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.enabled`|是否启用producer|true -|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.max-message-size`|消息发送的最大字节数|0(大于0才会生效,RocketMQ 默认值为4M = 1024 * 1024 * 4) -|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.transactional`|是否启用 `TransactionMQProducer` 发送事务消息|false -|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.executer`|事务消息对应的 `org.apache.rocketmq.client.producer.LocalTransactionExecuter` 接口实现类 全类名。 比如 `org.test.MyExecuter`| -|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.transaction-check-listener`|事务消息对应的 `org.apache.rocketmq.client.producer.TransactionCheckListener` 接口实现类 全类名。 比如 `org.test.MyTransactionCheckListener`| -|==== -Consumer端支持的配置: +#### RocketMQ Consumer Properties -:frame: topbot -[width="60%",options="header"] -|==== -^|配置项 ^|含义| 默认值 -|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.enabled`|是否启用consumer|true -|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.tags`|Consumer订阅只有包括这些tags的topic消息。多个标签之间使用 "\|\|" 分割(不填表示不进行tags的过滤,订阅所有消息)| -|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.sql`|Consumer订阅满足sql要求的topic消息(如果同时配置了tags内容,sql的优先级更高)| -|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.broadcasting`|Consumer是否是广播模式|false -|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.orderly`|顺序消费 or 异步消费|false -|==== +下面的这些配置是以 `spring.cloud.stream.rocketmq.bindings..consumer.` 为前缀的 RocketMQ Consumer 相关的配置。 -### Endpoint支持 +enable:: +是否启用 Consumer。 ++ +默认值: `true`. +tags:: +Consumer 基于 TAGS 订阅,多个 tag 以 `||` 分割。 ++ +默认值: empty. +sql:: +Consumer 基于 SQL 订阅。 ++ +默认值: empty. +broadcasting:: +Consumer 是否是广播消费模式。如果想让所有的订阅者都能接收到消息,可以使用广播模式。 ++ +默认值: `false`. +orderly:: +Consumer 是否同步消费消息模式。 ++ +默认值: `false`. +delayLevelWhenNextConsume:: +异步消费消息模式下消费失败重试策略: +* -1,不重复,直接放入死信队列 +* 0,broker 控制重试策略 +* >0,client 控制重试策略 ++ +默认值: `0`. +suspendCurrentQueueTimeMillis:: +同步消费消息模式下消费失败后再次消费的时间间隔。 ++ +默认值: `1000`. -在使用Endpoint特性之前需要在 Maven 中添加 `spring-boot-starter-actuator` 依赖,并在配置中允许 Endpoints 的访问。 +#### RocketMQ Provider Properties -* Spring Boot 1.x 中添加配置 `management.security.enabled=false`。暴露的 endpoint 路径为 `/rocketmq_binder` -* Spring Boot 2.x 中添加配置 `management.endpoints.web.exposure.include=*`。暴露的 endpoint 路径为 `/actuator/rocketmq-binder` +下面的这些配置是以 `spring.cloud.stream.rocketmq.bindings..producer.` 为前缀的 RocketMQ Producer 相关的配置。 -Endpoint 会统计消息最后一次发送的数据,消息发送成功或失败的次数,消息消费成功或失败的次数等数据。 - -```json -{ - "runtime": { - "lastSend.timestamp": 1542786623915 - }, - "metrics": { - "scs-rocketmq.consumer.test-topic.totalConsumed": { - "count": 11 - }, - "scs-rocketmq.consumer.test-topic.totalConsumedFailures": { - "count": 0 - }, - "scs-rocketmq.producer.test-topic.totalSentFailures": { - "count": 0 - }, - "scs-rocketmq.consumer.test-topic.consumedPerSecond": { - "count": 11, - "fifteenMinuteRate": 0.012163847780107841, - "fiveMinuteRate": 0.03614605351360527, - "meanRate": 0.3493213353657594, - "oneMinuteRate": 0.17099243039490175 - }, - "scs-rocketmq.producer.test-topic.totalSent": { - "count": 5 - }, - "scs-rocketmq.producer.test-topic.sentPerSecond": { - "count": 5, - "fifteenMinuteRate": 0.005540151995103271, - "fiveMinuteRate": 0.01652854617838251, - "meanRate": 0.10697493212602836, - "oneMinuteRate": 0.07995558537067671 - }, - "scs-rocketmq.producer.test-topic.sentFailuresPerSecond": { - "count": 0, - "fifteenMinuteRate": 0.0, - "fiveMinuteRate": 0.0, - "meanRate": 0.0, - "oneMinuteRate": 0.0 - }, - "scs-rocketmq.consumer.test-topic.consumedFailuresPerSecond": { - "count": 0, - "fifteenMinuteRate": 0.0, - "fiveMinuteRate": 0.0, - "meanRate": 0.0, - "oneMinuteRate": 0.0 - } - } -} -``` - -注意:要想查看统计数据需要在pom里加上 https://mvnrepository.com/artifact/io.dropwizard.metrics/metrics-core[metrics-core依赖]。如若不加,endpoint 将会显示 warning 信息而不会显示统计信息: - -```json -{ - "warning": "please add metrics-core dependency, we use it for metrics" -} -``` \ No newline at end of file +enable:: +是否启用 Producer。 ++ +默认值: `true`. +group:: +Producer group name。 ++ +默认值: empty. +maxMessageSize:: +消息发送的最大字节数。 ++ +默认值: `8249344`. +transactional:: +是否发送事务消息。 ++ +默认值: `false`. +sync:: +是否使用同步得方式发送消息。 ++ +默认值: `false`. +vipChannelEnabled:: +是否在 Vip Channel 上发送消息。 ++ +默认值: `true`. +sendMessageTimeout:: +发送消息的超时时间(毫秒)。 ++ +默认值: `3000`. +compressMessageBodyThreshold:: +消息体压缩阀值(当消息体超过 4k 的时候会被压缩)。 ++ +默认值: `4096`. +retryTimesWhenSendFailed:: +在同步发送消息的模式下,消息发送失败的重试次数。 ++ +默认值: `2`. +retryTimesWhenSendAsyncFailed:: +在异步发送消息的模式下,消息发送失败的重试次数。 ++ +默认值: `2`. +retryNextServer:: +消息发送失败的情况下是否重试其它的 broker。 ++ +默认值: `false`. diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/schedulerx.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/schedulerx.adoc index 02278db8..f3526317 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/schedulerx.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/schedulerx.adoc @@ -4,24 +4,7 @@ SchedulerX(分布式任务调度) 是隶属于阿里云EDAS产品的组件 === 如何引入 Spring Cloud AliCloud SchedulerX -Spring Cloud Alibaba 已经发布了0.2.1版本,需要首先导入依赖管理POM。 - -[source,xml] ----- - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - ----- - -接下来引入 Spring Cloud AliCloud SchedulerX Starter 即可。 +如果要在您的项目中引入 SchedulerX,使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-schedulerX` 的 starter。 [source,xml] ---- diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sentinel.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sentinel.adoc index 4cd91b44..736a24b8 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sentinel.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sentinel.adoc @@ -266,7 +266,7 @@ Sentinel Endpoint 里暴露的信息非常有用。包括当前应用的所有 ^|配置项 ^|含义 ^|默认值 |`spring.cloud.sentinel.enabled`|Sentinel自动化配置是否生效|true |`spring.cloud.sentinel.eager`|取消Sentinel控制台懒加载|false -|`spring.cloud.sentinel.transport.port`|应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer|8721 +|`spring.cloud.sentinel.transport.port`|应用与Sentinel控制台交互的端口,应用本地会起一个该端口占用的HttpServer|8719 |`spring.cloud.sentinel.transport.dashboard`|Sentinel 控制台地址| |`spring.cloud.sentinel.transport.heartbeat-interval-ms`|应用与Sentinel控制台的心跳间隔时间| |`spring.cloud.sentinel.transport.client-ip`|客户端IP| diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sms.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sms.adoc index 82e0ae02..8e9086f2 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sms.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sms.adoc @@ -4,24 +4,7 @@ === 如何引入 Spring Cloud AliCloud SMS -Spring Cloud Alibaba 已经发布了 0.2.2.BUILD-SNAPSHOT 版本,需要首先导入依赖管理 POM。 - -[source,xml] ----- - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.0.RELEASE - pom - import - - - ----- - -接下来引入 Spring Cloud AliCloud SMS Starter 即可。 +如果要在您的项目中引入 SMS,使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-sms` 的 starter。 [source,xml] ---- diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/acm.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/acm.adoc index 41fdef33..6c31d073 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/acm.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/acm.adoc @@ -6,24 +6,7 @@ Spring Cloud Alibaba Cloud ACM is an alternative solution for Config Server and === How to Introduce Spring Cloud Alibaba Cloud ACM -We’ve released Spring Cloud Alibaba version 0.2.2.BUILD-SNAPSHOT. You will need to add dependency management POM first. - -[source,xml] ----- - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - ----- - -Next we need to introduce Spring Cloud Alibaba Cloud ACM Starter. +If you want to use ACM in your project, please use the starter with the group ID as `org.springframework.cloud` and the artifact ID as `spring-cloud-starter-alicloud-acm`. [source,xml] ---- diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/ans.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/ans.adoc index cf0ef7f9..4728bc11 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/ans.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/ans.adoc @@ -4,24 +4,7 @@ ANS(Application Naming Service) is a component of EDAS. Spring Cloud Alibaba Cl === How to Introduce Spring Cloud Alibaba Cloud ANS -We’ve released Spring Cloud Alibaba version 0.2.1. You will need to add dependency management POM first. - -[source,xml] ----- - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - ----- - -Next we need to introduce Spring Cloud AliCloud ANS Starter. +If you want to use ANS in your project, please use the starter with the group ID as `org.springframework.cloud` and the artifact ID as `spring-cloud-starter-alicloud-ans`. [source,xml] ---- diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc index ac027a63..767a1e22 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc @@ -1,38 +1,38 @@ == Dependency Management -The Spring Cloud Alibaba Bill of Materials (BOM) contains the versions of all the dependencies it uses. +### Spring Cloud Alibaba Bill of Materials (BOM) -Version 0.2.2.BUILD-SNAPSHOT is compatible with the Spring Cloud Finchley. Version 0.1.1.RELEASE is compatible with the Spring Cloud Edgware. +If you’re a Maven Central user, add our BOM to your pom.xml section. This will allow you to omit versions for any of the Maven dependencies and instead delegate versioning to the BOM. -These artifacts are available from Maven Central and Spring Release repository via BOM: - -[source,xml] ----- +```xml org.springframework.cloud spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT + 2.1.0.RELEASE pom import ----- +``` -If you want to use the latest BUILD-SNAPSHOT version, add Spring Snapshot Repository in pom.xml , Attention: BUILD-SNAPSHOT may be updated in any time +### Spring Snapshots Maven Repository -[source,xml] ----- +If you want to use the latest BUILD-SNAPSHOT version, add Spring Snapshot Repository in pom.xml , Attention: BUILD-SNAPSHOT may be updated in any time: + +```xml - spring-snapshot - Spring Snapshot Repository - https://repo.spring.io/snapshot + spring-snapshots + Spring SnapShots + https://repo.spring.io/libs-snapshot true ----- \ No newline at end of file +``` + +For example, the 0.2.0.BUILD-SNAPSHOT is available from this repository. \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-config.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-config.adoc index ecea9602..e1d5be46 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-config.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-config.adoc @@ -28,55 +28,14 @@ NOTE: The default file extension of dataid is properties. ===== Usage on the Client -To use Nacos to manage externalized configurations for your applications, you need to add a Spring Boot Starter while building your application: org.springframework.cloud:spring-cloud-starter-alibaba-nacos-config The following is a basic configuration of maven dependency: +If you want to use Nacos to manage externalized configurations for your applications, please use the starter with the group ID as `org.springframework.cloud` and the artifact ID as `spring-cloud-starter-alibaba-nacos-config`. [source,xml] ---- - - org.springframework.boot - spring-boot-starter-parent - 2.0.5.RELEASE - - - - - - - org.springframework.cloud - spring-cloud-dependencies - Finchley.SR1 - pom - import - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - - - - - org.springframework.cloud - spring-cloud-starter-alibaba-nacos-config - - - org.springframework.boot - spring-boot-starter - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - + + org.springframework.cloud + spring-cloud-starter-alibaba-nacos-config + ---- Now we can create a standard Spring Boot application. diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc index 21f0b144..65e26897 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc @@ -11,39 +11,18 @@ Discovery Starter registers some of the metadata of the service instance, such a ==== How to Introduce Nacos Discovery Starter -To introduce Nacos Discovey Starter into your project, use the group ID of `org.springframework.cloud` and the artifact ID of `spring-cloud-starter-alibaba-nacos-discovery`. -pom.xml sample: +please use the starter with the group ID as `org.springframework.cloud` and the artifact ID as `spring-cloud-starter-alibaba-nacos-discovery`. [source,xml,indent=0] ---- - - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - - - - - - org.springframework.cloud - spring-cloud-starter-alibaba-nacos-discovery - - + + org.springframework.cloud + spring-cloud-starter-alibaba-nacos-discovery + ---- ==== Start a Provider Application -If you use the Finchley.SR1 version of Spring Cloud , then you will need to be very cautious when selecting the Spring Boot version, because the version incompatibility might lead to many unexpected results. -The best practice for Spring Cloud Finchley.SR1 is to use the 2.0.6 release of Spring Boot. When starting a provider application, please also check if your Spring Boot version is -1.X.Y.RELEASE or 2.1.0.RELEASE. If not, please change it to 2.0.6.RELEASE. - The following sample illustrates how to register a service to Nacos. 1. Configuration of pom.xml The following is a complete example of pom.xml: @@ -62,7 +41,7 @@ The following sample illustrates how to register a service to Nacos. org.springframework.boot spring-boot-starter-parent - 2.0.6.RELEASE + ${spring.boot.version} @@ -77,14 +56,14 @@ The following sample illustrates how to register a service to Nacos. org.springframework.cloud spring-cloud-dependencies - Finchley.SR1 + ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT + ${spring.cloud.alibaba.version} pom import diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/rocketmq.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/rocketmq.adoc index 81e36a94..b147b87d 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/rocketmq.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/rocketmq.adoc @@ -114,137 +114,164 @@ All the message types in this code are provided by the `spring-messaging`module. **The lower layer of Spring Cloud Stream also implements various code abstractions based on the previous code.** +### How to use Spring Cloud Alibaba RocketMQ Binder ### + +For using the Spring Cloud Alibaba RocketMQ Binder, you just need to add it to your Spring Cloud Stream application, using the following Maven coordinates: + +```xml + + org.springframework.cloud + spring-cloud-stream-binder-rocketmq + +``` + +Alternatively, you can also use the Spring Cloud Stream RocketMQ Starter: + +```xml + + org.springframework.cloud + spring-cloud-starter-stream-rocketmq + +``` + ### How Spring Cloud Alibaba RocketMQ Binder Works -.RocketMQ Binder Workflow -image::https://cdn.nlark.com/lark/0/2018/png/64647/1543560843558-24525bf4-1d0e-4e10-be5f-bdde7127f6e6.png[] +The implementation of RocketMQ Binder depend on the https://github.com/apache/rocketmq-spring[RocketMQ-Spring] framework. +RocketMQ Spring framework is an integration of RocketMQ and Spring Boot. It provides three main features: -The core of RocketMQ Binder are the 3 classes below: `RocketMQMessageChannelBinder`,`RocketMQInboundChannelAdapter` and `RocketMQMessageHandler`. +1. `RocketMQTemplate`: Sending messages, including synchronous, asynchronous, and transactional messages. +2. `@RocketMQTransactionListener`: Listen and check for transaction messages. +3. `@RocketMQMessageListener`: Consume messages. -`RocketMQMessageChannelBinder` is a standard implementation of Binder. It contains `RocketMQInboundChannelAdapter` and `RocketMQMessageHandler` as its internal constructions. +`RocketMQMessageChannelBinder` is a standard implementation of Binder, it will build `RocketMQInboundChannelAdapter` and `RocketMQMessageHandler` internally. -`RocketMQMessageHandler` is used to start RocketMQ `Producer` and send messages. It creates message type of RocketMQ `org.apache.rocketmq.common.message.Message` based on the message type of `org.springframework.messaging.Message` in the `spring-messaging` module. +`RocketMQMessageHandler` will construct `RocketMQTemplate` based on the Binding configuration. `RocketMQTemplate` will convert the `org.springframework.messaging.Message` message class of `spring-messaging` module to the RocketMQ message class `org.apache.rocketmq.common .message.Message` internally, then send it out. -When constructing `org.apache.rocketmq.common.message.Message`, it constructs `RocketMQMessageHeaderAccessor` based on the header of `org.springframework.messaging.Message`. Then, based on some of the properties in `RocketMQMessageHeaderAccessor` , it sets some of message attributes such as tags, keys, and flag in `org.apache.rocketmq.common.message.Message` of RocketMQ. +`RocketMQInboundChannelAdapter` will also construct `RocketMQListenerBindingContainer` based on the Binding configuration, and `RocketMQListenerBindingContainer` will start the RocketMQ `Consumer` to receive the messages. -`RocketMQInboundChannelAdapter` is used to start RocketMQ `Consumer` and receive messages. It also support the usage of https://github.com/spring-projects/spring-retry[spring-retry]. +NOTE: RocketMQ Binder Application can also be used to configure rocketmq.** to trigger RocketMQ Spring related AutoConfiguration -You can also obtain `Acknowledgement` from the Header and make some configurations. +Currently Binder supports setting the relevant key in `Header` to set the properties of the RocketMQ message. -For example, you can set delayed message consumption when `MessageListenerConcurrently` is used for asynchronous message consumption: +For example, `TAGS`, `DELAY`, `TRANSACTIONAL_ARG`, `KEYS`, `WAIT_STORE_MSG_OK`, `FLAG` represent the labels corresponding to the RocketMQ message. ```java -@StreamListener("input") -public void receive(Message message) { - RocketMQMessageHeaderAccessor headerAccessor = new RocketMQMessageHeaderAccessor(message); - Acknowledgement acknowledgement = headerAccessor.getAcknowledgement(message); - acknowledgement.setConsumeConcurrentlyStatus(ConsumeConcurrentlyStatus.RECONSUME_LATER); - acknowledgement.setConsumeConcurrentlyDelayLevel(1); -} +MessageBuilder builder = MessageBuilder.withPayload(msg) + .setHeader(RocketMQHeaders.TAGS, "binder") + .setHeader(RocketMQHeaders.KEYS, "my-key") + .setHeader("DELAY", "1"); +Message message = builder.build(); +output().send(message); ``` -You can also set delayed message consumption when `MessageListenerOrderly` is used for consuming ordered messages. +### Configuration Options -```java -@StreamListener("input") -public void receive(Message message) { - RocketMQMessageHeaderAccessor headerAccessor = new RocketMQMessageHeaderAccessor(message); - Acknowledgement acknowledgement = headerAccessor.getAcknowledgement(message); - acknowledgement.setConsumeOrderlyStatus(ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT); - acknowledgement.setConsumeOrderlySuspendCurrentQueueTimeMill(5000); -} -``` +#### RocketMQ Binder Properties -Supported Configurations of Provider: +spring.cloud.stream.rocketmq.binder.name-server:: +The name server of RocketMQ Server. ++ +Default: `127.0.0.1:9876`. +spring.cloud.stream.rocketmq.binder.access-key:: +The AccessKey of Alibaba Cloud Account. ++ +Default: null. +spring.cloud.stream.rocketmq.binder.secret-key:: +The SecretKey of Alibaba Cloud Account. ++ +Default: null. +spring.cloud.stream.rocketmq.binder.enable-msg-trace:: +Enable Message Trace feature for all producers and consumers. ++ +Default: `true`. +spring.cloud.stream.rocketmq.binder.customized-trace-topic:: +The trace topic for message trace. ++ +Default: `RMQ_SYS_TRACE_TOPIC`. -:frame: topbot -[width="60%",options="header"] -|==== -^|Configuration ^|Description ^| Default Value -|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.enabled`|Whether to use producer|true -|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.max-message-size`|Maximum bytes of messages sent|0(Take effect only when it’s bigger than 0. The default value of RocketMQ is 4M = 1024 * 1024 * 4) -|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.transactional`|Whether to use `TransactionMQProducer` to send transaction messages|false -|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.executer`|Full class name of the interface implementation class related to `org.apache.rocketmq.client.producer.LocalTransactionExecuter` For example, `org.test.MyExecuter`| -|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.transaction-check-listener`|Full class name of the interface implementation class related to `org.apache.rocketmq.client.producer.TransactionCheckListener` For example, `org.test.MyTransactionCheckListener`| -|==== -Supported Configurations of Consumer: +#### RocketMQ Consumer Properties -:frame: topbot -[width="60%",options="header"] -|==== -^|Configuration ^|Description| Default Value -|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.enabled`|Whether to use consumer|true -|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.tags`|Consumer will only subscribe to messages with these tags Tags are separated by "\|\|" (If not specified, it means the consumer subscribes to all messages)| -|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.sql`|Consumer subscribes to the messages as requested in the SQL(If tags are also specified, SQL has a higher priority than tags.)| -|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.broadcasting`|If the consumer uses the broadcasting mode|false -|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.orderly`|Ordered message consumption or asychronous consumption|false -|==== +The following properties are available for RocketMQ producers only and must be prefixed with `spring.cloud.stream.rocketmq.bindings..consumer.`. -### Endpoint Support +enable:: +Enable Consumer Binding. ++ +Default: `true`. +tags:: +Consumer subscription tags expression, tags split by `||`. ++ +Default: empty. +sql:: +Consumer subscription sql expression. ++ +Default: empty. +broadcasting:: +Control message mode, if you want all subscribers receive message all message, broadcasting is a good choice. ++ +Default: `false`. +orderly:: +Receiving message concurrently or orderly. ++ +Default: `false`. +delayLevelWhenNextConsume:: +Message consume retry strategy for concurrently consume: +* -1,no retry,put into DLQ directly +* 0,broker control retry frequency +* >0,client control retry frequency ++ +Default: `0`. +suspendCurrentQueueTimeMillis:: +Time interval of message consume retry for orderly consume. ++ +Default: `1000`. -Before you use the Endpoint feature, please add the `spring-boot-starter-actuator` dependency in Maven, and enable access of Endpoints in your configuration. +#### RocketMQ Provider Properties -* Add `management.security.enabled=false`in Spring Boot 1.x. The exposed endpoint path is `/rocketmq_binder` -* Add `management.endpoints.web.exposure.include=*`in Spring Boot 2.x. The exposed endpoint path is `/actuator/rocketmq-binder` +The following properties are available for RocketMQ producers only and must be prefixed with `spring.cloud.stream.rocketmq.bindings..producer.`. -Endpoint will collects data about the last message that is sent, the number of successes or failures of message sending, and the number of successes of failures of message consumption. - -```json -{ - "runtime": { - "lastSend.timestamp": 1542786623915 - }, - "metrics": { - "scs-rocketmq.consumer.test-topic.totalConsumed": { - "count": 11 - }, - "scs-rocketmq.consumer.test-topic.totalConsumedFailures": { - "count": 0 - }, - "scs-rocketmq.producer.test-topic.totalSentFailures": { - "count": 0 - }, - "scs-rocketmq.consumer.test-topic.consumedPerSecond": { - "count": 11, - "fifteenMinuteRate": 0.012163847780107841, - "fiveMinuteRate": 0.03614605351360527, - "meanRate": 0.3493213353657594, - "oneMinuteRate": 0.17099243039490175 - }, - "scs-rocketmq.producer.test-topic.totalSent": { - "count": 5 - }, - "scs-rocketmq.producer.test-topic.sentPerSecond": { - "count": 5, - "fifteenMinuteRate": 0.005540151995103271, - "fiveMinuteRate": 0.01652854617838251, - "meanRate": 0.10697493212602836, - "oneMinuteRate": 0.07995558537067671 - }, - "scs-rocketmq.producer.test-topic.sentFailuresPerSecond": { - "count": 0, - "fifteenMinuteRate": 0.0, - "fiveMinuteRate": 0.0, - "meanRate": 0.0, - "oneMinuteRate": 0.0 - }, - "scs-rocketmq.consumer.test-topic.consumedFailuresPerSecond": { - "count": 0, - "fifteenMinuteRate": 0.0, - "fiveMinuteRate": 0.0, - "meanRate": 0.0, - "oneMinuteRate": 0.0 - } - } -} -``` - -Note: To view statistics, add the https://mvnrepository.com/artifact/io.dropwizard.metrics/metrics-core[metrics-core dependency] in POM. If not added, the endpoint will return warning instead of statistics: - -```json -{ - "warning": "please add metrics-core dependency, we use it for metrics" -} -``` \ No newline at end of file +enable:: +Enable Producer Binding. ++ +Default: `true`. +group:: +Producer group name. ++ +Default: empty. +maxMessageSize:: +Maximum allowed message size in bytes. ++ +Default: `8249344`. +transactional:: +Send Transactional Message. ++ +Default: `false`. +sync:: +Send message in synchronous mode. ++ +Default: `false`. +vipChannelEnabled:: +Send message with vip channel. ++ +Default: `true`. +sendMessageTimeout:: +Millis of send message timeout. ++ +Default: `3000`. +compressMessageBodyThreshold:: +Compress message body threshold, namely, message body larger than 4k will be compressed on default. ++ +Default: `4096`. +retryTimesWhenSendFailed:: +Maximum number of retry to perform internally before claiming sending failure in synchronous mode. ++ +Default: `2`. +retryTimesWhenSendAsyncFailed:: +Maximum number of retry to perform internally before claiming sending failure in asynchronous mode. ++ +Default: `2`. +retryNextServer:: +Indicate whether to retry another broker on sending failure internally. ++ +Default: `false`. \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/schedulerx.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/schedulerx.adoc index 27d5e89d..2c95df6b 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/schedulerx.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/schedulerx.adoc @@ -4,24 +4,7 @@ SchedulerX(Distributed job scheduling) is a component of EDAS, an Alibaba Cl === How to Introduce Spring Cloud Alibaba Cloud SchedulerX -We’ve released Spring Cloud Alibaba version 0.2.1. You will need to add dependency management POM first. - -[source,xml] ----- - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.2.BUILD-SNAPSHOT - pom - import - - - ----- - -Next we need to introduce Spring Cloud AliCloud SchedulerX Starter. +If you want to use SchedulerX in your project, please use the starter with the group ID as `org.springframework.cloud` and the artifact ID as `spring-cloud-starter-alicloud-schedulerX`. [source,xml] ---- diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/sentinel.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/sentinel.adoc index 4cfb6bff..a2a828b2 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/sentinel.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/sentinel.adoc @@ -265,7 +265,7 @@ The following table shows all the configurations of Spring Cloud Alibaba Sentine ^|Configuration ^|Description ^|Default Value |`spring.cloud.sentinel.enabled`|Whether Sentinel automatic configuration takes effect|true |`spring.cloud.sentinel.eager`|Cancel Sentinel dashboard lazy load|false -|`spring.cloud.sentinel.transport.port`|Port for the application to interact with Sentinel dashboard. An HTTP Server which uses this port will be started in the application|8721 +|`spring.cloud.sentinel.transport.port`|Port for the application to interact with Sentinel dashboard. An HTTP Server which uses this port will be started in the application|8719 |`spring.cloud.sentinel.transport.dashboard`|Sentinel dashboard address| |`spring.cloud.sentinel.transport.heartbeatIntervalMs`|Hearbeat interval between the application and Sentinel dashboard| |`spring.cloud.sentinel.transport.client-ip`|Client IP| diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/sms.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/sms.adoc index 61852861..0bc1f758 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/sms.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/sms.adoc @@ -6,24 +6,7 @@ Spring Cloud AliCloud SMS provide an easier-to-use API for quick access to Aliba === 如何引入 Spring Cloud AliCloud SMS -We’ve released Spring Cloud Alibaba version 0.2.0. You will need to add dependency management POM first. - -[source,xml] ----- - - - - org.springframework.cloud - spring-cloud-alibaba-dependencies - 0.2.0.RELEASE - pom - import - - - ----- - -Next we need to introduce Spring Cloud Alibaba Cloud SMS Starter. +If you want to use SMS in your project, please use the starter with the group ID as `org.springframework.cloud` and the artifact ID as `spring-cloud-starter-alicloud-sms`. [source,xml] ---- diff --git a/spring-cloud-alibaba-dubbo/pom.xml b/spring-cloud-alibaba-dubbo/pom.xml index e29f655e..df606fd6 100644 --- a/spring-cloud-alibaba-dubbo/pom.xml +++ b/spring-cloud-alibaba-dubbo/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud spring-cloud-alibaba @@ -14,7 +14,7 @@ Spring Cloud Alibaba Dubbo - 2.7.0 + 2.7.1 2.1.0.RELEASE 2.1.0.RELEASE 4.0.1 diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/annotation/DubboTransported.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/annotation/DubboTransported.java index 2714923d..f5cfe644 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/annotation/DubboTransported.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/annotation/DubboTransported.java @@ -16,6 +16,11 @@ */ package org.springframework.cloud.alibaba.dubbo.annotation; +import org.apache.dubbo.common.Constants; +import org.apache.dubbo.config.annotation.Reference; +import org.apache.dubbo.rpc.ExporterListener; +import org.apache.dubbo.rpc.Filter; + import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.client.RestTemplate; @@ -26,6 +31,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import static org.apache.dubbo.common.Constants.DEFAULT_RETRIES; + /** * {@link DubboTransported @DubboTransported} annotation indicates that the traditional Spring Cloud Service-to-Service call is transported * by Dubbo under the hood, there are two main scenarios: @@ -64,4 +71,80 @@ public @interface DubboTransported { * @return the default cluster is "failover" */ String cluster() default "${dubbo.transport.cluster:failover}"; + + /** + * Whether to reconnect if connection is lost, if not specify, reconnect is enabled by default, and the interval + * for retry connecting is 2000 ms + * + * @see Constants#DEFAULT_RECONNECT_PERIOD + * @see Reference#reconnect() + */ + String reconnect() default "${dubbo.transport.reconnect:2000}"; + + /** + * Maximum connections service provider can accept, default value is 0 - connection is shared + * + * @see Reference#connections() + */ + int connections() default 0; + + /** + * Service invocation retry times + * + * @see Constants#DEFAULT_RETRIES + * @see Reference#retries() + */ + int retries() default DEFAULT_RETRIES; + + /** + * Load balance strategy, legal values include: random, roundrobin, leastactive + * + * @see Constants#DEFAULT_LOADBALANCE + * @see Reference#loadbalance() + */ + String loadbalance() default "${dubbo.transport.loadbalance:}"; + + /** + * Maximum active requests allowed, default value is 0 + * + * @see Reference#actives() + */ + int actives() default 0; + + /** + * Timeout value for service invocation, default value is 0 + * + * @see Reference#timeout() + */ + int timeout() default 0; + + /** + * Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache + * + * @see Reference#cache() + */ + String cache() default "${dubbo.transport.cache:}"; + + /** + * Filters for service invocation + * + * @see Filter + * @see Reference#filter() + */ + String[] filter() default {}; + + /** + * Listeners for service exporting and unexporting + * + * @see ExporterListener + * @see Reference#listener() + */ + String[] listener() default {}; + + /** + * Customized parameter key-value pair, for example: {key1, value1, key2, value2} + * + * @see Reference#parameters() + */ + String[] parameters() default {}; } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java index e97acfca..a6afc5a9 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java @@ -28,8 +28,8 @@ import org.springframework.boot.context.event.ApplicationStartedEvent; import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported; import org.springframework.cloud.alibaba.dubbo.client.loadbalancer.DubboMetadataInitializerInterceptor; import org.springframework.cloud.alibaba.dubbo.client.loadbalancer.DubboTransporterInterceptor; -import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository; +import org.springframework.cloud.alibaba.dubbo.metadata.resolver.DubboTransportedAttributesResolver; import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory; import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory; import org.springframework.cloud.client.loadbalancer.LoadBalanced; @@ -86,6 +86,7 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass private ClassLoader classLoader; + /** * Adapt the {@link RestTemplate} beans that are annotated {@link LoadBalanced @LoadBalanced} and * {@link LoadBalanced @LoadBalanced} when Spring Boot application started @@ -94,9 +95,12 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass */ @EventListener(ApplicationStartedEvent.class) public void adaptRestTemplates() { + + DubboTransportedAttributesResolver attributesResolver = new DubboTransportedAttributesResolver(environment); + for (Map.Entry entry : restTemplates.entrySet()) { String beanName = entry.getKey(); - Map dubboTranslatedAttributes = getDubboTranslatedAttributes(beanName); + Map dubboTranslatedAttributes = getDubboTranslatedAttributes(beanName, attributesResolver); if (!CollectionUtils.isEmpty(dubboTranslatedAttributes)) { adaptRestTemplate(entry.getValue(), dubboTranslatedAttributes); } @@ -107,10 +111,12 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass * Gets the annotation attributes {@link RestTemplate} bean being annotated * {@link DubboTransported @DubboTransported} * - * @param beanName the bean name of {@link LoadBalanced @LoadBalanced} {@link RestTemplate} + * @param beanName the bean name of {@link LoadBalanced @LoadBalanced} {@link RestTemplate} + * @param attributesResolver {@link DubboTransportedAttributesResolver} * @return non-null {@link Map} */ - private Map getDubboTranslatedAttributes(String beanName) { + private Map getDubboTranslatedAttributes(String beanName, + DubboTransportedAttributesResolver attributesResolver) { Map attributes = Collections.emptyMap(); BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); if (beanDefinition instanceof AnnotatedBeanDefinition) { @@ -119,7 +125,7 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass attributes = factoryMethodMetadata != null ? factoryMethodMetadata.getAnnotationAttributes(DUBBO_TRANSPORTED_CLASS_NAME) : Collections.emptyMap(); } - return attributes; + return attributesResolver.resolve(attributes); } @@ -132,8 +138,6 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass */ private void adaptRestTemplate(RestTemplate restTemplate, Map dubboTranslatedAttributes) { - DubboTransportedMetadata dubboTransportedMetadata = buildDubboTransportedMetadata(dubboTranslatedAttributes); - List interceptors = new ArrayList<>(restTemplate.getInterceptors()); int index = interceptors.indexOf(loadBalancerInterceptor); @@ -144,21 +148,11 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass interceptors.add(index++, new DubboMetadataInitializerInterceptor(repository)); interceptors.add(index++, new DubboTransporterInterceptor(repository, restTemplate.getMessageConverters(), - classLoader, dubboTransportedMetadata, serviceFactory, contextFactory)); + classLoader, dubboTranslatedAttributes, serviceFactory, contextFactory)); restTemplate.setInterceptors(interceptors); } - private DubboTransportedMetadata buildDubboTransportedMetadata(Map dubboTranslatedAttributes) { - DubboTransportedMetadata dubboTransportedMetadata = new DubboTransportedMetadata(); - String protocol = (String) dubboTranslatedAttributes.get("protocol"); - String cluster = (String) dubboTranslatedAttributes.get("cluster"); - // resolve placeholders - dubboTransportedMetadata.setProtocol(environment.resolvePlaceholders(protocol)); - dubboTransportedMetadata.setCluster(environment.resolvePlaceholders(cluster)); - return dubboTransportedMetadata; - } - @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java index 9464e211..7eff2f81 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java @@ -16,24 +16,34 @@ */ package org.springframework.cloud.alibaba.dubbo.autoconfigure; -import feign.Contract; import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.spring.ServiceBean; +import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent; + +import feign.Contract; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.event.ApplicationFailedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.cloud.alibaba.dubbo.metadata.DubboProtocolConfigSupplier; import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository; import org.springframework.cloud.alibaba.dubbo.metadata.resolver.DubboServiceBeanMetadataResolver; import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolver; +import org.springframework.cloud.alibaba.dubbo.registry.event.ServiceInstancePreRegisteredEvent; import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory; +import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigServiceExporter; import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigServiceProxy; import org.springframework.cloud.alibaba.dubbo.service.PublishingDubboMetadataConfigService; +import org.springframework.cloud.alibaba.dubbo.util.JSONUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.event.EventListener; import java.util.Collection; -import java.util.Iterator; - -import static com.alibaba.dubbo.common.Constants.DEFAULT_PROTOCOL; +import java.util.function.Supplier; /** * Spring Boot Auto-Configuration class for Dubbo Metadata @@ -41,10 +51,20 @@ import static com.alibaba.dubbo.common.Constants.DEFAULT_PROTOCOL; * @author Mercy */ @Configuration -@Import({DubboServiceMetadataRepository.class, PublishingDubboMetadataConfigService.class}) +@Import({DubboServiceMetadataRepository.class, + PublishingDubboMetadataConfigService.class, + DubboMetadataConfigServiceExporter.class, + JSONUtils.class}) public class DubboMetadataAutoConfiguration { - public static final String METADATA_PROTOCOL_BEAN_NAME = "metadata"; + @Autowired + private PublishingDubboMetadataConfigService dubboMetadataConfigService; + + @Autowired + private MetadataResolver metadataResolver; + + @Autowired + private DubboMetadataConfigServiceExporter dubboMetadataConfigServiceExporter; @Bean @ConditionalOnMissingBean @@ -52,35 +72,9 @@ public class DubboMetadataAutoConfiguration { return new DubboServiceBeanMetadataResolver(contract); } - /** - * Build an alias Bean for {@link ProtocolConfig} - * - * @param protocols {@link ProtocolConfig} Beans - * @return {@link ProtocolConfig} bean - */ - @Bean(name = METADATA_PROTOCOL_BEAN_NAME) - public ProtocolConfig protocolConfig(Collection protocols) { - ProtocolConfig protocolConfig = null; - for (ProtocolConfig protocol : protocols) { - String protocolName = protocol.getName(); - if (DEFAULT_PROTOCOL.equals(protocolName)) { - protocolConfig = protocol; - break; - } - } - - if (protocolConfig == null) { // If The ProtocolConfig bean named "dubbo" is absent, take first one of them - Iterator iterator = protocols.iterator(); - protocolConfig = iterator.hasNext() ? iterator.next() : null; - } - - if (protocolConfig == null) { - protocolConfig = new ProtocolConfig(); - protocolConfig.setName(DEFAULT_PROTOCOL); - protocolConfig.setPort(20880); - } - - return protocolConfig; + @Bean + public Supplier dubboProtocolConfigSupplier(ObjectProvider> protocols) { + return new DubboProtocolConfigSupplier(protocols); } @Bean @@ -88,4 +82,41 @@ public class DubboMetadataAutoConfiguration { public DubboMetadataConfigServiceProxy dubboMetadataConfigServiceProxy(DubboGenericServiceFactory factory) { return new DubboMetadataConfigServiceProxy(factory); } + + // Event-Handling + + @EventListener(ServiceBeanExportedEvent.class) + public void onServiceBeanExported(ServiceBeanExportedEvent event) { + ServiceBean serviceBean = event.getServiceBean(); + publishServiceRestMetadata(serviceBean); + } + + /** + * On Dubbo Service registering in Spring Cloud Registry + */ + @EventListener(ServiceInstancePreRegisteredEvent.class) + public void onServiceInstancePreRegistered() { + dubboMetadataConfigServiceExporter.export(); + } + + @EventListener(ApplicationReadyEvent.class) + public void onApplicationReady() { + // Maybe invoke again if used Spring Cloud Registry, + // but don't worry. + dubboMetadataConfigServiceExporter.export(); + } + + @EventListener(ApplicationFailedEvent.class) + public void onApplicationFailed() { + dubboMetadataConfigServiceExporter.unexport(); + } + + @EventListener(ContextClosedEvent.class) + public void onContextClosed() { + dubboMetadataConfigServiceExporter.unexport(); + } + + private void publishServiceRestMetadata(ServiceBean serviceBean) { + dubboMetadataConfigService.publishServiceRestMetadata(metadataResolver.resolveServiceRestMetadata(serviceBean)); + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataEventHandlingAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataEventHandlingAutoConfiguration.java deleted file mode 100644 index 049a2bcf..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataEventHandlingAutoConfiguration.java +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.alibaba.dubbo.autoconfigure; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.config.ApplicationConfig; -import org.apache.dubbo.config.ProtocolConfig; -import org.apache.dubbo.config.ServiceConfig; -import org.apache.dubbo.config.spring.ServiceBean; -import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent; -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.annotation.Value; -import org.springframework.boot.ApplicationRunner; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication; -import org.springframework.boot.context.event.ApplicationFailedEvent; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolver; -import org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactory; -import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigService; -import org.springframework.cloud.alibaba.dubbo.service.PublishingDubboMetadataConfigService; -import org.springframework.cloud.client.DefaultServiceInstance; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.cloud.client.serviceregistry.ServiceRegistry; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.event.ContextClosedEvent; -import org.springframework.context.event.EventListener; -import org.springframework.util.StringUtils; - -import java.util.HashMap; -import java.util.List; - -import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration.METADATA_PROTOCOL_BEAN_NAME; - -/** - * The Auto-Configuration class for Dubbo metadata {@link EventListener event handling}. - * - * @author Mercy - */ -@AutoConfigureAfter(value = {DubboMetadataAutoConfiguration.class}) -@Configuration -public class DubboMetadataEventHandlingAutoConfiguration { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - @Autowired - private MetadataResolver metadataResolver; - - @Autowired - private PublishingDubboMetadataConfigService dubboMetadataConfigService; - - @Autowired - private ApplicationConfig applicationConfig; - - @Autowired - @Qualifier(METADATA_PROTOCOL_BEAN_NAME) - private ProtocolConfig metadataProtocolConfig; - - @Autowired - private ConfigurableApplicationContext context; - - @Value("${spring.application.name:application}") - private String currentApplicationName; - - /** - * The ServiceConfig of DubboMetadataConfigService to be exported, can be nullable. - */ - private ServiceConfig serviceConfig; - - private ServiceInstance restServiceInstance; - - @EventListener(ServiceBeanExportedEvent.class) - public void onServiceBeanExported(ServiceBeanExportedEvent event) { - ServiceBean serviceBean = event.getServiceBean(); - publishServiceRestMetadata(serviceBean); - setRestServiceInstance(serviceBean); - } - - private void setRestServiceInstance(ServiceBean serviceBean) { - List urls = serviceBean.getExportedUrls(); - urls.stream() - .filter(url -> "rest".equalsIgnoreCase(url.getProtocol())) - .forEach(url -> { - String host = url.getIp(); - int port = url.getPort(); - - if (restServiceInstance == null) { - String instanceId = currentApplicationName + "-" + host + ":" + port; - this.restServiceInstance = new DefaultServiceInstance(instanceId, currentApplicationName, - host, port, false, new HashMap<>()); - } else { - - if (!host.equals(restServiceInstance.getHost())) { - if (logger.isWarnEnabled()) { - logger.warn("Current application[{}] host is not consistent, expected: {}, actual: {}", - currentApplicationName, restServiceInstance.getHost(), host); - } - } - - if (port != restServiceInstance.getPort()) { - if (logger.isWarnEnabled()) { - logger.warn("Current application[{}] port is not consistent, expected: {}, actual: {}", - currentApplicationName, restServiceInstance.getPort(), port); - } - } - } - }); - } - - @EventListener(ApplicationReadyEvent.class) - public void onApplicationReady() { - exportDubboMetadataConfigService(); - } - - @EventListener(ApplicationFailedEvent.class) - public void onApplicationFailed() { - unexportDubboMetadataConfigService(); - } - - @EventListener(ContextClosedEvent.class) - public void onContextClosed() { - unexportDubboMetadataConfigService(); - } - - @ConditionalOnNotWebApplication - @Bean - public ApplicationRunner applicationRunner() { - return args -> { - - if (restServiceInstance == null) { - return; - } - - // From RegistrationFactoryProvider - RegistrationFactory registrationFactory = context.getBean(RegistrationFactory.class); - - ServiceRegistry serviceRegistry = context.getBean(ServiceRegistry.class); - - Registration registration = context.getBean(Registration.class); - - restServiceInstance.getMetadata().putAll(registration.getMetadata()); - - serviceRegistry.register(registrationFactory.create(restServiceInstance, context)); - }; - } - - private void publishServiceRestMetadata(ServiceBean serviceBean) { - dubboMetadataConfigService.publishServiceRestMetadata(metadataResolver.resolveServiceRestMetadata(serviceBean)); - } - - private void exportDubboMetadataConfigService() { - - if (serviceConfig != null && serviceConfig.isExported()) { - return; - } - - if (StringUtils.isEmpty(dubboMetadataConfigService.getServiceRestMetadata())) { - // If there is no REST metadata, DubboMetadataConfigService will not be exported. - if (logger.isInfoEnabled()) { - logger.info("There is no REST metadata, the Dubbo service[{}] will not be exported.", - dubboMetadataConfigService.getClass().getName()); - } - return; - } - - serviceConfig = new ServiceConfig<>(); - - serviceConfig.setInterface(DubboMetadataConfigService.class); - // Use current Spring application name as the Dubbo Service version - serviceConfig.setVersion(currentApplicationName); - serviceConfig.setRef(dubboMetadataConfigService); - serviceConfig.setApplication(applicationConfig); - serviceConfig.setProtocol(metadataProtocolConfig); - - serviceConfig.export(); - - if (logger.isInfoEnabled()) { - logger.info("The Dubbo service[{}] has been exported.", serviceConfig.toString()); - } - } - - private void unexportDubboMetadataConfigService() { - - if (serviceConfig == null || serviceConfig.isUnexported()) { - return; - } - - serviceConfig.unexport(); - - if (logger.isInfoEnabled()) { - logger.info("The Dubbo service[{}] has been unexported.", serviceConfig.toString()); - } - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceAutoConfiguration.java index 88da964d..c4abb94e 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceAutoConfiguration.java @@ -18,18 +18,15 @@ package org.springframework.cloud.alibaba.dubbo.autoconfigure; import org.apache.dubbo.common.utils.Assert; import org.apache.dubbo.config.spring.util.PropertySourcesUtils; + import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.source.ConfigurationPropertySources; -import org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactoryProvider; -import org.springframework.cloud.alibaba.dubbo.registry.handler.DubboRegistryServiceIdHandler; -import org.springframework.cloud.alibaba.dubbo.registry.handler.StandardDubboRegistryServiceIdHandler; import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory; import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory; import org.springframework.cloud.alibaba.dubbo.service.parameter.PathVariableServiceParameterResolver; import org.springframework.cloud.alibaba.dubbo.service.parameter.RequestBodyServiceParameterResolver; import org.springframework.cloud.alibaba.dubbo.service.parameter.RequestHeaderServiceParameterResolver; import org.springframework.cloud.alibaba.dubbo.service.parameter.RequestParamServiceParameterResolver; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -71,17 +68,6 @@ public class DubboServiceAutoConfiguration { static class ParameterResolversConfiguration { } - @Bean - @ConditionalOnMissingBean - public DubboRegistryServiceIdHandler dubboRegistryServiceIdHandler(ConfigurableApplicationContext context) { - return new StandardDubboRegistryServiceIdHandler(context); - } - - @Bean - public RegistrationFactoryProvider registrationFactoryProvider() { - return new RegistrationFactoryProvider(); - } - /** * Bugfix code for an issue : https://github.com/apache/incubator-dubbo-spring-boot-project/issues/459 * diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationAutoConfiguration.java new file mode 100644 index 00000000..c926fd6b --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationAutoConfiguration.java @@ -0,0 +1,183 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.autoconfigure; + +import org.apache.dubbo.common.URL; + +import com.ecwid.consul.v1.agent.model.NewService; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository; +import org.springframework.cloud.alibaba.dubbo.registry.DubboServiceRegistrationEventPublishingAspect; +import org.springframework.cloud.alibaba.dubbo.registry.event.ServiceInstancePreRegisteredEvent; +import org.springframework.cloud.alibaba.dubbo.util.JSONUtils; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.cloud.consul.serviceregistry.ConsulRegistration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.event.EventListener; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_CONFIGURATION_CLASS_NAME; +import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.EUREKA_AUTO_CONFIGURATION_CLASS_NAME; +import static org.springframework.cloud.alibaba.dubbo.registry.DubboServiceRegistrationEventPublishingAspect.REGISTER_POINTCUT_EXPRESSION; +import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.DUBBO_URLS_METADATA_PROPERTY_NAME; + +/** + * Dubbo Service Registration Auto-{@link Configuration} + * + * @author Mercy + */ +@Configuration +@Import({DubboServiceRegistrationEventPublishingAspect.class}) +@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) +@AutoConfigureAfter(name = { + EUREKA_AUTO_CONFIGURATION_CLASS_NAME, + CONSUL_AUTO_CONFIGURATION_CLASS_NAME, + "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration" +}, value = { + DubboMetadataAutoConfiguration.class +}) +public class DubboServiceRegistrationAutoConfiguration { + + public static final String EUREKA_AUTO_CONFIGURATION_CLASS_NAME = + "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"; + + public static final String CONSUL_AUTO_CONFIGURATION_CLASS_NAME = + "org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration"; + + public static final String CONSUL_AUTO_REGISTRATION_CLASS_NAME = + "org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration"; + + private static final Logger logger = LoggerFactory.getLogger(DubboServiceRegistrationAutoConfiguration.class); + + @Autowired + private DubboServiceMetadataRepository dubboServiceMetadataRepository; + + @Autowired + private JSONUtils jsonUtils; + + + @EventListener(ServiceInstancePreRegisteredEvent.class) + public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { + Registration registration = event.getSource(); + attachURLsIntoMetadata(registration); + } + + @Configuration + @ConditionalOnBean(name = EUREKA_AUTO_CONFIGURATION_CLASS_NAME) + @AutoConfigureOrder + @Aspect + class EurekaConfiguration { + + @Autowired + private ServiceRegistry serviceRegistry; + + private Registration registration; + + private boolean deferredRegistration = true; + + @Around(REGISTER_POINTCUT_EXPRESSION) + public Object doRegister(ProceedingJoinPoint pjp, Registration registration) throws Throwable { + this.registration = registration; + if (deferredRegistration) { + return null; + } + return pjp.proceed(pjp.getArgs()); + } + + @EventListener(ApplicationStartedEvent.class) + public void onApplicationStarted() { + deferredRegistration = false; + serviceRegistry.register(registration); + } + + } + + @Configuration + @ConditionalOnBean(name = CONSUL_AUTO_CONFIGURATION_CLASS_NAME) + @AutoConfigureOrder + class ConsulConfiguration { + + /** + * Handle the pre-registered event of {@link ServiceInstance} for Consul + * + * @param event {@link ServiceInstancePreRegisteredEvent} + */ + @EventListener(ServiceInstancePreRegisteredEvent.class) + public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { + Registration registration = event.getSource(); + Class registrationClass = AopUtils.getTargetClass(registration); + String registrationClassName = registrationClass.getName(); + if (CONSUL_AUTO_REGISTRATION_CLASS_NAME.equalsIgnoreCase(registrationClassName)) { + ConsulRegistration consulRegistration = (ConsulRegistration) registration; + attachURLsIntoMetadata(consulRegistration); + } + } + + private void attachURLsIntoMetadata(ConsulRegistration consulRegistration) { + NewService newService = consulRegistration.getService(); + String dubboURLsJson = getDubboURLsJSON(); + if (StringUtils.hasText(dubboURLsJson)) { + List tags = newService.getTags(); + tags.add(DUBBO_URLS_METADATA_PROPERTY_NAME + "=" + dubboURLsJson); + } + } + } + + private void attachURLsIntoMetadata(Registration registration) { + if (registration == null) { + return; + } + Map metadata = registration.getMetadata(); + String dubboURLsJson = getDubboURLsJSON(); + if (StringUtils.hasText(dubboURLsJson)) { + metadata.put(DUBBO_URLS_METADATA_PROPERTY_NAME, dubboURLsJson); + } + } + + private String getDubboURLsJSON() { + Collection urls = dubboServiceMetadataRepository.getRegisteredUrls(); + if (CollectionUtils.isEmpty(urls)) { + if (logger.isDebugEnabled()) { + logger.debug("There is no registered URL to attach into metadata."); + } + return null; + } + return jsonUtils.toJSON(urls.stream().map(URL::toFullString).collect(Collectors.toList())); + } + + +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationNonWebApplicationAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationNonWebApplicationAutoConfiguration.java new file mode 100644 index 00000000..3f69e7ad --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationNonWebApplicationAutoConfiguration.java @@ -0,0 +1,156 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.autoconfigure; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.config.spring.ServiceBean; +import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent; + +import com.ecwid.consul.v1.agent.model.NewService; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.alibaba.dubbo.registry.event.ServiceInstancePreRegisteredEvent; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration; +import org.springframework.cloud.consul.serviceregistry.ConsulRegistration; +import org.springframework.cloud.netflix.eureka.CloudEurekaInstanceConfig; +import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.EventListener; + +import java.util.List; + +import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_CONFIGURATION_CLASS_NAME; +import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.EUREKA_AUTO_CONFIGURATION_CLASS_NAME; + +/** + * Dubbo Service Registration Auto-{@link Configuration} for Non-Web application + * + * @author Mercy + */ +@Configuration +@ConditionalOnNotWebApplication +@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) +@AutoConfigureAfter(DubboServiceRegistrationAutoConfiguration.class) +@Aspect +public class DubboServiceRegistrationNonWebApplicationAutoConfiguration { + + @Autowired + private ServiceRegistry serviceRegistry; + + @Autowired + private Registration registration; + + private volatile Integer webPort = null; + + private volatile boolean registered = false; + + @Around("execution(* org.springframework.cloud.client.serviceregistry.Registration.getPort())") + public Object getPort(ProceedingJoinPoint pjp) throws Throwable { + return webPort != null ? webPort : pjp.proceed(); + } + + @EventListener(ServiceBeanExportedEvent.class) + public void onServiceBeanExported(ServiceBeanExportedEvent event) { + setWebPort(event.getServiceBean()); + register(); + } + + private void register() { + if (registered) { + return; + } + serviceRegistry.register(registration); + registered = true; + } + + /** + * Set web port from {@link ServiceBean#getExportedUrls() exported URLs} if "rest" protocol is present. + * + * @param serviceBean {@link ServiceBean} + */ + private void setWebPort(ServiceBean serviceBean) { + if (webPort == null) { + List urls = serviceBean.getExportedUrls(); + urls.stream() + .filter(url -> "rest".equalsIgnoreCase(url.getProtocol())) + .findFirst() + .ifPresent(url -> { + webPort = url.getPort(); + }); + } + } + + @Configuration + @ConditionalOnBean(name = EUREKA_AUTO_CONFIGURATION_CLASS_NAME) + @AutoConfigureOrder + static class EurekaConfiguration { + + @EventListener(ServiceInstancePreRegisteredEvent.class) + public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { + setPort(event.getSource()); + } + + private void setPort(Registration registration) { + EurekaRegistration eurekaRegistration = (EurekaRegistration) registration; + CloudEurekaInstanceConfig cloudEurekaInstanceConfig = eurekaRegistration.getInstanceConfig(); + cloudEurekaInstanceConfig.setNonSecurePort(registration.getPort()); + } + + } + + @Configuration + @ConditionalOnBean(name = CONSUL_AUTO_CONFIGURATION_CLASS_NAME) + @AutoConfigureOrder + class ConsulConfiguration { + + /** + * Handle the pre-registered event of {@link ServiceInstance} for Consul + * + * @param event {@link ServiceInstancePreRegisteredEvent} + */ + @EventListener(ServiceInstancePreRegisteredEvent.class) + public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { + Registration registration = event.getSource(); + ConsulAutoRegistration consulRegistration = (ConsulAutoRegistration) registration; + setPort(consulRegistration); + } + + /** + * Set port on Non-Web Application + * + * @param consulRegistration {@link ConsulRegistration} + */ + private void setPort(ConsulAutoRegistration consulRegistration) { + int port = consulRegistration.getPort(); + NewService newService = consulRegistration.getService(); + if (newService.getPort() == null) { + newService.setPort(port); + } + } + } + +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboTransporterInterceptor.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboTransporterInterceptor.java index 0d2864fc..4860a3c3 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboTransporterInterceptor.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboTransporterInterceptor.java @@ -18,9 +18,9 @@ package org.springframework.cloud.alibaba.dubbo.client.loadbalancer; import org.apache.dubbo.rpc.service.GenericException; import org.apache.dubbo.rpc.service.GenericService; + import org.springframework.cloud.alibaba.dubbo.http.MutableHttpServerRequest; -import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata; -import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata; +import org.springframework.cloud.alibaba.dubbo.metadata.DubboRestServiceMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository; @@ -57,7 +57,7 @@ public class DubboTransporterInterceptor implements ClientHttpRequestInterceptor private final DubboClientHttpResponseFactory clientHttpResponseFactory; - private final DubboTransportedMetadata dubboTransportedMetadata; + private final Map dubboTranslatedAttributes; private final DubboGenericServiceFactory serviceFactory; @@ -68,11 +68,11 @@ public class DubboTransporterInterceptor implements ClientHttpRequestInterceptor public DubboTransporterInterceptor(DubboServiceMetadataRepository dubboServiceMetadataRepository, List> messageConverters, ClassLoader classLoader, - DubboTransportedMetadata dubboTransportedMetadata, + Map dubboTranslatedAttributes, DubboGenericServiceFactory serviceFactory, DubboGenericServiceExecutionContextFactory contextFactory) { this.repository = dubboServiceMetadataRepository; - this.dubboTransportedMetadata = dubboTransportedMetadata; + this.dubboTranslatedAttributes = dubboTranslatedAttributes; this.clientHttpResponseFactory = new DubboClientHttpResponseFactory(messageConverters, classLoader); this.serviceFactory = serviceFactory; this.contextFactory = contextFactory; @@ -87,16 +87,16 @@ public class DubboTransporterInterceptor implements ClientHttpRequestInterceptor RequestMetadata clientMetadata = buildRequestMetadata(request); - DubboServiceMetadata dubboServiceMetadata = repository.get(serviceName, clientMetadata); + DubboRestServiceMetadata metadata = repository.get(serviceName, clientMetadata); - if (dubboServiceMetadata == null) { + if (metadata == null) { // if DubboServiceMetadata is not found, executes next return execution.execute(request, body); } - RestMethodMetadata dubboRestMethodMetadata = dubboServiceMetadata.getRestMethodMetadata(); + RestMethodMetadata dubboRestMethodMetadata = metadata.getRestMethodMetadata(); - GenericService genericService = serviceFactory.create(dubboServiceMetadata, dubboTransportedMetadata); + GenericService genericService = serviceFactory.create(metadata, dubboTranslatedAttributes); MutableHttpServerRequest httpServerRequest = new MutableHttpServerRequest(request, body); diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboProtocolConfigSupplier.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboProtocolConfigSupplier.java new file mode 100644 index 00000000..d5d99430 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboProtocolConfigSupplier.java @@ -0,0 +1,67 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.metadata; + +import org.apache.dubbo.config.ProtocolConfig; + +import org.springframework.beans.factory.ObjectProvider; + +import java.util.Collection; +import java.util.Iterator; +import java.util.function.Supplier; + +import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL; + +/** + * Dubbo's {@link ProtocolConfig} {@link Supplier} + * + * @author Mercy + */ +public class DubboProtocolConfigSupplier implements Supplier { + + private final ObjectProvider> protocols; + + public DubboProtocolConfigSupplier(ObjectProvider> protocols) { + this.protocols = protocols; + } + + @Override + public ProtocolConfig get() { + ProtocolConfig protocolConfig = null; + Collection protocols = this.protocols.getIfAvailable(); + for (ProtocolConfig protocol : protocols) { + String protocolName = protocol.getName(); + if (DEFAULT_PROTOCOL.equals(protocolName)) { + protocolConfig = protocol; + break; + } + } + + if (protocolConfig == null) { // If The ProtocolConfig bean named "dubbo" is absent, take first one of them + Iterator iterator = protocols.iterator(); + protocolConfig = iterator.hasNext() ? iterator.next() : null; + } + + if (protocolConfig == null) { + protocolConfig = new ProtocolConfig(); + protocolConfig.setName(DEFAULT_PROTOCOL); + protocolConfig.setPort(20880); + } + + return protocolConfig; + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboServiceMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboRestServiceMetadata.java similarity index 84% rename from spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboServiceMetadata.java rename to spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboRestServiceMetadata.java index f245df09..5c5ef3ad 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboServiceMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboRestServiceMetadata.java @@ -19,17 +19,17 @@ package org.springframework.cloud.alibaba.dubbo.metadata; import java.util.Objects; /** - * Dubbo Service Metadata + * Dubbo Rest Service Metadata * * @author Mercy */ -public class DubboServiceMetadata { +public class DubboRestServiceMetadata { private final ServiceRestMetadata serviceRestMetadata; private final RestMethodMetadata restMethodMetadata; - public DubboServiceMetadata(ServiceRestMetadata serviceRestMetadata, RestMethodMetadata restMethodMetadata) { + public DubboRestServiceMetadata(ServiceRestMetadata serviceRestMetadata, RestMethodMetadata restMethodMetadata) { this.serviceRestMetadata = serviceRestMetadata; this.restMethodMetadata = restMethodMetadata; } @@ -45,8 +45,8 @@ public class DubboServiceMetadata { @Override public boolean equals(Object o) { if (this == o) return true; - if (!(o instanceof DubboServiceMetadata)) return false; - DubboServiceMetadata that = (DubboServiceMetadata) o; + if (!(o instanceof DubboRestServiceMetadata)) return false; + DubboRestServiceMetadata that = (DubboRestServiceMetadata) o; return Objects.equals(serviceRestMetadata, that.serviceRestMetadata) && Objects.equals(restMethodMetadata, that.restMethodMetadata); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMetadata.java deleted file mode 100644 index 2811bdaf..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMetadata.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.alibaba.dubbo.metadata; - -import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported; - -import java.util.Objects; - -/** - * {@link DubboTransported @DubboTransported} Metadata - */ -public class DubboTransportedMetadata { - - private String protocol; - - private String cluster; - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol; - } - - public String getCluster() { - return cluster; - } - - public void setCluster(String cluster) { - this.cluster = cluster; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof DubboTransportedMetadata)) return false; - DubboTransportedMetadata that = (DubboTransportedMetadata) o; - return Objects.equals(protocol, that.protocol) && - Objects.equals(cluster, that.cluster); - } - - @Override - public int hashCode() { - return Objects.hash(protocol, cluster); - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMethodMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMethodMetadata.java index ade65aa9..6e7ab9ac 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMethodMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMethodMetadata.java @@ -20,6 +20,7 @@ import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported; import java.lang.reflect.Method; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -29,29 +30,13 @@ import java.util.Objects; */ public class DubboTransportedMethodMetadata { - private final DubboTransportedMetadata dubboTransportedMetadata; - private final MethodMetadata methodMetadata; - public DubboTransportedMethodMetadata(Method method) { + private final Map attributes; + + public DubboTransportedMethodMetadata(Method method, Map attributes) { this.methodMetadata = new MethodMetadata(method); - this.dubboTransportedMetadata = new DubboTransportedMetadata(); - } - - public String getProtocol() { - return dubboTransportedMetadata.getProtocol(); - } - - public void setProtocol(String protocol) { - dubboTransportedMetadata.setProtocol(protocol); - } - - public String getCluster() { - return dubboTransportedMetadata.getCluster(); - } - - public void setCluster(String cluster) { - dubboTransportedMetadata.setCluster(cluster); + this.attributes = attributes; } public String getName() { @@ -82,25 +67,25 @@ public class DubboTransportedMethodMetadata { return methodMetadata.getMethod(); } - public DubboTransportedMetadata getDubboTransportedMetadata() { - return dubboTransportedMetadata; - } - public MethodMetadata getMethodMetadata() { return methodMetadata; } + public Map getAttributes() { + return attributes; + } + @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof DubboTransportedMethodMetadata)) return false; DubboTransportedMethodMetadata that = (DubboTransportedMethodMetadata) o; - return Objects.equals(dubboTransportedMetadata, that.dubboTransportedMetadata) && - Objects.equals(methodMetadata, that.methodMetadata); + return Objects.equals(methodMetadata, that.methodMetadata) && + Objects.equals(attributes, that.attributes); } @Override public int hashCode() { - return Objects.hash(dubboTransportedMetadata, methodMetadata); + return Objects.hash(methodMetadata, attributes); } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java index e8d21a31..e4a58102 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java @@ -16,13 +16,15 @@ */ package org.springframework.cloud.alibaba.dubbo.metadata.repository; +import org.apache.dubbo.common.URL; + import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.alibaba.dubbo.http.matcher.RequestMetadataMatcher; -import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata; +import org.springframework.cloud.alibaba.dubbo.metadata.DubboRestServiceMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata; import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigService; @@ -30,6 +32,7 @@ import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigServic import org.springframework.http.HttpRequest; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -51,17 +54,31 @@ public class DubboServiceMetadataRepository { private final ObjectMapper objectMapper = new ObjectMapper(); + private final Set registeredURLs = new LinkedHashSet<>(); + /** * Key is application name - * Value is Map + * Value is Map */ - private Map> repository = newHashMap(); + private Map> repository = newHashMap(); @Autowired private DubboMetadataConfigServiceProxy dubboMetadataConfigServiceProxy; + public void registerURL(URL url) { + this.registeredURLs.add(url); + } + + public void unregisterURL(URL url) { + this.registeredURLs.remove(url); + } + + public Collection getRegisteredUrls() { + return Collections.unmodifiableSet(registeredURLs); + } + /** - * Initialize the specified service's Dubbo Service Metadata + * Initialize the specified service's {@link ServiceRestMetadata} * * @param serviceName the service name */ @@ -81,14 +98,14 @@ public class DubboServiceMetadataRepository { return; } - Map metadataMap = getMetadataMap(serviceName); + Map metadataMap = getMetadataMap(serviceName); for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) { serviceRestMetadata.getMeta().forEach(restMethodMetadata -> { RequestMetadata requestMetadata = restMethodMetadata.getRequest(); RequestMetadataMatcher matcher = new RequestMetadataMatcher(requestMetadata); - DubboServiceMetadata metadata = new DubboServiceMetadata(serviceRestMetadata, restMethodMetadata); + DubboRestServiceMetadata metadata = new DubboRestServiceMetadata(serviceRestMetadata, restMethodMetadata); metadataMap.put(matcher, metadata); }); } @@ -99,13 +116,13 @@ public class DubboServiceMetadataRepository { } /** - * Get a {@link DubboServiceMetadata} by the specified service name if {@link RequestMetadata} matched + * Get a {@link DubboRestServiceMetadata} by the specified service name if {@link RequestMetadata} matched * * @param serviceName service name * @param requestMetadata {@link RequestMetadata} to be matched - * @return {@link DubboServiceMetadata} if matched, or null + * @return {@link DubboRestServiceMetadata} if matched, or null */ - public DubboServiceMetadata get(String serviceName, RequestMetadata requestMetadata) { + public DubboRestServiceMetadata get(String serviceName, RequestMetadata requestMetadata) { return match(repository, serviceName, requestMetadata); } @@ -148,7 +165,7 @@ public class DubboServiceMetadataRepository { return object; } - private Map getMetadataMap(String serviceName) { + private Map getMetadataMap(String serviceName) { return getMap(repository, serviceName); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedAttributesResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedAttributesResolver.java new file mode 100644 index 00000000..e403139e --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedAttributesResolver.java @@ -0,0 +1,56 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.metadata.resolver; + +import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported; +import org.springframework.core.env.PropertyResolver; + +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes; + +/** + * {@link DubboTransported} annotation attributes resolver + * + * @author Mercy + */ +public class DubboTransportedAttributesResolver { + + private final PropertyResolver propertyResolver; + + public DubboTransportedAttributesResolver(PropertyResolver propertyResolver) { + this.propertyResolver = propertyResolver; + } + + public Map resolve(DubboTransported dubboTransported) { + Map attributes = getAnnotationAttributes(dubboTransported); + return resolve(attributes); + } + + public Map resolve(Map attributes) { + Map resolvedAttributes = new LinkedHashMap<>(); + for (Map.Entry entry : attributes.entrySet()) { + Object value = entry.getValue(); + if (value instanceof String) { + value = propertyResolver.resolvePlaceholders(value.toString()); + } + resolvedAttributes.put(entry.getKey(), value); + } + return resolvedAttributes; + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java index d1aa6389..3e232440 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java @@ -42,12 +42,12 @@ public class DubboTransportedMethodMetadataResolver { private static final Class DUBBO_TRANSPORTED_CLASS = DubboTransported.class; - private final PropertyResolver propertyResolver; + private final DubboTransportedAttributesResolver attributesResolver; private final Contract contract; public DubboTransportedMethodMetadataResolver(PropertyResolver propertyResolver, Contract contract) { - this.propertyResolver = propertyResolver; + this.attributesResolver = new DubboTransportedAttributesResolver(propertyResolver); this.contract = contract; } @@ -93,12 +93,8 @@ public class DubboTransportedMethodMetadataResolver { private DubboTransportedMethodMetadata createDubboTransportedMethodMetadata(Method method, DubboTransported dubboTransported) { - DubboTransportedMethodMetadata methodMetadata = new DubboTransportedMethodMetadata(method); - String protocol = propertyResolver.resolvePlaceholders(dubboTransported.protocol()); - String cluster = propertyResolver.resolvePlaceholders(dubboTransported.cluster()); - methodMetadata.setProtocol(protocol); - methodMetadata.setCluster(cluster); - return methodMetadata; + Map attributes = attributesResolver.resolve(dubboTransported); + return new DubboTransportedMethodMetadata(method, attributes); } private DubboTransported resolveDubboTransported(Method method) { diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterInvocationHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterInvocationHandler.java index e606367a..fa93bb27 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterInvocationHandler.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterInvocationHandler.java @@ -17,14 +17,14 @@ package org.springframework.cloud.alibaba.dubbo.openfeign; +import org.apache.dubbo.rpc.service.GenericService; + import feign.Contract; import feign.Target; -import org.apache.dubbo.rpc.service.GenericService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported; -import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata; -import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata; +import org.springframework.cloud.alibaba.dubbo.metadata.DubboRestServiceMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMethodMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata; @@ -147,13 +147,13 @@ class TargeterInvocationHandler implements InvocationHandler { for (Map.Entry entry : feignRestMethodMetadataMap.entrySet()) { RestMethodMetadata feignRestMethodMetadata = entry.getValue(); RequestMetadata feignRequestMetadata = feignRestMethodMetadata.getRequest(); - DubboServiceMetadata dubboServiceMetadata = repository.get(serviceName, feignRequestMetadata); - if (dubboServiceMetadata != null) { + DubboRestServiceMetadata metadata = repository.get(serviceName, feignRequestMetadata); + if (metadata != null) { DubboTransportedMethodMetadata dubboTransportedMethodMetadata = entry.getKey(); - DubboTransportedMetadata dubboTransportedMetadata = dubboTransportedMethodMetadata.getDubboTransportedMetadata(); + Map dubboTranslatedAttributes = dubboTransportedMethodMetadata.getAttributes(); Method method = dubboTransportedMethodMetadata.getMethod(); - GenericService dubboGenericService = dubboGenericServiceFactory.create(dubboServiceMetadata, dubboTransportedMetadata); - RestMethodMetadata dubboRestMethodMetadata = dubboServiceMetadata.getRestMethodMetadata(); + GenericService dubboGenericService = dubboGenericServiceFactory.create(metadata, dubboTranslatedAttributes); + RestMethodMetadata dubboRestMethodMetadata = metadata.getRestMethodMetadata(); MethodMetadata methodMetadata = dubboTransportedMethodMetadata.getMethodMetadata(); FeignMethodMetadata feignMethodMetadata = new FeignMethodMetadata(dubboGenericService, dubboRestMethodMetadata, feignRestMethodMetadata); diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractRegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractRegistrationFactory.java deleted file mode 100644 index f17c3019..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractRegistrationFactory.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.alibaba.dubbo.registry; - -import org.apache.dubbo.common.Constants; -import org.apache.dubbo.common.URL; -import org.springframework.cloud.alibaba.dubbo.registry.handler.DubboRegistryServiceIdHandler; -import org.springframework.cloud.client.DefaultServiceInstance; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.context.ConfigurableApplicationContext; - -import java.util.LinkedHashMap; - -/** - * Abstract {@link RegistrationFactory} implementation - *

- * - * @param The subclass of {@link Registration} - * @author Mercy - */ -public abstract class AbstractRegistrationFactory implements RegistrationFactory { - - public final R create(URL url, ConfigurableApplicationContext applicationContext) { - ServiceInstance serviceInstance = createServiceInstance(url, applicationContext); - return create(serviceInstance, applicationContext); - } - - /** - * Create an instance {@link ServiceInstance}. This method maybe override by sub-class. - * - * @param url The Dubbo's {@link URL} - * @param applicationContext {@link ConfigurableApplicationContext} - * @return an instance {@link ServiceInstance} - */ - protected ServiceInstance createServiceInstance(URL url, ConfigurableApplicationContext applicationContext) { - String serviceId = createServiceId(url, applicationContext); - // Append default category if absent - String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY); - URL newURL = url.addParameter(Constants.CATEGORY_KEY, category); - newURL = newURL.addParameter(Constants.PROTOCOL_KEY, url.getProtocol()); - String ip = url.getIp(); - int port = newURL.getParameter(Constants.BIND_PORT_KEY, url.getPort()); - DefaultServiceInstance serviceInstance = new DefaultServiceInstance(url.toIdentityString(), serviceId, ip, port, false); - serviceInstance.getMetadata().putAll(new LinkedHashMap<>(newURL.getParameters())); - return serviceInstance; - } - - protected String createServiceId(URL url, ConfigurableApplicationContext applicationContext) { - DubboRegistryServiceIdHandler handler = applicationContext.getBean(DubboRegistryServiceIdHandler.class); - return handler.createServiceId(url); - } -} - diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractSpringCloudRegistry.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractSpringCloudRegistry.java new file mode 100644 index 00000000..c52cf231 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractSpringCloudRegistry.java @@ -0,0 +1,293 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.registry; + +import org.apache.dubbo.common.Constants; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.UrlUtils; +import org.apache.dubbo.registry.NotifyListener; +import org.apache.dubbo.registry.RegistryFactory; +import org.apache.dubbo.registry.support.FailbackRegistry; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import static java.util.Collections.singleton; +import static org.apache.dubbo.common.Constants.PROVIDER_SIDE; +import static org.apache.dubbo.common.Constants.SIDE_KEY; +import static org.springframework.util.ObjectUtils.isEmpty; + +/** + * Abstract Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud" + * + * @author Mercy + */ +public abstract class AbstractSpringCloudRegistry extends FailbackRegistry { + + /** + * The parameter name of {@link #servicesLookupInterval} + */ + public static final String SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.services.lookup.interval"; + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + /** + * The interval in second of lookup service names(only for Dubbo-OPS) + */ + private final long servicesLookupInterval; + + private final DiscoveryClient discoveryClient; + + protected final ScheduledExecutorService servicesLookupScheduler; + + public AbstractSpringCloudRegistry(URL url, + DiscoveryClient discoveryClient, + ScheduledExecutorService servicesLookupScheduler) { + super(url); + this.servicesLookupInterval = url.getParameter(SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 60L); + this.discoveryClient = discoveryClient; + this.servicesLookupScheduler = servicesLookupScheduler; + } + + protected boolean shouldRegister(URL url) { + String side = url.getParameter(SIDE_KEY); + + boolean should = PROVIDER_SIDE.equals(side); // Only register the Provider. + + if (!should) { + if (logger.isDebugEnabled()) { + logger.debug("The URL[{}] should not be registered.", url.toString()); + } + } + + return should; + } + + @Override + public final void doRegister(URL url) { + if (!shouldRegister(url)) { + return; + } + doRegister0(url); + } + + /** + * The sub-type should implement to register + * + * @param url {@link URL} + */ + protected abstract void doRegister0(URL url); + + @Override + public final void doUnregister(URL url) { + if (!shouldRegister(url)) { + return; + } + doUnregister0(url); + } + + /** + * The sub-type should implement to unregister + * + * @param url {@link URL} + */ + protected abstract void doUnregister0(URL url); + + @Override + public final void doSubscribe(URL url, NotifyListener listener) { + Set serviceNames = getServiceNames(url); + doSubscribe(url, listener, serviceNames); + } + + @Override + public final void doUnsubscribe(URL url, NotifyListener listener) { + if (isAdminProtocol(url)) { + shutdownServiceNamesLookup(); + } + } + + @Override + public boolean isAvailable() { + return !discoveryClient.getServices().isEmpty(); + } + + protected void shutdownServiceNamesLookup() { + if (servicesLookupScheduler != null) { + servicesLookupScheduler.shutdown(); + } + } + + private void filterServiceNames(Collection serviceNames) { + filter(serviceNames, new Filter() { + @Override + public boolean accept(String serviceName) { + return supports(serviceName); + } + }); + } + + protected abstract boolean supports(String serviceName); + + protected final Set getAllServiceNames() { + return new LinkedHashSet<>(discoveryClient.getServices()); + } + + /** + * Get the service names from the specified {@link URL url} + * + * @param url {@link URL} + * @return non-null + */ + private Set getServiceNames(URL url) { + if (isAdminProtocol(url)) { + return getServiceNamesForOps(url); + } else { + return singleton(getServiceName(url)); + } + } + + protected boolean isAdminProtocol(URL url) { + return Constants.ADMIN_PROTOCOL.equals(url.getProtocol()); + } + + /** + * Get the service names for Dubbo OPS + * + * @param url {@link URL} + * @return non-null + */ + protected Set getServiceNamesForOps(URL url) { + Set serviceNames = getAllServiceNames(); + filterServiceNames(serviceNames); + return serviceNames; + } + + protected abstract String getServiceName(URL url); + + private void doSubscribe(final URL url, final NotifyListener listener, final Collection serviceNames) { + + subscribe(url, listener, serviceNames); + + schedule(() -> { + subscribe(url, listener, serviceNames); + }); + } + + protected ScheduledFuture schedule(Runnable runnable) { + return this.servicesLookupScheduler.scheduleAtFixedRate(runnable, servicesLookupInterval, + servicesLookupInterval, TimeUnit.SECONDS); + } + + protected List getServiceInstances(String serviceName) { + return discoveryClient.getInstances(serviceName); + } + + private void subscribe(final URL url, final NotifyListener listener, final Collection serviceNames) { + for (String serviceName : serviceNames) { + List serviceInstances = getServiceInstances(serviceName); + if (!isEmpty(serviceInstances)) { + notifySubscriber(url, listener, serviceInstances); + } + } + } + + /** + * Notify the Healthy {@link ServiceInstance service instance} to subscriber. + * + * @param url {@link URL} + * @param listener {@link NotifyListener} + * @param serviceInstances all {@link ServiceInstance instances} + */ + protected abstract void notifySubscriber(URL url, NotifyListener listener, List serviceInstances); + + protected void filterHealthyInstances(Collection instances) { + filter(instances, new Filter() { + @Override + public boolean accept(ServiceInstance data) { + // TODO check the details of status +// return serviceRegistry.getStatus(new DubboRegistration(data)) != null; + return true; + } + }); + } + + protected List buildURLs(URL consumerURL, Collection serviceInstances) { + if (serviceInstances.isEmpty()) { + return Collections.emptyList(); + } + List urls = new LinkedList(); + for (ServiceInstance serviceInstance : serviceInstances) { + URL url = buildURL(serviceInstance); + if (UrlUtils.isMatch(consumerURL, url)) { + urls.add(url); + } + } + return urls; + } + + private URL buildURL(ServiceInstance serviceInstance) { + URL url = new URL(serviceInstance.getMetadata().get(Constants.PROTOCOL_KEY), + serviceInstance.getHost(), + serviceInstance.getPort(), + serviceInstance.getMetadata()); + return url; + } + + private void filter(Collection collection, Filter filter) { + Iterator iterator = collection.iterator(); + while (iterator.hasNext()) { + T data = iterator.next(); + if (!filter.accept(data)) { // remove if not accept + iterator.remove(); + } + } + } + + private static T[] of(T... values) { + return values; + } + + /** + * A filter + */ + public interface Filter { + + /** + * Tests whether or not the specified data should be accepted. + * + * @param data The data to be tested + * @return true if and only if data + * should be accepted + */ + boolean accept(T data); + + } + +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DubboServiceRegistrationEventPublishingAspect.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DubboServiceRegistrationEventPublishingAspect.java new file mode 100644 index 00000000..17826624 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DubboServiceRegistrationEventPublishingAspect.java @@ -0,0 +1,61 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.registry; + +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.cloud.alibaba.dubbo.registry.event.ServiceInstancePreRegisteredEvent; +import org.springframework.cloud.alibaba.dubbo.registry.event.ServiceInstanceRegisteredEvent; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; + +/** + * Dubbo Service Registration Event-Publishing Aspect + * + * @author Mercy + * @see ServiceInstancePreRegisteredEvent + * @see ServiceInstanceRegisteredEvent + */ +@Aspect +public class DubboServiceRegistrationEventPublishingAspect implements ApplicationEventPublisherAware { + + /** + * The pointcut expression for {@link ServiceRegistry#register(Registration)} + */ + public static final String REGISTER_POINTCUT_EXPRESSION = + "execution(* org.springframework.cloud.client.serviceregistry.ServiceRegistry.register(*)) && args(registration)"; + + private ApplicationEventPublisher applicationEventPublisher; + + @Before(REGISTER_POINTCUT_EXPRESSION) + public void beforeRegister(Registration registration) { + applicationEventPublisher.publishEvent(new ServiceInstancePreRegisteredEvent(registration)); + } + + @After(REGISTER_POINTCUT_EXPRESSION) + public void afterRegister(Registration registration) { + applicationEventPublisher.publishEvent(new ServiceInstanceRegisteredEvent(registration)); + } + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactory.java deleted file mode 100644 index d05eea94..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactory.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.alibaba.dubbo.registry; - -import org.apache.dubbo.common.URL; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.context.ConfigurableApplicationContext; - - -/** - * {@link Registration} Factory to createServiceInstance a instance of {@link Registration} - * - * @param The subclass of {@link Registration} - * @author Mercy - */ -public interface RegistrationFactory { - - /** - * Create a instance of {@link R} - * - * @param url The Dubbo's {@link URL} - * @param applicationContext {@link ConfigurableApplicationContext} - * @return a instance of {@link R}, if null, it indicates the registration will not be executed. - */ - R create(URL url, ConfigurableApplicationContext applicationContext); - - /** - * Create a instance of {@link R} - * - * @param serviceInstance {@link ServiceInstance} - * @param applicationContext {@link ConfigurableApplicationContext} - * @return a instance of {@link R}, if null, it indicates the registration will not be executed. - */ - R create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext); -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactoryProvider.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactoryProvider.java deleted file mode 100644 index c5debc68..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactoryProvider.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.alibaba.dubbo.registry; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.cloud.client.serviceregistry.ServiceRegistry; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.core.ResolvableType; - -import java.util.List; - -import static org.springframework.beans.BeanUtils.instantiateClass; -import static org.springframework.core.ResolvableType.forInstance; -import static org.springframework.core.ResolvableType.forType; -import static org.springframework.core.io.support.SpringFactoriesLoader.loadFactoryNames; -import static org.springframework.util.ClassUtils.isPresent; -import static org.springframework.util.ClassUtils.resolveClassName; - -/** - * {@link RegistrationFactory} Provider - * - * @author Mercy - */ -public class RegistrationFactoryProvider implements FactoryBean, ApplicationContextAware { - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private RegistrationFactory registrationFactory; - - @Override - public RegistrationFactory getObject() throws BeansException { - return registrationFactory; - } - - @Override - public Class getObjectType() { - return RegistrationFactory.class; - } - - public boolean isSingleton() { - return true; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - ServiceRegistry serviceRegistry = applicationContext.getBean(ServiceRegistry.class); - ClassLoader classLoader = applicationContext.getClassLoader(); - this.registrationFactory = buildRegistrationFactory(serviceRegistry, classLoader); - } - - private RegistrationFactory buildRegistrationFactory(ServiceRegistry serviceRegistry, - ClassLoader classLoader) { - RegistrationFactory registrationFactory = null; - List factoryClassNames = loadFactoryNames(RegistrationFactory.class, classLoader); - - ResolvableType serviceRegistryType = forInstance(serviceRegistry); - // Get first generic Class - Class registrationClass = resolveGenericClass(serviceRegistryType, ServiceRegistry.class, 0); - - for (String factoryClassName : factoryClassNames) { - if (isPresent(factoryClassName, classLoader)) { // ignore compilation issue - Class factoryClass = resolveClassName(factoryClassName, classLoader); - ResolvableType registrationFactoryType = forType(factoryClass); - Class actualRegistrationClass = resolveGenericClass(registrationFactoryType, RegistrationFactory.class, 0); - if (registrationClass.equals(actualRegistrationClass)) { - registrationFactory = (RegistrationFactory) instantiateClass(registrationFactoryType.getRawClass()); - break; - } - } - } - - if (registrationFactory == null) { - - if (logger.isWarnEnabled()) { - logger.warn("{} implementation can't be resolved by ServiceRegistry[{}]", - registrationClass.getSimpleName(), serviceRegistry.getClass().getName()); - } - - registrationFactory = new DefaultRegistrationFactory(); - } else { - if (logger.isInfoEnabled()) { - logger.info("{} has been resolved by ServiceRegistry[{}]", - registrationFactory.getClass().getName(), serviceRegistry.getClass().getName()); - } - } - - return registrationFactory; - } - - private Class resolveGenericClass(ResolvableType implementedType, Class interfaceClass, int index) { - - ResolvableType resolvableType = implementedType; - - try { - OUTER: - while (true) { - - ResolvableType[] interfaceTypes = resolvableType.getInterfaces(); - - for (ResolvableType interfaceType : interfaceTypes) { - if (interfaceType.resolve().equals(interfaceClass)) { - resolvableType = interfaceType; - break OUTER; - } - } - - ResolvableType superType = resolvableType.getSuperType(); - - Class superClass = superType.resolve(); - - if (Object.class.equals(superClass)) { - break; - } - - resolvableType = superType; - } - - } catch (Throwable e) { - resolvableType = ResolvableType.forType(void.class); - } - - return resolvableType.resolveGeneric(index); - } - -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java index d87e1f6c..d973e629 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java @@ -16,299 +16,138 @@ */ package org.springframework.cloud.alibaba.dubbo.registry; -import org.apache.dubbo.common.Constants; import org.apache.dubbo.common.URL; -import org.apache.dubbo.common.utils.UrlUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.RegistryFactory; -import org.apache.dubbo.registry.support.FailbackRegistry; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.cloud.alibaba.dubbo.registry.handler.DubboRegistryServiceIdHandler; + +import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository; +import org.springframework.cloud.alibaba.dubbo.util.JSONUtils; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; -import static java.util.Collections.singletonList; -import static org.apache.dubbo.common.Constants.CONFIGURATORS_CATEGORY; -import static org.apache.dubbo.common.Constants.CONSUMERS_CATEGORY; -import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY; -import static org.apache.dubbo.common.Constants.PROVIDER_SIDE; -import static org.apache.dubbo.common.Constants.ROUTERS_CATEGORY; -import static org.apache.dubbo.common.Constants.SIDE_KEY; +import static java.util.Collections.emptyMap; +import static org.apache.dubbo.common.Constants.APPLICATION_KEY; +import static org.springframework.util.CollectionUtils.isEmpty; /** * Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud" * * @author Mercy */ -public class SpringCloudRegistry extends FailbackRegistry { +public class SpringCloudRegistry extends AbstractSpringCloudRegistry { /** - * The parameter name of {@link #allServicesLookupInterval} + * The property name of Dubbo {@link URL URLs} metadata */ - public static final String ALL_SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.all.services.lookup.interval"; + public static final String DUBBO_URLS_METADATA_PROPERTY_NAME = "dubbo-urls"; /** - * The parameter name of {@link #registeredServicesLookupInterval} + * The parameter name of the services of Dubbo Provider */ - public static final String REGISTERED_SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.registered.services.lookup.interval"; + public static final String DUBBO_PROVIDER_SERVICES_PARAM_NAME = "dubbo-provider-services"; /** - * All supported categories + * All services of Dubbo Provider */ - public static final String[] ALL_SUPPORTED_CATEGORIES = of( - PROVIDERS_CATEGORY, - CONSUMERS_CATEGORY, - ROUTERS_CATEGORY, - CONFIGURATORS_CATEGORY - ); + public static final String ALL_DUBBO_PROVIDER_SERVICES = "*"; - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final DubboServiceMetadataRepository dubboServiceMetadataRepository; - /** - * The interval in second of lookup service names(only for Dubbo-OPS) - */ - private final long allServicesLookupInterval; + private final JSONUtils jsonUtils; - private final long registeredServicesLookupInterval; + private final Set dubboProviderServices; - private final ServiceRegistry serviceRegistry; + private final Map dubboServiceKeysCache; - private final RegistrationFactory registrationFactory; - - private final DiscoveryClient discoveryClient; - - private final DubboRegistryServiceIdHandler dubboRegistryServiceIdHandler; - - private final ScheduledExecutorService servicesLookupScheduler; - - private final ConfigurableApplicationContext applicationContext; - - public SpringCloudRegistry(URL url, - ServiceRegistry serviceRegistry, - RegistrationFactory registrationFactory, - DiscoveryClient discoveryClient, + public SpringCloudRegistry(URL url, DiscoveryClient discoveryClient, ScheduledExecutorService servicesLookupScheduler, + DubboServiceMetadataRepository dubboServiceMetadataRepository, ConfigurableApplicationContext applicationContext) { - super(url); - this.allServicesLookupInterval = url.getParameter(ALL_SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 30L); - this.registeredServicesLookupInterval = url.getParameter(REGISTERED_SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 300L); - this.serviceRegistry = serviceRegistry; - this.registrationFactory = registrationFactory; - this.discoveryClient = discoveryClient; - this.dubboRegistryServiceIdHandler = applicationContext.getBean(DubboRegistryServiceIdHandler.class); - this.applicationContext = applicationContext; - this.servicesLookupScheduler = servicesLookupScheduler; + super(url, discoveryClient, servicesLookupScheduler); + this.dubboServiceMetadataRepository = dubboServiceMetadataRepository; + this.jsonUtils = applicationContext.getBean(JSONUtils.class); + this.dubboProviderServices = getDubboProviderServices(); + this.dubboServiceKeysCache = this.initDubboServiceKeysCache(); } - protected boolean shouldRegister(Registration registration) { - Map metadata = registration.getMetadata(); - String side = metadata.get(SIDE_KEY); - return PROVIDER_SIDE.equals(side); // Only register the Provider. + private Map initDubboServiceKeysCache() { + + if (isEmpty(dubboProviderServices)) { + return emptyMap(); + } + + Map newCache = new HashMap<>(); + + dubboProviderServices.stream() + .map(this::getServiceInstances) + .filter(this::isNotEmpty) + .forEach(serviceInstances -> { + ServiceInstance serviceInstance = serviceInstances.get(0); + getURLs(serviceInstance).forEach(url -> { + String serviceKey = url.getServiceKey(); + String serviceName = url.getParameter(APPLICATION_KEY); + newCache.put(serviceKey, serviceName); + }); + }); + + return newCache; + } + + private boolean isNotEmpty(Collection collection) { + return !CollectionUtils.isEmpty(collection); + } + + private List getURLs(ServiceInstance serviceInstance) { + Map metadata = serviceInstance.getMetadata(); + String dubboURLsJSON = metadata.get(DUBBO_URLS_METADATA_PROPERTY_NAME); + List urlValues = jsonUtils.toList(dubboURLsJSON); + return urlValues.stream().map(URL::valueOf).collect(Collectors.toList()); + } + + private Set getDubboProviderServices() { + URL registryURL = getUrl(); + String services = registryURL.getParameter(DUBBO_PROVIDER_SERVICES_PARAM_NAME, ALL_DUBBO_PROVIDER_SERVICES); + return ALL_DUBBO_PROVIDER_SERVICES.equalsIgnoreCase(services) ? + getAllServiceNames() : StringUtils.commaDelimitedListToSet(services); } @Override - public void doRegister(URL url) { - final Registration registration = createRegistration(url); - if (shouldRegister(registration)) { - serviceRegistry.register(registration); - } + protected void doRegister0(URL url) { + dubboServiceMetadataRepository.registerURL(url); } @Override - public void doUnregister(URL url) { - final Registration registration = createRegistration(url); - if (shouldRegister(registration)) { - this.serviceRegistry.deregister(registration); - } + protected void doUnregister0(URL url) { + dubboServiceMetadataRepository.unregisterURL(url); } @Override - public void doSubscribe(URL url, NotifyListener listener) { - List serviceNames = getServiceNames(url, listener); - doSubscribe(url, listener, serviceNames); - this.servicesLookupScheduler.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - doSubscribe(url, listener, serviceNames); - } - }, registeredServicesLookupInterval, registeredServicesLookupInterval, TimeUnit.SECONDS); + protected boolean supports(String serviceName) { + return dubboProviderServices.contains(serviceName); } @Override - public void doUnsubscribe(URL url, NotifyListener listener) { - if (isAdminProtocol(url)) { - shutdownServiceNamesLookup(); - } + protected String getServiceName(URL url) { + String serviceKey = url.getServiceKey(); + return dubboServiceKeysCache.get(serviceKey); } @Override - public boolean isAvailable() { - return !discoveryClient.getServices().isEmpty(); + protected void notifySubscriber(URL url, NotifyListener listener, List serviceInstances) { + List urls = serviceInstances.stream().map(this::getURLs) + .flatMap(List::stream) + .collect(Collectors.toList()); + notify(url, listener, urls); } - - private void shutdownServiceNamesLookup() { - if (servicesLookupScheduler != null) { - servicesLookupScheduler.shutdown(); - } - } - - private Registration createRegistration(URL url) { - return registrationFactory.create(url, applicationContext); - } - - private void filterServiceNames(List serviceNames) { - filter(serviceNames, new Filter() { - @Override - public boolean accept(String serviceName) { - return dubboRegistryServiceIdHandler.supports(serviceName); - } - }); - } - - private List getAllServiceNames() { - return new LinkedList<>(discoveryClient.getServices()); - } - - /** - * Get the service names from the specified {@link URL url} - * - * @param url {@link URL} - * @param listener {@link NotifyListener} - * @return non-null - */ - private List getServiceNames(URL url, NotifyListener listener) { - if (isAdminProtocol(url)) { - initAllServicesLookupScheduler(url, listener); - return getServiceNamesForOps(url); - } else { - return singletonList(dubboRegistryServiceIdHandler.createServiceId(url)); - } - } - - - private boolean isAdminProtocol(URL url) { - return Constants.ADMIN_PROTOCOL.equals(url.getProtocol()); - } - - private void initAllServicesLookupScheduler(final URL url, final NotifyListener listener) { - servicesLookupScheduler.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - List serviceNames = getAllServiceNames(); - filterServiceNames(serviceNames); - doSubscribe(url, listener, serviceNames); - } - }, allServicesLookupInterval, allServicesLookupInterval, TimeUnit.SECONDS); - } - - private void doSubscribe(final URL url, final NotifyListener listener, final List serviceNames) { - for (String serviceName : serviceNames) { - List serviceInstances = discoveryClient.getInstances(serviceName); - notifySubscriber(url, listener, serviceInstances); - } - } - - /** - * Notify the Healthy {@link ServiceInstance service instance} to subscriber. - * - * @param url {@link URL} - * @param listener {@link NotifyListener} - * @param serviceInstances all {@link ServiceInstance instances} - */ - private void notifySubscriber(URL url, NotifyListener listener, List serviceInstances) { - List healthyInstances = new LinkedList(serviceInstances); - // Healthy Instances - filterHealthyInstances(healthyInstances); - List urls = buildURLs(url, healthyInstances); - this.notify(url, listener, urls); - } - - private void filterHealthyInstances(Collection instances) { - filter(instances, new Filter() { - @Override - public boolean accept(ServiceInstance data) { - // TODO check the details of status -// return serviceRegistry.getStatus(new DubboRegistration(data)) != null; - return true; - } - }); - } - - private List buildURLs(URL consumerURL, Collection serviceInstances) { - if (serviceInstances.isEmpty()) { - return Collections.emptyList(); - } - List urls = new LinkedList(); - for (ServiceInstance serviceInstance : serviceInstances) { - URL url = buildURL(serviceInstance); - if (UrlUtils.isMatch(consumerURL, url)) { - urls.add(url); - } - } - return urls; - } - - private URL buildURL(ServiceInstance serviceInstance) { - URL url = new URL(serviceInstance.getMetadata().get(Constants.PROTOCOL_KEY), - serviceInstance.getHost(), - serviceInstance.getPort(), - serviceInstance.getMetadata()); - return url; - } - - /** - * Get the service names for Dubbo OPS - * - * @param url {@link URL} - * @return non-null - */ - private List getServiceNamesForOps(URL url) { - List serviceNames = getAllServiceNames(); - filterServiceNames(serviceNames); - return serviceNames; - } - - private void filter(Collection collection, Filter filter) { - Iterator iterator = collection.iterator(); - while (iterator.hasNext()) { - T data = iterator.next(); - if (!filter.accept(data)) { // remove if not accept - iterator.remove(); - } - } - } - - private static T[] of(T... values) { - return values; - } - - /** - * A filter - */ - public interface Filter { - - /** - * Tests whether or not the specified data should be accepted. - * - * @param data The data to be tested - * @return true if and only if data - * should be accepted - */ - boolean accept(T data); - - } - } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java index 234c65d4..15dd3e5d 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java @@ -20,11 +20,9 @@ import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository; import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import org.springframework.context.ConfigurableApplicationContext; import java.util.concurrent.ScheduledExecutorService; @@ -46,16 +44,12 @@ public class SpringCloudRegistryFactory implements RegistryFactory { private static ConfigurableApplicationContext applicationContext; - private final Logger logger = LoggerFactory.getLogger(getClass()); - private final ScheduledExecutorService servicesLookupScheduler; - private ServiceRegistry serviceRegistry; - - private RegistrationFactory registrationFactory; - private DiscoveryClient discoveryClient; + private DubboServiceMetadataRepository dubboServiceMetadataRepository; + private volatile boolean initialized = false; public SpringCloudRegistryFactory() { @@ -67,17 +61,15 @@ public class SpringCloudRegistryFactory implements RegistryFactory { if (initialized || applicationContext == null) { return; } - - this.serviceRegistry = applicationContext.getBean(ServiceRegistry.class); - this.registrationFactory = applicationContext.getBean(RegistrationFactory.class); this.discoveryClient = applicationContext.getBean(DiscoveryClient.class); + this.dubboServiceMetadataRepository = applicationContext.getBean(DubboServiceMetadataRepository.class); } @Override public Registry getRegistry(URL url) { init(); - return new SpringCloudRegistry(url, serviceRegistry, registrationFactory, discoveryClient, - servicesLookupScheduler, applicationContext); + return new SpringCloudRegistry(url, discoveryClient, servicesLookupScheduler, + dubboServiceMetadataRepository, applicationContext); } public static void setApplicationContext(ConfigurableApplicationContext applicationContext) { diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/apache/zookeeper/ZookeeperRegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/apache/zookeeper/ZookeeperRegistrationFactory.java deleted file mode 100644 index 6657343f..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/apache/zookeeper/ZookeeperRegistrationFactory.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.alibaba.dubbo.registry.apache.zookeeper; - -import org.springframework.cloud.alibaba.dubbo.registry.AbstractRegistrationFactory; -import org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactory; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.zookeeper.discovery.ZookeeperInstance; -import org.springframework.cloud.zookeeper.serviceregistry.ServiceInstanceRegistration; -import org.springframework.cloud.zookeeper.serviceregistry.ZookeeperRegistration; -import org.springframework.context.ConfigurableApplicationContext; - -/** - * Zookeeper {@link RegistrationFactory} - * - * @author Mercy - */ -public class ZookeeperRegistrationFactory extends AbstractRegistrationFactory { - - @Override - public ZookeeperRegistration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) { - ZookeeperInstance zookeeperInstance = new ZookeeperInstance(serviceInstance.getInstanceId(), - serviceInstance.getServiceId(), serviceInstance.getMetadata()); - - ZookeeperRegistration registration = ServiceInstanceRegistration - .builder() - .address(serviceInstance.getHost()) - .name(serviceInstance.getServiceId()) - .payload(zookeeperInstance) - .port(serviceInstance.getPort()) - .build(); - - return registration; - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstancePreRegisteredEvent.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstancePreRegisteredEvent.java new file mode 100644 index 00000000..6233e9a5 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstancePreRegisteredEvent.java @@ -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.springframework.cloud.alibaba.dubbo.registry.event; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.ApplicationEvent; + +/** + * The before-{@link ServiceRegistry#register(Registration) register} event for {@link ServiceInstance} + * + * @author Mercy + */ +public class ServiceInstancePreRegisteredEvent extends ApplicationEvent { + + public ServiceInstancePreRegisteredEvent(Registration source) { + super(source); + } + + @Override + public Registration getSource() { + return (Registration) super.getSource(); + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DefaultRegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstanceRegisteredEvent.java similarity index 65% rename from spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DefaultRegistrationFactory.java rename to spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstanceRegisteredEvent.java index 1bcfd534..8aab6aa7 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DefaultRegistrationFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstanceRegisteredEvent.java @@ -14,21 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.cloud.alibaba.dubbo.registry; +package org.springframework.cloud.alibaba.dubbo.registry.event; -import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.serviceregistry.Registration; -import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; + +import java.util.EventObject; /** - * Default {@link RegistrationFactory} + * The after-{@link ServiceRegistry#register(Registration) register} event for {@link Registration} * * @author Mercy */ -public class DefaultRegistrationFactory extends AbstractRegistrationFactory { +public class ServiceInstanceRegisteredEvent extends EventObject { + + public ServiceInstanceRegisteredEvent(Registration source) { + super(source); + } @Override - public Registration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) { - return new DelegatingRegistration(serviceInstance); + public Registration getSource() { + return (Registration) super.getSource(); } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/DubboRegistryServiceIdHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/DubboRegistryServiceIdHandler.java deleted file mode 100644 index 15a35029..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/DubboRegistryServiceIdHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.alibaba.dubbo.registry.handler; - -import org.apache.dubbo.common.URL; -import org.apache.dubbo.registry.Registry; -import org.springframework.context.ConfigurableApplicationContext; - -/** - * Dubbo {@link Registry} Spring Cloud Service Id Builder - * - * @author Mercy - */ -public interface DubboRegistryServiceIdHandler { - - /** - * Supports the specified id of Spring Cloud Service or not - * - * @param serviceId the specified id of Spring Cloud Service - * @return if supports, return true, or false - */ - boolean supports(String serviceId); - - /** - * Creates the id of Spring Cloud Service - * - * @param url The Dubbo's {@link URL} - * @return non-null - */ - String createServiceId(URL url); - - /** - * The instance if {@link ConfigurableApplicationContext} . - * - * @return non-null - */ - ConfigurableApplicationContext getContext(); - -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/StandardDubboRegistryServiceIdHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/StandardDubboRegistryServiceIdHandler.java deleted file mode 100644 index caaa0706..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/StandardDubboRegistryServiceIdHandler.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.alibaba.dubbo.registry.handler; - -import org.apache.dubbo.common.Constants; -import org.apache.dubbo.common.URL; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.util.StringUtils; - -import java.util.Objects; - -import static java.lang.System.getProperty; -import static org.apache.dubbo.common.Constants.CONSUMERS_CATEGORY; -import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY; -import static org.springframework.util.StringUtils.startsWithIgnoreCase; - -/** - * The Standard {@link DubboRegistryServiceIdHandler} - *

- * The service ID pattern is "${category}:${interface}:${version}:${group}" - * - * @author Mercy - */ -public class StandardDubboRegistryServiceIdHandler implements DubboRegistryServiceIdHandler { - - /** - * The separator for service name that could be changed by the Java Property "dubbo.service.name.separator". - */ - protected static final String SERVICE_NAME_SEPARATOR = getProperty("dubbo.service.name.separator", ":"); - - private final ConfigurableApplicationContext context; - - public StandardDubboRegistryServiceIdHandler(ConfigurableApplicationContext context) { - this.context = context; - } - - @Override - public boolean supports(String serviceId) { - return startsWithIgnoreCase(serviceId, PROVIDERS_CATEGORY) || - startsWithIgnoreCase(serviceId, CONSUMERS_CATEGORY); - } - - @Override - public String createServiceId(URL url) { - String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY); - if (!Objects.equals(category, PROVIDERS_CATEGORY) && !Objects.equals(category, CONSUMERS_CATEGORY)) { - category = PROVIDERS_CATEGORY; - } - return createServiceId(url, category); - } - - @Override - public ConfigurableApplicationContext getContext() { - return context; - } - - /** - * This method maybe override by sub-class. - * - * @param url The Dubbo's {@link URL} - * @param category The category - * @return - */ - protected String createServiceId(URL url, String category) { - StringBuilder serviceNameBuilder = new StringBuilder(category); - appendIfPresent(serviceNameBuilder, url, Constants.INTERFACE_KEY); - appendIfPresent(serviceNameBuilder, url, Constants.VERSION_KEY); - appendIfPresent(serviceNameBuilder, url, Constants.GROUP_KEY); - return serviceNameBuilder.toString(); - } - - private static void appendIfPresent(StringBuilder target, URL url, String parameterName) { - String parameterValue = url.getParameter(parameterName); - appendIfPresent(target, parameterValue); - } - - private static void appendIfPresent(StringBuilder target, String parameterValue) { - if (StringUtils.hasText(parameterValue)) { - target.append(SERVICE_NAME_SEPARATOR).append(parameterValue); - } - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/hashicorp/consul/ConsulRegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/hashicorp/consul/ConsulRegistrationFactory.java deleted file mode 100644 index a5861fb9..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/hashicorp/consul/ConsulRegistrationFactory.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.alibaba.dubbo.registry.hashicorp.consul; - -import com.ecwid.consul.v1.agent.model.NewService; -import org.springframework.cloud.alibaba.dubbo.registry.AbstractRegistrationFactory; -import org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactory; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties; -import org.springframework.cloud.consul.discovery.ConsulServerUtils; -import org.springframework.cloud.consul.serviceregistry.ConsulRegistration; -import org.springframework.context.ConfigurableApplicationContext; - -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * {@link ConsulRegistration} {@link RegistrationFactory} implementation - * - * @author Mercy - */ -public class ConsulRegistrationFactory extends AbstractRegistrationFactory { - - @Override - public ConsulRegistration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) { - Map metadata = getMetadata(serviceInstance); - List tags = createTags(metadata); - - NewService service = new NewService(); - service.setId(serviceInstance.getInstanceId()); - service.setName(serviceInstance.getServiceId()); - service.setAddress(serviceInstance.getHost()); - service.setPort(serviceInstance.getPort()); - service.setMeta(metadata); - service.setTags(tags); - - ConsulDiscoveryProperties properties = applicationContext.getBean(ConsulDiscoveryProperties.class); - - ConsulRegistration registration = new ConsulRegistration(service, properties); - return registration; - } - - /** - * @param metadata - * @return - * @see ConsulServerUtils#getMetadata(java.util.List) - */ - private List createTags(Map metadata) { - List tags = new LinkedList<>(); - for (Map.Entry entry : metadata.entrySet()) { - String tag = entry.getKey() + "=" + entry.getValue(); - tags.add(tag); - - } - return tags; - } - - private Map getMetadata(ServiceInstance serviceInstance) { - Map metadata = serviceInstance.getMetadata(); - Set removedKeys = new LinkedHashSet<>(); - for (String key : metadata.keySet()) { - if (key.contains(".")) { - removedKeys.add(key); - } - } - for (String removedKey : removedKeys) { - metadata.remove(removedKey); - } - return metadata; - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/netflix/eureka/EurekaRegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/netflix/eureka/EurekaRegistrationFactory.java deleted file mode 100644 index 84fafa62..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/netflix/eureka/EurekaRegistrationFactory.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.alibaba.dubbo.registry.netflix.eureka; - -import com.netflix.appinfo.HealthCheckHandler; -import com.netflix.discovery.EurekaClientConfig; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.cloud.alibaba.dubbo.registry.AbstractRegistrationFactory; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.commons.util.InetUtils; -import org.springframework.cloud.netflix.eureka.CloudEurekaInstanceConfig; -import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; -import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration; -import org.springframework.context.ConfigurableApplicationContext; - -/** - * {@link EurekaRegistration} Factory - * - * @author Mercy - */ -public class EurekaRegistrationFactory extends AbstractRegistrationFactory { - - @Override - public EurekaRegistration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) { - CloudEurekaInstanceConfig cloudEurekaInstanceConfig = applicationContext.getBean(CloudEurekaInstanceConfig.class); - ObjectProvider healthCheckHandler = applicationContext.getBeanProvider(HealthCheckHandler.class); - EurekaClientConfig eurekaClientConfig = applicationContext.getBean(EurekaClientConfig.class); - InetUtils inetUtils = applicationContext.getBean(InetUtils.class); - EurekaInstanceConfigBean eurekaInstanceConfigBean = new EurekaInstanceConfigBean(inetUtils); - BeanUtils.copyProperties(cloudEurekaInstanceConfig, eurekaInstanceConfigBean); - String serviceId = serviceInstance.getServiceId(); - eurekaInstanceConfigBean.setInstanceId(serviceInstance.getInstanceId()); - eurekaInstanceConfigBean.setVirtualHostName(serviceId); - eurekaInstanceConfigBean.setSecureVirtualHostName(serviceId); - eurekaInstanceConfigBean.setAppname(serviceId); - eurekaInstanceConfigBean.setHostname(serviceInstance.getHost()); - eurekaInstanceConfigBean.setMetadataMap(serviceInstance.getMetadata()); - - return EurekaRegistration.builder(eurekaInstanceConfigBean) - .with(healthCheckHandler) - .with(eurekaClientConfig, applicationContext) - .build(); - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboGenericServiceFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboGenericServiceFactory.java index e15bbb69..d1648641 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboGenericServiceFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboGenericServiceFactory.java @@ -17,24 +17,31 @@ package org.springframework.cloud.alibaba.dubbo.service; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.config.spring.ReferenceBean; import org.apache.dubbo.rpc.service.GenericService; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata; -import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.propertyeditors.StringTrimmerEditor; +import org.springframework.cloud.alibaba.dubbo.metadata.DubboRestServiceMetadata; import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata; +import org.springframework.util.StringUtils; +import org.springframework.validation.DataBinder; import javax.annotation.PreDestroy; +import java.beans.PropertyEditorSupport; import java.util.Collection; +import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import static org.apache.dubbo.common.Constants.DEFAULT_CLUSTER; -import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL; +import static java.util.Collections.emptyMap; import static org.apache.dubbo.common.Constants.GROUP_KEY; import static org.apache.dubbo.common.Constants.VERSION_KEY; +import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; /** * Dubbo {@link GenericService} Factory @@ -47,39 +54,36 @@ public class DubboGenericServiceFactory { private final ConcurrentMap> cache = new ConcurrentHashMap<>(); - public GenericService create(DubboServiceMetadata dubboServiceMetadata, - DubboTransportedMetadata dubboTransportedMetadata) { + public GenericService create(DubboRestServiceMetadata dubboServiceMetadata, + Map dubboTranslatedAttributes) { - ReferenceBean referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTransportedMetadata); + ReferenceBean referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTranslatedAttributes); return referenceBean == null ? null : referenceBean.get(); } public GenericService create(String serviceName, Class serviceClass) { String interfaceName = serviceClass.getName(); - ReferenceBean referenceBean = build(interfaceName, serviceName, null, - DEFAULT_PROTOCOL, DEFAULT_CLUSTER); + ReferenceBean referenceBean = build(interfaceName, serviceName, null, emptyMap()); return referenceBean.get(); } private ReferenceBean build(ServiceRestMetadata serviceRestMetadata, - DubboTransportedMetadata dubboTransportedMetadata) { + Map dubboTranslatedAttributes) { String urlValue = serviceRestMetadata.getUrl(); URL url = URL.valueOf(urlValue); String interfaceName = url.getServiceInterface(); String version = url.getParameter(VERSION_KEY); - String group = url.getParameter(GROUP_KEY); - String protocol = dubboTransportedMetadata.getProtocol(); - String cluster = dubboTransportedMetadata.getCluster(); + String group = url.getParameter(GROUP_KEY); - return build(interfaceName, version, group, protocol, cluster); + return build(interfaceName, version, group, dubboTranslatedAttributes); } - private ReferenceBean build(String interfaceName, String version, String group, String protocol, - String cluster) { + private ReferenceBean build(String interfaceName, String version, String group, + Map dubboTranslatedAttributes) { - Integer key = Objects.hash(interfaceName, version, group, protocol, cluster); + Integer key = Objects.hash(interfaceName, version, group, dubboTranslatedAttributes); ReferenceBean referenceBean = cache.get(key); @@ -89,13 +93,38 @@ public class DubboGenericServiceFactory { referenceBean.setInterface(interfaceName); referenceBean.setVersion(version); referenceBean.setGroup(group); - referenceBean.setProtocol(protocol); - referenceBean.setCluster(cluster); + bindReferenceBean(referenceBean, dubboTranslatedAttributes); } return referenceBean; } + private void bindReferenceBean(ReferenceBean referenceBean, Map dubboTranslatedAttributes) { + DataBinder dataBinder = new DataBinder(referenceBean); + // Register CustomEditors for special fields + dataBinder.registerCustomEditor(String.class, "filter", new StringTrimmerEditor(true)); + dataBinder.registerCustomEditor(String.class, "listener", new StringTrimmerEditor(true)); + dataBinder.registerCustomEditor(Map.class, "parameters", new PropertyEditorSupport() { + + public void setAsText(String text) throws java.lang.IllegalArgumentException { + // Trim all whitespace + String content = StringUtils.trimAllWhitespace(text); + if (!StringUtils.hasText(content)) { // No content , ignore directly + return; + } + // replace "=" to "," + content = StringUtils.replace(content, "=", ","); + // replace ":" to "," + content = StringUtils.replace(content, ":", ","); + // String[] to Map + Map parameters = CollectionUtils.toStringMap(commaDelimitedListToStringArray(content)); + setValue(parameters); + } + }); + + dataBinder.bind(new MutablePropertyValues(dubboTranslatedAttributes)); + } + @PreDestroy public void destroy() { destroyReferenceBeans(); diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboMetadataConfigServiceExporter.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboMetadataConfigServiceExporter.java new file mode 100644 index 00000000..a47d1895 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboMetadataConfigServiceExporter.java @@ -0,0 +1,109 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.service; + +import org.apache.dubbo.config.ApplicationConfig; +import org.apache.dubbo.config.ProtocolConfig; +import org.apache.dubbo.config.ServiceConfig; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.util.function.Supplier; + +/** + * {@link DubboMetadataConfigService} exporter + * + * @author Mercy + */ +@Component +public class DubboMetadataConfigServiceExporter { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private ApplicationConfig applicationConfig; + + @Autowired + private PublishingDubboMetadataConfigService dubboMetadataConfigService; + + @Autowired + private Supplier protocolConfigSupplier; + + @Value("${spring.application.name:application}") + private String currentApplicationName; + + /** + * The ServiceConfig of DubboMetadataConfigService to be exported, can be nullable. + */ + private ServiceConfig serviceConfig; + + /** + * export {@link DubboMetadataConfigService} as Dubbo service + */ + public void export() { + + if (serviceConfig != null && serviceConfig.isExported()) { + return; + } + + if (StringUtils.isEmpty(dubboMetadataConfigService.getServiceRestMetadata())) { + // If there is no REST metadata, DubboMetadataConfigService will not be exported. + if (logger.isInfoEnabled()) { + logger.info("There is no REST metadata, the Dubbo service[{}] will not be exported.", + dubboMetadataConfigService.getClass().getName()); + } + return; + } + + serviceConfig = new ServiceConfig<>(); + + serviceConfig.setInterface(DubboMetadataConfigService.class); + // Use current Spring application name as the Dubbo Service version + serviceConfig.setVersion(currentApplicationName); + serviceConfig.setRef(dubboMetadataConfigService); + serviceConfig.setApplication(applicationConfig); + serviceConfig.setProtocol(protocolConfigSupplier.get()); + + serviceConfig.export(); + + if (logger.isInfoEnabled()) { + logger.info("The Dubbo service[{}] has been exported.", serviceConfig.toString()); + } + } + + + /** + * unexport {@link DubboMetadataConfigService} + */ + public void unexport() { + + if (serviceConfig == null || serviceConfig.isUnexported()) { + return; + } + + serviceConfig.unexport(); + + if (logger.isInfoEnabled()) { + logger.info("The Dubbo service[{}] has been unexported.", serviceConfig.toString()); + } + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/PublishingDubboMetadataConfigService.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/PublishingDubboMetadataConfigService.java index fa49e94b..76fe660f 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/PublishingDubboMetadataConfigService.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/PublishingDubboMetadataConfigService.java @@ -16,13 +16,11 @@ */ package org.springframework.cloud.alibaba.dubbo.service; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata; +import org.springframework.cloud.alibaba.dubbo.util.JSONUtils; import org.springframework.util.CollectionUtils; -import javax.annotation.PostConstruct; import java.util.LinkedHashSet; import java.util.Set; @@ -41,12 +39,8 @@ public class PublishingDubboMetadataConfigService implements DubboMetadataConfig */ private final Set serviceRestMetadata = new LinkedHashSet<>(); - private final ObjectMapper objectMapper = new ObjectMapper(); - - @PostConstruct - public void init() { - this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT); - } + @Autowired + private JSONUtils jsonUtils; /** * Publish the {@link Set} of {@link ServiceRestMetadata} @@ -64,12 +58,8 @@ public class PublishingDubboMetadataConfigService implements DubboMetadataConfig @Override public String getServiceRestMetadata() { String serviceRestMetadataJsonConfig = null; - try { - if (!isEmpty(serviceRestMetadata)) { - serviceRestMetadataJsonConfig = objectMapper.writeValueAsString(serviceRestMetadata); - } - } catch (JsonProcessingException e) { - throw new RuntimeException(e); + if (!isEmpty(serviceRestMetadata)) { + serviceRestMetadataJsonConfig = jsonUtils.toJSON(serviceRestMetadata); } return serviceRestMetadataJsonConfig; } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/util/JSONUtils.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/util/JSONUtils.java new file mode 100644 index 00000000..1dd1f6da --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/util/JSONUtils.java @@ -0,0 +1,72 @@ +/* + * 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.springframework.cloud.alibaba.dubbo.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * JSON Utilities class + * + * @author Mercy + */ +public class JSONUtils { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @PostConstruct + public void init() { + this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + } + + public String toJSON(Object object) { + String jsonContent = null; + try { + jsonContent = objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + if (logger.isErrorEnabled()) { + logger.error(e.getMessage(), e); + } + } + return jsonContent; + } + + public List toList(String json) { + List list = Collections.emptyList(); + try { + if (StringUtils.hasText(json)) { + list = objectMapper.readValue(json, List.class); + } + } catch (IOException e) { + if (logger.isErrorEnabled()) { + logger.error(e.getMessage(), e); + } + } + return list; + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories index ed1c9c3f..6f0ffd96 100644 --- a/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories @@ -1,14 +1,10 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration,\ org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration,\ - org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataEventHandlingAutoConfiguration,\ + org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration,\ + org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationNonWebApplicationAutoConfiguration,\ org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboLoadBalancedRestTemplateAutoConfiguration,\ org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceAutoConfiguration org.springframework.context.ApplicationContextInitializer=\ - org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer - -org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactory=\ - org.springframework.cloud.alibaba.dubbo.registry.netflix.eureka.EurekaRegistrationFactory,\ - org.springframework.cloud.alibaba.dubbo.registry.apache.zookeeper.ZookeeperRegistrationFactory,\ - org.springframework.cloud.alibaba.dubbo.registry.hashicorp.consul.ConsulRegistrationFactory \ No newline at end of file + org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml b/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml index 250a60fb..83c24c8b 100644 --- a/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml +++ b/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml b/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml index af0c708c..d7affc5a 100644 --- a/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml +++ b/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml b/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml index b66c22ff..b99150d9 100644 --- a/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml +++ b/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml b/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml index fc8f4b67..abfc0437 100644 --- a/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml +++ b/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/env-extension/pom.xml b/spring-cloud-alibaba-examples/env-extension/pom.xml index 35d98346..e84e953a 100644 --- a/spring-cloud-alibaba-examples/env-extension/pom.xml +++ b/spring-cloud-alibaba-examples/env-extension/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.boot diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/account-service/pom.xml index 8d93a588..67a478ff 100644 --- a/spring-cloud-alibaba-examples/fescar-example/account-service/pom.xml +++ b/spring-cloud-alibaba-examples/fescar-example/account-service/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/file.conf similarity index 85% rename from spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.conf rename to spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/file.conf index b264dd9d..857b089e 100644 --- a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.conf +++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/file.conf @@ -3,6 +3,8 @@ transport { type = "TCP" #NIO NATIVE server = "NIO" + #enable heartbeat + heartbeat = true #thread factory for netty thread-factory { boss-thread-prefix = "NettyBoss" @@ -28,3 +30,11 @@ service { #disable disable = false } + +client { + async.commit.buffer.limit = 10000 + lock { + retry.internal = 10 + retry.times = 30 + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/registry.conf b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/registry.conf new file mode 100644 index 00000000..8a79c9f2 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/registry.conf @@ -0,0 +1,35 @@ +registry { + # file 、nacos 、redis + type = "file" + + nacos { + serverAddr = "localhost" + namespace = "public" + cluster = "default" + } + redis { + serverAddr = "localhost:6379" + db = "0" + } + file { + name = "file.conf" + } +} + +config { + # file nacos apollo + type = "file" + + nacos { + serverAddr = "localhost" + namespace = "public" + cluster = "default" + } + apollo { + app.id = "fescar-server" + apollo.meta = "http://192.168.1.204:8801" + } + file { + name = "file.conf" + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/business-service/pom.xml index 53340dfb..76543699 100644 --- a/spring-cloud-alibaba-examples/fescar-example/business-service/pom.xml +++ b/spring-cloud-alibaba-examples/fescar-example/business-service/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/file.conf similarity index 85% rename from spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.conf rename to spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/file.conf index 65b69508..b087a18c 100644 --- a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.conf +++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/file.conf @@ -3,6 +3,8 @@ transport { type = "TCP" #NIO NATIVE server = "NIO" + #enable heartbeat + heartbeat = true #thread factory for netty thread-factory { boss-thread-prefix = "NettyBoss" @@ -28,3 +30,11 @@ service { #disable disable = false } + +client { + async.commit.buffer.limit = 10000 + lock { + retry.internal = 10 + retry.times = 30 + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/registry.conf b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/registry.conf new file mode 100644 index 00000000..8a79c9f2 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/registry.conf @@ -0,0 +1,35 @@ +registry { + # file 、nacos 、redis + type = "file" + + nacos { + serverAddr = "localhost" + namespace = "public" + cluster = "default" + } + redis { + serverAddr = "localhost:6379" + db = "0" + } + file { + name = "file.conf" + } +} + +config { + # file nacos apollo + type = "file" + + nacos { + serverAddr = "localhost" + namespace = "public" + cluster = "default" + } + apollo { + app.id = "fescar-server" + apollo.meta = "http://192.168.1.204:8801" + } + file { + name = "file.conf" + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/order-service/pom.xml index 7840c546..d0d928f2 100644 --- a/spring-cloud-alibaba-examples/fescar-example/order-service/pom.xml +++ b/spring-cloud-alibaba-examples/fescar-example/order-service/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/file.conf similarity index 85% rename from spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.conf rename to spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/file.conf index c298ca0c..4f893da5 100644 --- a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.conf +++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/file.conf @@ -3,6 +3,8 @@ transport { type = "TCP" #NIO NATIVE server = "NIO" + #enable heartbeat + heartbeat = true #thread factory for netty thread-factory { boss-thread-prefix = "NettyBoss" @@ -28,3 +30,11 @@ service { #disable disable = false } + +client { + async.commit.buffer.limit = 10000 + lock { + retry.internal = 10 + retry.times = 30 + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/registry.conf b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/registry.conf new file mode 100644 index 00000000..8a79c9f2 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/registry.conf @@ -0,0 +1,35 @@ +registry { + # file 、nacos 、redis + type = "file" + + nacos { + serverAddr = "localhost" + namespace = "public" + cluster = "default" + } + redis { + serverAddr = "localhost:6379" + db = "0" + } + file { + name = "file.conf" + } +} + +config { + # file nacos apollo + type = "file" + + nacos { + serverAddr = "localhost" + namespace = "public" + cluster = "default" + } + apollo { + app.id = "fescar-server" + apollo.meta = "http://192.168.1.204:8801" + } + file { + name = "file.conf" + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/readme-zh.md b/spring-cloud-alibaba-examples/fescar-example/readme-zh.md index e93f6866..d10ecbf2 100644 --- a/spring-cloud-alibaba-examples/fescar-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/fescar-example/readme-zh.md @@ -40,11 +40,12 @@ mysql.user.password=your mysql server password ``` -### 创建 UNDO_LOG 表 +### 创建 undo_log 表 -[Fescar AT 模式]() 需要使用到 UNDO_LOG 表。 +[Fescar AT 模式]() 需要使用到 undo_log 表。 ``` $sql +-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, @@ -55,8 +56,8 @@ CREATE TABLE `undo_log` ( `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), - KEY `idx_unionkey` (`xid`,`branch_id`) -) ENGINE=InnoDB AUTO_INCREMENT=159 DEFAULT CHARSET=utf8 + UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; ``` ### 创建 示例中 业务所需要的数据库表 diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/storage-service/pom.xml index fdef4eaf..c29c53da 100644 --- a/spring-cloud-alibaba-examples/fescar-example/storage-service/pom.xml +++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/file.conf similarity index 85% rename from spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.conf rename to spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/file.conf index 4bc5989e..29c81842 100644 --- a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.conf +++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/file.conf @@ -3,6 +3,8 @@ transport { type = "TCP" #NIO NATIVE server = "NIO" + #enable heartbeat + heartbeat = true #thread factory for netty thread-factory { boss-thread-prefix = "NettyBoss" @@ -28,3 +30,11 @@ service { #disable disable = false } + +client { + async.commit.buffer.limit = 10000 + lock { + retry.internal = 10 + retry.times = 30 + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/registry.conf b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/registry.conf new file mode 100644 index 00000000..8a79c9f2 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/registry.conf @@ -0,0 +1,35 @@ +registry { + # file 、nacos 、redis + type = "file" + + nacos { + serverAddr = "localhost" + namespace = "public" + cluster = "default" + } + redis { + serverAddr = "localhost:6379" + db = "0" + } + file { + name = "file.conf" + } +} + +config { + # file nacos apollo + type = "file" + + nacos { + serverAddr = "localhost" + namespace = "public" + cluster = "default" + } + apollo { + app.id = "fescar-server" + apollo.meta = "http://192.168.1.204:8801" + } + file { + name = "file.conf" + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml index 565a732d..a4b831ae 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml index 6a3af32b..096775c4 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml index a81b13bc..3ea94a29 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml index 843f6f4c..a8e0fca7 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md index 47b43179..279c516b 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md @@ -141,11 +141,11 @@ Nacos Discovery Starter 默认集成了 Ribbon ,所以对于使用了 Ribbon 2. 打包编译后启动:在 nacos-discovery-consumer-example 项目中执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar nacos-discovery-consumer-example.jar`启动应用。 #### 验证 -1. 在流量器地址栏中输入 http://127.0.0.1:18083/echo-rest/1234,点击跳转,可以看到浏览器显示了 nacos-discovery-provider-example 返回的消息 "hello Nacos Discovery 1234",证明服务发现生效。 +1. 在浏览器地址栏中输入 http://127.0.0.1:18083/echo-rest/1234,点击跳转,可以看到浏览器显示了 nacos-discovery-provider-example 返回的消息 "hello Nacos Discovery 1234",证明服务发现生效。 ![rest](https://cdn.nlark.com/lark/0/2018/png/54319/1536986302124-ee27670d-bdcc-4210-9f5d-875acec6d3ea.png) -1. 在流量器地址栏中输入 http://127.0.0.1:18083/echo-feign/12345,点击跳转,可以看到浏览器显示 nacos-discovery-provider-example 返回的消息 "hello Nacos Discovery 12345",证明服务发现生效。 +1. 在浏览器地址栏中输入 http://127.0.0.1:18083/echo-feign/12345,点击跳转,可以看到浏览器显示 nacos-discovery-provider-example 返回的消息 "hello Nacos Discovery 12345",证明服务发现生效。 ![feign](https://cdn.nlark.com/lark/0/2018/png/54319/1536986311685-6d0c1f9b-a453-4ec3-88ab-f7922d210f65.png) ## 原理 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-discovery-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-discovery-example/pom.xml index 6af752d7..4e9c7339 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-discovery-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-discovery-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud @@ -19,7 +19,6 @@ org.springframework.cloud spring-cloud-starter-gateway - 2.0.2.RELEASE diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-provider-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-provider-example/pom.xml index a4bf41d2..c6f5e4cd 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-provider-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-provider-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/pom.xml index c2aa58ed..3fc99d88 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/oss-example/pom.xml b/spring-cloud-alibaba-examples/oss-example/pom.xml index 624b3e66..ca134602 100644 --- a/spring-cloud-alibaba-examples/oss-example/pom.xml +++ b/spring-cloud-alibaba-examples/oss-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/pom.xml b/spring-cloud-alibaba-examples/pom.xml index bedfeff2..b4a0ae8c 100644 --- a/spring-cloud-alibaba-examples/pom.xml +++ b/spring-cloud-alibaba-examples/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud @@ -34,7 +34,8 @@ fescar-example/storage-service fescar-example/account-service acm-example/acm-local-example - rocketmq-example + rocketmq-example/rocketmq-consume-example + rocketmq-example/rocketmq-produce-example sms-example spring-cloud-bus-rocketmq-example schedulerx-example/schedulerx-simple-task-example diff --git a/spring-cloud-alibaba-examples/rocketmq-example/pom.xml b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/pom.xml similarity index 81% rename from spring-cloud-alibaba-examples/rocketmq-example/pom.xml rename to spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/pom.xml index 84ef5029..20fd0f82 100644 --- a/spring-cloud-alibaba-examples/rocketmq-example/pom.xml +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/pom.xml @@ -1,19 +1,19 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud spring-cloud-alibaba-examples 0.2.2.BUILD-SNAPSHOT - ../pom.xml + ../../pom.xml 4.0.0 - rocketmq-example + rocketmq-consume-example jar - Example demonstrating how to use rocketmq + Example demonstrating how to use rocketmq consume @@ -32,11 +32,6 @@ spring-boot-starter-actuator - - io.dropwizard.metrics - metrics-core - - diff --git a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Foo.java b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Foo.java similarity index 100% rename from spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Foo.java rename to spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Foo.java diff --git a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ReceiveService.java b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ReceiveService.java similarity index 69% rename from spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ReceiveService.java rename to spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ReceiveService.java index a486ebc0..35fc25a6 100644 --- a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ReceiveService.java +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/ReceiveService.java @@ -25,14 +25,9 @@ public class ReceiveService { System.out.println("input3 receive: " + foo); } - @StreamListener("input1") - public void receiveInput1Again(String receiveMsg) { - System.out.println("input1 receive again: " + receiveMsg); + @StreamListener("input4") + public void receiveTransactionalMsg(String transactionMsg) { + System.out.println("input4 receive transaction msg: " + transactionMsg); } - @StreamListener("input4") - public void receiveTransactionalMsg(String transactionMsg) { - System.out.println("input4 receive transaction msg: " + transactionMsg); - } - } diff --git a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/RocketMQConsumerApplication.java b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/RocketMQConsumerApplication.java new file mode 100644 index 00000000..25204919 --- /dev/null +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/RocketMQConsumerApplication.java @@ -0,0 +1,36 @@ +package org.springframework.cloud.alibaba.cloud.examples; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.alibaba.cloud.examples.RocketMQConsumerApplication.MySink; +import org.springframework.cloud.stream.annotation.EnableBinding; +import org.springframework.cloud.stream.annotation.Input; +import org.springframework.messaging.SubscribableChannel; + +/** + * @author Jim + */ +@SpringBootApplication +@EnableBinding({ MySink.class }) +public class RocketMQConsumerApplication { + + public interface MySink { + + @Input("input1") + SubscribableChannel input1(); + + @Input("input2") + SubscribableChannel input2(); + + @Input("input3") + SubscribableChannel input3(); + + @Input("input4") + SubscribableChannel input4(); + } + + public static void main(String[] args) { + SpringApplication.run(RocketMQConsumerApplication.class, args); + } + +} diff --git a/spring-cloud-alibaba-examples/rocketmq-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/resources/application.properties similarity index 53% rename from spring-cloud-alibaba-examples/rocketmq-example/src/main/resources/application.properties rename to spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/resources/application.properties index 3bfe511f..ec27539c 100644 --- a/spring-cloud-alibaba-examples/rocketmq-example/src/main/resources/application.properties +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/src/main/resources/application.properties @@ -1,21 +1,9 @@ -spring.cloud.stream.default-binder=rocketmq - -spring.cloud.stream.rocketmq.binder.namesrv-addr=127.0.0.1:9876 - -spring.cloud.stream.bindings.output1.destination=test-topic -spring.cloud.stream.bindings.output1.content-type=application/json - -spring.cloud.stream.bindings.output2.destination=TransactionTopic -spring.cloud.stream.bindings.output2.content-type=application/json -spring.cloud.stream.rocketmq.bindings.output2.producer.transactional=true -spring.cloud.stream.rocketmq.bindings.output2.producer.executer=org.springframework.cloud.alibaba.cloud.examples.MyTransactionExecuter -spring.cloud.stream.rocketmq.bindings.output2.producer.transaction-check-listener=org.springframework.cloud.alibaba.cloud.examples.MyTransactionCheckListener +spring.cloud.stream.rocketmq.binder.name-server=127.0.0.1:9876 spring.cloud.stream.bindings.input1.destination=test-topic spring.cloud.stream.bindings.input1.content-type=text/plain spring.cloud.stream.bindings.input1.group=test-group1 spring.cloud.stream.rocketmq.bindings.input1.consumer.orderly=true -spring.cloud.stream.bindings.input1.consumer.maxAttempts=1 spring.cloud.stream.bindings.input2.destination=test-topic spring.cloud.stream.bindings.input2.content-type=text/plain @@ -30,15 +18,15 @@ spring.cloud.stream.bindings.input3.content-type=application/json spring.cloud.stream.bindings.input3.group=test-group3 spring.cloud.stream.rocketmq.bindings.input3.consumer.tags=tagObj spring.cloud.stream.bindings.input3.consumer.concurrency=20 -spring.cloud.stream.bindings.input3.consumer.maxAttempts=1 spring.cloud.stream.bindings.input4.destination=TransactionTopic spring.cloud.stream.bindings.input4.content-type=text/plain spring.cloud.stream.bindings.input4.group=transaction-group -spring.cloud.stream.bindings.input4.consumer.concurrency=210 +spring.cloud.stream.bindings.input4.consumer.concurrency=5 -spring.application.name=rocketmq-example +spring.application.name=rocketmq-consume-example -server.port=28081 +server.port=28082 -management.endpoints.web.exposure.include=* \ No newline at end of file +management.endpoints.web.exposure.include=* +management.endpoint.health.show-details=always \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/pom.xml b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/pom.xml new file mode 100644 index 00000000..14c54bac --- /dev/null +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/pom.xml @@ -0,0 +1,54 @@ + + + + + org.springframework.cloud + spring-cloud-alibaba-examples + 0.2.2.BUILD-SNAPSHOT + ../../pom.xml + + 4.0.0 + + + rocketmq-produce-example + jar + Example demonstrating how to use rocketmq produce + + + + + org.springframework.cloud + spring-cloud-starter-stream-rocketmq + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + true + + + + + + diff --git a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Foo.java b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Foo.java new file mode 100644 index 00000000..e98b6a10 --- /dev/null +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Foo.java @@ -0,0 +1,39 @@ +package org.springframework.cloud.alibaba.cloud.examples; + +/** + * @author Jim + */ +public class Foo { + + private int id; + private String bar; + + public Foo() { + } + + public Foo(int id, String bar) { + this.id = id; + this.bar = bar; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getBar() { + return bar; + } + + public void setBar(String bar) { + this.bar = bar; + } + + @Override + public String toString() { + return "Foo{" + "id=" + id + ", bar='" + bar + '\'' + '}'; + } +} diff --git a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/RocketMQApplication.java b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/RocketMQProduceApplication.java similarity index 66% rename from spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/RocketMQApplication.java rename to spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/RocketMQProduceApplication.java index f736a0e5..bd157be0 100644 --- a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/RocketMQApplication.java +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/RocketMQProduceApplication.java @@ -4,36 +4,18 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.alibaba.cloud.examples.RocketMQApplication.MySink; -import org.springframework.cloud.alibaba.cloud.examples.RocketMQApplication.MySource; +import org.springframework.cloud.alibaba.cloud.examples.RocketMQProduceApplication.MySource; import org.springframework.cloud.stream.annotation.EnableBinding; -import org.springframework.cloud.stream.annotation.Input; import org.springframework.cloud.stream.annotation.Output; import org.springframework.context.annotation.Bean; import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.SubscribableChannel; /** * @author Jim */ @SpringBootApplication -@EnableBinding({ MySource.class, MySink.class }) -public class RocketMQApplication { - - public interface MySink { - - @Input("input1") - SubscribableChannel input1(); - - @Input("input2") - SubscribableChannel input2(); - - @Input("input3") - SubscribableChannel input3(); - - @Input("input4") - SubscribableChannel input4(); - } +@EnableBinding({ MySource.class }) +public class RocketMQProduceApplication { public interface MySource { @Output("output1") @@ -44,7 +26,7 @@ public class RocketMQApplication { } public static void main(String[] args) { - SpringApplication.run(RocketMQApplication.class, args); + SpringApplication.run(RocketMQProduceApplication.class, args); } @Bean @@ -86,13 +68,13 @@ public class RocketMQApplication { @Override public void run(String... args) throws Exception { // COMMIT_MESSAGE message - senderService.sendTransactionalMsg("transactional-msg1", false); + senderService.sendTransactionalMsg("transactional-msg1", 1); // ROLLBACK_MESSAGE message - senderService.sendTransactionalMsg("transactional-msg2", true); - // ROLLBACK_MESSAGE message - senderService.sendTransactionalMsg("transactional-msg3", true); - // COMMIT_MESSAGE message - senderService.sendTransactionalMsg("transactional-msg4", false); + senderService.sendTransactionalMsg("transactional-msg2", 2); + // ROLLBACK_MESSAGE message + senderService.sendTransactionalMsg("transactional-msg3", 3); + // COMMIT_MESSAGE message + senderService.sendTransactionalMsg("transactional-msg4", 4); } } diff --git a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/SenderService.java b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/SenderService.java similarity index 86% rename from spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/SenderService.java rename to spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/SenderService.java index e84ada23..b6d49cf8 100644 --- a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/SenderService.java +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/SenderService.java @@ -4,8 +4,9 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.rocketmq.common.message.MessageConst; +import org.apache.rocketmq.spring.support.RocketMQHeaders; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.alibaba.cloud.examples.RocketMQApplication.MySource; +import org.springframework.cloud.alibaba.cloud.examples.RocketMQProduceApplication.MySource; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.MessageBuilder; @@ -40,12 +41,11 @@ public class SenderService { source.output1().send(message); } - public void sendTransactionalMsg(T msg, boolean error) throws Exception { + public void sendTransactionalMsg(T msg, int num) throws Exception { MessageBuilder builder = MessageBuilder.withPayload(msg) .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON); - if (error) { - builder.setHeader("test", "1"); - } + builder.setHeader("test", String.valueOf(num)); + builder.setHeader(RocketMQHeaders.TAGS, "binder"); Message message = builder.build(); source.output2().send(message); } diff --git a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TransactionListenerImpl.java b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TransactionListenerImpl.java new file mode 100644 index 00000000..0bd48410 --- /dev/null +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/TransactionListenerImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2018 the original author or authors. + * + * 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. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener; +import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener; +import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState; +import org.springframework.messaging.Message; + +/** + * @author Jim + */ +@RocketMQTransactionListener(txProducerGroup = "myTxProducerGroup", corePoolSize = 5, maximumPoolSize = 10) +public class TransactionListenerImpl implements RocketMQLocalTransactionListener { + @Override + public RocketMQLocalTransactionState executeLocalTransaction(Message msg, + Object arg) { + Object num = msg.getHeaders().get("test"); + + if ("1".equals(num)) { + System.out.println( + "executer: " + new String((byte[]) msg.getPayload()) + " unknown"); + return RocketMQLocalTransactionState.UNKNOWN; + } + else if ("2".equals(num)) { + System.out.println( + "executer: " + new String((byte[]) msg.getPayload()) + " rollback"); + return RocketMQLocalTransactionState.ROLLBACK; + } + System.out.println( + "executer: " + new String((byte[]) msg.getPayload()) + " commit"); + return RocketMQLocalTransactionState.COMMIT; + } + + @Override + public RocketMQLocalTransactionState checkLocalTransaction(Message msg) { + System.out.println("check: " + new String((byte[]) msg.getPayload())); + return RocketMQLocalTransactionState.COMMIT; + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/resources/application.properties new file mode 100644 index 00000000..beca964a --- /dev/null +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/src/main/resources/application.properties @@ -0,0 +1,20 @@ +logging.level.org.springframework.cloud.stream.binder.rocketmq=DEBUG + +spring.cloud.stream.rocketmq.binder.name-server=127.0.0.1:9876 + +spring.cloud.stream.bindings.output1.destination=test-topic +spring.cloud.stream.bindings.output1.content-type=application/json +spring.cloud.stream.rocketmq.bindings.output1.producer.group=binder-group +spring.cloud.stream.rocketmq.bindings.output1.producer.sync=true + +spring.cloud.stream.bindings.output2.destination=TransactionTopic +spring.cloud.stream.bindings.output2.content-type=application/json +spring.cloud.stream.rocketmq.bindings.output2.producer.transactional=true +spring.cloud.stream.rocketmq.bindings.output2.producer.group=myTxProducerGroup + +spring.application.name=rocketmq-produce-example + +server.port=28081 + +management.endpoints.web.exposure.include=* +management.endpoint.health.show-details=always \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/MyTransactionCheckListener.java b/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/MyTransactionCheckListener.java deleted file mode 100644 index 65d72662..00000000 --- a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/MyTransactionCheckListener.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.springframework.cloud.alibaba.cloud.examples; - -import org.apache.rocketmq.client.producer.LocalTransactionState; -import org.apache.rocketmq.client.producer.TransactionCheckListener; -import org.apache.rocketmq.common.message.MessageExt; - -/** - * @author Jim - */ -public class MyTransactionCheckListener implements TransactionCheckListener { - - @Override - public LocalTransactionState checkLocalTransactionState(MessageExt msg) { - System.out.println("TransactionCheckListener: " + new String(msg.getBody())); - return LocalTransactionState.COMMIT_MESSAGE; - } - -} diff --git a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/MyTransactionExecuter.java b/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/MyTransactionExecuter.java deleted file mode 100644 index 752d4e5f..00000000 --- a/spring-cloud-alibaba-examples/rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/MyTransactionExecuter.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.springframework.cloud.alibaba.cloud.examples; - -import org.apache.rocketmq.client.producer.LocalTransactionExecuter; -import org.apache.rocketmq.client.producer.LocalTransactionState; -import org.apache.rocketmq.common.message.Message; - -/** - * @author Jim - */ -public class MyTransactionExecuter implements LocalTransactionExecuter { - @Override - public LocalTransactionState executeLocalTransactionBranch(Message msg, Object arg) { - if ("1".equals(msg.getUserProperty("test"))) { - System.out.println(new String(msg.getBody()) + " rollback"); - return LocalTransactionState.ROLLBACK_MESSAGE; - } - System.out.println(new String(msg.getBody()) + " commit"); - return LocalTransactionState.COMMIT_MESSAGE; - } -} diff --git a/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml b/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml index 57cb2df5..ec0e494d 100644 --- a/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml +++ b/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml index 507e8498..19e19124 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/flowrule.json b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/flowrule.json index d798f805..8aacfc33 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/flowrule.json +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/flowrule.json @@ -16,7 +16,7 @@ "strategy": 0 }, { - "resource": "http://www.taobao.com", + "resource": "GET:http://www.taobao.com", "controlBehavior": 0, "count": 0, "grade": 1, diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml index 5e0b8f4a..576fb2fe 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml index f10efec5..c8965042 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml index 5fde98b0..5f840343 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/sms-example/pom.xml b/spring-cloud-alibaba-examples/sms-example/pom.xml index d405a38d..9d2964d8 100644 --- a/spring-cloud-alibaba-examples/sms-example/pom.xml +++ b/spring-cloud-alibaba-examples/sms-example/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 sms-example diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml index b393fddb..b5140d31 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/pom.xml index 5a0debd1..2d68a55c 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-dubbo-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java index 8c2a0c9e..2e389ce2 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java @@ -17,6 +17,7 @@ package org.springframework.cloud.alibaba.dubbo.bootstrap; import org.apache.dubbo.config.annotation.Reference; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationRunner; @@ -52,7 +53,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; @EnableFeignClients public class DubboSpringCloudConsumerBootstrap { - @Reference(version = "1.0.0") + @Reference(version = "1.0.0", protocol = "dubbo") private RestService restService; @Autowired @@ -94,7 +95,7 @@ public class DubboSpringCloudConsumerBootstrap { } @FeignClient("${provider.application.name}") - @DubboTransported() + @DubboTransported(protocol = "dubbo") public interface DubboFeignRestService { @GetMapping(value = "/param") diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/resources/application.yaml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/resources/application.yaml index 263a5699..41c67146 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/resources/application.yaml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/resources/application.yaml @@ -9,4 +9,4 @@ server: provider: application: - name: spring-cloud-alibaba-dubbo-web-provider \ No newline at end of file + name: spring-cloud-alibaba-dubbo-provider \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml index 152e91aa..9c43a453 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-dubbo-examples org.springframework.cloud @@ -17,6 +17,13 @@ + + + javax.servlet + javax.servlet-api + 3.1.0 + + org.springframework diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudProviderBootstrap.java b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudProviderBootstrap.java index 4606a190..8e6ed4e9 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudProviderBootstrap.java +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudProviderBootstrap.java @@ -16,6 +16,7 @@ */ package org.springframework.cloud.alibaba.dubbo.bootstrap; +import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @@ -30,6 +31,7 @@ public class DubboSpringCloudProviderBootstrap { public static void main(String[] args) { new SpringApplicationBuilder(DubboSpringCloudProviderBootstrap.class) .properties("spring.profiles.active=nacos") + .web(WebApplicationType.NONE) .run(args); } } diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/pom.xml index af30d4b2..2c4cac57 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-dubbo-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/src/main/resources/application.yaml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/src/main/resources/application.yaml index a890294c..786da0f2 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/src/main/resources/application.yaml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/src/main/resources/application.yaml @@ -16,4 +16,4 @@ feign: enabled: true server: - port: 8080 \ No newline at end of file + port: 0 \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-sample-api/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-sample-api/pom.xml index 16b74cdb..75103947 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-sample-api/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-sample-api/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-dubbo-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml index 060bb532..67d9956d 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba-examples org.springframework.cloud diff --git a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/rocketmq/RocketMQBusApplication.java b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/rocketmq/RocketMQBusApplication.java index c4a05a8b..60299a22 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/rocketmq/RocketMQBusApplication.java +++ b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/rocketmq/RocketMQBusApplication.java @@ -16,8 +16,6 @@ */ package org.springframework.cloud.alibaba.cloud.examples.rocketmq; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -30,6 +28,9 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + /** * RocketMQ Bus Spring Application * @@ -41,54 +42,61 @@ import org.springframework.web.bind.annotation.RestController; @RemoteApplicationEventScan(basePackages = "org.springframework.cloud.alibaba.cloud.examples.rocketmq") public class RocketMQBusApplication { - public static void main(String[] args) { - new SpringApplicationBuilder(RocketMQBusApplication.class) - .properties("server.port=0") // Random server port - .properties("management.endpoints.web.exposure.include=*") // exposure includes all - .properties("spring.cloud.bus.trace.enabled=true") // Enable trace - .run(args); - } + public static void main(String[] args) { + new SpringApplicationBuilder(RocketMQBusApplication.class) + .properties("server.port=0") // Random server port + .properties("management.endpoints.web.exposure.include=*") // exposure + // includes + // all + .properties("spring.cloud.bus.trace.enabled=true") // Enable trace + .run(args); + } - @Autowired - private ApplicationEventPublisher publisher; + @Autowired + private ApplicationEventPublisher publisher; - @Value("${spring.cloud.bus.id}") - private String originService; + @Value("${spring.cloud.bus.id}") + private String originService; - @Value("${server.port}") - private int localServerPort; + @Value("${server.port}") + private int localServerPort; - @Autowired - private ObjectMapper objectMapper; + @Autowired + private ObjectMapper objectMapper; - /** - * Publish the {@link UserRemoteApplicationEvent} - * - * @param name the user name - * @param destination the destination - * @return If published - */ - @GetMapping("/bus/event/publish/user") - public boolean publish(@RequestParam String name, @RequestParam(required = false) String destination) { - User user = new User(); - user.setId(System.currentTimeMillis()); - user.setName(name); - publisher.publishEvent(new UserRemoteApplicationEvent(user, originService, destination)); - return true; - } + /** + * Publish the {@link UserRemoteApplicationEvent} + * + * @param name the user name + * @param destination the destination + * @return If published + */ + @GetMapping("/bus/event/publish/user") + public boolean publish(@RequestParam String name, + @RequestParam(required = false) String destination) { + User user = new User(); + user.setId(System.currentTimeMillis()); + user.setName(name); + publisher.publishEvent( + new UserRemoteApplicationEvent(this, user, originService, destination)); + return true; + } - /** - * Listener on the {@link UserRemoteApplicationEvent} - * - * @param event {@link UserRemoteApplicationEvent} - */ - @EventListener - public void onEvent(UserRemoteApplicationEvent event) { - System.out.printf("Server [port : %d] listeners on %s\n", localServerPort, event.getUser()); - } + /** + * Listener on the {@link UserRemoteApplicationEvent} + * + * @param event {@link UserRemoteApplicationEvent} + */ + @EventListener + public void onEvent(UserRemoteApplicationEvent event) { + System.out.printf("Server [port : %d] listeners on %s\n", localServerPort, + event.getUser()); + } - @EventListener - public void onAckEvent(AckRemoteApplicationEvent event) throws JsonProcessingException { - System.out.printf("Server [port : %d] listeners on %s\n", localServerPort, objectMapper.writeValueAsString(event)); - } + @EventListener + public void onAckEvent(AckRemoteApplicationEvent event) + throws JsonProcessingException { + System.out.printf("Server [port : %d] listeners on %s\n", localServerPort, + objectMapper.writeValueAsString(event)); + } } diff --git a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/rocketmq/UserRemoteApplicationEvent.java b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/rocketmq/UserRemoteApplicationEvent.java index 919e00ea..fa404e89 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/rocketmq/UserRemoteApplicationEvent.java +++ b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/java/org/springframework/cloud/alibaba/cloud/examples/rocketmq/UserRemoteApplicationEvent.java @@ -26,12 +26,22 @@ import org.springframework.cloud.bus.event.RemoteApplicationEvent; */ public class UserRemoteApplicationEvent extends RemoteApplicationEvent { - public UserRemoteApplicationEvent(User user, String originService, + private User user; + + public UserRemoteApplicationEvent() { + } + + public UserRemoteApplicationEvent(Object source, User user, String originService, String destinationService) { - super(user, originService, destinationService); + super(source, originService, destinationService); + this.user = user; + } + + public void setUser(User user) { + this.user = user; } public User getUser() { - return (User) getSource(); + return user; } } diff --git a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/resources/bootstrap.properties b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/resources/bootstrap.properties index 1b944de9..9e36d301 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/resources/bootstrap.properties +++ b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/src/main/resources/bootstrap.properties @@ -1,4 +1,4 @@ spring.application.name=spring-cloud-bus-rocketmq-example -spring.cloud.stream.rocketmq.binder.namesrv-addr=127.0.0.1:9876 +spring.cloud.stream.rocketmq.binder.name-server=127.0.0.1:9876 server.port=8080 spring.cloud.bus.id=${spring.application.name}:${server.port} \ No newline at end of file diff --git a/spring-cloud-alibaba-fescar/pom.xml b/spring-cloud-alibaba-fescar/pom.xml index dbdf4063..bc55fc99 100644 --- a/spring-cloud-alibaba-fescar/pom.xml +++ b/spring-cloud-alibaba-fescar/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud @@ -15,22 +15,10 @@ - - com.alibaba.fescar - fescar-core - - - com.alibaba.fescar - fescar-common - com.alibaba.fescar fescar-spring - - com.alibaba.fescar - fescar-rm-datasource - org.springframework.cloud diff --git a/spring-cloud-alibaba-nacos-config-server/pom.xml b/spring-cloud-alibaba-nacos-config-server/pom.xml index 4ec0478f..746a1549 100644 --- a/spring-cloud-alibaba-nacos-config-server/pom.xml +++ b/spring-cloud-alibaba-nacos-config-server/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud spring-cloud-alibaba diff --git a/spring-cloud-alibaba-nacos-config/pom.xml b/spring-cloud-alibaba-nacos-config/pom.xml index 2c85a841..05f4cd99 100644 --- a/spring-cloud-alibaba-nacos-config/pom.xml +++ b/spring-cloud-alibaba-nacos-config/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigAutoConfiguration.java b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigAutoConfiguration.java index 3833293e..caacccd8 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigAutoConfiguration.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigAutoConfiguration.java @@ -17,6 +17,7 @@ package org.springframework.cloud.alibaba.nacos; import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.alibaba.nacos.refresh.NacosContextRefresher; import org.springframework.cloud.alibaba.nacos.refresh.NacosRefreshHistory; import org.springframework.cloud.alibaba.nacos.refresh.NacosRefreshProperties; @@ -28,6 +29,7 @@ import org.springframework.context.annotation.Configuration; * @author juven.xuxb */ @Configuration +@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true) public class NacosConfigAutoConfiguration { @Bean diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigBootstrapConfiguration.java b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigBootstrapConfiguration.java index 94933a4d..5a6ad2bb 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigBootstrapConfiguration.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigBootstrapConfiguration.java @@ -26,6 +26,7 @@ import org.springframework.context.annotation.Configuration; * @author xiaojing */ @Configuration +@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true) public class NacosConfigBootstrapConfiguration { @Bean @@ -35,7 +36,6 @@ public class NacosConfigBootstrapConfiguration { } @Bean - @ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true) public NacosPropertySourceLocator nacosPropertySourceLocator( NacosConfigProperties nacosConfigProperties) { return new NacosPropertySourceLocator(nacosConfigProperties); diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigProperties.java b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigProperties.java index 5d13c01c..1518dcff 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigProperties.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/NacosConfigProperties.java @@ -55,11 +55,6 @@ public class NacosConfigProperties { private static final Logger log = LoggerFactory .getLogger(NacosConfigProperties.class); - /** - * whether to enable nacos config. - */ - private boolean enabled = true; - /** * nacos config server address */ @@ -122,8 +117,6 @@ public class NacosConfigProperties { private String name; - private String[] activeProfiles; - /** * the dataids for configurable multiple shared configurations , multiple separated by * commas . @@ -142,24 +135,8 @@ public class NacosConfigProperties { private ConfigService configService; - @Autowired - private Environment environment; - - @PostConstruct - public void init() { - this.activeProfiles = environment.getActiveProfiles(); - } - // todo sts support - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - public String getServerAddr() { return serverAddr; } @@ -260,10 +237,6 @@ public class NacosConfigProperties { return name; } - public String[] getActiveProfiles() { - return activeProfiles; - } - public String getSharedDataids() { return sharedDataids; } @@ -292,10 +265,6 @@ public class NacosConfigProperties { this.name = name; } - public void setActiveProfiles(String[] activeProfiles) { - this.activeProfiles = activeProfiles; - } - public static class Config { /** * the data id of extended configuration @@ -337,17 +306,15 @@ public class NacosConfigProperties { @Override public String toString() { - return "NacosConfigProperties{" + "enabled=" + enabled + ", serverAddr='" - + serverAddr + '\'' + ", encode='" + encode + '\'' + ", group='" + group - + '\'' + ", prefix='" + prefix + '\'' + ", fileExtension='" - + fileExtension + '\'' + ", timeout=" + timeout + ", endpoint='" - + endpoint + '\'' + ", namespace='" + namespace + '\'' + ", accessKey='" - + accessKey + '\'' + ", secretKey='" + secretKey + '\'' - + ", contextPath='" + contextPath + '\'' + ", clusterName='" + clusterName - + '\'' + ", name='" + name + '\'' + ", activeProfiles=" - + Arrays.toString(activeProfiles) + ", sharedDataids='" + sharedDataids - + '\'' + ", refreshableDataids='" + refreshableDataids + '\'' - + ", extConfig=" + extConfig + '}'; + return "NacosConfigProperties{" + "serverAddr='" + serverAddr + '\'' + + ", encode='" + encode + '\'' + ", group='" + group + '\'' + ", prefix='" + + prefix + '\'' + ", fileExtension='" + fileExtension + '\'' + + ", timeout=" + timeout + ", endpoint='" + endpoint + '\'' + + ", namespace='" + namespace + '\'' + ", accessKey='" + accessKey + '\'' + + ", secretKey='" + secretKey + '\'' + ", contextPath='" + contextPath + + '\'' + ", clusterName='" + clusterName + '\'' + ", name='" + name + '\'' + + ", sharedDataids='" + sharedDataids + '\'' + ", refreshableDataids='" + + refreshableDataids + '\'' + ", extConfig=" + extConfig + '}'; } public ConfigService configServiceInstance() { diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceLocator.java b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceLocator.java index 4c560658..306066eb 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceLocator.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/client/NacosPropertySourceLocator.java @@ -71,7 +71,6 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { timeout); String name = nacosConfigProperties.getName(); - String nacosGroup = nacosConfigProperties.getGroup(); String dataIdPrefix = nacosConfigProperties.getPrefix(); if (StringUtils.isEmpty(dataIdPrefix)) { dataIdPrefix = name; @@ -81,17 +80,12 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { dataIdPrefix = env.getProperty("spring.application.name"); } - List profiles = Arrays.asList(env.getActiveProfiles()); - nacosConfigProperties.setActiveProfiles(profiles.toArray(new String[0])); - - String fileExtension = nacosConfigProperties.getFileExtension(); - CompositePropertySource composite = new CompositePropertySource( NACOS_PROPERTY_SOURCE_NAME); loadSharedConfiguration(composite); loadExtConfiguration(composite); - loadApplicationConfiguration(composite, nacosGroup, dataIdPrefix, fileExtension); + loadApplicationConfiguration(composite, dataIdPrefix, nacosConfigProperties, env); return composite; } @@ -152,11 +146,15 @@ public class NacosPropertySourceLocator implements PropertySourceLocator { } private void loadApplicationConfiguration( - CompositePropertySource compositePropertySource, String nacosGroup, - String dataIdPrefix, String fileExtension) { + CompositePropertySource compositePropertySource, String dataIdPrefix, + NacosConfigProperties properties, Environment environment) { + + String fileExtension = properties.getFileExtension(); + String nacosGroup = properties.getGroup(); + loadNacosDataIfPresent(compositePropertySource, dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension, true); - for (String profile : nacosConfigProperties.getActiveProfiles()) { + for (String profile : environment.getActiveProfiles()) { String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension; loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup, fileExtension, true); diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/endpoint/NacosConfigEndpointAutoConfiguration.java b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/endpoint/NacosConfigEndpointAutoConfiguration.java index e853546d..21d77f7f 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/endpoint/NacosConfigEndpointAutoConfiguration.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/org/springframework/cloud/alibaba/nacos/endpoint/NacosConfigEndpointAutoConfiguration.java @@ -21,6 +21,7 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.condition.Conditi import org.springframework.boot.actuate.endpoint.annotation.Endpoint; 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.condition.ConditionalOnWebApplication; import org.springframework.cloud.alibaba.nacos.NacosConfigProperties; import org.springframework.cloud.alibaba.nacos.refresh.NacosRefreshHistory; @@ -31,6 +32,7 @@ import org.springframework.context.annotation.Bean; */ @ConditionalOnWebApplication @ConditionalOnClass(value = Endpoint.class) +@ConditionalOnProperty(name = "spring.cloud.nacos.config.enabled", matchIfMissing = true) public class NacosConfigEndpointAutoConfiguration { @Autowired diff --git a/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 3ce2a169..597126e8 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-alibaba-nacos-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -32,6 +32,12 @@ "name": "spring.cloud.nacos.config.ext-config", "type": "java.util.List", "description": "a set of extended configurations ." + }, + { + "name": "spring.cloud.nacos.config.enabled", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "enable nacos config or not." } ] } \ No newline at end of file diff --git a/spring-cloud-alibaba-nacos-config/src/test/java/org/springframework/cloud/alibaba/nacos/NacosConfigurationExtConfigTests.java b/spring-cloud-alibaba-nacos-config/src/test/java/org/springframework/cloud/alibaba/nacos/NacosConfigurationExtConfigTests.java index 024ddbc4..cc7098b1 100644 --- a/spring-cloud-alibaba-nacos-config/src/test/java/org/springframework/cloud/alibaba/nacos/NacosConfigurationExtConfigTests.java +++ b/spring-cloud-alibaba-nacos-config/src/test/java/org/springframework/cloud/alibaba/nacos/NacosConfigurationExtConfigTests.java @@ -16,15 +16,12 @@ package org.springframework.cloud.alibaba.nacos; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; -import java.util.Map; - import com.alibaba.nacos.client.config.NacosConfigService; import org.junit.Assert; @@ -42,7 +39,6 @@ import org.springframework.boot.autoconfigure.ImportAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.alibaba.nacos.client.NacosPropertySourceLocator; import org.springframework.cloud.alibaba.nacos.endpoint.NacosConfigEndpointAutoConfiguration; -import org.springframework.cloud.alibaba.nacos.refresh.NacosRefreshHistory; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.test.context.junit4.SpringRunner; diff --git a/spring-cloud-alibaba-nacos-config/src/test/java/org/springframework/cloud/alibaba/nacos/NacosConfigurationTests.java b/spring-cloud-alibaba-nacos-config/src/test/java/org/springframework/cloud/alibaba/nacos/NacosConfigurationTests.java index 8a4bdda6..fc2a3b12 100644 --- a/spring-cloud-alibaba-nacos-config/src/test/java/org/springframework/cloud/alibaba/nacos/NacosConfigurationTests.java +++ b/spring-cloud-alibaba-nacos-config/src/test/java/org/springframework/cloud/alibaba/nacos/NacosConfigurationTests.java @@ -163,13 +163,10 @@ public class NacosConfigurationTests { checkoutNacosConfigFileExtension(); checkoutNacosConfigTimeout(); checkoutNacosConfigEncode(); - checkoutNacosConfigProfiles(); - checkoutNacosConfigExtConfig(); checkoutEndpoint(); + checkoutDataLoad(); - Assert.assertEquals(environment.getProperty("user.name"), "dev"); - Assert.assertEquals(environment.getProperty("user.age"), "12"); } private void checkoutNacosConfigServerAddr() { @@ -235,20 +232,10 @@ public class NacosConfigurationTests { properties.getEncode()); } - private void checkoutNacosConfigExtConfig() { - assertEquals("NacosConfigProperties' ext config is wrong", - "ext-config-common01.properties", - properties.getExtConfig().get(0).getDataId()); - assertEquals("NacosConfigProperties' ext config is wrong", - "ext-config-common02.properties", - properties.getExtConfig().get(1).getDataId()); - assertEquals("NacosConfigProperties' ext config is wrong", "GLOBAL_GROUP", - properties.getExtConfig().get(1).getGroup()); - } + private void checkoutDataLoad() { - private void checkoutNacosConfigProfiles() { - assertEquals("NacosConfigProperties' profiles is wrong", - new String[] { "dev", "test" }, properties.getActiveProfiles()); + Assert.assertEquals(environment.getProperty("user.name"), "dev"); + Assert.assertEquals(environment.getProperty("user.age"), "12"); } private void checkoutEndpoint() throws Exception { diff --git a/spring-cloud-alibaba-nacos-discovery/pom.xml b/spring-cloud-alibaba-nacos-discovery/pom.xml index c775c097..ce2a8481 100644 --- a/spring-cloud-alibaba-nacos-discovery/pom.xml +++ b/spring-cloud-alibaba-nacos-discovery/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryAutoConfiguration.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryAutoConfiguration.java index 97e04060..6e6c2008 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryAutoConfiguration.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryAutoConfiguration.java @@ -16,12 +16,10 @@ package org.springframework.cloud.alibaba.nacos; -import org.springframework.boot.ApplicationRunner; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.alibaba.nacos.discovery.NacosDiscoveryClientAutoConfiguration; @@ -77,26 +75,4 @@ public class NacosDiscoveryAutoConfiguration { return new NacosAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration); } - - @Bean - @ConditionalOnBean(NacosAutoServiceRegistration.class) // NacosAutoServiceRegistration - // should be present - @ConditionalOnNotWebApplication // Not Web Application - public ApplicationRunner applicationRunner( - NacosAutoServiceRegistration nacosAutoServiceRegistration) { - return args -> { - // WebServerInitializedEvent should not be multicast in Non-Web environment. - // Whatever, NacosAutoServiceRegistration must be checked it's running or not. - if (!nacosAutoServiceRegistration.isRunning()) { // If it's not running, let - // it start. - // FIXME: Please make sure "spring.cloud.nacos.discovery.port" must be - // configured on an available port, - // or the startup or Nacos health check will be failed. - nacosAutoServiceRegistration.start(); - // NacosAutoServiceRegistration will be stopped after its destroy() method - // is invoked. - // @PreDestroy destroy() -> stop() - } - }; - } } \ No newline at end of file diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryProperties.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryProperties.java index 4805460f..48746f53 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryProperties.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryProperties.java @@ -19,7 +19,6 @@ package org.springframework.cloud.alibaba.nacos; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.client.naming.utils.UtilAndComs; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -28,25 +27,14 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.commons.util.InetUtils; import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; - import javax.annotation.PostConstruct; import java.net.Inet4Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Properties; +import java.util.*; -import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY; -import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME; -import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT; -import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE; -import static com.alibaba.nacos.api.PropertyKeyConst.NAMING_LOAD_CACHE_AT_START; -import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY; -import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; +import static com.alibaba.nacos.api.PropertyKeyConst.*; /** * @author dungu.zpf diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/discovery/NacosDiscoveryClient.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/discovery/NacosDiscoveryClient.java index ccf2e838..bcfa66f3 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/discovery/NacosDiscoveryClient.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/discovery/NacosDiscoveryClient.java @@ -18,7 +18,6 @@ package org.springframework.cloud.alibaba.nacos.discovery; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties; @@ -26,11 +25,7 @@ import org.springframework.cloud.alibaba.nacos.NacosServiceInstance; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * @author xiaojing @@ -38,74 +33,75 @@ import java.util.Map; */ public class NacosDiscoveryClient implements DiscoveryClient { - private static final Logger log = LoggerFactory - .getLogger(NacosDiscoveryClient.class); - public static final String DESCRIPTION = "Spring Cloud Nacos Discovery Client"; + private static final Logger log = LoggerFactory.getLogger(NacosDiscoveryClient.class); + public static final String DESCRIPTION = "Spring Cloud Nacos Discovery Client"; - private NacosDiscoveryProperties discoveryProperties; + private NacosDiscoveryProperties discoveryProperties; - public NacosDiscoveryClient(NacosDiscoveryProperties discoveryProperties) { - this.discoveryProperties = discoveryProperties; - } + public NacosDiscoveryClient(NacosDiscoveryProperties discoveryProperties) { + this.discoveryProperties = discoveryProperties; + } - @Override - public String description() { - return DESCRIPTION; - } + @Override + public String description() { + return DESCRIPTION; + } - @Override - public List getInstances(String serviceId) { - try { - List instances = discoveryProperties.namingServiceInstance() - .selectInstances(serviceId, true); - return hostToServiceInstanceList(instances, serviceId); - } catch (Exception e) { - throw new RuntimeException( - "Can not get hosts from nacos server. serviceId: " + serviceId, e); - } - } + @Override + public List getInstances(String serviceId) { + try { + List instances = discoveryProperties.namingServiceInstance() + .selectInstances(serviceId, true); + return hostToServiceInstanceList(instances, serviceId); + } + catch (Exception e) { + throw new RuntimeException( + "Can not get hosts from nacos server. serviceId: " + serviceId, e); + } + } - private static ServiceInstance hostToServiceInstance(Instance instance, - String serviceId) { - NacosServiceInstance nacosServiceInstance = new NacosServiceInstance(); - nacosServiceInstance.setHost(instance.getIp()); - nacosServiceInstance.setPort(instance.getPort()); - nacosServiceInstance.setServiceId(serviceId); + private static ServiceInstance hostToServiceInstance(Instance instance, + String serviceId) { + NacosServiceInstance nacosServiceInstance = new NacosServiceInstance(); + nacosServiceInstance.setHost(instance.getIp()); + nacosServiceInstance.setPort(instance.getPort()); + nacosServiceInstance.setServiceId(serviceId); - Map metadata = new HashMap<>(); - metadata.put("nacos.instanceId", instance.getInstanceId()); - metadata.put("nacos.weight", instance.getWeight() + ""); - metadata.put("nacos.healthy", instance.isHealthy() + ""); - metadata.put("nacos.cluster", instance.getClusterName() + ""); - metadata.putAll(instance.getMetadata()); - nacosServiceInstance.setMetadata(metadata); + Map metadata = new HashMap<>(); + metadata.put("nacos.instanceId", instance.getInstanceId()); + metadata.put("nacos.weight", instance.getWeight() + ""); + metadata.put("nacos.healthy", instance.isHealthy() + ""); + metadata.put("nacos.cluster", instance.getClusterName() + ""); + metadata.putAll(instance.getMetadata()); + nacosServiceInstance.setMetadata(metadata); - if (metadata.containsKey("secure")) { - boolean secure = Boolean.parseBoolean(metadata.get("secure")); - nacosServiceInstance.setSecure(secure); - } - return nacosServiceInstance; - } + if (metadata.containsKey("secure")) { + boolean secure = Boolean.parseBoolean(metadata.get("secure")); + nacosServiceInstance.setSecure(secure); + } + return nacosServiceInstance; + } - private static List hostToServiceInstanceList( - List instances, String serviceId) { - List result = new ArrayList<>(instances.size()); - for (Instance instance : instances) { - result.add(hostToServiceInstance(instance, serviceId)); - } - return result; - } + private static List hostToServiceInstanceList( + List instances, String serviceId) { + List result = new ArrayList<>(instances.size()); + for (Instance instance : instances) { + result.add(hostToServiceInstance(instance, serviceId)); + } + return result; + } - @Override - public List getServices() { + @Override + public List getServices() { - try { - ListView services = discoveryProperties.namingServiceInstance() - .getServicesOfServer(1, Integer.MAX_VALUE); - return services.getData(); - } catch (Exception e) { - log.error("get service name from nacos server fail,", e); - return Collections.emptyList(); - } - } + try { + ListView services = discoveryProperties.namingServiceInstance() + .getServicesOfServer(1, Integer.MAX_VALUE); + return services.getData(); + } + catch (Exception e) { + log.error("get service name from nacos server fail,", e); + return Collections.emptyList(); + } + } } diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/registry/NacosRegistration.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/registry/NacosRegistration.java index 587cc475..68d8c995 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/registry/NacosRegistration.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/registry/NacosRegistration.java @@ -40,6 +40,7 @@ public class NacosRegistration implements Registration, ServiceInstance { public static final String MANAGEMENT_PORT = "management.port"; public static final String MANAGEMENT_CONTEXT_PATH = "management.context-path"; public static final String MANAGEMENT_ADDRESS = "management.address"; + public static final String MANAGEMENT_ENDPOINT_BASE_PATH = "management.endpoints.web.base-path"; private NacosDiscoveryProperties nacosDiscoveryProperties; @@ -54,10 +55,16 @@ public class NacosRegistration implements Registration, ServiceInstance { @PostConstruct public void init() { + Map metadata = nacosDiscoveryProperties.getMetadata(); Environment env = context.getEnvironment(); + + String endpointBasePath = env.getProperty(MANAGEMENT_ENDPOINT_BASE_PATH); + if (!StringUtils.isEmpty(endpointBasePath)) { + metadata.put(MANAGEMENT_ENDPOINT_BASE_PATH, endpointBasePath); + } + Integer managementPort = ManagementServerPortUtils.getPort(context); if (null != managementPort) { - Map metadata = nacosDiscoveryProperties.getMetadata(); metadata.put(MANAGEMENT_PORT, managementPort.toString()); String contextPath = env .getProperty("management.server.servlet.context-path"); diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/registry/NacosServiceRegistry.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/registry/NacosServiceRegistry.java index 5e9d423f..6c362af6 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/registry/NacosServiceRegistry.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/registry/NacosServiceRegistry.java @@ -18,7 +18,6 @@ package org.springframework.cloud.alibaba.nacos.registry; import com.alibaba.nacos.api.naming.NamingService; import com.alibaba.nacos.api.naming.pojo.Instance; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties; diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServer.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServer.java index 95debd87..70f33f97 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServer.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServer.java @@ -17,7 +17,6 @@ package org.springframework.cloud.alibaba.nacos.ribbon; import com.alibaba.nacos.api.naming.pojo.Instance; - import com.netflix.loadbalancer.Server; import java.util.Map; diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServerList.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServerList.java index 2bad9e41..9bfcefaa 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServerList.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/NacosServerList.java @@ -17,7 +17,6 @@ package org.springframework.cloud.alibaba.nacos.ribbon; import com.alibaba.nacos.api.naming.pojo.Instance; - import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractServerList; import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties; diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/RibbonNacosAutoConfiguration.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/RibbonNacosAutoConfiguration.java index 8180a700..7021fa7c 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/RibbonNacosAutoConfiguration.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/ribbon/RibbonNacosAutoConfiguration.java @@ -19,6 +19,7 @@ package org.springframework.cloud.alibaba.nacos.ribbon; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.alibaba.nacos.ConditionalOnNacosDiscoveryEnabled; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; import org.springframework.cloud.netflix.ribbon.RibbonClients; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; @@ -32,6 +33,7 @@ import org.springframework.context.annotation.Configuration; @EnableConfigurationProperties @ConditionalOnBean(SpringClientFactory.class) @ConditionalOnRibbonNacos +@ConditionalOnNacosDiscoveryEnabled @AutoConfigureAfter(RibbonAutoConfiguration.class) @RibbonClients(defaultConfiguration = NacosRibbonClientConfiguration.class) public class RibbonNacosAutoConfiguration { diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json index d0e82a51..75e8abd4 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-alibaba-nacos-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -5,6 +5,12 @@ "defaultValue": "${spring.application.name}", "description": "the service name to register, default value is ${spring.application.name}." }, + { + "name": "spring.cloud.nacos.discovery.enabled", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "enable nacos discovery or not." + }, { "name": "spring.cloud.nacos.discovery.namingLoadCacheAtStart", "type": "java.lang.Boolean", diff --git a/spring-cloud-alibaba-sentinel-datasource/pom.xml b/spring-cloud-alibaba-sentinel-datasource/pom.xml index e75033f1..17335dec 100644 --- a/spring-cloud-alibaba-sentinel-datasource/pom.xml +++ b/spring-cloud-alibaba-sentinel-datasource/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-sentinel-zuul/pom.xml b/spring-cloud-alibaba-sentinel-zuul/pom.xml index d8f8fa80..7239c726 100644 --- a/spring-cloud-alibaba-sentinel-zuul/pom.xml +++ b/spring-cloud-alibaba-sentinel-zuul/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba org.springframework.cloud diff --git a/spring-cloud-alibaba-sentinel/pom.xml b/spring-cloud-alibaba-sentinel/pom.xml index 7f4354a8..1cf0ae99 100644 --- a/spring-cloud-alibaba-sentinel/pom.xml +++ b/spring-cloud-alibaba-sentinel/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java index 26613a42..3deda90f 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelAutoConfiguration.java @@ -16,6 +16,25 @@ package org.springframework.cloud.alibaba.sentinel.custom; +import java.util.Optional; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.alibaba.sentinel.SentinelProperties; +import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter; +import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; + import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser; import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler; import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlCleaner; @@ -32,25 +51,9 @@ import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; import com.alibaba.csp.sentinel.slots.system.SystemRule; import com.alibaba.csp.sentinel.transport.config.TransportConfig; import com.alibaba.csp.sentinel.util.AppNameUtil; + import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.xml.XmlMapper; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.alibaba.sentinel.SentinelProperties; -import org.springframework.cloud.alibaba.sentinel.datasource.converter.JsonConverter; -import org.springframework.cloud.alibaba.sentinel.datasource.converter.XmlConverter; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.util.StringUtils; - -import javax.annotation.PostConstruct; -import java.util.Optional; /** * @author xiaojing @@ -159,6 +162,7 @@ public class SentinelAutoConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") + @ConditionalOnProperty(name = "resttemplate.sentinel.enabled", havingValue = "true", matchIfMissing = true) public SentinelBeanPostProcessor sentinelBeanPostProcessor( ApplicationContext applicationContext) { return new SentinelBeanPostProcessor(applicationContext); diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java index fe22c902..7ec733b5 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelBeanPostProcessor.java @@ -16,7 +16,9 @@ package org.springframework.cloud.alibaba.sentinel.custom; -import com.alibaba.csp.sentinel.slots.block.BlockException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,9 +40,7 @@ import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.concurrent.ConcurrentHashMap; +import com.alibaba.csp.sentinel.slots.block.BlockException; /** * PostProcessor handle @SentinelRestTemplate Annotation, add interceptor for RestTemplate @@ -193,6 +193,9 @@ public class SentinelBeanPostProcessor implements MergedBeanDefinitionPostProces // register SentinelProtectInterceptor bean DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext .getAutowireCapableBeanFactory(); + if (beanFactory.containsBean(interceptorBeanName)) { + return; + } BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder .genericBeanDefinition(SentinelProtectInterceptor.class); beanDefinitionBuilder.addConstructorArgValue(sentinelRestTemplate); diff --git a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java index 4d9c2eac..48f63ef3 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/org/springframework/cloud/alibaba/sentinel/custom/SentinelProtectInterceptor.java @@ -16,12 +16,10 @@ package org.springframework.cloud.alibaba.sentinel.custom; -import com.alibaba.csp.sentinel.Entry; -import com.alibaba.csp.sentinel.SphU; -import com.alibaba.csp.sentinel.Tracer; -import com.alibaba.csp.sentinel.context.ContextUtil; -import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URI; import org.springframework.cloud.alibaba.sentinel.annotation.SentinelRestTemplate; import org.springframework.cloud.alibaba.sentinel.rest.SentinelClientHttpResponse; @@ -30,10 +28,12 @@ import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; -import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URI; +import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.Tracer; +import com.alibaba.csp.sentinel.context.ContextUtil; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; /** * Interceptor using by SentinelRestTemplate @@ -52,7 +52,8 @@ public class SentinelProtectInterceptor implements ClientHttpRequestInterceptor public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { URI uri = request.getURI(); - String hostResource = uri.getScheme() + "://" + uri.getHost() + String hostResource = request.getMethod().toString() + ":" + uri.getScheme() + + "://" + uri.getHost() + (uri.getPort() == -1 ? "" : ":" + uri.getPort()); String hostWithPathResource = hostResource + uri.getPath(); boolean entryWithPath = true; diff --git a/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 80fec1a5..21713de7 100644 --- a/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-alibaba-sentinel/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -6,6 +6,12 @@ "defaultValue": true, "description": "enable or disable sentinel auto configure." }, + { + "name": "resttemplate.sentinel.enabled", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "enable or disable @SentinelRestTemplate." + }, { "name": "spring.cloud.sentinel.eager", "type": "java.lang.Boolean", diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java index 138c6502..434d0237 100644 --- a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java +++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelAutoConfigurationTests.java @@ -16,16 +16,15 @@ package org.springframework.cloud.alibaba.sentinel; -import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig; -import com.alibaba.csp.sentinel.config.SentinelConfig; -import com.alibaba.csp.sentinel.log.LogBase; -import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.slots.block.RuleConstant; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; -import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; -import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; -import com.alibaba.csp.sentinel.transport.config.TransportConfig; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +import java.util.Arrays; +import java.util.Map; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -51,14 +50,16 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; -import java.util.Arrays; -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mock; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig; +import com.alibaba.csp.sentinel.config.SentinelConfig; +import com.alibaba.csp.sentinel.log.LogBase; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; +import com.alibaba.csp.sentinel.transport.config.TransportConfig; /** * @author Jim @@ -112,7 +113,7 @@ public class SentinelAutoConfigurationTests { FlowRule rule = new FlowRule(); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(0); - rule.setResource(url); + rule.setResource("GET:" + url); rule.setLimitApp("default"); rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); rule.setStrategy(RuleConstant.STRATEGY_DIRECT); @@ -120,7 +121,7 @@ public class SentinelAutoConfigurationTests { DegradeRule degradeRule = new DegradeRule(); degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT); - degradeRule.setResource(url + "/test"); + degradeRule.setResource("GET:" + url + "/test"); degradeRule.setCount(0); degradeRule.setTimeWindow(60); DegradeRuleManager.loadRules(Arrays.asList(degradeRule)); diff --git a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelRestTemplateTests.java b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelRestTemplateTests.java index f95c4616..43b0b43d 100644 --- a/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelRestTemplateTests.java +++ b/spring-cloud-alibaba-sentinel/src/test/java/org/springframework/cloud/alibaba/sentinel/SentinelRestTemplateTests.java @@ -85,6 +85,11 @@ public class SentinelRestTemplateTests { new AnnotationConfigApplicationContext(TestConfig9.class); } + @Test + public void testNormalWithoutParam() { + new AnnotationConfigApplicationContext(TestConfig10.class); + } + @Configuration public static class TestConfig1 { @Bean @@ -220,6 +225,27 @@ public class SentinelRestTemplateTests { } } + @Configuration + public static class TestConfig10 { + @Bean + SentinelBeanPostProcessor sentinelBeanPostProcessor( + ApplicationContext applicationContext) { + return new SentinelBeanPostProcessor(applicationContext); + } + + @Bean + @SentinelRestTemplate + RestTemplate restTemplate() { + return new RestTemplate(); + } + + @Bean + @SentinelRestTemplate + RestTemplate restTemplate2() { + return new RestTemplate(); + } + } + public static class ExceptionUtil { public static SentinelClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException ex) { diff --git a/spring-cloud-alibaba-test/core-support/pom.xml b/spring-cloud-alibaba-test/core-support/pom.xml index 46f75bc2..95533912 100644 --- a/spring-cloud-alibaba-test/core-support/pom.xml +++ b/spring-cloud-alibaba-test/core-support/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-test/pom.xml b/spring-cloud-alibaba-test/pom.xml index 2f01ce0a..39811370 100644 --- a/spring-cloud-alibaba-test/pom.xml +++ b/spring-cloud-alibaba-test/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alibaba-test/sentinel-test-support/pom.xml b/spring-cloud-alibaba-test/sentinel-test-support/pom.xml index 51e27297..e162f2ac 100644 --- a/spring-cloud-alibaba-test/sentinel-test-support/pom.xml +++ b/spring-cloud-alibaba-test/sentinel-test-support/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alicloud-acm/pom.xml b/spring-cloud-alicloud-acm/pom.xml index 5212fdd1..dd8492d2 100644 --- a/spring-cloud-alicloud-acm/pom.xml +++ b/spring-cloud-alicloud-acm/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-alicloud-ans/pom.xml b/spring-cloud-alicloud-ans/pom.xml index ac784829..e476f763 100644 --- a/spring-cloud-alicloud-ans/pom.xml +++ b/spring-cloud-alicloud-ans/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba org.springframework.cloud diff --git a/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/registry/AnsRegistration.java b/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/registry/AnsRegistration.java index 5cbcb732..f04abadf 100644 --- a/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/registry/AnsRegistration.java +++ b/spring-cloud-alicloud-ans/src/main/java/org/springframework/cloud/alicloud/ans/registry/AnsRegistration.java @@ -37,6 +37,7 @@ public class AnsRegistration implements Registration, ServiceInstance { static final String MANAGEMENT_PORT = "management.port"; static final String MANAGEMENT_CONTEXT_PATH = "management.context-path"; static final String MANAGEMENT_ADDRESS = "management.address"; + static final String MANAGEMENT_ENDPOINT_BASE_PATH = "management.endpoints.web.base-path"; private AnsProperties ansProperties; private ApplicationContext context; @@ -49,10 +50,16 @@ public class AnsRegistration implements Registration, ServiceInstance { @PostConstruct public void init() { + Map metadata = ansProperties.getClientMetadata(); Environment env = context.getEnvironment(); + + String endpointBasePath = env.getProperty(MANAGEMENT_ENDPOINT_BASE_PATH); + if (!StringUtils.isEmpty(endpointBasePath)) { + metadata.put(MANAGEMENT_ENDPOINT_BASE_PATH, endpointBasePath); + } + Integer managementPort = ManagementServerPortUtils.getPort(context); if (null != managementPort) { - Map metadata = ansProperties.getClientMetadata(); metadata.put(MANAGEMENT_PORT, managementPort.toString()); String contextPath = env .getProperty("management.server.servlet.context-path"); diff --git a/spring-cloud-alicloud-context/pom.xml b/spring-cloud-alicloud-context/pom.xml index 4b798458..a38ba6b0 100644 --- a/spring-cloud-alicloud-context/pom.xml +++ b/spring-cloud-alicloud-context/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alicloud-oss/pom.xml b/spring-cloud-alicloud-oss/pom.xml index 0596e451..806fd571 100644 --- a/spring-cloud-alicloud-oss/pom.xml +++ b/spring-cloud-alicloud-oss/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-alicloud-schedulerx/pom.xml b/spring-cloud-alicloud-schedulerx/pom.xml index db58050a..a491c1af 100644 --- a/spring-cloud-alicloud-schedulerx/pom.xml +++ b/spring-cloud-alicloud-schedulerx/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> spring-cloud-alibaba org.springframework.cloud diff --git a/spring-cloud-alicloud-sms/pom.xml b/spring-cloud-alicloud-sms/pom.xml index b69530a0..d7e1c670 100644 --- a/spring-cloud-alicloud-sms/pom.xml +++ b/spring-cloud-alicloud-sms/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud diff --git a/spring-cloud-starter-alibaba/pom.xml b/spring-cloud-starter-alibaba/pom.xml index 70e8dc18..0602a839 100644 --- a/spring-cloud-starter-alibaba/pom.xml +++ b/spring-cloud-starter-alibaba/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.cloud diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-fescar/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-fescar/pom.xml index 8376bfc1..6ee7fdcd 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-fescar/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-fescar/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config-server/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config-server/pom.xml index 31605341..e70f556b 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config-server/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config-server/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config/pom.xml index 83a97db9..aba7efa5 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-discovery/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-discovery/pom.xml index 39925f06..a731503c 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-discovery/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-discovery/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml index e05dd734..3af2f7be 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/pom.xml index 474b41b7..ca02bbe7 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud spring-cloud-starter-alibaba diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/src/main/java/org/springframework/cloud/bus/rocketmq/env/RocketMQBusEnvironmentPostProcessor.java b/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/src/main/java/org/springframework/cloud/bus/rocketmq/env/RocketMQBusEnvironmentPostProcessor.java index 384db727..093dce41 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/src/main/java/org/springframework/cloud/bus/rocketmq/env/RocketMQBusEnvironmentPostProcessor.java +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/src/main/java/org/springframework/cloud/bus/rocketmq/env/RocketMQBusEnvironmentPostProcessor.java @@ -16,6 +16,11 @@ */ package org.springframework.cloud.bus.rocketmq.env; +import static org.springframework.cloud.bus.SpringCloudBusClient.INPUT; + +import java.util.HashMap; +import java.util.Map; + import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.cloud.bus.BusEnvironmentPostProcessor; @@ -25,82 +30,88 @@ import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; -import java.util.HashMap; -import java.util.Map; - -import static org.springframework.cloud.bus.SpringCloudBusClient.INPUT; - /** - * The lowest precedence {@link EnvironmentPostProcessor} configures default RocketMQ Bus Properties that will be - * appended into {@link SpringApplication#defaultProperties} + * The lowest precedence {@link EnvironmentPostProcessor} configures default RocketMQ Bus + * Properties that will be appended into {@link SpringApplication#defaultProperties} * * @author Mercy * @see BusEnvironmentPostProcessor * @since 0.2.1 */ -public class RocketMQBusEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { +public class RocketMQBusEnvironmentPostProcessor + implements EnvironmentPostProcessor, Ordered { - /** - * The name of {@link PropertySource} of {@link SpringApplication#defaultProperties} - */ - private static final String PROPERTY_SOURCE_NAME = "defaultProperties"; + /** + * The name of {@link PropertySource} of {@link SpringApplication#defaultProperties} + */ + private static final String PROPERTY_SOURCE_NAME = "defaultProperties"; - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, + SpringApplication application) { - addDefaultPropertySource(environment); + addDefaultPropertySource(environment); - } + } - private void addDefaultPropertySource(ConfigurableEnvironment environment) { + private void addDefaultPropertySource(ConfigurableEnvironment environment) { - Map map = new HashMap(); + Map map = new HashMap(); - configureDefaultProperties(map); + configureDefaultProperties(map); - addOrReplace(environment.getPropertySources(), map); - } + addOrReplace(environment.getPropertySources(), map); + } - private void configureDefaultProperties(Map source) { - // Required Properties - String groupBindingPropertyName = createBindingPropertyName(INPUT, "group"); - source.put(groupBindingPropertyName, "rocketmq-bus-group"); - } + private void configureDefaultProperties(Map source) { + // Required Properties + String groupBindingPropertyName = createBindingPropertyName(INPUT, "group"); + String broadcastingPropertyName = createRocketMQPropertyName(INPUT, + "broadcasting"); + source.put(groupBindingPropertyName, "rocketmq-bus-group"); + source.put(broadcastingPropertyName, "true"); + } - private String createBindingPropertyName(String channel, String propertyName) { - return "spring.cloud.stream.bindings." + channel + "." + propertyName; - } + private String createRocketMQPropertyName(String channel, String propertyName) { + return "spring.cloud.stream.rocketmq.bindings." + INPUT + ".consumer." + + propertyName; + } - /** - * Copy from {@link BusEnvironmentPostProcessor#addOrReplace(MutablePropertySources, Map)} - * - * @param propertySources {@link MutablePropertySources} - * @param map Default RocketMQ Bus Properties - */ - private void addOrReplace(MutablePropertySources propertySources, - Map map) { - MapPropertySource target = null; - if (propertySources.contains(PROPERTY_SOURCE_NAME)) { - PropertySource source = propertySources.get(PROPERTY_SOURCE_NAME); - if (source instanceof MapPropertySource) { - target = (MapPropertySource) source; - for (String key : map.keySet()) { - if (!target.containsProperty(key)) { - target.getSource().put(key, map.get(key)); - } - } - } - } - if (target == null) { - target = new MapPropertySource(PROPERTY_SOURCE_NAME, map); - } - if (!propertySources.contains(PROPERTY_SOURCE_NAME)) { - propertySources.addLast(target); - } - } + private String createBindingPropertyName(String channel, String propertyName) { + return "spring.cloud.stream.bindings." + channel + "." + propertyName; + } - @Override - public int getOrder() { - return LOWEST_PRECEDENCE; - } + /** + * Copy from + * {@link BusEnvironmentPostProcessor#addOrReplace(MutablePropertySources, Map)} + * + * @param propertySources {@link MutablePropertySources} + * @param map Default RocketMQ Bus Properties + */ + private void addOrReplace(MutablePropertySources propertySources, + Map map) { + MapPropertySource target = null; + if (propertySources.contains(PROPERTY_SOURCE_NAME)) { + PropertySource source = propertySources.get(PROPERTY_SOURCE_NAME); + if (source instanceof MapPropertySource) { + target = (MapPropertySource) source; + for (String key : map.keySet()) { + if (!target.containsProperty(key)) { + target.getSource().put(key, map.get(key)); + } + } + } + } + if (target == null) { + target = new MapPropertySource(PROPERTY_SOURCE_NAME, map); + } + if (!propertySources.contains(PROPERTY_SOURCE_NAME)) { + propertySources.addLast(target); + } + } + + @Override + public int getOrder() { + return LOWEST_PRECEDENCE; + } } diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-dubbo/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-dubbo/pom.xml index 2eb028ff..70b21a86 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-dubbo/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-dubbo/pom.xml @@ -1,7 +1,7 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud spring-cloud-starter-alibaba diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-stream-rocketmq/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-stream-rocketmq/pom.xml index fb6007a8..88d98a1f 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-stream-rocketmq/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-stream-rocketmq/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-starter-alicloud/pom.xml b/spring-cloud-starter-alicloud/pom.xml index df47cfbb..2e27ba3b 100644 --- a/spring-cloud-starter-alicloud/pom.xml +++ b/spring-cloud-starter-alicloud/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.cloud diff --git a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-acm/pom.xml b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-acm/pom.xml index 00b8a78a..9079ba98 100644 --- a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-acm/pom.xml +++ b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-acm/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-ans/pom.xml b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-ans/pom.xml index 69c4981c..c7754336 100644 --- a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-ans/pom.xml +++ b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-ans/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.cloud diff --git a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-oss/pom.xml b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-oss/pom.xml index 8668c774..d012ccdd 100644 --- a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-oss/pom.xml +++ b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-oss/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-schedulerx/pom.xml b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-schedulerx/pom.xml index f6bcc917..eb9e816b 100644 --- a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-schedulerx/pom.xml +++ b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-schedulerx/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-sms/pom.xml b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-sms/pom.xml index 57a398f7..c457de27 100644 --- a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-sms/pom.xml +++ b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-sms/pom.xml @@ -1,5 +1,5 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 diff --git a/spring-cloud-stream-binder-rocketmq/pom.xml b/spring-cloud-stream-binder-rocketmq/pom.xml index fad85417..cc839023 100644 --- a/spring-cloud-stream-binder-rocketmq/pom.xml +++ b/spring-cloud-stream-binder-rocketmq/pom.xml @@ -1,6 +1,6 @@ + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> org.springframework.cloud @@ -21,54 +21,41 @@ spring-cloud-stream - - org.apache.rocketmq - rocketmq-client - - - - io.dropwizard.metrics - metrics-core - 4.0.3 - provided - true - - org.springframework.boot spring-boot-configuration-processor - provided true org.springframework.boot spring-boot - provided true org.springframework.boot spring-boot-autoconfigure - provided true org.springframework.boot spring-boot-actuator - provided true org.springframework.boot spring-boot-actuator-autoconfigure - provided true + + org.apache.rocketmq + rocketmq-spring-boot-starter + + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQBinderConstants.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQBinderConstants.java index 66517074..c57ce9b6 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQBinderConstants.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQBinderConstants.java @@ -21,43 +21,16 @@ package org.springframework.cloud.stream.binder.rocketmq; */ public interface RocketMQBinderConstants { - String ENDPOINT_ID = "rocketmq-binder"; - /** * Header key */ - String ORIGINAL_ROCKET_MESSAGE = "ORIGINAL_ROCKETMQ_MESSAGE"; - - String ROCKET_FLAG = "ROCKETMQ_FLAG"; - - String ROCKET_SEND_RESULT = "ROCKETMQ_SEND_RESULT"; - - String ROCKET_TRANSACTIONAL_ARG = "ROCKETMQ_TRANSACTIONAL_ARG"; - - String ACKNOWLEDGEMENT_KEY = "ACKNOWLEDGEMENT"; + String ROCKET_TRANSACTIONAL_ARG = "TRANSACTIONAL_ARG"; /** - * Instrumentation + * Default value */ - String LASTSEND_TIMESTAMP = "lastSend.timestamp"; + String DEFAULT_NAME_SERVER = "127.0.0.1:9876"; - interface Metrics { - interface Producer { - String PREFIX = "scs-rocketmq.producer."; - String TOTAL_SENT = "totalSent"; - String TOTAL_SENT_FAILURES = "totalSentFailures"; - String SENT_PER_SECOND = "sentPerSecond"; - String SENT_FAILURES_PER_SECOND = "sentFailuresPerSecond"; - } - - interface Consumer { - String GROUP_PREFIX = "scs-rocketmq.consumerGroup."; - String PREFIX = "scs-rocketmq.consumer."; - String TOTAL_CONSUMED = "totalConsumed"; - String CONSUMED_PER_SECOND = "consumedPerSecond"; - String TOTAL_CONSUMED_FAILURES = "totalConsumedFailures"; - String CONSUMED_FAILURES_PER_SECOND = "consumedFailuresPerSecond"; - } - } + String DEFAULT_GROUP = "rocketmq_binder_default_group_name"; } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQBinderUtils.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQBinderUtils.java new file mode 100644 index 00000000..b50e799d --- /dev/null +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQBinderUtils.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2018 the original author or authors. + * + * 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. + */ + +package org.springframework.cloud.stream.binder.rocketmq; + +import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties; +import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQBinderConfigurationProperties; +import org.springframework.util.StringUtils; + +/** + * @author Jim + */ +public class RocketMQBinderUtils { + + public static RocketMQBinderConfigurationProperties mergeProperties( + RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties, + RocketMQProperties rocketMQProperties) { + RocketMQBinderConfigurationProperties result = new RocketMQBinderConfigurationProperties(); + if (StringUtils.isEmpty(rocketMQProperties.getNameServer())) { + result.setNameServer(rocketBinderConfigurationProperties.getNameServer()); + } + else { + result.setNameServer(rocketMQProperties.getNameServer()); + } + if (rocketMQProperties.getProducer() == null + || StringUtils.isEmpty(rocketMQProperties.getProducer().getAccessKey())) { + result.setAccessKey(rocketBinderConfigurationProperties.getAccessKey()); + } + else { + result.setAccessKey(rocketMQProperties.getProducer().getAccessKey()); + } + if (rocketMQProperties.getProducer() == null + || StringUtils.isEmpty(rocketMQProperties.getProducer().getSecretKey())) { + result.setSecretKey(rocketBinderConfigurationProperties.getSecretKey()); + } + else { + result.setSecretKey(rocketMQProperties.getProducer().getSecretKey()); + } + if (rocketMQProperties.getProducer() == null || StringUtils + .isEmpty(rocketMQProperties.getProducer().getCustomizedTraceTopic())) { + result.setCustomizedTraceTopic( + rocketBinderConfigurationProperties.getCustomizedTraceTopic()); + } + else { + result.setCustomizedTraceTopic( + rocketMQProperties.getProducer().getCustomizedTraceTopic()); + } + if (rocketMQProperties.getProducer() != null + && rocketMQProperties.getProducer().isEnableMsgTrace()) { + result.setEnableMsgTrace(Boolean.TRUE); + } + else { + result.setEnableMsgTrace( + rocketBinderConfigurationProperties.isEnableMsgTrace()); + } + return result; + } + +} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQMessageChannelBinder.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQMessageChannelBinder.java index b85df2a0..38776db5 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQMessageChannelBinder.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQMessageChannelBinder.java @@ -16,17 +16,22 @@ package org.springframework.cloud.stream.binder.rocketmq; -import org.apache.commons.lang3.StringUtils; -import org.apache.rocketmq.client.producer.LocalTransactionExecuter; -import org.apache.rocketmq.client.producer.TransactionCheckListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.HashMap; +import java.util.Map; + +import org.apache.rocketmq.acl.common.AclClientRPCHook; +import org.apache.rocketmq.acl.common.SessionCredentials; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.remoting.RPCHook; +import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.apache.rocketmq.spring.support.RocketMQUtil; import org.springframework.cloud.stream.binder.AbstractMessageChannelBinder; import org.springframework.cloud.stream.binder.BinderSpecificPropertiesProvider; import org.springframework.cloud.stream.binder.ExtendedConsumerProperties; import org.springframework.cloud.stream.binder.ExtendedProducerProperties; import org.springframework.cloud.stream.binder.ExtendedPropertiesBinder; -import org.springframework.cloud.stream.binder.rocketmq.consuming.ConsumersManager; +import org.springframework.cloud.stream.binder.rocketmq.consuming.RocketMQListenerBindingContainer; import org.springframework.cloud.stream.binder.rocketmq.integration.RocketMQInboundChannelAdapter; import org.springframework.cloud.stream.binder.rocketmq.integration.RocketMQMessageHandler; import org.springframework.cloud.stream.binder.rocketmq.metrics.InstrumentationManager; @@ -40,10 +45,11 @@ import org.springframework.cloud.stream.provisioning.ProducerDestination; import org.springframework.integration.core.MessageProducer; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; -import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +import com.fasterxml.jackson.databind.ObjectMapper; /** - * @author Timur Valiev * @author Jim */ public class RocketMQMessageChannelBinder extends @@ -51,22 +57,23 @@ public class RocketMQMessageChannelBinder extends implements ExtendedPropertiesBinder { - private static final Logger logger = LoggerFactory - .getLogger(RocketMQMessageChannelBinder.class); - - private final RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties; - private final InstrumentationManager instrumentationManager; - private final ConsumersManager consumersManager; - private RocketMQExtendedBindingProperties extendedBindingProperties = new RocketMQExtendedBindingProperties(); - public RocketMQMessageChannelBinder(ConsumersManager consumersManager, - RocketMQTopicProvisioner provisioningProvider, + private final RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties; + private final RocketMQProperties rocketMQProperties; + private final InstrumentationManager instrumentationManager; + + private Map topicInUse = new HashMap<>(); + + public RocketMQMessageChannelBinder(RocketMQTopicProvisioner provisioningProvider, + RocketMQExtendedBindingProperties extendedBindingProperties, RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties, + RocketMQProperties rocketMQProperties, InstrumentationManager instrumentationManager) { super(null, provisioningProvider); - this.consumersManager = consumersManager; + this.extendedBindingProperties = extendedBindingProperties; this.rocketBinderConfigurationProperties = rocketBinderConfigurationProperties; + this.rocketMQProperties = rocketMQProperties; this.instrumentationManager = instrumentationManager; } @@ -75,22 +82,75 @@ public class RocketMQMessageChannelBinder extends ExtendedProducerProperties producerProperties, MessageChannel errorChannel) throws Exception { if (producerProperties.getExtension().getEnabled()) { - RocketMQMessageHandler messageHandler = new RocketMQMessageHandler( - destination.getName(), producerProperties, - rocketBinderConfigurationProperties, instrumentationManager); + + // if producerGroup is empty, using destination + String extendedProducerGroup = producerProperties.getExtension().getGroup(); + String producerGroup = StringUtils.isEmpty(extendedProducerGroup) + ? destination.getName() + : extendedProducerGroup; + + RocketMQBinderConfigurationProperties mergedProperties = RocketMQBinderUtils + .mergeProperties(rocketBinderConfigurationProperties, + rocketMQProperties); + + RocketMQTemplate rocketMQTemplate; if (producerProperties.getExtension().getTransactional()) { - // transaction message check LocalTransactionExecuter - messageHandler.setLocalTransactionExecuter( - getClassConfiguration(destination.getName(), - producerProperties.getExtension().getExecuter(), - LocalTransactionExecuter.class)); - // transaction message check TransactionCheckListener - messageHandler.setTransactionCheckListener( - getClassConfiguration(destination.getName(), - producerProperties.getExtension() - .getTransactionCheckListener(), - TransactionCheckListener.class)); + Map rocketMQTemplates = getBeanFactory() + .getBeansOfType(RocketMQTemplate.class); + if (rocketMQTemplates.size() == 0) { + throw new IllegalStateException( + "there is no RocketMQTemplate in Spring BeanFactory"); + } + else if (rocketMQTemplates.size() > 1) { + throw new IllegalStateException( + "there is more than 1 RocketMQTemplates in Spring BeanFactory"); + } + rocketMQTemplate = rocketMQTemplates.values().iterator().next(); } + else { + rocketMQTemplate = new RocketMQTemplate(); + rocketMQTemplate.setObjectMapper(this.getApplicationContext() + .getBeansOfType(ObjectMapper.class).values().iterator().next()); + DefaultMQProducer producer; + String ak = mergedProperties.getAccessKey(); + String sk = mergedProperties.getSecretKey(); + if (!StringUtils.isEmpty(ak) && !StringUtils.isEmpty(sk)) { + RPCHook rpcHook = new AclClientRPCHook( + new SessionCredentials(ak, sk)); + producer = new DefaultMQProducer(producerGroup, rpcHook, + mergedProperties.isEnableMsgTrace(), + mergedProperties.getCustomizedTraceTopic()); + producer.setVipChannelEnabled(false); + producer.setInstanceName( + RocketMQUtil.getInstanceName(rpcHook, destination.getName())); + } + else { + producer = new DefaultMQProducer(producerGroup); + producer.setVipChannelEnabled( + producerProperties.getExtension().getVipChannelEnabled()); + } + producer.setNamesrvAddr(mergedProperties.getNameServer()); + producer.setSendMsgTimeout( + producerProperties.getExtension().getSendMessageTimeout()); + producer.setRetryTimesWhenSendFailed( + producerProperties.getExtension().getRetryTimesWhenSendFailed()); + producer.setRetryTimesWhenSendAsyncFailed(producerProperties + .getExtension().getRetryTimesWhenSendAsyncFailed()); + producer.setCompressMsgBodyOverHowmuch(producerProperties.getExtension() + .getCompressMessageBodyThreshold()); + producer.setRetryAnotherBrokerWhenNotStoreOK( + producerProperties.getExtension().isRetryNextServer()); + producer.setMaxMessageSize( + producerProperties.getExtension().getMaxMessageSize()); + rocketMQTemplate.setProducer(producer); + } + + RocketMQMessageHandler messageHandler = new RocketMQMessageHandler( + rocketMQTemplate, destination.getName(), producerGroup, + producerProperties.getExtension().getTransactional(), + instrumentationManager); + messageHandler.setBeanFactory(this.getApplicationContext().getBeanFactory()); + messageHandler.setSync(producerProperties.getExtension().getSync()); if (errorChannel != null) { messageHandler.setSendFailureChannel(errorChannel); @@ -113,9 +173,22 @@ public class RocketMQMessageChannelBinder extends "'group must be configured for channel " + destination.getName()); } + RocketMQListenerBindingContainer listenerContainer = new RocketMQListenerBindingContainer( + consumerProperties, rocketBinderConfigurationProperties, this); + listenerContainer.setConsumerGroup(group); + listenerContainer.setTopic(destination.getName()); + listenerContainer.setConsumeThreadMax(consumerProperties.getConcurrency()); + listenerContainer.setSuspendCurrentQueueTimeMillis( + consumerProperties.getExtension().getSuspendCurrentQueueTimeMillis()); + listenerContainer.setDelayLevelWhenNextConsume( + consumerProperties.getExtension().getDelayLevelWhenNextConsume()); + listenerContainer + .setNameServer(rocketBinderConfigurationProperties.getNameServer()); + RocketMQInboundChannelAdapter rocketInboundChannelAdapter = new RocketMQInboundChannelAdapter( - consumersManager, consumerProperties, destination.getName(), group, - instrumentationManager); + listenerContainer, consumerProperties, instrumentationManager); + + topicInUse.put(destination.getName(), group); ErrorInfrastructure errorInfrastructure = registerErrorInfrastructure(destination, group, consumerProperties); @@ -143,6 +216,10 @@ public class RocketMQMessageChannelBinder extends return extendedBindingProperties.getExtendedProducerProperties(channelName); } + public Map getTopicInUse() { + return topicInUse; + } + @Override public String getDefaultsPrefix() { return extendedBindingProperties.getDefaultsPrefix(); @@ -153,47 +230,6 @@ public class RocketMQMessageChannelBinder extends return extendedBindingProperties.getExtendedPropertiesEntryClass(); } - private T getClassConfiguration(String destName, String className, - Class interfaceClass) { - if (StringUtils.isEmpty(className)) { - throw new RuntimeException("Binding for channel " + destName - + " using transactional message, should set " - + interfaceClass.getSimpleName() + " configuration" - + interfaceClass.getSimpleName() + " should be set, like " - + "'spring.cloud.stream.rocketmq.bindings.output.producer.xxx=TheFullClassNameOfYour" - + interfaceClass.getSimpleName() + "'"); - } - else if (StringUtils.isNotEmpty(className)) { - Class fieldClass; - // check class exists - try { - fieldClass = ClassUtils.forName(className, - RocketMQMessageChannelBinder.class.getClassLoader()); - } - catch (ClassNotFoundException e) { - throw new RuntimeException("Binding for channel " + destName - + " using transactional message, but " + className - + " class is not found"); - } - // check interface incompatible - if (!interfaceClass.isAssignableFrom(fieldClass)) { - throw new RuntimeException("Binding for channel " + destName - + " using transactional message, but " + className - + " is incompatible with " + interfaceClass.getSimpleName() - + " interface"); - } - try { - return (T) fieldClass.newInstance(); - } - catch (Exception e) { - throw new RuntimeException("Binding for channel " + destName - + " using transactional message, but " + className - + " instance error", e); - } - } - return null; - } - public void setExtendedBindingProperties( RocketMQExtendedBindingProperties extendedBindingProperties) { this.extendedBindingProperties = extendedBindingProperties; diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQMessageHeaderAccessor.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQMessageHeaderAccessor.java deleted file mode 100644 index 7707c07e..00000000 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQMessageHeaderAccessor.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2018 the original author or authors. - * - * 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. - */ - -package org.springframework.cloud.stream.binder.rocketmq; - -import static org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants.ACKNOWLEDGEMENT_KEY; -import static org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants.ORIGINAL_ROCKET_MESSAGE; -import static org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants.ROCKET_FLAG; -import static org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants.ROCKET_SEND_RESULT; -import static org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants.ROCKET_TRANSACTIONAL_ARG; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.rocketmq.client.producer.SendResult; -import org.apache.rocketmq.common.message.MessageConst; -import org.apache.rocketmq.common.message.MessageExt; -import org.springframework.cloud.stream.binder.rocketmq.consuming.Acknowledgement; -import org.springframework.integration.support.MutableMessage; -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageHeaders; -import org.springframework.messaging.support.MessageHeaderAccessor; - -/** - * @author Timur Valiev - * @author Jim - */ -public class RocketMQMessageHeaderAccessor extends MessageHeaderAccessor { - - public RocketMQMessageHeaderAccessor() { - super(); - } - - public RocketMQMessageHeaderAccessor(Message message) { - super(message); - } - - public Acknowledgement getAcknowledgement(Message message) { - return message.getHeaders().get(ACKNOWLEDGEMENT_KEY, Acknowledgement.class); - } - - public RocketMQMessageHeaderAccessor withAcknowledgment( - Acknowledgement acknowledgment) { - setHeader(ACKNOWLEDGEMENT_KEY, acknowledgment); - return this; - } - - public String getTags() { - return (String) getMessageHeaders().getOrDefault(MessageConst.PROPERTY_TAGS, ""); - } - - public RocketMQMessageHeaderAccessor withTags(String tag) { - setHeader(MessageConst.PROPERTY_TAGS, tag); - return this; - } - - public String getKeys() { - return (String) getMessageHeaders().getOrDefault(MessageConst.PROPERTY_KEYS, ""); - } - - public RocketMQMessageHeaderAccessor withKeys(String keys) { - setHeader(MessageConst.PROPERTY_KEYS, keys); - return this; - } - - public MessageExt getRocketMessage() { - return getMessageHeaders().get(ORIGINAL_ROCKET_MESSAGE, MessageExt.class); - } - - public RocketMQMessageHeaderAccessor withRocketMessage(MessageExt message) { - setHeader(ORIGINAL_ROCKET_MESSAGE, message); - return this; - } - - public Integer getDelayTimeLevel() { - return (Integer) getMessageHeaders() - .getOrDefault(MessageConst.PROPERTY_DELAY_TIME_LEVEL, 0); - } - - public RocketMQMessageHeaderAccessor withDelayTimeLevel(Integer delayTimeLevel) { - setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, delayTimeLevel); - return this; - } - - public Integer getFlag() { - return (Integer) getMessageHeaders().getOrDefault(ROCKET_FLAG, 0); - } - - public RocketMQMessageHeaderAccessor withFlag(Integer delayTimeLevel) { - setHeader(ROCKET_FLAG, delayTimeLevel); - return this; - } - - public Object getTransactionalArg() { - return getMessageHeaders().get(ROCKET_TRANSACTIONAL_ARG); - } - - public Object withTransactionalArg(Object arg) { - setHeader(ROCKET_TRANSACTIONAL_ARG, arg); - return this; - } - - public SendResult getSendResult() { - return getMessageHeaders().get(ROCKET_SEND_RESULT, SendResult.class); - } - - public static void putSendResult(MutableMessage message, SendResult sendResult) { - message.getHeaders().put(ROCKET_SEND_RESULT, sendResult); - } - - public Map getUserProperties() { - Map result = new HashMap<>(); - for (Map.Entry entry : this.toMap().entrySet()) { - if (entry.getValue() instanceof String - && !MessageConst.STRING_HASH_SET.contains(entry.getKey()) - && !entry.getKey().equals(MessageHeaders.CONTENT_TYPE)) { - result.put(entry.getKey(), (String) entry.getValue()); - } - } - return result; - } -} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/actuator/RocketMQBinderEndpoint.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/actuator/RocketMQBinderEndpoint.java deleted file mode 100644 index 1a7dc3b1..00000000 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/actuator/RocketMQBinderEndpoint.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2018 the original author or authors. - * - * 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. - */ - -package org.springframework.cloud.stream.binder.rocketmq.actuator; - -import static org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants.ENDPOINT_ID; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; -import org.springframework.cloud.stream.binder.rocketmq.metrics.InstrumentationManager; - -/** - * @author Timur Valiev - * @author Jim - */ -@Endpoint(id = ENDPOINT_ID) -public class RocketMQBinderEndpoint { - - @Autowired(required = false) - private InstrumentationManager instrumentationManager; - - @ReadOperation - public Map invoke() { - Map result = new HashMap<>(); - if (instrumentationManager != null) { - result.put("metrics", - instrumentationManager.getMetricRegistry().getMetrics()); - result.put("runtime", instrumentationManager.getRuntime()); - } - else { - result.put("warning", - "please add metrics-core dependency, we use it for metrics"); - } - return result; - } - -} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/actuator/RocketMQBinderHealthIndicator.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/actuator/RocketMQBinderHealthIndicator.java index 12ed17eb..b7e31036 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/actuator/RocketMQBinderHealthIndicator.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/actuator/RocketMQBinderHealthIndicator.java @@ -28,33 +28,25 @@ import org.springframework.cloud.stream.binder.rocketmq.metrics.InstrumentationM */ public class RocketMQBinderHealthIndicator extends AbstractHealthIndicator { - @Autowired(required = false) + @Autowired private InstrumentationManager instrumentationManager; @Override protected void doHealthCheck(Health.Builder builder) throws Exception { - if (instrumentationManager != null) { - if (instrumentationManager.getHealthInstrumentations().stream() - .allMatch(Instrumentation::isUp)) { - builder.up(); - return; - } - if (instrumentationManager.getHealthInstrumentations().stream() - .allMatch(Instrumentation::isOutOfService)) { - builder.outOfService(); - return; - } - builder.down(); - instrumentationManager.getHealthInstrumentations().stream() - .filter(instrumentation -> !instrumentation.isStarted()) - .forEach(instrumentation1 -> builder - .withException(instrumentation1.getStartException())); + if (instrumentationManager.getHealthInstrumentations().stream() + .allMatch(Instrumentation::isUp)) { + builder.up(); + return; } - else { - builder.down(); - builder.withDetail("warning", - "please add metrics-core dependency, we use it for metrics"); + if (instrumentationManager.getHealthInstrumentations().stream() + .allMatch(Instrumentation::isOutOfService)) { + builder.outOfService(); + return; } - + builder.down(); + instrumentationManager.getHealthInstrumentations().stream() + .filter(instrumentation -> !instrumentation.isStarted()) + .forEach(instrumentation1 -> builder + .withException(instrumentation1.getStartException())); } } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQBinderAutoConfiguration.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQBinderAutoConfiguration.java index ede8b259..aa7ca366 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQBinderAutoConfiguration.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQBinderAutoConfiguration.java @@ -16,23 +16,26 @@ package org.springframework.cloud.stream.binder.rocketmq.config; -import org.apache.rocketmq.client.log.ClientLogger; +import org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration; +import org.apache.rocketmq.spring.autoconfigure.RocketMQProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.stream.binder.rocketmq.RocketMQMessageChannelBinder; -import org.springframework.cloud.stream.binder.rocketmq.consuming.ConsumersManager; import org.springframework.cloud.stream.binder.rocketmq.metrics.InstrumentationManager; import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQBinderConfigurationProperties; import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQExtendedBindingProperties; import org.springframework.cloud.stream.binder.rocketmq.provisioning.RocketMQTopicProvisioner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; /** * @author Timur Valiev * @author Jim */ @Configuration +@Import({ RocketMQAutoConfiguration.class, + RocketMQBinderHealthIndicatorAutoConfiguration.class }) @EnableConfigurationProperties({ RocketMQBinderConfigurationProperties.class, RocketMQExtendedBindingProperties.class }) public class RocketMQBinderAutoConfiguration { @@ -42,7 +45,7 @@ public class RocketMQBinderAutoConfiguration { private final RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties; @Autowired(required = false) - private InstrumentationManager instrumentationManager; + private RocketMQProperties rocketMQProperties = new RocketMQProperties(); @Autowired public RocketMQBinderAutoConfiguration( @@ -50,8 +53,6 @@ public class RocketMQBinderAutoConfiguration { RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties) { this.extendedBindingProperties = extendedBindingProperties; this.rocketBinderConfigurationProperties = rocketBinderConfigurationProperties; - System.setProperty(ClientLogger.CLIENT_LOG_LEVEL, - this.rocketBinderConfigurationProperties.getLogLevel()); } @Bean @@ -62,18 +63,18 @@ public class RocketMQBinderAutoConfiguration { @Bean public RocketMQMessageChannelBinder rocketMessageChannelBinder( RocketMQTopicProvisioner provisioningProvider, - ConsumersManager consumersManager) { + InstrumentationManager instrumentationManager) { RocketMQMessageChannelBinder binder = new RocketMQMessageChannelBinder( - consumersManager, provisioningProvider, - rocketBinderConfigurationProperties, instrumentationManager); + provisioningProvider, extendedBindingProperties, + rocketBinderConfigurationProperties, rocketMQProperties, + instrumentationManager); binder.setExtendedBindingProperties(extendedBindingProperties); return binder; } @Bean - public ConsumersManager consumersManager() { - return new ConsumersManager(instrumentationManager, - rocketBinderConfigurationProperties); + public InstrumentationManager instrumentationManager() { + return new InstrumentationManager(); } } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQBinderEndpointAutoConfiguration.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQBinderHealthIndicatorAutoConfiguration.java similarity index 64% rename from spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQBinderEndpointAutoConfiguration.java rename to spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQBinderHealthIndicatorAutoConfiguration.java index f1fb5f1b..bac4927e 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQBinderEndpointAutoConfiguration.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQBinderHealthIndicatorAutoConfiguration.java @@ -16,13 +16,9 @@ package org.springframework.cloud.stream.binder.rocketmq.config; -import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; -import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.cloud.stream.binder.rocketmq.actuator.RocketMQBinderEndpoint; import org.springframework.cloud.stream.binder.rocketmq.actuator.RocketMQBinderHealthIndicator; -import org.springframework.cloud.stream.binder.rocketmq.metrics.InstrumentationManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -30,24 +26,12 @@ import org.springframework.context.annotation.Configuration; * @author Jim */ @Configuration -@AutoConfigureAfter(EndpointAutoConfiguration.class) @ConditionalOnClass(Endpoint.class) -public class RocketMQBinderEndpointAutoConfiguration { - - @Bean - public RocketMQBinderEndpoint rocketBinderEndpoint() { - return new RocketMQBinderEndpoint(); - } +public class RocketMQBinderHealthIndicatorAutoConfiguration { @Bean public RocketMQBinderHealthIndicator rocketBinderHealthIndicator() { return new RocketMQBinderHealthIndicator(); } - @Bean - @ConditionalOnClass(name = "com.codahale.metrics.Counter") - public InstrumentationManager instrumentationManager() { - return new InstrumentationManager(); - } - } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQComponent4BinderAutoConfiguration.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQComponent4BinderAutoConfiguration.java new file mode 100644 index 00000000..e481ac87 --- /dev/null +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/config/RocketMQComponent4BinderAutoConfiguration.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 the original author or authors. + * + * 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. + */ + +package org.springframework.cloud.stream.binder.rocketmq.config; + +import org.apache.rocketmq.acl.common.AclClientRPCHook; +import org.apache.rocketmq.acl.common.SessionCredentials; +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration; +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.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; + +import com.fasterxml.jackson.databind.ObjectMapper; + +/** + * @author Jim + */ +@Configuration +@AutoConfigureAfter(RocketMQAutoConfiguration.class) +@ConditionalOnMissingBean(DefaultMQProducer.class) +public class RocketMQComponent4BinderAutoConfiguration { + + private final Environment environment; + + public RocketMQComponent4BinderAutoConfiguration(Environment environment) { + this.environment = environment; + } + + @Bean + @ConditionalOnMissingBean(DefaultMQProducer.class) + public DefaultMQProducer defaultMQProducer() { + DefaultMQProducer producer; + String configNameServer = environment.resolveRequiredPlaceholders( + "${spring.cloud.stream.rocketmq.binder.name-server:${rocketmq.producer.name-server:}}"); + String ak = environment.resolveRequiredPlaceholders( + "${spring.cloud.stream.rocketmq.binder.access-key:${rocketmq.producer.access-key:}}"); + String sk = environment.resolveRequiredPlaceholders( + "${spring.cloud.stream.rocketmq.binder.secret-key:${rocketmq.producer.secret-key:}}"); + if (!StringUtils.isEmpty(ak) && !StringUtils.isEmpty(sk)) { + producer = new DefaultMQProducer(RocketMQBinderConstants.DEFAULT_GROUP, + new AclClientRPCHook(new SessionCredentials(ak, sk))); + producer.setVipChannelEnabled(false); + } + else { + producer = new DefaultMQProducer(RocketMQBinderConstants.DEFAULT_GROUP); + } + producer.setNamesrvAddr(configNameServer); + return producer; + } + + @Bean(destroyMethod = "destroy") + @ConditionalOnMissingBean + public RocketMQTemplate rocketMQTemplate(DefaultMQProducer mqProducer, + ObjectMapper objectMapper) { + RocketMQTemplate rocketMQTemplate = new RocketMQTemplate(); + rocketMQTemplate.setProducer(mqProducer); + rocketMQTemplate.setObjectMapper(objectMapper); + return rocketMQTemplate; + } + + @Bean + @ConditionalOnBean(RocketMQTemplate.class) + @ConditionalOnMissingBean(TransactionHandlerRegistry.class) + public TransactionHandlerRegistry transactionHandlerRegistry( + RocketMQTemplate template) { + return new TransactionHandlerRegistry(template); + } + + @Bean(name = RocketMQConfigUtils.ROCKETMQ_TRANSACTION_ANNOTATION_PROCESSOR_BEAN_NAME) + @ConditionalOnBean(TransactionHandlerRegistry.class) + public static RocketMQTransactionAnnotationProcessor transactionAnnotationProcessor( + TransactionHandlerRegistry transactionHandlerRegistry) { + return new RocketMQTransactionAnnotationProcessor(transactionHandlerRegistry); + } + +} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/consuming/Acknowledgement.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/consuming/Acknowledgement.java deleted file mode 100644 index 4b5d9e3f..00000000 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/consuming/Acknowledgement.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2018 the original author or authors. - * - * 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. - */ - -package org.springframework.cloud.stream.binder.rocketmq.consuming; - -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; - -/** - * @author Timur Valiev - * @author Jim - */ -public class Acknowledgement { - - /** - * for {@link ConsumeConcurrentlyContext} using - */ - private ConsumeConcurrentlyStatus consumeConcurrentlyStatus = ConsumeConcurrentlyStatus.CONSUME_SUCCESS; - /** - * Message consume retry strategy
- * -1,no retry,put into DLQ directly
- * 0,broker control retry frequency
- * >0,client control retry frequency - */ - private Integer consumeConcurrentlyDelayLevel = 0; - - /** - * for {@link ConsumeOrderlyContext} using - */ - private ConsumeOrderlyStatus consumeOrderlyStatus = ConsumeOrderlyStatus.SUCCESS; - private Long consumeOrderlySuspendCurrentQueueTimeMill = -1L; - - public Acknowledgement setConsumeConcurrentlyStatus( - ConsumeConcurrentlyStatus consumeConcurrentlyStatus) { - this.consumeConcurrentlyStatus = consumeConcurrentlyStatus; - return this; - } - - public ConsumeConcurrentlyStatus getConsumeConcurrentlyStatus() { - return consumeConcurrentlyStatus; - } - - public ConsumeOrderlyStatus getConsumeOrderlyStatus() { - return consumeOrderlyStatus; - } - - public Acknowledgement setConsumeOrderlyStatus( - ConsumeOrderlyStatus consumeOrderlyStatus) { - this.consumeOrderlyStatus = consumeOrderlyStatus; - return this; - } - - public Integer getConsumeConcurrentlyDelayLevel() { - return consumeConcurrentlyDelayLevel; - } - - public void setConsumeConcurrentlyDelayLevel(Integer consumeConcurrentlyDelayLevel) { - this.consumeConcurrentlyDelayLevel = consumeConcurrentlyDelayLevel; - } - - public Long getConsumeOrderlySuspendCurrentQueueTimeMill() { - return consumeOrderlySuspendCurrentQueueTimeMill; - } - - public void setConsumeOrderlySuspendCurrentQueueTimeMill( - Long consumeOrderlySuspendCurrentQueueTimeMill) { - this.consumeOrderlySuspendCurrentQueueTimeMill = consumeOrderlySuspendCurrentQueueTimeMill; - } - - public static Acknowledgement buildOrderlyInstance() { - Acknowledgement acknowledgement = new Acknowledgement(); - acknowledgement.setConsumeOrderlyStatus(ConsumeOrderlyStatus.SUCCESS); - return acknowledgement; - } - - public static Acknowledgement buildConcurrentlyInstance() { - Acknowledgement acknowledgement = new Acknowledgement(); - acknowledgement - .setConsumeConcurrentlyStatus(ConsumeConcurrentlyStatus.CONSUME_SUCCESS); - return acknowledgement; - } - -} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/consuming/ConsumersManager.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/consuming/ConsumersManager.java deleted file mode 100644 index 12fde2ba..00000000 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/consuming/ConsumersManager.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2018 the original author or authors. - * - * 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. - */ - -package org.springframework.cloud.stream.binder.rocketmq.consuming; - -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.Set; - -import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; -import org.apache.rocketmq.client.exception.MQClientException; -import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.cloud.stream.binder.ExtendedConsumerProperties; -import org.springframework.cloud.stream.binder.rocketmq.metrics.ConsumerGroupInstrumentation; -import org.springframework.cloud.stream.binder.rocketmq.metrics.InstrumentationManager; -import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQBinderConfigurationProperties; -import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQConsumerProperties; - -/** - * @author Timur Valiev - * @author Jim - */ -public class ConsumersManager { - - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - private final Map consumerGroups = new HashMap<>(); - private final Map started = new HashMap<>(); - private final Map, ExtendedConsumerProperties> propertiesMap = new HashMap<>(); - private final RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties; - - private InstrumentationManager instrumentationManager; - - public ConsumersManager(InstrumentationManager instrumentationManager, - RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties) { - this.instrumentationManager = instrumentationManager; - this.rocketBinderConfigurationProperties = rocketBinderConfigurationProperties; - } - - public synchronized DefaultMQPushConsumer getOrCreateConsumer(String group, - String topic, - ExtendedConsumerProperties consumerProperties) { - propertiesMap.put(new AbstractMap.SimpleEntry<>(group, topic), - consumerProperties); - - Optional.ofNullable(instrumentationManager).ifPresent(manager -> { - ConsumerGroupInstrumentation instrumentation = manager - .getConsumerGroupInstrumentation(group); - instrumentationManager.addHealthInstrumentation(instrumentation); - }); - - if (consumerGroups.containsKey(group)) { - return consumerGroups.get(group); - } - - DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(group); - consumer.setNamesrvAddr(rocketBinderConfigurationProperties.getNamesrvAddr()); - consumerGroups.put(group, consumer); - started.put(group, false); - consumer.setConsumeThreadMax(consumerProperties.getConcurrency()); - consumer.setConsumeThreadMin(consumerProperties.getConcurrency()); - if (consumerProperties.getExtension().getBroadcasting()) { - consumer.setMessageModel(MessageModel.BROADCASTING); - } - logger.info("RocketMQ consuming for SCS group {} created", group); - return consumer; - } - - public synchronized void startConsumers() throws MQClientException { - for (String group : getConsumerGroups()) { - start(group); - } - } - - public synchronized void startConsumer(String group) throws MQClientException { - start(group); - } - - public synchronized void stopConsumer(String group) { - stop(group); - } - - private void stop(String group) { - if (consumerGroups.get(group) != null) { - consumerGroups.get(group).shutdown(); - started.put(group, false); - } - } - - private synchronized void start(String group) throws MQClientException { - if (started.get(group)) { - return; - } - - ConsumerGroupInstrumentation groupInstrumentation = null; - if (Optional.ofNullable(instrumentationManager).isPresent()) { - groupInstrumentation = instrumentationManager - .getConsumerGroupInstrumentation(group); - instrumentationManager.addHealthInstrumentation(groupInstrumentation); - } - - try { - consumerGroups.get(group).start(); - started.put(group, true); - Optional.ofNullable(groupInstrumentation) - .ifPresent(g -> g.markStartedSuccessfully()); - } - catch (MQClientException e) { - Optional.ofNullable(groupInstrumentation) - .ifPresent(g -> g.markStartFailed(e)); - logger.error("RocketMQ Consumer hasn't been started. Caused by " - + e.getErrorMessage(), e); - throw e; - } - } - - public synchronized Set getConsumerGroups() { - return consumerGroups.keySet(); - } -} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/consuming/RocketMQListenerBindingContainer.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/consuming/RocketMQListenerBindingContainer.java new file mode 100644 index 00000000..35f29ae8 --- /dev/null +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/consuming/RocketMQListenerBindingContainer.java @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2018 the original author or authors. + * + * 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. + */ + +package org.springframework.cloud.stream.binder.rocketmq.consuming; + +import java.util.List; +import java.util.Objects; + +import org.apache.rocketmq.acl.common.AclClientRPCHook; +import org.apache.rocketmq.acl.common.SessionCredentials; +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.SelectorType; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.apache.rocketmq.spring.core.RocketMQPushConsumerLifecycleListener; +import org.apache.rocketmq.spring.support.RocketMQListenerContainer; +import org.apache.rocketmq.spring.support.RocketMQUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.cloud.stream.binder.ExtendedConsumerProperties; +import org.springframework.cloud.stream.binder.rocketmq.RocketMQMessageChannelBinder; +import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQBinderConfigurationProperties; +import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQConsumerProperties; +import org.springframework.context.SmartLifecycle; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * @author Jim + */ +public class RocketMQListenerBindingContainer + implements InitializingBean, RocketMQListenerContainer, SmartLifecycle { + + private final static Logger log = LoggerFactory + .getLogger(RocketMQListenerBindingContainer.class); + + private long suspendCurrentQueueTimeMillis = 1000; + + /** + * Message consume retry strategy
+ * -1,no retry,put into DLQ directly
+ * 0,broker control retry frequency
+ * >0,client control retry frequency. + */ + private int delayLevelWhenNextConsume = 0; + + private String nameServer; + + private String consumerGroup; + + private String topic; + + private int consumeThreadMax = 64; + + private String charset = "UTF-8"; + + private RocketMQListener rocketMQListener; + + private DefaultMQPushConsumer consumer; + + private boolean running; + + private final ExtendedConsumerProperties rocketMQConsumerProperties; + + private final RocketMQMessageChannelBinder rocketMQMessageChannelBinder; + private final RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties; + + // The following properties came from RocketMQConsumerProperties. + private ConsumeMode consumeMode; + private SelectorType selectorType; + private String selectorExpression; + private MessageModel messageModel; + + public RocketMQListenerBindingContainer( + ExtendedConsumerProperties rocketMQConsumerProperties, + RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties, + RocketMQMessageChannelBinder rocketMQMessageChannelBinder) { + this.rocketMQConsumerProperties = rocketMQConsumerProperties; + this.rocketBinderConfigurationProperties = rocketBinderConfigurationProperties; + this.rocketMQMessageChannelBinder = rocketMQMessageChannelBinder; + this.consumeMode = rocketMQConsumerProperties.getExtension().getOrderly() + ? ConsumeMode.ORDERLY + : ConsumeMode.CONCURRENTLY; + if (StringUtils.isEmpty(rocketMQConsumerProperties.getExtension().getSql())) { + this.selectorType = SelectorType.TAG; + this.selectorExpression = rocketMQConsumerProperties.getExtension().getTags(); + } + else { + this.selectorType = SelectorType.SQL92; + this.selectorExpression = rocketMQConsumerProperties.getExtension().getSql(); + } + this.messageModel = rocketMQConsumerProperties.getExtension().getBroadcasting() + ? MessageModel.BROADCASTING + : MessageModel.CLUSTERING; + } + + @Override + public void setupMessageListener(RocketMQListener rocketMQListener) { + this.rocketMQListener = rocketMQListener; + } + + @Override + public void destroy() throws Exception { + this.setRunning(false); + if (Objects.nonNull(consumer)) { + consumer.shutdown(); + } + log.info("container destroyed, {}", this.toString()); + } + + @Override + public void afterPropertiesSet() throws Exception { + initRocketMQPushConsumer(); + } + + @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() { + return Integer.MAX_VALUE; + } + + 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"); + + String ak = rocketBinderConfigurationProperties.getAccessKey(); + String sk = rocketBinderConfigurationProperties.getSecretKey(); + if (!StringUtils.isEmpty(ak) && !StringUtils.isEmpty(sk)) { + RPCHook rpcHook = new AclClientRPCHook(new SessionCredentials(ak, sk)); + consumer = new DefaultMQPushConsumer(consumerGroup, rpcHook, + new AllocateMessageQueueAveragely(), + rocketBinderConfigurationProperties.isEnableMsgTrace(), + rocketBinderConfigurationProperties.getCustomizedTraceTopic()); + consumer.setInstanceName(RocketMQUtil.getInstanceName(rpcHook, topic)); + consumer.setVipChannelEnabled(false); + } + else { + consumer = new DefaultMQPushConsumer(consumerGroup, + rocketBinderConfigurationProperties.isEnableMsgTrace(), + rocketBinderConfigurationProperties.getCustomizedTraceTopic()); + } + + consumer.setNamesrvAddr(nameServer); + consumer.setConsumeThreadMax(rocketMQConsumerProperties.getConcurrency()); + consumer.setConsumeThreadMin(rocketMQConsumerProperties.getConcurrency()); + + 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); + } + + } + + @Override + public String toString() { + return "RocketMQListenerBindingContainer{" + "consumerGroup='" + consumerGroup + + '\'' + ", nameServer='" + nameServer + '\'' + ", topic='" + topic + '\'' + + ", consumeMode=" + consumeMode + ", selectorType=" + selectorType + + ", selectorExpression='" + selectorExpression + '\'' + ", messageModel=" + + messageModel + '}'; + } + + 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 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 void setConsumeThreadMax(int consumeThreadMax) { + this.consumeThreadMax = consumeThreadMax; + } + + public String getCharset() { + return charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public RocketMQListener getRocketMQListener() { + return rocketMQListener; + } + + public void setRocketMQListener(RocketMQListener rocketMQListener) { + this.rocketMQListener = rocketMQListener; + } + + public DefaultMQPushConsumer getConsumer() { + return consumer; + } + + public void setConsumer(DefaultMQPushConsumer consumer) { + this.consumer = consumer; + } + + public ExtendedConsumerProperties getRocketMQConsumerProperties() { + return rocketMQConsumerProperties; + } + + public ConsumeMode getConsumeMode() { + return consumeMode; + } + + public SelectorType getSelectorType() { + return selectorType; + } + + public String getSelectorExpression() { + return selectorExpression; + } + + public MessageModel getMessageModel() { + return messageModel; + } + + public class DefaultMessageListenerConcurrently + implements MessageListenerConcurrently { + + @SuppressWarnings("unchecked") + @Override + public ConsumeConcurrentlyStatus consumeMessage(List msgs, + ConsumeConcurrentlyContext context) { + for (MessageExt messageExt : msgs) { + log.debug("received msg: {}", messageExt); + try { + long now = System.currentTimeMillis(); + rocketMQListener + .onMessage(RocketMQUtil.convertToSpringMessage(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 msgs, + ConsumeOrderlyContext context) { + for (MessageExt messageExt : msgs) { + log.debug("received msg: {}", messageExt); + try { + long now = System.currentTimeMillis(); + rocketMQListener + .onMessage(RocketMQUtil.convertToSpringMessage(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; + } + } + +} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/exception/RocketMQSendFailureException.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/exception/RocketMQSendFailureException.java deleted file mode 100644 index d8d7fc5d..00000000 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/exception/RocketMQSendFailureException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2018 the original author or authors. - * - * 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. - */ - -package org.springframework.cloud.stream.binder.rocketmq.exception; - -import org.springframework.messaging.Message; -import org.springframework.messaging.MessagingException; - -/** - * An exception that is the payload of an {@code ErrorMessage} when occurs send failure. - * - * @author Jim - * @since 0.2.2 - */ -public class RocketMQSendFailureException extends MessagingException { - - private final org.apache.rocketmq.common.message.Message rocketmqMsg; - - public RocketMQSendFailureException(Message message, - org.apache.rocketmq.common.message.Message rocketmqMsg, Throwable cause) { - super(message, cause); - this.rocketmqMsg = rocketmqMsg; - } - - public org.apache.rocketmq.common.message.Message getRocketmqMsg() { - return rocketmqMsg; - } - - @Override - public String toString() { - return super.toString() + " [rocketmqMsg=" + this.rocketmqMsg + "]"; - } - -} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/integration/RocketMQInboundChannelAdapter.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/integration/RocketMQInboundChannelAdapter.java index dd4c67b1..33b3fef1 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/integration/RocketMQInboundChannelAdapter.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/integration/RocketMQInboundChannelAdapter.java @@ -16,82 +16,49 @@ package org.springframework.cloud.stream.binder.rocketmq.integration; -import org.apache.commons.lang3.ClassUtils; -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.MessageListener; -import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; -import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly; -import org.apache.rocketmq.client.exception.MQClientException; -import org.apache.rocketmq.common.message.MessageExt; +import org.apache.rocketmq.spring.core.RocketMQListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.binder.ExtendedConsumerProperties; -import org.springframework.cloud.stream.binder.rocketmq.RocketMQMessageHeaderAccessor; -import org.springframework.cloud.stream.binder.rocketmq.consuming.Acknowledgement; -import org.springframework.cloud.stream.binder.rocketmq.consuming.ConsumersManager; -import org.springframework.cloud.stream.binder.rocketmq.metrics.ConsumerInstrumentation; +import org.springframework.cloud.stream.binder.rocketmq.consuming.RocketMQListenerBindingContainer; +import org.springframework.cloud.stream.binder.rocketmq.metrics.Instrumentation; import org.springframework.cloud.stream.binder.rocketmq.metrics.InstrumentationManager; import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQConsumerProperties; import org.springframework.integration.endpoint.MessageProducerSupport; +import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; +import org.springframework.messaging.MessagingException; import org.springframework.retry.RecoveryCallback; import org.springframework.retry.RetryCallback; import org.springframework.retry.RetryContext; import org.springframework.retry.RetryListener; import org.springframework.retry.support.RetryTemplate; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; /** * @author Jim */ public class RocketMQInboundChannelAdapter extends MessageProducerSupport { - private static final Logger logger = LoggerFactory + private static final Logger log = LoggerFactory .getLogger(RocketMQInboundChannelAdapter.class); - private ConsumerInstrumentation consumerInstrumentation; - - private InstrumentationManager instrumentationManager; - private RetryTemplate retryTemplate; private RecoveryCallback recoveryCallback; - private DefaultMQPushConsumer consumer; - - private CloudStreamMessageListener listener; + private RocketMQListenerBindingContainer rocketMQListenerContainer; private final ExtendedConsumerProperties consumerProperties; - private final String destination; + private final InstrumentationManager instrumentationManager; - private final String group; - - private final ConsumersManager consumersManager; - - public RocketMQInboundChannelAdapter(ConsumersManager consumersManager, + public RocketMQInboundChannelAdapter( + RocketMQListenerBindingContainer rocketMQListenerContainer, ExtendedConsumerProperties consumerProperties, - String destination, String group, InstrumentationManager instrumentationManager) { - this.consumersManager = consumersManager; + this.rocketMQListenerContainer = rocketMQListenerContainer; this.consumerProperties = consumerProperties; - this.destination = destination; - this.group = group; this.instrumentationManager = instrumentationManager; } @@ -108,16 +75,27 @@ public class RocketMQInboundChannelAdapter extends MessageProducerSupport { + "provided; use an 'ErrorMessageSendingRecoverer' in the 'recoveryCallback' property to " + "send an error message when retries are exhausted"); } - this.consumer = consumersManager.getOrCreateConsumer(group, destination, - consumerProperties); - Boolean isOrderly = consumerProperties.getExtension().getOrderly(); - this.listener = isOrderly ? new CloudStreamMessageListenerOrderly() - : new CloudStreamMessageListenerConcurrently(); + BindingRocketMQListener listener = new BindingRocketMQListener(); + rocketMQListenerContainer.setRocketMQListener(listener); if (retryTemplate != null) { - this.retryTemplate.registerListener(this.listener); + this.retryTemplate.registerListener(listener); } + + try { + rocketMQListenerContainer.afterPropertiesSet(); + + } + catch (Exception e) { + log.error("rocketMQListenerContainer init error: " + e.getMessage(), e); + throw new IllegalArgumentException( + "rocketMQListenerContainer init error: " + e.getMessage(), e); + } + + instrumentationManager.addHealthInstrumentation( + new Instrumentation(rocketMQListenerContainer.getTopic() + + rocketMQListenerContainer.getConsumerGroup())); } @Override @@ -126,53 +104,28 @@ public class RocketMQInboundChannelAdapter extends MessageProducerSupport { || !consumerProperties.getExtension().getEnabled()) { return; } - - String tags = consumerProperties.getExtension().getTags(); - - Set tagsSet = tags == null ? new HashSet<>() - : Arrays.stream(tags.split("\\|\\|")).map(String::trim) - .collect(Collectors.toSet()); - - Optional.ofNullable(instrumentationManager).ifPresent(manager -> { - consumerInstrumentation = manager.getConsumerInstrumentation(destination); - manager.addHealthInstrumentation(consumerInstrumentation); - }); - try { - if (!StringUtils.isEmpty(consumerProperties.getExtension().getSql())) { - this.consumer.subscribe(destination, MessageSelector - .bySql(consumerProperties.getExtension().getSql())); - } - else { - this.consumer.subscribe(destination, String.join(" || ", tagsSet)); - } - Optional.ofNullable(consumerInstrumentation) - .ifPresent(c -> c.markStartedSuccessfully()); + rocketMQListenerContainer.start(); + instrumentationManager + .getHealthInstrumentation(rocketMQListenerContainer.getTopic() + + rocketMQListenerContainer.getConsumerGroup()) + .markStartedSuccessfully(); } - catch (MQClientException e) { - Optional.ofNullable(consumerInstrumentation) - .ifPresent(c -> c.markStartFailed(e)); - logger.error("RocketMQ Consumer hasn't been subscribed. Caused by " - + e.getErrorMessage(), e); - throw new RuntimeException("RocketMQ Consumer hasn't been subscribed.", e); - } - - this.consumer.registerMessageListener(this.listener); - - try { - consumersManager.startConsumer(group); - } - catch (MQClientException e) { - logger.error( - "RocketMQ Consumer startup failed. Caused by " + e.getErrorMessage(), - e); - throw new RuntimeException("RocketMQ Consumer startup failed.", e); + catch (Exception e) { + instrumentationManager + .getHealthInstrumentation(rocketMQListenerContainer.getTopic() + + rocketMQListenerContainer.getConsumerGroup()) + .markStartFailed(e); + log.error("RocketMQTemplate startup failed, Caused by " + e.getMessage()); + throw new MessagingException(MessageBuilder.withPayload( + "RocketMQTemplate startup failed, Caused by " + e.getMessage()) + .build(), e); } } @Override protected void doStop() { - consumersManager.stopConsumer(group); + rocketMQListenerContainer.stop(); } public void setRetryTemplate(RetryTemplate retryTemplate) { @@ -183,84 +136,21 @@ public class RocketMQInboundChannelAdapter extends MessageProducerSupport { this.recoveryCallback = recoveryCallback; } - protected class CloudStreamMessageListener implements MessageListener, RetryListener { + protected class BindingRocketMQListener + implements RocketMQListener, RetryListener { - Acknowledgement consumeMessage(final List msgs) { + @Override + public void onMessage(Message message) { boolean enableRetry = RocketMQInboundChannelAdapter.this.retryTemplate != null; - try { - if (enableRetry) { - return RocketMQInboundChannelAdapter.this.retryTemplate.execute( - (RetryCallback) context -> doSendMsgs( - msgs, context), - new RecoveryCallback() { - @Override - public Acknowledgement recover(RetryContext context) - throws Exception { - RocketMQInboundChannelAdapter.this.recoveryCallback - .recover(context); - if (ClassUtils.isAssignable(this.getClass(), - MessageListenerConcurrently.class)) { - return Acknowledgement - .buildConcurrentlyInstance(); - } - else { - return Acknowledgement.buildOrderlyInstance(); - } - } - }); - } - else { - Acknowledgement result = doSendMsgs(msgs, null); - Optional.ofNullable( - RocketMQInboundChannelAdapter.this.instrumentationManager) - .ifPresent(manager -> { - manager.getConsumerInstrumentation( - RocketMQInboundChannelAdapter.this.destination) - .markConsumed(); - }); - return result; - } + if (enableRetry) { + RocketMQInboundChannelAdapter.this.retryTemplate.execute(context -> { + RocketMQInboundChannelAdapter.this.sendMessage(message); + return null; + }, (RecoveryCallback) RocketMQInboundChannelAdapter.this.recoveryCallback); } - catch (Exception e) { - logger.error( - "RocketMQ Message hasn't been processed successfully. Caused by ", - e); - Optional.ofNullable( - RocketMQInboundChannelAdapter.this.instrumentationManager) - .ifPresent(manager -> { - manager.getConsumerInstrumentation( - RocketMQInboundChannelAdapter.this.destination) - .markConsumedFailure(); - }); + else { + RocketMQInboundChannelAdapter.this.sendMessage(message); } - return null; - } - - private Acknowledgement doSendMsgs(final List msgs, - RetryContext context) { - List acknowledgements = new ArrayList<>(); - msgs.forEach(msg -> { - String retryInfo = context == null ? "" - : "retryCount-" + String.valueOf(context.getRetryCount()) + "|"; - logger.debug(retryInfo + "consuming msg:\n" + msg); - logger.debug(retryInfo + "message body:\n" + new String(msg.getBody())); - Acknowledgement acknowledgement = new Acknowledgement(); - Message toChannel = convertMessagingFromRocketMQMsg(msg, - acknowledgement); - acknowledgements.add(acknowledgement); - RocketMQInboundChannelAdapter.this.sendMessage(toChannel); - }); - return acknowledgements.get(0); - } - - private Message convertMessagingFromRocketMQMsg(MessageExt msg, - Acknowledgement acknowledgement) { - return MessageBuilder.withPayload(msg.getBody()) - .setHeaders(new RocketMQMessageHeaderAccessor() - .withAcknowledgment(acknowledgement).withTags(msg.getTags()) - .withKeys(msg.getKeys()).withFlag(msg.getFlag()) - .withRocketMessage(msg)) - .build(); } @Override @@ -272,69 +162,12 @@ public class RocketMQInboundChannelAdapter extends MessageProducerSupport { @Override public void close(RetryContext context, RetryCallback callback, Throwable throwable) { - if (throwable != null) { - Optional.ofNullable( - RocketMQInboundChannelAdapter.this.instrumentationManager) - .ifPresent(manager -> { - manager.getConsumerInstrumentation( - RocketMQInboundChannelAdapter.this.destination) - .markConsumedFailure(); - }); - } - else { - Optional.ofNullable( - RocketMQInboundChannelAdapter.this.instrumentationManager) - .ifPresent(manager -> { - manager.getConsumerInstrumentation( - RocketMQInboundChannelAdapter.this.destination) - .markConsumed(); - }); - } } @Override public void onError(RetryContext context, RetryCallback callback, Throwable throwable) { - } - } - protected class CloudStreamMessageListenerConcurrently - extends CloudStreamMessageListener implements MessageListenerConcurrently { - - @Override - public ConsumeConcurrentlyStatus consumeMessage(final List msgs, - ConsumeConcurrentlyContext context) { - Acknowledgement acknowledgement = consumeMessage(msgs); - if (acknowledgement != null) { - context.setDelayLevelWhenNextConsume( - acknowledgement.getConsumeConcurrentlyDelayLevel()); - return acknowledgement.getConsumeConcurrentlyStatus(); - } - else { - context.setDelayLevelWhenNextConsume(consumerProperties.getExtension() - .getError().getDelayLevelWhenNextConsume()); - return ConsumeConcurrentlyStatus.RECONSUME_LATER; - } - } - } - - protected class CloudStreamMessageListenerOrderly extends CloudStreamMessageListener - implements MessageListenerOrderly { - - @Override - public ConsumeOrderlyStatus consumeMessage(List msgs, - ConsumeOrderlyContext context) { - Acknowledgement acknowledgement = consumeMessage(msgs); - if (acknowledgement != null) { - context.setSuspendCurrentQueueTimeMillis( - (acknowledgement.getConsumeOrderlySuspendCurrentQueueTimeMill())); - return acknowledgement.getConsumeOrderlyStatus(); - } - else { - context.setSuspendCurrentQueueTimeMillis(consumerProperties.getExtension() - .getError().getSuspendCurrentQueueTimeMillis()); - return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT; - } } } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/integration/RocketMQMessageHandler.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/integration/RocketMQMessageHandler.java index f2dd937c..65dbb9e5 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/integration/RocketMQMessageHandler.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/integration/RocketMQMessageHandler.java @@ -16,122 +16,93 @@ package org.springframework.cloud.stream.binder.rocketmq.integration; -import org.apache.rocketmq.client.exception.MQBrokerException; +import java.util.Optional; + import org.apache.rocketmq.client.exception.MQClientException; -import org.apache.rocketmq.client.producer.DefaultMQProducer; -import org.apache.rocketmq.client.producer.LocalTransactionExecuter; +import org.apache.rocketmq.client.producer.SendCallback; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.client.producer.SendStatus; -import org.apache.rocketmq.client.producer.TransactionCheckListener; -import org.apache.rocketmq.client.producer.TransactionMQProducer; -import org.apache.rocketmq.common.message.Message; -import org.apache.rocketmq.remoting.exception.RemotingException; -import org.springframework.cloud.stream.binder.ExtendedProducerProperties; +import org.apache.rocketmq.common.message.MessageConst; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.apache.rocketmq.spring.support.RocketMQHeaders; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants; -import org.springframework.cloud.stream.binder.rocketmq.RocketMQMessageHeaderAccessor; -import org.springframework.cloud.stream.binder.rocketmq.exception.RocketMQSendFailureException; +import org.springframework.cloud.stream.binder.rocketmq.metrics.Instrumentation; import org.springframework.cloud.stream.binder.rocketmq.metrics.InstrumentationManager; -import org.springframework.cloud.stream.binder.rocketmq.metrics.ProducerInstrumentation; -import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQBinderConfigurationProperties; -import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQProducerProperties; import org.springframework.context.Lifecycle; import org.springframework.integration.handler.AbstractMessageHandler; import org.springframework.integration.support.DefaultErrorMessageStrategy; import org.springframework.integration.support.ErrorMessageStrategy; -import org.springframework.integration.support.MutableMessage; +import org.springframework.integration.support.MessageBuilder; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessagingException; import org.springframework.messaging.support.ErrorMessage; import org.springframework.util.Assert; - -import java.time.Instant; -import java.util.Map; -import java.util.Optional; +import org.springframework.util.StringUtils; /** * @author Jim */ public class RocketMQMessageHandler extends AbstractMessageHandler implements Lifecycle { + private final static Logger log = LoggerFactory + .getLogger(RocketMQMessageHandler.class); + private ErrorMessageStrategy errorMessageStrategy = new DefaultErrorMessageStrategy(); - private DefaultMQProducer producer; - - private ProducerInstrumentation producerInstrumentation; - - private InstrumentationManager instrumentationManager; - - private LocalTransactionExecuter localTransactionExecuter; - - private TransactionCheckListener transactionCheckListener; - private MessageChannel sendFailureChannel; - private final ExtendedProducerProperties producerProperties; + private final RocketMQTemplate rocketMQTemplate; + + private final Boolean transactional; private final String destination; - private final RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties; + private final String groupName; + + private final InstrumentationManager instrumentationManager; + + private boolean sync = false; private volatile boolean running = false; - public RocketMQMessageHandler(String destination, - ExtendedProducerProperties producerProperties, - RocketMQBinderConfigurationProperties rocketBinderConfigurationProperties, + public RocketMQMessageHandler(RocketMQTemplate rocketMQTemplate, String destination, + String groupName, Boolean transactional, InstrumentationManager instrumentationManager) { + this.rocketMQTemplate = rocketMQTemplate; this.destination = destination; - this.producerProperties = producerProperties; - this.rocketBinderConfigurationProperties = rocketBinderConfigurationProperties; + this.groupName = groupName; + this.transactional = transactional; this.instrumentationManager = instrumentationManager; } @Override public void start() { - if (producerProperties.getExtension().getTransactional()) { - producer = new TransactionMQProducer(destination); - if (transactionCheckListener != null) { - ((TransactionMQProducer) producer) - .setTransactionCheckListener(transactionCheckListener); + if (!transactional) { + instrumentationManager + .addHealthInstrumentation(new Instrumentation(destination)); + try { + rocketMQTemplate.afterPropertiesSet(); + instrumentationManager.getHealthInstrumentation(destination) + .markStartedSuccessfully(); + } + catch (Exception e) { + instrumentationManager.getHealthInstrumentation(destination) + .markStartFailed(e); + log.error("RocketMQTemplate startup failed, Caused by " + e.getMessage()); + throw new MessagingException(MessageBuilder.withPayload( + "RocketMQTemplate startup failed, Caused by " + e.getMessage()) + .build(), e); } - } - else { - producer = new DefaultMQProducer(destination); - } - - producer.setVipChannelEnabled( - producerProperties.getExtension().getVipChannelEnabled()); - - Optional.ofNullable(instrumentationManager).ifPresent(manager -> { - producerInstrumentation = manager.getProducerInstrumentation(destination); - manager.addHealthInstrumentation(producerInstrumentation); - }); - - producer.setNamesrvAddr(rocketBinderConfigurationProperties.getNamesrvAddr()); - - if (producerProperties.getExtension().getMaxMessageSize() > 0) { - producer.setMaxMessageSize( - producerProperties.getExtension().getMaxMessageSize()); - } - - try { - producer.start(); - Optional.ofNullable(producerInstrumentation) - .ifPresent(p -> p.markStartedSuccessfully()); - } - catch (MQClientException e) { - Optional.ofNullable(producerInstrumentation) - .ifPresent(p -> p.markStartFailed(e)); - logger.error( - "RocketMQ Message hasn't been sent. Caused by " + e.getMessage()); - throw new MessagingException(e.getMessage(), e); } running = true; } @Override public void stop() { - if (producer != null) { - producer.shutdown(); + if (!transactional) { + rocketMQTemplate.destroy(); } running = false; } @@ -144,100 +115,95 @@ public class RocketMQMessageHandler extends AbstractMessageHandler implements Li @Override protected void handleMessageInternal(org.springframework.messaging.Message message) throws Exception { - Message toSend = null; try { - if (message.getPayload() instanceof byte[]) { - toSend = new Message(destination, (byte[]) message.getPayload()); + final StringBuilder topicWithTags = new StringBuilder(destination); + String tags = Optional + .ofNullable(message.getHeaders().get(RocketMQHeaders.TAGS)).orElse("") + .toString(); + if (!StringUtils.isEmpty(tags)) { + topicWithTags.append(":").append(tags); } - else if (message.getPayload() instanceof String) { - toSend = new Message(destination, - ((String) message.getPayload()).getBytes()); + SendResult sendRes = null; + if (transactional) { + sendRes = rocketMQTemplate.sendMessageInTransaction(groupName, + topicWithTags.toString(), message, message.getHeaders() + .get(RocketMQBinderConstants.ROCKET_TRANSACTIONAL_ARG)); + log.debug("transactional send to topic " + topicWithTags + " " + sendRes); } else { - throw new UnsupportedOperationException("Payload class isn't supported: " - + message.getPayload().getClass()); - } - RocketMQMessageHeaderAccessor headerAccessor = new RocketMQMessageHeaderAccessor( - message); - headerAccessor.setLeaveMutable(true); - toSend.setDelayTimeLevel(headerAccessor.getDelayTimeLevel()); - toSend.setTags(headerAccessor.getTags()); - toSend.setKeys(headerAccessor.getKeys()); - toSend.setFlag(headerAccessor.getFlag()); - for (Map.Entry entry : headerAccessor.getUserProperties() - .entrySet()) { - toSend.putUserProperty(entry.getKey(), entry.getValue()); - } + int delayLevel = 0; + try { + Object delayLevelObj = message.getHeaders() + .getOrDefault(MessageConst.PROPERTY_DELAY_TIME_LEVEL, 0); + if (delayLevelObj instanceof Number) { + delayLevel = ((Number) delayLevelObj).intValue(); + } + else if (delayLevelObj instanceof String) { + delayLevel = Integer.parseInt((String) delayLevelObj); + } + } + catch (Exception e) { + // ignore + } + if (sync) { + sendRes = rocketMQTemplate.syncSend(topicWithTags.toString(), message, + rocketMQTemplate.getProducer().getSendMsgTimeout(), + delayLevel); + log.debug("sync send to topic " + topicWithTags + " " + sendRes); + } + else { + rocketMQTemplate.asyncSend(topicWithTags.toString(), message, + new SendCallback() { + @Override + public void onSuccess(SendResult sendResult) { + log.debug("async send to topic " + topicWithTags + " " + + sendResult); + } - SendResult sendRes; - if (producerProperties.getExtension().getTransactional()) { - sendRes = producer.sendMessageInTransaction(toSend, - localTransactionExecuter, headerAccessor.getTransactionalArg()); + @Override + public void onException(Throwable e) { + log.error( + "RocketMQ Message hasn't been sent. Caused by " + + e.getMessage()); + if (getSendFailureChannel() != null) { + getSendFailureChannel().send( + RocketMQMessageHandler.this.errorMessageStrategy + .buildErrorMessage( + new MessagingException( + message, e), + null)); + } + } + }); + } } - else { - sendRes = producer.send(toSend); - } - - if (!sendRes.getSendStatus().equals(SendStatus.SEND_OK)) { + if (sendRes != null && !sendRes.getSendStatus().equals(SendStatus.SEND_OK)) { if (getSendFailureChannel() != null) { this.getSendFailureChannel().send(message); } else { - throw new RocketMQSendFailureException(message, toSend, + throw new MessagingException(message, new MQClientException("message hasn't been sent", null)); } } - if (message instanceof MutableMessage) { - RocketMQMessageHeaderAccessor.putSendResult((MutableMessage) message, - sendRes); - } - Optional.ofNullable(instrumentationManager).ifPresent(manager -> { - manager.getRuntime().put(RocketMQBinderConstants.LASTSEND_TIMESTAMP, - Instant.now().toEpochMilli()); - }); - Optional.ofNullable(producerInstrumentation).ifPresent(p -> p.markSent()); } - catch (MQClientException | RemotingException | MQBrokerException - | InterruptedException | UnsupportedOperationException e) { - Optional.ofNullable(producerInstrumentation) - .ifPresent(p -> p.markSentFailure()); - logger.error( - "RocketMQ Message hasn't been sent. Caused by " + e.getMessage()); + catch (Exception e) { + log.error("RocketMQ Message hasn't been sent. Caused by " + e.getMessage()); if (getSendFailureChannel() != null) { - getSendFailureChannel().send(this.errorMessageStrategy.buildErrorMessage( - new RocketMQSendFailureException(message, toSend, e), null)); + getSendFailureChannel().send(this.errorMessageStrategy + .buildErrorMessage(new MessagingException(message, e), null)); } else { - throw new RocketMQSendFailureException(message, toSend, e); + throw new MessagingException(message, e); } } } - /** - * Using in RocketMQ Transactional Mode. Set RocketMQ localTransactionExecuter in - * {@link DefaultMQProducer#sendMessageInTransaction}. - * @param localTransactionExecuter the executer running when produce msg. - */ - public void setLocalTransactionExecuter( - LocalTransactionExecuter localTransactionExecuter) { - this.localTransactionExecuter = localTransactionExecuter; - } - - /** - * Using in RocketMQ Transactional Mode. Set RocketMQ transactionCheckListener in - * {@link TransactionMQProducer#setTransactionCheckListener}. - * @param transactionCheckListener the listener set in {@link TransactionMQProducer}. - */ - public void setTransactionCheckListener( - TransactionCheckListener transactionCheckListener) { - this.transactionCheckListener = transactionCheckListener; - } - /** * Set the failure channel. After a send failure, an {@link ErrorMessage} will be sent - * to this channel with a payload of a {@link RocketMQSendFailureException} with the - * failed message and cause. + * to this channel with a payload of a {@link MessagingException} with the failed + * message and cause. * @param sendFailureChannel the failure channel. * @since 0.2.2 */ @@ -259,4 +225,8 @@ public class RocketMQMessageHandler extends AbstractMessageHandler implements Li public MessageChannel getSendFailureChannel() { return sendFailureChannel; } + + public void setSync(boolean sync) { + this.sync = sync; + } } \ No newline at end of file diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/ConsumerGroupInstrumentation.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/ConsumerGroupInstrumentation.java deleted file mode 100644 index 6e4386d0..00000000 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/ConsumerGroupInstrumentation.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2018 the original author or authors. - * - * 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. - */ - -package org.springframework.cloud.stream.binder.rocketmq.metrics; - -import com.codahale.metrics.MetricRegistry; - -/** - * @author Timur Valiev - * @author Jim - */ -public class ConsumerGroupInstrumentation extends Instrumentation { - private MetricRegistry metricRegistry; - - public ConsumerGroupInstrumentation(MetricRegistry metricRegistry, String name) { - super(name); - this.metricRegistry = metricRegistry; - } - -} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/ConsumerInstrumentation.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/ConsumerInstrumentation.java deleted file mode 100644 index 787e4b83..00000000 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/ConsumerInstrumentation.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2018 the original author or authors. - * - * 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. - */ - -package org.springframework.cloud.stream.binder.rocketmq.metrics; - -import static com.codahale.metrics.MetricRegistry.name; - -import org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants.Metrics.Consumer; - -import com.codahale.metrics.Counter; -import com.codahale.metrics.Meter; -import com.codahale.metrics.MetricRegistry; - -/** - * @author juven.xuxb - * @author Jim - */ -public class ConsumerInstrumentation extends Instrumentation { - - private final Counter totalConsumed; - private final Counter totalConsumedFailures; - private final Meter consumedPerSecond; - private final Meter consumedFailuresPerSecond; - - public ConsumerInstrumentation(MetricRegistry registry, String baseMetricName) { - super(baseMetricName); - - this.totalConsumed = registry - .counter(name(baseMetricName, Consumer.TOTAL_CONSUMED)); - this.consumedPerSecond = registry - .meter(name(baseMetricName, Consumer.CONSUMED_PER_SECOND)); - this.totalConsumedFailures = registry - .counter(name(baseMetricName, Consumer.TOTAL_CONSUMED_FAILURES)); - this.consumedFailuresPerSecond = registry - .meter(name(baseMetricName, Consumer.CONSUMED_FAILURES_PER_SECOND)); - } - - public void markConsumed() { - totalConsumed.inc(); - consumedPerSecond.mark(); - } - - public void markConsumedFailure() { - totalConsumedFailures.inc(); - consumedFailuresPerSecond.mark(); - } -} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/Instrumentation.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/Instrumentation.java index d9997ce3..885a183f 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/Instrumentation.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/Instrumentation.java @@ -27,7 +27,7 @@ public class Instrumentation { protected final AtomicBoolean started = new AtomicBoolean(false); protected Exception startException = null; - Instrumentation(String name) { + public Instrumentation(String name) { this.name = name; } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/InstrumentationManager.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/InstrumentationManager.java index c1c1cead..e07628bb 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/InstrumentationManager.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/InstrumentationManager.java @@ -22,47 +22,16 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants.Metrics.Consumer; -import org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants.Metrics.Producer; - -import com.codahale.metrics.MetricRegistry; - /** * @author Timur Valiev * @author Jim */ public class InstrumentationManager { - private final MetricRegistry metricRegistry = new MetricRegistry(); private final Map runtime = new ConcurrentHashMap<>(); - private final Map producerInstrumentations = new HashMap<>(); - private final Map consumeInstrumentations = new HashMap<>(); - private final Map consumerGroupsInstrumentations = new HashMap<>(); - private final Map healthInstrumentations = new HashMap<>(); - public ProducerInstrumentation getProducerInstrumentation(String destination) { - String key = Producer.PREFIX + destination; - producerInstrumentations.putIfAbsent(key, - new ProducerInstrumentation(metricRegistry, key)); - return producerInstrumentations.get(key); - } - - public ConsumerInstrumentation getConsumerInstrumentation(String destination) { - String key = Consumer.PREFIX + destination; - consumeInstrumentations.putIfAbsent(key, - new ConsumerInstrumentation(metricRegistry, key)); - return consumeInstrumentations.get(key); - } - - public ConsumerGroupInstrumentation getConsumerGroupInstrumentation(String group) { - String key = Consumer.GROUP_PREFIX + group; - consumerGroupsInstrumentations.putIfAbsent(key, - new ConsumerGroupInstrumentation(metricRegistry, key)); - return consumerGroupsInstrumentations.get(key); - } - public Set getHealthInstrumentations() { return healthInstrumentations.entrySet().stream().map(Map.Entry::getValue) .collect(Collectors.toSet()); @@ -72,11 +41,12 @@ public class InstrumentationManager { healthInstrumentations.put(instrumentation.getName(), instrumentation); } + public Instrumentation getHealthInstrumentation(String key) { + return healthInstrumentations.get(key); + } + public Map getRuntime() { return runtime; } - public MetricRegistry getMetricRegistry() { - return metricRegistry; - } } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/ProducerInstrumentation.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/ProducerInstrumentation.java deleted file mode 100644 index 1ede7802..00000000 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/metrics/ProducerInstrumentation.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2018 the original author or authors. - * - * 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. - */ - -package org.springframework.cloud.stream.binder.rocketmq.metrics; - -import static com.codahale.metrics.MetricRegistry.name; - -import org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants.Metrics.Producer; - -import com.codahale.metrics.Counter; -import com.codahale.metrics.Meter; -import com.codahale.metrics.MetricRegistry; - -/** - * @author juven.xuxb - * @author Jim - */ -public class ProducerInstrumentation extends Instrumentation { - - private final Counter totalSent; - private final Counter totalSentFailures; - private final Meter sentPerSecond; - private final Meter sentFailuresPerSecond; - - public ProducerInstrumentation(MetricRegistry registry, String baseMetricName) { - super(baseMetricName); - - this.totalSent = registry.counter(name(baseMetricName, Producer.TOTAL_SENT)); - this.totalSentFailures = registry - .counter(name(baseMetricName, Producer.TOTAL_SENT_FAILURES)); - this.sentPerSecond = registry - .meter(name(baseMetricName, Producer.SENT_PER_SECOND)); - this.sentFailuresPerSecond = registry - .meter(name(baseMetricName, Producer.SENT_FAILURES_PER_SECOND)); - } - - public void markSent() { - totalSent.inc(); - sentPerSecond.mark(); - } - - public void markSentFailure() { - totalSentFailures.inc(); - sentFailuresPerSecond.mark(); - } -} diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQBinderConfigurationProperties.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQBinderConfigurationProperties.java index 5c12a3b4..af292e74 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQBinderConfigurationProperties.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQBinderConfigurationProperties.java @@ -16,7 +16,9 @@ package org.springframework.cloud.stream.binder.rocketmq.properties; +import org.apache.rocketmq.common.MixAll; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.stream.binder.rocketmq.RocketMQBinderConstants; /** * @author Timur Valiev @@ -25,24 +27,69 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "spring.cloud.stream.rocketmq.binder") public class RocketMQBinderConfigurationProperties { - private String namesrvAddr = "127.0.0.1:9876"; + /** + * The name server for rocketMQ, formats: `host:port;host:port`. + */ + private String nameServer = RocketMQBinderConstants.DEFAULT_NAME_SERVER; - private String logLevel = "ERROR"; + /** + * The property of "access-key". + */ + private String accessKey; - public String getNamesrvAddr() { - return namesrvAddr; + /** + * 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 getNameServer() { + return nameServer; } - public void setNamesrvAddr(String namesrvAddr) { - this.namesrvAddr = namesrvAddr; + public void setNameServer(String nameServer) { + this.nameServer = nameServer; } - public String getLogLevel() { - return logLevel; + public String getAccessKey() { + return accessKey; } - public void setLogLevel(String logLevel) { - this.logLevel = logLevel; + 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; + } } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQConsumerProperties.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQConsumerProperties.java index 8202fefb..6cfe9b84 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQConsumerProperties.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQConsumerProperties.java @@ -50,42 +50,18 @@ public class RocketMQConsumerProperties { */ private Boolean orderly = false; + /** + * for concurrently listener. message consume retry strategy + */ + private int delayLevelWhenNextConsume = 0; + + /** + * for orderly listener. next retry delay time + */ + private long suspendCurrentQueueTimeMillis = 1000; + private Boolean enabled = true; - private Error error; - - public static class Error { - - /** - * Reconsume later timeMillis in ConsumeOrderlyContext. - */ - private Long suspendCurrentQueueTimeMillis = 1000L; - - /** - * Message consume retry strategy in ConsumeConcurrentlyContext. - * - * -1,no retry,put into DLQ directly 0,broker control retry frequency >0,client - * control retry frequency - */ - private Integer delayLevelWhenNextConsume = 0; - - public Long getSuspendCurrentQueueTimeMillis() { - return suspendCurrentQueueTimeMillis; - } - - public void setSuspendCurrentQueueTimeMillis(Long suspendCurrentQueueTimeMillis) { - this.suspendCurrentQueueTimeMillis = suspendCurrentQueueTimeMillis; - } - - public Integer getDelayLevelWhenNextConsume() { - return delayLevelWhenNextConsume; - } - - public void setDelayLevelWhenNextConsume(Integer delayLevelWhenNextConsume) { - this.delayLevelWhenNextConsume = delayLevelWhenNextConsume; - } - } - public String getTags() { return tags; } @@ -126,11 +102,19 @@ public class RocketMQConsumerProperties { this.broadcasting = broadcasting; } - public Error getError() { - return error; + public int getDelayLevelWhenNextConsume() { + return delayLevelWhenNextConsume; } - public void setError(Error error) { - this.error = error; + public void setDelayLevelWhenNextConsume(int delayLevelWhenNextConsume) { + this.delayLevelWhenNextConsume = delayLevelWhenNextConsume; + } + + public long getSuspendCurrentQueueTimeMillis() { + return suspendCurrentQueueTimeMillis; + } + + public void setSuspendCurrentQueueTimeMillis(long suspendCurrentQueueTimeMillis) { + this.suspendCurrentQueueTimeMillis = suspendCurrentQueueTimeMillis; } } diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQProducerProperties.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQProducerProperties.java index b526d960..a8d784e7 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQProducerProperties.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/properties/RocketMQProducerProperties.java @@ -17,8 +17,6 @@ package org.springframework.cloud.stream.binder.rocketmq.properties; import org.apache.rocketmq.client.producer.DefaultMQProducer; -import org.apache.rocketmq.client.producer.LocalTransactionExecuter; -import org.apache.rocketmq.client.producer.TransactionCheckListener; /** * @author Timur Valiev @@ -29,24 +27,62 @@ public class RocketMQProducerProperties { private Boolean enabled = true; /** - * Maximum allowed message size in bytes {@link DefaultMQProducer#maxMessageSize} + * Name of producer. */ - private Integer maxMessageSize = 0; + private String group; + + /** + * Maximum allowed message size in bytes {@link DefaultMQProducer#maxMessageSize}. + */ + private Integer maxMessageSize = 1024 * 1024 * 4; private Boolean transactional = false; - /** - * full class name of {@link LocalTransactionExecuter} - */ - private String executer; - - /** - * full class name of {@link TransactionCheckListener} - */ - private String transactionCheckListener; + private Boolean sync = false; private Boolean vipChannelEnabled = true; + /** + * 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; + + /** + *

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

+ * This may potentially cause message duplication which is up to application + * developers to resolve. + */ + private int retryTimesWhenSendAsyncFailed = 2; + + /** + * Indicate whether to retry another broker on sending failure internally. + */ + private boolean retryNextServer = false; + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + public Boolean getEnabled() { return enabled; } @@ -71,20 +107,12 @@ public class RocketMQProducerProperties { this.transactional = transactional; } - public String getExecuter() { - return executer; + public Boolean getSync() { + return sync; } - public void setExecuter(String executer) { - this.executer = executer; - } - - public String getTransactionCheckListener() { - return transactionCheckListener; - } - - public void setTransactionCheckListener(String transactionCheckListener) { - this.transactionCheckListener = transactionCheckListener; + public void setSync(Boolean sync) { + this.sync = sync; } public Boolean getVipChannelEnabled() { @@ -94,4 +122,45 @@ public class RocketMQProducerProperties { public void setVipChannelEnabled(Boolean vipChannelEnabled) { this.vipChannelEnabled = vipChannelEnabled; } -} + + 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; + } + +} \ No newline at end of file diff --git a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/provisioning/RocketMQTopicProvisioner.java b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/provisioning/RocketMQTopicProvisioner.java index 456ef5cc..d8d08626 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/provisioning/RocketMQTopicProvisioner.java +++ b/spring-cloud-stream-binder-rocketmq/src/main/java/org/springframework/cloud/stream/binder/rocketmq/provisioning/RocketMQTopicProvisioner.java @@ -18,8 +18,6 @@ package org.springframework.cloud.stream.binder.rocketmq.provisioning; import org.apache.rocketmq.client.Validators; import org.apache.rocketmq.client.exception.MQClientException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.cloud.stream.binder.ExtendedConsumerProperties; import org.springframework.cloud.stream.binder.ExtendedProducerProperties; import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQConsumerProperties; @@ -36,9 +34,6 @@ import org.springframework.cloud.stream.provisioning.ProvisioningProvider; public class RocketMQTopicProvisioner implements ProvisioningProvider, ExtendedProducerProperties> { - private static final Logger logger = LoggerFactory - .getLogger(RocketMQTopicProvisioner.class); - @Override public ProducerDestination provisionProducerDestination(String name, ExtendedProducerProperties properties) diff --git a/spring-cloud-stream-binder-rocketmq/src/main/resources/META-INF/spring.factories b/spring-cloud-stream-binder-rocketmq/src/main/resources/META-INF/spring.factories index 43b85129..46f79a88 100644 --- a/spring-cloud-stream-binder-rocketmq/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-stream-binder-rocketmq/src/main/resources/META-INF/spring.factories @@ -1,2 +1,2 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.springframework.cloud.stream.binder.rocketmq.config.RocketMQBinderEndpointAutoConfiguration +org.springframework.cloud.stream.binder.rocketmq.config.RocketMQComponent4BinderAutoConfiguration diff --git a/spring-cloud-stream-binder-rocketmq/src/test/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQAutoConfigurationTests.java b/spring-cloud-stream-binder-rocketmq/src/test/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQAutoConfigurationTests.java index 9b1be23f..4a787e65 100644 --- a/spring-cloud-stream-binder-rocketmq/src/test/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQAutoConfigurationTests.java +++ b/spring-cloud-stream-binder-rocketmq/src/test/java/org/springframework/cloud/stream/binder/rocketmq/RocketMQAutoConfigurationTests.java @@ -22,7 +22,6 @@ import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.cloud.stream.binder.rocketmq.config.RocketMQBinderAutoConfiguration; -import org.springframework.cloud.stream.binder.rocketmq.config.RocketMQBinderEndpointAutoConfiguration; import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQBinderConfigurationProperties; import org.springframework.cloud.stream.binder.rocketmq.properties.RocketMQExtendedBindingProperties; @@ -33,8 +32,7 @@ public class RocketMQAutoConfigurationTests { private ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration( - AutoConfigurations.of(RocketMQBinderEndpointAutoConfiguration.class, - RocketMQBinderAutoConfiguration.class)) + AutoConfigurations.of(RocketMQBinderAutoConfiguration.class)) .withPropertyValues( "spring.cloud.stream.rocketmq.binder.namesrv-addr=127.0.0.1:9876", "spring.cloud.stream.bindings.output.destination=TopicOrderTest", @@ -55,7 +53,7 @@ public class RocketMQAutoConfigurationTests { this.contextRunner.run(context -> { RocketMQBinderConfigurationProperties binderConfigurationProperties = context .getBean(RocketMQBinderConfigurationProperties.class); - assertThat(binderConfigurationProperties.getNamesrvAddr()) + assertThat(binderConfigurationProperties.getNameServer()) .isEqualTo("127.0.0.1:9876"); RocketMQExtendedBindingProperties bindingProperties = context .getBean(RocketMQExtendedBindingProperties.class);