mirror of
https://gitee.com/mirrors/Spring-Cloud-Alibaba.git
synced 2021-06-26 13:25:11 +08:00
Merge branch 'master' of https://github.com/mercyblitz/spring-cloud-alibaba
This commit is contained in:
commit
5073b10ad1
12
.codecov.yml
12
.codecov.yml
@ -1,7 +1,5 @@
|
||||
ignore:
|
||||
- "spring-cloud-alibaba-dependencies/.*"
|
||||
- "spring-cloud-alibaba-docs/.*"
|
||||
- "spring-cloud-alibaba-examples/.*"
|
||||
- "spring-cloud-alibaba-test/.*"
|
||||
- "spring-cloud-starter-alibaba/.*"
|
||||
- "spring-cloud-starter-alicloud/.*"
|
||||
|
||||
coverage:
|
||||
status:
|
||||
project: off
|
||||
patch: off
|
@ -1 +0,0 @@
|
||||
language: java
|
@ -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
2
mvnw
vendored
@ -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
2
mvnw.cmd
vendored
@ -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
13
pom.xml
@ -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>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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]
|
||||
----
|
||||
|
@ -4,24 +4,7 @@ ANS(Application 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]
|
||||
----
|
||||
|
@ -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 版本就在这个仓库中可用。
|
||||
|
||||
|
||||
|
@ -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 应用。
|
||||
|
@ -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>
|
||||
|
@ -4,24 +4,7 @@ OSS(Object 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]
|
||||
----
|
||||
|
@ -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`.
|
||||
|
@ -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]
|
||||
----
|
||||
|
@ -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|
|
||||
|
@ -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]
|
||||
----
|
||||
|
@ -6,24 +6,7 @@ Spring Cloud Alibaba Cloud ACM is an alternative solution for Config Server and
|
||||
|
||||
=== How to Introduce Spring Cloud Alibaba Cloud ACM
|
||||
|
||||
We’ve released Spring Cloud Alibaba version 0.2.2.BUILD-SNAPSHOT. You will need to add dependency management POM first.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<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]
|
||||
----
|
||||
|
@ -4,24 +4,7 @@ ANS(Application Naming Service) is a component of EDAS. Spring Cloud Alibaba Cl
|
||||
|
||||
=== How to Introduce Spring Cloud Alibaba Cloud ANS
|
||||
|
||||
We’ve released Spring Cloud Alibaba version 0.2.1. You will need to add dependency management POM first.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<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]
|
||||
----
|
||||
|
@ -1,38 +1,38 @@
|
||||
== Dependency Management
|
||||
|
||||
The Spring Cloud Alibaba Bill of Materials (BOM) contains the versions of all the dependencies it uses.
|
||||
### Spring Cloud Alibaba Bill of Materials (BOM)
|
||||
|
||||
Version 0.2.2.BUILD-SNAPSHOT is compatible with the Spring Cloud Finchley. Version 0.1.1.RELEASE is compatible with the Spring Cloud Edgware.
|
||||
If you’re a Maven Central user, add our BOM to your pom.xml <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.
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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 it’s bigger than 0. The default value of RocketMQ is 4M = 1024 * 1024 * 4)
|
||||
|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.transactional`|Whether to use `TransactionMQProducer` to send transaction messages|false
|
||||
|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.executer`|Full class name of the interface implementation class related to `org.apache.rocketmq.client.producer.LocalTransactionExecuter` For example, `org.test.MyExecuter`|
|
||||
|`spring.cloud.stream.rocketmq.bindings.your-output-binding.producer.transaction-check-listener`|Full class name of the interface implementation class related to `org.apache.rocketmq.client.producer.TransactionCheckListener` For example, `org.test.MyTransactionCheckListener`|
|
||||
|====
|
||||
|
||||
Supported Configurations of Consumer:
|
||||
#### RocketMQ Consumer Properties
|
||||
|
||||
:frame: topbot
|
||||
[width="60%",options="header"]
|
||||
|====
|
||||
^|Configuration ^|Description| Default Value
|
||||
|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.enabled`|Whether to use consumer|true
|
||||
|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.tags`|Consumer will only subscribe to messages with these tags Tags are separated by "\|\|" (If not specified, it means the consumer subscribes to all messages)|
|
||||
|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.sql`|Consumer subscribes to the messages as requested in the SQL(If tags are also specified, SQL has a higher priority than tags.)|
|
||||
|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.broadcasting`|If the consumer uses the broadcasting mode|false
|
||||
|`spring.cloud.stream.rocketmq.bindings.your-input-binding.consumer.orderly`|Ordered message consumption or asychronous consumption|false
|
||||
|====
|
||||
The following properties are available for RocketMQ producers only and must be prefixed with `spring.cloud.stream.rocketmq.bindings.<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`.
|
@ -4,24 +4,7 @@ SchedulerX(Distributed job scheduling) is a component of EDAS, an Alibaba Cl
|
||||
|
||||
=== How to Introduce Spring Cloud Alibaba Cloud SchedulerX
|
||||
|
||||
We’ve released Spring Cloud Alibaba version 0.2.1. You will need to add dependency management POM first.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<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]
|
||||
----
|
||||
|
@ -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|
|
||||
|
@ -6,24 +6,7 @@ Spring Cloud AliCloud SMS provide an easier-to-use API for quick access to Aliba
|
||||
|
||||
=== 如何引入 Spring Cloud AliCloud SMS
|
||||
|
||||
We’ve released Spring Cloud Alibaba version 0.2.0. You will need to add dependency management POM first.
|
||||
|
||||
[source,xml]
|
||||
----
|
||||
<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]
|
||||
----
|
||||
|
@ -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>
|
||||
|
@ -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 {};
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
*
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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;
|
||||
```
|
||||
|
||||
### 创建 示例中 业务所需要的数据库表
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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",证明服务发现生效。
|
||||
|
||||

|
||||
|
||||
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",证明服务发现生效。
|
||||
|
||||

|
||||
## 原理
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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
|
@ -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>
|
@ -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 + '\'' + '}';
|
||||
}
|
||||
}
|
@ -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
Loading…
x
Reference in New Issue
Block a user