diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2122207fd604020fa266f40ef6f1d9bdac988ad7..b162ac3a2675dd80c4b54019dea2ad0a337b829c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -10,16 +10,19 @@ Other... Please describe: ## Describe what this PR does for and how you did. +## Adding the issue link (#xxx) if possible. -## Does this PR be associated with issue? If so, please adding the issue link below. +## Note +## Checklist -## Note +- [ ] Add copyright holder at the beginning of .class file if it is new. +- [ ] Add information of this PR to CHANGELOG.md in root of project. +- [ ] All junit tests passing. +- [ ] Coverage from `Codecov Report` should not decrease. +## Checklist (Optional) -### Checklist -- [ ] Code compiles correctly -- [ ] Create at least one junit test if possible -- [ ] All tests passing -- [ ] Extend documentation if necessary -- [ ] Add myself / the copyright holder to the AUTHORS file \ No newline at end of file +- [ ] Will Pull Request to branch of 2020.0 and 2021.0. +- [ ] Add documentation in javadoc in code or comment below the PR if necessary. +- [ ] Add your name as @author to the beginning of .class file. diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml new file mode 100644 index 0000000000000000000000000000000000000000..cc452e8610d04b8faebd5b1980dd236de86e4a41 --- /dev/null +++ b/.github/workflows/codecov.yml @@ -0,0 +1,37 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Codecov + +on: + push: + branches: + - main + - 2021.0 + - 2020.0 + - greenwich + pull_request: + branches: + - main + - 2021.0 + - 2020.0 + - greenwich + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout codes + uses: actions/checkout@v3 + - name: Set up JDK 8 + uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: 8 + - name: Test with Maven + run: mvn -B test --file pom.xml + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + file: ${{ github.workspace }}/target/site/jacoco/jacoco.xml diff --git a/.github/workflows/junit_test.yml b/.github/workflows/junit_test.yml index 5738a45088931b69a4f5705b37fc08ee0c78b622..1652552471794b3de08477f6d398886802738917 100644 --- a/.github/workflows/junit_test.yml +++ b/.github/workflows/junit_test.yml @@ -5,24 +5,41 @@ name: Test with Junit on: push: - branches: [ main ] + branches: + - main + - 2021.0 + - 2020.0 + - greenwich pull_request: - branches: [ main ] + branches: + - main + - 2021.0 + - 2020.0 + - greenwich jobs: build: + strategy: + matrix: + java: [ 8, 11, 17 ] + os: [ 'windows-latest', 'ubuntu-latest' ] - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} steps: - name: Checkout codes - uses: actions/checkout@v2 - - name: Set up JDK 8 - uses: actions/setup-java@v2 + uses: actions/checkout@v3 + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v3 with: - java-version: '8' - distribution: 'adopt' -# - name: Build with Maven -# run: mvn -B package --file pom.xml + distribution: 'temurin' + java-version: ${{ matrix.java }} + - name: Cache local Maven repository + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - name: Test with Maven run: mvn -B test --file pom.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index c55e24688c74c90aafa2a473180dcdb633beae59..745526838061242391364b39fc673f9e57de8193 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,38 @@ # Change Log --- -- [the server address only support one ip](https://github.com/Tencent/spring-cloud-tencent/pull/36) -- [feature: get service list by given namespace](https://github.com/Tencent/spring-cloud-tencent/pull/38) -- [feat:Support multi-discovery server.](https://github.com/Tencent/spring-cloud-tencent/pull/42) -- [fix:fix [Deserialization of Untrusted Data in logback]](https://github.com/Tencent/spring-cloud-tencent/pull/44/files) -- [feat:support customize registered ip address.](https://github.com/Tencent/spring-cloud-tencent/pull/47) -- [fix:fix import default log implement error](https://github.com/Tencent/spring-cloud-tencent/pull/53) -- [send heartbeat if healthcheck url not configured](https://github.com/Tencent/spring-cloud-tencent/pull/54) -- [feat:optimize project structure and checkstyle](https://github.com/Tencent/spring-cloud-tencent/pull/56) -- [feat:divide storage and transfer of metadata.](https://github.com/Tencent/spring-cloud-tencent/pull/63) -- [feat:support polaris config center.](https://github.com/Tencent/spring-cloud-tencent/pull/69) -- [feat:optimize metadata module.](https://github.com/Tencent/spring-cloud-tencent/pull/70) -- [feat: optimize pom dependency and demo](https://github.com/Tencent/spring-cloud-tencent/pull/71) -- [feat:upgrade polaris version to 1.3.0.](https://github.com/Tencent/spring-cloud-tencent/pull/72) -- [feat:rollback init polaris-context to application phase](https://github.com/Tencent/spring-cloud-tencent/pull/73) +- [UT: Add config module unit test](https://github.com/Tencent/spring-cloud-tencent/pull/229) +- [Feature: Add config change listener feature support](https://github.com/Tencent/spring-cloud-tencent/pull/220) +- [Feature: Support spring cloud gateway routers](https://github.com/Tencent/spring-cloud-tencent/pull/230) +- [Feature: Add instance metadata spi for registration](https://github.com/Tencent/spring-cloud-tencent/pull/244) +- [Bugfix: fix guava version conflict bug & fix router strong dependency on LoadBalancerClientFilter](https://github.com/Tencent/spring-cloud-tencent/pull/236) +- [Upgrade: fix third-party lib CVEs & upgrade core spring libs version](https://github.com/Tencent/spring-cloud-tencent/pull/237) +- [change escape way into encode](https://github.com/Tencent/spring-cloud-tencent/pull/251) +- [feat:support reading configuration from application.yml or application.properties.](https://github.com/Tencent/spring-cloud-tencent/pull/259) +- [fix:fix ClassNotFoundException while not importing openfeign when using circuit-breaker module.](https://github.com/Tencent/spring-cloud-tencent/pull/269) +- [Update GitHub Actions workflow](https://github.com/Tencent/spring-cloud-tencent/pull/273) +- [fix:fix TypeNotPresentException in @ConditionalOnClass of router.](https://github.com/Tencent/spring-cloud-tencent/pull/276) +- [fix:solve the chaos code problem on rejectTips.](https://github.com/Tencent/spring-cloud-tencent/pull/279) +- [fix:solve ratelimit-callee-service UnknownHostException.](https://github.com/Tencent/spring-cloud-tencent/pull/281) +- [fix:refactor to use text/html resolve chaos problem on rejectTips](https://github.com/Tencent/spring-cloud-tencent/pull/285) +- [UT: add metadata-transfer unit test](https://github.com/Tencent/spring-cloud-tencent/pull/294) +- [Feature:add restTemplate Report Polaris](https://github.com/Tencent/spring-cloud-tencent/pull/272) +- [Use jdk constants instead of magic variables](https://github.com/Tencent/spring-cloud-tencent/pull/313) +- [Fix the current limiting effect is that other requests cannot be processed when queuing at a constant speed](https://github.com/Tencent/spring-cloud-tencent/pull/316) +- [Fix config file format misspell](https://github.com/Tencent/spring-cloud-tencent/pull/319) +- [UT: improve test coverage for load balancer unit test](https://github.com/Tencent/spring-cloud-tencent/pull/325) +- [optimize polaris load balancer test code format](https://github.com/Tencent/spring-cloud-tencent/pull/333) +- [feat:Add GitHub action of codecov.yml.](https://github.com/Tencent/spring-cloud-tencent/pull/328) +- [Feature: add spring cloud tencent logo](https://github.com/Tencent/spring-cloud-tencent/pull/329) +- [Feature: Optimize static metadata manager](https://github.com/Tencent/spring-cloud-tencent/pull/327) +- [Feature: support actuator for sct core components](https://github.com/Tencent/spring-cloud-tencent/pull/343) +- [test:update junit of metadata.](https://github.com/Tencent/spring-cloud-tencent/pull/340) +- [Optimize code style & unit test case](https://github.com/Tencent/spring-cloud-tencent/pull/336) +- [rm code: Condition 'null != interceptors' is always 'true' ](https://github.com/Tencent/spring-cloud-tencent/pull/342) +- [fix: shutdown thread pool before the container closes](https://github.com/Tencent/spring-cloud-tencent/pull/353) +- [docs:update logo in README.](https://github.com/Tencent/spring-cloud-tencent/pull/358) +- [Refator JacksonUtils and JacksonUtilsTest](https://github.com/Tencent/spring-cloud-tencent/pull/365) +- [docs: Fix javadoc
error](https://github.com/Tencent/spring-cloud-tencent/pull/371) +- [UT: add Polaris LoadBalancer unit test](https://github.com/Tencent/spring-cloud-tencent/pull/373) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8c7972aca584c5e434a3d860b326fdf4e42597a4..c5b1e573f8aa74ec99bb24dc5e3189b1febc802b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,4 +24,4 @@ Please confirm before completing a PR: 4. Ensure a consistent code style. 5. Do adequate testing. 6. Add this pull request info to [CHANGELOG](./CHANGELOG.md). -7. Then, you can submit your code to the dev branch. \ No newline at end of file +7. Then, you can submit your code to the dev branch. diff --git a/LICENSE b/LICENSE index 800722292d476e4498653e73dce1aa297258a68a..9840b7dba4d73729a2bede078eaa09cf4b308061 100644 --- a/LICENSE +++ b/LICENSE @@ -41,9 +41,6 @@ Copyright (c) guava authors and contributors. 6. reactor Copyright (c) reactor authors and contributors. -7. powermock -Copyright 2007-2017 PowerMock Contributors - Terms of the Apache v2.0 License: -------------------------------------------------------------------- diff --git a/README-zh.md b/README-zh.md index 9553842a1eb7420273d58a9fe3e5d3a8101419b5..f298792852b39593db4da949d7b1968d3cc1252c 100644 --- a/README-zh.md +++ b/README-zh.md @@ -1,4 +1,11 @@ -# Spring Cloud Tencent +Spring-Cloud-Tencent-Logo + +[![Wiki](https://badgen.net/badge/icon/wiki?icon=wiki&label)](https://github.com/Tencent/spring-cloud-tencent/wiki) +[![Build Status](https://github.com/Tencent/spring-cloud-tencent/actions/workflows/junit_test.yml/badge.svg)](https://github.com/Tencent/spring-cloud-tencent/actions/workflows/junit_test.yml) +[![Maven Central](https://img.shields.io/maven-central/v/com.tencent.cloud/spring-cloud-tencent?label=Maven%20Central)](https://search.maven.org/search?q=g:com.tencent.cloud%20AND%20a:spring-cloud-tencent) +[![codecov.io](https://codecov.io/gh/Tencent/spring-cloud-tencent/branch/main/graph/badge.svg)](https://codecov.io/gh/Tencent/spring-cloud-tencent?branch=main) +[![Contributors](https://img.shields.io/github/contributors/Tencent/spring-cloud-tencent)](https://github.com/Tencent/spring-cloud-tencent/graphs/contributors) +[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [English](./README.md) | 简体中文 @@ -6,76 +13,97 @@ ## 介绍 -Spring Cloud Tencent包含了分布式应用微服务开发过程中所需的组件,基于 Spring Cloud 框架的开发者可以使用这些组件快速进行分布式应用的开发。 - -## 主要功能 - -* **服务注册与发现**:基于 Spring Cloud Common的标准进行微服务的注册与发现。 -* **服务路由与负载均衡**:基于 Ribbon 的接口标准,提供场景更丰富的动态路由以及负载均衡的能力。 -* **故障节点熔断**:提供故障节点的熔断剔除以及主/被动探测恢复的能力,保证分布式服务的可靠性。 -* **服务限流**:支持微服务被调接入层和网关主动调用的限流功能,保证后台微服务稳定性,可通过控制台动态配置规则,及查看流量监控数据。 -* **元数据传递**: 支持网关及微服务应用之间的自定义元数据传递。 - -## 如何构建 - -* [2020.0.x](https://github.com/Tencent/spring-cloud-tencent/tree/2020.0.x)分支对应的是 Spring Cloud 2020.0版本,编译环境最低支持JDK 1.8。 -* [main](https://github.com/Tencent/spring-cloud-tencent/tree/main) 分支对应的是 Spring Cloud Hoxton版本,编译环境最低支持JDK 1.8。 -* [greenwich](https://github.com/Tencent/spring-cloud-tencent/tree/greenwich) 分支对应的是 Spring Cloud Greenwich版本,编译环境最低支持JDK 1.8。 - -Spring Cloud Tencent 使用 Maven 来构建,最快的使用方式是将本项目 clone 到本地,然后执行以下命令: -```bash - ./mvnw install -``` -执行完毕后,项目将被安装到本地 Maven 仓库。 - -## 如何使用 - -### 如何引入依赖 - -在 dependencyManagement 中添加如下配置,然后在 dependencies 中添加自己所需使用的依赖即可使用。 +Spring Cloud Tencent 是腾讯开源的一站式微服务解决方案。 + +Spring Cloud Tencent 实现了Spring Cloud 标准微服务 SPI,开发者可以基于 Spring Cloud Tencent 快速开发 Spring Cloud 云原生分布式应用。 + +Spring Cloud Tencent 的核心依托腾讯开源的一站式服务发现与治理平台 [Polaris](https://github.com/polarismesh/polaris),实现各种分布式微服务场景。 + +- [Polaris Github home page](https://github.com/polarismesh/polaris) +- [Polaris official website](https://polarismesh.cn/) + +Spring Cloud Tencent提供的能力包括但不限于: + +image + +- 服务注册和发现 +- 动态配置管理 +- 服务治理 + - 服务限流 + - 服务熔断 + - 服务路由 + - ... +- 标签透传 + +## 体验环境 + +- 管控台地址: http://14.116.241.63:8080/ + - 账号:polaris + - 密码:polaris +- 控制面地址: `grpc://183.47.111.80:8091` +- + `spring-cloud-tencent-example` 下 example 地址都默认指向了体验服务地址(`grpc://183.47.111.80:8091`),如果您只是体验 Spring Cloud Tencent,可直接一键运行任何 example。 +## 管控台 + +image + +## 使用指南 + +Spring Cloud Tencent 所有组件都已上传到 Maven 中央仓库,只需要引入依赖即可。 + +例如: + +```` xml + + + + + com.tencent.cloud + spring-cloud-tencent-dependencies + + ${version} + pom + import + + + + + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + ```` - - - - com.tencent.cloud - spring-cloud-tencent-dependencies - 1.1.4.Hoxton.SR9 - pom - import - - - -```` - -### 示例 - -Spring Cloud Tencent 项目包含了一个子模块spring-cloud-tencent-examples。此模块中提供了体验接入用的 example ,您可以阅读对应的 example 工程下的 readme 文档,根据里面的步骤来体验。 - -Example 列表: -- [PolarisMesh](https://github.com/polarismesh)接入相关的样例: +- ### 快速开始 + - [Spring Cloud Tencent 版本管理](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86) + - [Spring Cloud Tencent 服务注册与发现](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Discovery-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) + - [Spring Cloud Tencent 配置中心](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Config-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) + - [Spring Cloud Tencent 限流](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Rate-Limit-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) + - [Spring Cloud Tencent 熔断](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Circuitbreaker-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) + - [Spring Cloud Tencent 服务路由](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Router-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) + - [Spring Cloud Tencent 标签传递](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Metadata-Transfer-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97) - - [服务发现](spring-cloud-tencent-examples/polaris-discovery-example/README-zh.md) +- ### 开发文档 + - [项目概览](https://github.com/Tencent/spring-cloud-tencent/wiki/%E9%A1%B9%E7%9B%AE%E6%A6%82%E8%A7%88) + - [参与共建](https://github.com/Tencent/spring-cloud-tencent/wiki/%E5%8F%82%E4%B8%8E%E5%85%B1%E5%BB%BA) - - [故障熔断](spring-cloud-tencent-examples/polaris-circuitbreaker-example/README-zh.md) +## 交流群 - - [限流](spring-cloud-tencent-examples/polaris-ratelimit-example/README-zh.md) +扫描下面的二维码加入 Spring Cloud Tencent 交流群。 - - [网关](spring-cloud-tencent-examples/polaris-gateway-example/README-zh.md) + -更多详细功能,请参考[polaris-java](https://github.com/polarismesh/polaris-java/blob/main/README-zh.md)。 -## 版本号规范 +## License +The spring-cloud-tencent is licensed under the BSD 3-Clause License. Copyright and license information can be found in the file [LICENSE](LICENSE) -采取与Spring Cloud大版本号相关的版本策略。 +## Stargazers over time -项目的版本号格式为 ```大版本号.小版本号.补丁版本号-对应Spring Cloud的大版本号.对应Spring Cloud的小版本号-发布类型``` 的形式。 -大版本号、小版本号、补丁版本号的类型为数字,从 0 开始取值。 -对应Spring Cloud的大版本号为Spring Cloud提供的英文版本号,例如Hoxton、Greenwich等。对应Spring Cloud的小版本号为Spring Cloud给出的小版本号,例如 RS9 等。 -发布类型目前包括正式发布和发布候选版(RC)。在实际的版本号中,正式发布版不额外添加发布类型,发布候选版将添加后缀,并从 RC0 开始。 +如果您对 Spring Cloud Tencent 有兴趣,请关注我们的项目~ -示例:1.2.0-Hoxton.SR9-RC0 +[![Stargazers over time](https://starchart.cc/Tencent/spring-cloud-tencent.svg)](https://starchart.cc/Tencent/spring-cloud-tencent) -## License -The spring-cloud-tencent is licensed under the BSD 3-Clause License. Copyright and license information can be found in the file [LICENSE](LICENSE) diff --git a/README.md b/README.md index b726f862acb05fd861c7efe3ebd03ecdfaf2833f..2c2965a5309b40064858f64ba9e68cfd108cc57a 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,109 @@ -# Spring Cloud Tencent +Spring-Cloud-Tencent-Logo + +[![Wiki](https://badgen.net/badge/icon/wiki?icon=wiki&label)](https://github.com/Tencent/spring-cloud-tencent/wiki) [![Build Status](https://github.com/Tencent/spring-cloud-tencent/actions/workflows/junit_test.yml/badge.svg)](https://github.com/Tencent/spring-cloud-tencent/actions/workflows/junit_test.yml) [![Maven Central](https://img.shields.io/maven-central/v/com.tencent.cloud/spring-cloud-tencent?label=Maven%20Central)](https://search.maven.org/search?q=g:com.tencent.cloud%20AND%20a:spring-cloud-tencent) +[![codecov.io](https://codecov.io/gh/Tencent/spring-cloud-tencent/branch/main/graph/badge.svg)](https://codecov.io/gh/Tencent/spring-cloud-tencent?branch=main) +[![Contributors](https://img.shields.io/github/contributors/Tencent/spring-cloud-tencent)](https://github.com/Tencent/spring-cloud-tencent/graphs/contributors) +[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) English | [简体中文](./README-zh.md) ## Introduction -Spring Cloud Tencent contains components distributed micro-service applications need during developing phase, developers that built their key architectures based on Spring Cloud can use these components - -Based on Spring Cloud Tencent, you only need a small configuration to launch Spring Cloud and micro-service's joint solutions. - -## Key Features +Spring Cloud Tencent is a open source one-stop microservice solution from Tencent. -* **Service Registration and Discovery**: Based on Spring Cloud's discovery and registration standard. -* **Service Routing and LoadBalancer**: Based on ribbon's API port, provide dynamic routing and load balancing use cases. -* **CircuitBreaker Node**: Support circuitbreaker auto-reset ability, ensure the reliability of distributed server -* **Rate Limiter**: Support rate limit of microservice and gateway, ensure the stability of backend, one can configure policies and traffic data from the control panel -* **Metadata Delivery**: Support metadata delivery between gateways and microservices. +Spring Cloud Tencent implements the Spring Cloud standard microservice SPI, so developers can quickly develop Spring Cloud cloud-native distributed applications based on Spring Cloud Tencent. -## Components +The core of Spring Cloud Tencent relies on Tencent's open-source one-stop service discovery and governance platform [Polaris](https://github.com/polarismesh/polaris) to realize various distributed microservice scenarios. -**[Polaris](https://github.com/PolarisMesh/polaris)**:Polaris Spring Cloud operation centre, provide solutions to registration, dynamic routing, load balancing and circuitbreaker. +- [Polaris Github home page](https://github.com/polarismesh/polaris) +- [Polaris official website](https://polarismesh.cn/) -## How to build +The capabilities provided by Spring Cloud Tencent include but are not limited to: -* master's branch matches Spring Cloud Hoxton, support lowest at JDK 1.8. +image -Spring Cloud Tencent uses Maven to construct, the fastest way is to clone project to local files, then execute the following orders: +- Service registration and discovery +- Dynamic configuration management +- Service Governance + - Service rate limit + - Service circuit breaker + - Service routing + - ... +- Label transparent transmission -```bash -./mvnw install -``` +## Demo Environment -When all the steps are finished, the project will be installed in local Maven repository. +- Console Address : http://14.116.241.63:8080/ + - Username: polaris + - Password: polaris +- Server Address: `grpc://183.47.111.80:8091` -## How to Use +The example addresses under `spring-cloud-tencent-example` all point to the experience service address (`grpc://183.47.111.80:8091`) by default. +If you only experience Spring Cloud Tencent, you can run any example directly with one click. -### How to Introduce Dependency +## Screenshots -Add the following configurations in dependencyManagement, then add the dependencies you need. -At the same time, you need to pay attention to the Spring Cloud version corresponding to Spring Cloud Tencent, and then the corresponding Spring Boot version. -For example, Spring Cloud Tencent's 1.0.1.Hoxton.SR9 corresponds to the Spring Cloud Hoxton version and requires Spring Boot 2.3.x. +image -```` - - - - com.tencent.cloud - spring-cloud-tencent-dependencies - - ${version} - pom - import - - - -```` +## Use Guide -### Example +All the components of Spring Cloud Tencent have been uploaded to the Maven central repository, just need to introduce dependencies. -Spring Cloud Tencent project contains a sub-module spring-cloud-tencent-examples. This module provides examples for users to experience, you can read the README.md in each example, and follow the instructions there. +For example: -Example List: +```` xml + + + + + com.tencent.cloud + spring-cloud-tencent-dependencies + + ${version} + pom + import + + + + + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + -- [Polaris Discovery Example](spring-cloud-tencent-examples/polaris-discovery-example/README.md) +```` -- [Polaris CircuitBreaker Example](spring-cloud-tencent-examples/polaris-circuitbreaker-example/README.md) + - ### Quick Start + - [Spring Cloud Tencent Version Management](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-%E7%89%88%E6%9C%AC%E7%AE%A1%E7%90%86) + - [Spring Cloud Tencent Discovery](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Discovery-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) + - [Spring Cloud Tencent Config](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Config-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) + - [Spring Cloud Tencent Rate Limit](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Rate-Limit-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) + - [Spring Cloud Tencent CircuitBreaker](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Circuitbreaker-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) + - [Spring Cloud Tencent Router](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Router-%E4%BD%BF%E7%94%A8%E6%96%87%E6%A1%A3) + - [Spring Cloud Tencent Metadata Transfer](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Metadata-Transfer-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97) -- [Polaris RateLimit Example](spring-cloud-tencent-examples/polaris-ratelimit-example/README.md) +- ### Development Documentation + - [Project Structure Overview](https://github.com/Tencent/spring-cloud-tencent/wiki/%E9%A1%B9%E7%9B%AE%E6%A6%82%E8%A7%88) + - [Participate in co-construction](https://github.com/Tencent/spring-cloud-tencent/wiki/%E5%8F%82%E4%B8%8E%E5%85%B1%E5%BB%BA) + +## Chat Group -- [Polaris Gateway Example](spring-cloud-tencent-examples/polaris-gateway-example/README.md) +Please scan the QR code to join the chat group. -For more features, please refer to [polaris-java](https://github.com/polarismesh/polaris-java). + -### Version Standard +## License +The spring-cloud-tencent is licensed under the BSD 3-Clause License. Copyright and license information can be found in the file [LICENSE](LICENSE) -We use a version policy related to Spring Cloud's major version number. -Project version includes ```${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}-${CORRESPONDING_MAJOR_VERSION_OF_SPRING_CLOUD}.${CORRESPONDING_MINOR_VERSION_OF_SPRING_CLOUD}-${RELEASE_TYPE}```. -```${MAJOR_VERSION}```, ```${MINOR_VERSION}```, ```${PATCH_VERSION}``` are in numbers starting from 0. -```${CORRESPONDING_MAJOR_VERSION_OF_SPRING_CLOUD}``` is the same as the major version number of Spring Cloud, like Hoxton, Greenwich. ```${CORRESPONDING_MINOR_VERSION_OF_SPRING_CLOUD}``` is the same as the major version number of Spring Cloud, like RS9. -```${RELEASE_TYPE}``` is like RELEASE or RC currently. Actually, the RELEASE version does not add a release type in the version, and the RS version will add a suffix and start from RC0. +## Stargazers over time -For example: 1.2.0-Hoxton.SR9-RC0 +If you are interested in Spring Cloud Tencent, please follow our project, thank you very much. -## License -The spring-cloud-tencent is licensed under the BSD 3-Clause License. Copyright and license information can be found in the file [LICENSE](LICENSE) +[![Stargazers over time](https://starchart.cc/Tencent/spring-cloud-tencent.svg)](https://starchart.cc/Tencent/spring-cloud-tencent) diff --git a/changes/changes-1.2.0.md b/changes/changes-1.2.0.md new file mode 100644 index 0000000000000000000000000000000000000000..536654eac145f0792a6d0e793bd969b2f0ef845e --- /dev/null +++ b/changes/changes-1.2.0.md @@ -0,0 +1,20 @@ +# Change Log +--- + +- [the server address only support one ip](https://github.com/Tencent/spring-cloud-tencent/pull/36) +- [feature: get service list by given namespace](https://github.com/Tencent/spring-cloud-tencent/pull/38) +- [feat:Support multi-discovery server.](https://github.com/Tencent/spring-cloud-tencent/pull/42) +- [fix:fix [Deserialization of Untrusted Data in logback]](https://github.com/Tencent/spring-cloud-tencent/pull/44/files) +- [feat:support customize registered ip address.](https://github.com/Tencent/spring-cloud-tencent/pull/47) +- [fix:fix import default log implement error](https://github.com/Tencent/spring-cloud-tencent/pull/53) +- [send heartbeat if healthcheck url not configured](https://github.com/Tencent/spring-cloud-tencent/pull/54) +- [feat:optimize project structure and checkstyle](https://github.com/Tencent/spring-cloud-tencent/pull/56) +- [feat:divide storage and transfer of metadata.](https://github.com/Tencent/spring-cloud-tencent/pull/63) +- [feat:support polaris config center.](https://github.com/Tencent/spring-cloud-tencent/pull/69) +- [feat:optimize metadata module.](https://github.com/Tencent/spring-cloud-tencent/pull/70) +- [feat: optimize pom dependency and demo](https://github.com/Tencent/spring-cloud-tencent/pull/71) +- [feat:upgrade polaris version to 1.3.0.](https://github.com/Tencent/spring-cloud-tencent/pull/72) +- [feat:rollback init polaris-context to application phase](https://github.com/Tencent/spring-cloud-tencent/pull/73) +- [feat:tencent-commons add spring-cloud-starter dependency](https://github.com/Tencent/spring-cloud-tencent/pull/76) +- [fix:fix fetching wrong PEER_SERVICE in SCG filter.](https://github.com/Tencent/spring-cloud-tencent/pull/79) + diff --git a/changes/changes-1.3.0.md b/changes/changes-1.3.0.md new file mode 100644 index 0000000000000000000000000000000000000000..ff2da2d2cfe6174e225e4f2c72ca23997fe3fae3 --- /dev/null +++ b/changes/changes-1.3.0.md @@ -0,0 +1,12 @@ +# Change Log +--- + +- [Bugfix: fix router bug and add router example](https://github.com/Tencent/spring-cloud-tencent/pull/89) +- [feat:add custom label resolver spi for rate limit](https://github.com/Tencent/spring-cloud-tencent/pull/105) +- [feat:fix discovery weight param not set to register request bug](https://github.com/Tencent/spring-cloud-tencent/pull/102) +- [Bugfix: fix causing cpu 100% when set ScheduledThreadPoolExecutor corePoolSize=0](https://github.com/Tencent/spring-cloud-tencent/pull/98) +- [Bugfix: fix circuitbreaker http code greater than 400 as fail response bug](https://github.com/Tencent/spring-cloud-tencent/pull/116) +- [Feat: optimize router dependency](https://github.com/Tencent/spring-cloud-tencent/pull/110) +- [Refactor: refactor transfer metadata](https://github.com/Tencent/spring-cloud-tencent/pull/111) +- [feat:add switch of polaris, discovery and register.](https://github.com/Tencent/spring-cloud-tencent/pull/122) + And [#124](https://github.com/Tencent/spring-cloud-tencent/pull/124) diff --git a/changes/changes-1.4.0.md b/changes/changes-1.4.0.md new file mode 100644 index 0000000000000000000000000000000000000000..2784188a0f14ab6ff3a7321e0906a8390ba46cf7 --- /dev/null +++ b/changes/changes-1.4.0.md @@ -0,0 +1,8 @@ +# Change Log +--- + +- [Feature: Support custom rate limit reject response info](https://github.com/Tencent/spring-cloud-tencent/pull/128) +- [Feature: Optimize config server address](https://github.com/Tencent/spring-cloud-tencent/pull/130) +- [Feature: Remove spring-javaformat-maven-plugin](https://github.com/Tencent/spring-cloud-tencent/pull/131) +- [feat:refactor loadbalancer module as a basic module for router and circuit breaker.](https://github.com/Tencent/spring-cloud-tencent/pull/136) +- [feat:enable distribute rate limit](https://github.com/Tencent/spring-cloud-tencent/pull/139) \ No newline at end of file diff --git a/changes/changes-1.4.2.md b/changes/changes-1.4.2.md new file mode 100644 index 0000000000000000000000000000000000000000..af06c333121b2fea59149938a6fbbeae100a3141 --- /dev/null +++ b/changes/changes-1.4.2.md @@ -0,0 +1,5 @@ +# Change Log +--- + +- [fix:fix routes of gateway doesn't refresh bug.](https://github.com/Tencent/spring-cloud-tencent/pull/158) +- [fix:Turn off automatic injection of Polars rule.](https://github.com/Tencent/spring-cloud-tencent/pull/162) diff --git a/changes/changes-1.4.3.md b/changes/changes-1.4.3.md new file mode 100644 index 0000000000000000000000000000000000000000..c8e3ed71d63ea7f78cfeb4e2da0496732a2625a1 --- /dev/null +++ b/changes/changes-1.4.3.md @@ -0,0 +1,5 @@ +# Change Log +--- + +- [fix:fix wrong context data storage.](https://github.com/Tencent/spring-cloud-tencent/pull/170) +- [fix:fix route not refreshing bug when first instance of one service up.](https://github.com/Tencent/spring-cloud-tencent/pull/174) diff --git a/changes/changes-1.4.4.md b/changes/changes-1.4.4.md new file mode 100644 index 0000000000000000000000000000000000000000..1aa0ff7404e87b2857538ee91e3194b99235243a --- /dev/null +++ b/changes/changes-1.4.4.md @@ -0,0 +1,4 @@ +# Change Log +--- + +- [fix:fix wrong isAliveFlag config when creating new PolarisServer.](https://github.com/Tencent/spring-cloud-tencent/pull/179) diff --git a/changes/changes-1.5.0.md b/changes/changes-1.5.0.md new file mode 100644 index 0000000000000000000000000000000000000000..e1f9ededea927314e1e910fb4ba776a0a559d517 --- /dev/null +++ b/changes/changes-1.5.0.md @@ -0,0 +1,15 @@ +# Change Log +--- + +- [Feature: Support parse ratelimit rule expression labels.](https://github.com/Tencent/spring-cloud-tencent/pull/183) +- [Feature: Router support request label.](https://github.com/Tencent/spring-cloud-tencent/pull/165) +- [Feature: Support router expression label](https://github.com/Tencent/spring-cloud-tencent/pull/190) +- [Add metadata transfer example.](https://github.com/Tencent/spring-cloud-tencent/pull/184) +- [Feature: Support metadata router.](https://github.com/Tencent/spring-cloud-tencent/pull/191) +- [Feature: Misc optimize metadata router.](https://github.com/Tencent/spring-cloud-tencent/pull/192) +- [Feature:Support near by router.](https://github.com/Tencent/spring-cloud-tencent/pull/196) +- [Feature: Load application.yml and application-${profile}.yml from polaris server.](https://github.com/Tencent/spring-cloud-tencent/pull/199) +- [feat:add rate limit of unirate.](https://github.com/Tencent/spring-cloud-tencent/pull/197) +- [test:add junit test to polaris-circuitbreaker.](https://github.com/Tencent/spring-cloud-tencent/pull/202) +- [test:add junit test to polaris-discovery.](https://github.com/Tencent/spring-cloud-tencent/pull/205) +- [Example:set example polaris address to demo environment.](https://github.com/Tencent/spring-cloud-tencent/pull/206) diff --git a/changes/changes-1.5.1.md b/changes/changes-1.5.1.md new file mode 100644 index 0000000000000000000000000000000000000000..a846de737862f5fa31067ef0511d8ef28930413f --- /dev/null +++ b/changes/changes-1.5.1.md @@ -0,0 +1,5 @@ +# Change Log +--- + +- [fix:fix consul connect bug.](https://github.com/Tencent/spring-cloud-tencent/pull/217) +- [fix:fix PolarisRegistration cannot get metadata bug.](https://github.com/Tencent/spring-cloud-tencent/pull/218) diff --git a/changes/changes-1.5.2.md b/changes/changes-1.5.2.md new file mode 100644 index 0000000000000000000000000000000000000000..c524907d291203c78ba6a270634c8937406950bf --- /dev/null +++ b/changes/changes-1.5.2.md @@ -0,0 +1,5 @@ +# Change Log +--- + +- [fix:use 1.6.1 version of polaris-java.](https://github.com/Tencent/spring-cloud-tencent/pull/221) +- [fix:use 1.6.1 version of polaris-java and fix some bug.](https://github.com/Tencent/spring-cloud-tencent/pull/222) diff --git a/doc/logo/1280x640-transparent.png b/doc/logo/1280x640-transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..9e722651cdc331f5c26a4ad5c3bdd25eaf50c048 Binary files /dev/null and b/doc/logo/1280x640-transparent.png differ diff --git a/doc/logo/1280x640-transparent.svg b/doc/logo/1280x640-transparent.svg new file mode 100644 index 0000000000000000000000000000000000000000..ed57761db982e714f7b71bbd975e14da9dfb59f4 --- /dev/null +++ b/doc/logo/1280x640-transparent.svg @@ -0,0 +1,17 @@ + + + 1280x640-svg透明 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/logo/1280x640-white.png b/doc/logo/1280x640-white.png new file mode 100644 index 0000000000000000000000000000000000000000..21181cfdafa0cc2f7624645c2ca76709df8edffc Binary files /dev/null and b/doc/logo/1280x640-white.png differ diff --git a/doc/logo/1280x640-white.svg b/doc/logo/1280x640-white.svg new file mode 100644 index 0000000000000000000000000000000000000000..e09dba3f998a27369501171fcb60ae42b6b69921 --- /dev/null +++ b/doc/logo/1280x640-white.svg @@ -0,0 +1,17 @@ + + + 1280x640-svg白底 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/logo/640x640-transparent.png b/doc/logo/640x640-transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..67e0c3f4ff357326a25b4ca256063b10d0fe223f Binary files /dev/null and b/doc/logo/640x640-transparent.png differ diff --git a/doc/logo/640x640-transparent.svg b/doc/logo/640x640-transparent.svg new file mode 100644 index 0000000000000000000000000000000000000000..bb45e2cd1bed4259201f18b7abd724e1ac046c95 --- /dev/null +++ b/doc/logo/640x640-transparent.svg @@ -0,0 +1,17 @@ + + + 640x640-svg透明 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/logo/640x640-white.png b/doc/logo/640x640-white.png new file mode 100644 index 0000000000000000000000000000000000000000..b4cfaab59882b0f97b38c5566207096e78dd51d5 Binary files /dev/null and b/doc/logo/640x640-white.png differ diff --git a/doc/logo/640x640-white.svg b/doc/logo/640x640-white.svg new file mode 100644 index 0000000000000000000000000000000000000000..f5870166a51f37fb89d0309b6ea3a26be1846f6e --- /dev/null +++ b/doc/logo/640x640-white.svg @@ -0,0 +1,17 @@ + + + 640x640-svg白底 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/logo/rectangle-transparent.png b/doc/logo/rectangle-transparent.png new file mode 100644 index 0000000000000000000000000000000000000000..772332df25043d6572d04a08fa94a66b9a7a0e2d Binary files /dev/null and b/doc/logo/rectangle-transparent.png differ diff --git a/doc/logo/rectangle-transparent.svg b/doc/logo/rectangle-transparent.svg new file mode 100644 index 0000000000000000000000000000000000000000..c4c34ccc351b5e4fb4885ea7759efc64f6c9d892 --- /dev/null +++ b/doc/logo/rectangle-transparent.svg @@ -0,0 +1,15 @@ + + + 长款的 svg格式透明 + + + + Spring Cloud Tencent + + + + + + + + \ No newline at end of file diff --git a/doc/logo/rectangle-white.png b/doc/logo/rectangle-white.png new file mode 100644 index 0000000000000000000000000000000000000000..46a1c77d9a264617aaee25147f7ce0e959ef59ae Binary files /dev/null and b/doc/logo/rectangle-white.png differ diff --git a/doc/logo/rectangle-white.svg b/doc/logo/rectangle-white.svg new file mode 100644 index 0000000000000000000000000000000000000000..5bc74ffb70fa119f362fa518ac85a9e1d838129f --- /dev/null +++ b/doc/logo/rectangle-white.svg @@ -0,0 +1,16 @@ + + + 长款的 svg 格式的白底 + + + + + Spring Cloud Tencent + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index d70a45685208236d545893f5fec87af7dea800f6..6e726b7b8397b361f0f6762f77395244a11e8662 100644 --- a/pom.xml +++ b/pom.xml @@ -1,46 +1,48 @@ - - org.springframework.cloud - spring-cloud-build - 2.3.4.RELEASE - - - 4.0.0 + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + org.springframework.cloud + spring-cloud-build + 2.3.5.RELEASE + + + 4.0.0 - com.tencent.cloud - spring-cloud-tencent - pom - ${revision} - Spring Cloud Tencent - Spring Cloud Tencent - https://github.com/Tencent/spring-cloud-tencent/tree/main + com.tencent.cloud + spring-cloud-tencent + pom + ${revision} + Spring Cloud Tencent + Spring Cloud Tencent + https://github.com/Tencent/spring-cloud-tencent/tree/main - - Tencent - https://opensource.tencent.com/ - + + Tencent + https://opensource.tencent.com/ + - - - The BSD 3-Clause License (BSD3) - https://raw.githubusercontent.com/Tencent/spring-cloud-tencent/main/LICENSE - repo - - + + + The BSD 3-Clause License (BSD3) + https://raw.githubusercontent.com/Tencent/spring-cloud-tencent/main/LICENSE + repo + + - - https://github.com/Tencent/spring-cloud-tencent - scm:git:git@github.com:Tencent/spring-cloud-tencent.git - scm:git:git@github.com:Tencent/spring-cloud-tencent.git - + + https://github.com/Tencent/spring-cloud-tencent + scm:git:git@github.com:Tencent/spring-cloud-tencent.git + scm:git:git@github.com:Tencent/spring-cloud-tencent.git + - - spring-cloud-tencent-polaris-context + spring-cloud-tencent-commons + spring-cloud-tencent-polaris-context + spring-cloud-tencent-polaris-loadbalancer spring-cloud-starter-tencent-metadata-transfer + spring-cloud-starter-tencent-polaris-config spring-cloud-starter-tencent-polaris-discovery spring-cloud-starter-tencent-polaris-ratelimit spring-cloud-starter-tencent-polaris-circuitbreaker @@ -48,7 +50,6 @@ spring-cloud-tencent-dependencies spring-cloud-tencent-examples spring-cloud-tencent-coverage - spring-cloud-starter-tencent-polaris-config @@ -60,6 +61,14 @@ https://github.com/SkyeBeFreeman/ + + lepdou + lepdou + lepdou@126.com + Tencent + https://github.com/lepdou + + Andrew Shan samshan08@126.com @@ -77,18 +86,19 @@ - 1.2.0-Hoxton.SR9-SNAPSHOT + 1.6.0-Hoxton.SR12-SNAPSHOT - Hoxton.SR9 + Hoxton.SR12 - - 1.2.7 + + 5.2.22.RELEASE - 0.8.3 + 0.8.8 3.2.0 1.2.7 + 3.0.1 true @@ -99,28 +109,31 @@ - + - org.springframework.cloud - spring-cloud-dependencies - ${spring.cloud.version} + com.tencent.cloud + spring-cloud-tencent-dependencies + ${revision} pom import - + - com.tencent.cloud - spring-cloud-tencent-dependencies - ${revision} + org.springframework + spring-framework-bom + ${spring.framework.version} pom import + - ch.qos.logback - logback-classic - ${logback.version} + org.springframework.cloud + spring-cloud-dependencies + ${spring.cloud.version} + pom + import @@ -136,10 +149,6 @@ - - io.spring.javaformat - spring-javaformat-maven-plugin - org.apache.maven.plugins maven-checkstyle-plugin @@ -258,6 +267,7 @@ ${maven-gpg-plugin.version} + sign-artifacts verify sign @@ -266,31 +276,31 @@ - + - - - nexus-snapshots - https://oss.sonatype.org/content/repositories/snapshots/ - - - nexus-releases - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - + + + nexus-snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + + nexus-releases + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + - - - nexus-snapshots - https://oss.sonatype.org/content/repositories/snapshots/ - - false - - - true - - - + + + nexus-snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + false + + + true + + + diff --git a/spring-cloud-starter-tencent-metadata-transfer/pom.xml b/spring-cloud-starter-tencent-metadata-transfer/pom.xml index 212bd2b29b6462dfb89618a0f8f5e14b7a5bab47..ad4714f7ae422666bf34d0054eb8e04a6a3a599c 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/pom.xml +++ b/spring-cloud-starter-tencent-metadata-transfer/pom.xml @@ -33,6 +33,12 @@ true + + org.springframework.boot + spring-boot-starter-web + true + + org.springframework.cloud spring-cloud-starter-openfeign @@ -50,20 +56,6 @@ spring-cloud-starter-netflix-ribbon test - - - - org.powermock - powermock-module-junit4 - test - - - - - org.powermock - powermock-api-mockito2 - test - diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java index eb30f331869c247a88167749455385ede54a85cc..9f7514ae6f71eb47a1791932d4b2c20f362db1dc 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfiguration.java @@ -13,6 +13,7 @@ * 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.tencent.cloud.metadata.config; @@ -21,14 +22,19 @@ import java.util.List; import java.util.Map; import com.netflix.zuul.ZuulFilter; -import com.tencent.cloud.metadata.core.filter.gateway.Metadata2HeaderScgFilter; -import com.tencent.cloud.metadata.core.filter.gateway.Metadata2HeaderZuulFilter; -import com.tencent.cloud.metadata.core.interceptor.Metadata2HeaderFeignInterceptor; -import com.tencent.cloud.metadata.core.interceptor.Metadata2HeaderRestTemplateInterceptor; +import com.tencent.cloud.common.constant.MetadataConstant; +import com.tencent.cloud.metadata.core.DecodeTransferMetadataReactiveFilter; +import com.tencent.cloud.metadata.core.DecodeTransferMetadataServletFilter; +import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignInterceptor; +import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateInterceptor; +import com.tencent.cloud.metadata.core.EncodeTransferMedataScgFilter; +import com.tencent.cloud.metadata.core.EncodeTransferMetadataZuulFilter; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -38,6 +44,12 @@ import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.util.CollectionUtils; import org.springframework.web.client.RestTemplate; +import static javax.servlet.DispatcherType.ASYNC; +import static javax.servlet.DispatcherType.ERROR; +import static javax.servlet.DispatcherType.FORWARD; +import static javax.servlet.DispatcherType.INCLUDE; +import static javax.servlet.DispatcherType.REQUEST; + /** * Metadata transfer auto configuration. * @@ -46,6 +58,46 @@ import org.springframework.web.client.RestTemplate; @Configuration public class MetadataTransferAutoConfiguration { + /** + * Create when web application type is SERVLET. + */ + @Configuration + @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) + static class MetadataServletFilterConfig { + + @Bean + public FilterRegistrationBean metadataServletFilterRegistrationBean( + DecodeTransferMetadataServletFilter decodeTransferMetadataServletFilter) { + FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>( + decodeTransferMetadataServletFilter); + filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, + REQUEST); + filterRegistrationBean + .setOrder(MetadataConstant.OrderConstant.WEB_FILTER_ORDER); + return filterRegistrationBean; + } + + @Bean + public DecodeTransferMetadataServletFilter metadataServletFilter() { + return new DecodeTransferMetadataServletFilter(); + } + + } + + /** + * Create when web application type is REACTIVE. + */ + @Configuration + @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) + static class MetadataReactiveFilterConfig { + + @Bean + public DecodeTransferMetadataReactiveFilter metadataReactiveFilter() { + return new DecodeTransferMetadataReactiveFilter(); + } + + } + /** * Create when gateway application is Zuul. */ @@ -54,8 +106,8 @@ public class MetadataTransferAutoConfiguration { static class MetadataTransferZuulFilterConfig { @Bean - public ZuulFilter metadata2HeaderZuulFilter() { - return new Metadata2HeaderZuulFilter(); + public ZuulFilter encodeTransferMetadataZuulFilter() { + return new EncodeTransferMetadataZuulFilter(); } } @@ -68,8 +120,8 @@ public class MetadataTransferAutoConfiguration { static class MetadataTransferScgFilterConfig { @Bean - public GlobalFilter metadata2HeaderScgFilter() { - return new Metadata2HeaderScgFilter(); + public GlobalFilter encodeTransferMedataScgFilter() { + return new EncodeTransferMedataScgFilter(); } } @@ -82,8 +134,8 @@ public class MetadataTransferAutoConfiguration { static class MetadataTransferFeignInterceptorConfig { @Bean - public Metadata2HeaderFeignInterceptor metadata2HeaderFeignInterceptor() { - return new Metadata2HeaderFeignInterceptor(); + public EncodeTransferMedataFeignInterceptor encodeTransferMedataFeignInterceptor() { + return new EncodeTransferMedataFeignInterceptor(); } } @@ -98,13 +150,13 @@ public class MetadataTransferAutoConfiguration { private ApplicationContext context; @Bean - public Metadata2HeaderRestTemplateInterceptor metadata2HeaderRestTemplateInterceptor() { - return new Metadata2HeaderRestTemplateInterceptor(); + public EncodeTransferMedataRestTemplateInterceptor encodeTransferMedataRestTemplateInterceptor() { + return new EncodeTransferMedataRestTemplateInterceptor(); } @Bean - BeanPostProcessor metadata2HeaderRestTemplatePostProcessor( - Metadata2HeaderRestTemplateInterceptor metadata2HeaderRestTemplateInterceptor) { + BeanPostProcessor encodeTransferMetadataRestTemplatePostProcessor( + EncodeTransferMedataRestTemplateInterceptor encodeTransferMedataRestTemplateInterceptor) { // Coping with multiple bean injection scenarios Map beans = this.context .getBeansOfType(RestTemplate.class); @@ -116,15 +168,14 @@ public class MetadataTransferAutoConfiguration { List interceptors = restTemplate .getInterceptors(); // Avoid setting interceptor repeatedly. - if (null != interceptors && !interceptors - .contains(metadata2HeaderRestTemplateInterceptor)) { - interceptors.add(metadata2HeaderRestTemplateInterceptor); + if (!interceptors.contains(encodeTransferMedataRestTemplateInterceptor)) { + interceptors.add(encodeTransferMedataRestTemplateInterceptor); restTemplate.setInterceptors(interceptors); } } } - return new Metadata2HeaderRestTemplatePostProcessor( - metadata2HeaderRestTemplateInterceptor); + return new EncodeTransferMetadataRestTemplatePostProcessor( + encodeTransferMedataRestTemplateInterceptor); } @Override @@ -133,14 +184,14 @@ public class MetadataTransferAutoConfiguration { this.context = applicationContext; } - public static class Metadata2HeaderRestTemplatePostProcessor + public static class EncodeTransferMetadataRestTemplatePostProcessor implements BeanPostProcessor { - private Metadata2HeaderRestTemplateInterceptor metadata2HeaderRestTemplateInterceptor; + private EncodeTransferMedataRestTemplateInterceptor encodeTransferMedataRestTemplateInterceptor; - Metadata2HeaderRestTemplatePostProcessor( - Metadata2HeaderRestTemplateInterceptor metadata2HeaderRestTemplateInterceptor) { - this.metadata2HeaderRestTemplateInterceptor = metadata2HeaderRestTemplateInterceptor; + EncodeTransferMetadataRestTemplatePostProcessor( + EncodeTransferMedataRestTemplateInterceptor encodeTransferMedataRestTemplateInterceptor) { + this.encodeTransferMedataRestTemplateInterceptor = encodeTransferMedataRestTemplateInterceptor; } @Override @@ -155,9 +206,8 @@ public class MetadataTransferAutoConfiguration { List interceptors = restTemplate .getInterceptors(); // Avoid setting interceptor repeatedly. - if (null != interceptors && !interceptors - .contains(metadata2HeaderRestTemplateInterceptor)) { - interceptors.add(this.metadata2HeaderRestTemplateInterceptor); + if (!interceptors.contains(encodeTransferMedataRestTemplateInterceptor)) { + interceptors.add(this.encodeTransferMedataRestTemplateInterceptor); restTemplate.setInterceptors(interceptors); } } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/CustomTransitiveMetadataResolver.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/CustomTransitiveMetadataResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..01be55f2b6d20acc29f5c760e115bd7afdf5b7b3 --- /dev/null +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/CustomTransitiveMetadataResolver.java @@ -0,0 +1,80 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.metadata.core; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang.StringUtils; + +import org.springframework.http.HttpHeaders; +import org.springframework.util.CollectionUtils; +import org.springframework.web.server.ServerWebExchange; + +/** + * Resolve custom transitive metadata from request. + * @author lepdou 2022-05-20 + */ +public class CustomTransitiveMetadataResolver { + + private static final String TRANSITIVE_HEADER_PREFIX = "X-SCT-Metadata-Transitive-"; + private static final int TRANSITIVE_HEADER_PREFIX_LENGTH = TRANSITIVE_HEADER_PREFIX.length(); + + public static Map resolve(ServerWebExchange exchange) { + Map result = new HashMap<>(); + + HttpHeaders headers = exchange.getRequest().getHeaders(); + for (Map.Entry> entry : headers.entrySet()) { + String key = entry.getKey(); + + if (StringUtils.isNotBlank(key) && + StringUtils.startsWithIgnoreCase(key, TRANSITIVE_HEADER_PREFIX) + && !CollectionUtils.isEmpty(entry.getValue())) { + + String sourceKey = StringUtils.substring(key, TRANSITIVE_HEADER_PREFIX_LENGTH); + result.put(sourceKey, entry.getValue().get(0)); + } + } + + return result; + } + + public static Map resolve(HttpServletRequest request) { + Map result = new HashMap<>(); + + Enumeration headers = request.getHeaderNames(); + while (headers.hasMoreElements()) { + String key = headers.nextElement(); + + if (StringUtils.isNotBlank(key) && + StringUtils.startsWithIgnoreCase(key, TRANSITIVE_HEADER_PREFIX) + && StringUtils.isNotBlank(request.getHeader(key))) { + + String sourceKey = StringUtils.substring(key, TRANSITIVE_HEADER_PREFIX_LENGTH); + result.put(sourceKey, request.getHeader(key)); + } + } + + return result; + } +} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/web/MetadataReactiveFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java similarity index 74% rename from spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/web/MetadataReactiveFilter.java rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java index adbf4a7806102de2cd9387b3accbfa2ce1e40997..3a258560ecab994390366fb861a954b23302b30b 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/web/MetadataReactiveFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilter.java @@ -13,12 +13,15 @@ * 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.tencent.cloud.common.metadata.filter.web; +package com.tencent.cloud.metadata.core; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; import java.util.Map; import com.tencent.cloud.common.constant.MetadataConstant; @@ -42,10 +45,10 @@ import org.springframework.web.server.WebFilterChain; * * @author Haotian Zhang */ -public class MetadataReactiveFilter implements WebFilter, Ordered { +public class DecodeTransferMetadataReactiveFilter implements WebFilter, Ordered { private static final Logger LOG = LoggerFactory - .getLogger(MetadataReactiveFilter.class); + .getLogger(DecodeTransferMetadataReactiveFilter.class); @Override public int getOrder() { @@ -57,12 +60,34 @@ public class MetadataReactiveFilter implements WebFilter, Ordered { WebFilterChain webFilterChain) { // Get metadata string from http header. ServerHttpRequest serverHttpRequest = serverWebExchange.getRequest(); + + Map internalTransitiveMetadata = getIntervalTransitiveMetadata(serverHttpRequest); + Map customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(serverWebExchange); + + Map mergedTransitiveMetadata = new HashMap<>(); + mergedTransitiveMetadata.putAll(internalTransitiveMetadata); + mergedTransitiveMetadata.putAll(customTransitiveMetadata); + + MetadataContextHolder.init(mergedTransitiveMetadata); + + // Save to ServerWebExchange. + serverWebExchange.getAttributes().put( + MetadataConstant.HeaderName.METADATA_CONTEXT, + MetadataContextHolder.get()); + + return webFilterChain.filter(serverWebExchange) + .doOnError(throwable -> LOG.error("handle metadata[{}] error.", + MetadataContextHolder.get(), throwable)) + .doFinally((type) -> MetadataContextHolder.remove()); + } + + private Map getIntervalTransitiveMetadata(ServerHttpRequest serverHttpRequest) { HttpHeaders httpHeaders = serverHttpRequest.getHeaders(); String customMetadataStr = httpHeaders .getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA); try { if (StringUtils.hasText(customMetadataStr)) { - customMetadataStr = URLDecoder.decode(customMetadataStr, "UTF-8"); + customMetadataStr = URLDecoder.decode(customMetadataStr, StandardCharsets.UTF_8.name()); } } catch (UnsupportedEncodingException e) { @@ -70,20 +95,8 @@ public class MetadataReactiveFilter implements WebFilter, Ordered { } LOG.debug("Get upstream metadata string: {}", customMetadataStr); - // create custom metadata. - Map upstreamCustomMetadataMap = JacksonUtils - .deserialize2Map(customMetadataStr); - - MetadataContextHolder.init(upstreamCustomMetadataMap, null); - - // Save to ServerWebExchange. - serverWebExchange.getAttributes().put( - MetadataConstant.HeaderName.METADATA_CONTEXT, - MetadataContextHolder.get()); - return webFilterChain.filter(serverWebExchange) - .doOnError(throwable -> LOG.error("handle metadata[{}] error.", - MetadataContextHolder.get(), throwable)) - .doFinally((type) -> MetadataContextHolder.remove()); + return JacksonUtils.deserialize2Map(customMetadataStr); } + } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/web/MetadataServletFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java similarity index 71% rename from spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/web/MetadataServletFilter.java rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java index 018313ec411d717756bd3feee4da03287ed3a99e..10aa7b60fee0659b86164681f984e5966bf874b6 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/web/MetadataServletFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilter.java @@ -13,13 +13,16 @@ * 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.tencent.cloud.common.metadata.filter.web; +package com.tencent.cloud.metadata.core; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; import java.util.Map; import javax.servlet.FilterChain; @@ -44,21 +47,39 @@ import org.springframework.web.filter.OncePerRequestFilter; * @author Haotian Zhang */ @Order(MetadataConstant.OrderConstant.WEB_FILTER_ORDER) -public class MetadataServletFilter extends OncePerRequestFilter { +public class DecodeTransferMetadataServletFilter extends OncePerRequestFilter { private static final Logger LOG = LoggerFactory - .getLogger(MetadataServletFilter.class); + .getLogger(DecodeTransferMetadataServletFilter.class); @Override protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { + Map internalTransitiveMetadata = getInternalTransitiveMetadata(httpServletRequest); + Map customTransitiveMetadata = CustomTransitiveMetadataResolver.resolve(httpServletRequest); + + Map mergedTransitiveMetadata = new HashMap<>(); + mergedTransitiveMetadata.putAll(internalTransitiveMetadata); + mergedTransitiveMetadata.putAll(customTransitiveMetadata); + + try { + MetadataContextHolder.init(mergedTransitiveMetadata); + + filterChain.doFilter(httpServletRequest, httpServletResponse); + } + catch (IOException | ServletException | RuntimeException e) { + throw e; + } + } + + private Map getInternalTransitiveMetadata(HttpServletRequest httpServletRequest) { // Get custom metadata string from http header. String customMetadataStr = httpServletRequest .getHeader(MetadataConstant.HeaderName.CUSTOM_METADATA); try { if (StringUtils.hasText(customMetadataStr)) { - customMetadataStr = URLDecoder.decode(customMetadataStr, "UTF-8"); + customMetadataStr = URLDecoder.decode(customMetadataStr, StandardCharsets.UTF_8.name()); } } catch (UnsupportedEncodingException e) { @@ -67,20 +88,7 @@ public class MetadataServletFilter extends OncePerRequestFilter { LOG.debug("Get upstream metadata string: {}", customMetadataStr); // create custom metadata. - Map upstreamCustomMetadataMap = JacksonUtils - .deserialize2Map(customMetadataStr); - - try { - MetadataContextHolder.init(upstreamCustomMetadataMap, null); - - filterChain.doFilter(httpServletRequest, httpServletResponse); - } - catch (IOException | ServletException | RuntimeException e) { - throw e; - } - finally { - MetadataContextHolder.remove(); - } + return JacksonUtils.deserialize2Map(customMetadataStr); } } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/interceptor/Metadata2HeaderFeignInterceptor.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java similarity index 68% rename from spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/interceptor/Metadata2HeaderFeignInterceptor.java rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java index 99cb21d680d579a9be103755563113c9b6bbf588..cf889d75421647a362af75e6d54b6711347da4eb 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/interceptor/Metadata2HeaderFeignInterceptor.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptor.java @@ -13,12 +13,14 @@ * 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.tencent.cloud.metadata.core.interceptor; +package com.tencent.cloud.metadata.core; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Map; import com.tencent.cloud.common.constant.MetadataConstant; @@ -41,10 +43,10 @@ import static com.tencent.cloud.common.constant.MetadataConstant.HeaderName.CUST * * @author Haotian Zhang */ -public class Metadata2HeaderFeignInterceptor implements RequestInterceptor, Ordered { +public class EncodeTransferMedataFeignInterceptor implements RequestInterceptor, Ordered { private static final Logger LOG = LoggerFactory - .getLogger(Metadata2HeaderFeignInterceptor.class); + .getLogger(EncodeTransferMedataFeignInterceptor.class); @Override public int getOrder() { @@ -55,33 +57,18 @@ public class Metadata2HeaderFeignInterceptor implements RequestInterceptor, Orde public void apply(RequestTemplate requestTemplate) { // get metadata of current thread MetadataContext metadataContext = MetadataContextHolder.get(); + Map customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); - // add new metadata and cover old - if (!CollectionUtils.isEmpty(requestTemplate.headers()) && !CollectionUtils - .isEmpty(requestTemplate.headers().get(CUSTOM_METADATA))) { - for (String headerMetadataStr : requestTemplate.headers() - .get(CUSTOM_METADATA)) { - Map headerMetadataMap = JacksonUtils - .deserialize2Map(headerMetadataStr); - for (String key : headerMetadataMap.keySet()) { - metadataContext.putTransitiveCustomMetadata(key, - headerMetadataMap.get(key)); - } - } - } - - Map customMetadata = metadataContext - .getAllTransitiveCustomMetadata(); if (!CollectionUtils.isEmpty(customMetadata)) { - String metadataStr = JacksonUtils.serialize2Json(customMetadata); + String encodedTransitiveMetadata = JacksonUtils.serialize2Json(customMetadata); requestTemplate.removeHeader(CUSTOM_METADATA); try { requestTemplate.header(CUSTOM_METADATA, - URLEncoder.encode(metadataStr, "UTF-8")); + URLEncoder.encode(encodedTransitiveMetadata, StandardCharsets.UTF_8.name())); } catch (UnsupportedEncodingException e) { LOG.error("Set header failed.", e); - requestTemplate.header(CUSTOM_METADATA, metadataStr); + requestTemplate.header(CUSTOM_METADATA, encodedTransitiveMetadata); } } } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/interceptor/Metadata2HeaderRestTemplateInterceptor.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java similarity index 74% rename from spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/interceptor/Metadata2HeaderRestTemplateInterceptor.java rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java index 2ce9d587548e1855c6437006771e88d254dc771d..9f3a7940fe8bca0b7cdcf6ac940aa8c25871cfd2 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/interceptor/Metadata2HeaderRestTemplateInterceptor.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptor.java @@ -13,13 +13,15 @@ * 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.tencent.cloud.metadata.core.interceptor; +package com.tencent.cloud.metadata.core; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Map; import com.tencent.cloud.common.constant.MetadataConstant; @@ -33,7 +35,6 @@ import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; /** * Interceptor used for adding the metadata in http headers from context when web client @@ -41,7 +42,7 @@ import org.springframework.util.StringUtils; * * @author Haotian Zhang */ -public class Metadata2HeaderRestTemplateInterceptor +public class EncodeTransferMedataRestTemplateInterceptor implements ClientHttpRequestInterceptor, Ordered { @Override @@ -54,31 +55,20 @@ public class Metadata2HeaderRestTemplateInterceptor ClientHttpRequestExecution clientHttpRequestExecution) throws IOException { // get metadata of current thread MetadataContext metadataContext = MetadataContextHolder.get(); + Map customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); - // add new metadata and cover old - String metadataStr = httpRequest.getHeaders() - .getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA); - if (!StringUtils.isEmpty(metadataStr)) { - Map headerMetadataMap = JacksonUtils - .deserialize2Map(metadataStr); - for (String key : headerMetadataMap.keySet()) { - metadataContext.putTransitiveCustomMetadata(key, - headerMetadataMap.get(key)); - } - } - Map customMetadata = metadataContext - .getAllTransitiveCustomMetadata(); if (!CollectionUtils.isEmpty(customMetadata)) { - metadataStr = JacksonUtils.serialize2Json(customMetadata); + String encodedTransitiveMetadata = JacksonUtils.serialize2Json(customMetadata); try { httpRequest.getHeaders().set(MetadataConstant.HeaderName.CUSTOM_METADATA, - URLEncoder.encode(metadataStr, "UTF-8")); + URLEncoder.encode(encodedTransitiveMetadata, StandardCharsets.UTF_8.name())); } catch (UnsupportedEncodingException e) { httpRequest.getHeaders().set(MetadataConstant.HeaderName.CUSTOM_METADATA, - metadataStr); + encodedTransitiveMetadata); } } + return clientHttpRequestExecution.execute(httpRequest, bytes); } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/filter/gateway/Metadata2HeaderScgFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java similarity index 88% rename from spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/filter/gateway/Metadata2HeaderScgFilter.java rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java index ce81ad8e13034d46c06fd8ee2257ef9e2aa0d2c6..cc3aa2aa148ce00cd579f8dbe7660ada978378f3 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/filter/gateway/Metadata2HeaderScgFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilter.java @@ -13,12 +13,14 @@ * 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.tencent.cloud.metadata.core.filter.gateway; +package com.tencent.cloud.metadata.core; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Map; import com.tencent.cloud.common.constant.MetadataConstant; @@ -41,7 +43,7 @@ import static org.springframework.cloud.gateway.filter.LoadBalancerClientFilter. * * @author Haotian Zhang */ -public class Metadata2HeaderScgFilter implements GlobalFilter, Ordered { +public class EncodeTransferMedataScgFilter implements GlobalFilter, Ordered { private static final int METADATA_SCG_FILTER_ORDER = LOAD_BALANCER_CLIENT_FILTER_ORDER + 1; @@ -64,13 +66,12 @@ public class Metadata2HeaderScgFilter implements GlobalFilter, Ordered { if (metadataContext == null) { metadataContext = MetadataContextHolder.get(); } - Map customMetadata = metadataContext - .getAllTransitiveCustomMetadata(); + Map customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); if (!CollectionUtils.isEmpty(customMetadata)) { String metadataStr = JacksonUtils.serialize2Json(customMetadata); try { builder.header(MetadataConstant.HeaderName.CUSTOM_METADATA, - URLEncoder.encode(metadataStr, "UTF-8")); + URLEncoder.encode(metadataStr, StandardCharsets.UTF_8.name())); } catch (UnsupportedEncodingException e) { builder.header(MetadataConstant.HeaderName.CUSTOM_METADATA, metadataStr); diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/filter/gateway/Metadata2HeaderZuulFilter.java b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMetadataZuulFilter.java similarity index 87% rename from spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/filter/gateway/Metadata2HeaderZuulFilter.java rename to spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMetadataZuulFilter.java index a2e2a334e31e2b47ee87306c45d046902db202f8..bb34a96f6d284794c48ebde6155ef6e0076dd697 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/filter/gateway/Metadata2HeaderZuulFilter.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/main/java/com/tencent/cloud/metadata/core/EncodeTransferMetadataZuulFilter.java @@ -13,12 +13,14 @@ * 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.tencent.cloud.metadata.core.filter.gateway; +package com.tencent.cloud.metadata.core; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.Map; import com.netflix.zuul.ZuulFilter; @@ -38,7 +40,7 @@ import static org.springframework.cloud.netflix.zuul.filters.support.FilterConst * * @author Haotian Zhang */ -public class Metadata2HeaderZuulFilter extends ZuulFilter { +public class EncodeTransferMetadataZuulFilter extends ZuulFilter { @Override public String filterType() { @@ -64,14 +66,13 @@ public class Metadata2HeaderZuulFilter extends ZuulFilter { MetadataContext metadataContext = MetadataContextHolder.get(); // add new metadata and cover old - Map customMetadata = metadataContext - .getAllTransitiveCustomMetadata(); + Map customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); if (!CollectionUtils.isEmpty(customMetadata)) { String metadataStr = JacksonUtils.serialize2Json(customMetadata); try { requestContext.addZuulRequestHeader( MetadataConstant.HeaderName.CUSTOM_METADATA, - URLEncoder.encode(metadataStr, "UTF-8")); + URLEncoder.encode(metadataStr, StandardCharsets.UTF_8.name())); } catch (UnsupportedEncodingException e) { requestContext.addZuulRequestHeader( diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java index 5b1c7aaeb8df91124db5b45a18f3c6a8835b6e0f..87968b8b39c6c5920b187acd5714cde735033589 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/config/MetadataTransferAutoConfigurationTest.java @@ -13,13 +13,14 @@ * 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.tencent.cloud.metadata.config; -import com.tencent.cloud.metadata.core.filter.gateway.Metadata2HeaderZuulFilter; -import com.tencent.cloud.metadata.core.interceptor.Metadata2HeaderFeignInterceptor; -import com.tencent.cloud.metadata.core.interceptor.Metadata2HeaderRestTemplateInterceptor; +import com.tencent.cloud.metadata.core.EncodeTransferMedataFeignInterceptor; +import com.tencent.cloud.metadata.core.EncodeTransferMedataRestTemplateInterceptor; +import com.tencent.cloud.metadata.core.EncodeTransferMetadataZuulFilter; import org.assertj.core.api.Assertions; import org.junit.Test; @@ -28,7 +29,7 @@ import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.cloud.gateway.filter.GlobalFilter; /** - * Test for {@link MetadataTransferAutoConfiguration} + * Test for {@link MetadataTransferAutoConfiguration}. * * @author Haotian Zhang */ @@ -48,17 +49,17 @@ public class MetadataTransferAutoConfigurationTest { Assertions.assertThat(context).hasSingleBean( MetadataTransferAutoConfiguration.MetadataTransferFeignInterceptorConfig.class); Assertions.assertThat(context) - .hasSingleBean(Metadata2HeaderFeignInterceptor.class); + .hasSingleBean(EncodeTransferMedataFeignInterceptor.class); Assertions.assertThat(context).hasSingleBean( MetadataTransferAutoConfiguration.MetadataTransferRestTemplateConfig.class); - Assertions.assertThat(context) - .hasSingleBean(Metadata2HeaderRestTemplateInterceptor.class); Assertions.assertThat(context).hasSingleBean( - MetadataTransferAutoConfiguration.MetadataTransferRestTemplateConfig.Metadata2HeaderRestTemplatePostProcessor.class); + EncodeTransferMedataRestTemplateInterceptor.class); + Assertions.assertThat(context).hasSingleBean( + MetadataTransferAutoConfiguration.MetadataTransferRestTemplateConfig.EncodeTransferMetadataRestTemplatePostProcessor.class); Assertions.assertThat(context).hasSingleBean( MetadataTransferAutoConfiguration.MetadataTransferZuulFilterConfig.class); Assertions.assertThat(context) - .hasSingleBean(Metadata2HeaderZuulFilter.class); + .hasSingleBean(EncodeTransferMetadataZuulFilter.class); Assertions.assertThat(context).hasSingleBean( MetadataTransferAutoConfiguration.MetadataTransferScgFilterConfig.class); Assertions.assertThat(context).hasSingleBean(GlobalFilter.class); diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/CustomTransitiveMetadataResolverTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/CustomTransitiveMetadataResolverTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9dc83bd51e4bd76c26992b1225a631f81c24b3bf --- /dev/null +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/CustomTransitiveMetadataResolverTest.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.metadata.core; + +import java.util.Map; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.server.MockServerWebExchange; + +/** + * Test for {@link CustomTransitiveMetadataResolver}. + * + * @author quan + */ +public class CustomTransitiveMetadataResolverTest { + + @Test + public void test() { + MockServerHttpRequest.BaseBuilder builder = MockServerHttpRequest.get(""); + builder.header("X-SCT-Metadata-Transitive-a", "test"); + MockServerWebExchange exchange = MockServerWebExchange.from(builder); + Map resolve = CustomTransitiveMetadataResolver.resolve(exchange); + Assertions.assertThat(resolve.size()).isEqualTo(1); + Assertions.assertThat(resolve.get("a")).isEqualTo("test"); + } + + @Test + public void testServlet() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("X-SCT-Metadata-Transitive-a", "test"); + Map resolve = CustomTransitiveMetadataResolver.resolve(request); + Assertions.assertThat(resolve.size()).isEqualTo(1); + Assertions.assertThat(resolve.get("a")).isEqualTo("test"); + } +} diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/filter/web/MetadataReactiveFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java similarity index 86% rename from spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/filter/web/MetadataReactiveFilterTest.java rename to spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java index d32fc77fdf962f46df2ba7a4fded174f63efb331..8edd3400c17d83da54b222888a0f65e7ce543de2 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/filter/web/MetadataReactiveFilterTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataReactiveFilterTest.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.common.metadata.filter.web; +package com.tencent.cloud.metadata.core; import com.tencent.cloud.common.constant.MetadataConstant; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; @@ -36,24 +36,24 @@ import org.springframework.web.server.WebFilterChain; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.MOCK; /** - * Test for {@link MetadataReactiveFilter} + * Test for {@link DecodeTransferMetadataReactiveFilter}. * * @author Haotian Zhang */ @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = MOCK, - classes = MetadataServletFilterTest.TestApplication.class, - properties = { "spring.config.location = classpath:application-test.yml" }) -public class MetadataReactiveFilterTest { + classes = DecodeTransferMetadataServletFilterTest.TestApplication.class, + properties = { "spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive" }) +public class DecodeTransferMetadataReactiveFilterTest { @Autowired private MetadataLocalProperties metadataLocalProperties; - private MetadataReactiveFilter metadataReactiveFilter; + private DecodeTransferMetadataReactiveFilter metadataReactiveFilter; @Before public void setUp() { - this.metadataReactiveFilter = new MetadataReactiveFilter(); + this.metadataReactiveFilter = new DecodeTransferMetadataReactiveFilter(); } @Test diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/filter/web/MetadataServletFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java similarity index 86% rename from spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/filter/web/MetadataServletFilterTest.java rename to spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java index 7b6db59b4e6d29522c147057a8b36119946866cd..fca418b5962f4cc310839b29e19250b7607c3b34 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/filter/web/MetadataServletFilterTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/DecodeTransferMetadataServletFilterTest.java @@ -15,7 +15,7 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.common.metadata.filter.web; +package com.tencent.cloud.metadata.core; import java.io.IOException; @@ -38,21 +38,23 @@ import org.springframework.test.context.junit4.SpringRunner; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** - * Test for {@link MetadataServletFilter} + * Test for {@link DecodeTransferMetadataServletFilter}. * * @author Haotian Zhang */ @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT, - classes = MetadataServletFilterTest.TestApplication.class, - properties = { "spring.config.location = classpath:application-test.yml" }) -public class MetadataServletFilterTest { + classes = DecodeTransferMetadataServletFilterTest.TestApplication.class, + properties = { "spring.config.location = classpath:application-test.yml", + "spring.main.web-application-type = servlet", + "spring.cloud.gateway.enabled = false" }) +public class DecodeTransferMetadataServletFilterTest { @Autowired private MetadataLocalProperties metadataLocalProperties; @Autowired - private MetadataServletFilter metadataServletFilter; + private DecodeTransferMetadataServletFilter metadataServletFilter; @Test public void test1() throws ServletException, IOException { diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/Metadata2HeaderFeignInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java similarity index 69% rename from spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/Metadata2HeaderFeignInterceptorTest.java rename to spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java index 97ee8a787443bed8a10777b816c5cd7ee0d3f8d1..c42629e0cc6379714ff0c9b06893b3bfe168e4c9 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/Metadata2HeaderFeignInterceptorTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataFeignInterceptorTest.java @@ -15,16 +15,14 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.metadata.core.intercepter; +package com.tencent.cloud.metadata.core; import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; import com.tencent.cloud.common.constant.MetadataConstant; +import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; -import com.tencent.cloud.common.util.JacksonUtils; -import com.tencent.cloud.metadata.core.interceptor.Metadata2HeaderFeignInterceptor; import feign.RequestInterceptor; import feign.RequestTemplate; import org.assertj.core.api.Assertions; @@ -45,16 +43,18 @@ import org.springframework.web.bind.annotation.RestController; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; /** - * Test for {@link Metadata2HeaderFeignInterceptor} + * Test for {@link EncodeTransferMedataFeignInterceptor}. * * @author Haotian Zhang */ @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = DEFINED_PORT, - classes = Metadata2HeaderFeignInterceptorTest.TestApplication.class, - properties = { "server.port=8081", - "spring.config.location = classpath:application-test.yml" }) -public class Metadata2HeaderFeignInterceptorTest { + classes = EncodeTransferMedataFeignInterceptorTest.TestApplication.class, + properties = {"server.port=8081", + "spring.config.location = classpath:application-test.yml", + "spring.main.web-application-type = servlet", + "spring.cloud.gateway.enabled = false"}) +public class EncodeTransferMedataFeignInterceptorTest { @Autowired private MetadataLocalProperties metadataLocalProperties; @@ -63,23 +63,13 @@ public class Metadata2HeaderFeignInterceptorTest { private TestApplication.TestFeign testFeign; @Test - public void test1() { + public void testTransitiveMetadataFromApplicationConfig() { String metadata = testFeign.test(); - Assertions.assertThat(metadata) - .isEqualTo("{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}{}"); + Assertions.assertThat(metadata).isEqualTo("2"); Assertions.assertThat(metadataLocalProperties.getContent().get("a")) .isEqualTo("1"); Assertions.assertThat(metadataLocalProperties.getContent().get("b")) .isEqualTo("2"); - Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("a")) - .isEqualTo("11"); - Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("b")) - .isEqualTo("22"); - Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("c")) - .isEqualTo("33"); } @SpringBootApplication @@ -91,17 +81,13 @@ public class Metadata2HeaderFeignInterceptorTest { public String test( @RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String customMetadataStr) throws UnsupportedEncodingException { - String systemMetadataStr = JacksonUtils - .serialize2Json(MetadataContextHolder.get().getAllSystemMetadata()); - return URLDecoder.decode(customMetadataStr, "UTF-8") + systemMetadataStr; + return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"); } @FeignClient(name = "test-feign", url = "http://localhost:8081") public interface TestFeign { - @RequestMapping(value = "/test", - headers = { MetadataConstant.HeaderName.CUSTOM_METADATA - + "={\"a\":\"11" + "\",\"b\":\"22\",\"c\":\"33\"}" }) + @RequestMapping("/test") String test(); } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/Metadata2HeaderRestTemplateInterceptorTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java similarity index 64% rename from spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/Metadata2HeaderRestTemplateInterceptorTest.java rename to spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java index 353f57918b9e6467a452b832b259405332b082f3..b61c42ed21227cd964b3de1fb6bdf969caaa7d1a 100644 --- a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/intercepter/Metadata2HeaderRestTemplateInterceptorTest.java +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataRestTemplateInterceptorTest.java @@ -15,16 +15,13 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.metadata.core.intercepter; +package com.tencent.cloud.metadata.core; import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; import com.tencent.cloud.common.constant.MetadataConstant; +import com.tencent.cloud.common.metadata.MetadataContext; import com.tencent.cloud.common.metadata.MetadataContextHolder; -import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; -import com.tencent.cloud.common.util.JacksonUtils; -import com.tencent.cloud.metadata.core.interceptor.Metadata2HeaderRestTemplateInterceptor; import org.assertj.core.api.Assertions; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,18 +43,15 @@ import org.springframework.web.client.RestTemplate; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** - * Test for {@link Metadata2HeaderRestTemplateInterceptor} + * Test for {@link EncodeTransferMedataRestTemplateInterceptor}. * * @author Haotian Zhang */ @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = RANDOM_PORT, - classes = Metadata2HeaderRestTemplateInterceptorTest.TestApplication.class, - properties = { "spring.config.location = classpath:application-test.yml" }) -public class Metadata2HeaderRestTemplateInterceptorTest { - - @Autowired - private MetadataLocalProperties metadataLocalProperties; + classes = EncodeTransferMedataRestTemplateInterceptorTest.TestApplication.class, + properties = { "spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive" }) +public class EncodeTransferMedataRestTemplateInterceptorTest { @Autowired private RestTemplate restTemplate; @@ -66,30 +60,14 @@ public class Metadata2HeaderRestTemplateInterceptorTest { private int localServerPort; @Test - public void test1() { + public void testTransitiveMetadataFromApplicationConfig() { HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.set(MetadataConstant.HeaderName.CUSTOM_METADATA, - "{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}"); HttpEntity httpEntity = new HttpEntity<>(httpHeaders); String metadata = restTemplate .exchange("http://localhost:" + localServerPort + "/test", HttpMethod.GET, httpEntity, String.class) .getBody(); - Assertions.assertThat(metadata) - .isEqualTo("{\"a\":\"11\",\"b\":\"22\",\"c\":\"33\"}{}"); - Assertions.assertThat(metadataLocalProperties.getContent().get("a")) - .isEqualTo("1"); - Assertions.assertThat(metadataLocalProperties.getContent().get("b")) - .isEqualTo("2"); - Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("a")) - .isEqualTo("11"); - Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("b")) - .isEqualTo("22"); - Assertions - .assertThat(MetadataContextHolder.get().getTransitiveCustomMetadata("c")) - .isEqualTo("33"); + Assertions.assertThat(metadata).isEqualTo("2"); } @SpringBootApplication @@ -105,9 +83,7 @@ public class Metadata2HeaderRestTemplateInterceptorTest { public String test( @RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String customMetadataStr) throws UnsupportedEncodingException { - String systemMetadataStr = JacksonUtils - .serialize2Json(MetadataContextHolder.get().getAllSystemMetadata()); - return URLDecoder.decode(customMetadataStr, "UTF-8") + systemMetadataStr; + return MetadataContextHolder.get().getContext(MetadataContext.FRAGMENT_TRANSITIVE, "b"); } } diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f6cc2fdcd68866b506cd2f72af95b8126686f36a --- /dev/null +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMedataScgFilterTest.java @@ -0,0 +1,83 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.metadata.core; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import com.tencent.cloud.common.constant.MetadataConstant; +import com.tencent.cloud.common.util.JacksonUtils; +import org.assertj.core.api.Assertions; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.context.ApplicationContext; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.server.MockServerWebExchange; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * Test for {@link EncodeTransferMedataScgFilter}. + * + * @author quan + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT, + classes = EncodeTransferMedataScgFilterTest.TestApplication.class, + properties = {"spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive"}) +public class EncodeTransferMedataScgFilterTest { + + @Autowired + private ApplicationContext applicationContext; + + @Mock + private GatewayFilterChain chain; + + @Test + public void testTransitiveMetadataFromApplicationConfig() throws UnsupportedEncodingException { + EncodeTransferMedataScgFilter filter = applicationContext.getBean(EncodeTransferMedataScgFilter.class); + + // Mock Server Http Request + MockServerHttpRequest.BaseBuilder builder = MockServerHttpRequest.get(""); + MockServerWebExchange exchange = MockServerWebExchange.from(builder); + filter.filter(exchange, chain); + + // Check metadata str + String metadata = exchange.getRequest().getHeaders().getFirst(MetadataConstant.HeaderName.CUSTOM_METADATA); + Assertions.assertThat(metadata).isNotNull(); + + String decode = URLDecoder.decode(metadata, StandardCharsets.UTF_8.name()); + Map transitiveMap = JacksonUtils.deserialize2Map(decode); + Assertions.assertThat(transitiveMap.size()).isEqualTo(1); + Assertions.assertThat(transitiveMap.get("b")).isEqualTo("2"); + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMetadataZuulFilterTest.java b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMetadataZuulFilterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..adc3e1f7e6449fc24bba3dd370b4706214245309 --- /dev/null +++ b/spring-cloud-starter-tencent-metadata-transfer/src/test/java/com/tencent/cloud/metadata/core/EncodeTransferMetadataZuulFilterTest.java @@ -0,0 +1,86 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.metadata.core; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +import com.netflix.zuul.context.RequestContext; +import com.tencent.cloud.common.constant.MetadataConstant; +import com.tencent.cloud.common.util.JacksonUtils; +import org.assertj.core.api.Assertions; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.mock.web.MockMultipartHttpServletRequest; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * Test for {@link EncodeTransferMetadataZuulFilter}. + * + * @author quan + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT, + classes = EncodeTransferMetadataZuulFilterTest.TestApplication.class, + properties = {"spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive"}) + +public class EncodeTransferMetadataZuulFilterTest { + + @Autowired + private ApplicationContext applicationContext; + + private final MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(); + + @Before + public void init() { + RequestContext ctx = RequestContext.getCurrentContext(); + ctx.clear(); + ctx.setRequest(this.request); + } + + @Test + public void multiplePartNamesWithMultipleParts() throws UnsupportedEncodingException { + EncodeTransferMetadataZuulFilter filter = applicationContext.getBean(EncodeTransferMetadataZuulFilter.class); + filter.run(); + final RequestContext ctx = RequestContext.getCurrentContext(); + Map zuulRequestHeaders = ctx.getZuulRequestHeaders(); + String metadata = zuulRequestHeaders.get(MetadataConstant.HeaderName.CUSTOM_METADATA.toLowerCase()); + Assertions.assertThat(metadata).isNotNull(); + + String decode = URLDecoder.decode(metadata, StandardCharsets.UTF_8.name()); + Map transitiveMap = JacksonUtils.deserialize2Map(decode); + Assertions.assertThat(transitiveMap.size()).isEqualTo(1); + Assertions.assertThat(transitiveMap.get("b")).isEqualTo("2"); + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml b/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml index 14b8257130034346439294d5c35efb2da03b68ab..80bb8a22a02a5cf9f1e5d0d50dcdcbae35f07b8a 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/pom.xml @@ -16,7 +16,7 @@ com.tencent.cloud - spring-cloud-tencent-polaris-context + spring-cloud-tencent-polaris-loadbalancer @@ -24,11 +24,63 @@ com.tencent.polaris polaris-discovery-factory + + + com.tencent.polaris + router-rule + + + com.tencent.polaris + router-nearby + + + com.tencent.polaris + router-metadata + + + com.tencent.polaris + router-canary + + + com.tencent.polaris + router-set + + com.tencent.polaris polaris-circuitbreaker-factory + + + com.tencent.polaris + router-rule + + + com.tencent.polaris + router-nearby + + + com.tencent.polaris + router-metadata + + + com.tencent.polaris + router-canary + + + com.tencent.polaris + router-set + + + com.tencent.polaris + router-isolated + + + com.tencent.polaris + router-healthy + + @@ -50,10 +102,22 @@ test + + org.springframework.boot + spring-boot-starter-web + test + + org.springframework.boot spring-boot-starter-test test + + + org.mockito + mockito-inline + test + diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfiguration.java similarity index 71% rename from spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfiguration.java rename to spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfiguration.java index 3718ba4bf4656094d6fabaa188bf23f347649025..393c0ea7dea719263ab545bee3667d4ea1e12221 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisCircuitBreakerBootstrapConfiguration.java @@ -15,11 +15,14 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.circuitbreaker; +package com.tencent.cloud.polaris.circuitbreaker.config; import com.tencent.cloud.common.constant.ContextConstant; +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.plugins.router.healthy.RecoverRouterConfig; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; @@ -30,6 +33,7 @@ import org.springframework.context.annotation.Configuration; * * @author lepdou 2022-03-29 */ +@ConditionalOnPolarisEnabled @ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true) @Configuration(proxyBeanMethods = false) @@ -46,6 +50,16 @@ public class PolarisCircuitBreakerBootstrapConfiguration { public void modify(ConfigurationImpl configuration) { // Turn on circuitbreaker configuration configuration.getConsumer().getCircuitBreaker().setEnable(true); + + // Set excludeCircuitBreakInstances to false + RecoverRouterConfig recoverRouterConfig = configuration.getConsumer().getServiceRouter() + .getPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, RecoverRouterConfig.class); + + recoverRouterConfig.setExcludeCircuitBreakInstances(true); + + // Update modified config to source properties + configuration.getConsumer().getServiceRouter() + .setPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, recoverRouterConfig); } @Override diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisFeignClientAutoConfiguration.java similarity index 82% rename from spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfiguration.java rename to spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisFeignClientAutoConfiguration.java index b03d1491058c27f90971121424079487454ada9a..a1f50a3df988cdeadf4cb1fed95d342905038454 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisFeignClientAutoConfiguration.java @@ -15,16 +15,17 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.circuitbreaker; +package com.tencent.cloud.polaris.circuitbreaker.config; import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignBeanPostProcessor; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.factory.api.DiscoveryAPIFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.openfeign.FeignAutoConfiguration; import org.springframework.context.annotation.Bean; @@ -39,10 +40,10 @@ import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE; * * @author Haotian Zhang */ -@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", - havingValue = "true", matchIfMissing = true) +@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", havingValue = "true", matchIfMissing = true) @Configuration(proxyBeanMethods = false) -@AutoConfigureAfter(PolarisContextConfiguration.class) +@ConditionalOnClass(name = "org.springframework.cloud.openfeign.FeignAutoConfiguration") +@AutoConfigureAfter(PolarisContextAutoConfiguration.class) @AutoConfigureBefore(FeignAutoConfiguration.class) public class PolarisFeignClientAutoConfiguration { @@ -53,8 +54,7 @@ public class PolarisFeignClientAutoConfiguration { @Bean @Order(HIGHEST_PRECEDENCE) - public PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor( - ConsumerAPI consumerAPI) { + public PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor(ConsumerAPI consumerAPI) { return new PolarisFeignBeanPostProcessor(consumerAPI); } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisRestTemplateAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisRestTemplateAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..a81f1344b4e695d2d1c0ead2c3029166fbde02e6 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/config/PolarisRestTemplateAutoConfiguration.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker.config; + +import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisResponseErrorHandler; +import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateModifier; +import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateResponseErrorHandler; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.polaris.api.core.ConsumerAPI; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +/** + * Auto configuration PolarisRestTemplateAutoConfiguration . + * + * @author wh 2022/6/21 + */ +@ConditionalOnProperty(value = "spring.cloud.polaris.circuitbreaker.enabled", + havingValue = "true", matchIfMissing = true) +@Configuration(proxyBeanMethods = false) +@AutoConfigureAfter(PolarisContextAutoConfiguration.class) +public class PolarisRestTemplateAutoConfiguration { + + @Bean + @ConditionalOnBean(RestTemplate.class) + public PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler( + ConsumerAPI consumerAPI, @Autowired(required = false) PolarisResponseErrorHandler polarisResponseErrorHandler) { + return new PolarisRestTemplateResponseErrorHandler(consumerAPI, polarisResponseErrorHandler); + } + + @Bean + @ConditionalOnBean(RestTemplate.class) + public PolarisRestTemplateModifier polarisRestTemplateBeanPostProcessor( + PolarisRestTemplateResponseErrorHandler restTemplateResponseErrorHandler) { + return new PolarisRestTemplateModifier(restTemplateResponseErrorHandler); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBeanPostProcessor.java index da6726481841fa3f0aea27c4aaf3b3b77d72aa7c..6198157957076ad406ab3fa97ed0613e93e9c331 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBeanPostProcessor.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBeanPostProcessor.java @@ -35,8 +35,7 @@ import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; * * @author Haotian Zhang */ -public class PolarisFeignBeanPostProcessor - implements BeanPostProcessor, BeanFactoryAware { +public class PolarisFeignBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { private final ConsumerAPI consumerAPI; @@ -47,8 +46,7 @@ public class PolarisFeignBeanPostProcessor } @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) - throws BeansException { + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return wrapper(bean); } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBlockingLoadBalancerClient.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBlockingLoadBalancerClient.java index 9adaa6bfe8cd11ad3488b3e4b7c626274ac31b37..4cf8780c10c157c6f1762087607f0841ce94efeb 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBlockingLoadBalancerClient.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBlockingLoadBalancerClient.java @@ -27,11 +27,9 @@ import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalance * * @author Haotian Zhang */ -public class PolarisFeignBlockingLoadBalancerClient - extends FeignBlockingLoadBalancerClient { +public class PolarisFeignBlockingLoadBalancerClient extends FeignBlockingLoadBalancerClient { - public PolarisFeignBlockingLoadBalancerClient(Client delegate, - BlockingLoadBalancerClient loadBalancerClient) { + public PolarisFeignBlockingLoadBalancerClient(Client delegate, BlockingLoadBalancerClient loadBalancerClient) { super(delegate, loadBalancerClient); } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClient.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClient.java index 0170fc34440bc7c9d159975564f6dfdb9953708f..dc49566f272be5940d3915a17911bdc9ec1e0b92 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClient.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClient.java @@ -13,6 +13,7 @@ * 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.tencent.cloud.polaris.circuitbreaker.feign; @@ -20,9 +21,7 @@ package com.tencent.cloud.polaris.circuitbreaker.feign; import java.io.IOException; import java.net.URI; -import com.tencent.cloud.common.constant.MetadataConstant.SystemMetadataKey; import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.polaris.api.pojo.RetStatus; import com.tencent.polaris.api.pojo.ServiceKey; @@ -32,6 +31,8 @@ import feign.Request; import feign.Request.Options; import feign.Response; import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import static feign.Util.checkNotNull; @@ -42,6 +43,9 @@ import static feign.Util.checkNotNull; */ public class PolarisFeignClient implements Client { + + private static final Logger LOG = LoggerFactory.getLogger(PolarisFeignClient.class); + private final Client delegate; private final ConsumerAPI consumerAPI; @@ -56,14 +60,17 @@ public class PolarisFeignClient implements Client { final ServiceCallResult resultRequest = createServiceCallResult(request); try { Response response = delegate.execute(request, options); - // HTTP code greater than 400 is an exception - if (response.status() >= 400) { + // HTTP code greater than 500 is an exception + if (response.status() >= 500) { resultRequest.setRetStatus(RetStatus.RetFail); } + LOG.debug("Will report result of {}. Request=[{}]. Response=[{}].", + resultRequest.getRetStatus().name(), request, response); return response; } catch (IOException origin) { resultRequest.setRetStatus(RetStatus.RetFail); + LOG.debug("Will report result of {}. Request=[{}].", resultRequest.getRetStatus().name(), request, origin); throw origin; } finally { @@ -74,24 +81,17 @@ public class PolarisFeignClient implements Client { private ServiceCallResult createServiceCallResult(final Request request) { ServiceCallResult resultRequest = new ServiceCallResult(); - MetadataContext metadataContext = MetadataContextHolder.get(); - String namespace = metadataContext - .getSystemMetadata(SystemMetadataKey.PEER_NAMESPACE); - resultRequest.setNamespace(namespace); - String serviceName = metadataContext - .getSystemMetadata(SystemMetadataKey.PEER_SERVICE); + resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE); + String serviceName = request.requestTemplate().feignTarget().name(); resultRequest.setService(serviceName); - String method = metadataContext.getSystemMetadata(SystemMetadataKey.PEER_PATH); - resultRequest.setMethod(method); + URI uri = URI.create(request.url()); + resultRequest.setMethod(uri.getPath()); resultRequest.setRetStatus(RetStatus.RetSuccess); String sourceNamespace = MetadataContext.LOCAL_NAMESPACE; String sourceService = MetadataContext.LOCAL_SERVICE; - if (StringUtils.isNotBlank(sourceNamespace) - && StringUtils.isNotBlank(sourceService)) { - resultRequest - .setCallerService(new ServiceKey(sourceNamespace, sourceService)); + if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) { + resultRequest.setCallerService(new ServiceKey(sourceNamespace, sourceService)); } - URI uri = URI.create(request.url()); resultRequest.setHost(uri.getHost()); resultRequest.setPort(uri.getPort()); diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisResponseErrorHandler.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisResponseErrorHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..3c690b1ccb59a7e735f2462be509de18ad6b0ec7 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisResponseErrorHandler.java @@ -0,0 +1,29 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker.resttemplate; + +import org.springframework.web.client.ResponseErrorHandler; + +/** + * Polaris Response Error Handler Definition Of {@link ResponseErrorHandler}. + * + * @author wh 2022/6/21 + */ +public interface PolarisResponseErrorHandler extends ResponseErrorHandler { + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateModifier.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateModifier.java new file mode 100644 index 0000000000000000000000000000000000000000..aa6eb3cbcd0411ff31087e88dbeba2d92b518369 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateModifier.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker.resttemplate; + +import java.util.Map; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.util.ObjectUtils; +import org.springframework.web.client.RestTemplate; + +/** + * Auto configuration RestTemplate, Find the RestTemplate bean annotated with {@link LoadBalanced}, + * then replace {@link org.springframework.web.client.ResponseErrorHandler} with {@link PolarisRestTemplateResponseErrorHandler} . + * + * @author wh 2022/6/21 + */ +public class PolarisRestTemplateModifier implements ApplicationContextAware, SmartInitializingSingleton { + + private ApplicationContext applicationContext; + + private final PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler; + + public PolarisRestTemplateModifier(PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler) { + this.polarisRestTemplateResponseErrorHandler = polarisRestTemplateResponseErrorHandler; + } + + @Override + public void afterSingletonsInstantiated() { + Map beans = this.applicationContext.getBeansWithAnnotation(LoadBalanced.class); + if (!ObjectUtils.isEmpty(beans)) { + beans.forEach(this::initRestTemplate); + } + } + + private void initRestTemplate(String beanName, Object bean) { + if (bean instanceof RestTemplate) { + RestTemplate restTemplate = (RestTemplate) bean; + restTemplate.setErrorHandler(polarisRestTemplateResponseErrorHandler); + } + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateResponseErrorHandler.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateResponseErrorHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..8a7dc16abb10074675e7f57cf9665c9483dc9785 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateResponseErrorHandler.java @@ -0,0 +1,115 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker.resttemplate; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.util.Objects; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.util.ReflectionUtils; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.pojo.RetStatus; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.ServiceCallResult; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.http.HttpMethod; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.lang.NonNull; +import org.springframework.web.client.ResponseErrorHandler; + +/** + * Extend ResponseErrorHandler to get request information. + * + * @author wh 2022/6/21 + */ +public class PolarisRestTemplateResponseErrorHandler implements ResponseErrorHandler { + + private static final Logger LOG = LoggerFactory.getLogger(PolarisRestTemplateResponseErrorHandler.class); + + private static final String FIELD_NAME = "connection"; + + private final ConsumerAPI consumerAPI; + + private final PolarisResponseErrorHandler polarisResponseErrorHandler; + + + public PolarisRestTemplateResponseErrorHandler(ConsumerAPI consumerAPI, PolarisResponseErrorHandler polarisResponseErrorHandler) { + this.consumerAPI = consumerAPI; + this.polarisResponseErrorHandler = polarisResponseErrorHandler; + } + + @Override + public boolean hasError(@NonNull ClientHttpResponse response) { + return true; + } + + @Override + public void handleError(@NonNull ClientHttpResponse response) throws IOException { + if (Objects.nonNull(polarisResponseErrorHandler)) { + if (polarisResponseErrorHandler.hasError(response)) { + polarisResponseErrorHandler.handleError(response); + } + } + } + + @Override + public void handleError(@NonNull URI url, @NonNull HttpMethod method, @NonNull ClientHttpResponse response) throws IOException { + ServiceCallResult resultRequest = createServiceCallResult(url); + try { + HttpURLConnection connection = (HttpURLConnection) ReflectionUtils.getFieldValue(response, FIELD_NAME); + if (connection != null) { + URL realURL = connection.getURL(); + resultRequest.setHost(realURL.getHost()); + resultRequest.setPort(realURL.getPort()); + } + + if (response.getStatusCode().value() > 500) { + resultRequest.setRetStatus(RetStatus.RetFail); + } + } + catch (Exception e) { + LOG.error("Will report response of {} url {}", response, url, e); + throw e; + } + finally { + consumerAPI.updateServiceCallResult(resultRequest); + } + } + + private ServiceCallResult createServiceCallResult(URI uri) { + ServiceCallResult resultRequest = new ServiceCallResult(); + String serviceName = uri.getHost(); + resultRequest.setService(serviceName); + resultRequest.setNamespace(MetadataContext.LOCAL_NAMESPACE); + resultRequest.setMethod(uri.getPath()); + resultRequest.setRetStatus(RetStatus.RetSuccess); + String sourceNamespace = MetadataContext.LOCAL_NAMESPACE; + String sourceService = MetadataContext.LOCAL_SERVICE; + if (StringUtils.isNotBlank(sourceNamespace) && StringUtils.isNotBlank(sourceService)) { + resultRequest.setCallerService(new ServiceKey(sourceNamespace, sourceService)); + } + return resultRequest; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring-configuration-metadata.json b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/additional-spring-configuration-metadata.json similarity index 63% rename from spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring-configuration-metadata.json rename to spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 195c3cb2ac74b72125d45b425f0a449a7f42f667..99dc05518a7902b9b20916b78283fbdc7222d1e4 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring-configuration-metadata.json +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -3,7 +3,7 @@ { "name": "spring.cloud.polaris.circuitbreaker.enabled", "type": "java.lang.Boolean", - "sourceType": "com.tencent.cloud.polaris.circuitbreaker.PolarisFeignProperties" + "defaultValue": "true" } ], "hints": [] diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories index 04fa47a13d4fdfdaa08fac786e17203138e5dd31..229cc2af0acf1d303054263673243387539314f9 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/main/resources/META-INF/spring.factories @@ -1,4 +1,6 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ - com.tencent.cloud.polaris.circuitbreaker.PolarisCircuitBreakerBootstrapConfiguration + com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.tencent.cloud.polaris.circuitbreaker.PolarisFeignClientAutoConfiguration + com.tencent.cloud.polaris.circuitbreaker.config.PolarisFeignClientAutoConfiguration,\ + com.tencent.cloud.polaris.circuitbreaker.config.PolarisRestTemplateAutoConfiguration + diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfigurationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a5bd4f2e66cb07bd02597c5173e2033b4102833c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisCircuitBreakerBootstrapConfigurationTest.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker; + +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisCircuitBreakerBootstrapConfiguration; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisCircuitBreakerBootstrapConfiguration}. + * + * @author Haotian Zhang + */ +public class PolarisCircuitBreakerBootstrapConfigurationTest { + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(PolarisCircuitBreakerBootstrapConfiguration.class)) + .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); + + @Test + public void testDefaultInitialization() { + this.contextRunner.run(context -> { + assertThat(context).hasSingleBean(PolarisCircuitBreakerBootstrapConfiguration.CircuitBreakerConfigModifier.class); + }); + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..682b9952254b410f620e86bdf3198ad08b34703f --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisFeignClientAutoConfigurationTest.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker; + +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisFeignClientAutoConfiguration; +import com.tencent.cloud.polaris.circuitbreaker.feign.PolarisFeignBeanPostProcessor; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.polaris.api.core.ConsumerAPI; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisFeignClientAutoConfiguration}. + * + * @author Haotian Zhang + */ +public class PolarisFeignClientAutoConfigurationTest { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of( + PolarisContextAutoConfiguration.class, + PolarisFeignClientAutoConfiguration.class)) + .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); + + @Test + public void testDefaultInitialization() { + this.contextRunner.run(context -> { + assertThat(context).hasSingleBean(ConsumerAPI.class); + assertThat(context).hasSingleBean(PolarisFeignBeanPostProcessor.class); + }); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisRestTemplateAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisRestTemplateAutoConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a3dc9999cb6e60f20703382fc2838fdb7363dd3c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/PolarisRestTemplateAutoConfigurationTest.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker; + +import com.tencent.cloud.polaris.circuitbreaker.config.PolarisRestTemplateAutoConfiguration; +import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateModifier; +import com.tencent.cloud.polaris.circuitbreaker.resttemplate.PolarisRestTemplateResponseErrorHandler; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test For {@link PolarisRestTemplateAutoConfiguration} . + * + * @author Palmer Xu 2022-06-28 + */ +public class PolarisRestTemplateAutoConfigurationTest { + + private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of( + PolarisRestTemplateAutoConfigurationTester.class, + PolarisContextAutoConfiguration.class, + PolarisRestTemplateAutoConfiguration.class)) + .withPropertyValues("spring.cloud.polaris.circuitbreaker.enabled=true"); + + @Test + public void testInitialization() { + this.contextRunner + .run(context -> { + assertThat(context).hasSingleBean(PolarisRestTemplateModifier.class); + assertThat(context).hasSingleBean(PolarisRestTemplateResponseErrorHandler.class); + }); + } + + @Configuration + @EnableAutoConfiguration + @AutoConfigureBefore(PolarisRestTemplateAutoConfiguration.class) + static class PolarisRestTemplateAutoConfigurationTester { + + @Bean + RestTemplate restTemplate() { + return new RestTemplate(); + } + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBeanPostProcessorTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBeanPostProcessorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..252dcc02142dc6e7604c02c4f3bc30e9da0ca147 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBeanPostProcessorTest.java @@ -0,0 +1,97 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker.feign; + +import com.tencent.polaris.api.core.ConsumerAPI; +import feign.Client; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient; +import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; +import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link PolarisFeignBeanPostProcessor}. + * + * @author Haotian Zhang + */ +public class PolarisFeignBeanPostProcessorTest { + + private PolarisFeignBeanPostProcessor polarisFeignBeanPostProcessor; + + @Before + public void setUp() { + ConsumerAPI consumerAPI = mock(ConsumerAPI.class); + + polarisFeignBeanPostProcessor = new PolarisFeignBeanPostProcessor(consumerAPI); + } + + @Test + public void testPostProcessBeforeInitialization() { + BeanFactory beanFactory = mock(BeanFactory.class); + doAnswer(invocation -> { + Class clazz = invocation.getArgument(0); + if (clazz.equals(BlockingLoadBalancerClient.class)) { + return mock(BlockingLoadBalancerClient.class); + } + if (clazz.equals(CachingSpringLoadBalancerFactory.class)) { + return mock(CachingSpringLoadBalancerFactory.class); + } + if (clazz.equals(SpringClientFactory.class)) { + return mock(SpringClientFactory.class); + } + return null; + }).when(beanFactory).getBean(any(Class.class)); + polarisFeignBeanPostProcessor.setBeanFactory(beanFactory); + + // isNeedWrap(bean) == false + Object bean1 = new Object(); + Object bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean1, "bean1"); + assertThat(bean).isNotInstanceOfAny( + PolarisFeignClient.class, + PolarisLoadBalancerFeignClient.class, + PolarisFeignBlockingLoadBalancerClient.class); + + // bean instanceOf Client.class + Client bean2 = mock(Client.class); + bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean2, "bean2"); + assertThat(bean).isInstanceOf(PolarisFeignClient.class); + + // bean instanceOf LoadBalancerFeignClient.class + LoadBalancerFeignClient bean3 = mock(LoadBalancerFeignClient.class); + doReturn(mock(Client.class)).when(bean3).getDelegate(); + bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean3, "bean3"); + assertThat(bean).isInstanceOf(PolarisLoadBalancerFeignClient.class); + + // bean instanceOf FeignBlockingLoadBalancerClient.class + FeignBlockingLoadBalancerClient bean4 = mock(FeignBlockingLoadBalancerClient.class); + doReturn(mock(Client.class)).when(bean4).getDelegate(); + bean = polarisFeignBeanPostProcessor.postProcessBeforeInitialization(bean4, "bean4"); + assertThat(bean).isInstanceOf(PolarisFeignBlockingLoadBalancerClient.class); + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBlockingLoadBalancerClientTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBlockingLoadBalancerClientTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5be8111bce260d9fd367d616a4477ba6abc9ff68 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignBlockingLoadBalancerClientTest.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker.feign; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +/** + * Test for {@link PolarisFeignBlockingLoadBalancerClient}. + * + * @author Haotian Zhang + */ +public class PolarisFeignBlockingLoadBalancerClientTest { + + @Test + public void testConstructor() { + try { + new PolarisFeignBlockingLoadBalancerClient(null, null); + } + catch (Exception e) { + Assertions.fail("Exception encountered.", e); + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClientTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClientTest.java index 757e12001188a9327e9e7fde080c26bc2276d0ae..993eedc58e51f3e1986949d3c891d55065413bb0 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClientTest.java +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisFeignClientTest.java @@ -17,52 +17,121 @@ package com.tencent.cloud.polaris.circuitbreaker.feign; -import com.tencent.cloud.polaris.circuitbreaker.PolarisFeignClientAutoConfiguration; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; +import java.io.IOException; + +import com.google.common.collect.Maps; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.rpc.ServiceCallResult; import feign.Client; +import feign.Request; +import feign.RequestTemplate; +import feign.Response; +import feign.Target; import org.junit.Test; -import org.junit.jupiter.api.Assertions; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.ApplicationContext; -import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringRunner; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; + /** * Test for {@link PolarisFeignClient}. * - * @author liaochuntao + * @author Haotian Zhang */ @RunWith(SpringRunner.class) -@SpringBootTest(classes = TestPolarisFeignApp.class) -@ContextConfiguration(classes = { PolarisFeignClientAutoConfiguration.class, PolarisContextConfiguration.class }) +@SpringBootTest(classes = PolarisFeignClientTest.TestApplication.class, + properties = {"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"}) public class PolarisFeignClientTest { - @Autowired - private ApplicationContext springCtx; - @Test - public void testPolarisFeignBeanPostProcessor() { - final PolarisFeignBeanPostProcessor postProcessor = springCtx - .getBean(PolarisFeignBeanPostProcessor.class); - Assertions.assertNotNull(postProcessor, "PolarisFeignBeanPostProcessor"); + public void testConstructor() { + try { + new PolarisFeignClient(null, null); + fail("NullPointerException should be thrown."); + } + catch (Throwable e) { + assertThat(e).isInstanceOf(NullPointerException.class); + assertThat(e.getMessage()).isEqualTo("target"); + } + + try { + new PolarisFeignClient(mock(Client.class), null); + fail("NullPointerException should be thrown."); + } + catch (Throwable e) { + assertThat(e).isInstanceOf(NullPointerException.class); + assertThat(e.getMessage()).isEqualTo("CircuitBreakAPI"); + } + + try { + assertThat(new PolarisFeignClient(mock(Client.class), mock(ConsumerAPI.class))).isInstanceOf(PolarisFeignClient.class); + } + catch (Throwable e) { + fail("Exception encountered.", e); + } } @Test - public void testFeignClient() { - final Client client = springCtx.getBean(Client.class); - if (client instanceof PolarisFeignClient) { - return; - } - if (client instanceof PolarisLoadBalancerFeignClient) { - return; + public void testExecute() throws IOException { + // mock Client.class + Client delegate = mock(Client.class); + doAnswer(invocation -> { + Request request = invocation.getArgument(0); + if (request.httpMethod().equals(Request.HttpMethod.GET)) { + return Response.builder().request(request).status(200).build(); + } + else if (request.httpMethod().equals(Request.HttpMethod.POST)) { + return Response.builder().request(request).status(500).build(); + } + throw new IOException("Mock exception."); + }).when(delegate).execute(any(Request.class), nullable(Request.Options.class)); + + // mock ConsumerAPI.class + ConsumerAPI consumerAPI = mock(ConsumerAPI.class); + doNothing().when(consumerAPI).updateServiceCallResult(any(ServiceCallResult.class)); + + // mock target + Target target = Target.EmptyTarget.create(Object.class); + + // mock RequestTemplate.class + RequestTemplate requestTemplate = new RequestTemplate(); + requestTemplate.feignTarget(target); + + PolarisFeignClient polarisFeignClient = new PolarisFeignClient(delegate, consumerAPI); + + // 200 + Response response = polarisFeignClient.execute(Request.create(Request.HttpMethod.GET, "http://localhost:8080/test", + Maps.newHashMap(), null, requestTemplate), null); + assertThat(response.status()).isEqualTo(200); + + // 200 + response = polarisFeignClient.execute(Request.create(Request.HttpMethod.POST, "http://localhost:8080/test", + Maps.newHashMap(), null, requestTemplate), null); + assertThat(response.status()).isEqualTo(500); + + // Exception + try { + polarisFeignClient.execute(Request.create(Request.HttpMethod.DELETE, "http://localhost:8080/test", + Maps.newHashMap(), null, requestTemplate), null); + fail("IOException should be thrown."); } - if (client instanceof PolarisFeignBlockingLoadBalancerClient) { - return; + catch (Throwable t) { + assertThat(t).isInstanceOf(IOException.class); + assertThat(t.getMessage()).isEqualTo("Mock exception."); } - throw new IllegalStateException("Polaris burying failed"); } + @SpringBootApplication + protected static class TestApplication { + + } } diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisLoadBalancerFeignClientTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisLoadBalancerFeignClientTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f0a726845ce48bdb4ba1646fd50f1177558259a6 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/PolarisLoadBalancerFeignClientTest.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker.feign; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +/** + * Test for {@link PolarisLoadBalancerFeignClient}. + * + * @author Haotian Zhang + */ +public class PolarisLoadBalancerFeignClientTest { + + @Test + public void testConstructor() { + try { + new PolarisLoadBalancerFeignClient(null, null, null); + } + catch (Exception e) { + Assertions.fail("Exception encountered.", e); + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateResponseErrorHandlerTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateResponseErrorHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6cb630e372d8ed2f506b6cd4793fc283bff81388 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/PolarisRestTemplateResponseErrorHandlerTest.java @@ -0,0 +1,69 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker.resttemplate; + + +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; + +import com.tencent.polaris.api.core.ConsumerAPI; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpMethod; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Test For {@link PolarisRestTemplateResponseErrorHandler}. + * + * @author wh 2022/6/22 + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PolarisRestTemplateResponseErrorHandlerTest.TestApplication.class, + properties = {"spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp"}) +public class PolarisRestTemplateResponseErrorHandlerTest { + + @Test + public void handleError() throws Exception { + ConsumerAPI consumerAPI = mock(ConsumerAPI.class); + PolarisRestTemplateResponseErrorHandler polarisRestTemplateResponseErrorHandler = new PolarisRestTemplateResponseErrorHandler(consumerAPI, null); + URI uri = mock(URI.class); + when(uri.getPath()).thenReturn("/test"); + when(uri.getHost()).thenReturn("host"); + HttpURLConnection httpURLConnection = mock(HttpURLConnection.class); + URL url = mock(URL.class); + when(httpURLConnection.getURL()).thenReturn(url); + when(url.getHost()).thenReturn("127.0.0.1"); + when(url.getPort()).thenReturn(8080); + when(httpURLConnection.getResponseCode()).thenReturn(200); + SimpleClientHttpResponseTest clientHttpResponse = new SimpleClientHttpResponseTest(httpURLConnection); + polarisRestTemplateResponseErrorHandler.handleError(uri, HttpMethod.GET, clientHttpResponse); + when(consumerAPI.unWatchService(null)).thenReturn(true); + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/SimpleClientHttpResponseTest.java b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/SimpleClientHttpResponseTest.java new file mode 100644 index 0000000000000000000000000000000000000000..871f39caa737856e84c263ab64b9578ccef88261 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/resttemplate/SimpleClientHttpResponseTest.java @@ -0,0 +1,106 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.circuitbreaker.resttemplate; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; + +import org.springframework.http.HttpHeaders; +import org.springframework.http.client.AbstractClientHttpResponse; +import org.springframework.lang.Nullable; +import org.springframework.util.StreamUtils; +import org.springframework.util.StringUtils; + + +/** + * Mock Test for {@link AbstractClientHttpResponse}. + * + * @author wh 2022/6/22 + */ +public class SimpleClientHttpResponseTest extends AbstractClientHttpResponse { + + private final HttpURLConnection connection; + + @Nullable + private HttpHeaders headers; + + @Nullable + private InputStream responseStream; + + + SimpleClientHttpResponseTest(HttpURLConnection connection) { + this.connection = connection; + } + + + @Override + public int getRawStatusCode() throws IOException { + return this.connection.getResponseCode(); + } + + @Override + public String getStatusText() throws IOException { + String result = this.connection.getResponseMessage(); + return (result != null) ? result : ""; + } + + @Override + public HttpHeaders getHeaders() { + if (this.headers == null) { + this.headers = new HttpHeaders(); + // Header field 0 is the status line for most HttpURLConnections, but not on GAE + String name = this.connection.getHeaderFieldKey(0); + if (StringUtils.hasLength(name)) { + this.headers.add(name, this.connection.getHeaderField(0)); + } + int i = 1; + while (true) { + name = this.connection.getHeaderFieldKey(i); + if (!StringUtils.hasLength(name)) { + break; + } + this.headers.add(name, this.connection.getHeaderField(i)); + i++; + } + } + return this.headers; + } + + @Override + public InputStream getBody() throws IOException { + InputStream errorStream = this.connection.getErrorStream(); + this.responseStream = (errorStream != null ? errorStream : this.connection.getInputStream()); + return this.responseStream; + } + + @Override + public void close() { + try { + if (this.responseStream == null) { + getBody(); + } + StreamUtils.drain(this.responseStream); + this.responseStream.close(); + } + catch (Exception ex) { + // ignore + } + } + +} diff --git a/spring-cloud-starter-tencent-polaris-config/pom.xml b/spring-cloud-starter-tencent-polaris-config/pom.xml index d257cd08e59ad7cba1e495abe1a4692d25a0d90c..e7262c29f2041f58e6a2572363f48f4760479a08 100644 --- a/spring-cloud-starter-tencent-polaris-config/pom.xml +++ b/spring-cloud-starter-tencent-polaris-config/pom.xml @@ -11,6 +11,7 @@ 4.0.0 spring-cloud-starter-tencent-polaris-config + Spring Cloud Starter Tencent Polaris Config @@ -24,6 +25,36 @@ com.tencent.polaris polaris-configuration-factory + + + com.tencent.polaris + router-rule + + + com.tencent.polaris + router-nearby + + + com.tencent.polaris + router-metadata + + + com.tencent.polaris + router-canary + + + com.tencent.polaris + router-set + + + com.tencent.polaris + router-isolated + + + com.tencent.polaris + router-healthy + + @@ -34,5 +65,40 @@ + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-actuator + true + + + + org.springframework.boot + spring-boot-actuator-autoconfigure + true + + + + org.mockito + mockito-inline + test + + + + org.mockito + mockito-core + test + + + + net.bytebuddy + byte-buddy + test + diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/ConditionalOnConnectRemoteServerEnabled.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/ConditionalOnConnectRemoteServerEnabled.java new file mode 100644 index 0000000000000000000000000000000000000000..89283ae187cac7dba0248eba93ed0853dbbada4d --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/ConditionalOnConnectRemoteServerEnabled.java @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +/** + * Whether to connect to a remote server, suitable for local development mode. + * + * @author lepdou 2022-06-11 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@ConditionalOnProperty(value = "spring.cloud.polaris.config.connect-remote-server", matchIfMissing = true) +public @interface ConditionalOnConnectRemoteServerEnabled { + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/ConfigurationModifier.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/ConfigurationModifier.java index 6c46f3c89e7cf5df01b0b1cc530fab7c670a8ec5..9953b3c676c4dbc73c0c3a4cddc579ca5d69de10 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/ConfigurationModifier.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/ConfigurationModifier.java @@ -18,16 +18,19 @@ package com.tencent.cloud.polaris.config; +import java.util.ArrayList; import java.util.List; import com.tencent.cloud.common.constant.ContextConstant; import com.tencent.cloud.common.util.AddressUtils; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import com.tencent.polaris.factory.config.ConfigurationImpl; +import org.apache.commons.lang.StringUtils; + +import org.springframework.util.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.util.StringUtils; /** * Read configuration from spring cloud's configuration file and override polaris.yaml. @@ -36,22 +39,38 @@ import org.springframework.util.StringUtils; */ public class ConfigurationModifier implements PolarisConfigModifier { - @Autowired - private PolarisConfigProperties polarisConfigProperties; + private final PolarisConfigProperties polarisConfigProperties; + + private final PolarisContextProperties polarisContextProperties; + + public ConfigurationModifier(PolarisConfigProperties polarisConfigProperties, + PolarisContextProperties polarisContextProperties) { + this.polarisConfigProperties = polarisConfigProperties; + this.polarisContextProperties = polarisContextProperties; + } @Override public void modify(ConfigurationImpl configuration) { + // set connector type configuration.getConfigFile().getServerConnector().setConnectorType("polaris"); - if (StringUtils.isEmpty(polarisConfigProperties.getAddress())) { - return; + // set config server address + List configAddresses; + String configAddressesStr = polarisConfigProperties.getAddress(); + + if (StringUtils.isNotEmpty(configAddressesStr)) { + configAddresses = AddressUtils.parseAddressList(polarisConfigProperties.getAddress()); + } + else { + configAddresses = resolveConfigAddressFromPolarisAddress(polarisContextProperties.getAddress()); } - // override polaris config server address - List addresses = AddressUtils - .parseAddressList(polarisConfigProperties.getAddress()); + if (CollectionUtils.isEmpty(configAddresses)) { + throw new RuntimeException("Config server address is blank. Please check your config in bootstrap.yml" + + " with spring.cloud.polaris.address or spring.cloud.polaris.config.address"); + } - configuration.getConfigFile().getServerConnector().setAddresses(addresses); + configuration.getConfigFile().getServerConnector().setAddresses(configAddresses); } @Override @@ -59,4 +78,24 @@ public class ConfigurationModifier implements PolarisConfigModifier { return ContextConstant.ModifierOrder.CONFIG_ORDER; } + /** + * In most cases, the address of the configuration center is the same as that of Polaris, but the port is different. + * Therefore, the address of the configuration center can be deduced directly from the Polaris address. + * + */ + private List resolveConfigAddressFromPolarisAddress(String polarisAddress) { + if (StringUtils.isEmpty(polarisAddress)) { + return null; + } + + List polarisAddresses = AddressUtils.parseAddressList(polarisAddress); + List configAddresses = new ArrayList<>(polarisAddresses.size()); + + for (String address : polarisAddresses) { + String ip = StringUtils.substringBeforeLast(address, ":"); + configAddresses.add(ip + ":" + polarisConfigProperties.getPort()); + } + + return configAddresses; + } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java index eab848b5dce5592cea5bb7ecd5f742df81513146..cccea11bc77314ef7c2b9e45e345c59779c55c6b 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigAutoConfiguration.java @@ -20,7 +20,10 @@ package com.tencent.cloud.polaris.config; import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceAutoRefresher; import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; +import com.tencent.cloud.polaris.config.annotation.PolarisConfigAnnotationProcessor; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import com.tencent.cloud.polaris.config.listener.PolarisConfigChangeEventListener; +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.context.refresh.ContextRefresher; @@ -28,11 +31,12 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** - * polaris config module auto configuration at init application context phase. + * polaris config module auto configuration at init application context phase. * * @author lepdou 2022-03-28 */ @Configuration(proxyBeanMethods = false) +@ConditionalOnPolarisEnabled @ConditionalOnProperty(value = "spring.cloud.polaris.config.enabled", matchIfMissing = true) public class PolarisConfigAutoConfiguration { @@ -46,4 +50,14 @@ public class PolarisConfigAutoConfiguration { polarisPropertySourceManager, contextRefresher); } + @Bean + public PolarisConfigAnnotationProcessor polarisConfigAnnotationProcessor() { + return new PolarisConfigAnnotationProcessor(); + } + + @Bean + public PolarisConfigChangeEventListener polarisConfigChangeEventListener() { + return new PolarisConfigChangeEventListener(); + } + } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java index 296793d0a003a6ca1742dec209ae43556ef968cb..e16f1480c09d92b8ee30d9486e37efaeb09735d5 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/PolarisConfigBootstrapAutoConfiguration.java @@ -20,8 +20,9 @@ package com.tencent.cloud.polaris.config; import com.tencent.cloud.polaris.config.adapter.PolarisConfigFileLocator; import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; -import com.tencent.cloud.polaris.context.PolarisContextProperties; +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.configuration.api.core.ConfigFileService; import com.tencent.polaris.configuration.factory.ConfigFileServiceFactory; @@ -30,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; +import org.springframework.core.env.Environment; /** * polaris config module auto configuration at bootstrap phase. @@ -37,9 +39,10 @@ import org.springframework.context.annotation.Import; * @author lepdou 2022-03-10 */ @Configuration(proxyBeanMethods = false) +@ConditionalOnPolarisEnabled @ConditionalOnProperty(value = "spring.cloud.polaris.config.enabled", matchIfMissing = true) -@Import(PolarisContextConfiguration.class) +@Import(PolarisContextAutoConfiguration.class) public class PolarisConfigBootstrapAutoConfiguration { @Bean @@ -48,29 +51,34 @@ public class PolarisConfigBootstrapAutoConfiguration { } @Bean - public ConfigFileService configFileService(SDKContext sdkContext) { - return ConfigFileServiceFactory.createConfigFileService(sdkContext); + public PolarisPropertySourceManager polarisPropertySourceManager() { + return new PolarisPropertySourceManager(); } @Bean - public PolarisPropertySourceManager polarisPropertySourceManager() { - return new PolarisPropertySourceManager(); + @ConditionalOnConnectRemoteServerEnabled + public ConfigFileService configFileService(SDKContext sdkContext) { + return ConfigFileServiceFactory.createConfigFileService(sdkContext); } @Bean + @ConditionalOnConnectRemoteServerEnabled public PolarisConfigFileLocator polarisConfigFileLocator( PolarisConfigProperties polarisConfigProperties, PolarisContextProperties polarisContextProperties, ConfigFileService configFileService, - PolarisPropertySourceManager polarisPropertySourceManager) { + PolarisPropertySourceManager polarisPropertySourceManager, + Environment environment) { return new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties, configFileService, - polarisPropertySourceManager); + polarisPropertySourceManager, environment); } @Bean - public ConfigurationModifier configurationModifier() { - return new ConfigurationModifier(); + @ConditionalOnConnectRemoteServerEnabled + public ConfigurationModifier configurationModifier(PolarisConfigProperties polarisConfigProperties, + PolarisContextProperties polarisContextProperties) { + return new ConfigurationModifier(polarisConfigProperties, polarisContextProperties); } } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java index 6ab0b45305ba218874920406e9d5759d9e08b1fb..b3b92ce30060acd40ce4ea3fe7fcc80364357f08 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocator.java @@ -1,23 +1,24 @@ /* * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * https://opensource.org/licenses/BSD-3-Clause + * https://opensource.org/licenses/BSD-3-Clause * - * 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. + * 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.tencent.cloud.polaris.config.adapter; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -25,9 +26,11 @@ import java.util.concurrent.ConcurrentHashMap; import com.tencent.cloud.polaris.config.config.ConfigFileGroup; import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; import com.tencent.cloud.polaris.config.enums.ConfigFileFormat; -import com.tencent.cloud.polaris.context.PolarisContextProperties; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; +import com.tencent.polaris.configuration.api.core.ConfigFileMetadata; import com.tencent.polaris.configuration.api.core.ConfigFileService; import com.tencent.polaris.configuration.api.core.ConfigKVFile; +import com.tencent.polaris.configuration.client.internal.DefaultConfigFileMetadata; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,14 +65,18 @@ public class PolarisConfigFileLocator implements PropertySourceLocator { private final PolarisPropertySourceManager polarisPropertySourceManager; + private final Environment environment; + public PolarisConfigFileLocator(PolarisConfigProperties polarisConfigProperties, PolarisContextProperties polarisContextProperties, ConfigFileService configFileService, - PolarisPropertySourceManager polarisPropertySourceManager) { + PolarisPropertySourceManager polarisPropertySourceManager, + Environment environment) { this.polarisConfigProperties = polarisConfigProperties; this.polarisContextProperties = polarisContextProperties; this.configFileService = configFileService; this.polarisPropertySourceManager = polarisPropertySourceManager; + this.environment = environment; } @Override @@ -77,17 +84,76 @@ public class PolarisConfigFileLocator implements PropertySourceLocator { CompositePropertySource compositePropertySource = new CompositePropertySource( POLARIS_CONFIG_PROPERTY_SOURCE_NAME); + // load spring boot default config files + initInternalConfigFiles(compositePropertySource); + + // load custom config files List configFileGroups = polarisConfigProperties.getGroups(); if (CollectionUtils.isEmpty(configFileGroups)) { return compositePropertySource; } - - initPolarisConfigFiles(compositePropertySource, configFileGroups); + initCustomPolarisConfigFiles(compositePropertySource, configFileGroups); return compositePropertySource; } - private void initPolarisConfigFiles(CompositePropertySource compositePropertySource, + private void initInternalConfigFiles(CompositePropertySource compositePropertySource) { + List internalConfigFiles = getInternalConfigFiles(); + + for (ConfigFileMetadata configFile : internalConfigFiles) { + PolarisPropertySource polarisPropertySource = loadPolarisPropertySource( + configFile.getNamespace(), configFile.getFileGroup(), configFile.getFileName()); + + compositePropertySource.addPropertySource(polarisPropertySource); + + polarisPropertySourceManager.addPropertySource(polarisPropertySource); + + LOGGER.info("[SCT Config] Load and inject polaris config file. file = {}", configFile); + } + } + + private List getInternalConfigFiles() { + String namespace = polarisContextProperties.getNamespace(); + String serviceName = polarisContextProperties.getService(); + if (StringUtils.isEmpty(serviceName)) { + serviceName = environment.getProperty("spring.application.name"); + } + + List internalConfigFiles = new LinkedList<>(); + + // priority: application-${profile} > application > boostrap-${profile} > boostrap + String[] activeProfiles = environment.getActiveProfiles(); + + for (String activeProfile : activeProfiles) { + if (StringUtils.isEmpty(activeProfile)) { + continue; + } + + internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application-" + activeProfile + ".properties")); + internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application-" + activeProfile + ".yml")); + } + + internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application.properties")); + internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "application.yml")); + + for (String activeProfile : activeProfiles) { + if (StringUtils.isEmpty(activeProfile)) { + continue; + } + + internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap-" + activeProfile + ".properties")); + internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap-" + activeProfile + ".yml")); + } + + internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap.properties")); + internalConfigFiles.add(new DefaultConfigFileMetadata(namespace, serviceName, "bootstrap.yml")); + + + return internalConfigFiles; + } + + + private void initCustomPolarisConfigFiles(CompositePropertySource compositePropertySource, List configFileGroups) { String namespace = polarisContextProperties.getNamespace(); @@ -125,12 +191,10 @@ public class PolarisConfigFileLocator implements PropertySourceLocator { // unknown extension is resolved as properties file if (ConfigFileFormat.isPropertyFile(fileName) || ConfigFileFormat.isUnknownFile(fileName)) { - configKVFile = configFileService.getConfigPropertiesFile(namespace, group, - fileName); + configKVFile = configFileService.getConfigPropertiesFile(namespace, group, fileName); } else if (ConfigFileFormat.isYamlFile(fileName)) { - configKVFile = configFileService.getConfigYamlFile(namespace, group, - fileName); + configKVFile = configFileService.getConfigYamlFile(namespace, group, fileName); } else { LOGGER.warn( diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigAnnotationProcessor.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigAnnotationProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..349799f6957fe49ce6d7f4087d622679102797e4 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigAnnotationProcessor.java @@ -0,0 +1,105 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.annotation; + +import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Sets; +import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent; +import com.tencent.cloud.polaris.config.listener.ConfigChangeListener; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.core.Ordered; +import org.springframework.core.PriorityOrdered; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.lang.NonNull; +import org.springframework.util.ReflectionUtils; + +import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.addChangeListener; + +/** + * {@link PolarisConfigAnnotationProcessor} implementation for spring . + *

Refer to the Apollo project implementation: + * + * ApolloAnnotationProcessor + * @author Palmer Xu 2022-06-07 + */ +public class PolarisConfigAnnotationProcessor implements BeanPostProcessor, PriorityOrdered { + + @Override + public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) + throws BeansException { + Class clazz = bean.getClass(); + for (Method method : findAllMethod(clazz)) { + this.processPolarisConfigChangeListener(bean, method); + } + return bean; + } + + @Override + public Object postProcessAfterInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException { + return bean; + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } + + private List findAllMethod(Class clazz) { + final List res = new LinkedList<>(); + ReflectionUtils.doWithMethods(clazz, res::add); + return res; + } + + private void processPolarisConfigChangeListener(final Object bean, final Method method) { + PolarisConfigKVFileChangeListener annotation = AnnotationUtils + .findAnnotation(method, PolarisConfigKVFileChangeListener.class); + if (annotation == null) { + return; + } + Class[] parameterTypes = method.getParameterTypes(); + Preconditions.checkArgument(parameterTypes.length == 1, + "Invalid number of parameters: %s for method: %s, should be 1", parameterTypes.length, + method); + Preconditions.checkArgument(ConfigChangeEvent.class.isAssignableFrom(parameterTypes[0]), + "Invalid parameter type: %s for method: %s, should be ConfigChangeEvent", parameterTypes[0], + method); + + ReflectionUtils.makeAccessible(method); + String[] annotatedInterestedKeys = annotation.interestedKeys(); + String[] annotatedInterestedKeyPrefixes = annotation.interestedKeyPrefixes(); + + ConfigChangeListener configChangeListener = changeEvent -> ReflectionUtils.invokeMethod(method, bean, changeEvent); + + Set interestedKeys = + annotatedInterestedKeys.length > 0 ? Sets.newHashSet(annotatedInterestedKeys) : null; + Set interestedKeyPrefixes = + annotatedInterestedKeyPrefixes.length > 0 ? Sets.newHashSet(annotatedInterestedKeyPrefixes) + : null; + + addChangeListener(configChangeListener, interestedKeys, interestedKeyPrefixes); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigKVFileChangeListener.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigKVFileChangeListener.java new file mode 100644 index 0000000000000000000000000000000000000000..142b41bed28dde1d3d68753f2f6c01b447228a05 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/annotation/PolarisConfigKVFileChangeListener.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Configuring the change listener annotation. + *

Refer to the Apollo project implementation: + * + * ApolloAnnotationProcessor + * @author Palmer Xu 2022-05-31 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Documented +public @interface PolarisConfigKVFileChangeListener { + + /** + * The keys interested in the listener, will only be notified if any of the interested keys is changed. + *

+ * If neither of {@code interestedKeys} and {@code interestedKeyPrefixes} is specified then the {@code listener} will be notified when any key is changed. + * @return interested keys in the listener + */ + String[] interestedKeys() default {}; + + /** + * The key prefixes that the listener is interested in, will be notified if and only if the changed keys start with anyone of the prefixes. + * The prefixes will simply be used to determine whether the {@code listener} should be notified or not using {@code changedKey.startsWith(prefix)}. + * e.g. "spring." means that {@code listener} is interested in keys that starts with "spring.", such as "spring.banner", "spring.jpa", etc. + * and "application" means that {@code listener} is interested in keys that starts with "application", such as "applicationName", "application.port", etc. + *

+ * If neither of {@code interestedKeys} and {@code interestedKeyPrefixes} is specified then the {@code listener} will be notified when whatever key is changed. + * @return interested key-prefixed in the listener + */ + String[] interestedKeyPrefixes() default {}; + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/config/PolarisConfigProperties.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/config/PolarisConfigProperties.java index 6ddc49f5fcd49622ad21d4facc2cf47c82a5e0bf..a2c9032fe4ccbfb9be31f6559dbfe40fbe2059d7 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/config/PolarisConfigProperties.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/config/PolarisConfigProperties.java @@ -39,6 +39,11 @@ public class PolarisConfigProperties { */ private String address; + /** + * Polaris config grpc port. + */ + private int port = 8093; + /** * Whether to automatically update to the spring context when the configuration file. * is updated @@ -66,6 +71,14 @@ public class PolarisConfigProperties { this.address = address; } + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + public boolean isAutoRefresh() { return autoRefresh; } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/endpoint/PolarisConfigEndpoint.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/endpoint/PolarisConfigEndpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..eb9e94f5b3052a0d6fd2e35e5d74bd632bbc2854 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/endpoint/PolarisConfigEndpoint.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.endpoint; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.tencent.cloud.polaris.config.adapter.PolarisPropertySource; +import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +/** + * Endpoint of polaris config. + * + * @author shuiqingliu + **/ +@Endpoint(id = "polaris-config") +public class PolarisConfigEndpoint { + + private final PolarisConfigProperties polarisConfigProperties; + private final PolarisPropertySourceManager polarisPropertySourceManager; + + public PolarisConfigEndpoint(PolarisConfigProperties polarisConfigProperties, PolarisPropertySourceManager polarisPropertySourceManager) { + this.polarisConfigProperties = polarisConfigProperties; + this.polarisPropertySourceManager = polarisPropertySourceManager; + } + + @ReadOperation + public Map polarisConfig() { + Map configInfo = new HashMap<>(); + configInfo.put("PolarisConfigProperties", polarisConfigProperties); + + List propertySourceList = polarisPropertySourceManager.getAllPropertySources(); + configInfo.put("PolarisPropertySource", propertySourceList); + + return configInfo; + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/endpoint/PolarisConfigEndpointAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/endpoint/PolarisConfigEndpointAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..cb26462ee44d4bdd250f5392df130522fa66f4b7 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/endpoint/PolarisConfigEndpointAutoConfiguration.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.endpoint; + +import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; + +import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * The AutoConfiguration for Polaris Config's endpoint. + * + * @author shuiqingliu + **/ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(Endpoint.class) +@ConditionalOnProperty(value = "spring.cloud.polaris.config.enabled", + matchIfMissing = true) +public class PolarisConfigEndpointAutoConfiguration { + + @Bean + @ConditionalOnAvailableEndpoint + @ConditionalOnMissingBean + public PolarisConfigEndpoint polarisConfigEndpoint(PolarisConfigProperties polarisConfigProperties, PolarisPropertySourceManager polarisPropertySourceManager) { + return new PolarisConfigEndpoint(polarisConfigProperties, polarisPropertySourceManager); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/enums/ConfigFileFormat.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/enums/ConfigFileFormat.java index 2b7be5774ef147d9a10d46485e65e33f79211fcc..024ad67d0dba1a763e2c88932e044251b8cbd486 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/enums/ConfigFileFormat.java +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/enums/ConfigFileFormat.java @@ -28,7 +28,7 @@ public enum ConfigFileFormat { /** * property format. */ - PROPERTY(".property"), + PROPERTY(".properties"), /** * yaml format. */ @@ -48,7 +48,7 @@ public enum ConfigFileFormat { /** * text format. */ - TEXT(".text"), + TEXT(".txt"), /** * html format. */ diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeEvent.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..119a5ce4c8fcebc6cab0b29a2e75afbc2799b1d4 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeEvent.java @@ -0,0 +1,88 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.listener; + +import java.util.Map; +import java.util.Set; + +import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; + +/** + * A change event when config is changed . + * + * @author Palmer Xu 2022-06-07 + */ +public final class ConfigChangeEvent { + + /** + * all changes keys map. + */ + private final Map changes; + + /** + * all interested changed keys. + */ + private final Set interestedChangedKeys; + + /** + * Config Change Event Constructor. + * @param changes all changes keys map + * @param interestedChangedKeys all interested changed keys + */ + public ConfigChangeEvent(Map changes, Set interestedChangedKeys) { + this.changes = changes; + this.interestedChangedKeys = interestedChangedKeys; + } + + /** + * Get the keys changed. + * @return the list of the keys + */ + public Set changedKeys() { + return changes.keySet(); + } + + /** + * Get a specific change instance for the key specified. + * @param key the changed key + * @return the change instance + */ + public ConfigPropertyChangeInfo getChange(String key) { + return changes.get(key); + } + + /** + * Check whether the specified key is changed . + * @param key the key + * @return true if the key is changed, false otherwise. + */ + public boolean isChanged(String key) { + return changes.containsKey(key); + } + + /** + * Maybe subclass override this method. + * + * @return interested and changed keys + */ + public Set interestedChangedKeys() { + return interestedChangedKeys; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListener.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListener.java new file mode 100644 index 0000000000000000000000000000000000000000..14ab64a321132eb08ac7be2062e5658a9d35680e --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListener.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.listener; + +/** + * Configuring the change listener interface. + * + * @author Palmer Xu 2022-05-31 + */ +public interface ConfigChangeListener { + + /** + * Invoked when there is any config change for the namespace. + * + * @param changeEvent the event for this change + */ + void onChange(ConfigChangeEvent changeEvent); + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigChangeEventListener.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigChangeEventListener.java new file mode 100644 index 0000000000000000000000000000000000000000..0a55255d533bf34adf78223a93806b34cf4cd5fa --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigChangeEventListener.java @@ -0,0 +1,111 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.listener; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.google.common.collect.Maps; +import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; + +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.lang.NonNull; + +import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.fireConfigChange; +import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.initialize; +import static com.tencent.cloud.polaris.config.listener.PolarisConfigListenerContext.merge; + +/** + * Polaris Config Change Event Listener . + * + * @author Elve.Xu 2022-06-08 + */ +public final class PolarisConfigChangeEventListener implements ApplicationListener { + + private static final AtomicBoolean started = new AtomicBoolean(); + + /** + * Handle an application event. + * + * @param event the event to respond to + */ + @Override + public void onApplicationEvent(@NonNull ApplicationEvent event) { + + // Initialize application all environment properties . + if (event instanceof ApplicationStartedEvent && started.compareAndSet(false, true)) { + ApplicationStartedEvent applicationStartedEvent = (ApplicationStartedEvent) event; + ConfigurableEnvironment environment = applicationStartedEvent.getApplicationContext().getEnvironment(); + Map ret = loadEnvironmentProperties(environment); + if (!ret.isEmpty()) { + initialize(ret); + } + } + + // Process Environment Change Event . + if (event instanceof EnvironmentChangeEvent) { + EnvironmentChangeEvent environmentChangeEvent = (EnvironmentChangeEvent) event; + ConfigurableApplicationContext context = (ConfigurableApplicationContext) environmentChangeEvent.getSource(); + ConfigurableEnvironment environment = context.getEnvironment(); + Map ret = loadEnvironmentProperties(environment); + Map changes = merge(ret); + fireConfigChange(changes.keySet(), Maps.newHashMap(changes)); + changes.clear(); + } + } + + /** + * Try load all application environment config properties . + * @param environment application environment instance of {@link Environment} + * @return properties + */ + @SuppressWarnings("unchecked") + private Map loadEnvironmentProperties(ConfigurableEnvironment environment) { + Map ret = Maps.newHashMap(); + MutablePropertySources sources = environment.getPropertySources(); + sources.iterator().forEachRemaining(propertySource -> { + Object o = propertySource.getSource(); + if (o instanceof Map) { + for (Map.Entry entry : ((Map) o).entrySet()) { + String key = entry.getKey(); + String value = environment.getProperty(key); + ret.put(key, value); + } + } + else if (o instanceof Collection) { + int count = 0; + Collection collection = (Collection) o; + for (Object object : collection) { + String key = "[" + (count++) + "]"; + ret.put(key, object); + } + } + }); + return ret; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigListenerContext.java b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigListenerContext.java new file mode 100644 index 0000000000000000000000000000000000000000..05ac41756a1a6b5ca27374fde3b439d6c82fa577 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/main/java/com/tencent/cloud/polaris/config/listener/PolarisConfigListenerContext.java @@ -0,0 +1,277 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.listener; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener; +import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; +import org.springframework.scheduling.concurrent.CustomizableThreadFactory; + +import static com.tencent.polaris.configuration.api.core.ChangeType.ADDED; +import static com.tencent.polaris.configuration.api.core.ChangeType.DELETED; +import static com.tencent.polaris.configuration.api.core.ChangeType.MODIFIED; + +/** + * Polaris Config Listener Context Defined . + *

Refer to the Apollo project implementation: + * + * AbstractConfig + * @author Palmer Xu 2022-06-06 + */ +public final class PolarisConfigListenerContext { + + /** + * Logger instance. + */ + private static final Logger LOG = LoggerFactory.getLogger(PolarisConfigListenerContext.class); + + /** + * Execute service Atomic Reference Cache . + */ + private static final AtomicReference EAR = new AtomicReference<>(); + + /** + * All custom {@link ConfigChangeListener} instance defined in application . + */ + private static final List listeners = Lists.newCopyOnWriteArrayList(); + + /** + * All custom interested keys defined in application . + */ + private static final Map> interestedKeys = Maps.newHashMap(); + + /** + * All custom interested key prefixes defined in application . + */ + private static final Map> interestedKeyPrefixes = Maps.newHashMap(); + + /** + * Cache all latest configuration information for users in the application environment . + */ + private static final Cache properties = CacheBuilder.newBuilder().build(); + + /** + * Get or Created new execute server . + * @return execute service instance of {@link ExecutorService} + */ + private static ExecutorService executor() { + if (EAR.get() == null) { + synchronized (PolarisConfigListenerContext.class) { + int coreThreadSize = Runtime.getRuntime().availableProcessors(); + final ExecutorService service = new ThreadPoolExecutor(coreThreadSize, coreThreadSize, + 0, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(64), + new CustomizableThreadFactory("Config-Change-Notify-Thread-Pool-")); + + // Register Jvm Shutdown Hook + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + try { + LOG.info("Shutting down config change notify thread pool"); + service.shutdown(); + } + catch (Exception ignore) { + } + })); + EAR.set(service); + } + } + return EAR.get(); + } + + /** + * Initialize Environment Properties cache after listen ApplicationStartedEvent event . + * @param ret origin properties map + */ + static void initialize(Map ret) { + properties.putAll(ret); + } + + /** + * Merge Changed Properties . + * @param ret current environment properties map + * @return merged properties result map + */ + static Map merge(Map ret) { + Map changes = Maps.newHashMap(); + if (!ret.isEmpty()) { + + Map origin = Maps.newHashMap(properties.asMap()); + Map deleted = Maps.newHashMap(); + + origin.keySet().parallelStream().forEach(key -> { + if (!ret.containsKey(key)) { + deleted.put(key, new ConfigPropertyChangeInfo(key, String.valueOf(origin.get(key)), null, DELETED)); + properties.invalidate(key); + } + }); + changes.putAll(deleted); + + ret.keySet().parallelStream().forEach(key -> { + Object oldValue = properties.getIfPresent(key); + Object newValue = ret.get(key); + if (oldValue != null) { + if (!newValue.equals(oldValue)) { + properties.put(key, newValue); + changes.put(key, new ConfigPropertyChangeInfo(key, String.valueOf(oldValue), String.valueOf(newValue), MODIFIED)); + } + } + else { + properties.put(key, newValue); + changes.put(key, new ConfigPropertyChangeInfo(key, null, String.valueOf(newValue), ADDED)); + } + }); + } + return changes; + } + + /** + * Adding a config file change listener, will trigger a callback when the config file is published . + * @param listener the listener will be added + * @param interestedKeys the keys interested in the listener, will only be notified if any of the interested keys is changed. + * @param interestedKeyPrefixes the key prefixes that the listener is interested in, + * will be notified if and only if the changed keys start with anyone of the prefixes. + */ + public static void addChangeListener(@NonNull ConfigChangeListener listener, + @Nullable Set interestedKeys, @Nullable Set interestedKeyPrefixes) { + if (!listeners.contains(listener)) { + listeners.add(listener); + PolarisConfigListenerContext.interestedKeys.put(listener, interestedKeys == null ? Sets.newHashSet() : interestedKeys); + PolarisConfigListenerContext.interestedKeyPrefixes.put(listener, interestedKeyPrefixes == null ? Sets.newHashSet() : interestedKeyPrefixes); + } + } + + /** + * Fire config change event with {@link ConfigKVFileChangeListener} . + * @param changedKeys changed keys in listener + * @param changes target config file changes info + */ + public static void fireConfigChange(Set changedKeys, Map changes) { + final List listeners = findMatchedConfigChangeListeners(changedKeys); + for (ConfigChangeListener listener : listeners) { + Set interestedChangedKeys = resolveInterestedChangedKeys(listener, changedKeys); + Map modifiedChanges = new HashMap<>(interestedChangedKeys.size()); + interestedChangedKeys.parallelStream().forEach(key -> modifiedChanges.put(key, changes.get(key))); + ConfigChangeEvent event = new ConfigChangeEvent(modifiedChanges, interestedChangedKeys); + PolarisConfigListenerContext.executor().execute(() -> listener.onChange(event)); + } + } + + /** + * Try to find all matched config change listeners . + * @param changedKeys received changed keys + * @return list of matched {@link ConfigChangeListener} + */ + private static List findMatchedConfigChangeListeners(Set changedKeys) { + final List configChangeListeners = Lists.newArrayList(); + for (ConfigChangeListener listener : listeners) { + if (isConfigChangeListenerInterested(listener, changedKeys)) { + configChangeListeners.add(listener); + } + } + return configChangeListeners; + } + + /** + * Check {@link ConfigChangeListener} is interested in custom keys. + * @param listener instance of {@link ConfigChangeListener} + * @param changedKeys received changed keys + * @return true is interested in custom keys + */ + private static boolean isConfigChangeListenerInterested(ConfigChangeListener listener, Set changedKeys) { + Set interestedKeys = PolarisConfigListenerContext.interestedKeys.get(listener); + Set interestedKeyPrefixes = PolarisConfigListenerContext.interestedKeyPrefixes.get(listener); + + if ((interestedKeys == null || interestedKeys.isEmpty()) + && (interestedKeyPrefixes == null || interestedKeyPrefixes.isEmpty())) { + return true; + } + + if (interestedKeys != null) { + for (String interestedKey : interestedKeys) { + if (changedKeys.contains(interestedKey)) { + return true; + } + } + } + + if (interestedKeyPrefixes != null) { + for (String prefix : interestedKeyPrefixes) { + for (final String changedKey : changedKeys) { + if (changedKey.startsWith(prefix)) { + return true; + } + } + } + } + return false; + } + + /** + * Resolve all interested keys . + * @param listener instance of {@link ConfigChangeListener} + * @param changedKeys received changed keys + * @return set of all interested keys in listener + */ + private static Set resolveInterestedChangedKeys(ConfigChangeListener listener, Set changedKeys) { + Set interestedChangedKeys = Sets.newHashSet(); + + if (interestedKeys.containsKey(listener)) { + Set interestedKeys = PolarisConfigListenerContext.interestedKeys.get(listener); + for (String interestedKey : interestedKeys) { + if (changedKeys.contains(interestedKey)) { + interestedChangedKeys.add(interestedKey); + } + } + } + + if (interestedKeyPrefixes.containsKey(listener)) { + Set interestedKeyPrefixes = PolarisConfigListenerContext.interestedKeyPrefixes.get(listener); + for (String interestedKeyPrefix : interestedKeyPrefixes) { + for (String changedKey : changedKeys) { + if (changedKey.startsWith(interestedKeyPrefix)) { + interestedChangedKeys.add(changedKey); + } + } + } + } + + return Collections.unmodifiableSet(interestedChangedKeys); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 152d24d643521a43307687f5613c49f90fa566f5..9954a62ce85f39b00fac6b216ea6dad21c4f0f2f 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -14,6 +14,13 @@ "description": "The polaris configuration server address.", "sourceType": "com.tencent.cloud.polaris.config.config.PolarisConfigProperties" }, + { + "name": "spring.cloud.polaris.config.port", + "type": "java.lang.Integer", + "defaultValue": "8093", + "description": "The polaris configuration server port.", + "sourceType": "com.tencent.cloud.polaris.config.config.PolarisConfigProperties" + }, { "name": "spring.cloud.polaris.config.auto-refresh", "type": "java.lang.Boolean", @@ -27,6 +34,13 @@ "defaultValue": "", "description": "List of imported config files.", "sourceType": "com.tencent.cloud.polaris.config.config.PolarisConfigProperties" + }, + { + "name": "spring.cloud.polaris.config.connect-remote-server", + "type": "java.lang.Boolean", + "defaultValue": "true", + "description": "Whether to connect to a remote server, suitable for local development mode.", + "sourceType": "com.tencent.cloud.polaris.config.config.PolarisConfigProperties" } ] } diff --git a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring.factories index a0c33067e3a17ffa51f9635765f25182fe50bc93..98221a39af65a955f42f79d0d4d546e846c06c46 100644 --- a/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-config/src/main/resources/META-INF/spring.factories @@ -1,4 +1,5 @@ org.springframework.cloud.bootstrap.BootstrapConfiguration=\ com.tencent.cloud.polaris.config.PolarisConfigBootstrapAutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration + com.tencent.cloud.polaris.config.PolarisConfigAutoConfiguration,\ + com.tencent.cloud.polaris.config.endpoint.PolarisConfigEndpointAutoConfiguration diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/MockedConfigKVFile.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/MockedConfigKVFile.java new file mode 100644 index 0000000000000000000000000000000000000000..026fe4d524e71f46db059df2c485fced30ccb2e8 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/MockedConfigKVFile.java @@ -0,0 +1,170 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.adapter; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.tencent.polaris.configuration.api.core.ConfigFileChangeListener; +import com.tencent.polaris.configuration.api.core.ConfigKVFile; +import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent; +import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeListener; + +/** + * Mock config kv file for test. + *@author lepdou 2022-06-11 + */ +public class MockedConfigKVFile implements ConfigKVFile { + + private final Map properties; + private final List listeners = new ArrayList<>(); + + public MockedConfigKVFile(Map properties) { + this.properties = properties; + } + + @Override + public String getProperty(String s, String s1) { + return String.valueOf(properties.get(s)); + } + + @Override + public Integer getIntProperty(String s, Integer integer) { + return null; + } + + @Override + public Long getLongProperty(String s, Long aLong) { + return null; + } + + @Override + public Short getShortProperty(String s, Short aShort) { + return null; + } + + @Override + public Float getFloatProperty(String s, Float aFloat) { + return null; + } + + @Override + public Double getDoubleProperty(String s, Double aDouble) { + return null; + } + + @Override + public Byte getByteProperty(String s, Byte aByte) { + return null; + } + + @Override + public Boolean getBooleanProperty(String s, Boolean aBoolean) { + return null; + } + + @Override + public String[] getArrayProperty(String s, String s1, String[] strings) { + return new String[0]; + } + + @Override + public > T getEnumProperty(String s, Class aClass, T t) { + return null; + } + + @Override + public T getJsonProperty(String s, Class aClass, T t) { + return null; + } + + @Override + public T getJsonProperty(String s, Type type, T t) { + return null; + } + + @Override + public Set getPropertyNames() { + return properties.keySet(); + } + + @Override + public void addChangeListener(ConfigKVFileChangeListener configKVFileChangeListener) { + listeners.add(configKVFileChangeListener); + } + + @Override + public void removeChangeListener(ConfigKVFileChangeListener configKVFileChangeListener) { + + } + + @Override + public String getContent() { + return null; + } + + @Override + public T asJson(Class aClass, T t) { + return null; + } + + @Override + public T asJson(Type type, T t) { + return null; + } + + @Override + public boolean hasContent() { + return false; + } + + @Override + public void addChangeListener(ConfigFileChangeListener configFileChangeListener) { + + } + + @Override + public void removeChangeListener(ConfigFileChangeListener configFileChangeListener) { + + } + + public void fireChangeListener(ConfigKVFileChangeEvent event) { + for (ConfigKVFileChangeListener listener : listeners) { + listener.onChange(event); + } + } + + @Override + public String getNamespace() { + return null; + } + + @Override + public String getFileGroup() { + return null; + } + + @Override + public String getFileName() { + return null; + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocatorTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocatorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8c072ef9d3fe40884772c3e60d62d455bd4ae735 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisConfigFileLocatorTest.java @@ -0,0 +1,188 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.adapter; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; +import com.tencent.cloud.polaris.config.config.ConfigFileGroup; +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; +import com.tencent.polaris.configuration.api.core.ConfigFileService; +import com.tencent.polaris.configuration.api.core.ConfigKVFile; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; + +import static org.mockito.Mockito.when; + +/** + * test for {@link PolarisConfigFileLocator} + *@author lepdou 2022-06-11 + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisConfigFileLocatorTest { + + @Mock + private PolarisConfigProperties polarisConfigProperties; + @Mock + private PolarisContextProperties polarisContextProperties; + @Mock + private ConfigFileService configFileService; + @Mock + private PolarisPropertySourceManager polarisPropertySourceManager; + @Mock + private Environment environment; + + private final String testNamespace = "testNamespace"; + private final String testServiceName = "testServiceName"; + + @Test + public void testLoadApplicationPropertiesFile() { + PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties, + configFileService, polarisPropertySourceManager, environment); + + when(polarisContextProperties.getNamespace()).thenReturn(testNamespace); + when(polarisContextProperties.getService()).thenReturn(testServiceName); + + // application.properties + Map applicationProperties = new HashMap<>(); + applicationProperties.put("k1", "v1"); + applicationProperties.put("k2", "v2"); + applicationProperties.put("k3", "v3"); + ConfigKVFile propertiesFile = new MockedConfigKVFile(applicationProperties); + when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties")) + .thenReturn(propertiesFile); + + Map emptyMap = new HashMap<>(); + ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap); + when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile); + when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile); + when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile); + + when(polarisConfigProperties.getGroups()).thenReturn(null); + when(environment.getActiveProfiles()).thenReturn(new String[] {}); + + PropertySource propertySource = locator.locate(environment); + + Assert.assertEquals("v1", propertySource.getProperty("k1")); + Assert.assertEquals("v2", propertySource.getProperty("k2")); + Assert.assertEquals("v3", propertySource.getProperty("k3")); + } + + @Test + public void testActiveProfileFilesPriorityBiggerThanDefault() { + PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties, + configFileService, polarisPropertySourceManager, environment); + + when(polarisContextProperties.getNamespace()).thenReturn(testNamespace); + when(polarisContextProperties.getService()).thenReturn(testServiceName); + + // application.properties + Map applicationProperties = new HashMap<>(); + applicationProperties.put("k1", "v1"); + applicationProperties.put("k2", "v2"); + applicationProperties.put("k3", "v3"); + ConfigKVFile propertiesFile = new MockedConfigKVFile(applicationProperties); + when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties")) + .thenReturn(propertiesFile); + + // application-dev.properties + Map devProperties = new HashMap<>(); + devProperties.put("k1", "v11"); + ConfigKVFile devFile = new MockedConfigKVFile(devProperties); + when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application-dev.properties")) + .thenReturn(devFile); + + Map emptyMap = new HashMap<>(); + ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap); + when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile); + when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application-dev.yml")).thenReturn(emptyConfigFile); + when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile); + when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap-dev.properties")).thenReturn(emptyConfigFile); + when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile); + when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap-dev.yml")).thenReturn(emptyConfigFile); + + when(polarisConfigProperties.getGroups()).thenReturn(null); + when(environment.getActiveProfiles()).thenReturn(new String[] {"dev"}); + + PropertySource propertySource = locator.locate(environment); + + Assert.assertEquals("v11", propertySource.getProperty("k1")); + Assert.assertEquals("v2", propertySource.getProperty("k2")); + Assert.assertEquals("v3", propertySource.getProperty("k3")); + } + + @Test + public void testGetCustomFiles() { + PolarisConfigFileLocator locator = new PolarisConfigFileLocator(polarisConfigProperties, polarisContextProperties, + configFileService, polarisPropertySourceManager, environment); + + when(polarisContextProperties.getNamespace()).thenReturn(testNamespace); + when(polarisContextProperties.getService()).thenReturn(testServiceName); + + Map emptyMap = new HashMap<>(); + ConfigKVFile emptyConfigFile = new MockedConfigKVFile(emptyMap); + + when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "application.properties")).thenReturn(emptyConfigFile); + when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "application.yml")).thenReturn(emptyConfigFile); + when(configFileService.getConfigPropertiesFile(testNamespace, testServiceName, "bootstrap.properties")).thenReturn(emptyConfigFile); + when(configFileService.getConfigYamlFile(testNamespace, testServiceName, "bootstrap.yml")).thenReturn(emptyConfigFile); + + List customFiles = new LinkedList<>(); + ConfigFileGroup configFileGroup = new ConfigFileGroup(); + String customGroup = "group1"; + configFileGroup.setName(customGroup); + String customFile1 = "file1.properties"; + String customFile2 = "file2.properties"; + configFileGroup.setFiles(Lists.newArrayList(customFile1, customFile2)); + customFiles.add(configFileGroup); + + when(polarisConfigProperties.getGroups()).thenReturn(customFiles); + when(environment.getActiveProfiles()).thenReturn(new String[] {}); + + // file1.properties + Map file1Map = new HashMap<>(); + file1Map.put("k1", "v1"); + file1Map.put("k2", "v2"); + ConfigKVFile file1 = new MockedConfigKVFile(file1Map); + when(configFileService.getConfigPropertiesFile(testNamespace, customGroup, customFile1)).thenReturn(file1); + + // file2.properties + Map file2Map = new HashMap<>(); + file2Map.put("k1", "v11"); + file2Map.put("k3", "v3"); + ConfigKVFile file2 = new MockedConfigKVFile(file2Map); + when(configFileService.getConfigPropertiesFile(testNamespace, customGroup, customFile2)).thenReturn(file2); + + PropertySource propertySource = locator.locate(environment); + + Assert.assertEquals("v1", propertySource.getProperty("k1")); + Assert.assertEquals("v2", propertySource.getProperty("k2")); + Assert.assertEquals("v3", propertySource.getProperty("k3")); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java new file mode 100644 index 0000000000000000000000000000000000000000..648860f35173706967ff311e4db332cba88e3445 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/adapter/PolarisPropertiesSourceAutoRefresherTest.java @@ -0,0 +1,122 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.adapter; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.collect.Lists; +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import com.tencent.polaris.configuration.api.core.ChangeType; +import com.tencent.polaris.configuration.api.core.ConfigKVFileChangeEvent; +import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.cloud.context.refresh.ContextRefresher; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * test for {@link PolarisPropertySourceAutoRefresher} + *@author lepdou 2022-06-11 + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisPropertiesSourceAutoRefresherTest { + + @Mock + private PolarisConfigProperties polarisConfigProperties; + @Mock + private PolarisPropertySourceManager polarisPropertySourceManager; + @Mock + private ContextRefresher contextRefresher; + + private final String testNamespace = "testNamespace"; + private final String testServiceName = "testServiceName"; + private final String testFileName = "application.properties"; + + @Test + public void testConfigFileChanged() { + PolarisPropertySourceAutoRefresher refresher = new PolarisPropertySourceAutoRefresher(polarisConfigProperties, + polarisPropertySourceManager, contextRefresher); + + when(polarisConfigProperties.isAutoRefresh()).thenReturn(true); + + Map content = new HashMap<>(); + content.put("k1", "v1"); + content.put("k2", "v2"); + content.put("k3", "v3"); + MockedConfigKVFile file = new MockedConfigKVFile(content); + PolarisPropertySource polarisPropertySource = new PolarisPropertySource(testNamespace, testServiceName, testFileName, + file, content); + + when(polarisPropertySourceManager.getAllPropertySources()).thenReturn(Lists.newArrayList(polarisPropertySource)); + + ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", "v1", "v11", ChangeType.MODIFIED); + ConfigPropertyChangeInfo changeInfo2 = new ConfigPropertyChangeInfo("k4", null, "v4", ChangeType.ADDED); + ConfigPropertyChangeInfo changeInfo3 = new ConfigPropertyChangeInfo("k2", "v2", null, ChangeType.DELETED); + Map changeInfos = new HashMap<>(); + changeInfos.put("k1", changeInfo); + changeInfos.put("k2", changeInfo3); + changeInfos.put("k4", changeInfo2); + + ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos); + refresher.onApplicationEvent(null); + + file.fireChangeListener(event); + + Assert.assertEquals("v11", polarisPropertySource.getProperty("k1")); + Assert.assertEquals("v3", polarisPropertySource.getProperty("k3")); + Assert.assertNull(polarisPropertySource.getProperty("k2")); + Assert.assertEquals("v4", polarisPropertySource.getProperty("k4")); + verify(contextRefresher).refresh(); + } + + @Test + public void testNewConfigFile() { + PolarisPropertySourceAutoRefresher refresher = new PolarisPropertySourceAutoRefresher(polarisConfigProperties, + polarisPropertySourceManager, contextRefresher); + + when(polarisConfigProperties.isAutoRefresh()).thenReturn(true); + + Map emptyContent = new HashMap<>(); + MockedConfigKVFile file = new MockedConfigKVFile(emptyContent); + PolarisPropertySource polarisPropertySource = new PolarisPropertySource(testNamespace, testServiceName, testFileName, + file, emptyContent); + + when(polarisPropertySourceManager.getAllPropertySources()).thenReturn(Lists.newArrayList(polarisPropertySource)); + + ConfigPropertyChangeInfo changeInfo = new ConfigPropertyChangeInfo("k1", null, "v1", ChangeType.ADDED); + Map changeInfos = new HashMap<>(); + changeInfos.put("k1", changeInfo); + + ConfigKVFileChangeEvent event = new ConfigKVFileChangeEvent(changeInfos); + refresher.onApplicationEvent(null); + + file.fireChangeListener(event); + + Assert.assertEquals("v1", polarisPropertySource.getProperty("k1")); + verify(contextRefresher).refresh(); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/endpoint/PolarisConfigEndpointTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/endpoint/PolarisConfigEndpointTest.java new file mode 100644 index 0000000000000000000000000000000000000000..93190ee1a1a566604c86638aec5f07c71616dfe6 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/endpoint/PolarisConfigEndpointTest.java @@ -0,0 +1,70 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.endpoint; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.collect.Lists; +import com.tencent.cloud.polaris.config.adapter.MockedConfigKVFile; +import com.tencent.cloud.polaris.config.adapter.PolarisPropertySource; +import com.tencent.cloud.polaris.config.adapter.PolarisPropertySourceManager; +import com.tencent.cloud.polaris.config.config.PolarisConfigProperties; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +/** + * Test for polaris config endpoint. + * + * @author shuiqingliu + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisConfigEndpointTest { + + private final String testNamespace = "testNamespace"; + private final String testServiceName = "testServiceName"; + private final String testFileName = "application.properties"; + + @Mock + private PolarisConfigProperties polarisConfigProperties; + @Mock + private PolarisPropertySourceManager polarisPropertySourceManager; + + @Test + public void testPolarisConfigEndpoint() { + Map content = new HashMap<>(); + content.put("k1", "v1"); + content.put("k2", "v2"); + content.put("k3", "v3"); + MockedConfigKVFile file = new MockedConfigKVFile(content); + PolarisPropertySource polarisPropertySource = new PolarisPropertySource(testNamespace, testServiceName, testFileName, + file, content); + when(polarisPropertySourceManager.getAllPropertySources()).thenReturn(Lists.newArrayList(polarisPropertySource)); + + PolarisConfigEndpoint endpoint = new PolarisConfigEndpoint(polarisConfigProperties, polarisPropertySourceManager); + Map info = endpoint.polarisConfig(); + assertThat(polarisConfigProperties).isEqualTo(info.get("PolarisConfigProperties")); + assertThat(Lists.newArrayList(polarisPropertySource)).isEqualTo(info.get("PolarisPropertySource")); + } +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c408f4f5455f46eac3203e4c8f4b0028c7f8183e --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/java/com/tencent/cloud/polaris/config/listener/ConfigChangeListenerTest.java @@ -0,0 +1,124 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.listener; + +import com.google.common.collect.Sets; +import com.tencent.cloud.polaris.config.annotation.PolarisConfigKVFileChangeListener; +import com.tencent.polaris.configuration.api.core.ConfigPropertyChangeInfo; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.stereotype.Component; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; + +/** + * Integration testing for change listener. + *@author lepdou 2022-06-11 + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = DEFINED_PORT, + classes = ConfigChangeListenerTest.TestApplication.class, + properties = {"server.port=8081", + "spring.config.location = classpath:application-test.yml"}) +public class ConfigChangeListenerTest { + + @Autowired + private ApplicationEventPublisher applicationEventPublisher; + @Autowired + private ConfigurableApplicationContext applicationContext; + @Autowired + private TestApplication.TestConfig testConfig; + + @Test + public void test() throws InterruptedException { + //before change + Assert.assertEquals(1000, testConfig.getTimeout()); + + //submit change event + System.setProperty("timeout", "2000"); + EnvironmentChangeEvent event = new EnvironmentChangeEvent(applicationContext, + Sets.newHashSet("timeout")); + + applicationEventPublisher.publishEvent(event); + Thread.sleep(200); + //after change + Assert.assertEquals(2, testConfig.getChangeCnt()); + Assert.assertEquals(2000, testConfig.getTimeout()); + } + + + @SpringBootApplication + protected static class TestApplication { + + @Component + protected static class TestConfig { + + @Value("${timeout:1000}") + private int timeout; + + private int changeCnt; + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public int getChangeCnt() { + return changeCnt; + } + + @PolarisConfigKVFileChangeListener(interestedKeys = {"timeout"}) + public void configChangedListener(ConfigChangeEvent event) { + ConfigPropertyChangeInfo changeInfo = event.getChange("timeout"); + timeout = Integer.parseInt(changeInfo.getNewValue()); + changeCnt++; + } + + @PolarisConfigKVFileChangeListener(interestedKeyPrefixes = {"timeout"}) + public void configChangedListener2(ConfigChangeEvent event) { + ConfigPropertyChangeInfo changeInfo = event.getChange("timeout"); + timeout = Integer.parseInt(changeInfo.getNewValue()); + changeCnt++; + } + } + + @Component + protected static class EventPublisher implements ApplicationEventPublisher { + + @Override + public void publishEvent(Object o) { + + } + } + } + +} diff --git a/spring-cloud-starter-tencent-polaris-config/src/test/resources/application-test.yml b/spring-cloud-starter-tencent-polaris-config/src/test/resources/application-test.yml new file mode 100644 index 0000000000000000000000000000000000000000..c90d1340b2541d09f2c967a929872a1bb3c15c99 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-config/src/test/resources/application-test.yml @@ -0,0 +1,9 @@ +spring: + application: + name: test + cloud: + polaris: + address: grpc://127.0.0.1:8091 + namespace: default + config: + connect-remote-server: false diff --git a/spring-cloud-starter-tencent-polaris-discovery/pom.xml b/spring-cloud-starter-tencent-polaris-discovery/pom.xml index 4fca20c4609971418c77200bd1ceb6aacc003779..ac077bb7b56aaa3c5f25b870fc1f718713bb4f22 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/pom.xml +++ b/spring-cloud-starter-tencent-polaris-discovery/pom.xml @@ -15,70 +15,100 @@ - - com.tencent.cloud - spring-cloud-tencent-polaris-context - - + + com.tencent.cloud + spring-cloud-tencent-polaris-loadbalancer + + - - - com.tencent.polaris - polaris-discovery-factory - + + + com.tencent.polaris + polaris-discovery-factory + + + com.tencent.polaris + router-rule + + + com.tencent.polaris + router-nearby + + + com.tencent.polaris + router-metadata + + + com.tencent.polaris + router-canary + + + com.tencent.polaris + router-set + + + com.tencent.polaris + router-isolated + + + com.tencent.polaris + router-healthy + + + - - com.tencent.polaris - polaris-test-common - test - + + com.tencent.polaris + polaris-test-common + test + - - com.tencent.polaris - polaris-test-mock-discovery - test - - + + com.tencent.polaris + polaris-test-mock-discovery + test + + - - org.springframework.cloud - spring-cloud-starter-netflix-ribbon - + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + - - org.springframework.boot - spring-boot-starter-web - true - + + org.springframework.boot + spring-boot-starter-web + true + - - org.springframework.boot - spring-boot-starter-webflux - true - + + org.springframework.boot + spring-boot-starter-webflux + true + - - org.springframework.boot - spring-boot-starter-test - test - + + org.springframework.boot + spring-boot-starter-test + test + - - io.projectreactor - reactor-test - test - + + org.springframework.boot + spring-boot-actuator + true + - - org.powermock - powermock-module-junit4 - test - + + org.springframework.boot + spring-boot-actuator-autoconfigure + true + - - org.powermock - powermock-api-mockito2 - test - - + + io.projectreactor + reactor-test + test + + diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/DiscoveryConfigModifier.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/DiscoveryConfigModifier.java new file mode 100644 index 0000000000000000000000000000000000000000..67690ef243934055d363ea97626403146c6b783b --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/DiscoveryConfigModifier.java @@ -0,0 +1,59 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris; + +import com.tencent.cloud.common.constant.ContextConstant; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.polaris.api.config.consumer.ServiceRouterConfig; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.plugins.router.healthy.RecoverRouterConfig; + +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Spring Cloud Tencent config Override polaris config. + * + *@author lepdou 2022-04-24 + */ +public class DiscoveryConfigModifier implements PolarisConfigModifier { + + @Autowired + private PolarisDiscoveryProperties polarisDiscoveryProperties; + + @Override + public void modify(ConfigurationImpl configuration) { + // Set excludeCircuitBreakInstances to false + RecoverRouterConfig recoverRouterConfig = configuration.getConsumer().getServiceRouter() + .getPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, RecoverRouterConfig.class); + recoverRouterConfig.setExcludeCircuitBreakInstances(false); + + // Update modified config to source properties + configuration.getConsumer().getServiceRouter() + .setPluginConfig(ServiceRouterConfig.DEFAULT_ROUTER_RECOVER, recoverRouterConfig); + + // Set ServiceRefreshInterval + configuration.getConsumer().getLocalCache() + .setServiceListRefreshInterval(polarisDiscoveryProperties.getServiceListRefreshInterval()); + } + + @Override + public int getOrder() { + return ContextConstant.ModifierOrder.DISCOVERY_ORDER; + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/DiscoveryPropertiesAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/DiscoveryPropertiesAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..d0129afc0e420f9dc66d1ba02ca73584e746b6a2 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/DiscoveryPropertiesAutoConfiguration.java @@ -0,0 +1,102 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris; + +import javax.annotation.PostConstruct; + +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; +import com.tencent.cloud.polaris.extend.consul.ConsulContextProperties; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.core.ProviderAPI; +import com.tencent.polaris.api.exception.PolarisException; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.factory.api.DiscoveryAPIFactory; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Common configuration of discovery. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnPolarisEnabled +@Import({ PolarisDiscoveryProperties.class, ConsulContextProperties.class }) +public class DiscoveryPropertiesAutoConfiguration { + + @Autowired(required = false) + private PolarisDiscoveryProperties polarisDiscoveryProperties; + + @Autowired(required = false) + private ConsulContextProperties consulContextProperties; + + private boolean registerEnabled = false; + + private boolean discoveryEnabled = false; + + @Bean + @ConditionalOnMissingBean + public ProviderAPI polarisProvider(SDKContext polarisContext) + throws PolarisException { + return DiscoveryAPIFactory.createProviderAPIByContext(polarisContext); + } + + @Bean + @ConditionalOnMissingBean + public ConsumerAPI polarisConsumer(SDKContext polarisContext) + throws PolarisException { + return DiscoveryAPIFactory.createConsumerAPIByContext(polarisContext); + } + + @Bean + @ConditionalOnMissingBean + public PolarisDiscoveryHandler polarisDiscoveryHandler() { + return new PolarisDiscoveryHandler(); + } + + @Bean + public DiscoveryConfigModifier discoveryConfigModifier() { + return new DiscoveryConfigModifier(); + } + + @PostConstruct + public void init() { + if (null != polarisDiscoveryProperties) { + registerEnabled |= polarisDiscoveryProperties.isRegisterEnabled(); + discoveryEnabled |= polarisDiscoveryProperties.isEnabled(); + } + if (null != consulContextProperties && consulContextProperties.isEnabled()) { + registerEnabled |= consulContextProperties.isRegister(); + discoveryEnabled |= consulContextProperties.isDiscoveryEnabled(); + } + } + + public boolean isRegisterEnabled() { + return registerEnabled; + } + + public boolean isDiscoveryEnabled() { + return discoveryEnabled; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/DiscoveryPropertiesBootstrapAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/DiscoveryPropertiesBootstrapAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..f04663c1dd471b48f4be2912a79cbca06f1d3550 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/DiscoveryPropertiesBootstrapAutoConfiguration.java @@ -0,0 +1,34 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Common configuration of discovery. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty("spring.cloud.polaris.enabled") +@Import(DiscoveryPropertiesAutoConfiguration.class) +public class DiscoveryPropertiesBootstrapAutoConfiguration { + +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/PolarisDiscoveryProperties.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/PolarisDiscoveryProperties.java index 35f397217d29933ef4c05ab9731eaad6fb53066c..2de7d014016c03b48ee3e5062ca33f869b88689e 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/PolarisDiscoveryProperties.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/PolarisDiscoveryProperties.java @@ -18,13 +18,17 @@ package com.tencent.cloud.polaris; -import javax.annotation.PostConstruct; - -import org.apache.commons.lang.StringUtils; +import com.tencent.cloud.common.constant.ContextConstant; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.factory.config.consumer.DiscoveryConfigImpl; +import com.tencent.polaris.factory.config.provider.RegisterConfigImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; /** @@ -35,11 +39,6 @@ import org.springframework.core.env.Environment; @ConfigurationProperties("spring.cloud.polaris.discovery") public class PolarisDiscoveryProperties { - /** - * The polaris authentication token. - */ - private String token; - /** * Namespace, separation registry of different environments. */ @@ -49,14 +48,19 @@ public class PolarisDiscoveryProperties { /** * Service name to registry. */ - @Value("${spring.cloud.polaris.discovery.service:${spring.application.name:}}") + @Value("${spring.cloud.polaris.discovery.service:${spring.cloud.polaris.service:${spring.application.name:}}}") private String service; + /** + * The polaris authentication token. + */ + private String token; + /** * Load balance weight. */ @Value("${spring.cloud.polaris.discovery.weight:#{100}}") - private float weight; + private int weight; /** * Version number. @@ -72,13 +76,18 @@ public class PolarisDiscoveryProperties { /** * Port of instance. */ - @Value("${server.port:}") + @Value("${server.port:8080}") private int port; + /** + * Enable polaris discovery or not. + */ + private Boolean enabled = true; + /** * If instance registered. */ - @Value("${spring.cloud.polaris.discovery.register.enabled:#{true}}") + @Value("${spring.cloud.polaris.discovery.register:#{true}}") private Boolean registerEnabled; /** @@ -93,32 +102,15 @@ public class PolarisDiscoveryProperties { @Value("${spring.cloud.polaris.discovery.health-check-url:}") private String healthCheckUrl; - @Autowired - private Environment environment; - /** - * Init properties. + * Millis interval of refresh of service info list. Default: 60000. */ - @PostConstruct - public void init() { - if (StringUtils.isEmpty(this.getNamespace())) { - this.setNamespace(environment - .resolvePlaceholders("${spring.cloud.polaris.discovery.namespace:}")); - } - if (StringUtils.isEmpty(this.getService())) { - this.setService(environment - .resolvePlaceholders("${spring.cloud.polaris.discovery.service:}")); - } - if (StringUtils.isEmpty(this.getToken())) { - this.setToken(environment - .resolvePlaceholders("${spring.cloud.polaris.discovery.token:}")); - } - } + private Long serviceListRefreshInterval = 60000L; + + @Autowired + private Environment environment; public boolean isHeartbeatEnabled() { - if (null == heartbeatEnabled) { - return false; - } return heartbeatEnabled; } @@ -134,11 +126,11 @@ public class PolarisDiscoveryProperties { this.namespace = namespace; } - public float getWeight() { + public int getWeight() { return weight; } - public void setWeight(float weight) { + public void setWeight(int weight) { this.weight = weight; } @@ -150,6 +142,14 @@ public class PolarisDiscoveryProperties { this.service = service; } + public Boolean isEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + public boolean isRegisterEnabled() { return registerEnabled; } @@ -198,14 +198,65 @@ public class PolarisDiscoveryProperties { this.healthCheckUrl = healthCheckUrl; } + public Long getServiceListRefreshInterval() { + return serviceListRefreshInterval; + } + + public void setServiceListRefreshInterval(Long serviceListRefreshInterval) { + this.serviceListRefreshInterval = serviceListRefreshInterval; + } + @Override public String toString() { - return "PolarisProperties{" + "token='" + token + '\'' + ", namespace='" - + namespace + '\'' + ", service='" + service + '\'' + ", weight=" + weight - + ", version='" + version + '\'' + ", protocol='" + protocol + '\'' - + ", port=" + port + '\'' + ", registerEnabled=" + registerEnabled - + ", heartbeatEnabled=" + heartbeatEnabled + ", healthCheckUrl=" - + healthCheckUrl + ", environment=" + environment + '}'; + return "PolarisDiscoveryProperties{" + + "namespace='" + namespace + '\'' + + ", service='" + service + '\'' + + ", token='" + token + '\'' + + ", weight=" + weight + + ", version='" + version + '\'' + + ", protocol='" + protocol + '\'' + + ", port=" + port + + ", enabled=" + enabled + + ", registerEnabled=" + registerEnabled + + ", heartbeatEnabled=" + heartbeatEnabled + + ", healthCheckUrl='" + healthCheckUrl + '\'' + + ", serviceListRefreshInterval=" + serviceListRefreshInterval + + '}'; + } + + @Bean + @ConditionalOnMissingBean + public PolarisDiscoveryConfigModifier polarisDiscoveryConfigModifier() { + return new PolarisDiscoveryConfigModifier(); + } + + private static class PolarisDiscoveryConfigModifier implements PolarisConfigModifier { + + private final String ID = "polaris"; + + @Autowired(required = false) + private PolarisDiscoveryProperties polarisDiscoveryProperties; + + @Override + public void modify(ConfigurationImpl configuration) { + if (polarisDiscoveryProperties != null) { + DiscoveryConfigImpl discoveryConfig = new DiscoveryConfigImpl(); + discoveryConfig.setServerConnectorId(ID); + discoveryConfig.setEnable(polarisDiscoveryProperties.enabled); + configuration.getConsumer().getDiscoveries().add(discoveryConfig); + + RegisterConfigImpl registerConfig = new RegisterConfigImpl(); + registerConfig.setServerConnectorId(ID); + registerConfig.setEnable(polarisDiscoveryProperties.registerEnabled); + configuration.getProvider().getRegisters().add(registerConfig); + } + } + + @Override + public int getOrder() { + return ContextConstant.ModifierOrder.LAST; + } + } } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/ConditionalOnPolarisDiscoveryEnabled.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/ConditionalOnPolarisDiscoveryEnabled.java index 49bcea808443f359490a9fb6a5ac15e13ecce3c1..97f478683e11b16d60760722cc99adb7f76c0ae8 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/ConditionalOnPolarisDiscoveryEnabled.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/ConditionalOnPolarisDiscoveryEnabled.java @@ -22,8 +22,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; + import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled; +import org.springframework.context.annotation.Conditional; /** * @author Haotian Zhang, Andrew Shan, Jie Cheng @@ -31,8 +33,8 @@ import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.METHOD }) @ConditionalOnDiscoveryEnabled -@ConditionalOnProperty(value = "spring.cloud.polaris.discovery.enabled", - matchIfMissing = true) +@ConditionalOnPolarisEnabled +@Conditional(DiscoveryEnabledCondition.class) public @interface ConditionalOnPolarisDiscoveryEnabled { } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/DiscoveryEnabledCondition.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/DiscoveryEnabledCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..84041557f72e506771fca7690d62c6d024ef1004 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/DiscoveryEnabledCondition.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.discovery; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * Condition for checking if discovery enabled. + * + * @author Haotian Zhang + */ +public class DiscoveryEnabledCondition implements Condition { + + @Override + public boolean matches(ConditionContext conditionContext, + AnnotatedTypeMetadata annotatedTypeMetadata) { + + boolean isDiscoveryEnabled = Boolean + .parseBoolean(conditionContext.getEnvironment() + .getProperty("spring.cloud.polaris.discovery.enabled", "true")); + + boolean isConsulDiscoveryEnabled = Boolean + .parseBoolean(conditionContext.getEnvironment() + .getProperty("spring.cloud.consul.enabled", "false")) + && Boolean.parseBoolean(conditionContext.getEnvironment() + .getProperty("spring.cloud.consul.discovery.enabled", "true")); + + isDiscoveryEnabled |= isConsulDiscoveryEnabled; + return isDiscoveryEnabled; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryAutoConfiguration.java index 34551aaf66ffb409939b419cb3de1e0883bd9a00..cc8000f44df0acaa3f79efe00a0cfde4dc0f8ae5 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryAutoConfiguration.java @@ -18,14 +18,8 @@ package com.tencent.cloud.polaris.discovery; -import com.tencent.cloud.polaris.PolarisDiscoveryProperties; import com.tencent.cloud.polaris.discovery.reactive.PolarisReactiveDiscoveryClientConfiguration; -import com.tencent.cloud.polaris.extend.consul.ConsulContextProperties; -import com.tencent.polaris.api.core.ConsumerAPI; -import com.tencent.polaris.api.core.ProviderAPI; -import com.tencent.polaris.api.exception.PolarisException; -import com.tencent.polaris.client.api.SDKContext; -import com.tencent.polaris.factory.api.DiscoveryAPIFactory; +import com.tencent.cloud.polaris.discovery.refresh.PolarisRefreshConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; @@ -39,37 +33,10 @@ import org.springframework.context.annotation.Import; */ @Configuration(proxyBeanMethods = false) @ConditionalOnPolarisDiscoveryEnabled -@Import({ PolarisDiscoveryClientConfiguration.class, - PolarisReactiveDiscoveryClientConfiguration.class, - ConsulContextProperties.class }) +@Import({PolarisDiscoveryClientConfiguration.class, + PolarisReactiveDiscoveryClientConfiguration.class, PolarisRefreshConfiguration.class}) public class PolarisDiscoveryAutoConfiguration { - @Bean - @ConditionalOnMissingBean - public PolarisDiscoveryProperties polarisDiscoveryProperties() { - return new PolarisDiscoveryProperties(); - } - - @Bean(name = "polarisProvider") - @ConditionalOnMissingBean - public ProviderAPI polarisProvider(SDKContext polarisContext) - throws PolarisException { - return DiscoveryAPIFactory.createProviderAPIByContext(polarisContext); - } - - @Bean(name = "polarisConsumer") - @ConditionalOnMissingBean - public ConsumerAPI polarisConsumer(SDKContext polarisContext) - throws PolarisException { - return DiscoveryAPIFactory.createConsumerAPIByContext(polarisContext); - } - - @Bean - @ConditionalOnMissingBean - public PolarisDiscoveryHandler polarisDiscoveryHandler() { - return new PolarisDiscoveryHandler(); - } - @Bean @ConditionalOnMissingBean public PolarisServiceDiscovery polarisServiceDiscovery( diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClient.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClient.java index 783ef4687e5460c20277c295ca536abebb7f1f24..f07469807184a40fb150beff08323fb7457cb80e 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClient.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClient.java @@ -32,7 +32,7 @@ public class PolarisDiscoveryClient implements DiscoveryClient { /** * Polaris Discovery Client Description. */ - public final String description = "Spring Cloud Polaris Discovery Client"; + public final String description = "Spring Cloud Tencent Polaris Discovery Client."; private final PolarisServiceDiscovery polarisServiceDiscovery; diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java index fd8b878e271d69e1ba254546acc800307aded6cf..a55443561c556e318212fca03828e4babfd5346e 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryHandler.java @@ -18,21 +18,15 @@ package com.tencent.cloud.polaris.discovery; -import java.util.Map; - -import com.tencent.cloud.common.constant.MetadataConstant.SystemMetadataKey; -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.metadata.MetadataContextHolder; import com.tencent.cloud.polaris.PolarisDiscoveryProperties; import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.polaris.api.core.ProviderAPI; -import com.tencent.polaris.api.pojo.ServiceInfo; import com.tencent.polaris.api.rpc.GetAllInstancesRequest; -import com.tencent.polaris.api.rpc.GetInstancesRequest; +import com.tencent.polaris.api.rpc.GetHealthyInstancesRequest; import com.tencent.polaris.api.rpc.GetServicesRequest; import com.tencent.polaris.api.rpc.InstancesResponse; import com.tencent.polaris.api.rpc.ServicesResponse; -import org.apache.commons.lang.StringUtils; +import com.tencent.polaris.client.api.SDKContext; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -51,35 +45,23 @@ public class PolarisDiscoveryHandler { @Autowired private ProviderAPI providerAPI; + @Autowired + private SDKContext sdkContext; + @Autowired private ConsumerAPI polarisConsumer; /** - * Get a list of instances after service routing. + * Get a list of healthy instances. * @param service service name - * @return list of instances + * @return list of healthy instances */ - public InstancesResponse getFilteredInstances(String service) { + public InstancesResponse getHealthyInstances(String service) { String namespace = polarisDiscoveryProperties.getNamespace(); - GetInstancesRequest getInstancesRequest = new GetInstancesRequest(); - getInstancesRequest.setNamespace(namespace); - getInstancesRequest.setService(service); - String method = MetadataContextHolder.get() - .getSystemMetadata(SystemMetadataKey.PEER_PATH); - getInstancesRequest.setMethod(method); - String localNamespace = MetadataContext.LOCAL_NAMESPACE; - String localService = MetadataContext.LOCAL_SERVICE; - Map allTransitiveCustomMetadata = MetadataContextHolder.get() - .getAllTransitiveCustomMetadata(); - if (StringUtils.isNotBlank(localNamespace) || StringUtils.isNotBlank(localService) - || null != allTransitiveCustomMetadata) { - ServiceInfo sourceService = new ServiceInfo(); - sourceService.setNamespace(localNamespace); - sourceService.setService(localService); - sourceService.setMetadata(allTransitiveCustomMetadata); - getInstancesRequest.setServiceInfo(sourceService); - } - return polarisConsumer.getInstances(getInstancesRequest); + GetHealthyInstancesRequest getHealthyInstancesRequest = new GetHealthyInstancesRequest(); + getHealthyInstancesRequest.setNamespace(namespace); + getHealthyInstancesRequest.setService(service); + return polarisConsumer.getHealthyInstances(getHealthyInstancesRequest); } /** @@ -99,6 +81,10 @@ public class PolarisDiscoveryHandler { return providerAPI; } + public SDKContext getSdkContext() { + return sdkContext; + } + /** * Return all service for given namespace. * @return service list diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java index 6f85be6c3f56cadf12634aea6ccfe8aecc6b4421..95387ae5cdc8edfbdc1b4348d8b7ea64a8fd1fa3 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscovery.java @@ -50,7 +50,7 @@ public class PolarisServiceDiscovery { public List getInstances(String serviceId) throws PolarisException { List instances = new ArrayList<>(); InstancesResponse filteredInstances = polarisDiscoveryHandler - .getFilteredInstances(serviceId); + .getHealthyInstances(serviceId); ServiceInstances serviceInstances = filteredInstances.toServiceInstances(); for (Instance instance : serviceInstances.getInstances()) { instances.add(new PolarisServiceInstance(instance)); diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClient.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClient.java index aa098ac0499742fd2c7e0c2aba000f881ccae732..6d3588c6f471f4ab58702f0b104627d9d273f7cc 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClient.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClient.java @@ -50,7 +50,7 @@ public class PolarisReactiveDiscoveryClient implements ReactiveDiscoveryClient { @Override public String description() { - return "Spring Cloud Polaris Reactive Discovery Client"; + return "Spring Cloud Tencent Polaris Reactive Discovery Client."; } @Override diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/refresh/PolarisRefreshApplicationReadyEventListener.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/refresh/PolarisRefreshApplicationReadyEventListener.java new file mode 100644 index 0000000000000000000000000000000000000000..53292e7cf1c9ebd76311bd432171e0e6cecadb1b --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/refresh/PolarisRefreshApplicationReadyEventListener.java @@ -0,0 +1,92 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.discovery.refresh; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; +import com.tencent.polaris.client.util.NamedThreadFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.cloud.client.discovery.event.HeartbeatEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.context.ApplicationListener; + +import static com.tencent.cloud.polaris.discovery.refresh.PolarisServiceStatusChangeListener.INDEX; + +/** + * Begin refresh when application is ready. + * + * @author Haotian Zhang + */ +public class PolarisRefreshApplicationReadyEventListener implements ApplicationListener, ApplicationEventPublisherAware, DisposableBean { + + private static final Logger LOG = LoggerFactory.getLogger(PolarisRefreshApplicationReadyEventListener.class); + private static final int DELAY = 60; + private final PolarisDiscoveryHandler polarisDiscoveryHandler; + private final PolarisServiceStatusChangeListener polarisServiceStatusChangeListener; + private final ScheduledExecutorService refreshExecutor; + private ApplicationEventPublisher publisher; + + public PolarisRefreshApplicationReadyEventListener(PolarisDiscoveryHandler polarisDiscoveryHandler, PolarisServiceStatusChangeListener polarisServiceStatusChangeListener) { + this.polarisDiscoveryHandler = polarisDiscoveryHandler; + this.polarisServiceStatusChangeListener = polarisServiceStatusChangeListener; + this.refreshExecutor = Executors.newSingleThreadScheduledExecutor( + new NamedThreadFactory("polaris-service-refresh")); + } + + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + // Register service change listener. + polarisDiscoveryHandler.getSdkContext().getExtensions().getLocalRegistry() + .registerResourceListener(polarisServiceStatusChangeListener); + + // Begin scheduled refresh thread. + refresh(); + } + + /** + * Start the refresh thread. + */ + public void refresh() { + refreshExecutor.scheduleWithFixedDelay(() -> { + try { + // Trigger reload of gateway route cache. + this.publisher.publishEvent(new HeartbeatEvent(this, INDEX.getAndIncrement())); + } + catch (Exception e) { + LOG.error("refresh polaris service error.", e); + } + }, DELAY, DELAY, TimeUnit.SECONDS); + } + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.publisher = applicationEventPublisher; + } + + @Override + public void destroy() throws Exception { + refreshExecutor.shutdown(); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/refresh/PolarisRefreshConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/refresh/PolarisRefreshConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..acbe54b7a8381114e722b526ebb749de0fcf1c5d --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/refresh/PolarisRefreshConfiguration.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.discovery.refresh; + +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configuration for listening the change of service status. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnPolarisEnabled +public class PolarisRefreshConfiguration { + + @Bean + @ConditionalOnMissingBean + public PolarisServiceStatusChangeListener polarisServiceChangeListener() { + return new PolarisServiceStatusChangeListener(); + } + + @Bean + @ConditionalOnMissingBean + public PolarisRefreshApplicationReadyEventListener polarisServiceStatusApplicationReadyEventListener( + PolarisDiscoveryHandler polarisDiscoveryHandler, + PolarisServiceStatusChangeListener polarisServiceStatusChangeListener) { + return new PolarisRefreshApplicationReadyEventListener(polarisDiscoveryHandler, polarisServiceStatusChangeListener); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/refresh/PolarisServiceStatusChangeListener.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/refresh/PolarisServiceStatusChangeListener.java new file mode 100644 index 0000000000000000000000000000000000000000..c923295d5d651a1a44091f0a4827e186ac095aa0 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/discovery/refresh/PolarisServiceStatusChangeListener.java @@ -0,0 +1,96 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.discovery.refresh; + +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +import com.google.common.collect.Sets; +import com.tencent.polaris.api.plugin.registry.AbstractResourceEventListener; +import com.tencent.polaris.api.pojo.RegistryCacheValue; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.client.pojo.ServiceInstancesByProto; +import com.tencent.polaris.client.pojo.ServicesByProto; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.client.discovery.event.HeartbeatEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; +import org.springframework.util.CollectionUtils; + +/** + * Change listener of Polaris service info. When service info is created or deleted, or, instance of service is from 0 to + * + * @author Haotian Zhang + */ +public class PolarisServiceStatusChangeListener extends AbstractResourceEventListener implements ApplicationEventPublisherAware { + + /** + * Index of service info status. + */ + public static final AtomicLong INDEX = new AtomicLong(0); + + private static final Logger LOG = LoggerFactory.getLogger(PolarisServiceStatusChangeListener.class); + + private ApplicationEventPublisher publisher; + + @Override + public void onResourceUpdated(ServiceEventKey svcEventKey, RegistryCacheValue oldValue, + RegistryCacheValue newValue) { + if (newValue.getEventType() == ServiceEventKey.EventType.SERVICE) { + if (oldValue instanceof ServicesByProto && newValue instanceof ServicesByProto) { + LOG.debug("receive service={} change event", svcEventKey); + Set oldServiceInfoSet = ((ServicesByProto) oldValue).getServices().stream() + .map(i -> i.getNamespace() + "::" + i.getService()).collect(Collectors.toSet()); + Set newServiceInfoSet = ((ServicesByProto) newValue).getServices().stream() + .map(i -> i.getNamespace() + "::" + i.getService()).collect(Collectors.toSet()); + + Sets.SetView addServiceInfoSetView = Sets.difference(newServiceInfoSet, oldServiceInfoSet); + Sets.SetView deleteServiceInfoSetView = Sets.difference(oldServiceInfoSet, newServiceInfoSet); + + if (addServiceInfoSetView.isEmpty() && deleteServiceInfoSetView.isEmpty()) { + return; + } + LOG.info("Service status is update. Add service of {}. Delete service of {}", addServiceInfoSetView, deleteServiceInfoSetView); + + // Trigger reload of gateway route cache. + this.publisher.publishEvent(new HeartbeatEvent(this, INDEX.getAndIncrement())); + } + } + else if (newValue.getEventType() == ServiceEventKey.EventType.INSTANCE) { + if (oldValue instanceof ServiceInstancesByProto && newValue instanceof ServiceInstancesByProto) { + LOG.debug("receive service instances={} change event", svcEventKey); + ServiceInstancesByProto oldIns = (ServiceInstancesByProto) oldValue; + ServiceInstancesByProto newIns = (ServiceInstancesByProto) newValue; + if ((CollectionUtils.isEmpty(oldIns.getInstances()) && !CollectionUtils.isEmpty(newIns.getInstances())) || + (!CollectionUtils.isEmpty(oldIns.getInstances()) && CollectionUtils.isEmpty(newIns.getInstances()))) { + LOG.info("Service status of {} is update.", newIns.getService()); + + // Trigger reload of gateway route cache. + this.publisher.publishEvent(new HeartbeatEvent(this, INDEX.getAndIncrement())); + } + } + } + } + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.publisher = applicationEventPublisher; + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/endpoint/PolarisDiscoveryEndPoint.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/endpoint/PolarisDiscoveryEndPoint.java new file mode 100644 index 0000000000000000000000000000000000000000..5e006ac009cbfc5374605d25140c325e6aa35ce8 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/endpoint/PolarisDiscoveryEndPoint.java @@ -0,0 +1,82 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.endpoint; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.tencent.cloud.polaris.PolarisDiscoveryProperties; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.rpc.InstancesResponse; +import org.apache.commons.lang.StringUtils; + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.actuate.endpoint.annotation.Selector; +import org.springframework.cloud.client.discovery.DiscoveryClient; + +/** + * Endpoint of polaris discovery, include discovery properties and service instance. + * + * @author shuiqingliu + */ +@Endpoint(id = "polaris-discovery") +public class PolarisDiscoveryEndPoint { + + private final PolarisDiscoveryProperties polarisDiscoveryProperties; + private final DiscoveryClient polarisDiscoveryClient; + private final PolarisDiscoveryHandler polarisDiscoveryHandler; + + public PolarisDiscoveryEndPoint(PolarisDiscoveryProperties polarisDiscoveryProperties, DiscoveryClient polarisDiscoveryClient, PolarisDiscoveryHandler polarisDiscoveryHandler) { + this.polarisDiscoveryProperties = polarisDiscoveryProperties; + this.polarisDiscoveryClient = polarisDiscoveryClient; + this.polarisDiscoveryHandler = polarisDiscoveryHandler; + } + + @ReadOperation + public Map polarisDiscovery(@Selector String serviceId) { + Map polarisDisConveryInfo = new HashMap<>(); + polarisDisConveryInfo.put("PolarisDiscoveryProperties", polarisDiscoveryProperties); + + List serviceInstancesInfoList = new ArrayList<>(); + + if (StringUtils.isNotEmpty(serviceId)) { + ServiceInstances serviceInstances = getServiceInstances(serviceId); + serviceInstancesInfoList.add(serviceInstances); + polarisDisConveryInfo.put("ServiceInstances", serviceInstancesInfoList); + return polarisDisConveryInfo; + } + + for (String service : polarisDiscoveryClient.getServices()) { + ServiceInstances serviceInstances = getServiceInstances(service); + serviceInstancesInfoList.add(serviceInstances); + } + + polarisDisConveryInfo.put("ServiceInstances", serviceInstancesInfoList); + return polarisDisConveryInfo; + } + + private ServiceInstances getServiceInstances(String serviceId) { + InstancesResponse instancesResponse = polarisDiscoveryHandler.getHealthyInstances(serviceId); + ServiceInstances serviceInstances = instancesResponse.toServiceInstances(); + return serviceInstances; + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/endpoint/PolarisDiscoveryEndpointAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/endpoint/PolarisDiscoveryEndpointAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..d6b5976349813602b6c9efa3019b1f800c37ad95 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/endpoint/PolarisDiscoveryEndpointAutoConfiguration.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.endpoint; + +import com.tencent.cloud.polaris.PolarisDiscoveryProperties; +import com.tencent.cloud.polaris.discovery.ConditionalOnPolarisDiscoveryEnabled; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; + +import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * The AutoConfiguration for Polaris Discovery's Endpoint. + * + * @author shuiqingliu + **/ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(Endpoint.class) +@ConditionalOnPolarisDiscoveryEnabled +public class PolarisDiscoveryEndpointAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + @ConditionalOnAvailableEndpoint + public PolarisDiscoveryEndPoint polarisDiscoveryEndPoint(PolarisDiscoveryProperties polarisDiscoveryProperties, + DiscoveryClient discoveryClient, PolarisDiscoveryHandler polarisDiscoveryHandler) { + return new PolarisDiscoveryEndPoint(polarisDiscoveryProperties, discoveryClient, polarisDiscoveryHandler); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/extend/consul/ConsulContextProperties.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/extend/consul/ConsulContextProperties.java index e9b0e7fcbe21ed935360233595cf1b9e00e663ba..2efbd93d671112e73e743376f1cfb68d5e80d89f 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/extend/consul/ConsulContextProperties.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/extend/consul/ConsulContextProperties.java @@ -25,13 +25,14 @@ import com.tencent.cloud.common.constant.ContextConstant.ModifierOrder; import com.tencent.cloud.polaris.context.PolarisConfigModifier; import com.tencent.polaris.api.config.plugin.DefaultPlugins; import com.tencent.polaris.factory.config.ConfigurationImpl; +import com.tencent.polaris.factory.config.consumer.DiscoveryConfigImpl; import com.tencent.polaris.factory.config.global.ServerConnectorConfigImpl; +import com.tencent.polaris.factory.config.provider.RegisterConfigImpl; import com.tencent.polaris.plugins.connector.common.constant.ConsulConstant.MetadataMapKey; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -42,8 +43,6 @@ import org.springframework.util.CollectionUtils; * * @author Haotian Zhang */ -@ConditionalOnExpression("'true'.equals('${spring.cloud.consul.enabled:true}')" - + " && 'true'.equals('${spring.cloud.consul.discovery.enabled:true}')") @ConfigurationProperties("spring.cloud.consul") public class ConsulContextProperties { @@ -54,7 +53,7 @@ public class ConsulContextProperties { private int port; - private boolean enabled; + private boolean enabled = false; @Value("${spring.cloud.consul.discovery.register:#{'true'}}") private boolean register; @@ -74,18 +73,38 @@ public class ConsulContextProperties { @Value("${spring.cloud.consul.discovery.prefer-ip-address:#{'false'}}") private boolean preferIpAddress; + 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 boolean isEnabled() { + return enabled; + } + public void setEnabled(boolean enabled) { this.enabled = enabled; } + public boolean isRegister() { + return register; + } + + public boolean isDiscoveryEnabled() { + return discoveryEnabled; + } + @Bean @ConditionalOnMissingBean public ConsulConfigModifier consulConfigModifier() { @@ -94,43 +113,50 @@ public class ConsulContextProperties { private static class ConsulConfigModifier implements PolarisConfigModifier { + private final String ID = "consul"; + @Autowired(required = false) private ConsulContextProperties consulContextProperties; @Override public void modify(ConfigurationImpl configuration) { - if (consulContextProperties != null && consulContextProperties.enabled - && consulContextProperties.discoveryEnabled - && consulContextProperties.register) { - if (CollectionUtils - .isEmpty(configuration.getGlobal().getServerConnectors())) { + if (consulContextProperties != null && consulContextProperties.enabled) { + if (CollectionUtils.isEmpty(configuration.getGlobal().getServerConnectors())) { configuration.getGlobal().setServerConnectors(new ArrayList<>()); } - configuration.getGlobal().getServerConnectors() - .add(configuration.getGlobal().getServerConnector()); + if (CollectionUtils.isEmpty(configuration.getGlobal().getServerConnectors()) + && null != configuration.getGlobal().getServerConnector()) { + configuration.getGlobal().getServerConnectors().add(configuration.getGlobal().getServerConnector()); + } ServerConnectorConfigImpl serverConnectorConfig = new ServerConnectorConfigImpl(); - serverConnectorConfig.setAddresses( - Collections.singletonList(consulContextProperties.host + ":" - + consulContextProperties.port)); + serverConnectorConfig.setId(ID); + serverConnectorConfig.setAddresses(Collections.singletonList(consulContextProperties.host + ":" + + consulContextProperties.port)); serverConnectorConfig.setProtocol(DefaultPlugins.SERVER_CONNECTOR_CONSUL); Map metadata = serverConnectorConfig.getMetadata(); if (StringUtils.isNotBlank(consulContextProperties.serviceName)) { - metadata.put(MetadataMapKey.SERVICE_NAME_KEY, - consulContextProperties.serviceName); + metadata.put(MetadataMapKey.SERVICE_NAME_KEY, consulContextProperties.serviceName); } if (StringUtils.isNotBlank(consulContextProperties.instanceId)) { - metadata.put(MetadataMapKey.INSTANCE_ID_KEY, - consulContextProperties.instanceId); + metadata.put(MetadataMapKey.INSTANCE_ID_KEY, consulContextProperties.instanceId); } if (consulContextProperties.preferIpAddress && StringUtils.isNotBlank(consulContextProperties.ipAddress)) { metadata.put(MetadataMapKey.PREFER_IP_ADDRESS_KEY, String.valueOf(consulContextProperties.preferIpAddress)); - metadata.put(MetadataMapKey.IP_ADDRESS_KEY, - consulContextProperties.ipAddress); + metadata.put(MetadataMapKey.IP_ADDRESS_KEY, consulContextProperties.ipAddress); } - configuration.getGlobal().getServerConnectors() - .add(serverConnectorConfig); + configuration.getGlobal().getServerConnectors().add(serverConnectorConfig); + + DiscoveryConfigImpl discoveryConfig = new DiscoveryConfigImpl(); + discoveryConfig.setServerConnectorId(ID); + discoveryConfig.setEnable(consulContextProperties.discoveryEnabled); + configuration.getConsumer().getDiscoveries().add(discoveryConfig); + + RegisterConfigImpl registerConfig = new RegisterConfigImpl(); + registerConfig.setServerConnectorId(ID); + registerConfig.setEnable(consulContextProperties.register); + configuration.getProvider().getRegisters().add(registerConfig); } } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/ConditionalOnPolarisRegisterEnabled.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/ConditionalOnPolarisRegisterEnabled.java new file mode 100644 index 0000000000000000000000000000000000000000..440312cab66084a118247d44f0e1f9d46718f11d --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/ConditionalOnPolarisRegisterEnabled.java @@ -0,0 +1,38 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.registry; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; + +import org.springframework.context.annotation.Conditional; + +/** + * @author Haotian Zhang, Andrew Shan, Jie Cheng + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@ConditionalOnPolarisEnabled +@Conditional(RegisterEnabledCondition.class) +public @interface ConditionalOnPolarisRegisterEnabled { + +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisAutoServiceRegistration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisAutoServiceRegistration.java index 727b38a58b505a296dd9711567cabfa8b4c33c79..bfba996f7b51d5cd3d990543db936fd5db1b02fe 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisAutoServiceRegistration.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisAutoServiceRegistration.java @@ -61,7 +61,7 @@ public class PolarisAutoServiceRegistration @Override protected void register() { - if (!this.registration.getPolarisProperties().isRegisterEnabled()) { + if (!this.registration.isRegisterEnabled()) { log.debug("Registration disabled."); return; } @@ -73,7 +73,7 @@ public class PolarisAutoServiceRegistration @Override protected void registerManagement() { - if (!this.registration.getPolarisProperties().isRegisterEnabled()) { + if (!this.registration.isRegisterEnabled()) { return; } super.registerManagement(); @@ -87,7 +87,7 @@ public class PolarisAutoServiceRegistration @Override protected boolean isEnabled() { - return this.registration.getPolarisProperties().isRegisterEnabled(); + return this.registration.isRegisterEnabled(); } @Override diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java index 38fedcb0b22ccefab5c7cc0fce0e17d7d5453ab6..558a6b5bf19f098ec04b7ed3e3c1ebc7a6fd370c 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisRegistration.java @@ -19,9 +19,12 @@ package com.tencent.cloud.polaris.registry; import java.net.URI; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; -import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.StaticMetadataManager; +import com.tencent.cloud.polaris.DiscoveryPropertiesAutoConfiguration; import com.tencent.cloud.polaris.PolarisDiscoveryProperties; import com.tencent.polaris.client.api.SDKContext; import org.apache.commons.lang.StringUtils; @@ -29,6 +32,7 @@ import org.apache.commons.lang.StringUtils; import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.util.CollectionUtils; /** * Registration object of Polaris. @@ -37,14 +41,31 @@ import org.springframework.cloud.client.serviceregistry.Registration; */ public class PolarisRegistration implements Registration, ServiceInstance { + private final static String METADATA_KEY_IP = "internal-ip"; + private final static String METADATA_KEY_ADDRESS = "internal-address"; + + private final DiscoveryPropertiesAutoConfiguration discoveryPropertiesAutoConfiguration; + private final PolarisDiscoveryProperties polarisDiscoveryProperties; private final SDKContext polarisContext; - public PolarisRegistration(PolarisDiscoveryProperties polarisDiscoveryProperties, - SDKContext context) { + private final StaticMetadataManager staticMetadataManager; + + private Map metadata; + + private final String host; + + public PolarisRegistration( + DiscoveryPropertiesAutoConfiguration discoveryPropertiesAutoConfiguration, + PolarisDiscoveryProperties polarisDiscoveryProperties, SDKContext context, + StaticMetadataManager staticMetadataManager) { + this.discoveryPropertiesAutoConfiguration = discoveryPropertiesAutoConfiguration; this.polarisDiscoveryProperties = polarisDiscoveryProperties; this.polarisContext = context; + this.staticMetadataManager = staticMetadataManager; + + host = polarisContext.getConfig().getGlobal().getAPI().getBindIP(); } @Override @@ -54,7 +75,7 @@ public class PolarisRegistration implements Registration, ServiceInstance { @Override public String getHost() { - return polarisContext.getConfig().getGlobal().getAPI().getBindIP(); + return host; } @Override @@ -79,17 +100,39 @@ public class PolarisRegistration implements Registration, ServiceInstance { @Override public Map getMetadata() { - return MetadataContextHolder.get().getAllSystemMetadata(); + if (CollectionUtils.isEmpty(metadata)) { + Map instanceMetadata = new HashMap<>(); + + // put internal metadata + instanceMetadata.put(METADATA_KEY_IP, host); + instanceMetadata.put(METADATA_KEY_ADDRESS, host + ":" + polarisDiscoveryProperties.getPort()); + + instanceMetadata.putAll(staticMetadataManager.getMergedStaticMetadata()); + + // location info will be putted both in metadata and instance's field + instanceMetadata.putAll(staticMetadataManager.getLocationMetadata()); + + this.metadata = Collections.unmodifiableMap(instanceMetadata); + } + return metadata; } public PolarisDiscoveryProperties getPolarisProperties() { return polarisDiscoveryProperties; } + public boolean isRegisterEnabled() { + return discoveryPropertiesAutoConfiguration.isRegisterEnabled(); + } + @Override public String toString() { - return "PolarisRegistration{" + "polarisDiscoveryProperties=" - + polarisDiscoveryProperties + ", polarisContext=" + polarisContext + '}'; + return "PolarisRegistration{" + + "discoveryPropertiesAutoConfiguration=" + discoveryPropertiesAutoConfiguration + + ", polarisDiscoveryProperties=" + polarisDiscoveryProperties + + ", polarisContext=" + polarisContext + + ", staticMetadataManager=" + staticMetadataManager + + ", metadata=" + metadata + + '}'; } - } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java index 7e72a075dbc48bb499d6c5911c25f9945ae7675b..b68b2b10f1380c177387a88b5e1b708772546a3c 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistry.java @@ -18,11 +18,11 @@ package com.tencent.cloud.polaris.registry; +import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.metadata.StaticMetadataManager; import com.tencent.cloud.polaris.PolarisDiscoveryProperties; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; import com.tencent.cloud.polaris.util.OkHttpUtil; @@ -34,14 +34,13 @@ import com.tencent.polaris.api.rpc.InstanceHeartbeatRequest; import com.tencent.polaris.api.rpc.InstanceRegisterRequest; import com.tencent.polaris.api.rpc.InstancesResponse; import com.tencent.polaris.client.util.NamedThreadFactory; -import org.apache.logging.log4j.util.Strings; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import org.springframework.cloud.client.serviceregistry.Registration; import org.springframework.cloud.client.serviceregistry.ServiceRegistry; -import org.springframework.util.StringUtils; import static org.springframework.util.ReflectionUtils.rethrowRuntimeException; @@ -61,21 +60,20 @@ public class PolarisServiceRegistry implements ServiceRegistry { private final PolarisDiscoveryHandler polarisDiscoveryHandler; - private final MetadataLocalProperties metadataLocalProperties; + private final StaticMetadataManager staticMetadataManager; private final ScheduledExecutorService heartbeatExecutor; public PolarisServiceRegistry(PolarisDiscoveryProperties polarisDiscoveryProperties, PolarisDiscoveryHandler polarisDiscoveryHandler, - MetadataLocalProperties metadataLocalProperties) { + StaticMetadataManager staticMetadataManager) { this.polarisDiscoveryProperties = polarisDiscoveryProperties; this.polarisDiscoveryHandler = polarisDiscoveryHandler; - this.metadataLocalProperties = metadataLocalProperties; + this.staticMetadataManager = staticMetadataManager; + if (polarisDiscoveryProperties.isHeartbeatEnabled()) { - ScheduledThreadPoolExecutor heartbeatExecutor = new ScheduledThreadPoolExecutor( - 0, new NamedThreadFactory("spring-cloud-heartbeat")); - heartbeatExecutor.setMaximumPoolSize(1); - this.heartbeatExecutor = heartbeatExecutor; + this.heartbeatExecutor = Executors.newSingleThreadScheduledExecutor( + new NamedThreadFactory("spring-cloud-heartbeat")); } else { this.heartbeatExecutor = null; @@ -95,11 +93,15 @@ public class PolarisServiceRegistry implements ServiceRegistry { instanceRegisterRequest.setService(registration.getServiceId()); instanceRegisterRequest.setHost(registration.getHost()); instanceRegisterRequest.setPort(registration.getPort()); + instanceRegisterRequest.setWeight(polarisDiscoveryProperties.getWeight()); instanceRegisterRequest.setToken(polarisDiscoveryProperties.getToken()); + instanceRegisterRequest.setRegion(staticMetadataManager.getRegion()); + instanceRegisterRequest.setZone(staticMetadataManager.getZone()); + instanceRegisterRequest.setCampus(staticMetadataManager.getCampus()); if (null != heartbeatExecutor) { instanceRegisterRequest.setTtl(ttl); } - instanceRegisterRequest.setMetadata(metadataLocalProperties.getContent()); + instanceRegisterRequest.setMetadata(registration.getMetadata()); instanceRegisterRequest.setProtocol(polarisDiscoveryProperties.getProtocol()); instanceRegisterRequest.setVersion(polarisDiscoveryProperties.getVersion()); try { @@ -108,7 +110,7 @@ public class PolarisServiceRegistry implements ServiceRegistry { log.info("polaris registry, {} {} {}:{} {} register finished", polarisDiscoveryProperties.getNamespace(), registration.getServiceId(), registration.getHost(), - registration.getPort(), metadataLocalProperties.getContent()); + registration.getPort(), staticMetadataManager.getMergedStaticMetadata()); if (null != heartbeatExecutor) { InstanceHeartbeatRequest heartbeatRequest = new InstanceHeartbeatRequest(); @@ -199,7 +201,7 @@ public class PolarisServiceRegistry implements ServiceRegistry { // first. // If the health check passes, the heartbeat will be reported. // If it does not pass, the heartbeat will not be reported. - if (Strings.isNotEmpty(healthCheckEndpoint)) { + if (StringUtils.isNotBlank(healthCheckEndpoint)) { if (!healthCheckEndpoint.startsWith("/")) { healthCheckEndpoint = "/" + healthCheckEndpoint; } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryAutoConfiguration.java index 7a07bae39f6bc5a07f458370f76a330c60f382db..5eadee573c80c865165d8cc5668bb1a7d9614fea 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryAutoConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryAutoConfiguration.java @@ -18,9 +18,9 @@ package com.tencent.cloud.polaris.registry; -import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.metadata.StaticMetadataManager; +import com.tencent.cloud.polaris.DiscoveryPropertiesAutoConfiguration; import com.tencent.cloud.polaris.PolarisDiscoveryProperties; -import com.tencent.cloud.polaris.discovery.ConditionalOnPolarisDiscoveryEnabled; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; import com.tencent.polaris.client.api.SDKContext; @@ -42,28 +42,29 @@ import org.springframework.context.annotation.Configuration; */ @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties -@ConditionalOnPolarisDiscoveryEnabled +@ConditionalOnPolarisRegisterEnabled @ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true) -@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class, +@AutoConfigureAfter({AutoServiceRegistrationConfiguration.class, AutoServiceRegistrationAutoConfiguration.class, - PolarisDiscoveryAutoConfiguration.class }) + PolarisDiscoveryAutoConfiguration.class}) public class PolarisServiceRegistryAutoConfiguration { @Bean public PolarisServiceRegistry polarisServiceRegistry( - PolarisDiscoveryProperties polarisDiscoveryProperties, - PolarisDiscoveryHandler polarisDiscoveryHandler, - MetadataLocalProperties metadataLocalProperties) { - return new PolarisServiceRegistry(polarisDiscoveryProperties, - polarisDiscoveryHandler, metadataLocalProperties); + PolarisDiscoveryProperties polarisDiscoveryProperties, PolarisDiscoveryHandler polarisDiscoveryHandler, + StaticMetadataManager staticMetadataManager) { + return new PolarisServiceRegistry(polarisDiscoveryProperties, polarisDiscoveryHandler, staticMetadataManager); } @Bean @ConditionalOnBean(AutoServiceRegistrationProperties.class) public PolarisRegistration polarisRegistration( - PolarisDiscoveryProperties polarisDiscoveryProperties, SDKContext context) { - return new PolarisRegistration(polarisDiscoveryProperties, context); + DiscoveryPropertiesAutoConfiguration discoveryPropertiesAutoConfiguration, + PolarisDiscoveryProperties polarisDiscoveryProperties, SDKContext context, + StaticMetadataManager staticMetadataManager) { + return new PolarisRegistration(discoveryPropertiesAutoConfiguration, + polarisDiscoveryProperties, context, staticMetadataManager); } @Bean @@ -75,5 +76,4 @@ public class PolarisServiceRegistryAutoConfiguration { return new PolarisAutoServiceRegistration(registry, autoServiceRegistrationProperties, registration); } - } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/RegisterEnabledCondition.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/RegisterEnabledCondition.java new file mode 100644 index 0000000000000000000000000000000000000000..c7820fb6a41a3fb701ceda6eab9e178e48f1e63d --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/registry/RegisterEnabledCondition.java @@ -0,0 +1,47 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.registry; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +/** + * Condition for checking if register enabled. + * + * @author Haotian Zhang + */ +public class RegisterEnabledCondition implements Condition { + + @Override + public boolean matches(ConditionContext conditionContext, + AnnotatedTypeMetadata annotatedTypeMetadata) { + boolean isRegisterEnabled = Boolean.parseBoolean(conditionContext.getEnvironment() + .getProperty("spring.cloud.polaris.discovery.register", "true")); + + boolean isConsulRegisterEnabled = Boolean + .parseBoolean(conditionContext.getEnvironment() + .getProperty("spring.cloud.consul.enabled", "false")) + && Boolean.parseBoolean(conditionContext.getEnvironment() + .getProperty("spring.cloud.consul.discovery.register", "true")); + + isRegisterEnabled |= isConsulRegisterEnabled; + return isRegisterEnabled; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/ribbon/PolarisServerList.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/ribbon/PolarisServerList.java index 77698eea1144930059131525930bdff0b3fa9888..71833fde98f8f7daead0ddfd27f608391482aa67 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/ribbon/PolarisServerList.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/ribbon/PolarisServerList.java @@ -13,6 +13,7 @@ * 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.tencent.cloud.polaris.ribbon; @@ -55,9 +56,9 @@ public class PolarisServerList extends AbstractServerList { } private List getServers() { - InstancesResponse filteredInstances = polarisDiscoveryHandler - .getFilteredInstances(serviceId); - ServiceInstances serviceInstances = filteredInstances.toServiceInstances(); + InstancesResponse allInstances = polarisDiscoveryHandler + .getHealthyInstances(serviceId); + ServiceInstances serviceInstances = allInstances.toServiceInstances(); List polarisServers = new ArrayList<>(); for (Instance instance : serviceInstances.getInstances()) { polarisServers.add(new PolarisServer(serviceInstances, instance)); diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/util/OkHttpUtil.java b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/util/OkHttpUtil.java index 3669fe3fea77e148e3a9b81e316163f6b32ffb8c..2a1ba0dc41fb4054b3040ab6809223fbd876a548 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/util/OkHttpUtil.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/java/com/tencent/cloud/polaris/util/OkHttpUtil.java @@ -20,7 +20,6 @@ package com.tencent.cloud.polaris.util; import java.util.Map; import java.util.Objects; -import com.squareup.okhttp.MediaType; import com.squareup.okhttp.OkHttpClient; import com.squareup.okhttp.Request; import com.squareup.okhttp.Response; @@ -39,12 +38,6 @@ public final class OkHttpUtil { */ public final static Logger logger = LoggerFactory.getLogger(OkHttpUtil.class); - /** - * JSON format. - */ - public static final MediaType MEDIA_TYPE_JSON = MediaType - .parse("application/json; charset=utf-8"); - /** * client. */ diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 89021b3d245d223ad92a1cdb06c2fdf97da25d89..7743b5f6ca8d53908d0eb2a816581cc5c0a1a718 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -18,6 +18,12 @@ "defaultValue": true, "description": "enable polaris discovery or not." }, + { + "name": "spring.cloud.polaris.discovery.register", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "enable polaris registration or not." + }, { "name": "spring.cloud.polaris.discovery.health-check-url", "type": "java.lang.String", @@ -50,9 +56,15 @@ }, { "name": "spring.cloud.polaris.weight", - "type": "java.lang.String", + "type": "java.lang.Integer", "defaultValue": 100, "description": "the weight of polaris instance , use to load-balance." + }, + { + "name": "spring.cloud.polaris.discovery.service-list-refresh-interval", + "type": "java.lang.Long", + "defaultValue": 60000, + "description": "Millis interval of refresh of service info list. Default: 60000." } ] } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories index a9cafd058aa32eb4fb7f609fe606996b77e6a029..e8deb25e8c7e562f5a23adc9c74ae224247d25c3 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-discovery/src/main/resources/META-INF/spring.factories @@ -1,4 +1,8 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.tencent.cloud.polaris.DiscoveryPropertiesAutoConfiguration,\ com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration,\ com.tencent.cloud.polaris.ribbon.PolarisDiscoveryRibbonAutoConfiguration,\ - com.tencent.cloud.polaris.registry.PolarisServiceRegistryAutoConfiguration + com.tencent.cloud.polaris.registry.PolarisServiceRegistryAutoConfiguration,\ + com.tencent.cloud.polaris.endpoint.PolarisDiscoveryEndpointAutoConfiguration +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ + com.tencent.cloud.polaris.DiscoveryPropertiesBootstrapAutoConfiguration diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/DiscoveryPropertiesAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/DiscoveryPropertiesAutoConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8f89a83137631ebbede2d5e10067263617022e5f --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/DiscoveryPropertiesAutoConfigurationTest.java @@ -0,0 +1,90 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris; + +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; +import com.tencent.cloud.polaris.extend.consul.ConsulContextProperties; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.core.ProviderAPI; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link DiscoveryPropertiesAutoConfiguration}. + * + * @author Haotian Zhang + */ +public class DiscoveryPropertiesAutoConfigurationTest { + + @Test + public void testDefaultInitialization() { + ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(PolarisContextAutoConfiguration.class, + DiscoveryPropertiesAutoConfiguration.class)); + applicationContextRunner.run(context -> { + assertThat(context).hasSingleBean(DiscoveryPropertiesAutoConfiguration.class); + assertThat(context).hasSingleBean(PolarisDiscoveryProperties.class); + assertThat(context).hasSingleBean(ConsulContextProperties.class); + assertThat(context).hasSingleBean(ProviderAPI.class); + assertThat(context).hasSingleBean(ConsumerAPI.class); + assertThat(context).hasSingleBean(PolarisDiscoveryHandler.class); + assertThat(context).hasSingleBean(DiscoveryConfigModifier.class); + }); + } + + @Test + public void testInit() { + ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(PolarisContextAutoConfiguration.class, + TestConfiguration.class, + DiscoveryPropertiesAutoConfiguration.class)) + .withPropertyValues("spring.cloud.polaris.discovery.register=false") + .withPropertyValues("spring.cloud.consul.discovery.register=false") + .withPropertyValues("spring.cloud.consul.discovery.enabled=false"); + applicationContextRunner.run(context -> { + assertThat(context).hasSingleBean(DiscoveryPropertiesAutoConfiguration.class); + DiscoveryPropertiesAutoConfiguration discoveryPropertiesAutoConfiguration = context.getBean(DiscoveryPropertiesAutoConfiguration.class); + assertThat(discoveryPropertiesAutoConfiguration.isRegisterEnabled()).isFalse(); + assertThat(discoveryPropertiesAutoConfiguration.isDiscoveryEnabled()).isFalse(); + }); + } + + @Configuration + static class TestConfiguration { + @Bean + public PolarisDiscoveryProperties polarisDiscoveryProperties() { + PolarisDiscoveryProperties polarisDiscoveryProperties = new PolarisDiscoveryProperties(); + polarisDiscoveryProperties.setEnabled(false); + return polarisDiscoveryProperties; + } + + @Bean + public ConsulContextProperties consulContextProperties() { + ConsulContextProperties consulContextProperties = new ConsulContextProperties(); + consulContextProperties.setEnabled(true); + return consulContextProperties; + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/DiscoveryPropertiesBootstrapAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/DiscoveryPropertiesBootstrapAutoConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3ef145d0ef19a2cc17342ca7181d2678c5b93a07 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/DiscoveryPropertiesBootstrapAutoConfigurationTest.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris; + +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link DiscoveryPropertiesBootstrapAutoConfiguration}. + * + * @author Haotian Zhang + */ +public class DiscoveryPropertiesBootstrapAutoConfigurationTest { + + @Test + public void testDefaultInitialization() { + ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(PolarisContextAutoConfiguration.class, + DiscoveryPropertiesBootstrapAutoConfiguration.class)) + .withPropertyValues("spring.cloud.polaris.enabled=true"); + applicationContextRunner.run(context -> { + assertThat(context).hasSingleBean(DiscoveryPropertiesBootstrapAutoConfiguration.class); + assertThat(context).hasSingleBean(DiscoveryPropertiesAutoConfiguration.class); + }); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/PolarisDiscoveryPropertiesTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/PolarisDiscoveryPropertiesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..390c6ea24b5abb4ddfb098b738f5a9049534dcc2 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/PolarisDiscoveryPropertiesTest.java @@ -0,0 +1,102 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris; + +import org.junit.Test; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.PORT; +import static com.tencent.polaris.test.common.Consts.PROVIDER_TOKEN; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisDiscoveryProperties}. + * + * @author Haotian Zhang + */ +public class PolarisDiscoveryPropertiesTest { + + @Test + public void testGetAndSet() { + PolarisDiscoveryProperties polarisDiscoveryProperties = new PolarisDiscoveryProperties(); + + // HeartbeatEnabled + polarisDiscoveryProperties.setHeartbeatEnabled(true); + assertThat(polarisDiscoveryProperties.isHeartbeatEnabled()).isTrue(); + + // Namespace + polarisDiscoveryProperties.setNamespace(NAMESPACE_TEST); + assertThat(polarisDiscoveryProperties.getNamespace()).isEqualTo(NAMESPACE_TEST); + + // Weight + polarisDiscoveryProperties.setWeight(10); + assertThat(polarisDiscoveryProperties.getWeight()).isEqualTo(10); + + // Service + polarisDiscoveryProperties.setService(SERVICE_PROVIDER); + assertThat(polarisDiscoveryProperties.getService()).isEqualTo(SERVICE_PROVIDER); + + // Enabled + polarisDiscoveryProperties.setEnabled(true); + assertThat(polarisDiscoveryProperties.isEnabled()).isTrue(); + + // RegisterEnabled + polarisDiscoveryProperties.setRegisterEnabled(true); + assertThat(polarisDiscoveryProperties.isRegisterEnabled()).isTrue(); + + // Token + polarisDiscoveryProperties.setToken(PROVIDER_TOKEN); + assertThat(polarisDiscoveryProperties.getToken()).isEqualTo(PROVIDER_TOKEN); + + // Version + polarisDiscoveryProperties.setVersion("1.0.0"); + assertThat(polarisDiscoveryProperties.getVersion()).isEqualTo("1.0.0"); + + // HTTP + polarisDiscoveryProperties.setProtocol("HTTP"); + assertThat(polarisDiscoveryProperties.getProtocol()).isEqualTo("HTTP"); + + // Port + polarisDiscoveryProperties.setPort(PORT); + assertThat(polarisDiscoveryProperties.getPort()).isEqualTo(PORT); + + // HealthCheckUrl + polarisDiscoveryProperties.setHealthCheckUrl("/health"); + assertThat(polarisDiscoveryProperties.getHealthCheckUrl()).isEqualTo("/health"); + + // ServiceListRefreshInterval + polarisDiscoveryProperties.setServiceListRefreshInterval(1000L); + assertThat(polarisDiscoveryProperties.getServiceListRefreshInterval()).isEqualTo(1000L); + + assertThat(polarisDiscoveryProperties.toString()) + .isEqualTo("PolarisDiscoveryProperties{" + + "namespace='Test'" + + ", service='java_provider_test'" + + ", token='19485a7674294e3c88dba293373c1534'" + + ", weight=10, version='1.0.0'" + + ", protocol='HTTP'" + + ", port=9091" + + ", enabled=true" + + ", registerEnabled=true" + + ", heartbeatEnabled=true" + + ", healthCheckUrl='/health'" + + ", serviceListRefreshInterval=1000}"); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryAutoConfigurationTest.java index 517c7e464b4e9eb858e6f2b70ac3a2d50a73a7f3..644cc4b7dd8530496ad2db955f5e7fab1873d507 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryAutoConfigurationTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryAutoConfigurationTest.java @@ -19,7 +19,7 @@ package com.tencent.cloud.polaris.discovery; import com.tencent.cloud.polaris.PolarisDiscoveryProperties; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.polaris.api.core.ConsumerAPI; import com.tencent.polaris.api.core.ProviderAPI; import com.tencent.polaris.test.mock.discovery.NamingServer; @@ -38,7 +38,7 @@ import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static org.assertj.core.api.Assertions.assertThat; /** - * Test for {@link PolarisDiscoveryAutoConfiguration} + * Test for {@link PolarisDiscoveryAutoConfiguration}. * * @author Haotian Zhang */ @@ -47,10 +47,11 @@ public class PolarisDiscoveryAutoConfigurationTest { private static NamingServer namingServer; private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class, - PolarisDiscoveryAutoConfiguration.class, - PolarisDiscoveryClientConfiguration.class, - PolarisContextConfiguration.class)) + .withConfiguration( + AutoConfigurations.of(PolarisContextAutoConfiguration.class, + PolarisDiscoveryAutoConfiguration.class, + PolarisDiscoveryClientConfiguration.class, + PolarisContextAutoConfiguration.class)) .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081"); diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientConfigurationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientConfigurationTest.java index 0de511243c7f03f4ff11de35972262372a7eb830..c98000f81b0e79ebd8241db7e6209cda2f992b10 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientConfigurationTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientConfigurationTest.java @@ -17,7 +17,7 @@ package com.tencent.cloud.polaris.discovery; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.polaris.test.mock.discovery.NamingServer; import org.junit.AfterClass; import org.junit.BeforeClass; @@ -34,7 +34,7 @@ import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static org.assertj.core.api.Assertions.assertThat; /** - * Test for {@link PolarisDiscoveryClientConfiguration} + * Test for {@link PolarisDiscoveryClientConfiguration}. * * @author Haotian Zhang */ @@ -43,9 +43,10 @@ public class PolarisDiscoveryClientConfigurationTest { private static NamingServer namingServer; private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class, - PolarisDiscoveryClientConfiguration.class, - PolarisContextConfiguration.class)) + .withConfiguration( + AutoConfigurations.of(PolarisContextAutoConfiguration.class, + PolarisDiscoveryClientConfiguration.class, + PolarisContextAutoConfiguration.class)) .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081"); diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientTest.java index baad9d365c6ecd10727735a4e1f167e5e304110c..b678593d9f9b4231ac51a9418ce61f644452fc7b 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisDiscoveryClientTest.java @@ -24,8 +24,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.modules.junit4.PowerMockRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.springframework.cloud.client.ServiceInstance; @@ -37,12 +36,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** - * Test for {@link PolarisDiscoveryClient} + * Test for {@link PolarisDiscoveryClient}. * * @author Haotian Zhang */ -@RunWith(PowerMockRunner.class) -@PowerMockIgnore("javax.management.*") +@RunWith(MockitoJUnitRunner.class) public class PolarisDiscoveryClientTest { @Mock @@ -74,4 +72,8 @@ public class PolarisDiscoveryClientTest { } + @Test + public void testDescription() { + assertThat(client.description()).isEqualTo("Spring Cloud Tencent Polaris Discovery Client."); + } } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscoveryTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscoveryTest.java index abd3ac98b1283c8c3f03c9ebb939bcb729c35364..4b03882d1ca17cce40215d1817244360afb2d4a1 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscoveryTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/PolarisServiceDiscoveryTest.java @@ -19,7 +19,7 @@ package com.tencent.cloud.polaris.discovery; import java.util.List; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.polaris.api.exception.PolarisException; import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.test.mock.discovery.NamingServer; @@ -41,7 +41,7 @@ import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static org.assertj.core.api.Assertions.assertThat; /** - * Test for {@link PolarisServiceDiscovery} + * Test for {@link PolarisServiceDiscovery}. * * @author Haotian Zhang */ @@ -50,11 +50,12 @@ public class PolarisServiceDiscoveryTest { private static NamingServer namingServer; private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class, + .withConfiguration(AutoConfigurations.of( + PolarisContextAutoConfiguration.class, PolarisServiceDiscoveryTest.PolarisPropertiesConfiguration.class, PolarisDiscoveryClientConfiguration.class, PolarisDiscoveryAutoConfiguration.class, - PolarisContextConfiguration.class)) + PolarisContextAutoConfiguration.class)) .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientConfigurationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientConfigurationTest.java index 1069b364e0bb73d461ee270d071fa717733d2e45..8588c77af78eae5ae5b4feb2bf625778b12280f4 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientConfigurationTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientConfigurationTest.java @@ -17,7 +17,7 @@ package com.tencent.cloud.polaris.discovery.reactive; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; import com.tencent.polaris.test.mock.discovery.NamingServer; import org.junit.AfterClass; @@ -35,7 +35,7 @@ import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static org.assertj.core.api.Assertions.assertThat; /** - * Test for {@link PolarisReactiveDiscoveryClientConfiguration} + * Test for {@link PolarisReactiveDiscoveryClientConfiguration}. * * @author Haotian Zhang */ @@ -44,10 +44,11 @@ public class PolarisReactiveDiscoveryClientConfigurationTest { private static NamingServer namingServer; private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class, - PolarisReactiveDiscoveryClientConfiguration.class, - PolarisDiscoveryClientConfiguration.class, - PolarisContextConfiguration.class)) + .withConfiguration( + AutoConfigurations.of(PolarisContextAutoConfiguration.class, + PolarisReactiveDiscoveryClientConfiguration.class, + PolarisDiscoveryClientConfiguration.class, + PolarisContextAutoConfiguration.class)) .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081"); diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientTest.java index 99aa8f1df19a02d992b56d745106cc414e07a031..ac5f2088a0f06914aa27e7b50cc4a1ea6a100fda 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/reactive/PolarisReactiveDiscoveryClientTest.java @@ -20,13 +20,13 @@ package com.tencent.cloud.polaris.discovery.reactive; import java.util.Arrays; import com.tencent.cloud.polaris.discovery.PolarisServiceDiscovery; +import com.tencent.polaris.api.exception.ErrorCode; import com.tencent.polaris.api.exception.PolarisException; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.modules.junit4.PowerMockRunner; +import org.mockito.junit.MockitoJUnitRunner; import reactor.core.publisher.Flux; import reactor.test.StepVerifier; @@ -34,48 +34,73 @@ import org.springframework.cloud.client.ServiceInstance; import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** - * Test for {@link PolarisReactiveDiscoveryClient} + * Test for {@link PolarisReactiveDiscoveryClient}. * * @author Haotian Zhang */ -@RunWith(PowerMockRunner.class) -@PowerMockIgnore("javax.management.*") +@RunWith(MockitoJUnitRunner.class) public class PolarisReactiveDiscoveryClientTest { @Mock private PolarisServiceDiscovery serviceDiscovery; - @Mock - private ServiceInstance serviceInstance; - @InjectMocks private PolarisReactiveDiscoveryClient client; + private int count = 0; + @Test public void testGetInstances() throws PolarisException { - when(serviceDiscovery.getInstances(SERVICE_PROVIDER)) - .thenReturn(singletonList(serviceInstance)); - + when(serviceDiscovery.getInstances(anyString())).thenAnswer(invocation -> { + String serviceName = invocation.getArgument(0); + if (SERVICE_PROVIDER.equalsIgnoreCase(serviceName)) { + return singletonList(mock(ServiceInstance.class)); + } + else { + throw new PolarisException(ErrorCode.UNKNOWN_SERVER_ERROR); + } + }); + + // Normal Flux instances = this.client.getInstances(SERVICE_PROVIDER); - StepVerifier.create(instances).expectNextCount(1).expectComplete().verify(); + + // PolarisException + instances = this.client.getInstances(SERVICE_PROVIDER + 1); + StepVerifier.create(instances).expectNextCount(0).expectComplete().verify(); } @Test public void testGetServices() throws PolarisException { - when(serviceDiscovery.getServices()) - .thenReturn(Arrays.asList(SERVICE_PROVIDER + 1, SERVICE_PROVIDER + 2)); - + when(serviceDiscovery.getServices()).thenAnswer(invocation -> { + if (count == 0) { + count++; + return Arrays.asList(SERVICE_PROVIDER + 1, SERVICE_PROVIDER + 2); + } + else { + throw new PolarisException(ErrorCode.UNKNOWN_SERVER_ERROR); + } + }); + + // Normal Flux services = this.client.getServices(); + StepVerifier.create(services).expectNext(SERVICE_PROVIDER + 1, SERVICE_PROVIDER + 2).expectComplete().verify(); - StepVerifier.create(services) - .expectNext(SERVICE_PROVIDER + 1, SERVICE_PROVIDER + 2).expectComplete() - .verify(); + // PolarisException + services = this.client.getServices(); + StepVerifier.create(services).expectNextCount(0).expectComplete().verify(); } + @Test + public void testDescription() { + assertThat(client.description()).isEqualTo("Spring Cloud Tencent Polaris Reactive Discovery Client."); + } } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/refresh/PolarisServiceStatusChangeListenerTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/refresh/PolarisServiceStatusChangeListenerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e468a46ed2307763eeed7088e3a5dd44418f8766 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/discovery/refresh/PolarisServiceStatusChangeListenerTest.java @@ -0,0 +1,114 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.discovery.refresh; + +import java.lang.reflect.Field; +import java.util.Collections; + +import com.tencent.polaris.api.pojo.DefaultInstance; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceInfo; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.client.pojo.ServiceInstancesByProto; +import com.tencent.polaris.client.pojo.ServicesByProto; +import org.assertj.core.api.Assertions; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; + +import static com.tencent.polaris.test.common.Consts.HOST; +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.PORT; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +/** + * Test for {@link PolarisServiceStatusChangeListener}. + * + * @author Haotian Zhang + */ +public class PolarisServiceStatusChangeListenerTest { + + private ApplicationEventPublisher publisher; + + @Before + public void setUp() { + publisher = mock(ApplicationEventPublisher.class); + doNothing().when(publisher).publishEvent(any(ApplicationEvent.class)); + } + + @Test + public void testOnResourceUpdated() { + PolarisServiceStatusChangeListener polarisServiceStatusChangeListener = new PolarisServiceStatusChangeListener(); + polarisServiceStatusChangeListener.setApplicationEventPublisher(publisher); + + // Service update event + ServiceEventKey serviceUpdateEventKey = new ServiceEventKey(new ServiceKey(NAMESPACE_TEST, SERVICE_PROVIDER), ServiceEventKey.EventType.SERVICE); + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.setNamespace(NAMESPACE_TEST); + serviceInfo.setService(SERVICE_PROVIDER); + // Need update + ServicesByProto oldServices = new ServicesByProto(Collections.emptyList()); + ServicesByProto newServices = new ServicesByProto(Collections.singletonList(serviceInfo)); + polarisServiceStatusChangeListener.onResourceUpdated(serviceUpdateEventKey, oldServices, newServices); + verify(publisher, times(1)).publishEvent(any(ApplicationEvent.class)); + // No need update + oldServices = new ServicesByProto(Collections.singletonList(serviceInfo)); + newServices = new ServicesByProto(Collections.singletonList(serviceInfo)); + polarisServiceStatusChangeListener.onResourceUpdated(serviceUpdateEventKey, oldServices, newServices); + verify(publisher, times(1)).publishEvent(any(ApplicationEvent.class)); + + + // Instance update event + ServiceEventKey instanceUpdateEventKey = new ServiceEventKey(new ServiceKey(NAMESPACE_TEST, SERVICE_PROVIDER), ServiceEventKey.EventType.INSTANCE); + DefaultInstance instance = new DefaultInstance(); + instance.setNamespace(NAMESPACE_TEST); + instance.setService(SERVICE_PROVIDER); + instance.setHost(HOST); + instance.setPort(PORT); + try { + Field instances = ServiceInstancesByProto.class.getDeclaredField("instances"); + instances.setAccessible(true); + + // Need update + ServiceInstancesByProto oldInstances = new ServiceInstancesByProto(); + instances.set(oldInstances, Collections.emptyList()); + ServiceInstancesByProto newInstances = new ServiceInstancesByProto(); + instances.set(newInstances, Collections.singletonList(instance)); + polarisServiceStatusChangeListener.onResourceUpdated(serviceUpdateEventKey, oldInstances, newInstances); + verify(publisher, times(2)).publishEvent(any(ApplicationEvent.class)); + + // No need update + oldInstances = new ServiceInstancesByProto(); + instances.set(oldInstances, Collections.singletonList(instance)); + newInstances = new ServiceInstancesByProto(); + instances.set(newInstances, Collections.singletonList(instance)); + polarisServiceStatusChangeListener.onResourceUpdated(serviceUpdateEventKey, oldInstances, newInstances); + verify(publisher, times(2)).publishEvent(any(ApplicationEvent.class)); + } + catch (NoSuchFieldException | IllegalAccessException e) { + Assertions.fail("Exception encountered.", e); + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/endpoint/PolarisDiscoveryEndPointTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/endpoint/PolarisDiscoveryEndPointTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ea5562d112c224d7a431298f8ab33bdce68b8a17 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/endpoint/PolarisDiscoveryEndPointTest.java @@ -0,0 +1,102 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.endpoint; + +import java.util.Map; + +import com.tencent.cloud.polaris.PolarisDiscoveryProperties; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClient; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; +import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; +import com.tencent.polaris.test.mock.discovery.NamingServer; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.context.annotation.Configuration; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.PORT; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for polaris discovery endpoint. + * + * @author shuiqingliu + */ +public class PolarisDiscoveryEndPointTest { + + private static NamingServer namingServer; + + private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + PolarisDiscoveryEndPointTest.PolarisPropertiesConfiguration.class, + PolarisDiscoveryClientConfiguration.class, + PolarisDiscoveryAutoConfiguration.class, + PolarisDiscoveryEndpointAutoConfiguration.class)) + .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) + .withPropertyValues("server.port=" + PORT) + .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") + .withPropertyValues( + "spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST) + .withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx"); + + @BeforeClass + public static void beforeClass() throws Exception { + namingServer = NamingServer.startNamingServer(10081); + } + + @AfterClass + public static void afterClass() throws Exception { + if (null != namingServer) { + namingServer.terminate(); + } + } + + @Test + public void testPolarisDiscoveryEndpoint() { + this.contextRunner.run(context -> { + PolarisDiscoveryProperties polarisDiscoveryProperties = context + .getBean(PolarisDiscoveryProperties.class); + DiscoveryClient discoveryClient = context + .getBean(PolarisDiscoveryClient.class); + PolarisDiscoveryHandler polarisDiscoveryHandler = context.getBean(PolarisDiscoveryHandler.class); + PolarisDiscoveryEndPoint polarisDiscoveryEndPoint = new PolarisDiscoveryEndPoint(polarisDiscoveryProperties, discoveryClient, polarisDiscoveryHandler); + + Map mapInfo = polarisDiscoveryEndPoint.polarisDiscovery("java_provider_test"); + + assertThat(polarisDiscoveryProperties).isEqualTo(mapInfo.get("PolarisDiscoveryProperties")); + + }); + } + + @Configuration + @EnableAutoConfiguration + @EnableDiscoveryClient + static class PolarisPropertiesConfiguration { + + } + +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/extend/consul/ConsulContextPropertiesTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/extend/consul/ConsulContextPropertiesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9366eed7dc0006039046dec13e508a72a9f8c60b --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/extend/consul/ConsulContextPropertiesTest.java @@ -0,0 +1,90 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.extend.consul; + +import java.util.List; +import java.util.Map; + +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.factory.config.global.ServerConnectorConfigImpl; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static com.tencent.polaris.plugins.connector.common.constant.ConsulConstant.MetadataMapKey.INSTANCE_ID_KEY; +import static com.tencent.polaris.plugins.connector.common.constant.ConsulConstant.MetadataMapKey.IP_ADDRESS_KEY; +import static com.tencent.polaris.plugins.connector.common.constant.ConsulConstant.MetadataMapKey.PREFER_IP_ADDRESS_KEY; +import static com.tencent.polaris.plugins.connector.common.constant.ConsulConstant.MetadataMapKey.SERVICE_NAME_KEY; +import static com.tencent.polaris.test.common.Consts.HOST; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link ConsulContextProperties}. + * + * @author Haotian Zhang + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = ConsulContextPropertiesTest.TestApplication.class) +@ActiveProfiles("test") +public class ConsulContextPropertiesTest { + + @Autowired + private ConsulContextProperties consulContextProperties; + + @Autowired + private SDKContext sdkContext; + + @Test + public void testDefaultInitialization() { + assertThat(consulContextProperties).isNotNull(); + assertThat(consulContextProperties.isEnabled()).isTrue(); + assertThat(consulContextProperties.getHost()).isEqualTo("127.0.0.1"); + assertThat(consulContextProperties.getPort()).isEqualTo(8500); + assertThat(consulContextProperties.isRegister()).isTrue(); + assertThat(consulContextProperties.isDiscoveryEnabled()).isTrue(); + } + + @Test + public void testModify() { + assertThat(sdkContext).isNotNull(); + com.tencent.polaris.api.config.Configuration configuration = sdkContext.getConfig(); + List serverConnectorConfigs = configuration.getGlobal().getServerConnectors(); + Map metadata = null; + for (ServerConnectorConfigImpl serverConnectorConfig : serverConnectorConfigs) { + if (serverConnectorConfig.getId().equals("consul")) { + metadata = serverConnectorConfig.getMetadata(); + } + } + assertThat(metadata).isNotNull(); + assertThat(metadata.get(SERVICE_NAME_KEY)).isEqualTo(SERVICE_PROVIDER); + assertThat(metadata.get(INSTANCE_ID_KEY)).isEqualTo("ins-test"); + assertThat(metadata.get(PREFER_IP_ADDRESS_KEY)).isEqualTo("true"); + assertThat(metadata.get(IP_ADDRESS_KEY)).isEqualTo(HOST); + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisAutoServiceRegistrationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisAutoServiceRegistrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..26aaf8efa49249e21457395bfbb42f1388212453 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisAutoServiceRegistrationTest.java @@ -0,0 +1,144 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.registry; + +import com.tencent.cloud.polaris.PolarisDiscoveryProperties; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties; +import org.springframework.cloud.client.serviceregistry.Registration; +import org.springframework.cloud.client.serviceregistry.ServiceRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.core.env.Environment; + +import static com.tencent.polaris.test.common.Consts.PORT; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; + +/** + * Test for {@link PolarisAutoServiceRegistration}. + * + * @author Haotian Zhang + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisAutoServiceRegistrationTest { + + @Mock + private ServiceRegistry serviceRegistry; + + @Mock + private AutoServiceRegistrationProperties autoServiceRegistrationProperties; + + @Mock + private PolarisDiscoveryProperties polarisDiscoveryProperties; + + @Mock + private ApplicationContext applicationContext; + + @Mock + private Environment environment; + + @Mock + private PolarisRegistration registration; + + private PolarisAutoServiceRegistration polarisAutoServiceRegistration; + + @Before + public void setUp() { + doReturn(polarisDiscoveryProperties).when(registration).getPolarisProperties(); + + doNothing().when(serviceRegistry).register(nullable(Registration.class)); + + polarisAutoServiceRegistration = + new PolarisAutoServiceRegistration(serviceRegistry, autoServiceRegistrationProperties, registration); + + doReturn(environment).when(applicationContext).getEnvironment(); + polarisAutoServiceRegistration.setApplicationContext(applicationContext); + } + + @Test + public void testRegister() { + doReturn(false).when(registration).isRegisterEnabled(); + try { + polarisAutoServiceRegistration.register(); + } + catch (Exception e) { + fail(); + } + + doReturn(true).when(registration).isRegisterEnabled(); + doReturn(-1).when(registration).getPort(); + try { + polarisAutoServiceRegistration.register(); + } + catch (Exception e) { + fail(); + } + + doReturn(PORT).when(registration).getPort(); + try { + polarisAutoServiceRegistration.register(); + } + catch (Exception e) { + fail(); + } + } + + @Test + public void testGetManagementRegistration() { + assertThat(polarisAutoServiceRegistration.getManagementRegistration()).isNull(); + } + + @Test + public void testRegisterManagement() { + doReturn(false).when(registration).isRegisterEnabled(); + try { + polarisAutoServiceRegistration.registerManagement(); + } + catch (Exception e) { + fail(); + } + + doReturn(true).when(registration).isRegisterEnabled(); + try { + polarisAutoServiceRegistration.registerManagement(); + } + catch (Exception e) { + fail(); + } + } + + @Test + public void testGetAppName() { + doReturn("application").when(environment).getProperty(anyString(), anyString()); + assertThat(polarisAutoServiceRegistration.getAppName()).isEqualTo("application"); + + doReturn(SERVICE_PROVIDER).when(polarisDiscoveryProperties).getService(); + assertThat(polarisAutoServiceRegistration.getAppName()).isEqualTo(SERVICE_PROVIDER); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisRegistrationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisRegistrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..cd32ec0900d331c669af375c4eb63a62c35b326f --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisRegistrationTest.java @@ -0,0 +1,133 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.registry; + +import java.util.Collections; +import java.util.Map; + +import com.tencent.cloud.common.metadata.StaticMetadataManager; +import com.tencent.cloud.polaris.DiscoveryPropertiesAutoConfiguration; +import com.tencent.cloud.polaris.PolarisDiscoveryProperties; +import com.tencent.polaris.api.config.Configuration; +import com.tencent.polaris.api.config.global.APIConfig; +import com.tencent.polaris.api.config.global.GlobalConfig; +import com.tencent.polaris.client.api.SDKContext; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import static com.tencent.polaris.test.common.Consts.HOST; +import static com.tencent.polaris.test.common.Consts.PORT; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +/** + * Test for {@link PolarisRegistration}. + * + * @author Haotian Zhang + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisRegistrationTest { + + private PolarisRegistration polarisRegistration; + + @Before + public void setUp() { + // mock DiscoveryPropertiesAutoConfiguration + DiscoveryPropertiesAutoConfiguration discoveryPropertiesAutoConfiguration = + mock(DiscoveryPropertiesAutoConfiguration.class); + doReturn(true).when(discoveryPropertiesAutoConfiguration).isRegisterEnabled(); + + // mock PolarisDiscoveryProperties + PolarisDiscoveryProperties polarisDiscoveryProperties = mock(PolarisDiscoveryProperties.class); + doReturn(SERVICE_PROVIDER).when(polarisDiscoveryProperties).getService(); + doReturn(PORT).when(polarisDiscoveryProperties).getPort(); + doReturn("http").when(polarisDiscoveryProperties).getProtocol(); + + // mock SDKContext + APIConfig apiConfig = mock(APIConfig.class); + doReturn(HOST).when(apiConfig).getBindIP(); + GlobalConfig globalConfig = mock(GlobalConfig.class); + doReturn(apiConfig).when(globalConfig).getAPI(); + Configuration configuration = mock(Configuration.class); + doReturn(globalConfig).when(configuration).getGlobal(); + SDKContext polarisContext = mock(SDKContext.class); + doReturn(configuration).when(polarisContext).getConfig(); + + // mock StaticMetadataManager + StaticMetadataManager staticMetadataManager = mock(StaticMetadataManager.class); + doReturn(Collections.singletonMap("key1", "value1")).when(staticMetadataManager).getMergedStaticMetadata(); + doReturn(Collections.singletonMap("key2", "value2")).when(staticMetadataManager).getLocationMetadata(); + + polarisRegistration = new PolarisRegistration( + discoveryPropertiesAutoConfiguration, polarisDiscoveryProperties, polarisContext, staticMetadataManager); + } + + @Test + public void testGetServiceId() { + assertThat(polarisRegistration.getServiceId()).isEqualTo(SERVICE_PROVIDER); + } + + @Test + public void testGetHost() { + assertThat(polarisRegistration.getHost()).isEqualTo(HOST); + } + + @Test + public void testGetPort() { + assertThat(polarisRegistration.getPort()).isEqualTo(PORT); + } + + @Test + public void testIsSecure() { + assertThat(polarisRegistration.isSecure()).isFalse(); + } + + @Test + public void testGetUri() { + assertThat(polarisRegistration.getUri().toString()).isEqualTo("http://" + HOST + ":" + PORT); + } + + @Test + public void testGetMetadata() { + Map metadata = polarisRegistration.getMetadata(); + assertThat(metadata).isNotNull(); + assertThat(metadata).isNotEmpty(); + assertThat(metadata.size()).isEqualTo(4); + assertThat(metadata.get("key1")).isEqualTo("value1"); + assertThat(metadata.get("key2")).isEqualTo("value2"); + } + + @Test + public void testGetPolarisProperties() { + assertThat(polarisRegistration.getPolarisProperties()).isNotNull(); + } + + @Test + public void testIsRegisterEnabled() { + assertThat(polarisRegistration.isRegisterEnabled()).isTrue(); + } + + @Test + public void testToString() { + System.out.println(polarisRegistration); + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryAutoConfigurationTest.java index 12f7d6a78f4f68d82027192a9f65abd0195f19c5..96b6b3fe878eb91be5c94b2037a20e276696d94a 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryAutoConfigurationTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryAutoConfigurationTest.java @@ -17,7 +17,7 @@ package com.tencent.cloud.polaris.registry; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; import com.tencent.polaris.test.mock.discovery.NamingServer; @@ -37,7 +37,7 @@ import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static org.assertj.core.api.Assertions.assertThat; /** - * Test for {@link PolarisServiceRegistryAutoConfiguration} + * Test for {@link PolarisServiceRegistryAutoConfiguration}. * * @author Haotian Zhang */ @@ -46,9 +46,10 @@ public class PolarisServiceRegistryAutoConfigurationTest { private static NamingServer namingServer; private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class, - PolarisServiceRegistryAutoConfiguration.class, - PolarisDiscoveryClientConfiguration.class)) + .withConfiguration( + AutoConfigurations.of(PolarisContextAutoConfiguration.class, + PolarisServiceRegistryAutoConfiguration.class, + PolarisDiscoveryClientConfiguration.class)) .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081"); diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryTest.java index 76aa9e6f0c950984997b7da2a501fd8455a918f3..d6f751d2fde8d31d354336ea53a046152c2973a6 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/registry/PolarisServiceRegistryTest.java @@ -17,7 +17,7 @@ package com.tencent.cloud.polaris.registry; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; import com.tencent.polaris.api.pojo.ServiceKey; @@ -30,7 +30,6 @@ import org.mockito.Mockito; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Configuration; import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; @@ -38,10 +37,11 @@ import static com.tencent.polaris.test.common.Consts.PORT; import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; /** - * Test for {@link PolarisServiceRegistry} + * Test for {@link PolarisServiceRegistry}. * * @author Haotian Zhang */ @@ -50,10 +50,11 @@ public class PolarisServiceRegistryTest { private static NamingServer namingServer; private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class, - PolarisPropertiesConfiguration.class, - PolarisDiscoveryClientConfiguration.class, - PolarisDiscoveryAutoConfiguration.class)) + .withConfiguration( + AutoConfigurations.of(PolarisContextAutoConfiguration.class, + PolarisPropertiesConfiguration.class, + PolarisDiscoveryClientConfiguration.class, + PolarisDiscoveryAutoConfiguration.class)) .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") @@ -110,9 +111,24 @@ public class PolarisServiceRegistryTest { }); } + @Test + public void testDeRegister() { + this.contextRunner.run(context -> { + PolarisServiceRegistry registry = context + .getBean(PolarisServiceRegistry.class); + PolarisRegistration registration = Mockito.mock(PolarisRegistration.class); + doReturn(null).when(registration).getServiceId(); + try { + registry.deregister(registration); + } + catch (Throwable throwable) { + fail(); + } + }); + } + @Configuration @EnableAutoConfiguration - @EnableDiscoveryClient static class PolarisPropertiesConfiguration { } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/PolarisPropertiesTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisDiscoveryRibbonAutoConfigurationTest.java similarity index 51% rename from spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/PolarisPropertiesTest.java rename to spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisDiscoveryRibbonAutoConfigurationTest.java index 518797e2cf41687242003c38b236172994ab1737..e14d9be7dbc037c39b482913c00ac30763a51238 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/PolarisPropertiesTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisDiscoveryRibbonAutoConfigurationTest.java @@ -13,45 +13,32 @@ * 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.tencent.cloud.polaris; +package com.tencent.cloud.polaris.ribbon; import org.junit.Test; -import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; -import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.fail; /** - * Test for {@link PolarisDiscoveryProperties} + * Test for {@link PolarisDiscoveryRibbonAutoConfiguration}. * * @author Haotian Zhang */ -public class PolarisPropertiesTest { - - @Test - public void testInitAndGetSet() { - PolarisDiscoveryProperties temp = new PolarisDiscoveryProperties(); - try { - temp.setNamespace(NAMESPACE_TEST); - assertThat(temp.getNamespace()).isEqualTo(NAMESPACE_TEST); +public class PolarisDiscoveryRibbonAutoConfigurationTest { - temp.setService(SERVICE_PROVIDER); - assertThat(temp.getService()).isEqualTo(SERVICE_PROVIDER); + private ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner(); - temp.setToken("xxxxxx"); - assertThat(temp.getToken()).isEqualTo("xxxxxx"); - - temp.init(); - assertThat(temp).isNotNull(); - } - catch (Exception e) { - fail(); - e.printStackTrace(); - } + @Test + public void testDefaultInitialization() { + this.applicationContextRunner + .withConfiguration(AutoConfigurations.of(PolarisDiscoveryRibbonAutoConfiguration.class)) + .run(context -> { + assertThat(context).hasSingleBean(PolarisDiscoveryRibbonAutoConfiguration.class); + }); } - } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisRibbonServerListConfigurationTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisRibbonServerListConfigurationTest.java index eea15316926b0a1947f77a85b889efa8524347cf..277555901c50db29285cf722f32a375734f47985 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisRibbonServerListConfigurationTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisRibbonServerListConfigurationTest.java @@ -19,74 +19,46 @@ package com.tencent.cloud.polaris.ribbon; import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; -import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; -import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; +import com.netflix.loadbalancer.ServerList; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.runner.WebApplicationContextRunner; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; -import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; -import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; -import static com.tencent.polaris.test.common.Consts.PORT; import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static org.assertj.core.api.Assertions.assertThat; /** - * Test for {@link PolarisRibbonServerListConfiguration} + * Test for {@link PolarisRibbonServerListConfiguration}. * * @author Haotian Zhang */ public class PolarisRibbonServerListConfigurationTest { - private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(PolarisRibbonClientTest.class, - PolarisDiscoveryClientConfiguration.class, - PolarisContextConfiguration.class)) - .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) - .withPropertyValues("server.port=" + PORT) - .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") - .withPropertyValues( - "spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST) - .withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx"); + private ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner(); @Test - public void testProperties() { - this.contextRunner.run(context -> { - PolarisDiscoveryHandler discoveryHandler = context - .getBean(PolarisDiscoveryHandler.class); - PolarisServerList serverList = new PolarisServerList(discoveryHandler); - IClientConfig iClientConfig = context.getBean(IClientConfig.class); - serverList.initWithNiwsConfig(iClientConfig); - - assertThat(serverList.getServiceId()).isEqualTo(SERVICE_PROVIDER); - }); + public void testDefaultInitialization() { + this.applicationContextRunner + .withConfiguration(AutoConfigurations.of( + TestApplication.class, PolarisRibbonServerListConfiguration.class)) + .run(context -> { + assertThat(context).hasSingleBean(PolarisRibbonServerListConfiguration.class); + assertThat(context).hasSingleBean(ServerList.class); + }); } - @Configuration - @EnableAutoConfiguration - @EnableDiscoveryClient - static class PolarisRibbonClientTest { + @SpringBootApplication + static class TestApplication { @Bean - IClientConfig iClientConfig() { + public IClientConfig iClientConfig() { DefaultClientConfigImpl config = new DefaultClientConfigImpl(); config.setClientName(SERVICE_PROVIDER); return config; } - - @Bean - @LoadBalanced - RestTemplate restTemplate() { - return new RestTemplate(); - } - } } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisServerListTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisServerListTest.java index 92cc4fb30a1097bbb8c71f4d3f1a8a97d37002a1..e441786e6eb74edbad2a62510bff9d1c76ec74b5 100644 --- a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisServerListTest.java +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/ribbon/PolarisServerListTest.java @@ -21,7 +21,7 @@ import java.util.List; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.Server; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryAutoConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryClientConfiguration; import com.tencent.cloud.polaris.discovery.PolarisDiscoveryHandler; @@ -29,13 +29,13 @@ import com.tencent.polaris.api.pojo.ServiceKey; import com.tencent.polaris.test.mock.discovery.NamingServer; import com.tencent.polaris.test.mock.discovery.NamingService; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Configuration; import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; @@ -46,7 +46,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** - * Test for {@link PolarisServerList} + * Test for {@link PolarisServerList}. * * @author Haotian Zhang */ @@ -55,11 +55,12 @@ public class PolarisServerListTest { private static NamingServer namingServer; private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class, - PolarisServerListTest.PolarisPropertiesConfiguration.class, - PolarisDiscoveryClientConfiguration.class, - PolarisDiscoveryAutoConfiguration.class, - PolarisContextConfiguration.class)) + .withConfiguration( + AutoConfigurations.of(PolarisContextAutoConfiguration.class, + PolarisServerListTest.PolarisPropertiesConfiguration.class, + PolarisDiscoveryClientConfiguration.class, + PolarisDiscoveryAutoConfiguration.class, + PolarisContextAutoConfiguration.class)) .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) .withPropertyValues("server.port=" + PORT) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") @@ -67,6 +68,8 @@ public class PolarisServerListTest { "spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST) .withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx"); + private IClientConfig iClientConfig; + @BeforeClass public static void beforeClass() throws Exception { namingServer = NamingServer.startNamingServer(10081); @@ -83,16 +86,17 @@ public class PolarisServerListTest { } } - /** - * Test {@link PolarisServerList#getInitialListOfServers()} with empty server list. - */ + @Before + public void setUp() { + // mock IClientConfig + iClientConfig = mock(IClientConfig.class); + when(iClientConfig.getClientName()).thenReturn(SERVICE_PROVIDER); + + } + @Test - @SuppressWarnings("unchecked") - public void test1() { + public void testGetInitialListOfServers() { this.contextRunner.run(context -> { - // mock - IClientConfig iClientConfig = mock(IClientConfig.class); - when(iClientConfig.getClientName()).thenReturn(SERVICE_PROVIDER); PolarisDiscoveryHandler polarisDiscoveryHandler = context .getBean(PolarisDiscoveryHandler.class); PolarisServerList serverList = new PolarisServerList(polarisDiscoveryHandler); @@ -103,17 +107,9 @@ public class PolarisServerListTest { }); } - /** - * Test {@link PolarisServerList#getUpdatedListOfServers()} with server list of size - * 3. - */ @Test - @SuppressWarnings("unchecked") - public void test2() { + public void testGetUpdatedListOfServers() { this.contextRunner.run(context -> { - // mock - IClientConfig iClientConfig = mock(IClientConfig.class); - when(iClientConfig.getClientName()).thenReturn(SERVICE_PROVIDER); PolarisDiscoveryHandler polarisDiscoveryHandler = context .getBean(PolarisDiscoveryHandler.class); PolarisServerList serverList = new PolarisServerList(polarisDiscoveryHandler); @@ -136,9 +132,20 @@ public class PolarisServerListTest { }); } + @Test + public void testProperties() { + this.contextRunner.run(context -> { + PolarisDiscoveryHandler polarisDiscoveryHandler = context + .getBean(PolarisDiscoveryHandler.class); + PolarisServerList serverList = new PolarisServerList(polarisDiscoveryHandler); + serverList.initWithNiwsConfig(iClientConfig); + + assertThat(serverList.getServiceId()).isEqualTo(SERVICE_PROVIDER); + }); + } + @Configuration @EnableAutoConfiguration - @EnableDiscoveryClient static class PolarisPropertiesConfiguration { } diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/util/OkHttpUtilTest.java b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/util/OkHttpUtilTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7256c3dc294a0bd57a037a5d04fa706e86700054 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/java/com/tencent/cloud/polaris/util/OkHttpUtilTest.java @@ -0,0 +1,60 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.util; + +import org.assertj.core.util.Maps; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link OkHttpUtil}. + * + * @author Haotian Zhang + */ +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = OkHttpUtilTest.TestApplication.class) +public class OkHttpUtilTest { + + @LocalServerPort + private int port; + + @Test + public void testGet() { + assertThat(OkHttpUtil.get("http://localhost:" + port + "/test", Maps.newHashMap("key", "value"))).isTrue(); + assertThat(OkHttpUtil.get("http://localhost:" + port + "/error", Maps.newHashMap("key", "value"))).isFalse(); + assertThat(OkHttpUtil.get("http://localhost:55555/error", Maps.newHashMap("key", "value"))).isFalse(); + } + + @SpringBootApplication + @RestController + static class TestApplication { + @GetMapping("/test") + public String test() { + return "test"; + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-discovery/src/test/resources/application-test.yml b/spring-cloud-starter-tencent-polaris-discovery/src/test/resources/application-test.yml new file mode 100644 index 0000000000000000000000000000000000000000..8fbdbcc48aba943edb90c55ef04d2987c2ae305f --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-discovery/src/test/resources/application-test.yml @@ -0,0 +1,24 @@ +server: + port: 48084 +spring: + application: + name: java_provider_test + cloud: + polaris: + address: grpc://127.0.0.1:8091 + namespace: Test + enabled: true + discovery: + enabled: true + register: true + consul: + port: 8500 + host: 127.0.0.1 + enabled: true + discovery: + enabled: true + register: true + instance-id: ins-test + service-name: ${spring.application.name} + ip-address: 127.0.0.1 + prefer-ip-address: true diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/pom.xml b/spring-cloud-starter-tencent-polaris-ratelimit/pom.xml index e3886ecf27f190dcab92d7a662a9cdc1c059b58f..d3499deeedd3e951d78398ca0d473056c6a098a6 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/pom.xml +++ b/spring-cloud-starter-tencent-polaris-ratelimit/pom.xml @@ -25,6 +25,32 @@ com.tencent.polaris polaris-ratelimit-factory + + + com.tencent.polaris + router-rule + + + com.tencent.polaris + router-nearby + + + com.tencent.polaris + router-canary + + + com.tencent.polaris + router-set + + + com.tencent.polaris + router-isolated + + + com.tencent.polaris + router-healthy + + @@ -65,14 +91,32 @@ - org.powermock - powermock-module-junit4 + org.springframework.boot + spring-boot-actuator + true + + + + org.springframework.boot + spring-boot-actuator-autoconfigure + true + + + + org.mockito + mockito-inline + test + + + + org.mockito + mockito-core test - org.powermock - powermock-api-mockito2 + net.bytebuddy + byte-buddy test diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolver.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..4c29cb3adc7d8debe2558280f31c871883d1feb7 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolver.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.common.util.ExpressionLabelUtils; +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.polaris.client.pb.ModelProto; +import com.tencent.polaris.client.pb.RateLimitProto; + +import org.springframework.util.CollectionUtils; + +/** + * resolve labels from rate limit rule. + * + *@author lepdou 2022-05-13 + */ +public class RateLimitRuleLabelResolver { + + private final ServiceRuleManager serviceRuleManager; + + public RateLimitRuleLabelResolver(ServiceRuleManager serviceRuleManager) { + this.serviceRuleManager = serviceRuleManager; + } + + public Set getExpressionLabelKeys(String namespace, String service) { + RateLimitProto.RateLimit rateLimitRule = serviceRuleManager.getServiceRateLimitRule(namespace, service); + if (rateLimitRule == null) { + return Collections.emptySet(); + } + + List rules = rateLimitRule.getRulesList(); + if (CollectionUtils.isEmpty(rules)) { + return Collections.emptySet(); + } + + Set expressionLabels = new HashSet<>(); + for (RateLimitProto.Rule rule : rules) { + Map labels = rule.getLabelsMap(); + if (CollectionUtils.isEmpty(labels)) { + return Collections.emptySet(); + } + for (String key : labels.keySet()) { + if (ExpressionLabelUtils.isExpressionLabel(key)) { + expressionLabels.add(key); + } + } + } + return expressionLabels; + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/RateLimitConfiguration.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfiguration.java similarity index 67% rename from spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/RateLimitConfiguration.java rename to spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfiguration.java index 27a161892e196a0003f98b730554dbac401d950f..83cc00c9176dca208571e43319fbdbe378528ca4 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/RateLimitConfiguration.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfiguration.java @@ -13,23 +13,32 @@ * 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.tencent.cloud.polaris.ratelimit.config; +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver; import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckReactiveFilter; import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter; +import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver; +import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver; import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.ratelimit.api.core.LimitAPI; import com.tencent.polaris.ratelimit.factory.LimitAPIFactory; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.lang.Nullable; import static javax.servlet.DispatcherType.ASYNC; import static javax.servlet.DispatcherType.ERROR; @@ -38,12 +47,15 @@ import static javax.servlet.DispatcherType.INCLUDE; import static javax.servlet.DispatcherType.REQUEST; /** + * Configuration of rate limit. + * * @author Haotian Zhang */ @Configuration -@ConditionalOnProperty(name = "spring.cloud.polaris.ratelimit.enabled", - matchIfMissing = true) -public class RateLimitConfiguration { +@ConditionalOnPolarisEnabled +@AutoConfigureAfter(PolarisContextAutoConfiguration.class) +@ConditionalOnProperty(name = "spring.cloud.polaris.ratelimit.enabled", matchIfMissing = true) +public class PolarisRateLimitAutoConfiguration { @Bean @ConditionalOnMissingBean @@ -51,6 +63,11 @@ public class RateLimitConfiguration { return LimitAPIFactory.createLimitAPIByContext(polarisContext); } + @Bean + public RateLimitRuleLabelResolver rateLimitRuleLabelService(ServiceRuleManager serviceRuleManager) { + return new RateLimitRuleLabelResolver(serviceRuleManager); + } + /** * Create when web application type is SERVLET. */ @@ -60,8 +77,12 @@ public class RateLimitConfiguration { @Bean @ConditionalOnMissingBean - public QuotaCheckServletFilter quotaCheckFilter(LimitAPI limitAPI) { - return new QuotaCheckServletFilter(limitAPI); + public QuotaCheckServletFilter quotaCheckFilter(LimitAPI limitAPI, + @Nullable PolarisRateLimiterLabelServletResolver labelResolver, + PolarisRateLimitProperties polarisRateLimitProperties, + RateLimitRuleLabelResolver rateLimitRuleLabelResolver) { + return new QuotaCheckServletFilter(limitAPI, labelResolver, + polarisRateLimitProperties, rateLimitRuleLabelResolver); } @Bean @@ -85,8 +106,12 @@ public class RateLimitConfiguration { static class MetadataReactiveFilterConfig { @Bean - public QuotaCheckReactiveFilter quotaCheckReactiveFilter(LimitAPI limitAPI) { - return new QuotaCheckReactiveFilter(limitAPI); + public QuotaCheckReactiveFilter quotaCheckReactiveFilter(LimitAPI limitAPI, + @Nullable PolarisRateLimiterLabelReactiveResolver labelResolver, + PolarisRateLimitProperties polarisRateLimitProperties, + RateLimitRuleLabelResolver rateLimitRuleLabelResolver) { + return new QuotaCheckReactiveFilter(limitAPI, labelResolver, + polarisRateLimitProperties, rateLimitRuleLabelResolver); } } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitProperties.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..a7a0f4995d867d8a535bad936baca06815cf25e2 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitProperties.java @@ -0,0 +1,83 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.http.HttpStatus; + +/** + * The properties for rate limit. + * + * @author lepdou 2022-04-20 + */ +@ConfigurationProperties("spring.cloud.polaris.ratelimit") +public class PolarisRateLimitProperties { + + /** + * custom tips when reject request. + */ + private String rejectRequestTips; + + /** + * context file path. + */ + private String rejectRequestTipsFilePath; + + /** + * custom http code when reject request. + */ + private int rejectHttpCode = HttpStatus.TOO_MANY_REQUESTS.value(); + + /** + * Max queuing time when using unirate. + */ + private long maxQueuingTime = 1000L; + + public String getRejectRequestTips() { + return rejectRequestTips; + } + + public void setRejectRequestTips(String rejectRequestTips) { + this.rejectRequestTips = rejectRequestTips; + } + + public String getRejectRequestTipsFilePath() { + return rejectRequestTipsFilePath; + } + + public void setRejectRequestTipsFilePath(String rejectRequestTipsFilePath) { + this.rejectRequestTipsFilePath = rejectRequestTipsFilePath; + } + + public int getRejectHttpCode() { + return rejectHttpCode; + } + + public void setRejectHttpCode(int rejectHttpCode) { + this.rejectHttpCode = rejectHttpCode; + } + + public long getMaxQueuingTime() { + return maxQueuingTime; + } + + public void setMaxQueuingTime(long maxQueuingTime) { + this.maxQueuingTime = maxQueuingTime; + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..e00d6f6358b55b498a296eb497628d5c23606a79 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesAutoConfiguration.java @@ -0,0 +1,42 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.config; + +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Properties auto configuration of rate limit. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnPolarisEnabled +@Import({PolarisRateLimitProperties.class}) +public class PolarisRateLimitPropertiesAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public RateLimitConfigModifier rateLimitConfigModifier(PolarisRateLimitProperties polarisRateLimitProperties) { + return new RateLimitConfigModifier(polarisRateLimitProperties); + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesBootstrapConfiguration.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesBootstrapConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..bd11dbe58ca7040b763866e1dadba248726e3add --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesBootstrapConfiguration.java @@ -0,0 +1,34 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.config; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * Autoconfiguration of rate limit at bootstrap phase. + * + * @author Haotian Zhang + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnProperty("spring.cloud.polaris.enabled") +@Import(PolarisRateLimitPropertiesAutoConfiguration.class) +public class PolarisRateLimitPropertiesBootstrapConfiguration { + +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/RateLimitConfigModifier.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/RateLimitConfigModifier.java new file mode 100644 index 0000000000000000000000000000000000000000..b0c097a68282d522f7054f5cfa9bfaddae05c34f --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/config/RateLimitConfigModifier.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.config; + +import com.tencent.cloud.common.constant.ContextConstant; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; +import com.tencent.polaris.factory.config.ConfigurationImpl; + +/** + * Config modifier for rate limit. + * + * @author Haotian Zhang + */ +public class RateLimitConfigModifier implements PolarisConfigModifier { + + private PolarisRateLimitProperties polarisRateLimitProperties; + + public RateLimitConfigModifier(PolarisRateLimitProperties polarisRateLimitProperties) { + this.polarisRateLimitProperties = polarisRateLimitProperties; + } + + @Override + public void modify(ConfigurationImpl configuration) { + // Update MaxQueuingTime. + configuration.getProvider().getRateLimit() + .setMaxQueuingTime(polarisRateLimitProperties.getMaxQueuingTime()); + } + + @Override + public int getOrder() { + return ContextConstant.ModifierOrder.CIRCUIT_BREAKER_ORDER; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/constant/RateLimitConstant.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/constant/RateLimitConstant.java index 5c96b86464befa7bca1017481da85326fe69c012..814134527bc866bad809d070c897332fcf64d1fd 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/constant/RateLimitConstant.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/constant/RateLimitConstant.java @@ -37,6 +37,11 @@ public final class RateLimitConstant { */ public static String QUOTA_LIMITED_INFO = "The request is deny by rate limit because the throttling threshold is reached"; + /** + * The build in label method. + */ + public static String LABEL_METHOD = "method"; + private RateLimitConstant() { } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/endpoint/PolarisRateLimitRuleEndpoint.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/endpoint/PolarisRateLimitRuleEndpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..28bbae229b0fb2f02e7cdb01f1ade072088f6193 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/endpoint/PolarisRateLimitRuleEndpoint.java @@ -0,0 +1,101 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.endpoint; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; +import com.tencent.polaris.client.pb.RateLimitProto; +import com.tencent.polaris.client.pb.RoutingProto; +import org.apache.commons.lang.StringUtils; + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; +import org.springframework.boot.actuate.endpoint.annotation.Selector; +import org.springframework.lang.Nullable; +import org.springframework.util.CollectionUtils; + +/** + * Endpoint of Polaris RateLimit rule. + * + * @author shuiqingliu + **/ +@Endpoint(id = "polaris-ratelimit") +public class PolarisRateLimitRuleEndpoint { + + private final ServiceRuleManager serviceRuleManager; + private final PolarisRateLimitProperties polarisRateLimitProperties; + + public PolarisRateLimitRuleEndpoint(ServiceRuleManager serviceRuleManager, PolarisRateLimitProperties polarisRateLimitProperties) { + this.serviceRuleManager = serviceRuleManager; + this.polarisRateLimitProperties = polarisRateLimitProperties; + } + + @ReadOperation + public Map rateLimit(@Selector String namespace, @Selector String service, @Nullable String dstService) { + Map result = new HashMap<>(); + RateLimitProto.RateLimit rateLimit = serviceRuleManager.getServiceRateLimitRule(namespace, service); + result.put("properties", polarisRateLimitProperties); + result.put("namespace", namespace); + result.put("service", service); + result.put("rateLimits", parseRateLimitRule(rateLimit)); + + if (StringUtils.isEmpty(dstService)) { + return result; + } + List routes = serviceRuleManager.getServiceRouterRule(namespace, service, dstService); + result.put("routes", routes); + return result; + } + + private List parseRateLimitRule(RateLimitProto.RateLimit rateLimit) { + List rateLimitRule = new ArrayList<>(); + if (rateLimit == null || CollectionUtils.isEmpty(rateLimit.getRulesList())) { + return rateLimitRule; + } + + for (RateLimitProto.Rule rule : rateLimit.getRulesList()) { + Map ruleMap = new HashMap<>(); + ruleMap.put("id", rule.getId()); + ruleMap.put("priority", rule.getPriority()); + ruleMap.put("resource", rule.getResource()); + ruleMap.put("type", rule.getType()); + ruleMap.put("labels", rule.getLabelsMap()); + ruleMap.put("amounts", rule.getAmountsList()); + ruleMap.put("action", rule.getAction()); + ruleMap.put("disable", rule.getDisable()); + ruleMap.put("report", rule.getReport()); + ruleMap.put("create_time", rule.getCtime()); + ruleMap.put("modify_time", rule.getMtime()); + ruleMap.put("revision", rule.getRevision()); + ruleMap.put("service_token", rule.getServiceToken()); + ruleMap.put("adjuster", rule.getAdjuster()); + ruleMap.put("regex_combine", rule.getRegexCombine()); + ruleMap.put("amount_mode", rule.getAmountMode()); + ruleMap.put("failover", rule.getFailover()); + ruleMap.put("cluster", rule.getCluster()); + rateLimitRule.add(ruleMap); + } + return rateLimitRule; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/endpoint/PolarisRateLimitRuleEndpointAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/endpoint/PolarisRateLimitRuleEndpointAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..d16832989399b262220b37899240a81a5bb068d1 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/endpoint/PolarisRateLimitRuleEndpointAutoConfiguration.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.endpoint; + +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; + +import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * The AutoConfiguration for Polaris RateLimit endpoint. + * + * @author shuiqingliu + **/ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(Endpoint.class) +@ConditionalOnPolarisEnabled +@ConditionalOnProperty(name = "spring.cloud.polaris.ratelimit.enabled", matchIfMissing = true) +public class PolarisRateLimitRuleEndpointAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + @ConditionalOnAvailableEndpoint + public PolarisRateLimitRuleEndpoint polarisRateLimitRuleEndpoint(ServiceRuleManager serviceRuleManager, PolarisRateLimitProperties properties) { + return new PolarisRateLimitRuleEndpoint(serviceRuleManager, properties); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java index 1727a018b3fd003ef630ec6b354d6aa6e67a6b37..31320dda05a68669b7c4c80650480223b4ffc920 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilter.java @@ -13,17 +13,28 @@ * 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.tencent.cloud.polaris.ratelimit.filter; import java.nio.charset.StandardCharsets; +import java.time.Duration; import java.util.HashMap; import java.util.Map; +import java.util.Set; + +import javax.annotation.PostConstruct; +import com.google.common.collect.Maps; import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.util.ExpressionLabelUtils; +import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver; +import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant; +import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver; import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils; +import com.tencent.cloud.polaris.ratelimit.utils.RateLimitUtils; import com.tencent.polaris.ratelimit.api.core.LimitAPI; import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode; @@ -34,27 +45,46 @@ import reactor.core.publisher.Mono; import org.springframework.core.Ordered; import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; +import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.LABEL_METHOD; + /** * Reactive filter to check quota. * - * @author Haotian Zhang + * @author Haotian Zhang, lepdou, cheese8, kaiy */ public class QuotaCheckReactiveFilter implements WebFilter, Ordered { - private static final Logger LOG = LoggerFactory - .getLogger(QuotaCheckReactiveFilter.class); + private static final Logger LOG = LoggerFactory.getLogger(QuotaCheckReactiveFilter.class); private final LimitAPI limitAPI; - public QuotaCheckReactiveFilter(LimitAPI limitAPI) { + private final PolarisRateLimiterLabelReactiveResolver labelResolver; + + private final PolarisRateLimitProperties polarisRateLimitProperties; + + private final RateLimitRuleLabelResolver rateLimitRuleLabelResolver; + + private String rejectTips; + + public QuotaCheckReactiveFilter(LimitAPI limitAPI, + PolarisRateLimiterLabelReactiveResolver labelResolver, + PolarisRateLimitProperties polarisRateLimitProperties, + RateLimitRuleLabelResolver rateLimitRuleLabelResolver) { this.limitAPI = limitAPI; + this.labelResolver = labelResolver; + this.polarisRateLimitProperties = polarisRateLimitProperties; + this.rateLimitRuleLabelResolver = rateLimitRuleLabelResolver; + } + + @PostConstruct + public void init() { + rejectTips = RateLimitUtils.getRejectTips(polarisRateLimitProperties); } @Override @@ -66,25 +96,26 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered { public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { String localNamespace = MetadataContext.LOCAL_NAMESPACE; String localService = MetadataContext.LOCAL_SERVICE; - String method = exchange.getRequest().getURI().getPath(); - Map labels = null; - if (StringUtils.isNotBlank(method)) { - labels = new HashMap<>(); - labels.put("method", method); - } + + Map labels = getRequestLabels(exchange, localNamespace, localService); try { + String path = exchange.getRequest().getURI().getPath(); QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, - localNamespace, localService, 1, labels, null); + localNamespace, localService, 1, labels, path); + if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) { ServerHttpResponse response = exchange.getResponse(); - response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS); - response.getHeaders().setContentType(MediaType.APPLICATION_JSON); - DataBuffer dataBuffer = response.bufferFactory().allocateBuffer().write( - (RateLimitConstant.QUOTA_LIMITED_INFO + quotaResponse.getInfo()) - .getBytes(StandardCharsets.UTF_8)); + response.setRawStatusCode(polarisRateLimitProperties.getRejectHttpCode()); + response.getHeaders().setContentType(MediaType.TEXT_HTML); + DataBuffer dataBuffer = response.bufferFactory().allocateBuffer() + .write(rejectTips.getBytes(StandardCharsets.UTF_8)); return response.writeWith(Mono.just(dataBuffer)); } + // Unirate + if (quotaResponse.getCode() == QuotaResultCode.QuotaResultOk && quotaResponse.getWaitMs() > 0) { + return Mono.delay(Duration.ofMillis(quotaResponse.getWaitMs())).flatMap(e -> chain.filter(exchange)); + } } catch (Throwable t) { // An exception occurs in the rate limiting API call, @@ -95,4 +126,41 @@ public class QuotaCheckReactiveFilter implements WebFilter, Ordered { return chain.filter(exchange); } + private Map getRequestLabels(ServerWebExchange exchange, String localNamespace, String localService) { + Map labels = new HashMap<>(); + + // add build in labels + String path = exchange.getRequest().getURI().getPath(); + if (StringUtils.isNotBlank(path)) { + labels.put(LABEL_METHOD, path); + } + + // add rule expression labels + Map expressionLabels = getRuleExpressionLabels(exchange, localNamespace, localService); + labels.putAll(expressionLabels); + + // add custom labels + Map customResolvedLabels = getCustomResolvedLabels(exchange); + labels.putAll(customResolvedLabels); + + return labels; + } + + private Map getCustomResolvedLabels(ServerWebExchange exchange) { + if (labelResolver != null) { + try { + return labelResolver.resolve(exchange); + } + catch (Throwable e) { + LOG.error("resolve custom label failed. resolver = {}", labelResolver.getClass().getName(), e); + } + } + return Maps.newHashMap(); + } + + private Map getRuleExpressionLabels(ServerWebExchange exchange, String namespace, String service) { + Set expressionLabels = rateLimitRuleLabelResolver.getExpressionLabelKeys(namespace, service); + return ExpressionLabelUtils.resolve(exchange, expressionLabels); + } + } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java index c436272ae357ccecfaf1e0b5e368a6d680cd5fdc..f3e4c3b0776fbf89cc3d270cb7f7e92a9a9203e2 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilter.java @@ -19,17 +19,25 @@ package com.tencent.cloud.polaris.ratelimit.filter; import java.io.IOException; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Set; +import javax.annotation.PostConstruct; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.util.ExpressionLabelUtils; +import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver; +import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant; +import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver; import com.tencent.cloud.polaris.ratelimit.utils.QuotaCheckUtils; +import com.tencent.cloud.polaris.ratelimit.utils.RateLimitUtils; import com.tencent.polaris.ratelimit.api.core.LimitAPI; import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode; @@ -40,48 +48,67 @@ import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.web.filter.OncePerRequestFilter; -import static org.springframework.http.HttpStatus.TOO_MANY_REQUESTS; +import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.LABEL_METHOD; /** * Servlet filter to check quota. * - * @author Haotian Zhang + * @author Haotian Zhang, lepdou, cheese8 */ @Order(RateLimitConstant.FILTER_ORDER) public class QuotaCheckServletFilter extends OncePerRequestFilter { - private static final Logger LOG = LoggerFactory - .getLogger(QuotaCheckServletFilter.class); + private static final Logger LOG = LoggerFactory.getLogger(QuotaCheckServletFilter.class); private final LimitAPI limitAPI; - public QuotaCheckServletFilter(LimitAPI limitAPI) { + private final PolarisRateLimiterLabelServletResolver labelResolver; + + private final PolarisRateLimitProperties polarisRateLimitProperties; + + private final RateLimitRuleLabelResolver rateLimitRuleLabelResolver; + + private String rejectTips; + + public QuotaCheckServletFilter(LimitAPI limitAPI, + PolarisRateLimiterLabelServletResolver labelResolver, + PolarisRateLimitProperties polarisRateLimitProperties, + RateLimitRuleLabelResolver rateLimitRuleLabelResolver) { this.limitAPI = limitAPI; + this.labelResolver = labelResolver; + this.polarisRateLimitProperties = polarisRateLimitProperties; + this.rateLimitRuleLabelResolver = rateLimitRuleLabelResolver; + } + + @PostConstruct + public void init() { + rejectTips = RateLimitUtils.getRejectTips(polarisRateLimitProperties); } @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, FilterChain filterChain) + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String localNamespace = MetadataContext.LOCAL_NAMESPACE; String localService = MetadataContext.LOCAL_SERVICE; - String method = request.getRequestURI(); - Map labels = null; - if (StringUtils.isNotBlank(method)) { - labels = new HashMap<>(); - labels.put("method", method); - } + + Map labels = getRequestLabels(request, localNamespace, localService); try { QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, - localNamespace, localService, 1, labels, null); + localNamespace, localService, 1, labels, request.getRequestURI()); + if (quotaResponse.getCode() == QuotaResultCode.QuotaResultLimited) { - response.setStatus(TOO_MANY_REQUESTS.value()); - response.getWriter().write(RateLimitConstant.QUOTA_LIMITED_INFO); + response.setStatus(polarisRateLimitProperties.getRejectHttpCode()); + response.setContentType("text/html;charset=UTF-8"); + response.getWriter().write(rejectTips); + return; } - else { - filterChain.doFilter(request, response); + // Unirate + if (quotaResponse.getCode() == QuotaResultCode.QuotaResultOk && quotaResponse.getWaitMs() > 0) { + Thread.sleep(quotaResponse.getWaitMs()); } + + filterChain.doFilter(request, response); } catch (Throwable t) { // An exception occurs in the rate limiting API call, @@ -91,4 +118,41 @@ public class QuotaCheckServletFilter extends OncePerRequestFilter { } } + private Map getRequestLabels(HttpServletRequest request, String localNamespace, String localService) { + Map labels = new HashMap<>(); + + // add build in labels + String path = request.getRequestURI(); + if (StringUtils.isNotBlank(path)) { + labels.put(LABEL_METHOD, path); + } + + // add rule expression labels + Map expressionLabels = getRuleExpressionLabels(request, localNamespace, localService); + labels.putAll(expressionLabels); + + // add custom resolved labels + Map customLabels = getCustomResolvedLabels(request); + labels.putAll(customLabels); + + return labels; + } + + private Map getCustomResolvedLabels(HttpServletRequest request) { + if (labelResolver != null) { + try { + return labelResolver.resolve(request); + } + catch (Throwable e) { + LOG.error("resolve custom label failed. resolver = {}", + labelResolver.getClass().getName(), e); + } + } + return Collections.emptyMap(); + } + + private Map getRuleExpressionLabels(HttpServletRequest request, String namespace, String service) { + Set expressionLabels = rateLimitRuleLabelResolver.getExpressionLabelKeys(namespace, service); + return ExpressionLabelUtils.resolve(request, expressionLabels); + } } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelReactiveResolver.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelReactiveResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..4ebcc1921250660de4ccaaba952f24cdd75421fb --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelReactiveResolver.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.spi; + +import java.util.Map; + +import org.springframework.web.server.ServerWebExchange; + +/** + * Resolve custom label from request. The label used for rate limit params. + * + * @author lepdou 2022-03-31 + */ +public interface PolarisRateLimiterLabelReactiveResolver { + + /** + * Resolve custom label from request. + * @param exchange the http request + * @return resolved labels + */ + Map resolve(ServerWebExchange exchange); + +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelServletResolver.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelServletResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..253b67b5c7cded720818b2f292f84da26af25af9 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/spi/PolarisRateLimiterLabelServletResolver.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.spi; + +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +/** + * Resolve custom label from request. The label used for rate limit params. + * + * @author lepdou 2022-03-31 + */ +public interface PolarisRateLimiterLabelServletResolver { + + /** + * Resolve custom label from request. + * @param request the http request + * @return resolved labels + */ + Map resolve(HttpServletRequest request); + +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtils.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..4a67aea017f3c136c35dbb1cbbbf0cbf307f3b6a --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtils.java @@ -0,0 +1,67 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.utils; + +import com.tencent.cloud.common.util.ResourceFileUtils; +import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; +import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.util.StringUtils; + +/** + * Rate limit utils. + * + * @author lepdou 2022-04-20 + */ +public final class RateLimitUtils { + + private static final Logger LOG = LoggerFactory.getLogger(RateLimitUtils.class); + + private RateLimitUtils() { + + } + + public static String getRejectTips(PolarisRateLimitProperties polarisRateLimitProperties) { + String tips = polarisRateLimitProperties.getRejectRequestTips(); + + if (!StringUtils.isEmpty(tips)) { + return tips; + } + + String rejectFilePath = polarisRateLimitProperties.getRejectRequestTipsFilePath(); + if (!StringUtils.isEmpty(rejectFilePath)) { + try { + tips = ResourceFileUtils.readFile(rejectFilePath); + } + catch (Exception e) { + LOG.error("[RateLimit] Read custom reject tips file error. path = {}", + rejectFilePath, e); + } + } + + if (!StringUtils.isEmpty(tips)) { + return tips; + } + + return RateLimitConstant.QUOTA_LIMITED_INFO; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/additional-spring-configuration-metadata.json index b71a17cc4bae9a7860341aa7eba89f6815e78bff..34d80d038c1c7b1af3d7367355a81fe2ff3f7834 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -5,6 +5,30 @@ "type": "java.lang.Boolean", "defaultValue": true, "description": "Enable polaris rate limit or not." + }, + { + "name": "spring.cloud.polaris.ratelimit.rejectRequestTips", + "type": "java.lang.String", + "defaultValue": "", + "description": "Custom tips when reject request." + }, + { + "name": "spring.cloud.polaris.ratelimit.rejectRequestTipsFilePath", + "type": "java.lang.String", + "defaultValue": "", + "description": "Custom tips file path when reject request." + }, + { + "name": "spring.cloud.polaris.ratelimit.rejectHttpCode", + "type": "java.lang.Integer", + "defaultValue": "429", + "description": "Custom http code when reject request." + }, + { + "name": "spring.cloud.polaris.ratelimit.maxQueuingTime", + "type": "java.lang.Long", + "defaultValue": "1000", + "description": "Max queuing time when using unirate." } ] } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring.factories index 89a6c50a1f59641c239b17dd8b5de90b627eb437..563704410a7f8daade013f689406c584d02d8d32 100644 --- a/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/main/resources/META-INF/spring.factories @@ -1,2 +1,6 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.tencent.cloud.polaris.ratelimit.config.RateLimitConfiguration + com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitAutoConfiguration,\ + com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitPropertiesAutoConfiguration,\ + com.tencent.cloud.polaris.ratelimit.endpoint.PolarisRateLimitRuleEndpointAutoConfiguration +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ + com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitPropertiesBootstrapConfiguration diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolverTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolverTest.java new file mode 100644 index 0000000000000000000000000000000000000000..73d39dade1bee14b205b65aa251eee1f15f04745 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/RateLimitRuleLabelResolverTest.java @@ -0,0 +1,101 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit; + +import java.util.Set; + +import com.google.protobuf.StringValue; +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.polaris.client.pb.ModelProto; +import com.tencent.polaris.client.pb.RateLimitProto; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Test for {@link RateLimitRuleLabelResolver}. + * + * @author Haotian Zhang + */ +@RunWith(MockitoJUnitRunner.class) +public class RateLimitRuleLabelResolverTest { + + private ServiceRuleManager serviceRuleManager; + + private RateLimitRuleLabelResolver rateLimitRuleLabelResolver; + + @Before + public void setUp() { + serviceRuleManager = mock(ServiceRuleManager.class); + when(serviceRuleManager.getServiceRateLimitRule(any(), anyString())).thenAnswer(invocationOnMock -> { + String serviceName = invocationOnMock.getArgument(1).toString(); + if (serviceName.equals("TestApp1")) { + return null; + } + else if (serviceName.equals("TestApp2")) { + return RateLimitProto.RateLimit.newBuilder().build(); + } + else if (serviceName.equals("TestApp3")) { + RateLimitProto.Rule rule = RateLimitProto.Rule.newBuilder().build(); + return RateLimitProto.RateLimit.newBuilder().addRules(rule).build(); + } + else { + ModelProto.MatchString matchString = ModelProto.MatchString.newBuilder() + .setType(ModelProto.MatchString.MatchStringType.EXACT) + .setValue(StringValue.of("value")) + .setValueType(ModelProto.MatchString.ValueType.TEXT).build(); + RateLimitProto.Rule rule = RateLimitProto.Rule.newBuilder() + .putLabels("${http.method}", matchString).build(); + return RateLimitProto.RateLimit.newBuilder().addRules(rule).build(); + } + }); + + rateLimitRuleLabelResolver = new RateLimitRuleLabelResolver(serviceRuleManager); + } + + @Test + public void testGetExpressionLabelKeys() { + // rateLimitRule == null + String serviceName = "TestApp1"; + Set labelKeys = rateLimitRuleLabelResolver.getExpressionLabelKeys(null, serviceName); + assertThat(labelKeys).isEmpty(); + + // CollectionUtils.isEmpty(rules) + serviceName = "TestApp2"; + labelKeys = rateLimitRuleLabelResolver.getExpressionLabelKeys(null, serviceName); + assertThat(labelKeys).isEmpty(); + + // CollectionUtils.isEmpty(labels) + serviceName = "TestApp3"; + labelKeys = rateLimitRuleLabelResolver.getExpressionLabelKeys(null, serviceName); + assertThat(labelKeys).isEmpty(); + + // Has labels + serviceName = "TestApp4"; + labelKeys = rateLimitRuleLabelResolver.getExpressionLabelKeys(null, serviceName); + assertThat(labelKeys).isNotEmpty(); + assertThat(labelKeys).contains("${http.method}"); + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..387fe510d16266fbb9e2c44defbf08223952ee5e --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitAutoConfigurationTest.java @@ -0,0 +1,99 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.config; + +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver; +import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckReactiveFilter; +import com.tencent.cloud.polaris.ratelimit.filter.QuotaCheckServletFilter; +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.boot.web.servlet.FilterRegistrationBean; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisRateLimitAutoConfiguration}. + * + * @author Haotian Zhang + */ +public class PolarisRateLimitAutoConfigurationTest { + + private ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner(); + + private WebApplicationContextRunner webApplicationContextRunner = new WebApplicationContextRunner(); + + private ReactiveWebApplicationContextRunner reactiveWebApplicationContextRunner = new ReactiveWebApplicationContextRunner(); + + @Test + public void testNoWebApplication() { + this.applicationContextRunner + .withConfiguration(AutoConfigurations.of( + PolarisContextAutoConfiguration.class, + PolarisRateLimitProperties.class, + PolarisRateLimitAutoConfiguration.class)) + .run(context -> { + assertThat(context).hasSingleBean(LimitAPI.class); + assertThat(context).hasSingleBean(RateLimitRuleLabelResolver.class); + assertThat(context).doesNotHaveBean(PolarisRateLimitAutoConfiguration.QuotaCheckFilterConfig.class); + assertThat(context).doesNotHaveBean(QuotaCheckServletFilter.class); + assertThat(context).doesNotHaveBean(FilterRegistrationBean.class); + assertThat(context).doesNotHaveBean(PolarisRateLimitAutoConfiguration.MetadataReactiveFilterConfig.class); + assertThat(context).doesNotHaveBean(QuotaCheckReactiveFilter.class); + }); + } + + @Test + public void testServletWebApplication() { + this.webApplicationContextRunner + .withConfiguration(AutoConfigurations.of(PolarisContextAutoConfiguration.class, + PolarisRateLimitProperties.class, + PolarisRateLimitAutoConfiguration.class)) + .run(context -> { + assertThat(context).hasSingleBean(LimitAPI.class); + assertThat(context).hasSingleBean(RateLimitRuleLabelResolver.class); + assertThat(context).hasSingleBean(PolarisRateLimitAutoConfiguration.QuotaCheckFilterConfig.class); + assertThat(context).hasSingleBean(QuotaCheckServletFilter.class); + assertThat(context).hasSingleBean(FilterRegistrationBean.class); + assertThat(context).doesNotHaveBean(PolarisRateLimitAutoConfiguration.MetadataReactiveFilterConfig.class); + assertThat(context).doesNotHaveBean(QuotaCheckReactiveFilter.class); + }); + } + + @Test + public void testReactiveWebApplication() { + this.reactiveWebApplicationContextRunner + .withConfiguration(AutoConfigurations.of(PolarisContextAutoConfiguration.class, + PolarisRateLimitProperties.class, + PolarisRateLimitAutoConfiguration.class)) + .run(context -> { + assertThat(context).hasSingleBean(LimitAPI.class); + assertThat(context).hasSingleBean(RateLimitRuleLabelResolver.class); + assertThat(context).doesNotHaveBean(PolarisRateLimitAutoConfiguration.QuotaCheckFilterConfig.class); + assertThat(context).doesNotHaveBean(QuotaCheckServletFilter.class); + assertThat(context).doesNotHaveBean(FilterRegistrationBean.class); + assertThat(context).hasSingleBean(PolarisRateLimitAutoConfiguration.MetadataReactiveFilterConfig.class); + assertThat(context).hasSingleBean(QuotaCheckReactiveFilter.class); + }); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/config/PolarisRibbonAutoConfigurationTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesAutoConfigurationTest.java similarity index 52% rename from spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/config/PolarisRibbonAutoConfigurationTest.java rename to spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesAutoConfigurationTest.java index af4645e6ef0f71f427480974933cc207f3c3f35b..258dd557badb72d8a009a48f9f17956557dd9d99 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/config/PolarisRibbonAutoConfigurationTest.java +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesAutoConfigurationTest.java @@ -15,48 +15,31 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.router.config; +package com.tencent.cloud.polaris.ratelimit.config; -import com.tencent.cloud.polaris.context.PolarisContextConfiguration; -import com.tencent.polaris.router.api.core.RouterAPI; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.runner.ApplicationContextRunner; -import org.springframework.context.annotation.Configuration; -import static com.tencent.polaris.test.common.Consts.PORT; -import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; import static org.assertj.core.api.Assertions.assertThat; /** - * Test for {@link PolarisRibbonAutoConfiguration} + * Test for {@link PolarisRateLimitPropertiesAutoConfiguration}. * * @author Haotian Zhang */ -public class PolarisRibbonAutoConfigurationTest { +public class PolarisRateLimitPropertiesAutoConfigurationTest { private ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(PolarisRibbonTest.class, - PolarisRibbonAutoConfiguration.class, - PolarisContextConfiguration.class)) - .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) - .withPropertyValues("server.port=" + PORT) - .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081"); + .withConfiguration( + AutoConfigurations.of(PolarisRateLimitPropertiesAutoConfiguration.class)); @Test public void testDefaultInitialization() { this.contextRunner.run(context -> { - assertThat(context).hasSingleBean(RouterAPI.class); - assertThat(context).hasSingleBean(PolarisRibbonProperties.class); + assertThat(context).hasSingleBean(PolarisRateLimitProperties.class); + assertThat(context).hasSingleBean(RateLimitConfigModifier.class); }); } - - @Configuration - @EnableAutoConfiguration - static class PolarisRibbonTest { - - } - } diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesBootstrapConfigurationTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesBootstrapConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..71e7334cc295cbca24036aa0d30dd7fee15594b0 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesBootstrapConfigurationTest.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.config; + +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisRateLimitPropertiesBootstrapConfiguration}. + * + * @author Haotian Zhang + */ +public class PolarisRateLimitPropertiesBootstrapConfigurationTest { + + private ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(PolarisRateLimitPropertiesBootstrapConfiguration.class)) + .withPropertyValues("spring.cloud.polaris.enabled=true"); + + @Test + public void testDefaultInitialization() { + this.contextRunner.run(context -> { + assertThat(context).hasSingleBean(PolarisRateLimitProperties.class); + assertThat(context).hasSingleBean(RateLimitConfigModifier.class); + }); + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8279ab0e48f837dbf5668be69d9f70e63417feeb --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/config/PolarisRateLimitPropertiesTest.java @@ -0,0 +1,57 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.config; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisRateLimitProperties}. + * + * @author Haotian Zhang + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = PolarisRateLimitPropertiesTest.TestApplication.class) +@ActiveProfiles("test") +public class PolarisRateLimitPropertiesTest { + + @Autowired + private PolarisRateLimitProperties polarisRateLimitProperties; + + @Test + public void testDefaultInitialization() { + assertThat(polarisRateLimitProperties).isNotNull(); + assertThat(polarisRateLimitProperties.getRejectRequestTips()).isEqualTo("xxx"); + assertThat(polarisRateLimitProperties.getRejectRequestTipsFilePath()).isEqualTo("/index.html"); + assertThat(polarisRateLimitProperties.getRejectHttpCode()).isEqualTo(419); + assertThat(polarisRateLimitProperties.getMaxQueuingTime()).isEqualTo(500L); + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/endpoint/PolarisRateLimitRuleEndpointTests.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/endpoint/PolarisRateLimitRuleEndpointTests.java new file mode 100644 index 0000000000000000000000000000000000000000..d5d7e6fd0f8c0a38da425a3972dfe11d9bd93a57 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/endpoint/PolarisRateLimitRuleEndpointTests.java @@ -0,0 +1,110 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.endpoint; + +import java.util.Map; + +import com.google.protobuf.StringValue; +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; +import com.tencent.polaris.client.pb.ModelProto; +import com.tencent.polaris.client.pb.RateLimitProto; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.context.annotation.Configuration; + +import static com.tencent.polaris.test.common.Consts.NAMESPACE_TEST; +import static com.tencent.polaris.test.common.Consts.PORT; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Test for polaris rete limit rule endpoint. + * + * @author shuiqingliu + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisRateLimitRuleEndpointTests { + + private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() + .withConfiguration(AutoConfigurations.of( + PolarisRateLimitRuleEndpointTests.PolarisRateLimitAutoConfiguration.class, + PolarisRateLimitRuleEndpointAutoConfiguration.class, + PolarisRateLimitAutoConfiguration.class, + PolarisRateLimitAutoConfiguration.class)) + .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) + .withPropertyValues("server.port=" + PORT) + .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081") + .withPropertyValues( + "spring.cloud.polaris.discovery.namespace=" + NAMESPACE_TEST) + .withPropertyValues("spring.cloud.polaris.discovery.token=xxxxxx"); + + private ServiceRuleManager serviceRuleManager; + private PolarisRateLimitProperties polarisRateLimitProperties; + + @Before + public void setUp() { + serviceRuleManager = mock(ServiceRuleManager.class); + when(serviceRuleManager.getServiceRateLimitRule(any(), anyString())).thenAnswer(invocationOnMock -> { + String serviceName = invocationOnMock.getArgument(1).toString(); + if (serviceName.equals("TestApp1")) { + return null; + } + else if (serviceName.equals("TestApp2")) { + return RateLimitProto.RateLimit.newBuilder().build(); + } + else if (serviceName.equals("TestApp3")) { + RateLimitProto.Rule rule = RateLimitProto.Rule.newBuilder().build(); + return RateLimitProto.RateLimit.newBuilder().addRules(rule).build(); + } + else { + ModelProto.MatchString matchString = ModelProto.MatchString.newBuilder() + .setType(ModelProto.MatchString.MatchStringType.EXACT) + .setValue(StringValue.of("value")) + .setValueType(ModelProto.MatchString.ValueType.TEXT).build(); + RateLimitProto.Rule rule = RateLimitProto.Rule.newBuilder() + .putLabels("${http.method}", matchString).build(); + return RateLimitProto.RateLimit.newBuilder().addRules(rule).build(); + } + }); + } + + @Test + public void testPolarisRateLimit() { + this.contextRunner.run(context -> polarisRateLimitProperties = context.getBean(PolarisRateLimitProperties.class)); + PolarisRateLimitRuleEndpoint polarisRateLimitRuleEndpoint = new PolarisRateLimitRuleEndpoint(serviceRuleManager, polarisRateLimitProperties); + Map rateLimit = polarisRateLimitRuleEndpoint.rateLimit("namespaceTest", "TestApp2", "TestApp3"); + assertThat(polarisRateLimitProperties).isEqualTo(rateLimit.get("properties")); + } + + @Configuration + @EnableAutoConfiguration + static class PolarisRateLimitAutoConfiguration { + + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6d3ef804f2bf83f8f9fcac963c9d9c4674fd5945 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckReactiveFilterTest.java @@ -0,0 +1,231 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.filter; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.common.util.ExpressionLabelUtils; +import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver; +import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; +import com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant; +import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelReactiveResolver; +import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import reactor.core.publisher.Mono; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.server.MockServerWebExchange; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilterChain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +/** + * Test for {@link QuotaCheckReactiveFilter}. + * + * @author Haotian Zhang, cheese8, kaiy + */ +@RunWith(MockitoJUnitRunner.class) +@SpringBootTest(classes = QuotaCheckReactiveFilterTest.TestApplication.class, properties = { + "spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp" +}) +public class QuotaCheckReactiveFilterTest { + + private PolarisRateLimiterLabelReactiveResolver labelResolver = exchange -> Collections.singletonMap("ReactiveResolver", "ReactiveResolver"); + + private QuotaCheckReactiveFilter quotaCheckReactiveFilter; + + private static MockedStatic mockedApplicationContextAwareUtils; + private static MockedStatic expressionLabelUtilsMockedStatic; + + @BeforeClass + public static void beforeClass() { + expressionLabelUtilsMockedStatic = mockStatic(ExpressionLabelUtils.class); + when(ExpressionLabelUtils.resolve(any(ServerWebExchange.class), anySet())).thenReturn(Collections.singletonMap("RuleLabelResolver", "RuleLabelResolver")); + + mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn("unit-test"); + } + + @AfterClass + public static void afterClass() { + mockedApplicationContextAwareUtils.close(); + expressionLabelUtilsMockedStatic.close(); + } + + @Before + public void setUp() { + MetadataContext.LOCAL_NAMESPACE = "TEST"; + + LimitAPI limitAPI = mock(LimitAPI.class); + when(limitAPI.getQuota(any(QuotaRequest.class))).thenAnswer(invocationOnMock -> { + String serviceName = ((QuotaRequest) invocationOnMock.getArgument(0)).getService(); + if (serviceName.equals("TestApp1")) { + return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultOk, 0, "QuotaResultOk")); + } + else if (serviceName.equals("TestApp2")) { + return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultOk, 1000, "QuotaResultOk")); + } + else if (serviceName.equals("TestApp3")) { + return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultLimited, 0, "QuotaResultLimited")); + } + else { + return new QuotaResponse(new QuotaResult(null, 0, null)); + } + }); + + PolarisRateLimitProperties polarisRateLimitProperties = new PolarisRateLimitProperties(); + polarisRateLimitProperties.setRejectRequestTips("RejectRequestTips提示消息"); + polarisRateLimitProperties.setRejectHttpCode(419); + + RateLimitRuleLabelResolver rateLimitRuleLabelResolver = mock(RateLimitRuleLabelResolver.class); + when(rateLimitRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString())).thenReturn(Collections.EMPTY_SET); + + this.quotaCheckReactiveFilter = new QuotaCheckReactiveFilter(limitAPI, labelResolver, polarisRateLimitProperties, rateLimitRuleLabelResolver); + } + + @Test + public void testGetOrder() { + assertThat(this.quotaCheckReactiveFilter.getOrder()).isEqualTo(RateLimitConstant.FILTER_ORDER); + } + + @Test + public void testInit() { + quotaCheckReactiveFilter.init(); + try { + Field rejectTips = QuotaCheckReactiveFilter.class.getDeclaredField("rejectTips"); + rejectTips.setAccessible(true); + assertThat(rejectTips.get(quotaCheckReactiveFilter)).isEqualTo("RejectRequestTips提示消息"); + } + catch (NoSuchFieldException | IllegalAccessException e) { + fail("Exception encountered.", e); + } + } + + @Test + public void testGetRuleExpressionLabels() { + try { + Method getCustomResolvedLabels = QuotaCheckReactiveFilter.class.getDeclaredMethod("getCustomResolvedLabels", ServerWebExchange.class); + getCustomResolvedLabels.setAccessible(true); + + // Mock request + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost:8080/test").build(); + ServerWebExchange exchange = MockServerWebExchange.from(request); + + // labelResolver != null + Map result = (Map) getCustomResolvedLabels.invoke(quotaCheckReactiveFilter, exchange); + assertThat(result.size()).isEqualTo(1); + assertThat(result.get("ReactiveResolver")).isEqualTo("ReactiveResolver"); + + // throw exception + PolarisRateLimiterLabelReactiveResolver exceptionLabelResolver = new PolarisRateLimiterLabelReactiveResolver() { + @Override + public Map resolve(ServerWebExchange exchange) { + throw new RuntimeException("Mock exception."); + } + }; + quotaCheckReactiveFilter = new QuotaCheckReactiveFilter(null, exceptionLabelResolver, null, null); + result = (Map) getCustomResolvedLabels.invoke(quotaCheckReactiveFilter, exchange); + assertThat(result.size()).isEqualTo(0); + + // labelResolver == null + quotaCheckReactiveFilter = new QuotaCheckReactiveFilter(null, null, null, null); + result = (Map) getCustomResolvedLabels.invoke(quotaCheckReactiveFilter, exchange); + assertThat(result.size()).isEqualTo(0); + + getCustomResolvedLabels.setAccessible(false); + } + catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail("Exception encountered.", e); + } + } + + @Test + public void testFilter() { + // Create mock WebFilterChain + WebFilterChain webFilterChain = serverWebExchange -> Mono.empty(); + + // Mock request + MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost:8080/test").build(); + ServerWebExchange exchange = MockServerWebExchange.from(request); + + quotaCheckReactiveFilter.init(); + + // Pass + MetadataContext.LOCAL_SERVICE = "TestApp1"; + quotaCheckReactiveFilter.filter(exchange, webFilterChain); + + // Unirate waiting 1000ms + MetadataContext.LOCAL_SERVICE = "TestApp2"; + long startTimestamp = System.currentTimeMillis(); + CountDownLatch countDownLatch = new CountDownLatch(1); + quotaCheckReactiveFilter.filter(exchange, webFilterChain).subscribe(e -> { }, t -> { }, countDownLatch::countDown); + try { + countDownLatch.await(); + } + catch (InterruptedException e) { + fail("Exception encountered.", e); + } + assertThat(System.currentTimeMillis() - startTimestamp).isGreaterThanOrEqualTo(1000L); + + // Rate limited + MetadataContext.LOCAL_SERVICE = "TestApp3"; + quotaCheckReactiveFilter.filter(exchange, webFilterChain); + ServerHttpResponse response = exchange.getResponse(); + assertThat(response.getRawStatusCode()).isEqualTo(419); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INSUFFICIENT_SPACE_ON_RESOURCE); + + // Exception + MetadataContext.LOCAL_SERVICE = "TestApp4"; + quotaCheckReactiveFilter.filter(exchange, webFilterChain); + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7ff8e8793f20f7de66fa5c3ba77c36cdb3a33fe2 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/filter/QuotaCheckServletFilterTest.java @@ -0,0 +1,240 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.filter; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.common.util.ExpressionLabelUtils; +import com.tencent.cloud.polaris.ratelimit.RateLimitRuleLabelResolver; +import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; +import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver; +import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.web.server.ServerWebExchange; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anySet; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +/** + * Test for {@link QuotaCheckServletFilter}. + * + * @author Haotian Zhang, cheese8 + */ +@RunWith(MockitoJUnitRunner.class) +@SpringBootTest(classes = QuotaCheckServletFilterTest.TestApplication.class, properties = { + "spring.cloud.polaris.namespace=Test", "spring.cloud.polaris.service=TestApp" +}) +public class QuotaCheckServletFilterTest { + + private PolarisRateLimiterLabelServletResolver labelResolver = exchange -> Collections.singletonMap("ServletResolver", "ServletResolver"); + + private QuotaCheckServletFilter quotaCheckServletFilter; + + private QuotaCheckServletFilter quotaCheckWithHtmlRejectTipsServletFilter; + + private static MockedStatic mockedApplicationContextAwareUtils; + private static MockedStatic expressionLabelUtilsMockedStatic; + @BeforeClass + public static void beforeClass() { + expressionLabelUtilsMockedStatic = mockStatic(ExpressionLabelUtils.class); + when(ExpressionLabelUtils.resolve(any(ServerWebExchange.class), anySet())).thenReturn(Collections.singletonMap("RuleLabelResolver", "RuleLabelResolver")); + + mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn("unit-test"); + + } + + @AfterClass + public static void afterClass() throws Exception { + mockedApplicationContextAwareUtils.close(); + expressionLabelUtilsMockedStatic.close(); + } + + @Before + public void setUp() { + MetadataContext.LOCAL_NAMESPACE = "TEST"; + + LimitAPI limitAPI = mock(LimitAPI.class); + when(limitAPI.getQuota(any(QuotaRequest.class))).thenAnswer(invocationOnMock -> { + String serviceName = ((QuotaRequest) invocationOnMock.getArgument(0)).getService(); + if (serviceName.equals("TestApp1")) { + return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultOk, 0, "QuotaResultOk")); + } + else if (serviceName.equals("TestApp2")) { + return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultOk, 1000, "QuotaResultOk")); + } + else if (serviceName.equals("TestApp3")) { + return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultLimited, 0, "QuotaResultLimited")); + } + else { + return new QuotaResponse(new QuotaResult(null, 0, null)); + } + }); + + PolarisRateLimitProperties polarisRateLimitProperties = new PolarisRateLimitProperties(); + polarisRateLimitProperties.setRejectRequestTips("RejectRequestTips提示消息"); + polarisRateLimitProperties.setRejectHttpCode(419); + + PolarisRateLimitProperties polarisRateLimitWithHtmlRejectTipsProperties = new PolarisRateLimitProperties(); + polarisRateLimitWithHtmlRejectTipsProperties.setRejectRequestTips("

RejectRequestTips提示消息

"); + polarisRateLimitWithHtmlRejectTipsProperties.setRejectHttpCode(419); + + RateLimitRuleLabelResolver rateLimitRuleLabelResolver = mock(RateLimitRuleLabelResolver.class); + when(rateLimitRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString())).thenReturn(Collections.EMPTY_SET); + + this.quotaCheckServletFilter = new QuotaCheckServletFilter(limitAPI, labelResolver, polarisRateLimitProperties, rateLimitRuleLabelResolver); + this.quotaCheckWithHtmlRejectTipsServletFilter = new QuotaCheckServletFilter(limitAPI, labelResolver, polarisRateLimitWithHtmlRejectTipsProperties, rateLimitRuleLabelResolver); + } + + @Test + public void testInit() { + quotaCheckServletFilter.init(); + try { + Field rejectTips = QuotaCheckServletFilter.class.getDeclaredField("rejectTips"); + rejectTips.setAccessible(true); + assertThat(rejectTips.get(quotaCheckServletFilter)).isEqualTo("RejectRequestTips提示消息"); + } + catch (NoSuchFieldException | IllegalAccessException e) { + fail("Exception encountered.", e); + } + quotaCheckWithHtmlRejectTipsServletFilter.init(); + try { + Field rejectTips = QuotaCheckServletFilter.class.getDeclaredField("rejectTips"); + rejectTips.setAccessible(true); + assertThat(rejectTips.get(quotaCheckWithHtmlRejectTipsServletFilter)).isEqualTo("

RejectRequestTips提示消息

"); + } + catch (NoSuchFieldException | IllegalAccessException e) { + fail("Exception encountered.", e); + } + } + + @Test + public void testGetRuleExpressionLabels() { + try { + Method getCustomResolvedLabels = QuotaCheckServletFilter.class.getDeclaredMethod("getCustomResolvedLabels", HttpServletRequest.class); + getCustomResolvedLabels.setAccessible(true); + + // Mock request + MockHttpServletRequest request = new MockHttpServletRequest(); + + // labelResolver != null + Map result = (Map) getCustomResolvedLabels.invoke(quotaCheckServletFilter, request); + assertThat(result.size()).isEqualTo(1); + assertThat(result.get("ServletResolver")).isEqualTo("ServletResolver"); + + // throw exception + PolarisRateLimiterLabelServletResolver exceptionLabelResolver = request1 -> { + throw new RuntimeException("Mock exception."); + }; + quotaCheckServletFilter = new QuotaCheckServletFilter(null, exceptionLabelResolver, null, null); + result = (Map) getCustomResolvedLabels.invoke(quotaCheckServletFilter, request); + assertThat(result.size()).isEqualTo(0); + + // labelResolver == null + quotaCheckServletFilter = new QuotaCheckServletFilter(null, null, null, null); + result = (Map) getCustomResolvedLabels.invoke(quotaCheckServletFilter, request); + assertThat(result.size()).isEqualTo(0); + + getCustomResolvedLabels.setAccessible(false); + } + catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + fail("Exception encountered.", e); + } + } + + @Test + public void testDoFilterInternal() { + // Create mock FilterChain + FilterChain filterChain = (servletRequest, servletResponse) -> { + + }; + + // Mock request + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + + quotaCheckServletFilter.init(); + try { + // Pass + MetadataContext.LOCAL_SERVICE = "TestApp1"; + quotaCheckServletFilter.doFilterInternal(request, response, filterChain); + + // Unirate waiting 1000ms + MetadataContext.LOCAL_SERVICE = "TestApp2"; + long startTimestamp = System.currentTimeMillis(); + quotaCheckServletFilter.doFilterInternal(request, response, filterChain); + assertThat(System.currentTimeMillis() - startTimestamp).isGreaterThanOrEqualTo(1000L); + + // Rate limited + MetadataContext.LOCAL_SERVICE = "TestApp3"; + quotaCheckServletFilter.doFilterInternal(request, response, filterChain); + assertThat(response.getStatus()).isEqualTo(419); + assertThat(response.getContentAsString()).isEqualTo("RejectRequestTips提示消息"); + + quotaCheckWithHtmlRejectTipsServletFilter.doFilterInternal(request, response, filterChain); + assertThat(response.getStatus()).isEqualTo(419); + assertThat(response.getContentAsString()).isEqualTo("RejectRequestTips提示消息"); + + + // Exception + MetadataContext.LOCAL_SERVICE = "TestApp4"; + quotaCheckServletFilter.doFilterInternal(request, response, filterChain); + } + catch (ServletException | IOException e) { + fail("Exception encountered.", e); + } + } + + @SpringBootApplication + protected static class TestApplication { + + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d679381cb6f13ae9a0bfa78dd4377b73f84ace7d --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/QuotaCheckUtilsTest.java @@ -0,0 +1,95 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.utils; + +import com.tencent.polaris.api.plugin.ratelimiter.QuotaResult; +import com.tencent.polaris.ratelimit.api.core.LimitAPI; +import com.tencent.polaris.ratelimit.api.rpc.QuotaRequest; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResponse; +import com.tencent.polaris.ratelimit.api.rpc.QuotaResultCode; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Test for {@link QuotaCheckUtils}. + * + * @author Haotian Zhang + */ +@RunWith(MockitoJUnitRunner.class) +public class QuotaCheckUtilsTest { + + private LimitAPI limitAPI; + + @Before + public void setUp() { + limitAPI = mock(LimitAPI.class); + when(limitAPI.getQuota(any(QuotaRequest.class))).thenAnswer(invocationOnMock -> { + String serviceName = ((QuotaRequest) invocationOnMock.getArgument(0)).getService(); + if (serviceName.equals("TestApp1")) { + return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultOk, 0, "QuotaResultOk")); + } + else if (serviceName.equals("TestApp2")) { + return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultOk, 1000, "QuotaResultOk")); + } + else if (serviceName.equals("TestApp3")) { + return new QuotaResponse(new QuotaResult(QuotaResult.Code.QuotaResultLimited, 0, "QuotaResultLimited")); + } + else { + throw new RuntimeException("Mock exception."); + } + }); + } + + @Test + public void testGetQuota() { + // Pass + String serviceName = "TestApp1"; + QuotaResponse quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, null, null); + assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultOk); + assertThat(quotaResponse.getWaitMs()).isEqualTo(0); + assertThat(quotaResponse.getInfo()).isEqualTo("QuotaResultOk"); + + // Unirate waiting 1000ms + serviceName = "TestApp2"; + quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, null, null); + assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultOk); + assertThat(quotaResponse.getWaitMs()).isEqualTo(1000); + assertThat(quotaResponse.getInfo()).isEqualTo("QuotaResultOk"); + + // Rate limited + serviceName = "TestApp3"; + quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, null, null); + assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultLimited); + assertThat(quotaResponse.getWaitMs()).isEqualTo(0); + assertThat(quotaResponse.getInfo()).isEqualTo("QuotaResultLimited"); + + // Exception + serviceName = "TestApp4"; + quotaResponse = QuotaCheckUtils.getQuota(limitAPI, null, serviceName, 1, null, null); + assertThat(quotaResponse.getCode()).isEqualTo(QuotaResultCode.QuotaResultOk); + assertThat(quotaResponse.getWaitMs()).isEqualTo(0); + assertThat(quotaResponse.getInfo()).isEqualTo("get quota failed"); + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtilsTest.java b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..351e0b737dbbfd4ccc6f1f62092e212f107424e4 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/java/com/tencent/cloud/polaris/ratelimit/utils/RateLimitUtilsTest.java @@ -0,0 +1,75 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ratelimit.utils; + +import java.io.IOException; + +import com.tencent.cloud.common.util.ResourceFileUtils; +import com.tencent.cloud.polaris.ratelimit.config.PolarisRateLimitProperties; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import static com.tencent.cloud.polaris.ratelimit.constant.RateLimitConstant.QUOTA_LIMITED_INFO; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +/** + * Test for {@link RateLimitUtils}. + * + * @author Haotian Zhang + */ +@RunWith(MockitoJUnitRunner.class) +public class RateLimitUtilsTest { + + @BeforeClass + public static void beforeClass() throws IOException { + mockStatic(ResourceFileUtils.class); + when(ResourceFileUtils.readFile(anyString())).thenAnswer(invocation -> { + String rejectFilePath = invocation.getArgument(0).toString(); + if (rejectFilePath.equals("exception.html")) { + throw new IOException("Mock exceptions"); + } + else { + return "RejectRequestTips"; + } + }); + } + + @Test + public void testGetRejectTips() { + PolarisRateLimitProperties polarisRateLimitProperties = new PolarisRateLimitProperties(); + + // RejectRequestTips + polarisRateLimitProperties.setRejectRequestTips("RejectRequestTips"); + assertThat(RateLimitUtils.getRejectTips(polarisRateLimitProperties)).isEqualTo("RejectRequestTips"); + + // RejectRequestTipsFilePath + polarisRateLimitProperties.setRejectRequestTips(null); + polarisRateLimitProperties.setRejectRequestTipsFilePath("reject-tips.html"); + assertThat(RateLimitUtils.getRejectTips(polarisRateLimitProperties)).isEqualTo("RejectRequestTips"); + + // RejectRequestTipsFilePath with Exception + polarisRateLimitProperties.setRejectRequestTips(null); + polarisRateLimitProperties.setRejectRequestTipsFilePath("exception.html"); + assertThat(RateLimitUtils.getRejectTips(polarisRateLimitProperties)).isEqualTo(QUOTA_LIMITED_INFO); + } +} diff --git a/spring-cloud-starter-tencent-polaris-ratelimit/src/test/resources/application-test.properties b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/resources/application-test.properties new file mode 100644 index 0000000000000000000000000000000000000000..236bb29c174a8204cba8c821342f9b5529b984e0 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-ratelimit/src/test/resources/application-test.properties @@ -0,0 +1,4 @@ +spring.cloud.polaris.ratelimit.rejectRequestTips=xxx +spring.cloud.polaris.ratelimit.rejectRequestTipsFilePath=/index.html +spring.cloud.polaris.ratelimit.rejectHttpCode=419 +spring.cloud.polaris.ratelimit.maxQueuingTime=500 diff --git a/spring-cloud-starter-tencent-polaris-router/pom.xml b/spring-cloud-starter-tencent-polaris-router/pom.xml index cffa4f08445963f3d304cb6bf3246062c7d05720..1424f98b5c1199a0d3cac678f63976473c7c40a4 100644 --- a/spring-cloud-starter-tencent-polaris-router/pom.xml +++ b/spring-cloud-starter-tencent-polaris-router/pom.xml @@ -17,33 +17,71 @@ com.tencent.cloud - spring-cloud-tencent-polaris-context + spring-cloud-tencent-polaris-loadbalancer + + + com.tencent.cloud + spring-cloud-starter-tencent-metadata-transfer com.tencent.polaris - polaris-router-factory + router-rule + + + com.tencent.polaris + router-metadata + + + com.tencent.polaris + router-nearby + org.springframework.cloud - spring-cloud-starter-netflix-ribbon + spring-cloud-starter-openfeign + true - com.tencent.polaris - polaris-test-common - test - - - - - org.springframework.boot - spring-boot-starter-test - test - - + org.springframework.boot + spring-boot-starter-web + true + + + + org.springframework.cloud + spring-cloud-gateway-server + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.mockito + mockito-inline + test + + + + org.mockito + mockito-core + test + + + + net.bytebuddy + byte-buddy + test + + + diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java new file mode 100644 index 0000000000000000000000000000000000000000..4be83e682993953b34109623e982fa323a7e560f --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRule.java @@ -0,0 +1,222 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.AbstractLoadBalancerRule; +import com.netflix.loadbalancer.AvailabilityFilteringRule; +import com.netflix.loadbalancer.BestAvailableRule; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.RandomRule; +import com.netflix.loadbalancer.RetryRule; +import com.netflix.loadbalancer.RoundRobinRule; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.WeightedResponseTimeRule; +import com.netflix.loadbalancer.ZoneAvoidanceRule; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.pojo.PolarisServer; +import com.tencent.cloud.polaris.loadbalancer.LoadBalancerUtils; +import com.tencent.cloud.polaris.loadbalancer.PolarisWeightedRule; +import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties; +import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties; +import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties; +import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInfo; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.plugins.router.metadata.MetadataRouter; +import com.tencent.polaris.plugins.router.nearby.NearbyRouter; +import com.tencent.polaris.plugins.router.rule.RuleBasedRouter; +import com.tencent.polaris.router.api.core.RouterAPI; +import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest; +import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse; + +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +/** + * + * Service routing entrance. + * + * Rule routing needs to rely on request parameters for server filtering, + * and {@link com.netflix.loadbalancer.ServerListFilter#getFilteredListOfServers(List)} + * The interface cannot obtain the context object of the request granularity, + * so the routing capability cannot be achieved through ServerListFilter. + * + * And {@link com.netflix.loadbalancer.IRule#choose(Object)} provides the ability to pass in context parameters, + * so routing capabilities are implemented through IRule. + * + * @author Haotian Zhang, lepdou + */ +public class PolarisLoadBalancerCompositeRule extends AbstractLoadBalancerRule { + + final static String STRATEGY_RANDOM = "random"; + final static String STRATEGY_ROUND_ROBIN = "roundRobin"; + final static String STRATEGY_WEIGHT = "polarisWeighted"; + final static String STRATEGY_RETRY = "retry"; + final static String STRATEGY_RESPONSE_TIME_WEIGHTED = "responseTimeWeighted"; + final static String STRATEGY_BEST_AVAILABLE = "bestAvailable"; + final static String STRATEGY_ZONE_AVOIDANCE = "zoneAvoidance"; + final static String STRATEGY_AVAILABILITY_FILTERING = "availabilityFilteringRule"; + + private final PolarisLoadBalancerProperties loadBalancerProperties; + private final PolarisNearByRouterProperties polarisNearByRouterProperties; + private final PolarisMetadataRouterProperties polarisMetadataRouterProperties; + private final PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties; + private final RouterAPI routerAPI; + + private final AbstractLoadBalancerRule delegateRule; + + public PolarisLoadBalancerCompositeRule(RouterAPI routerAPI, + PolarisLoadBalancerProperties polarisLoadBalancerProperties, + PolarisNearByRouterProperties polarisNearByRouterProperties, + PolarisMetadataRouterProperties polarisMetadataRouterProperties, + PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties, + IClientConfig iClientConfig) { + this.routerAPI = routerAPI; + this.polarisNearByRouterProperties = polarisNearByRouterProperties; + this.loadBalancerProperties = polarisLoadBalancerProperties; + this.polarisMetadataRouterProperties = polarisMetadataRouterProperties; + this.polarisRuleBasedRouterProperties = polarisRuleBasedRouterProperties; + + delegateRule = getRule(); + delegateRule.initWithNiwsConfig(iClientConfig); + } + + @Override + public void initWithNiwsConfig(IClientConfig clientConfig) { + } + + @Override + public Server choose(Object key) { + // 1. get all servers + List allServers = getLoadBalancer().getReachableServers(); + if (CollectionUtils.isEmpty(allServers)) { + return null; + } + + // 2. filter by router + List serversAfterRouter = doRouter(allServers, key); + + // 3. filter by load balance. + // A LoadBalancer needs to be regenerated for each request, + // because the list of servers may be different after filtered by router + ILoadBalancer loadBalancer = new SimpleLoadBalancer(); + loadBalancer.addServers(serversAfterRouter); + delegateRule.setLoadBalancer(loadBalancer); + + return delegateRule.choose(key); + } + + List doRouter(List allServers, Object key) { + ServiceInstances serviceInstances = LoadBalancerUtils.transferServersToServiceInstances(allServers); + + // filter instance by routers + ProcessRoutersRequest processRoutersRequest = buildProcessRoutersRequest(serviceInstances, key); + + ProcessRoutersResponse processRoutersResponse = routerAPI.processRouters(processRoutersRequest); + + List filteredInstances = new ArrayList<>(); + ServiceInstances filteredServiceInstances = processRoutersResponse.getServiceInstances(); + for (Instance instance : filteredServiceInstances.getInstances()) { + filteredInstances.add(new PolarisServer(serviceInstances, instance)); + } + return filteredInstances; + } + + ProcessRoutersRequest buildProcessRoutersRequest(ServiceInstances serviceInstances, Object key) { + ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest(); + processRoutersRequest.setDstInstances(serviceInstances); + + // metadata router + if (polarisMetadataRouterProperties.isEnabled()) { + Map transitiveLabels = getRouterLabels(key, PolarisRouterContext.TRANSITIVE_LABELS); + processRoutersRequest.putRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA, transitiveLabels); + } + + // nearby router + if (polarisNearByRouterProperties.isEnabled()) { + Map nearbyRouterMetadata = new HashMap<>(); + nearbyRouterMetadata.put(NearbyRouter.ROUTER_ENABLED, "true"); + processRoutersRequest.putRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY, nearbyRouterMetadata); + } + + // rule based router + // set dynamic switch for rule based router + boolean ruleBasedRouterEnabled = polarisRuleBasedRouterProperties.isEnabled(); + Map ruleRouterMetadata = new HashMap<>(); + ruleRouterMetadata.put(RuleBasedRouter.ROUTER_ENABLED, String.valueOf(ruleBasedRouterEnabled)); + processRoutersRequest.putRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED, ruleRouterMetadata); + + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.setNamespace(MetadataContext.LOCAL_NAMESPACE); + serviceInfo.setService(MetadataContext.LOCAL_SERVICE); + + if (ruleBasedRouterEnabled) { + Map ruleRouterLabels = getRouterLabels(key, PolarisRouterContext.RULE_ROUTER_LABELS); + // The label information that the rule based routing depends on + // is placed in the metadata of the source service for transmission. + // Later, can consider putting it in routerMetadata like other routers. + serviceInfo.setMetadata(ruleRouterLabels); + } + + processRoutersRequest.setSourceService(serviceInfo); + + return processRoutersRequest; + } + + private Map getRouterLabels(Object key, String type) { + if (key instanceof PolarisRouterContext) { + return ((PolarisRouterContext) key).getLabels(type); + } + return Collections.emptyMap(); + } + + public AbstractLoadBalancerRule getRule() { + String loadBalanceStrategy = loadBalancerProperties.getStrategy(); + if (StringUtils.isEmpty(loadBalanceStrategy)) { + return new ZoneAvoidanceRule(); + } + switch (loadBalanceStrategy) { + case STRATEGY_RANDOM: + return new RandomRule(); + case STRATEGY_WEIGHT: + return new PolarisWeightedRule(routerAPI); + case STRATEGY_RETRY: + return new RetryRule(); + case STRATEGY_RESPONSE_TIME_WEIGHTED: + return new WeightedResponseTimeRule(); + case STRATEGY_BEST_AVAILABLE: + return new BestAvailableRule(); + case STRATEGY_ROUND_ROBIN: + return new RoundRobinRule(); + case STRATEGY_AVAILABILITY_FILTERING: + return new AvailabilityFilteringRule(); + case STRATEGY_ZONE_AVOIDANCE: + default: + return new ZoneAvoidanceRule(); + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java new file mode 100644 index 0000000000000000000000000000000000000000..114548722ba6db4e40f41b03a5d7f514b4761516 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRouterContext.java @@ -0,0 +1,62 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.util.CollectionUtils; + +/** + * the context for router. + * + *@author lepdou 2022-05-17 + */ +public class PolarisRouterContext { + + /** + * the label for rule router. + */ + public static final String RULE_ROUTER_LABELS = "ruleRouter"; + /** + * transitive labels. + */ + public static final String TRANSITIVE_LABELS = "transitive"; + + private Map> labels; + + public Map getLabels(String labelType) { + if (CollectionUtils.isEmpty(labels)) { + return Collections.emptyMap(); + } + Map subLabels = labels.get(labelType); + if (CollectionUtils.isEmpty(subLabels)) { + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(subLabels); + } + + public void setLabels(String labelType, Map subLabels) { + if (this.labels == null) { + this.labels = new HashMap<>(); + } + labels.put(labelType, subLabels); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRoutingLoadBalancer.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRoutingLoadBalancer.java deleted file mode 100644 index e1015097963f3378f44d3b906623a2d45b7be58e..0000000000000000000000000000000000000000 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/PolarisRoutingLoadBalancer.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * 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.tencent.cloud.polaris.router; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import com.netflix.client.config.IClientConfig; -import com.netflix.loadbalancer.DynamicServerListLoadBalancer; -import com.netflix.loadbalancer.IPing; -import com.netflix.loadbalancer.IRule; -import com.netflix.loadbalancer.PollingServerListUpdater; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; -import com.tencent.cloud.common.constant.MetadataConstant.SystemMetadataKey; -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.metadata.MetadataContextHolder; -import com.tencent.cloud.common.pojo.PolarisServer; -import com.tencent.polaris.api.pojo.DefaultInstance; -import com.tencent.polaris.api.pojo.DefaultServiceInstances; -import com.tencent.polaris.api.pojo.Instance; -import com.tencent.polaris.api.pojo.ServiceInfo; -import com.tencent.polaris.api.pojo.ServiceInstances; -import com.tencent.polaris.api.pojo.ServiceKey; -import com.tencent.polaris.router.api.core.RouterAPI; -import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest; -import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse; -import org.apache.commons.lang.StringUtils; - -import org.springframework.util.CollectionUtils; - -/** - * Routing load balancer of polaris. - * - * @author Haotian Zhang - */ -public class PolarisRoutingLoadBalancer extends DynamicServerListLoadBalancer { - - private final RouterAPI routerAPI; - - public PolarisRoutingLoadBalancer(IClientConfig config, IRule rule, IPing ping, - ServerList serverList, RouterAPI routerAPI) { - super(config, rule, ping, serverList, null, new PollingServerListUpdater()); - this.routerAPI = routerAPI; - } - - @Override - public List getReachableServers() { - List allServers = super.getAllServers(); - if (CollectionUtils.isEmpty(allServers)) { - return allServers; - } - ServiceInstances serviceInstances = null; - if (allServers.get(0) instanceof PolarisServer) { - serviceInstances = ((PolarisServer) allServers.get(0)).getServiceInstances(); - } - else { - String serviceName; - // notice the difference between different service registries - if (StringUtils.isNotBlank(allServers.get(0).getMetaInfo().getServiceIdForDiscovery())) { - serviceName = allServers.get(0).getMetaInfo().getServiceIdForDiscovery(); - } - else { - serviceName = allServers.get(0).getMetaInfo().getAppName(); - } - if (StringUtils.isBlank(serviceName)) { - throw new IllegalStateException( - "PolarisRoutingLoadBalancer only Server with AppName or ServiceIdForDiscovery attribute"); - } - ServiceKey serviceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, serviceName); - List instances = new ArrayList<>(8); - for (Server server : allServers) { - DefaultInstance instance = new DefaultInstance(); - instance.setNamespace(MetadataContext.LOCAL_NAMESPACE); - instance.setService(serviceName); - instance.setHealthy(server.isAlive()); - instance.setProtocol(server.getScheme()); - instance.setId(server.getId()); - instance.setHost(server.getHost()); - instance.setPort(server.getPort()); - instance.setZone(server.getZone()); - instance.setWeight(100); - instances.add(instance); - } - serviceInstances = new DefaultServiceInstances(serviceKey, instances); - } - ProcessRoutersRequest processRoutersRequest = new ProcessRoutersRequest(); - processRoutersRequest.setDstInstances(serviceInstances); - String srcNamespace = MetadataContext.LOCAL_NAMESPACE; - String srcService = MetadataContext.LOCAL_SERVICE; - Map transitiveCustomMetadata = MetadataContextHolder.get().getAllTransitiveCustomMetadata(); - String method = MetadataContextHolder.get().getSystemMetadata(SystemMetadataKey.PEER_PATH); - processRoutersRequest.setMethod(method); - if (StringUtils.isNotBlank(srcNamespace) && StringUtils.isNotBlank(srcService)) { - ServiceInfo serviceInfo = new ServiceInfo(); - serviceInfo.setNamespace(srcNamespace); - serviceInfo.setService(srcService); - serviceInfo.setMetadata(transitiveCustomMetadata); - processRoutersRequest.setSourceService(serviceInfo); - } - ProcessRoutersResponse processRoutersResponse = routerAPI - .processRouters(processRoutersRequest); - ServiceInstances filteredServiceInstances = processRoutersResponse - .getServiceInstances(); - List filteredInstances = new ArrayList<>(); - for (Instance instance : filteredServiceInstances.getInstances()) { - filteredInstances.add(new PolarisServer(serviceInstances, instance)); - } - return filteredInstances; - } - - @Override - public List getAllServers() { - return getReachableServers(); - } - -} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterConstants.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..6437c83e877aee488096694223d2f40627b161e7 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterConstants.java @@ -0,0 +1,32 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router; + +/** + * Router constants. + * + *@author lepdou 2022-05-17 + */ +public class RouterConstants { + + /** + * the header of router label. + */ + public static final String ROUTER_LABEL_HEADER = "internal-router-label"; +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterRuleLabelResolver.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterRuleLabelResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..5ab6e549fa43bde4875677fdf57d8f227f0c6a60 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/RouterRuleLabelResolver.java @@ -0,0 +1,75 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.common.util.ExpressionLabelUtils; +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.polaris.client.pb.ModelProto; +import com.tencent.polaris.client.pb.RoutingProto; + +import org.springframework.util.CollectionUtils; + +/** + * Resolve label expressions from routing rules. + * @author lepdou 2022-05-19 + */ +public class RouterRuleLabelResolver { + + private final ServiceRuleManager serviceRuleManager; + + public RouterRuleLabelResolver(ServiceRuleManager serviceRuleManager) { + this.serviceRuleManager = serviceRuleManager; + } + + public Set getExpressionLabelKeys(String namespace, String sourceService, String dstService) { + List rules = serviceRuleManager.getServiceRouterRule(namespace, sourceService, dstService); + + if (CollectionUtils.isEmpty(rules)) { + return Collections.emptySet(); + } + + Set expressionLabels = new HashSet<>(); + + for (RoutingProto.Route rule : rules) { + List sources = rule.getSourcesList(); + if (CollectionUtils.isEmpty(sources)) { + continue; + } + for (RoutingProto.Source source : sources) { + Map labels = source.getMetadataMap(); + if (CollectionUtils.isEmpty(labels)) { + continue; + } + for (String labelKey : labels.keySet()) { + if (ExpressionLabelUtils.isExpressionLabel(labelKey)) { + expressionLabels.add(labelKey); + } + } + } + } + + return expressionLabels; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/SimpleLoadBalancer.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/SimpleLoadBalancer.java new file mode 100644 index 0000000000000000000000000000000000000000..d509c1bbf11546cafe5b4ba42670684f2721c3a9 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/SimpleLoadBalancer.java @@ -0,0 +1,73 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router; + +import java.util.Collections; +import java.util.List; + +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; + +/** + * Simple load balancer only for getting and setting servers. + * + *@author lepdou 2022-05-17 + */ +public class SimpleLoadBalancer implements ILoadBalancer { + private List servers; + + @Override + public void addServers(List newServers) { + this.servers = newServers; + } + + @Override + public Server chooseServer(Object key) { + return null; + } + + @Override + public void markServerDown(Server server) { + + } + + @Override + public List getServerList(boolean availableOnly) { + if (servers == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(servers); + } + + @Override + public List getReachableServers() { + if (servers == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(servers); + } + + @Override + public List getAllServers() { + if (servers == null) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(servers); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerClientFilterBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerClientFilterBeanPostProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..5111bda51d654c4fe38e71c680e3c903bd0a8ccf --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerClientFilterBeanPostProcessor.java @@ -0,0 +1,66 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.beanprocessor; + +import java.util.List; + +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.util.BeanFactoryUtils; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.scg.PolarisLoadBalancerClientFilter; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.cloud.gateway.config.LoadBalancerProperties; +import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter; + +/** + * Replace LoadBalancerClientFilter with PolarisLoadBalancerClientFilter. + *@author lepdou 2022-06-15 + */ +public class LoadBalancerClientFilterBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { + + private BeanFactory factory; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.factory = beanFactory; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof LoadBalancerClientFilter) { + // Support spring cloud gateway router. + // Replaces the default LoadBalancerClientFilter implementation and returns a custom PolarisLoadBalancerClientFilter + LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class); + LoadBalancerProperties loadBalancerProperties = this.factory.getBean(LoadBalancerProperties.class); + List routerLabelResolvers = BeanFactoryUtils.getBeans(factory, RouterLabelResolver.class); + MetadataLocalProperties metadataLocalProperties = this.factory.getBean(MetadataLocalProperties.class); + RouterRuleLabelResolver routerRuleLabelResolver = this.factory.getBean(RouterRuleLabelResolver.class); + + return new PolarisLoadBalancerClientFilter(loadBalancerClient, loadBalancerProperties, + metadataLocalProperties, routerRuleLabelResolver, routerLabelResolvers); + } + return bean; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java new file mode 100644 index 0000000000000000000000000000000000000000..0b383a6828d2a8f1254a4b92fb28fde7bf6db512 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/beanprocessor/LoadBalancerInterceptorBeanPostProcessor.java @@ -0,0 +1,67 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.beanprocessor; + +import java.util.List; + +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.util.BeanFactoryUtils; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.resttemplate.PolarisLoadBalancerInterceptor; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor; +import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory; + +/** + * Replace LoadBalancerInterceptor with PolarisLoadBalancerInterceptor. + * PolarisLoadBalancerInterceptor can pass routing context information. + *@author lepdou 2022-05-18 + */ +public class LoadBalancerInterceptorBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { + + private BeanFactory factory; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.factory = beanFactory; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof LoadBalancerInterceptor) { + // Support rest template router. + // Replaces the default LoadBalancerInterceptor implementation and returns a custom PolarisLoadBalancerInterceptor + LoadBalancerRequestFactory requestFactory = this.factory.getBean(LoadBalancerRequestFactory.class); + LoadBalancerClient loadBalancerClient = this.factory.getBean(LoadBalancerClient.class); + List routerLabelResolvers = BeanFactoryUtils.getBeans(factory, RouterLabelResolver.class); + MetadataLocalProperties metadataLocalProperties = this.factory.getBean(MetadataLocalProperties.class); + RouterRuleLabelResolver routerRuleLabelResolver = this.factory.getBean(RouterRuleLabelResolver.class); + + return new PolarisLoadBalancerInterceptor(loadBalancerClient, requestFactory, + routerLabelResolvers, metadataLocalProperties, routerRuleLabelResolver); + } + return bean; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..5b87668ea2eed96d89d8b7cedbd20a428fab5bdc --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignAutoConfiguration.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.config; + +import java.util.List; + +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.feign.PolarisCachingSpringLoadBalanceFactory; +import com.tencent.cloud.polaris.router.feign.RouterLabelFeignInterceptor; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.cloud.netflix.ribbon.RibbonClients; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.lang.Nullable; + +/** + * configuration for feign singleton components. + * Feign-related components need to be loaded only in the feign environment. + *@author lepdou 2022-06-10 + */ +@Configuration +@ConditionalOnClass(name = {"org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer"}) +@RibbonClients(defaultConfiguration = {FeignLoadBalancerConfiguration.class}) +public class FeignAutoConfiguration { + + @Bean + public RouterLabelFeignInterceptor routerLabelInterceptor(@Nullable List routerLabelResolvers, + MetadataLocalProperties metadataLocalProperties, + RouterRuleLabelResolver routerRuleLabelResolver) { + return new RouterLabelFeignInterceptor(routerLabelResolvers, metadataLocalProperties, routerRuleLabelResolver); + } + + @Bean + public PolarisCachingSpringLoadBalanceFactory polarisCachingSpringLoadBalanceFactory(SpringClientFactory factory) { + return new PolarisCachingSpringLoadBalanceFactory(factory); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignLoadBalancerConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignLoadBalancerConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..e6368cc85250b36aa77c5148b4eb6afb35ce0769 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/FeignLoadBalancerConfiguration.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.config; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; +import com.tencent.cloud.polaris.router.feign.PolarisFeignLoadBalancer; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.context.annotation.Bean; + +/** + * configuration for feign load balance components. PolarisFeignLoadBalancer is not singleton bean, Each service corresponds to a PolarisFeignLoadBalancer. + *@author lepdou 2022-05-16 + */ +public class FeignLoadBalancerConfiguration { + + @Bean + @ConditionalOnMissingBean + public PolarisFeignLoadBalancer polarisFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, + ServerIntrospector serverIntrospector) { + return new PolarisFeignLoadBalancer(lb, clientConfig, serverIntrospector); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RibbonConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RibbonConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..9bc17b276fecd497bebd6772bbd51e98ce180880 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RibbonConfiguration.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.config; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.IRule; +import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties; +import com.tencent.cloud.polaris.router.PolarisLoadBalancerCompositeRule; +import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties; +import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties; +import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties; +import com.tencent.polaris.router.api.core.RouterAPI; + +import org.springframework.context.annotation.Bean; + +/** + * Configuration for ribbon components. IRule is not singleton bean, Each service corresponds to an IRule. + * @author lepdou 2022-05-17 + */ +public class RibbonConfiguration { + + @Bean + public IRule polarisLoadBalancerCompositeRule(RouterAPI routerAPI, + PolarisLoadBalancerProperties polarisLoadBalancerProperties, + PolarisNearByRouterProperties polarisNearByRouterProperties, + PolarisMetadataRouterProperties polarisMetadataRouterProperties, + PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties, + IClientConfig iClientConfig) { + return new PolarisLoadBalancerCompositeRule(routerAPI, polarisLoadBalancerProperties, + polarisNearByRouterProperties, polarisMetadataRouterProperties, + polarisRuleBasedRouterProperties, iClientConfig); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..690ba9f0c2b4c466eadb23990d319954a78cc858 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/RouterAutoConfiguration.java @@ -0,0 +1,66 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.config; + +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerClientFilterBeanPostProcessor; +import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerInterceptorBeanPostProcessor; +import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties; +import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties; +import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.cloud.netflix.ribbon.RibbonClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.core.annotation.Order; + +import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE; + +/** + * configuration for router module singleton beans. + * + *@author lepdou 2022-05-11 + */ +@Configuration +@RibbonClients(defaultConfiguration = {RibbonConfiguration.class}) +@Import({PolarisNearByRouterProperties.class, PolarisMetadataRouterProperties.class, PolarisRuleBasedRouterProperties.class}) +public class RouterAutoConfiguration { + + @Bean + @Order(HIGHEST_PRECEDENCE) + @ConditionalOnClass(name = "org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor") + public LoadBalancerInterceptorBeanPostProcessor loadBalancerInterceptorBeanPostProcessor() { + return new LoadBalancerInterceptorBeanPostProcessor(); + } + + @Bean + @Order(HIGHEST_PRECEDENCE) + @ConditionalOnClass(name = "org.springframework.cloud.gateway.filter.LoadBalancerClientFilter") + public LoadBalancerClientFilterBeanPostProcessor loadBalancerClientFilterBeanPostProcessor() { + return new LoadBalancerClientFilterBeanPostProcessor(); + } + + @Bean + public RouterRuleLabelResolver routerRuleLabelResolver(ServiceRuleManager serviceRuleManager) { + return new RouterRuleLabelResolver(serviceRuleManager); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/properties/PolarisMetadataRouterProperties.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/properties/PolarisMetadataRouterProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..70ded3a8cc9301def8970d54619141ad88f9e057 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/properties/PolarisMetadataRouterProperties.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.config.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * the configuration for metadata router. + * @author lepdou 2022-05-23 + */ +@ConfigurationProperties(prefix = "spring.cloud.polaris.router.metadata-router") +public class PolarisMetadataRouterProperties { + + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + return "PolarisMetadataRouterProperties{" + + "enabled=" + enabled + + '}'; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/properties/PolarisNearByRouterProperties.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/properties/PolarisNearByRouterProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..3cd69ca12d34ad1590c2b09a04fffb7033fd210f --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/properties/PolarisNearByRouterProperties.java @@ -0,0 +1,47 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.config.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * the configuration for nearby router. + * + * @author lepdou 2022-05-23 + */ +@ConfigurationProperties(prefix = "spring.cloud.polaris.router.nearby-router") +public class PolarisNearByRouterProperties { + + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + return "PolarisNearByRouterProperties{" + + "enabled=" + enabled + + '}'; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/properties/PolarisRuleBasedRouterProperties.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/properties/PolarisRuleBasedRouterProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..67a17975ff1eb6555ff6c9065bbc5de66b3f9010 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/properties/PolarisRuleBasedRouterProperties.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.config.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * the configuration for rule based router. + * + * @author lepdou 2022-05-23 + */ +@ConfigurationProperties(prefix = "spring.cloud.polaris.router.rule-router") +public class PolarisRuleBasedRouterProperties { + + private boolean enabled = true; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + return "PolarisNearByRouterProperties{" + + "enabled=" + enabled + + '}'; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtils.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..92b8a66087c8b9c21ca33e16913ca61f398bae25 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtils.java @@ -0,0 +1,83 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.feign; + +import java.net.URI; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.common.util.ExpressionLabelUtils; +import feign.RequestTemplate; +import org.apache.commons.lang.StringUtils; + +import org.springframework.util.CollectionUtils; + +/** + * Resolve rule expression label from feign request. + * @author lepdou 2022-05-20 + */ +public class FeignExpressionLabelUtils { + + public static Map resolve(RequestTemplate request, Set labelKeys) { + if (CollectionUtils.isEmpty(labelKeys)) { + return Collections.emptyMap(); + } + + Map labels = new HashMap<>(); + + for (String labelKey : labelKeys) { + if (StringUtils.startsWithIgnoreCase(labelKey, ExpressionLabelUtils.LABEL_HEADER_PREFIX)) { + String headerKey = ExpressionLabelUtils.parseHeaderKey(labelKey); + if (StringUtils.isBlank(headerKey)) { + continue; + } + labels.put(labelKey, getHeaderValue(request, headerKey)); + } + else if (StringUtils.startsWithIgnoreCase(labelKey, ExpressionLabelUtils.LABEL_QUERY_PREFIX)) { + String queryKey = ExpressionLabelUtils.parseQueryKey(labelKey); + if (StringUtils.isBlank(queryKey)) { + continue; + } + labels.put(labelKey, getQueryValue(request, queryKey)); + } + else if (StringUtils.equalsIgnoreCase(ExpressionLabelUtils.LABEL_METHOD, labelKey)) { + labels.put(labelKey, request.method()); + } + else if (StringUtils.equalsIgnoreCase(ExpressionLabelUtils.LABEL_URI, labelKey)) { + URI uri = URI.create(request.request().url()); + labels.put(labelKey, uri.getPath()); + } + } + + return labels; + } + + public static String getHeaderValue(RequestTemplate request, String key) { + Map> headers = request.headers(); + return ExpressionLabelUtils.getFirstValue(headers, key); + + } + + public static String getQueryValue(RequestTemplate request, String key) { + return ExpressionLabelUtils.getFirstValue(request.queries(), key); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactory.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..2c15ce790689bdb53ebf8b72acb542c0b0431651 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactory.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.feign; + +import java.util.Map; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; + +import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; +import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer; +import org.springframework.util.ConcurrentReferenceHashMap; + +/** + * Extends CachingSpringLoadBalancerFactory to be able to create PolarisFeignLoadBalance. + * + *@author lepdou 2022-05-16 + */ +public class PolarisCachingSpringLoadBalanceFactory extends CachingSpringLoadBalancerFactory { + + private final Map cache = new ConcurrentReferenceHashMap<>(); + + public PolarisCachingSpringLoadBalanceFactory(SpringClientFactory factory) { + super(factory); + } + + public PolarisCachingSpringLoadBalanceFactory(SpringClientFactory factory, + LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) { + super(factory, loadBalancedRetryPolicyFactory); + } + + @Override + public FeignLoadBalancer create(String clientName) { + FeignLoadBalancer client = this.cache.get(clientName); + if (client != null) { + return client; + } + + IClientConfig config = this.factory.getClientConfig(clientName); + ILoadBalancer lb = this.factory.getLoadBalancer(clientName); + ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class); + + FeignLoadBalancer loadBalancer = new PolarisFeignLoadBalancer(lb, config, serverIntrospector); + + //There is a concurrency problem here. + //When the concurrency is high, it may cause a service to create multiple FeignLoadBalancers. + //But there is no concurrency control in CachingSpringLoadBalancerFactory, + //so no locks will be added here for the time being + cache.putIfAbsent(clientName, loadBalancer); + + return loadBalancer; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancer.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancer.java new file mode 100644 index 0000000000000000000000000000000000000000..76c824f62303f3d8b1234e14f0cd42635c1e9fde --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancer.java @@ -0,0 +1,88 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.feign; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.reactive.LoadBalancerCommand; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.polaris.router.PolarisRouterContext; +import com.tencent.cloud.polaris.router.RouterConstants; + +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer; +import org.springframework.util.CollectionUtils; + +/** + * In order to pass router context for {@link com.tencent.cloud.polaris.router.PolarisLoadBalancerCompositeRule}. + * + * @author lepdou 2022-05-16 + * @author cheese8 2022-06-18 + */ +public class PolarisFeignLoadBalancer extends FeignLoadBalancer { + + public PolarisFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, ServerIntrospector serverIntrospector) { + super(lb, clientConfig, serverIntrospector); + } + + @Override + protected void customizeLoadBalancerCommandBuilder(RibbonRequest request, IClientConfig config, + LoadBalancerCommand.Builder builder) { + Map> headers = request.getRequest().headers(); + + PolarisRouterContext routerContext = buildRouterContext(headers); + + builder.withServerLocator(routerContext); + } + + //set method to public for unit test + PolarisRouterContext buildRouterContext(Map> headers) { + Collection labelHeaderValues = headers.get(RouterConstants.ROUTER_LABEL_HEADER); + + if (CollectionUtils.isEmpty(labelHeaderValues)) { + return null; + } + + PolarisRouterContext routerContext = new PolarisRouterContext(); + + routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, MetadataContextHolder.get() + .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)); + + Map labelHeaderValuesMap = new HashMap<>(); + try { + String labelHeaderValuesContent = labelHeaderValues.stream().findFirst().get(); + labelHeaderValuesMap.putAll(JacksonUtils.deserialize2Map(URLDecoder.decode(labelHeaderValuesContent, StandardCharsets.UTF_8.name()))); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException("unsupported charset exception " + StandardCharsets.UTF_8.name()); + } + routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labelHeaderValuesMap); + + return routerContext; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..4eb59a2083f2a7ef3815bc8a3e112262fc50b12e --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptor.java @@ -0,0 +1,135 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.feign; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.polaris.router.RouterConstants; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.core.Ordered; +import org.springframework.util.CollectionUtils; + +/** + * Resolver labels from request. + * + * @author lepdou 2022-05-12 + * @author cheese8 2022-06-18 + */ +public class RouterLabelFeignInterceptor implements RequestInterceptor, Ordered { + private static final Logger LOGGER = LoggerFactory.getLogger(RouterLabelFeignInterceptor.class); + + private final List routerLabelResolvers; + private final MetadataLocalProperties metadataLocalProperties; + private final RouterRuleLabelResolver routerRuleLabelResolver; + + public RouterLabelFeignInterceptor(List routerLabelResolvers, + MetadataLocalProperties metadataLocalProperties, + RouterRuleLabelResolver routerRuleLabelResolver) { + if (!CollectionUtils.isEmpty(routerLabelResolvers)) { + routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder)); + this.routerLabelResolvers = routerLabelResolvers; + } + else { + this.routerLabelResolvers = null; + } + this.metadataLocalProperties = metadataLocalProperties; + this.routerRuleLabelResolver = routerRuleLabelResolver; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void apply(RequestTemplate requestTemplate) { + // local service labels + Map labels = new HashMap<>(metadataLocalProperties.getContent()); + + // labels from rule expression + String peerServiceName = requestTemplate.feignTarget().name(); + Map ruleExpressionLabels = getRuleExpressionLabels(requestTemplate, peerServiceName); + labels.putAll(ruleExpressionLabels); + + // labels from request + if (!CollectionUtils.isEmpty(routerLabelResolvers)) { + routerLabelResolvers.forEach(resolver -> { + try { + Map customResolvedLabels = resolver.resolve(requestTemplate); + if (!CollectionUtils.isEmpty(customResolvedLabels)) { + labels.putAll(customResolvedLabels); + } + } + catch (Throwable t) { + LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t); + } + }); + } + + // labels from downstream + Map transitiveLabels = MetadataContextHolder.get() + .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + labels.putAll(transitiveLabels); + + // pass label by header + if (labels.size() == 0) { + requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER); + return; + } + + String encodedLabelsContent; + try { + encodedLabelsContent = URLEncoder.encode(JacksonUtils.serialize2Json(labels), StandardCharsets.UTF_8.name()); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException("unsupported charset exception " + StandardCharsets.UTF_8.name()); + } + requestTemplate.header(RouterConstants.ROUTER_LABEL_HEADER, encodedLabelsContent); + } + + private Map getRuleExpressionLabels(RequestTemplate requestTemplate, String peerService) { + Set labelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE, + MetadataContext.LOCAL_SERVICE, peerService); + + if (CollectionUtils.isEmpty(labelKeys)) { + return Collections.emptyMap(); + } + + return FeignExpressionLabelUtils.resolve(requestTemplate, labelKeys); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..de474c3f13beca2a1cb1db77a89904cc44fa0421 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptor.java @@ -0,0 +1,156 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.resttemplate; + +import java.io.IOException; +import java.net.URI; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.util.ExpressionLabelUtils; +import com.tencent.cloud.polaris.router.PolarisRouterContext; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor; +import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient; +import org.springframework.core.Ordered; +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; + +/** + * PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor capabilities. + * Parses the label from the request and puts it into the RouterContext for routing. + * + *@author lepdou 2022-05-18 + */ +public class PolarisLoadBalancerInterceptor extends LoadBalancerInterceptor { + private static final Logger LOGGER = LoggerFactory.getLogger(PolarisLoadBalancerInterceptor.class); + + private final LoadBalancerClient loadBalancer; + private final LoadBalancerRequestFactory requestFactory; + private final List routerLabelResolvers; + private final MetadataLocalProperties metadataLocalProperties; + private final RouterRuleLabelResolver routerRuleLabelResolver; + + private final boolean isRibbonLoadBalanceClient; + + public PolarisLoadBalancerInterceptor(LoadBalancerClient loadBalancer, + LoadBalancerRequestFactory requestFactory, + List routerLabelResolvers, + MetadataLocalProperties metadataLocalProperties, + RouterRuleLabelResolver routerRuleLabelResolver) { + super(loadBalancer, requestFactory); + this.loadBalancer = loadBalancer; + this.requestFactory = requestFactory; + this.metadataLocalProperties = metadataLocalProperties; + this.routerRuleLabelResolver = routerRuleLabelResolver; + + if (!CollectionUtils.isEmpty(routerLabelResolvers)) { + routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder)); + this.routerLabelResolvers = routerLabelResolvers; + } + else { + this.routerLabelResolvers = null; + } + + this.isRibbonLoadBalanceClient = loadBalancer instanceof RibbonLoadBalancerClient; + } + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + final URI originalUri = request.getURI(); + String peerServiceName = originalUri.getHost(); + Assert.state(peerServiceName != null, + "Request URI does not contain a valid hostname: " + originalUri); + + if (isRibbonLoadBalanceClient) { + PolarisRouterContext routerContext = genRouterContext(request, body, peerServiceName); + + return ((RibbonLoadBalancerClient) loadBalancer).execute(peerServiceName, + this.requestFactory.createRequest(request, body, execution), routerContext); + } + + return this.loadBalancer.execute(peerServiceName, + this.requestFactory.createRequest(request, body, execution)); + } + + PolarisRouterContext genRouterContext(HttpRequest request, byte[] body, String peerServiceName) { + // local service labels + Map labels = new HashMap<>(metadataLocalProperties.getContent()); + + // labels from rule expression + Map ruleExpressionLabels = getExpressionLabels(request, peerServiceName); + if (!CollectionUtils.isEmpty(ruleExpressionLabels)) { + labels.putAll(ruleExpressionLabels); + } + + // labels from request + if (!CollectionUtils.isEmpty(routerLabelResolvers)) { + routerLabelResolvers.forEach(resolver -> { + try { + Map customResolvedLabels = resolver.resolve(request, body); + if (!CollectionUtils.isEmpty(customResolvedLabels)) { + labels.putAll(customResolvedLabels); + } + } + catch (Throwable t) { + LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t); + } + }); + } + + // labels from downstream + Map transitiveLabels = MetadataContextHolder.get() + .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + labels.putAll(transitiveLabels); + + PolarisRouterContext routerContext = new PolarisRouterContext(); + + routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labels); + routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, transitiveLabels); + + return routerContext; + } + + private Map getExpressionLabels(HttpRequest request, String peerServiceName) { + Set labelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE, + MetadataContext.LOCAL_SERVICE, peerServiceName); + + if (CollectionUtils.isEmpty(labelKeys)) { + return Collections.emptyMap(); + } + + return ExpressionLabelUtils.resolve(request, labelKeys); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientFilter.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..35de57737a53615cb591f1df9ad5eb5a3308e2de --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientFilter.java @@ -0,0 +1,144 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.scg; + +import java.net.URI; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.util.ExpressionLabelUtils; +import com.tencent.cloud.polaris.router.PolarisRouterContext; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.cloud.gateway.config.LoadBalancerProperties; +import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient; +import org.springframework.core.Ordered; +import org.springframework.util.CollectionUtils; +import org.springframework.web.server.ServerWebExchange; + +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; + +/** + * Replaces the default LoadBalancerClientFilter implementation. + *@author lepdou 2022-06-10 + */ +public class PolarisLoadBalancerClientFilter extends LoadBalancerClientFilter { + private final static Logger LOGGER = LoggerFactory.getLogger(PolarisLoadBalancerClientFilter.class); + + private final MetadataLocalProperties metadataLocalProperties; + private final RouterRuleLabelResolver routerRuleLabelResolver; + private final List routerLabelResolvers; + + private final boolean isRibbonLoadBalanceClient; + + public PolarisLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties, + MetadataLocalProperties metadataLocalProperties, + RouterRuleLabelResolver routerRuleLabelResolver, + List routerLabelResolvers) { + super(loadBalancer, properties); + this.metadataLocalProperties = metadataLocalProperties; + this.routerRuleLabelResolver = routerRuleLabelResolver; + + if (!CollectionUtils.isEmpty(routerLabelResolvers)) { + routerLabelResolvers.sort(Comparator.comparingInt(Ordered::getOrder)); + this.routerLabelResolvers = routerLabelResolvers; + } + else { + this.routerLabelResolvers = null; + } + + this.isRibbonLoadBalanceClient = loadBalancer instanceof RibbonLoadBalancerClient; + } + + @Override + protected ServiceInstance choose(ServerWebExchange exchange) { + String peerServiceName = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost(); + + if (isRibbonLoadBalanceClient) { + // Pass routing context to ribbon load balancer + PolarisRouterContext routerContext = genRouterContext(exchange, peerServiceName); + return ((RibbonLoadBalancerClient) loadBalancer).choose(peerServiceName, routerContext); + } + else { + return loadBalancer.choose(peerServiceName); + } + } + + PolarisRouterContext genRouterContext(ServerWebExchange exchange, String peerServiceName) { + // local service labels + Map labels = new HashMap<>(metadataLocalProperties.getContent()); + + // labels from rule expression + Map ruleExpressionLabels = getExpressionLabels(exchange, peerServiceName); + if (!CollectionUtils.isEmpty(ruleExpressionLabels)) { + labels.putAll(ruleExpressionLabels); + } + + // labels from request + if (!CollectionUtils.isEmpty(routerLabelResolvers)) { + routerLabelResolvers.forEach(resolver -> { + try { + Map customResolvedLabels = resolver.resolve(exchange); + if (!CollectionUtils.isEmpty(customResolvedLabels)) { + labels.putAll(customResolvedLabels); + } + } + catch (Throwable t) { + LOGGER.error("[SCT][Router] revoke RouterLabelResolver occur some exception. ", t); + } + }); + } + + // labels from downstream + Map transitiveLabels = MetadataContextHolder.get() + .getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + labels.putAll(transitiveLabels); + + PolarisRouterContext routerContext = new PolarisRouterContext(); + + routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labels); + routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, transitiveLabels); + + return routerContext; + } + + private Map getExpressionLabels(ServerWebExchange exchange, String peerServiceName) { + Set labelKeys = routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE, + MetadataContext.LOCAL_SERVICE, peerServiceName); + + if (CollectionUtils.isEmpty(labelKeys)) { + return Collections.emptyMap(); + } + + return ExpressionLabelUtils.resolve(exchange, labelKeys); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/RouterLabelResolver.java b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/RouterLabelResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..a87ca81edd33da5efd16d734f6e05e749a75a6c1 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/spi/RouterLabelResolver.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.spi; + +import java.util.Collections; +import java.util.Map; + +import feign.RequestTemplate; + +import org.springframework.core.Ordered; +import org.springframework.http.HttpRequest; +import org.springframework.web.server.ServerWebExchange; + +/** + * The spi for resolving labels from request. + * + * @author lepdou 2022-05-11 + */ +public interface RouterLabelResolver extends Ordered { + + /** + * resolve labels from feign request. + * @param requestTemplate the feign request. + * @return resolved labels + */ + default Map resolve(RequestTemplate requestTemplate) { + return Collections.emptyMap(); + } + + /** + * resolve labels from rest template request. + * @param request the rest template request. + * @param body the rest template request body. + * @return resolved labels + */ + default Map resolve(HttpRequest request, byte[] body) { + return Collections.emptyMap(); + } + + + /** + * resolve labels from server web exchange. + * @param exchange the server web exchange. + * @return resolved labels + */ + default Map resolve(ServerWebExchange exchange) { + return Collections.emptyMap(); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/additional-spring-configuration-metadata.json index c94ac4e0fbc0be589c63f30a7b87cf0ae8712f33..4f248b7276fae27296eda86230b50422676f047b 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,16 +1,22 @@ { "properties": [ { - "name": "spring.cloud.polaris.loadbalancer.enabled", + "name": "spring.cloud.polaris.router.metadata-router.enabled", "type": "java.lang.Boolean", - "defaultValue": "true", - "description": "polaris loadbalancer" + "defaultValue": true, + "description": "the switch for metadata router." }, { - "name": "spring.cloud.polaris.loadbalancer.strategy", - "type": "java.lang.String", - "defaultValue": "random", - "description": "retry,best_available,availability_filtering,round_robin,weighted_response_time,zone_avoidance,random,consistent_hash,weighted_random" + "name": "spring.cloud.polaris.router.nearby-router.enabled", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "the switch for near by router." + }, + { + "name": "spring.cloud.polaris.router.rule-router.enabled", + "type": "java.lang.Boolean", + "defaultValue": true, + "description": "the switch for rule based router." } ] } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories b/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories index 75c1910462fbe91748016169d6f4a087e6d92c17..3b20deb56fee2a94e37f40b5018207545f7832f1 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-starter-tencent-polaris-router/src/main/resources/META-INF/spring.factories @@ -1,2 +1,3 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ - com.tencent.cloud.polaris.router.config.PolarisRibbonAutoConfiguration + com.tencent.cloud.polaris.router.config.RouterAutoConfiguration,\ + com.tencent.cloud.polaris.router.config.FeignAutoConfiguration diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRuleTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRuleTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0d55e9cde03fc6b996a026146b69fb18dba38a20 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisLoadBalancerCompositeRuleTest.java @@ -0,0 +1,362 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.netflix.client.config.DefaultClientConfigImpl; +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.AbstractLoadBalancerRule; +import com.netflix.loadbalancer.AvailabilityFilteringRule; +import com.netflix.loadbalancer.BestAvailableRule; +import com.netflix.loadbalancer.RandomRule; +import com.netflix.loadbalancer.RetryRule; +import com.netflix.loadbalancer.RoundRobinRule; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.WeightedResponseTimeRule; +import com.netflix.loadbalancer.ZoneAvoidanceRule; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.pojo.PolarisServer; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.polaris.loadbalancer.PolarisWeightedRule; +import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties; +import com.tencent.cloud.polaris.router.config.properties.PolarisMetadataRouterProperties; +import com.tencent.cloud.polaris.router.config.properties.PolarisNearByRouterProperties; +import com.tencent.cloud.polaris.router.config.properties.PolarisRuleBasedRouterProperties; +import com.tencent.polaris.api.pojo.DefaultInstance; +import com.tencent.polaris.api.pojo.DefaultServiceInstances; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.plugins.router.metadata.MetadataRouter; +import com.tencent.polaris.plugins.router.nearby.NearbyRouter; +import com.tencent.polaris.plugins.router.rule.RuleBasedRouter; +import com.tencent.polaris.router.api.core.RouterAPI; +import com.tencent.polaris.router.api.rpc.ProcessRoutersRequest; +import com.tencent.polaris.router.api.rpc.ProcessRoutersResponse; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * test for {@link PolarisLoadBalancerCompositeRule} + *@author lepdou 2022-05-26 + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisLoadBalancerCompositeRuleTest { + + @Mock + private PolarisLoadBalancerProperties polarisLoadBalancerProperties; + @Mock + private PolarisNearByRouterProperties polarisNearByRouterProperties; + @Mock + private PolarisMetadataRouterProperties polarisMetadataRouterProperties; + @Mock + private PolarisRuleBasedRouterProperties polarisRuleBasedRouterProperties; + @Mock + private RouterAPI routerAPI; + + private IClientConfig config; + + private static AtomicBoolean initTransitiveMetadata = new AtomicBoolean(false); + + private String testNamespace = "testNamespace"; + private String testCallerService = "testCallerService"; + private String testCalleeService = "testCalleeService"; + + @Before + public void before() { + config = new DefaultClientConfigImpl(); + config.loadDefaultValues(); + } + + @Test + public void testGetDefaultLB() { + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(""); + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + AbstractLoadBalancerRule defaultRule = compositeRule.getRule(); + + Assert.assertTrue(defaultRule instanceof ZoneAvoidanceRule); + } + + @Test + public void testRandomLB() { + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_RANDOM); + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + AbstractLoadBalancerRule lbRule = compositeRule.getRule(); + + Assert.assertTrue(lbRule instanceof RandomRule); + } + + @Test + public void testWeightLB() { + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_WEIGHT); + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + AbstractLoadBalancerRule lbRule = compositeRule.getRule(); + + Assert.assertTrue(lbRule instanceof PolarisWeightedRule); + } + + @Test + public void testRetryLB() { + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_RETRY); + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + AbstractLoadBalancerRule lbRule = compositeRule.getRule(); + + Assert.assertTrue(lbRule instanceof RetryRule); + } + + @Test + public void testWeightedResponseTimeLB() { + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_RESPONSE_TIME_WEIGHTED); + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + AbstractLoadBalancerRule lbRule = compositeRule.getRule(); + + Assert.assertTrue(lbRule instanceof WeightedResponseTimeRule); + } + + @Test + public void tesBestAvailableLB() { + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_BEST_AVAILABLE); + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + AbstractLoadBalancerRule lbRule = compositeRule.getRule(); + + Assert.assertTrue(lbRule instanceof BestAvailableRule); + } + + @Test + public void tesRoundRobinLB() { + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_ROUND_ROBIN); + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + AbstractLoadBalancerRule lbRule = compositeRule.getRule(); + + Assert.assertTrue(lbRule instanceof RoundRobinRule); + } + + @Test + public void testAvailabilityFilteringLB() { + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(PolarisLoadBalancerCompositeRule.STRATEGY_AVAILABILITY_FILTERING); + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + AbstractLoadBalancerRule lbRule = compositeRule.getRule(); + + Assert.assertTrue(lbRule instanceof AvailabilityFilteringRule); + } + + @Test + public void testBuildMetadataRouteRequest() { + when(polarisMetadataRouterProperties.isEnabled()).thenReturn(true); + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(""); + + try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) { + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn(testCallerService); + + setTransitiveMetadata(); + + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + ServiceInstances serviceInstances = assembleServiceInstances(); + PolarisRouterContext routerContext = assembleRouterContext(); + + ProcessRoutersRequest request = compositeRule.buildProcessRoutersRequest(serviceInstances, routerContext); + + Map routerMetadata = request.getRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA); + + Assert.assertEquals(1, routerMetadata.size()); + Assert.assertEquals(0, request.getRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY).size()); + Assert.assertEquals(1, request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size()); + Assert.assertEquals("false", request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED) + .get(RuleBasedRouter.ROUTER_ENABLED)); + } + } + + @Test + public void testBuildNearbyRouteRequest() { + when(polarisNearByRouterProperties.isEnabled()).thenReturn(true); + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(""); + + try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) { + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn(testCallerService); + + setTransitiveMetadata(); + + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + ServiceInstances serviceInstances = assembleServiceInstances(); + PolarisRouterContext routerContext = assembleRouterContext(); + + ProcessRoutersRequest request = compositeRule.buildProcessRoutersRequest(serviceInstances, routerContext); + + Map routerMetadata = request.getRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY); + + Assert.assertEquals(0, request.getRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA).size()); + Assert.assertEquals(1, routerMetadata.size()); + Assert.assertEquals("true", routerMetadata.get(NearbyRouter.ROUTER_ENABLED)); + Assert.assertEquals(1, request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size()); + Assert.assertEquals("false", request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED) + .get(RuleBasedRouter.ROUTER_ENABLED)); + } + } + + @Test + public void testBuildRuleBasedRouteRequest() { + when(polarisRuleBasedRouterProperties.isEnabled()).thenReturn(true); + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(""); + + try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) { + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())). + thenReturn(testCallerService); + + setTransitiveMetadata(); + + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + ServiceInstances serviceInstances = assembleServiceInstances(); + PolarisRouterContext routerContext = assembleRouterContext(); + + ProcessRoutersRequest request = compositeRule.buildProcessRoutersRequest(serviceInstances, routerContext); + + Map routerMetadata = request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED); + + Assert.assertEquals(1, routerMetadata.size()); + Assert.assertEquals(0, request.getRouterMetadata(MetadataRouter.ROUTER_TYPE_METADATA).size()); + Assert.assertEquals(0, request.getRouterMetadata(NearbyRouter.ROUTER_TYPE_NEAR_BY).size()); + Assert.assertEquals(1, request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED).size()); + Assert.assertEquals("true", request.getRouterMetadata(RuleBasedRouter.ROUTER_TYPE_RULE_BASED) + .get(RuleBasedRouter.ROUTER_ENABLED)); + } + } + + @Test + public void testRouter() { + when(polarisRuleBasedRouterProperties.isEnabled()).thenReturn(true); + when(polarisLoadBalancerProperties.getStrategy()).thenReturn(""); + + try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) { + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn(testCallerService); + + setTransitiveMetadata(); + + PolarisLoadBalancerCompositeRule compositeRule = new PolarisLoadBalancerCompositeRule(routerAPI, + polarisLoadBalancerProperties, polarisNearByRouterProperties, + polarisMetadataRouterProperties, polarisRuleBasedRouterProperties, config); + + ProcessRoutersResponse assembleResponse = assembleProcessRoutersResponse(); + when(routerAPI.processRouters(any())).thenReturn(assembleResponse); + + List servers = compositeRule.doRouter(assembleServers(), assembleRouterContext()); + + Assert.assertEquals(assembleResponse.getServiceInstances().getInstances().size(), servers.size()); + } + } + + private void setTransitiveMetadata() { + if (initTransitiveMetadata.compareAndSet(false, true)) { + // mock transitive metadata + MetadataContext metadataContext = Mockito.mock(MetadataContext.class); + try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) { + mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); + } + } + } + + private ServiceInstances assembleServiceInstances() { + ServiceKey serviceKey = new ServiceKey(testNamespace, testCalleeService); + List instances = new LinkedList<>(); + instances.add(new DefaultInstance()); + instances.add(new DefaultInstance()); + instances.add(new DefaultInstance()); + instances.add(new DefaultInstance()); + instances.add(new DefaultInstance()); + + return new DefaultServiceInstances(serviceKey, instances); + } + + private PolarisRouterContext assembleRouterContext() { + PolarisRouterContext routerContext = new PolarisRouterContext(); + Map transitiveLabels = new HashMap<>(); + transitiveLabels.put("k1", "v1"); + Map routerLabels = new HashMap<>(); + routerLabels.put("k2", "v2"); + routerLabels.put("k3", "v3"); + routerContext.setLabels(PolarisRouterContext.TRANSITIVE_LABELS, transitiveLabels); + routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, routerLabels); + return routerContext; + } + + private ProcessRoutersResponse assembleProcessRoutersResponse() { + return new ProcessRoutersResponse(assembleServiceInstances()); + } + + private List assembleServers() { + ServiceInstances serviceInstances = assembleServiceInstances(); + List servers = new LinkedList<>(); + servers.add(new PolarisServer(serviceInstances, new DefaultInstance())); + servers.add(new PolarisServer(serviceInstances, new DefaultInstance())); + servers.add(new PolarisServer(serviceInstances, new DefaultInstance())); + servers.add(new PolarisServer(serviceInstances, new DefaultInstance())); + return servers; + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisRouterContextTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisRouterContextTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2a8d3875228063e5884567272950a2f6ce9b548a --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/PolarisRouterContextTest.java @@ -0,0 +1,64 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +/** + * test for {@link PolarisRouterContext} + * + *@author lepdou 2022-05-26 + */ +public class PolarisRouterContextTest { + + @Test + public void testNormalGetterSetter() { + Map labels = new HashMap<>(); + labels.put("k1", "v1"); + labels.put("k2", "v2"); + + PolarisRouterContext routerContext = new PolarisRouterContext(); + routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, labels); + + Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).size()); + Assert.assertEquals(2, routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).size()); + Assert.assertEquals("v1", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k1")); + Assert.assertEquals("v2", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k2")); + Assert.assertNull(routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k3")); + } + + @Test + public void testSetNull() { + PolarisRouterContext routerContext = new PolarisRouterContext(); + routerContext.setLabels(PolarisRouterContext.RULE_ROUTER_LABELS, null); + Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).size()); + Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).size()); + } + + @Test + public void testGetEmptyRouterContext() { + PolarisRouterContext routerContext = new PolarisRouterContext(); + Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).size()); + Assert.assertEquals(0, routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).size()); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/RouterRuleLabelResolverTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/RouterRuleLabelResolverTest.java new file mode 100644 index 0000000000000000000000000000000000000000..26c1dfb13c89fda0fa7b22320ca6bd014e40fb2c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/RouterRuleLabelResolverTest.java @@ -0,0 +1,93 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router; + + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Lists; +import com.tencent.cloud.polaris.context.ServiceRuleManager; +import com.tencent.polaris.client.pb.ModelProto; +import com.tencent.polaris.client.pb.RoutingProto; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.Mockito.when; + +/** + * test for {@link RouterRuleLabelResolver} + *@author lepdou 2022-05-26 + */ +@RunWith(MockitoJUnitRunner.class) +public class RouterRuleLabelResolverTest { + + @Mock + private ServiceRuleManager serviceRuleManager; + + private final String testNamespace = "testNamespace"; + private final String testSourceService = "sourceService"; + private final String testDstService = "dstService"; + + @Test + public void test() { + Map labels = new HashMap<>(); + ModelProto.MatchString matchString = ModelProto.MatchString.getDefaultInstance(); + String validKey1 = "${http.header.uid}"; + String validKey2 = "${http.query.name}"; + String validKey3 = "${http.method}"; + String validKey4 = "${http.uri}"; + String invalidKey = "${http.expression.wrong}"; + labels.put(validKey1, matchString); + labels.put(validKey2, matchString); + labels.put(validKey3, matchString); + labels.put(validKey4, matchString); + labels.put(invalidKey, matchString); + + RoutingProto.Source source1 = RoutingProto.Source.newBuilder().putAllMetadata(labels).build(); + RoutingProto.Source source2 = RoutingProto.Source.newBuilder().putAllMetadata(labels).build(); + RoutingProto.Source source3 = RoutingProto.Source.newBuilder().putAllMetadata(new HashMap<>()).build(); + + List routes = new LinkedList<>(); + RoutingProto.Route route = RoutingProto.Route.newBuilder() + .addAllSources(Lists.newArrayList(source1, source2, source3)) + .build(); + routes.add(route); + + when(serviceRuleManager.getServiceRouterRule(testNamespace, testSourceService, testDstService)).thenReturn(routes); + + RouterRuleLabelResolver resolver = new RouterRuleLabelResolver(serviceRuleManager); + + Set resolvedExpressionLabelKeys = resolver.getExpressionLabelKeys(testNamespace, testSourceService, testDstService); + + Assert.assertNotNull(resolvedExpressionLabelKeys); + Assert.assertEquals(4, resolvedExpressionLabelKeys.size()); + Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey1)); + Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey2)); + Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey3)); + Assert.assertTrue(resolvedExpressionLabelKeys.contains(validKey4)); + Assert.assertFalse(resolvedExpressionLabelKeys.contains(invalidKey)); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/SimpleLoadBalancerTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/SimpleLoadBalancerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..36e512b4ca032905b89046b2e29ec4b2a853eb0c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/SimpleLoadBalancerTest.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router; + +import java.util.LinkedList; +import java.util.List; + +import com.netflix.loadbalancer.Server; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +/** + * test for {@link SimpleLoadBalancer} + *@author lepdou 2022-05-26 + */ +public class SimpleLoadBalancerTest { + + @Test + public void testSetterGetter() { + List servers = new LinkedList<>(); + servers.add(Mockito.mock(Server.class)); + servers.add(Mockito.mock(Server.class)); + servers.add(Mockito.mock(Server.class)); + servers.add(Mockito.mock(Server.class)); + servers.add(Mockito.mock(Server.class)); + + SimpleLoadBalancer simpleLoadBalancer = new SimpleLoadBalancer(); + + simpleLoadBalancer.addServers(servers); + + List allServers = simpleLoadBalancer.getAllServers(); + List reachableServers = simpleLoadBalancer.getReachableServers(); + List availableServers = simpleLoadBalancer.getServerList(true); + + Assert.assertEquals(servers.size(), allServers.size()); + Assert.assertEquals(servers.size(), reachableServers.size()); + Assert.assertEquals(servers.size(), availableServers.size()); + } + + @Test + public void testSetNull() { + SimpleLoadBalancer simpleLoadBalancer = new SimpleLoadBalancer(); + + simpleLoadBalancer.addServers(null); + + List allServers = simpleLoadBalancer.getAllServers(); + List reachableServers = simpleLoadBalancer.getReachableServers(); + List availableServers = simpleLoadBalancer.getServerList(true); + + Assert.assertEquals(0, allServers.size()); + Assert.assertEquals(0, reachableServers.size()); + Assert.assertEquals(0, availableServers.size()); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtilsTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6078be75cf24782bcc90e94f1b19a7bd09076c2b --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/FeignExpressionLabelUtilsTest.java @@ -0,0 +1,140 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.feign; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.collect.Sets; +import feign.Request; +import feign.RequestTemplate; +import org.junit.Assert; +import org.junit.Test; + +import org.springframework.util.StringUtils; + +/** + * Test for {@link FeignExpressionLabelUtils} + *@author lepdou 2022-05-26 + */ +public class FeignExpressionLabelUtilsTest { + + @Test + public void testGetHeaderLabel() { + String headerKey = "uid"; + String headerValue = "1000"; + String headerKey2 = "teacher.age"; + String headerValue2 = "1000"; + + RequestTemplate requestTemplate = new RequestTemplate(); + requestTemplate.header(headerKey, headerValue); + requestTemplate.header(headerKey2, headerValue2); + + String labelKey1 = "${http.header.uid}"; + String labelKey2 = "${http.header.name}"; + String labelKey3 = "${http.headername}"; + String labelKey4 = "${http.header.}"; + String labelKey5 = "${http.header.teacher.age}"; + Map result = FeignExpressionLabelUtils.resolve(requestTemplate, + Sets.newHashSet(labelKey1, labelKey2, labelKey3, labelKey4, labelKey5)); + + Assert.assertFalse(result.isEmpty()); + Assert.assertEquals(headerValue, result.get(labelKey1)); + Assert.assertEquals(headerValue2, result.get(labelKey5)); + Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey2))); + Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey3))); + Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey4))); + } + + @Test + public void testGetQueryLabel() { + String headerKey = "uid"; + String headerValue = "1000"; + String headerKey2 = "teacher.age"; + String headerValue2 = "1000"; + + RequestTemplate requestTemplate = new RequestTemplate(); + requestTemplate.query(headerKey, headerValue); + requestTemplate.query(headerKey2, headerValue2); + + String labelKey1 = "${http.query.uid}"; + String labelKey2 = "${http.query.name}"; + String labelKey3 = "${http.queryname}"; + String labelKey4 = "${http.query.}"; + String labelKey5 = "${http.query.teacher.age}"; + Map result = FeignExpressionLabelUtils.resolve(requestTemplate, + Sets.newHashSet(labelKey1, labelKey2, labelKey3, labelKey4, labelKey5)); + + Assert.assertFalse(result.isEmpty()); + Assert.assertEquals(headerValue, result.get(labelKey1)); + Assert.assertEquals(headerValue2, result.get(labelKey5)); + Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey2))); + Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey3))); + Assert.assertTrue(StringUtils.isEmpty(result.get(labelKey4))); + } + + @Test + public void testGetMethod() { + RequestTemplate requestTemplate = new RequestTemplate(); + requestTemplate.method(Request.HttpMethod.GET); + + String labelKey1 = "${http.method}"; + Map result = FeignExpressionLabelUtils.resolve(requestTemplate, + Sets.newHashSet(labelKey1)); + + Assert.assertFalse(result.isEmpty()); + Assert.assertEquals("GET", result.get(labelKey1)); + } + + @Test + public void testGetUri() { + String uri = "/user/get"; + + RequestTemplate requestTemplate = new RequestTemplate(); + requestTemplate.uri(uri); + requestTemplate.method(Request.HttpMethod.GET); + requestTemplate.target("http://localhost"); + requestTemplate = requestTemplate.resolve(new HashMap<>()); + + String labelKey1 = "${http.uri}"; + Map result = FeignExpressionLabelUtils.resolve(requestTemplate, + Sets.newHashSet(labelKey1)); + + Assert.assertFalse(result.isEmpty()); + Assert.assertEquals(uri, result.get(labelKey1)); + } + + @Test + public void testGetUri2() { + String uri = "/"; + + RequestTemplate requestTemplate = new RequestTemplate(); + requestTemplate.uri(uri); + requestTemplate.method(Request.HttpMethod.GET); + requestTemplate.target("http://localhost"); + requestTemplate = requestTemplate.resolve(new HashMap<>()); + + String labelKey1 = "${http.uri}"; + Map result = FeignExpressionLabelUtils.resolve(requestTemplate, + Sets.newHashSet(labelKey1)); + + Assert.assertFalse(result.isEmpty()); + Assert.assertEquals(uri, result.get(labelKey1)); + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactoryTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactoryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c259e3b60f0775bd3052677488bcd4a43b3eaa8c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisCachingSpringLoadBalanceFactoryTest.java @@ -0,0 +1,104 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.feign; + +import com.netflix.client.config.DefaultClientConfigImpl; +import com.netflix.loadbalancer.ILoadBalancer; +import com.tencent.cloud.polaris.router.SimpleLoadBalancer; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * Test for {@link PolarisCachingSpringLoadBalanceFactory} + *@author lepdou 2022-05-26 + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisCachingSpringLoadBalanceFactoryTest { + + @Mock + private SpringClientFactory factory; + + private String service1 = "service1"; + private String service2 = "service2"; + + @Test + public void test() { + PolarisCachingSpringLoadBalanceFactory polarisCachingSpringLoadBalanceFactory = + new PolarisCachingSpringLoadBalanceFactory(factory, null); + + DefaultClientConfigImpl config1 = new DefaultClientConfigImpl(); + config1.loadDefaultValues(); + config1.setClientName(service1); + DefaultClientConfigImpl config2 = new DefaultClientConfigImpl(); + config2.loadDefaultValues(); + config2.setClientName(service2); + + when(factory.getClientConfig(service1)).thenReturn(config1); + when(factory.getClientConfig(service2)).thenReturn(config2); + + ILoadBalancer loadBalancer = new SimpleLoadBalancer(); + when(factory.getLoadBalancer(service1)).thenReturn(loadBalancer); + when(factory.getLoadBalancer(service2)).thenReturn(loadBalancer); + + ServerIntrospector serverIntrospector = new DefaultServerIntrospector(); + when(factory.getInstance(service1, ServerIntrospector.class)).thenReturn(serverIntrospector); + when(factory.getInstance(service2, ServerIntrospector.class)).thenReturn(serverIntrospector); + + // load balancer for service1 + FeignLoadBalancer feignLoadBalancer = polarisCachingSpringLoadBalanceFactory.create(service1); + + Assert.assertNotNull(feignLoadBalancer); + verify(factory).getClientConfig(service1); + verify(factory, times(0)).getClientConfig(service2); + verify(factory).getLoadBalancer(service1); + verify(factory, times(0)).getLoadBalancer(service2); + verify(factory).getInstance(service1, ServerIntrospector.class); + verify(factory, times(0)).getInstance(service2, ServerIntrospector.class); + Assert.assertEquals(loadBalancer, feignLoadBalancer.getLoadBalancer()); + Assert.assertEquals(service1, feignLoadBalancer.getClientName()); + + // load balancer for service2 + FeignLoadBalancer feignLoadBalancer2 = polarisCachingSpringLoadBalanceFactory.create(service2); + // load balancer for service1 again + feignLoadBalancer = polarisCachingSpringLoadBalanceFactory.create(service1); + + Assert.assertNotNull(feignLoadBalancer); + verify(factory).getClientConfig(service1); + verify(factory).getClientConfig(service2); + verify(factory).getLoadBalancer(service1); + verify(factory).getLoadBalancer(service2); + verify(factory).getInstance(service1, ServerIntrospector.class); + verify(factory).getInstance(service2, ServerIntrospector.class); + Assert.assertEquals(loadBalancer, feignLoadBalancer2.getLoadBalancer()); + Assert.assertEquals(service2, feignLoadBalancer2.getClientName()); + + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancerTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..52be650b663804ded4c9140b2181c1270c18914e --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/PolarisFeignLoadBalancerTest.java @@ -0,0 +1,121 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.feign; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.netflix.client.config.DefaultClientConfigImpl; +import com.netflix.loadbalancer.ILoadBalancer; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.polaris.router.PolarisRouterContext; +import com.tencent.cloud.polaris.router.RouterConstants; +import com.tencent.cloud.polaris.router.SimpleLoadBalancer; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.cloud.netflix.ribbon.DefaultServerIntrospector; +import org.springframework.cloud.netflix.ribbon.ServerIntrospector; + +import static org.mockito.Mockito.anyString; + +/** + * test for {@link PolarisFeignLoadBalancer} + * @author lepdou 2022-05-26 + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisFeignLoadBalancerTest { + + @Test + public void testHasRouterContext() { + DefaultClientConfigImpl config = new DefaultClientConfigImpl(); + config.loadDefaultValues(); + ILoadBalancer loadBalancer = new SimpleLoadBalancer(); + ServerIntrospector serverIntrospector = new DefaultServerIntrospector(); + + PolarisFeignLoadBalancer polarisFeignLoadBalancer = new PolarisFeignLoadBalancer(loadBalancer, config, serverIntrospector); + + Map labels = new HashMap<>(); + labels.put("k1", "v1"); + labels.put("k2", "v2"); + + List headerValues = new ArrayList<>(); + headerValues.add(JacksonUtils.serialize2Json(labels)); + + Map> headers = new HashMap<>(); + headers.put(RouterConstants.ROUTER_LABEL_HEADER, headerValues); + + // mock ApplicationContextAwareUtils#getProperties + try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) { + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())).thenReturn("unit-test"); + + MetadataContext metadataContext = Mockito.mock(MetadataContext.class); + // mock MetadataContextHolder#get + try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) { + mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); + + PolarisRouterContext routerContext = polarisFeignLoadBalancer.buildRouterContext(headers); + + Assert.assertNotNull(routerContext); + Map routerLabels = routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS); + Assert.assertNotNull(routerLabels); + Assert.assertEquals("v1", routerLabels.get("k1")); + Assert.assertEquals("v2", routerLabels.get("k2")); + Assert.assertNull(routerLabels.get("k3")); + } + } + } + + @Test + public void testHasNoneRouterContext() { + DefaultClientConfigImpl config = new DefaultClientConfigImpl(); + config.loadDefaultValues(); + ILoadBalancer loadBalancer = new SimpleLoadBalancer(); + ServerIntrospector serverIntrospector = new DefaultServerIntrospector(); + + PolarisFeignLoadBalancer polarisFeignLoadBalancer = new PolarisFeignLoadBalancer(loadBalancer, config, serverIntrospector); + + Map> headers = new HashMap<>(); + + // mock ApplicationContextAwareUtils#getProperties + try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) { + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())).thenReturn("unit-test"); + + MetadataContext metadataContext = Mockito.mock(MetadataContext.class); + // mock MetadataContextHolder#get + try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) { + mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); + + PolarisRouterContext routerContext = polarisFeignLoadBalancer.buildRouterContext(headers); + + Assert.assertNull(routerContext); + } + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..16f15c37216b7f5ee6861aa95ea5b29dddfef93a --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/feign/RouterLabelFeignInterceptorTest.java @@ -0,0 +1,142 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.feign; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.common.util.JacksonUtils; +import com.tencent.cloud.polaris.router.RouterConstants; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; +import feign.RequestTemplate; +import feign.Target; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * test for {@link RouterLabelFeignInterceptor} + * @author lepdou 2022-05-26 + * @author cheese8 2022-06-18 + */ +@RunWith(MockitoJUnitRunner.class) +public class RouterLabelFeignInterceptorTest { + + @Mock + private MetadataLocalProperties metadataLocalProperties; + @Mock + private RouterRuleLabelResolver routerRuleLabelResolver; + @Mock + private RouterLabelResolver routerLabelResolver; + + @Test + public void testResolveRouterLabel() { + RouterLabelFeignInterceptor routerLabelFeignInterceptor = new RouterLabelFeignInterceptor( + Collections.singletonList(routerLabelResolver), + metadataLocalProperties, routerRuleLabelResolver); + + // mock request template + RequestTemplate requestTemplate = new RequestTemplate(); + String headerUidKey = "uid"; + String headerUidValue = "1000"; + requestTemplate.header(headerUidKey, headerUidValue); + String peerService = "peerService"; + Target.EmptyTarget target = Target.EmptyTarget.create(Object.class, peerService); + requestTemplate.feignTarget(target); + + // mock ApplicationContextAwareUtils#getProperties + try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) { + String testService = "callerService"; + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn(testService); + + MetadataContext metadataContext = Mockito.mock(MetadataContext.class); + + // mock transitive metadata + Map transitiveLabels = new HashMap<>(); + transitiveLabels.put("k1", "v1"); + transitiveLabels.put("k2", "v22"); + when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels); + + // mock MetadataContextHolder#get + try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) { + mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); + + // mock custom resolved labels from request + Map customResolvedLabels = new HashMap<>(); + customResolvedLabels.put("k2", "v2"); + customResolvedLabels.put("k3", "v3"); + when(routerLabelResolver.resolve(requestTemplate)).thenReturn(customResolvedLabels); + + // mock expression rule labels + Set expressionKeys = new HashSet<>(); + expressionKeys.add("${http.header.uid}"); + expressionKeys.add("${http.header.name}"); + when(routerRuleLabelResolver.getExpressionLabelKeys(MetadataContext.LOCAL_NAMESPACE, + MetadataContext.LOCAL_SERVICE, peerService)).thenReturn(expressionKeys); + + // mock local metadata + Map localMetadata = new HashMap<>(); + localMetadata.put("k3", "v31"); + localMetadata.put("k4", "v4"); + when(metadataLocalProperties.getContent()).thenReturn(localMetadata); + + routerLabelFeignInterceptor.apply(requestTemplate); + + Collection routerLabels = requestTemplate.headers().get(RouterConstants.ROUTER_LABEL_HEADER); + Map routerLabelsMap = new HashMap<>(); + try { + String routerLabelContent = routerLabels.stream().findFirst().get(); + routerLabelsMap.putAll(JacksonUtils.deserialize2Map(URLDecoder.decode(routerLabelContent, StandardCharsets.UTF_8.name()))); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException("unsupported charset exception " + StandardCharsets.UTF_8.name()); + } + Assert.assertNotNull(routerLabelsMap); + for (String value : routerLabelsMap.values()) { + Assert.assertEquals("v1", routerLabelsMap.get("k1")); + Assert.assertEquals("v22", routerLabelsMap.get("k2")); + Assert.assertEquals("v3", routerLabelsMap.get("k3")); + Assert.assertEquals("v4", routerLabelsMap.get("k4")); + Assert.assertEquals(headerUidValue, routerLabelsMap.get("${http.header.uid}")); + Assert.assertEquals("", routerLabelsMap.get("${http.header.name}")); + } + } + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerBeanPostProcessorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerBeanPostProcessorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..18229efa68650ec8be06bd414af4989ec16828c8 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerBeanPostProcessorTest.java @@ -0,0 +1,94 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.resttemplate; + +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.util.BeanFactoryUtils; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.beanprocessor.LoadBalancerInterceptorBeanPostProcessor; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor; +import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory; + +import static org.mockito.Mockito.when; + +/** + * Test for ${@link LoadBalancerInterceptorBeanPostProcessor} + * @author lepdou 2022-05-26 + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisLoadBalancerBeanPostProcessorTest { + + @Mock + private LoadBalancerClient loadBalancerClient; + @Mock + private LoadBalancerRequestFactory loadBalancerRequestFactory; + @Mock + private MetadataLocalProperties metadataLocalProperties; + @Mock + private RouterRuleLabelResolver routerRuleLabelResolver; + @Mock + private BeanFactory beanFactory; + + @Test + public void testWrapperLoadBalancerInterceptor() { + when(beanFactory.getBean(LoadBalancerRequestFactory.class)).thenReturn(loadBalancerRequestFactory); + when(beanFactory.getBean(LoadBalancerClient.class)).thenReturn(loadBalancerClient); + when(beanFactory.getBean(MetadataLocalProperties.class)).thenReturn(metadataLocalProperties); + when(beanFactory.getBean(RouterRuleLabelResolver.class)).thenReturn(routerRuleLabelResolver); + + try (MockedStatic mockedBeanFactoryUtils = Mockito.mockStatic(BeanFactoryUtils.class)) { + mockedBeanFactoryUtils.when(() -> BeanFactoryUtils.getBeans(beanFactory, RouterLabelResolver.class)) + .thenReturn(null); + LoadBalancerInterceptor loadBalancerInterceptor = new LoadBalancerInterceptor(loadBalancerClient, loadBalancerRequestFactory); + + LoadBalancerInterceptorBeanPostProcessor processor = new LoadBalancerInterceptorBeanPostProcessor(); + processor.setBeanFactory(beanFactory); + + Object bean = processor.postProcessBeforeInitialization(loadBalancerInterceptor, ""); + + Assert.assertTrue(bean instanceof PolarisLoadBalancerInterceptor); + } + } + + @Test + public void testNotWrapperLoadBalancerInterceptor() { + LoadBalancerInterceptorBeanPostProcessor processor = new LoadBalancerInterceptorBeanPostProcessor(); + processor.setBeanFactory(beanFactory); + + OtherBean otherBean = new OtherBean(); + Object bean = processor.postProcessBeforeInitialization(otherBean, ""); + Assert.assertFalse(bean instanceof PolarisLoadBalancerInterceptor); + Assert.assertTrue(bean instanceof OtherBean); + } + + static class OtherBean { + + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..edbe8f24069371b89613316a1708633047c02149 --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/resttemplate/PolarisLoadBalancerInterceptorTest.java @@ -0,0 +1,250 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.resttemplate; + + +import java.io.IOException; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.polaris.router.PolarisRouterContext; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest; +import org.springframework.cloud.client.loadbalancer.LoadBalancerRequestFactory; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpRequest; +import org.springframework.http.HttpStatus; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.mock.http.client.MockClientHttpResponse; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +/** + * test for {@link PolarisLoadBalancerInterceptor} + * @author lepdou 2022-05-26 + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisLoadBalancerInterceptorTest { + + @Mock + private RibbonLoadBalancerClient loadBalancerClient; + @Mock + private LoadBalancerRequestFactory loadBalancerRequestFactory; + @Mock + private RouterLabelResolver routerLabelResolver; + @Mock + private MetadataLocalProperties metadataLocalProperties; + @Mock + private RouterRuleLabelResolver routerRuleLabelResolver; + + @Test + public void testProxyRibbonLoadBalance() throws Exception { + String callerService = "callerService"; + String calleeService = "calleeService"; + HttpRequest request = new MockedHttpRequest("http://" + calleeService + "/user/get"); + + // mock local metadata + Map localMetadata = new HashMap<>(); + localMetadata.put("k1", "v1"); + localMetadata.put("k2", "v2"); + when(metadataLocalProperties.getContent()).thenReturn(localMetadata); + + // mock custom resolved from request + Map customResolvedLabels = new HashMap<>(); + customResolvedLabels.put("k3", "v3"); + customResolvedLabels.put("k4", "v4"); + when(routerLabelResolver.resolve(request, null)).thenReturn(customResolvedLabels); + + // mock expression rule labels + + Set expressionKeys = new HashSet<>(); + expressionKeys.add("${http.method}"); + expressionKeys.add("${http.uri}"); + when(routerRuleLabelResolver.getExpressionLabelKeys(callerService, callerService, calleeService)).thenReturn(expressionKeys); + + try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) { + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn(callerService); + + MetadataContext metadataContext = Mockito.mock(MetadataContext.class); + + // mock transitive metadata + Map transitiveLabels = new HashMap<>(); + transitiveLabels.put("k1", "v1"); + transitiveLabels.put("k2", "v22"); + when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels); + + try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) { + mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); + + LoadBalancerRequest loadBalancerRequest = new MockedLoadBalancerRequest<>(); + when(loadBalancerRequestFactory.createRequest(request, null, null)).thenReturn(loadBalancerRequest); + + PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor = new PolarisLoadBalancerInterceptor(loadBalancerClient, + loadBalancerRequestFactory, Collections.singletonList(routerLabelResolver), metadataLocalProperties, routerRuleLabelResolver); + + polarisLoadBalancerInterceptor.intercept(request, null, null); + + verify(metadataLocalProperties).getContent(); + verify(routerRuleLabelResolver).getExpressionLabelKeys(callerService, callerService, calleeService); + verify(routerLabelResolver).resolve(request, null); + } + } + } + + @Test + public void testNotProxyRibbonLoadBalance() throws IOException { + String calleeService = "calleeService"; + HttpRequest request = new MockedHttpRequest("http://" + calleeService + "/user/get"); + + LoadBalancerRequest loadBalancerRequest = new MockedLoadBalancerRequest<>(); + when(loadBalancerRequestFactory.createRequest(request, null, null)).thenReturn(loadBalancerRequest); + + LoadBalancerClient notRibbonLoadBalancerClient = Mockito.mock(LoadBalancerClient.class); + ClientHttpResponse mockedResponse = new MockClientHttpResponse(new byte[] {}, HttpStatus.OK); + when(notRibbonLoadBalancerClient.execute(calleeService, loadBalancerRequest)).thenReturn(mockedResponse); + + PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor = new PolarisLoadBalancerInterceptor( + notRibbonLoadBalancerClient, loadBalancerRequestFactory, + Collections.singletonList(routerLabelResolver), metadataLocalProperties, + routerRuleLabelResolver); + + ClientHttpResponse response = polarisLoadBalancerInterceptor.intercept(request, null, null); + + Assert.assertEquals(mockedResponse, response); + verify(loadBalancerRequestFactory).createRequest(request, null, null); + verify(notRibbonLoadBalancerClient).execute(calleeService, loadBalancerRequest); + + } + + @Test + public void testRouterContext() throws Exception { + String callerService = "callerService"; + String calleeService = "calleeService"; + HttpRequest request = new MockedHttpRequest("http://" + calleeService + "/user/get"); + + // mock local metadata + Map localMetadata = new HashMap<>(); + localMetadata.put("k1", "v1"); + localMetadata.put("k2", "v2"); + when(metadataLocalProperties.getContent()).thenReturn(localMetadata); + + // mock custom resolved from request + Map customResolvedLabels = new HashMap<>(); + customResolvedLabels.put("k2", "v22"); + customResolvedLabels.put("k4", "v4"); + when(routerLabelResolver.resolve(request, null)).thenReturn(customResolvedLabels); + + // mock expression rule labels + + Set expressionKeys = new HashSet<>(); + expressionKeys.add("${http.method}"); + expressionKeys.add("${http.uri}"); + when(routerRuleLabelResolver.getExpressionLabelKeys(callerService, callerService, calleeService)).thenReturn(expressionKeys); + + try (MockedStatic mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class)) { + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn(callerService); + + MetadataContext metadataContext = Mockito.mock(MetadataContext.class); + + // mock transitive metadata + Map transitiveLabels = new HashMap<>(); + transitiveLabels.put("k1", "v1"); + transitiveLabels.put("k2", "v22"); + when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels); + + try (MockedStatic mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class)) { + mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); + + PolarisLoadBalancerInterceptor polarisLoadBalancerInterceptor = new PolarisLoadBalancerInterceptor(loadBalancerClient, + loadBalancerRequestFactory, Collections.singletonList(routerLabelResolver), metadataLocalProperties, routerRuleLabelResolver); + + PolarisRouterContext routerContext = polarisLoadBalancerInterceptor.genRouterContext(request, null, calleeService); + + verify(metadataLocalProperties).getContent(); + verify(routerRuleLabelResolver).getExpressionLabelKeys(callerService, callerService, calleeService); + verify(routerLabelResolver).resolve(request, null); + + Assert.assertEquals("v1", routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).get("k1")); + Assert.assertEquals("v22", routerContext.getLabels(PolarisRouterContext.TRANSITIVE_LABELS).get("k2")); + Assert.assertEquals("v1", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k1")); + Assert.assertEquals("v22", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k2")); + Assert.assertEquals("v4", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("k4")); + Assert.assertEquals("GET", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("${http.method}")); + Assert.assertEquals("/user/get", routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS).get("${http.uri}")); + } + } + } + + static class MockedLoadBalancerRequest implements LoadBalancerRequest { + + @Override + public T apply(ServiceInstance instance) throws Exception { + return null; + } + } + + static class MockedHttpRequest implements HttpRequest { + + private URI uri; + + MockedHttpRequest(String url) { + this.uri = URI.create(url); + } + + @Override + public String getMethodValue() { + return HttpMethod.GET.name(); + } + + @Override + public URI getURI() { + return uri; + } + + @Override + public HttpHeaders getHeaders() { + return null; + } + } +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientFilterTest.java b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientFilterTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6010689af0b86b5019f61817f4efd5c66f67b21c --- /dev/null +++ b/spring-cloud-starter-tencent-polaris-router/src/test/java/com/tencent/cloud/polaris/router/scg/PolarisLoadBalancerClientFilterTest.java @@ -0,0 +1,192 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.scg; + + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.polaris.router.PolarisRouterContext; +import com.tencent.cloud.polaris.router.RouterRuleLabelResolver; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; +import org.springframework.cloud.gateway.config.LoadBalancerProperties; +import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.server.MockServerWebExchange; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR; + +/** + * test for ${@link PolarisLoadBalancerClientFilter} + *@author lepdou 2022-06-13 + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisLoadBalancerClientFilterTest { + + @Mock + private MetadataLocalProperties metadataLocalProperties; + @Mock + private RouterRuleLabelResolver routerRuleLabelResolver; + @Mock + private RouterLabelResolver routerLabelResolver; + @Mock + private LoadBalancerClient loadBalancerClient; + @Mock + private LoadBalancerProperties loadBalancerProperties; + + private static final String callerService = "callerService"; + private static final String calleeService = "calleeService"; + + private static MockedStatic mockedApplicationContextAwareUtils; + private static MockedStatic mockedMetadataContextHolder; + + @BeforeClass + public static void beforeClass() { + mockedApplicationContextAwareUtils = Mockito.mockStatic(ApplicationContextAwareUtils.class); + mockedApplicationContextAwareUtils.when(() -> ApplicationContextAwareUtils.getProperties(anyString())) + .thenReturn(callerService); + + MetadataContext metadataContext = Mockito.mock(MetadataContext.class); + + // mock transitive metadata + Map transitiveLabels = new HashMap<>(); + transitiveLabels.put("t1", "v1"); + transitiveLabels.put("t2", "v2"); + when(metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE)).thenReturn(transitiveLabels); + + mockedMetadataContextHolder = Mockito.mockStatic(MetadataContextHolder.class); + mockedMetadataContextHolder.when(MetadataContextHolder::get).thenReturn(metadataContext); + } + + @AfterClass + public static void afterClass() { + mockedApplicationContextAwareUtils.close(); + mockedMetadataContextHolder.close(); + } + + @Test + public void testGenRouterContext() { + PolarisLoadBalancerClientFilter polarisLoadBalancerClientFilter = new PolarisLoadBalancerClientFilter( + loadBalancerClient, loadBalancerProperties, metadataLocalProperties, routerRuleLabelResolver, + Lists.newArrayList(routerLabelResolver)); + + Map localMetadata = new HashMap<>(); + localMetadata.put("env", "blue"); + when(metadataLocalProperties.getContent()).thenReturn(localMetadata); + + Set expressionLabelKeys = Sets.newHashSet("${http.header.k1}", "${http.query.userid}"); + when(routerRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString(), anyString())).thenReturn(expressionLabelKeys); + + MockServerHttpRequest request = MockServerHttpRequest.get("/" + calleeService + "/users") + .header("k1", "v1") + .queryParam("userid", "zhangsan") + .build(); + MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build(); + + Map customMetadata = new HashMap<>(); + customMetadata.put("k2", "v2"); + when(routerLabelResolver.resolve(webExchange)).thenReturn(customMetadata); + + PolarisRouterContext routerContext = polarisLoadBalancerClientFilter.genRouterContext(webExchange, calleeService); + + Map routerLabels = routerContext.getLabels(PolarisRouterContext.RULE_ROUTER_LABELS); + Assert.assertEquals("v1", routerLabels.get("${http.header.k1}")); + Assert.assertEquals("zhangsan", routerLabels.get("${http.query.userid}")); + Assert.assertEquals("blue", routerLabels.get("env")); + Assert.assertEquals("v1", routerLabels.get("t1")); + Assert.assertEquals("v2", routerLabels.get("t2")); + } + + @Test + public void testChooseInstanceWithoutRibbon() { + PolarisLoadBalancerClientFilter polarisLoadBalancerClientFilter = new PolarisLoadBalancerClientFilter( + loadBalancerClient, loadBalancerProperties, metadataLocalProperties, routerRuleLabelResolver, + Lists.newArrayList(routerLabelResolver)); + + String url = "/" + calleeService + "/users"; + MockServerHttpRequest request = MockServerHttpRequest.get(url) + .header("k1", "v1") + .queryParam("userid", "zhangsan") + .build(); + MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build(); + webExchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, URI.create("http://" + calleeService + "/users")); + + polarisLoadBalancerClientFilter.choose(webExchange); + + verify(loadBalancerClient).choose(calleeService); + verify(metadataLocalProperties, times(0)).getContent(); + } + + @Test + public void testChooseInstanceWithRibbon() { + RibbonLoadBalancerClient ribbonLoadBalancerClient = Mockito.mock(RibbonLoadBalancerClient.class); + + PolarisLoadBalancerClientFilter polarisLoadBalancerClientFilter = new PolarisLoadBalancerClientFilter( + ribbonLoadBalancerClient, loadBalancerProperties, metadataLocalProperties, routerRuleLabelResolver, + Lists.newArrayList(routerLabelResolver)); + + Map localMetadata = new HashMap<>(); + localMetadata.put("env", "blue"); + when(metadataLocalProperties.getContent()).thenReturn(localMetadata); + + Set expressionLabelKeys = Sets.newHashSet("${http.header.k1}", "${http.query.userid}"); + when(routerRuleLabelResolver.getExpressionLabelKeys(anyString(), anyString(), anyString())).thenReturn(expressionLabelKeys); + + String url = "/" + calleeService + "/users"; + MockServerHttpRequest request = MockServerHttpRequest.get(url) + .header("k1", "v1") + .queryParam("userid", "zhangsan") + .build(); + MockServerWebExchange webExchange = new MockServerWebExchange.Builder(request).build(); + webExchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, URI.create("http://" + calleeService + "/users")); + + Map customMetadata = new HashMap<>(); + customMetadata.put("k2", "v2"); + when(routerLabelResolver.resolve(webExchange)).thenReturn(customMetadata); + + polarisLoadBalancerClientFilter.choose(webExchange); + + verify(ribbonLoadBalancerClient).choose(anyString(), any()); + verify(metadataLocalProperties, times(1)).getContent(); + } +} diff --git a/spring-cloud-tencent-commons/pom.xml b/spring-cloud-tencent-commons/pom.xml index 8f3ad8e84b020c01a6fe6b678a8bed3f3bd37fe3..495fc4a1544cff657d3790dd49b356354b260aed 100644 --- a/spring-cloud-tencent-commons/pom.xml +++ b/spring-cloud-tencent-commons/pom.xml @@ -14,7 +14,6 @@ Spring Cloud Tencent Commons - 3.2.2 2.5 2.7 @@ -44,13 +43,7 @@ org.springframework.cloud - spring-cloud-commons - - - - commons-collections - commons-collections - ${commons.collections.version} + spring-cloud-starter @@ -83,11 +76,35 @@ true + + org.springframework.boot + spring-boot-starter-web + true + + + + org.springframework.boot + spring-boot-starter-webflux + true + + org.springframework.boot spring-boot-starter-test test + + + org.springframework.boot + spring-boot-actuator + true + + + + org.springframework.boot + spring-boot-actuator-autoconfigure + true + diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java index 54f2038786c5caad236394baf6c621c1d0b73661..71ce1e8861f444e585d12050bdcfb29bb9a712fb 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/ContextConstant.java @@ -24,6 +24,14 @@ package com.tencent.cloud.common.constant; */ public final class ContextConstant { + /** + * Name of Polaris. + */ + public static final String POLARIS = "POLARIS"; + + private ContextConstant() { + } + /** * Order of configuration modifier. */ @@ -44,6 +52,11 @@ public final class ContextConstant { */ public static Integer CIRCUIT_BREAKER_ORDER = 1; + /** + * Order of discovery configuration modifier. + */ + public static Integer DISCOVERY_ORDER = 0; + /** * Order of configuration modifier. */ diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/MetadataConstant.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/MetadataConstant.java index ce498126b54db44773dec6513c62c3ad403c98ce..d45c7156874f218a5957e73de4e324e5894b4120 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/MetadataConstant.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/constant/MetadataConstant.java @@ -26,28 +26,6 @@ import org.springframework.core.Ordered; */ public final class MetadataConstant { - /** - * System metadata key. - */ - public static class SystemMetadataKey { - - /** - * Peer namespace. - */ - public static String PEER_NAMESPACE = "PEER_NAMESPACE"; - - /** - * Peer service. - */ - public static String PEER_SERVICE = "PEER_SERVICE"; - - /** - * Peer path. - */ - public static String PEER_PATH = "PEER_PATH"; - - } - /** * Order of filter, interceptor, ... */ diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java index cf4e59b84ab754ec6d883b6bd3f80b51c46dc025..90148cd229d7346766bc87ebab594922be4456d0 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContext.java @@ -13,6 +13,7 @@ * 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.tencent.cloud.common.metadata; @@ -23,6 +24,10 @@ import java.util.concurrent.ConcurrentHashMap; import com.tencent.cloud.common.util.ApplicationContextAwareUtils; import com.tencent.cloud.common.util.JacksonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.util.StringUtils; /** * Metadata Context. @@ -32,70 +37,94 @@ import com.tencent.cloud.common.util.JacksonUtils; public class MetadataContext { /** - * Namespace of local instance. + * transitive context. */ - public static final String LOCAL_NAMESPACE = ApplicationContextAwareUtils - .getProperties("spring.cloud.polaris.discovery.namespace", "default"); - + public static final String FRAGMENT_TRANSITIVE = "transitive"; + private static final Logger LOG = LoggerFactory.getLogger(MetadataContext.class); /** - * Service name of local instance. - */ - public static final String LOCAL_SERVICE = ApplicationContextAwareUtils.getProperties( - "spring.cloud.polaris.discovery.service", - ApplicationContextAwareUtils.getProperties("spring.application.name", null)); - - /** - * Transitive custom metadata content. + * Namespace of local instance. */ - private final Map transitiveCustomMetadata; + public static String LOCAL_NAMESPACE; /** - * System metadata content. + * Service name of local instance. */ - private final Map systemMetadata; - - public MetadataContext() { - this.transitiveCustomMetadata = new ConcurrentHashMap<>(); - this.systemMetadata = new ConcurrentHashMap<>(); + public static String LOCAL_SERVICE; + + static { + String namespace = ApplicationContextAwareUtils + .getProperties("spring.cloud.polaris.namespace"); + if (StringUtils.isEmpty(namespace)) { + namespace = ApplicationContextAwareUtils + .getProperties("spring.cloud.polaris.discovery.namespace", "default"); + } + + if (StringUtils.isEmpty(namespace)) { + LOG.error("namespace should not be blank. please configure spring.cloud.polaris.namespace or " + + "spring.cloud.polaris.discovery.namespace"); + throw new RuntimeException("namespace should not be blank. please configure spring.cloud.polaris.namespace or " + + "spring.cloud.polaris.discovery.namespace"); + } + + LOCAL_NAMESPACE = namespace; + + String serviceName = ApplicationContextAwareUtils + .getProperties("spring.cloud.polaris.service"); + if (StringUtils.isEmpty(serviceName)) { + serviceName = ApplicationContextAwareUtils.getProperties( + "spring.cloud.polaris.discovery.service", ApplicationContextAwareUtils + .getProperties("spring.application.name", null)); + } + + if (StringUtils.isEmpty(serviceName)) { + LOG.error("service name should not be blank. please configure spring.cloud.polaris.service or " + + "spring.cloud.polaris.discovery.service or spring.application.name"); + throw new RuntimeException("service name should not be blank. please configure spring.cloud.polaris.service or " + + "spring.cloud.polaris.discovery.service or spring.application.name"); + } + LOCAL_SERVICE = serviceName; } - public Map getAllTransitiveCustomMetadata() { - return Collections.unmodifiableMap(this.transitiveCustomMetadata); - } + private final Map> fragmentContexts; - public String getTransitiveCustomMetadata(String key) { - return this.transitiveCustomMetadata.get(key); - } - - public void putTransitiveCustomMetadata(String key, String value) { - this.transitiveCustomMetadata.put(key, value); + public MetadataContext() { + this.fragmentContexts = new ConcurrentHashMap<>(); } - public void putAllTransitiveCustomMetadata(Map customMetadata) { - this.transitiveCustomMetadata.putAll(customMetadata); - } - public Map getAllSystemMetadata() { - return Collections.unmodifiableMap(this.systemMetadata); + public Map getFragmentContext(String fragment) { + Map fragmentContext = fragmentContexts.get(fragment); + if (fragmentContext == null) { + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(fragmentContext); } - public String getSystemMetadata(String key) { - return this.systemMetadata.get(key); + public String getContext(String fragment, String key) { + Map fragmentContext = fragmentContexts.get(fragment); + if (fragmentContext == null) { + return null; + } + return fragmentContext.get(key); } - public void putSystemMetadata(String key, String value) { - this.systemMetadata.put(key, value); + public void putContext(String fragment, String key, String value) { + Map fragmentContext = fragmentContexts.get(fragment); + if (fragmentContext == null) { + fragmentContext = new ConcurrentHashMap<>(); + fragmentContexts.put(fragment, fragmentContext); + } + fragmentContext.put(key, value); } - public void putAllSystemMetadata(Map systemMetadata) { - this.systemMetadata.putAll(systemMetadata); + public void putFragmentContext(String fragment, Map context) { + fragmentContexts.put(fragment, context); } @Override public String toString() { - return "MetadataContext{" + "transitiveCustomMetadata=" - + JacksonUtils.serialize2Json(transitiveCustomMetadata) - + ", systemMetadata=" + JacksonUtils.serialize2Json(systemMetadata) + '}'; + return "MetadataContext{" + + "fragmentContexts=" + JacksonUtils.serialize2Json(fragmentContexts) + + '}'; } - } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java index b8811f8a511a221dc9e0106792fc7237f2772aac..b270c368b5c378c00e83f76da0a0baa35e1a7cc6 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/MetadataContextHolder.java @@ -13,12 +13,13 @@ * 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.tencent.cloud.common.metadata; +import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; @@ -36,9 +37,9 @@ public final class MetadataContextHolder { private static final ThreadLocal METADATA_CONTEXT = new InheritableThreadLocal<>(); private static MetadataLocalProperties metadataLocalProperties; + private static StaticMetadataManager staticMetadataManager; private MetadataContextHolder() { - } /** @@ -46,39 +47,27 @@ public final class MetadataContextHolder { * @return METADATA_CONTEXT */ public static MetadataContext get() { - if (null == METADATA_CONTEXT.get()) { - MetadataContext metadataContext = new MetadataContext(); - if (metadataLocalProperties == null) { - metadataLocalProperties = (MetadataLocalProperties) ApplicationContextAwareUtils - .getApplicationContext().getBean("metadataLocalProperties"); - } - - // init custom metadata and load local metadata - Map transitiveMetadataMap = getTransitiveMetadataMap( - metadataLocalProperties.getContent(), - metadataLocalProperties.getTransitive()); - metadataContext.putAllTransitiveCustomMetadata(transitiveMetadataMap); - - METADATA_CONTEXT.set(metadataContext); + if (METADATA_CONTEXT.get() != null) { + return METADATA_CONTEXT.get(); } - return METADATA_CONTEXT.get(); - } - /** - * Filter and store the transitive metadata to transitive metadata context. - * @param source all metadata content - * @param transitiveMetadataKeyList transitive metadata name list - * @return result - */ - private static Map getTransitiveMetadataMap( - Map source, List transitiveMetadataKeyList) { - Map result = new HashMap<>(); - for (String key : transitiveMetadataKeyList) { - if (source.containsKey(key)) { - result.put(key, source.get(key)); - } + if (metadataLocalProperties == null) { + metadataLocalProperties = (MetadataLocalProperties) ApplicationContextAwareUtils + .getApplicationContext().getBean("metadataLocalProperties"); + } + if (staticMetadataManager == null) { + staticMetadataManager = (StaticMetadataManager) ApplicationContextAwareUtils + .getApplicationContext().getBean("metadataManager"); } - return result; + + // init static transitive metadata + MetadataContext metadataContext = new MetadataContext(); + metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, + staticMetadataManager.getMergedStaticTransitiveMetadata()); + + METADATA_CONTEXT.set(metadataContext); + + return METADATA_CONTEXT.get(); } /** @@ -91,21 +80,22 @@ public final class MetadataContextHolder { /** * Save metadata map to thread local. - * @param customMetadataMap custom metadata collection - * @param systemMetadataMap system metadata collection + * @param dynamicTransitiveMetadata custom metadata collection */ - public static void init(Map customMetadataMap, - Map systemMetadataMap) { + public static void init(Map dynamicTransitiveMetadata) { // Init ThreadLocal. MetadataContextHolder.remove(); MetadataContext metadataContext = MetadataContextHolder.get(); - // Save to ThreadLocal. - if (!CollectionUtils.isEmpty(customMetadataMap)) { - metadataContext.putAllTransitiveCustomMetadata(customMetadataMap); - } - if (!CollectionUtils.isEmpty(systemMetadataMap)) { - metadataContext.putAllSystemMetadata(systemMetadataMap); + // Save transitive metadata to ThreadLocal. + if (!CollectionUtils.isEmpty(dynamicTransitiveMetadata)) { + Map staticTransitiveMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + Map mergedTransitiveMetadata = new HashMap<>(); + mergedTransitiveMetadata.putAll(staticTransitiveMetadata); + mergedTransitiveMetadata.putAll(dynamicTransitiveMetadata); + + metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, + Collections.unmodifiableMap(mergedTransitiveMetadata)); } MetadataContextHolder.set(metadataContext); } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/StaticMetadataManager.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/StaticMetadataManager.java new file mode 100644 index 0000000000000000000000000000000000000000..5d4143b73912adbef80ed1e2a562932d6972841a --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/StaticMetadataManager.java @@ -0,0 +1,300 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.metadata; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.spi.InstanceMetadataProvider; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.util.CollectionUtils; + +/** + * manage metadata from env/config file/custom spi. + * + *@author lepdou 2022-05-20 + */ +public class StaticMetadataManager { + private static final Logger LOGGER = LoggerFactory.getLogger(StaticMetadataManager.class); + + private static final String ENV_METADATA_PREFIX = "SCT_METADATA_CONTENT_"; + private static final int ENV_METADATA_PREFIX_LENGTH = ENV_METADATA_PREFIX.length(); + private static final String ENV_METADATA_CONTENT_TRANSITIVE = "SCT_METADATA_CONTENT_TRANSITIVE"; + private static final String ENV_METADATA_ZONE = "SCT_METADATA_ZONE"; + private static final String ENV_METADATA_REGION = "SCT_METADATA_REGION"; + private static final String ENV_METADATA_CAMPUS = "SCT_METADATA_CAMPUS"; + + /** + * the metadata key of region. + */ + public static final String LOCATION_KEY_REGION = "region"; + /** + * the metadata key of zone. + */ + public static final String LOCATION_KEY_ZONE = "zone"; + /** + * the metadata key of campus/datacenter. + */ + public static final String LOCATION_KEY_CAMPUS = "campus"; + + private Map envMetadata; + private Map envTransitiveMetadata; + private Map configMetadata; + private Map configTransitiveMetadata; + private Map customSPIMetadata; + private Map customSPITransitiveMetadata; + + private Map mergedStaticMetadata; + private Map mergedStaticTransitiveMetadata; + private String zone; + private String region; + private String campus; + + public StaticMetadataManager(MetadataLocalProperties metadataLocalProperties, + InstanceMetadataProvider instanceMetadataProvider) { + parseConfigMetadata(metadataLocalProperties); + + parseEnvMetadata(); + + parseCustomMetadata(instanceMetadataProvider); + + parseLocationMetadata(metadataLocalProperties, instanceMetadataProvider); + + merge(); + + LOGGER.info("[SCT] Loaded static metadata info. {}", this); + } + + private void parseEnvMetadata() { + Map allEnvs = System.getenv(); + + envMetadata = new HashMap<>(); + // parse all metadata + for (Map.Entry entry : allEnvs.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (StringUtils.isNotBlank(key) && key.startsWith(ENV_METADATA_PREFIX) + && !key.equals(ENV_METADATA_CONTENT_TRANSITIVE)) { + String sourceKey = StringUtils.substring(key, ENV_METADATA_PREFIX_LENGTH); + envMetadata.put(sourceKey, value); + + LOGGER.info("[SCT] resolve metadata from env. key = {}, value = {}", sourceKey, value); + } + } + envMetadata = Collections.unmodifiableMap(envMetadata); + + envTransitiveMetadata = new HashMap<>(); + // parse transitive metadata + String transitiveKeys = allEnvs.get(ENV_METADATA_CONTENT_TRANSITIVE); + if (StringUtils.isNotBlank(transitiveKeys)) { + String[] keyArr = StringUtils.split(transitiveKeys, ","); + if (keyArr != null && keyArr.length > 0) { + for (String key : keyArr) { + String value = envMetadata.get(key); + if (StringUtils.isNotBlank(value)) { + envTransitiveMetadata.put(key, value); + } + } + } + } + envTransitiveMetadata = Collections.unmodifiableMap(envTransitiveMetadata); + } + + private void parseConfigMetadata(MetadataLocalProperties metadataLocalProperties) { + Map allMetadata = metadataLocalProperties.getContent(); + List transitiveKeys = metadataLocalProperties.getTransitive(); + + Map result = new HashMap<>(); + for (String key : transitiveKeys) { + if (allMetadata.containsKey(key)) { + result.put(key, allMetadata.get(key)); + } + } + + configTransitiveMetadata = Collections.unmodifiableMap(result); + configMetadata = Collections.unmodifiableMap(allMetadata); + } + + private void parseCustomMetadata(InstanceMetadataProvider instanceMetadataProvider) { + if (instanceMetadataProvider == null) { + customSPIMetadata = Collections.emptyMap(); + customSPITransitiveMetadata = Collections.emptyMap(); + return; + } + + // resolve all metadata + Map allMetadata = instanceMetadataProvider.getMetadata(); + if (allMetadata == null) { + customSPIMetadata = Collections.emptyMap(); + } + else { + customSPIMetadata = Collections.unmodifiableMap(allMetadata); + } + + // resolve transitive metadata + Set transitiveKeys = instanceMetadataProvider.getTransitiveMetadataKeys(); + Map transitiveMetadata = new HashMap<>(); + if (!CollectionUtils.isEmpty(transitiveKeys)) { + for (String key : transitiveKeys) { + if (customSPIMetadata.containsKey(key)) { + transitiveMetadata.put(key, customSPIMetadata.get(key)); + } + } + } + customSPITransitiveMetadata = Collections.unmodifiableMap(transitiveMetadata); + } + + private void merge() { + // the priority is : custom > env > config + Map mergedMetadataResult = new HashMap<>(); + + mergedMetadataResult.putAll(configMetadata); + mergedMetadataResult.putAll(envMetadata); + mergedMetadataResult.putAll(customSPIMetadata); + // set location info as metadata + mergedMetadataResult.putAll(getLocationMetadata()); + + this.mergedStaticMetadata = Collections.unmodifiableMap(mergedMetadataResult); + + Map mergedTransitiveMetadataResult = new HashMap<>(); + mergedTransitiveMetadataResult.putAll(configTransitiveMetadata); + mergedTransitiveMetadataResult.putAll(envTransitiveMetadata); + mergedTransitiveMetadataResult.putAll(customSPITransitiveMetadata); + + this.mergedStaticTransitiveMetadata = Collections.unmodifiableMap(mergedTransitiveMetadataResult); + } + + private void parseLocationMetadata(MetadataLocalProperties metadataLocalProperties, + InstanceMetadataProvider instanceMetadataProvider) { + // resolve zone info + if (instanceMetadataProvider != null) { + zone = instanceMetadataProvider.getZone(); + } + if (StringUtils.isBlank(zone)) { + zone = System.getenv(ENV_METADATA_ZONE); + } + if (StringUtils.isBlank(zone)) { + zone = metadataLocalProperties.getContent().get(LOCATION_KEY_ZONE); + } + + // resolve region info + if (instanceMetadataProvider != null) { + region = instanceMetadataProvider.getRegion(); + } + if (StringUtils.isBlank(region)) { + region = System.getenv(ENV_METADATA_REGION); + } + if (StringUtils.isBlank(region)) { + region = metadataLocalProperties.getContent().get(LOCATION_KEY_REGION); + } + + // resolve campus info + if (instanceMetadataProvider != null) { + campus = instanceMetadataProvider.getCampus(); + } + if (StringUtils.isBlank(campus)) { + campus = System.getenv(ENV_METADATA_CAMPUS); + } + if (StringUtils.isBlank(campus)) { + campus = metadataLocalProperties.getContent().get(LOCATION_KEY_CAMPUS); + } + } + + public Map getAllEnvMetadata() { + return envMetadata; + } + + public Map getEnvTransitiveMetadata() { + return envTransitiveMetadata; + } + + public Map getAllConfigMetadata() { + return configMetadata; + } + + public Map getConfigTransitiveMetadata() { + return configTransitiveMetadata; + } + + public Map getAllCustomMetadata() { + return customSPIMetadata; + } + + public Map getCustomSPITransitiveMetadata() { + return customSPITransitiveMetadata; + } + + public Map getMergedStaticMetadata() { + return mergedStaticMetadata; + } + + public Map getMergedStaticTransitiveMetadata() { + return mergedStaticTransitiveMetadata; + } + + public String getZone() { + return zone; + } + + public String getRegion() { + return region; + } + + public String getCampus() { + return campus; + } + + public Map getLocationMetadata() { + Map locationMetadata = new HashMap<>(); + if (StringUtils.isNotBlank(region)) { + locationMetadata.put(LOCATION_KEY_REGION, region); + } + if (StringUtils.isNotBlank(zone)) { + locationMetadata.put(LOCATION_KEY_ZONE, zone); + } + if (StringUtils.isNotBlank(campus)) { + locationMetadata.put(LOCATION_KEY_CAMPUS, campus); + } + return locationMetadata; + } + + @Override + public String toString() { + return "StaticMetadataManager{" + + "envMetadata=" + envMetadata + + ", envTransitiveMetadata=" + envTransitiveMetadata + + ", configMetadata=" + configMetadata + + ", configTransitiveMetadata=" + configTransitiveMetadata + + ", customSPIMetadata=" + customSPIMetadata + + ", customSPITransitiveMetadata=" + customSPITransitiveMetadata + + ", mergedStaticMetadata=" + mergedStaticMetadata + + ", mergedStaticTransitiveMetadata=" + mergedStaticTransitiveMetadata + + ", zone='" + zone + '\'' + + ", region='" + region + '\'' + + ", campus='" + campus + '\'' + + '}'; + } +} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/config/MetadataAutoConfiguration.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/config/MetadataAutoConfiguration.java index b7254737fb909898644f2ada136b88714e5cde76..0271297ea376e37251f5dab138e9a895d73e7adb 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/config/MetadataAutoConfiguration.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/config/MetadataAutoConfiguration.java @@ -13,30 +13,20 @@ * 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.tencent.cloud.common.metadata.config; -import com.netflix.zuul.ZuulFilter; -import com.tencent.cloud.common.constant.MetadataConstant; +import com.tencent.cloud.common.metadata.StaticMetadataManager; import com.tencent.cloud.common.metadata.filter.gateway.MetadataFirstScgFilter; -import com.tencent.cloud.common.metadata.filter.gateway.MetadataFirstZuulFilter; -import com.tencent.cloud.common.metadata.filter.web.MetadataReactiveFilter; -import com.tencent.cloud.common.metadata.filter.web.MetadataServletFilter; -import com.tencent.cloud.common.metadata.interceptor.feign.MetadataFirstFeignInterceptor; +import com.tencent.cloud.common.spi.InstanceMetadataProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; - -import static javax.servlet.DispatcherType.ASYNC; -import static javax.servlet.DispatcherType.ERROR; -import static javax.servlet.DispatcherType.FORWARD; -import static javax.servlet.DispatcherType.INCLUDE; -import static javax.servlet.DispatcherType.REQUEST; +import org.springframework.lang.Nullable; /** * Metadata auto configuration. @@ -55,72 +45,10 @@ public class MetadataAutoConfiguration { return new MetadataLocalProperties(); } - /** - * Create when web application type is SERVLET. - */ - @Configuration - @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) - static class MetadataServletFilterConfig { - - @Bean - public FilterRegistrationBean metadataServletFilterRegistrationBean( - MetadataServletFilter metadataServletFilter) { - FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>( - metadataServletFilter); - filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, - REQUEST); - filterRegistrationBean - .setOrder(MetadataConstant.OrderConstant.WEB_FILTER_ORDER); - return filterRegistrationBean; - } - - @Bean - public MetadataServletFilter metadataServletFilter() { - return new MetadataServletFilter(); - } - - } - - /** - * Create when web application type is REACTIVE. - */ - @Configuration - @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) - static class MetadataReactiveFilterConfig { - - @Bean - public MetadataReactiveFilter metadataReactiveFilter() { - return new MetadataReactiveFilter(); - } - - } - - /** - * Create when Feign exists. - */ - @Configuration - @ConditionalOnClass(name = "feign.Feign") - static class MetadataFeignInterceptorConfig { - - @Bean - public MetadataFirstFeignInterceptor metadataFirstFeignInterceptor() { - return new MetadataFirstFeignInterceptor(); - } - - } - - /** - * Create when gateway application is Zuul. - */ - @Configuration - @ConditionalOnClass(name = "com.netflix.zuul.http.ZuulServlet") - static class MetadataZuulFilterConfig { - - @Bean - public ZuulFilter metadataFirstZuulFilter() { - return new MetadataFirstZuulFilter(); - } - + @Bean + public StaticMetadataManager metadataManager(MetadataLocalProperties metadataLocalProperties, + @Nullable InstanceMetadataProvider instanceMetadataProvider) { + return new StaticMetadataManager(metadataLocalProperties, instanceMetadataProvider); } /** diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/endpoint/PolarisMetadataEndpoint.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/endpoint/PolarisMetadataEndpoint.java new file mode 100644 index 0000000000000000000000000000000000000000..360018ea2bc460a295fb9bf8796efa66e49f3d15 --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/endpoint/PolarisMetadataEndpoint.java @@ -0,0 +1,56 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.metadata.endpoint; + +import java.util.HashMap; +import java.util.Map; + +import com.tencent.cloud.common.metadata.StaticMetadataManager; + +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; + +/** + * Endpoint of polaris's metadata. + * + * @author shuiqingliu + **/ +@Endpoint(id = "polaris-metadata") +public class PolarisMetadataEndpoint { + + private final StaticMetadataManager staticMetadataManager; + + public PolarisMetadataEndpoint(StaticMetadataManager staticMetadataManager) { + this.staticMetadataManager = staticMetadataManager; + } + + @ReadOperation + public Map metadata() { + Map result = new HashMap<>(); + result.put("Env", staticMetadataManager.getAllEnvMetadata()); + result.put("EnvTransitive", staticMetadataManager.getEnvTransitiveMetadata()); + result.put("ConfigTransitive", staticMetadataManager.getConfigTransitiveMetadata()); + result.put("Config", staticMetadataManager.getAllConfigMetadata()); + result.put("MergeStatic", staticMetadataManager.getMergedStaticMetadata()); + result.put("CustomSPI", staticMetadataManager.getCustomSPITransitiveMetadata()); + result.put("zone", staticMetadataManager.getZone()); + result.put("region", staticMetadataManager.getRegion()); + result.put("campus", staticMetadataManager.getCampus()); + return result; + } +} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/endpoint/PolarisMetadataEndpointAutoConfiguration.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/endpoint/PolarisMetadataEndpointAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..9c87587c9dac67eee6863b8f7e509241f57a1b0a --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/endpoint/PolarisMetadataEndpointAutoConfiguration.java @@ -0,0 +1,44 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.metadata.endpoint; + +import com.tencent.cloud.common.metadata.StaticMetadataManager; + +import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint; +import org.springframework.boot.actuate.endpoint.annotation.Endpoint; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * The AutoConfiguration for metadata endpoint. + * + * @author shuiqingliu + **/ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(Endpoint.class) +public class PolarisMetadataEndpointAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + @ConditionalOnAvailableEndpoint + public PolarisMetadataEndpoint metadataEndpoint(StaticMetadataManager staticMetadataManager) { + return new PolarisMetadataEndpoint(staticMetadataManager); + } +} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/gateway/MetadataFirstScgFilter.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/gateway/MetadataFirstScgFilter.java index 13952a6c54f708419f9ceb9d8a1e44e4124bb6e9..a79ba5323d9b1359609542263025ba8d876d2b32 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/gateway/MetadataFirstScgFilter.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/gateway/MetadataFirstScgFilter.java @@ -25,12 +25,10 @@ import reactor.core.publisher.Mono; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; -import org.springframework.cloud.gateway.route.Route; import org.springframework.core.Ordered; import org.springframework.web.server.ServerWebExchange; import static org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter.ROUTE_TO_URL_FILTER_ORDER; -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR; /** * Scg output first filter used for setting peer info in context. @@ -51,9 +49,6 @@ public class MetadataFirstScgFilter implements GlobalFilter, Ordered { @Override public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - // get request context - Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); - // get metadata of current thread MetadataContext metadataContext = exchange .getAttribute(MetadataConstant.HeaderName.METADATA_CONTEXT); @@ -61,15 +56,6 @@ public class MetadataFirstScgFilter implements GlobalFilter, Ordered { metadataContext = MetadataContextHolder.get(); } - // TODO The peer namespace is temporarily the same as the local namespace - metadataContext.putSystemMetadata( - MetadataConstant.SystemMetadataKey.PEER_NAMESPACE, - MetadataContext.LOCAL_NAMESPACE); - metadataContext.putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_SERVICE, - route.getId()); - metadataContext.putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_PATH, - exchange.getRequest().getURI().getPath()); - exchange.getAttributes().put(MetadataConstant.HeaderName.METADATA_CONTEXT, metadataContext); diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/gateway/MetadataFirstZuulFilter.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/gateway/MetadataFirstZuulFilter.java deleted file mode 100644 index acb2acc877154e50a3833d4c63183bae221412bd..0000000000000000000000000000000000000000 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/filter/gateway/MetadataFirstZuulFilter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * 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.tencent.cloud.common.metadata.filter.gateway; - -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; -import com.tencent.cloud.common.constant.MetadataConstant; -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.metadata.MetadataContextHolder; - -import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_DECORATION_FILTER_ORDER; -import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; -import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.REQUEST_URI_KEY; -import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.SERVICE_ID_KEY; - -/** - * Zuul output first filter used for setting peer info in context. - * - * @author Haotian Zhang - */ -public class MetadataFirstZuulFilter extends ZuulFilter { - - @Override - public String filterType() { - return PRE_TYPE; - } - - @Override - public int filterOrder() { - return PRE_DECORATION_FILTER_ORDER + 1; - } - - @Override - public boolean shouldFilter() { - return true; - } - - @Override - public Object run() { - // get request context - RequestContext requestContext = RequestContext.getCurrentContext(); - - // TODO The peer namespace is temporarily the same as the local namespace - MetadataContextHolder.get().putSystemMetadata( - MetadataConstant.SystemMetadataKey.PEER_NAMESPACE, - MetadataContext.LOCAL_NAMESPACE); - MetadataContextHolder.get().putSystemMetadata( - MetadataConstant.SystemMetadataKey.PEER_SERVICE, - (String) requestContext.get(SERVICE_ID_KEY)); - MetadataContextHolder.get().putSystemMetadata( - MetadataConstant.SystemMetadataKey.PEER_PATH, - (String) requestContext.get(REQUEST_URI_KEY)); - return null; - } - -} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/interceptor/feign/MetadataFirstFeignInterceptor.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/interceptor/feign/MetadataFirstFeignInterceptor.java deleted file mode 100644 index 18f5390ab6f015c2b02e0092cbaacbdf1f4e2f27..0000000000000000000000000000000000000000 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/metadata/interceptor/feign/MetadataFirstFeignInterceptor.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. - * - * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. - * - * Licensed under the BSD 3-Clause License (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - * - * 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.tencent.cloud.common.metadata.interceptor.feign; - -import com.tencent.cloud.common.constant.MetadataConstant; -import com.tencent.cloud.common.metadata.MetadataContext; -import com.tencent.cloud.common.metadata.MetadataContextHolder; -import feign.RequestInterceptor; -import feign.RequestTemplate; - -import org.springframework.core.Ordered; - -/** - * Interceptor used for setting peer info in context. - * - * @author Haotian Zhang - */ -public class MetadataFirstFeignInterceptor implements RequestInterceptor, Ordered { - - @Override - public int getOrder() { - return MetadataConstant.OrderConstant.METADATA_FIRST_FEIGN_INTERCEPTOR_ORDER; - } - - @Override - public void apply(RequestTemplate requestTemplate) { - // get metadata of current thread - MetadataContext metadataContext = MetadataContextHolder.get(); - - // TODO The peer namespace is temporarily the same as the local namespace - metadataContext.putSystemMetadata( - MetadataConstant.SystemMetadataKey.PEER_NAMESPACE, - MetadataContext.LOCAL_NAMESPACE); - metadataContext.putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_SERVICE, - requestTemplate.feignTarget().name()); - metadataContext.putSystemMetadata(MetadataConstant.SystemMetadataKey.PEER_PATH, - requestTemplate.path()); - } - -} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/pojo/PolarisServer.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/pojo/PolarisServer.java index be55ed31eaee444b5ce633f330a526ab0f5cbb2d..fa458f5c3fb75d51360862783385c779f8b8cef6 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/pojo/PolarisServer.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/pojo/PolarisServer.java @@ -48,6 +48,7 @@ public class PolarisServer extends Server { } this.serviceInstances = serviceInstances; this.instance = instance; + this.setAlive(true); this.metaInfo = new MetaInfo() { @Override public String getAppName() { diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/spi/InstanceMetadataProvider.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/spi/InstanceMetadataProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..dd5f03e160c7c19fdae98477c88bf7fbd37c4018 --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/spi/InstanceMetadataProvider.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.spi; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * + * Instance's custom metadata, metadata will be register to polaris server. + * @author lepdou 2022-06-16 + */ +public interface InstanceMetadataProvider { + + /** + * @return the metadata of instance. + */ + default Map getMetadata() { + return Collections.emptyMap(); + } + + /** + * @return the keys of transitive metadata. + */ + default Set getTransitiveMetadataKeys() { + return Collections.emptySet(); + } + + /** + * The region of current instance. + * + * @return the region info. + */ + default String getRegion() { + return ""; + } + + /** + * The zone of current instance. + * + * @return the zone info. + */ + default String getZone() { + return ""; + } + + /** + * The campus/datacenter of current instance. + * + * @return the campus or datacenter info. + */ + default String getCampus() { + return ""; + } +} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/AddressUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/AddressUtils.java index 4cfcb11a11526b8fd0eacd15c75dd5ed38dca71f..0bda18cde5fbb5554e2a83dc6f9398d3d3d0c8fe 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/AddressUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/AddressUtils.java @@ -20,8 +20,11 @@ package com.tencent.cloud.common.util; import java.net.URI; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import org.springframework.util.StringUtils; + /** * the utils of parse address. * @@ -36,6 +39,9 @@ public final class AddressUtils { } public static List parseAddressList(String addressInfo) { + if (StringUtils.isEmpty(addressInfo)) { + return Collections.emptyList(); + } List addressList = new ArrayList<>(); String[] addresses = addressInfo.split(ADDRESS_SEPARATOR); for (String address : addresses) { diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ApplicationContextAwareUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ApplicationContextAwareUtils.java index e945a7a2a7290121b6b67f87742abd0785151965..9c6aff74d64a9335ff860bdf9b16048d70a11aaf 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ApplicationContextAwareUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ApplicationContextAwareUtils.java @@ -40,8 +40,8 @@ public class ApplicationContextAwareUtils implements ApplicationContextAware { return applicationContext; } - public void setApplicationContext(ApplicationContext applicationContext) - throws BeansException { + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ApplicationContextAwareUtils.applicationContext = applicationContext; } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/BeanFactoryUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/BeanFactoryUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..715a154fc4fb5a979248753df336d56da2c6fe1f --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/BeanFactoryUtils.java @@ -0,0 +1,51 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.util; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; + +/** + * the utils for bean factory. + * @author lepdou 2022-05-23 + */ +public class BeanFactoryUtils { + + public static List getBeans(BeanFactory beanFactory, Class requiredType) { + if (!(beanFactory instanceof DefaultListableBeanFactory)) { + throw new RuntimeException("bean factory not support get list bean. factory type = " + beanFactory.getClass() + .getName()); + } + + String[] beanNames = ((DefaultListableBeanFactory) beanFactory).getBeanNamesForType(requiredType); + + if (beanNames.length == 0) { + return Collections.emptyList(); + } + + return Arrays.stream(beanNames).map( + beanName -> beanFactory.getBean(beanName, requiredType) + ).collect(Collectors.toList()); + } +} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ExpressionLabelUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ExpressionLabelUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..a4c53ccc174ea65165303eb6e5dca55407910aa7 --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ExpressionLabelUtils.java @@ -0,0 +1,317 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang.StringUtils; + +import org.springframework.http.HttpCookie; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.CollectionUtils; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; + +/** + * the utils for parse label expression. + * + * @author lepdou 2022-05-13 + * @author cheese8 2022-06-20 + */ +public class ExpressionLabelUtils { + + /** + * the expression prefix of header label. + */ + public static final String LABEL_HEADER_PREFIX = "${http.header."; + /** + * the length of expression header label prefix. + */ + public static final int LABEL_HEADER_PREFIX_LEN = LABEL_HEADER_PREFIX.length(); + /** + * the expression prefix of query. + */ + public static final String LABEL_QUERY_PREFIX = "${http.query."; + /** + * the length of expression query label prefix. + */ + public static final int LABEL_QUERY_PREFIX_LEN = LABEL_QUERY_PREFIX.length(); + /** + * the expression prefix of cookie. + */ + public static final String LABEL_COOKIE_PREFIX = "${http.cookie."; + /** + * the length of expression cookie label prefix. + */ + public static final int LABEL_COOKIE_PREFIX_LEN = LABEL_COOKIE_PREFIX.length(); + /** + * the expression of method. + */ + public static final String LABEL_METHOD = "${http.method}"; + /** + * the expression of uri. + */ + public static final String LABEL_URI = "${http.uri}"; + /** + * the suffix of expression. + */ + public static final String LABEL_SUFFIX = "}"; + + public static boolean isExpressionLabel(String labelKey) { + if (StringUtils.isEmpty(labelKey)) { + return false; + } + if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey) || + StringUtils.startsWithIgnoreCase(LABEL_URI, labelKey)) { + return true; + } + return (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX) || + StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX) || + StringUtils.startsWithIgnoreCase(labelKey, LABEL_COOKIE_PREFIX)) + && StringUtils.endsWith(labelKey, LABEL_SUFFIX); + } + + public static Map resolve(HttpServletRequest request, Set labelKeys) { + if (CollectionUtils.isEmpty(labelKeys)) { + return Collections.emptyMap(); + } + + Map labels = new HashMap<>(); + + for (String labelKey : labelKeys) { + if (!isExpressionLabel(labelKey)) { + continue; + } + if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX)) { + String headerKey = parseHeaderKey(labelKey); + if (StringUtils.isBlank(headerKey)) { + continue; + } + labels.put(labelKey, request.getHeader(headerKey)); + } + else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX)) { + String queryKey = parseQueryKey(labelKey); + if (StringUtils.isBlank(queryKey)) { + continue; + } + labels.put(labelKey, getQueryValue(request.getQueryString(), queryKey)); + } + else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_COOKIE_PREFIX)) { + String cookieKey = parseCookieKey(labelKey); + if (StringUtils.isBlank(cookieKey)) { + continue; + } + labels.put(labelKey, getCookieValue(request.getCookies(), cookieKey)); + } + else if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey)) { + labels.put(labelKey, request.getMethod()); + } + else if (StringUtils.equalsIgnoreCase(LABEL_URI, labelKey)) { + labels.put(labelKey, request.getRequestURI()); + } + } + + return labels; + } + + public static Map resolve(ServerWebExchange exchange, Set labelKeys) { + if (CollectionUtils.isEmpty(labelKeys)) { + return Collections.emptyMap(); + } + + Map labels = new HashMap<>(); + + for (String labelKey : labelKeys) { + if (!isExpressionLabel(labelKey)) { + continue; + } + if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX)) { + String headerKey = parseHeaderKey(labelKey); + if (StringUtils.isBlank(headerKey)) { + continue; + } + labels.put(labelKey, getHeaderValue(exchange.getRequest(), headerKey)); + } + else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX)) { + String queryKey = parseQueryKey(labelKey); + if (StringUtils.isBlank(queryKey)) { + continue; + } + labels.put(labelKey, getQueryValue(exchange.getRequest(), queryKey)); + } + else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_COOKIE_PREFIX)) { + String cookieKey = parseCookieKey(labelKey); + if (StringUtils.isBlank(cookieKey)) { + continue; + } + labels.put(labelKey, getCookieValue(exchange.getRequest(), cookieKey)); + } + else if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey)) { + labels.put(labelKey, exchange.getRequest().getMethodValue()); + } + else if (StringUtils.equalsIgnoreCase(LABEL_URI, labelKey)) { + labels.put(labelKey, exchange.getRequest().getURI().getPath()); + } + } + + return labels; + } + + public static Map resolve(HttpRequest request, Set labelKeys) { + if (CollectionUtils.isEmpty(labelKeys)) { + return Collections.emptyMap(); + } + + Map labels = new HashMap<>(); + + for (String labelKey : labelKeys) { + if (!isExpressionLabel(labelKey)) { + continue; + } + if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_HEADER_PREFIX)) { + String headerKey = parseHeaderKey(labelKey); + if (StringUtils.isBlank(headerKey)) { + continue; + } + labels.put(labelKey, getHeaderValue(request, headerKey)); + } + else if (StringUtils.startsWithIgnoreCase(labelKey, LABEL_QUERY_PREFIX)) { + String queryKey = parseQueryKey(labelKey); + if (StringUtils.isBlank(queryKey)) { + continue; + } + labels.put(labelKey, getQueryValue(request, queryKey)); + } + else if (StringUtils.equalsIgnoreCase(LABEL_METHOD, labelKey)) { + labels.put(labelKey, request.getMethodValue()); + } + else if (StringUtils.equalsIgnoreCase(LABEL_URI, labelKey)) { + labels.put(labelKey, request.getURI().getPath()); + } + } + + return labels; + } + + public static String parseHeaderKey(String expression) { + return expression.substring(LABEL_HEADER_PREFIX_LEN, expression.length() - 1); + } + + public static String parseQueryKey(String expression) { + return expression.substring(LABEL_QUERY_PREFIX_LEN, expression.length() - 1); + } + + public static String parseCookieKey(String expression) { + return expression.substring(LABEL_COOKIE_PREFIX_LEN, expression.length() - 1); + } + + public static String getQueryValue(String queryString, String queryKey) { + if (StringUtils.isBlank(queryString)) { + return StringUtils.EMPTY; + } + String[] queries = StringUtils.split(queryString, "&"); + if (queries == null || queries.length == 0) { + return StringUtils.EMPTY; + } + for (String query : queries) { + String[] queryKV = StringUtils.split(query, "="); + if (queryKV != null && queryKV.length == 2 && StringUtils.equals(queryKV[0], queryKey)) { + return queryKV[1]; + } + } + return StringUtils.EMPTY; + } + + public static String getCookieValue(Cookie[] cookies, String key) { + if (cookies == null || cookies.length == 0) { + return StringUtils.EMPTY; + } + for (Cookie cookie : cookies) { + if (StringUtils.equals(cookie.getName(), key)) { + return cookie.getValue(); + } + } + return StringUtils.EMPTY; + } + + public static String getHeaderValue(ServerHttpRequest request, String key) { + String value = request.getHeaders().getFirst(key); + if (value == null) { + return StringUtils.EMPTY; + } + return value; + } + + public static String getQueryValue(ServerHttpRequest request, String key) { + MultiValueMap queries = request.getQueryParams(); + if (CollectionUtils.isEmpty(queries)) { + return StringUtils.EMPTY; + } + String value = queries.getFirst(key); + if (value == null) { + return StringUtils.EMPTY; + } + return value; + } + + public static String getCookieValue(ServerHttpRequest request, String key) { + HttpCookie cookie = request.getCookies().getFirst(key); + if (cookie == null) { + return StringUtils.EMPTY; + } + return cookie.getValue(); + } + + public static String getHeaderValue(HttpRequest request, String key) { + HttpHeaders headers = request.getHeaders(); + return headers.getFirst(key); + } + + public static String getQueryValue(HttpRequest request, String key) { + String query = request.getURI().getQuery(); + return getQueryValue(query, key); + } + + public static String getFirstValue(Map> valueMaps, String key) { + if (CollectionUtils.isEmpty(valueMaps)) { + return StringUtils.EMPTY; + } + + Collection values = valueMaps.get(key); + + if (CollectionUtils.isEmpty(values)) { + return StringUtils.EMPTY; + } + + for (String value : values) { + return value; + } + + return StringUtils.EMPTY; + } +} diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/JacksonUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/JacksonUtils.java index 628c718a1c38afb37aceba02295778ae47a4b211..2b55f102a39bd239161d37997a302221de334a62 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/JacksonUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/JacksonUtils.java @@ -69,14 +69,17 @@ public final class JacksonUtils { public static Map deserialize2Map(String jsonStr) { try { if (StringUtils.hasText(jsonStr)) { - return OM.readValue(jsonStr, Map.class); + Map temp = OM.readValue(jsonStr, Map.class); + Map result = new HashMap<>(); + temp.forEach((key, value) -> { + result.put(String.valueOf(key), String.valueOf(value)); + }); + return result; } return new HashMap<>(); } catch (JsonProcessingException e) { - LOG.error( - "Json to map failed. check if the format of the json string[{}] is correct.", - jsonStr, e); + LOG.error("Json to map failed. check if the format of the json string[{}] is correct.", jsonStr, e); throw new RuntimeException("Json to map failed.", e); } } diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java index 67e4c9210af0dadbc44e9f9b4badc9a03cb144a4..6c4a3353e00350815303cf4e1bfc05d6bb5ca04f 100644 --- a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ReflectionUtils.java @@ -31,8 +31,10 @@ public final class ReflectionUtils { } public static Object getFieldValue(Object instance, String fieldName) { - Field field = org.springframework.util.ReflectionUtils - .findField(instance.getClass(), fieldName); + Field field = org.springframework.util.ReflectionUtils.findField(instance.getClass(), fieldName); + if (field == null) { + return null; + } field.setAccessible(true); try { diff --git a/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ResourceFileUtils.java b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ResourceFileUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..b5ea45d5ddda7daec053b55e6c6683f9bfd6b464 --- /dev/null +++ b/spring-cloud-tencent-commons/src/main/java/com/tencent/cloud/common/util/ResourceFileUtils.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.util; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.StreamUtils; + +/** + * Read file content from classpath resource. + * + * @author lepdou 2022-04-20 + */ +public final class ResourceFileUtils { + + private ResourceFileUtils() { + } + + public static String readFile(String path) throws IOException { + + ClassPathResource classPathResource = new ClassPathResource(path); + + if (classPathResource.exists() && classPathResource.isReadable()) { + try (InputStream inputStream = classPathResource.getInputStream()) { + return StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); + } + } + return ""; + } + +} diff --git a/spring-cloud-tencent-commons/src/main/resources/META-INF/spring.factories b/spring-cloud-tencent-commons/src/main/resources/META-INF/spring.factories index ba9f2b86731aca591fe59136d05e05f99380c655..4c54d3372a6113aa4a6c5f9cd8df3f30df277756 100644 --- a/spring-cloud-tencent-commons/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-tencent-commons/src/main/resources/META-INF/spring.factories @@ -1,3 +1,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.tencent.cloud.common.util.ApplicationContextAwareUtils,\ - com.tencent.cloud.common.metadata.config.MetadataAutoConfiguration + com.tencent.cloud.common.metadata.config.MetadataAutoConfiguration,\ + com.tencent.cloud.common.metadata.endpoint.PolarisMetadataEndpointAutoConfiguration diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java index 6883ae50d6729afa86a21462748358464ced14cd..9695b4d4840d8c312f0c05a1d1d510264c472134 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/MetadataContextHolderTest.java @@ -13,6 +13,7 @@ * 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.tencent.cloud.common.metadata; @@ -29,14 +30,14 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** - * Test for {@link MetadataContextHolder} + * Test for {@link MetadataContextHolder}. * * @author Haotian Zhang */ @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = MetadataContextHolderTest.TestApplication.class, - properties = { "spring.config.location = classpath:application-test.yml" }) + properties = { "spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive" }) public class MetadataContextHolderTest { @Test @@ -45,10 +46,10 @@ public class MetadataContextHolderTest { customMetadata.put("a", "1"); customMetadata.put("b", "2"); MetadataContext metadataContext = MetadataContextHolder.get(); - metadataContext.putAllTransitiveCustomMetadata(customMetadata); + metadataContext.putFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE, customMetadata); MetadataContextHolder.set(metadataContext); - customMetadata = MetadataContextHolder.get().getAllTransitiveCustomMetadata(); + customMetadata = MetadataContextHolder.get().getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Assertions.assertThat(customMetadata.get("a")).isEqualTo("1"); Assertions.assertThat(customMetadata.get("b")).isEqualTo("2"); @@ -58,10 +59,9 @@ public class MetadataContextHolderTest { customMetadata.put("a", "1"); customMetadata.put("b", "22"); customMetadata.put("c", "3"); - Map systemMetadata = new HashMap<>(); - MetadataContextHolder.init(customMetadata, systemMetadata); + MetadataContextHolder.init(customMetadata); metadataContext = MetadataContextHolder.get(); - customMetadata = metadataContext.getAllTransitiveCustomMetadata(); + customMetadata = metadataContext.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); Assertions.assertThat(customMetadata.get("a")).isEqualTo("1"); Assertions.assertThat(customMetadata.get("b")).isEqualTo("22"); Assertions.assertThat(customMetadata.get("c")).isEqualTo("3"); diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/StaticMetadataManagerTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/StaticMetadataManagerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3c8ae438dd4cf3cdc632f14579d6c56a7a41d400 --- /dev/null +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/StaticMetadataManagerTest.java @@ -0,0 +1,186 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.metadata; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; +import com.tencent.cloud.common.spi.InstanceMetadataProvider; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.util.CollectionUtils; + +import static org.mockito.Mockito.when; + + +/** + * test for {@link StaticMetadataManager} + *@author lepdou 2022-06-27 + */ +@RunWith(MockitoJUnitRunner.class) +public class StaticMetadataManagerTest { + + @Mock + private MetadataLocalProperties metadataLocalProperties; + + @Test + public void testParseConfigMetadata() { + Map content = new HashMap<>(); + content.put("k1", "v1"); + content.put("k2", "v22"); + content.put("zone", "zone1"); + content.put("region", "region1"); + + when(metadataLocalProperties.getContent()).thenReturn(content); + when(metadataLocalProperties.getTransitive()).thenReturn(Collections.singletonList("k1")); + + StaticMetadataManager metadataManager = new StaticMetadataManager(metadataLocalProperties, null); + + Map metadata = metadataManager.getAllConfigMetadata(); + Assert.assertEquals(4, metadata.size()); + Assert.assertEquals("v1", metadata.get("k1")); + Assert.assertEquals("v22", metadata.get("k2")); + + Map transitiveMetadata = metadataManager.getConfigTransitiveMetadata(); + Assert.assertEquals(1, transitiveMetadata.size()); + Assert.assertEquals("v1", transitiveMetadata.get("k1")); + + Assert.assertEquals("zone1", metadataManager.getZone()); + Assert.assertEquals("region1", metadataManager.getRegion()); + + Map locationInfo = metadataManager.getLocationMetadata(); + Assert.assertEquals("zone1", locationInfo.get("zone")); + Assert.assertEquals("region1", locationInfo.get("region")); + } + + @Test + public void testCustomSPIMetadata() { + Map content = new HashMap<>(); + content.put("k1", "v1"); + content.put("k2", "v2"); + + when(metadataLocalProperties.getContent()).thenReturn(content); + when(metadataLocalProperties.getTransitive()).thenReturn(Collections.singletonList("k1")); + + StaticMetadataManager metadataManager = new StaticMetadataManager(metadataLocalProperties, + new MockedMetadataProvider()); + + Map metadata = metadataManager.getAllCustomMetadata(); + Assert.assertEquals(3, metadata.size()); + Assert.assertEquals("v1", metadata.get("k1")); + Assert.assertEquals("v22", metadata.get("k2")); + Assert.assertEquals("v33", metadata.get("k3")); + + Map transitiveMetadata = metadataManager.getCustomSPITransitiveMetadata(); + Assert.assertEquals(1, transitiveMetadata.size()); + Assert.assertEquals("v22", metadata.get("k2")); + + Assert.assertEquals("zone2", metadataManager.getZone()); + Assert.assertEquals("region1", metadataManager.getRegion()); + + Map locationInfo = metadataManager.getLocationMetadata(); + Assert.assertEquals("zone2", locationInfo.get("zone")); + Assert.assertEquals("region1", locationInfo.get("region")); + } + + @Test + public void testMergedMetadata() { + Map content = new HashMap<>(); + content.put("k1", "v1"); + content.put("k2", "v2"); + content.put("zone", "zone1"); + content.put("region", "region1"); + content.put("campus", "campus1"); + + when(metadataLocalProperties.getContent()).thenReturn(content); + when(metadataLocalProperties.getTransitive()).thenReturn(Collections.singletonList("k1")); + + StaticMetadataManager metadataManager = new StaticMetadataManager(metadataLocalProperties, + new MockedMetadataProvider()); + + Map metadata = metadataManager.getMergedStaticMetadata(); + Assert.assertEquals(6, metadata.size()); + Assert.assertEquals("v1", metadata.get("k1")); + Assert.assertEquals("v22", metadata.get("k2")); + Assert.assertEquals("v33", metadata.get("k3")); + Assert.assertEquals("zone2", metadata.get("zone")); + Assert.assertEquals("region1", metadata.get("region")); + Assert.assertEquals("campus1", metadata.get("campus")); + + Map transitiveMetadata = metadataManager.getMergedStaticTransitiveMetadata(); + Assert.assertEquals(2, transitiveMetadata.size()); + Assert.assertEquals("v1", metadata.get("k1")); + Assert.assertEquals("v22", metadata.get("k2")); + + Assert.assertEquals("zone2", metadataManager.getZone()); + Assert.assertEquals("region1", metadataManager.getRegion()); + + Assert.assertTrue(CollectionUtils.isEmpty(metadataManager.getAllEnvMetadata())); + Assert.assertTrue(CollectionUtils.isEmpty(metadataManager.getEnvTransitiveMetadata())); + + Map locationInfo = metadataManager.getLocationMetadata(); + Assert.assertEquals("zone2", locationInfo.get("zone")); + Assert.assertEquals("region1", locationInfo.get("region")); + Assert.assertEquals("campus1", locationInfo.get("campus")); + + } + + static class MockedMetadataProvider implements InstanceMetadataProvider { + + @Override + public Map getMetadata() { + Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + metadata.put("k2", "v22"); + metadata.put("k3", "v33"); + return metadata; + } + + @Override + public Set getTransitiveMetadataKeys() { + Set transitiveKeys = new HashSet<>(); + transitiveKeys.add("k2"); + return transitiveKeys; + } + + @Override + public String getRegion() { + return "region1"; + } + + @Override + public String getZone() { + return "zone2"; + } + + @Override + public String getCampus() { + return null; + } + } + +} diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataAutoConfigurationTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataAutoConfigurationTest.java index 8b871c1740c16ebb6fdae8b6818cee915995d4ab..fb19efb18441c1803f4c324abb338ec95343c219 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataAutoConfigurationTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataAutoConfigurationTest.java @@ -13,15 +13,12 @@ * 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.tencent.cloud.common.metadata.config; import com.tencent.cloud.common.metadata.filter.gateway.MetadataFirstScgFilter; -import com.tencent.cloud.common.metadata.filter.gateway.MetadataFirstZuulFilter; -import com.tencent.cloud.common.metadata.filter.web.MetadataReactiveFilter; -import com.tencent.cloud.common.metadata.filter.web.MetadataServletFilter; -import com.tencent.cloud.common.metadata.interceptor.feign.MetadataFirstFeignInterceptor; import org.assertj.core.api.Assertions; import org.junit.Test; @@ -31,7 +28,7 @@ import org.springframework.boot.test.context.runner.ReactiveWebApplicationContex import org.springframework.boot.test.context.runner.WebApplicationContextRunner; /** - * Test for {@link MetadataAutoConfiguration} + * Test for {@link MetadataAutoConfiguration}. * * @author Haotian Zhang */ @@ -51,28 +48,10 @@ public class MetadataAutoConfigurationTest { this.applicationContextRunner .withConfiguration(AutoConfigurations.of(MetadataAutoConfiguration.class)) .run(context -> { - Assertions.assertThat(context) - .hasSingleBean(MetadataLocalProperties.class); - Assertions.assertThat(context).doesNotHaveBean( - MetadataAutoConfiguration.MetadataServletFilterConfig.class); - Assertions.assertThat(context) - .doesNotHaveBean(MetadataServletFilter.class); - Assertions.assertThat(context).doesNotHaveBean( - MetadataAutoConfiguration.MetadataReactiveFilterConfig.class); - Assertions.assertThat(context) - .doesNotHaveBean(MetadataReactiveFilter.class); - Assertions.assertThat(context).hasSingleBean( - MetadataAutoConfiguration.MetadataFeignInterceptorConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataFirstFeignInterceptor.class); - Assertions.assertThat(context).hasSingleBean( - MetadataAutoConfiguration.MetadataZuulFilterConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataFirstZuulFilter.class); + Assertions.assertThat(context).hasSingleBean(MetadataLocalProperties.class); Assertions.assertThat(context).hasSingleBean( MetadataAutoConfiguration.MetadataScgFilterConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataFirstScgFilter.class); + Assertions.assertThat(context).hasSingleBean(MetadataFirstScgFilter.class); }); } @@ -84,28 +63,10 @@ public class MetadataAutoConfigurationTest { this.webApplicationContextRunner .withConfiguration(AutoConfigurations.of(MetadataAutoConfiguration.class)) .run(context -> { - Assertions.assertThat(context) - .hasSingleBean(MetadataLocalProperties.class); - Assertions.assertThat(context).hasSingleBean( - MetadataAutoConfiguration.MetadataServletFilterConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataServletFilter.class); - Assertions.assertThat(context).doesNotHaveBean( - MetadataAutoConfiguration.MetadataReactiveFilterConfig.class); - Assertions.assertThat(context) - .doesNotHaveBean(MetadataReactiveFilter.class); - Assertions.assertThat(context).hasSingleBean( - MetadataAutoConfiguration.MetadataFeignInterceptorConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataFirstFeignInterceptor.class); - Assertions.assertThat(context).hasSingleBean( - MetadataAutoConfiguration.MetadataZuulFilterConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataFirstZuulFilter.class); + Assertions.assertThat(context).hasSingleBean(MetadataLocalProperties.class); Assertions.assertThat(context).hasSingleBean( MetadataAutoConfiguration.MetadataScgFilterConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataFirstScgFilter.class); + Assertions.assertThat(context).hasSingleBean(MetadataFirstScgFilter.class); }); } @@ -117,28 +78,10 @@ public class MetadataAutoConfigurationTest { this.reactiveWebApplicationContextRunner .withConfiguration(AutoConfigurations.of(MetadataAutoConfiguration.class)) .run(context -> { - Assertions.assertThat(context) - .hasSingleBean(MetadataLocalProperties.class); - Assertions.assertThat(context).doesNotHaveBean( - MetadataAutoConfiguration.MetadataServletFilterConfig.class); - Assertions.assertThat(context) - .doesNotHaveBean(MetadataServletFilter.class); - Assertions.assertThat(context).hasSingleBean( - MetadataAutoConfiguration.MetadataReactiveFilterConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataReactiveFilter.class); - Assertions.assertThat(context).hasSingleBean( - MetadataAutoConfiguration.MetadataFeignInterceptorConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataFirstFeignInterceptor.class); - Assertions.assertThat(context).hasSingleBean( - MetadataAutoConfiguration.MetadataZuulFilterConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataFirstZuulFilter.class); + Assertions.assertThat(context).hasSingleBean(MetadataLocalProperties.class); Assertions.assertThat(context).hasSingleBean( MetadataAutoConfiguration.MetadataScgFilterConfig.class); - Assertions.assertThat(context) - .hasSingleBean(MetadataFirstScgFilter.class); + Assertions.assertThat(context).hasSingleBean(MetadataFirstScgFilter.class); }); } diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTest.java index 44128591c7fbed322789bfe54ae7176570cbfe28..3fdb2f44169b43ba2e15c9863dbe615415254089 100644 --- a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTest.java +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/config/MetadataLocalPropertiesTest.java @@ -27,14 +27,14 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** - * Test for {@link MetadataLocalProperties} + * Test for {@link MetadataLocalProperties}. * * @author Haotian Zhang */ @RunWith(SpringRunner.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = MetadataLocalPropertiesTest.TestApplication.class, - properties = { "spring.config.location = classpath:application-test.yml" }) + properties = { "spring.config.location = classpath:application-test.yml", "spring.main.web-application-type = reactive" }) public class MetadataLocalPropertiesTest { @Autowired diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/endpoint/PolarisMetadataEndpointTests.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/endpoint/PolarisMetadataEndpointTests.java new file mode 100644 index 0000000000000000000000000000000000000000..d20a2fd4f0f7429da6d0e53af37c36ab9069edc5 --- /dev/null +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/metadata/endpoint/PolarisMetadataEndpointTests.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.metadata.endpoint; + +import java.util.HashMap; +import java.util.Map; + +import com.tencent.cloud.common.metadata.StaticMetadataManager; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +/** + * Test for polaris metadata endpoint. + * + * @author shuiqingliu + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisMetadataEndpointTests { + + @Mock + private StaticMetadataManager staticMetadataManager; + + @Test + public void testPolarisMetadataEndpoint() { + Map envMetadata = new HashMap<>(); + envMetadata.put("k1", "v1"); + envMetadata.put("k2", "v2"); + envMetadata.put("k3", "v3"); + + when(staticMetadataManager.getAllEnvMetadata()).thenReturn(envMetadata); + + PolarisMetadataEndpoint polarisMetadataEndpoint = new PolarisMetadataEndpoint(staticMetadataManager); + Map metaMap = polarisMetadataEndpoint.metadata(); + assertThat(envMetadata).isEqualTo(metaMap.get("Env")); + } +} diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/AddressUtilsTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/AddressUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8040e7add1a1f3300e1f6fd077dc17ac1b3f6f27 --- /dev/null +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/AddressUtilsTest.java @@ -0,0 +1,66 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.util; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * test for {@link AddressUtils} + *@author lepdou 2022-05-27 + */ +@RunWith(MockitoJUnitRunner.class) +public class AddressUtilsTest { + + @Test + public void testEmptyStr() { + List result = AddressUtils.parseAddressList(""); + Assert.assertEquals(0, result.size()); + } + + @Test + public void testNullStr() { + List result = AddressUtils.parseAddressList(null); + Assert.assertEquals(0, result.size()); + } + + @Test + public void testOneStr() { + String host1 = "http://localhost"; + List result = AddressUtils.parseAddressList(host1); + Assert.assertEquals(1, result.size()); + Assert.assertTrue(result.contains("localhost")); + } + + @Test + public void testMultiStr() { + String host1 = "http://localhost"; + String host2 = "http://localhost2"; + String host3 = "http://localhost3"; + List result = AddressUtils.parseAddressList(host1 + "," + host2 + "," + host3); + Assert.assertEquals(3, result.size()); + Assert.assertTrue(result.contains("localhost")); + Assert.assertTrue(result.contains("localhost2")); + Assert.assertTrue(result.contains("localhost3")); + } +} diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ExpressionLabelUtilsTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ExpressionLabelUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0dedc4698d0f67b705a02d755a1e6173a155c8cc --- /dev/null +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ExpressionLabelUtilsTest.java @@ -0,0 +1,214 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.util; + +import java.net.URI; +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Sets; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.http.HttpCookie; +import org.springframework.http.HttpMethod; +import org.springframework.mock.http.client.MockClientHttpRequest; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.MockCookie; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.server.MockServerWebExchange; + +/** + * test for {@link ExpressionLabelUtils} + * @author lepdou 2022-05-27 + * @auther cheese8 2022-06-20 + */ +@RunWith(MockitoJUnitRunner.class) +public class ExpressionLabelUtilsTest { + + @Test + public void testExpressionLabel() { + String validLabel1 = "${http.query.uid}"; + String validLabel2 = "${http.header.uid}"; + String validLabel3 = "${http.cookie.uid}"; + String validLabel4 = "${http.method}"; + String validLabel5 = "${http.uri}"; + String invalidLabel1 = "${http.queryuid}"; + String invalidLabel2 = "{http.query.uid}"; + String invalidLabel3 = "${http.query.uid"; + String invalidLabel4 = "$ {http.query.uid}"; + String invalidLabel5 = "${ http.query.uid}"; + String invalidLabel6 = "${query.uid}"; + String invalidLabel7 = "http.query.uid"; + String invalidLabel8 = "$${http.uri}"; + String invalidLabel9 = "#{http.uri}"; + + Assert.assertTrue(ExpressionLabelUtils.isExpressionLabel(validLabel1)); + Assert.assertTrue(ExpressionLabelUtils.isExpressionLabel(validLabel2)); + Assert.assertTrue(ExpressionLabelUtils.isExpressionLabel(validLabel3)); + Assert.assertTrue(ExpressionLabelUtils.isExpressionLabel(validLabel4)); + Assert.assertTrue(ExpressionLabelUtils.isExpressionLabel(validLabel5)); + Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel1)); + Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel2)); + Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel3)); + Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel4)); + Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel5)); + Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel6)); + Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel7)); + Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel8)); + Assert.assertFalse(ExpressionLabelUtils.isExpressionLabel(invalidLabel9)); + } + + @Test + public void testResolveHttpServletRequest() { + String validLabel1 = "${http.query.uid}"; + String validLabel2 = "${http.header.uid}"; + String validLabel3 = "${http.cookie.uid}"; + String validLabel4 = "${http.method}"; + String validLabel5 = "${http.uri}"; + String invalidLabel1 = "${http.queryuid}"; + String invalidLabel2 = "{http.query.uid}"; + String invalidLabel3 = "${http.query.uid"; + String invalidLabel4 = "$ {http.query.uid}"; + String invalidLabel5 = "${ http.query.uid}"; + String invalidLabel6 = "${query.uid}"; + String invalidLabel7 = "http.query.uid"; + String invalidLabel8 = "$${http.uri}"; + String invalidLabel9 = "#{http.uri}"; + + Set labelKeys = Sets.newHashSet(validLabel1, validLabel2, validLabel3, validLabel4, validLabel5, + invalidLabel1, invalidLabel2, invalidLabel3, invalidLabel4, invalidLabel5, invalidLabel6, invalidLabel7, + invalidLabel8, invalidLabel9); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setQueryString("uid=zhangsan"); + request.addHeader("uid", "zhangsan"); + request.setCookies(new MockCookie("uid", "zhangsan")); + request.setMethod(HttpMethod.GET.name()); + request.setRequestURI("/users"); + + Map result = ExpressionLabelUtils.resolve(request, labelKeys); + + Assert.assertEquals("zhangsan", result.get(validLabel1)); + Assert.assertEquals("zhangsan", result.get(validLabel2)); + Assert.assertEquals("zhangsan", result.get(validLabel3)); + Assert.assertEquals("GET", result.get(validLabel4)); + Assert.assertEquals("/users", result.get(validLabel5)); + Assert.assertNull(result.get(invalidLabel1)); + Assert.assertNull(result.get(invalidLabel2)); + Assert.assertNull(result.get(invalidLabel3)); + Assert.assertNull(result.get(invalidLabel4)); + Assert.assertNull(result.get(invalidLabel5)); + Assert.assertNull(result.get(invalidLabel6)); + Assert.assertNull(result.get(invalidLabel7)); + Assert.assertNull(result.get(invalidLabel8)); + Assert.assertNull(result.get(invalidLabel9)); + } + + @Test + public void testResolveServerWebExchange() { + String validLabel1 = "${http.query.uid}"; + String validLabel2 = "${http.header.uid}"; + String validLabel3 = "${http.cookie.uid}"; + String validLabel4 = "${http.method}"; + String validLabel5 = "${http.uri}"; + String invalidLabel1 = "${http.queryuid}"; + String invalidLabel2 = "{http.query.uid}"; + String invalidLabel3 = "${http.query.uid"; + String invalidLabel4 = "$ {http.query.uid}"; + String invalidLabel5 = "${ http.query.uid}"; + String invalidLabel6 = "${query.uid}"; + String invalidLabel7 = "http.query.uid"; + String invalidLabel8 = "$${http.uri}"; + String invalidLabel9 = "#{http.uri}"; + + Set labelKeys = Sets.newHashSet(validLabel1, validLabel2, validLabel3, validLabel4, validLabel5, + invalidLabel1, invalidLabel2, invalidLabel3, invalidLabel4, invalidLabel5, invalidLabel6, invalidLabel7, + invalidLabel8, invalidLabel9); + + MockServerHttpRequest httpRequest = MockServerHttpRequest.get("http://calleeService/user/get?uid=zhangsan") + .header("uid", "zhangsan") + .cookie(new HttpCookie("uid", "zhangsan")).build(); + MockServerWebExchange exchange = new MockServerWebExchange.Builder(httpRequest).build(); + + Map result = ExpressionLabelUtils.resolve(exchange, labelKeys); + + Assert.assertEquals("zhangsan", result.get(validLabel1)); + Assert.assertEquals("zhangsan", result.get(validLabel2)); + Assert.assertEquals("zhangsan", result.get(validLabel3)); + Assert.assertEquals("GET", result.get(validLabel4)); + Assert.assertEquals("/user/get", result.get(validLabel5)); + Assert.assertNull(result.get(invalidLabel1)); + Assert.assertNull(result.get(invalidLabel2)); + Assert.assertNull(result.get(invalidLabel3)); + Assert.assertNull(result.get(invalidLabel4)); + Assert.assertNull(result.get(invalidLabel5)); + Assert.assertNull(result.get(invalidLabel6)); + Assert.assertNull(result.get(invalidLabel7)); + Assert.assertNull(result.get(invalidLabel8)); + Assert.assertNull(result.get(invalidLabel9)); + } + + @Test + public void testResolveHttpRequest() { + String validLabel1 = "${http.query.uid}"; + String validLabel2 = "${http.header.uid}"; + String validLabel3 = "${http.cookie.uid}"; + String validLabel4 = "${http.method}"; + String validLabel5 = "${http.uri}"; + String invalidLabel1 = "${http.queryuid}"; + String invalidLabel2 = "{http.query.uid}"; + String invalidLabel3 = "${http.query.uid"; + String invalidLabel4 = "$ {http.query.uid}"; + String invalidLabel5 = "${ http.query.uid}"; + String invalidLabel6 = "${query.uid}"; + String invalidLabel7 = "http.query.uid"; + String invalidLabel8 = "$${http.uri}"; + String invalidLabel9 = "#{http.uri}"; + + Set labelKeys = Sets.newHashSet(validLabel1, validLabel2, validLabel3, validLabel4, validLabel5, + invalidLabel1, invalidLabel2, invalidLabel3, invalidLabel4, invalidLabel5, invalidLabel6, invalidLabel7, + invalidLabel8, invalidLabel9); + + MockClientHttpRequest request = new MockClientHttpRequest(); + request.setMethod(HttpMethod.GET); + request.setURI(URI.create("http://calleeService/user/get?uid=zhangsan")); + request.getHeaders().add("uid", "zhangsan"); + + Map result = ExpressionLabelUtils.resolve(request, labelKeys); + + Assert.assertEquals("zhangsan", result.get(validLabel1)); + Assert.assertEquals("zhangsan", result.get(validLabel2)); + Assert.assertNull(result.get(validLabel3)); + Assert.assertEquals("GET", result.get(validLabel4)); + Assert.assertEquals("/user/get", result.get(validLabel5)); + Assert.assertNull(result.get(invalidLabel1)); + Assert.assertNull(result.get(invalidLabel2)); + Assert.assertNull(result.get(invalidLabel3)); + Assert.assertNull(result.get(invalidLabel4)); + Assert.assertNull(result.get(invalidLabel5)); + Assert.assertNull(result.get(invalidLabel6)); + Assert.assertNull(result.get(invalidLabel7)); + Assert.assertNull(result.get(invalidLabel8)); + Assert.assertNull(result.get(invalidLabel9)); + } + +} diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/JacksonUtilsTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/JacksonUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e07dcceb20036f6d8b5d493700fd67390b7fe71e --- /dev/null +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/JacksonUtilsTest.java @@ -0,0 +1,71 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.util; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +/** + * Test for {@link JacksonUtils}. + * + * @author lepdou, Haotian Zhang, cheese8 + */ +@RunWith(MockitoJUnitRunner.class) +public class JacksonUtilsTest { + + @Test + public void testSerialize2Json() { + Map sourceMap = new HashMap<>(); + sourceMap.put("k1", "v1"); + sourceMap.put("k2", "v2"); + sourceMap.put("k3", "v3"); + assertThat(JacksonUtils.serialize2Json(sourceMap)).isEqualTo("{\"k1\":\"v1\",\"k2\":\"v2\",\"k3\":\"v3\"}"); + } + + @Test + public void testDeserialize2Map() { + String jsonStr = "{\"k1\":\"v1\",\"k2\":\"v2\",\"k3\":\"v3\"}"; + Map map = JacksonUtils.deserialize2Map(jsonStr); + assertThat(map.size()).isEqualTo(3); + assertThat(map.get("k1")).isEqualTo("v1"); + assertThat(map.get("k2")).isEqualTo("v2"); + assertThat(map.get("k3")).isEqualTo("v3"); + } + + @Test + public void testDeserializeBlankIntoEmptyMap() { + Map map = JacksonUtils.deserialize2Map(""); + assertThat(map).isNotNull(); + assertThat(map).isEmpty(); + } + + @Test + public void testDeserializeThrowsRuntimeException() { + String jsonStr = "{\"k1\":\"v1\",\"k2\":\"v2\",\"k3\":\"v3\""; + assertThatThrownBy(() -> JacksonUtils.deserialize2Map(jsonStr)) + .isExactlyInstanceOf(RuntimeException.class).hasMessage("Json to map failed."); + } +} diff --git a/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ResourceFileUtilsTest.java b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ResourceFileUtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..769d99ce7b814721606b5584237a9859360692d0 --- /dev/null +++ b/spring-cloud-tencent-commons/src/test/java/com/tencent/cloud/common/util/ResourceFileUtilsTest.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.common.util; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +/** + * test for {@link ResourceFileUtils} + *@author lepdou 2022-05-27 + */ +@RunWith(MockitoJUnitRunner.class) +public class ResourceFileUtilsTest { + + @Test + public void testReadExistedFile() throws IOException { + String content = ResourceFileUtils.readFile("test.txt"); + Assert.assertEquals("just for test", content); + } + + @Test + public void testReadNotExistedFile() throws IOException { + String content = ResourceFileUtils.readFile("not_existed_test.txt"); + Assert.assertEquals("", content); + } +} diff --git a/spring-cloud-tencent-commons/src/test/resources/test.txt b/spring-cloud-tencent-commons/src/test/resources/test.txt new file mode 100644 index 0000000000000000000000000000000000000000..e18c37483a835b3e7282dd14d9b2fb2527d9bc9c --- /dev/null +++ b/spring-cloud-tencent-commons/src/test/resources/test.txt @@ -0,0 +1 @@ +just for test \ No newline at end of file diff --git a/spring-cloud-tencent-coverage/pom.xml b/spring-cloud-tencent-coverage/pom.xml index 1c8ba0ed413318b35d46fe63855c0c8af7865ad8..37903c143dc468a3999bdecd918a65e1302c5e0a 100644 --- a/spring-cloud-tencent-coverage/pom.xml +++ b/spring-cloud-tencent-coverage/pom.xml @@ -24,6 +24,16 @@ spring-cloud-tencent-commons + + com.tencent.cloud + spring-cloud-tencent-polaris-context + + + + com.tencent.cloud + spring-cloud-tencent-polaris-loadbalancer + + com.tencent.cloud spring-cloud-starter-tencent-polaris-discovery @@ -49,10 +59,10 @@ spring-cloud-starter-tencent-polaris-router - - com.tencent.cloud - spring-cloud-tencent-polaris-context - + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-config + @@ -75,4 +85,4 @@ - \ No newline at end of file + diff --git a/spring-cloud-tencent-dependencies/pom.xml b/spring-cloud-tencent-dependencies/pom.xml index eb776422d972f2c9dcdcb80ec53cf53360f2fd23..bd1ae87f7c09bd903942d82641ef3fb79e17ac56 100644 --- a/spring-cloud-tencent-dependencies/pom.xml +++ b/spring-cloud-tencent-dependencies/pom.xml @@ -1,93 +1,106 @@ - - org.springframework.cloud - spring-cloud-dependencies-parent - 2.3.1.RELEASE - - - 4.0.0 - - com.tencent.cloud - spring-cloud-tencent-dependencies - ${revision} - pom - Spring Cloud Tencent Dependencies - Spring Cloud Tencent Dependencies - https://github.com/Tencent/spring-cloud-tencent/tree/main - - - Tencent - https://opensource.tencent.com/ - - - - - The BSD 3-Clause License (BSD3) - https://raw.githubusercontent.com/Tencent/spring-cloud-tencent/main/LICENSE - repo - - - - - https://github.com/Tencent/spring-cloud-tencent - scm:git:git@github.com:Tencent/spring-cloud-tencent.git - scm:git:git@github.com:Tencent/spring-cloud-tencent.git - - - - - SkyeBeFreeman - Haotian Zhang - 928016560@qq.com - Tencent - https://github.com/SkyeBeFreeman/ - - - - Andrew Shan - samshan08@126.com - Tencent - - - - xiaoyao1999hn - Jie Cheng - 348893717@qq.com - Tencent - https://github.com/xiaoyao1999hn/ - - - - - 1.2.0-Hoxton.SR9-SNAPSHOT - 1.3.0 - 2.0.0 - - - 3.2.0 - 3.1.1 - 1.2.7 - 1.6 - - - - - - polaris-dependencies - com.tencent.polaris - ${polaris.version} - pom - import - - - - com.tencent.cloud - spring-cloud-tencent-commons - ${revision} - + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + org.springframework.cloud + spring-cloud-dependencies-parent + 2.3.5.RELEASE + + + 4.0.0 + + com.tencent.cloud + spring-cloud-tencent-dependencies + ${revision} + pom + Spring Cloud Tencent Dependencies + Spring Cloud Tencent Dependencies + https://github.com/Tencent/spring-cloud-tencent/tree/main + + + Tencent + https://opensource.tencent.com/ + + + + + The BSD 3-Clause License (BSD3) + https://raw.githubusercontent.com/Tencent/spring-cloud-tencent/main/LICENSE + repo + + + + + https://github.com/Tencent/spring-cloud-tencent + scm:git:git@github.com:Tencent/spring-cloud-tencent.git + scm:git:git@github.com:Tencent/spring-cloud-tencent.git + + + + + SkyeBeFreeman + Haotian Zhang + 928016560@qq.com + Tencent + https://github.com/SkyeBeFreeman/ + + + + lepdou + lepdou + lepdou@126.com + Tencent + https://github.com/lepdou + + + + Andrew Shan + samshan08@126.com + Tencent + + + + xiaoyao1999hn + Jie Cheng + 348893717@qq.com + Tencent + https://github.com/xiaoyao1999hn/ + + + + + 1.6.0-Hoxton.SR12-SNAPSHOT + 1.7.0-SNAPSHOT + 1.2.11 + 4.5.1 + 1.12.10 + 2.12.7 + 3.16.1 + 1.69 + 31.0.1-jre + + + 3.2.0 + 1.2.7 + 3.0.1 + + + + + + polaris-dependencies + com.tencent.polaris + ${polaris.version} + pom + import + + + + com.tencent.cloud + spring-cloud-tencent-commons + ${revision} + com.tencent.cloud @@ -95,30 +108,36 @@ ${revision} + + com.tencent.cloud + spring-cloud-tencent-polaris-loadbalancer + ${revision} + + com.tencent.cloud spring-cloud-starter-tencent-metadata-transfer ${revision} - - - com.tencent.cloud - spring-cloud-starter-tencent-polaris-ratelimit - ${revision} - + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-ratelimit + ${revision} + - - com.tencent.cloud - spring-cloud-starter-tencent-polaris-circuitbreaker - ${revision} - + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-circuitbreaker + ${revision} + - - com.tencent.cloud - spring-cloud-starter-tencent-polaris-router - ${revision} - + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-router + ${revision} + com.tencent.cloud @@ -126,116 +145,186 @@ ${revision} - - com.tencent.cloud - spring-cloud-starter-tencent-polaris-discovery - ${revision} - - - - - org.powermock - powermock-module-junit4 - ${powermock.version} - - - - - org.powermock - powermock-api-mockito2 - ${powermock.version} - - - - - - - - org.codehaus.mojo - flatten-maven-plugin - ${flatten-maven-plugin.version} - - true - resolveCiFriendliesOnly - - - - flatten - process-resources - - flatten - - - - flatten.clean - clean - - clean - - - - - - - - - - release - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - attach-javadocs - - jar - - - - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - - package - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - verify - - sign - - - - - - - - - - nexus-snapshots - https://oss.sonatype.org/content/repositories/snapshots/ - - - nexus-releases - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + ${revision} + + + + + com.google.guava + guava + ${guava.version} + + + jsr305 + com.google.code.findbugs + + + animal-sniffer-annotations + org.codehaus.mojo + + + error_prone_annotations + com.google.errorprone + + + + + + ch.qos.logback + logback-core + ${logback.version} + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + + + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + com.google.protobuf + protobuf-java + ${protobuf-java.version} + + + + org.bouncycastle + bcprov-jdk15on + ${bcprov-jdk15on.version} + + + + org.mockito + mockito-inline + ${mocktio.version} + test + + + + org.mockito + mockito-core + ${mocktio.version} + test + + + + net.bytebuddy + byte-buddy + ${byte-buddy.version} + test + + + + + + + + org.codehaus.mojo + flatten-maven-plugin + ${flatten-maven-plugin.version} + + true + resolveCiFriendliesOnly + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + + + + + + + release + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + package + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + verify + + sign + + + + + + + + + + nexus-snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + + nexus-releases + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/README-zh.md b/spring-cloud-tencent-examples/metadata-transfer-example/README-zh.md new file mode 100644 index 0000000000000000000000000000000000000000..9af9aff6d146436736dade9884dfc0879c0684fd --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/README-zh.md @@ -0,0 +1,118 @@ +# Spring Cloud Tencent Metadata Transfer example + +## 样例简介 + +本样例将介绍如何在Spring Cloud项目中使用```spring-cloud-starter-tencent-metadata-transfer```以使用其各项功能。 + +本样例包括```metadata-callee-service```、```metadata-caller-service```。 + +## 使用说明 + +### 修改配置 + +配置如下所示。其中,${ip}和${port}为Polaris后端服务的IP地址与端口号。 + +```yaml +spring: + application: + name: ${application.name} + cloud: + polaris: + address: ${ip}:${port} +``` + +### Maven依赖 + +```xml + + com.tencent.cloud + spring-cloud-starter-tencent-metadata-transfer + +``` + +### 启动样例 + +#### 启动Polaris后端服务 + +参考[Polaris Getting Started](https://github.com/PolarisMesh/polaris#getting-started)。 + +#### 启动应用 + +##### IDEA启动 + +分别启动 +- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service```的```MetadataCalleeService``` +- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service```的```MetadataCallerService``` + + +##### Maven打包启动 + +在```spring-cloud-tencent-examples/metadata-transfer-example```下执行 + +```sh +mvn clean package +``` + +然后在```metadata-callee-service```、```metadata-caller-service```下找到生成的jar包,运行 + +``` +java -jar ${app.jar} +``` + +启动应用,其中${app.jar}替换为对应的jar包名。 + +### 元数据配置 + +在```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service```项目的```bootstrap.yml```配置文件中 + +```yaml +spring: + cloud: + tencent: + metadata: + # 定义元数据的键值对 + content: + # 示例:本地元数据,默认不在链路中传递 + CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL + # 示例:可传递元数据 + CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE + # 指定哪个元数据的键值将沿着链接传递 + transitive: + - CUSTOM-METADATA-KEY-TRANSITIVE + +``` + +### 验证 + +#### 请求调用 + +```shell +curl -L -X GET 'http://127.0.0.1:48080/metadata/service/caller/feign/info' +``` + +预期返回值 + +``` +{ + "caller-metadata-contents": { + "CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE", + "CUSTOM-METADATA-KEY-LOCAL": "CUSTOM-VALUE-LOCAL" + }, + "callee-transitive-metadata": { + "CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE" + }, + "caller-transitive-metadata": { + "CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE" + } +} +``` + +返回值解析 + +- Key `caller-metadata-contents` 表示 `metadata-caller-service` 项目中默认配置的所有的元数据。 +- Key `caller-transitive-metadata` 表示 `metadata-caller-service` 项目中指定的可以在链路中传递的元数据列表。 +- Key `callee-transitive-metadata` 表示 `metadata-callee-service` 项目被 `metadata-caller-service` 调用时传递过来的上游的元数据列表。 + +### Wiki参考 + +查看 [Spring Cloud Tencent Metadata Transfer 使用指南](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Metadata-Transfer-%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97) . \ No newline at end of file diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/README.md b/spring-cloud-tencent-examples/metadata-transfer-example/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c89a12d7bdae594ba8f0884440753fb6d4552141 --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/README.md @@ -0,0 +1,118 @@ +# Spring Cloud Tencent Metadata Transfer example + +## Example Introduction + +This example shows how to use ```spring-cloud-starter-tencent-metadata-transfer``` in Spring Cloud project for its +features. + +This example contains ```metadata-callee-service```、```metadata-caller-service```. + +## Instruction + +### Configuration + +The configuration is as the following shows. ${ip} and ${port} are Polaris backend IP address and port number. + +```yaml +spring: + application: + name: ${application.name} + cloud: + polaris: + address: ${ip}:${port} +``` + +### Maven Dependency + +```xml + + com.tencent.cloud + spring-cloud-starter-tencent-metadata-transfer + +``` + +### Launching Example + +#### Launching Polaris Backend Service + +Reference to [Polaris Getting Started](https://github.com/PolarisMesh/polaris#getting-started) + +#### Launching Application + +- IDEA Launching + +- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service```‘s```MetadataCalleeService``` +- ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service```'s```MetadataCallerService``` + +- Maven Package Launching + +Execute under ```spring-cloud-tencent-examples/metadata-transfer-example``` + +```sh +mvn clean package +``` + +Then find the jars under ```metadata-callee-service```、```metadata-caller-service```, and run it: + +``` +java -jar ${app.jar} +``` + +Launch application, change ${app.jar} to jar's package name. + + +### Metadata Configuration + +In the ```bootstrap.yml``` configuration file of the ```spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service``` project + +```yaml +spring: + cloud: + tencent: + metadata: + # Defined your metadata keys & values + content: + # Example: intransitive + CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL + # Example: transitive + CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE + # Assigned which metadata key-value will be passed along the link + transitive: + - CUSTOM-METADATA-KEY-TRANSITIVE + +``` + +### Verify + +#### Request Invoke + +```shell +curl -L -X GET 'http://127.0.0.1:48080/metadata/service/caller/feign/info' +``` + +Expected return rate + +``` +{ + "caller-metadata-contents": { + "CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE", + "CUSTOM-METADATA-KEY-LOCAL": "CUSTOM-VALUE-LOCAL" + }, + "callee-transitive-metadata": { + "CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE" + }, + "caller-transitive-metadata": { + "CUSTOM-METADATA-KEY-TRANSITIVE": "CUSTOM-VALUE-TRANSITIVE" + } +} +``` + +Response value description + +- Key `caller-metadata-contents` represents all metadata configured by default in the `metadata-caller-service` project. +- Key `caller-transitive-metadata` represents the list of metadata that can be passed in the link specified in the `metadata-caller-service` item. +- Key `callee-transitive-metadata` represents the list of upstream metadata passed when the `metadata-callee-service` project is called by `metadata-caller-service`. + +### Wiki Reference + +See [Spring Cloud Tencent Metadata Transfer Usage Document](https://github.com/Tencent/spring-cloud-tencent/wiki/Spring-Cloud-Tencent-Metadata-Transfer-Usage-Document) for more reference . diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/pom.xml b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..237c9877f2f4a9b8544054c8877d13d9155d23ff --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/pom.xml @@ -0,0 +1,51 @@ + + + + metadata-transfer-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + metadata-callee-service + Spring Cloud Tencent Metadata Transfer Callee Service + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/src/main/java/com/tencent/cloud/metadata/service/callee/MetadataCalleeController.java b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/src/main/java/com/tencent/cloud/metadata/service/callee/MetadataCalleeController.java new file mode 100644 index 0000000000000000000000000000000000000000..4966e9bdc8cfc14612d09bbe2e719d504df179e4 --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/src/main/java/com/tencent/cloud/metadata/service/callee/MetadataCalleeController.java @@ -0,0 +1,65 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.metadata.service.callee; + +import java.util.Map; + +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Metadata callee controller. + * + * @author Palmer Xu + */ +@RestController +@RequestMapping("/metadata/service/callee") +public class MetadataCalleeController { + + private static final Logger LOG = LoggerFactory.getLogger(MetadataCalleeController.class); + + @Value("${server.port:0}") + private int port; + + /** + * Get information of callee. + * @return information of callee + */ + @GetMapping("/info") + public Map info() { + LOG.info("Metadata Service Callee [{}] is called.", port); + + // Get Custom Metadata From Context + MetadataContext context = MetadataContextHolder.get(); + Map customMetadataMap = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + + customMetadataMap.forEach((key, value) -> { + LOG.info("Custom Metadata (Key-Value): {} : {}", key, value); + }); + + return customMetadataMap; + } + +} diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/src/main/java/com/tencent/cloud/metadata/service/callee/MetadataCalleeService.java b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/src/main/java/com/tencent/cloud/metadata/service/callee/MetadataCalleeService.java new file mode 100644 index 0000000000000000000000000000000000000000..06771a5e8b211c844e0e1eb214bdf1d3db2e5ebb --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/src/main/java/com/tencent/cloud/metadata/service/callee/MetadataCalleeService.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.metadata.service.callee; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Metadata callee application. + * + * @author Palmer Xu + */ +@SpringBootApplication +public class MetadataCalleeService { + + public static void main(String[] args) { + SpringApplication.run(MetadataCalleeService.class, args); + } + +} diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..85c842c7cd3d8921557c7b70892298f1d785f0d9 --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-callee-service/src/main/resources/bootstrap.yml @@ -0,0 +1,13 @@ +server: + port: 48084 +spring: + application: + name: MetadataCalleeService + cloud: + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true + discovery: + enabled: true + register: true diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/pom.xml b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..1c13068054169cac33bad98a3133de3269fb9486 --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/pom.xml @@ -0,0 +1,51 @@ + + + + metadata-transfer-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + metadata-caller-service + Spring Cloud Tencent Metadata Transfer Caller Service + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCalleeService.java b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCalleeService.java new file mode 100644 index 0000000000000000000000000000000000000000..65bea6707d8301477649c84967e1bc24b659e589 --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCalleeService.java @@ -0,0 +1,41 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.metadata.service.caller; + +import java.util.Map; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * Metadata callee feign client. + * + * @author Palmer Xu + */ +@FeignClient(value = "MetadataCalleeService", + fallback = MetadataCalleeServiceFallback.class) +public interface MetadataCalleeService { + + /** + * Get information of callee. + * @return information of callee + */ + @GetMapping("/metadata/service/callee/info") + Map info(); + +} diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCalleeServiceFallback.java b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCalleeServiceFallback.java new file mode 100644 index 0000000000000000000000000000000000000000..ba508831a032b86f5e1a681bb2509f3a098b617c --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCalleeServiceFallback.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.metadata.service.caller; + +import java.util.Map; + +import com.google.common.collect.Maps; + +import org.springframework.stereotype.Component; + +/** + * Metadata callee feign client fallback. + * + * @author Palmer Xu + */ +@Component +public class MetadataCalleeServiceFallback implements MetadataCalleeService { + + @Override + public Map info() { + return Maps.newHashMap(); + } + +} diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCallerController.java b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCallerController.java new file mode 100644 index 0000000000000000000000000000000000000000..cf9ec84eb599a0ecf993b2fe63dca54e62ec7c25 --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCallerController.java @@ -0,0 +1,109 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.metadata.service.caller; + +import java.util.Map; + +import com.google.common.collect.Maps; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.metadata.MetadataContextHolder; +import com.tencent.cloud.common.metadata.config.MetadataLocalProperties; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +/** + * Metadata caller controller. + * + * @author Palmer Xu + */ +@RestController +@RequestMapping("/metadata/service/caller") +public class MetadataCallerController { + + private final RestTemplate restTemplate; + + private final MetadataCalleeService metadataCalleeService; + + private final MetadataLocalProperties metadataLocalProperties; + + public MetadataCallerController(RestTemplate restTemplate, + MetadataCalleeService metadataCalleeService, + MetadataLocalProperties metadataLocalProperties) { + this.restTemplate = restTemplate; + this.metadataCalleeService = metadataCalleeService; + this.metadataLocalProperties = metadataLocalProperties; + } + + /** + * Get metadata info from remote service. + * @return metadata map + */ + @GetMapping("/feign/info") + public Map> feign() { + Map> ret = Maps.newHashMap(); + + // Call remote service with feign client + Map calleeMetadata = metadataCalleeService.info(); + ret.put("callee-transitive-metadata", calleeMetadata); + + // Get Custom Metadata From Context + MetadataContext context = MetadataContextHolder.get(); + Map callerTransitiveMetadata = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + ret.put("caller-transitive-metadata", callerTransitiveMetadata); + ret.put("caller-metadata-contents", metadataLocalProperties.getContent()); + + return ret; + } + + /** + * Get metadata information of callee. + * @return information of callee + */ + @SuppressWarnings("unchecked") + @GetMapping("/rest/info") + public Map> rest() { + Map> ret = Maps.newHashMap(); + + // Call remote service with RestTemplate + Map calleeMetadata = restTemplate.getForObject( + "http://MetadataCalleeService/metadata/service/callee/info", + Map.class); + ret.put("callee-transitive-metadata", calleeMetadata); + + // Get Custom Metadata From Context + MetadataContext context = MetadataContextHolder.get(); + Map callerTransitiveMetadata = context.getFragmentContext(MetadataContext.FRAGMENT_TRANSITIVE); + ret.put("caller-transitive-metadata", callerTransitiveMetadata); + ret.put("caller-metadata-contents", metadataLocalProperties.getContent()); + + return ret; + } + + /** + * health check. + * @return health check info + */ + @GetMapping("/healthCheck") + public String healthCheck() { + return "pk ok"; + } + +} diff --git a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/TestPolarisFeignApp.java b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCallerService.java similarity index 56% rename from spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/TestPolarisFeignApp.java rename to spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCallerService.java index b593392d538e7ec726dcd7be60ec0c35e15c6976..a586c7e725dea9559187b5342b42b3850f3a07a0 100644 --- a/spring-cloud-starter-tencent-polaris-circuitbreaker/src/test/java/com/tencent/cloud/polaris/circuitbreaker/feign/TestPolarisFeignApp.java +++ b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/java/com/tencent/cloud/metadata/service/caller/MetadataCallerService.java @@ -15,48 +15,34 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.circuitbreaker.feign; +package com.tencent.cloud.metadata.service.caller; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +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.stereotype.Component; -import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; /** - * Test application. + * Metadata caller application. * - * @author liaochuntao + * @author Palmer Xu */ @SpringBootApplication +@EnableDiscoveryClient @EnableFeignClients -public class TestPolarisFeignApp { +public class MetadataCallerService { public static void main(String[] args) { - SpringApplication.run(TestPolarisFeignApp.class); + SpringApplication.run(MetadataCallerService.class, args); } - @FeignClient(name = "feign-service-polaris", - fallback = TestPolarisServiceFallback.class) - public interface TestPolarisService { - - /** - * Get info of service B. - */ - @GetMapping("/example/service/b/info") - String info(); - - } - - @Component - public static class TestPolarisServiceFallback implements TestPolarisService { - - @Override - public String info() { - return "trigger the refuse"; - } - + @Bean + @LoadBalanced + public RestTemplate restTemplate() { + return new RestTemplate(); } } diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..a390f51a67b3cbda789dd6219739ad48eba22498 --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/metadata-caller-service/src/main/resources/bootstrap.yml @@ -0,0 +1,32 @@ +server: + port: 48080 +spring: + application: + name: MetadataCallerService + cloud: + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true + discovery: + enabled: true + register: true + heartbeat-enabled: true + health-check-url: /metadata/service/caller/healthCheck + tencent: + metadata: + # Defined your metadata keys & values + content: + # Example: intransitive + CUSTOM-METADATA-KEY-LOCAL: CUSTOM-VALUE-LOCAL + # Example: transitive + CUSTOM-METADATA-KEY-TRANSITIVE: CUSTOM-VALUE-TRANSITIVE + # Assigned which metadata key-value will be passed along the link + transitive: + - CUSTOM-METADATA-KEY-TRANSITIVE +management: + endpoints: + web: + exposure: + include: + - polaris-metadata \ No newline at end of file diff --git a/spring-cloud-tencent-examples/metadata-transfer-example/pom.xml b/spring-cloud-tencent-examples/metadata-transfer-example/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..bd2b961ec1b641fbafe5d4b8caf287545194e7f1 --- /dev/null +++ b/spring-cloud-tencent-examples/metadata-transfer-example/pom.xml @@ -0,0 +1,43 @@ + + + + spring-cloud-tencent-examples + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + metadata-transfer-example + pom + Spring Cloud Starter Tencent Metadata Transfer Example + + + metadata-callee-service + metadata-caller-service + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + com.tencent.cloud + spring-cloud-starter-tencent-metadata-transfer + + + diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/README-zh.md b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/README-zh.md index 9f340eb66ab2d0fc05213ec7f3ac3112ef3c4083..23fe50ad61f667d467962ed33a439880cd2d0b82 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/README-zh.md +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/README-zh.md @@ -4,21 +4,24 @@ 本样例将介绍如何在Spring Cloud项目中使用```spring-cloud-starter-tencent-polaris-circuitbreaker```以使用其各项功能。 -该样例分为两个微服务,即polaris-circuitbreaker-example-a和polaris-circuitbreaker-example-b。其中,polaris-circuitbreaker-example-a对polaris-circuitbreaker-example-b发生调用。 +该样例分为两个微服务,即 + +1. ```polaris-circuitbreaker-example-a``` +2. ```polaris-circuitbreaker-example-b``` 有两个实例 B(默认正常服务)和 B2(模拟异常服务) + +``` polaris-circuitbreaker-example-a``` 对 ```polaris-circuitbreaker-example-b```发生调用。 ## 使用说明 ### 修改配置 -配置如下所示。其中,${ip}和${port}为Polaris后端服务的IP地址与端口号。 +修改 resource/bootstrap.yml 中北极星的服务端地址 ```yaml spring: - application: - name: ${application.name} cloud: polaris: - address: ${ip}:${port} + address: grpc://${ip}:8091 ``` ### 启动样例 @@ -27,40 +30,20 @@ spring: 参考[Polaris Getting Started](https://github.com/PolarisMesh/polaris#getting-started)。 -#### 启动应用 +### 启动应用 -注意,由于需要验证熔断功能,因此需要部署两个及以上的被调服务(样例中部署两个即可)。 - IDEA启动 -分别启动```spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a```下的```ServiceA```和```spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b```下的```ServiceB```。 - -注意,ServiceB需要启动两个。同机器上可以修改端口号来实现。 - -两个ServiceB的com.tencent.cloud.polaris.circuitbreaker.example.ServiceBController.info的逻辑需不同,即一个正常返回一个抛出异常。 - -- Maven打包启动 - -在```spring-cloud-tencent-examples/polaris-discovery-example```下执行 - -注意,ServiceB需要启动两个。同机器上可以修改端口号来实现。 - -两个ServiceB的com.tencent.cloud.polaris.circuitbreaker.example.ServiceBController.info的逻辑需不同,即一个正常返回一个抛出异常。 - -```sh -mvn clean package -``` +分别启动 -然后在```polaris-circuitbreaker-example-a```和```polaris-circuitbreaker-example-b```下找到生成的jar包,运行 +1. ```spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a```下的```ServiceA``` +2. ```spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b```下的```ServiceB``` +3. ```spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2```下的```ServiceB2``` -``` -java -jar ${app.jar} -``` -启动应用,其中${app.jar}替换为对应的jar包名。 +## 验证 -### 验证 - -#### Feign调用 +### Feign调用 执行以下命令发起Feign调用,其逻辑为```ServiceB```抛出一个异常 @@ -72,7 +55,18 @@ curl -L -X GET 'localhost:48080/example/service/a/getBServiceInfo' 在出现 ``` -trigger the refuse for service b -``` -时,表示请求到有异常的ServiceB,需要熔断这个实例。后面的所有请求都会得到正常返回。 +hello world ! I'm a service B1 +``` + +时,表示 B2 已经被熔断了,请求只会打到 B1。 + +### 验证更多场景 + +您也可以调整 ```example-b``` 和 ```example-b2``` 中 ```resource/bootstrap.yml``` is-throw-runtime-exception +参数调整服务是否抛出异常。 + +例如测试以下场景: +1. 两个实例都是正常的,这时候预期是 B1 和 B2 都能正常被调用到 +2. 一个实例正常一个实例不正常,这时候预期是不正常实例被熔断,请求只会打到正常的实例 +3. 两个实例都不正常,这时候 Feign 会自动 Fallback 到 ProviderBFallback.java 的实现类 diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/README.md b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/README.md index cf30a8c7d80bd598d052b8764809a31ff8260741..d298346d1ba4e5d09e4356a807c10c4331581ba5 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/README.md +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/README.md @@ -75,4 +75,4 @@ when appear trigger the refuse for service b ``` -it means the request signals abnormal ServiceB, and will ciruitbreak this instance, the later requests will return normally. \ No newline at end of file +it means the request signals abnormal ServiceB, and will ciruitbreak this instance, the later requests will return normally. diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceAController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceAController.java index d9d2e4370b0c9932edc428577398f5d17eb94013..29768d58415e00b7f843a7bf20e6584e467869f3 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceAController.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceAController.java @@ -48,6 +48,11 @@ public class ServiceAController { return polarisServiceB.info(); } + @GetMapping("/getBServiceInfoByRestTemplate") + public String getBServiceInfoByRestTemplate() { + return restTemplate.getForObject("http://polaris-circuitbreaker-example-b/example/service/b/info", String.class); + } + /** * Get info of Service B by RestTemplate. * @return info of Service B diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/resources/bootstrap.yml index 08b1172358acf33b7a1397e880d7f253affbfaba..0a36a8df777d24289baf2cc3b63c302c8dd80ef3 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-a/src/main/resources/bootstrap.yml @@ -5,8 +5,11 @@ spring: name: polaris-circuitbreaker-example-a cloud: polaris: - address: grpc://127.0.0.1:8091 + address: grpc://183.47.111.80:8091 namespace: default + enabled: true + circuitbreaker: + enabled: true feign: hystrix: enabled: true diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceBController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceBController.java index edcef56e71a7927e7f320af5213e625ffb4b25c5..67540c2c9adf0b3728577857132ddd509a511f6d 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceBController.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/java/com/tencent/cloud/polaris/circuitbreaker/example/ServiceBController.java @@ -13,6 +13,7 @@ * 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.tencent.cloud.polaris.circuitbreaker.example; @@ -44,7 +45,7 @@ public class ServiceBController { throw new RuntimeException("failed for call my service"); } else { - return "hello world ! I'm a service B"; + return "hello world ! I'm a service B1"; } } diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/resources/bootstrap.yml index ee986b845eda020b27a70d921b7e4d929146c919..4aba505009493e6d658ad906ebecaf045186e285 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b/src/main/resources/bootstrap.yml @@ -5,6 +5,7 @@ spring: name: polaris-circuitbreaker-example-b cloud: polaris: - address: grpc://127.0.0.1:8091 + address: grpc://183.47.111.80:8091 namespace: default + enabled: true is-throw-runtime-exception: false diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..ba28b0cddb135b26b50dd666f3c90a28784577e6 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/pom.xml @@ -0,0 +1,51 @@ + + + + polaris-circuitbreaker-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + polaris-circuitbreaker-example-b2 + + + + + org.springframework.boot + spring-boot-starter-webflux + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/rule/PolarisLoadBalanceRule.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceB2.java similarity index 56% rename from spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/rule/PolarisLoadBalanceRule.java rename to spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceB2.java index b95f351971fb27b0b86643a9313167c96657797b..4aacc0d220c1d6cc3cd19e07ce4265024cf38511 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/rule/PolarisLoadBalanceRule.java +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceB2.java @@ -13,40 +13,24 @@ * 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.tencent.cloud.polaris.router.rule; +package com.tencent.cloud.polaris.ciruitbreaker.example; -import java.util.Arrays; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; /** - * Load balance rule. + * Circuit breaker example callee application. * * @author Haotian Zhang */ -public enum PolarisLoadBalanceRule { - - /** - * Weighted random load balance rule. - */ - WEIGHTED_RANDOM_RULE("weighted_random"); - - /** - * Load balance strategy. - */ - final String policy; - - PolarisLoadBalanceRule(String strategy) { - this.policy = strategy; - } - - public static PolarisLoadBalanceRule fromStrategy(String strategy) { - return Arrays.stream(values()).filter(t -> t.getPolicy().equals(strategy)) - .findAny().orElse(WEIGHTED_RANDOM_RULE); - } +@SpringBootApplication +public class ServiceB2 { - public String getPolicy() { - return policy; + public static void main(String[] args) { + SpringApplication.run(ServiceB2.class); } } diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceBController.java b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceBController.java new file mode 100644 index 0000000000000000000000000000000000000000..7e4bb9b4cffc3bc1455f2f5e5fdf03d6bf38dab0 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/java/com/tencent/cloud/polaris/ciruitbreaker/example/ServiceBController.java @@ -0,0 +1,52 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.ciruitbreaker.example; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Service B Controller. + * + * @author Haotian Zhang + */ +@RestController +@RequestMapping("/example/service/b") +public class ServiceBController { + + @Value("${is-throw-runtime-exception:#{false}}") + private boolean isThrowRuntimeException; + + /** + * Get service information. + * @return service information + */ + @GetMapping("/info") + public String info() { + if (isThrowRuntimeException) { + throw new RuntimeException("failed for call my service"); + } + else { + return "hello world ! I'm a service B2"; + } + } + +} diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..b150a54245edcce79d3f9ee17b6b0c04c002ac70 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/polaris-circuitbreaker-example-b2/src/main/resources/bootstrap.yml @@ -0,0 +1,11 @@ +server: + port: 48082 +spring: + application: + name: polaris-circuitbreaker-example-b + cloud: + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true +is-throw-runtime-exception: true diff --git a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/pom.xml b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/pom.xml index 115e40e5f4c777262d514b6868ec8e9c7a8b48cc..5876625dc6ffd42eee84df860ddee876f9835568 100644 --- a/spring-cloud-tencent-examples/polaris-circuitbreaker-example/pom.xml +++ b/spring-cloud-tencent-examples/polaris-circuitbreaker-example/pom.xml @@ -17,6 +17,7 @@ polaris-circuitbreaker-example-a polaris-circuitbreaker-example-b + polaris-circuitbreaker-example-b2 @@ -26,4 +27,4 @@ - \ No newline at end of file + diff --git a/spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/README-zh.md b/spring-cloud-tencent-examples/polaris-config-example/README-zh.md similarity index 91% rename from spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/README-zh.md rename to spring-cloud-tencent-examples/polaris-config-example/README-zh.md index 3b1048a61a9dc3722789e52b4670c7c71794f412..f131d319e01e71ad17a564e899a771c8b8267dea 100644 --- a/spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/README-zh.md +++ b/spring-cloud-tencent-examples/polaris-config-example/README-zh.md @@ -2,6 +2,7 @@ ## 1. bootstrap.yml 配置 +修改 resources/bootstrap.yml ```spring.cloud.polaris.config.address``` 北极星服务端地址。 > 注意是在 bootstrap.yml 里配置,而不是在 application.yml 里配置。因为配置中心相关的配置是在 bootstrap 阶段依赖的配置。 ```` yaml @@ -12,7 +13,7 @@ spring: polaris: namespace: dev config: - addresses: grpc://9.134.122.18:8093 # the address of polaris config server + address: grpc://127.0.0.1:8093 # the address of polaris config server auto-refresh: true # auto refresh when config file changed groups: - name: ${spring.application.name} # group name diff --git a/spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/polaris-config-ui.png b/spring-cloud-tencent-examples/polaris-config-example/polaris-config-ui.png similarity index 100% rename from spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/polaris-config-ui.png rename to spring-cloud-tencent-examples/polaris-config-example/polaris-config-ui.png diff --git a/spring-cloud-tencent-examples/polaris-config-example/pom.xml b/spring-cloud-tencent-examples/polaris-config-example/pom.xml index cef91e5a30a3c1560657961b2407cc9babc65f51..bb7ef026e281df75d04c853c1205ff79e9aefe8b 100644 --- a/spring-cloud-tencent-examples/polaris-config-example/pom.xml +++ b/spring-cloud-tencent-examples/polaris-config-example/pom.xml @@ -24,6 +24,41 @@ com.tencent.cloud spring-cloud-starter-tencent-polaris-config + + + org.springframework.boot + spring-boot-starter-actuator + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/Person.java b/spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/Person.java index 492af0a8bc3ee256d47f4eaf8f05f2fde9d92f50..1d97d1fb9240c113bc077abf45b829efb4acbb98 100644 --- a/spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/Person.java +++ b/spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/Person.java @@ -34,19 +34,19 @@ public class Person { private int age; - String getName() { + public String getName() { return name; } - void setName(String name) { + public void setName(String name) { this.name = name; } - int getAge() { + public int getAge() { return age; } - void setAge(int age) { + public void setAge(int age) { this.age = age; } diff --git a/spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/PersonConfigChangeListener.java b/spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/PersonConfigChangeListener.java new file mode 100644 index 0000000000000000000000000000000000000000..b0efd8b36f1eebbdad9126ce777dfb3d5a7147b3 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-config-example/src/main/java/com/tencent/cloud/polaris/config/example/PersonConfigChangeListener.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.config.example; + +import java.util.Set; + +import com.tencent.cloud.polaris.config.annotation.PolarisConfigKVFileChangeListener; +import com.tencent.cloud.polaris.config.listener.ConfigChangeEvent; + +import org.springframework.stereotype.Component; + +/** + * Custom Config Listener Example . + * + * @author Palmer Xu 2022-06-06 + */ +@Component +public final class PersonConfigChangeListener { + + /** + * PolarisConfigKVFileChangeListener Example . + * @param event instance of {@link ConfigChangeEvent} + */ + @PolarisConfigKVFileChangeListener(interestedKeyPrefixes = "teacher") + public void onChange(ConfigChangeEvent event) { + Set changedKeys = event.changedKeys(); + + for (String changedKey : changedKeys) { + System.out.printf("%s = %s \n", changedKey, event.getChange(changedKey)); + } + } + +} diff --git a/spring-cloud-tencent-examples/polaris-config-example/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-config-example/src/main/resources/bootstrap.yml index b8ab4e08addb71fbb7be07b293d3a053abf6b278..84e44d4aa83ce29f7f970dd5bfba35b2e27fc09c 100644 --- a/spring-cloud-tencent-examples/polaris-config-example/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-config-example/src/main/resources/bootstrap.yml @@ -5,10 +5,16 @@ spring: name: polaris-config-example cloud: polaris: + address: grpc://183.47.111.80:8091 namespace: default config: - address: grpc://127.0.0.1:8093 auto-refresh: true # auto refresh when config file changed groups: - name: ${spring.application.name} # group name files: [ "config/application.properties", "config/bootstrap.yml" ] # config/application.properties takes precedence over config/bootstrap.yml +management: + endpoints: + web: + exposure: + include: + - polaris-config \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-discovery-example/README-zh.md b/spring-cloud-tencent-examples/polaris-discovery-example/README-zh.md index e58d1346459ee67a310eac871f3c261c0f5f56e4..851860f06b004453993eaa3cbe8f204988bd328d 100644 --- a/spring-cloud-tencent-examples/polaris-discovery-example/README-zh.md +++ b/spring-cloud-tencent-examples/polaris-discovery-example/README-zh.md @@ -4,21 +4,20 @@ 本样例将介绍如何在Spring Cloud项目中使用```spring-cloud-starter-tencent-polaris-discovery```以使用其各项功能。 -该样例分为两个微服务,即discovery-caller-service和discovery-callee-service。其中,discovery-caller-service对discovery-callee-service发生调用。 +该样例分为两个微服务,即 ```discovery-caller-service``` 和 ```discovery-callee-service ```。 +其中 ```discovery-caller-service``` 调用 ```discovery-callee-service``` ## 使用说明 ### 修改配置 -配置如下所示。其中,${ip}和${port}为Polaris后端服务的IP地址与端口号。 +修改 resource/bootstrap.yml 中北极星的服务端地址 ```yaml spring: - application: - name: ${application.name} cloud: polaris: - address: ${ip}:${port} + address: grpc://${ip}:8091 ``` ### 启动样例 @@ -31,29 +30,16 @@ spring: - IDEA启动 -分别启动```spring-cloud-tencent-examples/polaris-discovery-example/discovery-caller-service```下的```DiscoveryCallerService```和```spring-cloud-tencent-examples/polaris-discovery-example/discovery-callee-service```下的```DiscoveryCalleeService```。 +分别启动 -- Maven打包启动 - -在```spring-cloud-tencent-examples/polaris-discovery-example```下执行 - -```sh -mvn clean package -``` - -然后在```discovery-caller-service```和```discovery-callee-service```下找到生成的jar包,运行 - -``` -java -jar ${app.jar} -``` - -启动应用,其中${app.jar}替换为对应的jar包名。 +1. ```spring-cloud-tencent-examples/polaris-discovery-example/discovery-caller-service```下的```DiscoveryCallerService``` +2. ```spring-cloud-tencent-examples/polaris-discovery-example/discovery-callee-service```下的```DiscoveryCalleeService``` ### 验证 -#### Feign调用 +#### 调用 discovery-caller-service 暴露的接口 -执行以下命令发起Feign调用,其逻辑为```DiscoveryCalleeService```返回value1+value2的和 +执行以下命令发起Feign调用,其逻辑为```DiscoveryCalleeService```返回 value1+value2 的和 ```shell curl -L -X GET 'http://localhost:48080/discovery/service/caller/feign?value1=1&value2=2' diff --git a/spring-cloud-tencent-examples/polaris-discovery-example/discovery-callee-service/src/main/java/com/tencent/cloud/polaris/discovery/service/callee/CustomMetadata.java b/spring-cloud-tencent-examples/polaris-discovery-example/discovery-callee-service/src/main/java/com/tencent/cloud/polaris/discovery/service/callee/CustomMetadata.java new file mode 100644 index 0000000000000000000000000000000000000000..9504e9758d43e15235ec69b78b8a41337f09dfe3 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-discovery-example/discovery-callee-service/src/main/java/com/tencent/cloud/polaris/discovery/service/callee/CustomMetadata.java @@ -0,0 +1,46 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.discovery.service.callee; + +import java.util.HashMap; +import java.util.Map; + +import com.tencent.cloud.common.spi.InstanceMetadataProvider; + +import org.springframework.stereotype.Component; + +/** + * custom metadata for instance. + *@author lepdou 2022-06-16 + */ +@Component +public class CustomMetadata implements InstanceMetadataProvider { + + @Override + public Map getMetadata() { + Map metadata = new HashMap<>(); + metadata.put("k1", "v1"); + return metadata; + } + + @Override + public String getZone() { + return "shanghai-zone-1"; + } +} diff --git a/spring-cloud-tencent-examples/polaris-discovery-example/discovery-callee-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-discovery-example/discovery-callee-service/src/main/resources/bootstrap.yml index 991d77a34cbd3ac77d357dc7a44078952eeaf4d4..75b4e33d9076f213480f91e6c1d2dc498e16bd46 100644 --- a/spring-cloud-tencent-examples/polaris-discovery-example/discovery-callee-service/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-discovery-example/discovery-callee-service/src/main/resources/bootstrap.yml @@ -5,16 +5,24 @@ spring: name: DiscoveryCalleeService cloud: polaris: - address: grpc://127.0.0.1:8091 + address: grpc://183.47.111.80:8091 namespace: default + enabled: true + discovery: + enabled: true + register: true + tencent: + metadata: + content: + region: shanghai # consul: # port: 8500 # host: 127.0.0.1 # enabled: true # discovery: +# enabled: true # register: true # instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port} -# enabled: true # service-name: ${spring.application.name} # ip-address: localhost # prefer-ip-address: true diff --git a/spring-cloud-tencent-examples/polaris-discovery-example/discovery-caller-service/pom.xml b/spring-cloud-tencent-examples/polaris-discovery-example/discovery-caller-service/pom.xml index c8bfaf0cf45c73ee3f1025f8079c946dcb4de974..fd2eed66aafabe8a312910c12bbd21c4910c3d7c 100644 --- a/spring-cloud-tencent-examples/polaris-discovery-example/discovery-caller-service/pom.xml +++ b/spring-cloud-tencent-examples/polaris-discovery-example/discovery-caller-service/pom.xml @@ -32,11 +32,6 @@ - - - com.tencent.cloud - spring-cloud-starter-tencent-polaris-router - @@ -66,4 +61,4 @@ - \ No newline at end of file + diff --git a/spring-cloud-tencent-examples/polaris-discovery-example/discovery-caller-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-discovery-example/discovery-caller-service/src/main/resources/bootstrap.yml index d0cb0ec1e8ae54dceebdcaf2dd3d2165aff56f1d..544193b7e2842e4a40b801aee6a005c5994a95fb 100644 --- a/spring-cloud-tencent-examples/polaris-discovery-example/discovery-caller-service/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-discovery-example/discovery-caller-service/src/main/resources/bootstrap.yml @@ -6,9 +6,12 @@ spring: name: DiscoveryCallerService cloud: polaris: - address: grpc://127.0.0.1:8091 + address: grpc://183.47.111.80:8091 namespace: default + enabled: true discovery: + enabled: true + register: true heartbeat: enabled: true health-check-url: /discovery/service/caller/healthCheck @@ -17,11 +20,11 @@ spring: # host: 127.0.0.1 # enabled: true # discovery: +# enabled: true # register: true # health-check-path: /actuator/health # health-check-interval: 10s # instance-id: ${spring.application.name}:${server.port} -# enabled: true # service-name: ${spring.application.name} # ip-address: localhost # prefer-ip-address: true @@ -29,3 +32,9 @@ spring: # client: # serviceUrl: # defaultZone: http://127.0.0.1:7654/eureka/ +management: + endpoints: + web: + exposure: + include: + - polaris-discovery \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeController.java b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeController.java index 77f84f518da04d8ecdc30e34b2cb98c521f39fb9..7b53d688f642be556b05f0755711632e1e950372 100644 --- a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeController.java +++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeController.java @@ -19,6 +19,7 @@ package com.tencent.cloud.polaris.gateway.example.callee; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import com.tencent.cloud.common.constant.MetadataConstant; import org.slf4j.Logger; @@ -55,13 +56,17 @@ public class GatewayCalleeController { /** * Get metadata in HTTP header. + * + * @param metadataStr metadata string + * @return metadata in HTTP header + * @throws UnsupportedEncodingException encoding exception */ @RequestMapping("/echo") public String echoHeader( @RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String metadataStr) throws UnsupportedEncodingException { - LOG.info(URLDecoder.decode(metadataStr, "UTF-8")); - return URLDecoder.decode(metadataStr, "UTF-8"); + LOG.info(URLDecoder.decode(metadataStr, StandardCharsets.UTF_8.name())); + return URLDecoder.decode(metadataStr, StandardCharsets.UTF_8.name()); } } diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service/src/main/resources/bootstrap.yml index c23ece6d7243e3397bc46518dc529efbafb09e4e..c6d40440f2e2f1d58704658030dc66c92ddf10f4 100644 --- a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service/src/main/resources/bootstrap.yml @@ -5,6 +5,10 @@ spring: application: name: GatewayCalleeService cloud: + tencent: + metadata: + content: + env: blue polaris: - address: grpc://127.0.0.1:8091 + address: grpc://183.47.111.80:8091 namespace: default diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/pom.xml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..6454f43d7a7356ccc715dd5674d35dfcd78f76e8 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/pom.xml @@ -0,0 +1,26 @@ + + + + polaris-gateway-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + gateway-callee-service2 + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + + + org.springframework.boot + spring-boot-starter-web + + + diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeApplication2.java b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeApplication2.java new file mode 100644 index 0000000000000000000000000000000000000000..5d261d56f4614dc195fa94b6158cc0f1fcd83f02 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeApplication2.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.gateway.example.callee; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Gateway callee application. + * + * @author Haotian Zhang + */ +@SpringBootApplication +public class GatewayCalleeApplication2 { + + public static void main(String[] args) { + SpringApplication.run(GatewayCalleeApplication2.class, args); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeController.java b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeController.java new file mode 100644 index 0000000000000000000000000000000000000000..7b53d688f642be556b05f0755711632e1e950372 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/java/com/tencent/cloud/polaris/gateway/example/callee/GatewayCalleeController.java @@ -0,0 +1,72 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.gateway.example.callee; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; + +import com.tencent.cloud.common.constant.MetadataConstant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Gateway callee controller. + * + * @author Haotian Zhang + */ +@RestController +@RequestMapping("/gateway/example/callee") +public class GatewayCalleeController { + + private static Logger LOG = LoggerFactory.getLogger(GatewayCalleeController.class); + + @Value("${server.port:0}") + private int port; + + /** + * Get information of callee. + * @return information of callee + */ + @RequestMapping("/info") + public String info() { + LOG.info("Gateway Example Callee [{}] is called.", port); + return String.format("Gateway Example Callee [%s] is called.", port); + } + + /** + * Get metadata in HTTP header. + * + * @param metadataStr metadata string + * @return metadata in HTTP header + * @throws UnsupportedEncodingException encoding exception + */ + @RequestMapping("/echo") + public String echoHeader( + @RequestHeader(MetadataConstant.HeaderName.CUSTOM_METADATA) String metadataStr) + throws UnsupportedEncodingException { + LOG.info(URLDecoder.decode(metadataStr, StandardCharsets.UTF_8.name())); + return URLDecoder.decode(metadataStr, StandardCharsets.UTF_8.name()); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..a0cf12581dd6c1c91a49a7210cebaf5135c47b42 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-callee-service2/src/main/resources/bootstrap.yml @@ -0,0 +1,14 @@ +server: + session-timeout: 1800 + port: 48082 +spring: + application: + name: GatewayCalleeService + cloud: + tencent: + metadata: + content: + env: green + polaris: + address: grpc://183.47.111.80:8091 + namespace: default diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/pom.xml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/pom.xml index 5e02ccb4317933f1f03244c78ed7bf8809493c68..755b04d6313220b4e87069abe1ba618764f3e9e5 100644 --- a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/pom.xml +++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/pom.xml @@ -26,7 +26,7 @@ com.tencent.cloud - spring-cloud-starter-tencent-metadata-transfer + spring-cloud-starter-tencent-polaris-router @@ -34,4 +34,4 @@ spring-cloud-starter-gateway - \ No newline at end of file + diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/src/main/resources/bootstrap.yml index d41fcf960b2f25a6ee68c84a460693a9adf8115b..f50100264c160dc2050c7e61a6372680b27f5a75 100644 --- a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-scg-service/src/main/resources/bootstrap.yml @@ -8,17 +8,48 @@ spring: tencent: metadata: content: - a: 1 + env: blue transitive: - - a + - env polaris: - address: grpc://127.0.0.1:8091 + address: grpc://183.47.111.80:8091 namespace: default + enabled: true gateway: discovery: locator: enabled: true - lowerCaseServiceId: false + 'predicates[0]': + name: Path + args: + patterns: '''/'' + serviceId + ''/**''' + 'filters[0]': + name: RewritePath + args: + regexp: '''/'' + serviceId + ''/(?.*)''' + replacement: '''/$\{remaining}''' + 'filters[1]': + name: Retry + args: + retries: 3 + exceptions: + '[0]': '''java.net.ConnectException''' + '[1]': '''java.io.IOException''' + statuses: + '[0]': '''BAD_GATEWAY''' + '[1]': '''SERVICE_UNAVAILABLE''' + series: + '[0]': '''CLIENT_ERROR''' + methods: + '[0]': '''GET''' + '[1]': '''POST''' + '[2]': '''PUT''' + '[3]': '''DELETE''' + backoff: + firstBackoff: '''100ms''' + maxBackoff: '''500ms''' + factor: 2 + basedOnPreviousValue: false routes: - id: GatewayCalleeService uri: lb://GatewayCalleeService @@ -30,3 +61,4 @@ spring: logging: level: org.springframework.cloud.gateway: info + com.tencent.cloud.polaris: debug diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-zuul-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-zuul-service/src/main/resources/bootstrap.yml index 332041060f94f708bc789acc0d47432f8f1893df..0ed6cb16ca87a8d9fb62e75c5efd44b257fe6756 100644 --- a/spring-cloud-tencent-examples/polaris-gateway-example/gateway-zuul-service/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-gateway-example/gateway-zuul-service/src/main/resources/bootstrap.yml @@ -6,7 +6,7 @@ spring: name: GatewayZuulService cloud: polaris: - address: grpc://127.0.0.1:8091 + address: grpc://183.47.111.80:8091 namespace: default tencent: metadata: diff --git a/spring-cloud-tencent-examples/polaris-gateway-example/pom.xml b/spring-cloud-tencent-examples/polaris-gateway-example/pom.xml index 2645ccc628fb2300c5bccfc5e536150496a82255..fb7a97f2c6ac83ad10c99f0273a945635cdf4325 100644 --- a/spring-cloud-tencent-examples/polaris-gateway-example/pom.xml +++ b/spring-cloud-tencent-examples/polaris-gateway-example/pom.xml @@ -18,6 +18,7 @@ gateway-zuul-service gateway-scg-service gateway-callee-service + gateway-callee-service2 @@ -35,4 +36,4 @@ - \ No newline at end of file + diff --git a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml index 6f19d1ad55305d79d0430f41a4dab278a683fc12..f7cbb1e6fffe59a50a7cc4c0d70179a51d81a580 100644 --- a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml +++ b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/pom.xml @@ -1,32 +1,45 @@ - - polaris-ratelimit-example - com.tencent.cloud - ${revision} - ../pom.xml - - 4.0.0 + xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + polaris-ratelimit-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 - ratelimit-callee-service + ratelimit-callee-service - - - org.springframework.boot - spring-boot-starter-web - + + + org.springframework.boot + spring-boot-starter-web + - + com.tencent.cloud spring-cloud-starter-tencent-polaris-discovery - com.tencent.cloud - spring-cloud-starter-tencent-polaris-ratelimit - + com.tencent.cloud + spring-cloud-starter-tencent-polaris-ratelimit + + + + org.springframework.boot + spring-boot-starter-actuator + + - + + + + org.springframework.boot + spring-boot-maven-plugin + + + diff --git a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/java/com/tencent/cloud/ratelimit/example/service/callee/BusinessController.java b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/java/com/tencent/cloud/ratelimit/example/service/callee/BusinessController.java index e001201f41110c38e497b2082e2e9291f63de0bb..05e18a191852ba5360dba602b80b12d2a432f47a 100644 --- a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/java/com/tencent/cloud/ratelimit/example/service/callee/BusinessController.java +++ b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/java/com/tencent/cloud/ratelimit/example/service/callee/BusinessController.java @@ -18,6 +18,10 @@ package com.tencent.cloud.ratelimit.example.service.callee; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -38,6 +42,8 @@ import org.springframework.web.client.RestTemplate; @RequestMapping("/business") public class BusinessController { + private static final Logger LOG = LoggerFactory.getLogger(BusinessController.class); + private final AtomicInteger index = new AtomicInteger(0); @Autowired @@ -46,6 +52,8 @@ public class BusinessController { @Value("${spring.application.name}") private String appName; + private AtomicLong lastTimestamp = new AtomicLong(0); + /** * Get information. * @return information @@ -77,4 +85,21 @@ public class BusinessController { return builder.toString(); } + /** + * Get information with unirate. + * @return information + */ + @GetMapping("/unirate") + public String unirate() { + long currentTimestamp = System.currentTimeMillis(); + long lastTime = lastTimestamp.get(); + if (lastTime != 0) { + LOG.info("Current timestamp:" + currentTimestamp + ", diff from last timestamp:" + (currentTimestamp - lastTime)); + } + else { + LOG.info("Current timestamp:" + currentTimestamp); + } + lastTimestamp.set(currentTimestamp); + return "hello world for ratelimit service with diff from last request:" + (currentTimestamp - lastTime) + "ms."; + } } diff --git a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/java/com/tencent/cloud/ratelimit/example/service/callee/CustomLabelResolver.java b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/java/com/tencent/cloud/ratelimit/example/service/callee/CustomLabelResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..f441f4d2bf0034cddbc86dc44edab0a6c979cf84 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/java/com/tencent/cloud/ratelimit/example/service/callee/CustomLabelResolver.java @@ -0,0 +1,48 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.ratelimit.example.service.callee; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import com.tencent.cloud.polaris.ratelimit.spi.PolarisRateLimiterLabelServletResolver; + +import org.springframework.stereotype.Component; + +/** + * resolver custom label from request. + * + * @author lepdou 2022-03-31 + */ +@Component +public class CustomLabelResolver implements PolarisRateLimiterLabelServletResolver { + + @Override + public Map resolve(HttpServletRequest request) { + // rate limit by some request params. such as query params, headers .. + + Map labels = new HashMap<>(); + labels.put("user", "zhangsan"); + + return labels; + } + +} diff --git a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/bootstrap.yml index 6b8ba88a086e0dbf3979733bce489eb7d51bd463..6741bde1133ab04b594bedfcf85037d2bfed08d9 100644 --- a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/bootstrap.yml +++ b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/bootstrap.yml @@ -5,5 +5,17 @@ spring: name: RateLimitCalleeService cloud: polaris: - address: grpc://127.0.0.1:8091 + address: grpc://183.47.111.80:8091 namespace: default + enabled: true + ratelimit: + enabled: true + rejectRequestTipsFilePath: reject-tips.html + maxQueuingTime: 500 + +management: + endpoints: + web: + exposure: + include: + - polaris-ratelimit \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/reject-tips.html b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/reject-tips.html new file mode 100644 index 0000000000000000000000000000000000000000..693ef256b731a1038e060aa26b7773d8df297242 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/reject-tips.html @@ -0,0 +1,5 @@ +

+ + Custom reject content. + +

diff --git a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/rule.json b/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/rule.json deleted file mode 100644 index f047dc2bb4d26616f264bd60c1d110fe5ee97fb7..0000000000000000000000000000000000000000 --- a/spring-cloud-tencent-examples/polaris-ratelimit-example/ratelimit-callee-service/src/main/resources/rule.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "service": "RateLimitCalleeService", - "namespace": "Production", - "priority": 0, - "resource": "QPS", - "type": "LOCAL", - "labels": { - "method": { - "value": "/business/invoke" - } - }, - "amounts": [ - { - "maxAmount": 10, - "validDuration": "1s" - } - ], - "action": "REJECT" -} diff --git a/spring-cloud-tencent-examples/polaris-router-example/pom.xml b/spring-cloud-tencent-examples/polaris-router-example/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..37e8aee3422fc7ac1ad406122ec532bd12f42284 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/pom.xml @@ -0,0 +1,39 @@ + + + + spring-cloud-tencent-examples + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + polaris-router-example + pom + + + router-callee-service1 + router-callee-service2 + router-caller-service + + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/pom.xml b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..253d3739a17fa1bf80078c8aa5b7ea6c502a742d --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/pom.xml @@ -0,0 +1,50 @@ + + + + polaris-router-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + router-callee-service1 + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeApplication1.java b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeApplication1.java new file mode 100644 index 0000000000000000000000000000000000000000..6b481e47200b7054555e0358860913e45f7cc861 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeApplication1.java @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Router callee application. + * + * @author lepdou 2022-04-06 + */ +@SpringBootApplication +public class RouterCalleeApplication1 { + + public static void main(String[] args) { + SpringApplication.run(RouterCalleeApplication1.class, args); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java new file mode 100644 index 0000000000000000000000000000000000000000..925031a7bc6ccca507f3cf0f510943d33cfcf68d --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java @@ -0,0 +1,54 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Discovery callee controller. + * + * @author lepdou 2022-04-06 + */ +@RestController +@RequestMapping("/router/service/callee") +public class RouterCalleeController { + + private static Logger LOG = LoggerFactory.getLogger(RouterCalleeController.class); + + @Value("${server.port:0}") + private int port; + + /** + * Get information of callee. + * @return information of callee + */ + @PostMapping("/info") + public String info(String name, @RequestBody User user) { + LOG.info("Discovery Service Callee [{}] is called.", port); + return String.format("Discovery Service Callee [%s] is called. user = %s", port, user); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/User.java b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/User.java new file mode 100644 index 0000000000000000000000000000000000000000..ff83552db0132362fa9f17322151d4d1b500f42f --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/java/com/tencent/cloud/polaris/router/example/User.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +/** + * demo object. + * @author lepdou 2022-05-12 + */ +public class User { + + private String name; + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public String toString() { + return "User{" + + "name='" + name + '\'' + + ", age=" + age + + '}'; + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..fdd9f27749486762f066e3f291e202db86ef486d --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service1/src/main/resources/bootstrap.yml @@ -0,0 +1,14 @@ +server: + port: 48081 +spring: + application: + name: RouterCalleeService + cloud: + tencent: + metadata: + content: + label1: value1 + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/pom.xml b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..0dd00a788b71e9de3c125f7da258f4b384c84ff4 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/pom.xml @@ -0,0 +1,50 @@ + + + + polaris-router-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + router-callee-service2 + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeApplication2.java b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeApplication2.java new file mode 100644 index 0000000000000000000000000000000000000000..9664f46d32b6b5c3cb547bc1505c9641e1a973da --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeApplication2.java @@ -0,0 +1,36 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Router callee application. + * + * @author lepdou 2022-04-06 + */ +@SpringBootApplication +public class RouterCalleeApplication2 { + + public static void main(String[] args) { + SpringApplication.run(RouterCalleeApplication2.class, args); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java new file mode 100644 index 0000000000000000000000000000000000000000..b3e365ab81c382ebd6adbe0fc23696a3b15a7a3b --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeController.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * Discovery callee controller. + * + * @author lepdou 2022-04-06 + */ +@RestController +@RequestMapping("/router/service/callee") +public class RouterCalleeController { + + private static Logger LOG = LoggerFactory.getLogger(RouterCalleeController.class); + + @Value("${server.port:0}") + private int port; + + /** + * Get information of callee. + * @return information of callee + */ + @PostMapping("/info") + public String info(@RequestParam("name") String name, @RequestBody User user) { + LOG.info("Discovery Service Callee [{}] is called.", port); + return String.format("Discovery Service Callee [%s] is called. user = %s", port, user); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/User.java b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/User.java new file mode 100644 index 0000000000000000000000000000000000000000..ff83552db0132362fa9f17322151d4d1b500f42f --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/java/com/tencent/cloud/polaris/router/example/User.java @@ -0,0 +1,53 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +/** + * demo object. + * @author lepdou 2022-05-12 + */ +public class User { + + private String name; + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public String toString() { + return "User{" + + "name='" + name + '\'' + + ", age=" + age + + '}'; + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..d4b54a582fc4f4f36bd6ef238eb60a13eeeacd33 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-callee-service2/src/main/resources/bootstrap.yml @@ -0,0 +1,14 @@ +server: + port: 48082 +spring: + application: + name: RouterCalleeService + cloud: + tencent: + metadata: + content: + label1: value2 + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..fbdbf7081665b7cd47fd419dd7ee21c3c85a0d4f --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/pom.xml @@ -0,0 +1,62 @@ + + + + polaris-router-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + router-caller-service + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-discovery + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-router + + + + com.google.code.gson + gson + 2.9.0 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + + diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/CustomRouterLabelResolver.java b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/CustomRouterLabelResolver.java new file mode 100644 index 0000000000000000000000000000000000000000..815b2baffca07e3b7aa99d026e97aa35e8ee5124 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/CustomRouterLabelResolver.java @@ -0,0 +1,66 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +import java.util.HashMap; +import java.util.Map; + +import com.google.gson.Gson; +import com.tencent.cloud.polaris.router.spi.RouterLabelResolver; +import feign.RequestTemplate; + +import org.springframework.http.HttpRequest; +import org.springframework.stereotype.Component; + +/** + * + * Customize the business tag information obtained from the request + * + *@author lepdou 2022-05-12 + */ +@Component +public class CustomRouterLabelResolver implements RouterLabelResolver { + private final Gson gson = new Gson(); + + @Override + public Map resolve(RequestTemplate requestTemplate) { + Map labels = new HashMap<>(); + + User user = gson.fromJson(new String(requestTemplate.body()), User.class); + + labels.put("user", user.getName()); + + return labels; + } + + @Override + public Map resolve(HttpRequest request, byte[] body) { + Map labels = new HashMap<>(); + User user = gson.fromJson(new String(body), User.class); + + labels.put("user", user.getName()); + return labels; + } + + + @Override + public int getOrder() { + return 0; + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeService.java b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeService.java new file mode 100644 index 0000000000000000000000000000000000000000..7f1f1db39761bec530a5846c46b1ef3381a723df --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCalleeService.java @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * Router callee feign client. + * + * @author lepdou 2022-04-06 + */ +@FeignClient("RouterCalleeService") +public interface RouterCalleeService { + + @PostMapping("/router/service/callee/info") + String info(@RequestParam("name") String name, @RequestBody User user); + +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCallerApplication.java b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCallerApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..824a1226ff2bfd44528a869acbeface0a17e41a8 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCallerApplication.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +/** + * Router caller application. + * + * @author lepdou 2022-04-06 + */ +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients +public class RouterCallerApplication { + + public static void main(String[] args) { + SpringApplication.run(RouterCallerApplication.class, args); + } + + @Bean + @LoadBalanced + public RestTemplate restTemplate() { + return new RestTemplate(); + } + +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCallerController.java b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCallerController.java new file mode 100644 index 0000000000000000000000000000000000000000..866069d245b67e6c7072765ebb2e48225a1e94a5 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/RouterCallerController.java @@ -0,0 +1,77 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +/** + * Discovery caller controller. + * + * @author lepdou 2022-04-06 + */ +@RestController +@RequestMapping("/router/service/caller") +public class RouterCallerController { + + @Autowired + private RestTemplate restTemplate; + + @Autowired + private RouterCalleeService routerCalleeService; + + /** + * Get info of two value. + * @return info + */ + @GetMapping("/feign") + public String feign(@RequestParam String name) { + User user = new User(); + user.setName(name); + user.setAge(18); + return routerCalleeService.info(name, user); + } + + /** + * Get information of callee. + * @return information of callee + */ + @GetMapping("/rest") + public String rest(@RequestParam String name) { + User user = new User(); + user.setName(name); + user.setAge(18); + return restTemplate.postForObject( + "http://RouterCalleeService/router/service/callee/info?name={name}", user, String.class, name); + } + + /** + * health check. + * @return health check info + */ + @GetMapping("/healthCheck") + public String healthCheck() { + return "pk ok"; + } + +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/User.java b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/User.java new file mode 100644 index 0000000000000000000000000000000000000000..f68c6fff4076724a3acbea71e4feb643126cd9f8 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/java/com/tencent/cloud/polaris/router/example/User.java @@ -0,0 +1,45 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.example; + +/** + * demo object. + * @author lepdou 2022-05-12 + */ +public class User { + + private String name; + private int age; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..e8f7933307b1539678e483332f5c6f5bc7e0e283 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-example/router-caller-service/src/main/resources/bootstrap.yml @@ -0,0 +1,16 @@ +server: + port: 48083 +spring: + application: + name: RouterCallerService + cloud: + tencent: + metadata: + content: + k1: v1 + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true + loadbalancer: + enabled: true diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/README-zh.md b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/README-zh.md new file mode 100644 index 0000000000000000000000000000000000000000..eb80b2cc64a6e6571be37429a79a80fdb08187cc --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/README-zh.md @@ -0,0 +1,263 @@ +# Spring Cloud Polaris Gray Release Example + +[English](./README.md) | 简体中文 + +## 项目说明 + +本项目演示如何使用 Spring Cloud Tencent 的路由和标签透传功能 完成 Spring Cloud 应用的全链路灰度。 + +## 示例架构 + +![](https://qcloudimg.tencent-cloud.cn/raw/488182fd3001b3e77d9450e2c8798ff3.png) + +本示例请求都通过最上层网关进行分发,分发的目的地主要涉及3个环境: +- 灰度环境1(只针对uid=1的请求放开),环境标识为env=green(绿色环境) +- 灰度环境2(只针对uid=2的请求放开),环境标识为env=purple(紫色环境) +- 基线环境(稳定的业务版本,针对其他请求放开),环境标识为env=blue(蓝色环境) + +## 如何接入 + +### 启动网关服务 + +1. 添加环境变量 + + - 北极星服务端地址:polaris_address=grpc://127.0.0.1:8091 + - 可观测性PushGateway地址:prometheus_address=127.0.0.1:9091 + +2. 启动router-grayrelease-gateway应用 + + - IDE直接启动:找到主类 `GrayReleaseGatewayApplication`,执行 main 方法启动应用。 + - 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar router-grayrelease-gateway-${verion}.jar`启动应用。 + +3. 添加路由规则 + + 通过往北极星接口发送以下数据,为网关服务添加路由规则,路由规则可以针对用户ID进行环境的分发。 + ```` + POST /naming/v1/routings + + [{ + "service": "gray-release-gateway", + "namespace": "default", + "outbounds": [ + { + "sources": [ + { + "service": "gray-release-gateway", + "namespace": "default", + "metadata": { + "${http.header.uid}": { + "type": "EXACT", + "value": "2" + } + } + }], + "destinations": [ + { + "service": "*", + "namespace": "*", + "metadata": { + "env": { + "type": "EXACT", + "value": "purple" + } + }, + "priority": 0, + "weight": 100, + "isolate": false + }] + }, + { + "sources": [ + { + "service": "gray-release-gateway", + "namespace": "default", + "metadata": { + "${http.header.uid}": { + "type": "EXACT", + "value": "1" + } + } + }], + "destinations": [ + { + "service": "*", + "namespace": "*", + "metadata": { + "env": { + "type": "EXACT", + "value": "green" + } + }, + "priority": 0, + "weight": 100, + "isolate": false + }] + }, + { + "sources": [ + { + "service": "gray-release-gateway", + "namespace": "default", + "metadata": { + "*": { + "type": "EXACT", + "value": "*" + } + } + }], + "destinations": [ + { + "service": "*", + "namespace": "*", + "metadata": { + "env": { + "type": "EXACT", + "value": "blue" + } + }, + "priority": 0, + "weight": 100, + "isolate": false + }] + } + ] + }] + ```` + + 路由规则也可以通过北极星控制台进行定义,最终控制台效果如下: + + ![](https://qcloudimg.tencent-cloud.cn/raw/28e3d734c4b73624869a5b9b7059b118.png) + +### 启动Front服务 + +#### 启动基线环境(蓝色) + +1. 添加环境变量 + + - 北极星服务端地址:polaris_address=grpc://127.0.0.1:8091 + - 可观测性PushGateway地址:prometheus_address=127.0.0.1:9091 + - 环境标识:SCT_METADATA_CONTENT_env=blue + - 透传环境标识:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. 启动router-grayrelease-frontend应用 + + - IDE直接启动:找到主类 `GrayReleaseFrontApplication`,执行 main 方法启动应用。 + - 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar router-grayrelease-frontend-${verion}.jar`启动应用。 + +#### 启动灰度环境1(绿色) + +1. 添加环境变量 + + - 北极星服务端地址:polaris_address=grpc://127.0.0.1:8091 + - 可观测性PushGateway地址:prometheus_address=127.0.0.1:9091 + - 环境标识:SCT_METADATA_CONTENT_env=green + - 透传环境标识:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. 启动router-grayrelease-frontend应用(与前面一致) + + 如果遇到端口冲突,可以通过-Dserver.port来指定端口 + +#### 启动灰度环境2(紫色) + +1. 添加环境变量 + + - 北极星服务端地址:polaris_address=grpc://127.0.0.1:8091 + - 可观测性PushGateway地址:prometheus_address=127.0.0.1:9091 + - 环境标识:SCT_METADATA_CONTENT_env=purple + - 透传环境标识:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. 启动router-grayrelease-frontend应用(与前面一致) + +#### 启动后效果 + +在北极星控制台,可以看到gray-release-front服务下有3个节点,每个节点有不同的环境标识。 + +![](https://qcloudimg.tencent-cloud.cn/raw/96d2bdd2fb3495f737ab278e31a4a2e7.png) + +### 启动middle服务 + +#### 启动基线环境(蓝色) + +1. 添加环境变量 + + - 北极星服务端地址:polaris_address=grpc://127.0.0.1:8091 + - 可观测性PushGateway地址:prometheus_address=127.0.0.1:9091 + - 环境标识:SCT_METADATA_CONTENT_env=blue + - 透传环境标识:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. 启动router-grayrelease-middle应用 + + - IDE直接启动:找到主类 `GrayReleaseMiddleApplication`,执行 main 方法启动应用。 + - 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar router-grayrelease-middle-${verion}.jar`启动应用。 + + +#### 启动灰度环境2(紫色) + +1. 添加环境变量 + + - 北极星服务端地址:polaris_address=grpc://127.0.0.1:8091 + - 可观测性PushGateway地址:prometheus_address=127.0.0.1:9091 + - 环境标识:SCT_METADATA_CONTENT_env=purple + - 透传环境标识:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. 启动router-grayrelease-middle应用(与前面一致) + +### 启动back服务 + +#### 启动基线环境(蓝色) + +1. 添加环境变量 + + - 北极星服务端地址:polaris_address=grpc://127.0.0.1:8091 + - 可观测性PushGateway地址:prometheus_address=127.0.0.1:9091 + - 环境标识:SCT_METADATA_CONTENT_env=blue + - 透传环境标识:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. 启动router-grayrelease-backend应用 + + - IDE直接启动:找到主类 `GrayReleaseBackendApplication`,执行 main 方法启动应用。 + - 打包编译后启动:首先执行 `mvn clean package` 将工程编译打包,然后执行 `java -jar router-grayrelease-backend-${verion}.jar`启动应用。 + +#### 启动灰度环境1(绿色) + +1. 添加环境变量 + + - 北极星服务端地址:polaris_address=grpc://127.0.0.1:8091 + - 可观测性PushGateway地址:prometheus_address=127.0.0.1:9091 + - 环境标识:SCT_METADATA_CONTENT_env=green + - 透传环境标识:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. 启动router-grayrelease-backend应用(与前面一致) + +### 测试 + +#### 基线环境路由 + +```` +curl -H'uid:0' 127.0.0.1:59100/router/gray/route_rule +```` +获取结果 +```` +gray-release-gateway -> gray-release-front[blue] -> gray-release-middle[blue] -> gray-release-back[blue] +```` + +#### 灰度环境1(绿色)路由 + +```` +curl -H'uid:1' 127.0.0.1:59100/router/gray/route_rule +```` +获取结果 +```` +gray-release-gateway -> gray-release-front[green] -> gray-release-middle[blue] -> gray-release-back[green] +```` + +#### 灰度环境2(紫色)路由 + +```` +curl -H'uid:2' 127.0.0.1:59100/router/gray/route_rule +```` +获取结果 +```` +gray-release-gateway -> gray-release-front[purple] -> gray-release-middle[purple] -> gray-release-back[blue] +```` + diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/README.md b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8ad68bbeb97a36ab48c3cd92eced9d88e0d063d4 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/README.md @@ -0,0 +1,262 @@ +# Spring Cloud Polaris Gray Release Example + +English | [简体中文](./README-zh.md) + +## Project Explanation + +This project shows how to use Spring Cloud Tencent route and transitive feature to do the full chain gray releasing. + +## Architecture + +![](https://qcloudimg.tencent-cloud.cn/raw/488182fd3001b3e77d9450e2c8798ff3.png) + +Incoming requests dispatched from Gateway service to 3 environments: +- gray1(match uid=1), env=green(green environment) +- gray2(match uid=2), env=purple(purple environment) +- baseline(stable environment, match all other requests), env=blue(blue environment) + +## How to access + +### Start Gateway service + +1. add environment variables + + - polaris server address: polaris_address=grpc://127.0.0.1:8091 + - pushgateway address: prometheus_address=127.0.0.1:9091 + +2. start router-grayrelease-gateway application + + - Launch by IDE:Start the main class `GrayReleaseGatewayApplication`. + - Launch by Jar:Execute `mvn clean package` to compile with jar package, then use `java -jar router-grayrelease-gateway-${verion}.jar` to launch application. + +3. add the route rule + + Send http request to polaris server to add the route rule, make requests dispatched to 3 environments. + ```` + POST /naming/v1/routings + + [{ + "service": "gray-release-gateway", + "namespace": "default", + "outbounds": [ + { + "sources": [ + { + "service": "gray-release-gateway", + "namespace": "default", + "metadata": { + "${http.header.uid}": { + "type": "EXACT", + "value": "2" + } + } + }], + "destinations": [ + { + "service": "*", + "namespace": "*", + "metadata": { + "env": { + "type": "EXACT", + "value": "purple" + } + }, + "priority": 0, + "weight": 100, + "isolate": false + }] + }, + { + "sources": [ + { + "service": "gray-release-gateway", + "namespace": "default", + "metadata": { + "${http.header.uid}": { + "type": "EXACT", + "value": "1" + } + } + }], + "destinations": [ + { + "service": "*", + "namespace": "*", + "metadata": { + "env": { + "type": "EXACT", + "value": "green" + } + }, + "priority": 0, + "weight": 100, + "isolate": false + }] + }, + { + "sources": [ + { + "service": "gray-release-gateway", + "namespace": "default", + "metadata": { + "*": { + "type": "EXACT", + "value": "*" + } + } + }], + "destinations": [ + { + "service": "*", + "namespace": "*", + "metadata": { + "env": { + "type": "EXACT", + "value": "blue" + } + }, + "priority": 0, + "weight": 100, + "isolate": false + }] + } + ] + }] + ```` + + The route rule can be added by polaris console: + + ![](https://qcloudimg.tencent-cloud.cn/raw/28e3d734c4b73624869a5b9b7059b118.png) + +### Start Front service + +#### Start baseline environment (blue) + +1. add environment variables + + - polaris server address: polaris_address=grpc://127.0.0.1:8091 + - pushgateway address: prometheus_address=127.0.0.1:9091 + - env tag:SCT_METADATA_CONTENT_env=blue + - transitive tag:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. start router-grayrelease-frontend application + + - Launch by IDE:Start the main class `GrayReleaseFrontApplication`. + - Launch by Jar:Execute `mvn clean package` to compile with jar package, then use `java -jar router-grayrelease-frontend-${verion}.jar` to launch application. + +#### Start gray1 environment (green) + +1. add environment variables + + - polaris server address: polaris_address=grpc://127.0.0.1:8091 + - pushgateway address: prometheus_address=127.0.0.1:9091 + - env tag:SCT_METADATA_CONTENT_env=green + - transitive tag:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. start router-grayrelease-frontend application (same as previous instruction) + + If port conflicted, you can specify another port by -Dserver.port + +#### Start gray2 environment (purple) + +1. add environment variables + + - polaris server address: polaris_address=grpc://127.0.0.1:8091 + - pushgateway address: prometheus_address=127.0.0.1:9091 + - env tag:SCT_METADATA_CONTENT_env=purple + - transitive tag:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. start router-grayrelease-frontend application (same as previous instruction) + +#### Start effective + +You can find the instances with different tags in polaris console. + +![](https://qcloudimg.tencent-cloud.cn/raw/96d2bdd2fb3495f737ab278e31a4a2e7.png) + +### Start Middle service + +#### Start baseline environment (blue) + +1. add environment variables + + - polaris server address: polaris_address=grpc://127.0.0.1:8091 + - pushgateway address: prometheus_address=127.0.0.1:9091 + - env tag:SCT_METADATA_CONTENT_env=blue + - transitive tag:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. start router-grayrelease-middle application + + - Launch by IDE:Start the main class `GrayReleaseMiddleApplication`. + - Launch by Jar:Execute `mvn clean package` to compile with jar package, then use `java -jar router-grayrelease-middle-${verion}.jar` to launch application. + +#### Start gray2 environment (purple) + +1. add environment variables + + - polaris server address: polaris_address=grpc://127.0.0.1:8091 + - pushgateway address: prometheus_address=127.0.0.1:9091 + - env tag:SCT_METADATA_CONTENT_env=purple + - transitive tag:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. start router-grayrelease-middle application (same as previous instruction) + +### Start Back service + +#### Start baseline environment (blue) + +1. add environment variables + + - polaris server address: polaris_address=grpc://127.0.0.1:8091 + - pushgateway address: prometheus_address=127.0.0.1:9091 + - env tag:SCT_METADATA_CONTENT_env=blue + - transitive tag:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. start router-grayrelease-backend application + + - Launch by IDE:Start the main class `GrayReleaseBackendApplication`. + - Launch by Jar:Execute `mvn clean package` to compile with jar package, then use `java -jar router-grayrelease-backend-${verion}.jar` to launch application. + +#### Start gray1 environment (green) + +1. add environment variables + + - polaris server address: polaris_address=grpc://127.0.0.1:8091 + - pushgateway address: prometheus_address=127.0.0.1:9091 + - env tag:SCT_METADATA_CONTENT_env=green + - transitive tag:SCT_METADATA_CONTENT_TRANSITIVE=env + +2. start router-grayrelease-backend application (same as previous instruction) + +### Test + +#### Baseline routing + +```` +curl -H'uid:0' 127.0.0.1:59100/router/gray/route_rule +```` +Got result +```` +gray-release-gateway -> gray-release-front[blue] -> gray-release-middle[blue] -> gray-release-back[blue] +```` + +#### Green routing + +```` +curl -H'uid:1' 127.0.0.1:59100/router/gray/route_rule +```` +Got result +```` +gray-release-gateway -> gray-release-front[green] -> gray-release-middle[blue] -> gray-release-back[green] +```` + +#### Purple routing + +```` +curl -H'uid:2' 127.0.0.1:59100/router/gray/route_rule +```` +Got result +```` +gray-release-gateway -> gray-release-front[purple] -> gray-release-middle[purple] -> gray-release-back[blue] +```` + diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/pom.xml b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..5ef821730b5f9fea5afa8b224f27e6b7a5e0de2b --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/pom.xml @@ -0,0 +1,23 @@ + + + + spring-cloud-tencent-examples + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + polaris-router-grayrelease-example + pom + Spring Cloud Tencent Polaris Router GrayRelease Example + Example of Spring Cloud Tencent Polaris Router GrayRelease + + router-grayrelease-gateway + router-grayrelease-frontend + router-grayrelease-middle + router-grayrelease-backend + + \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/pom.xml b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..170f87ed53136843702f8a30935ee477a1b83cfd --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/pom.xml @@ -0,0 +1,69 @@ + + + + polaris-router-grayrelease-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + router-grayrelease-backend + + + + spring-cloud-starter-tencent-polaris-discovery + com.tencent.cloud + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-router + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/docker/Dockerfile b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/docker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..482d43ee9050b2379c40661d67f28316b2024972 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/docker/Dockerfile @@ -0,0 +1,15 @@ +############################################################ +# Dockerfile to build polaris-java quickstart example provider + +# 1. You need to build the binary from the source code, +# use `mvn clean install` to build the binary. +# 2. You need to copy the quickstart-example-provider-*.jar to this directory +# 3. Replace the ${VERSION} to the real version of the project + +############################################################ + +FROM java:8 + +ADD router-grayrelease-backend-1.5.0-Hoxton.SR9-SNAPSHOT.jar /root/app.jar + +ENTRYPOINT ["java","-jar","/root/app.jar"] \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/back/BackController.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/back/BackController.java new file mode 100644 index 0000000000000000000000000000000000000000..7d71eab986dda856d639048f356a924572af1eca --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/back/BackController.java @@ -0,0 +1,43 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.back; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/router/gray") +public class BackController { + + @Autowired + private Environment environment; + + /** + * Get information of callee. + * @return information of callee + */ + @GetMapping("/rest") + public String rest() { + String env = System.getenv("SCT_METADATA_CONTENT_env"); + String appName = environment.getProperty("spring.application.name"); + return appName + "[" + env + "]"; + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/back/GrayReleaseBackendApplication.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/back/GrayReleaseBackendApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..a70a2ab391560fc4af33ae937c2209c47bec1af7 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/back/GrayReleaseBackendApplication.java @@ -0,0 +1,29 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.back; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class GrayReleaseBackendApplication { + + public static void main(String[] args) { + SpringApplication.run(GrayReleaseBackendApplication.class, args); + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..a6715a57d9614306a545f799ab05dc526220b8fb --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-backend/src/main/resources/bootstrap.yml @@ -0,0 +1,15 @@ +server: + session-timeout: 1800 + port: 59002 +spring: + application: + name: gray-release-back + cloud: + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true +logging: + level: + org.springframework.cloud.gateway: info + com.tencent.cloud.polaris: debug diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/pom.xml b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..62524d2d0b8a448a279fa132d4978b258914631a --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/pom.xml @@ -0,0 +1,69 @@ + + + + polaris-router-grayrelease-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + router-grayrelease-frontend + + + + spring-cloud-starter-tencent-polaris-discovery + com.tencent.cloud + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-router + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/docker/Dockerfile b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/docker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..7aec6aad2cef33bdc356f2d3c4bccbbb26045ecd --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/docker/Dockerfile @@ -0,0 +1,15 @@ +############################################################ +# Dockerfile to build polaris-java quickstart example provider + +# 1. You need to build the binary from the source code, +# use `mvn clean install` to build the binary. +# 2. You need to copy the quickstart-example-provider-*.jar to this directory +# 3. Replace the ${VERSION} to the real version of the project + +############################################################ + +FROM java:8 + +ADD router-grayrelease-frontend-1.5.0-Hoxton.SR9-SNAPSHOT.jar /root/app.jar + +ENTRYPOINT ["java","-jar","/root/app.jar"] \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/front/FrontController.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/front/FrontController.java new file mode 100644 index 0000000000000000000000000000000000000000..4274744a6ce7f83f073d9af7743b51ffad87cadf --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/front/FrontController.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.front; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/router/gray") +public class FrontController { + + @Autowired + private Environment environment; + + @Autowired + private RouterService routerService; + + /** + * Get information of callee. + * @return information of callee + */ + @GetMapping("/rest") + public String rest() { + String env = System.getenv("SCT_METADATA_CONTENT_env"); + String appName = environment.getProperty("spring.application.name"); + String curName = appName + "[" + env + "]"; + String resp = routerService.rest(); + return curName + " -> " + resp; + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/front/GrayReleaseFrontApplication.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/front/GrayReleaseFrontApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..6d8653eeb83b837087189418578fca60df9edf89 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/front/GrayReleaseFrontApplication.java @@ -0,0 +1,33 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.front; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients +public class GrayReleaseFrontApplication { + + public static void main(String[] args) { + SpringApplication.run(GrayReleaseFrontApplication.class, args); + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/front/RouterService.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/front/RouterService.java new file mode 100644 index 0000000000000000000000000000000000000000..00c7d5b9ac331e50c16bb2c6ae3e4e8e621b86f0 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/java/com/tencent/cloud/polaris/router/grayrelease/front/RouterService.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.front; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * Router callee feign client. + * + * @author lepdou 2022-04-06 + */ +@FeignClient("gray-release-middle") +public interface RouterService { + + @GetMapping("/router/gray/rest") + String rest(); + +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..cb7232d1244f145e8d80d85a03d4edf5c6249ff2 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-frontend/src/main/resources/bootstrap.yml @@ -0,0 +1,15 @@ +server: + session-timeout: 1800 + port: 59000 +spring: + application: + name: gray-release-front + cloud: + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true +logging: + level: + org.springframework.cloud.gateway: info + com.tencent.cloud.polaris: debug diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/pom.xml b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..e847b465f39e700d307ceadc72a1d3f15907a951 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/pom.xml @@ -0,0 +1,53 @@ + + + + polaris-router-grayrelease-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + router-grayrelease-gateway + + + + org.springframework.boot + spring-boot-starter-web + + + + spring-cloud-starter-tencent-polaris-discovery + com.tencent.cloud + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-router + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/docker/Dockerfile b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/docker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..315baa8349c6e4076f7d63ff78773f24136d90f1 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/docker/Dockerfile @@ -0,0 +1,15 @@ +############################################################ +# Dockerfile to build polaris-java quickstart example provider + +# 1. You need to build the binary from the source code, +# use `mvn clean install` to build the binary. +# 2. You need to copy the quickstart-example-provider-*.jar to this directory +# 3. Replace the ${VERSION} to the real version of the project + +############################################################ + +FROM java:8 + +ADD router-grayrelease-gateway-1.5.0-Hoxton.SR9-SNAPSHOT.jar /root/app.jar + +ENTRYPOINT ["java","-jar","/root/app.jar"] \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/java/com/tencent/cloud/polaris/router/grayrelease/gateway/GatewayController.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/java/com/tencent/cloud/polaris/router/grayrelease/gateway/GatewayController.java new file mode 100644 index 0000000000000000000000000000000000000000..cee5a465c58de97b5bca93063ab3ea9820a0211c --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/java/com/tencent/cloud/polaris/router/grayrelease/gateway/GatewayController.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.gateway; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/router/gray") +public class GatewayController { + + @Autowired + private Environment environment; + + @Autowired + private RouterService routerService; + + /** + * Get information of callee. + * @return information of callee + */ + @GetMapping("/route_rule") + public String routeRule(@RequestHeader("uid") int userId) { + String appName = environment.getProperty("spring.application.name"); + String resp = routerService.restByUser(userId); + return appName + " -> " + resp; + } + +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/java/com/tencent/cloud/polaris/router/grayrelease/gateway/GrayReleaseGatewayApplication.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/java/com/tencent/cloud/polaris/router/grayrelease/gateway/GrayReleaseGatewayApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..a7b5a115e1b486149d0b5b5f72bfe0bc4dcc5743 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/java/com/tencent/cloud/polaris/router/grayrelease/gateway/GrayReleaseGatewayApplication.java @@ -0,0 +1,33 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients +public class GrayReleaseGatewayApplication { + + public static void main(String[] args) { + SpringApplication.run(GrayReleaseGatewayApplication.class, args); + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/java/com/tencent/cloud/polaris/router/grayrelease/gateway/RouterService.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/java/com/tencent/cloud/polaris/router/grayrelease/gateway/RouterService.java new file mode 100644 index 0000000000000000000000000000000000000000..23ffb30a0a5000abc9a852fc46a71d0afde83a18 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/java/com/tencent/cloud/polaris/router/grayrelease/gateway/RouterService.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.gateway; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; + +/** + * Router callee feign client. + * + * @author lepdou 2022-04-06 + */ +@FeignClient("gray-release-front") +public interface RouterService { + + @GetMapping("/router/gray/rest") + String restByUser(@RequestHeader("uid") int user); +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..44041c268ae86ab14eb52bd0dad16a05b85be001 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-gateway/src/main/resources/bootstrap.yml @@ -0,0 +1,15 @@ +server: + session-timeout: 1800 + port: 59100 +spring: + application: + name: gray-release-gateway + cloud: + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true +logging: + level: + org.springframework.cloud.gateway: info + com.tencent.cloud.polaris: debug diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/pom.xml b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..87060aa96d4018098a98a29067f21d6e56997de3 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/pom.xml @@ -0,0 +1,69 @@ + + + + polaris-router-grayrelease-example + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + router-grayrelease-middle + + + + spring-cloud-starter-tencent-polaris-discovery + com.tencent.cloud + + + + com.tencent.cloud + spring-cloud-starter-tencent-polaris-router + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.0 + + + attach-sources + + jar + + + + + + + diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/docker/Dockerfile b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/docker/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..33d9b4606a8ede663b9d4f6e547a7fe7caa81546 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/docker/Dockerfile @@ -0,0 +1,15 @@ +############################################################ +# Dockerfile to build polaris-java quickstart example provider + +# 1. You need to build the binary from the source code, +# use `mvn clean install` to build the binary. +# 2. You need to copy the quickstart-example-provider-*.jar to this directory +# 3. Replace the ${VERSION} to the real version of the project + +############################################################ + +FROM java:8 + +ADD router-grayrelease-middle-1.5.0-Hoxton.SR9-SNAPSHOT.jar /root/app.jar + +ENTRYPOINT ["java","-jar","/root/app.jar"] \ No newline at end of file diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/java/com/tencent/cloud/polaris/router/grayrelease/middle/GrayReleaseMiddleApplication.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/java/com/tencent/cloud/polaris/router/grayrelease/middle/GrayReleaseMiddleApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..4857030e200a428929a69e82c139d9e16d60af25 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/java/com/tencent/cloud/polaris/router/grayrelease/middle/GrayReleaseMiddleApplication.java @@ -0,0 +1,33 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.middle; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; + +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients +public class GrayReleaseMiddleApplication { + + public static void main(String[] args) { + SpringApplication.run(GrayReleaseMiddleApplication.class, args); + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/java/com/tencent/cloud/polaris/router/grayrelease/middle/MiddleController.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/java/com/tencent/cloud/polaris/router/grayrelease/middle/MiddleController.java new file mode 100644 index 0000000000000000000000000000000000000000..85fe8ddf8c469e62d83924c143fbcaff3c001c8b --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/java/com/tencent/cloud/polaris/router/grayrelease/middle/MiddleController.java @@ -0,0 +1,49 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.middle; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/router/gray") +public class MiddleController { + + @Autowired + private Environment environment; + + @Autowired + private RouterService routerService; + + /** + * Get information of callee. + * @return information of callee + */ + @GetMapping("/rest") + public String rest() { + String env = System.getenv("SCT_METADATA_CONTENT_env"); + String appName = environment.getProperty("spring.application.name"); + String curName = appName + "[" + env + "]"; + String resp = routerService.rest(); + return curName + " -> " + resp; + } +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/java/com/tencent/cloud/polaris/router/grayrelease/middle/RouterService.java b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/java/com/tencent/cloud/polaris/router/grayrelease/middle/RouterService.java new file mode 100644 index 0000000000000000000000000000000000000000..32225209cdc818d76d3677f5164df5618ef597a0 --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/java/com/tencent/cloud/polaris/router/grayrelease/middle/RouterService.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.router.grayrelease.middle; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; + +/** + * Router callee feign client. + * + * @author lepdou 2022-04-06 + */ +@FeignClient("gray-release-back") +public interface RouterService { + + @GetMapping("/router/gray/rest") + String rest(); + +} diff --git a/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/resources/bootstrap.yml b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/resources/bootstrap.yml new file mode 100644 index 0000000000000000000000000000000000000000..9638411e858770467fb550e940ab74da8cb308af --- /dev/null +++ b/spring-cloud-tencent-examples/polaris-router-grayrelease-example/router-grayrelease-middle/src/main/resources/bootstrap.yml @@ -0,0 +1,15 @@ +server: + session-timeout: 1800 + port: 59001 +spring: + application: + name: gray-release-middle + cloud: + polaris: + address: grpc://183.47.111.80:8091 + namespace: default + enabled: true +logging: + level: + org.springframework.cloud.gateway: info + com.tencent.cloud.polaris: debug diff --git a/spring-cloud-tencent-examples/pom.xml b/spring-cloud-tencent-examples/pom.xml index 61106d50167d8af7c72dede889b4af12a649499c..a4c6c9bdae89990aec3cfdd6c7be16f0e2d59160 100644 --- a/spring-cloud-tencent-examples/pom.xml +++ b/spring-cloud-tencent-examples/pom.xml @@ -21,6 +21,9 @@ polaris-circuitbreaker-example polaris-gateway-example polaris-config-example + polaris-router-example + metadata-transfer-example + polaris-router-grayrelease-example diff --git a/spring-cloud-tencent-polaris-context/pom.xml b/spring-cloud-tencent-polaris-context/pom.xml index a11dc5aec7cdcff342c1265b8634d38777689c9e..6d7fda248ad3b3b100adab19e7665ca29c6a9edb 100644 --- a/spring-cloud-tencent-polaris-context/pom.xml +++ b/spring-cloud-tencent-polaris-context/pom.xml @@ -18,86 +18,72 @@ com.tencent.cloud spring-cloud-tencent-commons - - + + - - - com.tencent.polaris - polaris-client - - - - com.tencent.polaris - polaris-plugin-api - - - - com.tencent.polaris - connector-polaris-grpc - - - - com.tencent.polaris - connector-consul - + + + com.tencent.polaris + polaris-client + - - com.tencent.polaris - connector-composite - + + com.tencent.polaris + polaris-plugin-api + - - com.tencent.polaris - registry-memory - + + com.tencent.polaris + connector-polaris-grpc + - - com.tencent.polaris - flow-cache-expired - + + com.tencent.polaris + connector-consul + - - com.tencent.polaris - router-isolated - + + com.tencent.polaris + connector-composite + - - com.tencent.polaris - router-healthy - + + com.tencent.polaris + registry-memory + - - com.tencent.polaris - router-rule - + + com.tencent.polaris + flow-cache-expired + - - com.tencent.polaris - router-nearby - + + + com.tencent.polaris + router-isolated + - - com.tencent.polaris - router-metadata - + + com.tencent.polaris + router-healthy + - - com.tencent.polaris - loadbalancer-random - + + com.tencent.polaris + loadbalancer-random + - - com.tencent.polaris - loadbalancer-ringhash - - + + com.tencent.polaris + loadbalancer-ringhash + + - - org.springframework.boot - spring-boot-starter-test - test - - + + org.springframework.boot + spring-boot-starter-test + test + + diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ConditionalOnPolarisEnabled.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ConditionalOnPolarisEnabled.java new file mode 100644 index 0000000000000000000000000000000000000000..7d2b960b9873addac757f1d37d0deb266653482c --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ConditionalOnPolarisEnabled.java @@ -0,0 +1,37 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.context; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +/** + * Condition that if Polaris enabled. + * + * @author Haotian Zhang + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@ConditionalOnProperty(value = "spring.cloud.polaris.enabled", matchIfMissing = true) +public @interface ConditionalOnPolarisEnabled { + +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ModifyAddress.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ModifyAddress.java index 6d3dcf4b943c33c8dfbe632263ebc090087d5ce0..b57cbfe910c1b9952c4e94db8d56d076ff27203e 100644 --- a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ModifyAddress.java +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ModifyAddress.java @@ -22,6 +22,7 @@ import java.util.List; import com.tencent.cloud.common.constant.ContextConstant; import com.tencent.cloud.common.util.AddressUtils; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import com.tencent.polaris.factory.config.ConfigurationImpl; import org.apache.commons.lang.StringUtils; @@ -34,24 +35,23 @@ import org.springframework.beans.factory.annotation.Autowired; */ public class ModifyAddress implements PolarisConfigModifier { - @Autowired - private PolarisContextProperties properties; + @Autowired + private PolarisContextProperties properties; - @Override - public void modify(ConfigurationImpl configuration) { - if (StringUtils.isBlank(properties.getAddress())) { - return; - } - - List addresses = AddressUtils - .parseAddressList(properties.getAddress()); - - configuration.getGlobal().getServerConnector().setAddresses(addresses); + @Override + public void modify(ConfigurationImpl configuration) { + if (StringUtils.isBlank(properties.getAddress())) { + return; } - @Override - public int getOrder() { - return ContextConstant.ModifierOrder.FIRST; - } + List addresses = AddressUtils.parseAddressList(properties.getAddress()); + configuration.getGlobal().getServerConnector().setAddresses(addresses); } + + @Override + public int getOrder() { + return ContextConstant.ModifierOrder.FIRST; + } + +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PostInitPolarisSDKContext.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PostInitPolarisSDKContext.java new file mode 100644 index 0000000000000000000000000000000000000000..014ca5ba22e0c684479d879c7e8c2775d723dc5a --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PostInitPolarisSDKContext.java @@ -0,0 +1,50 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.context; + +import com.tencent.cloud.common.metadata.StaticMetadataManager; +import com.tencent.polaris.api.plugin.common.ValueContext; +import com.tencent.polaris.api.plugin.route.LocationLevel; +import com.tencent.polaris.client.api.SDKContext; +import org.apache.commons.lang.StringUtils; + +/** + * After all configurations are loaded, post-initialize SDKContext. + *@author lepdou 2022-06-28 + */ +public class PostInitPolarisSDKContext { + + public PostInitPolarisSDKContext(SDKContext sdkContext, StaticMetadataManager staticMetadataManager) { + // set instance's location info + String region = staticMetadataManager.getRegion(); + String zone = staticMetadataManager.getZone(); + String campus = staticMetadataManager.getCampus(); + + ValueContext valueContext = sdkContext.getValueContext(); + if (StringUtils.isNotBlank(region)) { + valueContext.setValue(LocationLevel.region.name(), region); + } + if (StringUtils.isNotBlank(zone)) { + valueContext.setValue(LocationLevel.zone.name(), zone); + } + if (StringUtils.isNotBlank(campus)) { + valueContext.setValue(LocationLevel.campus.name(), campus); + } + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ServiceRuleManager.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ServiceRuleManager.java new file mode 100644 index 0000000000000000000000000000000000000000..1a0af32b4e58ecd5e68442fa404100df6706039d --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/ServiceRuleManager.java @@ -0,0 +1,118 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.context; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.tencent.polaris.api.pojo.DefaultServiceEventKeysProvider; +import com.tencent.polaris.api.pojo.ServiceEventKey; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.pojo.ServiceRule; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.client.flow.BaseFlow; +import com.tencent.polaris.client.flow.DefaultFlowControlParam; +import com.tencent.polaris.client.flow.FlowControlParam; +import com.tencent.polaris.client.flow.ResourcesResponse; +import com.tencent.polaris.client.pb.RateLimitProto; +import com.tencent.polaris.client.pb.RoutingProto; + +/** + * the manager of service governance rules. for example: rate limit rule, router rules. + * + *@author lepdou 2022-05-13 + */ +public class ServiceRuleManager { + + private final SDKContext sdkContext; + + private final FlowControlParam controlParam; + + public ServiceRuleManager(SDKContext sdkContext) { + this.sdkContext = sdkContext; + controlParam = new DefaultFlowControlParam(); + controlParam.setTimeoutMs(sdkContext.getConfig().getGlobal().getAPI().getTimeout()); + controlParam.setMaxRetry(sdkContext.getConfig().getGlobal().getAPI().getMaxRetryTimes()); + controlParam.setRetryIntervalMs(sdkContext.getConfig().getGlobal().getAPI().getRetryInterval()); + } + + public RateLimitProto.RateLimit getServiceRateLimitRule(String namespace, String service) { + ServiceEventKey serviceEventKey = new ServiceEventKey(new ServiceKey(namespace, service), + ServiceEventKey.EventType.RATE_LIMITING); + + DefaultServiceEventKeysProvider svcKeysProvider = new DefaultServiceEventKeysProvider(); + svcKeysProvider.setSvcEventKey(serviceEventKey); + + ResourcesResponse resourcesResponse = BaseFlow + .syncGetResources(sdkContext.getExtensions(), true, svcKeysProvider, controlParam); + + ServiceRule serviceRule = resourcesResponse.getServiceRule(serviceEventKey); + if (serviceRule != null) { + Object rule = serviceRule.getRule(); + if (rule instanceof RateLimitProto.RateLimit) { + return (RateLimitProto.RateLimit) rule; + } + } + + return null; + } + + public List getServiceRouterRule(String namespace, String sourceService, String dstService) { + Set routerKeys = new HashSet<>(); + + ServiceEventKey dstSvcEventKey = new ServiceEventKey(new ServiceKey(namespace, dstService), + ServiceEventKey.EventType.ROUTING); + routerKeys.add(dstSvcEventKey); + + ServiceEventKey srcSvcEventKey = new ServiceEventKey(new ServiceKey(namespace, sourceService), + ServiceEventKey.EventType.ROUTING); + routerKeys.add(srcSvcEventKey); + + DefaultServiceEventKeysProvider svcKeysProvider = new DefaultServiceEventKeysProvider(); + svcKeysProvider.setSvcEventKeys(routerKeys); + + + ResourcesResponse resourcesResponse = BaseFlow + .syncGetResources(sdkContext.getExtensions(), true, svcKeysProvider, controlParam); + + List rules = new ArrayList<>(); + + //get source service outbound rules. + ServiceRule sourceServiceRule = resourcesResponse.getServiceRule(srcSvcEventKey); + if (sourceServiceRule != null) { + Object rule = sourceServiceRule.getRule(); + if (rule instanceof RoutingProto.Routing) { + rules.addAll(((RoutingProto.Routing) rule).getOutboundsList()); + } + } + + //get peer service inbound rules. + ServiceRule dstServiceRule = resourcesResponse.getServiceRule(dstSvcEventKey); + if (dstServiceRule != null) { + Object rule = dstServiceRule.getRule(); + if (rule instanceof RoutingProto.Routing) { + rules.addAll(((RoutingProto.Routing) rule).getInboundsList()); + } + } + + return rules; + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PolarisContextConfiguration.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextAutoConfiguration.java similarity index 71% rename from spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PolarisContextConfiguration.java rename to spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextAutoConfiguration.java index 63ddd97e8e757b5d0bfe970c98ff69a17ac94fb0..eb623825383bc8392727d5b217b78b9032c902d7 100644 --- a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PolarisContextConfiguration.java +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextAutoConfiguration.java @@ -16,8 +16,11 @@ * */ -package com.tencent.cloud.polaris.context; +package com.tencent.cloud.polaris.context.config; +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; +import com.tencent.cloud.polaris.context.ModifyAddress; +import com.tencent.cloud.polaris.context.ServiceRuleManager; import com.tencent.polaris.api.exception.PolarisException; import com.tencent.polaris.client.api.SDKContext; @@ -26,12 +29,13 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.annotation.Bean; /** - * Configuration for Polaris {@link SDKContext}. + * Autoconfiguration for Polaris {@link SDKContext}. * * @author Haotian Zhang */ -@EnableConfigurationProperties({ PolarisContextProperties.class }) -public class PolarisContextConfiguration { +@ConditionalOnPolarisEnabled +@EnableConfigurationProperties({PolarisContextProperties.class}) +public class PolarisContextAutoConfiguration { @Bean(name = "polarisContext", initMethod = "init", destroyMethod = "destroy") @ConditionalOnMissingBean @@ -45,4 +49,10 @@ public class PolarisContextConfiguration { public ModifyAddress polarisConfigModifier() { return new ModifyAddress(); } + + @Bean + @ConditionalOnMissingBean + public ServiceRuleManager serviceRuleManager(SDKContext sdkContext) { + return new ServiceRuleManager(sdkContext); + } } diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextBootstrapAutoConfiguration.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextBootstrapAutoConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..75d11f8b56ab01abafe2ca5209552f810505aad9 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextBootstrapAutoConfiguration.java @@ -0,0 +1,35 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.context.config; + +import com.tencent.polaris.client.api.SDKContext; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Import; + +/** + * Bootstrap autoconfiguration for Polaris {@link SDKContext}. + * + * @author Haotian Zhang + */ +@ConditionalOnProperty("spring.cloud.polaris.enabled") +@Import(PolarisContextAutoConfiguration.class) +public class PolarisContextBootstrapAutoConfiguration { + +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextPostConfiguration.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextPostConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..f553974627dee8850d9c5319ec102cccf5ed4a85 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextPostConfiguration.java @@ -0,0 +1,39 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.context.config; + +import com.tencent.cloud.common.metadata.StaticMetadataManager; +import com.tencent.cloud.polaris.context.PostInitPolarisSDKContext; +import com.tencent.polaris.client.api.SDKContext; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Post-initialization operations after the application initialization phase is completed. + *@author lepdou 2022-06-28 + */ +@Configuration +public class PolarisContextPostConfiguration { + + @Bean + public PostInitPolarisSDKContext postInitPolarisSDKContext(SDKContext sdkContext, StaticMetadataManager staticMetadataManager) { + return new PostInitPolarisSDKContext(sdkContext, staticMetadataManager); + } +} diff --git a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PolarisContextProperties.java b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextProperties.java similarity index 83% rename from spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PolarisContextProperties.java rename to spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextProperties.java index 9bc28682a968b8fb3a22c13b3262cded0fc84d3a..87a076e21348cd8b0787521f21531ebafa0b285b 100644 --- a/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/PolarisContextProperties.java +++ b/spring-cloud-tencent-polaris-context/src/main/java/com/tencent/cloud/polaris/context/config/PolarisContextProperties.java @@ -16,13 +16,14 @@ * */ -package com.tencent.cloud.polaris.context; +package com.tencent.cloud.polaris.context.config; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; +import com.tencent.cloud.polaris.context.PolarisConfigModifier; import com.tencent.polaris.api.config.ConfigProvider; import com.tencent.polaris.api.config.Configuration; import com.tencent.polaris.factory.ConfigAPIFactory; @@ -52,11 +53,21 @@ public class PolarisContextProperties { */ private String localIpAddress; + /** + * If polaris enabled. + */ + private Boolean enabled; + /** * polaris namespace. */ private String namespace = "default"; + /** + * polaris service name. + */ + private String service; + @Autowired private Environment environment; @@ -64,11 +75,14 @@ public class PolarisContextProperties { private List modifierList; protected Configuration configuration() { + // 1. Read user-defined polaris.yml configuration ConfigurationImpl configuration = (ConfigurationImpl) ConfigAPIFactory .defaultConfig(ConfigProvider.DEFAULT_CONFIG); - configuration.setDefault(); + + // 2. Override user-defined polaris.yml configuration with SCT configuration String defaultHost = getHost(); configuration.getGlobal().getAPI().setBindIP(defaultHost); + Collection modifiers = modifierList; modifiers = modifiers.stream() .sorted(Comparator.comparingInt(PolarisConfigModifier::getOrder)) @@ -78,6 +92,7 @@ public class PolarisContextProperties { modifier.modify(configuration); } } + return configuration; } @@ -104,6 +119,14 @@ public class PolarisContextProperties { this.localIpAddress = localIpAddress; } + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + public String getNamespace() { return namespace; } @@ -112,4 +135,12 @@ public class PolarisContextProperties { this.namespace = namespace; } + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + } diff --git a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000000000000000000000000000000000000..7bb1d659689f6243d87334ad200570b55fb56ef8 --- /dev/null +++ b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,45 @@ +{ + "groups": [ + { + "name": "spring.cloud.polaris", + "type": "com.tencent.cloud.polaris.context.config.PolarisContextProperties", + "sourceType": "com.tencent.cloud.polaris.context.config.PolarisContextProperties" + } + ], + "properties": [ + { + "name": "spring.cloud.polaris.address", + "type": "java.lang.String", + "description": "polaris server address list that can be separated by \",\"", + "sourceType": "com.tencent.cloud.polaris.context.config.PolarisContextProperties" + }, + { + "name": "spring.cloud.polaris.namespace", + "type": "java.lang.String", + "description": "polaris namespace", + "default": "default", + "sourceType": "com.tencent.cloud.polaris.context.config.PolarisContextProperties" + }, + { + "name": "spring.cloud.polaris.service", + "type": "java.lang.String", + "description": "polaris service name", + "default": "${spring.application.name}", + "sourceType": "com.tencent.cloud.polaris.context.config.PolarisContextProperties" + }, + { + "name": "spring.cloud.polaris.enabled", + "type": "java.lang.Boolean", + "description": "polaris enabled", + "default": "true", + "sourceType": "com.tencent.cloud.polaris.context.config.PolarisContextProperties" + }, + { + "name": "spring.cloud.polaris.local-ip-address", + "type": "java.lang.String", + "defaultValue": "", + "description": "current server local ip address." + } + ], + "hints": [] +} diff --git a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring-configuration-metadata.json b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring-configuration-metadata.json deleted file mode 100644 index 75fbdd4f47164f05fa5eb6637357d43844ef9303..0000000000000000000000000000000000000000 --- a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring-configuration-metadata.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "groups": [ - { - "name": "spring.cloud.polaris", - "type": "com.tencent.cloud.polaris.context.PolarisContextProperties", - "sourceType": "com.tencent.cloud.polaris.context.PolarisContextProperties" - } - ], - "properties": [ - { - "name": "spring.cloud.polaris.address", - "type": "java.lang.String", - "description": "polaris server address list that can be separated by \",\"", - "sourceType": "com.tencent.cloud.polaris.context.PolarisContextProperties" - }, - { - "name": "spring.cloud.polaris.namespace", - "type": "java.lang.String", - "description": "polaris namespace", - "default": "default", - "sourceType": "com.tencent.cloud.polaris.context.PolarisContextProperties" - }, - { - "name": "spring.cloud.polaris.local-ip-address", - "type": "java.lang.String", - "defaultValue": "", - "description": "current server local ip address." - } - ], - "hints": [] -} diff --git a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories index 5b3b188e7895c0fc686970fe556abd062dfc1a9c..723fc1d78b2dc86d82b5adbf7212fc7288c56a71 100644 --- a/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-tencent-polaris-context/src/main/resources/META-INF/spring.factories @@ -1 +1,5 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.tencent.cloud.polaris.context.PolarisContextConfiguration +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration,\ + com.tencent.cloud.polaris.context.config.PolarisContextPostConfiguration +org.springframework.cloud.bootstrap.BootstrapConfiguration=\ + com.tencent.cloud.polaris.context.config.PolarisContextBootstrapAutoConfiguration diff --git a/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/PolarisContextConfigurationTest.java b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/PolarisContextAutoConfigurationTest.java similarity index 87% rename from spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/PolarisContextConfigurationTest.java rename to spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/PolarisContextAutoConfigurationTest.java index d81c849be584709c68dfe32a2c1af40ba207be07..2a926cc12206be7d4f4d4798df95134c1657340c 100644 --- a/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/PolarisContextConfigurationTest.java +++ b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/PolarisContextAutoConfigurationTest.java @@ -17,6 +17,7 @@ package com.tencent.cloud.polaris.context; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; import com.tencent.polaris.client.api.SDKContext; import org.junit.Assert; import org.junit.Test; @@ -28,11 +29,12 @@ import org.springframework.cloud.commons.util.UtilAutoConfiguration; /** * @author liaochuntao */ -public class PolarisContextConfigurationTest { +public class PolarisContextAutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(UtilAutoConfiguration.class)) - .withConfiguration(AutoConfigurations.of(PolarisContextConfiguration.class)) + .withConfiguration( + AutoConfigurations.of(PolarisContextAutoConfiguration.class)) .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:8083"); @Test diff --git a/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/PolarisContextGetHostTest.java b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/PolarisContextGetHostTest.java index be28008e078ad36a747012d12631413199ac671c..9985113df58eb297022378bcc5e5c2c30d6d5e5e 100644 --- a/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/PolarisContextGetHostTest.java +++ b/spring-cloud-tencent-polaris-context/src/test/java/com/tencent/cloud/polaris/context/PolarisContextGetHostTest.java @@ -17,6 +17,8 @@ package com.tencent.cloud.polaris.context; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.polaris.context.config.PolarisContextProperties; import com.tencent.polaris.client.api.SDKContext; import org.junit.Assert; import org.junit.Test; @@ -31,7 +33,7 @@ import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest(classes = PolarisContextApplication.class, properties = { "spring.config.location = classpath:bootstrap.yml" }) -@ImportAutoConfiguration({ PolarisContextConfiguration.class }) +@ImportAutoConfiguration({ PolarisContextAutoConfiguration.class }) public class PolarisContextGetHostTest { @Autowired diff --git a/spring-cloud-tencent-polaris-loadbalancer/pom.xml b/spring-cloud-tencent-polaris-loadbalancer/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..2a26fcd8f35a9d5befa40d08a132b0f9dda3251e --- /dev/null +++ b/spring-cloud-tencent-polaris-loadbalancer/pom.xml @@ -0,0 +1,66 @@ + + + + spring-cloud-tencent + com.tencent.cloud + ${revision} + ../pom.xml + + 4.0.0 + + spring-cloud-tencent-polaris-loadbalancer + Spring Cloud Tencent Polaris LoadBalancer + + + + + com.tencent.cloud + spring-cloud-tencent-polaris-context + + + + + + com.tencent.polaris + polaris-router-factory + + + + com.tencent.polaris + polaris-discovery-api + + + + com.tencent.polaris + polaris-test-common + test + + + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.mockito + mockito-inline + test + + + com.tencent.polaris + polaris-discovery-client + test + + + + + diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..d7295447eaf4ba27980748c88c224699f03e65ac --- /dev/null +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/LoadBalancerUtils.java @@ -0,0 +1,58 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.loadbalancer; + +import java.util.ArrayList; +import java.util.List; + +import com.netflix.loadbalancer.Server; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.pojo.PolarisServer; +import com.tencent.polaris.api.pojo.DefaultServiceInstances; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; + +/** + * load balancer utils. + * + *@author lepdou 2022-05-17 + */ +public class LoadBalancerUtils { + + public static ServiceInstances transferServersToServiceInstances(List servers) { + List instances = new ArrayList<>(servers.size()); + String serviceName = null; + + for (Server server : servers) { + if (server instanceof PolarisServer) { + Instance instance = ((PolarisServer) server).getInstance(); + instances.add(instance); + + if (serviceName == null) { + serviceName = instance.getService(); + } + } + } + + ServiceKey serviceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, serviceName); + + return new DefaultServiceInstances(serviceKey, instances); + } +} diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java new file mode 100644 index 0000000000000000000000000000000000000000..5f83c5aec95d3ee5045618985e945bcb936cdddb --- /dev/null +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancer.java @@ -0,0 +1,140 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.loadbalancer; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.DynamicServerListLoadBalancer; +import com.netflix.loadbalancer.IPing; +import com.netflix.loadbalancer.IRule; +import com.netflix.loadbalancer.PollingServerListUpdater; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import com.tencent.cloud.common.constant.ContextConstant; +import com.tencent.cloud.common.metadata.MetadataContext; +import com.tencent.cloud.common.pojo.PolarisServer; +import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.pojo.DefaultInstance; +import com.tencent.polaris.api.pojo.DefaultServiceInstances; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.GetHealthyInstancesRequest; +import com.tencent.polaris.api.rpc.InstancesResponse; +import org.apache.commons.lang.StringUtils; + +import org.springframework.util.CollectionUtils; + +/** + * Routing load balancer of polaris. + * + * @author Haotian Zhang + */ +public class PolarisLoadBalancer extends DynamicServerListLoadBalancer { + + private final ConsumerAPI consumerAPI; + + private final PolarisLoadBalancerProperties polarisLoadBalancerProperties; + + public PolarisLoadBalancer(IClientConfig config, IRule rule, IPing ping, ServerList serverList, + ConsumerAPI consumerAPI, PolarisLoadBalancerProperties properties) { + super(config, rule, ping, serverList, null, new PollingServerListUpdater()); + this.consumerAPI = consumerAPI; + this.polarisLoadBalancerProperties = properties; + } + + @Override + public List getReachableServers() { + ServiceInstances serviceInstances; + if (polarisLoadBalancerProperties.getDiscoveryType().equals(ContextConstant.POLARIS)) { + serviceInstances = getPolarisDiscoveryServiceInstances(); + } + else { + serviceInstances = getExtendDiscoveryServiceInstances(); + } + + if (serviceInstances == null || CollectionUtils.isEmpty(serviceInstances.getInstances())) { + return Collections.emptyList(); + } + + List servers = new LinkedList<>(); + for (Instance instance : serviceInstances.getInstances()) { + servers.add(new PolarisServer(serviceInstances, instance)); + } + + return servers; + } + + private ServiceInstances getPolarisDiscoveryServiceInstances() { + return getAllInstances(MetadataContext.LOCAL_NAMESPACE, name).toServiceInstances(); + } + + private ServiceInstances getExtendDiscoveryServiceInstances() { + List allServers = super.getAllServers(); + if (CollectionUtils.isEmpty(allServers)) { + return null; + } + ServiceInstances serviceInstances; + if (StringUtils.isBlank(name)) { + throw new IllegalStateException( + "PolarisLoadBalancer only Server with AppName or ServiceIdForDiscovery attribute"); + } + ServiceKey serviceKey = new ServiceKey(MetadataContext.LOCAL_NAMESPACE, name); + List instances = new ArrayList<>(8); + for (Server server : allServers) { + DefaultInstance instance = new DefaultInstance(); + instance.setNamespace(MetadataContext.LOCAL_NAMESPACE); + instance.setService(name); + instance.setHealthy(server.isAlive()); + instance.setProtocol(server.getScheme()); + instance.setId(server.getId()); + instance.setHost(server.getHost()); + instance.setPort(server.getPort()); + instance.setZone(server.getZone()); + instance.setWeight(100); + instances.add(instance); + } + serviceInstances = new DefaultServiceInstances(serviceKey, instances); + return serviceInstances; + } + + @Override + public List getAllServers() { + return getReachableServers(); + } + + /** + * Get a list of instances. + * @param namespace namespace + * @param serviceName service name + * @return list of instances + */ + public InstancesResponse getAllInstances(String namespace, String serviceName) { + GetHealthyInstancesRequest request = new GetHealthyInstancesRequest(); + request.setNamespace(namespace); + request.setService(serviceName); + return consumerAPI.getHealthyInstances(request); + } + +} diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/rule/PolarisWeightedRandomRule.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRule.java similarity index 61% rename from spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/rule/PolarisWeightedRandomRule.java rename to spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRule.java index 4da574cf4b6d66cac30c812713c084bd96418714..124085717ed999289781849e3eeff453abfae347 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/rule/PolarisWeightedRandomRule.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/PolarisWeightedRule.java @@ -13,9 +13,10 @@ * 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.tencent.cloud.polaris.router.rule; +package com.tencent.cloud.polaris.loadbalancer; import java.util.List; @@ -25,49 +26,47 @@ import com.netflix.loadbalancer.Server; import com.tencent.cloud.common.pojo.PolarisServer; import com.tencent.polaris.api.config.consumer.LoadBalanceConfig; import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; import com.tencent.polaris.router.api.core.RouterAPI; import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceRequest; import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; /** - * Weighted random load balance strategy. + * Polaris weighted load balancer. * - * @author Haotian Zhang + *@author lepdou 2022-05-17 */ -public class PolarisWeightedRandomRule extends AbstractLoadBalancerRule { +public class PolarisWeightedRule extends AbstractLoadBalancerRule { - private static final String POLICY = LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM; + private final RouterAPI routerAPI; - @Autowired - private RouterAPI polarisRouter; + public PolarisWeightedRule(RouterAPI routerAPI) { + this.routerAPI = routerAPI; + } @Override public void initWithNiwsConfig(IClientConfig clientConfig) { - } @Override public Server choose(Object key) { - List allServers = getLoadBalancer().getReachableServers(); - if (CollectionUtils.isEmpty(allServers)) { + List servers = getLoadBalancer().getReachableServers(); + if (CollectionUtils.isEmpty(servers)) { return null; } - Server server = allServers.get(0); - if (!(server instanceof PolarisServer)) { - throw new IllegalStateException( - "PolarisDiscoveryRule only support PolarisServer instances"); - } - PolarisServer polarisServer = (PolarisServer) server; + + ServiceInstances serviceInstances = LoadBalancerUtils.transferServersToServiceInstances(servers); + ProcessLoadBalanceRequest request = new ProcessLoadBalanceRequest(); - request.setDstInstances(polarisServer.getServiceInstances()); - request.setLbPolicy(POLICY); - ProcessLoadBalanceResponse processLoadBalanceResponse = polarisRouter - .processLoadBalance(request); + request.setDstInstances(serviceInstances); + request.setLbPolicy(LoadBalanceConfig.LOAD_BALANCE_WEIGHTED_RANDOM); + + ProcessLoadBalanceResponse processLoadBalanceResponse = routerAPI.processLoadBalance(request); + Instance targetInstance = processLoadBalanceResponse.getTargetInstance(); - return new PolarisServer(polarisServer.getServiceInstances(), targetInstance); - } + return new PolarisServer(serviceInstances, targetInstance); + } } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/PolarisRibbonAutoConfiguration.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java similarity index 79% rename from spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/PolarisRibbonAutoConfiguration.java rename to spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java index 5a7469ea38f5df39e6367ae34995a54a4ab4f4c6..6774b548718e24eec1117c1e9fcc8d421aaff850 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/PolarisRibbonAutoConfiguration.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfiguration.java @@ -15,15 +15,15 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.router.config; +package com.tencent.cloud.polaris.loadbalancer.config; +import com.tencent.cloud.polaris.context.ConditionalOnPolarisEnabled; import com.tencent.polaris.api.exception.PolarisException; import com.tencent.polaris.client.api.SDKContext; import com.tencent.polaris.factory.api.RouterAPIFactory; import com.tencent.polaris.router.api.core.RouterAPI; import org.springframework.boot.autoconfigure.AutoConfigureAfter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; @@ -38,22 +38,19 @@ import org.springframework.context.annotation.Configuration; */ @Configuration(proxyBeanMethods = false) @EnableConfigurationProperties -@ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.enabled", - matchIfMissing = true) +@ConditionalOnPolarisEnabled +@ConditionalOnProperty(value = "spring.cloud.polaris.loadbalancer.enabled", matchIfMissing = true) @AutoConfigureAfter(RibbonAutoConfiguration.class) @RibbonClients(defaultConfiguration = PolarisRibbonClientConfiguration.class) -public class PolarisRibbonAutoConfiguration { +public class PolarisLoadBalancerAutoConfiguration { @Bean - @ConditionalOnMissingBean - public PolarisRibbonProperties polarisRibbonProperties() { - return new PolarisRibbonProperties(); + public PolarisLoadBalancerProperties polarisLoadBalancerProperties() { + return new PolarisLoadBalancerProperties(); } - @Bean(name = "polarisRoute") - @ConditionalOnMissingBean - public RouterAPI polarisRouter(SDKContext polarisContext) throws PolarisException { + @Bean + public RouterAPI routerAPI(SDKContext polarisContext) throws PolarisException { return RouterAPIFactory.createRouterAPIByContext(polarisContext); } - } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/PolarisRibbonProperties.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerProperties.java similarity index 61% rename from spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/PolarisRibbonProperties.java rename to spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerProperties.java index db5e14fddaab9047556d390a6bab28f95f040144..9d4d0605998b648d057524f22c1003a23ba7c844 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/PolarisRibbonProperties.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerProperties.java @@ -15,31 +15,35 @@ * specific language governing permissions and limitations under the License. */ -package com.tencent.cloud.polaris.router.config; +package com.tencent.cloud.polaris.loadbalancer.config; + +import com.tencent.cloud.common.constant.ContextConstant; -import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; /** - * Properties of Ribbon. + * Properties of Polaris loadbalancer. * * @author Haotian Zhang */ -@ConfigurationProperties("spring.cloud.polaris.ribbon") -public class PolarisRibbonProperties { +@ConfigurationProperties("spring.cloud.polaris.loadbalancer") +public class PolarisLoadBalancerProperties { /** * If load-balance enabled. */ - @Value("${spring.cloud.polaris.discovery.loadbalancer.enabled:#{true}}") - private Boolean loadbalancerEnabled; + private Boolean enabled = true; /** * Load balance strategy. */ - @Value("${spring.cloud.polaris.loadbalancer.strategy:#{'weightedRandom'}}") private String strategy; + /** + * Type of discovery server. + */ + private String discoveryType = ContextConstant.POLARIS; + public String getStrategy() { return strategy; } @@ -48,17 +52,25 @@ public class PolarisRibbonProperties { this.strategy = strategy; } - public Boolean getLoadbalancerEnabled() { - return loadbalancerEnabled; + public Boolean getEnabled() { + return enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public String getDiscoveryType() { + return discoveryType; } - public void setLoadbalancerEnabled(Boolean loadbalancerEnabled) { - this.loadbalancerEnabled = loadbalancerEnabled; + public void setDiscoveryType(String discoveryType) { + this.discoveryType = discoveryType; } @Override public String toString() { - return "PolarisRibbonProperties{" + "loadbalancerEnabled=" + loadbalancerEnabled + return "PolarisLoadBalancerProperties{" + "loadbalancerEnabled=" + enabled + ", strategy='" + strategy + '\'' + '}'; } diff --git a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/PolarisRibbonClientConfiguration.java b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisRibbonClientConfiguration.java similarity index 56% rename from spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/PolarisRibbonClientConfiguration.java rename to spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisRibbonClientConfiguration.java index 6fa1dc51b81649cd61d083cc1f89c6d6f8f5d0b4..3fbc2c76d9fb9f78bf2ff714e7dbe5c240d552c1 100644 --- a/spring-cloud-starter-tencent-polaris-router/src/main/java/com/tencent/cloud/polaris/router/config/PolarisRibbonClientConfiguration.java +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisRibbonClientConfiguration.java @@ -13,9 +13,10 @@ * 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.tencent.cloud.polaris.router.config; +package com.tencent.cloud.polaris.loadbalancer.config; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.ILoadBalancer; @@ -23,12 +24,9 @@ import com.netflix.loadbalancer.IPing; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.Server; import com.netflix.loadbalancer.ServerList; -import com.tencent.cloud.polaris.router.PolarisRoutingLoadBalancer; -import com.tencent.cloud.polaris.router.rule.PolarisLoadBalanceRule; -import com.tencent.cloud.polaris.router.rule.PolarisWeightedRandomRule; -import com.tencent.polaris.router.api.core.RouterAPI; +import com.tencent.cloud.polaris.loadbalancer.PolarisLoadBalancer; +import com.tencent.polaris.api.core.ConsumerAPI; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -41,23 +39,11 @@ import org.springframework.context.annotation.Configuration; public class PolarisRibbonClientConfiguration { @Bean - @ConditionalOnMissingBean - public IRule polarisRibbonRule(PolarisRibbonProperties polarisRibbonProperties) { - switch (PolarisLoadBalanceRule - .fromStrategy(polarisRibbonProperties.getStrategy())) { - case WEIGHTED_RANDOM_RULE: - default: - return new PolarisWeightedRandomRule(); - } - } - - @Bean - @ConditionalOnMissingBean - public ILoadBalancer polarisRoutingLoadBalancer(IClientConfig iClientConfig, - IRule iRule, IPing iPing, ServerList serverList, - RouterAPI polarisRouter) { - return new PolarisRoutingLoadBalancer(iClientConfig, iRule, iPing, serverList, - polarisRouter); + public ILoadBalancer polarisLoadBalancer(IClientConfig iClientConfig, IRule iRule, + IPing iPing, ServerList serverList, + ConsumerAPI consumerAPI, PolarisLoadBalancerProperties polarisLoadBalancerProperties) { + return new PolarisLoadBalancer(iClientConfig, iRule, iPing, serverList, + consumerAPI, polarisLoadBalancerProperties); } } diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-tencent-polaris-loadbalancer/src/main/resources/META-INF/additional-spring-configuration-metadata.json new file mode 100644 index 0000000000000000000000000000000000000000..5ecb156bc350d6a1b2ed3d0f4555edc31d0a4c4e --- /dev/null +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -0,0 +1,22 @@ +{ + "properties": [ + { + "name": "spring.cloud.polaris.loadbalancer.enabled", + "type": "java.lang.Boolean", + "defaultValue": "true", + "description": "polaris loadbalancer." + }, + { + "name": "spring.cloud.polaris.loadbalancer.discoveryType", + "type": "java.lang.String", + "defaultValue": "POLARIS", + "description": "Type of discovery server." + }, + { + "name": "spring.cloud.polaris.loadbalancer.strategy", + "type": "java.lang.String", + "defaultValue": "random", + "description": "retry,best_available,availability_filtering,round_robin,weighted_response_time,zone_avoidance,random,consistent_hash,weighted_random." + } + ] +} diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/main/resources/META-INF/spring.factories b/spring-cloud-tencent-polaris-loadbalancer/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000000000000000000000000000000000000..a13924bacd59eb7862e0dcc8ef9c6bad3920aeda --- /dev/null +++ b/spring-cloud-tencent-polaris-loadbalancer/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ + com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerAutoConfiguration diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerTest.java b/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e9d8580d3bd01ee2a47bae73df51572ecbd8a0fe --- /dev/null +++ b/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/PolarisLoadBalancerTest.java @@ -0,0 +1,173 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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. + */ +// CHECKSTYLE:OFF + +package com.tencent.cloud.polaris.loadbalancer; + + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.netflix.client.config.DefaultClientConfigImpl; +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.DummyPing; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import com.tencent.cloud.common.util.ApplicationContextAwareUtils; +import com.tencent.cloud.polaris.loadbalancer.config.PolarisLoadBalancerProperties; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.api.pojo.DefaultInstance; +import com.tencent.polaris.api.pojo.DefaultServiceInstances; +import com.tencent.polaris.api.pojo.Instance; +import com.tencent.polaris.api.pojo.ServiceInstances; +import com.tencent.polaris.api.pojo.ServiceKey; +import com.tencent.polaris.api.rpc.InstancesResponse; +import com.tencent.polaris.router.api.core.RouterAPI; +import com.tencent.polaris.router.api.rpc.ProcessLoadBalanceResponse; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import org.springframework.cloud.netflix.ribbon.StaticServerList; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + + +/** + * Test for {@link PolarisLoadBalancer}. + * + * @author lapple.lei 2022-06-28 + */ +@RunWith(MockitoJUnitRunner.class) +public class PolarisLoadBalancerTest { + private static final String CLIENT_NAME = "polaris-test-server"; + private static final String NS = "testNamespace"; + private static final String[] HOST_LIST = new String[] { + "127.0.0.1", + "127.0.0.2", + "127.0.0.3", + "127.0.0.4", + "127.0.0.5", + }; + + @Mock + private RouterAPI routerAPI; + @Mock + private ConsumerAPI consumerAPI; + + @Test + public void testPolarisLoadBalancer() { + //mock consumerAPI + when(consumerAPI.getHealthyInstances(any())).thenReturn(this.assembleInstanceResp()); + + //mock routerAPI for rule + when(routerAPI.processLoadBalance(any())).thenReturn(assembleProcessLoadBalanceResp()); + PolarisWeightedRule rule = new PolarisWeightedRule(routerAPI); + + // clientConfig + IClientConfig config = new DefaultClientConfigImpl(); + config.loadProperties(CLIENT_NAME); + + //mock for MetadataContext + try (MockedStatic mockedCtxUtils = Mockito + .mockStatic(ApplicationContextAwareUtils.class)) { + mockedCtxUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.namespace")) + .thenReturn(NS); + mockedCtxUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.service")) + .thenReturn("TestServer"); + + PolarisLoadBalancerProperties properties = new PolarisLoadBalancerProperties(); + ServerList emptyServerList = new StaticServerList<>(); + + PolarisLoadBalancer balancer = new PolarisLoadBalancer(config, rule, new DummyPing(), emptyServerList, + consumerAPI, properties); + + String host = balancer.choose(null); + + Assert.assertNotNull(host); + Assert.assertEquals("127.0.0.1:8080", host); + } + } + + @Test + public void testExtendDiscoveryServiceInstance() { + //mock routerAPI for rule + when(routerAPI.processLoadBalance(any())).thenReturn(assembleProcessLoadBalanceResp()); + PolarisWeightedRule rule = new PolarisWeightedRule(routerAPI); + + // clientConfig + IClientConfig config = new DefaultClientConfigImpl(); + config.loadProperties(CLIENT_NAME); + + //mock for MetadataContext + try (MockedStatic mockedCtxUtils = Mockito + .mockStatic(ApplicationContextAwareUtils.class)) { + + mockedCtxUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.namespace")) + .thenReturn(NS); + mockedCtxUtils.when(() -> ApplicationContextAwareUtils.getProperties("spring.cloud.polaris.service")) + .thenReturn("TestServer"); + + PolarisLoadBalancerProperties properties = new PolarisLoadBalancerProperties(); + properties.setDiscoveryType("TEST"); + ServerList staticServerList = assembleServerList(); + + PolarisLoadBalancer balancer = new PolarisLoadBalancer(config, rule, new DummyPing(), staticServerList, + consumerAPI, properties); + + String host = balancer.choose(null); + Assert.assertEquals("127.0.0.1:8080", host); + } + } + + private ServerList assembleServerList() { + return new StaticServerList<>(Stream.of(HOST_LIST).map(this::convertServer).toArray(Server[]::new)); + } + + private ProcessLoadBalanceResponse assembleProcessLoadBalanceResp() { + ServiceInstances serviceInstances = assembleServiceInstances(); + return new ProcessLoadBalanceResponse(serviceInstances.getInstances().get(0)); + } + + private InstancesResponse assembleInstanceResp() { + return new InstancesResponse(assembleServiceInstances()); + } + + private ServiceInstances assembleServiceInstances() { + ServiceKey serviceKey = new ServiceKey(NS, CLIENT_NAME); + + List instances = Stream.of(HOST_LIST).map(this::convertInstance).collect(Collectors.toList()); + return new DefaultServiceInstances(serviceKey, instances); + } + + private Instance convertInstance(String host) { + DefaultInstance instance = new DefaultInstance(); + instance.setHost(host); + instance.setPort(8080); + return instance; + } + + private Server convertServer(String host) { + return new Server("http", host, 8080); + } +} diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfigurationTest.java b/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d1111dd02f347bfe5d103a870eee06bdc9919fea --- /dev/null +++ b/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisLoadBalancerAutoConfigurationTest.java @@ -0,0 +1,119 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.loadbalancer.config; + +import com.netflix.client.config.DefaultClientConfigImpl; +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.IPing; +import com.netflix.loadbalancer.IRule; +import com.netflix.loadbalancer.NoOpPing; +import com.netflix.loadbalancer.RandomRule; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import com.tencent.cloud.polaris.context.config.PolarisContextAutoConfiguration; +import com.tencent.cloud.polaris.loadbalancer.PolarisLoadBalancer; +import com.tencent.polaris.api.core.ConsumerAPI; +import com.tencent.polaris.client.api.SDKContext; +import com.tencent.polaris.discovery.client.api.DefaultConsumerAPI; +import com.tencent.polaris.router.api.core.RouterAPI; +import org.junit.Test; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.cloud.netflix.ribbon.StaticServerList; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static com.tencent.polaris.test.common.Consts.PORT; +import static com.tencent.polaris.test.common.Consts.SERVICE_PROVIDER; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisLoadBalancerAutoConfiguration}. + * + * @author Haotian Zhang + */ +public class PolarisLoadBalancerAutoConfigurationTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(PolarisRibbonTest.class, + PolarisLoadBalancerAutoConfiguration.class, + PolarisContextAutoConfiguration.class, + RibbonAutoConfiguration.class)) + .withPropertyValues("spring.application.name=" + SERVICE_PROVIDER) + .withPropertyValues("server.port=" + PORT) + .withPropertyValues("spring.cloud.polaris.address=grpc://127.0.0.1:10081"); + + @Test + public void testDefaultInitialization() { + this.contextRunner.run(context -> { + assertThat(context).hasSingleBean(RouterAPI.class); + assertThat(context).hasSingleBean(PolarisLoadBalancerProperties.class); + assertThat(hasSinglePolarisLoadBalancer(context)).isTrue(); + }); + } + + private boolean hasSinglePolarisLoadBalancer(BeanFactory beanFactory) { + SpringClientFactory contextBean = beanFactory.getBean(SpringClientFactory.class); + ILoadBalancer loadBalancer = contextBean.getLoadBalancer(SERVICE_PROVIDER); + return loadBalancer instanceof PolarisLoadBalancer; + } + + @Configuration + @EnableAutoConfiguration + static class PolarisRibbonTest { + + @Autowired + private SDKContext sdkContext; + + @Bean + public IClientConfig iClientConfig() { + DefaultClientConfigImpl config = new DefaultClientConfigImpl(); + config.setClientName(SERVICE_PROVIDER); + return config; + } + + @Bean + public IRule iRule() { + return new RandomRule(); + } + + @Bean + public IPing iPing() { + return new NoOpPing(); + } + + @Bean + public ServerList serverList() { + return new StaticServerList<>(); + } + + @Bean + public ConsumerAPI consumerAPI() { + return new DefaultConsumerAPI(sdkContext); + } + + } + +} diff --git a/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisRibbonClientConfigurationTest.java b/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisRibbonClientConfigurationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d514af6db6de05024e520eac4dc9ad3552c105f5 --- /dev/null +++ b/spring-cloud-tencent-polaris-loadbalancer/src/test/java/com/tencent/cloud/polaris/loadbalancer/config/PolarisRibbonClientConfigurationTest.java @@ -0,0 +1,55 @@ +/* + * Tencent is pleased to support the open source community by making Spring Cloud Tencent available. + * + * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. + * + * Licensed under the BSD 3-Clause License (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + * + * 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.tencent.cloud.polaris.loadbalancer.config; + +import com.tencent.cloud.polaris.loadbalancer.PolarisLoadBalancer; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Test for {@link PolarisRibbonClientConfiguration}. + * + * @author wlx + * @date 2022/7/2 10:36 上午 + */ +public class PolarisRibbonClientConfigurationTest { + + private final ApplicationContextRunner applicationContextRunner = new ApplicationContextRunner(); + + @Test + public void testDefaultInitialization() { + this.applicationContextRunner + .withConfiguration(AutoConfigurations.of( + TestApplication.class, + PolarisRibbonClientConfiguration.class)) + .run(context -> { + assertThat(context).hasSingleBean(PolarisRibbonClientConfiguration.class); + assertThat(context).hasSingleBean(PolarisLoadBalancer.class); + }); + } + + @SpringBootApplication + static class TestApplication { + } + +} diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml index 5993108df66e390bfd861ece9269bccf58afc0c9..ace6b329d6608540e838acfc4e729d5bfcb2b112 100644 --- a/src/checkstyle/checkstyle-suppressions.xml +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -4,4 +4,6 @@ "https://www.puppycrawl.com/dtds/suppressions_1_1.dtd"> + + \ No newline at end of file