1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00
This commit is contained in:
小马哥 2019-03-29 20:08:57 +08:00
commit 5073b10ad1
208 changed files with 3764 additions and 3735 deletions

View File

@ -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/.*"
coverage:
status:
project: off
patch: off

View File

@ -1 +0,0 @@
language: java

View File

@ -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

2
mvnw vendored
View File

@ -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

2
mvnw.cmd vendored
View File

@ -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

13
pom.xml
View File

@ -2,7 +2,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
@ -22,7 +22,7 @@
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
@ -74,6 +74,7 @@
<spring-cloud-netflix.version>2.1.0.RELEASE</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>2.1.0.RELEASE</spring-cloud-openfeign.version>
<spring-cloud-bus.version>2.1.0.RELEASE</spring-cloud-bus.version>
<spring-cloud-gateway.version>2.1.0.RELEASE</spring-cloud-gateway.version>
<junit.version>4.12</junit.version>
<javax-servlet-api>3.0</javax-servlet-api>
@ -171,6 +172,14 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-gateway-dependencies</artifactId>
<version>${spring-cloud-gateway.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba</artifactId>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
@ -19,15 +19,15 @@
<properties>
<sentinel.version>1.4.2</sentinel.version>
<oss.version>3.1.0</oss.version>
<fescar.version>0.1.3</fescar.version>
<nacos.client.version>0.8.2</nacos.client.version>
<fescar.version>0.4.0</fescar.version>
<nacos.client.version>1.0.0-RC2</nacos.client.version>
<nacos.config.version>0.8.0</nacos.config.version>
<acm.version>1.0.8</acm.version>
<ans.version>1.0.1</ans.version>
<aliyun.sdk.version>4.0.1</aliyun.sdk.version>
<alicloud.context.version>1.0.5</alicloud.context.version>
<aliyun.sdk.edas.version>2.16.0</aliyun.sdk.edas.version>
<rocketmq.version>4.3.1</rocketmq.version>
<rocketmq.starter.version>2.0.2</rocketmq.starter.version>
<schedulerX.client.version>2.1.6</schedulerX.client.version>
<dubbo.version>2.6.5</dubbo.version>
<dubbo-spring-boot.version>0.2.1.RELEASE</dubbo-spring-boot.version>
@ -107,8 +107,8 @@
<!-- Apache RocketMQ -->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>${rocketmq.version}</version>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>${rocketmq.starter.version}</version>
</dependency>
<!-- Sentinel -->
@ -186,26 +186,11 @@
<!--Alibaba Fescar-->
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-core</artifactId>
<version>${fescar.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-common</artifactId>
<version>${fescar.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-spring</artifactId>
<version>${fescar.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-rm-datasource</artifactId>
<version>${fescar.version}</version>
</dependency>
<!-- Dubbo -->
<dependency>
@ -393,6 +378,14 @@
<version>${project.version}</version>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Testing Dependencies -->
</dependencies>
@ -453,4 +446,4 @@
</pluginRepositories>
</profile>
</profiles>
</project>
</project>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>

View File

@ -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]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
并引入 Spring Cloud AliCloud ACM Starter 依赖。
如果要在您的项目中引入 ACM使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-acm` 的 starter。
[source,xml]
----

View File

@ -4,24 +4,7 @@ ANSApplication Naming Service 是隶属于阿里云 EDAS 产品的组件
=== 如何引入 Spring Cloud AliCloud ANS
Spring Cloud Alibaba 已经发布了 0.2.2.BUILD-SNAPSHOT 版本,需要首先导入依赖管理 POM。
[source,xml]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
接下来引入 Spring Cloud AliCloud ANS Starter 即可。
如果要在您的项目中引入 ANS使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-ans` 的 starter。
[source,xml]
----

View File

@ -2,3 +2,41 @@
Spring Cloud Alibaba BOM 包含了它所使用的所有依赖的版本。
### Spring Cloud Alibaba Bill of Materials (BOM)
如果您是 Maven Central 用户,请将我们的 BOM 添加到您的 pom.xml 中的 <dependencyManagement> 部分。 这将允许您省略任何Maven依赖项的版本而是将版本控制委派给BOM。
```xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
```
### Spring Snapshots Maven Repository
如果要使用最新的 BUILD-SNAPSHOT 版本,请在 pom.xml 中添加 Spring Snapshot Repository注意BUILD-SNAPSHOT随时可能更新:
```xml
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring SnapShots</name>
<url>https://repo.spring.io/libs-snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
```
举个例子, 0.2.0.BUILD-SNAPSHOT 版本就在这个仓库中可用。

View File

@ -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]
----
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
----
现在就可以创建一个标准的 Spring Boot 应用。

View File

@ -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]
----
<!-- dependency management-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- dependencies -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
----
==== 启动一个 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.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<version>${spring.boot.version}</version>
<relativePath/>
</parent>
@ -77,14 +56,14 @@ Spring Cloud 的 Finchley.SR1 版本最佳实践的 Spring Boot 版本是 2.0.6.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

View File

@ -4,24 +4,7 @@ OSSObject Storage Service是阿里云的一款对象存储服务产品
=== 如何引入 Spring Cloud AliCloud OSS
Spring Cloud Alibaba 已经发布了0.2.0版本需要首先导入依赖管理POM。
[source,xml]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
接下来引入 Spring Cloud AliCloud OSS Starter 即可。
如果要在您的项目中引入 OSS使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-oss` 的 starter。
[source,xml]
----

View File

@ -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
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rocketmq</artifactId>
</dependency>
```
在构造 `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
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>
```
在消费消息的时候可以从 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.<channelName>.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.<channelName>.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"
}
```
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`.

View File

@ -4,24 +4,7 @@ SchedulerX分布式任务调度 是隶属于阿里云EDAS产品的组件
=== 如何引入 Spring Cloud AliCloud SchedulerX
Spring Cloud Alibaba 已经发布了0.2.1版本需要首先导入依赖管理POM。
[source,xml]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
接下来引入 Spring Cloud AliCloud SchedulerX Starter 即可。
如果要在您的项目中引入 SchedulerX使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-schedulerX` 的 starter。
[source,xml]
----

View File

@ -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|

View File

@ -4,24 +4,7 @@
=== 如何引入 Spring Cloud AliCloud SMS
Spring Cloud Alibaba 已经发布了 0.2.2.BUILD-SNAPSHOT 版本,需要首先导入依赖管理 POM。
[source,xml]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
接下来引入 Spring Cloud AliCloud SMS Starter 即可。
如果要在您的项目中引入 SMS使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-sms` 的 starter。
[source,xml]
----

View File

@ -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
Weve released Spring Cloud Alibaba version 0.2.2.BUILD-SNAPSHOT. You will need to add dependency management POM first.
[source,xml]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
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]
----

View File

@ -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
Weve released Spring Cloud Alibaba version 0.2.1. You will need to add dependency management POM first.
[source,xml]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
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]
----

View File

@ -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 youre a Maven Central user, add our BOM to your pom.xml <dependencyManagement> 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
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
```
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
<repositories>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot Repository</name>
<path>https://repo.spring.io/snapshot</path>
<id>spring-snapshots</id>
<name>Spring SnapShots</name>
<url>https://repo.spring.io/libs-snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
----
```
For example, the 0.2.0.BUILD-SNAPSHOT is available from this repository.

View File

@ -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]
----
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
----
Now we can create a standard Spring Boot application.

View File

@ -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]
----
<! -- dependency management-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<! -- dependencies -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
----
==== 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.
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<version>${spring.boot.version}</version>
<relativePath/>
</parent>
@ -77,14 +56,14 @@ The following sample illustrates how to register a service to Nacos.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>

View File

@ -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
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rocketmq</artifactId>
</dependency>
```
Alternatively, you can also use the Spring Cloud Stream RocketMQ Starter:
```xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>
```
### 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 its 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.<channelName>.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.<channelName>.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"
}
```
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`.

View File

@ -4,24 +4,7 @@ SchedulerXDistributed job scheduling is a component of EDAS, an Alibaba Cl
=== How to Introduce Spring Cloud Alibaba Cloud SchedulerX
Weve released Spring Cloud Alibaba version 0.2.1. You will need to add dependency management POM first.
[source,xml]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
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]
----

View File

@ -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|

View File

@ -6,24 +6,7 @@ Spring Cloud AliCloud SMS provide an easier-to-use API for quick access to Aliba
=== 如何引入 Spring Cloud AliCloud SMS
Weve released Spring Cloud Alibaba version 0.2.0. You will need to add dependency management POM first.
[source,xml]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
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]
----

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
@ -14,7 +14,7 @@
<name>Spring Cloud Alibaba Dubbo</name>
<properties>
<dubbo.version>2.7.0</dubbo.version>
<dubbo.version>2.7.1</dubbo.version>
<spring-cloud-zookeeper.version>2.1.0.RELEASE</spring-cloud-zookeeper.version>
<spring-cloud-consul.version>2.1.0.RELEASE</spring-cloud-consul.version>
<curator.version>4.0.1</curator.version>

View File

@ -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 {};
}

View File

@ -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<String, RestTemplate> entry : restTemplates.entrySet()) {
String beanName = entry.getKey();
Map<String, Object> dubboTranslatedAttributes = getDubboTranslatedAttributes(beanName);
Map<String, Object> 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<String, Object> getDubboTranslatedAttributes(String beanName) {
private Map<String, Object> getDubboTranslatedAttributes(String beanName,
DubboTransportedAttributesResolver attributesResolver) {
Map<String, Object> 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<String, Object> dubboTranslatedAttributes) {
DubboTransportedMetadata dubboTransportedMetadata = buildDubboTransportedMetadata(dubboTranslatedAttributes);
List<ClientHttpRequestInterceptor> 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<String, Object> 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;

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@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<ProtocolConfig> 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<ProtocolConfig> 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<ProtocolConfig> dubboProtocolConfigSupplier(ObjectProvider<Collection<ProtocolConfig>> 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));
}
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@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<DubboMetadataConfigService> 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<URL> 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<Registration> 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());
}
}
}

View File

@ -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
*

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@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<String> tags = newService.getTags();
tags.add(DUBBO_URLS_METADATA_PROPERTY_NAME + "=" + dubboURLsJson);
}
}
}
private void attachURLsIntoMetadata(Registration registration) {
if (registration == null) {
return;
}
Map<String, String> metadata = registration.getMetadata();
String dubboURLsJson = getDubboURLsJSON();
if (StringUtils.hasText(dubboURLsJson)) {
metadata.put(DUBBO_URLS_METADATA_PROPERTY_NAME, dubboURLsJson);
}
}
private String getDubboURLsJSON() {
Collection<URL> 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()));
}
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@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<URL> 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);
}
}
}
}

View File

@ -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<String, Object> dubboTranslatedAttributes;
private final DubboGenericServiceFactory serviceFactory;
@ -68,11 +68,11 @@ public class DubboTransporterInterceptor implements ClientHttpRequestInterceptor
public DubboTransporterInterceptor(DubboServiceMetadataRepository dubboServiceMetadataRepository,
List<HttpMessageConverter<?>> messageConverters,
ClassLoader classLoader,
DubboTransportedMetadata dubboTransportedMetadata,
Map<String, Object> 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);

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboProtocolConfigSupplier implements Supplier<ProtocolConfig> {
private final ObjectProvider<Collection<ProtocolConfig>> protocols;
public DubboProtocolConfigSupplier(ObjectProvider<Collection<ProtocolConfig>> protocols) {
this.protocols = protocols;
}
@Override
public ProtocolConfig get() {
ProtocolConfig protocolConfig = null;
Collection<ProtocolConfig> 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<ProtocolConfig> iterator = protocols.iterator();
protocolConfig = iterator.hasNext() ? iterator.next() : null;
}
if (protocolConfig == null) {
protocolConfig = new ProtocolConfig();
protocolConfig.setName(DEFAULT_PROTOCOL);
protocolConfig.setPort(20880);
}
return protocolConfig;
}
}

View File

@ -19,17 +19,17 @@ package org.springframework.cloud.alibaba.dubbo.metadata;
import java.util.Objects;
/**
* Dubbo Service Metadata
* Dubbo Rest Service Metadata
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
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);
}

View File

@ -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);
}
}

View File

@ -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<String, Object> attributes;
public DubboTransportedMethodMetadata(Method method, Map<String, Object> 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<String, Object> 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);
}
}

View File

@ -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<URL> registeredURLs = new LinkedHashSet<>();
/**
* Key is application name
* Value is Map<RequestMetadata, DubboServiceMetadata>
* Value is Map<RequestMetadata, DubboRestServiceMetadata>
*/
private Map<String, Map<RequestMetadataMatcher, DubboServiceMetadata>> repository = newHashMap();
private Map<String, Map<RequestMetadataMatcher, DubboRestServiceMetadata>> 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<URL> 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<RequestMetadataMatcher, DubboServiceMetadata> metadataMap = getMetadataMap(serviceName);
Map<RequestMetadataMatcher, DubboRestServiceMetadata> 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 <code>null</code>
* @return {@link DubboRestServiceMetadata} if matched, or <code>null</code>
*/
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<RequestMetadataMatcher, DubboServiceMetadata> getMetadataMap(String serviceName) {
private Map<RequestMetadataMatcher, DubboRestServiceMetadata> getMetadataMap(String serviceName) {
return getMap(repository, serviceName);
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboTransportedAttributesResolver {
private final PropertyResolver propertyResolver;
public DubboTransportedAttributesResolver(PropertyResolver propertyResolver) {
this.propertyResolver = propertyResolver;
}
public Map<String, Object> resolve(DubboTransported dubboTransported) {
Map<String, Object> attributes = getAnnotationAttributes(dubboTransported);
return resolve(attributes);
}
public Map<String, Object> resolve(Map<String, Object> attributes) {
Map<String, Object> resolvedAttributes = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
Object value = entry.getValue();
if (value instanceof String) {
value = propertyResolver.resolvePlaceholders(value.toString());
}
resolvedAttributes.put(entry.getKey(), value);
}
return resolvedAttributes;
}
}

View File

@ -42,12 +42,12 @@ public class DubboTransportedMethodMetadataResolver {
private static final Class<DubboTransported> 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<String, Object> attributes = attributesResolver.resolve(dubboTransported);
return new DubboTransportedMethodMetadata(method, attributes);
}
private DubboTransported resolveDubboTransported(Method method) {

View File

@ -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<DubboTransportedMethodMetadata, RestMethodMetadata> 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<String, Object> 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);

View File

@ -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
* <p>
*
* @param <R> The subclass of {@link Registration}
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public abstract class AbstractRegistrationFactory<R extends Registration> implements RegistrationFactory<R> {
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);
}
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
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<String> 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<String> serviceNames) {
filter(serviceNames, new Filter<String>() {
@Override
public boolean accept(String serviceName) {
return supports(serviceName);
}
});
}
protected abstract boolean supports(String serviceName);
protected final Set<String> 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<String> 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<String> getServiceNamesForOps(URL url) {
Set<String> serviceNames = getAllServiceNames();
filterServiceNames(serviceNames);
return serviceNames;
}
protected abstract String getServiceName(URL url);
private void doSubscribe(final URL url, final NotifyListener listener, final Collection<String> 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<ServiceInstance> getServiceInstances(String serviceName) {
return discoveryClient.getInstances(serviceName);
}
private void subscribe(final URL url, final NotifyListener listener, final Collection<String> serviceNames) {
for (String serviceName : serviceNames) {
List<ServiceInstance> 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<ServiceInstance> serviceInstances);
protected void filterHealthyInstances(Collection<ServiceInstance> instances) {
filter(instances, new Filter<ServiceInstance>() {
@Override
public boolean accept(ServiceInstance data) {
// TODO check the details of status
// return serviceRegistry.getStatus(new DubboRegistration(data)) != null;
return true;
}
});
}
protected List<URL> buildURLs(URL consumerURL, Collection<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
return Collections.emptyList();
}
List<URL> urls = new LinkedList<URL>();
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 <T> void filter(Collection<T> collection, Filter<T> filter) {
Iterator<T> iterator = collection.iterator();
while (iterator.hasNext()) {
T data = iterator.next();
if (!filter.accept(data)) { // remove if not accept
iterator.remove();
}
}
}
private static <T> T[] of(T... values) {
return values;
}
/**
* A filter
*/
public interface Filter<T> {
/**
* Tests whether or not the specified data should be accepted.
*
* @param data The data to be tested
* @return <code>true</code> if and only if <code>data</code>
* should be accepted
*/
boolean accept(T data);
}
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @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;
}
}

View File

@ -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 <R> The subclass of {@link Registration}
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface RegistrationFactory<R extends Registration> {
/**
* 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);
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class RegistrationFactoryProvider implements FactoryBean<RegistrationFactory>, 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<Registration> serviceRegistry = applicationContext.getBean(ServiceRegistry.class);
ClassLoader classLoader = applicationContext.getClassLoader();
this.registrationFactory = buildRegistrationFactory(serviceRegistry, classLoader);
}
private RegistrationFactory buildRegistrationFactory(ServiceRegistry<Registration> serviceRegistry,
ClassLoader classLoader) {
RegistrationFactory registrationFactory = null;
List<String> 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);
}
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
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<String> dubboProviderServices;
private final ServiceRegistry<Registration> serviceRegistry;
private final Map<String, String> 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<Registration> 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<String, String> metadata = registration.getMetadata();
String side = metadata.get(SIDE_KEY);
return PROVIDER_SIDE.equals(side); // Only register the Provider.
private Map<String, String> initDubboServiceKeysCache() {
if (isEmpty(dubboProviderServices)) {
return emptyMap();
}
Map<String, String> 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<URL> getURLs(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
String dubboURLsJSON = metadata.get(DUBBO_URLS_METADATA_PROPERTY_NAME);
List<String> urlValues = jsonUtils.toList(dubboURLsJSON);
return urlValues.stream().map(URL::valueOf).collect(Collectors.toList());
}
private Set<String> 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<String> 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<ServiceInstance> serviceInstances) {
List<URL> 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<String> serviceNames) {
filter(serviceNames, new Filter<String>() {
@Override
public boolean accept(String serviceName) {
return dubboRegistryServiceIdHandler.supports(serviceName);
}
});
}
private List<String> 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<String> 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<String> serviceNames = getAllServiceNames();
filterServiceNames(serviceNames);
doSubscribe(url, listener, serviceNames);
}
}, allServicesLookupInterval, allServicesLookupInterval, TimeUnit.SECONDS);
}
private void doSubscribe(final URL url, final NotifyListener listener, final List<String> serviceNames) {
for (String serviceName : serviceNames) {
List<ServiceInstance> 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<ServiceInstance> serviceInstances) {
List<ServiceInstance> healthyInstances = new LinkedList<ServiceInstance>(serviceInstances);
// Healthy Instances
filterHealthyInstances(healthyInstances);
List<URL> urls = buildURLs(url, healthyInstances);
this.notify(url, listener, urls);
}
private void filterHealthyInstances(Collection<ServiceInstance> instances) {
filter(instances, new Filter<ServiceInstance>() {
@Override
public boolean accept(ServiceInstance data) {
// TODO check the details of status
// return serviceRegistry.getStatus(new DubboRegistration(data)) != null;
return true;
}
});
}
private List<URL> buildURLs(URL consumerURL, Collection<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
return Collections.emptyList();
}
List<URL> urls = new LinkedList<URL>();
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<String> getServiceNamesForOps(URL url) {
List<String> serviceNames = getAllServiceNames();
filterServiceNames(serviceNames);
return serviceNames;
}
private <T> void filter(Collection<T> collection, Filter<T> filter) {
Iterator<T> iterator = collection.iterator();
while (iterator.hasNext()) {
T data = iterator.next();
if (!filter.accept(data)) { // remove if not accept
iterator.remove();
}
}
}
private static <T> T[] of(T... values) {
return values;
}
/**
* A filter
*/
public interface Filter<T> {
/**
* Tests whether or not the specified data should be accepted.
*
* @param data The data to be tested
* @return <code>true</code> if and only if <code>data</code>
* should be accepted
*/
boolean accept(T data);
}
}

View File

@ -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<Registration> 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) {

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class ZookeeperRegistrationFactory extends AbstractRegistrationFactory<ZookeeperRegistration> {
@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;
}
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class ServiceInstancePreRegisteredEvent extends ApplicationEvent {
public ServiceInstancePreRegisteredEvent(Registration source) {
super(source);
}
@Override
public Registration getSource() {
return (Registration) super.getSource();
}
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DefaultRegistrationFactory extends AbstractRegistrationFactory<Registration> {
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();
}
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
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 <code>true</code>, or <code>false</code>
*/
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();
}

View File

@ -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}
* <p>
* The service ID pattern is "${category}:${interface}:${version}:${group}"
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
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);
}
}
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class ConsulRegistrationFactory extends AbstractRegistrationFactory<ConsulRegistration> {
@Override
public ConsulRegistration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) {
Map<String, String> metadata = getMetadata(serviceInstance);
List<String> 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<String> createTags(Map<String, String> metadata) {
List<String> tags = new LinkedList<>();
for (Map.Entry<String, String> entry : metadata.entrySet()) {
String tag = entry.getKey() + "=" + entry.getValue();
tags.add(tag);
}
return tags;
}
private Map<String, String> getMetadata(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
Set<String> removedKeys = new LinkedHashSet<>();
for (String key : metadata.keySet()) {
if (key.contains(".")) {
removedKeys.add(key);
}
}
for (String removedKey : removedKeys) {
metadata.remove(removedKey);
}
return metadata;
}
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class EurekaRegistrationFactory extends AbstractRegistrationFactory<EurekaRegistration> {
@Override
public EurekaRegistration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) {
CloudEurekaInstanceConfig cloudEurekaInstanceConfig = applicationContext.getBean(CloudEurekaInstanceConfig.class);
ObjectProvider<HealthCheckHandler> 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();
}
}

View File

@ -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<Integer, ReferenceBean<GenericService>> cache = new ConcurrentHashMap<>();
public GenericService create(DubboServiceMetadata dubboServiceMetadata,
DubboTransportedMetadata dubboTransportedMetadata) {
public GenericService create(DubboRestServiceMetadata dubboServiceMetadata,
Map<String, Object> dubboTranslatedAttributes) {
ReferenceBean<GenericService> referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTransportedMetadata);
ReferenceBean<GenericService> referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTranslatedAttributes);
return referenceBean == null ? null : referenceBean.get();
}
public GenericService create(String serviceName, Class<?> serviceClass) {
String interfaceName = serviceClass.getName();
ReferenceBean<GenericService> referenceBean = build(interfaceName, serviceName, null,
DEFAULT_PROTOCOL, DEFAULT_CLUSTER);
ReferenceBean<GenericService> referenceBean = build(interfaceName, serviceName, null, emptyMap());
return referenceBean.get();
}
private ReferenceBean<GenericService> build(ServiceRestMetadata serviceRestMetadata,
DubboTransportedMetadata dubboTransportedMetadata) {
Map<String, Object> 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<GenericService> build(String interfaceName, String version, String group, String protocol,
String cluster) {
private ReferenceBean<GenericService> build(String interfaceName, String version, String group,
Map<String, Object> dubboTranslatedAttributes) {
Integer key = Objects.hash(interfaceName, version, group, protocol, cluster);
Integer key = Objects.hash(interfaceName, version, group, dubboTranslatedAttributes);
ReferenceBean<GenericService> 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<GenericService> referenceBean, Map<String, Object> 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<String, String> parameters = CollectionUtils.toStringMap(commaDelimitedListToStringArray(content));
setValue(parameters);
}
});
dataBinder.bind(new MutablePropertyValues(dubboTranslatedAttributes));
}
@PreDestroy
public void destroy() {
destroyReferenceBeans();

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Component
public class DubboMetadataConfigServiceExporter {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ApplicationConfig applicationConfig;
@Autowired
private PublishingDubboMetadataConfigService dubboMetadataConfigService;
@Autowired
private Supplier<ProtocolConfig> protocolConfigSupplier;
@Value("${spring.application.name:application}")
private String currentApplicationName;
/**
* The ServiceConfig of DubboMetadataConfigService to be exported, can be nullable.
*/
private ServiceConfig<DubboMetadataConfigService> 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());
}
}
}

View File

@ -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> 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;
}

View File

@ -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 <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
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<String> toList(String json) {
List<String> 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;
}
}

View File

@ -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
org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>

View File

@ -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
}
}

View File

@ -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"
}
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>

View File

@ -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
}
}

View File

@ -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"
}
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>

View File

@ -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
}
}

View File

@ -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"
}
}

View File

@ -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;
```
### 创建 示例中 业务所需要的数据库表

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>

View File

@ -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
}
}

View File

@ -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"
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>

View File

@ -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)
## 原理

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>
@ -19,7 +19,6 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
<dependency>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>
@ -34,7 +34,8 @@
<module>fescar-example/storage-service</module>
<module>fescar-example/account-service</module>
<module>acm-example/acm-local-example</module>
<module>rocketmq-example</module>
<module>rocketmq-example/rocketmq-consume-example</module>
<module>rocketmq-example/rocketmq-produce-example</module>
<module>sms-example</module>
<module>spring-cloud-bus-rocketmq-example</module>
<module>schedulerx-example/schedulerx-simple-task-example</module>

View File

@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>rocketmq-example</artifactId>
<artifactId>rocketmq-consume-example</artifactId>
<packaging>jar</packaging>
<description>Example demonstrating how to use rocketmq</description>
<description>Example demonstrating how to use rocketmq consume</description>
<dependencies>
@ -32,11 +32,6 @@
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -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);
}
}

View File

@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@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);
}
}

View File

@ -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=*
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>rocketmq-produce-example</artifactId>
<packaging>jar</packaging>
<description>Example demonstrating how to use rocketmq produce</description>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>${maven-deploy-plugin.version}</version>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,39 @@
package org.springframework.cloud.alibaba.cloud.examples;
/**
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
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 + '\'' + '}';
}
}

View File

@ -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 <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@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);
}
}

Some files were not shown because too many files have changed in this diff Show More