diff --git a/.circleci/config.yml b/.circleci/config.yml index 8fbf11a2..1de4cc36 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,7 +24,7 @@ jobs: - ~/.m2 - run: name: "Running build" - command: ./mvnw -Pspring -Pdocs clean install -U -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn + command: ./mvnw -Pdocs clean install -U -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -Dgpg.skip - run: name: "Aggregate test results" when: always diff --git a/README-zh.md b/README-zh.md index 59804214..86538830 100644 --- a/README-zh.md +++ b/README-zh.md @@ -1,21 +1,21 @@ # Spring Cloud Alibaba [![CircleCI](https://circleci.com/gh/alibaba/spring-cloud-alibaba/tree/master.svg?style=svg)](https://circleci.com/gh/alibaba/spring-cloud-alibaba/tree/master) -[![Maven Central](https://img.shields.io/maven-central/v/org.springframework.cloud/spring-cloud-alibaba-dependencies.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:org.springframework.cloud%20AND%20a:spring-cloud-alibaba-dependencies) -[![Codecov](https://codecov.io/gh/spring-cloud-incubator/spring-cloud-alibaba/branch/master/graph/badge.svg)](https://codecov.io/gh/spring-cloud-incubator/spring-cloud-alibaba) +[![Maven Central](https://img.shields.io/maven-central/v/com.alibaba.cloud/spring-cloud-alibaba-dependencies.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:com.alibaba.cloud%20AND%20a:spring-cloud-alibaba-dependencies) +[![Codecov](https://codecov.io/gh/alibaba/spring-cloud-alibaba/branch/master/graph/badge.svg)](https://codecov.io/gh/alibaba/spring-cloud-alibaba) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。 依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。 -参考文档 请查看 [WIKI](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki) 。 +参考文档 请查看 [WIKI](https://github.com/alibaba/spring-cloud-alibaba/wiki) 。 -为 Spring Cloud Alibaba 贡献代码请参考 [如何贡献](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki/%E5%A6%82%E4%BD%95%E8%B4%A1%E7%8C%AE%E4%BB%A3%E7%A0%81) 。 +为 Spring Cloud Alibaba 贡献代码请参考 [如何贡献](https://github.com/alibaba/spring-cloud-alibaba/wiki/%E5%A6%82%E4%BD%95%E8%B4%A1%E7%8C%AE%E4%BB%A3%E7%A0%81) 。 ## 主要功能 -* **服务限流降级**:默认支持 Servlet、Feign、RestTemplate、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。 +* **服务限流降级**:默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。 * **服务注册与发现**:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。 * **分布式配置管理**:支持分布式系统中的外部化配置,配置更改时自动刷新。 * **消息驱动能力**:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。 @@ -25,7 +25,7 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。 * **阿里云短信服务**:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。 -更多功能请参考 [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap-zh.md)。 +更多功能请参考 [Roadmap](https://github.com/alibaba/spring-cloud-alibaba/blob/master/Roadmap-zh.md)。 ## 组件 @@ -47,11 +47,12 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。 **[Alibaba Cloud SMS](https://www.aliyun.com/product/sms)**: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。 -更多组件请参考 [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap-zh.md)。 +更多组件请参考 [Roadmap](https://github.com/alibaba/spring-cloud-alibaba/blob/master/Roadmap-zh.md)。 ## 如何构建 -* master 分支对应的是 Spring Cloud Finchley,最低支持 JDK 1.8。 +* master 分支对应的是 Spring Cloud Greenwich,最低支持 JDK 1.8。 +* finchley 分支对应的是 Spring Cloud Finchley,最低支持 JDK 1.8。 * 1.x 分支对应的是 Spring Cloud Edgware,最低支持 JDK 1.7。 Spring Cloud 使用 Maven 来构建,最快的使用方式是将本项目 clone 到本地,然后执行以下命令: @@ -63,17 +64,15 @@ Spring Cloud 使用 Maven 来构建,最快的使用方式是将本项目 clone ## 如何使用 ### 如何引入依赖 -项目的最新版本是 0.2.1.RELEASE 和 0.1.1.RELEASE,版本 0.2.1.RELEASE 对应的是 Spring Cloud Finchley 版本,版本 0.1.1.RELEASE 对应的是 Spring Cloud Edgware 版本。 -注意:如果默认不配置 Spring Cloud 版本,则对应的 Spring Boot 2.0.x 版本 如果需要使用已发布的版本,在 `dependencyManagement` 中添加如下配置。 - org.springframework.cloud + com.alibaba.cloud spring-cloud-alibaba-dependencies - 0.9.0.RELEASE + 2.1.0.RELEASE pom import @@ -82,53 +81,39 @@ Spring Cloud 使用 Maven 来构建,最快的使用方式是将本项目 clone 然后在 `dependencies` 中添加自己所需使用的依赖即可使用。 -如果您想体验最新的 BUILD-SNAPSHOT 的新功能,则可以将版本换成最新的版本,但是需要在 pom.xml 中配置 Spring BUILDSNAPSHOT 仓库,**注意: SNAPSHOT 版本随时可能更新** - - - - spring-snapshot - Spring Snapshot Repository - https://repo.spring.io/snapshot - - true - - - - ## 演示 Demo 为了演示如何使用,Spring Cloud Alibaba 项目包含了一个子模块`spring-cloud-alibaba-examples`。此模块中提供了演示用的 example ,您可以阅读对应的 example 工程下的 readme 文档,根据里面的步骤来体验。 Example 列表: -[Sentinel Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md) +[Sentinel Example](https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md) -[Nacos Config Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme-zh.md) +[Nacos Config Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme-zh.md) -[Nacos Discovery Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md) +[Nacos Discovery Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md) -[RocketMQ Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme-zh.md) +[RocketMQ Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme-zh.md) -[Seata Example](https://github.com/alibaba/spring-cloud-alibaba/blob/finchley/spring-cloud-alibaba-examples/seata-example/readme-zh.md) +[Seata Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/seata-example/readme-zh.md) -[Alibaba Cloud OSS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme-zh.md) +[Alibaba Cloud OSS Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme-zh.md) -[Alibaba Cloud ANS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/ans-example/ans-provider-example/readme-zh.md) +[Alibaba Cloud ANS Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/ans-example/ans-provider-example/readme-zh.md) -[Alibaba Cloud ACM Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/acm-example/acm-local-example/readme-zh.md) +[Alibaba Cloud ACM Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/acm-example/acm-local-example/readme-zh.md) -[Alibaba Cloud SchedulerX Example](https://github.com/xiaolongzuo/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/readme-zh.md) +[Alibaba Cloud SchedulerX Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/readme-zh.md) ## 版本管理规范 + 项目的版本号格式为 x.x.x 的形式,其中 x 的数值类型为数字,从 0 开始取值,且不限于 0~9 这个范围。项目处于孵化器阶段时,第一位版本号固定使用 0,即版本号为 0.x.x 的格式。 -由于 Spring Boot 1 和 Spring Boot 2 在 Actuator 模块的接口和注解有很大的变更,且 spring-cloud-commons 从 1.x.x 版本升级到 2.0.0 版本也有较大的变更,因此我们使用了两个不同分支来分别支持 Spring Boot 1 和 Spring Boot 2: -* 0.1.x 版本适用于 Spring Boot 1 -* 0.2.x 版本适用于 Spring Boot 2 - -项目孵化阶段,项目版本升级机制如下: -* 功能改动的升级会增加第三位版本号的数值,例如 0.1.0 的下一个版本为0.1.1。 +由于 Spring Boot 1 和 Spring Boot 2 在 Actuator 模块的接口和注解有很大的变更,且 spring-cloud-commons 从 1.x.x 版本升级到 2.0.0 版本也有较大的变更,因此我们采取跟 SpringBoot 版本号一致的版本: +* 1.5.x 版本适用于 Spring Boot 1.5.x +* 2.0.x 版本适用于 Spring Boot 2.0.x +* 2.1.x 版本适用于 Spring Boot 2.1.x ## 社区交流 diff --git a/README.md b/README.md index 3da7a9c3..6f69e3dc 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # Spring Cloud Alibaba [![CircleCI](https://circleci.com/gh/alibaba/spring-cloud-alibaba/tree/master.svg?style=svg)](https://circleci.com/gh/alibaba/spring-cloud-alibaba/tree/master) -[![Maven Central](https://img.shields.io/maven-central/v/org.springframework.cloud/spring-cloud-alibaba-dependencies.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:org.springframework.cloud%20AND%20a:spring-cloud-alibaba-dependencies) -[![Codecov](https://codecov.io/gh/spring-cloud-incubator/spring-cloud-alibaba/branch/master/graph/badge.svg)](https://codecov.io/gh/spring-cloud-incubator/spring-cloud-alibaba) +[![Maven Central](https://img.shields.io/maven-central/v/com.alibaba.cloud/spring-cloud-alibaba-dependencies.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:com.alibaba.cloud%20AND%20a:spring-cloud-alibaba-dependencies) +[![Codecov](https://codecov.io/gh/alibaba/spring-cloud-alibaba/branch/master/graph/badge.svg)](https://codecov.io/gh/alibaba/spring-cloud-alibaba) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) A project maintained by Alibaba. -See the [中文文档](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/README-zh.md) for Chinese readme. +See the [中文文档](https://github.com/alibaba/spring-cloud-alibaba/blob/master/README-zh.md) for Chinese readme. Spring Cloud Alibaba provides a one-stop solution for distributed application development. It contains all the components required to develop distributed applications, making it easy for you to develop your applications using Spring Cloud. @@ -25,7 +25,7 @@ With Spring Cloud Alibaba, you only need to add some annotations and a small amo * **Alibaba Cloud SchedulerX**:accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds. * **Alibaba Cloud SMS**: A messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers. -For more features, please refer to [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap.md). +For more features, please refer to [Roadmap](https://github.com/alibaba/spring-cloud-alibaba/blob/master/Roadmap.md). ## Components @@ -47,12 +47,13 @@ For more features, please refer to [Roadmap](https://github.com/spring-cloud-inc **[Alibaba Cloud SchedulerX](https://www.aliyun.com/product/SchedulerX)**:accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds.. -For more features please refer to [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap.md). +For more features please refer to [Roadmap](https://github.com/alibaba/spring-cloud-alibaba/blob/master/Roadmap.md). ## How to build -* **master branch**: Corresponds to Spring Boot 2.x. JDK 1.8 or later versions are supported. -* **1.x branch**: Corresponds to Spring Boot 1.x, JDK 1.7 or later versions are supported. +* **master branch**: Corresponds to Spring Cloud Greenwich & Spring Boot 2.x. JDK 1.8 or later versions are supported. +* **finchley branch**: Corresponds to Spring Cloud Finchley & Spring Boot 2.x. JDK 1.8 or later versions are supported. +* **1.x branch**: Corresponds to Spring Cloud Edgware & Spring Boot 1.x, JDK 1.7 or later versions are supported. Spring Cloud uses Maven for most build-related activities, and you should be able to get off the ground quite quickly by cloning the project you are interested in and typing: @@ -62,16 +63,15 @@ Spring Cloud uses Maven for most build-related activities, and you should be abl ## How to Use ### Add maven dependency -Version 0.2.1.RELEASE is compatible with the Spring Cloud Finchley. Version 0.1.1.RELEASE is compatible with the Spring Cloud Edgware. These artifacts are available from Maven Central and Spring Release repository via BOM: - org.springframework.cloud + com.alibaba.cloud spring-cloud-alibaba-dependencies - 0.9.0.RELEASE + 2.1.0.RELEASE pom import @@ -80,29 +80,16 @@ These artifacts are available from Maven Central and Spring Release repository v add the module in `dependencies`. -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-snapshot - Spring Snapshot Repository - https://repo.spring.io/snapshot - - true - - - - ### Reference Doc -[Contents](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc) +[Contents](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc) -[Nacos Config](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc) +[Nacos Config](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc) -[Nacos Discovery](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc) +[Nacos Discovery](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc) -[ACM](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc) +[ACM](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc) ## Examples @@ -111,28 +98,26 @@ A `spring-cloud-alibaba-examples` module is included in our project for you to g Examples: -[Sentinel Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md) +[Sentinel Example](https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md) -[Nacos Config Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme.md) +[Nacos Config Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/readme.md) -[Nacos Discovery Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md) +[Nacos Discovery Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme.md) -[RocketMQ Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme.md) +[RocketMQ Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme.md) -[Alibaba Cloud OSS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme.md) +[Alibaba Cloud OSS Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme.md) -[Duboo Spring Cloud Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/README_CN.md) +[Duboo Spring Cloud Example](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/README_CN.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 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 -* 0.2.x for Spring Boot 2 - -During the incubation period, the version management of the project will follow these rules: -* Functional updates will be reflected in the 3rd number of the version, for example, the next version of 0.1.0 will be 0.1.1. +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 take the same version rule as SpringBoot version number. +* 1.5.x for Spring Boot 1.5.x +* 2.0.x for Spring Boot 2.0.x +* 2.1.x for Spring Boot 2.1.x ## Code of Conduct This project is a sub-project of Spring Cloud, it adheres to the Contributor Covenant [code of conduct](https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io. diff --git a/Roadmap.md b/Roadmap.md index 32722c70..7d1f4118 100644 --- a/Roadmap.md +++ b/Roadmap.md @@ -1,6 +1,6 @@ # Roadmap -See the [中文文档](https://github.com/spring-cloud-incubator/spring-cloud-alibabacloud/blob/master/Roadmap-zh.md) for Chinese Roadmap. +See the [中文文档](https://github.com/alibaba/spring-cloud-alibaba/blob/master/Roadmap-zh.md) for Chinese Roadmap. Spring Cloud Alibaba provides a one-stop solution for distributed application development. It contains all the components required to develop distributed applications, making it easy for you to develop your applications using Spring Cloud. diff --git a/pom.xml b/pom.xml index 2bd3102b..3be13842 100644 --- a/pom.xml +++ b/pom.xml @@ -14,10 +14,11 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT pom Spring Cloud Alibaba - + Spring Cloud Alibaba + https://github.com/alibaba/spring-cloud-alibaba @@ -28,12 +29,12 @@ - https://github.com/spring-cloud-incubator/spring-cloud-alibaba + https://github.com/alibaba/spring-cloud-alibaba - scm:git:git://github.com/spring-cloud-incubator/spring-cloud-alibaba.git + scm:git:git://github.com/alibaba/spring-cloud-alibaba.git - scm:git:ssh://git@github.com/spring-cloud-incubator/spring-cloud-alibaba.git + scm:git:ssh://git@github.com/alibaba/spring-cloud-alibaba.git HEAD @@ -218,6 +219,7 @@ + spring @@ -279,6 +281,9 @@ + + + \ No newline at end of file diff --git a/spring-cloud-alibaba-coverage/pom.xml b/spring-cloud-alibaba-coverage/pom.xml index 589a50f5..456dd65d 100644 --- a/spring-cloud-alibaba-coverage/pom.xml +++ b/spring-cloud-alibaba-coverage/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 @@ -13,7 +13,7 @@ Spring Cloud Alibaba Coverage - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index 57b7ed10..6c6b85fc 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -12,13 +12,13 @@ com.alibaba.cloud spring-cloud-alibaba-dependencies - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT pom Spring Cloud Alibaba Dependencies Spring Cloud Alibaba Dependencies - 1.6.2 + 1.6.3 3.1.0 0.7.1 1.1.1 @@ -30,7 +30,7 @@ 2.44.0 2.0.2 2.1.6 - 2.7.1 + 2.7.3 2.7.1 2.7.1 1.1.0 @@ -143,6 +143,11 @@ sentinel-datasource-nacos ${sentinel.version} + + com.alibaba.csp + sentinel-datasource-redis + ${sentinel.version} + com.alibaba.csp sentinel-web-servlet @@ -205,7 +210,6 @@ - diff --git a/spring-cloud-alibaba-docs/pom.xml b/spring-cloud-alibaba-docs/pom.xml index af8b12c9..e15d9554 100644 --- a/spring-cloud-alibaba-docs/pom.xml +++ b/spring-cloud-alibaba-docs/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc index 03e90861..1bf27292 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc @@ -1,8 +1,10 @@ -== Spring Cloud Alibaba Cloud ACM +== Spring Cloud AliCloud ACM Spring Cloud AliCloud ACM 是阿里云提供的商业版应用配置管理(Application Configuration Management) 产品 在 Spring Cloud 应用侧的客户端实现,且目前完全免费。 -Spring Cloud AliCloud ACM 是 Config Server 和 Client 的替代方案,客户端和服务器上的概念与 Spring Environment 和 PropertySource 有着一致的抽象,在特殊的 bootstrap 阶段,配置被加载到 Spring 环境中。当应用程序通过部署管道从开发到测试再到生产时,您可以管理这些环境之间的配置,并确保应用程序具有迁移时需要运行的所有内容。 +使用 Spring Cloud AliCloud ACM,可基于 Spring Cloud 的编程模型快速接入 ACM 配置管理功能。 + +NOTE: 目前 EDAS 已经支持直接部署 Nacos Config 应用 === 如何引入 Spring Cloud AliCloud ACM @@ -171,4 +173,4 @@ NOTE: 如果设置了 `spring.profiles.active` ,DataId 的格式还支持 `{sp === Actuator 监控 -ACM 对应的 Actuator 监控地址为 `/acm`,其中 config 代表了 ACM 元数据配置的信息,`runtime.sources` 对应的是从 ACM 服务端获取的配置的信息及最后刷新时间, `runtime.refreshHistory` 对应的是动态刷新的历史记录。 \ No newline at end of file +ACM 对应的 Actuator 监控地址为 `/acm`,其中 `config` 代表了 ACM 元数据配置的信息,`runtime.sources` 对应的是从 ACM 服务端获取的配置的信息及最后刷新时间, `runtime.refreshHistory` 对应的是动态刷新的历史记录。 \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/ans.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/ans.adoc index 8e43cdf6..f44c1a18 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/ans.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/ans.adoc @@ -1,7 +1,9 @@ -== Spring Cloud Alibaba Cloud ANS +== Spring Cloud AliCloud ANS ANS(Application Naming Service) 是隶属于阿里云 EDAS 产品的组件, Spring Cloud AliCloud ANS 提供了 Spring Cloud 规范下商业版的服务注册与发现,可以让用户方便的在本地开发,同时也可以运行在云环境里。 +NOTE: 目前 EDAS 已经支持直接部署 Nacos Discovery 应用 + === 如何引入 Spring Cloud AliCloud ANS 如果要在您的项目中引入 ANS,使用 group ID 为 `com.alibaba.cloud` 和 artifact ID 为 `spring-cloud-starter-alicloud-ans` 的 starter。 diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc index ef5d96a4..c0c263da 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dependency-management.adoc @@ -2,17 +2,15 @@ Spring Cloud Alibaba BOM 包含了它所使用的所有依赖的版本。 -### Spring Cloud Alibaba Bill of Materials (BOM) - 如果您是 Maven Central 用户,请将我们的 BOM 添加到您的 pom.xml 中的 部分。 这将允许您省略任何Maven依赖项的版本,而是将版本控制委派给BOM。 ```xml - org.springframework.cloud + com.alibaba.cloud spring-cloud-alibaba-dependencies - 2.1.0.RELEASE + 2.1.1.BUILD-SNAPSHOT pom import @@ -20,23 +18,6 @@ Spring Cloud Alibaba BOM 包含了它所使用的所有依赖的版本。 ``` -### Spring Snapshots Maven Repository - -如果要使用最新的 BUILD-SNAPSHOT 版本,请在 pom.xml 中添加 Spring Snapshot Repository,注意:BUILD-SNAPSHOT随时可能更新: - -```xml - - - spring-snapshots - Spring SnapShots - https://repo.spring.io/libs-snapshot - - true - - - -``` - -举个例子, 0.2.0.BUILD-SNAPSHOT 版本就在这个仓库中可用。 +在下面的章节中,假设您使用的是 Spring Cloud Alibaba bom,相关 starter 依赖将不包含版本号。 diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dubbo.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dubbo.adoc new file mode 100644 index 00000000..5704475a --- /dev/null +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/dubbo.adoc @@ -0,0 +1,47 @@ +== Spring Cloud Alibaba Dubbo + +=== 简介 + +Dubbo Spring Cloud 基于 Dubbo Spring Boot 2.7.3[1] 和 Spring Cloud 2.x 开发,无论开发人员是 Dubbo 用户还是 Spring Cloud 用户, +都能轻松地驾驭,并以接近“零”成本的代价使应用向上迁移。Dubbo Spring Cloud 致力于简化 Cloud Native 开发成本,提高研发效能以及提升应用性能等目的。 + +=== 功能 + +由于 Dubbo Spring Cloud 构建在原生的 Spring Cloud 之上,其服务治理方面的能力可认为是 Spring Cloud Plus, +不仅完全覆盖 Spring Cloud 原生特性[5],而且提供更为稳定和成熟的实现,特性比对如下表所示: + +:frame: topbot +[width="60%",options="header"] +|==== +^|功能组件 ^|Spring Cloud ^|Dubbo Spring Cloud +| 分布式配置(Distributed configuration) | Git、Zookeeper、Consul、JDBC | Spring Cloud 分布式配置 + Dubbo 配置中心[6] +| 服务注册与发现(Service registration and discovery) | Eureka、Zookeeper、Consul | Spring Cloud 原生注册中心[7] + Dubbo 原生注册中心[8] +| 负载均衡(Load balancing) | Ribbon(随机、轮询等算法) | Dubbo 内建实现(随机、轮询等算法 + 权重等特性) +| 服务熔断(Circuit Breakers) | Spring Cloud Hystrix | Spring Cloud Hystrix + Alibaba Sentinel[9] 等 +| 服务调用(Service-to-service calls) | Open Feign、`RestTemplate` | Spring Cloud 服务调用 + Dubbo `@Reference` +| 链路跟踪(Tracing) | Spring Cloud Sleuth[10] + Zipkin[11] | Zipkin、opentracing 等 +|==== + +=== Reference 说明 + +[1]: 从 2.7.0 开始,Dubbo Spring Boot 与 Dubbo 在版本上保持一致 + +[2]: Preview releases of Spring Cloud Alibaba are available: 0.9.0, 0.2.2, and 0.1.2 - https://spring.io/blog/2011/04/11/preview-releases-of-spring-cloud-alibaba-are-available-0-9-0-0-2-2-and-0-1-2 + +[3]: 目前最新的 Spring Cloud “F” 版的版本为:`Finchley.SR2` - https://cloud.spring.io/spring-cloud-static/Finchley.SR2/single/spring-cloud.html + +[4]: 当前Spring Cloud “G” 版为 `Greenwich.RELEASE` + +[5]: Spring Cloud 特性列表 - https://cloud.spring.io/spring-cloud-static/Greenwich.RELEASE/single/spring-cloud.html#_features + +[6]: Dubbo 2.7 开始支持配置中心,可自定义适配 - http://dubbo.apache.org/zh-cn/docs/user/configuration/config-center.html + +[7]: Spring Cloud 原生注册中心,除 Eureka、Zookeeper、Consul 之外,还包括 Spring Cloud Alibaba 中的 Nacos + +[8]: Dubbo 原生注册中心 - http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html + +[9]: Alibaba Sentinel:Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性 - https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D ,目前 Sentinel 已被 Spring Cloud 项目纳为 Circuit Breaker 的候选实现 - https://spring.io/blog/2011/04/8/introducing-spring-cloud-circuit-breaker + +[10]:Spring Cloud Sleuth - https://spring.io/projects/spring-cloud-sleuth + +[11]: Zipkin - https://github.com/apache/incubator-zipkin \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/introduction.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/introduction.adoc new file mode 100644 index 00000000..06d53cc6 --- /dev/null +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/introduction.adoc @@ -0,0 +1,19 @@ +## 介绍 + +Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。 + +依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里分布式应用解决方案,通过阿里中间件来迅速搭建分布式应用系统。 + +目前 Spring Cloud Alibaba 提供了如下功能: + +1. **服务限流降级**:支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Dubbo 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。 +2. **服务注册与发现**:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。 +3. **分布式配置管理**:支持分布式系统中的外部化配置,配置更改时自动刷新。 +4. **Rpc服务**:扩展 Spring Cloud 客户端 RestTemplate 和 OpenFeign,支持调用 Dubbo RPC 服务 +5. **消息驱动能力**:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。 +6. **分布式事务**:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。 +7. **阿里云对象存储**:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。 +8. **分布式任务调度**:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker(schedulerx-client)上执行。 +9. **阿里云短信服务**:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。 + +Spring Cloud Alibaba 也提供了丰富的 https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples[examples]。 \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config-custom-solution.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config-custom-solution.adoc deleted file mode 100644 index 57987af9..00000000 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config-custom-solution.adoc +++ /dev/null @@ -1,116 +0,0 @@ -= SCA Nacos Config 共享配置方案设计 - -随着社区的回馈,发现 Spring Cloud Alibaba Nacos Config Starter 目前不能完美的来支持多个应用间的一些共享配置。 -在实际的业务场景中应用和共享配置间的关系可能如下图所示: - -image::http://edas.oss-cn-hangzhou.aliyuncs.com/sca/sca_shared_01.png[] - -* 从单个应用的角度来看: 应用可能会有多套(develop/beta/product)发布环境,多套发布环境之间有不同的基础配置,例如数据库。 -* 从多个应用的角度来看:多个应用间可能会有一些共享通用的配置,比如多个应用之间共用一套zookeeper集群。 - -目前 SCA Nacos Config 可以灵活的支持单个应用间在多套环境可灵活的切换,但是在多个应用间共享一些通用的配置支持的还不是很完美。 -本方案设计的目标就是来解决这个问题。目前有三种设计方案,分别是: - -* 新增一个应用分组的配置,分组命名的格式是 通过域名的命名方式,来自动的生成共享配置的 Data Id。 -* 自定义命名的方式来命名 Data Id 。 -* 通过类似面向对象的方式自定义配置(相对于第二种的升级版)。 - -下面分别来分析三种方案的具体实现和优缺点。 - -== 基于域名的配置方式给应用分组 - -通过一个配置参数(例如:${spring.application.group}) 来指明当前应用所属的分组(或者说所属的域)。 -例如我有两个应用分别为Order_Application和Auth_Application,给这两个应用配置的分组名(域)是: - -.bootstrap.properties ----- -spring.application.group=com.alibaba.aliware.edas ----- - -那对于Spring Cloud Alibaba Nacos Config 来说,多个应用可以属于 com.alibaba 这个应用分组(域),也可以属于 -com.alibaba.aliware 这个应用分组(域),当然也可以属于 com.alibaba.aliware.edas 这个应用分组(域)。 -罗里吧嗦了这么多,目的就是 Data Id 通过以这个分组(域)来命名,从而实现多个应用间在某个分组(域)下的共享配置。如下图所示: - -image::http://edas.oss-cn-hangzhou.aliyuncs.com/sca/sca_shared_02.png[] - -以这种方式来实现多个应用间的配置共享,可以看出他具有天然的局限性。 - -* 受到 ${spring.application.group }配置的影响,Data Id 的表现力是非常有限的(当然85%的场景应该够用了)。 - ** 一方面是Data Id 的命名个数受到了限制。 - ** 另一方面如果两个应用的 file-extension 不一致(一个是properties,一个是yaml),那这个时候共享配置的Data Id 必须同时要含有 properties 和 yaml 为扩展名的配置。 - ** Data Id 的命名也受到了 ${spring.application.group } 配置的束缚。 - -* 学习成本偏高(当然还是可以学会的)。学习成本偏高体现在: - ** 要知道Data Id 的命名规则,才能在项目实施过程中对 Data Id 的命名运用自如。 - ** 此外对于域名命名的层次个数也不太好把握。少了的话,担心dataid的个数不够用,多了的话看上去有显得的比较冗余。 - ** 还需要学习并理解这里配置的优先级,不然在程序中有可能就会拿到意想不到的配置。 - -* 易出错(当然是可以克服的)。对于多级应用分组的配置共享,这个时候Data Id 的命名要格外注意了。注意他们的层次关系,Data Id 书写时不要张冠李戴。 - -* 实现起来稍微复杂 - -当然他的好处也非常明显,当你理解了他背后的设计理念时,这个共享配置的层次也非常明显。因为层次的关系天然依托于域名的层次关系。 - -== 自定义的方式来命名 Data Id - -这种方式实现简单易懂,即 SCA Nacos Config 会新增加一个配置,用来配置可实现共享配置所有的 Data Id。如下所示: - -.bootstrap.properties ----- -spring.cloud.nacos.shared.dataids=global.yaml,app-common.yaml,app-local-common.yaml ----- - -NOTE: 为了尽可能的和Nacos使用方式(即Data Id 是一个带有额外文件扩展名的)保持一致,这里配置的Data Id 是一定需要带上文件扩展名的。 - -这个时候两个应用(或多个应用)之间共享配置的 Data Id 关系如下图所示: - -image::http://edas.oss-cn-hangzhou.aliyuncs.com/sca/sca_shared_03.png[] - -Spring Boot 提倡约定大于配置。当使用这种方式来实现应用间的共享配置时,我们也继承了Spring Boot的这个优良传统,多个共享配置间的一个优先级的关系我们约定:按照配置出现的先后顺序,即后面的优先级要高于前面的。 -这种方式的优点在于: - -* dataid的命名方式完全交给业务方本身,不受 SCA Nacos Config Starter 实现的束缚。 -* dataid的命名方式既可以参考第一种方式来命名,又可以充分的发挥主观能动性,结合自己实际的业务给dataid命名。 -* 减少了多个应用间如果file-extension不一致,为每个 file-extension 多加这么一个配置的麻烦。 -* 当使用这种方式时,不会为这些共享配置强制绑定一个 file-extenson,即可以直接在我们暴露出来的一个变量中 dataid以file-extension 结尾。如果没有显示的说明,这个时候就会以file-extenson为准。 - -当然这种方案的缺点在于扩展性不强。即如果对于某个共享配置需要做额外的配置,例如额外配置Group/是否需要刷新/是否需要从本地缓存加载等等。因此为了应对这种类型的场景,小组内讨论出了第三种方案。 - -== 通过类似面向对象方式的自定义配置 - -说明:Spring 可以支持在加了 ConfigurationProperties 注解配置类的内部某个对象实例来注入应用中的一些配置。 -这种使用方式查了一下官方和网络上没有一个大的标题总结,结合这种方式很像给某个实例中的字段赋值,所以这里先暂时取名: -类似面向对象方式的自定义配置(有更好能够形象的标明其含义的命名可以在下面留下评论-_-)。 - -这种方案沿用了第二种设计方案的优点,同时又弥补了第二种方案的不足。我们通过内部定义一个对象,来支持一些灵活的扩展配置。 -我们给这个对象可以预留一些可扩展的配置字段。例如: - -.Config.java ----- -public class Config{ - private String dataId; - private String group = "DEFAULT_GROUP"; - private Boolean refresh = false; - //.....后期可能还有其他的一些配置 - - //省略 set/get 方法 -} ----- - -最终在实现时是可以支持以list的方式来配置其值。如下是两个扩展配置的实例: - -.bootstrap.properties ----- -spring.cloud.nacos.config.ext-config[0].data-id=global-shared.properties # group 和referesh 使用默认值 -spring.cloud.nacos.config.ext-config[1].data-id=app-common.properties -spring.cloud.nacos.config.ext-config[1].group=DEVELOP_GROUP #配置自定义所在的组 -spring.cloud.nacos.config.ext-config[1].refresh=true #需要刷新 ----- - -NOTE: 为了尽可能的和Nacos使用方式(即data id是一个带有额外文件扩展名的)保持一致,这里配置的dataid是一定需要带上文件扩展名的。 - -== 最终的实现 - -SCA Nacos Config 在第二种方案和第三种方案的实现上是并存的。如果你觉得第三种方案配置的比较麻烦,同时第二种方案就可以满足你的需求,这个时候就可以选择第二种方案。 -如果你需外可读性好、层级感比较明显、后期的扩展性更强,那这个时候第三种方案也是OK的。 - diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc index dca74840..1b953eef 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc @@ -1,16 +1,35 @@ == Spring Cloud Alibaba Nacos Config -Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理你 Spring Cloud 应用的外部属性配置。 +Nacos 是一个 Alibaba 开源的、易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 -Spring Cloud Alibaba Nacos Config 是 Config Server 和 Client 的替代方案,客户端和服务器上的概念与 Spring Environment 和 PropertySource 有着一致的抽象,在特殊的 bootstrap 阶段,配置被加载到 Spring 环境中。当应用程序通过部署管道从开发到测试再到生产时,您可以管理这些环境之间的配置,并确保应用程序具有迁移时需要运行的所有内容。 +使用 Spring Cloud Alibaba Nacos Config,可基于 Spring Cloud 的编程模型快速接入 Nacos 配置管理功能。 + +=== 如何引入 Nacos Config 进行配置管理 + +如果要在您的项目中使用 Nacos 来实现配置管理,使用 group ID 为 `com.alibaba.cloud` 和 artifact ID 为 `spring-cloud-starter-alibaba-nacos-config` 的 starter。 + +[source,xml,indent=0] +---- + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + +---- === 快速开始 -===== Nacos 服务端初始化 +Nacos Config 使用 DataId 和 GROUP 确定一个配置。 -1、启动Nacos Server。启动方式可见 https://nacos.io/zh-cn/docs/quick-start.html[Nacos 官网] +下图表示 DataId 使用 `myDataid`, GROUP 使用 `DEFAULT_GROUP`,配置格式为 Properties 的一个配置项: -2、启动好Nacos之后,在Nacos添加如下的配置: +.Nacos Config Item +image::https://img.alicdn.com/tfs/TB1Yli3bUY1gK0jSZFMXXaWcVXa-2436-1138.png[] + +==== Nacos 服务端初始化 + +具体启动方式参考 Spring Cloud Alibaba Nacos Discovery 小节的 "Nacos Server 启动" 章节。 + +Nacos Server 启动完毕后,添加如何配置: [source,subs="normal"] ---- @@ -24,9 +43,9 @@ Group : DEFAULT_GROUP user.age=90 ---- -NOTE: 注意dataid是以 properties(默认的文件扩展名方式)为扩展名。 +NOTE: 注意DataId是以 properties(默认的文件扩展名方式)为扩展名。 -===== 客户端使用方式 +==== 客户端使用方式 如果要在您的项目中使用 Nacos 来实现应用的外部化配置,使用 group ID 为 `com.alibaba.cloud` 和 artifact ID 为 `spring-cloud-starter-alibaba-nacos-config` 的 starter。 @@ -38,15 +57,15 @@ NOTE: 注意dataid是以 properties(默认的文件扩展名方式)为扩展名 ---- -现在就可以创建一个标准的 Spring Boot 应用。 +现在创建一个标准的 Spring Boot 应用。 [source,java] ---- @SpringBootApplication -public class ProviderApplication { +public class NacosConfigApplication { public static void main(String[] args) { - ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args); + ConfigurableApplicationContext applicationContext = SpringApplication.run(ConfigApplication.class, args); String userName = applicationContext.getEnvironment().getProperty("user.name"); String userAge = applicationContext.getEnvironment().getProperty("user.age"); System.err.println("user name :"+userName+"; age: "+userAge); @@ -54,11 +73,12 @@ public class ProviderApplication { } ---- -在运行此 Example 之前, 必须使用 bootstrap.properties 配置文件来配置Nacos Server 地址,例如: +在运行此 NacosConfigApplication 之前, 必须使用 `bootstrap.properties` 配置文件来配置 Nacos Server 地址,例如: .bootstrap.properties [source,properties] ---- +# DataId 默认使用 `spring.application.name` 配置跟文件扩展名结合(配置格式默认使用 properties), GROUP 不配置默认使用 DEFAULT_GROUP。因此该配置文件对应的 Nacos Config 配置的 DataId 为 nacos-config.properties, GROUP 为 DEFAULT_GROUP spring.application.name=nacos-config spring.cloud.nacos.config.server-addr=127.0.0.1:8848 ---- @@ -71,17 +91,16 @@ NOTE: 注意当你使用域名的方式来访问 Nacos 时,`spring.cloud.nacos [source,subs="normal"] ---- -2018-11-02 14:24:51.638 INFO 32700 --- [main] c.a.demo.provider.ProviderApplication : Started ProviderApplication in 14.645 seconds (JVM running for 15.139) +2018-11-02 14:24:51.638 INFO 32700 --- [main] c.a.demo.provider.ConfigApplication : Started ConfigApplication in 14.645 seconds (JVM running for 15.139) user name :nacos-config-properties; age: 90 2018-11-02 14:24:51.688 INFO 32700 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@a8c5e74: startup date [Fri Nov 02 14:24:51 CST 2018]; root of context hierarchy -2018-11 ---- -=== 基于 dataid 为 yaml 的文件扩展名配置方式 +=== 基于 DataId 为 yaml 的文件扩展名配置方式 -spring-cloud-starter-alibaba-nacos-config 对于 yaml 格式也是完美支持的。这个时候只需要完成以下两步: +Nacos Config 除了支持 properties 格式以外,也支持 yaml 格式。这个时候只需要完成以下两步: -1、在应用的 bootstrap.properties 配置文件中显示的声明 dataid 文件扩展名。如下所示 +1、在应用的 bootstrap.properties 配置文件中显示的声明 DataId 文件扩展名。如下所示 .bootstrap.properties [source,yaml] @@ -89,7 +108,7 @@ spring-cloud-starter-alibaba-nacos-config 对于 yaml 格式也是完美支持 spring.cloud.nacos.config.file-extension=yaml ---- -2、在 Nacos 的控制台新增一个dataid为yaml为扩展名的配置,如下所示: +2、在 Nacos 的控制台新增一个DataId为yaml为扩展名的配置,如下所示: [source,subs="normal"] ---- @@ -107,22 +126,22 @@ Group : DEFAULT_GROUP [source,subs="normal"] ---- -2018-11-02 14:59:00.484 INFO 32928 --- [main] c.a.demo.provider.ProviderApplication:Started ProviderApplication in 14.183 seconds (JVM running for 14.671) +2018-11-02 14:59:00.484 INFO 32928 --- [main] c.a.demo.provider.ConfigApplication:Started ConfigApplication in 14.183 seconds (JVM running for 14.671) user name :nacos-config-yaml; age: 68 2018-11-02 14:59:00.529 INFO 32928 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@265a478e: startup date [Fri Nov 02 14:59:00 CST 2018]; root of context hierarchy ---- === 支持配置的动态更新 -spring-cloud-starter-alibaba-nacos-config 也支持配置的动态更新,启动 Spring Boot 应用测试的代码如下: +Nacos Config 默认支持配置的动态更新,启动 Spring Boot 应用测试的代码如下: [source,java] ---- @SpringBootApplication -public class ProviderApplication { +public class ConfigApplication { public static void main(String[] args) { - ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args); + ConfigurableApplicationContext applicationContext = SpringApplication.run(ConfigApplication.class, args); while(true) { //当动态配置刷新时,会更新到 Enviroment中,因此这里每隔一秒中从Enviroment中获取配置 String userName = applicationContext.getEnvironment().getProperty("user.name"); @@ -151,9 +170,9 @@ user name :nacos-config-yaml-update; age: 68 NOTE: 你可以通过配置 `spring.cloud.nacos.config.refresh.enabled=false` 来关闭动态刷新 -=== 可支持profile粒度的配置 +=== 支持profile粒度的配置 -spring-cloud-starter-alibaba-nacos-config 在加载配置的时候,不仅仅加载了以 dataid 为 `${spring.application.name}.${file-extension:properties}` 为前缀的基础配置,还加载了dataid为 `${spring.application.name}-${profile}.${file-extension:properties}` 的基础配置。在日常开发中如果遇到多套环境下的不同配置,可以通过Spring 提供的 `${spring.profiles.active}` 这个配置项来配置。 +Nacos Config 在加载配置的时候,不仅仅加载了以 DataId 为 `${spring.application.name}.${file-extension:properties}` 为前缀的基础配置,还加载了DataId为 `${spring.application.name}-${profile}.${file-extension:properties}` 的基础配置。在日常开发中如果遇到多套环境下的不同配置,可以通过Spring 提供的 `${spring.profiles.active}` 这个配置项来配置。 [source,properties] ---- @@ -162,7 +181,7 @@ spring.profiles.active=develop NOTE: ${spring.profiles.active} 当通过配置文件来指定时必须放在 bootstrap.properties 文件中。 -Nacos 上新增一个dataid为:nacos-config-develop.yaml的基础配置,如下所示: +Nacos 上新增一个DataId为:nacos-config-develop.yaml的基础配置,如下所示: [source,subs="normal"] ---- @@ -180,10 +199,10 @@ Group : DEFAULT_GROUP [source,java] ---- @SpringBootApplication -public class ProviderApplication { +public class ConfigApplication { public static void main(String[] args) { - ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args); + ConfigurableApplicationContext applicationContext = SpringApplication.run(ConfigApplication.class, args); while(true) { String userName = applicationContext.getEnvironment().getProperty("user.name"); String userAge = applicationContext.getEnvironment().getProperty("user.age"); @@ -210,7 +229,7 @@ in develop-env enviroment; user name :nacos-config-yaml-update; age: 68 spring.profiles.active=product ---- -同时生产环境上 Nacos 需要添加对应 dataid 的基础配置。例如,在生成环境下的 Naocs 添加了dataid为:nacos-config-product.yaml的配置: +同时生产环境上 Nacos 需要添加对应 DataId 的基础配置。例如,在生成环境下的 Naocs 添加了DataId为:nacos-config-product.yaml的配置: [source,subs="normal"] ---- @@ -235,7 +254,8 @@ in product-env enviroment; user name :nacos-config-yaml-update; age: 68 NOTE: 此案例中我们通过 `spring.profiles.active=` 的方式写死在配置文件中,而在真正的项目实施过程中这个变量的值是需要不同环境而有不同的值。这个时候通常的做法是通过 `-Dspring.profiles.active=` 参数指定其配置来达到环境间灵活的切换。 === 支持自定义 namespace 的配置 -首先看一下 Nacos 的 Namespace 的概念, https://nacos.io/zh-cn/docs/concepts.html[Nacos 概念] + +Nacos 内部有 https://nacos.io/zh-cn/docs/concepts.html[Namespace 的概念]: [quote] 用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。 @@ -261,7 +281,7 @@ NOTE: 该配置必须放在 bootstrap.properties 文件中。并且在添加配 === 支持自定义扩展的 Data Id 配置 -Spring Cloud Alibaba Nacos Config 从 0.2.1 版本后,可支持自定义 Data Id 的配置。关于这部分详细的设计可参考 https://github.com/spring-cloud-incubator/spring-cloud-alibaba/issues/141[这里]。 +Nacos Config 从 0.2.1 版本后,可支持自定义 Data Id 的配置。关于这部分详细的设计可参考 https://github.com/spring-cloud-incubator/spring-cloud-alibaba/issues/141[这里]。 一个完整的配置案例如下所示: [source,properties] @@ -322,10 +342,94 @@ NOTE: `spring.cloud.nacos.config.refreshable-dataids` 给出哪些需要支持 === 配置的优先级 -Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置。 +Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置 * A: 通过 `spring.cloud.nacos.config.shared-dataids` 支持多个共享 Data Id 的配置 * B: 通过 `spring.cloud.nacos.config.ext-config[n].data-id` 的方式支持多个扩展 Data Id 的配置 * C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置 -当三种方式共同使用时,他们的一个优先级关系是:A < B < C \ No newline at end of file +当三种方式共同使用时,他们的一个优先级关系是:A < B < C + +=== Nacos Config 对外暴露的 Endpoint + +Nacos Config 内部提供了一个 Endpoint, 对应的 endpoint id 为 `nacos-config`。 + +Endpoint 暴露的 json 中包含了三种属性: + +1. Sources: 当前应用配置的数据信息 + +2. RefreshHistory: 配置刷新的历史记录 + +3. NacosConfigProperties: 当前应用 Nacos 的基础配置信息 + +这是 Endpoint 暴露的 json 示例: + +[source,json,indent=0] +---- +{ + "NacosConfigProperties": { + "serverAddr": "127.0.0.1:8848", + "encode": null, + "group": "DEFAULT_GROUP", + "prefix": null, + "fileExtension": "properties", + "timeout": 3000, + "endpoint": null, + "namespace": null, + "accessKey": null, + "secretKey": null, + "contextPath": null, + "clusterName": null, + "name": null, + "sharedDataids": "base-common.properties,common.properties", + "refreshableDataids": "common.properties", + "extConfig": null + }, + "RefreshHistory": [{ + "timestamp": "2019-07-29 11:20:04", + "dataId": "nacos-config-example.properties", + "md5": "7d5d7f1051ff6571e2ec9f90887d9d91" + }], + "Sources": [{ + "lastSynced": "2019-07-29 11:19:04", + "dataId": "common.properties" + }, { + "lastSynced": "2019-07-29 11:19:04", + "dataId": "base-common.properties" + }, { + "lastSynced": "2019-07-29 11:19:04", + "dataId": "nacos-config-example.properties" + }] +} +---- + + +=== 完全关闭 Nacos Config 的自动化配置 + +通过设置 spring.cloud.nacos.config.enabled = false 来完全关闭 Spring Cloud Nacos Config + +=== 关于 Nacos Config Starter 更多的配置项信息 + +更多关于 Nacos Config Starter 的配置项如下所示: + +:frame: topbot +[width="60%",options="header"] +|==== +^|配置项 ^|Key ^|默认值 ^|说明 +|服务端地址|`spring.cloud.nacos.config.server-addr`|| Nacos Server 启动监听的ip地址和端口 +|配置对应的 DataId|`spring.cloud.nacos.config.name`|| 先取 prefix,再去 name,最后取 spring.application.name +|配置对应的 DataId|`spring.cloud.nacos.config.prefix`|| 先取 prefix,再去 name,最后取 spring.application.name +|配置内容编码|`spring.cloud.nacos.config.encode`||读取的配置内容对应的编码 +|GROUP|`spring.cloud.nacos.config.group`|`DEFAULT_GROUP`|配置对应的组 +|文件扩展名|`spring.cloud.nacos.config.fileExtension`|`properties`|配置项对应的文件扩展名,目前支持 properties 和 yaml(yml) +|获取配置超时时间|`spring.cloud.nacos.config.timeout`|`3000`|客户端获取配置的超时时间(毫秒) +|接入点|`spring.cloud.nacos.config.endpoint`||地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址 +|命名空间|`spring.cloud.nacos.config.namespace`||常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等 +|AccessKey|`spring.cloud.nacos.config.accessKey`||当要上阿里云时,阿里云上面的一个云账号名 +|SecretKey|`spring.cloud.nacos.config.secretKey`||当要上阿里云时,阿里云上面的一个云账号密码 +|Nacos Server 对应的 context path|`spring.cloud.nacos.config.contextPath`||Nacos Server 对外暴露的 context path +|集群|`spring.cloud.nacos.config.clusterName`||配置成Nacos集群名称 +|共享配置|`spring.cloud.nacos.config.sharedDataids`||共享配置的 DataId, "," 分割 +|共享配置动态刷新|`spring.cloud.nacos.config.refreshableDataids`||共享配置中需要动态刷新的 DataId, "," 分割 +|自定义 Data Id 配置|`spring.cloud.nacos.config.extConfig`||属性是个集合,内部由 `Config` POJO 组成。`Config` 有 3 个属性,分别是 `dataId`, `group` 以及 `refresh` +|==== diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc index 825b6694..dba2de5d 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-discovery.adoc @@ -1,17 +1,18 @@ == Spring Cloud Alibaba Nacos Discovery -该项目通过自动配置以及其他 Spring 编程模型的习惯用法为 Spring Boot 应用程序在服务注册与发现方面提供和 Nacos 的无缝集成。 -通过一些简单的注解,您可以快速来注册一个服务,并使用经过双十一考验的 Nacos 组件来作为大规模分布式系统的服务注册中心。 +Nacos 是一个 Alibaba 开源的、易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 -=== 服务注册发现: Nacos Discovery Starter +使用 Spring Cloud Alibaba Nacos Discovery,可基于 Spring Cloud 的编程模型快速接入 Nacos 服务注册功能。 + +=== 服务注册/发现: Nacos Discovery 服务发现是微服务架构体系中最关键的组件之一。如果尝试着用手动的方式来给每一个客户端来配置所有服务提供者的服务列表是一件非常困难的事,而且也不利于 -服务的动态扩缩容。Nacos Discovery Starter 可以帮助您将服务自动注册到 Nacos 服务端并且能够动态感知和刷新某个服务实例的服务列表。除此之外,Nacos -Discovery Starter 也将服务实例自身的一些元数据信息-例如 host,port,健康检查URL,主页等-注册到 Nacos 。Nacos 的获取和启动方式可以参考 https://nacos.io/zh-cn/docs/quick-start.html[Nacos 官网]。 +服务的动态扩缩容。Nacos Discovery 可以帮助您将服务自动注册到 Nacos 服务端并且能够动态感知和刷新某个服务实例的服务列表。除此之外,Nacos +Discovery 也将服务实例自身的一些元数据信息-例如 host,port, 健康检查URL,主页等内容注册到 Nacos。Nacos 的获取和启动方式可以参考 https://nacos.io/zh-cn/docs/quick-start.html[Nacos 官网]。 -==== 如何引入 Nacos Discovery Starter +=== 如何引入 Nacos Discovery 进行服务注册/发现 -如果要在您的项目中使用 Nacos 来实现服务发现,使用 group ID 为 `com.alibaba.cloud` 和 artifact ID 为 `spring-cloud-starter-alibaba-nacos-discovery` 的 starter。 +如果要在您的项目中使用 Nacos 来实现服务注册/发现,使用 group ID 为 `com.alibaba.cloud` 和 artifact ID 为 `spring-cloud-starter-alibaba-nacos-discovery` 的 starter。 [source,xml,indent=0] ---- @@ -21,12 +22,27 @@ Discovery Starter 也将服务实例自身的一些元数据信息-例如 host ---- -==== 启动一个 Provider 应用 +=== 一个使用 Nacos Discovery 进行服务注册/发现并调用的例子 + +Nacos Discovery 适配了 Netflix Ribbon,可以使用 RestTemplate 或 OpenFeign 进行服务的调用。 + +==== Nacos Server 启动 + +具体启动方式参考 https://nacos.io/zh-cn/docs/quick-start.html[Nacos 官网]。 + +Nacos Server 启动后,进入 http://ip:8848 查看控制台(默认账号名/密码为 nacos/nacos): + +.Nacos Dashboard +image::https://img.alicdn.com/tfs/TB1dyWJbQL0gK0jSZFtXXXQCXXa-2788-1086.png[] + +关于更多的 Nacos Server 版本,可以从 https://github.com/alibaba/nacos/releases[release 页面] 下载最新的版本。 + +==== Provider 应用 以下步骤向您展示了如何将一个服务注册到 Nacos。 -1. pom.xml的配置。一个完整的 pom.xml 配置如下所示: -[source, xml] +* pom.xml的配置。一个完整的 pom.xml 配置如下所示: +[source,xml,indent=0] ---- import - org.springframework.cloud + com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom @@ -98,12 +114,12 @@ Discovery Starter 也将服务实例自身的一些元数据信息-例如 host ---- -2. application.properties 配置。一些关于 Nacos 基本的配置也必须在 application.properties(也可以是application.yaml)配置,如下所示: +* application.properties 配置。一些关于 Nacos 基本的配置也必须在 application.properties(也可以是application.yaml)配置,如下所示: application.properties -[source,properties] +[source,properties,indent=0] ---- server.port=8081 -spring.application.name=nacos-producer +spring.application.name=nacos-provider spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 management.endpoints.web.exposure.include=* ---- @@ -111,7 +127,7 @@ management.endpoints.web.exposure.include=* NOTE: 如果不想使用 Nacos 作为您的服务注册与发现,可以将 `spring.cloud.nacos.discovery` 设置为 `false`。 -3. 启动 Provider 示例。如下所示: +* 启动 Provider 示例。如下所示: [source,java,indent=0] ---- @SpringBootApplication @@ -119,7 +135,7 @@ NOTE: 如果不想使用 Nacos 作为您的服务注册与发现,可以将 `sp public class NacosProviderDemoApplication { public static void main(String[] args) { - SpringApplication.run(NacosProducerDemoApplication.class, args); + SpringApplication.run(NacosProviderDemoApplication.class, args); } @RestController @@ -134,19 +150,79 @@ public class NacosProviderDemoApplication { 这个时候你就可以在 Nacos的控制台上看到注册上来的服务信息了。 -NOTE: 再启动 Provider 应用之前 请先将 Nacos 服务启动。具体启动方式可参考 https://nacos.io/zh-cn/docs/quick-start.html[Nacos 官网]。 +==== Consumer 应用 -=== 服务的 EndPoint +Consumer 应用可能还没像启动一个 Provider 应用那么简单。因为在 Consumer 端需要去调用 Provider 端提供的REST 服务。例子中我们使用最原始的一种方式, +即显示的使用 LoadBalanceClient 和 RestTemplate 结合的方式来访问。 +pom.xml 和 application.properties 的配置可以参考 1.2 小结。启动一个 Consumer应用的示例代码如下所示: -spring-cloud-starter-alibaba-nacos-discovery 在实现的时候提供了一个EndPoint,EndPoint的访问地址为 `http://ip:port/actuator/nacos-discovery`。 -EndPoint 的信息主要提供了两类: +NOTE: 通过带有负载均衡的RestTemplate 和 FeignClient 也是可以访问的。 - 1、subscribe: 显示了当前有哪些服务订阅者 - 2、NacosDiscoveryProperties: 显示了当前服务实例关于 Nacos 的基础配置 +[source,java,indent=0] +---- +@SpringBootApplication +@EnableDiscoveryClient +public class NacosConsumerApp { -一个服务实例访问 EndPoint 的信息如下所示: + @RestController + public class NacosController{ -[source, json] + @Autowired + private LoadBalancerClient loadBalancerClient; + @Autowired + private RestTemplate restTemplate; + + @Value("${spring.application.name}") + private String appName; + + @GetMapping("/echo/app-name") + public String echoAppName(){ + //使用 LoadBalanceClient 和 RestTemplate 结合的方式来访问 + ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-provider"); + String url = String.format("http://%s:%s/echo/%s",serviceInstance.getHost(),serviceInstance.getPort(),appName); + System.out.println("request url:"+url); + return restTemplate.getForObject(url,String.class); + } + + } + + //实例化 RestTemplate 实例 + @Bean + public RestTemplate restTemplate(){ + + return new RestTemplate(); + } + + public static void main(String[] args) { + + SpringApplication.run(NacosConsumerApp.class,args); + } +} +---- + +这个例子中我们注入了一个 LoadBalancerClient 的实例,并且手动的实例化一个 RestTemplate,同时将 `spring.application.name` 的配置值 注入到应用中来, +目的是调用 Provider 提供的服务时,希望将当前配置的应用名给显示出来。 + +NOTE: 在启动 Consumer 应用之前请先将 Nacos 服务启动好。具体启动方式可参考 https://nacos.io/zh-cn/docs/quick-start.html[Nacos 官网]。 + +启动后,访问 Consumer 提供出来的 `http://ip:port/echo/app-name` 接口。我这里测试启动的 port是 8082。访问结果如下所示: + + 访问地址:http://127.0.0.1:8082/echo/app-name + 访问结果:Hello Nacos Discovery nacos-consumer + +=== Nacos Discovery 对外暴露的 Endpoint + +Nacos Discovery 内部提供了一个 Endpoint, 对应的 endpoint id 为 `nacos-discovery`。 + +Endpoint 暴露的 json 中包含了两种属性: + +1. subscribe: 显示了当前服务有哪些服务订阅者 + +2. NacosDiscoveryProperties: 当前应用 Nacos 的基础配置信息 + +这是 Endpoint 暴露的 json 示例: + +[source,json,indent=0] ---- { "subscribe": [ @@ -185,7 +261,7 @@ EndPoint 的信息主要提供了两类: "lastRefTime": 1541755293119, "checksum": "e5a699c9201f5328241c178e804657e11541755293119", "allIPs": false, - "key": "nacos-producer", + "key": "nacos-provider", "valid": true } ], @@ -211,87 +287,28 @@ EndPoint 的信息主要提供了两类: } ---- -=== 启动一个 Consumer 应用 +=== 关于 Nacos Discovery Starter 更多的配置项信息 -Consumer 的应用可能还没像启动一个 Provider 应用那么简单。因为在 Consumer 端需要去调用 Provider 端提供的REST 服务。例子中我们使用最原始的一种方式, -即显示的使用 LoadBalanceClient 和 RestTemolate 结合的方式来访问。 -pom.xml 和 application.properties 的配置可以参考 1.2 小结。启动一个 Consumer应用的示例代码如下所示: - -NOTE: 通过带有负载均衡的RestTemplate 和 FeignClient 也是可以访问的。 - -[source, java] ----- -@SpringBootApplication -@EnableDiscoveryClient -public class NacosConsumerApp { - - @RestController - public class NacosController{ - - @Autowired - private LoadBalancerClient loadBalancerClient; - @Autowired - private RestTemplate restTemplate; - - @Value("${spring.application.name}") - private String appName; - - @GetMapping("/echo/app-name") - public String echoAppName(){ - //使用 LoadBalanceClient 和 RestTemolate 结合的方式来访问 - ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-provider"); - String url = String.format("http://%s:%s/echo/%s",serviceInstance.getHost(),serviceInstance.getPort(),appName); - System.out.println("request url:"+url); - return restTemplate.getForObject(url,String.class); - } - - } - - //实例化 RestTemplate 实例 - @Bean - public RestTemplate restTemplate(){ - - return new RestTemplate(); - } - - public static void main(String[] args) { - - SpringApplication.run(NacosConsumerApp.class,args); - } -} ----- - -这个例子中我们注入了一个 LoadBalancerClient 的实例,并且手动的实例化一个 RestTemplate,同时将 `spring.application.name` 的配置值 注入到应用中来, -目的是调用 Provider 提供的服务时,希望将当前配置的应用名给显示出来。 - -NOTE: 在启动 Consumer 应用之前请先将 Nacos 服务启动好。具体启动方式可参考 https://nacos.io/zh-cn/docs/quick-start.html[Nacos 官网]。 - -启动后,访问 Consumer 提供出来的 `http://ip:port/echo/app-name` 接口。我这里测试启动的 port是 8082。访问结果如下所示: - - 访问地址:http://127.0.0.1:8082/echo/app-name - 访问结果:Hello Nacos Discovery nacos-consumer - -=== 关于 Nacos Starter 更多的配置项信息 - -更多关于 spring-cloud-starter-alibaba-nacos-discovery 的 starter 配置项如下所示: +更多关于 Nacos Discovery Starter 的配置项如下所示: :frame: topbot [width="60%",options="header"] |==== ^|配置项 ^|Key ^|默认值 ^|说明 -|`服务端地址`|`spring.cloud.nacos.discovery.server-addr`|`无`| `Nacos Server 启动监听的ip地址和端口` -|`服务名`|`spring.cloud.nacos.discovery.service`|`${spring.application.name}`|`给当前的服务命名` -|`权重`|`spring.cloud.nacos.discovery.weight`|`1`|`取值范围 1 到 100,数值越大,权重越大` -|`网卡名`|`spring.cloud.nacos.discovery.network-interface`|`无`|`当IP未配置时,注册的IP为此网卡所对应的IP地址,如果此项也未配置,则默认取第一块网卡的地址` -|`注册的IP地址`|`spring.cloud.nacos.discovery.ip`|`无`|`优先级最高` -|`注册的端口`|`spring.cloud.nacos.discovery.port`|`-1`|`默认情况下不用配置,会自动探测` -|`命名空间`|`spring.cloud.nacos.discovery.namespace`|`无`|`常用场景之一是不同环境的注册的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。` -|`AccessKey`|`spring.cloud.nacos.discovery.access-key`|`无`|`当要上阿里云时,阿里云上面的一个云账号名` -|`SecretKey`|`spring.cloud.nacos.discovery.secret-key`|`无`|`当要上阿里云时,阿里云上面的一个云账号密码` -|`Metadata`|`spring.cloud.nacos.discovery.metadata`|`无`|`使用Map格式配置,用户可以根据自己的需要自定义一些和服务相关的元数据信息` -|`日志文件名`|`spring.cloud.nacos.discovery.log-name`|`无`| -|`集群`|`spring.cloud.nacos.discovery.cluster-name`|`DEFAULT`|`配置成Nacos集群名称` -|`接入点`|`spring.cloud.nacos.discovery.enpoint`|`UTF-8`|`地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址` -|`是否集成Ribbon`|`ribbon.nacos.enabled`|`true`|`一般都设置成true即可` +|服务端地址|`spring.cloud.nacos.discovery.server-addr`|| Nacos Server 启动监听的ip地址和端口 +|服务名|`spring.cloud.nacos.discovery.service`|`${spring.application.name}`|注册的服务名 +|权重|`spring.cloud.nacos.discovery.weight`|`1`|取值范围 1 到 100,数值越大,权重越大 +|网卡名|`spring.cloud.nacos.discovery.network-interface`||当IP未配置时,注册的IP为此网卡所对应的IP地址,如果此项也未配置,则默认取第一块网卡的地址 +|注册的IP地址|`spring.cloud.nacos.discovery.ip`||优先级最高 +|注册的端口|`spring.cloud.nacos.discovery.port`|`-1`|默认情况下不用配置,会自动探测 +|命名空间|`spring.cloud.nacos.discovery.namespace`||常用场景之一是不同环境的注册的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等 +|AccessKey|`spring.cloud.nacos.discovery.access-key`||当要上阿里云时,阿里云上面的一个云账号名 +|SecretKey|`spring.cloud.nacos.discovery.secret-key`||当要上阿里云时,阿里云上面的一个云账号密码 +|Metadata|`spring.cloud.nacos.discovery.metadata`||使用Map格式配置,用户可以根据自己的需要自定义一些和服务相关的元数据信息 +|日志文件名|`spring.cloud.nacos.discovery.log-name`|| +|集群|`spring.cloud.nacos.discovery.cluster-name`|`DEFAULT`|Nacos集群名称 +|接入点|`spring.cloud.nacos.discovery.endpoint`||地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址 +|是否集成Ribbon|`ribbon.nacos.enabled`|`true`|一般都设置成true即可 +|是否开启Nacos Watch|`spring.cloud.nacos.discovery.watch.enabled`|`true`|可以设置成false来关闭 watch |==== diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/oss.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/oss.adoc index c18adea5..bff6b663 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/oss.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/oss.adoc @@ -1,4 +1,4 @@ -== Spring Cloud Alibaba Cloud OSS +== Spring Cloud AliCloud OSS OSS(Object Storage Service)是阿里云的一款对象存储服务产品, Spring Cloud AliCloud OSS 提供了Spring Cloud规范下商业版的对象存储服务,提供简单易用的API,并且支持与 Spring 框架中 Resource 的整合。 diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/rocketmq.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/rocketmq.adoc index 8c328533..ef4b0980 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/rocketmq.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/rocketmq.adoc @@ -137,6 +137,11 @@ messageChannel.send(MessageBuilder.withPayload("simple msg").build()); ### Spring Cloud Alibaba RocketMQ Binder 实现 +这是 Spring Cloud Stream RocketMQ Binder 的实现架构: + +.SCS RocketMQ Binder +image::https://img.alicdn.com/tfs/TB1v8rcbUY1gK0jSZFCXXcwqXXa-1236-773.png[] + RocketMQ Binder 的实现依赖于 https://github.com/apache/rocketmq-spring[RocketMQ-Spring] 框架。 RocketMQ-Spring 框架是 RocketMQ 与 Spring Boot 的整合,RocketMQ Spring 主要提供了 3 个特性: @@ -166,12 +171,66 @@ Message message = builder.build(); output().send(message); ``` +### MessageSource 支持 + +SCS RocketMQ Binder 支持 `MessageSource`,可以进行消息的拉取,例子如下: + +```java +@SpringBootApplication +@EnableBinding(MQApplication.PolledProcessor.class) +public class MQApplication { + + private final Logger logger = + LoggerFactory.getLogger(MQApplication.class); + + public static void main(String[] args) { + SpringApplication.run(MQApplication.class, args); + } + + @Bean + public ApplicationRunner runner(PollableMessageSource source, + MessageChannel dest) { + return args -> { + while (true) { + boolean result = source.poll(m -> { + String payload = (String) m.getPayload(); + logger.info("Received: " + payload); + dest.send(MessageBuilder.withPayload(payload.toUpperCase()) + .copyHeaders(m.getHeaders()) + .build()); + }, new ParameterizedTypeReference() { }); + if (result) { + logger.info("Processed a message"); + } + else { + logger.info("Nothing to do"); + } + Thread.sleep(5_000); + } + }; + } + + public static interface PolledProcessor { + + @Input + PollableMessageSource source(); + + @Output + MessageChannel dest(); + + } + +} +``` + + + ### 配置选项 #### RocketMQ Binder Properties spring.cloud.stream.rocketmq.binder.name-server:: -RocketMQ NameServer 地址。 +RocketMQ NameServer 地址(老版本使用 namesrv-addr 配置项)。 + Default: `127.0.0.1:9876`. spring.cloud.stream.rocketmq.binder.access-key:: @@ -276,3 +335,20 @@ retryNextServer:: 消息发送失败的情况下是否重试其它的 broker。 + 默认值: `false`. + +### 阿里云 MQ 服务 + +使用阿里云 MQ 服务需要配置 AccessKey、SecretKey 以及云上的 NameServer 地址。 + +NOTE: 0.1.2 & 0.2.2 & 0.9.0 才支持该功能 + +```properties +spring.cloud.stream.rocketmq.binder.access-key=YourAccessKey +spring.cloud.stream.rocketmq.binder.secret-key=YourSecretKey +spring.cloud.stream.rocketmq.binder.name-server=NameServerInMQ +``` + +NOTE: topic 和 group 请以 实例id% 为前缀进行配置。比如 topic 为 "test",需要配置成 "实例id%test" + +.NameServer 的获取(配置中请去掉 http:// 前缀) +image::https://spring-cloud-alibaba.oss-cn-beijing.aliyuncs.com/MQ.png[] diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/schedulerx.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/schedulerx.adoc index d26983d0..bb23022a 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/schedulerx.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/schedulerx.adoc @@ -1,4 +1,4 @@ -== Spring Cloud Alibaba Cloud SchedulerX +== Spring Cloud AliCloud SchedulerX SchedulerX(分布式任务调度) 是隶属于阿里云EDAS产品的组件, Spring Cloud AliCloud SchedulerX 提供了在Spring Cloud的配置规范下,分布式任务调度的功能支持。SchedulerX可提供秒级、精准、高可靠、高可用的定时任务调度服务,并支持多种类型的任务调度,如简单单机任务、简单多机任务、脚本任务以及网格任务。 @@ -75,7 +75,7 @@ public class SimpleTask implements ScxSimpleJobProcessor { [source,text] ---- Job分组:测试——***-*-*-**** -Job处理接口:SimpleTask +Job处理接口:org.springframework.cloud.alibaba.cloud.examples.SimpleTask 类型:简单Job单机版 定时表达式:默认选项——0 * * * * ? Job描述:无 @@ -111,4 +111,4 @@ NOTE: group-id必须创建在namespace当中。 access-key以及secret-key为阿里云账号的AK/SK信息,如果应用在EDAS上部署,则不需要填写这两项信息,否则请前往 https://usercenter.console.aliyun.com/#/manage/ak[安全信息管理]获取。 -domain-name并不是必须的,具体请参考 https://help.aliyun.com/document_detail/35359.html[SchedulerX官方文档]。 +domain-name并不是必须的,具体请参考 https://help.aliyun.com/document_detail/35359.html[SchedulerX官方文档]。 \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sentinel.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sentinel.adoc index 79ef08e8..b62f35dd 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sentinel.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sentinel.adoc @@ -6,7 +6,6 @@ https://github.com/alibaba/Sentinel[Sentinel] 具有以下特征: - * *丰富的应用场景*: Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。 * *完备的实时监控*: Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。 * *广泛的开源生态*: Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。 @@ -14,7 +13,7 @@ https://github.com/alibaba/Sentinel[Sentinel] 具有以下特征: ### 如何使用 Sentinel -如果要在您的项目中引入 Sentinel,使用 group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alibaba-sentinel` 的 starter。 +如果要在您的项目中引入 Sentinel,使用 group ID 为 `com.alibaba.cloud` 和 artifact ID 为 `spring-cloud-starter-alibaba-sentinel` 的 starter。 ```xml @@ -51,6 +50,31 @@ public class TestController { @SentinelResource 还提供了其它额外的属性如 `blockHandler`,`blockHandlerClass`,`fallback` 用于表示限流或降级的操作,更多内容可以参考 https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81[Sentinel注解支持]。 +以上例子都是在 WebServlet 环境下使用的,Sentinel 目前已经支持 WebFlux,需要配合 `spring-boot-starter-webflux` 依赖触发 sentinel-starter 中 WebFlux 相关的自动化配置。 + +```java +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(ServiceApplication.class, args); + } + +} + +@RestController +public class TestController { + + @GetMapping("/mono") + @SentinelResource("hello") + public Mono mono() { + return Mono.just("simple string") + .transform(new SentinelReactorTransformer<>("otherResourceName")); + } + +} +``` + ##### Sentinel 控制台 Sentinel 控制台提供一个轻量级的控制台,它提供机器发现、单机资源实时监控、集群资源汇总,以及规则管理的功能。您只需要对应用进行简单的配置,就可以使用这些功能。 @@ -98,12 +122,12 @@ spring: 更多 Sentinel 控制台的使用及问题参考: https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0[Sentinel控制台] -### Feign 支持 +### OpenFeign 支持 -Sentinel 适配了 https://github.com/OpenFeign/feign[Feign] 组件。如果想使用,除了引入 `sentinel-starter` 的依赖外还需要 2 个步骤: +Sentinel 适配了 https://github.com/OpenFeign/feign[OpenFeign] 组件。如果想使用,除了引入 `sentinel-starter` 的依赖外还需要 2 个步骤: * 配置文件打开 sentinel 对 feign 的支持:`feign.sentinel.enabled=true` -* 加入 `feign starter` 依赖触发 `sentinel starter` 的配置类生效: +* 加入 `openfeign starter` 依赖使 `sentinel starter` 中的自动化配置类生效: ```xml org.springframework.cloud @@ -175,11 +199,11 @@ NOTE: 应用启动的时候会检查 `@SentinelRestTemplate` 注解对应的限 Sentinel RestTemplate 限流的资源规则提供两种粒度: -* `schema://host:port/path`:协议、主机、端口和路径 +* `httpmethod:schema://host:port/path`:协议、主机、端口和路径 -* `schema://host:port`:协议、主机和端口 +* `httpmethod:schema://host:port`:协议、主机和端口 -NOTE: 以 `https://www.taobao.com/test` 这个 url 为例。对应的资源名有两种粒度,分别是 `https://www.taobao.com` 以及 `https://www.taobao.com/test` +NOTE: 以 `https://www.taobao.com/test` 这个 url 并使用 GET 方法为例。对应的资源名有两种粒度,分别是 `GET:https://www.taobao.com` 以及 `GET:https://www.taobao.com/test` ### 动态数据源支持 @@ -193,12 +217,12 @@ spring.cloud.sentinel.datasource.ds1.file.rule-type=flow #spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json #spring.cloud.sentinel.datasource.ds1.file.data-type=custom -#spring.cloud.sentinel.datasource.ds1.file.converter-class=JsonFlowRuleListConverter +#spring.cloud.sentinel.datasource.ds1.file.converter-class=org.springframework.cloud.alibaba.cloud.examples.JsonFlowRuleListConverter #spring.cloud.sentinel.datasource.ds1.file.rule-type=flow spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848 -spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel -spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP +spring.cloud.sentinel.datasource.ds2.nacos.data-id=sentinel +spring.cloud.sentinel.datasource.ds2.nacos.group-id=DEFAULT_GROUP spring.cloud.sentinel.datasource.ds2.nacos.data-type=json spring.cloud.sentinel.datasource.ds2.nacos.rule-type=degrade @@ -209,7 +233,7 @@ spring.cloud.sentinel.datasource.ds3.zk.rule-type=authority spring.cloud.sentinel.datasource.ds4.apollo.namespace-name = application spring.cloud.sentinel.datasource.ds4.apollo.flow-rules-key = sentinel spring.cloud.sentinel.datasource.ds4.apollo.default-flow-rule-value = test -spring.cloud.sentinel.datasource.ds5.apollo.rule-type=param-flow +spring.cloud.sentinel.datasource.ds4.apollo.rule-type=param-flow ``` @@ -219,16 +243,9 @@ NOTE: d1, ds2, ds3, ds4 是 `ReadableDataSource` 的名字,可随意编写。 每种数据源都有两个共同的配置项: `data-type`、 `converter-class` 以及 `rule-type`。 -`data-type` 配置项表示 `Converter` 类型,Spring Cloud Alibaba Sentinel 默认提供两种内置的值,分别是 `json` 和 `xml` (不填默认是json)。 如果不想使用内置的 `json` 或 `xml` 这两种 `Converter`,可以填写 `custom` 表示自定义 `Converter`,然后再配置 `converter-class` 配置项,该配置项需要写类的全路径名(比如 `spring.cloud.sentinel.datasource.ds1.file.converter-class=JsonFlowRuleListConverter`)。 +`data-type` 配置项表示 `Converter` 类型,Spring Cloud Alibaba Sentinel 默认提供两种内置的值,分别是 `json` 和 `xml` (不填默认是json)。 如果不想使用内置的 `json` 或 `xml` 这两种 `Converter`,可以填写 `custom` 表示自定义 `Converter`,然后再配置 `converter-class` 配置项,该配置项需要写类的全路径名(比如 `spring.cloud.sentinel.datasource.ds1.file.converter-class=org.springframework.cloud.alibaba.cloud.examples.JsonFlowRuleListConverter`)。 -`rule-type` 配置表示该数据源中的规则属于哪种类型的规则(`flow`,`degrade`,`authority`,`system`, `param-flow`)。 - -如果数据源生效并且规则成功加载,控制台会打印类似如下信息: - -``` -[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule -[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule -``` +`rule-type` 配置表示该数据源中的规则属于哪种类型的规则(`flow`,`degrade`,`authority`,`system`, `param-flow`, `gw-flow`, `gw-api-group`)。 NOTE: 当某个数据源规则信息加载失败的情况下,不会影响应用的启动,会在日志中打印出错误信息。 @@ -236,18 +253,159 @@ NOTE: 默认情况下,xml 格式是不支持的。需要添加 `jackson-datafo 关于 Sentinel 动态数据源的实现原理,参考: https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95[动态规则扩展] -### Endpoint 支持 +### Zuul 支持 -在使用 Endpoint 特性之前需要在 Maven 中添加 `spring-boot-starter-actuator` 依赖,并在配置中允许 Endpoints 的访问。 +https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81[参考 Sentinel 网关限流] -* Spring Boot 1.x 中添加配置 `management.security.enabled=false`。暴露的 endpoint 路径为 `/sentinel` -* Spring Boot 2.x 中添加配置 `management.endpoints.web.exposure.include=*`。暴露的 endpoint 路径为 `/actuator/sentinel` +若想跟 Sentinel Starter 配合使用,需要加上 `spring-cloud-alibaba-sentinel-gateway` 依赖,同时需要添加 `spring-cloud-starter-netflix-zuul` 依赖来让 `spring-cloud-alibaba-sentinel-gateway` 模块里的 Zuul 自动化配置类生效: -Sentinel Endpoint 里暴露的信息非常有用。包括当前应用的所有规则信息、日志目录、当前实例的 IP,Sentinel Dashboard 地址,Block Page,应用与 Sentinel Dashboard 的心跳频率等等信息。 +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + -### More + + com.alibaba.cloud + spring-cloud-alibaba-sentinel-gateway + -下表显示当应用的 `ApplicationContext` 中存在对应的Bean的类型时,会进行的一些操作: + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + +``` + +### Spring Cloud Gateway 支持 + +https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81[参考 Sentinel 网关限流] + +若想跟 Sentinel Starter 配合使用,需要加上 `spring-cloud-alibaba-sentinel-gateway` 依赖,同时需要添加 `spring-cloud-starter-gateway` 依赖来让 `spring-cloud-alibaba-sentinel-gateway` 模块里的 Spring Cloud Gateway 自动化配置类生效: + +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + com.alibaba.cloud + spring-cloud-alibaba-sentinel-gateway + + + + org.springframework.cloud + spring-cloud-starter-gateway + +``` + +### Sentinel 对外暴露的 Endpoint + +Sentinel 内部提供了一个 Endpoint, 对应的 endpoint id 为 `sentinel`。 + +Endpoint 暴露的 json 中包含了多种属性: + +1. appName: 应用名 +2. logDir: 日志所在目录 +3. logUsePid: 日志文件名是否带上进程id +4. blockPage: 限流 block 之后跳转的页面 +5. metricsFileSize: metrics 文件的大小 +6. metricsFileCharset: metrics 文件对应的字符集 +7. totalMetricsFileCount: metrics 最多保留的文件数 +8. consoleServer: sentinel dashboard 地址 +9. clientIp: 客户端 ip +10. heartbeatIntervalMs: 客户端跟 dashboard 的心跳间隔时间 +11. clientPort: 客户端需要暴露的端口跟 dashboard 进行交互 +12. coldFactor: 冷启动因子 +13. filter: CommonFilter 相关的属性, 比如 order, urlPatterns 以及 enable +14. datasource: 客户端配置的数据源信息 +15. rules: 客户端生效的规则,内部含有 flowRules, degradeRules, systemRules, authorityRule, paramFlowRule + +这是 Endpoint 暴露的 json 示例: + + +[source,json,indent=0] +---- +{ + "blockPage": null, + "appName": "sentinel-example", + "consoleServer": "localhost:8080", + "coldFactor": "3", + "rules": { + "flowRules": [{ + "resource": "GET:http://www.taobao.com", + "limitApp": "default", + "grade": 1, + "count": 0.0, + "strategy": 0, + "refResource": null, + "controlBehavior": 0, + "warmUpPeriodSec": 10, + "maxQueueingTimeMs": 500, + "clusterMode": false, + "clusterConfig": null + }, { + "resource": "/test", + "limitApp": "default", + "grade": 1, + "count": 0.0, + "strategy": 0, + "refResource": null, + "controlBehavior": 0, + "warmUpPeriodSec": 10, + "maxQueueingTimeMs": 500, + "clusterMode": false, + "clusterConfig": null + }, { + "resource": "/hello", + "limitApp": "default", + "grade": 1, + "count": 1.0, + "strategy": 0, + "refResource": null, + "controlBehavior": 0, + "warmUpPeriodSec": 10, + "maxQueueingTimeMs": 500, + "clusterMode": false, + "clusterConfig": null + }] + }, + "metricsFileCharset": "UTF-8", + "filter": { + "order": -2147483648, + "urlPatterns": ["/*"], + "enabled": true + }, + "totalMetricsFileCount": 6, + "datasource": { + "ds1": { + "file": { + "dataType": "json", + "ruleType": "FLOW", + "converterClass": null, + "file": "...", + "charset": "utf-8", + "recommendRefreshMs": 3000, + "bufSize": 1048576 + }, + "nacos": null, + "zk": null, + "apollo": null, + "redis": null + } + }, + "clientIp": "30.5.121.91", + "clientPort": "8719", + "logUsePid": false, + "metricsFileSize": 52428800, + "logDir": "...", + "heartbeatIntervalMs": 10000 +} +---- + +### 关于 Sentinel Starter 更多的配置项信息 + +下表显示当应用的 `ApplicationContext` 中存在对应的Bean的类型时,会进行自动化设置: :frame: topbot [width="60%",options="header"] @@ -258,18 +416,20 @@ Sentinel Endpoint 里暴露的信息非常有用。包括当前应用的所有 |`RequestOriginParser`|`WebCallbackManager.setRequestOriginParser(requestOriginParser)`|设置来源信息 |==== -下表显示 Spring Cloud Alibaba Sentinel 的所有配置信息: + +Spring Cloud Alibaba Sentinel 提供了这些配置选项 :frame: topbot [width="60%",options="header"] |==== ^|配置项 ^|含义 ^|默认值 +|`spring.application.name` or `project.name`|Sentinel项目名| |`spring.cloud.sentinel.enabled`|Sentinel自动化配置是否生效|true -|`spring.cloud.sentinel.eager`|取消Sentinel控制台懒加载|false +|`spring.cloud.sentinel.eager`|是否提前触发 Sentinel 初始化|false |`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| +|`spring.cloud.sentinel.transport.client-ip`|此配置的客户端IP将被注册到 Sentinel Server 端| |`spring.cloud.sentinel.filter.order`|Servlet Filter的加载顺序。Starter内部会构造这个filter|Integer.MIN_VALUE |`spring.cloud.sentinel.filter.url-patterns`|数据类型是数组。表示Servlet Filter的url pattern集合|/* |`spring.cloud.sentinel.filter.enabled`|Enable to instance CommonFilter|true @@ -279,7 +439,16 @@ Sentinel Endpoint 里暴露的信息非常有用。包括当前应用的所有 |`spring.cloud.sentinel.log.dir`|Sentinel 日志文件所在的目录| |`spring.cloud.sentinel.log.switch-pid`|Sentinel 日志文件名是否需要带上pid|false |`spring.cloud.sentinel.servlet.block-page`| 自定义的跳转 URL,当请求被限流时会自动跳转至设定好的 URL | -|`spring.cloud.sentinel.flow.cold-factor`| https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8[冷启动因子] |3 +|`spring.cloud.sentinel.flow.cold-factor`| https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81--- +%E5%86%B7%E5%90%AF%E5%8A%A8[冷启动因子] |3 +|`spring.cloud.sentinel.zuul.order.pre`| SentinelZuulPreFilter 的 order | 10000 +|`spring.cloud.sentinel.zuul.order.post`| SentinelZuulPostFilter 的 order | 1000 +|`spring.cloud.sentinel.zuul.order.error`| SentinelZuulErrorFilter 的 order | -1 +|`spring.cloud.sentinel.scg.fallback.mode`| Spring Cloud Gateway 熔断后的响应模式(选择 `redirect` or `response`) | +|`spring.cloud.sentinel.scg.fallback.redirect`| Spring Cloud Gateway 响应模式为 'redirect' 模式对应的重定向 URL | +|`spring.cloud.sentinel.scg.fallback.response-body`| Spring Cloud Gateway 响应模式为 'response' 模式对应的响应内容 | +|`spring.cloud.sentinel.scg.fallback.response-status`| Spring Cloud Gateway 响应模式为 'response' 模式对应的响应码 | 429 +|`spring.cloud.sentinel.scg.fallback.content-type`| Spring Cloud Gateway 响应模式为 'response' 模式对应的 content-type | application/json |==== NOTE: 请注意。这些配置只有在 Servlet 环境下才会生效,RestTemplate 和 Feign 针对这些配置都无法生效 \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sms.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sms.adoc index e5af0329..6bd7d89c 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sms.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/sms.adoc @@ -1,4 +1,4 @@ -== Spring Cloud Alibaba Cloud SMS +== Spring Cloud AliCloud SMS 短信服务(Short Message Service)是阿里云为用户提供的一种通信服务的能力。 Spring Cloud AliCloud SMS 实现了与 SMS 的简单集成,提供更为简单易用的 API,可以基于 Spring Cloud Alibaba SMS 来快速的接入阿里云的 SMS 服务。 @@ -31,7 +31,7 @@ access-key 和 secret-key 是阿里云账号的 AK/SK,需要首先注册阿里 ==== 引入 SMS API -Spring Cloud Alicloud SMS 中的 SMS API 基于阿里云官方 SMS SDK 提供,具备单个短信发送、多个短信批量发送、短信查询、短信消息(短信回执消息 和 上行短信消息) 类行操作API。 +Spring Cloud Alicloud SMS 中的 SMS API 基于阿里云官方 SMS SDK ,提供具备单个短信发送、多个短信批量发送、短信查询、短信消息(`短信回执消息` 和 `上行短信消息`) 类型操作API。 一个简单的使用 SMS API 发送短信的应用如下。 @@ -43,7 +43,13 @@ public class SmsApplication { @Autowired private ISmsService smsService; + /** + * 短信发送 Example + * @param code + * @return + */ @RequestMapping("/batch-sms-send.do") + public SendBatchSmsResponse batchsendCheckCode( @RequestParam(name = "code") String code) { @@ -69,6 +75,7 @@ public class SmsApplication { } public static void main(String[] args) throws URISyntaxException { + SpringApplication.run(SmsApplication.class, args); } @@ -88,10 +95,15 @@ Spring Cloud Alicloud SMS 封装的 API 接口为了降低学习的成本,尽 * 批量短信发送 -参考以下的 Example ,来快速开发一个具有批量短信发送的功能。在 Controller 中或者新建一个 Controller 新增如下代码: +参考以下的 Example ,来快速开发一个具有批量短信发送的功能。在 Controller 中或者新建一个 Controler 新增如下代码: [source,java] ---- +/** + * 批量短信发送 Example + * @param code + * @return + */ @RequestMapping("/batch-sms-send.do") public SendBatchSmsResponse batchsendCheckCode( @RequestParam(name = "code") String code) { @@ -129,10 +141,16 @@ NOTE: 这里设置请求的 MethodType 为 GET,和官网给出的例子还有 * 短信查询 -参考以下的 Example ,可以快速开发根据某个指定的号码查询短信历史发送状态。在 Controller 中或者新建一个 Controller 新增如下代码: +参考以下的 Example ,可以快速开发根据某个指定的号码查询短信历史发送状态。在 Controller 中或者新建一个 Controler 新增如下代码: [source,java] ---- +/** + * + * 短信查询 Example + * @param telephone + * @return + */ @RequestMapping("/query.do") public QuerySendDetailsResponse querySendDetailsResponse( @RequestParam(name = "tel") String telephone) { @@ -181,7 +199,7 @@ spring.cloud.alicloud.sms.report-queue-name=Alicom-Queue-********-SmsReport */ @Component public class SmsReportMessageListener - implements SmsReportMessageListener { + implements org.springframework.cloud.alicloud.sms.SmsReportMessageListener { @Override public boolean dealMessage(Message message) { @@ -199,7 +217,7 @@ public class SmsReportMessageListener * 上行短信消息 -通过订阅 SmsUp 上行短信消息,可以获知终端用户回复短信的内容。这些工作也已经被 Spring Cloud AliCloud SMS 封装好了。你只需要完成以下两步即可。 +通过订阅SmsUp上行短信消息,可以获知终端用户回复短信的内容。这些工作也已经被 Spring Cloud AliCloud SMS 封装好了。你只需要完成以下两步即可。 1、 在 `application.properties` 配置文件中(也可以是 application.yaml)配置 SmsReport 的队列名称。 @@ -217,7 +235,7 @@ spring.cloud.alicloud.sms.up-queue-name=Alicom-Queue-********-SmsUp */ @Component public class SmsUpMessageListener - implements SmsUpMessageListener { + implements org.springframework.cloud.alicloud.sms.SmsUpMessageListener { @Override public boolean dealMessage(Message message) { @@ -231,4 +249,4 @@ public class SmsUpMessageListener } ---- -更多关于 Message 的消息体格式可 https://help.aliyun.com/document_detail/55496.html?spm=a2c4g.11186623.6.570.7f792c78rOiWXO[参考这里]。 +更多关于 Message 的消息体格式可 https://help.aliyun.com/document_detail/55496.html?spm=a2c4g.11186623.6.570.7f792c78rOiWXO[参考这里]。 \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc index f74d82eb..c38e2134 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc @@ -1,17 +1,13 @@ [[spring-cloud-alibaba-reference]] = Spring Cloud Alibaba 参考文档 -xiaojing; xiaolongzuo; jim fang; bingting peng; wangyuxin +Jim Fang, Jing Xiao, Mercy Ma, Xiaolong Zuo, Bingting Peng, Yuxin Wang :doctype: book :toc: :toclevels: 4 :source-highlighter: prettify :numbered: -== 介绍 - -Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。 - -依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里分布式应用解决方案,通过阿里中间件来迅速搭建分布式应用系统。 +include::introduction.adoc[] include::dependency-management.adoc[] @@ -21,6 +17,8 @@ include::nacos-config.adoc[] include::sentinel.adoc[] +include::dubbo.adoc[] + include::rocketmq.adoc[] include::ans.adoc[] diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/acm.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/acm.adoc index 42ed6ceb..11468106 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/acm.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/acm.adoc @@ -1,8 +1,10 @@ == Spring Cloud Alibaba Cloud ACM -Spring Cloud Alibaba Cloud ACM is an implementation of the commercial product Application Configuration Management(ACM) in the client side of Spring Cloud, and is free of charge. +Spring Cloud AliCloud ACM is an implementation of the commercial product Application Configuration Management(ACM) in the client side of Spring Cloud, and is free of charge. -Spring Cloud Alibaba Cloud ACM is an alternative solution for Config Server and Client. The concepts on the client and server have the same abstractions with Spring Environment and PropertySource. In special Bootstrap phases, configurations are loaded to the Spring environment. During the application lifecycle from development, deployment, test to production, you can manage the configurations across all the environments, and make sure that all information required for application migration is ready when needed. +Use Spring Cloud AliCloud ACM to quickly access ACM configuration management capabilities based on Spring Cloud's programming model. + +NOTE: Currently EDAS already supports direct deployment of the Nacos Config app. === How to Introduce Spring Cloud Alibaba Cloud ACM @@ -162,12 +164,12 @@ ACM provides a solution to share the same configuration across multiple applicat spring.application.group=company.department.team ---- -Then, you application will retrieve configurations from the following DataId in turn before it retrieves its own configuration: company:application.properties, company.department:application.properties, company.department.team:application.properties。 +Then, you application will retrieve configurations from the following DataId in turn before it retrieves its own configuration: company:application.properties, company.department:application.properties, company.department.team:application.properties. After that, it also retrieves configuration from {spring.application.group}: {spring.application.name}. {file-extension} The later in order, the higer the priority, and the unique configuration of the application itself has the highest priority. -NOTE: The default suffix of DataId is properties, and you can change it using spring.cloud.alicloud.acm.file-extension. `{spring.application.group}: {spring.application.name}. {file-extension}` 。 +NOTE: The default suffix of DataId is properties, and you can change it using spring.cloud.alicloud.acm.file-extension. `{spring.application.group}: {spring.application.name}. {file-extension}` . NOTE: If you configured `spring.profiles.active` , then the DataId format of `{spring.application.group}: {spring.application.name}-{spring.profiles.active}. {file-extension}` is also supported, and has higher priority than `{spring.application.group}: {spring.application.name}. {file-extension}` diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/ans.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/ans.adoc index e9c025b5..dede7648 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/ans.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/ans.adoc @@ -2,6 +2,8 @@ ANS(Application Naming Service) is a component of EDAS. Spring Cloud Alibaba Cloud ANS provides the commercial version of service registration and discovery in conformity with the Spring Cloud specifications, so that you can develop your applications locally and run them on the cloud. +NOTE: EDAS currently supports direct deployment of Nacos Discovery applications + === How to Introduce Spring Cloud Alibaba Cloud ANS If you want to use ANS in your project, please use the starter with the group ID as `com.alibaba.cloud` and the artifact ID as `spring-cloud-starter-alicloud-ans`. diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc index 767a1e22..a66eb70b 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/dependency-management.adoc @@ -1,16 +1,14 @@ == Dependency Management -### Spring Cloud Alibaba Bill of Materials (BOM) - If you’re a Maven Central user, add our BOM to your pom.xml section. This will allow you to omit versions for any of the Maven dependencies and instead delegate versioning to the BOM. ```xml - org.springframework.cloud + com.alibaba.cloud spring-cloud-alibaba-dependencies - 2.1.0.RELEASE + 2.1.1.BUILD-SNAPSHOT pom import @@ -18,21 +16,5 @@ If you’re a Maven Central user, add our BOM to your pom.xml ``` -### Spring Snapshots Maven Repository +In the following sections, it will be assumed you are using the Spring Cloud Alibaba BOM and the dependency snippets will not contain versions. -If you want to use the latest BUILD-SNAPSHOT version, add Spring Snapshot Repository in pom.xml , Attention: BUILD-SNAPSHOT may be updated in any time: - -```xml - - - spring-snapshots - Spring SnapShots - https://repo.spring.io/libs-snapshot - - true - - - -``` - -For example, the 0.2.0.BUILD-SNAPSHOT is available from this repository. \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/dubbo.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/dubbo.adoc new file mode 100644 index 00000000..fcdee943 --- /dev/null +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/dubbo.adoc @@ -0,0 +1,47 @@ +== Spring Cloud Alibaba Dubbo + +=== Introduction + +Dubbo Spring Cloud is based on Dubbo Spring Boot 2.7.3[1] and Spring Cloud 2.x development, whether the developer is a Dubbo user or a Spring Cloud user. +Easily navigate and move apps up at a cost close to “zero” costs. Dubbo Spring Cloud is designed to simplify Cloud Native development costs, improve R&D performance, and improve application performance. + +=== Features + +Since Dubbo Spring Cloud is built on top of the native Spring Cloud, its service governance capabilities are considered Spring Cloud Plus. +Not only does it fully cover the Spring Cloud native features [5], but it also provides a more stable and mature implementation, as shown in the following table: + +:frame: topbot +[width="60%",options="header"] +|==== +^|Feature ^|Spring Cloud ^|Dubbo Spring Cloud +| Distributed configuration | Git、Zookeeper、Consul、JDBC | Spring Cloud Distributed Configuration + Dubbo Configuration Center[6] +| Service registration and discovery | Eureka、Zookeeper、Consul | Spring Cloud Native Registration Center[7] + Dubbo Native Registration Center[8] +| Load balancing | Ribbon(Random, RoundRobin) | Dubbo built-in implementation (random, polling, etc. + weights, etc.) +| Circuit Breakers | Spring Cloud Hystrix | Spring Cloud Hystrix + Alibaba Sentinel[9] etc. +| Service-to-service calls | Open Feign、`RestTemplate` | Spring Cloud service call + Dubbo `@Reference`. +| Tracing | Spring Cloud Sleuth[10] + Zipkin[11] | Zipkin、opentracing, etc. +|==== + +=== Reference + +[1]: Starting with 2.7.0, Dubbo Spring Boot and Dubbo are consistent in version + +[2]: Preview releases of Spring Cloud Alibaba are available: 0.9.0, 0.2.2, and 0.1.2 - https://spring.io/blog/2011/04/11/preview-releases-of-spring-cloud-alibaba-are-available-0-9-0-0-2-2-and-0-1-2 + +[3]: The current version of the Spring Cloud "F" is: `Finchley.SR2` - https://cloud.spring.io/spring-cloud-static/Finchley.SR2/single/spring-cloud.html + +[4]: The current Spring Cloud "G" version is `Greenwich.RELEASE` + +[5]: Spring Cloud feature list - https://cloud.spring.io/spring-cloud-static/Greenwich.RELEASE/single/spring-cloud.html#_features + +[6]: Dubbo 2.7 starts supporting the configuration center and can be customized - http://dubbo.apache.org/en-us/docs/user/configuration/config-center.html + +[7]: Spring Cloud native registry, in addition to Eureka, Zookeeper, and Consul, includes Nacos in Spring Cloud Alibaba + +[8]: Dubbo Native Registration Center - http://dubbo.apache.org/en-us/docs/user/references/registry/introduction.html + +[9]: Alibaba Sentinel: Sentinel uses traffic as an entry point to protect service stability from multiple dimensions such as flow control, blowdown, and system load protection - https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B %E7%BB%8D, currently Sentinel has been accepted as a candidate for Circuit Breaker by the Spring Cloud project - https://spring.io/blog/2011/04/8/introducing-spring-cloud-circuit-breaker + +[10]:Spring Cloud Sleuth - https://spring.io/projects/spring-cloud-sleuth + +[11]: Zipkin - https://github.com/apache/incubator-zipkin \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/introduction.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/introduction.adoc new file mode 100644 index 00000000..f115d5ed --- /dev/null +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/introduction.adoc @@ -0,0 +1,19 @@ +## Introduction + +Spring Cloud Alibaba aims to provide a one-stop solution for microservices development. This prjoect includes the required components for developing distributed applications and services, so that developers can develop distributed applications easily with the Spring Cloud programming models. + +With Spring Cloud Alibaba, you only need to add a few annotations and configurations, and you will be able to use the distributed solutions of Alibaba for your applications, and build a distributed system of your own with Alibaba middleware. + +The features of Spring Cloud Alibaba: + +1. **Flow control and service degradation**:support WebServlet, WebFlux, OpenFeign, RestTemplate, Dubbo access to the function of limiting and degrading flow. It can modify the rules of limiting and degrading flow in real time through the console at run time, and it also supports the monitoring of limiting and degrading Metrics. +2. **Service registration and discovery**:Service can be registered and clients can discover the instances using Spring-managed beans, auto integration Ribbon. +3. **Distributed configuration**:support for externalized configuration in a distributed system, auto refresh when configuration changes. +4. **Rpc Service**:extend Spring Cloud client RestTemplate and OpenFeign to support calling Dubbo RPC services. +5. **Event-driven**:support for building highly scalable event-driven microservices connected with shared messaging systems. +6. **Distributed Transaction**:support for distributed transaction solution with high performance and ease of use. +7. **Alibaba Cloud Object Storage**:massive, secure, low-cost, and highly reliable cloud storage services. Support for storing and accessing any type of data in any application, anytime, anywhere. +8. **Alibaba Cloud SchedulerX**:accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds. +9. **Alibaba Cloud SMS**: A messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers. + +Spring Cloud Alibaba also provide rich https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples[examples]. \ No newline at end of file diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-config.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-config.adoc index b610cf4b..5c84dd06 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-config.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-config.adoc @@ -1,16 +1,35 @@ == Spring Cloud Alibaba Nacos Config -Nacos provides key/value storage of configurations and other metadata as well as server and client support for externalized configurations in distributed systems. With Spring Cloud Alibaba Nacos Config, you can manage externalized configurations of your Spring Cloud applications in the Nacos Server. +Nacos is an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications. -Spring Cloud Alibaba Nacos Config is an alternative solution for Config Server and Client. The concepts on the client and server have the same abstractions with Spring Environment and PropertySource. In special Bootstrap phases, configurations are loaded to the Spring environment. During the application lifecycle from development, deployment, test to production, you can manage the configurations across all the environments, and make sure that all information required for application migration is ready when needed. +Use Spring Cloud Alibaba Nacos Config to quickly access Nacos configuration management capabilities based on Spring Cloud's programming model. + +=== How to Introduce Nacos Config for configuration + +please use the starter with the group ID as `com.alibaba.cloud` and the artifact ID as `spring-cloud-starter-alibaba-nacos-config`. + +[source,xml,indent=0] +---- + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + +---- === Quickstart -===== Initialize Nacos Server +Nacos Config uses DataId and GROUP to determine a configuration. -1. Start Nacos Server. Refer to https://nacos.io/zh-cn/docs/quick-start.html[Nacos Documentation] for details about how to start the Nacos server. +The following figure shows that the DataId uses `myDataid`, GROUP uses `DEFAULT_GROUP`, and configures a configuration item of the format Properties: -2. Add the following configurations in Nacos: +.Nacos Config Item +image::https://img.alicdn.com/tfs/TB1N2nxbRr0gK0jSZFnXXbRRXXa-2448-1194.png[] + +==== Initialize Nacos Server + +For specific startup methods, refer to the "Nacos Server Startup" section of the Spring Cloud Alibaba Nacos Discovery section. + +After the Nacos Server is started, add how to configure it: [source,subs="normal"] ---- @@ -24,7 +43,7 @@ Configuration content: user.name=nacos-config-properties user.age=90 ---- -NOTE: The default file extension of dataid is properties. +NOTE: The default file extension of DataId is properties. ===== Usage on the Client @@ -43,10 +62,10 @@ Now we can create a standard Spring Boot application. [source,java] ---- @SpringBootApplication -public class ProviderApplication { +public class NacosConfigApplication { public static void main(String[] args) { - ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args); + ConfigurableApplicationContext applicationContext = SpringApplication.run(NacosConfigApplication.class, args); String userName = applicationContext.getEnvironment().getProperty("user.name"); String userAge = applicationContext.getEnvironment().getProperty("user.age"); System.err.println("user name :" +userName+"; age: "+userAge); @@ -59,6 +78,7 @@ Before running this example, we need to configure the address of the Nacos serve .bootstrap.properties [source,properties] ---- +# DataId By default, the `spring.application.name` configuration is combined with the file extension (the configuration format uses properties by default), and the GROUP is not configured to use DEFAULT_GROUP by default. Therefore, the Nacos Config configuration corresponding to the configuration file has a DataId of nacos-config.properties and a GROUP of DEFAULT_GROUP spring.application.name=nacos-config spring.cloud.nacos.config.server-addr=127.0.0.1:8848 ---- @@ -71,17 +91,16 @@ Run this example and you can see the following output: [source,subs="normal"] ---- -2018-11-02 14:24:51.638 INFO 32700 --- [main] c.a.demo.provider.ProviderApplication : Started ProviderApplication in 14.645 seconds (JVM running for 15.139) +2018-11-02 14:24:51.638 INFO 32700 --- [main] c.a.demo.provider.NacosConfigApplication : Started NacosConfigApplication in 14.645 seconds (JVM running for 15.139) user name :nacos-config-properties; age: 90 2018-11-02 14:24:51.688 INFO 32700 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@a8c5e74: startup date [Fri Nov 02 14:24:51 CST 2018]; root of context hierarchy -2018-11 ---- === Add Configurations with DataId in YAML Format -spring-cloud-starter-alibaba-nacos-config supports yaml format as well. You only need to complete the following 2 steps. +Nacos Config supports yaml format as well. You only need to complete the following 2 steps. -1. In the bootstrap.properties file, add the following line to claim that the format of dataid is yaml. As follows: +1. In the bootstrap.properties file, add the following line to claim that the format of DataId is yaml. As follows: .bootstrap.properties [source,yaml] @@ -89,7 +108,7 @@ spring-cloud-starter-alibaba-nacos-config supports yaml format as well. You only spring.cloud.nacos.config.file-extension=yaml ---- -2. Add a configuration with the dataId in yaml format on the Nacos console, as shown below: +2. Add a configuration with the DataId in yaml format on the Nacos console, as shown below: [source,subs="normal"] ---- @@ -100,29 +119,29 @@ Group : DEFAULT_GROUP Configuration format: YAML Configuration content: user.name: nacos-config-yaml - user.age: 68 + user.age: 68 ---- After completing the preivous two steps, restart the testing program and you will see the following result. [source,subs="normal"] ---- -2018-11-02 14:59:00.484 INFO 32928 --- [main] c.a.demo.provider.ProviderApplication:Started ProviderApplication in 14.183 seconds (JVM running for 14.671) +2018-11-02 14:59:00.484 INFO 32928 --- [main] c.a.demo.provider.NacosConfigApplication:Started NacosConfigApplication in 14.183 seconds (JVM running for 14.671) user name :nacos-config-yaml; age: 68 2018-11-02 14:59:00.529 INFO 32928 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@265a478e: startup date [Fri Nov 02 14:59:00 CST 2018]; root of context hierarchy ---- === Support Dynamic Configuration Udpates -spring-cloud-starter-alibaba-nacos-config also supports dynamic configuration updates. The code for starting Spring Boot application testing is as follows: +Nacos Config also supports dynamic configuration updates. The code for starting Spring Boot application testing is as follows: [source,java] ---- @SpringBootApplication -public class ProviderApplication { +public class NacosConfigApplication { public static void main(String[] args) { - ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args); + ConfigurableApplicationContext applicationContext = SpringApplication.run(NacosConfigApplication.class, args); while(true) { //When configurations are refreshed dynamically, they will be updated in the Enviroment, therefore here we retrieve configurations from Environment every other second. String userName = applicationContext.getEnvironment().getProperty("user.name"); @@ -153,7 +172,7 @@ NOTE: You can disable automatic refresh with this setting`spring.cloud.nacos.con === Support configurations at the profile level -When configurations are loaded by spring-cloud-starter-alibaba-nacos-config, basic configurations with dataid of `${spring.application.name}. ${file-extension:properties}` , and dataid of `${spring.application.name}-${profile}. ${file-extension:properties}` are also loaded. If you need to use different configurations from different environments, you can use the `${spring.profiles.active}` configuration provided by Spring. +When configurations are loaded by Nacos Config, basic configurations with DataId of `${spring.application.name}. ${file-extension:properties}` , and DataId of `${spring.application.name}-${profile}. ${file-extension:properties}` are also loaded. If you need to use different configurations from different environments, you can use the `${spring.profiles.active}` configuration provided by Spring. [source,properties] ---- @@ -162,7 +181,7 @@ spring.profiles.active=develop NOTE: When specified in configuration files, ${spring.profiles.active} must be placed in bootstrap.properties. -Add a basic configuration in Nacos, with a dataid of nacos-config-develop.yaml, as shown below: +Add a basic configuration in Nacos, with a DataId of nacos-config-develop.yaml, as shown below: [source,subs="normal"] ---- @@ -180,10 +199,10 @@ Run the following Spring Boot application testing code: [source,java] ---- @SpringBootApplication -public class ProviderApplication { +public class NacosConfigApplication { public static void main(String[] args) { - ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args); + ConfigurableApplicationContext applicationContext = SpringApplication.run(NacosConfigApplication.class, args); while(true) { String userName = applicationContext.getEnvironment().getProperty("user.name"); String userAge = applicationContext.getEnvironment().getProperty("user.age"); @@ -210,7 +229,7 @@ To switch to the production environment, you only need to change the parameter o spring.profiles.active=product ---- -At the same time, add the basic configuration with the dataid in the Nacos of your production environment. For example, you can add the configuration with the dataid of nacos-config-product.yaml in Nacos of your production environment: +At the same time, add the basic configuration with the DataId in the Nacos of your production environment. For example, you can add the configuration with the DataId of nacos-config-product.yaml in Nacos of your production environment: [source,subs="normal"] ---- @@ -318,4 +337,88 @@ we agree on the following priority between the shared configurations: Priorities NOTE: When using `spring.cloud.nacos.config.shared-dataids`, the data Id must have a file extension, and it could be properties or yaml/yml. And the configuration in `spring.cloud.nacos.config.file-extension` does not have any impact on the customized Data Id file extension. -NOTE: When `spring.cloud.nacos.config.refreshable-dataids` specifies the data ids that support dynamic refresh, the corresponding values of the data ids should also specify file extensions. \ No newline at end of file +NOTE: When `spring.cloud.nacos.config.refreshable-dataids` specifies the data ids that support dynamic refresh, the corresponding values of the data ids should also specify file extensions. + +=== Nacos Config Endpoint + +Nacos Config provides an Endpoint internally with a corresponding endpoint id of `nacos-config`. + +Endpoint exposed json contains three properties: + +1. Sources: Current application configuration data information + +2. RefreshHistory: Configuration refresh history + +3. NacosConfigProperties: Shows the current basic Nacos configurations of the current service + +The followings shows how a service instance accesses the Endpoint: + +[source,json,indent=0] +---- +{ + "NacosConfigProperties": { + "serverAddr": "127.0.0.1:8848", + "encode": null, + "group": "DEFAULT_GROUP", + "prefix": null, + "fileExtension": "properties", + "timeout": 3000, + "endpoint": null, + "namespace": null, + "accessKey": null, + "secretKey": null, + "contextPath": null, + "clusterName": null, + "name": null, + "sharedDataids": "base-common.properties,common.properties", + "refreshableDataids": "common.properties", + "extConfig": null + }, + "RefreshHistory": [{ + "timestamp": "2019-07-29 11:20:04", + "dataId": "nacos-config-example.properties", + "md5": "7d5d7f1051ff6571e2ec9f90887d9d91" + }], + "Sources": [{ + "lastSynced": "2019-07-29 11:19:04", + "dataId": "common.properties" + }, { + "lastSynced": "2019-07-29 11:19:04", + "dataId": "base-common.properties" + }, { + "lastSynced": "2019-07-29 11:19:04", + "dataId": "nacos-config-example.properties" + }] +} +---- + + +=== Disable Nacos Config AutoConfiguration + +set spring.cloud.nacos.config.enabled = false to disable Spring Cloud Nacos Config AutoConfiguration. + +=== More Information about Nacos Config Starter Configurations + +The following shows the other configurations of the starter of Nacos Config: + +:frame: topbot +[width="60%",options="header"] +|==== +^|Configuration ^|Key ^|Default Value ^|Description +|Server address|`spring.cloud.nacos.config.server-addr`|| IP and port of the Nacos Server listener +|Dataid from nacos config|`spring.cloud.nacos.config.name`|| First take the prefix, then go to the name, and finally take spring.application.name +|Dataid from nacos config|`spring.cloud.nacos.config.prefix`|| First take the prefix, then go to the name, and finally take spring.application.name +|Encode for nacos config content|`spring.cloud.nacos.config.encode`||Encode for nacos config content +|GROUP for nacos config|`spring.cloud.nacos.config.group`|`DEFAULT_GROUP`|GROUP for nacos config +|The suffix of nacos config dataId, also the file extension of config content.|`spring.cloud.nacos.config.fileExtension`|`properties`|The suffix of nacos config dataId, also the file extension of config content(now support properties or yaml(yml)) +|Timeout for get config from nacos|`spring.cloud.nacos.config.timeout`|`3000`|Timeout for get config from nacos +|Endpoint|`spring.cloud.nacos.config.endpoint`||Endpoint +|Namespace|`spring.cloud.nacos.config.namespace`||Namespace +|AccessKey|`spring.cloud.nacos.config.accessKey`||Alibaba Cloud account accesskey +|SecretKey|`spring.cloud.nacos.config.secretKey`||Alibaba Cloud account secretkey +|The context path of Nacos Server|`spring.cloud.nacos.config.contextPath`||The context path of Nacos Server +|Cluster name|`spring.cloud.nacos.config.clusterName`||Cluster name +|Dataid for Shared Configuration|`spring.cloud.nacos.config.sharedDataids`||Dataid for Shared Configuration, split by "," +|Dynamic refresh dataid for Shared Configuration|`spring.cloud.nacos.config.refreshableDataids`||Dynamic refresh dataid for Shared Configuration, split by "," +|custom dataid|`spring.cloud.nacos.config.extConfig`||It's a List,build up by `Config` POJO. `Config` has 3 attributes, `dataId`, `group` and `refresh` +|==== diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc index 16bb6acc..3ad13376 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/nacos-discovery.adoc @@ -1,15 +1,16 @@ == Spring Cloud Alibaba Nacos Discovery -This project provides seamless integration with Nacos for Spring Boot applications in terms of service registration and discovery through automatic configurations and other standard usages of the Spring programming model. -Nacos has endured the tough tests of the Double 11 Shopping festivals for years. By adding a few simple annotations, you can register a service quickly and use Nacos as the service registration center for a large scale distributed system. +Nacos is an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications. -=== Service Registration and Discovery: Nacos Discovery Starter +With Spring Cloud Alibaba Nacos Discovery, you can quickly access the Nacos service registration feature based on Spring Cloud's programming model. + +=== Service Registration/Discovery: Nacos Discovery Service discovery is one of the key components in the microservices architecture. In such a architecture, configuring a service list for every client manually could be a daunting task, and makes dynamic scaling extremely difficult. - Nacos Discovery Starter helps you to register your service to the Nacos server automatically, and the Nacos server keeps track of the services and refreshes the service list dynamically. In addition, Nacos -Discovery Starter registers some of the metadata of the service instance, such as host, port, health check URL, homepage to Nacos. For details about how to download and start Nacos, refer to the https://nacos.io/zh-cn/docs/quick-start.html[Nacos Website]。 + Nacos Discovery helps you to register your service to the Nacos server automatically, and the Nacos server keeps track of the services and refreshes the service list dynamically. In addition, Nacos +Discovery registers some of the metadata of the service instance, such as host, port, health check URL, homepage to Nacos. For details about how to download and start Nacos, refer to the https://nacos.io/zh-cn/docs/quick-start.html[Nacos Website]. -==== How to Introduce Nacos Discovery Starter +=== How to Introduce Nacos Discovery for service registration/discovery please use the starter with the group ID as `com.alibaba.cloud` and the artifact ID as `spring-cloud-starter-alibaba-nacos-discovery`. @@ -21,11 +22,26 @@ please use the starter with the group ID as `com.alibaba.cloud` and the artifact ---- +=== An example of using Nacos Discovery for service registration/discovery and call + +Nacos Discovery integrate with the Netflix Ribbon, RestTemplate or OpenFeign can be used for service-to-service calls. + +==== Nacos Server Startup + +For details about how to download and start Nacos, refer to the https://nacos.io/zh-cn/docs/quick-start.html[Nacos Website]. + +After Nacos Server starts, go to http://ip:8848 to view the console (default account name/password is nacos/nacos): + +.Nacos Dashboard +image::https://img.alicdn.com/tfs/TB1XEfwbQH0gK0jSZPiXXavapXa-2790-1060.png[] + +For more Nacos Server versions, you can download the latest version from https://github.com/alibaba/nacos/releases[release page]. + ==== Start a Provider Application 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: +* Configuration of pom.xml The following is a complete example of pom.xml: [source, xml] ---- @@ -61,7 +77,7 @@ The following sample illustrates how to register a service to Nacos. import - org.springframework.cloud + com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom @@ -98,12 +114,12 @@ The following sample illustrates how to register a service to Nacos. ---- -2. Configuration of application.properties Some of the basic configurations of Nacos must be included in application.properties(or application.yaml), as shown below: +* Configuration of application.properties Some of the basic configurations of Nacos must be included in application.properties(or application.yaml), as shown below: application.properties [source,properties] ---- server.port=8081 -spring.application.name=nacos-producer +spring.application.name=nacos-provider spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848 management.endpoints.web.exposure.include=* ---- @@ -111,7 +127,7 @@ management.endpoints.web.exposure.include=* NOTE: If you do not want to use Nacos for service registration and discovery, you can set `spring.cloud.nacos.discovery` to `false`. -3. The following is a sample for starting Provider: +* The following is a sample for starting Provider: [source,java,indent=0] ---- @SpringBootApplication @@ -119,7 +135,7 @@ NOTE: If you do not want to use Nacos for service registration and discovery, yo public class NacosProviderDemoApplication { public static void main(String[] args) { - SpringApplication.run(NacosProducerDemoApplication.class, args); + SpringApplication.run(NacosProviderDemoApplication.class, args); } @RestController @@ -136,15 +152,78 @@ Now you can see the registered services on the Nacos console. NOTE: Before you start the provider application, please start Nacos first. Refer to https://nacos.io/zh-cn/docs/quick-start.html[Naco Website] for more details. -=== Service EndPoint +==== Start a Consumer Application -spring-cloud-starter-alibaba-nacos-discovery provides an EndPoint, and the address is `http://ip:port/actuator/nacos-discovery`. -The EndPoint mainly provides two types of information: +It might not be as easy as starting a provider application, because the consumer needs to call the RESTful service of the provider. In this example, we will use the most primitive way, that is, +combining the LoadBalanceClient and RestTemolate explicitly to access the RESTful service. +You can refer to section 1.2 for pom.xml and application.properties configurations. The following is the sample code for starting a consumer application. - 1. Subscribe: Shows the current service subscribers - 2. NacosDiscoveryProperties: Shows the current basic Nacos configurations of the current service +NOTE: You can also access the service by using RestTemplate and FeignClient with load balancing. -The followings shows how a service instance accesses the EndPoint: +[source, java] +---- +@SpringBootApplication +@EnableDiscoveryClient +public class NacosConsumerApp { + + @RestController + public class NacosController{ + + @Autowired + private LoadBalancerClient loadBalancerClient; + @Autowired + private RestTemplate restTemplate; + + @Value("${spring.application.name}") + private String appName; + + @GetMapping("/echo/app-name") + public String echoAppName(){ + //Access through the combination of LoadBalanceClient and RestTemplate + ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-provider"); + String path = String.format("http://%s:%s/echo/%s",serviceInstance.getHost(),serviceInstance.getPort(),appName); + System.out.println("request path:" +path); + return restTemplate.getForObject(path,String.class); + } + + } + + //Instantiate RestTemplate Instance + @Bean + public RestTemplate restTemplate(){ + + return new RestTemplate(); + } + + public static void main(String[] args) { + + SpringApplication.run(NacosConsumerApp.class,args); + } +} +---- + +In this example, we injected a LoadBalancerClient instance, and instantiated a RestTemplate manually. At the same time, we injected the configuration value of `spring.application.name` into the application, +so that the current application name can be displayed when calling the service of the provider. + +NOTE: Please start Nacos before you start the consumer application. For details, please refer to https://nacos.io/zh-cn/docs/quick-start.html[Nacos Website]. + +Next, access the `http://ip:port/echo/app-name` interface provided by the consumer. Here we started the port of 8082. The access result is shown below: + + Address:http://127.0.0.1:8082/echo/app-name + Access result: Hello Nacos Discovery nacos-consumer + + +=== Nacos Discovery Endpoint + +Nacos Discovery provides an Endpoint internally with a corresponding endpoint id of `nacos-discovery`. + +Endpoint exposed json contains two properties: + +1. subscribe: Shows the current service subscribers + +2. NacosDiscoveryProperties: Shows the current basic Nacos configurations of the current service + +The followings shows how a service instance accesses the Endpoint: [source, json] ---- @@ -185,7 +264,7 @@ The followings shows how a service instance accesses the EndPoint: "lastRefTime": 1541755293119, "checksum": "e5a699c9201f5328241c178e804657e11541755293119", "allIPs": false, - "key": "nacos-producer", + "key": "nacos-provider", "valid": true } ], @@ -211,86 +290,28 @@ The followings shows how a service instance accesses the EndPoint: } ---- -=== Start a Consumer Application +=== More Information about Nacos Discovery Starter Configurations -It might not be as easy as starting a provider application, because the consumer needs to call the RESTful service of the provider. In this example, we will use the most primitive way, that is, -combining the LoadBalanceClient and RestTemolate explicitly to access the RESTful service. -You can refer to section 1.2 for pom.xml and application.properties configurations. The following is the sample code for starting a consumer application. - -NOTE: You can also access the service by using RestTemplate and FeignClient with load balancing. - -[source, java] ----- -@SpringBootApplication -@EnableDiscoveryClient -public class NacosConsumerApp { - - @RestController - public class NacosController{ - - @Autowired - private LoadBalancerClient loadBalancerClient; - @Autowired - private RestTemplate restTemplate; - - @Value("${spring.application.name}") - private String appName; - - @GetMapping("/echo/app-name") - public String echoAppName(){ - //Access through the combination of LoadBalanceClient and RestTemolate - ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-provider"); - String path = String.format("http://%s:%s/echo/%s",serviceInstance.getHost(),serviceInstance.getPort(),appName); - System.out.println("request path:" +path); - return restTemplate.getForObject(path,String.class); - } - - } - - //Instantiate RestTemplate Instance - @Bean - public RestTemplate restTemplate(){ - - return new RestTemplate(); - } - - public static void main(String[] args) { - - SpringApplication.run(NacosConsumerApp.class,args); - } -} ----- - -In this example, we injected a LoadBalancerClient instance, and instantiated a RestTemplate manually. At the same time, we injected the configuration value of `spring.application.name` into the application, -so that the current application name can be displayed when calling the service of the provider. - -NOTE: Please start Nacos before you start the consumer application. For details, please refer to https://nacos.io/zh-cn/docs/quick-start.html[Nacos Website]. - -Next, access the `http://ip:port/echo/app-name` interface provided by the consumer. Here we started the port of 8082. The access result is shown below: - - Address:http://127.0.0.1:8082/echo/app-name - Access result: Hello Nacos Discovery nacos-consumer - -=== More Information about Nacos Starter Configurations - -The following shows the other configurations of the starter of spring-cloud-starter-alibaba-nacos-discovery: +The following shows the other configurations of the starter of Nacos Discovery: :frame: topbot [width="60%",options="header"] |==== ^|Configuration ^|Key ^|Default Value ^|Description -|`Server address`|`spring.cloud.nacos.discovery.server-addr`|`No default value`|`IP and port of the Nacos Server listener` -|`Service name`|`spring.cloud.nacos.discovery.service`|`${spring.application.name}`|`Name the current service` -|`Weight`|`spring.cloud.nacos.discovery.weight`|`1`|`Value range: 1 to 100. The bigger the value, the greater the weight` -|`Network card name`|`spring.cloud.nacos.discovery.network-interface`|`No default value`|`If the IP address is not specified, the registered IP address is the IP address of the network card. If this is not specified either, the IP address of the first network card will be used by default.` -|`Registered IP address`|`spring.cloud.nacos.discovery.ip`|`No default value`|`Highest priority` -|`Registered port`|`spring.cloud.nacos.discovery.port`|`-1`|`Will be detected automatically by default. Do not need to be configured.` -|`Namespace`|`spring.cloud.nacos.discovery.namespace`|`No default value`|`A typical scenario is to isolate the service registration for different environment, such as resource (configurations, services etc.) isolation between testing and production environment` ` -|`AccessKey`|`spring.cloud.nacos.discovery.access-key`|`No default value`|`Alibaba Cloud account` -|`SecretKey`|`spring.cloud.nacos.discovery.secret-key`|`No default value`|`Alibaba Cloud account accesskey` -|`Metadata`|`spring.cloud.nacos.discovery.metadata`|`No default value`|`You can define some of the metadata for your services in the Map format` -|`Log file name`|`spring.cloud.nacos.discovery.log-name`|`No default value`| -|`Endpoint`|`spring.cloud.nacos.discovery.enpoint`|`UTF-8`|`The domain name of a certain service in a specific region. You can retrieve the server address dynamically with this domain name` -|`Integrate Ribbon or not`|`ribbon.nacos.enabled`|`true`|`Set to true in most cases` +|Server address|`spring.cloud.nacos.discovery.server-addr`||IP and port of the Nacos Server listener +|Service name|`spring.cloud.nacos.discovery.service`|`${spring.application.name}`|Name the current service +|Weight|`spring.cloud.nacos.discovery.weight`|`1`|Value range: 1 to 100. The bigger the value, the greater the weight +|Network card name|`spring.cloud.nacos.discovery.network-interface`||If the IP address is not specified, the registered IP address is the IP address of the network card. If this is not specified either, the IP address of the first network card will be used by default. +|Registered IP address|`spring.cloud.nacos.discovery.ip`||Highest priority +|Registered port|`spring.cloud.nacos.discovery.port`|`-1`|Will be detected automatically by default. Do not need to be configured. +|Namespace|`spring.cloud.nacos.discovery.namespace`||A typical scenario is to isolate the service registration for different environment, such as resource (configurations, services etc.) isolation between testing and production environment +|AccessKey|`spring.cloud.nacos.discovery.access-key`||Alibaba Cloud account accesskey +|SecretKey|`spring.cloud.nacos.discovery.secret-key`||Alibaba Cloud account secretkey +|Metadata|`spring.cloud.nacos.discovery.metadata`||You can define some of the metadata for your services in the Map format +|Log file name|`spring.cloud.nacos.discovery.log-name`|| +|Cluster Name|`spring.cloud.nacos.discovery.cluster-name`|`DEFAULT`|Cluster name of Nacos +|Endpoint|`spring.cloud.nacos.discovery.endpoint`||The domain name of a certain service in a specific region. You can retrieve the server address dynamically with this domain name +|Integrate Ribbon or not|`ribbon.nacos.enabled`|`true`|Set to true in most cases +|Enable Nacos Watch|`spring.cloud.nacos.discovery.watch.enabled`|`true`|set to false to close watch |==== diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/oss.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/oss.adoc index 7a59c11c..a44fd72d 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/oss.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/oss.adoc @@ -11,7 +11,7 @@ We’ve released Spring Cloud Alibaba version 0.2.1. You will need to add depend - org.springframework.cloud + com.alibaba.cloud spring-cloud-alibaba-dependencies 0.2.2.BUILD-SNAPSHOT pom @@ -154,4 +154,4 @@ spring.cloud.alicloud.oss.config.max-connections=1000 For more configurations, refer to the table at the bottom of https://help.aliyun.com/document_detail/32010.html[OSSClient Configurations]. -NOTE: In most cases, you need to connect the parameter names with “-” for the parameters in the table of https://help.aliyun.com/document_detail/32010.html[OSSClient Configurations] with “-”, and all letters should be in lowercase. For example, ConnectionTimeout should be changed to connection-timeout. \ No newline at end of file +NOTE: In most cases, you need to connect the parameter names with “-” for the parameters in the table of https://help.aliyun.com/document_detail/32010.html[OSSClient Configurations] with “-”, and all letters should be in lowercase. For example, ConnectionTimeout should be changed to connection-timeout. diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/rocketmq.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/rocketmq.adoc index 48d7f750..9ebe4212 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/rocketmq.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/rocketmq.adoc @@ -86,7 +86,7 @@ There are two concepts in Spring Cloud Stream: Binder and Binding For example, `Kafka` uses `KafkaMessageChannelBinder`, `RabbitMQ` uses `RabbitMessageChannelBinder`, while `RocketMQ` uses `RocketMQMessageChannelBinder`. -* Binding: Includes Input Binding and Output Binding。 +* Binding: Includes Input Binding and Output Binding. Binding serves as a bridge between message middleware and the provider and consumer of the applications. Developers only need to use the Provider or Consumer to produce or consume data, and do not need to worry about the interactions with the message middleware. @@ -136,6 +136,11 @@ Alternatively, you can also use the Spring Cloud Stream RocketMQ Starter: ### How Spring Cloud Alibaba RocketMQ Binder Works +This is the implementation architecture of Spring Cloud Stream RocketMQ Binder: + +.SCS RocketMQ Binder +image::https://img.alicdn.com/tfs/TB1v8rcbUY1gK0jSZFCXXcwqXXa-1236-773.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: @@ -165,12 +170,64 @@ Message message = builder.build(); output().send(message); ``` +### Support MessageSource + +SCS RocketMQ Binder support `MessageSource`,which can receive messages by pull mode: + +```java +@SpringBootApplication +@EnableBinding(MQApplication.PolledProcessor.class) +public class MQApplication { + + private final Logger logger = + LoggerFactory.getLogger(MQApplication.class); + + public static void main(String[] args) { + SpringApplication.run(MQApplication.class, args); + } + + @Bean + public ApplicationRunner runner(PollableMessageSource source, + MessageChannel dest) { + return args -> { + while (true) { + boolean result = source.poll(m -> { + String payload = (String) m.getPayload(); + logger.info("Received: " + payload); + dest.send(MessageBuilder.withPayload(payload.toUpperCase()) + .copyHeaders(m.getHeaders()) + .build()); + }, new ParameterizedTypeReference() { }); + if (result) { + logger.info("Processed a message"); + } + else { + logger.info("Nothing to do"); + } + Thread.sleep(5_000); + } + }; + } + + public static interface PolledProcessor { + + @Input + PollableMessageSource source(); + + @Output + MessageChannel dest(); + + } + +} +``` + ### Configuration Options #### RocketMQ Binder Properties spring.cloud.stream.rocketmq.binder.name-server:: -The name server of RocketMQ Server. +The name server of RocketMQ Server(Older versions use the namesrv-addr configuration item). + Default: `127.0.0.1:9876`. spring.cloud.stream.rocketmq.binder.access-key:: diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/sentinel.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/sentinel.adoc index da1a4f2d..5545f82f 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/sentinel.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/sentinel.adoc @@ -18,7 +18,7 @@ If you want to use Sentinel in your project, please use the starter with the gro ```xml - org.springframework.cloud + com.alibaba.cloud spring-cloud-starter-alibaba-sentinel ``` @@ -51,6 +51,31 @@ The @SentinelResource annotation is used to identify if a resource is rate limit @SentinelResource also provides attributes such as `blockHandler`, `blockHandlerClass`, and `fallback` to identify rate limiting or degradation operations. For more details, refer to https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81[Sentinel Annotation Support]. +The above examples are all used in the WebServlet environment. Sentinel currently supports WebFlux and needs to cooperate with the `spring-boot-starter-webflux` dependency to trigger the WebFlux-related automation configuration in sentinel starter. + +```java +@SpringBootApplication +public class Application { + + public static void main(String[] args) { + SpringApplication.run(ServiceApplication.class, args); + } + +} + +@RestController +public class TestController { + + @GetMapping("/mono") + @SentinelResource("hello") + public Mono mono() { + return Mono.just("simple string") + .transform(new SentinelReactorTransformer<>("otherResourceName")); + } + +} +``` + ##### Sentinel Dashboard Sentinel dashboard is a lightweight console that provides functions such as machine discovery, single-server resource monitoring, overview of cluster resource data, as well as rule management. To use these features, you only need to complete a few steps. @@ -98,12 +123,12 @@ The port number specified in `spring.cloud.sentinel.transport.port` will start a For more information about Sentinel dashboard, please refer to https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0[Sentinel Dashboard]. -### Feign Support +### OpenFeign Support -Sentinel is compatible with the https://github.com/OpenFeign/feign[Feign] component. To use it, in addition to introducing the `sentinel-starter` dependency, complete the following 2 steps: +Sentinel is compatible with the https://github.com/OpenFeign/feign[OpenFeign] component. To use it, in addition to introducing the `sentinel-starter` dependency, complete the following 2 steps: * Enable the Sentinel support for feign in the properties file. `feign.sentinel.enabled=true` -* Add the `feign starter` dependency to trigger and enable `sentinel starter`: +* Add the `openfeign starter` dependency to trigger and enable `sentinel starter`: ```xml org.springframework.cloud @@ -177,11 +202,11 @@ It will return `RestTemplate request block by sentinel` when you using `RestTemp Sentinel RestTemplate provides two granularities for resource rate limiting: -* `schema://host:port/path`: Protocol, host, port and path +* `httpmethod:schema://host:port/path`: Protocol, host, port and path -* `schema://host:port`: Protocol, host and port +* `httpmethod:schema://host:port`: Protocol, host and port -NOTE: Take `https://www.taobao.com/test` as an example. The corresponding resource names have two levels of granularities, `https://www.taobao.com` and `https://www.taobao.com/test`. +NOTE: Take Http GET `https://www.taobao.com/test` as an example. The corresponding resource names have two levels of granularities, `GET:https://www.taobao.com` and `GET:https://www.taobao.com/test`. ### Dynamic Data Source Support @@ -199,8 +224,8 @@ spring.cloud.sentinel.datasource.ds1.file.rule-type=flow #spring.cloud.sentinel.datasource.ds1.file.rule-type=flow spring.cloud.sentinel.datasource.ds2.nacos.server-addr=localhost:8848 -spring.cloud.sentinel.datasource.ds2.nacos.dataId=sentinel -spring.cloud.sentinel.datasource.ds2.nacos.groupId=DEFAULT_GROUP +spring.cloud.sentinel.datasource.ds2.nacos.data-id=sentinel +spring.cloud.sentinel.datasource.ds2.nacos.group-id=DEFAULT_GROUP spring.cloud.sentinel.datasource.ds2.nacos.data-type=json spring.cloud.sentinel.datasource.ds2.nacos.rule-type=degrade @@ -211,7 +236,7 @@ spring.cloud.sentinel.datasource.ds3.zk.rule-type=authority spring.cloud.sentinel.datasource.ds4.apollo.namespace-name = application spring.cloud.sentinel.datasource.ds4.apollo.flow-rules-key = sentinel spring.cloud.sentinel.datasource.ds4.apollo.default-flow-rule-value = test -spring.cloud.sentinel.datasource.ds5.apollo.rule-type=param-flow +spring.cloud.sentinel.datasource.ds4.apollo.rule-type=param-flow ``` This method follows the configuration of Spring Cloud Stream Binder. `TreeMap` is used for storage internally, and comparator is `String.CASE_INSENSITIVE_ORDER`. @@ -222,29 +247,162 @@ Every data source has 3 common configuration items: `data-type`, `converter-clas `data-type` refers to `Converter`. Spring Cloud Alibaba Sentinel provides two embedded values by default: `json` and `xml` (the default is json if not specified). If you do not want to use the embedded `json` or `xml` `Converter`, you can also fill in `custom` to indicate that you will define your own `Converter`, and then configure the `converter-class`. You need to specify the full path of the class for this configuration. -`rule-type` refers to the rule type in datasource(`flow`, `degrade`, `authority`, `system`, `param-flow`). - -If the data source takes effect and is loaded successfully, the dashboard will print information as shown below: - -``` -[Sentinel Starter] DataSource ds1-sentinel-file-datasource load 3 DegradeRule -[Sentinel Starter] DataSource ds2-sentinel-nacos-datasource load 2 FlowRule -``` +`rule-type` refers to the rule type in datasource(`flow`,`degrade`,`authority`,`system`, `param-flow`, `gw-flow`, `gw-api-group`). NOTE: XML format is not supported by default. To make it effective, you need to add the `jackson-dataformat-xml` dependency. To learn more about how dynamic data sources work in Sentinel, refer to https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95[Dynamic Rule Extension]. -### Endpoint Support +### Support Zuul -Before you use the Endpoint feature, please add the `spring-boot-starter-actuator` dependency in Maven, and enable access of Endpoints in your configuration. +https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81[参考 Sentinel 网关限流] -* Add `management.security.enabled=false` in Spring Boot 1.x. The exposed endpoint path is `/sentinel`. -* Add `management.endpoints.web.exposure.include=*` in Spring Boot 2.x. The exposed endpoint path is `/actuator/sentinel`. +If you want to use Sentinel Starter with Zuul, you need to add the `spring-cloud-alibaba-sentinel-gateway` dependency, and you need to add the `spring-cloud-starter-netflix-zuul` dependency to let Zuul AutoConfiguration class in the gateway module takes effect: -The information exposed in Sentinel Endpoint is very useful. It includes all the rules of the current application, the log directory, the IP of the current instance, the Sentinel Dashboard address, the Block Page, the heartbeat frequency of the application and the Sentinel Dashboard, and so on. +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + -### More + + com.alibaba.cloud + spring-cloud-alibaba-sentinel-gateway + + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + +``` + +### Support Spring Cloud Gateway + +https://github.com/alibaba/Sentinel/wiki/%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81[参考 Sentinel 网关限流] + +If you want to use Sentinel Starter with Spring Cloud Gateway, you need to add the `spring-cloud-alibaba-sentinel-gateway` dependency and add the `spring-cloud-starter-gateway` dependency to let Spring Cloud Gateway AutoConfiguration class in the module takes effect: + +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + com.alibaba.cloud + spring-cloud-alibaba-sentinel-gateway + + + + org.springframework.cloud + spring-cloud-starter-gateway + +``` + +### Sentinel Endpoint + +Sentinel provides an Endpoint internally with a corresponding endpoint id of `sentinel`. + +Endpoint exposed json contains multi properties: + +1. appName: application name +2. logDir: the directory of log +3. logUsePid: log name with pid ot not +4. blockPage: redirect page after sentinel block +5. metricsFileSize: the size of metrics file +6. metricsFileCharset: metrics file charset +7. totalMetricsFileCount: the total file count of of metrics file +8. consoleServer: sentinel dashboard address +9. clientIp: client ip +10. heartbeatIntervalMs: client heartbeat interval with dashboard +11. clientPort: the client needs to expose the port to interact with the dashboard +12. coldFactor: cold factor +13. filter: CommonFilter related properties, such as order, urlPatterns and enable +14. datasource: datasource configuration info by client +15. rules: the rule that the client takes effect internally contains flowRules, degradeRules, systemRules, authorityRule, paramFlowRule + +The followings shows how a service instance accesses the Endpoint: + +[source,json,indent=0] +---- +{ + "blockPage": null, + "appName": "sentinel-example", + "consoleServer": "localhost:8080", + "coldFactor": "3", + "rules": { + "flowRules": [{ + "resource": "GET:http://www.taobao.com", + "limitApp": "default", + "grade": 1, + "count": 0.0, + "strategy": 0, + "refResource": null, + "controlBehavior": 0, + "warmUpPeriodSec": 10, + "maxQueueingTimeMs": 500, + "clusterMode": false, + "clusterConfig": null + }, { + "resource": "/test", + "limitApp": "default", + "grade": 1, + "count": 0.0, + "strategy": 0, + "refResource": null, + "controlBehavior": 0, + "warmUpPeriodSec": 10, + "maxQueueingTimeMs": 500, + "clusterMode": false, + "clusterConfig": null + }, { + "resource": "/hello", + "limitApp": "default", + "grade": 1, + "count": 1.0, + "strategy": 0, + "refResource": null, + "controlBehavior": 0, + "warmUpPeriodSec": 10, + "maxQueueingTimeMs": 500, + "clusterMode": false, + "clusterConfig": null + }] + }, + "metricsFileCharset": "UTF-8", + "filter": { + "order": -2147483648, + "urlPatterns": ["/*"], + "enabled": true + }, + "totalMetricsFileCount": 6, + "datasource": { + "ds1": { + "file": { + "dataType": "json", + "ruleType": "FLOW", + "converterClass": null, + "file": "...", + "charset": "utf-8", + "recommendRefreshMs": 3000, + "bufSize": 1048576 + }, + "nacos": null, + "zk": null, + "apollo": null, + "redis": null + } + }, + "clientIp": "30.5.121.91", + "clientPort": "8719", + "logUsePid": false, + "metricsFileSize": 52428800, + "logDir": "...", + "heartbeatIntervalMs": 10000 +} +---- + +### Configuration The following table shows that when there are corresponding bean types in `ApplicationContext`, some actions will be taken: @@ -263,12 +421,13 @@ The following table shows all the configurations of Spring Cloud Alibaba Sentine [width="60%",options="header"] |==== ^|Configuration ^|Description ^|Default Value +|`spring.application.name` or `project.name`|Project Name Of Sentinel| |`spring.cloud.sentinel.enabled`|Whether Sentinel automatic configuration takes effect|true -|`spring.cloud.sentinel.eager`|Cancel Sentinel dashboard lazy load|false +|`spring.cloud.sentinel.eager`|Whether to trigger Sentinel initialization in advance|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|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| +|`spring.cloud.sentinel.transport.client-ip`|The client IP of this configuration will be registered to the Sentinel Server side.| |`spring.cloud.sentinel.filter.order`|Loading order of Servlet Filter. The filter will be constructed in the Starter|Integer.MIN_VALUE |`spring.cloud.sentinel.filter.url-patterns`|Data type is array. Refers to the collection of Servlet Filter ULR patterns|/* |`spring.cloud.sentinel.filter.enabled`|Enable to instance CommonFilter|true @@ -279,6 +438,15 @@ The following table shows all the configurations of Spring Cloud Alibaba Sentine |`spring.cloud.sentinel.log.switch-pid`|If PID is required for Sentinel log file names|false |`spring.cloud.sentinel.servlet.blockPage`| Customized redirection URL. When rate limited, the request will be redirected to the pre-defined URL | |`spring.cloud.sentinel.flow.coldFactor`| https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8[ColdFactor] |3 +|`spring.cloud.sentinel.zuul.order.pre`| The order of SentinelZuulPreFilter | 10000 +|`spring.cloud.sentinel.zuul.order.post`| The order of SentinelZuulPostFilter | 1000 +|`spring.cloud.sentinel.zuul.order.error`| The order of SentinelZuulErrorFilter | -1 +|`spring.cloud.sentinel.scg.fallback.mode`| Response mode after Spring Cloud Gateway circuit break (select `redirect` or `response`) | +|`spring.cloud.sentinel.scg.fallback.redirect`| Spring Cloud Gateway response mode is the redirect URL corresponding to 'redirect' mode | +|`spring.cloud.sentinel.scg.fallback.response-body`| Spring Cloud Gateway response mode is response content corresponding to 'response' mode | +|`spring.cloud.sentinel.scg.fallback.response-status`| Spring Cloud Gateway response mode is the response code corresponding to 'response' mode | 429 +|`spring.cloud.sentinel.scg.fallback.content-type`| The Spring Cloud Gateway response mode is the content-type corresponding to the 'response' mode. | application/json |==== -NOTE: These configurations will only take effect in servlet environment. RestTemplate and Feign will not take effect for these configurations. \ No newline at end of file + +NOTE: These configurations will only take effect in servlet environment. RestTemplate and Feign will not take effect for these configurations. diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/sms.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/sms.adoc index 1bc6129d..40a5826f 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/sms.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/sms.adoc @@ -2,9 +2,9 @@ SMS(Short Message Service)is a messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers. -Spring Cloud AliCloud SMS provide an easier-to-use API for quick access to Alibaba Cloud's SMS service based on Spring Cloud Alibaba SMS. +Spring Cloud Alibaba Cloud SMS provide an easier-to-use API for quick access to Alibaba Cloud's SMS service based on Spring Cloud Alibaba SMS. -=== 如何引入 Spring Cloud AliCloud SMS +=== How to Introduce Spring Cloud Alibaba Cloud SMS If you want to use SMS in your project, please use the starter with the group ID as `com.alibaba.cloud` and the artifact ID as `spring-cloud-starter-alicloud-sms`. @@ -195,7 +195,7 @@ spring.cloud.alicloud.sms.up-queue-name=Alicom-Queue-********-SmsUp ---- @Component public class SmsUpMessageListener - implements SmsUpMessageListener { + implements org.springframework.cloud.alicloud.sms.SmsUpMessageListener { @Override public boolean dealMessage(Message message) { diff --git a/spring-cloud-alibaba-docs/src/main/asciidoc/spring-cloud-alibaba.adoc b/spring-cloud-alibaba-docs/src/main/asciidoc/spring-cloud-alibaba.adoc index 5018c2ca..884e0d56 100644 --- a/spring-cloud-alibaba-docs/src/main/asciidoc/spring-cloud-alibaba.adoc +++ b/spring-cloud-alibaba-docs/src/main/asciidoc/spring-cloud-alibaba.adoc @@ -1,17 +1,13 @@ [[spring-cloud-alibaba-reference]] = Spring Cloud Alibaba Reference Documentation -xiaojing; xiaolongzuo; jim fang; bingting peng; wangyuxin +Jim Fang, Jing Xiao, Mercy Ma, Xiaolong Zuo, Bingting Peng, Yuxin Wang :doctype: book :toc: :toclevels: 4 :source-highlighter: prettify :numbered: -== Introduction - -Spring Cloud Alibaba aims to provide a one-stop solution for microservices development. This prjoect includes the required components for developing distributed applications and services, so that developers can develop distributed applications easily with the Spring Cloud programming models. - -With Spring Cloud Alibaba, you only need to add a few annotations and configurations, and you will be able to use the distributed solutions of Alibaba for your applications, and build a distributed system of your own with Alibaba middleware. +include::introduction.adoc[] include::dependency-management.adoc[] @@ -21,6 +17,8 @@ include::nacos-config.adoc[] include::sentinel.adoc[] +include::dubbo.adoc[] + include::rocketmq.adoc[] include::ans.adoc[] @@ -30,3 +28,5 @@ include::acm.adoc[] include::oss.adoc[] include::schedulerx.adoc[] + +include::sms.adoc[] \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/pom.xml b/spring-cloud-alibaba-dubbo/pom.xml index 36a9f4ac..b71a42e5 100644 --- a/spring-cloud-alibaba-dubbo/pom.xml +++ b/spring-cloud-alibaba-dubbo/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0 @@ -14,7 +14,8 @@ Spring Cloud Alibaba Dubbo - 2.7.1 + 2.7.3 + 2.7.1 2.1.2.RELEASE 2.1.2.RELEASE 4.0.1 @@ -178,14 +179,15 @@ org.apache.dubbo dubbo-spring-boot-starter - ${dubbo.version} + ${dubbo-spring-boot.version} - + org.apache.dubbo dubbo-spring-boot-actuator - ${dubbo.version} + ${dubbo-spring-boot.version} + true diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/DubboMetadataEndpointAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/DubboMetadataEndpointAutoConfiguration.java index 0cb46e70..f992365e 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/DubboMetadataEndpointAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/DubboMetadataEndpointAutoConfiguration.java @@ -20,25 +20,26 @@ import org.springframework.boot.actuate.autoconfigure.endpoint.condition.Conditi import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import com.alibaba.cloud.dubbo.actuate.endpoint.DubboRestMetadataEndpoint; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; +import com.alibaba.cloud.dubbo.actuate.endpoint.DubboRestMetadataEndpoint; + /** * Dubbo Metadata Endpoints Auto-{@link Configuration} + * + * @author Mercy */ @ConditionalOnClass(name = "org.springframework.boot.actuate.endpoint.annotation.Endpoint") @PropertySource(value = "classpath:/META-INF/dubbo/default/actuator-endpoints.properties") @ManagementContextConfiguration public class DubboMetadataEndpointAutoConfiguration { - @Bean - @ConditionalOnMissingBean - @ConditionalOnEnabledEndpoint - public DubboRestMetadataEndpoint dubboRestMetadataEndpoint() { - return new DubboRestMetadataEndpoint(); - } + @Bean + @ConditionalOnMissingBean + @ConditionalOnEnabledEndpoint + public DubboRestMetadataEndpoint dubboRestMetadataEndpoint() { + return new DubboRestMetadataEndpoint(); + } } - - diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/endpoint/DubboRestMetadataEndpoint.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/endpoint/DubboRestMetadataEndpoint.java index 38794246..e04e246d 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/endpoint/DubboRestMetadataEndpoint.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/actuate/endpoint/DubboRestMetadataEndpoint.java @@ -16,24 +16,27 @@ */ package com.alibaba.cloud.dubbo.actuate.endpoint; +import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; -import com.alibaba.cloud.dubbo.service.DubboMetadataService; -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; +import com.alibaba.cloud.dubbo.service.DubboMetadataService; /** * Dubbo Rest Metadata {@link Endpoint} + * + * @author Mercy */ @Endpoint(id = "dubborestmetadata") public class DubboRestMetadataEndpoint { - @Autowired - private DubboMetadataService dubboMetadataService; + @Autowired + private DubboMetadataService dubboMetadataService; - @ReadOperation(produces = APPLICATION_JSON_UTF8_VALUE) - public String get() { - return dubboMetadataService.getServiceRestMetadata(); - } + @ReadOperation(produces = APPLICATION_JSON_UTF8_VALUE) + public String get() { + return dubboMetadataService.getServiceRestMetadata(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/annotation/DubboTransported.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/annotation/DubboTransported.java index d1236b3e..23dcf34f 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/annotation/DubboTransported.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/annotation/DubboTransported.java @@ -16,7 +16,14 @@ */ package com.alibaba.cloud.dubbo.annotation; -import org.apache.dubbo.common.Constants; +import static org.apache.dubbo.rpc.cluster.Constants.DEFAULT_RETRIES; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.rpc.ExporterListener; import org.apache.dubbo.rpc.Filter; @@ -25,126 +32,124 @@ import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.client.RestTemplate; -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -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: + * {@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: *
    *
  1. {@link FeignClient @FeignClient} annotated classes: *
      - * If {@link DubboTransported @DubboTransported} annotated classes, the invocation of all methods of - * {@link FeignClient @FeignClient} annotated classes. + * If {@link DubboTransported @DubboTransported} annotated classes, the invocation of all + * methods of {@link FeignClient @FeignClient} annotated classes. *
    *
      - * If {@link DubboTransported @DubboTransported} annotated methods of {@link FeignClient @FeignClient} annotated classes. + * If {@link DubboTransported @DubboTransported} annotated methods of + * {@link FeignClient @FeignClient} annotated classes. *
    *
  2. - *
  3. {@link LoadBalanced @LoadBalanced} {@link RestTemplate} annotated field, method and parameters
  4. + *
  5. {@link LoadBalanced @LoadBalanced} {@link RestTemplate} annotated field, method and + * parameters
  6. *
*

* + * @author Mercy * @see FeignClient * @see LoadBalanced */ @Retention(RetentionPolicy.RUNTIME) -@Target(value = {ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) +@Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, + ElementType.PARAMETER }) @Documented public @interface DubboTransported { - /** - * The protocol of Dubbo transport whose value could be used the placeholder "dubbo.transport.protocol" - * - * @return the default protocol is "dubbo" - */ - String protocol() default "${dubbo.transport.protocol:dubbo}"; + /** + * The protocol of Dubbo transport whose value could be used the placeholder + * "dubbo.transport.protocol" + * + * @return the default protocol is "dubbo" + */ + String protocol() default "${dubbo.transport.protocol:dubbo}"; - /** - * The cluster of Dubbo transport whose value could be used the placeholder "dubbo.transport.cluster" - * - * @return the default cluster is "failover" - */ - String cluster() default "${dubbo.transport.cluster:failover}"; + /** + * The cluster of Dubbo transport whose value could be used the placeholder + * "dubbo.transport.cluster" + * + * @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}"; + /** + * 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 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; + /** + * 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; + /** + * Service invocation retry times + * + * @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:}"; + /** + * Load balance strategy, legal values include: random, roundrobin, leastactive + * + * @see Reference#loadbalance() + */ + String loadbalance() default "${dubbo.transport.loadbalance:}"; - /** - * Maximum active requests allowed, default value is 0 - * - * @see Reference#actives() - */ - int actives() default 0; + /** + * 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; + /** + * 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:}"; + /** + * 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 {}; + /** + * 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 {}; + /** + * 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 {}; + /** + * Customized parameter key-value pair, for example: {key1, value1, key2, value2} + * + * @see Reference#parameters() + */ + String[] parameters() default {}; } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java index 5447c2b2..5165e4b5 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java @@ -16,6 +16,11 @@ */ package com.alibaba.cloud.dubbo.autoconfigure; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; @@ -25,13 +30,6 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.context.event.ApplicationStartedEvent; -import com.alibaba.cloud.dubbo.annotation.DubboTransported; -import com.alibaba.cloud.dubbo.client.loadbalancer.DubboMetadataInitializerInterceptor; -import com.alibaba.cloud.dubbo.client.loadbalancer.DubboTransporterInterceptor; -import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; -import com.alibaba.cloud.dubbo.metadata.resolver.DubboTransportedAttributesResolver; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor; import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer; @@ -44,133 +42,154 @@ import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.util.CollectionUtils; import org.springframework.web.client.RestTemplate; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import com.alibaba.cloud.dubbo.annotation.DubboTransported; +import com.alibaba.cloud.dubbo.client.loadbalancer.DubboMetadataInitializerInterceptor; +import com.alibaba.cloud.dubbo.client.loadbalancer.DubboTransporterInterceptor; +import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import com.alibaba.cloud.dubbo.metadata.resolver.DubboTransportedAttributesResolver; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; /** - * Dubbo Auto-{@link Configuration} for {@link LoadBalanced @LoadBalanced} {@link RestTemplate} + * Dubbo Auto-{@link Configuration} for {@link LoadBalanced @LoadBalanced} + * {@link RestTemplate} * * @author Mercy */ @Configuration -@ConditionalOnClass(name = {"org.springframework.web.client.RestTemplate"}) -@AutoConfigureAfter(name = {"org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration"}) -public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClassLoaderAware, SmartInitializingSingleton { +@ConditionalOnClass(name = { "org.springframework.web.client.RestTemplate" }) +@AutoConfigureAfter(name = { + "org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration" }) +public class DubboLoadBalancedRestTemplateAutoConfiguration + implements BeanClassLoaderAware, SmartInitializingSingleton { - private static final Class DUBBO_TRANSPORTED_CLASS = DubboTransported.class; + private static final Class DUBBO_TRANSPORTED_CLASS = DubboTransported.class; - private static final String DUBBO_TRANSPORTED_CLASS_NAME = DUBBO_TRANSPORTED_CLASS.getName(); + private static final String DUBBO_TRANSPORTED_CLASS_NAME = DUBBO_TRANSPORTED_CLASS + .getName(); - @Autowired - private DubboServiceMetadataRepository repository; + @Autowired + private DubboServiceMetadataRepository repository; - @Autowired(required = false) - private LoadBalancerInterceptor loadBalancerInterceptor; + @Autowired(required = false) + private LoadBalancerInterceptor loadBalancerInterceptor; - @Autowired(required = false) - private RetryLoadBalancerInterceptor retryLoadBalancerInterceptor; + @Autowired(required = false) + private RetryLoadBalancerInterceptor retryLoadBalancerInterceptor; - @Autowired - private ConfigurableListableBeanFactory beanFactory; + @Autowired + private ConfigurableListableBeanFactory beanFactory; - @Autowired - private DubboGenericServiceFactory serviceFactory; + @Autowired + private DubboGenericServiceFactory serviceFactory; - @Autowired - private DubboGenericServiceExecutionContextFactory contextFactory; + @Autowired + private DubboGenericServiceExecutionContextFactory contextFactory; - @Autowired - private Environment environment; + @Autowired + private Environment environment; - @LoadBalanced - @Autowired(required = false) - private Map restTemplates = Collections.emptyMap(); + @LoadBalanced + @Autowired(required = false) + private Map restTemplates = Collections.emptyMap(); - private ClassLoader classLoader; + private ClassLoader classLoader; - /** - * The {@link ClientHttpRequestInterceptor} bean that may be {@link LoadBalancerInterceptor} or {@link RetryLoadBalancerInterceptor} - */ - private ClientHttpRequestInterceptor loadBalancerInterceptorBean; + /** + * The {@link ClientHttpRequestInterceptor} bean that may be + * {@link LoadBalancerInterceptor} or {@link RetryLoadBalancerInterceptor} + */ + private ClientHttpRequestInterceptor loadBalancerInterceptorBean; - @Override - public void afterSingletonsInstantiated() { - loadBalancerInterceptorBean = retryLoadBalancerInterceptor != null ? - retryLoadBalancerInterceptor : - loadBalancerInterceptor; - } + @Override + public void afterSingletonsInstantiated() { + loadBalancerInterceptorBean = retryLoadBalancerInterceptor != null + ? retryLoadBalancerInterceptor + : loadBalancerInterceptor; + } - /** - * Adapt the {@link RestTemplate} beans that are annotated {@link LoadBalanced @LoadBalanced} and - * {@link LoadBalanced @LoadBalanced} when Spring Boot application started - * (after the callback of {@link SmartInitializingSingleton} beans or - * {@link RestTemplateCustomizer#customize(RestTemplate) customization}) - */ - @EventListener(ApplicationStartedEvent.class) - public void adaptRestTemplates() { + /** + * Adapt the {@link RestTemplate} beans that are annotated + * {@link LoadBalanced @LoadBalanced} and {@link LoadBalanced @LoadBalanced} when + * Spring Boot application started (after the callback of + * {@link SmartInitializingSingleton} beans or + * {@link RestTemplateCustomizer#customize(RestTemplate) customization}) + */ + @EventListener(ApplicationStartedEvent.class) + public void adaptRestTemplates() { - DubboTransportedAttributesResolver attributesResolver = new DubboTransportedAttributesResolver(environment); + DubboTransportedAttributesResolver attributesResolver = new DubboTransportedAttributesResolver( + environment); - for (Map.Entry entry : restTemplates.entrySet()) { - String beanName = entry.getKey(); - Map dubboTranslatedAttributes = getDubboTranslatedAttributes(beanName, attributesResolver); - if (!CollectionUtils.isEmpty(dubboTranslatedAttributes)) { - adaptRestTemplate(entry.getValue(), dubboTranslatedAttributes); - } - } - } + for (Map.Entry entry : restTemplates.entrySet()) { + String beanName = entry.getKey(); + Map dubboTranslatedAttributes = getDubboTranslatedAttributes( + beanName, attributesResolver); + if (!CollectionUtils.isEmpty(dubboTranslatedAttributes)) { + adaptRestTemplate(entry.getValue(), dubboTranslatedAttributes); + } + } + } - /** - * Gets the annotation attributes {@link RestTemplate} bean being annotated - * {@link DubboTransported @DubboTransported} - * - * @param beanName the bean name of {@link LoadBalanced @LoadBalanced} {@link RestTemplate} - * @param attributesResolver {@link DubboTransportedAttributesResolver} - * @return non-null {@link Map} - */ - private Map getDubboTranslatedAttributes(String beanName, - DubboTransportedAttributesResolver attributesResolver) { - Map attributes = Collections.emptyMap(); - BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); - if (beanDefinition instanceof AnnotatedBeanDefinition) { - AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition; - MethodMetadata factoryMethodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata(); - attributes = factoryMethodMetadata != null ? - factoryMethodMetadata.getAnnotationAttributes(DUBBO_TRANSPORTED_CLASS_NAME) : Collections.emptyMap(); - } - return attributesResolver.resolve(attributes); - } + /** + * Gets the annotation attributes {@link RestTemplate} bean being annotated + * {@link DubboTransported @DubboTransported} + * + * @param beanName the bean name of {@link LoadBalanced @LoadBalanced} + * {@link RestTemplate} + * @param attributesResolver {@link DubboTransportedAttributesResolver} + * @return non-null {@link Map} + */ + private Map getDubboTranslatedAttributes(String beanName, + DubboTransportedAttributesResolver attributesResolver) { + Map attributes = Collections.emptyMap(); + BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); + if (beanDefinition instanceof AnnotatedBeanDefinition) { + AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDefinition; + MethodMetadata factoryMethodMetadata = annotatedBeanDefinition + .getFactoryMethodMetadata(); + attributes = factoryMethodMetadata != null + ? factoryMethodMetadata + .getAnnotationAttributes(DUBBO_TRANSPORTED_CLASS_NAME) + : Collections.emptyMap(); + } + return attributesResolver.resolve(attributes); + } + /** + * Adapt the instance of {@link DubboTransporterInterceptor} to the + * {@link LoadBalancerInterceptor} Bean. + * + * @param restTemplate {@link LoadBalanced @LoadBalanced} {@link RestTemplate} Bean + * @param dubboTranslatedAttributes the annotation dubboTranslatedAttributes + * {@link RestTemplate} bean being annotated + * {@link DubboTransported @DubboTransported} + */ + private void adaptRestTemplate(RestTemplate restTemplate, + Map dubboTranslatedAttributes) { - /** - * Adapt the instance of {@link DubboTransporterInterceptor} to the {@link LoadBalancerInterceptor} Bean. - * - * @param restTemplate {@link LoadBalanced @LoadBalanced} {@link RestTemplate} Bean - * @param dubboTranslatedAttributes the annotation dubboTranslatedAttributes {@link RestTemplate} bean being annotated - * {@link DubboTransported @DubboTransported} - */ - private void adaptRestTemplate(RestTemplate restTemplate, Map dubboTranslatedAttributes) { + List interceptors = new ArrayList<>( + restTemplate.getInterceptors()); - List interceptors = new ArrayList<>(restTemplate.getInterceptors()); + int index = loadBalancerInterceptorBean == null ? -1 + : interceptors.indexOf(loadBalancerInterceptorBean); - int index = loadBalancerInterceptorBean == null ? -1 : interceptors.indexOf(loadBalancerInterceptorBean); + index = index < 0 ? 0 : index; - index = index < 0 ? 0 : index; + // Add ClientHttpRequestInterceptor instances before loadBalancerInterceptor + interceptors.add(index++, new DubboMetadataInitializerInterceptor(repository)); - // Add ClientHttpRequestInterceptor instances before loadBalancerInterceptor - interceptors.add(index++, new DubboMetadataInitializerInterceptor(repository)); + interceptors.add(index++, + new DubboTransporterInterceptor(repository, + restTemplate.getMessageConverters(), classLoader, + dubboTranslatedAttributes, serviceFactory, contextFactory)); - interceptors.add(index++, new DubboTransporterInterceptor(repository, restTemplate.getMessageConverters(), - classLoader, dubboTranslatedAttributes, serviceFactory, contextFactory)); + restTemplate.setInterceptors(interceptors); + } - restTemplate.setInterceptors(interceptors); - } - - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java index 57a58176..f39aa5fb 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java @@ -16,15 +16,23 @@ */ package com.alibaba.cloud.dubbo.autoconfigure; +import java.util.Collection; +import java.util.function.Supplier; + 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.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 com.alibaba.cloud.dubbo.metadata.DubboProtocolConfigSupplier; import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; import com.alibaba.cloud.dubbo.metadata.resolver.DubboServiceBeanMetadataResolver; @@ -34,14 +42,8 @@ import com.alibaba.cloud.dubbo.service.DubboMetadataServiceExporter; import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy; import com.alibaba.cloud.dubbo.service.IntrospectiveDubboMetadataService; import com.alibaba.cloud.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.function.Supplier; +import feign.Contract; /** * Spring Boot Auto-Configuration class for Dubbo Metadata @@ -49,61 +51,62 @@ import java.util.function.Supplier; * @author Mercy */ @Configuration -@Import({DubboServiceMetadataRepository.class, - IntrospectiveDubboMetadataService.class, - DubboMetadataServiceExporter.class, - JSONUtils.class}) +@Import({ DubboServiceMetadataRepository.class, IntrospectiveDubboMetadataService.class, + DubboMetadataServiceExporter.class, JSONUtils.class }) public class DubboMetadataAutoConfiguration { - @Autowired - private ObjectProvider dubboServiceMetadataRepository; + @Autowired + private ObjectProvider dubboServiceMetadataRepository; - @Autowired - private MetadataResolver metadataResolver; + @Autowired + private MetadataResolver metadataResolver; - @Autowired - private DubboMetadataServiceExporter dubboMetadataConfigServiceExporter; + @Autowired + private DubboMetadataServiceExporter dubboMetadataConfigServiceExporter; - @Bean - @ConditionalOnMissingBean - public MetadataResolver metadataJsonResolver(ObjectProvider contract) { - return new DubboServiceBeanMetadataResolver(contract); - } + @Bean + @ConditionalOnMissingBean + public MetadataResolver metadataJsonResolver(ObjectProvider contract) { + return new DubboServiceBeanMetadataResolver(contract); + } - @Bean - public Supplier dubboProtocolConfigSupplier(ObjectProvider> protocols) { - return new DubboProtocolConfigSupplier(protocols); - } + @Bean + public Supplier dubboProtocolConfigSupplier( + ObjectProvider> protocols) { + return new DubboProtocolConfigSupplier(protocols); + } - @Bean - @ConditionalOnMissingBean - public DubboMetadataServiceProxy dubboMetadataConfigServiceProxy(DubboGenericServiceFactory factory) { - return new DubboMetadataServiceProxy(factory); - } + @Bean + @ConditionalOnMissingBean + public DubboMetadataServiceProxy dubboMetadataConfigServiceProxy( + DubboGenericServiceFactory factory) { + return new DubboMetadataServiceProxy(factory); + } - // Event-Handling + // Event-Handling - @EventListener(ServiceBeanExportedEvent.class) - public void onServiceBeanExported(ServiceBeanExportedEvent event) { - ServiceBean serviceBean = event.getServiceBean(); - publishServiceRestMetadata(serviceBean); - } + @EventListener(ServiceBeanExportedEvent.class) + public void onServiceBeanExported(ServiceBeanExportedEvent event) { + ServiceBean serviceBean = event.getServiceBean(); + publishServiceRestMetadata(serviceBean); + } - @EventListener(ApplicationFailedEvent.class) - public void onApplicationFailed() { - unExportDubboMetadataConfigService(); - } + @EventListener(ApplicationFailedEvent.class) + public void onApplicationFailed() { + unExportDubboMetadataConfigService(); + } - @EventListener(ContextClosedEvent.class) - public void onContextClosed() { - unExportDubboMetadataConfigService(); - } + @EventListener(ContextClosedEvent.class) + public void onContextClosed() { + unExportDubboMetadataConfigService(); + } - private void publishServiceRestMetadata(ServiceBean serviceBean) { - dubboServiceMetadataRepository.getIfAvailable().publishServiceRestMetadata(metadataResolver.resolveServiceRestMetadata(serviceBean)); - } + private void publishServiceRestMetadata(ServiceBean serviceBean) { + dubboServiceMetadataRepository.getIfAvailable().publishServiceRestMetadata( + metadataResolver.resolveServiceRestMetadata(serviceBean)); + } - private void unExportDubboMetadataConfigService() { - dubboMetadataConfigServiceExporter.unexport(); - } + private void unExportDubboMetadataConfigService() { + dubboMetadataConfigServiceExporter.unexport(); + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java index 9f956e05..0f282bf3 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboOpenFeignAutoConfiguration.java @@ -16,40 +16,39 @@ */ package com.alibaba.cloud.dubbo.autoconfigure; -import com.alibaba.cloud.dubbo.openfeign.TargeterBeanPostProcessor; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; +import static com.alibaba.cloud.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration.TARGETER_CLASS_NAME; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; - import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; -import static com.alibaba.cloud.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration.TARGETER_CLASS_NAME; - +import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import com.alibaba.cloud.dubbo.openfeign.TargeterBeanPostProcessor; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; /** * Dubbo Feign Auto-{@link Configuration Configuration} * * @author Mercy */ -@ConditionalOnClass(name = {"feign.Feign", TARGETER_CLASS_NAME}) -@AutoConfigureAfter(name = {"org.springframework.cloud.openfeign.FeignAutoConfiguration"}) +@ConditionalOnClass(name = { "feign.Feign", TARGETER_CLASS_NAME }) +@AutoConfigureAfter(name = { + "org.springframework.cloud.openfeign.FeignAutoConfiguration" }) @Configuration public class DubboOpenFeignAutoConfiguration { - public static final String TARGETER_CLASS_NAME = "org.springframework.cloud.openfeign.Targeter"; + public static final String TARGETER_CLASS_NAME = "org.springframework.cloud.openfeign.Targeter"; - @Bean - public TargeterBeanPostProcessor targeterBeanPostProcessor(Environment environment, - DubboServiceMetadataRepository dubboServiceMetadataRepository, - DubboGenericServiceFactory dubboGenericServiceFactory, - DubboGenericServiceExecutionContextFactory contextFactory) { - return new TargeterBeanPostProcessor(environment, dubboServiceMetadataRepository, - dubboGenericServiceFactory, contextFactory); - } + @Bean + public TargeterBeanPostProcessor targeterBeanPostProcessor(Environment environment, + DubboServiceMetadataRepository dubboServiceMetadataRepository, + DubboGenericServiceFactory dubboGenericServiceFactory, + DubboGenericServiceExecutionContextFactory contextFactory) { + return new TargeterBeanPostProcessor(environment, dubboServiceMetadataRepository, + dubboGenericServiceFactory, contextFactory); + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceAutoConfiguration.java index 00af57ae..70d992dc 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceAutoConfiguration.java @@ -16,18 +16,9 @@ */ package com.alibaba.cloud.dubbo.autoconfigure; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; -import com.alibaba.cloud.dubbo.service.parameter.PathVariableServiceParameterResolver; -import com.alibaba.cloud.dubbo.service.parameter.RequestBodyServiceParameterResolver; -import com.alibaba.cloud.dubbo.service.parameter.RequestHeaderServiceParameterResolver; -import com.alibaba.cloud.dubbo.service.parameter.RequestParamServiceParameterResolver; - import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import com.alibaba.cloud.dubbo.env.DubboCloudProperties; - import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -35,6 +26,14 @@ import org.springframework.context.annotation.Primary; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertyResolver; +import com.alibaba.cloud.dubbo.env.DubboCloudProperties; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; +import com.alibaba.cloud.dubbo.service.parameter.PathVariableServiceParameterResolver; +import com.alibaba.cloud.dubbo.service.parameter.RequestBodyServiceParameterResolver; +import com.alibaba.cloud.dubbo.service.parameter.RequestHeaderServiceParameterResolver; +import com.alibaba.cloud.dubbo.service.parameter.RequestParamServiceParameterResolver; + /** * Spring Boot Auto-Configuration class for Dubbo Service * @@ -44,32 +43,30 @@ import org.springframework.core.env.PropertyResolver; @EnableConfigurationProperties(DubboCloudProperties.class) public class DubboServiceAutoConfiguration { - @Bean - @ConditionalOnMissingBean - public DubboGenericServiceFactory dubboGenericServiceFactory() { - return new DubboGenericServiceFactory(); - } + @Bean + @ConditionalOnMissingBean + public DubboGenericServiceFactory dubboGenericServiceFactory() { + return new DubboGenericServiceFactory(); + } - @Configuration - @Import(value = { - DubboGenericServiceExecutionContextFactory.class, - RequestParamServiceParameterResolver.class, - RequestBodyServiceParameterResolver.class, - RequestHeaderServiceParameterResolver.class, - PathVariableServiceParameterResolver.class - }) - static class ParameterResolversConfiguration { - } + /** + * Build a primary {@link PropertyResolver} bean to {@link Autowired @Autowired} + * + * @param environment {@link Environment} + * @return alias bean for {@link Environment} + */ + @Bean + @Primary + public PropertyResolver primaryPropertyResolver(Environment environment) { + return environment; + } - /** - * Build a primary {@link PropertyResolver} bean to {@link Autowired @Autowired} - * - * @param environment {@link Environment} - * @return alias bean for {@link Environment} - */ - @Bean - @Primary - public PropertyResolver primaryPropertyResolver(Environment environment) { - return environment; - } + @Configuration + @Import(value = { DubboGenericServiceExecutionContextFactory.class, + RequestParamServiceParameterResolver.class, + RequestBodyServiceParameterResolver.class, + RequestHeaderServiceParameterResolver.class, + PathVariableServiceParameterResolver.class }) + static class ParameterResolversConfiguration { + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceDiscoveryAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceDiscoveryAutoConfiguration.java new file mode 100644 index 00000000..db22da91 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceDiscoveryAutoConfiguration.java @@ -0,0 +1,552 @@ +/* + * 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 com.alibaba.cloud.dubbo.autoconfigure; + +import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceDiscoveryAutoConfiguration.CONSUL_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME; +import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceDiscoveryAutoConfiguration.NACOS_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME; +import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceDiscoveryAutoConfiguration.ZOOKEEPER_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME; +import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; +import static com.alibaba.cloud.nacos.discovery.NacosDiscoveryClient.hostToServiceInstanceList; +import static org.springframework.util.StringUtils.hasText; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.listen.Listenable; +import org.apache.curator.framework.listen.ListenerContainer; +import org.apache.curator.framework.recipes.cache.ChildData; +import org.apache.curator.framework.recipes.cache.TreeCache; +import org.apache.curator.framework.recipes.cache.TreeCacheEvent; +import org.apache.curator.framework.recipes.cache.TreeCacheListener; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.registry.NotifyListener; + +import org.apache.zookeeper.Watcher; +import org.aspectj.lang.annotation.After; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.event.HeartbeatEvent; +import org.springframework.cloud.client.discovery.event.InstanceRegisteredEvent; +import org.springframework.cloud.consul.discovery.ConsulCatalogWatch; +import org.springframework.cloud.netflix.eureka.CloudEurekaClient; +import org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryProperties; +import org.springframework.cloud.zookeeper.discovery.ZookeeperServiceWatch; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.ReflectionUtils; + +import com.alibaba.cloud.dubbo.env.DubboCloudProperties; +import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import com.alibaba.cloud.dubbo.registry.AbstractSpringCloudRegistry; +import com.alibaba.cloud.dubbo.registry.event.ServiceInstancesChangedEvent; +import com.alibaba.cloud.dubbo.registry.event.SubscribedServicesChangedEvent; +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.cloud.nacos.discovery.NacosWatch; +import com.alibaba.nacos.api.exception.NacosException; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.listener.NamingEvent; +import com.netflix.discovery.CacheRefreshedEvent; +import com.netflix.discovery.shared.Applications; + +/** + * Dubbo Service Discovery Auto {@link Configuration} (after + * {@link DubboServiceRegistrationAutoConfiguration}) + * + * @author Mercy + * @see DubboServiceRegistrationAutoConfiguration + * @see Configuration + * @see DiscoveryClient + */ +@Configuration +@ConditionalOnClass(name = "org.springframework.cloud.client.discovery.DiscoveryClient") +@ConditionalOnProperty(name = "spring.cloud.discovery.enabled", matchIfMissing = true) +@AutoConfigureAfter(name = { EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME, + ZOOKEEPER_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME, + CONSUL_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME, + NACOS_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME }, value = { + DubboServiceRegistrationAutoConfiguration.class }) +public class DubboServiceDiscoveryAutoConfiguration { + + public static final String ZOOKEEPER_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.zookeeper.discovery.ZookeeperDiscoveryAutoConfiguration"; + + public static final String CONSUL_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.consul.discovery.ConsulDiscoveryClientConfiguration"; + + public static final String NACOS_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME = "com.alibaba.cloud.nacos.NacosDiscoveryAutoConfiguration"; + + private final DubboServiceMetadataRepository dubboServiceMetadataRepository; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final ApplicationEventPublisher applicationEventPublisher; + + private final DiscoveryClient discoveryClient; + + private final AtomicReference heartbeatState = new AtomicReference<>(); + + /** + * @see #defaultHeartbeatEventChangePredicate() + */ + private final ObjectProvider> heartbeatEventChangedPredicate; + + public DubboServiceDiscoveryAutoConfiguration( + DubboServiceMetadataRepository dubboServiceMetadataRepository, + ApplicationEventPublisher applicationEventPublisher, + DiscoveryClient discoveryClient, + ObjectProvider> heartbeatEventChangedPredicate) { + this.dubboServiceMetadataRepository = dubboServiceMetadataRepository; + this.applicationEventPublisher = applicationEventPublisher; + this.discoveryClient = discoveryClient; + this.heartbeatEventChangedPredicate = heartbeatEventChangedPredicate; + } + + /** + * Dispatch a {@link ServiceInstancesChangedEvent} + * + * @param serviceName the name of service + * @param serviceInstances the {@link ServiceInstance instances} of some service + * @see AbstractSpringCloudRegistry#registerServiceInstancesChangedEventListener(URL, + * NotifyListener) + */ + private void dispatchServiceInstancesChangedEvent(String serviceName, + Collection serviceInstances) { + if (!hasText(serviceName) || serviceInstances == null) { + return; + } + ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent(serviceName, + serviceInstances); + if (logger.isInfoEnabled()) { + logger.info( + "The event of the service instances[name : {} , size : {}] change is about to be dispatched", + serviceName, serviceInstances.size()); + } + applicationEventPublisher.publishEvent(event); + } + + private List getInstances(String serviceName) { + return discoveryClient.getInstances(serviceName); + } + + /** + * Dispatch a {@link ServiceInstancesChangedEvent} when the {@link HeartbeatEvent} + * raised, use for these scenarios: + *
    + *
  • Eureka : {@link CloudEurekaClient#onCacheRefreshed()} publishes a + * {@link HeartbeatEvent} instead of {@link CacheRefreshedEvent}
  • + *
  • Zookeeper : + * {@link ZookeeperServiceWatch#childEvent(CuratorFramework, TreeCacheEvent)} + * publishes a {@link HeartbeatEvent} when + * {@link ZookeeperDiscoveryProperties#getRoot() the services' root path} has been + * changed
  • + *
  • Consul : {@link ConsulCatalogWatch#catalogServicesWatch()} publishes a + * {@link HeartbeatEvent} when + * Consul's Blocking + * Queries response
  • + *
  • Nacos : {@link NacosWatch#nacosServicesWatch()} publishes a + * {@link HeartbeatEvent} under a {@link TaskScheduler} every + * {@link NacosDiscoveryProperties#getWatchDelay()} milliseconds
  • + *
+ *

+ * In order to reduce the duplicated handling for + * {@link ServiceInstancesChangedEvent}, + * {@link #defaultHeartbeatEventChangePredicate()} method providers the default + * implementation to detect whether the {@link HeartbeatEvent#getValue() state} is + * changed or not. If and only if changed, the + * {@link #dispatchServiceInstancesChangedEvent(String, Collection)} will be executed. + *

+ * Note : Spring Cloud {@link HeartbeatEvent} has a critical flaw that can't + * figure out which service was changed precisely, it's just used for a notification + * that the {@link DubboServiceMetadataRepository#getSubscribedServices() subscribed + * services} used to {@link NotifyListener#notify(List) notify} the Dubbo consumer + * cached {@link URL URLs}. Because of some {@link DiscoveryClient} implementations + * have the better and fine-grained the event mechanism for service instances change, + * thus {@link HeartbeatEvent} handle will be ignored in these scenarios: + *

    + *
  • Zookeeper : {@link Watcher}, see + * {@link ZookeeperConfiguration#heartbeatEventChangedPredicate()}
  • + *
  • Nacos : {@link com.alibaba.nacos.api.naming.listener.EventListener} , see + * {@link NacosConfiguration#heartbeatEventChangedPredicate()}
  • + *
+ *

+ * If the customized {@link DiscoveryClient} also providers the similar mechanism, the + * implementation could declare a Spring Bean of {@link Predicate} of + * {@link HeartbeatEvent} to override {@link #defaultHeartbeatEventChangePredicate() + * default one}. + * + * @param event the instance of {@link HeartbeatEvent} + * @see HeartbeatEvent + */ + @EventListener(HeartbeatEvent.class) + public void onHeartbeatEvent(HeartbeatEvent event) { + /** + * Try to re-initialize the subscribed services, in order to sense the change of + * services if {@link DubboCloudProperties#getSubscribedServices()} is wildcard + * that indicates all services should be subscribed. + */ + Stream subscribedServices = dubboServiceMetadataRepository + .initSubscribedServices(); + + heartbeatEventChangedPredicate.ifAvailable(predicate -> { + if (predicate.test(event)) { + // Dispatch ServiceInstancesChangedEvent for each service + subscribedServices.forEach(serviceName -> { + List serviceInstances = getInstances(serviceName); + dispatchServiceInstancesChangedEvent(serviceName, serviceInstances); + }); + } + }); + } + + /** + * The default {@link Predicate} implementation of {@link HeartbeatEvent} based on the + * comparison between previous and current {@link HeartbeatEvent#getValue() state + * value}, the {@link DiscoveryClient} implementation may override current. + * + * @return {@link Predicate} + * @see EurekaConfiguration#heartbeatEventChangedPredicate() + */ + @Bean + @ConditionalOnMissingBean + public Predicate defaultHeartbeatEventChangePredicate() { + return event -> { + Object oldState = heartbeatState.get(); + Object newState = event.getValue(); + return heartbeatState.compareAndSet(oldState, newState) + && !Objects.equals(oldState, newState); + }; + } + + /** + * Eureka Customized Configuration + */ + @Configuration + @ConditionalOnBean(name = EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME) + public class EurekaConfiguration { + + private final AtomicReference appsHashCodeCache = new AtomicReference<>(); + + /** + * Compare {@link Applications#getAppsHashCode() apps hash code} between last + * {@link Applications} and current. + * + * @see Applications#getAppsHashCode() + */ + @Bean + public Predicate heartbeatEventChangedPredicate() { + return event -> { + String oldAppsHashCode = appsHashCodeCache.get(); + CloudEurekaClient cloudEurekaClient = (CloudEurekaClient) event + .getSource(); + Applications applications = cloudEurekaClient.getApplications(); + String appsHashCode = applications.getAppsHashCode(); + return appsHashCodeCache.compareAndSet(oldAppsHashCode, appsHashCode) + && !Objects.equals(oldAppsHashCode, appsHashCode); + }; + } + } + + /** + * Zookeeper Customized Configuration + */ + @Configuration + @ConditionalOnBean(name = ZOOKEEPER_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME) + @Aspect + public class ZookeeperConfiguration + implements ApplicationListener { + /** + * The pointcut expression for + * {@link ZookeeperServiceWatch#childEvent(CuratorFramework, TreeCacheEvent)} + */ + public static final String CHILD_EVENT_POINTCUT_EXPRESSION = "execution(void org.springframework.cloud.zookeeper.discovery.ZookeeperServiceWatch.childEvent(..)) && args(client,event)"; + + /** + * The path separator of Zookeeper node + */ + public static final String NODE_PATH_SEPARATOR = "/"; + + /** + * The path variable name for the name of service + */ + private static final String SERVICE_NAME_PATH_VARIABLE_NAME = "serviceName"; + + /** + * The path variable name for the id of {@link ServiceInstance service instance} + */ + private static final String SERVICE_INSTANCE_ID_PATH_VARIABLE_NAME = "serviceInstanceId"; + + private final ZookeeperServiceWatch zookeeperServiceWatch; + + private final String rootPath; + + private final AntPathMatcher pathMatcher; + + /** + * Ant Path pattern for {@link ServiceInstance} : + *

+ *

+ * ${{@link #rootPath}}/{serviceName}/{serviceInstanceId} + * + * @see #rootPath + * @see #SERVICE_NAME_PATH_VARIABLE_NAME + * @see #SERVICE_INSTANCE_ID_PATH_VARIABLE_NAME + */ + private final String serviceInstancePathPattern; + + /** + * The {@link ThreadLocal} holds the processed service name + */ + private final ThreadLocal processedServiceNameThreadLocal; + + ZookeeperConfiguration(ZookeeperDiscoveryProperties zookeeperDiscoveryProperties, + ZookeeperServiceWatch zookeeperServiceWatch) { + this.zookeeperServiceWatch = zookeeperServiceWatch; + this.rootPath = zookeeperDiscoveryProperties.getRoot(); + this.pathMatcher = new AntPathMatcher(NODE_PATH_SEPARATOR); + this.serviceInstancePathPattern = rootPath + NODE_PATH_SEPARATOR + "{" + + SERVICE_NAME_PATH_VARIABLE_NAME + "}" + NODE_PATH_SEPARATOR + "{" + + SERVICE_INSTANCE_ID_PATH_VARIABLE_NAME + "}"; + this.processedServiceNameThreadLocal = new ThreadLocal<>(); + } + + /** + * Zookeeper uses {@link TreeCacheEvent} to trigger + * {@link #dispatchServiceInstancesChangedEvent(String, Collection)} , thus + * {@link HeartbeatEvent} handle is always ignored + * + * @return false forever + */ + @Bean + public Predicate heartbeatEventChangedPredicate() { + return event -> false; + } + + /** + * Handle on {@link InstanceRegisteredEvent} after + * {@link ZookeeperServiceWatch#onApplicationEvent(InstanceRegisteredEvent)} + * + * @param event {@link InstanceRegisteredEvent} + * @see #reattachTreeCacheListeners() + */ + @Override + public void onApplicationEvent(InstanceRegisteredEvent event) { + reattachTreeCacheListeners(); + } + + /** + * Re-attach the {@link TreeCacheListener TreeCacheListeners} + */ + private void reattachTreeCacheListeners() { + + TreeCache treeCache = zookeeperServiceWatch.getCache(); + + Listenable listenable = treeCache.getListenable(); + + /** + * All registered TreeCacheListeners except {@link ZookeeperServiceWatch}. + * Usually, "otherListeners" will be empty because Spring Cloud Zookeeper only + * adds "zookeeperServiceWatch" bean as {@link TreeCacheListener} + */ + List otherListeners = new LinkedList<>(); + + if (listenable instanceof ListenerContainer) { + ListenerContainer listenerContainer = (ListenerContainer) listenable; + listenerContainer.forEach(listener -> { + /** + * Even though "listener" is an instance of + * {@link ZookeeperServiceWatch}, "zookeeperServiceWatch" bean that + * was enhanced by AOP is different from the former, thus it's + * required to exclude "listener" + */ + if (!(listener instanceof ZookeeperServiceWatch)) { + otherListeners.add(listener); + } + return null; + }); + + // remove all TreeCacheListeners temporarily + listenerContainer.clear(); + // re-store zookeeperServiceWatch, and make sure it's first one + // now "beforeChildEvent" is available for Spring AOP + listenerContainer.addListener(zookeeperServiceWatch); + // re-store others + otherListeners.forEach(listenerContainer::addListener); + } + else { + if (logger.isWarnEnabled()) { + logger.warn( + "Tell me which version Curator framework current application used? I will do better :D"); + } + } + } + + /** + * Try to {@link #dispatchServiceInstancesChangedEvent(String, Collection) + * dispatch} {@link ServiceInstancesChangedEvent} before + * {@link ZookeeperServiceWatch#childEvent(CuratorFramework, TreeCacheEvent)} + * execution if required + * + * @param client {@link CuratorFramework} + * @param event {@link TreeCacheEvent} + */ + @Before(CHILD_EVENT_POINTCUT_EXPRESSION) + public void beforeChildEvent(CuratorFramework client, TreeCacheEvent event) { + if (supportsEventType(event)) { + String serviceName = resolveServiceName(event); + if (hasText(serviceName)) { + dispatchServiceInstancesChangedEvent(serviceName, + getInstances(serviceName)); + } + } + } + + @After(CHILD_EVENT_POINTCUT_EXPRESSION) + public void afterChildEvent(CuratorFramework client, TreeCacheEvent event) { + } + + /** + * Resolve the name of service + * + * @param event {@link TreeCacheEvent} + * @return If the Zookeeper's {@link ChildData#getPath() node path} that was + * notified comes from {@link ServiceInstance the service instance}, return it's + * parent path as the service name, or return null + */ + private String resolveServiceName(TreeCacheEvent event) { + ChildData childData = event.getData(); + String path = childData.getPath(); + if (logger.isDebugEnabled()) { + logger.debug("ZK node[path : {}] event type : {}", path, event.getType()); + } + + String serviceName = null; + + if (pathMatcher.match(serviceInstancePathPattern, path)) { + Map variables = pathMatcher + .extractUriTemplateVariables(serviceInstancePathPattern, path); + serviceName = variables.get(SERVICE_NAME_PATH_VARIABLE_NAME); + } + + return serviceName; + } + + /** + * The {@link TreeCacheEvent#getType() event type} is supported or not + * + * @param event {@link TreeCacheEvent} + * @return the rule is same as + * {@link ZookeeperServiceWatch#childEvent(CuratorFramework, TreeCacheEvent)} + * method + */ + private boolean supportsEventType(TreeCacheEvent event) { + TreeCacheEvent.Type eventType = event.getType(); + return eventType.equals(TreeCacheEvent.Type.NODE_ADDED) + || eventType.equals(TreeCacheEvent.Type.NODE_REMOVED) + || eventType.equals(TreeCacheEvent.Type.NODE_UPDATED); + } + } + + /** + * Consul Customized Configuration + */ + @Configuration + @ConditionalOnBean(name = CONSUL_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME) + class ConsulConfiguration { + + } + + /** + * Nacos Customized Configuration + */ + @Configuration + @ConditionalOnBean(name = NACOS_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME) + class NacosConfiguration { + + private final NamingService namingService; + + /** + * the set of services is listening + */ + private final Set listeningServices; + + NacosConfiguration(NacosDiscoveryProperties nacosDiscoveryProperties) { + this.namingService = nacosDiscoveryProperties.namingServiceInstance(); + this.listeningServices = new ConcurrentSkipListSet<>(); + } + + /** + * Nacos uses {@link EventListener} to trigger + * {@link #dispatchServiceInstancesChangedEvent(String, Collection)} , thus + * {@link HeartbeatEvent} handle is always ignored + * + * @return false forever + */ + @Bean + public Predicate heartbeatEventChangedPredicate() { + return event -> false; + } + + @EventListener(SubscribedServicesChangedEvent.class) + public void onSubscribedServicesChangedEvent(SubscribedServicesChangedEvent event) + throws Exception { + // subscribe EventListener for each service + event.getNewSubscribedServices().forEach(this::subscribeEventListener); + } + + private void subscribeEventListener(String serviceName) { + if (listeningServices.add(serviceName)) { + try { + namingService.subscribe(serviceName, event -> { + if (event instanceof NamingEvent) { + NamingEvent namingEvent = (NamingEvent) event; + List serviceInstances = hostToServiceInstanceList( + namingEvent.getInstances(), serviceName); + dispatchServiceInstancesChangedEvent(serviceName, + serviceInstances); + } + }); + } + catch (NacosException e) { + ReflectionUtils.rethrowRuntimeException(e); + } + } + } + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceRegistrationAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceRegistrationAutoConfiguration.java index d21e841f..6031edce 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceRegistrationAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceRegistrationAutoConfiguration.java @@ -16,11 +16,19 @@ */ package com.alibaba.cloud.dubbo.autoconfigure; +import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME; +import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME; +import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.ADDRESS; +import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.PROTOCOL; +import static org.springframework.util.ObjectUtils.isEmpty; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + import org.apache.dubbo.config.RegistryConfig; import org.apache.dubbo.config.spring.ServiceBean; -import com.ecwid.consul.v1.agent.model.NewService; -import com.netflix.appinfo.InstanceInfo; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -32,10 +40,6 @@ 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 com.alibaba.cloud.dubbo.autoconfigure.condition.MissingSpringCloudRegistryConfigPropertyCondition; -import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; -import com.alibaba.cloud.dubbo.registry.DubboServiceRegistrationEventPublishingAspect; -import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.consul.serviceregistry.ConsulRegistration; @@ -49,15 +53,12 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.event.EventListener; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_CONFIGURATION_CLASS_NAME; -import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.EUREKA_AUTO_CONFIGURATION_CLASS_NAME; -import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.ADDRESS; -import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.PROTOCOL; -import static org.springframework.util.ObjectUtils.isEmpty; +import com.alibaba.cloud.dubbo.autoconfigure.condition.MissingSpringCloudRegistryConfigPropertyCondition; +import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import com.alibaba.cloud.dubbo.registry.DubboServiceRegistrationEventPublishingAspect; +import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent; +import com.ecwid.consul.v1.agent.model.NewService; +import com.netflix.appinfo.InstanceInfo; /** * Dubbo Service Registration Auto-{@link Configuration} @@ -65,125 +66,126 @@ import static org.springframework.util.ObjectUtils.isEmpty; * @author Mercy */ @Configuration -@Import({DubboServiceRegistrationEventPublishingAspect.class}) +@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 -}) +@AutoConfigureAfter(name = { EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME, + CONSUL_AUTO_SERVICE_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 EUREKA_CLIENT_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_SERVICE_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"; + public static final String CONSUL_AUTO_SERVICE_AUTO_REGISTRATION_CLASS_NAME = "org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration"; - public static final String ZOOKEEPER_AUTO_CONFIGURATION_CLASS_NAME = - "org.springframework.cloud.zookeeper.serviceregistry.ZookeeperAutoServiceRegistrationAutoConfiguration"; + public static final String ZOOKEEPER_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME = "org.springframework.cloud.zookeeper.serviceregistry.ZookeeperAutoServiceRegistrationAutoConfiguration"; - private static final Logger logger = LoggerFactory.getLogger(DubboServiceRegistrationAutoConfiguration.class); + private static final Logger logger = LoggerFactory + .getLogger(DubboServiceRegistrationAutoConfiguration.class); - @Autowired - private DubboServiceMetadataRepository dubboServiceMetadataRepository; + @Autowired + private DubboServiceMetadataRepository dubboServiceMetadataRepository; - @Bean - @Conditional(value = { - MissingSpringCloudRegistryConfigPropertyCondition.class - }) - public RegistryConfig defaultSpringCloudRegistryConfig() { - return new RegistryConfig(ADDRESS, PROTOCOL); - } + @Bean + @Conditional(value = { MissingSpringCloudRegistryConfigPropertyCondition.class }) + public RegistryConfig defaultSpringCloudRegistryConfig() { + return new RegistryConfig(ADDRESS, PROTOCOL); + } - @EventListener(ServiceInstancePreRegisteredEvent.class) - public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { - Registration registration = event.getSource(); - attachDubboMetadataServiceMetadata(registration); - } + @EventListener(ServiceInstancePreRegisteredEvent.class) + public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { + Registration registration = event.getSource(); + attachDubboMetadataServiceMetadata(registration); + } - @Configuration - @ConditionalOnBean(name = EUREKA_AUTO_CONFIGURATION_CLASS_NAME) - @Aspect - class EurekaConfiguration implements SmartInitializingSingleton { + private void attachDubboMetadataServiceMetadata(Registration registration) { + if (registration == null) { + return; + } + synchronized (registration) { + Map metadata = registration.getMetadata(); + attachDubboMetadataServiceMetadata(metadata); + } + } - @Autowired - private ObjectProvider> serviceBeans; + private void attachDubboMetadataServiceMetadata(Map metadata) { + Map serviceMetadata = dubboServiceMetadataRepository + .getDubboMetadataServiceMetadata(); + if (!isEmpty(serviceMetadata)) { + metadata.putAll(serviceMetadata); + } + } - @EventListener(ServiceInstancePreRegisteredEvent.class) - public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { - Registration registration = event.getSource(); - EurekaRegistration eurekaRegistration = EurekaRegistration.class.cast(registration); - InstanceInfo instanceInfo = eurekaRegistration.getApplicationInfoManager().getInfo(); - attachDubboMetadataServiceMetadata(instanceInfo.getMetadata()); - } + @Configuration + @ConditionalOnBean(name = EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME) + @Aspect + class EurekaConfiguration implements SmartInitializingSingleton { - /** - * {@link EurekaServiceRegistry} will register current {@link ServiceInstance service instance} on - * {@link EurekaAutoServiceRegistration#start()} execution(in {@link SmartLifecycle#start() start phase}), - * thus this method must {@link ServiceBean#export() export} all {@link ServiceBean ServiceBeans} in advance. - */ - @Override - public void afterSingletonsInstantiated() { - Collection serviceBeans = this.serviceBeans.getIfAvailable(); - if (!isEmpty(serviceBeans)) { - serviceBeans.forEach(ServiceBean::export); - } - } - } + @Autowired + private ObjectProvider> serviceBeans; - @Configuration - @ConditionalOnBean(name = CONSUL_AUTO_CONFIGURATION_CLASS_NAME) - @AutoConfigureOrder - class ConsulConfiguration { + @EventListener(ServiceInstancePreRegisteredEvent.class) + public void onServiceInstancePreRegistered( + ServiceInstancePreRegisteredEvent event) { + Registration registration = event.getSource(); + EurekaRegistration eurekaRegistration = EurekaRegistration.class + .cast(registration); + InstanceInfo instanceInfo = eurekaRegistration.getApplicationInfoManager() + .getInfo(); + attachDubboMetadataServiceMetadata(instanceInfo.getMetadata()); + } - /** - * 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); - } - } + /** + * {@link EurekaServiceRegistry} will register current {@link ServiceInstance + * service instance} on {@link EurekaAutoServiceRegistration#start()} execution(in + * {@link SmartLifecycle#start() start phase}), thus this method must + * {@link ServiceBean#export() export} all {@link ServiceBean ServiceBeans} in + * advance. + */ + @Override + public void afterSingletonsInstantiated() { + Collection serviceBeans = this.serviceBeans.getIfAvailable(); + if (!isEmpty(serviceBeans)) { + serviceBeans.forEach(ServiceBean::export); + } + } + } - private void attachURLsIntoMetadata(ConsulRegistration consulRegistration) { - NewService newService = consulRegistration.getService(); - Map serviceMetadata = dubboServiceMetadataRepository.getDubboMetadataServiceMetadata(); - if (!isEmpty(serviceMetadata)) { - List tags = newService.getTags(); - for (Map.Entry entry : serviceMetadata.entrySet()) { - tags.add(entry.getKey() + "=" + entry.getValue()); - } - } - } - } + @Configuration + @ConditionalOnBean(name = CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME) + @AutoConfigureOrder + class ConsulConfiguration { - private void attachDubboMetadataServiceMetadata(Registration registration) { - if (registration == null) { - return; - } - synchronized (registration) { - Map metadata = registration.getMetadata(); - attachDubboMetadataServiceMetadata(metadata); - } - } + /** + * 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_SERVICE_AUTO_REGISTRATION_CLASS_NAME + .equalsIgnoreCase(registrationClassName)) { + ConsulRegistration consulRegistration = (ConsulRegistration) registration; + attachURLsIntoMetadata(consulRegistration); + } + } - private void attachDubboMetadataServiceMetadata(Map metadata) { - Map serviceMetadata = dubboServiceMetadataRepository.getDubboMetadataServiceMetadata(); - if (!isEmpty(serviceMetadata)) { - metadata.putAll(serviceMetadata); - } - } + private void attachURLsIntoMetadata(ConsulRegistration consulRegistration) { + NewService newService = consulRegistration.getService(); + Map serviceMetadata = dubboServiceMetadataRepository + .getDubboMetadataServiceMetadata(); + if (!isEmpty(serviceMetadata)) { + List tags = newService.getTags(); + for (Map.Entry entry : serviceMetadata.entrySet()) { + tags.add(entry.getKey() + "=" + entry.getValue()); + } + } + } + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceRegistrationNonWebApplicationAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceRegistrationNonWebApplicationAutoConfiguration.java index 6247a520..9c8e209c 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceRegistrationNonWebApplicationAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/DubboServiceRegistrationNonWebApplicationAutoConfiguration.java @@ -16,10 +16,14 @@ */ package com.alibaba.cloud.dubbo.autoconfigure; +import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME; +import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.ZOOKEEPER_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME; + +import java.util.List; + import org.apache.dubbo.common.URL; import org.apache.dubbo.config.spring.ServiceBean; -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; @@ -30,8 +34,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.event.ApplicationStartedEvent; -import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; -import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; @@ -41,10 +43,9 @@ import org.springframework.cloud.zookeeper.serviceregistry.ServiceInstanceRegist import org.springframework.context.annotation.Configuration; import org.springframework.context.event.EventListener; -import java.util.List; - -import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_CONFIGURATION_CLASS_NAME; -import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.ZOOKEEPER_AUTO_CONFIGURATION_CLASS_NAME; +import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent; +import com.ecwid.consul.v1.agent.model.NewService; /** * Dubbo Service Registration Auto-{@link Configuration} for Non-Web application @@ -58,111 +59,113 @@ import static com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAuto @Aspect public class DubboServiceRegistrationNonWebApplicationAutoConfiguration { - private static final String REST_PROTOCOL = "rest"; + private static final String REST_PROTOCOL = "rest"; - @Autowired - private ServiceRegistry serviceRegistry; + @Autowired + private ServiceRegistry serviceRegistry; - @Autowired - private Registration registration; + @Autowired + private Registration registration; - private volatile Integer serverPort = null; + private volatile Integer serverPort = null; - private volatile boolean registered = false; + private volatile boolean registered = false; - @Autowired - private DubboServiceMetadataRepository repository; + @Autowired + private DubboServiceMetadataRepository repository; - @Around("execution(* org.springframework.cloud.client.serviceregistry.Registration.getPort())") - public Object getPort(ProceedingJoinPoint pjp) throws Throwable { - return serverPort != null ? serverPort : pjp.proceed(); - } + @Around("execution(* org.springframework.cloud.client.serviceregistry.Registration.getPort())") + public Object getPort(ProceedingJoinPoint pjp) throws Throwable { + return serverPort != null ? serverPort : pjp.proceed(); + } - @EventListener(ApplicationStartedEvent.class) - public void onApplicationStarted() { - setServerPort(); - register(); - } + @EventListener(ApplicationStartedEvent.class) + public void onApplicationStarted() { + setServerPort(); + register(); + } - private void register() { - if (registered) { - return; - } - serviceRegistry.register(registration); - registered = true; - } + 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. - */ - private void setServerPort() { - if (serverPort == null) { - for (List urls : repository.getAllExportedUrls().values()) { - urls.stream() - .filter(url -> REST_PROTOCOL.equalsIgnoreCase(url.getProtocol())) - .findFirst() - .ifPresent(url -> { - serverPort = url.getPort(); - }); + /** + * Set web port from {@link ServiceBean#getExportedUrls() exported URLs} if "rest" + * protocol is present. + */ + private void setServerPort() { + if (serverPort == null) { + for (List urls : repository.getAllExportedUrls().values()) { + urls.stream() + .filter(url -> REST_PROTOCOL.equalsIgnoreCase(url.getProtocol())) + .findFirst().ifPresent(url -> { + serverPort = url.getPort(); + }); - // If REST protocol is not present, use any applied port. - if (serverPort == null) { - urls.stream() - .findAny().ifPresent(url -> { - serverPort = url.getPort(); - }); - } - } - } - } + // If REST protocol is not present, use any applied port. + if (serverPort == null) { + urls.stream().findAny().ifPresent(url -> { + serverPort = url.getPort(); + }); + } + } + } + } - @Configuration - @ConditionalOnBean(name = ZOOKEEPER_AUTO_CONFIGURATION_CLASS_NAME) - class ZookeeperConfiguration implements SmartInitializingSingleton { + @Configuration + @ConditionalOnBean(name = ZOOKEEPER_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME) + class ZookeeperConfiguration implements SmartInitializingSingleton { - @Autowired - private ServiceInstanceRegistration registration; + @Autowired + private ServiceInstanceRegistration registration; - @EventListener(ServiceInstancePreRegisteredEvent.class) - public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) { - registration.setPort(serverPort); - } + @EventListener(ServiceInstancePreRegisteredEvent.class) + public void onServiceInstancePreRegistered( + ServiceInstancePreRegisteredEvent event) { + registration.setPort(serverPort); + } - @Override - public void afterSingletonsInstantiated() { - // invoke getServiceInstance() method to trigger the ServiceInstance building before register - registration.getServiceInstance(); - } - } + @Override + public void afterSingletonsInstantiated() { + // invoke getServiceInstance() method to trigger the ServiceInstance building + // before register + registration.getServiceInstance(); + } + } - @Configuration - @ConditionalOnBean(name = CONSUL_AUTO_CONFIGURATION_CLASS_NAME) - class ConsulConfiguration { + @Configuration + @ConditionalOnBean(name = CONSUL_AUTO_SERVICE_AUTO_CONFIGURATION_CLASS_NAME) + 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); - } + /** + * 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); - } - } - } + /** + * 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); + } + } + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/condition/MissingSpringCloudRegistryConfigPropertyCondition.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/condition/MissingSpringCloudRegistryConfigPropertyCondition.java index 66429440..3aa14cd7 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/condition/MissingSpringCloudRegistryConfigPropertyCondition.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/autoconfigure/condition/MissingSpringCloudRegistryConfigPropertyCondition.java @@ -16,55 +16,65 @@ */ package com.alibaba.cloud.dubbo.autoconfigure.condition; +import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.PROTOCOL; +import static org.apache.dubbo.config.spring.util.PropertySourcesUtils.getSubProperties; + +import java.util.Map; + import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; -import com.alibaba.cloud.dubbo.registry.SpringCloudRegistry; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.StringUtils; -import java.util.Map; - -import static org.apache.dubbo.config.spring.util.PropertySourcesUtils.getSubProperties; -import static com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory.PROTOCOL; +import com.alibaba.cloud.dubbo.registry.SpringCloudRegistry; /** * Missing {@link SpringCloudRegistry} Property {@link Condition} * + * @author Mercy * @see SpringCloudRegistry * @see Condition */ -public class MissingSpringCloudRegistryConfigPropertyCondition extends SpringBootCondition { +public class MissingSpringCloudRegistryConfigPropertyCondition + extends SpringBootCondition { + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + ConfigurableEnvironment environment = (ConfigurableEnvironment) context + .getEnvironment(); - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { - ConfigurableEnvironment environment = (ConfigurableEnvironment) context.getEnvironment(); + String protocol = environment.getProperty("dubbo.registry.protocol"); - String protocol = environment.getProperty("dubbo.registry.protocol"); + if (PROTOCOL.equals(protocol)) { + return ConditionOutcome.noMatch( + "'spring-cloud' protocol was found from 'dubbo.registry.protocol'"); + } - if (PROTOCOL.equals(protocol)) { - return ConditionOutcome.noMatch("'spring-cloud' protocol was found from 'dubbo.registry.protocol'"); - } + String address = environment.getProperty("dubbo.registry.address"); - String address = environment.getProperty("dubbo.registry.address"); + if (StringUtils.startsWithIgnoreCase(address, PROTOCOL)) { + return ConditionOutcome.noMatch( + "'spring-cloud' protocol was found from 'dubbo.registry.address'"); + } - if (StringUtils.startsWithIgnoreCase(address, PROTOCOL)) { - return ConditionOutcome.noMatch("'spring-cloud' protocol was found from 'dubbo.registry.address'"); - } + Map properties = getSubProperties(environment, + "dubbo.registries."); - Map properties = getSubProperties(environment, "dubbo.registries."); + boolean found = properties.entrySet().stream().anyMatch(entry -> { + String key = entry.getKey(); + String value = String.valueOf(entry.getValue()); + return (key.endsWith(".address") && value.startsWith(PROTOCOL)) + || (key.endsWith(".protocol") && PROTOCOL.equals(value)); - boolean found = properties.entrySet().stream().anyMatch(entry -> { - String key = entry.getKey(); - String value = String.valueOf(entry.getValue()); - return (key.endsWith(".address") && value.startsWith(PROTOCOL)) || - (key.endsWith(".protocol") && PROTOCOL.equals(value)); + }); - }); - - return found ? ConditionOutcome.noMatch("'spring-cloud' protocol was found in 'dubbo.registries.*'") : ConditionOutcome.match(); - } + return found + ? ConditionOutcome.noMatch( + "'spring-cloud' protocol was found in 'dubbo.registries.*'") + : ConditionOutcome.match(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboClientHttpResponse.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboClientHttpResponse.java index c1458ed6..27fc1c94 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboClientHttpResponse.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboClientHttpResponse.java @@ -16,14 +16,15 @@ */ package com.alibaba.cloud.dubbo.client.loadbalancer; +import java.io.IOException; +import java.io.InputStream; + import org.apache.dubbo.rpc.service.GenericException; + import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpResponse; -import java.io.IOException; -import java.io.InputStream; - /** * Dubbo {@link ClientHttpResponse} implementation * @@ -32,47 +33,50 @@ import java.io.InputStream; */ class DubboClientHttpResponse implements ClientHttpResponse { - private final HttpStatus httpStatus; + private final HttpStatus httpStatus; - private final String statusText; + private final String statusText; - private final HttpHeaders httpHeaders = new HttpHeaders(); + private final HttpHeaders httpHeaders = new HttpHeaders(); - private final DubboHttpOutputMessage httpOutputMessage; + private final DubboHttpOutputMessage httpOutputMessage; - public DubboClientHttpResponse(DubboHttpOutputMessage httpOutputMessage, GenericException exception) { - this.httpStatus = exception != null ? HttpStatus.INTERNAL_SERVER_ERROR : HttpStatus.OK; - this.statusText = exception != null ? exception.getExceptionMessage() : httpStatus.getReasonPhrase(); - this.httpOutputMessage = httpOutputMessage; - this.httpHeaders.putAll(httpOutputMessage.getHeaders()); - } + public DubboClientHttpResponse(DubboHttpOutputMessage httpOutputMessage, + GenericException exception) { + this.httpStatus = exception != null ? HttpStatus.INTERNAL_SERVER_ERROR + : HttpStatus.OK; + this.statusText = exception != null ? exception.getExceptionMessage() + : httpStatus.getReasonPhrase(); + this.httpOutputMessage = httpOutputMessage; + this.httpHeaders.putAll(httpOutputMessage.getHeaders()); + } - @Override - public HttpStatus getStatusCode() throws IOException { - return httpStatus; - } + @Override + public HttpStatus getStatusCode() throws IOException { + return httpStatus; + } - @Override - public int getRawStatusCode() throws IOException { - return httpStatus.value(); - } + @Override + public int getRawStatusCode() throws IOException { + return httpStatus.value(); + } - @Override - public String getStatusText() throws IOException { - return statusText; - } + @Override + public String getStatusText() throws IOException { + return statusText; + } - @Override - public void close() { - } + @Override + public void close() { + } - @Override - public InputStream getBody() throws IOException { - return httpOutputMessage.getBody().getInputStream(); - } + @Override + public InputStream getBody() throws IOException { + return httpOutputMessage.getBody().getInputStream(); + } - @Override - public HttpHeaders getHeaders() { - return httpHeaders; - } + @Override + public HttpHeaders getHeaders() { + return httpHeaders; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboClientHttpResponseFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboClientHttpResponseFactory.java index c503ac91..561515f7 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboClientHttpResponseFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboClientHttpResponseFactory.java @@ -16,17 +16,19 @@ */ package com.alibaba.cloud.dubbo.client.loadbalancer; +import java.io.IOException; +import java.util.List; + import org.apache.dubbo.rpc.service.GenericException; -import com.alibaba.cloud.dubbo.http.converter.HttpMessageConverterHolder; -import com.alibaba.cloud.dubbo.http.util.HttpMessageConverterResolver; -import com.alibaba.cloud.dubbo.metadata.RequestMetadata; -import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; + import org.springframework.http.MediaType; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.converter.HttpMessageConverter; -import java.io.IOException; -import java.util.List; +import com.alibaba.cloud.dubbo.http.converter.HttpMessageConverterHolder; +import com.alibaba.cloud.dubbo.http.util.HttpMessageConverterResolver; +import com.alibaba.cloud.dubbo.metadata.RequestMetadata; +import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; /** * Dubbo {@link ClientHttpResponse} Factory @@ -35,29 +37,33 @@ import java.util.List; */ class DubboClientHttpResponseFactory { - private final HttpMessageConverterResolver httpMessageConverterResolver; + private final HttpMessageConverterResolver httpMessageConverterResolver; - public DubboClientHttpResponseFactory(List> messageConverters, ClassLoader classLoader) { - this.httpMessageConverterResolver = new HttpMessageConverterResolver(messageConverters, classLoader); - } + public DubboClientHttpResponseFactory(List> messageConverters, + ClassLoader classLoader) { + this.httpMessageConverterResolver = new HttpMessageConverterResolver( + messageConverters, classLoader); + } - public ClientHttpResponse build(Object result, GenericException exception, - RequestMetadata requestMetadata, RestMethodMetadata restMethodMetadata) { + public ClientHttpResponse build(Object result, GenericException exception, + RequestMetadata requestMetadata, RestMethodMetadata restMethodMetadata) { - DubboHttpOutputMessage httpOutputMessage = new DubboHttpOutputMessage(); + DubboHttpOutputMessage httpOutputMessage = new DubboHttpOutputMessage(); - HttpMessageConverterHolder httpMessageConverterHolder = httpMessageConverterResolver.resolve(requestMetadata, restMethodMetadata); + HttpMessageConverterHolder httpMessageConverterHolder = httpMessageConverterResolver + .resolve(requestMetadata, restMethodMetadata); - if (httpMessageConverterHolder != null) { - MediaType mediaType = httpMessageConverterHolder.getMediaType(); - HttpMessageConverter converter = httpMessageConverterHolder.getConverter(); - try { - converter.write(result, mediaType, httpOutputMessage); - } catch (IOException e) { - e.printStackTrace(); - } - } + if (httpMessageConverterHolder != null) { + MediaType mediaType = httpMessageConverterHolder.getMediaType(); + HttpMessageConverter converter = httpMessageConverterHolder.getConverter(); + try { + converter.write(result, mediaType, httpOutputMessage); + } + catch (IOException e) { + e.printStackTrace(); + } + } - return new DubboClientHttpResponse(httpOutputMessage, exception); - } + return new DubboClientHttpResponse(httpOutputMessage, exception); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboHttpOutputMessage.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboHttpOutputMessage.java index 63457902..96a3e6c8 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboHttpOutputMessage.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboHttpOutputMessage.java @@ -16,12 +16,12 @@ */ package com.alibaba.cloud.dubbo.client.loadbalancer; +import java.io.IOException; + import org.springframework.http.HttpHeaders; import org.springframework.http.HttpOutputMessage; import org.springframework.util.FastByteArrayOutputStream; -import java.io.IOException; - /** * Dubbo {@link HttpOutputMessage} implementation * @@ -29,17 +29,17 @@ import java.io.IOException; */ class DubboHttpOutputMessage implements HttpOutputMessage { - private final FastByteArrayOutputStream outputStream = new FastByteArrayOutputStream(); + private final FastByteArrayOutputStream outputStream = new FastByteArrayOutputStream(); - private final HttpHeaders httpHeaders = new HttpHeaders(); + private final HttpHeaders httpHeaders = new HttpHeaders(); - @Override - public FastByteArrayOutputStream getBody() throws IOException { - return outputStream; - } + @Override + public FastByteArrayOutputStream getBody() throws IOException { + return outputStream; + } - @Override - public HttpHeaders getHeaders() { - return httpHeaders; - } + @Override + public HttpHeaders getHeaders() { + return httpHeaders; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboMetadataInitializerInterceptor.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboMetadataInitializerInterceptor.java index 7abf5143..53bc6a3f 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboMetadataInitializerInterceptor.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboMetadataInitializerInterceptor.java @@ -16,39 +16,42 @@ */ package com.alibaba.cloud.dubbo.client.loadbalancer; -import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import java.io.IOException; +import java.net.URI; + import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; -import java.io.IOException; -import java.net.URI; +import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; /** - * Dubbo Metadata {@link ClientHttpRequestInterceptor} Initializing Interceptor executes intercept before - * {@link DubboTransporterInterceptor} + * Dubbo Metadata {@link ClientHttpRequestInterceptor} Initializing Interceptor executes + * intercept before {@link DubboTransporterInterceptor} * * @author Mercy */ public class DubboMetadataInitializerInterceptor implements ClientHttpRequestInterceptor { - private final DubboServiceMetadataRepository repository; + private final DubboServiceMetadataRepository repository; - public DubboMetadataInitializerInterceptor(DubboServiceMetadataRepository repository) { - this.repository = repository; - } + public DubboMetadataInitializerInterceptor( + DubboServiceMetadataRepository repository) { + this.repository = repository; + } - @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, + ClientHttpRequestExecution execution) throws IOException { - URI originalUri = request.getURI(); + URI originalUri = request.getURI(); - String serviceName = originalUri.getHost(); + String serviceName = originalUri.getHost(); - repository.initialize(serviceName); + repository.initializeMetadata(serviceName); - // Execute next - return execution.execute(request, body); - } + // Execute next + return execution.execute(request, body); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboTransporterInterceptor.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboTransporterInterceptor.java index bec422ed..4d73112d 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboTransporterInterceptor.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/client/loadbalancer/DubboTransporterInterceptor.java @@ -16,17 +16,16 @@ */ package com.alibaba.cloud.dubbo.client.loadbalancer; +import static org.springframework.web.util.UriComponentsBuilder.fromUri; + +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.Map; + import org.apache.dubbo.rpc.service.GenericException; import org.apache.dubbo.rpc.service.GenericService; -import com.alibaba.cloud.dubbo.http.MutableHttpServerRequest; -import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata; -import com.alibaba.cloud.dubbo.metadata.RequestMetadata; -import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; -import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContext; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; @@ -38,12 +37,14 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.PathMatcher; import org.springframework.web.util.UriComponents; -import java.io.IOException; -import java.net.URI; -import java.util.List; -import java.util.Map; - -import static org.springframework.web.util.UriComponentsBuilder.fromUri; +import com.alibaba.cloud.dubbo.http.MutableHttpServerRequest; +import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata; +import com.alibaba.cloud.dubbo.metadata.RequestMetadata; +import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; +import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContext; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; /** * Dubbo Transporter {@link ClientHttpRequestInterceptor} implementation @@ -53,91 +54,100 @@ import static org.springframework.web.util.UriComponentsBuilder.fromUri; */ public class DubboTransporterInterceptor implements ClientHttpRequestInterceptor { - private final DubboServiceMetadataRepository repository; + private final DubboServiceMetadataRepository repository; - private final DubboClientHttpResponseFactory clientHttpResponseFactory; + private final DubboClientHttpResponseFactory clientHttpResponseFactory; - private final Map dubboTranslatedAttributes; + private final Map dubboTranslatedAttributes; - private final DubboGenericServiceFactory serviceFactory; + private final DubboGenericServiceFactory serviceFactory; - private final DubboGenericServiceExecutionContextFactory contextFactory; + private final DubboGenericServiceExecutionContextFactory contextFactory; - private final PathMatcher pathMatcher = new AntPathMatcher(); + private final PathMatcher pathMatcher = new AntPathMatcher(); - public DubboTransporterInterceptor(DubboServiceMetadataRepository dubboServiceMetadataRepository, - List> messageConverters, - ClassLoader classLoader, - Map dubboTranslatedAttributes, - DubboGenericServiceFactory serviceFactory, - DubboGenericServiceExecutionContextFactory contextFactory) { - this.repository = dubboServiceMetadataRepository; - this.dubboTranslatedAttributes = dubboTranslatedAttributes; - this.clientHttpResponseFactory = new DubboClientHttpResponseFactory(messageConverters, classLoader); - this.serviceFactory = serviceFactory; - this.contextFactory = contextFactory; - } + public DubboTransporterInterceptor( + DubboServiceMetadataRepository dubboServiceMetadataRepository, + List> messageConverters, ClassLoader classLoader, + Map dubboTranslatedAttributes, + DubboGenericServiceFactory serviceFactory, + DubboGenericServiceExecutionContextFactory contextFactory) { + this.repository = dubboServiceMetadataRepository; + this.dubboTranslatedAttributes = dubboTranslatedAttributes; + this.clientHttpResponseFactory = new DubboClientHttpResponseFactory( + messageConverters, classLoader); + this.serviceFactory = serviceFactory; + this.contextFactory = contextFactory; + } - @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, + ClientHttpRequestExecution execution) throws IOException { - URI originalUri = request.getURI(); + URI originalUri = request.getURI(); - String serviceName = originalUri.getHost(); + String serviceName = originalUri.getHost(); - RequestMetadata clientMetadata = buildRequestMetadata(request); + RequestMetadata clientMetadata = buildRequestMetadata(request); - DubboRestServiceMetadata metadata = repository.get(serviceName, clientMetadata); + DubboRestServiceMetadata metadata = repository.get(serviceName, clientMetadata); - if (metadata == null) { - // if DubboServiceMetadata is not found, executes next - return execution.execute(request, body); - } + if (metadata == null) { + // if DubboServiceMetadata is not found, executes next + return execution.execute(request, body); + } - RestMethodMetadata dubboRestMethodMetadata = metadata.getRestMethodMetadata(); + RestMethodMetadata dubboRestMethodMetadata = metadata.getRestMethodMetadata(); - GenericService genericService = serviceFactory.create(metadata, dubboTranslatedAttributes); + GenericService genericService = serviceFactory.create(metadata, + dubboTranslatedAttributes); - MutableHttpServerRequest httpServerRequest = new MutableHttpServerRequest(request, body); + MutableHttpServerRequest httpServerRequest = new MutableHttpServerRequest(request, + body); - customizeRequest(httpServerRequest, dubboRestMethodMetadata, clientMetadata); + customizeRequest(httpServerRequest, dubboRestMethodMetadata, clientMetadata); - DubboGenericServiceExecutionContext context = contextFactory.create(dubboRestMethodMetadata, httpServerRequest); + DubboGenericServiceExecutionContext context = contextFactory + .create(dubboRestMethodMetadata, httpServerRequest); - Object result = null; - GenericException exception = null; + Object result = null; + GenericException exception = null; - try { - result = genericService.$invoke(context.getMethodName(), context.getParameterTypes(), context.getParameters()); - } catch (GenericException e) { - exception = e; - } + try { + result = genericService.$invoke(context.getMethodName(), + context.getParameterTypes(), context.getParameters()); + } + catch (GenericException e) { + exception = e; + } - return clientHttpResponseFactory.build(result, exception, clientMetadata, dubboRestMethodMetadata); - } + return clientHttpResponseFactory.build(result, exception, clientMetadata, + dubboRestMethodMetadata); + } - protected void customizeRequest(MutableHttpServerRequest httpServerRequest, - RestMethodMetadata dubboRestMethodMetadata, RequestMetadata clientMetadata) { + protected void customizeRequest(MutableHttpServerRequest httpServerRequest, + RestMethodMetadata dubboRestMethodMetadata, RequestMetadata clientMetadata) { - RequestMetadata dubboRequestMetadata = dubboRestMethodMetadata.getRequest(); - String pathPattern = dubboRequestMetadata.getPath(); + RequestMetadata dubboRequestMetadata = dubboRestMethodMetadata.getRequest(); + String pathPattern = dubboRequestMetadata.getPath(); - Map pathVariables = pathMatcher.extractUriTemplateVariables(pathPattern, httpServerRequest.getPath()); + Map pathVariables = pathMatcher + .extractUriTemplateVariables(pathPattern, httpServerRequest.getPath()); - if (!CollectionUtils.isEmpty(pathVariables)) { - // Put path variables Map into query parameters Map - httpServerRequest.params(pathVariables); - } + if (!CollectionUtils.isEmpty(pathVariables)) { + // Put path variables Map into query parameters Map + httpServerRequest.params(pathVariables); + } - } + } - private RequestMetadata buildRequestMetadata(HttpRequest request) { - UriComponents uriComponents = fromUri(request.getURI()).build(true); - RequestMetadata requestMetadata = new RequestMetadata(); - requestMetadata.setPath(uriComponents.getPath()); - requestMetadata.setMethod(request.getMethod().name()); - requestMetadata.setParams(uriComponents.getQueryParams()); - requestMetadata.setHeaders(request.getHeaders()); - return requestMetadata; - } + private RequestMetadata buildRequestMetadata(HttpRequest request) { + UriComponents uriComponents = fromUri(request.getURI()).build(true); + RequestMetadata requestMetadata = new RequestMetadata(); + requestMetadata.setPath(uriComponents.getPath()); + requestMetadata.setMethod(request.getMethod().name()); + requestMetadata.setParams(uriComponents.getQueryParams()); + requestMetadata.setHeaders(request.getHeaders()); + return requestMetadata; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/context/DubboServiceRegistrationApplicationContextInitializer.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/context/DubboServiceRegistrationApplicationContextInitializer.java index 83dd9831..e9c747cf 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/context/DubboServiceRegistrationApplicationContextInitializer.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/context/DubboServiceRegistrationApplicationContextInitializer.java @@ -16,23 +16,26 @@ */ package com.alibaba.cloud.dubbo.context; -import com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; +import com.alibaba.cloud.dubbo.registry.SpringCloudRegistryFactory; + /** - * The Dubbo services will be registered as the specified Spring cloud applications that will not be considered - * normal ones, but only are used to Dubbo's service discovery even if it is based on Spring Cloud Commons abstraction. - * However, current application will be registered by other DiscoveryClientAutoConfiguration. + * The Dubbo services will be registered as the specified Spring cloud applications that + * will not be considered normal ones, but only are used to Dubbo's service discovery even + * if it is based on Spring Cloud Commons abstraction. However, current application will + * be registered by other DiscoveryClientAutoConfiguration. * * @author Mercy */ -public class DubboServiceRegistrationApplicationContextInitializer implements - ApplicationContextInitializer { +public class DubboServiceRegistrationApplicationContextInitializer + implements ApplicationContextInitializer { - @Override - public void initialize(ConfigurableApplicationContext applicationContext) { - // Set ApplicationContext into SpringCloudRegistryFactory before Dubbo Service Register - SpringCloudRegistryFactory.setApplicationContext(applicationContext); - } + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + // Set ApplicationContext into SpringCloudRegistryFactory before Dubbo Service + // Register + SpringCloudRegistryFactory.setApplicationContext(applicationContext); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/env/DubboCloudProperties.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/env/DubboCloudProperties.java index dc349e58..b9d33c37 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/env/DubboCloudProperties.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/env/DubboCloudProperties.java @@ -16,15 +16,15 @@ */ package com.alibaba.cloud.dubbo.env; -import org.springframework.boot.context.properties.ConfigurationProperties; +import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; +import static org.springframework.util.StringUtils.hasText; +import static org.springframework.util.StringUtils.trimAllWhitespace; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; -import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; -import static org.springframework.util.StringUtils.hasText; -import static org.springframework.util.StringUtils.trimAllWhitespace; +import org.springframework.boot.context.properties.ConfigurationProperties; /** * Dubbo Cloud {@link ConfigurationProperties Properties} @@ -34,48 +34,49 @@ import static org.springframework.util.StringUtils.trimAllWhitespace; @ConfigurationProperties(prefix = "dubbo.cloud") public class DubboCloudProperties { - /** - * All services of Dubbo - */ - public static final String ALL_DUBBO_SERVICES = "*"; + /** + * All services of Dubbo + */ + public static final String ALL_DUBBO_SERVICES = "*"; - /** - * The subscribed services, the default value is "*". The multiple value will use comma(",") as the separator. - * - * @see #ALL_DUBBO_SERVICES - */ - private String subscribedServices = ALL_DUBBO_SERVICES; + /** + * The subscribed services, the default value is "*". The multiple value will use + * comma(",") as the separator. + * + * @see #ALL_DUBBO_SERVICES + */ + private String subscribedServices = ALL_DUBBO_SERVICES; - public String getSubscribedServices() { - return subscribedServices; - } + public String getSubscribedServices() { + return subscribedServices; + } - public void setSubscribedServices(String subscribedServices) { - this.subscribedServices = subscribedServices; - } + public void setSubscribedServices(String subscribedServices) { + this.subscribedServices = subscribedServices; + } - /** - * Get the subscribed services as a {@link Set} with configuration order. - * - * @return non-null Read-only {@link Set} - */ - public Set subscribedServices() { + /** + * Get the subscribed services as a {@link Set} with configuration order. + * + * @return non-null Read-only {@link Set} + */ + public Set subscribedServices() { - String[] services = commaDelimitedListToStringArray(getSubscribedServices()); + String[] services = commaDelimitedListToStringArray(getSubscribedServices()); - if (services.length < 1) { - return Collections.emptySet(); - } + if (services.length < 1) { + return Collections.emptySet(); + } - Set subscribedServices = new LinkedHashSet<>(); + Set subscribedServices = new LinkedHashSet<>(); - for (String service : services) { - if (hasText(service)) { // filter blank service name - // remove all whitespace - subscribedServices.add(trimAllWhitespace(service)); - } - } + for (String service : services) { + if (hasText(service)) { // filter blank service name + // remove all whitespace + subscribedServices.add(trimAllWhitespace(service)); + } + } - return Collections.unmodifiableSet(subscribedServices); - } + return Collections.unmodifiableSet(subscribedServices); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/env/DubboNonWebApplicationEnvironmentPostProcessor.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/env/DubboNonWebApplicationEnvironmentPostProcessor.java index b7261e74..90eae069 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/env/DubboNonWebApplicationEnvironmentPostProcessor.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/env/DubboNonWebApplicationEnvironmentPostProcessor.java @@ -16,6 +16,13 @@ */ package com.alibaba.cloud.dubbo.env; +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_PROTOCOL; +import static org.apache.dubbo.config.spring.util.PropertySourcesUtils.getSubProperties; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; @@ -29,179 +36,191 @@ import org.springframework.core.env.PropertySource; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL; -import static org.apache.dubbo.config.spring.util.PropertySourcesUtils.getSubProperties; - /** - * Dubbo {@link WebApplicationType#NONE Non-Web Application} {@link EnvironmentPostProcessor} + * Dubbo {@link WebApplicationType#NONE Non-Web Application} + * {@link EnvironmentPostProcessor} * * @author Mercy */ -public class DubboNonWebApplicationEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { +public class DubboNonWebApplicationEnvironmentPostProcessor + implements EnvironmentPostProcessor, Ordered { - private static final String DOT = "."; + private static final String DOT = "."; - /** - * The name of default {@link PropertySource} defined in SpringApplication#configurePropertySources method. - */ - private static final String PROPERTY_SOURCE_NAME = "defaultProperties"; + /** + * The name of default {@link PropertySource} defined in + * SpringApplication#configurePropertySources method. + */ + private static final String PROPERTY_SOURCE_NAME = "defaultProperties"; - private static final String SERVER_PORT_PROPERTY_NAME = "server.port"; + private static final String SERVER_PORT_PROPERTY_NAME = "server.port"; - private static final String PORT_PROPERTY_NAME = "port"; + private static final String PORT_PROPERTY_NAME = "port"; - private static final String PROTOCOL_PROPERTY_NAME_PREFIX = "dubbo.protocol"; + private static final String PROTOCOL_PROPERTY_NAME_PREFIX = "dubbo.protocol"; - private static final String PROTOCOL_NAME_PROPERTY_NAME_SUFFIX = DOT + "name"; + private static final String PROTOCOL_NAME_PROPERTY_NAME_SUFFIX = DOT + "name"; - private static final String PROTOCOL_PORT_PROPERTY_NAME_SUFFIX = DOT + PORT_PROPERTY_NAME; + private static final String PROTOCOL_PORT_PROPERTY_NAME_SUFFIX = DOT + + PORT_PROPERTY_NAME; - private static final String PROTOCOL_PORT_PROPERTY_NAME = PROTOCOL_PROPERTY_NAME_PREFIX + PROTOCOL_PORT_PROPERTY_NAME_SUFFIX; + private static final String PROTOCOL_PORT_PROPERTY_NAME = PROTOCOL_PROPERTY_NAME_PREFIX + + PROTOCOL_PORT_PROPERTY_NAME_SUFFIX; - private static final String PROTOCOL_NAME_PROPERTY_NAME = PROTOCOL_PROPERTY_NAME_PREFIX + PROTOCOL_NAME_PROPERTY_NAME_SUFFIX; + private static final String PROTOCOL_NAME_PROPERTY_NAME = PROTOCOL_PROPERTY_NAME_PREFIX + + PROTOCOL_NAME_PROPERTY_NAME_SUFFIX; - private static final String PROTOCOLS_PROPERTY_NAME_PREFIX = "dubbo.protocols"; + private static final String PROTOCOLS_PROPERTY_NAME_PREFIX = "dubbo.protocols"; - private static final String REST_PROTOCOL = "rest"; + private static final String REST_PROTOCOL = "rest"; - private final Logger logger = LoggerFactory.getLogger(DubboNonWebApplicationEnvironmentPostProcessor.class); + private final Logger logger = LoggerFactory + .getLogger(DubboNonWebApplicationEnvironmentPostProcessor.class); - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { - WebApplicationType webApplicationType = application.getWebApplicationType(); + private static boolean isRestProtocol(String protocol) { + return REST_PROTOCOL.equalsIgnoreCase(protocol); + } - if (!WebApplicationType.NONE.equals(webApplicationType)) { // Just works in Non-Web Application - if (logger.isDebugEnabled()) { - logger.debug("Current application is a Web Application, the process will be ignored."); - } - return; - } + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, + SpringApplication application) { + WebApplicationType webApplicationType = application.getWebApplicationType(); - MutablePropertySources propertySources = environment.getPropertySources(); - Map defaultProperties = createDefaultProperties(environment); - if (!CollectionUtils.isEmpty(defaultProperties)) { - addOrReplace(propertySources, defaultProperties); - } - } + if (!WebApplicationType.NONE.equals(webApplicationType)) { // Just works in + // Non-Web Application + if (logger.isDebugEnabled()) { + logger.debug( + "Current application is a Web Application, the process will be ignored."); + } + return; + } - private Map createDefaultProperties(ConfigurableEnvironment environment) { - Map defaultProperties = new HashMap(); - resetServerPort(environment, defaultProperties); - return defaultProperties; - } + MutablePropertySources propertySources = environment.getPropertySources(); + Map defaultProperties = createDefaultProperties(environment); + if (!CollectionUtils.isEmpty(defaultProperties)) { + addOrReplace(propertySources, defaultProperties); + } + } - /** - * Reset server port property if it's absent, whose value is configured by "dubbbo.protocol.port" - * or "dubbo.protcols.rest.port" - * - * @param environment - * @param defaultProperties - */ - private void resetServerPort(ConfigurableEnvironment environment, Map defaultProperties) { + private Map createDefaultProperties( + ConfigurableEnvironment environment) { + Map defaultProperties = new HashMap(); + resetServerPort(environment, defaultProperties); + return defaultProperties; + } - String serverPort = environment.getProperty(SERVER_PORT_PROPERTY_NAME, environment.getProperty(PORT_PROPERTY_NAME)); + /** + * Reset server port property if it's absent, whose value is configured by + * "dubbbo.protocol.port" or "dubbo.protcols.rest.port" + * + * @param environment + * @param defaultProperties + */ + private void resetServerPort(ConfigurableEnvironment environment, + Map defaultProperties) { - if (serverPort != null) { - return; - } + String serverPort = environment.getProperty(SERVER_PORT_PROPERTY_NAME, + environment.getProperty(PORT_PROPERTY_NAME)); - serverPort = getRestPortFromProtocolProperty(environment); + if (serverPort != null) { + return; + } - if (serverPort == null) { - serverPort = getRestPortFromProtocolsProperties(environment); - } + serverPort = getRestPortFromProtocolProperty(environment); - setServerPort(environment, serverPort, defaultProperties); - } + if (serverPort == null) { + serverPort = getRestPortFromProtocolsProperties(environment); + } - private String getRestPortFromProtocolProperty(ConfigurableEnvironment environment) { + setServerPort(environment, serverPort, defaultProperties); + } - String protocol = environment.getProperty(PROTOCOL_NAME_PROPERTY_NAME, DEFAULT_PROTOCOL); + private String getRestPortFromProtocolProperty(ConfigurableEnvironment environment) { - return isRestProtocol(protocol) ? - environment.getProperty(PROTOCOL_PORT_PROPERTY_NAME) : - null; - } + String protocol = environment.getProperty(PROTOCOL_NAME_PROPERTY_NAME, + DEFAULT_PROTOCOL); - private String getRestPortFromProtocolsProperties(ConfigurableEnvironment environment) { + return isRestProtocol(protocol) + ? environment.getProperty(PROTOCOL_PORT_PROPERTY_NAME) + : null; + } - String restPort = null; + private String getRestPortFromProtocolsProperties( + ConfigurableEnvironment environment) { - Map subProperties = getSubProperties(environment, PROTOCOLS_PROPERTY_NAME_PREFIX); + String restPort = null; - Properties properties = new Properties(); + Map subProperties = getSubProperties(environment, + PROTOCOLS_PROPERTY_NAME_PREFIX); - properties.putAll(subProperties); + Properties properties = new Properties(); - for (String propertyName : properties.stringPropertyNames()) { - if (propertyName.endsWith(PROTOCOL_NAME_PROPERTY_NAME_SUFFIX)) { // protocol name property - String protocol = properties.getProperty(propertyName); - if (isRestProtocol(protocol)) { - String beanName = resolveBeanName(propertyName); - if (StringUtils.hasText(beanName)) { - restPort = properties.getProperty(beanName + PROTOCOL_PORT_PROPERTY_NAME_SUFFIX); - break; - } - } - } - } + properties.putAll(subProperties); - return restPort; - } + for (String propertyName : properties.stringPropertyNames()) { + if (propertyName.endsWith(PROTOCOL_NAME_PROPERTY_NAME_SUFFIX)) { // protocol + // name + // property + String protocol = properties.getProperty(propertyName); + if (isRestProtocol(protocol)) { + String beanName = resolveBeanName(propertyName); + if (StringUtils.hasText(beanName)) { + restPort = properties.getProperty( + beanName + PROTOCOL_PORT_PROPERTY_NAME_SUFFIX); + break; + } + } + } + } - private String resolveBeanName(String propertyName) { - int index = propertyName.indexOf(DOT); - return index > -1 ? propertyName.substring(0, index) : null; - } + return restPort; + } - private void setServerPort(ConfigurableEnvironment environment, String serverPort, - Map defaultProperties) { - if (serverPort == null) { - return; - } + private String resolveBeanName(String propertyName) { + int index = propertyName.indexOf(DOT); + return index > -1 ? propertyName.substring(0, index) : null; + } - defaultProperties.put(SERVER_PORT_PROPERTY_NAME, serverPort); + private void setServerPort(ConfigurableEnvironment environment, String serverPort, + Map defaultProperties) { + if (serverPort == null) { + return; + } - } + defaultProperties.put(SERVER_PORT_PROPERTY_NAME, serverPort); - /** - * Copy from BusEnvironmentPostProcessor#addOrReplace(MutablePropertySources, Map) - * - * @param propertySources {@link MutablePropertySources} - * @param map Default Dubbo Properties - */ - private void addOrReplace(MutablePropertySources propertySources, - Map map) { - MapPropertySource target = null; - if (propertySources.contains(PROPERTY_SOURCE_NAME)) { - PropertySource source = propertySources.get(PROPERTY_SOURCE_NAME); - if (source instanceof MapPropertySource) { - target = (MapPropertySource) source; - for (String key : map.keySet()) { - if (!target.containsProperty(key)) { - target.getSource().put(key, map.get(key)); - } - } - } - } - if (target == null) { - target = new MapPropertySource(PROPERTY_SOURCE_NAME, map); - } - if (!propertySources.contains(PROPERTY_SOURCE_NAME)) { - propertySources.addLast(target); - } - } + } - @Override - public int getOrder() { // Keep LOWEST_PRECEDENCE - return LOWEST_PRECEDENCE; - } + /** + * Copy from BusEnvironmentPostProcessor#addOrReplace(MutablePropertySources, Map) + * + * @param propertySources {@link MutablePropertySources} + * @param map Default Dubbo Properties + */ + private void addOrReplace(MutablePropertySources propertySources, + Map map) { + MapPropertySource target = null; + if (propertySources.contains(PROPERTY_SOURCE_NAME)) { + PropertySource source = propertySources.get(PROPERTY_SOURCE_NAME); + if (source instanceof MapPropertySource) { + target = (MapPropertySource) source; + for (String key : map.keySet()) { + if (!target.containsProperty(key)) { + target.getSource().put(key, map.get(key)); + } + } + } + } + if (target == null) { + target = new MapPropertySource(PROPERTY_SOURCE_NAME, map); + } + if (!propertySources.contains(PROPERTY_SOURCE_NAME)) { + propertySources.addLast(target); + } + } - private static boolean isRestProtocol(String protocol) { - return REST_PROTOCOL.equalsIgnoreCase(protocol); - } + @Override + public int getOrder() { // Keep LOWEST_PRECEDENCE + return LOWEST_PRECEDENCE; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/ByteArrayHttpInputMessage.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/ByteArrayHttpInputMessage.java index 83d0b1e6..4484a580 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/ByteArrayHttpInputMessage.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/ByteArrayHttpInputMessage.java @@ -16,13 +16,14 @@ */ package com.alibaba.cloud.dubbo.http; -import org.apache.dubbo.common.io.UnsafeByteArrayInputStream; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpInputMessage; - import java.io.IOException; import java.io.InputStream; +import org.apache.dubbo.common.io.UnsafeByteArrayInputStream; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpInputMessage; + /** * Byte array {@link HttpInputMessage} implementation * @@ -30,26 +31,26 @@ import java.io.InputStream; */ class ByteArrayHttpInputMessage implements HttpInputMessage { - private final HttpHeaders httpHeaders; + private final HttpHeaders httpHeaders; - private final InputStream inputStream; + private final InputStream inputStream; - public ByteArrayHttpInputMessage(byte[] body) { - this(new HttpHeaders(), body); - } + public ByteArrayHttpInputMessage(byte[] body) { + this(new HttpHeaders(), body); + } - public ByteArrayHttpInputMessage(HttpHeaders httpHeaders, byte[] body) { - this.httpHeaders = httpHeaders; - this.inputStream = new UnsafeByteArrayInputStream(body); - } + public ByteArrayHttpInputMessage(HttpHeaders httpHeaders, byte[] body) { + this.httpHeaders = httpHeaders; + this.inputStream = new UnsafeByteArrayInputStream(body); + } - @Override - public InputStream getBody() throws IOException { - return inputStream; - } + @Override + public InputStream getBody() throws IOException { + return inputStream; + } - @Override - public HttpHeaders getHeaders() { - return httpHeaders; - } + @Override + public HttpHeaders getHeaders() { + return httpHeaders; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/DefaultHttpRequest.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/DefaultHttpRequest.java index 228b8340..183c4096 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/DefaultHttpRequest.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/DefaultHttpRequest.java @@ -16,6 +16,12 @@ */ package com.alibaba.cloud.dubbo.http; +import static org.springframework.web.util.UriComponentsBuilder.fromPath; + +import java.net.URI; +import java.util.List; +import java.util.Map; + import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; @@ -23,12 +29,6 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.util.UriComponentsBuilder; -import java.net.URI; -import java.util.List; -import java.util.Map; - -import static org.springframework.web.util.UriComponentsBuilder.fromPath; - /** * Default {@link HttpRequest} implementation * @@ -36,96 +36,95 @@ import static org.springframework.web.util.UriComponentsBuilder.fromPath; */ public class DefaultHttpRequest implements HttpRequest { - private final String method; + private final String method; - private final URI uri; + private final URI uri; - private final HttpHeaders headers = new HttpHeaders(); + private final HttpHeaders headers = new HttpHeaders(); - public DefaultHttpRequest(String method, String path, Map> params, - Map> headers) { - this.method = method == null ? HttpMethod.GET.name() : method.toUpperCase(); - this.uri = buildURI(path, params); - this.headers.putAll(headers); - } + public DefaultHttpRequest(String method, String path, + Map> params, Map> headers) { + this.method = method == null ? HttpMethod.GET.name() : method.toUpperCase(); + this.uri = buildURI(path, params); + this.headers.putAll(headers); + } - private URI buildURI(String path, Map> params) { - UriComponentsBuilder builder = fromPath(path) - .queryParams(new LinkedMultiValueMap<>(params)); - return builder.build().toUri(); - } + public static Builder builder() { + return new Builder(); + } - @Override - public HttpMethod getMethod() { - return HttpMethod.resolve(getMethodValue()); - } + private URI buildURI(String path, Map> params) { + UriComponentsBuilder builder = fromPath(path) + .queryParams(new LinkedMultiValueMap<>(params)); + return builder.build().toUri(); + } - @Override - public String getMethodValue() { - return method; - } + @Override + public HttpMethod getMethod() { + return HttpMethod.resolve(getMethodValue()); + } - @Override - public URI getURI() { - return uri; - } + @Override + public String getMethodValue() { + return method; + } - @Override - public HttpHeaders getHeaders() { - return headers; - } + @Override + public URI getURI() { + return uri; + } - public static Builder builder() { - return new Builder(); - } + @Override + public HttpHeaders getHeaders() { + return headers; + } - /** - * {@link HttpRequest} Builder - */ - public static class Builder { + /** + * {@link HttpRequest} Builder + */ + public static class Builder { - String method; + String method; - String path; + String path; - MultiValueMap params = new LinkedMultiValueMap<>(); + MultiValueMap params = new LinkedMultiValueMap<>(); - MultiValueMap headers = new LinkedMultiValueMap<>(); + MultiValueMap headers = new LinkedMultiValueMap<>(); - public Builder method(String method) { - this.method = method; - return this; - } + public Builder method(String method) { + this.method = method; + return this; + } - public Builder path(String path) { - this.path = path; - return this; - } + public Builder path(String path) { + this.path = path; + return this; + } - public Builder param(String name, String value) { - this.params.add(name, value); - return this; - } + public Builder param(String name, String value) { + this.params.add(name, value); + return this; + } - public Builder header(String name, String value) { - this.headers.add(name, value); - return this; - } + public Builder header(String name, String value) { + this.headers.add(name, value); + return this; + } - public Builder params(Map> params) { - this.params.putAll(params); - return this; - } + public Builder params(Map> params) { + this.params.putAll(params); + return this; + } - public Builder headers(Map> headers) { - this.headers.putAll(headers); - return this; - } - - public HttpRequest build() { - return new DefaultHttpRequest(method, path, params, headers); - } - } + public Builder headers(Map> headers) { + this.headers.putAll(headers); + return this; + } + public HttpRequest build() { + return new DefaultHttpRequest(method, path, params, headers); + } + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/HttpServerRequest.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/HttpServerRequest.java index 00bcbf40..11af6767 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/HttpServerRequest.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/HttpServerRequest.java @@ -27,16 +27,16 @@ import org.springframework.util.MultiValueMap; */ public interface HttpServerRequest extends HttpRequest, HttpInputMessage { - /** - * Return a path of current HTTP request - * - * @return - */ - String getPath(); + /** + * Return a path of current HTTP request + * + * @return + */ + String getPath(); - /** - * Return a map with parsed and decoded query parameter values. - */ - MultiValueMap getQueryParams(); + /** + * Return a map with parsed and decoded query parameter values. + */ + MultiValueMap getQueryParams(); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/MutableHttpServerRequest.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/MutableHttpServerRequest.java index 602c685a..3348bd69 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/MutableHttpServerRequest.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/MutableHttpServerRequest.java @@ -16,18 +16,18 @@ */ package com.alibaba.cloud.dubbo.http; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpInputMessage; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpRequest; -import org.springframework.util.MultiValueMap; +import static com.alibaba.cloud.dubbo.http.util.HttpUtils.getParameters; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.Map; -import static com.alibaba.cloud.dubbo.http.util.HttpUtils.getParameters; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpRequest; +import org.springframework.util.MultiValueMap; /** * Mutable {@link HttpServerRequest} implementation @@ -36,65 +36,65 @@ import static com.alibaba.cloud.dubbo.http.util.HttpUtils.getParameters; */ public class MutableHttpServerRequest implements HttpServerRequest { - private final HttpMethod httpMethod; + private final HttpMethod httpMethod; - private final URI uri; + private final URI uri; - private final String path; + private final String path; - private final MultiValueMap queryParams; + private final MultiValueMap queryParams; - private final HttpHeaders httpHeaders; + private final HttpHeaders httpHeaders; - private final HttpInputMessage httpInputMessage; + private final HttpInputMessage httpInputMessage; - public MutableHttpServerRequest(HttpRequest httpRequest, byte[] body) { - this.httpMethod = httpRequest.getMethod(); - this.uri = httpRequest.getURI(); - this.path = uri.getPath(); - this.httpHeaders = httpRequest.getHeaders(); - this.queryParams = getParameters(httpRequest); - this.httpInputMessage = new ByteArrayHttpInputMessage(body); - } + public MutableHttpServerRequest(HttpRequest httpRequest, byte[] body) { + this.httpMethod = httpRequest.getMethod(); + this.uri = httpRequest.getURI(); + this.path = uri.getPath(); + this.httpHeaders = httpRequest.getHeaders(); + this.queryParams = getParameters(httpRequest); + this.httpInputMessage = new ByteArrayHttpInputMessage(body); + } - public MutableHttpServerRequest params(Map params) { - queryParams.setAll(params); - return this; - } + public MutableHttpServerRequest params(Map params) { + queryParams.setAll(params); + return this; + } - @Override - public InputStream getBody() throws IOException { - return httpInputMessage.getBody(); - } + @Override + public InputStream getBody() throws IOException { + return httpInputMessage.getBody(); + } - @Override - public HttpMethod getMethod() { - return httpMethod; - } + @Override + public HttpMethod getMethod() { + return httpMethod; + } - // Override method since Spring Framework 5.0 - @Override - public String getMethodValue() { - return httpMethod.name(); - } + // Override method since Spring Framework 5.0 + @Override + public String getMethodValue() { + return httpMethod.name(); + } - @Override - public URI getURI() { - return uri; - } + @Override + public URI getURI() { + return uri; + } - @Override - public HttpHeaders getHeaders() { - return httpHeaders; - } + @Override + public HttpHeaders getHeaders() { + return httpHeaders; + } - @Override - public String getPath() { - return path; - } + @Override + public String getPath() { + return path; + } - @Override - public MultiValueMap getQueryParams() { - return queryParams; - } + @Override + public MultiValueMap getQueryParams() { + return queryParams; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/converter/HttpMessageConverterHolder.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/converter/HttpMessageConverterHolder.java index 2a242386..67d2f30b 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/converter/HttpMessageConverterHolder.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/converter/HttpMessageConverterHolder.java @@ -26,20 +26,21 @@ import org.springframework.http.converter.HttpMessageConverter; */ public class HttpMessageConverterHolder { - private final MediaType mediaType; + private final MediaType mediaType; - private final HttpMessageConverter converter; + private final HttpMessageConverter converter; - public HttpMessageConverterHolder(MediaType mediaType, HttpMessageConverter converter) { - this.mediaType = mediaType; - this.converter = converter; - } + public HttpMessageConverterHolder(MediaType mediaType, + HttpMessageConverter converter) { + this.mediaType = mediaType; + this.converter = converter; + } - public MediaType getMediaType() { - return mediaType; - } + public MediaType getMediaType() { + return mediaType; + } - public HttpMessageConverter getConverter() { - return converter; - } + public HttpMessageConverter getConverter() { + return converter; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/AbstractMediaTypeExpression.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/AbstractMediaTypeExpression.java index 5f47f1bc..60e8939f 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/AbstractMediaTypeExpression.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/AbstractMediaTypeExpression.java @@ -19,73 +19,77 @@ package com.alibaba.cloud.dubbo.http.matcher; import org.springframework.http.MediaType; /** - * The some source code is scratched from org.springframework.web.servlet.mvc.condition.AbstractMediaTypeExpression + * The some source code is scratched from + * org.springframework.web.servlet.mvc.condition.AbstractMediaTypeExpression * * @author Arjen Poutsma * @author Rossen Stoyanchev * @author Mercy */ -public class AbstractMediaTypeExpression implements MediaTypeExpression, Comparable { +public class AbstractMediaTypeExpression + implements MediaTypeExpression, Comparable { - private final MediaType mediaType; + private final MediaType mediaType; - private final boolean negated; + private final boolean negated; - AbstractMediaTypeExpression(String expression) { - if (expression.startsWith("!")) { - this.negated = true; - expression = expression.substring(1); - } else { - this.negated = false; - } - this.mediaType = MediaType.parseMediaType(expression); - } + AbstractMediaTypeExpression(String expression) { + if (expression.startsWith("!")) { + this.negated = true; + expression = expression.substring(1); + } + else { + this.negated = false; + } + this.mediaType = MediaType.parseMediaType(expression); + } - AbstractMediaTypeExpression(MediaType mediaType, boolean negated) { - this.mediaType = mediaType; - this.negated = negated; - } + AbstractMediaTypeExpression(MediaType mediaType, boolean negated) { + this.mediaType = mediaType; + this.negated = negated; + } - @Override - public MediaType getMediaType() { - return this.mediaType; - } + @Override + public MediaType getMediaType() { + return this.mediaType; + } - @Override - public boolean isNegated() { - return this.negated; - } + @Override + public boolean isNegated() { + return this.negated; + } + @Override + public int compareTo(AbstractMediaTypeExpression other) { + return MediaType.SPECIFICITY_COMPARATOR.compare(this.getMediaType(), + other.getMediaType()); + } - @Override - public int compareTo(AbstractMediaTypeExpression other) { - return MediaType.SPECIFICITY_COMPARATOR.compare(this.getMediaType(), other.getMediaType()); - } + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + AbstractMediaTypeExpression otherExpr = (AbstractMediaTypeExpression) other; + return (this.mediaType.equals(otherExpr.mediaType) + && this.negated == otherExpr.negated); + } - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; - } - AbstractMediaTypeExpression otherExpr = (AbstractMediaTypeExpression) other; - return (this.mediaType.equals(otherExpr.mediaType) && this.negated == otherExpr.negated); - } + @Override + public int hashCode() { + return this.mediaType.hashCode(); + } - @Override - public int hashCode() { - return this.mediaType.hashCode(); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - if (this.negated) { - builder.append('!'); - } - builder.append(this.mediaType.toString()); - return builder.toString(); - } + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (this.negated) { + builder.append('!'); + } + builder.append(this.mediaType.toString()); + return builder.toString(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/AbstractNameValueExpression.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/AbstractNameValueExpression.java index 877a7aa0..fe50e187 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/AbstractNameValueExpression.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/AbstractNameValueExpression.java @@ -16,14 +16,15 @@ */ package com.alibaba.cloud.dubbo.http.matcher; +import static org.springframework.util.StringUtils.trimWhitespace; + import org.springframework.http.HttpRequest; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import static org.springframework.util.StringUtils.trimWhitespace; - /** - * The some source code is scratched from org.springframework.web.servlet.mvc.condition.AbstractNameValueExpression + * The some source code is scratched from + * org.springframework.web.servlet.mvc.condition.AbstractNameValueExpression * * @author Rossen Stoyanchev * @author Arjen Poutsma @@ -31,116 +32,123 @@ import static org.springframework.util.StringUtils.trimWhitespace; */ abstract class AbstractNameValueExpression implements NameValueExpression { - protected final String name; + protected final String name; - protected final T value; + protected final T value; - protected final boolean negated; + protected final boolean negated; - AbstractNameValueExpression(String expression) { - int separator = expression.indexOf('='); - if (separator == -1) { - this.negated = expression.startsWith("!"); - this.name = trimWhitespace((this.negated ? expression.substring(1) : expression)); - this.value = null; - } else { - this.negated = (separator > 0) && (expression.charAt(separator - 1) == '!'); - this.name = trimWhitespace((this.negated ? expression.substring(0, separator - 1) - : expression.substring(0, separator))); - String valueExpression = getValueExpression(expression, separator); - this.value = isExcludedValue(valueExpression) ? null : parseValue(valueExpression); - } - } + AbstractNameValueExpression(String expression) { + int separator = expression.indexOf('='); + if (separator == -1) { + this.negated = expression.startsWith("!"); + this.name = trimWhitespace( + (this.negated ? expression.substring(1) : expression)); + this.value = null; + } + else { + this.negated = (separator > 0) && (expression.charAt(separator - 1) == '!'); + this.name = trimWhitespace( + (this.negated ? expression.substring(0, separator - 1) + : expression.substring(0, separator))); + String valueExpression = getValueExpression(expression, separator); + this.value = isExcludedValue(valueExpression) ? null + : parseValue(valueExpression); + } + } - private String getValueExpression(String expression, int separator) { - return trimWhitespace(expression.substring(separator + 1)); - } + private String getValueExpression(String expression, int separator) { + return trimWhitespace(expression.substring(separator + 1)); + } - /** - * Exclude the pattern value Expression: "{value}", subclass could override this method. - * - * @param valueExpression - * @return - */ - protected boolean isExcludedValue(String valueExpression) { - return StringUtils.hasText(valueExpression) && - valueExpression.startsWith("{") - && valueExpression.endsWith("}"); - } + /** + * Exclude the pattern value Expression: "{value}", subclass could override this + * method. + * + * @param valueExpression + * @return + */ + protected boolean isExcludedValue(String valueExpression) { + return StringUtils.hasText(valueExpression) && valueExpression.startsWith("{") + && valueExpression.endsWith("}"); + } - @Override - public String getName() { - return this.name; - } + @Override + public String getName() { + return this.name; + } - @Override - public T getValue() { - return this.value; - } + @Override + public T getValue() { + return this.value; + } - @Override - public boolean isNegated() { - return this.negated; - } + @Override + public boolean isNegated() { + return this.negated; + } - public final boolean match(HttpRequest request) { - boolean isMatch; - if (this.value != null) { - isMatch = matchValue(request); - } else { - isMatch = matchName(request); - } - return (this.negated ? !isMatch : isMatch); - } + public final boolean match(HttpRequest request) { + boolean isMatch; + if (this.value != null) { + isMatch = matchValue(request); + } + else { + isMatch = matchName(request); + } + return (this.negated ? !isMatch : isMatch); + } + protected abstract boolean isCaseSensitiveName(); - protected abstract boolean isCaseSensitiveName(); + protected abstract T parseValue(String valueExpression); - protected abstract T parseValue(String valueExpression); + protected abstract boolean matchName(HttpRequest request); - protected abstract boolean matchName(HttpRequest request); + protected abstract boolean matchValue(HttpRequest request); - protected abstract boolean matchValue(HttpRequest request); + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + AbstractNameValueExpression that = (AbstractNameValueExpression) other; + return ((isCaseSensitiveName() ? this.name.equals(that.name) + : this.name.equalsIgnoreCase(that.name)) + && ObjectUtils.nullSafeEquals(this.value, that.value) + && this.negated == that.negated); + } + @Override + public int hashCode() { + int result = (isCaseSensitiveName() ? this.name.hashCode() + : this.name.toLowerCase().hashCode()); + result = 31 * result + (this.value != null ? this.value.hashCode() : 0); + result = 31 * result + (this.negated ? 1 : 0); + return result; + } - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; - } - AbstractNameValueExpression that = (AbstractNameValueExpression) other; - return ((isCaseSensitiveName() ? this.name.equals(that.name) : this.name.equalsIgnoreCase(that.name)) && - ObjectUtils.nullSafeEquals(this.value, that.value) && this.negated == that.negated); - } - - @Override - public int hashCode() { - int result = (isCaseSensitiveName() ? this.name.hashCode() : this.name.toLowerCase().hashCode()); - result = 31 * result + (this.value != null ? this.value.hashCode() : 0); - result = 31 * result + (this.negated ? 1 : 0); - return result; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - if (this.value != null) { - builder.append(this.name); - if (this.negated) { - builder.append('!'); - } - builder.append('='); - builder.append(this.value); - } else { - if (this.negated) { - builder.append('!'); - } - builder.append(this.name); - } - return builder.toString(); - } + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (this.value != null) { + builder.append(this.name); + if (this.negated) { + builder.append('!'); + } + builder.append('='); + builder.append(this.value); + } + else { + if (this.negated) { + builder.append('!'); + } + builder.append(this.name); + } + return builder.toString(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/CompositeHttpRequestMatcher.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/CompositeHttpRequestMatcher.java index 255a8829..2e634732 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/CompositeHttpRequestMatcher.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/CompositeHttpRequestMatcher.java @@ -16,13 +16,13 @@ */ package com.alibaba.cloud.dubbo.http.matcher; -import org.springframework.http.HttpRequest; - import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; +import org.springframework.http.HttpRequest; + /** * Composite {@link HttpRequestMatcher} implementation * @@ -30,44 +30,44 @@ import java.util.List; */ public abstract class CompositeHttpRequestMatcher extends AbstractHttpRequestMatcher { - private final List matchers = new LinkedList<>(); + private final List matchers = new LinkedList<>(); - public CompositeHttpRequestMatcher(HttpRequestMatcher... matchers) { - this.matchers.addAll(Arrays.asList(matchers)); - } + public CompositeHttpRequestMatcher(HttpRequestMatcher... matchers) { + this.matchers.addAll(Arrays.asList(matchers)); + } - public CompositeHttpRequestMatcher and(HttpRequestMatcher matcher) { - this.matchers.add(matcher); - return this; - } + public CompositeHttpRequestMatcher and(HttpRequestMatcher matcher) { + this.matchers.add(matcher); + return this; + } - @Override - public boolean match(HttpRequest request) { - for (HttpRequestMatcher matcher : matchers) { - if (!matcher.match(request)) { - return false; - } - } - return true; - } + @Override + public boolean match(HttpRequest request) { + for (HttpRequestMatcher matcher : matchers) { + if (!matcher.match(request)) { + return false; + } + } + return true; + } - protected List getMatchers() { - return this.matchers; - } + protected List getMatchers() { + return this.matchers; + } - @Override - protected Collection getContent() { - List content = new LinkedList<>(); - for (HttpRequestMatcher matcher : getMatchers()) { - if (matcher instanceof AbstractHttpRequestMatcher) { - content.addAll(((AbstractHttpRequestMatcher) matcher).getContent()); - } - } - return content; - } + @Override + protected Collection getContent() { + List content = new LinkedList<>(); + for (HttpRequestMatcher matcher : getMatchers()) { + if (matcher instanceof AbstractHttpRequestMatcher) { + content.addAll(((AbstractHttpRequestMatcher) matcher).getContent()); + } + } + return content; + } - @Override - protected String getToStringInfix() { - return " && "; - } + @Override + protected String getToStringInfix() { + return " && "; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ConsumeMediaTypeExpression.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ConsumeMediaTypeExpression.java index 33f39a2a..e34c394a 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ConsumeMediaTypeExpression.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ConsumeMediaTypeExpression.java @@ -29,16 +29,16 @@ import org.springframework.http.MediaType; */ class ConsumeMediaTypeExpression extends AbstractMediaTypeExpression { - ConsumeMediaTypeExpression(String expression) { - super(expression); - } + ConsumeMediaTypeExpression(String expression) { + super(expression); + } - ConsumeMediaTypeExpression(MediaType mediaType, boolean negated) { - super(mediaType, negated); - } + ConsumeMediaTypeExpression(MediaType mediaType, boolean negated) { + super(mediaType, negated); + } - public final boolean match(MediaType contentType) { - boolean match = getMediaType().includes(contentType); - return (!isNegated() ? match : !match); - } + public final boolean match(MediaType contentType) { + boolean match = getMediaType().includes(contentType); + return (!isNegated() ? match : !match); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HeaderExpression.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HeaderExpression.java index 12a85bc0..1f2e80cf 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HeaderExpression.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HeaderExpression.java @@ -23,7 +23,8 @@ import org.springframework.util.ObjectUtils; /** * Parses and matches a single header expression to a request. *

- * The some source code is scratched from org.springframework.web.servlet.mvc.condition.HeadersRequestCondition.HeaderExpression + * The some source code is scratched from + * org.springframework.web.servlet.mvc.condition.HeadersRequestCondition.HeaderExpression * * @author Arjen Poutsma * @author Rossen Stoyanchev @@ -31,30 +32,30 @@ import org.springframework.util.ObjectUtils; */ class HeaderExpression extends AbstractNameValueExpression { - HeaderExpression(String expression) { - super(expression); - } + HeaderExpression(String expression) { + super(expression); + } - @Override - protected boolean isCaseSensitiveName() { - return false; - } + @Override + protected boolean isCaseSensitiveName() { + return false; + } - @Override - protected String parseValue(String valueExpression) { - return valueExpression; - } + @Override + protected String parseValue(String valueExpression) { + return valueExpression; + } - @Override - protected boolean matchName(HttpRequest request) { - HttpHeaders httpHeaders = request.getHeaders(); - return httpHeaders.containsKey(this.name); - } + @Override + protected boolean matchName(HttpRequest request) { + HttpHeaders httpHeaders = request.getHeaders(); + return httpHeaders.containsKey(this.name); + } - @Override - protected boolean matchValue(HttpRequest request) { - HttpHeaders httpHeaders = request.getHeaders(); - String headerValue = httpHeaders.getFirst(this.name); - return ObjectUtils.nullSafeEquals(this.value, headerValue); - } + @Override + protected boolean matchValue(HttpRequest request) { + HttpHeaders httpHeaders = request.getHeaders(); + String headerValue = httpHeaders.getFirst(this.name); + return ObjectUtils.nullSafeEquals(this.value, headerValue); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestConsumersMatcher.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestConsumersMatcher.java index 7117c891..aa160e29 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestConsumersMatcher.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestConsumersMatcher.java @@ -16,10 +16,6 @@ */ package com.alibaba.cloud.dubbo.http.matcher; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpRequest; -import org.springframework.http.MediaType; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -27,6 +23,10 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpRequest; +import org.springframework.http.MediaType; + /** * {@link HttpRequest} 'Content-Type' header {@link HttpRequestMatcher matcher} * @@ -36,88 +36,91 @@ import java.util.Set; */ public class HttpRequestConsumersMatcher extends AbstractHttpRequestMatcher { - private final List expressions; + private final List expressions; - /** - * Creates a new instance from 0 or more "consumes" expressions. - * - * @param consumes consumes expressions if 0 expressions are provided, - * the condition will match to every request - */ - public HttpRequestConsumersMatcher(String... consumes) { - this(consumes, null); - } + /** + * Creates a new instance from 0 or more "consumes" expressions. + * + * @param consumes consumes expressions if 0 expressions are provided, the condition + * will match to every request + */ + public HttpRequestConsumersMatcher(String... consumes) { + this(consumes, null); + } - /** - * Creates a new instance with "consumes" and "header" expressions. - * "Header" expressions where the header name is not 'Content-Type' or have - * no header value defined are ignored. If 0 expressions are provided in - * total, the condition will match to every request - * - * @param consumes consumes expressions - * @param headers headers expressions - */ - public HttpRequestConsumersMatcher(String[] consumes, String[] headers) { - this(parseExpressions(consumes, headers)); - } + /** + * Creates a new instance with "consumes" and "header" expressions. "Header" + * expressions where the header name is not 'Content-Type' or have no header value + * defined are ignored. If 0 expressions are provided in total, the condition will + * match to every request + * + * @param consumes consumes expressions + * @param headers headers expressions + */ + public HttpRequestConsumersMatcher(String[] consumes, String[] headers) { + this(parseExpressions(consumes, headers)); + } - /** - * Private constructor accepting parsed media type expressions. - */ - private HttpRequestConsumersMatcher(Collection expressions) { - this.expressions = new ArrayList<>(expressions); - Collections.sort(this.expressions); - } + /** + * Private constructor accepting parsed media type expressions. + */ + private HttpRequestConsumersMatcher( + Collection expressions) { + this.expressions = new ArrayList<>(expressions); + Collections.sort(this.expressions); + } - @Override - public boolean match(HttpRequest request) { + private static Set parseExpressions(String[] consumes, + String[] headers) { + Set result = new LinkedHashSet<>(); + if (headers != null) { + for (String header : headers) { + HeaderExpression expr = new HeaderExpression(header); + if ("Content-Type".equalsIgnoreCase(expr.name) && expr.value != null) { + for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) { + result.add( + new ConsumeMediaTypeExpression(mediaType, expr.negated)); + } + } + } + } + for (String consume : consumes) { + result.add(new ConsumeMediaTypeExpression(consume)); + } + return result; + } - if (expressions.isEmpty()) { - return true; - } + @Override + public boolean match(HttpRequest request) { - HttpHeaders httpHeaders = request.getHeaders(); + if (expressions.isEmpty()) { + return true; + } - MediaType contentType = httpHeaders.getContentType(); + HttpHeaders httpHeaders = request.getHeaders(); - if (contentType == null) { - contentType = MediaType.APPLICATION_OCTET_STREAM; - } + MediaType contentType = httpHeaders.getContentType(); - for (ConsumeMediaTypeExpression expression : expressions) { - if (!expression.match(contentType)) { - return false; - } - } + if (contentType == null) { + contentType = MediaType.APPLICATION_OCTET_STREAM; + } - return true; - } + for (ConsumeMediaTypeExpression expression : expressions) { + if (!expression.match(contentType)) { + return false; + } + } - private static Set parseExpressions(String[] consumes, String[] headers) { - Set result = new LinkedHashSet<>(); - if (headers != null) { - for (String header : headers) { - HeaderExpression expr = new HeaderExpression(header); - if ("Content-Type".equalsIgnoreCase(expr.name) && expr.value != null) { - for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) { - result.add(new ConsumeMediaTypeExpression(mediaType, expr.negated)); - } - } - } - } - for (String consume : consumes) { - result.add(new ConsumeMediaTypeExpression(consume)); - } - return result; - } + return true; + } - @Override - protected Collection getContent() { - return this.expressions; - } + @Override + protected Collection getContent() { + return this.expressions; + } - @Override - protected String getToStringInfix() { - return " || "; - } + @Override + protected String getToStringInfix() { + return " || "; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestHeadersMatcher.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestHeadersMatcher.java index b83a02b0..8ad427e4 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestHeadersMatcher.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestHeadersMatcher.java @@ -16,13 +16,13 @@ */ package com.alibaba.cloud.dubbo.http.matcher; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpRequest; - import java.util.Collection; import java.util.LinkedHashSet; import java.util.Set; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpRequest; + /** * {@link HttpRequest} headers {@link HttpRequestMatcher matcher} * @@ -30,42 +30,42 @@ import java.util.Set; */ public class HttpRequestHeadersMatcher extends AbstractHttpRequestMatcher { - private final Set expressions; + private final Set expressions; - public HttpRequestHeadersMatcher(String... headers) { - this.expressions = parseExpressions(headers); - } + public HttpRequestHeadersMatcher(String... headers) { + this.expressions = parseExpressions(headers); + } - private static Set parseExpressions(String... headers) { - Set expressions = new LinkedHashSet<>(); - for (String header : headers) { - HeaderExpression expr = new HeaderExpression(header); - if (HttpHeaders.ACCEPT.equalsIgnoreCase(expr.name) || - HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(expr.name)) { - continue; - } - expressions.add(expr); - } - return expressions; - } + private static Set parseExpressions(String... headers) { + Set expressions = new LinkedHashSet<>(); + for (String header : headers) { + HeaderExpression expr = new HeaderExpression(header); + if (HttpHeaders.ACCEPT.equalsIgnoreCase(expr.name) + || HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(expr.name)) { + continue; + } + expressions.add(expr); + } + return expressions; + } - @Override - public boolean match(HttpRequest request) { - for (HeaderExpression expression : this.expressions) { - if (!expression.match(request)) { - return false; - } - } - return true; - } + @Override + public boolean match(HttpRequest request) { + for (HeaderExpression expression : this.expressions) { + if (!expression.match(request)) { + return false; + } + } + return true; + } - @Override - protected Collection getContent() { - return this.expressions; - } + @Override + protected Collection getContent() { + return this.expressions; + } - @Override - protected String getToStringInfix() { - return " && "; - } + @Override + protected String getToStringInfix() { + return " && "; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestMatcher.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestMatcher.java index 85606f2a..d53536b9 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestMatcher.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestMatcher.java @@ -25,11 +25,11 @@ import org.springframework.http.HttpRequest; */ public interface HttpRequestMatcher { - /** - * Match {@link HttpRequest} or not - * - * @param request The {@link HttpRequest} instance - * @return if matched, return true, or false. - */ - boolean match(HttpRequest request); + /** + * Match {@link HttpRequest} or not + * + * @param request The {@link HttpRequest} instance + * @return if matched, return true, or false. + */ + boolean match(HttpRequest request); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestMethodsMatcher.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestMethodsMatcher.java index 88a32394..d520f792 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestMethodsMatcher.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestMethodsMatcher.java @@ -16,15 +16,15 @@ */ package com.alibaba.cloud.dubbo.http.matcher; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpRequest; -import org.springframework.util.StringUtils; +import static org.springframework.http.HttpMethod.resolve; import java.util.Collection; import java.util.LinkedHashSet; import java.util.Set; -import static org.springframework.http.HttpMethod.resolve; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpRequest; +import org.springframework.util.StringUtils; /** * {@link HttpRequest} {@link HttpMethod methods} {@link HttpRequestMatcher matcher} @@ -33,50 +33,50 @@ import static org.springframework.http.HttpMethod.resolve; */ public class HttpRequestMethodsMatcher extends AbstractHttpRequestMatcher { - private final Set methods; + private final Set methods; - public HttpRequestMethodsMatcher(String... methods) { - this.methods = resolveHttpMethods(methods); - } + public HttpRequestMethodsMatcher(String... methods) { + this.methods = resolveHttpMethods(methods); + } - private Set resolveHttpMethods(String[] methods) { - Set httpMethods = new LinkedHashSet<>(methods.length); - for (String method : methods) { - if (!StringUtils.hasText(method)) { - continue; - } - HttpMethod httpMethod = resolve(method); - httpMethods.add(httpMethod); - } - return httpMethods; - } + private Set resolveHttpMethods(String[] methods) { + Set httpMethods = new LinkedHashSet<>(methods.length); + for (String method : methods) { + if (!StringUtils.hasText(method)) { + continue; + } + HttpMethod httpMethod = resolve(method); + httpMethods.add(httpMethod); + } + return httpMethods; + } - public Set getMethods() { - return methods; - } + public Set getMethods() { + return methods; + } - @Override - public boolean match(HttpRequest request) { - boolean matched = false; - HttpMethod httpMethod = request.getMethod(); - if (httpMethod != null) { - for (HttpMethod method : getMethods()) { - if (httpMethod.equals(method)) { - matched = true; - break; - } - } - } - return matched; - } + @Override + public boolean match(HttpRequest request) { + boolean matched = false; + HttpMethod httpMethod = request.getMethod(); + if (httpMethod != null) { + for (HttpMethod method : getMethods()) { + if (httpMethod.equals(method)) { + matched = true; + break; + } + } + } + return matched; + } - @Override - protected Collection getContent() { - return methods; - } + @Override + protected Collection getContent() { + return methods; + } - @Override - protected String getToStringInfix() { - return " || "; - } + @Override + protected String getToStringInfix() { + return " || "; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestParamsMatcher.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestParamsMatcher.java index 83fb599f..01cafdfd 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestParamsMatcher.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestParamsMatcher.java @@ -16,13 +16,13 @@ */ package com.alibaba.cloud.dubbo.http.matcher; -import org.springframework.http.HttpRequest; -import org.springframework.util.CollectionUtils; - import java.util.Collection; import java.util.LinkedHashSet; import java.util.Set; +import org.springframework.http.HttpRequest; +import org.springframework.util.CollectionUtils; + /** * {@link HttpRequest} parameters {@link HttpRequestMatcher matcher} * @@ -30,47 +30,47 @@ import java.util.Set; */ public class HttpRequestParamsMatcher extends AbstractHttpRequestMatcher { - private final Set expressions; + private final Set expressions; - /** - * @param params The pattern of params : - *

    - *
  • name=value
  • - *
  • name
  • - *
- */ - public HttpRequestParamsMatcher(String... params) { - this.expressions = parseExpressions(params); - } + /** + * @param params The pattern of params : + *
    + *
  • name=value
  • + *
  • name
  • + *
+ */ + public HttpRequestParamsMatcher(String... params) { + this.expressions = parseExpressions(params); + } - @Override - public boolean match(HttpRequest request) { - if (CollectionUtils.isEmpty(expressions)) { - return true; - } - for (ParamExpression paramExpression : expressions) { - if (paramExpression.match(request)) { - return true; - } - } - return false; - } + private static Set parseExpressions(String... params) { + Set expressions = new LinkedHashSet<>(); + for (String param : params) { + expressions.add(new ParamExpression(param)); + } + return expressions; + } - private static Set parseExpressions(String... params) { - Set expressions = new LinkedHashSet<>(); - for (String param : params) { - expressions.add(new ParamExpression(param)); - } - return expressions; - } + @Override + public boolean match(HttpRequest request) { + if (CollectionUtils.isEmpty(expressions)) { + return true; + } + for (ParamExpression paramExpression : expressions) { + if (paramExpression.match(request)) { + return true; + } + } + return false; + } - @Override - protected Collection getContent() { - return this.expressions; - } + @Override + protected Collection getContent() { + return this.expressions; + } - @Override - protected String getToStringInfix() { - return " && "; - } + @Override + protected String getToStringInfix() { + return " && "; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestPathMatcher.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestPathMatcher.java index 07108e9f..1d2a2264 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestPathMatcher.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestPathMatcher.java @@ -16,11 +16,6 @@ */ package com.alibaba.cloud.dubbo.http.matcher; -import org.springframework.http.HttpRequest; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.PathMatcher; -import org.springframework.util.StringUtils; - import java.net.URI; import java.util.ArrayList; import java.util.Collection; @@ -29,6 +24,11 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import org.springframework.http.HttpRequest; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import org.springframework.util.StringUtils; + /** * {@link HttpRequest} {@link URI} {@link HttpRequestMatcher matcher} * @@ -36,82 +36,82 @@ import java.util.Set; */ public class HttpRequestPathMatcher extends AbstractHttpRequestMatcher { - private final Set patterns; + private final Set patterns; - private final PathMatcher pathMatcher; + private final PathMatcher pathMatcher; - public HttpRequestPathMatcher(String... patterns) { - this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns)); - this.pathMatcher = new AntPathMatcher(); - } + public HttpRequestPathMatcher(String... patterns) { + this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns)); + this.pathMatcher = new AntPathMatcher(); + } - @Override - public boolean match(HttpRequest request) { - List matches = getMatchingPatterns(request); - return !matches.isEmpty(); - } + private static Set prependLeadingSlash(String[] patterns) { + Set result = new LinkedHashSet<>(patterns.length); + for (String pattern : patterns) { + if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) { + pattern = "/" + pattern; + } + result.add(pattern); + } + return result; + } - public List getMatchingPatterns(HttpRequest request) { - String path = getPath(request); - List matches = getMatchingPatterns(path); - return matches; - } + @Override + public boolean match(HttpRequest request) { + List matches = getMatchingPatterns(request); + return !matches.isEmpty(); + } - public List getMatchingPatterns(String lookupPath) { - List matches = new ArrayList<>(); - for (String pattern : this.patterns) { - String match = getMatchingPattern(pattern, lookupPath); - if (match != null) { - matches.add(match); - } - } - if (matches.size() > 1) { - matches.sort(this.pathMatcher.getPatternComparator(lookupPath)); - } - return matches; - } + public List getMatchingPatterns(HttpRequest request) { + String path = getPath(request); + List matches = getMatchingPatterns(path); + return matches; + } - private String getMatchingPattern(String pattern, String lookupPath) { - if (pattern.equals(lookupPath)) { - return pattern; - } - boolean hasSuffix = pattern.indexOf('.') != -1; - if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) { - return pattern + ".*"; - } - if (this.pathMatcher.match(pattern, lookupPath)) { - return pattern; - } + public List getMatchingPatterns(String lookupPath) { + List matches = new ArrayList<>(); + for (String pattern : this.patterns) { + String match = getMatchingPattern(pattern, lookupPath); + if (match != null) { + matches.add(match); + } + } + if (matches.size() > 1) { + matches.sort(this.pathMatcher.getPatternComparator(lookupPath)); + } + return matches; + } - if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) { - return pattern + "/"; - } - return null; - } + private String getMatchingPattern(String pattern, String lookupPath) { + if (pattern.equals(lookupPath)) { + return pattern; + } + boolean hasSuffix = pattern.indexOf('.') != -1; + if (!hasSuffix && this.pathMatcher.match(pattern + ".*", lookupPath)) { + return pattern + ".*"; + } + if (this.pathMatcher.match(pattern, lookupPath)) { + return pattern; + } - private String getPath(HttpRequest request) { - URI uri = request.getURI(); - return uri.getPath(); - } + if (!pattern.endsWith("/") && this.pathMatcher.match(pattern + "/", lookupPath)) { + return pattern + "/"; + } + return null; + } - private static Set prependLeadingSlash(String[] patterns) { - Set result = new LinkedHashSet<>(patterns.length); - for (String pattern : patterns) { - if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) { - pattern = "/" + pattern; - } - result.add(pattern); - } - return result; - } + private String getPath(HttpRequest request) { + URI uri = request.getURI(); + return uri.getPath(); + } - @Override - protected Collection getContent() { - return this.patterns; - } + @Override + protected Collection getContent() { + return this.patterns; + } - @Override - protected String getToStringInfix() { - return " || "; - } + @Override + protected String getToStringInfix() { + return " || "; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestProducesMatcher.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestProducesMatcher.java index efc7300d..ea949aed 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestProducesMatcher.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/HttpRequestProducesMatcher.java @@ -16,10 +16,6 @@ */ package com.alibaba.cloud.dubbo.http.matcher; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpRequest; -import org.springframework.http.MediaType; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -27,6 +23,10 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpRequest; +import org.springframework.http.MediaType; + /** * {@link HttpRequest} 'Accept' header {@link HttpRequestMatcher matcher} * @@ -36,84 +36,88 @@ import java.util.Set; */ public class HttpRequestProducesMatcher extends AbstractHttpRequestMatcher { - private final List expressions; + private final List expressions; - /** - * Creates a new instance from "produces" expressions. If 0 expressions - * are provided in total, this condition will match to any request. - * - * @param produces produces expressions - */ - public HttpRequestProducesMatcher(String... produces) { - this(produces, null); - } + /** + * Creates a new instance from "produces" expressions. If 0 expressions are provided + * in total, this condition will match to any request. + * + * @param produces produces expressions + */ + public HttpRequestProducesMatcher(String... produces) { + this(produces, null); + } - /** - * Creates a new instance with "produces" and "header" expressions. "Header" - * expressions where the header name is not 'Accept' or have no header value - * defined are ignored. If 0 expressions are provided in total, this condition - * will match to any request. - * - * @param produces produces expressions - * @param headers headers expressions - */ - public HttpRequestProducesMatcher(String[] produces, String[] headers) { - this(parseExpressions(produces, headers)); - } + /** + * Creates a new instance with "produces" and "header" expressions. "Header" + * expressions where the header name is not 'Accept' or have no header value defined + * are ignored. If 0 expressions are provided in total, this condition will match to + * any request. + * + * @param produces produces expressions + * @param headers headers expressions + */ + public HttpRequestProducesMatcher(String[] produces, String[] headers) { + this(parseExpressions(produces, headers)); + } - /** - * Private constructor accepting parsed media type expressions. - */ - private HttpRequestProducesMatcher(Collection expressions) { - this.expressions = new ArrayList<>(expressions); - Collections.sort(this.expressions); - } + /** + * Private constructor accepting parsed media type expressions. + */ + private HttpRequestProducesMatcher( + Collection expressions) { + this.expressions = new ArrayList<>(expressions); + Collections.sort(this.expressions); + } - @Override - public boolean match(HttpRequest request) { + private static Set parseExpressions(String[] produces, + String[] headers) { + Set result = new LinkedHashSet<>(); + if (headers != null) { + for (String header : headers) { + HeaderExpression expr = new HeaderExpression(header); + if (HttpHeaders.ACCEPT.equalsIgnoreCase(expr.name) + && expr.value != null) { + for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) { + result.add( + new ProduceMediaTypeExpression(mediaType, expr.negated)); + } + } + } + } + for (String produce : produces) { + result.add(new ProduceMediaTypeExpression(produce)); + } + return result; + } - if (expressions.isEmpty()) { - return true; - } + @Override + public boolean match(HttpRequest request) { - HttpHeaders httpHeaders = request.getHeaders(); + if (expressions.isEmpty()) { + return true; + } - List acceptedMediaTypes = httpHeaders.getAccept(); + HttpHeaders httpHeaders = request.getHeaders(); - for (ProduceMediaTypeExpression expression : expressions) { - if (!expression.match(acceptedMediaTypes)) { - return false; - } - } + List acceptedMediaTypes = httpHeaders.getAccept(); - return true; - } + for (ProduceMediaTypeExpression expression : expressions) { + if (!expression.match(acceptedMediaTypes)) { + return false; + } + } - private static Set parseExpressions(String[] produces, String[] headers) { - Set result = new LinkedHashSet<>(); - if (headers != null) { - for (String header : headers) { - HeaderExpression expr = new HeaderExpression(header); - if (HttpHeaders.ACCEPT.equalsIgnoreCase(expr.name) && expr.value != null) { - for (MediaType mediaType : MediaType.parseMediaTypes(expr.value)) { - result.add(new ProduceMediaTypeExpression(mediaType, expr.negated)); - } - } - } - } - for (String produce : produces) { - result.add(new ProduceMediaTypeExpression(produce)); - } - return result; - } + return true; + } - @Override - protected Collection getContent() { - return expressions; - } + @Override + protected Collection getContent() { + return expressions; + } - @Override - protected String getToStringInfix() { - return " || "; - } + @Override + protected String getToStringInfix() { + return " || "; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/MediaTypeExpression.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/MediaTypeExpression.java index 61244f0a..5f33de34 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/MediaTypeExpression.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/MediaTypeExpression.java @@ -19,17 +19,18 @@ package com.alibaba.cloud.dubbo.http.matcher; import org.springframework.http.MediaType; /** - * A contract for media type expressions (e.g. "text/plain", "!text/plain") as - * defined in the for "consumes" and "produces". + * A contract for media type expressions (e.g. "text/plain", "!text/plain") as defined in + * the for "consumes" and "produces". *

- * The source code is scratched from org.springframework.web.servlet.mvc.condition.MediaTypeExpression + * The source code is scratched from + * org.springframework.web.servlet.mvc.condition.MediaTypeExpression * * @author Rossen Stoyanchev */ interface MediaTypeExpression { - MediaType getMediaType(); + MediaType getMediaType(); - boolean isNegated(); + boolean isNegated(); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/NameValueExpression.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/NameValueExpression.java index 1da0b2fa..bbf3a96a 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/NameValueExpression.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/NameValueExpression.java @@ -16,12 +16,12 @@ */ package com.alibaba.cloud.dubbo.http.matcher; - /** * A contract for {@code "name!=value"} style expression used to specify request * parameters and request header in HTTP request *

- * The some source code is scratched from org.springframework.web.servlet.mvc.condition.NameValueExpression + * The some source code is scratched from + * org.springframework.web.servlet.mvc.condition.NameValueExpression * * @param the value type * @author Rossen Stoyanchev @@ -29,10 +29,10 @@ package com.alibaba.cloud.dubbo.http.matcher; */ interface NameValueExpression { - String getName(); + String getName(); - T getValue(); + T getValue(); - boolean isNegated(); + boolean isNegated(); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ParamExpression.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ParamExpression.java index 8d14cd7b..555d1d57 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ParamExpression.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ParamExpression.java @@ -16,16 +16,17 @@ */ package com.alibaba.cloud.dubbo.http.matcher; +import static com.alibaba.cloud.dubbo.http.util.HttpUtils.getParameters; + import org.springframework.http.HttpRequest; import org.springframework.util.MultiValueMap; import org.springframework.util.ObjectUtils; -import static com.alibaba.cloud.dubbo.http.util.HttpUtils.getParameters; - /** * Parses and matches a single param expression to a request. *

- * The some source code is scratched from org.springframework.web.servlet.mvc.condition.ParamsRequestCondition.ParamExpression + * The some source code is scratched from + * org.springframework.web.servlet.mvc.condition.ParamsRequestCondition.ParamExpression * * @author Arjen Poutsma * @author Rossen Stoyanchev @@ -33,30 +34,30 @@ import static com.alibaba.cloud.dubbo.http.util.HttpUtils.getParameters; */ class ParamExpression extends AbstractNameValueExpression { - ParamExpression(String expression) { - super(expression); - } + ParamExpression(String expression) { + super(expression); + } - @Override - protected boolean isCaseSensitiveName() { - return true; - } + @Override + protected boolean isCaseSensitiveName() { + return true; + } - @Override - protected String parseValue(String valueExpression) { - return valueExpression; - } + @Override + protected String parseValue(String valueExpression) { + return valueExpression; + } - @Override - protected boolean matchName(HttpRequest request) { - MultiValueMap parametersMap = getParameters(request); - return parametersMap.containsKey(this.name); - } + @Override + protected boolean matchName(HttpRequest request) { + MultiValueMap parametersMap = getParameters(request); + return parametersMap.containsKey(this.name); + } - @Override - protected boolean matchValue(HttpRequest request) { - MultiValueMap parametersMap = getParameters(request); - String parameterValue = parametersMap.getFirst(this.name); - return ObjectUtils.nullSafeEquals(this.value, parameterValue); - } + @Override + protected boolean matchValue(HttpRequest request) { + MultiValueMap parametersMap = getParameters(request); + String parameterValue = parametersMap.getFirst(this.name); + return ObjectUtils.nullSafeEquals(this.value, parameterValue); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ProduceMediaTypeExpression.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ProduceMediaTypeExpression.java index d9830695..7b9cfe3f 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ProduceMediaTypeExpression.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/ProduceMediaTypeExpression.java @@ -16,10 +16,10 @@ */ package com.alibaba.cloud.dubbo.http.matcher; -import org.springframework.http.MediaType; - import java.util.List; +import org.springframework.http.MediaType; + /** * Parses and matches a single media type expression to a request's 'Accept' header. *

@@ -31,25 +31,25 @@ import java.util.List; */ class ProduceMediaTypeExpression extends AbstractMediaTypeExpression { - ProduceMediaTypeExpression(String expression) { - super(expression); - } + ProduceMediaTypeExpression(String expression) { + super(expression); + } - ProduceMediaTypeExpression(MediaType mediaType, boolean negated) { - super(mediaType, negated); - } + ProduceMediaTypeExpression(MediaType mediaType, boolean negated) { + super(mediaType, negated); + } - public final boolean match(List acceptedMediaTypes) { - boolean match = matchMediaType(acceptedMediaTypes); - return (!isNegated() ? match : !match); - } + public final boolean match(List acceptedMediaTypes) { + boolean match = matchMediaType(acceptedMediaTypes); + return (!isNegated() ? match : !match); + } - private boolean matchMediaType(List acceptedMediaTypes) { - for (MediaType acceptedMediaType : acceptedMediaTypes) { - if (getMediaType().isCompatibleWith(acceptedMediaType)) { - return true; - } - } - return false; - } + private boolean matchMediaType(List acceptedMediaTypes) { + for (MediaType acceptedMediaType : acceptedMediaTypes) { + if (getMediaType().isCompatibleWith(acceptedMediaType)) { + return true; + } + } + return false; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/RequestMetadataMatcher.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/RequestMetadataMatcher.java index 1f1b5b8e..0385bd34 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/RequestMetadataMatcher.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/matcher/RequestMetadataMatcher.java @@ -16,10 +16,10 @@ */ package com.alibaba.cloud.dubbo.http.matcher; -import com.alibaba.cloud.dubbo.metadata.RequestMetadata; - import static com.alibaba.cloud.dubbo.http.util.HttpUtils.toNameAndValues; +import com.alibaba.cloud.dubbo.metadata.RequestMetadata; + /** * {@link RequestMetadata} {@link HttpRequestMatcher} implementation * @@ -27,20 +27,21 @@ import static com.alibaba.cloud.dubbo.http.util.HttpUtils.toNameAndValues; */ public class RequestMetadataMatcher extends CompositeHttpRequestMatcher { - public RequestMetadataMatcher(RequestMetadata metadata) { - super( - // method - new HttpRequestMethodsMatcher(metadata.getMethod()), - // url - new HttpRequestPathMatcher(metadata.getPath()), - // params - new HttpRequestParamsMatcher(toNameAndValues(metadata.getParams())), - // headers - new HttpRequestHeadersMatcher(toNameAndValues(metadata.getHeaders())), - // consumes - new HttpRequestConsumersMatcher(metadata.getConsumes().toArray(new String[0])), - // produces - new HttpRequestProducesMatcher(metadata.getProduces().toArray(new String[0])) - ); - } + public RequestMetadataMatcher(RequestMetadata metadata) { + super( + // method + new HttpRequestMethodsMatcher(metadata.getMethod()), + // url + new HttpRequestPathMatcher(metadata.getPath()), + // params + new HttpRequestParamsMatcher(toNameAndValues(metadata.getParams())), + // headers + new HttpRequestHeadersMatcher(toNameAndValues(metadata.getHeaders())), + // consumes + new HttpRequestConsumersMatcher( + metadata.getConsumes().toArray(new String[0])), + // produces + new HttpRequestProducesMatcher( + metadata.getProduces().toArray(new String[0]))); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/util/HttpMessageConverterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/util/HttpMessageConverterResolver.java index a5488f62..e27b0df6 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/util/HttpMessageConverterResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/util/HttpMessageConverterResolver.java @@ -16,9 +16,14 @@ */ package com.alibaba.cloud.dubbo.http.util; -import com.alibaba.cloud.dubbo.http.converter.HttpMessageConverterHolder; -import com.alibaba.cloud.dubbo.metadata.RequestMetadata; -import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; +import static java.util.Collections.unmodifiableList; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + import org.springframework.core.MethodParameter; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; @@ -30,13 +35,9 @@ import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import static java.util.Collections.unmodifiableList; +import com.alibaba.cloud.dubbo.http.converter.HttpMessageConverterHolder; +import com.alibaba.cloud.dubbo.metadata.RequestMetadata; +import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; /** * {@link HttpMessageConverter} Resolver @@ -45,184 +46,202 @@ import static java.util.Collections.unmodifiableList; */ public class HttpMessageConverterResolver { - private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application"); + private static final MediaType MEDIA_TYPE_APPLICATION = new MediaType("application"); - private final List> messageConverters; + private final List> messageConverters; - private final List allSupportedMediaTypes; + private final List allSupportedMediaTypes; - private final ClassLoader classLoader; + private final ClassLoader classLoader; - public HttpMessageConverterResolver(List> messageConverters, ClassLoader classLoader) { - this.messageConverters = messageConverters; - this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters); - this.classLoader = classLoader; - } + public HttpMessageConverterResolver(List> messageConverters, + ClassLoader classLoader) { + this.messageConverters = messageConverters; + this.allSupportedMediaTypes = getAllSupportedMediaTypes(messageConverters); + this.classLoader = classLoader; + } - public HttpMessageConverterHolder resolve(HttpRequest request, Class parameterType) { + public HttpMessageConverterHolder resolve(HttpRequest request, + Class parameterType) { - HttpMessageConverterHolder httpMessageConverterHolder = null; + HttpMessageConverterHolder httpMessageConverterHolder = null; - HttpHeaders httpHeaders = request.getHeaders(); + HttpHeaders httpHeaders = request.getHeaders(); - MediaType contentType = httpHeaders.getContentType(); + MediaType contentType = httpHeaders.getContentType(); - if (contentType == null) { - contentType = MediaType.APPLICATION_OCTET_STREAM; - } + if (contentType == null) { + contentType = MediaType.APPLICATION_OCTET_STREAM; + } - for (HttpMessageConverter converter : this.messageConverters) { - if (converter instanceof GenericHttpMessageConverter) { - GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter; - if (genericConverter.canRead(parameterType, parameterType, contentType)) { - httpMessageConverterHolder = new HttpMessageConverterHolder(contentType, converter); - break; - } - } else { - if (converter.canRead(parameterType, contentType)) { - httpMessageConverterHolder = new HttpMessageConverterHolder(contentType, converter); - break; - } - } + for (HttpMessageConverter converter : this.messageConverters) { + if (converter instanceof GenericHttpMessageConverter) { + GenericHttpMessageConverter genericConverter = (GenericHttpMessageConverter) converter; + if (genericConverter.canRead(parameterType, parameterType, contentType)) { + httpMessageConverterHolder = new HttpMessageConverterHolder( + contentType, converter); + break; + } + } + else { + if (converter.canRead(parameterType, contentType)) { + httpMessageConverterHolder = new HttpMessageConverterHolder( + contentType, converter); + break; + } + } - } + } - return httpMessageConverterHolder; - } + return httpMessageConverterHolder; + } - /** - * Resolve the most match {@link HttpMessageConverter} from {@link RequestMetadata} - * - * @param requestMetadata {@link RequestMetadata} - * @param restMethodMetadata {@link RestMethodMetadata} - * @return - */ - public HttpMessageConverterHolder resolve(RequestMetadata requestMetadata, RestMethodMetadata - restMethodMetadata) { + /** + * Resolve the most match {@link HttpMessageConverter} from {@link RequestMetadata} + * + * @param requestMetadata {@link RequestMetadata} + * @param restMethodMetadata {@link RestMethodMetadata} + * @return + */ + public HttpMessageConverterHolder resolve(RequestMetadata requestMetadata, + RestMethodMetadata restMethodMetadata) { - HttpMessageConverterHolder httpMessageConverterHolder = null; + HttpMessageConverterHolder httpMessageConverterHolder = null; - Class returnValueClass = resolveReturnValueClass(restMethodMetadata); + Class returnValueClass = resolveReturnValueClass(restMethodMetadata); - /** - * @see AbstractMessageConverterMethodProcessor#writeWithMessageConverters(Object, MethodParameter, ServletServerHttpRequest, ServletServerHttpResponse) - */ - List requestedMediaTypes = getAcceptableMediaTypes(requestMetadata); - List producibleMediaTypes = getProducibleMediaTypes(restMethodMetadata, returnValueClass); + /** + * @see AbstractMessageConverterMethodProcessor#writeWithMessageConverters(Object, + * MethodParameter, ServletServerHttpRequest, ServletServerHttpResponse) + */ + List requestedMediaTypes = getAcceptableMediaTypes(requestMetadata); + List producibleMediaTypes = getProducibleMediaTypes(restMethodMetadata, + returnValueClass); - Set compatibleMediaTypes = new LinkedHashSet(); - for (MediaType requestedType : requestedMediaTypes) { - for (MediaType producibleType : producibleMediaTypes) { - if (requestedType.isCompatibleWith(producibleType)) { - compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType)); - } - } - } + Set compatibleMediaTypes = new LinkedHashSet(); + for (MediaType requestedType : requestedMediaTypes) { + for (MediaType producibleType : producibleMediaTypes) { + if (requestedType.isCompatibleWith(producibleType)) { + compatibleMediaTypes + .add(getMostSpecificMediaType(requestedType, producibleType)); + } + } + } - if (compatibleMediaTypes.isEmpty()) { - return httpMessageConverterHolder; - } + if (compatibleMediaTypes.isEmpty()) { + return httpMessageConverterHolder; + } - List mediaTypes = new ArrayList<>(compatibleMediaTypes); + List mediaTypes = new ArrayList<>(compatibleMediaTypes); - MediaType.sortBySpecificityAndQuality(mediaTypes); + MediaType.sortBySpecificityAndQuality(mediaTypes); - MediaType selectedMediaType = null; - for (MediaType mediaType : mediaTypes) { - if (mediaType.isConcrete()) { - selectedMediaType = mediaType; - break; - } else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) { - selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; - break; - } - } + MediaType selectedMediaType = null; + for (MediaType mediaType : mediaTypes) { + if (mediaType.isConcrete()) { + selectedMediaType = mediaType; + break; + } + else if (mediaType.equals(MediaType.ALL) + || mediaType.equals(MEDIA_TYPE_APPLICATION)) { + selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; + break; + } + } - if (selectedMediaType != null) { - selectedMediaType = selectedMediaType.removeQualityValue(); - for (HttpMessageConverter messageConverter : this.messageConverters) { - if (messageConverter.canWrite(returnValueClass, selectedMediaType)) { - httpMessageConverterHolder = new HttpMessageConverterHolder(selectedMediaType, messageConverter); - break; - } - } - } + if (selectedMediaType != null) { + selectedMediaType = selectedMediaType.removeQualityValue(); + for (HttpMessageConverter messageConverter : this.messageConverters) { + if (messageConverter.canWrite(returnValueClass, selectedMediaType)) { + httpMessageConverterHolder = new HttpMessageConverterHolder( + selectedMediaType, messageConverter); + break; + } + } + } - return httpMessageConverterHolder; - } + return httpMessageConverterHolder; + } - public List getAllSupportedMediaTypes() { - return unmodifiableList(allSupportedMediaTypes); - } + public List getAllSupportedMediaTypes() { + return unmodifiableList(allSupportedMediaTypes); + } - private Class resolveReturnValueClass(RestMethodMetadata restMethodMetadata) { - String returnClassName = restMethodMetadata.getMethod().getReturnType(); - return ClassUtils.resolveClassName(returnClassName, classLoader); - } + private Class resolveReturnValueClass(RestMethodMetadata restMethodMetadata) { + String returnClassName = restMethodMetadata.getMethod().getReturnType(); + return ClassUtils.resolveClassName(returnClassName, classLoader); + } - /** - * Resolve the {@link MediaType media-types} - * - * @param requestMetadata {@link RequestMetadata} from client side - * @return non-null {@link List} - */ - private List getAcceptableMediaTypes(RequestMetadata requestMetadata) { - return requestMetadata.getProduceMediaTypes(); - } + /** + * Resolve the {@link MediaType media-types} + * + * @param requestMetadata {@link RequestMetadata} from client side + * @return non-null {@link List} + */ + private List getAcceptableMediaTypes(RequestMetadata requestMetadata) { + return requestMetadata.getProduceMediaTypes(); + } - /** - * Returns - * the media types that can be produced:

  • The producible media types specified in the request mappings, or - *
  • Media types of configured converters that can write the specific return value, or
  • {@link MediaType#ALL} - *
- * - * @param restMethodMetadata {@link RestMethodMetadata} from server side - * @param returnValueClass the class of return value - * @return non-null {@link List} - */ - private List getProducibleMediaTypes(RestMethodMetadata restMethodMetadata, Class - returnValueClass) { - RequestMetadata serverRequestMetadata = restMethodMetadata.getRequest(); - List mediaTypes = serverRequestMetadata.getProduceMediaTypes(); - if (!CollectionUtils.isEmpty(mediaTypes)) { // Empty - return mediaTypes; - } else if (!this.allSupportedMediaTypes.isEmpty()) { - List result = new ArrayList<>(); - for (HttpMessageConverter converter : this.messageConverters) { - if (converter.canWrite(returnValueClass, null)) { - result.addAll(converter.getSupportedMediaTypes()); - } - } - return result; - } else { - return Collections.singletonList(MediaType.ALL); - } - } + /** + * Returns the media types that can be produced: + *
    + *
  • The producible media types specified in the request mappings, or + *
  • Media types of configured converters that can write the specific return value, + * or + *
  • {@link MediaType#ALL} + *
+ * + * @param restMethodMetadata {@link RestMethodMetadata} from server side + * @param returnValueClass the class of return value + * @return non-null {@link List} + */ + private List getProducibleMediaTypes(RestMethodMetadata restMethodMetadata, + Class returnValueClass) { + RequestMetadata serverRequestMetadata = restMethodMetadata.getRequest(); + List mediaTypes = serverRequestMetadata.getProduceMediaTypes(); + if (!CollectionUtils.isEmpty(mediaTypes)) { // Empty + return mediaTypes; + } + else if (!this.allSupportedMediaTypes.isEmpty()) { + List result = new ArrayList<>(); + for (HttpMessageConverter converter : this.messageConverters) { + if (converter.canWrite(returnValueClass, null)) { + result.addAll(converter.getSupportedMediaTypes()); + } + } + return result; + } + else { + return Collections.singletonList(MediaType.ALL); + } + } - /** - * Return the media types - * supported by all provided message converters sorted by specificity via {@link - * MediaType#sortBySpecificity(List)}. - * - * @param messageConverters - * @return - */ - private List getAllSupportedMediaTypes(List> messageConverters) { - Set allSupportedMediaTypes = new LinkedHashSet(); - for (HttpMessageConverter messageConverter : messageConverters) { - allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); - } - List result = new ArrayList(allSupportedMediaTypes); - MediaType.sortBySpecificity(result); - return unmodifiableList(result); - } + /** + * Return the media types supported by all provided message converters sorted by + * specificity via {@link MediaType#sortBySpecificity(List)}. + * + * @param messageConverters + * @return + */ + private List getAllSupportedMediaTypes( + List> messageConverters) { + Set allSupportedMediaTypes = new LinkedHashSet(); + for (HttpMessageConverter messageConverter : messageConverters) { + allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes()); + } + List result = new ArrayList(allSupportedMediaTypes); + MediaType.sortBySpecificity(result); + return unmodifiableList(result); + } - /** - * Return the more specific of the acceptable and the producible media types - * with the q-value of the former. - */ - private MediaType getMostSpecificMediaType(MediaType acceptType, MediaType produceType) { - MediaType produceTypeToUse = produceType.copyQualityValue(acceptType); - return (MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceTypeToUse) <= 0 ? acceptType : produceTypeToUse); - } + /** + * Return the more specific of the acceptable and the producible media types with the + * q-value of the former. + */ + private MediaType getMostSpecificMediaType(MediaType acceptType, + MediaType produceType) { + MediaType produceTypeToUse = produceType.copyQualityValue(acceptType); + return (MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, + produceTypeToUse) <= 0 ? acceptType : produceTypeToUse); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/util/HttpUtils.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/util/HttpUtils.java index 202509dc..30620cc5 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/util/HttpUtils.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/http/util/HttpUtils.java @@ -16,10 +16,9 @@ */ package com.alibaba.cloud.dubbo.http.util; -import org.springframework.http.HttpRequest; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.util.StringUtils; +import static org.springframework.util.StringUtils.delimitedListToStringArray; +import static org.springframework.util.StringUtils.hasText; +import static org.springframework.util.StringUtils.trimAllWhitespace; import java.io.UnsupportedEncodingException; import java.net.URI; @@ -31,9 +30,10 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static org.springframework.util.StringUtils.delimitedListToStringArray; -import static org.springframework.util.StringUtils.hasText; -import static org.springframework.util.StringUtils.trimAllWhitespace; +import org.springframework.http.HttpRequest; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; /** * Http Utilities class @@ -42,198 +42,206 @@ import static org.springframework.util.StringUtils.trimAllWhitespace; */ public abstract class HttpUtils { - private static final String UTF_8 = "UTF-8"; + private static final String UTF_8 = "UTF-8"; - private static final String EQUAL = "="; + private static final String EQUAL = "="; - private static final String AND = "&"; + private static final String AND = "&"; - private static final String SEMICOLON = ";"; + private static final String SEMICOLON = ";"; - private static final String QUESTION_MASK = "?"; + private static final String QUESTION_MASK = "?"; - /** - * The empty value - */ - private static final String EMPTY_VALUE = ""; + /** + * The empty value + */ + private static final String EMPTY_VALUE = ""; - /** - * Normalize path: - *
    - *
  1. To remove query string if presents
  2. - *
  3. To remove duplicated slash("/") if exists
  4. - *
- * - * @param path path to be normalized - * @return a normalized path if required - */ - public static String normalizePath(String path) { - if (!hasText(path)) { - return path; - } - String normalizedPath = path; - int index = normalizedPath.indexOf(QUESTION_MASK); - if (index > -1) { - normalizedPath = normalizedPath.substring(0, index); - } - return StringUtils.replace(normalizedPath, "//", "/"); - } + /** + * Normalize path: + *
    + *
  1. To remove query string if presents
  2. + *
  3. To remove duplicated slash("/") if exists
  4. + *
+ * + * @param path path to be normalized + * @return a normalized path if required + */ + public static String normalizePath(String path) { + if (!hasText(path)) { + return path; + } + String normalizedPath = path; + int index = normalizedPath.indexOf(QUESTION_MASK); + if (index > -1) { + normalizedPath = normalizedPath.substring(0, index); + } + return StringUtils.replace(normalizedPath, "//", "/"); + } + /** + * Get Parameters from the specified {@link HttpRequest request} + * + * @param request the specified {@link HttpRequest request} + * @return + */ + public static MultiValueMap getParameters(HttpRequest request) { + URI uri = request.getURI(); + return getParameters(uri.getQuery()); + } - /** - * Get Parameters from the specified {@link HttpRequest request} - * - * @param request the specified {@link HttpRequest request} - * @return - */ - public static MultiValueMap getParameters(HttpRequest request) { - URI uri = request.getURI(); - return getParameters(uri.getQuery()); - } + /** + * Get Parameters from the specified query string. + *

+ * + * @param queryString The query string + * @return The query parameters + */ + public static MultiValueMap getParameters(String queryString) { + return getParameters(delimitedListToStringArray(queryString, AND)); + } - /** - * Get Parameters from the specified query string. - *

- * - * @param queryString The query string - * @return The query parameters - */ - public static MultiValueMap getParameters(String queryString) { - return getParameters(delimitedListToStringArray(queryString, AND)); - } + /** + * Get Parameters from the specified pairs of name-value. + *

+ * + * @param pairs The pairs of name-value + * @return The query parameters + */ + public static MultiValueMap getParameters(Iterable pairs) { + MultiValueMap parameters = new LinkedMultiValueMap<>(); + if (pairs != null) { + for (String pair : pairs) { + String[] nameAndValue = delimitedListToStringArray(pair, EQUAL); + String name = decode(nameAndValue[0]); + String value = nameAndValue.length < 2 ? null : nameAndValue[1]; + value = decode(value); + addParam(parameters, name, value); + } + } + return parameters; + } - /** - * Get Parameters from the specified pairs of name-value. - *

- * - * @param pairs The pairs of name-value - * @return The query parameters - */ - public static MultiValueMap getParameters(Iterable pairs) { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - if (pairs != null) { - for (String pair : pairs) { - String[] nameAndValue = delimitedListToStringArray(pair, EQUAL); - String name = decode(nameAndValue[0]); - String value = nameAndValue.length < 2 ? null : nameAndValue[1]; - value = decode(value); - addParam(parameters, name, value); - } - } - return parameters; - } + /** + * Get Parameters from the specified pairs of name-value. + *

+ * + * @param pairs The pairs of name-value + * @return The query parameters + */ + public static MultiValueMap getParameters(String... pairs) { + return getParameters(Arrays.asList(pairs)); + } - /** - * Get Parameters from the specified pairs of name-value. - *

- * - * @param pairs The pairs of name-value - * @return The query parameters - */ - public static MultiValueMap getParameters(String... pairs) { - return getParameters(Arrays.asList(pairs)); - } + // /** + // * Parse a read-only {@link MultiValueMap} of {@link HttpCookie} from {@link + // HttpHeaders} + // * + // * @param httpHeaders {@link HttpHeaders} + // * @return non-null, the key is a cookie name , the value is {@link HttpCookie} + // */ + // public static MultiValueMap parseCookies(HttpHeaders + // httpHeaders) { + // + // String cookie = httpHeaders.getFirst(COOKIE); + // + // String[] cookieNameAndValues = StringUtils.delimitedListToStringArray(cookie, + // SEMICOLON); + // + // MultiValueMap cookies = new + // LinkedMultiValueMap<>(cookieNameAndValues.length); + // + // for (String cookeNameAndValue : cookieNameAndValues) { + // String[] nameAndValue = + // delimitedListToStringArray(trimWhitespace(cookeNameAndValue), EQUAL); + // String name = nameAndValue[0]; + // String value = nameAndValue.length < 2 ? null : nameAndValue[1]; + // HttpCookie httpCookie = new HttpCookie(name, value); + // cookies.add(name, httpCookie); + // } + // + // return cookies; + // } -// /** -// * Parse a read-only {@link MultiValueMap} of {@link HttpCookie} from {@link HttpHeaders} -// * -// * @param httpHeaders {@link HttpHeaders} -// * @return non-null, the key is a cookie name , the value is {@link HttpCookie} -// */ -// public static MultiValueMap parseCookies(HttpHeaders httpHeaders) { -// -// String cookie = httpHeaders.getFirst(COOKIE); -// -// String[] cookieNameAndValues = StringUtils.delimitedListToStringArray(cookie, SEMICOLON); -// -// MultiValueMap cookies = new LinkedMultiValueMap<>(cookieNameAndValues.length); -// -// for (String cookeNameAndValue : cookieNameAndValues) { -// String[] nameAndValue = delimitedListToStringArray(trimWhitespace(cookeNameAndValue), EQUAL); -// String name = nameAndValue[0]; -// String value = nameAndValue.length < 2 ? null : nameAndValue[1]; -// HttpCookie httpCookie = new HttpCookie(name, value); -// cookies.add(name, httpCookie); -// } -// -// return cookies; -// } + /** + * To the name and value line sets + * + * @param nameAndValuesMap {@link MultiValueMap} the map of name and values + * @return non-null + */ + public static Set toNameAndValuesSet( + Map> nameAndValuesMap) { + Set nameAndValues = new LinkedHashSet<>(); + for (Map.Entry> entry : nameAndValuesMap.entrySet()) { + String name = entry.getKey(); + List values = entry.getValue(); + for (String value : values) { + String nameAndValue = name + EQUAL + value; + nameAndValues.add(nameAndValue); + } + } + return nameAndValues; + } - /** - * To the name and value line sets - * - * @param nameAndValuesMap {@link MultiValueMap} the map of name and values - * @return non-null - */ - public static Set toNameAndValuesSet(Map> nameAndValuesMap) { - Set nameAndValues = new LinkedHashSet<>(); - for (Map.Entry> entry : nameAndValuesMap.entrySet()) { - String name = entry.getKey(); - List values = entry.getValue(); - for (String value : values) { - String nameAndValue = name + EQUAL + value; - nameAndValues.add(nameAndValue); - } - } - return nameAndValues; - } + public static String[] toNameAndValues(Map> nameAndValuesMap) { + return toNameAndValuesSet(nameAndValuesMap).toArray(new String[0]); + } - public static String[] toNameAndValues(Map> nameAndValuesMap) { - return toNameAndValuesSet(nameAndValuesMap).toArray(new String[0]); - } + /** + * Generate a string of query string from the specified request parameters {@link Map} + * + * @param params the specified request parameters {@link Map} + * @return non-null + */ + public static String toQueryString(Map> params) { + StringBuilder builder = new StringBuilder(); + for (String line : toNameAndValuesSet(params)) { + builder.append(line).append(AND); + } + return builder.toString(); + } - /** - * Generate a string of query string from the specified request parameters {@link Map} - * - * @param params the specified request parameters {@link Map} - * @return non-null - */ - public static String toQueryString(Map> params) { - StringBuilder builder = new StringBuilder(); - for (String line : toNameAndValuesSet(params)) { - builder.append(line).append(AND); - } - return builder.toString(); - } + /** + * Decode value + * + * @param value the value requires to decode + * @return the decoded value + */ + public static String decode(String value) { + if (value == null) { + return value; + } + String decodedValue = value; + try { + decodedValue = URLDecoder.decode(value, UTF_8); + } + catch (UnsupportedEncodingException ex) { + } + return decodedValue; + } - /** - * Decode value - * - * @param value the value requires to decode - * @return the decoded value - */ - public static String decode(String value) { - if (value == null) { - return value; - } - String decodedValue = value; - try { - decodedValue = URLDecoder.decode(value, UTF_8); - } catch (UnsupportedEncodingException ex) { - } - return decodedValue; - } + /** + * encode value + * + * @param value the value requires to encode + * @return the encoded value + */ + public static String encode(String value) { + String encodedValue = value; + try { + encodedValue = URLEncoder.encode(value, UTF_8); + } + catch (UnsupportedEncodingException ex) { + } + return encodedValue; + } - /** - * encode value - * - * @param value the value requires to encode - * @return the encoded value - */ - public static String encode(String value) { - String encodedValue = value; - try { - encodedValue = URLEncoder.encode(value, UTF_8); - } catch (UnsupportedEncodingException ex) { - } - return encodedValue; - } - - private static void addParam(MultiValueMap paramsMap, String name, String value) { - String paramValue = trimAllWhitespace(value); - if (!StringUtils.hasText(paramValue)) { - paramValue = EMPTY_VALUE; - } - paramsMap.add(trimAllWhitespace(name), paramValue); - } + private static void addParam(MultiValueMap paramsMap, String name, + String value) { + String paramValue = trimAllWhitespace(value); + if (!StringUtils.hasText(paramValue)) { + paramValue = EMPTY_VALUE; + } + paramsMap.add(trimAllWhitespace(name), paramValue); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboProtocolConfigSupplier.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboProtocolConfigSupplier.java index 8d4023ac..3b8257b1 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboProtocolConfigSupplier.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboProtocolConfigSupplier.java @@ -16,16 +16,16 @@ */ package com.alibaba.cloud.dubbo.metadata; -import org.apache.dubbo.config.ProtocolConfig; - -import org.springframework.beans.factory.ObjectProvider; +import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_PROTOCOL; +import static org.springframework.util.CollectionUtils.isEmpty; import java.util.Collection; import java.util.Iterator; import java.util.function.Supplier; -import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL; -import static org.springframework.util.CollectionUtils.isEmpty; +import org.apache.dubbo.config.ProtocolConfig; + +import org.springframework.beans.factory.ObjectProvider; /** * Dubbo's {@link ProtocolConfig} {@link Supplier} @@ -34,38 +34,40 @@ import static org.springframework.util.CollectionUtils.isEmpty; */ public class DubboProtocolConfigSupplier implements Supplier { - private final ObjectProvider> protocols; + private final ObjectProvider> protocols; - public DubboProtocolConfigSupplier(ObjectProvider> protocols) { - this.protocols = protocols; - } + public DubboProtocolConfigSupplier( + ObjectProvider> protocols) { + this.protocols = protocols; + } - @Override - public ProtocolConfig get() { - ProtocolConfig protocolConfig = null; - Collection protocols = this.protocols.getIfAvailable(); + @Override + public ProtocolConfig get() { + ProtocolConfig protocolConfig = null; + Collection protocols = this.protocols.getIfAvailable(); - if (!isEmpty(protocols)) { - for (ProtocolConfig protocol : protocols) { - String protocolName = protocol.getName(); - if (DEFAULT_PROTOCOL.equals(protocolName)) { - protocolConfig = protocol; - break; - } - } + if (!isEmpty(protocols)) { + for (ProtocolConfig protocol : protocols) { + String protocolName = protocol.getName(); + if (DEFAULT_PROTOCOL.equals(protocolName)) { + protocolConfig = protocol; + break; + } + } - if (protocolConfig == null) { // If The ProtocolConfig bean named "dubbo" is absent, take first one of them - Iterator iterator = protocols.iterator(); - protocolConfig = iterator.hasNext() ? iterator.next() : null; - } - } + if (protocolConfig == null) { // If The ProtocolConfig bean named "dubbo" is + // absent, take first one of them + Iterator iterator = protocols.iterator(); + protocolConfig = iterator.hasNext() ? iterator.next() : null; + } + } - if (protocolConfig == null) { - protocolConfig = new ProtocolConfig(); - protocolConfig.setName(DEFAULT_PROTOCOL); - protocolConfig.setPort(-1); - } + if (protocolConfig == null) { + protocolConfig = new ProtocolConfig(); + protocolConfig.setName(DEFAULT_PROTOCOL); + protocolConfig.setPort(-1); + } - return protocolConfig; - } + return protocolConfig; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboRestServiceMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboRestServiceMetadata.java index f689a499..b02094b9 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboRestServiceMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboRestServiceMetadata.java @@ -25,38 +25,39 @@ import java.util.Objects; */ public class DubboRestServiceMetadata { - private final ServiceRestMetadata serviceRestMetadata; + private final ServiceRestMetadata serviceRestMetadata; - private final RestMethodMetadata restMethodMetadata; + private final RestMethodMetadata restMethodMetadata; - public DubboRestServiceMetadata(ServiceRestMetadata serviceRestMetadata, RestMethodMetadata restMethodMetadata) { - this.serviceRestMetadata = serviceRestMetadata; - this.restMethodMetadata = restMethodMetadata; - } + public DubboRestServiceMetadata(ServiceRestMetadata serviceRestMetadata, + RestMethodMetadata restMethodMetadata) { + this.serviceRestMetadata = serviceRestMetadata; + this.restMethodMetadata = restMethodMetadata; + } - public ServiceRestMetadata getServiceRestMetadata() { - return serviceRestMetadata; - } + public ServiceRestMetadata getServiceRestMetadata() { + return serviceRestMetadata; + } - public RestMethodMetadata getRestMethodMetadata() { - return restMethodMetadata; - } + public RestMethodMetadata getRestMethodMetadata() { + return restMethodMetadata; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof DubboRestServiceMetadata)) { - return false; - } - DubboRestServiceMetadata that = (DubboRestServiceMetadata) o; - return Objects.equals(serviceRestMetadata, that.serviceRestMetadata) && - Objects.equals(restMethodMetadata, that.restMethodMetadata); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof DubboRestServiceMetadata)) { + return false; + } + DubboRestServiceMetadata that = (DubboRestServiceMetadata) o; + return Objects.equals(serviceRestMetadata, that.serviceRestMetadata) + && Objects.equals(restMethodMetadata, that.restMethodMetadata); + } - @Override - public int hashCode() { - return Objects.hash(serviceRestMetadata, restMethodMetadata); - } + @Override + public int hashCode() { + return Objects.hash(serviceRestMetadata, restMethodMetadata); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboTransportedMethodMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboTransportedMethodMetadata.java index 22fdeb5e..6ce0e24c 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboTransportedMethodMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/DubboTransportedMethodMetadata.java @@ -16,13 +16,13 @@ */ package com.alibaba.cloud.dubbo.metadata; -import com.alibaba.cloud.dubbo.annotation.DubboTransported; - import java.lang.reflect.Method; import java.util.List; import java.util.Map; import java.util.Objects; +import com.alibaba.cloud.dubbo.annotation.DubboTransported; + /** * {@link MethodMetadata} annotated {@link DubboTransported @DubboTransported} * @@ -30,66 +30,66 @@ import java.util.Objects; */ public class DubboTransportedMethodMetadata { - private final MethodMetadata methodMetadata; + private final MethodMetadata methodMetadata; - private final Map attributes; + private final Map attributes; - public DubboTransportedMethodMetadata(Method method, Map attributes) { - this.methodMetadata = new MethodMetadata(method); - this.attributes = attributes; - } + public DubboTransportedMethodMetadata(Method method, Map attributes) { + this.methodMetadata = new MethodMetadata(method); + this.attributes = attributes; + } - public String getName() { - return methodMetadata.getName(); - } + public String getName() { + return methodMetadata.getName(); + } - public void setName(String name) { - methodMetadata.setName(name); - } + public void setName(String name) { + methodMetadata.setName(name); + } - public String getReturnType() { - return methodMetadata.getReturnType(); - } + public String getReturnType() { + return methodMetadata.getReturnType(); + } - public void setReturnType(String returnType) { - methodMetadata.setReturnType(returnType); - } + public void setReturnType(String returnType) { + methodMetadata.setReturnType(returnType); + } - public List getParams() { - return methodMetadata.getParams(); - } + public List getParams() { + return methodMetadata.getParams(); + } - public void setParams(List params) { - methodMetadata.setParams(params); - } + public void setParams(List params) { + methodMetadata.setParams(params); + } - public Method getMethod() { - return methodMetadata.getMethod(); - } + public Method getMethod() { + return methodMetadata.getMethod(); + } - public MethodMetadata getMethodMetadata() { - return methodMetadata; - } + public MethodMetadata getMethodMetadata() { + return methodMetadata; + } - public Map getAttributes() { - return attributes; - } + public Map getAttributes() { + return attributes; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof DubboTransportedMethodMetadata)) { - return false; - } - DubboTransportedMethodMetadata that = (DubboTransportedMethodMetadata) o; - return Objects.equals(methodMetadata, that.methodMetadata) && - Objects.equals(attributes, that.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(methodMetadata, that.methodMetadata) + && Objects.equals(attributes, that.attributes); + } - @Override - public int hashCode() { - return Objects.hash(methodMetadata, attributes); - } + @Override + public int hashCode() { + return Objects.hash(methodMetadata, attributes); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/MethodMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/MethodMetadata.java index 34598677..5fbc3d20 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/MethodMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/MethodMetadata.java @@ -16,10 +16,6 @@ */ package com.alibaba.cloud.dubbo.metadata; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; - import java.lang.reflect.Method; import java.lang.reflect.Parameter; import java.util.ArrayList; @@ -28,6 +24,10 @@ import java.util.LinkedList; import java.util.List; import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * {@link Method} Metadata * @@ -36,104 +36,101 @@ import java.util.Objects; @JsonInclude(JsonInclude.Include.NON_NULL) public class MethodMetadata { - private String name; + private String name; - @JsonProperty("return-type") - private String returnType; + @JsonProperty("return-type") + private String returnType; - private List params; + private List params; - @JsonIgnore - private Method method; + @JsonIgnore + private Method method; - public MethodMetadata() { - this.params = new LinkedList<>(); - } + public MethodMetadata() { + this.params = new LinkedList<>(); + } - public MethodMetadata(Method method) { - this.name = method.getName(); - this.returnType = method.getReturnType().getName(); - this.params = initParameters(method); - this.method = method; - } + public MethodMetadata(Method method) { + this.name = method.getName(); + this.returnType = method.getReturnType().getName(); + this.params = initParameters(method); + this.method = method; + } - private List initParameters(Method method) { - int parameterCount = method.getParameterCount(); - if (parameterCount < 1) { - return Collections.emptyList(); - } - List params = new ArrayList<>(parameterCount); - Parameter[] parameters = method.getParameters(); - for (int i = 0; i < parameterCount; i++) { - Parameter parameter = parameters[i]; - MethodParameterMetadata param = toMethodParameterMetadata(i, parameter); - params.add(param); - } - return params; - } + private List initParameters(Method method) { + int parameterCount = method.getParameterCount(); + if (parameterCount < 1) { + return Collections.emptyList(); + } + List params = new ArrayList<>(parameterCount); + Parameter[] parameters = method.getParameters(); + for (int i = 0; i < parameterCount; i++) { + Parameter parameter = parameters[i]; + MethodParameterMetadata param = toMethodParameterMetadata(i, parameter); + params.add(param); + } + return params; + } - private MethodParameterMetadata toMethodParameterMetadata(int index, Parameter parameter) { - MethodParameterMetadata metadata = new MethodParameterMetadata(); - metadata.setIndex(index); - metadata.setName(parameter.getName()); - metadata.setType(parameter.getType().getTypeName()); - return metadata; - } + private MethodParameterMetadata toMethodParameterMetadata(int index, + Parameter parameter) { + MethodParameterMetadata metadata = new MethodParameterMetadata(); + metadata.setIndex(index); + metadata.setName(parameter.getName()); + metadata.setType(parameter.getType().getTypeName()); + return metadata; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public String getReturnType() { - return returnType; - } + public String getReturnType() { + return returnType; + } - public void setReturnType(String returnType) { - this.returnType = returnType; - } + public void setReturnType(String returnType) { + this.returnType = returnType; + } - public List getParams() { - return params; - } + public List getParams() { + return params; + } - public void setParams(List params) { - this.params = params; - } + public void setParams(List params) { + this.params = params; + } - public Method getMethod() { - return method; - } + public Method getMethod() { + return method; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - MethodMetadata that = (MethodMetadata) o; - return Objects.equals(name, that.name) && - Objects.equals(returnType, that.returnType) && - Objects.equals(params, that.params); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MethodMetadata that = (MethodMetadata) o; + return Objects.equals(name, that.name) + && Objects.equals(returnType, that.returnType) + && Objects.equals(params, that.params); + } - @Override - public int hashCode() { - return Objects.hash(name, returnType, params); - } + @Override + public int hashCode() { + return Objects.hash(name, returnType, params); + } - @Override - public String toString() { - return "MethodMetadata{" + - "name='" + name + '\'' + - ", returnType='" + returnType + '\'' + - ", params=" + params + - ", method=" + method + - '}'; - } + @Override + public String toString() { + return "MethodMetadata{" + "name='" + name + '\'' + ", returnType='" + returnType + + '\'' + ", params=" + params + ", method=" + method + '}'; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/MethodParameterMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/MethodParameterMetadata.java index 39d4ac4a..56b79b27 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/MethodParameterMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/MethodParameterMetadata.java @@ -16,11 +16,11 @@ */ package com.alibaba.cloud.dubbo.metadata; -import com.fasterxml.jackson.annotation.JsonInclude; - import java.lang.reflect.Method; import java.util.Objects; +import com.fasterxml.jackson.annotation.JsonInclude; + /** * {@link Method} Parameter Metadata * @@ -29,61 +29,57 @@ import java.util.Objects; @JsonInclude(JsonInclude.Include.NON_NULL) public class MethodParameterMetadata { - private int index; + private int index; - private String name; + private String name; - private String type; + private String type; - public int getIndex() { - return index; - } + public int getIndex() { + return index; + } - public void setIndex(int index) { - this.index = index; - } + public void setIndex(int index) { + this.index = index; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public String getType() { - return type; - } + public String getType() { + return type; + } - public void setType(String type) { - this.type = type; - } + public void setType(String type) { + this.type = type; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - MethodParameterMetadata that = (MethodParameterMetadata) o; - return index == that.index && - Objects.equals(name, that.name) && - Objects.equals(type, that.type); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MethodParameterMetadata that = (MethodParameterMetadata) o; + return index == that.index && Objects.equals(name, that.name) + && Objects.equals(type, that.type); + } - @Override - public int hashCode() { - return Objects.hash(index, name, type); - } + @Override + public int hashCode() { + return Objects.hash(index, name, type); + } - @Override - public String toString() { - return "MethodParameterMetadata{" + - "index=" + index + - ", name='" + name + '\'' + - ", type='" + type + '\'' + - '}'; - } + @Override + public String toString() { + return "MethodParameterMetadata{" + "index=" + index + ", name='" + name + '\'' + + ", type='" + type + '\'' + '}'; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RequestMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RequestMetadata.java index 05b6b60d..6f2a88f3 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RequestMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RequestMetadata.java @@ -16,14 +16,8 @@ */ package com.alibaba.cloud.dubbo.metadata; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import feign.RequestTemplate; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.util.CollectionUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; +import static com.alibaba.cloud.dubbo.http.util.HttpUtils.normalizePath; +import static org.springframework.http.MediaType.parseMediaTypes; import java.util.ArrayList; import java.util.Collection; @@ -37,8 +31,16 @@ import java.util.Objects; import java.util.Set; import java.util.SortedMap; -import static com.alibaba.cloud.dubbo.http.util.HttpUtils.normalizePath; -import static org.springframework.http.MediaType.parseMediaTypes; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.util.CollectionUtils; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +import feign.RequestTemplate; /** * Request Metadata @@ -47,226 +49,226 @@ import static org.springframework.http.MediaType.parseMediaTypes; */ public class RequestMetadata { - private String method; + private String method; - private String path; + private String path; - @JsonProperty("params") - private MultiValueMap params = new LinkedMultiValueMap<>(); + @JsonProperty("params") + private MultiValueMap params = new LinkedMultiValueMap<>(); - @JsonProperty("headers") - private HttpHeaders headers = new HttpHeaders(); + @JsonProperty("headers") + private HttpHeaders headers = new HttpHeaders(); - private Set consumes = new LinkedHashSet<>(); + private Set consumes = new LinkedHashSet<>(); - private Set produces = new LinkedHashSet<>(); + private Set produces = new LinkedHashSet<>(); - public RequestMetadata() { - } + public RequestMetadata() { + } - public RequestMetadata(RequestTemplate requestTemplate) { - setMethod(requestTemplate.method()); - setPath(requestTemplate.url()); - params(requestTemplate.queries()); - headers(requestTemplate.headers()); - } + public RequestMetadata(RequestTemplate requestTemplate) { + setMethod(requestTemplate.method()); + setPath(requestTemplate.url()); + params(requestTemplate.queries()); + headers(requestTemplate.headers()); + } - public String getMethod() { - return method; - } + /** + * Get the best matched {@link RequestMetadata} via specified {@link RequestMetadata} + * + * @param requestMetadataMap the source of {@link NavigableMap} + * @param requestMetadata the match object + * @return if not matched, return null + */ + public static RequestMetadata getBestMatch( + NavigableMap requestMetadataMap, + RequestMetadata requestMetadata) { - public void setMethod(String method) { - this.method = method.toUpperCase(); - } + RequestMetadata key = requestMetadata; - public String getPath() { - return path; - } + RequestMetadata result = requestMetadataMap.get(key); - public void setPath(String path) { - this.path = normalizePath(path); - } + if (result == null) { + SortedMap headMap = requestMetadataMap + .headMap(key, true); + result = headMap.isEmpty() ? null : requestMetadataMap.get(headMap.lastKey()); + } - public MultiValueMap getParams() { - return params; - } + return result; + } - public void setParams(Map> params) { - params(params); - } + private static void add(String key, String value, + MultiValueMap destination) { + destination.add(key, value); + } - public Map> getHeaders() { - return headers; - } + private static > void addAll(Map source, + MultiValueMap destination) { + for (Map.Entry entry : source.entrySet()) { + String key = entry.getKey(); + for (String value : entry.getValue()) { + add(key, value, destination); + } + } + } - public void setHeaders(Map> headers) { - headers(headers); - } + private static void mediaTypes(HttpHeaders httpHeaders, String headerName, + Collection destination) { + List value = httpHeaders.get(headerName); + List mediaTypes = parseMediaTypes(value); + destination.addAll(toMediaTypeValues(mediaTypes)); + } - public Set getConsumes() { - return consumes; - } + private static List toMediaTypeValues(List mediaTypes) { + List list = new ArrayList<>(mediaTypes.size()); + for (MediaType mediaType : mediaTypes) { + list.add(mediaType.toString()); + } + return list; + } - public void setConsumes(Set consumes) { - this.consumes = consumes; - } + private static List toMediaTypes(Collection mediaTypeValues) { + if (mediaTypeValues.isEmpty()) { + return Collections.singletonList(MediaType.ALL); + } + return parseMediaTypes(new LinkedList<>(mediaTypeValues)); + } - public Set getProduces() { - return produces; - } + public String getMethod() { + return method; + } - public void setProduces(Set produces) { - this.produces = produces; - } + public void setMethod(String method) { + this.method = method.toUpperCase(); + } - // @JsonIgnore properties - @JsonIgnore - public Set getParamNames() { - return params.keySet(); - } + public String getPath() { + return path; + } - @JsonIgnore - public Set getHeaderNames() { - return headers.keySet(); - } + public void setPath(String path) { + this.path = normalizePath(path); + } - @JsonIgnore - public List getConsumeMediaTypes() { - return toMediaTypes(consumes); - } + public MultiValueMap getParams() { + return params; + } - @JsonIgnore - public List getProduceMediaTypes() { - return toMediaTypes(produces); - } + public void setParams(Map> params) { + params(params); + } - public String getParameter(String name) { - return this.params.getFirst(name); - } + public Map> getHeaders() { + return headers; + } - public String getHeader(String name) { - return this.headers.getFirst(name); - } + public void setHeaders(Map> headers) { + headers(headers); + } - public RequestMetadata addParam(String name, String value) { - add(name, value, this.params); - return this; - } + public Set getConsumes() { + return consumes; + } - public RequestMetadata addHeader(String name, String value) { - add(name, value, this.headers); - return this; - } + public void setConsumes(Set consumes) { + this.consumes = consumes; + } - private > RequestMetadata params(Map params) { - addAll(params, this.params); - return this; - } + public Set getProduces() { + return produces; + } - private > RequestMetadata headers(Map headers) { - if (!CollectionUtils.isEmpty(headers)) { - HttpHeaders httpHeaders = new HttpHeaders(); - // Add all headers - addAll(headers, httpHeaders); - // Handles "Content-Type" and "Accept" headers if present - mediaTypes(httpHeaders, HttpHeaders.CONTENT_TYPE, this.consumes); - mediaTypes(httpHeaders, HttpHeaders.ACCEPT, this.produces); - this.headers.putAll(httpHeaders); - } - return this; - } + public void setProduces(Set produces) { + this.produces = produces; + } - /** - * Get the best matched {@link RequestMetadata} via specified {@link RequestMetadata} - * - * @param requestMetadataMap the source of {@link NavigableMap} - * @param requestMetadata the match object - * @return if not matched, return null - */ - public static RequestMetadata getBestMatch(NavigableMap requestMetadataMap, - RequestMetadata requestMetadata) { + // @JsonIgnore properties + @JsonIgnore + public Set getParamNames() { + return params.keySet(); + } - RequestMetadata key = requestMetadata; + @JsonIgnore + public Set getHeaderNames() { + return headers.keySet(); + } - RequestMetadata result = requestMetadataMap.get(key); + @JsonIgnore + public List getConsumeMediaTypes() { + return toMediaTypes(consumes); + } - if (result == null) { - SortedMap headMap = requestMetadataMap.headMap(key, true); - result = headMap.isEmpty() ? null : requestMetadataMap.get(headMap.lastKey()); - } + @JsonIgnore + public List getProduceMediaTypes() { + return toMediaTypes(produces); + } - return result; - } + public String getParameter(String name) { + return this.params.getFirst(name); + } - private static void add(String key, String value, MultiValueMap destination) { - destination.add(key, value); - } + public String getHeader(String name) { + return this.headers.getFirst(name); + } - private static > void addAll(Map source, - MultiValueMap destination) { - for (Map.Entry entry : source.entrySet()) { - String key = entry.getKey(); - for (String value : entry.getValue()) { - add(key, value, destination); - } - } - } + public RequestMetadata addParam(String name, String value) { + add(name, value, this.params); + return this; + } - private static void mediaTypes(HttpHeaders httpHeaders, String headerName, Collection destination) { - List value = httpHeaders.get(headerName); - List mediaTypes = parseMediaTypes(value); - destination.addAll(toMediaTypeValues(mediaTypes)); - } + public RequestMetadata addHeader(String name, String value) { + add(name, value, this.headers); + return this; + } - private static List toMediaTypeValues(List mediaTypes) { - List list = new ArrayList<>(mediaTypes.size()); - for (MediaType mediaType : mediaTypes) { - list.add(mediaType.toString()); - } - return list; - } + private > RequestMetadata params(Map params) { + addAll(params, this.params); + return this; + } - private static List toMediaTypes(Collection mediaTypeValues) { - if (mediaTypeValues.isEmpty()) { - return Collections.singletonList(MediaType.ALL); - } - return parseMediaTypes(new LinkedList<>(mediaTypeValues)); - } + private > RequestMetadata headers( + Map headers) { + if (!CollectionUtils.isEmpty(headers)) { + HttpHeaders httpHeaders = new HttpHeaders(); + // Add all headers + addAll(headers, httpHeaders); + // Handles "Content-Type" and "Accept" headers if present + mediaTypes(httpHeaders, HttpHeaders.CONTENT_TYPE, this.consumes); + mediaTypes(httpHeaders, HttpHeaders.ACCEPT, this.produces); + this.headers.putAll(httpHeaders); + } + return this; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof RequestMetadata)) { - return false; - } - RequestMetadata that = (RequestMetadata) o; - return Objects.equals(method, that.method) && - Objects.equals(path, that.path) && - Objects.equals(consumes, that.consumes) && - Objects.equals(produces, that.produces) && - // Metadata should not compare the values - Objects.equals(getParamNames(), that.getParamNames()) && - Objects.equals(getHeaderNames(), that.getHeaderNames()); + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof RequestMetadata)) { + return false; + } + RequestMetadata that = (RequestMetadata) o; + return Objects.equals(method, that.method) && Objects.equals(path, that.path) + && Objects.equals(consumes, that.consumes) + && Objects.equals(produces, that.produces) && + // Metadata should not compare the values + Objects.equals(getParamNames(), that.getParamNames()) + && Objects.equals(getHeaderNames(), that.getHeaderNames()); - } + } - @Override - public int hashCode() { - // The values of metadata should not use for the hashCode() method - return Objects.hash(method, path, consumes, produces, getParamNames(), getHeaderNames()); - } + @Override + public int hashCode() { + // The values of metadata should not use for the hashCode() method + return Objects.hash(method, path, consumes, produces, getParamNames(), + getHeaderNames()); + } - @Override - public String toString() { - return "RequestMetadata{" + - "method='" + method + '\'' + - ", path='" + path + '\'' + - ", params=" + params + - ", headers=" + headers + - ", consumes=" + consumes + - ", produces=" + produces + - '}'; - } + @Override + public String toString() { + return "RequestMetadata{" + "method='" + method + '\'' + ", path='" + path + '\'' + + ", params=" + params + ", headers=" + headers + ", consumes=" + consumes + + ", produces=" + produces + '}'; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RestMethodMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RestMethodMetadata.java index 7840d448..fceec4e6 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RestMethodMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/RestMethodMetadata.java @@ -16,16 +16,17 @@ */ package com.alibaba.cloud.dubbo.metadata; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import org.springframework.core.ResolvableType; - import java.lang.reflect.Type; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; +import org.springframework.core.ResolvableType; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + /** * Method Request Metadata * @@ -34,205 +35,199 @@ import java.util.Objects; @JsonInclude(JsonInclude.Include.NON_NULL) public class RestMethodMetadata { - private MethodMetadata method; + private MethodMetadata method; - private RequestMetadata request; + private RequestMetadata request; - @JsonProperty("url-index") - private Integer urlIndex; + @JsonProperty("url-index") + private Integer urlIndex; - @JsonProperty("setBody-index") - private Integer bodyIndex; + @JsonProperty("setBody-index") + private Integer bodyIndex; - @JsonProperty("header-map-index") - private Integer headerMapIndex; + @JsonProperty("header-map-index") + private Integer headerMapIndex; - @JsonProperty("query-map-index") - private Integer queryMapIndex; + @JsonProperty("query-map-index") + private Integer queryMapIndex; - @JsonProperty("query-map-encoded") - private boolean queryMapEncoded; + @JsonProperty("query-map-encoded") + private boolean queryMapEncoded; - @JsonProperty("return-type") - private String returnType; + @JsonProperty("return-type") + private String returnType; - @JsonProperty("setBody-type") - private String bodyType; + @JsonProperty("setBody-type") + private String bodyType; - @JsonProperty("index-to-name") - private Map> indexToName; + @JsonProperty("index-to-name") + private Map> indexToName; - @JsonProperty("form-params") - private List formParams; + @JsonProperty("form-params") + private List formParams; - @JsonProperty("index-to-encoded") - private Map indexToEncoded; + @JsonProperty("index-to-encoded") + private Map indexToEncoded; - public RestMethodMetadata() { - } + public RestMethodMetadata() { + } - public RestMethodMetadata(feign.MethodMetadata methodMetadata) { - this.request = new RequestMetadata(methodMetadata.template()); - this.urlIndex = methodMetadata.urlIndex(); - this.bodyIndex = methodMetadata.bodyIndex(); - this.headerMapIndex = methodMetadata.headerMapIndex(); - this.queryMapEncoded = methodMetadata.queryMapEncoded(); - this.queryMapEncoded = methodMetadata.queryMapEncoded(); - this.returnType = getClassName(methodMetadata.returnType()); - this.bodyType = getClassName(methodMetadata.bodyType()); - this.indexToName = methodMetadata.indexToName(); - this.formParams = methodMetadata.formParams(); - this.indexToEncoded = methodMetadata.indexToEncoded(); - } + public RestMethodMetadata(feign.MethodMetadata methodMetadata) { + this.request = new RequestMetadata(methodMetadata.template()); + this.urlIndex = methodMetadata.urlIndex(); + this.bodyIndex = methodMetadata.bodyIndex(); + this.headerMapIndex = methodMetadata.headerMapIndex(); + this.queryMapEncoded = methodMetadata.queryMapEncoded(); + this.queryMapEncoded = methodMetadata.queryMapEncoded(); + this.returnType = getClassName(methodMetadata.returnType()); + this.bodyType = getClassName(methodMetadata.bodyType()); + this.indexToName = methodMetadata.indexToName(); + this.formParams = methodMetadata.formParams(); + this.indexToEncoded = methodMetadata.indexToEncoded(); + } - public MethodMetadata getMethod() { - return method; - } + public MethodMetadata getMethod() { + return method; + } - public void setMethod(MethodMetadata method) { - this.method = method; - } + public void setMethod(MethodMetadata method) { + this.method = method; + } - public RequestMetadata getRequest() { - return request; - } + public RequestMetadata getRequest() { + return request; + } - public void setRequest(RequestMetadata request) { - this.request = request; - } + public void setRequest(RequestMetadata request) { + this.request = request; + } - public Map> getIndexToName() { - return indexToName; - } + public Map> getIndexToName() { + return indexToName; + } - public void setIndexToName(Map> indexToName) { - this.indexToName = indexToName; - } + public void setIndexToName(Map> indexToName) { + this.indexToName = indexToName; + } - public Integer getUrlIndex() { - return urlIndex; - } + public Integer getUrlIndex() { + return urlIndex; + } - public void setUrlIndex(Integer urlIndex) { - this.urlIndex = urlIndex; - } + public void setUrlIndex(Integer urlIndex) { + this.urlIndex = urlIndex; + } - public Integer getBodyIndex() { - return bodyIndex; - } + public Integer getBodyIndex() { + return bodyIndex; + } - public void setBodyIndex(Integer bodyIndex) { - this.bodyIndex = bodyIndex; - } + public void setBodyIndex(Integer bodyIndex) { + this.bodyIndex = bodyIndex; + } - public Integer getHeaderMapIndex() { - return headerMapIndex; - } + public Integer getHeaderMapIndex() { + return headerMapIndex; + } - public void setHeaderMapIndex(Integer headerMapIndex) { - this.headerMapIndex = headerMapIndex; - } + public void setHeaderMapIndex(Integer headerMapIndex) { + this.headerMapIndex = headerMapIndex; + } - public Integer getQueryMapIndex() { - return queryMapIndex; - } + public Integer getQueryMapIndex() { + return queryMapIndex; + } - public void setQueryMapIndex(Integer queryMapIndex) { - this.queryMapIndex = queryMapIndex; - } + public void setQueryMapIndex(Integer queryMapIndex) { + this.queryMapIndex = queryMapIndex; + } - public boolean isQueryMapEncoded() { - return queryMapEncoded; - } + public boolean isQueryMapEncoded() { + return queryMapEncoded; + } - public void setQueryMapEncoded(boolean queryMapEncoded) { - this.queryMapEncoded = queryMapEncoded; - } + public void setQueryMapEncoded(boolean queryMapEncoded) { + this.queryMapEncoded = queryMapEncoded; + } - public String getReturnType() { - return returnType; - } + public String getReturnType() { + return returnType; + } - public void setReturnType(String returnType) { - this.returnType = returnType; - } + public void setReturnType(String returnType) { + this.returnType = returnType; + } - public String getBodyType() { - return bodyType; - } + public String getBodyType() { + return bodyType; + } - public void setBodyType(String bodyType) { - this.bodyType = bodyType; - } + public void setBodyType(String bodyType) { + this.bodyType = bodyType; + } - public List getFormParams() { - return formParams; - } + public List getFormParams() { + return formParams; + } - public void setFormParams(List formParams) { - this.formParams = formParams; - } + public void setFormParams(List formParams) { + this.formParams = formParams; + } - public Map getIndexToEncoded() { - return indexToEncoded; - } + public Map getIndexToEncoded() { + return indexToEncoded; + } - public void setIndexToEncoded(Map indexToEncoded) { - this.indexToEncoded = indexToEncoded; - } + public void setIndexToEncoded(Map indexToEncoded) { + this.indexToEncoded = indexToEncoded; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof RestMethodMetadata)) { - return false; - } - RestMethodMetadata that = (RestMethodMetadata) o; - return queryMapEncoded == that.queryMapEncoded && - Objects.equals(method, that.method) && - Objects.equals(request, that.request) && - Objects.equals(urlIndex, that.urlIndex) && - Objects.equals(bodyIndex, that.bodyIndex) && - Objects.equals(headerMapIndex, that.headerMapIndex) && - Objects.equals(queryMapIndex, that.queryMapIndex) && - Objects.equals(returnType, that.returnType) && - Objects.equals(bodyType, that.bodyType) && - Objects.equals(indexToName, that.indexToName) && - Objects.equals(formParams, that.formParams) && - Objects.equals(indexToEncoded, that.indexToEncoded); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof RestMethodMetadata)) { + return false; + } + RestMethodMetadata that = (RestMethodMetadata) o; + return queryMapEncoded == that.queryMapEncoded + && Objects.equals(method, that.method) + && Objects.equals(request, that.request) + && Objects.equals(urlIndex, that.urlIndex) + && Objects.equals(bodyIndex, that.bodyIndex) + && Objects.equals(headerMapIndex, that.headerMapIndex) + && Objects.equals(queryMapIndex, that.queryMapIndex) + && Objects.equals(returnType, that.returnType) + && Objects.equals(bodyType, that.bodyType) + && Objects.equals(indexToName, that.indexToName) + && Objects.equals(formParams, that.formParams) + && Objects.equals(indexToEncoded, that.indexToEncoded); + } - @Override - public int hashCode() { - return Objects.hash(method, request, urlIndex, bodyIndex, headerMapIndex, queryMapIndex, queryMapEncoded, - returnType, bodyType, indexToName, formParams, indexToEncoded); - } + @Override + public int hashCode() { + return Objects.hash(method, request, urlIndex, bodyIndex, headerMapIndex, + queryMapIndex, queryMapEncoded, returnType, bodyType, indexToName, + formParams, indexToEncoded); + } - private String getClassName(Type type) { - if (type == null) { - return null; - } - ResolvableType resolvableType = ResolvableType.forType(type); - return resolvableType.resolve().getName(); - } + private String getClassName(Type type) { + if (type == null) { + return null; + } + ResolvableType resolvableType = ResolvableType.forType(type); + return resolvableType.resolve().getName(); + } - @Override - public String toString() { - return "RestMethodMetadata{" + - "method=" + method + - ", request=" + request + - ", urlIndex=" + urlIndex + - ", bodyIndex=" + bodyIndex + - ", headerMapIndex=" + headerMapIndex + - ", queryMapIndex=" + queryMapIndex + - ", queryMapEncoded=" + queryMapEncoded + - ", returnType='" + returnType + '\'' + - ", bodyType='" + bodyType + '\'' + - ", indexToName=" + indexToName + - ", formParams=" + formParams + - ", indexToEncoded=" + indexToEncoded + - '}'; - } + @Override + public String toString() { + return "RestMethodMetadata{" + "method=" + method + ", request=" + request + + ", urlIndex=" + urlIndex + ", bodyIndex=" + bodyIndex + + ", headerMapIndex=" + headerMapIndex + ", queryMapIndex=" + + queryMapIndex + ", queryMapEncoded=" + queryMapEncoded + + ", returnType='" + returnType + '\'' + ", bodyType='" + bodyType + '\'' + + ", indexToName=" + indexToName + ", formParams=" + formParams + + ", indexToEncoded=" + indexToEncoded + '}'; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/ServiceRestMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/ServiceRestMetadata.java index 3a871b21..e0c93d69 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/ServiceRestMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/ServiceRestMetadata.java @@ -16,11 +16,11 @@ */ package com.alibaba.cloud.dubbo.metadata; -import com.fasterxml.jackson.annotation.JsonInclude; - import java.util.Objects; import java.util.Set; +import com.fasterxml.jackson.annotation.JsonInclude; + /** * Service Rest Metadata * @@ -30,41 +30,40 @@ import java.util.Set; @JsonInclude(JsonInclude.Include.NON_NULL) public class ServiceRestMetadata { - private String url; + private String url; - private Set meta; + private Set meta; - public String getUrl() { - return url; - } + public String getUrl() { + return url; + } - public void setUrl(String url) { - this.url = url; - } + public void setUrl(String url) { + this.url = url; + } - public Set getMeta() { - return meta; - } + public Set getMeta() { + return meta; + } - public void setMeta(Set meta) { - this.meta = meta; - } + public void setMeta(Set meta) { + this.meta = meta; + } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ServiceRestMetadata)) { - return false; - } - ServiceRestMetadata that = (ServiceRestMetadata) o; - return Objects.equals(url, that.url) && - Objects.equals(meta, that.meta); - } + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ServiceRestMetadata)) { + return false; + } + ServiceRestMetadata that = (ServiceRestMetadata) o; + return Objects.equals(url, that.url) && Objects.equals(meta, that.meta); + } - @Override - public int hashCode() { - return Objects.hash(url, meta); - } + @Override + public int hashCode() { + return Objects.hash(url, meta); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/DubboServiceMetadataRepository.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/DubboServiceMetadataRepository.java index e14855df..e0a7a487 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/DubboServiceMetadataRepository.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/repository/DubboServiceMetadataRepository.java @@ -16,44 +16,8 @@ */ package com.alibaba.cloud.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.beans.factory.annotation.Value; -import com.alibaba.cloud.dubbo.env.DubboCloudProperties; -import com.alibaba.cloud.dubbo.http.matcher.RequestMetadataMatcher; -import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata; -import com.alibaba.cloud.dubbo.metadata.RequestMetadata; -import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata; -import com.alibaba.cloud.dubbo.service.DubboMetadataService; -import com.alibaba.cloud.dubbo.service.DubboMetadataServiceExporter; -import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy; -import com.alibaba.cloud.dubbo.util.JSONUtils; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.commons.util.InetUtils; -import org.springframework.http.HttpRequest; -import org.springframework.stereotype.Repository; -import org.springframework.util.CollectionUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - -import javax.annotation.PostConstruct; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - +import static com.alibaba.cloud.dubbo.env.DubboCloudProperties.ALL_DUBBO_SERVICES; +import static com.alibaba.cloud.dubbo.http.DefaultHttpRequest.builder; import static java.lang.String.format; import static java.lang.String.valueOf; import static java.util.Collections.emptyList; @@ -61,480 +25,638 @@ import static java.util.Collections.emptySet; import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableMap; import static java.util.Collections.unmodifiableSet; -import static org.apache.dubbo.common.Constants.APPLICATION_KEY; -import static org.apache.dubbo.common.Constants.VERSION_KEY; -import static com.alibaba.cloud.dubbo.env.DubboCloudProperties.ALL_DUBBO_SERVICES; -import static com.alibaba.cloud.dubbo.http.DefaultHttpRequest.builder; +import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.springframework.util.CollectionUtils.isEmpty; import static org.springframework.util.StringUtils.hasText; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.annotation.PostConstruct; + +import org.apache.dubbo.common.URL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.commons.util.InetUtils; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.http.HttpRequest; +import org.springframework.stereotype.Repository; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import com.alibaba.cloud.dubbo.env.DubboCloudProperties; +import com.alibaba.cloud.dubbo.http.matcher.RequestMetadataMatcher; +import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata; +import com.alibaba.cloud.dubbo.metadata.RequestMetadata; +import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata; +import com.alibaba.cloud.dubbo.registry.event.SubscribedServicesChangedEvent; +import com.alibaba.cloud.dubbo.service.DubboMetadataService; +import com.alibaba.cloud.dubbo.service.DubboMetadataServiceExporter; +import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy; +import com.alibaba.cloud.dubbo.util.JSONUtils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.TypeFactory; + /** * Dubbo Service Metadata {@link Repository} * * @author Mercy */ @Repository -public class DubboServiceMetadataRepository { - - /** - * The prefix of {@link DubboMetadataService} : "dubbo.metadata-service." - */ - public static final String DUBBO_METADATA_SERVICE_PREFIX = "dubbo.metadata-service."; - - /** - * The {@link URL URLs} property name of {@link DubboMetadataService} : "dubbo.metadata-service.urls" - */ - public static final String DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME = DUBBO_METADATA_SERVICE_PREFIX + "urls"; - - /** - * The {@link String#format(String, Object...) pattern} of dubbo protocols port - */ - public static final String DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN = "dubbo.protocols.%s.port"; - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - private final ObjectMapper objectMapper = new ObjectMapper(); - - // =================================== Registration =================================== // - - /** - * All exported {@link URL urls} {@link Map} whose key is the return value of {@link URL#getServiceKey()} method - * and value is the {@link List} of {@link URL URLs} - */ - private final MultiValueMap allExportedURLs = new LinkedMultiValueMap<>(); - - // ==================================================================================== // - - - // =================================== Subscription =================================== // - - private Set subscribedServices; - - /** - * The subscribed {@link URL urls} {@link Map} of {@link DubboMetadataService}, - * whose key is the return value of {@link URL#getServiceKey()} method and value is the {@link List} of - * {@link URL URLs} - */ - private final MultiValueMap subscribedDubboMetadataServiceURLs = new LinkedMultiValueMap<>(); - - // ==================================================================================== // - - - // =================================== REST Metadata ================================== // - - /** - * A Map to store REST metadata temporary, its' key is the special service name for a Dubbo service, - * the value is a JSON content of JAX-RS or Spring MVC REST metadata from the annotated methods. - */ - private final Set serviceRestMetadata = new LinkedHashSet<>(); - - /** - * Key is application name - * Value is Map - */ - private Map> dubboRestServiceMetadataRepository = newHashMap(); - - // ==================================================================================== // - - - // =================================== Dependencies =================================== // - - @Autowired - private DubboCloudProperties dubboCloudProperties; - - @Autowired - private DubboMetadataServiceProxy dubboMetadataConfigServiceProxy; - - @Autowired - private DiscoveryClient discoveryClient; - - @Autowired - private JSONUtils jsonUtils; - - @Autowired - private InetUtils inetUtils; - - @Value("${spring.application.name}") - private String currentApplicationName; - - @Autowired - private DubboMetadataServiceExporter dubboMetadataServiceExporter; - - // ==================================================================================== // - - - @PostConstruct - public void init() { - // Keep the order in following invocations - initSubscribedServices(); - initSubscribedDubboMetadataServices(); - initDubboRestServiceMetadataRepository(); - } - - /** - * Get the metadata {@link Map} of {@link DubboMetadataService} - * - * @return non-null read-only {@link Map} - */ - public Map getDubboMetadataServiceMetadata() { - - List dubboMetadataServiceURLs = dubboMetadataServiceExporter.export(); - - // remove the exported URLs of DubboMetadataService - removeDubboMetadataServiceURLs(dubboMetadataServiceURLs); - - Map metadata = newHashMap(); - - addDubboMetadataServiceURLsMetadata(metadata, dubboMetadataServiceURLs); - addDubboProtocolsPortMetadata(metadata); - - return Collections.unmodifiableMap(metadata); - } - - private void removeDubboMetadataServiceURLs(List dubboMetadataServiceURLs) { - dubboMetadataServiceURLs.forEach(this::unexportURL); - } - - private void addDubboMetadataServiceURLsMetadata(Map metadata, List dubboMetadataServiceURLs) { - String dubboMetadataServiceURLsJSON = jsonUtils.toJSON(dubboMetadataServiceURLs); - metadata.put(DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME, dubboMetadataServiceURLsJSON); - } - - private void addDubboProtocolsPortMetadata(Map metadata) { - - allExportedURLs.values() - .stream() - .flatMap(v -> v.stream()) - .forEach(url -> { - String protocol = url.getProtocol(); - String propertyName = getDubboProtocolPropertyName(protocol); - String propertyValue = valueOf(url.getPort()); - metadata.put(propertyName, propertyValue); - }); - } - - /** - * Get the property name of Dubbo Protocol - * - * @param protocol Dubbo Protocol - * @return non-null - */ - public String getDubboProtocolPropertyName(String protocol) { - return format(DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN, protocol); - } - - /** - * Publish the {@link Set} of {@link ServiceRestMetadata} - * - * @param serviceRestMetadataSet the {@link Set} of {@link ServiceRestMetadata} - */ - public void publishServiceRestMetadata(Set serviceRestMetadataSet) { - for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) { - if (!isEmpty(serviceRestMetadata.getMeta())) { - this.serviceRestMetadata.add(serviceRestMetadata); - } - } - } - - /** - * Get the {@link Set} of {@link ServiceRestMetadata} - * - * @return non-null read-only {@link Set} - */ - public Set getServiceRestMetadata() { - return unmodifiableSet(serviceRestMetadata); - } - -// /** -// * Get The subscribed {@link DubboMetadataService}'s {@link URL URLs} -// * -// * @return non-null read-only {@link List} -// */ -// public List getSubscribedDubboMetadataServiceURLs() { -// return Collections.unmodifiableList(subscribedDubboMetadataServiceURLs); -// } - - public List findSubscribedDubboMetadataServiceURLs(String serviceName, String group, String version, - String protocol) { - String serviceKey = URL.buildKey(serviceName, group, version); - List urls = subscribedDubboMetadataServiceURLs.get(serviceKey); - - if (isEmpty(urls)) { - return emptyList(); - } - - return hasText(protocol) ? - urls.stream().filter(url -> url.getProtocol().equalsIgnoreCase(protocol)).collect(Collectors.toList()) : - unmodifiableList(urls) - ; - } - - /** - * The specified service is subscribe or not - * - * @param serviceName the service name - * @return - */ - public boolean isSubscribedService(String serviceName) { - return subscribedServices.contains(serviceName); - } - - public void exportURL(URL url) { - URL actualURL = url; - InetUtils.HostInfo hostInfo = inetUtils.findFirstNonLoopbackHostInfo(); - String ipAddress = hostInfo.getIpAddress(); - // To use InetUtils to set IP if they are different - // issue : https://github.com/spring-cloud-incubator/spring-cloud-alibaba/issues/589 - if (!Objects.equals(url.getHost(), ipAddress)) { - actualURL = url.setHost(ipAddress); - } - this.allExportedURLs.add(actualURL.getServiceKey(), actualURL); - } - - public void unexportURL(URL url) { - String key = url.getServiceKey(); - // NPE issue : https://github.com/spring-cloud-incubator/spring-cloud-alibaba/issues/591 - List urls = allExportedURLs.get(key); - if (!isEmpty(urls)) { - urls.remove(url); - allExportedURLs.addAll(key, urls); - } - } - - /** - * Get all exported {@link URL urls}. - * - * @return non-null read-only - */ - public Map> getAllExportedUrls() { - return unmodifiableMap(allExportedURLs); - } - - /** - * Get all exported {@link URL#getServiceKey() service keys} - * - * @return non-null read-only - */ - public Set getAllServiceKeys() { - return allExportedURLs.keySet(); - } - - /** - * Get the {@link URL urls} that {@link DubboMetadataService} exported by the specified {@link ServiceInstance} - * - * @param serviceInstance {@link ServiceInstance} - * @return the mutable {@link URL urls} - */ - public List getDubboMetadataServiceURLs(ServiceInstance serviceInstance) { - Map metadata = serviceInstance.getMetadata(); - String dubboURLsJSON = metadata.get(DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME); - return jsonUtils.toURLs(dubboURLsJSON); - } - - public Integer getDubboProtocolPort(ServiceInstance serviceInstance, String protocol) { - String protocolProperty = getDubboProtocolPropertyName(protocol); - Map metadata = serviceInstance.getMetadata(); - String protocolPort = metadata.get(protocolProperty); - return hasText(protocolPort) ? Integer.valueOf(protocolPort) : null; - } - - public List getExportedURLs(String serviceInterface, String group, String version) { - String serviceKey = URL.buildKey(serviceInterface, group, version); - return allExportedURLs.getOrDefault(serviceKey, Collections.emptyList()); - } - - /** - * Initialize the specified service's {@link ServiceRestMetadata} - * - * @param serviceName the service name - */ - public void initialize(String serviceName) { - - if (dubboRestServiceMetadataRepository.containsKey(serviceName)) { - return; - } - - Set serviceRestMetadataSet = getServiceRestMetadataSet(serviceName); - - if (isEmpty(serviceRestMetadataSet)) { - if (logger.isWarnEnabled()) { - logger.warn("The Spring application[name : {}] does not expose The REST metadata in the Dubbo services." - , serviceName); - } - return; - } - - Map metadataMap = getMetadataMap(serviceName); - - for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) { - - serviceRestMetadata.getMeta().forEach(restMethodMetadata -> { - RequestMetadata requestMetadata = restMethodMetadata.getRequest(); - RequestMetadataMatcher matcher = new RequestMetadataMatcher(requestMetadata); - DubboRestServiceMetadata metadata = new DubboRestServiceMetadata(serviceRestMetadata, restMethodMetadata); - metadataMap.put(matcher, metadata); - }); - } - - if (logger.isInfoEnabled()) { - logger.info("The REST metadata in the dubbo services has been loaded in the Spring application[name : {}]", serviceName); - } - } - - /** - * 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 DubboRestServiceMetadata} if matched, or null - */ - public DubboRestServiceMetadata get(String serviceName, RequestMetadata requestMetadata) { - return match(dubboRestServiceMetadataRepository, serviceName, requestMetadata); - } - - public Set getSubscribedServices() { - return Collections.unmodifiableSet(subscribedServices); - } - - private T match(Map> repository, String serviceName, - RequestMetadata requestMetadata) { - - Map map = repository.get(serviceName); - - T object = null; - - if (!isEmpty(map)) { - RequestMetadataMatcher matcher = new RequestMetadataMatcher(requestMetadata); - object = map.get(matcher); - if (object == null) { // Can't match exactly - // Require to match one by one - HttpRequest request = builder() - .method(requestMetadata.getMethod()) - .path(requestMetadata.getPath()) - .params(requestMetadata.getParams()) - .headers(requestMetadata.getHeaders()) - .build(); - - for (Map.Entry entry : map.entrySet()) { - RequestMetadataMatcher possibleMatcher = entry.getKey(); - if (possibleMatcher.match(request)) { - object = entry.getValue(); - break; - } - } - } - } - - if (object == null) { - if (logger.isWarnEnabled()) { - logger.warn("DubboServiceMetadata can't be found in the Spring application [%s] and %s", - serviceName, requestMetadata); - } - } - - return object; - } - - private Map getMetadataMap(String serviceName) { - return getMap(dubboRestServiceMetadataRepository, serviceName); - } - - private Set getServiceRestMetadataSet(String serviceName) { - - Set metadata = emptySet(); - - DubboMetadataService dubboMetadataService = dubboMetadataConfigServiceProxy.getProxy(serviceName); - - if (dubboMetadataService != null) { - try { - String serviceRestMetadataJsonConfig = dubboMetadataService.getServiceRestMetadata(); - if (hasText(serviceRestMetadataJsonConfig)) { - metadata = objectMapper.readValue(serviceRestMetadataJsonConfig, - TypeFactory.defaultInstance().constructCollectionType(LinkedHashSet.class, ServiceRestMetadata.class)); - } - } catch (Exception e) { - if (logger.isErrorEnabled()) { - logger.error(e.getMessage(), e); - } - } - } - return metadata; - } - - private static Map getMap(Map> repository, String key) { - return getOrDefault(repository, key, newHashMap()); - } - - private static V getOrDefault(Map source, K key, V defaultValue) { - V value = source.get(key); - if (value == null) { - value = defaultValue; - source.put(key, value); - } - return value; - } - - private static Map newHashMap() { - return new LinkedHashMap<>(); - } - - private void initSubscribedServices() { - // If subscribes all services - if (ALL_DUBBO_SERVICES.equals(dubboCloudProperties.getSubscribedServices())) { - List services = discoveryClient.getServices(); - subscribedServices = new HashSet<>(services); - if (logger.isWarnEnabled()) { - logger.warn("Current application will subscribe all services(size:{}) in registry, " + - "a lot of memory and CPU cycles may be used, " + - "thus it's strongly recommend you using the externalized property '{}' " + - "to specify the services", - subscribedServices.size(), "dubbo.cloud.subscribed-services"); - } - } else { - subscribedServices = new HashSet<>(dubboCloudProperties.subscribedServices()); - } - - excludeSelf(subscribedServices); - } - - private void excludeSelf(Set subscribedServices) { - subscribedServices.remove(currentApplicationName); - } - - private void initSubscribedDubboMetadataServices() { - // clear subscribedDubboMetadataServiceURLs - subscribedDubboMetadataServiceURLs.clear(); - - subscribedServices.stream() - .map(discoveryClient::getInstances) - .filter(this::isNotEmpty) - .forEach(serviceInstances -> { - ServiceInstance serviceInstance = serviceInstances.get(0); - getDubboMetadataServiceURLs(serviceInstance).forEach(dubboMetadataServiceURL -> { - initSubscribedDubboMetadataServiceURLs(dubboMetadataServiceURL); - initDubboMetadataServiceProxy(dubboMetadataServiceURL); - }); - }); - } - - private void initSubscribedDubboMetadataServiceURLs(URL dubboMetadataServiceURL) { - // add subscriptions - String serviceKey = dubboMetadataServiceURL.getServiceKey(); - subscribedDubboMetadataServiceURLs.add(serviceKey, dubboMetadataServiceURL); - } - - private void initDubboMetadataServiceProxy(URL dubboMetadataServiceURL) { - String serviceName = dubboMetadataServiceURL.getParameter(APPLICATION_KEY); - String version = dubboMetadataServiceURL.getParameter(VERSION_KEY); - // Initialize DubboMetadataService with right version - dubboMetadataConfigServiceProxy.initProxy(serviceName, version); - } - - private void initDubboRestServiceMetadataRepository() { - subscribedServices.forEach(this::initialize); - } - - private boolean isNotEmpty(Collection collection) { - return !CollectionUtils.isEmpty(collection); - } +public class DubboServiceMetadataRepository + implements SmartInitializingSingleton, ApplicationEventPublisherAware { + + /** + * The prefix of {@link DubboMetadataService} : "dubbo.metadata-service." + */ + public static final String DUBBO_METADATA_SERVICE_PREFIX = "dubbo.metadata-service."; + + /** + * The {@link URL URLs} property name of {@link DubboMetadataService} : + * "dubbo.metadata-service.urls" + */ + public static final String DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME = DUBBO_METADATA_SERVICE_PREFIX + + "urls"; + + /** + * The {@link String#format(String, Object...) pattern} of dubbo protocols port + */ + public static final String DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN = "dubbo.protocols.%s.port"; + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + private final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * Monitor object for synchronization + */ + private final Object monitor = new Object(); + /** + * A {@link Set} of service names that had been initialized + */ + private final Set initializedServices = new LinkedHashSet<>(); + /** + * All exported {@link URL urls} {@link Map} whose key is the return value of + * {@link URL#getServiceKey()} method and value is the {@link List} of {@link URL + * URLs} + */ + private final MultiValueMap allExportedURLs = new LinkedMultiValueMap<>(); + + // =================================== Registration + // =================================== // + /** + * The subscribed {@link URL urls} {@link Map} of {@link DubboMetadataService}, whose + * key is the return value of {@link URL#getServiceKey()} method and value is the + * {@link List} of {@link URL URLs} + */ + private final MultiValueMap subscribedDubboMetadataServiceURLs = new LinkedMultiValueMap<>(); + + // ==================================================================================== + // // + + // =================================== Subscription + // =================================== // + /** + * A Map to store REST metadata temporary, its' key is the special service name for a + * Dubbo service, the value is a JSON content of JAX-RS or Spring MVC REST metadata + * from the annotated methods. + */ + private final Set serviceRestMetadata = new LinkedHashSet<>(); + private ApplicationEventPublisher applicationEventPublisher; + + // ==================================================================================== + // // + + // =================================== REST Metadata + // ================================== // + private volatile Set subscribedServices = emptySet(); + /** + * Key is application name Value is Map + */ + private Map> dubboRestServiceMetadataRepository = newHashMap(); + + // ==================================================================================== + // // + + // =================================== Dependencies + // =================================== // + + @Autowired + private DubboCloudProperties dubboCloudProperties; + + @Autowired + private DubboMetadataServiceProxy dubboMetadataConfigServiceProxy; + + @Autowired + private DiscoveryClient discoveryClient; + + @Autowired + private JSONUtils jsonUtils; + + @Autowired + private InetUtils inetUtils; + + @Value("${spring.application.name}") + private String currentApplicationName; + + @Autowired + private DubboMetadataServiceExporter dubboMetadataServiceExporter; + + // ==================================================================================== + // // + + private static Map getMap(Map> repository, + String key) { + return getOrDefault(repository, key, newHashMap()); + } + + private static V getOrDefault(Map source, K key, V defaultValue) { + V value = source.get(key); + if (value == null) { + value = defaultValue; + source.put(key, value); + } + return value; + } + + private static Map newHashMap() { + return new LinkedHashMap<>(); + } + + /** + * Initialize {@link #subscribedServices the subscribed services} + * + * @return + */ + @PostConstruct + public Stream initSubscribedServices() { + Set newSubscribedServices = new LinkedHashSet<>(); + + // If subscribes all services + if (ALL_DUBBO_SERVICES.equals(dubboCloudProperties.getSubscribedServices())) { + List services = discoveryClient.getServices(); + newSubscribedServices.addAll(services); + if (logger.isWarnEnabled()) { + logger.warn( + "Current application will subscribe all services(size:{}) in registry, " + + "a lot of memory and CPU cycles may be used, " + + "thus it's strongly recommend you using the externalized property '{}' " + + "to specify the services", + newSubscribedServices.size(), "dubbo.cloud.subscribed-services"); + } + } + else { + newSubscribedServices.addAll(dubboCloudProperties.subscribedServices()); + } + + // exclude current application name + excludeSelf(newSubscribedServices); + + // copy from subscribedServices + Set oldSubscribedServices = this.subscribedServices; + + // volatile update subscribedServices to be new one + this.subscribedServices = newSubscribedServices; + + // dispatch SubscribedServicesChangedEvent + dispatchEvent(new SubscribedServicesChangedEvent(this, oldSubscribedServices, + newSubscribedServices)); + + // clear old one, help GC + oldSubscribedServices.clear(); + + return newSubscribedServices.stream(); + } + + private void dispatchEvent(ApplicationEvent event) { + applicationEventPublisher.publishEvent(event); + } + + @Override + public void afterSingletonsInstantiated() { + initializeMetadata(); + } + + /** + * Initialize the metadata + */ + private void initializeMetadata() { + doGetSubscribedServices().forEach(this::initializeMetadata); + if (logger.isInfoEnabled()) { + logger.info("The metadata of Dubbo services has been initialized"); + } + } + + /** + * Initialize the metadata of Dubbo Services + */ + public void initializeMetadata(String serviceName) { + synchronized (monitor) { + if (initializedServices.contains(serviceName)) { + if (logger.isDebugEnabled()) { + logger.debug( + "The metadata of Dubbo service[name : {}] has been initialized", + serviceName); + } + } + else { + if (logger.isInfoEnabled()) { + logger.info( + "The metadata of Dubbo service[name : {}] is about to be initialized", + serviceName); + } + + // Keep the order in following invocations + initSubscribedDubboMetadataService(serviceName); + initDubboRestServiceMetadataRepository(serviceName); + // mark this service name having been initialized + initializedServices.add(serviceName); + } + } + } + + /** + * Remove the metadata of Dubbo Services if no there is no service instance + * @param serviceName the service name + */ + public void removeInitializedService(String serviceName) { + synchronized (monitor) { + initializedServices.remove(serviceName); + } + } + + /** + * Get the metadata {@link Map} of {@link DubboMetadataService} + * + * @return non-null read-only {@link Map} + */ + public Map getDubboMetadataServiceMetadata() { + + List dubboMetadataServiceURLs = dubboMetadataServiceExporter.export(); + + // remove the exported URLs of DubboMetadataService + removeDubboMetadataServiceURLs(dubboMetadataServiceURLs); + + Map metadata = newHashMap(); + + addDubboMetadataServiceURLsMetadata(metadata, dubboMetadataServiceURLs); + addDubboProtocolsPortMetadata(metadata); + + return Collections.unmodifiableMap(metadata); + } + + private void removeDubboMetadataServiceURLs(List dubboMetadataServiceURLs) { + dubboMetadataServiceURLs.forEach(this::unexportURL); + } + + private void addDubboMetadataServiceURLsMetadata(Map metadata, + List dubboMetadataServiceURLs) { + String dubboMetadataServiceURLsJSON = jsonUtils.toJSON(dubboMetadataServiceURLs); + metadata.put(DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME, + dubboMetadataServiceURLsJSON); + } + + private void addDubboProtocolsPortMetadata(Map metadata) { + + allExportedURLs.values().stream().flatMap(v -> v.stream()).forEach(url -> { + String protocol = url.getProtocol(); + String propertyName = getDubboProtocolPropertyName(protocol); + String propertyValue = valueOf(url.getPort()); + metadata.put(propertyName, propertyValue); + }); + } + + /** + * Get the property name of Dubbo Protocol + * + * @param protocol Dubbo Protocol + * @return non-null + */ + public String getDubboProtocolPropertyName(String protocol) { + return format(DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN, protocol); + } + + /** + * Publish the {@link Set} of {@link ServiceRestMetadata} + * + * @param serviceRestMetadataSet the {@link Set} of {@link ServiceRestMetadata} + */ + public void publishServiceRestMetadata( + Set serviceRestMetadataSet) { + for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) { + if (!isEmpty(serviceRestMetadata.getMeta())) { + this.serviceRestMetadata.add(serviceRestMetadata); + } + } + } + + /** + * Get the {@link Set} of {@link ServiceRestMetadata} + * + * @return non-null read-only {@link Set} + */ + public Set getServiceRestMetadata() { + return unmodifiableSet(serviceRestMetadata); + } + + public List findSubscribedDubboMetadataServiceURLs(String serviceName, + String group, String version, String protocol) { + String serviceKey = URL.buildKey(serviceName, group, version); + + List urls = null; + + synchronized (monitor) { + urls = subscribedDubboMetadataServiceURLs.get(serviceKey); + } + + if (isEmpty(urls)) { + return emptyList(); + } + + return hasText(protocol) + ? urls.stream() + .filter(url -> url.getProtocol().equalsIgnoreCase(protocol)) + .collect(Collectors.toList()) + : unmodifiableList(urls); + } + + /** + * The specified service is subscribe or not + * + * @param serviceName the service name + * @return + */ + public boolean isSubscribedService(String serviceName) { + return doGetSubscribedServices().contains(serviceName); + } + + public void exportURL(URL url) { + URL actualURL = url; + InetUtils.HostInfo hostInfo = inetUtils.findFirstNonLoopbackHostInfo(); + String ipAddress = hostInfo.getIpAddress(); + // To use InetUtils to set IP if they are different + // issue : + // https://github.com/spring-cloud-incubator/spring-cloud-alibaba/issues/589 + if (!Objects.equals(url.getHost(), ipAddress)) { + actualURL = url.setHost(ipAddress); + } + this.allExportedURLs.add(actualURL.getServiceKey(), actualURL); + } + + public void unexportURL(URL url) { + String key = url.getServiceKey(); + // NPE issue : + // https://github.com/spring-cloud-incubator/spring-cloud-alibaba/issues/591 + List urls = allExportedURLs.get(key); + if (!isEmpty(urls)) { + urls.remove(url); + allExportedURLs.addAll(key, urls); + } + } + + /** + * Get all exported {@link URL urls}. + * + * @return non-null read-only + */ + public Map> getAllExportedUrls() { + return unmodifiableMap(allExportedURLs); + } + + /** + * Get all exported {@link URL#getServiceKey() service keys} + * + * @return non-null read-only + */ + public Set getAllServiceKeys() { + return allExportedURLs.keySet(); + } + + /** + * Get the {@link URL urls} that {@link DubboMetadataService} exported by the + * specified {@link ServiceInstance} + * + * @param serviceInstance {@link ServiceInstance} + * @return the mutable {@link URL urls} + */ + public List getDubboMetadataServiceURLs(ServiceInstance serviceInstance) { + Map metadata = serviceInstance.getMetadata(); + String dubboURLsJSON = metadata.get(DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME); + return jsonUtils.toURLs(dubboURLsJSON); + } + + public Integer getDubboProtocolPort(ServiceInstance serviceInstance, + String protocol) { + String protocolProperty = getDubboProtocolPropertyName(protocol); + Map metadata = serviceInstance.getMetadata(); + String protocolPort = metadata.get(protocolProperty); + return hasText(protocolPort) ? Integer.valueOf(protocolPort) : null; + } + + public List getExportedURLs(String serviceInterface, String group, + String version) { + String serviceKey = URL.buildKey(serviceInterface, group, version); + return allExportedURLs.getOrDefault(serviceKey, Collections.emptyList()); + } + + /** + * Initialize the specified service's {@link ServiceRestMetadata} + * + * @param serviceName the service name + */ + protected void initDubboRestServiceMetadataRepository(String serviceName) { + + if (dubboRestServiceMetadataRepository.containsKey(serviceName)) { + return; + } + + Set serviceRestMetadataSet = getServiceRestMetadataSet( + serviceName); + + if (isEmpty(serviceRestMetadataSet)) { + if (logger.isWarnEnabled()) { + logger.warn( + "The Spring application[name : {}] does not expose The REST metadata in the Dubbo services.", + serviceName); + } + return; + } + + Map metadataMap = getMetadataMap( + serviceName); + + for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) { + + serviceRestMetadata.getMeta().forEach(restMethodMetadata -> { + RequestMetadata requestMetadata = restMethodMetadata.getRequest(); + RequestMetadataMatcher matcher = new RequestMetadataMatcher( + requestMetadata); + DubboRestServiceMetadata metadata = new DubboRestServiceMetadata( + serviceRestMetadata, restMethodMetadata); + metadataMap.put(matcher, metadata); + }); + } + + if (logger.isInfoEnabled()) { + logger.info( + "The REST metadata in the dubbo services has been loaded in the Spring application[name : {}]", + serviceName); + } + } + + /** + * 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 DubboRestServiceMetadata} if matched, or null + */ + public DubboRestServiceMetadata get(String serviceName, + RequestMetadata requestMetadata) { + return match(dubboRestServiceMetadataRepository, serviceName, requestMetadata); + } + + /** + * @return not-null + */ + protected Set doGetSubscribedServices() { + Set subscribedServices = this.subscribedServices; + return subscribedServices == null ? emptySet() : subscribedServices; + } + + public Set getSubscribedServices() { + return unmodifiableSet(doGetSubscribedServices()); + } + + private T match(Map> repository, + String serviceName, RequestMetadata requestMetadata) { + + Map map = repository.get(serviceName); + + T object = null; + + if (!isEmpty(map)) { + RequestMetadataMatcher matcher = new RequestMetadataMatcher(requestMetadata); + object = map.get(matcher); + if (object == null) { // Can't match exactly + // Require to match one by one + HttpRequest request = builder().method(requestMetadata.getMethod()) + .path(requestMetadata.getPath()) + .params(requestMetadata.getParams()) + .headers(requestMetadata.getHeaders()).build(); + + for (Map.Entry entry : map.entrySet()) { + RequestMetadataMatcher possibleMatcher = entry.getKey(); + if (possibleMatcher.match(request)) { + object = entry.getValue(); + break; + } + } + } + } + + if (object == null) { + if (logger.isWarnEnabled()) { + logger.warn( + "DubboServiceMetadata can't be found in the Spring application [{}] and {}", + serviceName, requestMetadata); + } + } + + return object; + } + + private Map getMetadataMap( + String serviceName) { + return getMap(dubboRestServiceMetadataRepository, serviceName); + } + + private Set getServiceRestMetadataSet(String serviceName) { + + Set metadata = emptySet(); + + DubboMetadataService dubboMetadataService = dubboMetadataConfigServiceProxy + .getProxy(serviceName); + + if (dubboMetadataService != null) { + try { + String serviceRestMetadataJsonConfig = dubboMetadataService + .getServiceRestMetadata(); + if (hasText(serviceRestMetadataJsonConfig)) { + metadata = objectMapper.readValue(serviceRestMetadataJsonConfig, + TypeFactory.defaultInstance().constructCollectionType( + LinkedHashSet.class, ServiceRestMetadata.class)); + } + } + catch (Exception e) { + if (logger.isErrorEnabled()) { + logger.error(e.getMessage(), e); + } + } + } + return metadata; + } + + private void excludeSelf(Set subscribedServices) { + subscribedServices.remove(currentApplicationName); + } + + protected void initSubscribedDubboMetadataService(String serviceName) { + discoveryClient.getInstances(serviceName).stream().findAny() + .map(this::getDubboMetadataServiceURLs) + .ifPresent(dubboMetadataServiceURLs -> { + dubboMetadataServiceURLs.forEach(dubboMetadataServiceURL -> { + try { + initSubscribedDubboMetadataServiceURL( + dubboMetadataServiceURL); + initDubboMetadataServiceProxy(dubboMetadataServiceURL); + } + catch (Throwable e) { + if (logger.isErrorEnabled()) { + logger.error(e.getMessage(), e); + } + } + }); + }); + } + + private void initSubscribedDubboMetadataServiceURL(URL dubboMetadataServiceURL) { + // add subscriptions + String serviceKey = dubboMetadataServiceURL.getServiceKey(); + subscribedDubboMetadataServiceURLs.add(serviceKey, dubboMetadataServiceURL); + } + + private void initDubboMetadataServiceProxy(URL dubboMetadataServiceURL) { + String serviceName = dubboMetadataServiceURL.getParameter(APPLICATION_KEY); + String version = dubboMetadataServiceURL.getParameter(VERSION_KEY); + // Initialize DubboMetadataService with right version + dubboMetadataConfigServiceProxy.initProxy(serviceName, version); + } + + public void removeServiceMetadata(String serviceName) { + dubboRestServiceMetadataRepository.remove(serviceName); + } + + @Override + public void setApplicationEventPublisher( + ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboServiceBeanMetadataResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboServiceBeanMetadataResolver.java index 9bf740c1..ce5c0d30 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboServiceBeanMetadataResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboServiceBeanMetadataResolver.java @@ -16,20 +16,6 @@ */ package com.alibaba.cloud.dubbo.metadata.resolver; -import feign.Contract; -import feign.Feign; -import feign.MethodMetadata; -import feign.Util; -import org.apache.dubbo.common.URL; -import org.apache.dubbo.config.spring.ServiceBean; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.SmartInitializingSingleton; -import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; -import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata; -import org.springframework.util.ClassUtils; - import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; @@ -41,155 +27,179 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.config.spring.ServiceBean; + +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.util.ClassUtils; + +import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; +import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata; + +import feign.Contract; +import feign.Feign; +import feign.MethodMetadata; +import feign.Util; + /** - * The metadata resolver for {@link Feign} for {@link ServiceBean Dubbo Service Bean} in the provider side. + * The metadata resolver for {@link Feign} for {@link ServiceBean Dubbo Service Bean} in + * the provider side. * * @author Mercy */ -public class DubboServiceBeanMetadataResolver implements BeanClassLoaderAware, SmartInitializingSingleton, - MetadataResolver { +public class DubboServiceBeanMetadataResolver + implements BeanClassLoaderAware, SmartInitializingSingleton, MetadataResolver { - private static final String[] CONTRACT_CLASS_NAMES = { - "feign.jaxrs2.JAXRS2Contract", - "org.springframework.cloud.openfeign.support.SpringMvcContract", - }; + private static final String[] CONTRACT_CLASS_NAMES = { "feign.jaxrs2.JAXRS2Contract", + "org.springframework.cloud.openfeign.support.SpringMvcContract", }; - private final ObjectProvider contractObjectProvider; + private final ObjectProvider contractObjectProvider; - private ClassLoader classLoader; + private ClassLoader classLoader; - /** - * Feign Contracts - */ - private Collection contracts; + /** + * Feign Contracts + */ + private Collection contracts; - public DubboServiceBeanMetadataResolver(ObjectProvider contractObjectProvider) { - this.contractObjectProvider = contractObjectProvider; - } + public DubboServiceBeanMetadataResolver( + ObjectProvider contractObjectProvider) { + this.contractObjectProvider = contractObjectProvider; + } - @Override - public void afterSingletonsInstantiated() { + @Override + public void afterSingletonsInstantiated() { - LinkedList contracts = new LinkedList<>(); + LinkedList contracts = new LinkedList<>(); - // Add injected Contract if available, for example SpringMvcContract Bean under Spring Cloud Open Feign - Contract contract = contractObjectProvider.getIfAvailable(); + // Add injected Contract if available, for example SpringMvcContract Bean under + // Spring Cloud Open Feign + Contract contract = contractObjectProvider.getIfAvailable(); - if (contract != null) { - contracts.add(contract); - } + if (contract != null) { + contracts.add(contract); + } - Stream.of(CONTRACT_CLASS_NAMES) - .filter(this::isClassPresent) // filter the existed classes - .map(this::loadContractClass) // load Contract Class - .map(this::createContract) // createServiceInstance instance by the specified class - .forEach(contracts::add); // add the Contract instance into contracts + Stream.of(CONTRACT_CLASS_NAMES).filter(this::isClassPresent) // filter the existed + // classes + .map(this::loadContractClass) // load Contract Class + .map(this::createContract) // createServiceInstance instance by the + // specified class + .forEach(contracts::add); // add the Contract instance into contracts - this.contracts = Collections.unmodifiableCollection(contracts); - } + this.contracts = Collections.unmodifiableCollection(contracts); + } - private Contract createContract(Class contractClassName) { - return (Contract) BeanUtils.instantiateClass(contractClassName); - } + private Contract createContract(Class contractClassName) { + return (Contract) BeanUtils.instantiateClass(contractClassName); + } - private Class loadContractClass(String contractClassName) { - return ClassUtils.resolveClassName(contractClassName, classLoader); - } + private Class loadContractClass(String contractClassName) { + return ClassUtils.resolveClassName(contractClassName, classLoader); + } - private boolean isClassPresent(String className) { - return ClassUtils.isPresent(className, classLoader); - } + private boolean isClassPresent(String className) { + return ClassUtils.isPresent(className, classLoader); + } - @Override - public Set resolveServiceRestMetadata(ServiceBean serviceBean) { + @Override + public Set resolveServiceRestMetadata(ServiceBean serviceBean) { - Object bean = serviceBean.getRef(); + Object bean = serviceBean.getRef(); - Class beanType = bean.getClass(); + Class beanType = bean.getClass(); - Set serviceRestMetadata = new LinkedHashSet<>(); + Set serviceRestMetadata = new LinkedHashSet<>(); - Set methodRestMetadata = resolveMethodRestMetadata(beanType); + Set methodRestMetadata = resolveMethodRestMetadata(beanType); - List urls = serviceBean.getExportedUrls(); + List urls = serviceBean.getExportedUrls(); - urls.stream() - .map(URL::toString) - .forEach(url -> { - ServiceRestMetadata metadata = new ServiceRestMetadata(); - metadata.setUrl(url); - metadata.setMeta(methodRestMetadata); - serviceRestMetadata.add(metadata); - }); + urls.stream().map(URL::toString).forEach(url -> { + ServiceRestMetadata metadata = new ServiceRestMetadata(); + metadata.setUrl(url); + metadata.setMeta(methodRestMetadata); + serviceRestMetadata.add(metadata); + }); - return serviceRestMetadata; - } + return serviceRestMetadata; + } - @Override - public Set resolveMethodRestMetadata(Class targetType) { - List feignContractMethods = selectFeignContractMethods(targetType); - return contracts.stream() - .map(contract -> parseAndValidateMetadata(contract, targetType)) - .flatMap(v -> v.stream()) - .map(methodMetadata -> resolveMethodRestMetadata(methodMetadata, targetType, feignContractMethods)) - .collect(Collectors.toSet()); - } + @Override + public Set resolveMethodRestMetadata(Class targetType) { + List feignContractMethods = selectFeignContractMethods(targetType); + return contracts.stream() + .map(contract -> parseAndValidateMetadata(contract, targetType)) + .flatMap(v -> v.stream()) + .map(methodMetadata -> resolveMethodRestMetadata(methodMetadata, + targetType, feignContractMethods)) + .collect(Collectors.toSet()); + } - private List parseAndValidateMetadata(Contract contract, Class targetType) { - List methodMetadataList = Collections.emptyList(); - try { - methodMetadataList = contract.parseAndValidatateMetadata(targetType); - } catch (Throwable ignored) { - // ignore - } - return methodMetadataList; - } + private List parseAndValidateMetadata(Contract contract, + Class targetType) { + List methodMetadataList = Collections.emptyList(); + try { + methodMetadataList = contract.parseAndValidatateMetadata(targetType); + } + catch (Throwable ignored) { + // ignore + } + return methodMetadataList; + } - /** - * Select feign contract methods - *

- * extract some code from {@link Contract.BaseContract#parseAndValidatateMetadata(java.lang.Class)} - * - * @param targetType - * @return non-null - */ - private List selectFeignContractMethods(Class targetType) { - List methods = new LinkedList<>(); - for (Method method : targetType.getMethods()) { - if (method.getDeclaringClass() == Object.class || - (method.getModifiers() & Modifier.STATIC) != 0 || - Util.isDefault(method)) { - continue; - } - methods.add(method); - } - return methods; - } + /** + * Select feign contract methods + *

+ * extract some code from + * {@link Contract.BaseContract#parseAndValidatateMetadata(java.lang.Class)} + * + * @param targetType + * @return non-null + */ + private List selectFeignContractMethods(Class targetType) { + List methods = new LinkedList<>(); + for (Method method : targetType.getMethods()) { + if (method.getDeclaringClass() == Object.class + || (method.getModifiers() & Modifier.STATIC) != 0 + || Util.isDefault(method)) { + continue; + } + methods.add(method); + } + return methods; + } - protected RestMethodMetadata resolveMethodRestMetadata(MethodMetadata methodMetadata, Class targetType, - List feignContractMethods) { - String configKey = methodMetadata.configKey(); - Method feignContractMethod = getMatchedFeignContractMethod(targetType, feignContractMethods, configKey); - RestMethodMetadata metadata = new RestMethodMetadata(methodMetadata); - metadata.setMethod(new com.alibaba.cloud.dubbo.metadata.MethodMetadata(feignContractMethod)); - return metadata; - } + protected RestMethodMetadata resolveMethodRestMetadata(MethodMetadata methodMetadata, + Class targetType, List feignContractMethods) { + String configKey = methodMetadata.configKey(); + Method feignContractMethod = getMatchedFeignContractMethod(targetType, + feignContractMethods, configKey); + RestMethodMetadata metadata = new RestMethodMetadata(methodMetadata); + metadata.setMethod( + new com.alibaba.cloud.dubbo.metadata.MethodMetadata(feignContractMethod)); + return metadata; + } - private Method getMatchedFeignContractMethod(Class targetType, List methods, String expectedConfigKey) { - Method matchedMethod = null; - for (Method method : methods) { - String configKey = Feign.configKey(targetType, method); - if (expectedConfigKey.equals(configKey)) { - matchedMethod = method; - break; - } - } - return matchedMethod; - } + private Method getMatchedFeignContractMethod(Class targetType, + List methods, String expectedConfigKey) { + Method matchedMethod = null; + for (Method method : methods) { + String configKey = Feign.configKey(targetType, method); + if (expectedConfigKey.equals(configKey)) { + matchedMethod = method; + break; + } + } + return matchedMethod; + } - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboTransportedAttributesResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboTransportedAttributesResolver.java index 21357661..aafd94ca 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboTransportedAttributesResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboTransportedAttributesResolver.java @@ -16,13 +16,14 @@ */ package com.alibaba.cloud.dubbo.metadata.resolver; -import com.alibaba.cloud.dubbo.annotation.DubboTransported; -import org.springframework.core.env.PropertyResolver; +import static org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes; import java.util.LinkedHashMap; import java.util.Map; -import static org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes; +import org.springframework.core.env.PropertyResolver; + +import com.alibaba.cloud.dubbo.annotation.DubboTransported; /** * {@link DubboTransported} annotation attributes resolver @@ -31,26 +32,26 @@ import static org.springframework.core.annotation.AnnotationUtils.getAnnotationA */ public class DubboTransportedAttributesResolver { - private final PropertyResolver propertyResolver; + private final PropertyResolver propertyResolver; - public DubboTransportedAttributesResolver(PropertyResolver propertyResolver) { - this.propertyResolver = propertyResolver; - } + public DubboTransportedAttributesResolver(PropertyResolver propertyResolver) { + this.propertyResolver = propertyResolver; + } - public Map resolve(DubboTransported dubboTransported) { - Map attributes = getAnnotationAttributes(dubboTransported); - return resolve(attributes); - } + public Map resolve(DubboTransported dubboTransported) { + Map attributes = getAnnotationAttributes(dubboTransported); + return resolve(attributes); + } - public Map resolve(Map attributes) { - Map resolvedAttributes = new LinkedHashMap<>(); - for (Map.Entry entry : attributes.entrySet()) { - Object value = entry.getValue(); - if (value instanceof String) { - value = propertyResolver.resolvePlaceholders(value.toString()); - } - resolvedAttributes.put(entry.getKey(), value); - } - return resolvedAttributes; - } + public Map resolve(Map attributes) { + Map resolvedAttributes = new LinkedHashMap<>(); + for (Map.Entry entry : attributes.entrySet()) { + Object value = entry.getValue(); + if (value instanceof String) { + value = propertyResolver.resolvePlaceholders(value.toString()); + } + resolvedAttributes.put(entry.getKey(), value); + } + return resolvedAttributes; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java index 1c09c0bc..8ef05379 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java @@ -16,13 +16,7 @@ */ package com.alibaba.cloud.dubbo.metadata.resolver; -import feign.Contract; -import com.alibaba.cloud.dubbo.annotation.DubboTransported; -import com.alibaba.cloud.dubbo.metadata.DubboTransportedMethodMetadata; -import com.alibaba.cloud.dubbo.metadata.MethodMetadata; -import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.env.PropertyResolver; +import static feign.Feign.configKey; import java.lang.reflect.Method; import java.util.LinkedHashSet; @@ -30,79 +24,96 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import static feign.Feign.configKey; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.env.PropertyResolver; + +import com.alibaba.cloud.dubbo.annotation.DubboTransported; +import com.alibaba.cloud.dubbo.metadata.DubboTransportedMethodMetadata; +import com.alibaba.cloud.dubbo.metadata.MethodMetadata; +import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; + +import feign.Contract; /** - * {@link MethodMetadata} Resolver for the {@link DubboTransported} annotated classes or methods in client side. + * {@link MethodMetadata} Resolver for the {@link DubboTransported} annotated classes or + * methods in client side. * * @author Mercy * @see DubboTransportedMethodMetadata */ public class DubboTransportedMethodMetadataResolver { - private static final Class DUBBO_TRANSPORTED_CLASS = DubboTransported.class; + private static final Class DUBBO_TRANSPORTED_CLASS = DubboTransported.class; - private final DubboTransportedAttributesResolver attributesResolver; + private final DubboTransportedAttributesResolver attributesResolver; - private final Contract contract; + private final Contract contract; - public DubboTransportedMethodMetadataResolver(PropertyResolver propertyResolver, Contract contract) { - this.attributesResolver = new DubboTransportedAttributesResolver(propertyResolver); - this.contract = contract; - } + public DubboTransportedMethodMetadataResolver(PropertyResolver propertyResolver, + Contract contract) { + this.attributesResolver = new DubboTransportedAttributesResolver( + propertyResolver); + this.contract = contract; + } - public Map resolve(Class targetType) { - Set dubboTransportedMethodMetadataSet = - resolveDubboTransportedMethodMetadataSet(targetType); - Map restMethodMetadataMap = resolveRestRequestMetadataMap(targetType); - return dubboTransportedMethodMetadataSet - .stream() - .collect(Collectors.toMap(methodMetadata -> methodMetadata, methodMetadata -> { - RestMethodMetadata restMethodMetadata = restMethodMetadataMap.get(configKey(targetType, methodMetadata.getMethod())); - restMethodMetadata.setMethod(methodMetadata.getMethodMetadata()); - return restMethodMetadata; - } - )); - } + public Map resolve( + Class targetType) { + Set dubboTransportedMethodMetadataSet = resolveDubboTransportedMethodMetadataSet( + targetType); + Map restMethodMetadataMap = resolveRestRequestMetadataMap( + targetType); + return dubboTransportedMethodMetadataSet.stream().collect( + Collectors.toMap(methodMetadata -> methodMetadata, methodMetadata -> { + RestMethodMetadata restMethodMetadata = restMethodMetadataMap + .get(configKey(targetType, methodMetadata.getMethod())); + restMethodMetadata.setMethod(methodMetadata.getMethodMetadata()); + return restMethodMetadata; + })); + } - protected Set resolveDubboTransportedMethodMetadataSet(Class targetType) { - // The public methods of target interface - Method[] methods = targetType.getMethods(); + protected Set resolveDubboTransportedMethodMetadataSet( + Class targetType) { + // The public methods of target interface + Method[] methods = targetType.getMethods(); - Set methodMetadataSet = new LinkedHashSet<>(); + Set methodMetadataSet = new LinkedHashSet<>(); - for (Method method : methods) { - DubboTransported dubboTransported = resolveDubboTransported(method); - if (dubboTransported != null) { - DubboTransportedMethodMetadata methodMetadata = createDubboTransportedMethodMetadata(method, dubboTransported); - methodMetadataSet.add(methodMetadata); - } - } - return methodMetadataSet; - } + for (Method method : methods) { + DubboTransported dubboTransported = resolveDubboTransported(method); + if (dubboTransported != null) { + DubboTransportedMethodMetadata methodMetadata = createDubboTransportedMethodMetadata( + method, dubboTransported); + methodMetadataSet.add(methodMetadata); + } + } + return methodMetadataSet; + } + private Map resolveRestRequestMetadataMap( + Class targetType) { + return contract.parseAndValidatateMetadata(targetType).stream().collect(Collectors + .toMap(feign.MethodMetadata::configKey, this::restMethodMetadata)); + } - private Map resolveRestRequestMetadataMap(Class targetType) { - return contract.parseAndValidatateMetadata(targetType) - .stream().collect(Collectors.toMap(feign.MethodMetadata::configKey, this::restMethodMetadata)); - } + private RestMethodMetadata restMethodMetadata(feign.MethodMetadata methodMetadata) { + return new RestMethodMetadata(methodMetadata); + } - private RestMethodMetadata restMethodMetadata(feign.MethodMetadata methodMetadata) { - return new RestMethodMetadata(methodMetadata); - } + private DubboTransportedMethodMetadata createDubboTransportedMethodMetadata( + Method method, DubboTransported dubboTransported) { + Map attributes = attributesResolver.resolve(dubboTransported); + return new DubboTransportedMethodMetadata(method, attributes); + } - private DubboTransportedMethodMetadata createDubboTransportedMethodMetadata(Method method, - DubboTransported dubboTransported) { - Map attributes = attributesResolver.resolve(dubboTransported); - return new DubboTransportedMethodMetadata(method, attributes); - } - - private DubboTransported resolveDubboTransported(Method method) { - DubboTransported dubboTransported = AnnotationUtils.findAnnotation(method, DUBBO_TRANSPORTED_CLASS); - if (dubboTransported == null) { // Attempt to find @DubboTransported in the declaring class - Class declaringClass = method.getDeclaringClass(); - dubboTransported = AnnotationUtils.findAnnotation(declaringClass, DUBBO_TRANSPORTED_CLASS); - } - return dubboTransported; - } + private DubboTransported resolveDubboTransported(Method method) { + DubboTransported dubboTransported = AnnotationUtils.findAnnotation(method, + DUBBO_TRANSPORTED_CLASS); + if (dubboTransported == null) { // Attempt to find @DubboTransported in the + // declaring class + Class declaringClass = method.getDeclaringClass(); + dubboTransported = AnnotationUtils.findAnnotation(declaringClass, + DUBBO_TRANSPORTED_CLASS); + } + return dubboTransported; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/MetadataResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/MetadataResolver.java index 52917c5d..3938d8b8 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/MetadataResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/metadata/resolver/MetadataResolver.java @@ -16,12 +16,13 @@ */ package com.alibaba.cloud.dubbo.metadata.resolver; +import java.util.Set; + import org.apache.dubbo.config.spring.ServiceBean; + import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata; -import java.util.Set; - /** * The REST metadata resolver * @@ -29,19 +30,19 @@ import java.util.Set; */ public interface MetadataResolver { - /** - * Resolve the {@link ServiceRestMetadata} {@link Set set} from {@link ServiceBean} - * - * @param serviceBean {@link ServiceBean} - * @return non-null {@link Set} - */ - Set resolveServiceRestMetadata(ServiceBean serviceBean); + /** + * Resolve the {@link ServiceRestMetadata} {@link Set set} from {@link ServiceBean} + * + * @param serviceBean {@link ServiceBean} + * @return non-null {@link Set} + */ + Set resolveServiceRestMetadata(ServiceBean serviceBean); - /** - * Resolve {@link RestMethodMetadata} {@link Set set} from {@link Class target type} - * - * @param targetType {@link Class target type} - * @return non-null {@link Set} - */ - Set resolveMethodRestMetadata(Class targetType); + /** + * Resolve {@link RestMethodMetadata} {@link Set set} from {@link Class target type} + * + * @param targetType {@link Class target type} + * @return non-null {@link Set} + */ + Set resolveMethodRestMetadata(Class targetType); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/DubboInvocationHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/DubboInvocationHandler.java index 6bd21d06..3242dc85 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/DubboInvocationHandler.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/DubboInvocationHandler.java @@ -16,18 +16,19 @@ */ package com.alibaba.cloud.dubbo.openfeign; -import org.apache.dubbo.rpc.service.GenericService; - -import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContext; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; -import org.springframework.util.ClassUtils; +import static org.apache.dubbo.common.utils.PojoUtils.realize; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Map; -import static org.apache.dubbo.common.utils.PojoUtils.realize; +import org.apache.dubbo.rpc.service.GenericService; + +import org.springframework.util.ClassUtils; + +import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContext; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; /** * Dubbo {@link GenericService} for {@link InvocationHandler} @@ -36,52 +37,56 @@ import static org.apache.dubbo.common.utils.PojoUtils.realize; */ public class DubboInvocationHandler implements InvocationHandler { - private final Map feignMethodMetadataMap; + private final Map feignMethodMetadataMap; - private final InvocationHandler defaultInvocationHandler; + private final InvocationHandler defaultInvocationHandler; - private final DubboGenericServiceExecutionContextFactory contextFactory; + private final DubboGenericServiceExecutionContextFactory contextFactory; - private final ClassLoader classLoader; + private final ClassLoader classLoader; - public DubboInvocationHandler(Map feignMethodMetadataMap, - InvocationHandler defaultInvocationHandler, - ClassLoader classLoader, - DubboGenericServiceExecutionContextFactory contextFactory) { - this.feignMethodMetadataMap = feignMethodMetadataMap; - this.defaultInvocationHandler = defaultInvocationHandler; - this.classLoader = classLoader; - this.contextFactory = contextFactory; - } + public DubboInvocationHandler(Map feignMethodMetadataMap, + InvocationHandler defaultInvocationHandler, ClassLoader classLoader, + DubboGenericServiceExecutionContextFactory contextFactory) { + this.feignMethodMetadataMap = feignMethodMetadataMap; + this.defaultInvocationHandler = defaultInvocationHandler; + this.classLoader = classLoader; + this.contextFactory = contextFactory; + } - @Override - public Object invoke(Object proxy, Method feignMethod, Object[] args) throws Throwable { + @Override + public Object invoke(Object proxy, Method feignMethod, Object[] args) + throws Throwable { - FeignMethodMetadata feignMethodMetadata = feignMethodMetadataMap.get(feignMethod); + FeignMethodMetadata feignMethodMetadata = feignMethodMetadataMap.get(feignMethod); - if (feignMethodMetadata == null) { - return defaultInvocationHandler.invoke(proxy, feignMethod, args); - } + if (feignMethodMetadata == null) { + return defaultInvocationHandler.invoke(proxy, feignMethod, args); + } - GenericService dubboGenericService = feignMethodMetadata.getDubboGenericService(); - RestMethodMetadata dubboRestMethodMetadata = feignMethodMetadata.getDubboRestMethodMetadata(); - RestMethodMetadata feignRestMethodMetadata = feignMethodMetadata.getFeignMethodMetadata(); + GenericService dubboGenericService = feignMethodMetadata.getDubboGenericService(); + RestMethodMetadata dubboRestMethodMetadata = feignMethodMetadata + .getDubboRestMethodMetadata(); + RestMethodMetadata feignRestMethodMetadata = feignMethodMetadata + .getFeignMethodMetadata(); - DubboGenericServiceExecutionContext context = contextFactory.create(dubboRestMethodMetadata, feignRestMethodMetadata, args); + DubboGenericServiceExecutionContext context = contextFactory + .create(dubboRestMethodMetadata, feignRestMethodMetadata, args); - String methodName = context.getMethodName(); - String[] parameterTypes = context.getParameterTypes(); - Object[] parameters = context.getParameters(); + String methodName = context.getMethodName(); + String[] parameterTypes = context.getParameterTypes(); + Object[] parameters = context.getParameters(); - Object result = dubboGenericService.$invoke(methodName, parameterTypes, parameters); + Object result = dubboGenericService.$invoke(methodName, parameterTypes, + parameters); - Class returnType = getReturnType(dubboRestMethodMetadata); + Class returnType = getReturnType(dubboRestMethodMetadata); - return realize(result, returnType); - } + return realize(result, returnType); + } - private Class getReturnType(RestMethodMetadata dubboRestMethodMetadata) { - String returnType = dubboRestMethodMetadata.getReturnType(); - return ClassUtils.resolveClassName(returnType, classLoader); - } + private Class getReturnType(RestMethodMetadata dubboRestMethodMetadata) { + String returnType = dubboRestMethodMetadata.getReturnType(); + return ClassUtils.resolveClassName(returnType, classLoader); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/FeignMethodMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/FeignMethodMetadata.java index 3c11c914..e2fb23bf 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/FeignMethodMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/FeignMethodMetadata.java @@ -16,11 +16,12 @@ */ package com.alibaba.cloud.dubbo.openfeign; -import org.apache.dubbo.rpc.service.GenericService; -import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; - import java.lang.reflect.Method; +import org.apache.dubbo.rpc.service.GenericService; + +import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; + /** * Feign {@link Method} Metadata * @@ -28,29 +29,29 @@ import java.lang.reflect.Method; */ class FeignMethodMetadata { - private final GenericService dubboGenericService; + private final GenericService dubboGenericService; - private final RestMethodMetadata dubboRestMethodMetadata; + private final RestMethodMetadata dubboRestMethodMetadata; - private final RestMethodMetadata feignMethodMetadata; + private final RestMethodMetadata feignMethodMetadata; + FeignMethodMetadata(GenericService dubboGenericService, + RestMethodMetadata dubboRestMethodMetadata, + RestMethodMetadata feignMethodMetadata) { + this.dubboGenericService = dubboGenericService; + this.dubboRestMethodMetadata = dubboRestMethodMetadata; + this.feignMethodMetadata = feignMethodMetadata; + } - FeignMethodMetadata(GenericService dubboGenericService, RestMethodMetadata dubboRestMethodMetadata, - RestMethodMetadata feignMethodMetadata) { - this.dubboGenericService = dubboGenericService; - this.dubboRestMethodMetadata = dubboRestMethodMetadata; - this.feignMethodMetadata = feignMethodMetadata; - } + GenericService getDubboGenericService() { + return dubboGenericService; + } - GenericService getDubboGenericService() { - return dubboGenericService; - } + RestMethodMetadata getDubboRestMethodMetadata() { + return dubboRestMethodMetadata; + } - RestMethodMetadata getDubboRestMethodMetadata() { - return dubboRestMethodMetadata; - } - - RestMethodMetadata getFeignMethodMetadata() { - return feignMethodMetadata; - } + RestMethodMetadata getFeignMethodMetadata() { + return feignMethodMetadata; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/TargeterBeanPostProcessor.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/TargeterBeanPostProcessor.java index 289ffb33..e1d0c9d9 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/TargeterBeanPostProcessor.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/TargeterBeanPostProcessor.java @@ -16,70 +16,73 @@ */ package com.alibaba.cloud.dubbo.openfeign; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; +import static com.alibaba.cloud.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration.TARGETER_CLASS_NAME; +import static java.lang.reflect.Proxy.newProxyInstance; +import static org.springframework.util.ClassUtils.getUserClass; +import static org.springframework.util.ClassUtils.isPresent; +import static org.springframework.util.ClassUtils.resolveClassName; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanClassLoaderAware; import org.springframework.beans.factory.config.BeanPostProcessor; -import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; - import org.springframework.core.env.Environment; -import static java.lang.reflect.Proxy.newProxyInstance; -import static com.alibaba.cloud.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration.TARGETER_CLASS_NAME; -import static org.springframework.util.ClassUtils.getUserClass; -import static org.springframework.util.ClassUtils.isPresent; -import static org.springframework.util.ClassUtils.resolveClassName; +import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; /** * org.springframework.cloud.openfeign.Targeter {@link BeanPostProcessor} * * @author Mercy */ -public class TargeterBeanPostProcessor implements BeanPostProcessor, BeanClassLoaderAware { +public class TargeterBeanPostProcessor + implements BeanPostProcessor, BeanClassLoaderAware { - private final Environment environment; + private final Environment environment; - private final DubboServiceMetadataRepository dubboServiceMetadataRepository; + private final DubboServiceMetadataRepository dubboServiceMetadataRepository; - private final DubboGenericServiceFactory dubboGenericServiceFactory; + private final DubboGenericServiceFactory dubboGenericServiceFactory; - private final DubboGenericServiceExecutionContextFactory contextFactory; + private final DubboGenericServiceExecutionContextFactory contextFactory; - private ClassLoader classLoader; + private ClassLoader classLoader; - public TargeterBeanPostProcessor(Environment environment, - DubboServiceMetadataRepository dubboServiceMetadataRepository, - DubboGenericServiceFactory dubboGenericServiceFactory, - DubboGenericServiceExecutionContextFactory contextFactory) { - this.environment = environment; - this.dubboServiceMetadataRepository = dubboServiceMetadataRepository; - this.dubboGenericServiceFactory = dubboGenericServiceFactory; - this.contextFactory = contextFactory; - } + public TargeterBeanPostProcessor(Environment environment, + DubboServiceMetadataRepository dubboServiceMetadataRepository, + DubboGenericServiceFactory dubboGenericServiceFactory, + DubboGenericServiceExecutionContextFactory contextFactory) { + this.environment = environment; + this.dubboServiceMetadataRepository = dubboServiceMetadataRepository; + this.dubboGenericServiceFactory = dubboGenericServiceFactory; + this.contextFactory = contextFactory; + } - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - return bean; - } + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + return bean; + } - @Override - public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException { - if (isPresent(TARGETER_CLASS_NAME, classLoader)) { - Class beanClass = getUserClass(bean.getClass()); - Class targetClass = resolveClassName(TARGETER_CLASS_NAME, classLoader); - if (targetClass.isAssignableFrom(beanClass)) { - return newProxyInstance(classLoader, new Class[]{targetClass}, - new TargeterInvocationHandler(bean, environment, classLoader, dubboServiceMetadataRepository, - dubboGenericServiceFactory, contextFactory)); - } - } - return bean; - } + @Override + public Object postProcessAfterInitialization(final Object bean, String beanName) + throws BeansException { + if (isPresent(TARGETER_CLASS_NAME, classLoader)) { + Class beanClass = getUserClass(bean.getClass()); + Class targetClass = resolveClassName(TARGETER_CLASS_NAME, classLoader); + if (targetClass.isAssignableFrom(beanClass)) { + return newProxyInstance(classLoader, new Class[] { targetClass }, + new TargeterInvocationHandler(bean, environment, classLoader, + dubboServiceMetadataRepository, + dubboGenericServiceFactory, contextFactory)); + } + } + return bean; + } - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/TargeterInvocationHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/TargeterInvocationHandler.java index 6ef5cb86..c2b335d5 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/TargeterInvocationHandler.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/openfeign/TargeterInvocationHandler.java @@ -16,13 +16,21 @@ */ package com.alibaba.cloud.dubbo.openfeign; +import static java.lang.reflect.Proxy.newProxyInstance; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; import org.apache.dubbo.rpc.service.GenericService; -import feign.Contract; -import feign.Target; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FeignContext; +import org.springframework.core.env.Environment; + import com.alibaba.cloud.dubbo.annotation.DubboTransported; import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata; import com.alibaba.cloud.dubbo.metadata.DubboTransportedMethodMetadata; @@ -33,16 +41,9 @@ import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepositor import com.alibaba.cloud.dubbo.metadata.resolver.DubboTransportedMethodMetadataResolver; import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; -import org.springframework.cloud.openfeign.FeignContext; -import org.springframework.core.env.Environment; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.util.HashMap; -import java.util.Map; - -import static java.lang.reflect.Proxy.newProxyInstance; +import feign.Contract; +import feign.Target; /** * org.springframework.cloud.openfeign.Targeter {@link InvocationHandler} @@ -51,125 +52,137 @@ import static java.lang.reflect.Proxy.newProxyInstance; */ class TargeterInvocationHandler implements InvocationHandler { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); - private final Object bean; + private final Object bean; - private final Environment environment; + private final Environment environment; - private final ClassLoader classLoader; + private final ClassLoader classLoader; - private final DubboServiceMetadataRepository repository; + private final DubboServiceMetadataRepository repository; - private final DubboGenericServiceFactory dubboGenericServiceFactory; + private final DubboGenericServiceFactory dubboGenericServiceFactory; - private final DubboGenericServiceExecutionContextFactory contextFactory; + private final DubboGenericServiceExecutionContextFactory contextFactory; - TargeterInvocationHandler(Object bean, Environment environment, - ClassLoader classLoader, - DubboServiceMetadataRepository repository, - DubboGenericServiceFactory dubboGenericServiceFactory, - DubboGenericServiceExecutionContextFactory contextFactory) { - this.bean = bean; - this.environment = environment; - this.classLoader = classLoader; - this.repository = repository; - this.dubboGenericServiceFactory = dubboGenericServiceFactory; - this.contextFactory = contextFactory; - } + TargeterInvocationHandler(Object bean, Environment environment, + ClassLoader classLoader, DubboServiceMetadataRepository repository, + DubboGenericServiceFactory dubboGenericServiceFactory, + DubboGenericServiceExecutionContextFactory contextFactory) { + this.bean = bean; + this.environment = environment; + this.classLoader = classLoader; + this.repository = repository; + this.dubboGenericServiceFactory = dubboGenericServiceFactory; + this.contextFactory = contextFactory; + } - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - /** - * args[0]: FeignClientFactoryBean factory - * args[1]: Feign.Builder feign - * args[2]: FeignContext context - * args[3]: Target.HardCodedTarget target - */ - FeignContext feignContext = cast(args[2]); - Target.HardCodedTarget target = cast(args[3]); + private static T cast(Object object) { + return (T) object; + } - // Execute Targeter#target method first - method.setAccessible(true); - // Get the default proxy object - Object defaultProxy = method.invoke(bean, args); - // Create Dubbo Proxy if required - return createDubboProxyIfRequired(feignContext, target, defaultProxy); - } + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + /** + * args[0]: FeignClientFactoryBean factory args[1]: Feign.Builder feign args[2]: + * FeignContext context args[3]: Target.HardCodedTarget target + */ + FeignContext feignContext = cast(args[2]); + Target.HardCodedTarget target = cast(args[3]); - private Object createDubboProxyIfRequired(FeignContext feignContext, Target target, Object defaultProxy) { + // Execute Targeter#target method first + method.setAccessible(true); + // Get the default proxy object + Object defaultProxy = method.invoke(bean, args); + // Create Dubbo Proxy if required + return createDubboProxyIfRequired(feignContext, target, defaultProxy); + } - DubboInvocationHandler dubboInvocationHandler = createDubboInvocationHandler(feignContext, target, defaultProxy); + private Object createDubboProxyIfRequired(FeignContext feignContext, Target target, + Object defaultProxy) { - if (dubboInvocationHandler == null) { - return defaultProxy; - } + DubboInvocationHandler dubboInvocationHandler = createDubboInvocationHandler( + feignContext, target, defaultProxy); - return newProxyInstance(target.type().getClassLoader(), new Class[]{target.type()}, dubboInvocationHandler); - } + if (dubboInvocationHandler == null) { + return defaultProxy; + } - private DubboInvocationHandler createDubboInvocationHandler(FeignContext feignContext, Target target, - Object defaultFeignClientProxy) { + return newProxyInstance(target.type().getClassLoader(), + new Class[] { target.type() }, dubboInvocationHandler); + } - // Service name equals @FeignClient.name() - String serviceName = target.name(); - Class targetType = target.type(); + private DubboInvocationHandler createDubboInvocationHandler(FeignContext feignContext, + Target target, Object defaultFeignClientProxy) { - // Get Contract Bean from FeignContext - Contract contract = feignContext.getInstance(serviceName, Contract.class); + // Service name equals @FeignClient.name() + String serviceName = target.name(); + Class targetType = target.type(); - DubboTransportedMethodMetadataResolver resolver = - new DubboTransportedMethodMetadataResolver(environment, contract); + // Get Contract Bean from FeignContext + Contract contract = feignContext.getInstance(serviceName, Contract.class); - Map feignRestMethodMetadataMap = resolver.resolve(targetType); + DubboTransportedMethodMetadataResolver resolver = new DubboTransportedMethodMetadataResolver( + environment, contract); - if (feignRestMethodMetadataMap.isEmpty()) { // @DubboTransported method was not found from the Client interface - if (logger.isDebugEnabled()) { - logger.debug("@{} method was not found in the Feign target type[{}]", - DubboTransported.class.getSimpleName(), targetType.getName()); - } - return null; - } + Map feignRestMethodMetadataMap = resolver + .resolve(targetType); - // Update Metadata - repository.initialize(serviceName); + if (feignRestMethodMetadataMap.isEmpty()) { // @DubboTransported method was not + // found from the Client interface + if (logger.isDebugEnabled()) { + logger.debug("@{} method was not found in the Feign target type[{}]", + DubboTransported.class.getSimpleName(), targetType.getName()); + } + return null; + } - Map feignMethodMetadataMap = getFeignMethodMetadataMap(serviceName, feignRestMethodMetadataMap); + // Update Metadata + repository.initializeMetadata(serviceName); - InvocationHandler defaultFeignClientInvocationHandler = Proxy.getInvocationHandler(defaultFeignClientProxy); + Map feignMethodMetadataMap = getFeignMethodMetadataMap( + serviceName, feignRestMethodMetadataMap); - DubboInvocationHandler dubboInvocationHandler = new DubboInvocationHandler(feignMethodMetadataMap, - defaultFeignClientInvocationHandler, classLoader, contextFactory); + InvocationHandler defaultFeignClientInvocationHandler = Proxy + .getInvocationHandler(defaultFeignClientProxy); - return dubboInvocationHandler; - } + DubboInvocationHandler dubboInvocationHandler = new DubboInvocationHandler( + feignMethodMetadataMap, defaultFeignClientInvocationHandler, classLoader, + contextFactory); - private Map getFeignMethodMetadataMap(String serviceName, - Map - feignRestMethodMetadataMap) { - Map feignMethodMetadataMap = new HashMap<>(); + return dubboInvocationHandler; + } - for (Map.Entry entry : feignRestMethodMetadataMap.entrySet()) { - RestMethodMetadata feignRestMethodMetadata = entry.getValue(); - RequestMetadata feignRequestMetadata = feignRestMethodMetadata.getRequest(); - DubboRestServiceMetadata metadata = repository.get(serviceName, feignRequestMetadata); - if (metadata != null) { - DubboTransportedMethodMetadata dubboTransportedMethodMetadata = entry.getKey(); - Map dubboTranslatedAttributes = dubboTransportedMethodMetadata.getAttributes(); - Method method = dubboTransportedMethodMetadata.getMethod(); - GenericService dubboGenericService = dubboGenericServiceFactory.create(metadata, dubboTranslatedAttributes); - RestMethodMetadata dubboRestMethodMetadata = metadata.getRestMethodMetadata(); - MethodMetadata methodMetadata = dubboTransportedMethodMetadata.getMethodMetadata(); - FeignMethodMetadata feignMethodMetadata = new FeignMethodMetadata(dubboGenericService, - dubboRestMethodMetadata, feignRestMethodMetadata); - feignMethodMetadataMap.put(method, feignMethodMetadata); - } - } + private Map getFeignMethodMetadataMap(String serviceName, + Map feignRestMethodMetadataMap) { + Map feignMethodMetadataMap = new HashMap<>(); - return feignMethodMetadataMap; - } + for (Map.Entry entry : feignRestMethodMetadataMap + .entrySet()) { + RestMethodMetadata feignRestMethodMetadata = entry.getValue(); + RequestMetadata feignRequestMetadata = feignRestMethodMetadata.getRequest(); + DubboRestServiceMetadata metadata = repository.get(serviceName, + feignRequestMetadata); + if (metadata != null) { + DubboTransportedMethodMetadata dubboTransportedMethodMetadata = entry + .getKey(); + Map dubboTranslatedAttributes = dubboTransportedMethodMetadata + .getAttributes(); + Method method = dubboTransportedMethodMetadata.getMethod(); + GenericService dubboGenericService = dubboGenericServiceFactory + .create(metadata, dubboTranslatedAttributes); + RestMethodMetadata dubboRestMethodMetadata = metadata + .getRestMethodMetadata(); + MethodMetadata methodMetadata = dubboTransportedMethodMetadata + .getMethodMetadata(); + FeignMethodMetadata feignMethodMetadata = new FeignMethodMetadata( + dubboGenericService, dubboRestMethodMetadata, + feignRestMethodMetadata); + feignMethodMetadataMap.put(method, feignMethodMetadata); + } + } - private static T cast(Object object) { - return (T) object; - } + return feignMethodMetadataMap; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/AbstractSpringCloudRegistry.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/AbstractSpringCloudRegistry.java index 8fe3ee4a..a223e963 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/AbstractSpringCloudRegistry.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/AbstractSpringCloudRegistry.java @@ -16,267 +16,355 @@ */ package com.alibaba.cloud.dubbo.registry; -import org.apache.dubbo.common.Constants; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static org.apache.dubbo.common.URLBuilder.from; +import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE; +import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; +import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL; +import static org.apache.dubbo.registry.Constants.ADMIN_PROTOCOL; +import static org.springframework.util.StringUtils.hasText; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + import org.apache.dubbo.common.URL; 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 org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.util.CollectionUtils; + import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import com.alibaba.cloud.dubbo.registry.event.ServiceInstancesChangedEvent; import com.alibaba.cloud.dubbo.service.DubboMetadataService; import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy; import com.alibaba.cloud.dubbo.util.JSONUtils; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.discovery.DiscoveryClient; - -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; - -import static java.util.Collections.emptyList; -import static org.apache.dubbo.common.Constants.APPLICATION_KEY; -import static org.apache.dubbo.common.Constants.GROUP_KEY; -import static org.apache.dubbo.common.Constants.PROTOCOL_KEY; -import static org.apache.dubbo.common.Constants.PROVIDER_SIDE; -import static org.apache.dubbo.common.Constants.SIDE_KEY; -import static org.apache.dubbo.common.Constants.VERSION_KEY; -import static org.springframework.util.StringUtils.hasText; /** - * Abstract Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud" + * Abstract Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration + * abstraction, whose protocol is "spring-cloud" * * @author Mercy */ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry { - /** - * The parameter name of {@link #servicesLookupInterval} - */ - public static final String SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.services.lookup.interval"; + /** + * The parameter name of {@link #servicesLookupInterval} + */ + public static final String SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.services.lookup.interval"; - protected static final String DUBBO_METADATA_SERVICE_CLASS_NAME = DubboMetadataService.class.getName(); + protected static final String DUBBO_METADATA_SERVICE_CLASS_NAME = DubboMetadataService.class + .getName(); + /** + * Caches the IDs of {@link ApplicationListener} + */ + private static final Set registerListeners = new HashSet<>(); - private static final Set SCHEDULER_TASKS = new HashSet<>(); + protected final Logger logger = LoggerFactory.getLogger(getClass()); - protected final Logger logger = LoggerFactory.getLogger(getClass()); + /** + * The interval in second of lookup service names(only for Dubbo-OPS) + */ + private final long servicesLookupInterval; - /** - * The interval in second of lookup service names(only for Dubbo-OPS) - */ - private final long servicesLookupInterval; + private final DiscoveryClient discoveryClient; - private final DiscoveryClient discoveryClient; + private final DubboServiceMetadataRepository repository; - private final DubboServiceMetadataRepository repository; + private final DubboMetadataServiceProxy dubboMetadataConfigServiceProxy; - private final DubboMetadataServiceProxy dubboMetadataConfigServiceProxy; + private final JSONUtils jsonUtils; - private final JSONUtils jsonUtils; + private final ConfigurableApplicationContext applicationContext; + public AbstractSpringCloudRegistry(URL url, DiscoveryClient discoveryClient, + DubboServiceMetadataRepository dubboServiceMetadataRepository, + DubboMetadataServiceProxy dubboMetadataConfigServiceProxy, + JSONUtils jsonUtils, ConfigurableApplicationContext applicationContext) { + super(url); + this.servicesLookupInterval = url + .getParameter(SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 60L); + this.discoveryClient = discoveryClient; + this.repository = dubboServiceMetadataRepository; + this.dubboMetadataConfigServiceProxy = dubboMetadataConfigServiceProxy; + this.jsonUtils = jsonUtils; + this.applicationContext = applicationContext; + } - protected final ScheduledExecutorService servicesLookupScheduler; + protected boolean shouldRegister(URL url) { + String side = url.getParameter(SIDE_KEY); - public AbstractSpringCloudRegistry(URL url, - DiscoveryClient discoveryClient, - DubboServiceMetadataRepository dubboServiceMetadataRepository, - DubboMetadataServiceProxy dubboMetadataConfigServiceProxy, - JSONUtils jsonUtils, - ScheduledExecutorService servicesLookupScheduler) { - super(url); - this.servicesLookupInterval = url.getParameter(SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 60L); - this.discoveryClient = discoveryClient; - this.repository = dubboServiceMetadataRepository; - this.dubboMetadataConfigServiceProxy = dubboMetadataConfigServiceProxy; - this.jsonUtils = jsonUtils; - this.servicesLookupScheduler = servicesLookupScheduler; - } + boolean should = PROVIDER_SIDE.equals(side); // Only register the Provider. - protected boolean shouldRegister(URL url) { - String side = url.getParameter(SIDE_KEY); + if (!should) { + if (logger.isDebugEnabled()) { + logger.debug("The URL[{}] should not be registered.", url.toString()); + } + } - boolean should = PROVIDER_SIDE.equals(side); // Only register the Provider. + return should; + } - if (!should) { - if (logger.isDebugEnabled()) { - logger.debug("The URL[{}] should not be registered.", url.toString()); - } - } + @Override + public final void doRegister(URL url) { + if (!shouldRegister(url)) { + return; + } + doRegister0(url); + } - return should; - } + /** + * The sub-type should implement to register + * + * @param url {@link URL} + */ + protected abstract void doRegister0(URL url); - @Override - public final void doRegister(URL url) { - if (!shouldRegister(url)) { - return; - } - doRegister0(url); - } + @Override + public final void doUnregister(URL url) { + if (!shouldRegister(url)) { + return; + } + doUnregister0(url); + } - /** - * The sub-type should implement to register - * - * @param url {@link URL} - */ - protected abstract void doRegister0(URL url); + /** + * The sub-type should implement to unregister + * + * @param url {@link URL} + */ + protected abstract void doUnregister0(URL url); - @Override - public final void doUnregister(URL url) { - if (!shouldRegister(url)) { - return; - } - doUnregister0(url); - } + @Override + public final void doSubscribe(URL url, NotifyListener listener) { - /** - * The sub-type should implement to unregister - * - * @param url {@link URL} - */ - protected abstract void doUnregister0(URL url); + if (isAdminURL(url)) { + // TODO in future + } + else if (isDubboMetadataServiceURL(url)) { // for DubboMetadataService + subscribeDubboMetadataServiceURLs(url, listener); + } + else { // for general Dubbo Services + subscribeDubboServiceURLs(url, listener); + } + } - @Override - public final void doSubscribe(URL url, NotifyListener listener) { + protected void subscribeDubboServiceURLs(URL url, NotifyListener listener) { - if (isAdminURL(url)) { - // TODO in future - } else if (isDubboMetadataServiceURL(url)) { // for DubboMetadataService - subscribeDubboMetadataServiceURLs(url, listener); - } else { // for general Dubbo Services - subscribeDubboServiceURLs(url, listener); - } - } + doSubscribeDubboServiceURLs(url, listener); - protected void subscribeDubboServiceURLs(URL url, NotifyListener listener) { + registerServiceInstancesChangedEventListener(url, listener); + } - doSubscribeDubboServiceURLs(url, listener); + /** + * Register a {@link ApplicationListener listener} for + * {@link ServiceInstancesChangedEvent} + * + * @param url {@link URL} + * @param listener {@link NotifyListener} + */ + private void registerServiceInstancesChangedEventListener(URL url, + NotifyListener listener) { + String listenerId = generateId(url); + if (registerListeners.add(listenerId)) { + applicationContext.addApplicationListener( + new ApplicationListener() { + @Override + public void onApplicationEvent( + ServiceInstancesChangedEvent event) { + String serviceName = event.getServiceName(); + Collection serviceInstances = event + .getServiceInstances(); + subscribeDubboServiceURL(url, listener, serviceName, + s -> serviceInstances); + } + }); + } + } - submitSchedulerTaskIfAbsent(url, listener); - } + private void doSubscribeDubboServiceURLs(URL url, NotifyListener listener) { - private void submitSchedulerTaskIfAbsent(URL url, NotifyListener listener) { - String taskId = url.toIdentityString(); - if (SCHEDULER_TASKS.add(taskId)) { - schedule(() -> doSubscribeDubboServiceURLs(url, listener)); - } - } + Set subscribedServices = repository.getSubscribedServices(); + // Sync + subscribedServices.forEach(service -> subscribeDubboServiceURL(url, listener, + service, this::getServiceInstances)); + } - protected void doSubscribeDubboServiceURLs(URL url, NotifyListener listener) { + protected void subscribeDubboServiceURL(URL url, NotifyListener listener, + String serviceName, + Function> serviceInstancesFunction) { - Set subscribedServices = repository.getSubscribedServices(); + if (logger.isInfoEnabled()) { + logger.info( + "The Dubbo Service URL[ID : {}] is being subscribed for service[name : {}]", + generateId(url), serviceName); + } - subscribedServices.stream() - .map(dubboMetadataConfigServiceProxy::getProxy) - .filter(Objects::nonNull) - .forEach(dubboMetadataService -> { - List exportedURLs = getExportedURLs(dubboMetadataService, url); - List allSubscribedURLs = new LinkedList<>(); - for (URL exportedURL : exportedURLs) { - String serviceName = exportedURL.getParameter(APPLICATION_KEY); - List serviceInstances = getServiceInstances(serviceName); - String protocol = exportedURL.getProtocol(); - List subscribedURLs = new LinkedList<>(); - serviceInstances.forEach(serviceInstance -> { - Integer port = repository.getDubboProtocolPort(serviceInstance, protocol); - String host = serviceInstance.getHost(); - if (port == null) { - if (logger.isWarnEnabled()) { - logger.warn("The protocol[{}] port of Dubbo service instance[host : {}] " + - "can't be resolved", protocol, host); - } - } else { - URL subscribedURL = new URL(protocol, host, port, exportedURL.getParameters()); - subscribedURLs.add(subscribedURL); - } - }); + List allSubscribedURLs = new LinkedList<>(); - if (logger.isDebugEnabled()) { - logger.debug("The subscribed URL[{}] will notify all URLs : {}", url, subscribedURLs); - } + Collection serviceInstances = serviceInstancesFunction + .apply(serviceName); - allSubscribedURLs.addAll(subscribedURLs); - } + if (CollectionUtils.isEmpty(serviceInstances)) { + dubboMetadataConfigServiceProxy.removeProxy(serviceName); + repository.removeInitializedService(serviceName); + repository.removeServiceMetadata(serviceName); + if (logger.isWarnEnabled()) { + logger.warn( + "There is no instance from service[name : {}], and then Dubbo Service[key : {}] will not be " + + "available , please make sure the further impact", + serviceName, url.getServiceKey()); + } + /** + * URLs with {@link RegistryConstants#EMPTY_PROTOCOL} + */ + allSubscribedURLs.addAll(emptyURLs(url)); + if (logger.isDebugEnabled()) { + logger.debug("The subscribed URL[{}] will notify all URLs : {}", url, + allSubscribedURLs); + } + listener.notify(allSubscribedURLs); + return; + } - listener.notify(allSubscribedURLs); - }); - } + DubboMetadataService dubboMetadataService = dubboMetadataConfigServiceProxy + .getProxy(serviceName); - private List getServiceInstances(String serviceName) { - return hasText(serviceName) ? doGetServiceInstances(serviceName) : emptyList(); - } + if (dubboMetadataService == null) { // If not found, try to initialize + if (logger.isInfoEnabled()) { + logger.info( + "The metadata of Dubbo service[key : {}] can't be found when the subscribed service[name : {}], " + + "and then try to initialize it", + url.getServiceKey(), serviceName); + } + repository.initializeMetadata(serviceName); + dubboMetadataService = dubboMetadataConfigServiceProxy.getProxy(serviceName); + } - private List doGetServiceInstances(String serviceName) { - List serviceInstances = emptyList(); - try { - serviceInstances = discoveryClient.getInstances(serviceName); - } catch (Exception e) { - if (logger.isErrorEnabled()) { - logger.error(e.getMessage(), e); - } - } - return serviceInstances; - } + if (dubboMetadataService == null) { // It makes sure not-found, return immediately + if (logger.isWarnEnabled()) { + logger.warn( + "The metadata of Dubbo service[key : {}] still can't be found, it could effect the further " + + "Dubbo service invocation", + url.getServiceKey()); + } + return; + } - private List getExportedURLs(DubboMetadataService dubboMetadataService, URL url) { - String serviceInterface = url.getServiceInterface(); - String group = url.getParameter(GROUP_KEY); - String version = url.getParameter(VERSION_KEY); - // The subscribed protocol may be null - String subscribedProtocol = url.getParameter(PROTOCOL_KEY); - String exportedURLsJSON = dubboMetadataService.getExportedURLs(serviceInterface, group, version); - return jsonUtils - .toURLs(exportedURLsJSON) - .stream() - .filter(exportedURL -> - subscribedProtocol == null || subscribedProtocol.equalsIgnoreCase(exportedURL.getProtocol()) - ).collect(Collectors.toList()); - } + List exportedURLs = getExportedURLs(dubboMetadataService, url); - private void subscribeDubboMetadataServiceURLs(URL url, NotifyListener listener) { - String serviceInterface = url.getServiceInterface(); - String group = url.getParameter(GROUP_KEY); - String version = url.getParameter(VERSION_KEY); - String protocol = url.getParameter(PROTOCOL_KEY); - List urls = repository.findSubscribedDubboMetadataServiceURLs(serviceInterface, group, version, protocol); - listener.notify(urls); - } + for (URL exportedURL : exportedURLs) { + String protocol = exportedURL.getProtocol(); + List subscribedURLs = new LinkedList<>(); + serviceInstances.forEach(serviceInstance -> { + Integer port = repository.getDubboProtocolPort(serviceInstance, protocol); + String host = serviceInstance.getHost(); + if (port == null) { + if (logger.isWarnEnabled()) { + logger.warn( + "The protocol[{}] port of Dubbo service instance[host : {}] " + + "can't be resolved", + protocol, host); + } + } + else { + URL subscribedURL = new URL(protocol, host, port, + exportedURL.getParameters()); + subscribedURLs.add(subscribedURL); + } + }); - @Override - public final void doUnsubscribe(URL url, NotifyListener listener) { - if (isAdminURL(url)) { - shutdownServiceNamesLookup(); - } - } + allSubscribedURLs.addAll(subscribedURLs); + } - @Override - public boolean isAvailable() { - return !discoveryClient.getServices().isEmpty(); - } + if (logger.isDebugEnabled()) { + logger.debug("The subscribed URL[{}] will notify all URLs : {}", url, + allSubscribedURLs); + } - protected void shutdownServiceNamesLookup() { - if (servicesLookupScheduler != null) { - servicesLookupScheduler.shutdown(); - } - } + listener.notify(allSubscribedURLs); + } - protected boolean isAdminURL(URL url) { - return Constants.ADMIN_PROTOCOL.equals(url.getProtocol()); - } + private String generateId(URL url) { + return url.toString(VERSION_KEY, GROUP_KEY, PROTOCOL_KEY); + } - protected boolean isDubboMetadataServiceURL(URL url) { - return DUBBO_METADATA_SERVICE_CLASS_NAME.equals(url.getServiceInterface()); - } + private List emptyURLs(URL url) { + return asList(from(url).setProtocol(EMPTY_PROTOCOL).build()); + } - protected ScheduledFuture schedule(Runnable runnable) { - return this.servicesLookupScheduler.scheduleAtFixedRate(runnable, servicesLookupInterval, - servicesLookupInterval, TimeUnit.SECONDS); - } + private List getServiceInstances(String serviceName) { + return hasText(serviceName) ? doGetServiceInstances(serviceName) : emptyList(); + } + + private List doGetServiceInstances(String serviceName) { + List serviceInstances = emptyList(); + try { + serviceInstances = discoveryClient.getInstances(serviceName); + } + catch (Exception e) { + if (logger.isErrorEnabled()) { + logger.error(e.getMessage(), e); + } + } + return serviceInstances; + } + + private List getExportedURLs(DubboMetadataService dubboMetadataService, + URL url) { + String serviceInterface = url.getServiceInterface(); + String group = url.getParameter(GROUP_KEY); + String version = url.getParameter(VERSION_KEY); + // The subscribed protocol may be null + String subscribedProtocol = url.getParameter(PROTOCOL_KEY); + String exportedURLsJSON = dubboMetadataService.getExportedURLs(serviceInterface, + group, version); + return jsonUtils.toURLs(exportedURLsJSON).stream() + .filter(exportedURL -> subscribedProtocol == null + || subscribedProtocol.equalsIgnoreCase(exportedURL.getProtocol())) + .collect(Collectors.toList()); + } + + private void subscribeDubboMetadataServiceURLs(URL url, NotifyListener listener) { + String serviceInterface = url.getServiceInterface(); + String group = url.getParameter(GROUP_KEY); + String version = url.getParameter(VERSION_KEY); + String protocol = url.getParameter(PROTOCOL_KEY); + List urls = repository.findSubscribedDubboMetadataServiceURLs( + serviceInterface, group, version, protocol); + listener.notify(urls); + } + + @Override + public final void doUnsubscribe(URL url, NotifyListener listener) { + if (isAdminURL(url)) { + } + } + + @Override + public boolean isAvailable() { + return !discoveryClient.getServices().isEmpty(); + } + + protected boolean isAdminURL(URL url) { + return ADMIN_PROTOCOL.equals(url.getProtocol()); + } + + protected boolean isDubboMetadataServiceURL(URL url) { + return DUBBO_METADATA_SERVICE_CLASS_NAME.equals(url.getServiceInterface()); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DelegatingRegistration.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DelegatingRegistration.java index 94491b48..8789e80f 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DelegatingRegistration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DelegatingRegistration.java @@ -16,57 +16,58 @@ */ package com.alibaba.cloud.dubbo.registry; -import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.client.serviceregistry.Registration; - import java.net.URI; import java.util.Map; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.serviceregistry.Registration; + /** - * The {@link Registration} of Dubbo uses an external of {@link ServiceInstance} instance as the delegate. + * The {@link Registration} of Dubbo uses an external of {@link ServiceInstance} instance + * as the delegate. * * @author Mercy */ class DelegatingRegistration implements Registration { - private final ServiceInstance delegate; + private final ServiceInstance delegate; - public DelegatingRegistration(ServiceInstance delegate) { - this.delegate = delegate; - } + public DelegatingRegistration(ServiceInstance delegate) { + this.delegate = delegate; + } - @Override - public String getServiceId() { - return delegate.getServiceId(); - } + @Override + public String getServiceId() { + return delegate.getServiceId(); + } - @Override - public String getHost() { - return delegate.getHost(); - } + @Override + public String getHost() { + return delegate.getHost(); + } - @Override - public int getPort() { - return delegate.getPort(); - } + @Override + public int getPort() { + return delegate.getPort(); + } - @Override - public boolean isSecure() { - return delegate.isSecure(); - } + @Override + public boolean isSecure() { + return delegate.isSecure(); + } - @Override - public URI getUri() { - return delegate.getUri(); - } + @Override + public URI getUri() { + return delegate.getUri(); + } - @Override - public Map getMetadata() { - return delegate.getMetadata(); - } + @Override + public Map getMetadata() { + return delegate.getMetadata(); + } - @Override - public String getScheme() { - return delegate.getScheme(); - } + @Override + public String getScheme() { + return delegate.getScheme(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DubboServiceRegistrationEventPublishingAspect.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DubboServiceRegistrationEventPublishingAspect.java index c6c8c210..33ed52d5 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DubboServiceRegistrationEventPublishingAspect.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/DubboServiceRegistrationEventPublishingAspect.java @@ -19,13 +19,14 @@ package com.alibaba.cloud.dubbo.registry; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; -import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent; -import com.alibaba.cloud.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; +import com.alibaba.cloud.dubbo.registry.event.ServiceInstancePreRegisteredEvent; +import com.alibaba.cloud.dubbo.registry.event.ServiceInstanceRegisteredEvent; + /** * Dubbo Service Registration Event-Publishing Aspect * @@ -34,28 +35,31 @@ import org.springframework.context.ApplicationEventPublisherAware; * @see ServiceInstanceRegisteredEvent */ @Aspect -public class DubboServiceRegistrationEventPublishingAspect implements ApplicationEventPublisherAware { +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)"; + /** + * 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; + private ApplicationEventPublisher applicationEventPublisher; - @Before(REGISTER_POINTCUT_EXPRESSION) - public void beforeRegister(Registration registration) { - applicationEventPublisher.publishEvent(new ServiceInstancePreRegisteredEvent(registration)); - } + @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)); - } + @After(REGISTER_POINTCUT_EXPRESSION) + public void afterRegister(Registration registration) { + applicationEventPublisher + .publishEvent(new ServiceInstanceRegisteredEvent(registration)); + } - @Override - public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { - this.applicationEventPublisher = applicationEventPublisher; - } + @Override + public void setApplicationEventPublisher( + ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistry.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistry.java index ce09de4e..be9039ae 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistry.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistry.java @@ -19,38 +19,39 @@ package com.alibaba.cloud.dubbo.registry; import org.apache.dubbo.common.URL; import org.apache.dubbo.registry.RegistryFactory; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.context.ConfigurableApplicationContext; + import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy; import com.alibaba.cloud.dubbo.util.JSONUtils; -import org.springframework.cloud.client.discovery.DiscoveryClient; - -import java.util.concurrent.ScheduledExecutorService; /** - * Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud" + * Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose + * protocol is "spring-cloud" * * @author Mercy */ public class SpringCloudRegistry extends AbstractSpringCloudRegistry { - private final DubboServiceMetadataRepository dubboServiceMetadataRepository; + private final DubboServiceMetadataRepository dubboServiceMetadataRepository; - public SpringCloudRegistry(URL url, DiscoveryClient discoveryClient, - DubboServiceMetadataRepository dubboServiceMetadataRepository, - DubboMetadataServiceProxy dubboMetadataConfigServiceProxy, - JSONUtils jsonUtils, - ScheduledExecutorService servicesLookupScheduler) { - super(url, discoveryClient, dubboServiceMetadataRepository, dubboMetadataConfigServiceProxy, jsonUtils, servicesLookupScheduler); - this.dubboServiceMetadataRepository = dubboServiceMetadataRepository; - } + public SpringCloudRegistry(URL url, DiscoveryClient discoveryClient, + DubboServiceMetadataRepository dubboServiceMetadataRepository, + DubboMetadataServiceProxy dubboMetadataConfigServiceProxy, + JSONUtils jsonUtils, ConfigurableApplicationContext applicationContext) { + super(url, discoveryClient, dubboServiceMetadataRepository, + dubboMetadataConfigServiceProxy, jsonUtils, applicationContext); + this.dubboServiceMetadataRepository = dubboServiceMetadataRepository; + } - @Override - protected void doRegister0(URL url) { - dubboServiceMetadataRepository.exportURL(url); - } + @Override + protected void doRegister0(URL url) { + dubboServiceMetadataRepository.exportURL(url); + } - @Override - protected void doUnregister0(URL url) { - dubboServiceMetadataRepository.unexportURL(url); - } + @Override + protected void doUnregister0(URL url) { + dubboServiceMetadataRepository.unexportURL(url); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistryFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistryFactory.java index 9cdf099c..ca0e1dc1 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistryFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/SpringCloudRegistryFactory.java @@ -16,24 +16,22 @@ */ package com.alibaba.cloud.dubbo.registry; +import static java.lang.System.getProperty; + 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.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.context.ConfigurableApplicationContext; + import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy; import com.alibaba.cloud.dubbo.util.JSONUtils; -import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.context.ConfigurableApplicationContext; - -import java.util.concurrent.ScheduledExecutorService; - -import static java.lang.System.getProperty; -import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; /** - * Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud" + * Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose + * protocol is "spring-cloud" * * @author Mercy * @see RegistryFactory @@ -41,50 +39,51 @@ import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; */ public class SpringCloudRegistryFactory implements RegistryFactory { - public static String PROTOCOL = "spring-cloud"; + public static String PROTOCOL = "spring-cloud"; - public static String ADDRESS = "localhost"; + public static String ADDRESS = "localhost"; - private static String SERVICES_LOOKUP_SCHEDULER_THREAD_NAME_PREFIX = - getProperty("dubbo.services.lookup.scheduler.thread.name.prefix ", "dubbo-services-lookup-"); + private static String SERVICES_LOOKUP_SCHEDULER_THREAD_NAME_PREFIX = getProperty( + "dubbo.services.lookup.scheduler.thread.name.prefix ", + "dubbo-services-lookup-"); - private static ConfigurableApplicationContext applicationContext; + private static ConfigurableApplicationContext applicationContext; - private final ScheduledExecutorService servicesLookupScheduler; + private DiscoveryClient discoveryClient; - private DiscoveryClient discoveryClient; + private DubboServiceMetadataRepository dubboServiceMetadataRepository; - private DubboServiceMetadataRepository dubboServiceMetadataRepository; + private DubboMetadataServiceProxy dubboMetadataConfigServiceProxy; - private DubboMetadataServiceProxy dubboMetadataConfigServiceProxy; + private JSONUtils jsonUtils; - private JSONUtils jsonUtils; + private volatile boolean initialized = false; - private volatile boolean initialized = false; + public SpringCloudRegistryFactory() { + } - public SpringCloudRegistryFactory() { - servicesLookupScheduler = newSingleThreadScheduledExecutor( - new NamedThreadFactory(SERVICES_LOOKUP_SCHEDULER_THREAD_NAME_PREFIX)); - } + public static void setApplicationContext( + ConfigurableApplicationContext applicationContext) { + SpringCloudRegistryFactory.applicationContext = applicationContext; + } - protected void init() { - if (initialized || applicationContext == null) { - return; - } - this.discoveryClient = applicationContext.getBean(DiscoveryClient.class); - this.dubboServiceMetadataRepository = applicationContext.getBean(DubboServiceMetadataRepository.class); - this.dubboMetadataConfigServiceProxy = applicationContext.getBean(DubboMetadataServiceProxy.class); - this.jsonUtils = applicationContext.getBean(JSONUtils.class); - } + protected void init() { + if (initialized || applicationContext == null) { + return; + } + this.discoveryClient = applicationContext.getBean(DiscoveryClient.class); + this.dubboServiceMetadataRepository = applicationContext + .getBean(DubboServiceMetadataRepository.class); + this.dubboMetadataConfigServiceProxy = applicationContext + .getBean(DubboMetadataServiceProxy.class); + this.jsonUtils = applicationContext.getBean(JSONUtils.class); + } - @Override - public Registry getRegistry(URL url) { - init(); - return new SpringCloudRegistry(url, discoveryClient, dubboServiceMetadataRepository, - dubboMetadataConfigServiceProxy, jsonUtils, servicesLookupScheduler); - } - - public static void setApplicationContext(ConfigurableApplicationContext applicationContext) { - SpringCloudRegistryFactory.applicationContext = applicationContext; - } + @Override + public Registry getRegistry(URL url) { + init(); + return new SpringCloudRegistry(url, discoveryClient, + dubboServiceMetadataRepository, dubboMetadataConfigServiceProxy, + jsonUtils, applicationContext); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstancePreRegisteredEvent.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstancePreRegisteredEvent.java index 08389694..1821ebda 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstancePreRegisteredEvent.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstancePreRegisteredEvent.java @@ -22,18 +22,19 @@ import org.springframework.cloud.client.serviceregistry.ServiceRegistry; import org.springframework.context.ApplicationEvent; /** - * The before-{@link ServiceRegistry#register(Registration) register} event for {@link ServiceInstance} + * The before-{@link ServiceRegistry#register(Registration) register} event for + * {@link ServiceInstance} * * @author Mercy */ public class ServiceInstancePreRegisteredEvent extends ApplicationEvent { - public ServiceInstancePreRegisteredEvent(Registration source) { - super(source); - } + public ServiceInstancePreRegisteredEvent(Registration source) { + super(source); + } - @Override - public Registration getSource() { - return (Registration) super.getSource(); - } + @Override + public Registration getSource() { + return (Registration) super.getSource(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstanceRegisteredEvent.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstanceRegisteredEvent.java index 0022cfac..0e5a0a2a 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstanceRegisteredEvent.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstanceRegisteredEvent.java @@ -18,22 +18,22 @@ package com.alibaba.cloud.dubbo.registry.event; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; - -import java.util.EventObject; +import org.springframework.context.ApplicationEvent; /** - * The after-{@link ServiceRegistry#register(Registration) register} event for {@link Registration} + * The after-{@link ServiceRegistry#register(Registration) register} event for + * {@link Registration} * * @author Mercy */ -public class ServiceInstanceRegisteredEvent extends EventObject { +public class ServiceInstanceRegisteredEvent extends ApplicationEvent { - public ServiceInstanceRegisteredEvent(Registration source) { - super(source); - } + public ServiceInstanceRegisteredEvent(Registration source) { + super(source); + } - @Override - public Registration getSource() { - return (Registration) super.getSource(); - } + @Override + public Registration getSource() { + return (Registration) super.getSource(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstancesChangedEvent.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstancesChangedEvent.java new file mode 100644 index 00000000..2123b485 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/ServiceInstancesChangedEvent.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.alibaba.cloud.dubbo.registry.event; + +import static java.util.Collections.unmodifiableCollection; + +import java.util.Collection; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.ApplicationEventMulticaster; +import org.springframework.context.event.SimpleApplicationEventMulticaster; + +/** + * An event raised after the {@link ServiceInstance instances} of one service has been + * changed. + * + * @author Mercy + */ +public class ServiceInstancesChangedEvent extends ApplicationEvent { + + private final String serviceName; + + private final Collection serviceInstances; + + /** + * Current event has been processed or not. Typically, Spring Event was based on sync + * {@link ApplicationEventMulticaster} + * + * @see SimpleApplicationEventMulticaster + */ + private boolean processed = false; + + /** + * @param serviceName The name of service that was changed + * @param serviceInstances all {@link ServiceInstance service instances} + * @throws IllegalArgumentException if source is null. + */ + public ServiceInstancesChangedEvent(String serviceName, + Collection serviceInstances) { + super(serviceName); + this.serviceName = serviceName; + this.serviceInstances = unmodifiableCollection(serviceInstances); + } + + /** + * @return The name of service that was changed + */ + public String getServiceName() { + return serviceName; + } + + /** + * @return all {@link ServiceInstance service instances} + */ + public Collection getServiceInstances() { + return serviceInstances; + } + + /** + * Mark current event being processed + */ + public void processed() { + processed = true; + } + + /** + * Current event has been processed or not + * + * @return if processed, return true, or false + */ + public boolean isProcessed() { + return processed; + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/SubscribedServicesChangedEvent.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/SubscribedServicesChangedEvent.java new file mode 100644 index 00000000..384d8a1c --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/registry/event/SubscribedServicesChangedEvent.java @@ -0,0 +1,66 @@ +/* + * 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 com.alibaba.cloud.dubbo.registry.event; + +import java.util.LinkedHashSet; +import java.util.Objects; +import java.util.Set; + +import org.springframework.context.ApplicationEvent; + +/** + * {@link ApplicationEvent Event} raised when the subscribed services are changed + *

+ * + * @author Mercy + * @see ApplicationEvent + */ +public class SubscribedServicesChangedEvent extends ApplicationEvent { + + private final Set oldSubscribedServices; + + private final Set newSubscribedServices; + + private final boolean changed; + + /** + * Create a new ApplicationEvent. + * + * @param source the object on which the event initially occurred (never {@code null}) + * @param oldSubscribedServices the subscribed services before changed + * @param newSubscribedServices the subscribed services after changed + */ + public SubscribedServicesChangedEvent(Object source, + Set oldSubscribedServices, Set newSubscribedServices) { + super(source); + this.oldSubscribedServices = new LinkedHashSet<>(oldSubscribedServices); + this.newSubscribedServices = new LinkedHashSet<>(newSubscribedServices); + this.changed = !Objects.equals(oldSubscribedServices, newSubscribedServices); + } + + public Set getOldSubscribedServices() { + return oldSubscribedServices; + } + + public Set getNewSubscribedServices() { + return newSubscribedServices; + } + + public boolean isChanged() { + return changed; + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceExecutionContext.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceExecutionContext.java index d8586933..53c7fd6d 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceExecutionContext.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceExecutionContext.java @@ -25,27 +25,28 @@ import org.apache.dubbo.rpc.service.GenericService; */ public class DubboGenericServiceExecutionContext { - private final String methodName; + private final String methodName; - private final String[] parameterTypes; + private final String[] parameterTypes; - private final Object[] parameters; + private final Object[] parameters; - public DubboGenericServiceExecutionContext(String methodName, String[] parameterTypes, Object[] parameters) { - this.methodName = methodName; - this.parameterTypes = parameterTypes; - this.parameters = parameters; - } + public DubboGenericServiceExecutionContext(String methodName, String[] parameterTypes, + Object[] parameters) { + this.methodName = methodName; + this.parameterTypes = parameterTypes; + this.parameters = parameters; + } - public String getMethodName() { - return methodName; - } + public String getMethodName() { + return methodName; + } - public String[] getParameterTypes() { - return parameterTypes; - } + public String[] getParameterTypes() { + return parameterTypes; + } - public Object[] getParameters() { - return parameters; - } + public Object[] getParameters() { + return parameters; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceExecutionContextFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceExecutionContextFactory.java index b40fe92c..8d61b2fd 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceExecutionContextFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceExecutionContextFactory.java @@ -16,19 +16,21 @@ */ package com.alibaba.cloud.dubbo.service; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; + import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; + import com.alibaba.cloud.dubbo.http.HttpServerRequest; import com.alibaba.cloud.dubbo.metadata.MethodMetadata; import com.alibaba.cloud.dubbo.metadata.MethodParameterMetadata; import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; import com.alibaba.cloud.dubbo.service.parameter.DubboGenericServiceParameterResolver; -import org.springframework.core.annotation.AnnotationAwareOrderComparator; - -import javax.annotation.PostConstruct; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; /** * {@link DubboGenericServiceExecutionContext} Factory @@ -38,110 +40,119 @@ import java.util.Map; */ public class DubboGenericServiceExecutionContextFactory { - @Autowired(required = false) - private final List resolvers = Collections.emptyList(); + @Autowired(required = false) + private final List resolvers = Collections + .emptyList(); - @PostConstruct - public void init() { - AnnotationAwareOrderComparator.sort(resolvers); - } + @PostConstruct + public void init() { + AnnotationAwareOrderComparator.sort(resolvers); + } - public DubboGenericServiceExecutionContext create(RestMethodMetadata dubboRestMethodMetadata, - RestMethodMetadata clientMethodMetadata, Object[] arguments) { + public DubboGenericServiceExecutionContext create( + RestMethodMetadata dubboRestMethodMetadata, + RestMethodMetadata clientMethodMetadata, Object[] arguments) { - MethodMetadata dubboMethodMetadata = dubboRestMethodMetadata.getMethod(); + MethodMetadata dubboMethodMetadata = dubboRestMethodMetadata.getMethod(); - String methodName = dubboMethodMetadata.getName(); + String methodName = dubboMethodMetadata.getName(); - String[] parameterTypes = resolveParameterTypes(dubboMethodMetadata); + String[] parameterTypes = resolveParameterTypes(dubboMethodMetadata); - Object[] parameters = resolveParameters(dubboRestMethodMetadata, clientMethodMetadata, arguments); + Object[] parameters = resolveParameters(dubboRestMethodMetadata, + clientMethodMetadata, arguments); - return new DubboGenericServiceExecutionContext(methodName, parameterTypes, parameters); - } + return new DubboGenericServiceExecutionContext(methodName, parameterTypes, + parameters); + } - public DubboGenericServiceExecutionContext create(RestMethodMetadata dubboRestMethodMetadata, - HttpServerRequest request) { - MethodMetadata methodMetadata = dubboRestMethodMetadata.getMethod(); + public DubboGenericServiceExecutionContext create( + RestMethodMetadata dubboRestMethodMetadata, HttpServerRequest request) { + MethodMetadata methodMetadata = dubboRestMethodMetadata.getMethod(); - String methodName = methodMetadata.getName(); + String methodName = methodMetadata.getName(); - String[] parameterTypes = resolveParameterTypes(methodMetadata); + String[] parameterTypes = resolveParameterTypes(methodMetadata); - Object[] parameters = resolveParameters(dubboRestMethodMetadata, request); + Object[] parameters = resolveParameters(dubboRestMethodMetadata, request); - return new DubboGenericServiceExecutionContext(methodName, parameterTypes, parameters); - } + return new DubboGenericServiceExecutionContext(methodName, parameterTypes, + parameters); + } - private Map buildParamNameToIndex(List params) { - Map paramNameToIndex = new LinkedHashMap<>(); - for (MethodParameterMetadata param : params) { - paramNameToIndex.put(param.getName(), param.getIndex()); - } - return paramNameToIndex; - } + private Map buildParamNameToIndex( + List params) { + Map paramNameToIndex = new LinkedHashMap<>(); + for (MethodParameterMetadata param : params) { + paramNameToIndex.put(param.getName(), param.getIndex()); + } + return paramNameToIndex; + } - protected String[] resolveParameterTypes(MethodMetadata methodMetadata) { + protected String[] resolveParameterTypes(MethodMetadata methodMetadata) { - List params = methodMetadata.getParams(); + List params = methodMetadata.getParams(); - String[] parameterTypes = new String[params.size()]; + String[] parameterTypes = new String[params.size()]; - for (MethodParameterMetadata parameterMetadata : params) { - int index = parameterMetadata.getIndex(); - String parameterType = parameterMetadata.getType(); - parameterTypes[index] = parameterType; - } + for (MethodParameterMetadata parameterMetadata : params) { + int index = parameterMetadata.getIndex(); + String parameterType = parameterMetadata.getType(); + parameterTypes[index] = parameterType; + } - return parameterTypes; - } + return parameterTypes; + } - protected Object[] resolveParameters(RestMethodMetadata dubboRestMethodMetadata, HttpServerRequest request) { + protected Object[] resolveParameters(RestMethodMetadata dubboRestMethodMetadata, + HttpServerRequest request) { - MethodMetadata dubboMethodMetadata = dubboRestMethodMetadata.getMethod(); + MethodMetadata dubboMethodMetadata = dubboRestMethodMetadata.getMethod(); - List params = dubboMethodMetadata.getParams(); + List params = dubboMethodMetadata.getParams(); - Object[] parameters = new Object[params.size()]; + Object[] parameters = new Object[params.size()]; - for (MethodParameterMetadata parameterMetadata : params) { + for (MethodParameterMetadata parameterMetadata : params) { - int index = parameterMetadata.getIndex(); + int index = parameterMetadata.getIndex(); - for (DubboGenericServiceParameterResolver resolver : resolvers) { - Object parameter = resolver.resolve(dubboRestMethodMetadata, parameterMetadata, request); - if (parameter != null) { - parameters[index] = parameter; - break; - } - } - } + for (DubboGenericServiceParameterResolver resolver : resolvers) { + Object parameter = resolver.resolve(dubboRestMethodMetadata, + parameterMetadata, request); + if (parameter != null) { + parameters[index] = parameter; + break; + } + } + } - return parameters; - } + return parameters; + } - protected Object[] resolveParameters(RestMethodMetadata dubboRestMethodMetadata, - RestMethodMetadata clientRestMethodMetadata, Object[] arguments) { + protected Object[] resolveParameters(RestMethodMetadata dubboRestMethodMetadata, + RestMethodMetadata clientRestMethodMetadata, Object[] arguments) { - MethodMetadata dubboMethodMetadata = dubboRestMethodMetadata.getMethod(); + MethodMetadata dubboMethodMetadata = dubboRestMethodMetadata.getMethod(); - List params = dubboMethodMetadata.getParams(); + List params = dubboMethodMetadata.getParams(); - Object[] parameters = new Object[params.size()]; + Object[] parameters = new Object[params.size()]; - for (MethodParameterMetadata parameterMetadata : params) { + for (MethodParameterMetadata parameterMetadata : params) { - int index = parameterMetadata.getIndex(); + int index = parameterMetadata.getIndex(); - for (DubboGenericServiceParameterResolver resolver : resolvers) { - Object parameter = resolver.resolve(dubboRestMethodMetadata, parameterMetadata, clientRestMethodMetadata, arguments); - if (parameter != null) { - parameters[index] = parameter; - break; - } - } - } + for (DubboGenericServiceParameterResolver resolver : resolvers) { + Object parameter = resolver.resolve(dubboRestMethodMetadata, + parameterMetadata, clientRestMethodMetadata, arguments); + if (parameter != null) { + parameters[index] = parameter; + break; + } + } + } - return parameters; - } + return parameters; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceFactory.java index 7c9ca828..b96ce36c 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboGenericServiceFactory.java @@ -16,6 +16,21 @@ */ package com.alibaba.cloud.dubbo.service; +import static java.util.Collections.emptyMap; +import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; +import static org.springframework.util.StringUtils.commaDelimitedListToStringArray; + +import java.beans.PropertyEditorSupport; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.annotation.PreDestroy; + import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.config.RegistryConfig; @@ -28,24 +43,11 @@ import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.propertyeditors.StringTrimmerEditor; -import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata; -import com.alibaba.cloud.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.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -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; +import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata; +import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata; /** * Dubbo {@link GenericService} Factory @@ -54,104 +56,114 @@ import static org.springframework.util.StringUtils.commaDelimitedListToStringArr */ public class DubboGenericServiceFactory { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); - private final ConcurrentMap> cache = new ConcurrentHashMap<>(); + private final ConcurrentMap> cache = new ConcurrentHashMap<>(); - @Autowired - private ObjectProvider> registryConfigs; + @Autowired + private ObjectProvider> registryConfigs; - public GenericService create(DubboRestServiceMetadata dubboServiceMetadata, - Map dubboTranslatedAttributes) { + public GenericService create(DubboRestServiceMetadata dubboServiceMetadata, + Map dubboTranslatedAttributes) { - ReferenceBean referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTranslatedAttributes); + ReferenceBean referenceBean = build( + dubboServiceMetadata.getServiceRestMetadata(), dubboTranslatedAttributes); - return referenceBean == null ? null : referenceBean.get(); - } + return referenceBean == null ? null : referenceBean.get(); + } - public GenericService create(String serviceName, Class serviceClass, String version) { - String interfaceName = serviceClass.getName(); - ReferenceBean referenceBean = build(interfaceName, version, serviceName, emptyMap()); - return referenceBean.get(); - } + public GenericService create(String serviceName, Class serviceClass, + String version) { + String interfaceName = serviceClass.getName(); + ReferenceBean referenceBean = build(interfaceName, version, + serviceName, emptyMap()); + return referenceBean.get(); + } + private ReferenceBean build(ServiceRestMetadata serviceRestMetadata, + Map dubboTranslatedAttributes) { + String urlValue = serviceRestMetadata.getUrl(); + URL url = URL.valueOf(urlValue); + String interfaceName = url.getServiceInterface(); + String version = url.getParameter(VERSION_KEY); + String group = url.getParameter(GROUP_KEY); - private ReferenceBean build(ServiceRestMetadata serviceRestMetadata, - Map dubboTranslatedAttributes) { - String urlValue = serviceRestMetadata.getUrl(); - URL url = URL.valueOf(urlValue); - String interfaceName = url.getServiceInterface(); - String version = url.getParameter(VERSION_KEY); - String group = url.getParameter(GROUP_KEY); + return build(interfaceName, version, group, dubboTranslatedAttributes); + } - return build(interfaceName, version, group, dubboTranslatedAttributes); - } + private ReferenceBean build(String interfaceName, String version, + String group, Map dubboTranslatedAttributes) { - private ReferenceBean build(String interfaceName, String version, String group, - Map dubboTranslatedAttributes) { + Integer key = Objects.hash(interfaceName, version, group, + dubboTranslatedAttributes); - Integer key = Objects.hash(interfaceName, version, group, dubboTranslatedAttributes); + return cache.computeIfAbsent(key, k -> { + ReferenceBean referenceBean = new ReferenceBean<>(); + referenceBean.setGeneric(true); + referenceBean.setInterface(interfaceName); + referenceBean.setVersion(version); + referenceBean.setGroup(group); + bindReferenceBean(referenceBean, dubboTranslatedAttributes); + return referenceBean; + }); + } - return cache.computeIfAbsent(key, k -> { - ReferenceBean referenceBean = new ReferenceBean<>(); - referenceBean.setGeneric(true); - referenceBean.setInterface(interfaceName); - referenceBean.setVersion(version); - referenceBean.setGroup(group); - bindReferenceBean(referenceBean, dubboTranslatedAttributes); - return referenceBean; - }); - } + private void bindReferenceBean(ReferenceBean referenceBean, + Map dubboTranslatedAttributes) { + DataBinder dataBinder = new DataBinder(referenceBean); + // Register CustomEditors for special fields + dataBinder.registerCustomEditor(String.class, "filter", + new StringTrimmerEditor(true)); + dataBinder.registerCustomEditor(String.class, "listener", + new StringTrimmerEditor(true)); + dataBinder.registerCustomEditor(Map.class, "parameters", + new PropertyEditorSupport() { - private void bindReferenceBean(ReferenceBean referenceBean, Map dubboTranslatedAttributes) { - DataBinder dataBinder = new DataBinder(referenceBean); - // Register CustomEditors for special fields - dataBinder.registerCustomEditor(String.class, "filter", new StringTrimmerEditor(true)); - dataBinder.registerCustomEditor(String.class, "listener", new StringTrimmerEditor(true)); - dataBinder.registerCustomEditor(Map.class, "parameters", new PropertyEditorSupport() { + @Override + public void setAsText(String text) + throws java.lang.IllegalArgumentException { + // Trim all whitespace + String content = StringUtils.trimAllWhitespace(text); + if (!StringUtils.hasText(content)) { // No content , ignore + // directly + return; + } + // replace "=" to "," + content = StringUtils.replace(content, "=", ","); + // replace ":" to "," + content = StringUtils.replace(content, ":", ","); + // String[] to Map + Map parameters = CollectionUtils + .toStringMap(commaDelimitedListToStringArray(content)); + setValue(parameters); + } + }); - @Override - public void setAsText(String text) throws java.lang.IllegalArgumentException { - // Trim all whitespace - String content = StringUtils.trimAllWhitespace(text); - if (!StringUtils.hasText(content)) { // No content , ignore directly - return; - } - // replace "=" to "," - content = StringUtils.replace(content, "=", ","); - // replace ":" to "," - content = StringUtils.replace(content, ":", ","); - // String[] to Map - Map parameters = CollectionUtils.toStringMap(commaDelimitedListToStringArray(content)); - setValue(parameters); - } - }); + // ignore "registries" field and then use RegistryConfig beans + dataBinder.setDisallowedFields("registries"); - // ignore "registries" field and then use RegistryConfig beans - dataBinder.setDisallowedFields("registries"); + dataBinder.bind(new MutablePropertyValues(dubboTranslatedAttributes)); - dataBinder.bind(new MutablePropertyValues(dubboTranslatedAttributes)); + registryConfigs.ifAvailable(referenceBean::setRegistries); + } - registryConfigs.ifAvailable(referenceBean::setRegistries); - } + @PreDestroy + public void destroy() { + destroyReferenceBeans(); + cache.values(); + } - @PreDestroy - public void destroy() { - destroyReferenceBeans(); - cache.values(); - } - - private void destroyReferenceBeans() { - Collection> referenceBeans = cache.values(); - if (logger.isInfoEnabled()) { - logger.info("The Dubbo GenericService ReferenceBeans are destroying..."); - } - for (ReferenceBean referenceBean : referenceBeans) { - referenceBean.destroy(); // destroy ReferenceBean - if (logger.isInfoEnabled()) { - logger.info("Destroyed the ReferenceBean : {} ", referenceBean); - } - } - } + private void destroyReferenceBeans() { + Collection> referenceBeans = cache.values(); + if (logger.isInfoEnabled()) { + logger.info("The Dubbo GenericService ReferenceBeans are destroying..."); + } + for (ReferenceBean referenceBean : referenceBeans) { + referenceBean.destroy(); // destroy ReferenceBean + if (logger.isInfoEnabled()) { + logger.info("Destroyed the ReferenceBean : {} ", referenceBean); + } + } + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataService.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataService.java index 0c491952..134bb028 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataService.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataService.java @@ -16,61 +16,63 @@ */ package com.alibaba.cloud.dubbo.service; +import java.util.List; +import java.util.Map; +import java.util.Set; + import org.apache.dubbo.common.URL; import org.apache.dubbo.config.annotation.Service; import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata; -import java.util.List; -import java.util.Map; -import java.util.Set; - /** - * Dubbo Metadata Service is a core interface for service subscribers, - * it must keep the stable of structure in every evolution , makes sure all subscribers' compatibility. + * Dubbo Metadata Service is a core interface for service subscribers, it must keep the + * stable of structure in every evolution , makes sure all subscribers' compatibility. *

- * The interface contract's version must be {@link #VERSION} constant and group must be current Dubbo application name + * The interface contract's version must be {@link #VERSION} constant and group must be + * current Dubbo application name * * @author Mercy */ public interface DubboMetadataService { - /** - * Current version of the interface contract - */ - String VERSION = "1.0.0"; + /** + * Current version of the interface contract + */ + String VERSION = "1.0.0"; - /** - * Get the json content of {@link ServiceRestMetadata} {@link Set} - * - * @return null if present - */ - String getServiceRestMetadata(); + /** + * Get the json content of {@link ServiceRestMetadata} {@link Set} + * + * @return null if present + */ + String getServiceRestMetadata(); + /** + * Get all exported {@link URL#getServiceKey() service keys} + * + * @return non-null read-only {@link Set} + */ + Set getAllServiceKeys(); - /** - * Get all exported {@link URL#getServiceKey() service keys} - * - * @return non-null read-only {@link Set} - */ - Set getAllServiceKeys(); + /** + * Get all exported Dubbo's {@link URL URLs} {@link Map} whose key is the return value + * of {@link URL#getServiceKey()} method and value is the json content of List of + * {@link URL URLs} + * + * @return non-null read-only {@link Map} + */ + Map getAllExportedURLs(); - /** - * Get all exported Dubbo's {@link URL URLs} {@link Map} whose key is the return value of - * {@link URL#getServiceKey()} method and value is the json content of List of {@link URL URLs} - * - * @return non-null read-only {@link Map} - */ - Map getAllExportedURLs(); - - /** - * Get the json content of an exported List of {@link URL URLs} by the serviceInterface , group and version - * - * @param serviceInterface The class name of service interface - * @param group {@link Service#group() the service group} (optional) - * @param version {@link Service#version() the service version} (optional) - * @return non-null read-only {@link List} - * @see URL - */ - String getExportedURLs(String serviceInterface, String group, String version); + /** + * Get the json content of an exported List of {@link URL URLs} by the + * serviceInterface , group and version + * + * @param serviceInterface The class name of service interface + * @param group {@link Service#group() the service group} (optional) + * @param version {@link Service#version() the service version} (optional) + * @return non-null read-only {@link List} + * @see URL + */ + String getExportedURLs(String serviceInterface, String group, String version); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceExporter.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceExporter.java index 7c04abd7..d8a248e2 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceExporter.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceExporter.java @@ -16,6 +16,11 @@ */ package com.alibaba.cloud.dubbo.service; +import java.util.List; +import java.util.function.Supplier; + +import javax.annotation.PreDestroy; + import org.apache.dubbo.common.URL; import org.apache.dubbo.config.ApplicationConfig; import org.apache.dubbo.config.ProtocolConfig; @@ -28,10 +33,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; -import javax.annotation.PreDestroy; -import java.util.List; -import java.util.function.Supplier; - /** * {@link DubboMetadataService} exporter * @@ -40,70 +41,71 @@ import java.util.function.Supplier; @Component public class DubboMetadataServiceExporter { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); - @Autowired - private ApplicationConfig applicationConfig; + @Autowired + private ApplicationConfig applicationConfig; - @Autowired - private ObjectProvider dubboMetadataService; + @Autowired + private ObjectProvider dubboMetadataService; - @Autowired - private Supplier protocolConfigSupplier; + @Autowired + private Supplier protocolConfigSupplier; - @Value("${spring.application.name:${dubbo.application.name:application}}") - private String currentApplicationName; + @Value("${spring.application.name:${dubbo.application.name:application}}") + private String currentApplicationName; - /** - * The ServiceConfig of DubboMetadataConfigService to be exported, can be nullable. - */ - private ServiceConfig serviceConfig; + /** + * The ServiceConfig of DubboMetadataConfigService to be exported, can be nullable. + */ + private ServiceConfig serviceConfig; - /** - * export {@link DubboMetadataService} as Dubbo service - * - * @return the exported {@link URL URLs} - */ - public List export() { + /** + * export {@link DubboMetadataService} as Dubbo service + * + * @return the exported {@link URL URLs} + */ + public List export() { - if (serviceConfig == null || !serviceConfig.isExported()) { + if (serviceConfig == null || !serviceConfig.isExported()) { - serviceConfig = new ServiceConfig<>(); + serviceConfig = new ServiceConfig<>(); - serviceConfig.setInterface(DubboMetadataService.class); - // Use DubboMetadataService.VERSION as the Dubbo Service version - serviceConfig.setVersion(DubboMetadataService.VERSION); - // Use current Spring application name as the Dubbo Service group - serviceConfig.setGroup(currentApplicationName); - serviceConfig.setRef(dubboMetadataService.getIfAvailable()); - serviceConfig.setApplication(applicationConfig); - serviceConfig.setProtocol(protocolConfigSupplier.get()); + serviceConfig.setInterface(DubboMetadataService.class); + // Use DubboMetadataService.VERSION as the Dubbo Service version + serviceConfig.setVersion(DubboMetadataService.VERSION); + // Use current Spring application name as the Dubbo Service group + serviceConfig.setGroup(currentApplicationName); + serviceConfig.setRef(dubboMetadataService.getIfAvailable()); + serviceConfig.setApplication(applicationConfig); + serviceConfig.setProtocol(protocolConfigSupplier.get()); - serviceConfig.export(); + serviceConfig.export(); - if (logger.isInfoEnabled()) { - logger.info("The Dubbo service[{}] has been exported.", serviceConfig.toString()); - } - } + if (logger.isInfoEnabled()) { + logger.info("The Dubbo service[{}] has been exported.", + serviceConfig.toString()); + } + } - return serviceConfig.getExportedUrls(); - } + return serviceConfig.getExportedUrls(); + } + /** + * unexport {@link DubboMetadataService} + */ + @PreDestroy + public void unexport() { - /** - * unexport {@link DubboMetadataService} - */ - @PreDestroy - public void unexport() { + if (serviceConfig == null || serviceConfig.isUnexported()) { + return; + } - if (serviceConfig == null || serviceConfig.isUnexported()) { - return; - } + serviceConfig.unexport(); - serviceConfig.unexport(); - - if (logger.isInfoEnabled()) { - logger.info("The Dubbo service[{}] has been unexported.", serviceConfig.toString()); - } - } + if (logger.isInfoEnabled()) { + logger.info("The Dubbo service[{}] has been unexported.", + serviceConfig.toString()); + } + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceInvocationHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceInvocationHandler.java index 9312581f..ca4b05f6 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceInvocationHandler.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceInvocationHandler.java @@ -16,15 +16,15 @@ */ package com.alibaba.cloud.dubbo.service; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.stream.Stream; + import org.apache.dubbo.rpc.service.GenericService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.util.stream.Stream; - /** * {@link DubboMetadataService} {@link InvocationHandler} * @@ -32,29 +32,34 @@ import java.util.stream.Stream; */ class DubboMetadataServiceInvocationHandler implements InvocationHandler { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); - private final GenericService genericService; + private final GenericService genericService; - public DubboMetadataServiceInvocationHandler(String serviceName, String version, DubboGenericServiceFactory dubboGenericServiceFactory) { - this.genericService = dubboGenericServiceFactory.create(serviceName, DubboMetadataService.class, version); - } + public DubboMetadataServiceInvocationHandler(String serviceName, String version, + DubboGenericServiceFactory dubboGenericServiceFactory) { + this.genericService = dubboGenericServiceFactory.create(serviceName, + DubboMetadataService.class, version); + } - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - Object returnValue = null; - try { - returnValue = genericService.$invoke(method.getName(), getParameterTypes(method), args); - } catch (Throwable e) { - if (logger.isErrorEnabled()) { - logger.error(e.getMessage(), e); - } - } - return returnValue; - } + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Object returnValue = null; + try { + returnValue = genericService.$invoke(method.getName(), + getParameterTypes(method), args); + } + catch (Throwable e) { + if (logger.isErrorEnabled()) { + logger.error(e.getMessage(), e); + } + } + return returnValue; + } - private String[] getParameterTypes(Method method) { - Class[] parameterTypes = method.getParameterTypes(); - return Stream.of(parameterTypes).map(Class::getName).toArray(length -> new String[length]); - } + private String[] getParameterTypes(Method method) { + Class[] parameterTypes = method.getParameterTypes(); + return Stream.of(parameterTypes).map(Class::getName) + .toArray(length -> new String[length]); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java index da57fd55..1c13a23f 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/DubboMetadataServiceProxy.java @@ -16,69 +16,81 @@ */ package com.alibaba.cloud.dubbo.service; -import org.springframework.beans.factory.BeanClassLoaderAware; -import org.springframework.beans.factory.DisposableBean; +import static java.lang.reflect.Proxy.newProxyInstance; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import static java.lang.reflect.Proxy.newProxyInstance; +import org.springframework.beans.factory.BeanClassLoaderAware; +import org.springframework.beans.factory.DisposableBean; /** * The proxy of {@link DubboMetadataService} + * + * @author Mercy */ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, DisposableBean { - private final DubboGenericServiceFactory dubboGenericServiceFactory; + private final DubboGenericServiceFactory dubboGenericServiceFactory; + private final Map dubboMetadataServiceCache = new ConcurrentHashMap<>(); + private ClassLoader classLoader; - private ClassLoader classLoader; + public DubboMetadataServiceProxy( + DubboGenericServiceFactory dubboGenericServiceFactory) { + this.dubboGenericServiceFactory = dubboGenericServiceFactory; + } - private final Map dubboMetadataServiceCache = new ConcurrentHashMap<>(); - - public DubboMetadataServiceProxy(DubboGenericServiceFactory dubboGenericServiceFactory) { - this.dubboGenericServiceFactory = dubboGenericServiceFactory; - } + /** + * Initializes {@link DubboMetadataService}'s Proxy + * + * @param serviceName the service name + * @param version the service version + * @return a {@link DubboMetadataService} proxy + */ + public DubboMetadataService initProxy(String serviceName, String version) { + return dubboMetadataServiceCache.computeIfAbsent(serviceName, + name -> newProxy(name, version)); + } /** - * Initializes {@link DubboMetadataService}'s Proxy - * + * Remove {@link DubboMetadataService}'s Proxy by service name * @param serviceName the service name - * @param version the service version - * @return a {@link DubboMetadataService} proxy */ - public DubboMetadataService initProxy(String serviceName, String version) { - return dubboMetadataServiceCache.computeIfAbsent(serviceName, name -> newProxy(name, version)); - } + public void removeProxy(String serviceName) { + dubboMetadataServiceCache.remove(serviceName); + } - /** - * Get a proxy instance of {@link DubboMetadataService} via the specified service name - * - * @param serviceName the service name - * @return a {@link DubboMetadataService} proxy - */ - public DubboMetadataService getProxy(String serviceName) { - return dubboMetadataServiceCache.get(serviceName); - } + /** + * Get a proxy instance of {@link DubboMetadataService} via the specified service name + * + * @param serviceName the service name + * @return a {@link DubboMetadataService} proxy + */ + public DubboMetadataService getProxy(String serviceName) { + return dubboMetadataServiceCache.get(serviceName); + } - @Override - public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - } + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } - @Override - public void destroy() throws Exception { - dubboMetadataServiceCache.clear(); - } + @Override + public void destroy() throws Exception { + dubboMetadataServiceCache.clear(); + } - /** - * New a proxy instance of {@link DubboMetadataService} via the specified service name - * - * @param serviceName the service name - * @param version the service version - * @return a {@link DubboMetadataService} proxy - */ - protected DubboMetadataService newProxy(String serviceName, String version) { - return (DubboMetadataService) newProxyInstance(classLoader, new Class[]{DubboMetadataService.class}, - new DubboMetadataServiceInvocationHandler(serviceName, version, dubboGenericServiceFactory)); - } + /** + * New a proxy instance of {@link DubboMetadataService} via the specified service name + * + * @param serviceName the service name + * @param version the service version + * @return a {@link DubboMetadataService} proxy + */ + protected DubboMetadataService newProxy(String serviceName, String version) { + return (DubboMetadataService) newProxyInstance(classLoader, + new Class[] { DubboMetadataService.class }, + new DubboMetadataServiceInvocationHandler(serviceName, version, + dubboGenericServiceFactory)); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/IntrospectiveDubboMetadataService.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/IntrospectiveDubboMetadataService.java index 3e4e1a66..b4d8cc20 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/IntrospectiveDubboMetadataService.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/IntrospectiveDubboMetadataService.java @@ -16,15 +16,8 @@ */ package com.alibaba.cloud.dubbo.service; -import org.apache.dubbo.common.URL; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Autowired; -import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata; -import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; -import com.alibaba.cloud.dubbo.util.JSONUtils; +import static java.util.Collections.unmodifiableMap; +import static org.springframework.util.CollectionUtils.isEmpty; import java.util.Collections; import java.util.HashMap; @@ -32,8 +25,16 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static java.util.Collections.unmodifiableMap; -import static org.springframework.util.CollectionUtils.isEmpty; +import org.apache.dubbo.common.URL; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; + +import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata; +import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import com.alibaba.cloud.dubbo.util.JSONUtils; /** * Introspective {@link DubboMetadataService} implementation @@ -42,55 +43,57 @@ import static org.springframework.util.CollectionUtils.isEmpty; */ public class IntrospectiveDubboMetadataService implements DubboMetadataService { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); - @Autowired - private ObjectProvider dubboServiceMetadataRepository; + @Autowired + private ObjectProvider dubboServiceMetadataRepository; - @Autowired - private JSONUtils jsonUtils; + @Autowired + private JSONUtils jsonUtils; - @Override - public String getServiceRestMetadata() { - Set serviceRestMetadata = getRepository().getServiceRestMetadata(); - String serviceRestMetadataJsonConfig = null; - if (!isEmpty(serviceRestMetadata)) { - serviceRestMetadataJsonConfig = jsonUtils.toJSON(serviceRestMetadata); - } - return serviceRestMetadataJsonConfig; - } + @Override + public String getServiceRestMetadata() { + Set serviceRestMetadata = getRepository() + .getServiceRestMetadata(); + String serviceRestMetadataJsonConfig = null; + if (!isEmpty(serviceRestMetadata)) { + serviceRestMetadataJsonConfig = jsonUtils.toJSON(serviceRestMetadata); + } + return serviceRestMetadataJsonConfig; + } - @Override - public Set getAllServiceKeys() { - return getRepository().getAllServiceKeys(); - } + @Override + public Set getAllServiceKeys() { + return getRepository().getAllServiceKeys(); + } - @Override - public Map getAllExportedURLs() { - Map> allExportedUrls = getRepository().getAllExportedUrls(); - if (isEmpty(allExportedUrls)) { - if (logger.isDebugEnabled()) { - logger.debug("There is no registered URL."); - } - return Collections.emptyMap(); - } + @Override + public Map getAllExportedURLs() { + Map> allExportedUrls = getRepository().getAllExportedUrls(); + if (isEmpty(allExportedUrls)) { + if (logger.isDebugEnabled()) { + logger.debug("There is no registered URL."); + } + return Collections.emptyMap(); + } - Map result = new HashMap<>(); + Map result = new HashMap<>(); - allExportedUrls.forEach((serviceKey, urls) -> { - result.put(serviceKey, jsonUtils.toJSON(urls)); - }); + allExportedUrls.forEach((serviceKey, urls) -> { + result.put(serviceKey, jsonUtils.toJSON(urls)); + }); - return unmodifiableMap(result); - } + return unmodifiableMap(result); + } - @Override - public String getExportedURLs(String serviceInterface, String group, String version) { - List urls = getRepository().getExportedURLs(serviceInterface, group, version); - return jsonUtils.toJSON(urls); - } + @Override + public String getExportedURLs(String serviceInterface, String group, String version) { + List urls = getRepository().getExportedURLs(serviceInterface, group, + version); + return jsonUtils.toJSON(urls); + } - private DubboServiceMetadataRepository getRepository() { - return dubboServiceMetadataRepository.getIfAvailable(); - } + private DubboServiceMetadataRepository getRepository() { + return dubboServiceMetadataRepository.getIfAvailable(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/AbstractDubboGenericServiceParameterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/AbstractDubboGenericServiceParameterResolver.java index 8b6b1d6b..4d63363e 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/AbstractDubboGenericServiceParameterResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/AbstractDubboGenericServiceParameterResolver.java @@ -58,15 +58,15 @@ public abstract class AbstractDubboGenericServiceParameterResolver this.classLoader = classLoader; } - public void setOrder(int order) { - this.order = order; - } - @Override public int getOrder() { return order; } + public void setOrder(int order) { + this.order = order; + } + protected Class resolveClass(String className) { return resolveClassName(className, classLoader); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/AbstractNamedValueServiceParameterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/AbstractNamedValueServiceParameterResolver.java index 721109f3..50fc7b76 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/AbstractNamedValueServiceParameterResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/AbstractNamedValueServiceParameterResolver.java @@ -16,107 +16,117 @@ */ package com.alibaba.cloud.dubbo.service.parameter; -import com.alibaba.cloud.dubbo.http.HttpServerRequest; -import com.alibaba.cloud.dubbo.metadata.MethodParameterMetadata; -import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; -import org.springframework.util.CollectionUtils; -import org.springframework.util.MultiValueMap; +import static org.springframework.util.ObjectUtils.isEmpty; import java.util.Collection; import java.util.Collections; import java.util.Map; -import static org.springframework.util.ObjectUtils.isEmpty; +import org.springframework.util.CollectionUtils; +import org.springframework.util.MultiValueMap; + +import com.alibaba.cloud.dubbo.http.HttpServerRequest; +import com.alibaba.cloud.dubbo.metadata.MethodParameterMetadata; +import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; /** - * Abstract HTTP Names Value {@link DubboGenericServiceParameterResolver Dubbo GenericService Parameter Resolver} + * Abstract HTTP Names Value {@link DubboGenericServiceParameterResolver Dubbo + * GenericService Parameter Resolver} * * @author Mercy */ -public abstract class AbstractNamedValueServiceParameterResolver extends AbstractDubboGenericServiceParameterResolver { +public abstract class AbstractNamedValueServiceParameterResolver + extends AbstractDubboGenericServiceParameterResolver { - /** - * Get the {@link MultiValueMap} of names and values - * - * @param request - * @return - */ - protected abstract MultiValueMap getNameAndValuesMap(HttpServerRequest request); + /** + * Get the {@link MultiValueMap} of names and values + * + * @param request + * @return + */ + protected abstract MultiValueMap getNameAndValuesMap( + HttpServerRequest request); - @Override - public Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata, - HttpServerRequest request) { + @Override + public Object resolve(RestMethodMetadata restMethodMetadata, + MethodParameterMetadata methodParameterMetadata, HttpServerRequest request) { - Collection names = getNames(restMethodMetadata, methodParameterMetadata); + Collection names = getNames(restMethodMetadata, methodParameterMetadata); - if (isEmpty(names)) { // index can't match - return null; - } + if (isEmpty(names)) { // index can't match + return null; + } - MultiValueMap nameAndValues = getNameAndValuesMap(request); + MultiValueMap nameAndValues = getNameAndValuesMap(request); - String targetName = null; + String targetName = null; - for (String name : names) { - if (nameAndValues.containsKey(name)) { - targetName = name; - break; - } - } + for (String name : names) { + if (nameAndValues.containsKey(name)) { + targetName = name; + break; + } + } - if (targetName == null) { // request parameter is abstract - return null; - } + if (targetName == null) { // request parameter is abstract + return null; + } - Class parameterType = resolveClass(methodParameterMetadata.getType()); + Class parameterType = resolveClass(methodParameterMetadata.getType()); - Object paramValue = null; + Object paramValue = null; - if (parameterType.isArray()) { // Array type - paramValue = nameAndValues.get(targetName); - } else { - paramValue = nameAndValues.getFirst(targetName); - } + if (parameterType.isArray()) { // Array type + paramValue = nameAndValues.get(targetName); + } + else { + paramValue = nameAndValues.getFirst(targetName); + } - return resolveValue(paramValue, parameterType); - } + return resolveValue(paramValue, parameterType); + } - @Override - public Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata, - RestMethodMetadata clientRestMethodMetadata, Object[] arguments) { + @Override + public Object resolve(RestMethodMetadata restMethodMetadata, + MethodParameterMetadata methodParameterMetadata, + RestMethodMetadata clientRestMethodMetadata, Object[] arguments) { - Collection names = getNames(restMethodMetadata, methodParameterMetadata); + Collection names = getNames(restMethodMetadata, methodParameterMetadata); - if (isEmpty(names)) { // index can't match - return null; - } + if (isEmpty(names)) { // index can't match + return null; + } - Integer index = null; + Integer index = null; - Map> clientIndexToName = clientRestMethodMetadata.getIndexToName(); + Map> clientIndexToName = clientRestMethodMetadata + .getIndexToName(); - for (Map.Entry> entry : clientIndexToName.entrySet()) { + for (Map.Entry> entry : clientIndexToName + .entrySet()) { - Collection clientParamNames = entry.getValue(); + Collection clientParamNames = entry.getValue(); - if (CollectionUtils.containsAny(names, clientParamNames)) { - index = entry.getKey(); - break; - } - } + if (CollectionUtils.containsAny(names, clientParamNames)) { + index = entry.getKey(); + break; + } + } - return index > -1 ? arguments[index] : null; - } + return index > -1 ? arguments[index] : null; + } - protected Collection getNames(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata) { + protected Collection getNames(RestMethodMetadata restMethodMetadata, + MethodParameterMetadata methodParameterMetadata) { - Map> indexToName = restMethodMetadata.getIndexToName(); + Map> indexToName = restMethodMetadata + .getIndexToName(); - int index = methodParameterMetadata.getIndex(); + int index = methodParameterMetadata.getIndex(); - Collection paramNames = indexToName.get(index); + Collection paramNames = indexToName.get(index); - return paramNames == null ? Collections.emptyList() : paramNames; - } + return paramNames == null ? Collections.emptyList() : paramNames; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/DubboGenericServiceParameterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/DubboGenericServiceParameterResolver.java index 1700c303..9fc4a609 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/DubboGenericServiceParameterResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/DubboGenericServiceParameterResolver.java @@ -16,13 +16,14 @@ */ package com.alibaba.cloud.dubbo.service.parameter; -import com.alibaba.cloud.dubbo.metadata.MethodParameterMetadata; - import org.apache.dubbo.rpc.service.GenericService; -import com.alibaba.cloud.dubbo.http.HttpServerRequest; -import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; + import org.springframework.core.Ordered; +import com.alibaba.cloud.dubbo.http.HttpServerRequest; +import com.alibaba.cloud.dubbo.metadata.MethodParameterMetadata; +import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; + /** * Dubbo {@link GenericService} Parameter Resolver * @@ -30,14 +31,15 @@ import org.springframework.core.Ordered; */ public interface DubboGenericServiceParameterResolver extends Ordered { - /** - * Resolves a method parameter into an argument value from a given request. - * - * @return - */ - Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata, - HttpServerRequest request); + /** + * Resolves a method parameter into an argument value from a given request. + * + * @return + */ + Object resolve(RestMethodMetadata restMethodMetadata, + MethodParameterMetadata methodParameterMetadata, HttpServerRequest request); - Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata, - RestMethodMetadata clientRestMethodMetadata, Object[] arguments); + Object resolve(RestMethodMetadata restMethodMetadata, + MethodParameterMetadata methodParameterMetadata, + RestMethodMetadata clientRestMethodMetadata, Object[] arguments); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/PathVariableServiceParameterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/PathVariableServiceParameterResolver.java index e861fa60..0bf093d3 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/PathVariableServiceParameterResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/PathVariableServiceParameterResolver.java @@ -16,25 +16,29 @@ */ package com.alibaba.cloud.dubbo.service.parameter; -import com.alibaba.cloud.dubbo.http.HttpServerRequest; import org.springframework.util.MultiValueMap; +import com.alibaba.cloud.dubbo.http.HttpServerRequest; + /** - * HTTP Request Path Variable {@link DubboGenericServiceParameterResolver Dubbo GenericService Parameter Resolver} + * HTTP Request Path Variable {@link DubboGenericServiceParameterResolver Dubbo + * GenericService Parameter Resolver} * * @author Mercy */ -public class PathVariableServiceParameterResolver extends AbstractNamedValueServiceParameterResolver { +public class PathVariableServiceParameterResolver + extends AbstractNamedValueServiceParameterResolver { - public static final int DEFAULT_ORDER = 3; + public static final int DEFAULT_ORDER = 3; - public PathVariableServiceParameterResolver() { - super(); - setOrder(DEFAULT_ORDER); - } + public PathVariableServiceParameterResolver() { + super(); + setOrder(DEFAULT_ORDER); + } - @Override - protected MultiValueMap getNameAndValuesMap(HttpServerRequest request) { - return request.getQueryParams(); - } + @Override + protected MultiValueMap getNameAndValuesMap( + HttpServerRequest request) { + return request.getQueryParams(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestBodyServiceParameterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestBodyServiceParameterResolver.java index f9c2020c..7e3fa88e 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestBodyServiceParameterResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestBodyServiceParameterResolver.java @@ -16,102 +16,112 @@ */ package com.alibaba.cloud.dubbo.service.parameter; +import java.io.IOException; +import java.util.Collections; +import java.util.Objects; + +import javax.annotation.PostConstruct; + import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; + import com.alibaba.cloud.dubbo.http.HttpServerRequest; import com.alibaba.cloud.dubbo.http.converter.HttpMessageConverterHolder; import com.alibaba.cloud.dubbo.http.util.HttpMessageConverterResolver; import com.alibaba.cloud.dubbo.metadata.MethodParameterMetadata; import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.HttpMessageNotReadableException; - -import javax.annotation.PostConstruct; -import java.io.IOException; -import java.util.Collections; -import java.util.Objects; /** * HTTP Request Body {@link DubboGenericServiceParameterResolver} * * @author Mercy */ -public class RequestBodyServiceParameterResolver extends AbstractDubboGenericServiceParameterResolver { +public class RequestBodyServiceParameterResolver + extends AbstractDubboGenericServiceParameterResolver { - public static final int DEFAULT_ORDER = 7; + public static final int DEFAULT_ORDER = 7; - @Autowired - private ObjectProvider httpMessageConverters; + @Autowired + private ObjectProvider httpMessageConverters; - private HttpMessageConverterResolver httpMessageConverterResolver; + private HttpMessageConverterResolver httpMessageConverterResolver; - public RequestBodyServiceParameterResolver() { - super(); - setOrder(DEFAULT_ORDER); - } + public RequestBodyServiceParameterResolver() { + super(); + setOrder(DEFAULT_ORDER); + } - @PostConstruct - public void init() { - HttpMessageConverters httpMessageConverters = this.httpMessageConverters.getIfAvailable(); + @PostConstruct + public void init() { + HttpMessageConverters httpMessageConverters = this.httpMessageConverters + .getIfAvailable(); - httpMessageConverterResolver = new HttpMessageConverterResolver(httpMessageConverters == null ? - Collections.emptyList() : httpMessageConverters.getConverters(), - getClassLoader()); - } + httpMessageConverterResolver = new HttpMessageConverterResolver( + httpMessageConverters == null ? Collections.emptyList() + : httpMessageConverters.getConverters(), + getClassLoader()); + } - private boolean supportParameter(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata) { + private boolean supportParameter(RestMethodMetadata restMethodMetadata, + MethodParameterMetadata methodParameterMetadata) { - Integer index = methodParameterMetadata.getIndex(); + Integer index = methodParameterMetadata.getIndex(); - Integer bodyIndex = restMethodMetadata.getBodyIndex(); + Integer bodyIndex = restMethodMetadata.getBodyIndex(); - if (!Objects.equals(index, bodyIndex)) { - return false; - } + if (!Objects.equals(index, bodyIndex)) { + return false; + } - Class parameterType = resolveClass(methodParameterMetadata.getType()); + Class parameterType = resolveClass(methodParameterMetadata.getType()); - Class bodyType = resolveClass(restMethodMetadata.getBodyType()); + Class bodyType = resolveClass(restMethodMetadata.getBodyType()); - return Objects.equals(parameterType, bodyType); - } + return Objects.equals(parameterType, bodyType); + } - @Override - public Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata, - HttpServerRequest request) { + @Override + public Object resolve(RestMethodMetadata restMethodMetadata, + MethodParameterMetadata methodParameterMetadata, HttpServerRequest request) { - if (!supportParameter(restMethodMetadata, methodParameterMetadata)) { - return null; - } + if (!supportParameter(restMethodMetadata, methodParameterMetadata)) { + return null; + } - Object result = null; + Object result = null; - Class parameterType = resolveClass(methodParameterMetadata.getType()); + Class parameterType = resolveClass(methodParameterMetadata.getType()); - HttpMessageConverterHolder holder = httpMessageConverterResolver.resolve(request, parameterType); + HttpMessageConverterHolder holder = httpMessageConverterResolver.resolve(request, + parameterType); - if (holder != null) { - HttpMessageConverter converter = holder.getConverter(); - try { - result = converter.read(parameterType, request); - } catch (IOException e) { - throw new HttpMessageNotReadableException("I/O error while reading input message", e); - } - } + if (holder != null) { + HttpMessageConverter converter = holder.getConverter(); + try { + result = converter.read(parameterType, request); + } + catch (IOException e) { + throw new HttpMessageNotReadableException( + "I/O error while reading input message", e); + } + } - return result; - } + return result; + } - @Override - public Object resolve(RestMethodMetadata restMethodMetadata, MethodParameterMetadata methodParameterMetadata, - RestMethodMetadata clientRestMethodMetadata, Object[] arguments) { + @Override + public Object resolve(RestMethodMetadata restMethodMetadata, + MethodParameterMetadata methodParameterMetadata, + RestMethodMetadata clientRestMethodMetadata, Object[] arguments) { - if (!supportParameter(restMethodMetadata, methodParameterMetadata)) { - return null; - } + if (!supportParameter(restMethodMetadata, methodParameterMetadata)) { + return null; + } - Integer clientBodyIndex = clientRestMethodMetadata.getBodyIndex(); - return arguments[clientBodyIndex]; - } + Integer clientBodyIndex = clientRestMethodMetadata.getBodyIndex(); + return arguments[clientBodyIndex]; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestHeaderServiceParameterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestHeaderServiceParameterResolver.java index 7581f466..4656ad01 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestHeaderServiceParameterResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestHeaderServiceParameterResolver.java @@ -16,25 +16,29 @@ */ package com.alibaba.cloud.dubbo.service.parameter; -import com.alibaba.cloud.dubbo.http.HttpServerRequest; import org.springframework.util.MultiValueMap; +import com.alibaba.cloud.dubbo.http.HttpServerRequest; + /** - * HTTP Request Header {@link DubboGenericServiceParameterResolver Dubbo GenericService Parameter Resolver} + * HTTP Request Header {@link DubboGenericServiceParameterResolver Dubbo GenericService + * Parameter Resolver} * * @author Mercy */ -public class RequestHeaderServiceParameterResolver extends AbstractNamedValueServiceParameterResolver { +public class RequestHeaderServiceParameterResolver + extends AbstractNamedValueServiceParameterResolver { - public static final int DEFAULT_ORDER = 9; + public static final int DEFAULT_ORDER = 9; - public RequestHeaderServiceParameterResolver() { - super(); - setOrder(DEFAULT_ORDER); - } + public RequestHeaderServiceParameterResolver() { + super(); + setOrder(DEFAULT_ORDER); + } - @Override - protected MultiValueMap getNameAndValuesMap(HttpServerRequest request) { - return request.getHeaders(); - } + @Override + protected MultiValueMap getNameAndValuesMap( + HttpServerRequest request) { + return request.getHeaders(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestParamServiceParameterResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestParamServiceParameterResolver.java index 18cd92d2..6ec3cdc1 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestParamServiceParameterResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/service/parameter/RequestParamServiceParameterResolver.java @@ -16,25 +16,29 @@ */ package com.alibaba.cloud.dubbo.service.parameter; -import com.alibaba.cloud.dubbo.http.HttpServerRequest; import org.springframework.util.MultiValueMap; +import com.alibaba.cloud.dubbo.http.HttpServerRequest; + /** - * HTTP Request Parameter {@link DubboGenericServiceParameterResolver Dubbo GenericService Parameter Resolver} + * HTTP Request Parameter {@link DubboGenericServiceParameterResolver Dubbo GenericService + * Parameter Resolver} * * @author Mercy */ -public class RequestParamServiceParameterResolver extends AbstractNamedValueServiceParameterResolver { +public class RequestParamServiceParameterResolver + extends AbstractNamedValueServiceParameterResolver { - public static final int DEFAULT_ORDER = 1; + public static final int DEFAULT_ORDER = 1; - public RequestParamServiceParameterResolver() { - super(); - setOrder(DEFAULT_ORDER); - } + public RequestParamServiceParameterResolver() { + super(); + setOrder(DEFAULT_ORDER); + } - @Override - protected MultiValueMap getNameAndValuesMap(HttpServerRequest request) { - return request.getQueryParams(); - } + @Override + protected MultiValueMap getNameAndValuesMap( + HttpServerRequest request) { + return request.getQueryParams(); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/util/JSONUtils.java b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/util/JSONUtils.java index a1290a18..94071d03 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/util/JSONUtils.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/com/alibaba/cloud/dubbo/util/JSONUtils.java @@ -16,22 +16,24 @@ */ package com.alibaba.cloud.dubbo.util; -import org.apache.dubbo.common.URL; - -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.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import javax.annotation.PostConstruct; + +import org.apache.dubbo.common.URL; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + /** * JSON Utilities class * @@ -39,47 +41,49 @@ import java.util.stream.Collectors; */ public class JSONUtils { - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final Logger logger = LoggerFactory.getLogger(getClass()); - private final ObjectMapper objectMapper = new ObjectMapper(); + private final ObjectMapper objectMapper = new ObjectMapper(); - @PostConstruct - public void init() { - this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT); - } + @PostConstruct + public void init() { + this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT); + } - public String toJSON(Collection urls) { - return toJSON(urls.stream().map(URL::toFullString).collect(Collectors.toSet())); - } + public String toJSON(Collection urls) { + return toJSON(urls.stream().map(URL::toFullString).collect(Collectors.toSet())); + } - 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 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 toURLs(String urlsJSON) { - List list = toList(urlsJSON); - return list.stream().map(URL::valueOf).collect(Collectors.toList()); - } + public List toURLs(String urlsJSON) { + List list = toList(urlsJSON); + return list.stream().map(URL::valueOf).collect(Collectors.toList()); + } - public List toList(String json) { - List list = Collections.emptyList(); - try { - if (StringUtils.hasText(json)) { - list = objectMapper.readValue(json, List.class); - } - } catch (IOException e) { - if (logger.isErrorEnabled()) { - logger.error(e.getMessage(), e); - } - } - return list; - } + public List toList(String json) { + List list = Collections.emptyList(); + try { + if (StringUtils.hasText(json)) { + list = objectMapper.readValue(json, List.class); + } + } + catch (IOException e) { + if (logger.isErrorEnabled()) { + logger.error(e.getMessage(), e); + } + } + return list; + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories index dbaa6c57..5969971e 100644 --- a/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories @@ -4,7 +4,8 @@ com.alibaba.cloud.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration,\ com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration,\ com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationNonWebApplicationAutoConfiguration,\ com.alibaba.cloud.dubbo.autoconfigure.DubboLoadBalancedRestTemplateAutoConfiguration,\ -com.alibaba.cloud.dubbo.autoconfigure.DubboServiceAutoConfiguration +com.alibaba.cloud.dubbo.autoconfigure.DubboServiceAutoConfiguration,\ +com.alibaba.cloud.dubbo.autoconfigure.DubboServiceDiscoveryAutoConfiguration org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=\ com.alibaba.cloud.dubbo.actuate.DubboMetadataEndpointAutoConfiguration diff --git a/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml b/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml index 8b18a688..5ad1812f 100644 --- a/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml +++ b/spring-cloud-alibaba-examples/acm-example/acm-local-example/pom.xml @@ -5,7 +5,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml b/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml index 4f27f00e..cfccf8c5 100644 --- a/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml +++ b/spring-cloud-alibaba-examples/ans-example/ans-consumer-feign-example/pom.xml @@ -5,7 +5,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml b/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml index 42b19550..e1bc8438 100644 --- a/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml +++ b/spring-cloud-alibaba-examples/ans-example/ans-consumer-ribbon-example/pom.xml @@ -5,7 +5,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml b/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml index 378e663c..c2d3265a 100644 --- a/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml +++ b/spring-cloud-alibaba-examples/ans-example/ans-provider-example/pom.xml @@ -5,7 +5,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml index 1f796717..ab411e5f 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-config-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml index c405cf5b..afa3904b 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud nacos-discovery-example - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/feign-defaultmethod-error.sh b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/feign-defaultmethod-error.sh new file mode 100644 index 00000000..6efb8368 --- /dev/null +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/scripts/feign-defaultmethod-error.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +n=1 +while [ $n -le 10 ] +do + echo `curl -s http://localhost:18083/divide-feign2?a=1` + let n++ +done diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java index 9be8af45..ab4ffd7e 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java @@ -49,6 +49,10 @@ public class ConsumerApplication { @RequestMapping(value = "/divide", method = RequestMethod.GET) String divide(@RequestParam("a") Integer a, @RequestParam("b") Integer b); + default String divide(Integer a) { + return divide(a, 0); + } + @RequestMapping(value = "/notFound", method = RequestMethod.GET) String notFound(); } diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/TestController.java b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/TestController.java index 6a6d81d4..e037cd33 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/TestController.java +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/java/com/alibaba/cloud/examples/TestController.java @@ -75,6 +75,11 @@ public class TestController { return echoService.divide(a, b); } + @RequestMapping(value = "/divide-feign2", method = RequestMethod.GET) + public String divide(@RequestParam Integer a) { + return echoService.divide(a); + } + @RequestMapping(value = "/echo-feign/{str}", method = RequestMethod.GET) public String feign(@PathVariable String str) { return echoService.echo(str); diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/flowrule.json b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/flowrule.json index 3dd01162..5fe40fcf 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/flowrule.json +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-consumer-example/src/main/resources/flowrule.json @@ -7,4 +7,4 @@ "limitApp": "default", "strategy": 0 } -] \ No newline at end of file +] diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml index b534ee8b..ca15c8ba 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-provider-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud nacos-discovery-example - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-spring-cloud-config-client/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-spring-cloud-config-client/pom.xml index 8cbd8768..116b91e9 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-spring-cloud-config-client/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-spring-cloud-config-client/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud nacos-discovery-example - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-spring-cloud-config-server/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-spring-cloud-config-server/pom.xml index fe7516fc..b09a7a17 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-spring-cloud-config-server/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/nacos-discovery-spring-cloud-config-server/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud nacos-discovery-example - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml index c3cf8324..0b996ec3 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-discovery-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-discovery-example/pom.xml index 79b4867b..a1c35a7d 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-discovery-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-discovery-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud nacos-gateway-example - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-provider-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-provider-example/pom.xml index 8153d629..8a0969af 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-provider-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/nacos-gateway-provider-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud nacos-gateway-example - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/pom.xml b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/pom.xml index 0a4eae60..a0c2e94e 100644 --- a/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/pom.xml +++ b/spring-cloud-alibaba-examples/nacos-example/nacos-gateway-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/oss-example/pom.xml b/spring-cloud-alibaba-examples/oss-example/pom.xml index 53309e10..c0fab28c 100644 --- a/spring-cloud-alibaba-examples/oss-example/pom.xml +++ b/spring-cloud-alibaba-examples/oss-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-examples/pom.xml b/spring-cloud-alibaba-examples/pom.xml index 4e9a155e..bb19d74e 100644 --- a/spring-cloud-alibaba-examples/pom.xml +++ b/spring-cloud-alibaba-examples/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0 @@ -20,6 +20,8 @@ sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api + sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example + sentinel-example/sentinel-feign-example/sentinel-feign-provider-example sentinel-example/sentinel-webflux-example sentinel-example/sentinel-spring-cloud-gateway-example sentinel-example/sentinel-zuul-example diff --git a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/pom.xml b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/pom.xml index b2f4be9e..495b3c48 100644 --- a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/pom.xml +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-consume-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/pom.xml b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/pom.xml index f3b93f86..1222a865 100644 --- a/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/pom.xml +++ b/spring-cloud-alibaba-examples/rocketmq-example/rocketmq-produce-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml b/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml index 7ac813f4..8c2e6b5e 100644 --- a/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml +++ b/spring-cloud-alibaba-examples/schedulerx-example/schedulerx-simple-task-example/pom.xml @@ -5,7 +5,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/seata-example/account-service/pom.xml b/spring-cloud-alibaba-examples/seata-example/account-service/pom.xml index e62f4f78..a85ebef2 100644 --- a/spring-cloud-alibaba-examples/seata-example/account-service/pom.xml +++ b/spring-cloud-alibaba-examples/seata-example/account-service/pom.xml @@ -5,7 +5,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/file.conf b/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/file.conf index fd3d304f..d686eeec 100644 --- a/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/file.conf +++ b/spring-cloud-alibaba-examples/seata-example/account-service/src/main/resources/file.conf @@ -28,7 +28,7 @@ transport { } service { #vgroup->rgroup - vgroup_mapping.account-service-fescar-service-group = "default" + vgroup_mapping.account-service-seata-service-group = "default" #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support diff --git a/spring-cloud-alibaba-examples/seata-example/business-service/pom.xml b/spring-cloud-alibaba-examples/seata-example/business-service/pom.xml index fbe5f508..4cffabf1 100644 --- a/spring-cloud-alibaba-examples/seata-example/business-service/pom.xml +++ b/spring-cloud-alibaba-examples/seata-example/business-service/pom.xml @@ -5,7 +5,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/file.conf b/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/file.conf index a766d498..b3047ec3 100644 --- a/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/file.conf +++ b/spring-cloud-alibaba-examples/seata-example/business-service/src/main/resources/file.conf @@ -28,7 +28,7 @@ transport { } service { #vgroup->rgroup - vgroup_mapping.business-service-fescar-service-group = "default" + vgroup_mapping.business-service-seata-service-group = "default" #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support diff --git a/spring-cloud-alibaba-examples/seata-example/order-service/pom.xml b/spring-cloud-alibaba-examples/seata-example/order-service/pom.xml index 5d4119ad..a257acc8 100644 --- a/spring-cloud-alibaba-examples/seata-example/order-service/pom.xml +++ b/spring-cloud-alibaba-examples/seata-example/order-service/pom.xml @@ -5,7 +5,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/file.conf b/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/file.conf index 536cc785..22184e04 100644 --- a/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/file.conf +++ b/spring-cloud-alibaba-examples/seata-example/order-service/src/main/resources/file.conf @@ -28,7 +28,7 @@ transport { } service { #vgroup->rgroup - vgroup_mapping.order-service-fescar-service-group = "default" + vgroup_mapping.order-service-seata-service-group = "default" #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support diff --git a/spring-cloud-alibaba-examples/seata-example/storage-service/pom.xml b/spring-cloud-alibaba-examples/seata-example/storage-service/pom.xml index 20b7c270..9c6b2c34 100644 --- a/spring-cloud-alibaba-examples/seata-example/storage-service/pom.xml +++ b/spring-cloud-alibaba-examples/seata-example/storage-service/pom.xml @@ -5,7 +5,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/file.conf b/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/file.conf index 90cb0ede..930a65c0 100644 --- a/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/file.conf +++ b/spring-cloud-alibaba-examples/seata-example/storage-service/src/main/resources/file.conf @@ -28,7 +28,7 @@ transport { } service { #vgroup->rgroup - vgroup_mapping.storage-service-fescar-service-group = "default" + vgroup_mapping.storage-service-seata-service-group = "default" #only support single node default.grouplist = "127.0.0.1:8091" #degrade current not support diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml index 011989ab..960b34ec 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 @@ -43,6 +43,10 @@ + + + + diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md index 8b3a9214..c5a8111f 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md @@ -19,7 +19,7 @@ ```xml - org.springframework.cloud + com.alibaba.cloud spring-cloud-starter-alibaba-sentinel ``` @@ -214,9 +214,9 @@ spring.cloud.sentinel.datasource.ds2.nacos.data-type=json `ds1` 和 `ds2` 表示ReadableDataSource的名称,可随意编写。`ds1` 和 `ds2` 后面的 `file` 和 `nacos` 表示ReadableDataSource的类型。 -目前支持`file`, `nacos`, `zk`, `apollo` 这4种类型。 +目前支持`file`, `nacos`, `zk`, `apollo`,`redis` 这5种类型。 -其中`nacos`,`zk`,`apollo`这3种类型的使用需要加上对应的依赖`sentinel-datasource-nacos`, `sentinel-datasource-zookeeper`, `sentinel-datasource-apollo`。 +其中`nacos`,`zk`,`apollo`,`redis` 这4种类型的使用需要加上对应的依赖`sentinel-datasource-nacos`, `sentinel-datasource-zookeeper`, `sentinel-datasource-apollo`, `sentinel-datasource-redis`。 当ReadableDataSource加载规则数据成功的时候,控制台会打印出相应的日志信息: diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md index b95a97d2..8cc2f53e 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme.md @@ -189,9 +189,9 @@ spring.cloud.sentinel.datasource.ds2.nacos.data-type=json `ds1` and `ds2` means the name of ReadableDataSource, you can write whatever you want. The `file` and `nacos` after name `ds1` and `ds2` means the type of ReadableDataSource. -Now ReadableDataSource type support 4 categories: `file`, `nacos`, `zk` and `apollo`. +Now ReadableDataSource type support 5 categories: `file`, `nacos`, `zk`, `apollo` and `redis`. -If you want to use `nacos`, `zk` or `apollo` ReadableDataSource, you could add `sentinel-datasource-nacos`, `sentinel-datasource-zookeeper` or `sentinel-datasource-apollo` dependency. +If you want to use `nacos`, `zk`, `apollo` or `redis` ReadableDataSource, you could add `sentinel-datasource-nacos`, `sentinel-datasource-zookeeper`,`sentinel-datasource-apollo` or `sentinel-datasource-redis` dependency. When ReadableDataSource load rule data successfully, console will print some logs: @@ -203,5 +203,4 @@ When ReadableDataSource load rule data successfully, console will print some log ## More For more information about Sentinel, see [Sentinel Project](https://github.com/alibaba/Sentinel). -If you have any ideas or suggestions for Spring Cloud Sentinel starter, please don't hesitate to tell us by submitting github issues. - +If you have any ideas or suggestions for Spring Cloud Sentinel starter, please don't hesitate to tell us by submitting github issues. \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties index c3d0959d..4a60ba53 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/src/main/resources/application.properties @@ -9,6 +9,7 @@ management.health.diskspace.enabled=false spring.cloud.sentinel.transport.dashboard=localhost:8080 spring.cloud.sentinel.eager=true +#spring.cloud.sentinel.http-method-specify=false spring.cloud.sentinel.datasource.ds1.file.file=classpath: flowrule.json spring.cloud.sentinel.datasource.ds1.file.data-type=json diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/readme-zh.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/readme-zh.md index 3ff703f4..ca7459db 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/readme-zh.md +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/readme-zh.md @@ -8,7 +8,7 @@ [Dubbo](http://dubbo.apache.org/)是一款高性能Java RPC框架,有对应的[SpringBoot工程](https://github.com/apache/dubbo-spring-boot-project)。 -本项目专注于Sentinel与Dubbo的整合,关于Sentinel的更多特性可以查看[sentinel-core-example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example)。 +本项目专注于Sentinel与Dubbo的整合,关于Sentinel的更多特性可以查看[sentinel-core-example](https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example)。 ## 示例 @@ -19,7 +19,7 @@ 1. 首先,修改 pom.xml 文件,引入 Sentinel starter 和 Dubbo starter。 - org.springframework.cloud + com.alibaba.cloud spring-cloud-starter-alibaba-sentinel diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/readme.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/readme.md index 569c0c0d..9d22f71a 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/readme.md +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/readme.md @@ -7,7 +7,7 @@ This example illustrates how to use Sentinel starter to implement flow control f [Dubbo](http://dubbo.apache.org/) is a high-performance, java based open source RPC framework. -This example focus on the integration of Sentinel and Dubbo. You can see more features on [sentinel-core-example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example). +This example focus on the integration of Sentinel and Dubbo. You can see more features on [sentinel-core-example](https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example). ## Demo diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml index 0c3dea0a..7daa7a75 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-api/pom.xml @@ -5,12 +5,11 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../../pom.xml 4.0.0 - com.alibaba.cloud sentinel-dubbo-api jar api for sentinel dubbo example diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml index 6bd4f98a..f0b9a131 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-consumer-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml index c4cbb8d1..cd888ac8 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-dubbo-example/sentinel-dubbo-provider-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/readme-zh.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/readme-zh.md new file mode 100644 index 00000000..542d9ef7 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/readme-zh.md @@ -0,0 +1,125 @@ +# Sentinel Feign Example + +## 项目说明 + +本项目演示如何使用 Sentinel starter 完成 Spring Cloud 应用调用。 + +[Sentinel](https://github.com/alibaba/Sentinel) 是阿里巴巴开源的分布式系统的流量防卫组件,Sentinel 把流量作为切入点,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。 + +[OpenFeign](https://github.com/spring-cloud/spring-cloud-openfeign)是一款声明式、模板化的HTTP客户端, Feign可以帮助我们更快捷、优雅地调用HTTP API。 + +本项目专注于Sentinel与Feign的整合,关于Sentinel的更多特性可以查看[sentinel-core-example](https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example)。 + +## 示例 + +### 服务消费方 +在启动示例进行演示之前,我们先了解一下 Feign 如何接入 Sentinel。 +**注意 本章节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您无需再进行修改。** + +1. 首先,修改 pom.xml 文件,引入 Sentinel starter 和 Dubbo starter。 + +```xml + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + +``` +2. 其次, 使用nacos 注册中心 + +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + +``` + +3. 定义FeignClient,及其降级配置 + +- 定义FeignClient +```java +@FeignClient(name = "service-provider", fallbackFactory = EchoServiceFallbackFactory.class) +public interface EchoService { + + /** + * 调用服务提供方的输出接口 + * + * @param str 用户输入 + * @return + */ + @GetMapping(value = "/echo/{str}") + String echo(@PathVariable("str") String str); +} +``` +- 定义fallback 工厂,获取异常 + +```java +@Component +public class EchoServiceFallbackFactory implements FallbackFactory { + @Override + public EchoServiceFallback create(Throwable throwable) { + return new EchoServiceFallback(throwable); + } +} +``` + +- 定义具体的fallback 实现 +```java +public class EchoServiceFallback implements EchoService { + private Throwable throwable; + + EchoServiceFallback(Throwable throwable) { + this.throwable = throwable; + } + + @Override + public String echo(String str) { + return "consumer-fallback-default-str" + throwable.getMessage(); + } +} +``` +### 服务提供方 + +1. 首先, 依赖nacos 注册中心 + +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + +``` + +2. 定义服务提供方接口 + +```java +@RestController +public class EchoController { + + @GetMapping("/echo/{str}") + public String echo(@PathVariable String str) { + return "provider-" + str; + } + +} +``` +### 应用启动 + + +支持 IDE 直接启动和编译打包后启动。 + +- 启动nacos 注册中心 + +- 启动服务提供方: + +1. IDE直接启动:找到主类 `ProviderApplication`,执行 main 方法启动应用。 +2. 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar sentinel-feign-provider-example.jar`启动应用。 + +- 启动服务消费方: + +1. IDE直接启动:找到主类 `ConsumerApplication`,执行 main 方法启动应用。 +2. 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar sentinel-feign-consumer-example.jar`启动应用。 diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/readme.md b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/readme.md new file mode 100644 index 00000000..875c019e --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/readme.md @@ -0,0 +1,126 @@ +# Sentinel Feign Example + +Project description + +This project demonstrates how to use Sentinel starter to complete the Spring Cloud application call. + +[Sentinel](https://github.com/alibaba/Sentinel) is alibaba open source distributed system flow defense components, Sentinel flow as the breakthrough point, from the flow control, fusing the drop, the stability of the system load multiple dimensions, such as protection services. + +[OpenFeign](https://github.com/spring-cloud/spring-cloud-openfeign) is a declarative, templated HTTP client, Feign can help us faster and gracefully HTTP API calls. + +By focusing on this project, the integration of Sentinel and Feign more characteristics about Sentinel can view [Sentinel - core - example](https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example). + +## sample + +Service consumer +Before launching the example, let's see how Feign can access Sentinel. +** note that this section is for your convenience only. The access has been completed in this sample code and you do not need to modify it. * * + +First, modify the pom.xml file to introduce Sentinel starter and Dubbo starter. + +```xml + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + +``` +2. Secondly, nacos registries are used + +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + +``` + +3. Define the FeignClient and its degraded configuration + +```java +@FeignClient(name = "service-provider", fallbackFactory = EchoServiceFallbackFactory.class) +public interface EchoService { + + /** + * 调用服务提供方的输出接口 + * + * @param str 用户输入 + * @return + */ + @GetMapping(value = "/echo/{str}") + String echo(@PathVariable("str") String str); +} +``` +- define a fallback factory to get an exception + +```java +@Component +public class EchoServiceFallbackFactory implements FallbackFactory { + @Override + public EchoServiceFallback create(Throwable throwable) { + return new EchoServiceFallback(throwable); + } +} +``` + +- define a specific fallback implementation +```java +public class EchoServiceFallback implements EchoService { + private Throwable throwable; + + EchoServiceFallback(Throwable throwable) { + this.throwable = throwable; + } + + @Override + public String echo(String str) { + return "consumer-fallback-default-str" + throwable.getMessage(); + } +} +``` +Service provider + +1. First, rely on the nacos registry + +```xml + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + +``` + + +2. Define the service provider interface + +```java +@RestController +public class EchoController { + + @GetMapping("/echo/{str}") + public String echo(@PathVariable String str) { + return "provider-" + str; + } + +} +``` + +Application launch + + +Support for IDE startup directly and after compilation and packaging. + +- launch the nacos registry + +- starting service provider: + +1. IDE starts directly: find the main class `ProviderApplication` and execute the main method to start the application. +2. Start after packaging and compilation: first execute `mvn clean package` to compile and package the project, and then execute `java-jar sentinel-feign-provider-example.jar` to start the application. + +- starting service consumer: + +1. IDE launch directly: find the main class `ConsumerApplication` and execute the main method to launch the application. +2. Start after packaging and compilation: first execute `mvn clean package` to compile and package the project, and then execute `java-jar sentinel-feign-consumer-example.jar` to start the application. \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/pom.xml new file mode 100644 index 00000000..4a706808 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/pom.xml @@ -0,0 +1,63 @@ + + + + + com.alibaba.cloud + spring-cloud-alibaba-examples + 2.1.1.BUILD-SNAPSHOT + ../../../pom.xml + + 4.0.0 + + + sentinel-feign-consumer-example + jar + Example demonstrating how to use sentinel with feign + + + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + true + + + + + + diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java new file mode 100644 index 00000000..ff036477 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/ConsumerApplication.java @@ -0,0 +1,20 @@ +package com.alibaba.cloud.examples; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.SpringCloudApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * @author lengleng + */ +@EnableFeignClients +@SpringCloudApplication +public class ConsumerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConsumerApplication.class, args); + } + +} diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/controller/TestController.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/controller/TestController.java new file mode 100644 index 00000000..f90fe107 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/controller/TestController.java @@ -0,0 +1,23 @@ +package com.alibaba.cloud.examples.controller; + +import com.alibaba.cloud.examples.service.EchoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author lengleng + */ +@RestController +public class TestController { + + @Autowired + private EchoService echoService; + + @GetMapping(value = "/echo-feign/{str}") + public String feign(@PathVariable String str) { + return echoService.echo(str); + } + +} diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/fallback/EchoServiceFallback.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/fallback/EchoServiceFallback.java new file mode 100644 index 00000000..6d4fd77d --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/fallback/EchoServiceFallback.java @@ -0,0 +1,28 @@ +package com.alibaba.cloud.examples.fallback; + +import com.alibaba.cloud.examples.service.EchoService; + +/** + * @author lengleng + * @date 2019-08-01 + *

+ * sentinel 降级处理 + */ +public class EchoServiceFallback implements EchoService { + private Throwable throwable; + + EchoServiceFallback(Throwable throwable) { + this.throwable = throwable; + } + + /** + * 调用服务提供方的输出接口 + * + * @param str 用户输入 + * @return + */ + @Override + public String echo(String str) { + return "consumer-fallback-default-str" + throwable.getMessage(); + } +} diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/fallback/EchoServiceFallbackFactory.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/fallback/EchoServiceFallbackFactory.java new file mode 100644 index 00000000..6d33d713 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/fallback/EchoServiceFallbackFactory.java @@ -0,0 +1,16 @@ +package com.alibaba.cloud.examples.fallback; + +import feign.hystrix.FallbackFactory; +import org.springframework.stereotype.Component; + +/** + * @author lengleng + * @date 2019-08-01 + */ +@Component +public class EchoServiceFallbackFactory implements FallbackFactory { + @Override + public EchoServiceFallback create(Throwable throwable) { + return new EchoServiceFallback(throwable); + } +} diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/service/EchoService.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/service/EchoService.java new file mode 100644 index 00000000..2dcbdc96 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/java/com/alibaba/cloud/examples/service/EchoService.java @@ -0,0 +1,25 @@ +package com.alibaba.cloud.examples.service; + +import com.alibaba.cloud.examples.fallback.EchoServiceFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +/** + * @author lengleng + * @date 2019-08-01 + *

+ * example feign client + */ +@FeignClient(name = "service-provider", fallbackFactory = EchoServiceFallbackFactory.class) +public interface EchoService { + + /** + * 调用服务提供方的输出接口 + * + * @param str 用户输入 + * @return + */ + @GetMapping(value = "/echo/{str}") + String echo(@PathVariable("str") String str); +} diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/resources/application.yml new file mode 100644 index 00000000..219c38d0 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-consumer-example/src/main/resources/application.yml @@ -0,0 +1,20 @@ +server: + port: 18087 + +spring: + application: + name: service-consumer + cloud: + nacos: + discovery: + server-addr: 127.0.0.1:8848 + +feign: + sentinel: + enabled: true + +management: + endpoints: + web: + exposure: + include: '*' diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/pom.xml new file mode 100644 index 00000000..e57dd23f --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/pom.xml @@ -0,0 +1,52 @@ + + + + + com.alibaba.cloud + spring-cloud-alibaba-examples + 2.1.1.BUILD-SNAPSHOT + ../../../pom.xml + + 4.0.0 + + + sentinel-feign-provider-example + jar + Example demonstrating how to use sentinel with feign + + + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + true + + + + + + diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java new file mode 100644 index 00000000..cdc17dd8 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/src/main/java/com/alibaba/cloud/examples/ProviderApplication.java @@ -0,0 +1,17 @@ +package com.alibaba.cloud.examples; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; + +/** + * @author lengleng + */ +@EnableDiscoveryClient +@SpringBootApplication +public class ProviderApplication { + + public static void main(String[] args) { + SpringApplication.run(ProviderApplication.class, args); + } +} diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/src/main/java/com/alibaba/cloud/examples/controller/EchoController.java b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/src/main/java/com/alibaba/cloud/examples/controller/EchoController.java new file mode 100644 index 00000000..6d99f33d --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/src/main/java/com/alibaba/cloud/examples/controller/EchoController.java @@ -0,0 +1,19 @@ +package com.alibaba.cloud.examples.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author lengleng + * @date 2019-08-01 + */ +@RestController +public class EchoController { + + @GetMapping("/echo/{str}") + public String echo(@PathVariable String str) { + return "provider-" + str; + } + +} diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/src/main/resources/application.yml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/src/main/resources/application.yml new file mode 100644 index 00000000..f6098ec7 --- /dev/null +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-feign-example/sentinel-feign-provider-example/src/main/resources/application.yml @@ -0,0 +1,16 @@ +server: + port: 18088 + +spring: + application: + name: service-provider + cloud: + nacos: + discovery: + server-addr: 127.0.0.1:8848 + +management: + endpoints: + web: + exposure: + include: '*' diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/pom.xml index f52460e4..2cf43f39 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/resources/application.yaml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/resources/application.yaml index 27ae0cb9..37181d40 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/resources/application.yaml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-spring-cloud-gateway-example/src/main/resources/application.yaml @@ -37,5 +37,7 @@ spring: mode: response response-status: 444 response-body: 1234 + scg: + order: -100 management.endpoints.web.exposure.include: "*" \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/pom.xml index 72190745..482c5907 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-webflux-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/sentinel-example/sentinel-zuul-example/pom.xml b/spring-cloud-alibaba-examples/sentinel-example/sentinel-zuul-example/pom.xml index 61374f72..eaedfdc7 100644 --- a/spring-cloud-alibaba-examples/sentinel-example/sentinel-zuul-example/pom.xml +++ b/spring-cloud-alibaba-examples/sentinel-example/sentinel-zuul-example/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/sms-example/pom.xml b/spring-cloud-alibaba-examples/sms-example/pom.xml index 97eb0f23..b5379981 100644 --- a/spring-cloud-alibaba-examples/sms-example/pom.xml +++ b/spring-cloud-alibaba-examples/sms-example/pom.xml @@ -10,7 +10,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/README_CN.md b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/README_CN.md index 30c2d3a6..412d5722 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/README_CN.md +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/README_CN.md @@ -15,7 +15,7 @@ public interface EchoService { ``` 为了确保契约的一致性,推荐的做法是将 Dubbo 服务接口打包在第二方或者第三方的 artifact(jar)中,如以上接口就存放在 - artifact [spring-cloud-dubbo-sample-api](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-sample-api) 之中。 + artifact [spring-cloud-dubbo-sample-api](https://github.com/alibaba/spring-cloud-alibaba/tree/master/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-sample-api) 之中。 对于服务提供方而言,不仅通过依赖 artifact 的形式引入 Dubbo 服务接口,而且需要将其实现。对应的服务消费端,同样地需要依赖该 artifact, 并以接口调用的方式执行远程方法。接下来进一步讨论怎样实现 Dubbo 服务提供方和消费方。 @@ -149,8 +149,8 @@ spring: - `dubbo.protocol` : Dubbo 服务暴露的协议配置,其中子属性 `name` 为协议名称,`port` 为协议端口( -1 表示自增端口,从 20880 开始) - `dubbo.registry` : Dubbo 服务注册中心配置,其中子属性 `address` 的值 "spring-cloud://localhost",说明挂载到 Spring Cloud 注册中心 > 当前 Dubbo Spring Cloud 实现必须配置 `dubbo.registry.address = spring-cloud://localhost`,下一个版本将其配置变为可选 -(参考 [issue #592](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/issues/592)), -> 并且支持传统 Dubbo 协议的支持(参考 [issue #588](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/issues/588)) +(参考 [issue #592](https://github.com/alibaba/spring-cloud-alibaba/issues/592)), +> 并且支持传统 Dubbo 协议的支持(参考 [issue #588](https://github.com/alibaba/spring-cloud-alibaba/issues/588)) 下半部分则是 Spring Cloud 相关配置: diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml index 66a1b5bf..89621923 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml @@ -12,7 +12,7 @@ com.alibaba.cloud spring-cloud-alibaba-dubbo-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT Spring Cloud Alibaba Dubbo Examples pom diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-client-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-client-sample/pom.xml index adc03a2f..9e5ab0fb 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-client-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-client-sample/pom.xml @@ -13,7 +13,7 @@ com.alibaba.cloud spring-cloud-dubbo-client-sample Spring Cloud Dubbo Client Sample - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/pom.xml index cb575169..15bef9e8 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-dubbo-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/com/alibaba/cloud/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/com/alibaba/cloud/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java index 809ebc7d..37361fc8 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/com/alibaba/cloud/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/com/alibaba/cloud/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java @@ -16,23 +16,26 @@ */ package com.alibaba.cloud.dubbo.bootstrap; -import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; - -import java.util.HashMap; -import java.util.Map; - import org.apache.dubbo.config.annotation.Reference; + +import com.alibaba.cloud.dubbo.annotation.DubboTransported; +import com.alibaba.cloud.dubbo.service.RestService; +import com.alibaba.cloud.dubbo.service.User; +import com.alibaba.cloud.dubbo.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.cache.annotation.EnableCaching; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Lazy; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -41,10 +44,10 @@ import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.client.RestTemplate; -import com.alibaba.cloud.dubbo.annotation.DubboTransported; -import com.alibaba.cloud.dubbo.service.RestService; -import com.alibaba.cloud.dubbo.service.User; -import com.alibaba.cloud.dubbo.service.UserService; +import java.util.HashMap; +import java.util.Map; + +import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; /** * Dubbo Spring Cloud Consumer Bootstrap @@ -52,194 +55,204 @@ import com.alibaba.cloud.dubbo.service.UserService; @EnableDiscoveryClient @EnableAutoConfiguration @EnableFeignClients +@EnableScheduling +@EnableCaching public class DubboSpringCloudConsumerBootstrap { - @Reference - private UserService userService; + @Reference + private UserService userService; - @Reference(version = "1.0.0", protocol = "dubbo") - private RestService restService; + @Reference(version = "1.0.0", protocol = "dubbo") + private RestService restService; - @Autowired - @Lazy - private FeignRestService feignRestService; + @Autowired + @Lazy + private FeignRestService feignRestService; - @Autowired - @Lazy - private DubboFeignRestService dubboFeignRestService; + @Autowired + @Lazy + private DubboFeignRestService dubboFeignRestService; - @Value("${provider.application.name}") - private String providerApplicationName; + @Value("${provider.application.name}") + private String providerApplicationName; - @Autowired - @LoadBalanced - private RestTemplate restTemplate; + @Autowired + @LoadBalanced + private RestTemplate restTemplate; - @FeignClient("${provider.application.name}") - public interface FeignRestService { + @FeignClient("${provider.application.name}") + public interface FeignRestService { - @GetMapping(value = "/param") - String param(@RequestParam("param") String param); + @GetMapping(value = "/param") + String param(@RequestParam("param") String param); - @PostMapping("/params") - public String params(@RequestParam("b") String b, @RequestParam("a") int a); + @PostMapping("/params") + public String params(@RequestParam("b") String b, @RequestParam("a") int a); - @PostMapping(value = "/request/body/map", produces = APPLICATION_JSON_UTF8_VALUE) - User requestBody(@RequestParam("param") String param, - @RequestBody Map data); + @PostMapping(value = "/request/body/map", produces = APPLICATION_JSON_UTF8_VALUE) + User requestBody(@RequestParam("param") String param, + @RequestBody Map data); - @GetMapping("/headers") - public String headers(@RequestHeader("h2") String header2, - @RequestHeader("h") String header, @RequestParam("v") Integer value); + @GetMapping("/headers") + public String headers(@RequestHeader("h2") String header2, + @RequestHeader("h") String header, @RequestParam("v") Integer value); - @GetMapping("/path-variables/{p1}/{p2}") - public String pathVariables(@PathVariable("p2") String path2, - @PathVariable("p1") String path1, @RequestParam("v") String param); - } + @GetMapping("/path-variables/{p1}/{p2}") + public String pathVariables(@PathVariable("p2") String path2, + @PathVariable("p1") String path1, @RequestParam("v") String param); + } - @FeignClient("${provider.application.name}") - @DubboTransported(protocol = "dubbo") - public interface DubboFeignRestService { + @FeignClient("${provider.application.name}") + @DubboTransported(protocol = "dubbo") + public interface DubboFeignRestService { - @GetMapping(value = "/param") - String param(@RequestParam("param") String param); + @GetMapping(value = "/param") + String param(@RequestParam("param") String param); - @PostMapping("/params") - String params(@RequestParam("b") String paramB, @RequestParam("a") int paramA); + @PostMapping("/params") + String params(@RequestParam("b") String paramB, @RequestParam("a") int paramA); - @PostMapping(value = "/request/body/map", produces = APPLICATION_JSON_UTF8_VALUE) - User requestBody(@RequestParam("param") String param, - @RequestBody Map data); + @PostMapping(value = "/request/body/map", produces = APPLICATION_JSON_UTF8_VALUE) + User requestBody(@RequestParam("param") String param, + @RequestBody Map data); - @GetMapping("/headers") - public String headers(@RequestHeader("h2") String header2, - @RequestParam("v") Integer value, @RequestHeader("h") String header); + @GetMapping("/headers") + public String headers(@RequestHeader("h2") String header2, + @RequestParam("v") Integer value, @RequestHeader("h") String header); - @GetMapping("/path-variables/{p1}/{p2}") - public String pathVariables(@RequestParam("v") String param, - @PathVariable("p2") String path2, @PathVariable("p1") String path1); - } + @GetMapping("/path-variables/{p1}/{p2}") + public String pathVariables(@RequestParam("v") String param, + @PathVariable("p2") String path2, @PathVariable("p1") String path1); + } - @Bean - public ApplicationRunner userServiceRunner() { - return arguments -> { + @Bean + public ApplicationRunner userServiceRunner() { + return arguments -> { - User user = new User(); - user.setId(1L); - user.setName("小马哥"); - user.setAge(33); + User user = new User(); + user.setId(1L); + user.setName("小马哥"); + user.setAge(33); - // save User - System.out.printf("UserService.save(%s) : %s\n", user, - userService.save(user)); + // save User + System.out.printf("UserService.save(%s) : %s\n", user, + userService.save(user)); - // find all Users - System.out.printf("UserService.findAll() : %s\n", user, - userService.findAll()); + // find all Users + System.out.printf("UserService.findAll() : %s\n", user, + userService.findAll()); - // remove User - System.out.printf("UserService.remove(%d) : %s\n", user.getId(), - userService.remove(user.getId())); + // remove User + System.out.printf("UserService.remove(%d) : %s\n", user.getId(), + userService.remove(user.getId())); - }; - } + }; + } - @Bean - public ApplicationRunner callRunner() { - return arguments -> { + @Bean + public ApplicationRunner callRunner() { + return arguments -> { + callAll(); + }; + } - // To call /path-variables - callPathVariables(); + private void callAll() { - // To call /headers - callHeaders(); + // To call /path-variables + callPathVariables(); - // To call /param - callParam(); + // To call /headers + callHeaders(); - // To call /params - callParams(); + // To call /param + callParam(); - // To call /request/body/map - callRequestBodyMap(); + // To call /params + callParams(); - }; - } + // To call /request/body/map + callRequestBodyMap(); + } - private void callPathVariables() { - // Dubbo Service call - System.out.println(restService.pathVariables("a", "b", "c")); - // Spring Cloud Open Feign REST Call (Dubbo Transported) - System.out.println(dubboFeignRestService.pathVariables("c", "b", "a")); - // Spring Cloud Open Feign REST Call - System.out.println(feignRestService.pathVariables("b", "a", "c")); + @Scheduled(fixedDelay = 10 * 1000L) + public void onScheduled() { + callAll(); + } - // RestTemplate call - System.out.println(restTemplate.getForEntity( - "http://" + providerApplicationName + "//path-variables/{p1}/{p2}?v=c", - String.class, "a", "b")); - } + private void callPathVariables() { + // Dubbo Service call + System.out.println(restService.pathVariables("a", "b", "c")); + // Spring Cloud Open Feign REST Call (Dubbo Transported) + System.out.println(dubboFeignRestService.pathVariables("c", "b", "a")); + // Spring Cloud Open Feign REST Call +// System.out.println(feignRestService.pathVariables("b", "a", "c")); - private void callHeaders() { - // Dubbo Service call - System.out.println(restService.headers("a", "b", 10)); - // Spring Cloud Open Feign REST Call (Dubbo Transported) - System.out.println(dubboFeignRestService.headers("b", 10, "a")); - // Spring Cloud Open Feign REST Call - System.out.println(feignRestService.headers("b", "a", 10)); - } + // RestTemplate call + System.out.println(restTemplate.getForEntity( + "http://" + providerApplicationName + "//path-variables/{p1}/{p2}?v=c", + String.class, "a", "b")); + } - private void callParam() { - // Dubbo Service call - System.out.println(restService.param("mercyblitz")); - // Spring Cloud Open Feign REST Call (Dubbo Transported) - System.out.println(dubboFeignRestService.param("mercyblitz")); - // Spring Cloud Open Feign REST Call - System.out.println(feignRestService.param("mercyblitz")); - } + private void callHeaders() { + // Dubbo Service call + System.out.println(restService.headers("a", "b", 10)); + // Spring Cloud Open Feign REST Call (Dubbo Transported) + System.out.println(dubboFeignRestService.headers("b", 10, "a")); + // Spring Cloud Open Feign REST Call +// System.out.println(feignRestService.headers("b", "a", 10)); + } - private void callParams() { - // Dubbo Service call - System.out.println(restService.params(1, "1")); - // Spring Cloud Open Feign REST Call (Dubbo Transported) - System.out.println(dubboFeignRestService.params("1", 1)); - // Spring Cloud Open Feign REST Call - System.out.println(feignRestService.params("1", 1)); + private void callParam() { + // Dubbo Service call + System.out.println(restService.param("mercyblitz")); + // Spring Cloud Open Feign REST Call (Dubbo Transported) + System.out.println(dubboFeignRestService.param("mercyblitz")); + // Spring Cloud Open Feign REST Call +// System.out.println(feignRestService.param("mercyblitz")); + } - // RestTemplate call - System.out.println(restTemplate.getForEntity( - "http://" + providerApplicationName + "/param?param=小马哥", String.class)); - } + private void callParams() { + // Dubbo Service call + System.out.println(restService.params(1, "1")); + // Spring Cloud Open Feign REST Call (Dubbo Transported) + System.out.println(dubboFeignRestService.params("1", 1)); + // Spring Cloud Open Feign REST Call +// System.out.println(feignRestService.params("1", 1)); - private void callRequestBodyMap() { + // RestTemplate call + System.out.println(restTemplate.getForEntity( + "http://" + providerApplicationName + "/param?param=小马哥", String.class)); + } - Map data = new HashMap<>(); - data.put("id", 1); - data.put("name", "小马哥"); - data.put("age", 33); + private void callRequestBodyMap() { - // Dubbo Service call - System.out.println(restService.requestBodyMap(data, "Hello,World")); - // Spring Cloud Open Feign REST Call (Dubbo Transported) - System.out.println(dubboFeignRestService.requestBody("Hello,World", data)); - // Spring Cloud Open Feign REST Call - System.out.println(feignRestService.requestBody("Hello,World", data)); + Map data = new HashMap<>(); + data.put("id", 1); + data.put("name", "小马哥"); + data.put("age", 33); - // RestTemplate call - System.out.println(restTemplate.postForObject( - "http://" + providerApplicationName + "/request/body/map?param=小马哥", data, - User.class)); - } + // Dubbo Service call + System.out.println(restService.requestBodyMap(data, "Hello,World")); + // Spring Cloud Open Feign REST Call (Dubbo Transported) + System.out.println(dubboFeignRestService.requestBody("Hello,World", data)); + // Spring Cloud Open Feign REST Call +// System.out.println(feignRestService.requestBody("Hello,World", data)); - @Bean - @LoadBalanced - @DubboTransported - public RestTemplate restTemplate() { - return new RestTemplate(); - } + // RestTemplate call + System.out.println(restTemplate.postForObject( + "http://" + providerApplicationName + "/request/body/map?param=小马哥", data, + User.class)); + } - public static void main(String[] args) { - new SpringApplicationBuilder(DubboSpringCloudConsumerBootstrap.class) - .properties("spring.profiles.active=nacos").run(args); - } + @Bean + @LoadBalanced + @DubboTransported + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + public static void main(String[] args) { + new SpringApplicationBuilder(DubboSpringCloudConsumerBootstrap.class) + .properties("spring.profiles.active=nacos").run(args); + } } diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml index 47de4040..b876fe4c 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-dubbo-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0 @@ -28,12 +28,6 @@ spring-web - - - org.springframework.boot - spring-boot-actuator - - com.alibaba.cloud diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/pom.xml index 648c064e..c6d7d7d5 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-dubbo-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-sample-api/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-sample-api/pom.xml index 6ed389d1..989aac8e 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-sample-api/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-sample-api/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-dubbo-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-server-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-server-sample/pom.xml index c829aaf7..9e1103ad 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-server-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-server-sample/pom.xml @@ -14,7 +14,7 @@ com.alibaba.cloud spring-cloud-dubbo-server-sample Spring Cloud Dubbo Server Sample - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml index ebec8cfe..3a6803a4 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba-dubbo-examples - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/com/alibaba/cloud/dubbo/gateway/DubboGatewayServlet.java b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/com/alibaba/cloud/dubbo/gateway/DubboGatewayServlet.java index 5ca9451f..be73c115 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/com/alibaba/cloud/dubbo/gateway/DubboGatewayServlet.java +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/com/alibaba/cloud/dubbo/gateway/DubboGatewayServlet.java @@ -1,9 +1,29 @@ package com.alibaba.cloud.dubbo.gateway; -import static org.apache.commons.lang3.StringUtils.substringAfter; -import static org.apache.commons.lang3.StringUtils.substringBetween; -import static org.springframework.web.util.UriComponentsBuilder.fromUriString; +import org.apache.dubbo.rpc.service.GenericException; +import org.apache.dubbo.rpc.service.GenericService; +import com.alibaba.cloud.dubbo.http.MutableHttpServerRequest; +import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata; +import com.alibaba.cloud.dubbo.metadata.RequestMetadata; +import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; +import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContext; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; +import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpRequest; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; +import org.springframework.util.StreamUtils; +import org.springframework.web.servlet.HttpServletBean; +import org.springframework.web.util.UriComponents; + +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; @@ -15,30 +35,9 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.dubbo.rpc.service.GenericException; -import org.apache.dubbo.rpc.service.GenericService; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpRequest; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.PathMatcher; -import org.springframework.util.StreamUtils; -import org.springframework.web.servlet.HttpServletBean; -import org.springframework.web.util.UriComponents; - -import com.alibaba.cloud.dubbo.http.MutableHttpServerRequest; -import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata; -import com.alibaba.cloud.dubbo.metadata.RequestMetadata; -import com.alibaba.cloud.dubbo.metadata.RestMethodMetadata; -import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContext; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceExecutionContextFactory; -import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory; +import static org.apache.commons.lang3.StringUtils.substringAfter; +import static org.apache.commons.lang3.StringUtils.substringBetween; +import static org.springframework.web.util.UriComponentsBuilder.fromUriString; @WebServlet(urlPatterns = "/dsc/*") public class DubboGatewayServlet extends HttpServletBean { @@ -86,7 +85,7 @@ public class DubboGatewayServlet extends HttpServletBean { String restPath = substringAfter(request.getRequestURI(), serviceName); // 初始化 serviceName 的 REST 请求元数据 - repository.initialize(serviceName); + repository.initializeMetadata(serviceName); // 将 HttpServletRequest 转化为 RequestMetadata RequestMetadata clientMetadata = buildRequestMetadata(request, restPath); diff --git a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml index 62e2580a..4d78df66 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-bus-rocketmq-example/pom.xml @@ -5,7 +5,7 @@ spring-cloud-alibaba-examples com.alibaba.cloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-nacos-config-server/pom.xml b/spring-cloud-alibaba-nacos-config-server/pom.xml index 9c9c33c2..e604fba6 100644 --- a/spring-cloud-alibaba-nacos-config-server/pom.xml +++ b/spring-cloud-alibaba-nacos-config-server/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0 diff --git a/spring-cloud-alibaba-nacos-config/pom.xml b/spring-cloud-alibaba-nacos-config/pom.xml index 26115f00..d40aeaf2 100644 --- a/spring-cloud-alibaba-nacos-config/pom.xml +++ b/spring-cloud-alibaba-nacos-config/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java index 82ab1fac..adfae5e3 100644 --- a/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java +++ b/spring-cloud-alibaba-nacos-config/src/main/java/com/alibaba/cloud/nacos/NacosConfigProperties.java @@ -25,6 +25,10 @@ import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT_PORT; import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE; import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY; import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR; +import static com.alibaba.nacos.api.PropertyKeyConst.MAX_RETRY; +import static com.alibaba.nacos.api.PropertyKeyConst.CONFIG_LONG_POLL_TIMEOUT; +import static com.alibaba.nacos.api.PropertyKeyConst.CONFIG_RETRY_TIME; +import static com.alibaba.nacos.api.PropertyKeyConst.ENABLE_REMOTE_SYNC_CONFIG; import java.util.List; import java.util.Objects; @@ -53,7 +57,7 @@ public class NacosConfigProperties { .getLogger(NacosConfigProperties.class); /** - * nacos config server address + * nacos config server address. */ private String serverAddr; @@ -68,7 +72,7 @@ public class NacosConfigProperties { private String group = "DEFAULT_GROUP"; /** - * nacos config dataId prefix + * nacos config dataId prefix. */ private String prefix; /** @@ -81,6 +85,29 @@ public class NacosConfigProperties { */ private int timeout = 3000; + /** + * nacos maximum number of tolerable server reconnection errors. + */ + private String maxRetry; + + /** + * nacos get config long poll timeout. + */ + private String configLongPollTimeout; + + /** + * nacos get config failure retry time. + */ + private String configRetryTime; + + /** + * If you want to pull it yourself when the program starts to get the configuration for the first time, + * and the registered Listener is used for future configuration updates, you can keep the original + * code unchanged, just add the system parameter: enableRemoteSyncConfig = "true" ( But there is network overhead); + * therefore we recommend that you use {@link ConfigService#getConfigAndSignListener} directly. + */ + private boolean enableRemoteSyncConfig = false; + /** * endpoint for Nacos, the domain name of a service, through which the server address * can be dynamically obtained. @@ -108,10 +135,13 @@ public class NacosConfigProperties { private String contextPath; /** - * nacos config cluster name + * nacos config cluster name. */ private String clusterName; + /** + * nacos config dataId name. + */ private String name; /** @@ -174,6 +204,38 @@ public class NacosConfigProperties { this.timeout = timeout; } + public String getMaxRetry() { + return maxRetry; + } + + public void setMaxRetry(String maxRetry) { + this.maxRetry = maxRetry; + } + + public String getConfigLongPollTimeout() { + return configLongPollTimeout; + } + + public void setConfigLongPollTimeout(String configLongPollTimeout) { + this.configLongPollTimeout = configLongPollTimeout; + } + + public String getConfigRetryTime() { + return configRetryTime; + } + + public void setConfigRetryTime(String configRetryTime) { + this.configRetryTime = configRetryTime; + } + + public Boolean getEnableRemoteSyncConfig() { + return enableRemoteSyncConfig; + } + + public void setEnableRemoteSyncConfig(Boolean enableRemoteSyncConfig) { + this.enableRemoteSyncConfig = enableRemoteSyncConfig; + } + public String getEndpoint() { return endpoint; } @@ -328,6 +390,10 @@ public class NacosConfigProperties { properties.put(SECRET_KEY, Objects.toString(this.secretKey, "")); properties.put(CONTEXT_PATH, Objects.toString(this.contextPath, "")); properties.put(CLUSTER_NAME, Objects.toString(this.clusterName, "")); + properties.put(MAX_RETRY, Objects.toString(this.maxRetry, "")); + properties.put(CONFIG_LONG_POLL_TIMEOUT, Objects.toString(this.configLongPollTimeout, "")); + properties.put(CONFIG_RETRY_TIME, Objects.toString(this.configRetryTime, "")); + properties.put(ENABLE_REMOTE_SYNC_CONFIG, Objects.toString(this.enableRemoteSyncConfig, "")); String endpoint = Objects.toString(this.endpoint, ""); if (endpoint.contains(":")) { diff --git a/spring-cloud-alibaba-nacos-discovery/pom.xml b/spring-cloud-alibaba-nacos-discovery/pom.xml index edcd04c2..db9fed76 100644 --- a/spring-cloud-alibaba-nacos-discovery/pom.xml +++ b/spring-cloud-alibaba-nacos-discovery/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java index f6cb2506..96bcc860 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/NacosDiscoveryProperties.java @@ -66,7 +66,7 @@ public class NacosDiscoveryProperties { .getLogger(NacosDiscoveryProperties.class); /** - * nacos discovery server address + * nacos discovery server address. */ private String serverAddr; @@ -87,12 +87,12 @@ public class NacosDiscoveryProperties { private long watchDelay = 30000; /** - * nacos naming log file name + * nacos naming log file name. */ private String logName; /** - * service name to registry + * service name to registry. */ @Value("${spring.cloud.nacos.discovery.service:${spring.application.name:}}") private String service; @@ -108,7 +108,7 @@ public class NacosDiscoveryProperties { private String clusterName = "DEFAULT"; /** - * naming load from local cache at application start. true is load + * naming load from local cache at application start. true is load. */ private String namingLoadCacheAtStart = "false"; @@ -130,18 +130,18 @@ public class NacosDiscoveryProperties { private String ip; /** - * which network interface's ip you want to register + * which network interface's ip you want to register. */ private String networkInterface = ""; /** * The port your want to register for your service instance, needn't to set it if the - * auto detect port works well + * auto detect port works well. */ private int port = -1; /** - * whether your service is a https service + * whether your service is a https service. */ private boolean secure = false; diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryClient.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryClient.java index 941a2319..8a54b7d8 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryClient.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/com/alibaba/cloud/nacos/discovery/NacosDiscoveryClient.java @@ -16,21 +16,20 @@ package com.alibaba.cloud.nacos.discovery; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - +import com.alibaba.cloud.nacos.NacosDiscoveryProperties; +import com.alibaba.cloud.nacos.NacosServiceInstance; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ListView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; -import com.alibaba.cloud.nacos.NacosDiscoveryProperties; -import com.alibaba.cloud.nacos.NacosServiceInstance; -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.naming.pojo.ListView; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * @author xiaojing @@ -38,75 +37,79 @@ import com.alibaba.nacos.api.naming.pojo.ListView; */ public class NacosDiscoveryClient implements DiscoveryClient { - private static final Logger log = LoggerFactory.getLogger(NacosDiscoveryClient.class); - public static final String DESCRIPTION = "Spring Cloud Nacos Discovery Client"; + private static final Logger log = LoggerFactory.getLogger(NacosDiscoveryClient.class); + public static final String DESCRIPTION = "Spring Cloud Nacos Discovery Client"; - private NacosDiscoveryProperties discoveryProperties; + private NacosDiscoveryProperties discoveryProperties; - public NacosDiscoveryClient(NacosDiscoveryProperties discoveryProperties) { - this.discoveryProperties = discoveryProperties; - } + public NacosDiscoveryClient(NacosDiscoveryProperties discoveryProperties) { + this.discoveryProperties = discoveryProperties; + } - @Override - public String description() { - return DESCRIPTION; - } + @Override + public String description() { + return DESCRIPTION; + } - @Override - public List getInstances(String serviceId) { - try { - List instances = discoveryProperties.namingServiceInstance() - .selectInstances(serviceId, true); - return hostToServiceInstanceList(instances, serviceId); - } - catch (Exception e) { - throw new RuntimeException( - "Can not get hosts from nacos server. serviceId: " + serviceId, e); - } - } + @Override + public List getInstances(String serviceId) { + try { + List instances = discoveryProperties.namingServiceInstance() + .selectInstances(serviceId, true); + return hostToServiceInstanceList(instances, serviceId); + } catch (Exception e) { + throw new RuntimeException( + "Can not get hosts from nacos server. serviceId: " + serviceId, e); + } + } - private static ServiceInstance hostToServiceInstance(Instance instance, - String serviceId) { - NacosServiceInstance nacosServiceInstance = new NacosServiceInstance(); - nacosServiceInstance.setHost(instance.getIp()); - nacosServiceInstance.setPort(instance.getPort()); - nacosServiceInstance.setServiceId(serviceId); + public static ServiceInstance hostToServiceInstance(Instance instance, + String serviceId) { + if (instance == null || !instance.isEnabled() || !instance.isHealthy()) { + return null; + } + NacosServiceInstance nacosServiceInstance = new NacosServiceInstance(); + nacosServiceInstance.setHost(instance.getIp()); + nacosServiceInstance.setPort(instance.getPort()); + nacosServiceInstance.setServiceId(serviceId); - Map metadata = new HashMap<>(); - metadata.put("nacos.instanceId", instance.getInstanceId()); - metadata.put("nacos.weight", instance.getWeight() + ""); - metadata.put("nacos.healthy", instance.isHealthy() + ""); - metadata.put("nacos.cluster", instance.getClusterName() + ""); - metadata.putAll(instance.getMetadata()); - nacosServiceInstance.setMetadata(metadata); + Map metadata = new HashMap<>(); + metadata.put("nacos.instanceId", instance.getInstanceId()); + metadata.put("nacos.weight", instance.getWeight() + ""); + metadata.put("nacos.healthy", instance.isHealthy() + ""); + metadata.put("nacos.cluster", instance.getClusterName() + ""); + metadata.putAll(instance.getMetadata()); + nacosServiceInstance.setMetadata(metadata); - if (metadata.containsKey("secure")) { - boolean secure = Boolean.parseBoolean(metadata.get("secure")); - nacosServiceInstance.setSecure(secure); - } - return nacosServiceInstance; - } + if (metadata.containsKey("secure")) { + boolean secure = Boolean.parseBoolean(metadata.get("secure")); + nacosServiceInstance.setSecure(secure); + } + return nacosServiceInstance; + } - private static List hostToServiceInstanceList( - List instances, String serviceId) { - List result = new ArrayList<>(instances.size()); - for (Instance instance : instances) { - result.add(hostToServiceInstance(instance, serviceId)); - } - return result; - } + public static List hostToServiceInstanceList( + List instances, String serviceId) { + List result = new ArrayList<>(instances.size()); + for (Instance instance : instances) { + ServiceInstance serviceInstance = hostToServiceInstance(instance, serviceId); + if (serviceInstance != null) { + result.add(serviceInstance); + } + } + return result; + } - @Override - public List getServices() { + @Override + public List getServices() { - try { - ListView services = discoveryProperties.namingServiceInstance() - .getServicesOfServer(1, Integer.MAX_VALUE); - return services.getData(); - } - catch (Exception e) { - log.error("get service name from nacos server fail,", e); - return Collections.emptyList(); - } - } + try { + ListView services = discoveryProperties.namingServiceInstance() + .getServicesOfServer(1, Integer.MAX_VALUE); + return services.getData(); + } catch (Exception e) { + log.error("get service name from nacos server fail,", e); + return Collections.emptyList(); + } + } } diff --git a/spring-cloud-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/NacosDiscoveryClientTests.java b/spring-cloud-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/NacosDiscoveryClientTests.java index 260f46ff..6fe6680c 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/NacosDiscoveryClientTests.java +++ b/spring-cloud-alibaba-nacos-discovery/src/test/java/com/alibaba/cloud/nacos/NacosDiscoveryClientTests.java @@ -16,24 +16,23 @@ package com.alibaba.cloud.nacos; -import static com.alibaba.cloud.nacos.test.NacosMockTest.serviceInstance; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClient; +import com.alibaba.nacos.api.naming.NamingService; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ListView; +import org.junit.Test; +import org.springframework.cloud.client.ServiceInstance; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import org.junit.Test; -import org.springframework.cloud.client.ServiceInstance; - -import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClient; -import com.alibaba.nacos.api.naming.NamingService; -import com.alibaba.nacos.api.naming.pojo.Instance; -import com.alibaba.nacos.api.naming.pojo.ListView; +import static com.alibaba.cloud.nacos.test.NacosMockTest.serviceInstance; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * @author xiaojing @@ -53,7 +52,7 @@ public class NacosDiscoveryClientTests { map.put("test-key", "test-value"); map.put("secure", "true"); - instances.add(serviceInstance(serviceName, false, host, port, map)); + instances.add(serviceInstance(serviceName, true, host, port, map)); NacosDiscoveryProperties nacosDiscoveryProperties = mock( NacosDiscoveryProperties.class); diff --git a/spring-cloud-alibaba-seata/pom.xml b/spring-cloud-alibaba-seata/pom.xml index 4989d04e..c283204a 100644 --- a/spring-cloud-alibaba-seata/pom.xml +++ b/spring-cloud-alibaba-seata/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/GlobalTransactionAutoConfiguration.java b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/GlobalTransactionAutoConfiguration.java index a26aa578..dad69eda 100644 --- a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/GlobalTransactionAutoConfiguration.java +++ b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/GlobalTransactionAutoConfiguration.java @@ -51,7 +51,7 @@ public class GlobalTransactionAutoConfiguration { String txServiceGroup = seataProperties.getTxServiceGroup(); if (StringUtils.isEmpty(txServiceGroup)) { - txServiceGroup = applicationName + "-fescar-service-group"; + txServiceGroup = applicationName + "-seata-service-group"; seataProperties.setTxServiceGroup(txServiceGroup); } diff --git a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/SeataProperties.java b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/SeataProperties.java index 492d054d..c9531434 100644 --- a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/SeataProperties.java +++ b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/SeataProperties.java @@ -24,10 +24,10 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties("spring.cloud.alibaba.seata") public class SeataProperties { - // todo support config Fescar server information + // todo support config Seata server information /** - * Seata tx service group.default is ${spring.application.name}-fescar-service-group. + * Seata tx service group.default is ${spring.application.name}-seata-service-group. */ private String txServiceGroup; diff --git a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignClient.java b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignClient.java index 9e486120..c13b4cbc 100644 --- a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignClient.java +++ b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataFeignClient.java @@ -68,9 +68,9 @@ public class SeataFeignClient implements Client { Map> headers = new HashMap<>(MAP_SIZE); headers.putAll(request.headers()); - List fescarXid = new ArrayList<>(); - fescarXid.add(xid); - headers.put(RootContext.KEY_XID, fescarXid); + List seataXid = new ArrayList<>(); + seataXid.add(xid); + headers.put(RootContext.KEY_XID, seataXid); return Request.create(request.method(), request.url(), headers, request.body(), request.charset()); diff --git a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataLoadBalancerFeignClient.java b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataLoadBalancerFeignClient.java index 912e66c8..fd0a9ebc 100644 --- a/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataLoadBalancerFeignClient.java +++ b/spring-cloud-alibaba-seata/src/main/java/com/alibaba/cloud/seata/feign/SeataLoadBalancerFeignClient.java @@ -53,7 +53,7 @@ public class SeataLoadBalancerFeignClient extends LoadBalancerFeignClient { @Override public Response execute(Request request, Request.Options options) throws IOException { Request modifiedRequest = getModifyRequest(request); - return super.execute(request, options); + return super.execute(modifiedRequest, options); } private static Client wrap(Client delegate, BeanFactory beanFactory) { @@ -71,9 +71,9 @@ public class SeataLoadBalancerFeignClient extends LoadBalancerFeignClient { Map> headers = new HashMap<>(MAP_SIZE); headers.putAll(request.headers()); - List fescarXid = new ArrayList<>(); - fescarXid.add(xid); - headers.put(RootContext.KEY_XID, fescarXid); + List seataXid = new ArrayList<>(); + seataXid.add(xid); + headers.put(RootContext.KEY_XID, seataXid); return Request.create(request.method(), request.url(), headers, request.body(), request.charset()); diff --git a/spring-cloud-alibaba-sentinel-datasource/pom.xml b/spring-cloud-alibaba-sentinel-datasource/pom.xml index ad4b67a9..6df8f7b8 100644 --- a/spring-cloud-alibaba-sentinel-datasource/pom.xml +++ b/spring-cloud-alibaba-sentinel-datasource/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 @@ -53,6 +53,13 @@ true + + com.alibaba.csp + sentinel-datasource-redis + provided + true + + com.fasterxml.jackson.core jackson-databind diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java index 12dde119..9726668f 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/DataSourcePropertiesConfiguration.java @@ -33,6 +33,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; * @see ApolloDataSourceProperties * @see ZookeeperDataSourceProperties * @see FileDataSourceProperties + * @see RedisDataSourceProperties */ public class DataSourcePropertiesConfiguration { @@ -44,6 +45,8 @@ public class DataSourcePropertiesConfiguration { private ApolloDataSourceProperties apollo; + private RedisDataSourceProperties redis; + public DataSourcePropertiesConfiguration() { } @@ -63,7 +66,11 @@ public class DataSourcePropertiesConfiguration { this.apollo = apollo; } - public FileDataSourceProperties getFile() { + public DataSourcePropertiesConfiguration(RedisDataSourceProperties redis) { + this.redis = redis; + } + + public FileDataSourceProperties getFile() { return file; } @@ -95,6 +102,14 @@ public class DataSourcePropertiesConfiguration { this.apollo = apollo; } + public RedisDataSourceProperties getRedis() { + return redis; + } + + public void setRedis(RedisDataSourceProperties redis) { + this.redis = redis; + } + @JsonIgnore public List getValidField() { return Arrays.stream(this.getClass().getDeclaredFields()).map(field -> { diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java new file mode 100644 index 00000000..8bea7625 --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/RedisDataSourceProperties.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.alibaba.cloud.sentinel.datasource.config; + +import com.alibaba.cloud.sentinel.datasource.factorybean.RedisDataSourceFactoryBean; +import org.springframework.util.StringUtils; + +import java.time.Duration; +import java.util.List; + +/** + * Zookeeper Properties class Using by {@link DataSourcePropertiesConfiguration} and + * {@link RedisDataSourceFactoryBean} + * + * @author lengleng + */ +public class RedisDataSourceProperties extends AbstractDataSourceProperties { + + public RedisDataSourceProperties() { + super(RedisDataSourceFactoryBean.class.getName()); + } + + /** + * redis server host + */ + private String host = "localhost"; + + /** + * redis server port + */ + private int port = 6379; + + /** + * redis server password + */ + private String password; + + /** + * redis server default select database + */ + private int database; + + /** + * redis server timeout + */ + private Duration timeout; + + /** + * Comma-separated list of "host:port" pairs. + */ + private List nodes; + + /** + * data key in Redis + */ + private String ruleKey; + + /** + * channel to subscribe in Redis + */ + private String channel; + + /** + * redis sentinel model + */ + private String masterId; + + @Override + public void preCheck(String dataSourceName) { + super.preCheck(dataSourceName); + if (StringUtils.isEmpty(ruleKey)) { + throw new IllegalArgumentException( + "RedisDataSource ruleKey can not be empty"); + } + + if (StringUtils.isEmpty(channel)) { + throw new IllegalArgumentException( + "RedisDataSource channel can not be empty"); + } + + if (!StringUtils.isEmpty(masterId) && StringUtils.isEmpty(masterId)) { + throw new IllegalArgumentException( + "RedisDataSource sentinel model,masterId can not be empty"); + } + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getRuleKey() { + return ruleKey; + } + + public void setRuleKey(String ruleKey) { + this.ruleKey = ruleKey; + } + + public String getChannel() { + return channel; + } + + public void setChannel(String channel) { + this.channel = channel; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getDatabase() { + return database; + } + + public void setDatabase(int database) { + this.database = database; + } + + public Duration getTimeout() { + return timeout; + } + + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public String getMasterId() { + return masterId; + } + + public void setMasterId(String masterId) { + this.masterId = masterId; + } +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/ZookeeperDataSourceProperties.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/ZookeeperDataSourceProperties.java index 4d1755bf..3de046bc 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/ZookeeperDataSourceProperties.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/config/ZookeeperDataSourceProperties.java @@ -32,7 +32,7 @@ public class ZookeeperDataSourceProperties extends AbstractDataSourceProperties super(ZookeeperDataSourceFactoryBean.class.getName()); } - private String serverAddr; + private String serverAddr = "localhost:2181"; private String path; diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/converter/SentinelConverter.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/converter/SentinelConverter.java index 1d8269d7..cf2645cd 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/converter/SentinelConverter.java +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/converter/SentinelConverter.java @@ -16,6 +16,7 @@ package com.alibaba.cloud.sentinel.datasource.converter; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -27,17 +28,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; -import com.alibaba.cloud.sentinel.datasource.RuleType; import com.alibaba.csp.sentinel.datasource.Converter; import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; -import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleUtil; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; import com.alibaba.csp.sentinel.slots.system.SystemRule; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -88,56 +85,34 @@ public abstract class SentinelConverter List sourceArray = objectMapper.readValue(source, new TypeReference>() { }); - sourceArray.stream().forEach(obj -> { + for (Object obj : sourceArray) { String item = null; try { item = objectMapper.writeValueAsString(obj); + Optional.ofNullable(convertRule(item)) + .ifPresent(convertRule -> ruleCollection.add(convertRule)); } - catch (JsonProcessingException e) { - // won't be happen + catch (IOException e) { + log.error("sentinel rule convert error: " + e.getMessage(), e); + throw new IllegalArgumentException( + "sentinel rule convert error: " + e.getMessage(), e); } - - Optional.ofNullable(convertRule(item)) - .ifPresent(convertRule -> ruleCollection.add(convertRule)); - }); - - if (ruleCollection.size() != sourceArray.size()) { - throw new IllegalArgumentException("convert " + ruleCollection.size() - + " rules but there are " + sourceArray.size() - + " rules from datasource. RuleClass: " - + ruleClass.getSimpleName()); } } catch (Exception e) { - throw new RuntimeException("convert error: " + e.getMessage(), e); + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + else { + throw new RuntimeException("convert error: " + e.getMessage(), e); + } } return ruleCollection; } - private Object convertRule(String ruleStr) { - try { - final Object rule = objectMapper.readValue(ruleStr, ruleClass); - RuleType ruleType = RuleType.getByClass(ruleClass).get(); - switch (ruleType) { - case FLOW: - if (!FlowRuleUtil.isValidRule((FlowRule) rule)) { - return null; - } - break; - case DEGRADE: - if (!DegradeRuleManager.isValidRule((DegradeRule) rule)) { - return null; - } - default: - break; - } - return rule; - } - catch (Exception e) { - // ignore - } - return null; + private Object convertRule(String ruleStr) throws IOException { + return objectMapper.readValue(ruleStr, ruleClass); } } diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java new file mode 100644 index 00000000..bbf8435b --- /dev/null +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/java/com/alibaba/cloud/sentinel/datasource/factorybean/RedisDataSourceFactoryBean.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.alibaba.cloud.sentinel.datasource.factorybean; + +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.datasource.redis.RedisDataSource; +import com.alibaba.csp.sentinel.datasource.redis.config.RedisConnectionConfig; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import java.time.Duration; +import java.util.List; + +/** + * A {@link FactoryBean} for creating {@link RedisDataSource} instance. + * + * @author lengleng + * @see RedisDataSource + */ +public class RedisDataSourceFactoryBean implements FactoryBean { + + private String host; + + private int port; + + private int database; + + private Duration timeout; + + /** + * Comma-separated list of "host:port" pairs. + */ + private List nodes; + + private Converter converter; + + /** + * data key in Redis + */ + private String ruleKey; + + /** + * channel to subscribe in Redis + */ + private String channel; + + /** + * redis server password + */ + private String password; + + private String masterId; + + @Override + public RedisDataSource getObject() { + RedisConnectionConfig.Builder builder = RedisConnectionConfig.builder(); + + if (nodes == null || nodes.isEmpty()) { + builder.withHost(host) + .withPort(port) + .withDatabase(database); + } else { + nodes.forEach(node -> { + try { + String[] parts = StringUtils.split(node, ":"); + Assert.state(parts.length == 2, "Must be defined as 'host:port'"); + builder.withRedisSentinel(parts[0], Integer.parseInt(parts[1])); + } catch (RuntimeException ex) { + throw new IllegalStateException("Invalid redis sentinel property " + node, ex); + } + }); + builder.withSentinelMasterId(masterId); + } + + if (timeout != null) { + builder.withTimeout(timeout.toMillis()); + } + + if (StringUtils.hasText(password)) { + builder.withPassword(password); + } + + return new RedisDataSource>(builder.build(), ruleKey, channel, converter); + } + + @Override + public Class getObjectType() { + return RedisDataSource.class; + } + + public Converter getConverter() { + return converter; + } + + public void setConverter(Converter converter) { + this.converter = converter; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public String getRuleKey() { + return ruleKey; + } + + public void setRuleKey(String ruleKey) { + this.ruleKey = ruleKey; + } + + public String getChannel() { + return channel; + } + + public void setChannel(String channel) { + this.channel = channel; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public int getDatabase() { + return database; + } + + public void setDatabase(int database) { + this.database = database; + } + + public Duration getTimeout() { + return timeout; + } + + public void setTimeout(Duration timeout) { + this.timeout = timeout; + } + + public List getNodes() { + return nodes; + } + + public void setNodes(List nodes) { + this.nodes = nodes; + } + + public String getMasterId() { + return masterId; + } + + public void setMasterId(String masterId) { + this.masterId = masterId; + } +} diff --git a/spring-cloud-alibaba-sentinel-datasource/src/main/resources/META-INF/sentinel-datasource.properties b/spring-cloud-alibaba-sentinel-datasource/src/main/resources/META-INF/sentinel-datasource.properties index 8326eff6..3fa290ee 100644 --- a/spring-cloud-alibaba-sentinel-datasource/src/main/resources/META-INF/sentinel-datasource.properties +++ b/spring-cloud-alibaba-sentinel-datasource/src/main/resources/META-INF/sentinel-datasource.properties @@ -1,4 +1,5 @@ nacos = com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource file =com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource apollo = com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource -zk = com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource \ No newline at end of file +zk = com.alibaba.csp.sentinel.datasource.zookeeper.ZookeeperDataSource +redis = com.alibaba.csp.sentinel.datasource.redis.RedisDataSource \ No newline at end of file diff --git a/spring-cloud-alibaba-sentinel-gateway/pom.xml b/spring-cloud-alibaba-sentinel-gateway/pom.xml index fbdb5d71..8312f2f8 100644 --- a/spring-cloud-alibaba-sentinel-gateway/pom.xml +++ b/spring-cloud-alibaba-sentinel-gateway/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/ConfigConstants.java b/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/ConfigConstants.java index edbb6df4..89ae0453 100644 --- a/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/ConfigConstants.java +++ b/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/ConfigConstants.java @@ -22,6 +22,7 @@ package com.alibaba.cloud.sentinel.gateway; public interface ConfigConstants { String APP_TYPE_ZUUL_GATEWAY = "12"; + String APP_TYPE_SCG_GATEWAY = "11"; String ZUUl_PREFIX = "spring.cloud.sentinel.zuul"; diff --git a/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/scg/SentinelGatewayProperties.java b/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/scg/SentinelGatewayProperties.java index 35ead382..86e3996b 100644 --- a/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/scg/SentinelGatewayProperties.java +++ b/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/scg/SentinelGatewayProperties.java @@ -18,6 +18,7 @@ package com.alibaba.cloud.sentinel.gateway.scg; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.core.Ordered; import com.alibaba.cloud.sentinel.gateway.ConfigConstants; import com.alibaba.cloud.sentinel.gateway.FallbackProperties; @@ -31,6 +32,8 @@ public class SentinelGatewayProperties { @NestedConfigurationProperty private FallbackProperties fallback; + private Integer order = Ordered.HIGHEST_PRECEDENCE; + public FallbackProperties getFallback() { return fallback; } @@ -40,4 +43,11 @@ public class SentinelGatewayProperties { return this; } + public Integer getOrder() { + return order; + } + + public void setOrder(Integer order) { + this.order = order; + } } diff --git a/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/scg/SentinelSCGAutoConfiguration.java b/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/scg/SentinelSCGAutoConfiguration.java index 1a8e6835..b7693f38 100644 --- a/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/scg/SentinelSCGAutoConfiguration.java +++ b/spring-cloud-alibaba-sentinel-gateway/src/main/java/com/alibaba/cloud/sentinel/gateway/scg/SentinelSCGAutoConfiguration.java @@ -45,7 +45,6 @@ import org.springframework.web.server.ServerWebExchange; import com.alibaba.cloud.sentinel.gateway.ConfigConstants; import com.alibaba.cloud.sentinel.gateway.FallbackProperties; -import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants; import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter; import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler; import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager; @@ -94,7 +93,7 @@ public class SentinelSCGAutoConfiguration { private void initAppType() { System.setProperty(SentinelConfig.APP_TYPE, - String.valueOf(SentinelGatewayConstants.APP_TYPE_GATEWAY)); + String.valueOf(ConfigConstants.APP_TYPE_SCG_GATEWAY)); } private void initFallback() { @@ -149,8 +148,10 @@ public class SentinelSCGAutoConfiguration { @Order(-1) @ConditionalOnMissingBean public SentinelGatewayFilter sentinelGatewayFilter() { - logger.info("[Sentinel SpringCloudGateway] register SentinelGatewayFilter"); - return new SentinelGatewayFilter(); + logger.info( + "[Sentinel SpringCloudGateway] register SentinelGatewayFilter with order: {}", + gatewayProperties.getOrder()); + return new SentinelGatewayFilter(gatewayProperties.getOrder()); } } diff --git a/spring-cloud-alibaba-sentinel/pom.xml b/spring-cloud-alibaba-sentinel/pom.xml index 5f2a2ada..352cf953 100644 --- a/spring-cloud-alibaba-sentinel/pom.xml +++ b/spring-cloud-alibaba-sentinel/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelProperties.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelProperties.java index 8fab12bf..72dcfa99 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelProperties.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelProperties.java @@ -89,6 +89,11 @@ public class SentinelProperties { */ private Log log = new Log(); + /** + * Add HTTP method prefix for Sentinel Resource. + */ + private Boolean httpMethodSpecify = false; + public boolean isEager() { return eager; } @@ -161,7 +166,15 @@ public class SentinelProperties { this.log = log; } - public static class Flow { + public Boolean getHttpMethodSpecify() { + return httpMethodSpecify; + } + + public void setHttpMethodSpecify(Boolean httpMethodSpecify) { + this.httpMethodSpecify = httpMethodSpecify; + } + + public static class Flow { /** * The cold factor {@link SentinelConfig#COLD_FACTOR}. diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelWebAutoConfiguration.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelWebAutoConfiguration.java index dadb235f..5dfe6884 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelWebAutoConfiguration.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/SentinelWebAutoConfiguration.java @@ -91,6 +91,8 @@ public class SentinelWebAutoConfiguration { Filter filter = new CommonFilter(); registration.setFilter(filter); registration.setOrder(filterConfig.getOrder()); + registration.addInitParameter("HTTP_METHOD_SPECIFY", + String.valueOf(properties.getHttpMethodSpecify())); log.info( "[Sentinel Starter] register Sentinel CommonFilter with urlPatterns: {}.", filterConfig.getUrlPatterns()); diff --git a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/feign/SentinelInvocationHandler.java b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/feign/SentinelInvocationHandler.java index df68afb2..5564ca01 100644 --- a/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/feign/SentinelInvocationHandler.java +++ b/spring-cloud-alibaba-sentinel/src/main/java/com/alibaba/cloud/sentinel/feign/SentinelInvocationHandler.java @@ -94,44 +94,50 @@ public class SentinelInvocationHandler implements InvocationHandler { .get(hardCodedTarget.type().getName() + Feign.configKey(hardCodedTarget.type(), method)); // resource default is HttpMethod:protocol://url - String resourceName = methodMetadata.template().method().toUpperCase() + ":" - + hardCodedTarget.url() + methodMetadata.template().path(); - Entry entry = null; - try { - ContextUtil.enter(resourceName); - entry = SphU.entry(resourceName, EntryType.OUT, 1, args); + if (methodMetadata == null) { result = methodHandler.invoke(args); } - catch (Throwable ex) { - // fallback handle - if (!BlockException.isBlockException(ex)) { - Tracer.trace(ex); + else { + String resourceName = methodMetadata.template().method().toUpperCase() + + ":" + hardCodedTarget.url() + methodMetadata.template().path(); + Entry entry = null; + try { + ContextUtil.enter(resourceName); + entry = SphU.entry(resourceName, EntryType.OUT, 1, args); + result = methodHandler.invoke(args); } - if (fallbackFactory != null) { - try { - Object fallbackResult = fallbackMethodMap.get(method) - .invoke(fallbackFactory.create(ex), args); - return fallbackResult; + catch (Throwable ex) { + // fallback handle + if (!BlockException.isBlockException(ex)) { + Tracer.trace(ex); } - catch (IllegalAccessException e) { - // shouldn't happen as method is public due to being an interface - throw new AssertionError(e); + if (fallbackFactory != null) { + try { + Object fallbackResult = fallbackMethodMap.get(method) + .invoke(fallbackFactory.create(ex), args); + return fallbackResult; + } + catch (IllegalAccessException e) { + // shouldn't happen as method is public due to being an + // interface + throw new AssertionError(e); + } + catch (InvocationTargetException e) { + throw new AssertionError(e.getCause()); + } } - catch (InvocationTargetException e) { - throw new AssertionError(e.getCause()); + else { + // throw exception if fallbackFactory is null + throw ex; } } - else { - // throw exception if fallbackFactory is null - throw ex; + finally { + if (entry != null) { + entry.exit(1, args); + } + ContextUtil.exit(); } } - finally { - if (entry != null) { - entry.exit(1, args); - } - ContextUtil.exit(); - } } else { // other target type using default strategy diff --git a/spring-cloud-alicloud-acm/pom.xml b/spring-cloud-alicloud-acm/pom.xml index b9bbba73..f771a74a 100644 --- a/spring-cloud-alicloud-acm/pom.xml +++ b/spring-cloud-alicloud-acm/pom.xml @@ -6,7 +6,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-alicloud-acm diff --git a/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/AcmAutoConfiguration.java b/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/AcmAutoConfiguration.java index 4bb29694..ac82882f 100644 --- a/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/AcmAutoConfiguration.java +++ b/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/AcmAutoConfiguration.java @@ -18,6 +18,7 @@ package com.alibaba.alicloud.acm; import org.springframework.beans.BeansException; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.context.refresh.ContextRefresher; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -37,6 +38,7 @@ import com.taobao.diamond.client.Diamond; */ @Configuration @ConditionalOnClass({ Diamond.class }) +@ConditionalOnProperty(name = "spring.cloud.alicloud.acm.enabled", matchIfMissing = true) public class AcmAutoConfiguration implements ApplicationContextAware { private ApplicationContext applicationContext; diff --git a/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/bootstrap/AcmPropertySourceLocator.java b/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/bootstrap/AcmPropertySourceLocator.java index 6f59f333..3dae0710 100644 --- a/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/bootstrap/AcmPropertySourceLocator.java +++ b/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/bootstrap/AcmPropertySourceLocator.java @@ -17,6 +17,7 @@ package com.alibaba.alicloud.acm.bootstrap; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.bootstrap.config.PropertySourceLocator; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.Environment; @@ -28,6 +29,7 @@ import com.alibaba.alicloud.context.acm.AcmIntegrationProperties; * @author juven.xuxb * @author xiaolongzuo */ +@ConditionalOnProperty(name = "spring.cloud.alicloud.acm.enabled", matchIfMissing = true) public class AcmPropertySourceLocator implements PropertySourceLocator { private static final String DIAMOND_PROPERTY_SOURCE_NAME = "diamond"; diff --git a/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/endpoint/AcmEndpointAutoConfiguration.java b/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/endpoint/AcmEndpointAutoConfiguration.java index e997b1ac..587e90a2 100644 --- a/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/endpoint/AcmEndpointAutoConfiguration.java +++ b/spring-cloud-alicloud-acm/src/main/java/com/alibaba/alicloud/acm/endpoint/AcmEndpointAutoConfiguration.java @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; @@ -32,6 +33,7 @@ import com.alibaba.alicloud.context.acm.AcmProperties; */ @ConditionalOnWebApplication @ConditionalOnClass(name = "org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration") +@ConditionalOnProperty(name = "spring.cloud.alicloud.acm.enabled", matchIfMissing = true) public class AcmEndpointAutoConfiguration { @Autowired diff --git a/spring-cloud-alicloud-acm/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-alicloud-acm/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 29f53c76..34d3e4f9 100644 --- a/spring-cloud-alicloud-acm/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-alicloud-acm/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -4,6 +4,12 @@ "name": "spring.application.group", "type": "java.lang.String", "description": "spring application group." + }, + { + "name": "spring.cloud.alicloud.acm.enabled", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "enable acm or not." } ] } \ No newline at end of file diff --git a/spring-cloud-alicloud-ans/pom.xml b/spring-cloud-alicloud-ans/pom.xml index e6bf9917..16876a22 100644 --- a/spring-cloud-alicloud-ans/pom.xml +++ b/spring-cloud-alicloud-ans/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alicloud-context/pom.xml b/spring-cloud-alicloud-context/pom.xml index 59cbbc63..6aff6346 100644 --- a/spring-cloud-alicloud-context/pom.xml +++ b/spring-cloud-alicloud-context/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alicloud-oss/pom.xml b/spring-cloud-alicloud-oss/pom.xml index 68d30b85..85318849 100644 --- a/spring-cloud-alicloud-oss/pom.xml +++ b/spring-cloud-alicloud-oss/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-alicloud-schedulerx/pom.xml b/spring-cloud-alicloud-schedulerx/pom.xml index df28db4a..dac0e7c5 100644 --- a/spring-cloud-alicloud-schedulerx/pom.xml +++ b/spring-cloud-alicloud-schedulerx/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 spring-cloud-alicloud-schedulerx diff --git a/spring-cloud-alicloud-sms/pom.xml b/spring-cloud-alicloud-sms/pom.xml index 14f53905..de32ec3b 100644 --- a/spring-cloud-alicloud-sms/pom.xml +++ b/spring-cloud-alicloud-sms/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT 4.0.0 diff --git a/spring-cloud-starter-alibaba/pom.xml b/spring-cloud-starter-alibaba/pom.xml index 5afd96b0..9cfb5133 100644 --- a/spring-cloud-starter-alibaba/pom.xml +++ b/spring-cloud-starter-alibaba/pom.xml @@ -4,7 +4,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml spring-cloud-starter-alibaba diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config-server/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config-server/pom.xml index 6cdb3f93..f6013446 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config-server/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config-server/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alibaba-nacos-config-server Spring Cloud Starter Alibaba Nacos Config Server diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config/pom.xml index 0e58cd6e..0f9ca32d 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-config/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alibaba-nacos-config Spring Cloud Starter Alibaba Nacos Config diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-discovery/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-discovery/pom.xml index 1479674b..9a4c6d72 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-discovery/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-nacos-discovery/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alibaba-nacos-discovery Spring Cloud Starter Alibaba Nacos Discovery diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-seata/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-seata/pom.xml index 47cc5acd..64c25d06 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-seata/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-seata/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alibaba-seata Spring Cloud Starter Alibaba Seata diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml index cc903895..db1ec624 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-sentinel/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alibaba-sentinel Spring Cloud Starter Alibaba Sentinel diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/pom.xml index 44b1726f..efd7a14f 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-bus-rocketmq/pom.xml @@ -5,12 +5,11 @@ com.alibaba.cloud spring-cloud-starter-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0 - com.alibaba.cloud spring-cloud-starter-bus-rocketmq Spring Cloud Alibaba Bus RocketMQ @@ -30,4 +29,4 @@ - \ No newline at end of file + diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-dubbo/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-dubbo/pom.xml index 9d26ad96..28511ce4 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-dubbo/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-dubbo/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0 diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-stream-rocketmq/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-stream-rocketmq/pom.xml index 1ad87f56..c8eddc61 100644 --- a/spring-cloud-starter-alibaba/spring-cloud-starter-stream-rocketmq/pom.xml +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-stream-rocketmq/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml spring-cloud-starter-stream-rocketmq diff --git a/spring-cloud-starter-alicloud/pom.xml b/spring-cloud-starter-alicloud/pom.xml index 498be0fe..4ee2d7be 100644 --- a/spring-cloud-starter-alicloud/pom.xml +++ b/spring-cloud-starter-alicloud/pom.xml @@ -4,7 +4,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alicloud pom diff --git a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-acm/pom.xml b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-acm/pom.xml index 6195e93c..2417d872 100644 --- a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-acm/pom.xml +++ b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-acm/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alicloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alicloud-acm Spring Cloud Starter Alibaba Cloud ACM diff --git a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-ans/pom.xml b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-ans/pom.xml index 6bd99648..cbbce151 100644 --- a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-ans/pom.xml +++ b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-ans/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alicloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alicloud-ans diff --git a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-oss/pom.xml b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-oss/pom.xml index 4cad9b7c..2d8ef360 100644 --- a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-oss/pom.xml +++ b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-oss/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alicloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alicloud-oss Spring Cloud Starter Alibaba Cloud OSS diff --git a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-schedulerx/pom.xml b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-schedulerx/pom.xml index dc9f7bfe..a9fbdf9c 100644 --- a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-schedulerx/pom.xml +++ b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-schedulerx/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alicloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alicloud-schedulerx Spring Cloud Starter Alibaba Cloud SchedulerX diff --git a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-sms/pom.xml b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-sms/pom.xml index b53e0599..226977da 100644 --- a/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-sms/pom.xml +++ b/spring-cloud-starter-alicloud/spring-cloud-starter-alicloud-sms/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-starter-alicloud - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT spring-cloud-starter-alicloud-sms Spring Cloud Starter Alibaba Cloud SMS diff --git a/spring-cloud-stream-binder-rocketmq/pom.xml b/spring-cloud-stream-binder-rocketmq/pom.xml index 75979bd1..9b09fc4c 100644 --- a/spring-cloud-stream-binder-rocketmq/pom.xml +++ b/spring-cloud-stream-binder-rocketmq/pom.xml @@ -5,7 +5,7 @@ com.alibaba.cloud spring-cloud-alibaba - 0.9.1.BUILD-SNAPSHOT + 2.1.1.BUILD-SNAPSHOT ../pom.xml 4.0.0