diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000000..185873ad02a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,33 @@ +name: Bug Report +description: Create a report to help us improve +title: "[BUG] - Issue Title" +labels: bug + +body: + - type: markdown + attributes: + value: "## Describe the bug\nA clear and concise description of what the bug is." + + - type: textarea + attributes: + label: Steps to Reproduce + description: "List the steps to reproduce the bug." + placeholder: "1. Go to '...'\n2. Click on '...'\n3. See error" + + - type: textarea + attributes: + label: Expected Behavior + description: "Describe the behavior you expected to see." + placeholder: "A clear and concise description of what you expected to happen." + + - type: input + attributes: + label: Servicecomb Version + description: "Please provide your Servicecomb version" + placeholder: "3.2.4" + + - type: textarea + attributes: + label: Additional Context + description: "Add any other context about the problem here." + placeholder: "Any additional information that might help us debug the issue." diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..16fd2512ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,18 @@ +blank_issues_enabled: false +contact_links: + - name: "Contact Support" + url: "mailto:dev@servicecomb.apache.org" + about: "If you need help, please contact us." +templates: + - name: "Bug Report" + description: "Report a bug or issue." + filename: "bug_report.yml" + - name: "Feature Request" + description: "Request a new feature or improvement." + filename: "feature_request.yml" + - name: "Documentation Issue" + description: "Report an issue with documentation." + filename: "documentation_issue.yml" + - name: "Question" + description: "Ask a question or seek help." + filename: "question.yml" diff --git a/.github/ISSUE_TEMPLATE/documentation_issue.yml b/.github/ISSUE_TEMPLATE/documentation_issue.yml new file mode 100644 index 00000000000..94ef3490041 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_issue.yml @@ -0,0 +1,27 @@ +name: Documentation Issue +description: Report an issue with the documentation +title: "[DOCUMENTATION] - Documentation Issue Title" +labels: documentation + +body: + - type: markdown + attributes: + value: "## Describe the documentation issue\nA clear and concise description of the issue with the documentation." + + - type: textarea + attributes: + label: Location in documentation + description: "Please provide the URL or section where the issue appears." + placeholder: "e.g. https://docs.example.com/section" + + - type: textarea + attributes: + label: What should be changed? + description: "Please describe what changes or improvements should be made to the documentation." + placeholder: "Explain how we can improve the content." + + - type: textarea + attributes: + label: Additional Context + description: "Provide any other context or examples here." + placeholder: "Additional comments or suggestions for the documentation." diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000000..3a44565e0b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,33 @@ +name: Feature Request +description: Suggest an idea or feature for this project +title: "[FEATURE] - Feature Title" +labels: enhancement + +body: + - type: markdown + attributes: + value: "## Describe the feature request\nA clear and concise description of what you want to be added to the project." + + - type: textarea + attributes: + label: Problem this feature would solve + description: "Please describe the problem or challenge you're facing." + placeholder: "Explain why this feature would be helpful." + + - type: textarea + attributes: + label: Describe the solution + description: "How do you envision this feature working?" + placeholder: "Provide a description of how the feature would solve the problem." + + - type: textarea + attributes: + label: Alternatives considered + description: "Have you considered other solutions or alternatives? If yes, describe them." + placeholder: "Provide any alternative solutions you've thought about." + + - type: textarea + attributes: + label: Additional Context + description: "Any other context or information that might help us implement this feature." + placeholder: "Include any links, mockups, or additional details." diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 00000000000..af3f350b518 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,27 @@ +name: General Question +description: Ask a general question related to the project +title: "[QUESTION] - Question Title" +labels: question + +body: + - type: markdown + attributes: + value: "## What is your question?\nDescribe the question or issue you are facing. Be as specific as possible." + + - type: textarea + attributes: + label: Steps to reproduce (if applicable) + description: "If your question is about a specific problem or bug, please include steps to reproduce it." + placeholder: "Provide steps, if applicable." + + - type: textarea + attributes: + label: What have you tried so far? + description: "Please explain what you have already tried to resolve the issue or answer the question." + placeholder: "Describe the steps you've taken." + + - type: textarea + attributes: + label: Additional context + description: "Provide any other context or information related to the question." + placeholder: "Include any relevant links or background information." diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b8f6e71dbe3..7f7754fc299 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -21,44 +21,9 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "monthly" - package-ecosystem: "maven" directory: "/" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 20 - ignore: - - dependency-name: "asciidoctorj" - versions: - - "2.x" - - dependency-name: "hibernate-validator" - versions: - - "8.x" - # asciidoctorj new version needs higher swagger2markup, which will need java11 - - dependency-name: "jakarta.activation" - versions: - - "2.x" - - dependency-name: "jakarta.servlet-api" - versions: - - "5.x" - - "6.x" - - dependency-name: "jakarta.ws.rs-api" - versions: - - "3.x" - - dependency-name: "jersey" - versions: - - "3.x" - - dependency-name: "jersey-client" - versions: - - "3.x" - - dependency-name: "jersey-common" - versions: - - "3.x" - - dependency-name: "logback-classic" - # logback new versions needs java11 now - - dependency-name: "logback-core" - # logback new versions needs java11 now - - dependency-name: "protostuff-parser" - # protostuff-parser new versions needs java11 now - - dependency-name: "swagger2markup" - # swagger2markup new version needs java11 now diff --git a/.github/workflows/checkstyle.yml b/.github/workflows/checkstyle.yml index fb1e00c9881..3f97b9f1818 100644 --- a/.github/workflows/checkstyle.yml +++ b/.github/workflows/checkstyle.yml @@ -26,13 +26,13 @@ on: jobs: checkstyle: timeout-minutes: 60 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up jdk - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' - name: checkstyle - run: mvn -B -Pit install -DskipTests -Dspotbugs.skip=true checkstyle:check + run: mvn checkstyle:check -B -Pit diff --git a/.github/workflows/spotbugs.yml b/.github/workflows/linelint.yml similarity index 73% rename from .github/workflows/spotbugs.yml rename to .github/workflows/linelint.yml index ff68dcf4966..8d5112ea3d6 100644 --- a/.github/workflows/spotbugs.yml +++ b/.github/workflows/linelint.yml @@ -17,22 +17,19 @@ # under the License. # -name: spot bugs +name: line lint on: pull_request: branches: - master jobs: - spotbugs: + linelint: timeout-minutes: 60 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 - - name: Set up jdk - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - name: spotbugs - run: mvn -B -Pit -DskipTests clean verify spotbugs:spotbugs + - uses: actions/checkout@v6 + - name: install linelint + run: cargo install linelint-cli + - name: Run linelint check + run: linelint check diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 9f1c414091d..24118c35c99 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -29,22 +29,24 @@ jobs: build: timeout-minutes: 60 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up jdk - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: - java-version: '17' + java-version: '17.0.8' distribution: 'temurin' - - uses: actions/cache@v3 + - name: Set up Maven + uses: stCarolas/setup-maven@v5 + with: + maven-version: 3.9.9 + - uses: actions/cache@v5 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: Compilation and Installation - run: mvn clean install -Dcheckstyle.skip -Dspotbugs.skip=true -B -Pdocker -Pjacoco -Pit -Pcoverage - - name: publish coverage report - run: bash <(curl -s https://codecov.io/bash) + run: mvn clean verify -Dcheckstyle.skip=true -B -Pdocker -Pjacoco -Pit -Pcoverage diff --git a/.github/workflows/rat_check.yml b/.github/workflows/rat_check.yml index bbdf9066c23..c2db2508fea 100644 --- a/.github/workflows/rat_check.yml +++ b/.github/workflows/rat_check.yml @@ -29,11 +29,11 @@ on: jobs: rat_check: timeout-minutes: 60 - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - name: Set up jdk - uses: actions/setup-java@v3 + uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' diff --git a/.github/workflows/typo_check.yml b/.github/workflows/typo_check.yml new file mode 100644 index 00000000000..999530a79f7 --- /dev/null +++ b/.github/workflows/typo_check.yml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +name: typo check +on: + pull_request: + branches: + - master + +jobs: + typo-check: + timeout-minutes: 60 + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v6 + # To run the typo check locally, you can follow these steps: + # 1. Install typos locally using cargo: + # cargo install typos-cli + # 2. Run the typo check with the following command: + # typos + - name: Check typos + uses: crate-ci/typos@v1.38.1 diff --git a/.typos.toml b/.typos.toml new file mode 100644 index 00000000000..e8b6eb33f39 --- /dev/null +++ b/.typos.toml @@ -0,0 +1,38 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +[default.extend-words] +"fo" = "fo" +"VERTX" = "VERTX" +"Vertx" = "Vertx" +"vertx" = "vertx" + +[files] +extend-exclude = [ + "**/cobertura.ser" +] +[default] +extend-ignore-words-re = [ + "Verticle", + "verticle", + "VERTICLE", + "Prelease", + "cobertura", + "ser", +] diff --git a/LICENSE b/LICENSE index 3394da99ceb..7898c758729 100644 --- a/LICENSE +++ b/LICENSE @@ -238,4 +238,11 @@ For foundations/foundation-protobuf/src/main/java/io/protostuff/runtime/ArrayFie foundations/foundation-protobuf/src/main/java/io/protostuff/SchemaWriter.java ================================================================ This product bundles files from protostuff which is licensed under the Apache License v2. -For details, see https://github.com/protostuff/protostuff \ No newline at end of file +For details, see https://github.com/protostuff/protostuff + +================================================================ +For foundations/foundation-protobuf/src/main/resources/google/protobuf/any.proto +For foundations/foundation-protobuf/src/main/resources/google/protobuf/empty.proto +================================================================ +This product bundles files from swagger which is licensed under the BSD-3-Clause. +For details, see https://github.com/protocolbuffers/protobuf diff --git a/NOTICE b/NOTICE index d4848d021c3..e4df59ae99a 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache ServiceComb Java Chassis -Copyright 2017-2022 The Apache Software Foundation +Copyright 2017-2025 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/README.md b/README.md index 673d3b0072a..b2fbc167625 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,87 @@ # Java Chassis [中文](README_ZH.md) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.servicecomb/java-chassis-core/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Corg.apache.servicecomb) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) -Apache ServiceComb Java Chassis is a Software Development Kit (SDK) for rapid development of microservices in Java, providing service registration, service discovery, dynamic routing, and service management features +Apache ServiceComb Java Chassis is a Software Development Kit (SDK) for rapid development of microservices in Java, providing service registration, service discovery, dynamic routing, and service management features. -# releases - -| Release Train | Latest Version | Compiled JDK Version | Tested JDK Version | -|---------------|----------------|----------------------|-------------------------------| -| 2.x.x | 2.8.3 | OpenJDK 8 | OpenJDK 8, 11, 17 | -| 1.x.x | 1.3.10 | OpenJDK 8 | OpenJDK 8 | - -# Why use Java Chassis +> If you find this project helpful, please don't forget to `star` it. -- **High performance** +# releases - The transport capability of Java Chassis is based on [Vert.x](https://vertx.io), which enables Java Chassis to process - massive requests with relatively less hardware resources, and support [reactive develop style](https://www.reactivemanifesto.org). +| Release Train | Latest Version | Compiled JDK Version | Tested JDK Version | Open API | Notes | +|----------------|----------------|----------------------|--------------------|----------|--------------------------| +| Java Chassis 3 | 3.3.0 | OpenJDK 17 | OpenJDK 17 | 3.0.x | Depends on Spring Boot 3 | +| Java Chassis 2 | 2.8.24 | OpenJDK 8 | OpenJDK 8, 11, 17 | 2.0.x | Depends on Spring 5 | +| Java Chassis 1 | 1.3.11 | OpenJDK 8 | OpenJDK 8 | 2.0.x | End of Support | -- **Native support for OpenAPI** +Java Chassis core dependencies - Java Chassis describes the APIs of the microservices via [Swagger](https://swagger.io) natively, to help - developers to design microservices that comply to [OpenAPI standard](https://swagger.io/specification/v2/). +| Java Chassis | Spring Boot | Vert.x | Swagger | Jackson | +|--------------|-------------|--------|---------|---------| +| 3.3.x | 3.4.x | 4.5.x | 2.2.x | 2.18.x | +| 3.2.x | 3.3.x | 4.5.x | 2.2.x | 2.18.x | -- **Flexible develop style** +> NOTICE: Since Open API 3.0.x is not compatible with 2.0.x, Java Chassis 2 and Java Chassis 1 can not +> work together with Java Chassis 3. All related consumers, providers and edge service need use Java Chassis 3 when upgrading. - Currently Java Chassis allow developers to develop their microservice APIs in `SpringMVC`/`JAX-RS`/`transparent RPC` styles, - and to send the request in `RPC`/`RestTemplate` styles. And there are three kind of build-in transport mode: - `Rest over Vertx`/`Rest over Servlet`/`Highway`. All of these features can be combined and replaced easily, - because they are decoupled and all based on the Swagger schema, which can provide high flexibility. +> NOTICE: Java Chassis 1 reached its end of support now after it's first release from 2018. -- **Out-of-box microservice governance features** +# Quick Start - Java Chassis provides a lot of features for microservice governance and monitor. +* Define API +```java +@RequestMapping(path = "/provider") +public interface ProviderService { + @GetMapping("/sayHello") + String sayHello(@RequestParam("name") String name); +} +``` -# Quick Start +* Provider service +```java +@RestSchema(schemaId = "ProviderController", schemaInterface = ProviderService.class) +public class ProviderController implements ProviderService { + @Override + public String sayHello(String name) { + return "Hello " + name; + } +} +``` -Provider service: +* Consumer service ```java -import org.apache.servicecomb.*; -@RpcSchema(schemaId = "helloworld") -public class HelloWorldProvider implements HelloWorld { - public String sayHello(String name) { - return "Hello " + name; - } +@Configuration +public class ProviderServiceConfiguration { + @Bean + public ProviderService providerService() { + return Invoker.createProxy("provider", "ProviderController", ProviderService.class); + } } ``` -Consumer service: +Invoke Provider service with RPC ```java -import org.apache.servicecomb.*; -@Component -public class HelloWorldConsumer { - @RpcReference(microserviceName = "pojo", schemaId = "helloworld") - private static HelloWorld helloWorld; - - public static void main(String[] args) { - helloWorld.sayHello("Tank"); - } +@RestSchema(schemaId = "ConsumerController", schemaInterface = ConsumerService.class) +public class ConsumerController implements ConsumerService { + private ProviderService providerService; + + @Autowired + public void setProviderService(ProviderService providerService) { + this.providerService = providerService; + } + + @Override + public String sayHello(String name) { + return providerService.sayHello(name); + } } ``` +Try out this example [here](https://servicecomb.apache.org/references/java-chassis/zh_CN/start/first-sample.html) . + # Documentation Project documentation is available on the [ServiceComb Java Chassis Developer Guide][java-chassis-developer-guide]. -[java-chassis-developer-guide]: https://servicecomb.apache.org/references/java-chassis/en_US/ +[java-chassis-developer-guide]: https://servicecomb.apache.org/references/java-chassis/zh_CN/ # Building @@ -96,13 +114,3 @@ See [CONTRIBUTING](http://servicecomb.apache.org/developers/contributing) for de # License Licensed under an [Apache 2.0 license](LICENSE). - -# Export Notice - -This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. See for more information. - -The Apache Software Foundation has classified this software as Export Commodity Control Number (ECCN) 5D002, which includes information security software using or performing cryptographic functions with asymmetric algorithms. The form and manner of this Apache Software Foundation distribution makes it eligible for export under the "publicly available" Section 742.15(b) exemption (see the BIS Export Administration Regulations, Section 742.15(b)) for both object code and source code. - -The following provides more details on the included cryptographic software: - - * Vertx transport can be configured for secure communications diff --git a/README_ZH.md b/README_ZH.md index 19706c5e19e..bfae9746c2b 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -1,77 +1,102 @@ -# Java Chassis | [English](README.md) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.servicecomb/java-chassis-core/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Corg.apache.servicecomb) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) +# Java Chassis | [English](README.md) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.apache.servicecomb/java-chassis-core/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Corg.apache.servicecomb) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) Apache ServiceComb Java Chassis 给开发者提供一个快速构建微服务的JAVA SDK。它包含如下特性: -* 多种开发风格,REST(JAX-RS、Spring MVC)和RPC -* 多种通信协议, HTTP over Vert.x、Http Over Servlet、Highway等 -* 统一一致的服务提供者、服务消费者处理链,以及基于契约的开箱即用的服务治理能力 +* 基于Open API的契约优先(API First)开发模式,满足开发过程可管理、开发规范可验证要求。 +* 多种开发风格,REST(JAX-RS、Spring MVC)和RPC等,高效支持遗留系统迁移和新系统开发场景。 +* 多种通信协议, HTTP over Vert.x、Http Over Servlet、Highway等,满足不同场景对于性能、韧性的需求。 +* 统一一致的服务提供者、服务消费者处理链,易于扩展新功能。 +* 提供服务发现、配置管理、熔断、限流、灰度发布等开箱即用的服务治理能力。 -开发者可以通过[设计选型参考][design]了解更多特性和设计原理。 +可以通过[设计选型参考](https://servicecomb.apache.org/references/java-chassis/zh_CN/start/design.html) 了解更多特性和设计原理。 -[design]: https://servicecomb.apache.org/references/java-chassis/zh_CN/start/design.html +> 如果发现项目能帮助到您,别忘了点击右上角`star`表示鼓励。 -# releases +# 发布版本 -| 版本火车 | 最新版本 | 编译的JDK版本 | 支持的JDK版本 | -|-------|--------|-----------|-------------------| -| 2.x.x | 2.8.3 | OpenJDK 8 | OpenJDK 8, 11, 17 | -| 1.x.x | 1.3.10 | OpenJDK 8 | OpenJDK 8 | +| 版本火车 | 最新版本 | 编译的JDK版本 | 支持的JDK版本 | Open API | 备注 | +|----------------|--------|------------|-------------------|----------|-----------------| +| Java Chassis 3 | 3.3.0 | OpenJDK 17 | OpenJDK 17 | 3.0.x | 依赖Spring Boot 3 | +| Java Chassis 2 | 2.8.24 | OpenJDK 8 | OpenJDK 8, 11, 17 | 2.0.x | 依赖Spring 5 | +| Java Chassis 1 | 1.3.11 | OpenJDK 8 | OpenJDK 8 | 2.0.x | 停止更新 | -# 为什么使用Java Chassis +Java Chassis 的核心依赖 -- **高性能** +| Java Chassis | Spring Boot | Vert.x | Swagger | Jackson | +|--------------|-------------|--------|---------|---------| +| 3.3.x | 3.4.x | 4.5.x | 2.2.x | 2.18.x | +| 3.2.x | 3.3.x | 4.5.x | 2.2.x | 2.18.x | - Java Chassis 网络层基于 [Vert.x](https://vertx.io) 实现, 支持开发者使用[响应式编程](https://www.reactivemanifesto.org), 开发者在使用熟悉的REST风格设计业务接口的时候,也能够获取到非常高性能的吞吐量。同时还提供了Highway协议,满足更高性能场景的要求。 +> NOTICE: Open API 3.0.x 不兼容 2.0.x, 因此Java Chassis 2、Java Chassis 1不能与Java Chassis 3共存互访. 升级Java Chassis 3, 需要将相关的消费者、提供者和边缘服务同时升级. -- **原生支持OpenAPI** +> NOTICE: Java Chassis 1 第一个版本于2018发布,已经停止更新. - Java Chassis 的接口开发、服务治理都基于 [Swagger](https://swagger.io) ,并通过接口语义检查,使得接口定义符合 [OpenAPI 规范](https://swagger.io/specification/v2/). - -- **灵活的开发方式** +# 快速开始 - 开发者可以使用 `SpringMVC`/`JAX-RS`/`transparent RPC` 任意一种方式定义服务端接口, 并使用`RPC`/`RestTemplate` 等方式访问这些接口. 得益于Java Chassis的通信层与开发方式分离的设计,开发者可以在 `Rest over Vertx`/`Rest over Servlet`/`Highway`等通信模式下自由切换. +* 定义服务契约 -- **开箱即用的服务治理能力** +```java +@RequestMapping(path = "/provider") +public interface ProviderService { + @GetMapping("/sayHello") + String sayHello(@RequestParam("name") String name); +} +``` - Java Chassis 提供了大量开箱即用的服务治理能力,包括服务发现、熔断容错、负载均衡、流量控制等。 +* 定义提供者 +```java +@RestSchema(schemaId = "ProviderController", schemaInterface = ProviderService.class) +public class ProviderController implements ProviderService { + @Override + public String sayHello(String name) { + return "Hello " + name; + } +} +``` -# 快速开始 +* 定义消费者 -定义提供者: ```java -import org.apache.servicecomb.*; -@RpcSchema(schemaId = "helloworld") -public class HelloWorldProvider implements HelloWorld { - public String sayHello(String name) { - return "Hello " + name; - } +@Configuration +public class ProviderServiceConfiguration { + @Bean + public ProviderService providerService() { + return Invoker.createProxy("provider", "ProviderController", ProviderService.class); + } } ``` -定义消费者: +使用RPC方式访问提供者。 + ```java -import org.apache.servicecomb.*; -@Component -public class HelloWorldConsumer { - @RpcReference(microserviceName = "pojo", schemaId = "helloworld") - private static HelloWorld helloWorld; - - public static void main(String[] args) { - helloWorld.sayHello("Tank"); - } +@RestSchema(schemaId = "ConsumerController", schemaInterface = ConsumerService.class) +public class ConsumerController implements ConsumerService { + private ProviderService providerService; + + @Autowired + public void setProviderService(ProviderService providerService) { + this.providerService = providerService; + } + + @Override + public String sayHello(String name) { + return providerService.sayHello(name); + } } ``` +下载并体验上述[示例项目](https://servicecomb.apache.org/references/java-chassis/zh_CN/start/first-sample.html) . + # 用户文档 请访问 [ServiceComb Java Chassis 开发指南][java-chassis-developer-guide]. [java-chassis-developer-guide]: https://servicecomb.apache.org/references/java-chassis/zh_CN/ -# 编译Java Chassis +# 编译 Java Chassis -开发者可以通过公有mavan仓库使用Java Chassis。 如果需要构建项目,需要使用JDK 8版本,并预先安装maven。 +开发者可以通过MAVEN仓库使用Java Chassis。 如果需要构建项目,需要使用JDK 17版本,并预先安装maven。 mvn clean install @@ -97,13 +122,3 @@ public class HelloWorldConsumer { # License Licensed under an [Apache 2.0 license](LICENSE). - -# Export Notice - -This distribution includes cryptographic software. The country in which you currently reside may have restrictions on the import, possession, use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check your country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. See for more information. - -The Apache Software Foundation has classified this software as Export Commodity Control Number (ECCN) 5D002, which includes information security software using or performing cryptographic functions with asymmetric algorithms. The form and manner of this Apache Software Foundation distribution makes it eligible for export under the "publicly available" Section 742.15(b) exemption (see the BIS Export Administration Regulations, Section 742.15(b)) for both object code and source code. - -The following provides more details on the included cryptographic software: - - * Vertx transport can be configured for secure communications diff --git a/ci/README.md b/ci/README.md new file mode 100644 index 00000000000..08c14d23741 --- /dev/null +++ b/ci/README.md @@ -0,0 +1,25 @@ +# Java Chassis Code Checks + +* Compilation and Installation + + see .github/workflows/maven.yml + +* Checkstyle + + see .github/workflows/checkstyle.yml + +* Rat Check + + see .github/workflows/rat_check.yml + +* Spot Bugs + + see .github/workflows/spotbugs.yml + +* OWASP Dependency Check + + `mvn verify -Powasp-dependency-check` . Very Slow, run manually. + +* Distribution + + `mvn clean deploy -Dcheckstyle.skip -Dspotbugs.skip=true -Dmaven.javadoc.skip=true -DskipTests -Prelease -Pdistribution` . Run manually when preparing a release. diff --git a/ci/spotbugs/exclude.xml b/ci/spotbugs/exclude.xml index b59b0b113a2..b7c53996e85 100644 --- a/ci/spotbugs/exclude.xml +++ b/ci/spotbugs/exclude.xml @@ -35,6 +35,11 @@ + + + + + @@ -186,6 +191,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/clients/README.md b/clients/README.md index bdaaafb125d..53180a4a03b 100644 --- a/clients/README.md +++ b/clients/README.md @@ -1,14 +1,13 @@ # About This module implements common http clients for servicecomb-service-center, servicecomb-kie and other -3rd-party services. +3rd-party services. + +This module is independent on servicecomb-java-chassis, and can be used in many other projects like +Spring Cloud, Dubbo, etc. -This module is independent on servicecomb-java-chassis, and can be used in many other projects like -Spring Cloud, Dubbo, etc. - # 关于 -这个模块给 servicecomb-service-center, servicecomb-kie 以及其他第三方服务实现通用的 Http Client。 +这个模块给 servicecomb-service-center, servicecomb-kie 以及其他第三方服务实现通用的 Http Client。 这个模块独立于 servicecomb-java-chassis, 可以用于 Spring Cloud, Dubbo 等项目。 - diff --git a/clients/config-center-client/pom.xml b/clients/config-center-client/pom.xml index 2de6ce378f7..e7e2fa1fc2b 100644 --- a/clients/config-center-client/pom.xml +++ b/clients/config-center-client/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManager.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManager.java index 3b346153a0b..977c84ef63f 100644 --- a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManager.java +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterAddressManager.java @@ -28,8 +28,9 @@ public class ConfigCenterAddressManager extends AbstractAddressManager { - public ConfigCenterAddressManager(String projectName, List addresses, EventBus eventBus) { - super(projectName, addresses); + public ConfigCenterAddressManager(String projectName, List addresses, EventBus eventBus, String region, + String availableZone) { + super(projectName, addresses, region, availableZone); eventBus.register(this); } diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java index a8ee3b99ec0..8ed31a5a61b 100644 --- a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterClient.java @@ -18,7 +18,9 @@ package org.apache.servicecomb.config.center.client; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.commons.lang3.StringUtils; @@ -30,10 +32,14 @@ import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpUtils; +import org.apache.servicecomb.http.client.event.OperationEvents.UnAuthorizedOperationEvent; +import org.apache.servicecomb.http.client.utils.ServiceCombServiceAvailableUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; import com.fasterxml.jackson.core.type.TypeReference; +import com.google.common.eventbus.EventBus; public class ConfigCenterClient implements ConfigCenterOperation { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCenterClient.class); @@ -48,24 +54,34 @@ public class ConfigCenterClient implements ConfigCenterOperation { public static final String DARK_LAUNCH = "darklaunch@"; + private static final String ADDRESS_CHECK_PATH = "/v3/default/configuration/health?mode=readiness"; + private final HttpTransport httpTransport; private final ConfigCenterAddressManager addressManager; + private final Map> dimensionConfigNames = new HashMap<>(); + + private EventBus eventBus; + public ConfigCenterClient(ConfigCenterAddressManager addressManager, HttpTransport httpTransport) { this.addressManager = addressManager; this.httpTransport = httpTransport; } + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + addressManager.setEventBus(eventBus); + } + @Override - public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsRequest request) { + public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsRequest request, String address) { String dimensionsInfo = buildDimensionsInfo(request, true); QueryConfigurationsResponse queryConfigurationsResponse = new QueryConfigurationsResponse(); Map configurations = new HashMap<>(); String uri = null; - String address = addressManager.address(); try { uri = address + "/configuration/items?dimensionsInfo=" + HttpUtils.encodeURLParam(dimensionsInfo) + "&revision=" + request.getRevision(); @@ -76,6 +92,7 @@ public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsReques HttpRequest.GET); HttpResponse httpResponse = httpTransport.doRequest(httpRequest); + recordAndSendUnAuthorizedEvent(httpResponse, address); if (httpResponse.getStatusCode() == HttpStatus.SC_OK) { Map> allConfigMap = HttpUtils.deserialize( httpResponse.getContent(), @@ -88,31 +105,38 @@ public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsReques if (allConfigMap.get(APPLICATION_CONFIG) != null) { configurations.putAll(allConfigMap.get(APPLICATION_CONFIG)); + logConfigurationNames(APPLICATION_CONFIG, allConfigMap.get(APPLICATION_CONFIG)); } if (allConfigMap.get(buildDimensionsInfo(request, false)) != null) { configurations.putAll(allConfigMap.get(buildDimensionsInfo(request, false))); + logConfigurationNames(buildDimensionsInfo(request, false), + allConfigMap.get(buildDimensionsInfo(request, false))); } if (allConfigMap.get(buildDarkLaunchDimensionsInfo(request)) != null) { configurations.putAll(allConfigMap.get(buildDarkLaunchDimensionsInfo(request))); + logConfigurationNames(buildDarkLaunchDimensionsInfo(request), + allConfigMap.get(buildDarkLaunchDimensionsInfo(request))); } if (allConfigMap.get(dimensionsInfo) != null) { configurations.putAll(allConfigMap.get(dimensionsInfo)); + logConfigurationNames(dimensionsInfo, allConfigMap.get(dimensionsInfo)); } queryConfigurationsResponse.setConfigurations(configurations); queryConfigurationsResponse.setChanged(true); - addressManager.recordSuccessState(address); return queryConfigurationsResponse; } else if (httpResponse.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { queryConfigurationsResponse.setChanged(false); - addressManager.recordSuccessState(address); + return queryConfigurationsResponse; + } else if (httpResponse.getStatusCode() == HttpStatus.SC_TOO_MANY_REQUESTS) { + LOGGER.warn("rate limited, keep the local dimension [{}] configs unchanged.", dimensionsInfo); + queryConfigurationsResponse.setChanged(false); return queryConfigurationsResponse; } else if (httpResponse.getStatusCode() == HttpStatus.SC_BAD_REQUEST) { throw new OperationException("Bad request for query configurations."); } else { - addressManager.recordFailState(address); throw new OperationException( "read response failed. status:" + httpResponse.getStatusCode() @@ -128,6 +152,53 @@ public QueryConfigurationsResponse queryConfigurations(QueryConfigurationsReques } } + private void recordAndSendUnAuthorizedEvent(HttpResponse response, String address) { + if (this.eventBus != null && response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { + LOGGER.warn("query configuration unauthorized from server [{}], message [{}]", address, response.getMessage()); + addressManager.recordFailState(address); + this.eventBus.post(new UnAuthorizedOperationEvent(address)); + } else { + addressManager.recordSuccessState(address); + } + } + + /** + * Only the name of the new configuration item is printed. + * No log is printed when the configuration content is updated. + * + * @param dimension dimension + * @param configs configs + */ + private void logConfigurationNames(String dimension, Map configs) { + if (CollectionUtils.isEmpty(configs)) { + return; + } + List configNames = dimensionConfigNames.get(dimension); + if (configNames == null) { + configNames = new ArrayList<>(); + } + StringBuilder names = new StringBuilder(); + for (String key : configs.keySet()) { + if (configNames.contains(key)) { + continue; + } + names.append(key).append(","); + configNames.add(key); + } + if (names.isEmpty()) { + return; + } + dimensionConfigNames.put(dimension, configNames); + String fileNames = names.substring(0, names.length() - 1); + LOGGER.info("pulling dimension [{}] configurations success, get config names: [{}].", + dimension, fileNames); + } + + @Override + public void checkAddressAvailable(String address) { + ServiceCombServiceAvailableUtils.checkAddressAvailable(addressManager, address, httpTransport, ADDRESS_CHECK_PATH); + } + private String buildDimensionsInfo(QueryConfigurationsRequest request, boolean withVersion) { String result = request.getServiceName() + DEFAULT_APP_SEPARATOR diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterManager.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterManager.java index bdccc35434e..7bfc43b4e05 100644 --- a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterManager.java +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterManager.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.config.center.client; +import java.util.List; import java.util.Map; import org.apache.servicecomb.config.center.client.model.ConfigCenterConfiguration; @@ -44,13 +45,16 @@ public class ConfigCenterManager extends AbstractTask { private final ConfigCenterConfiguration configCenterConfiguration; - public ConfigCenterManager(ConfigCenterClient configCenterClient, EventBus eventBus, - ConfigConverter configConverter, ConfigCenterConfiguration configCenterConfiguration) { + private final ConfigCenterAddressManager configCenterAddressManager; + + public ConfigCenterManager(ConfigCenterClient configCenterClient, EventBus eventBus, ConfigConverter configConverter, + ConfigCenterConfiguration configCenterConfiguration, ConfigCenterAddressManager configCenterAddressManager) { super("config-center-configuration-task"); this.configCenterClient = configCenterClient; this.eventBus = eventBus; this.configConverter = configConverter; this.configCenterConfiguration = configCenterConfiguration; + this.configCenterAddressManager = configCenterAddressManager; } public void setQueryConfigurationsRequest(QueryConfigurationsRequest queryConfigurationsRequest) { @@ -59,6 +63,8 @@ public void setQueryConfigurationsRequest(QueryConfigurationsRequest queryConfig public void startConfigCenterManager() { this.startTask(new PollConfigurationTask(0)); + schedulerCheckAddressAvailable("cc-addr-check", new CheckConfigCenterAddressTask(), + configCenterConfiguration.getRefreshIntervalInMillis()); } class PollConfigurationTask implements Task { @@ -71,7 +77,8 @@ public PollConfigurationTask(int failCount) { @Override public void execute() { try { - QueryConfigurationsResponse response = configCenterClient.queryConfigurations(queryConfigurationsRequest); + QueryConfigurationsResponse response = configCenterClient.queryConfigurations(queryConfigurationsRequest, + configCenterAddressManager.address()); if (response.isChanged()) { queryConfigurationsRequest.setRevision(response.getRevision()); Map lastData = configConverter.updateData(response.getConfigurations()); @@ -84,9 +91,23 @@ public void execute() { startTask( new BackOffSleepTask(configCenterConfiguration.getRefreshIntervalInMillis(), new PollConfigurationTask(0))); } catch (Exception e) { - LOGGER.error("get configurations from ConfigCenter failed, and will try again.", e); + LOGGER.warn("get configurations from ConfigCenter failed, and will try again, cause message: {}. current " + + "fail does not affect the obtained historical configuration.", e.getCause().getMessage()); startTask(new BackOffSleepTask(failCount + 1, new PollConfigurationTask(failCount + 1))); } } } + + class CheckConfigCenterAddressTask implements Runnable { + @Override + public void run() { + List isolationAddresses = configCenterAddressManager.getIsolationAddresses(); + if (isolationAddresses.isEmpty()) { + return; + } + for (String address : isolationAddresses) { + configCenterClient.checkAddressAvailable(address); + } + } + } } diff --git a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterOperation.java b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterOperation.java index 5e3a2207067..919e9f30cd7 100644 --- a/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterOperation.java +++ b/clients/config-center-client/src/main/java/org/apache/servicecomb/config/center/client/ConfigCenterOperation.java @@ -25,8 +25,16 @@ public interface ConfigCenterOperation { /** * 根据查询条件查询配置项。 * @param request 查询的维度(project, application, serviceName, version) 和 revision 信息。 + * @param address 查询的配置中心地址。 * @return 如果存在配置变更,返回全量的配置项, changed = true。 如果没有变更, 返回 null, changed = false, * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ - QueryConfigurationsResponse queryConfigurations(QueryConfigurationsRequest request); + QueryConfigurationsResponse queryConfigurations(QueryConfigurationsRequest request, String address); + + /** + * Check config center isolation address available + * + * @param address isolation address + */ + void checkAddressAvailable(String address); } diff --git a/clients/config-common/pom.xml b/clients/config-common/pom.xml index 58991dedd61..ff10b993583 100644 --- a/clients/config-common/pom.xml +++ b/clients/config-common/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -41,4 +41,4 @@ - \ No newline at end of file + diff --git a/clients/config-kie-client/pom.xml b/clients/config-kie-client/pom.xml index 4c520dd359e..30dac44ba53 100644 --- a/clients/config-kie-client/pom.xml +++ b/clients/config-kie-client/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -51,4 +51,4 @@ - \ No newline at end of file + diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java index 1e099e030f1..b98a7d3b2a1 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieClient.java @@ -17,11 +17,16 @@ package org.apache.servicecomb.config.kie.client; +import com.google.common.eventbus.EventBus; + import java.io.StringReader; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Properties; import java.util.stream.Collectors; @@ -41,15 +46,20 @@ import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpUtils; +import org.apache.servicecomb.http.client.event.OperationEvents.UnAuthorizedOperationEvent; +import org.apache.servicecomb.http.client.utils.ServiceCombServiceAvailableUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.core.io.ByteArrayResource; +import org.springframework.util.CollectionUtils; public class KieClient implements KieConfigOperation { private static final Logger LOGGER = LoggerFactory.getLogger(KieClient.class); + private static final String ADDRESS_CHECK_PATH = "/v1/health"; + protected HttpTransport httpTransport; protected String revision = "0"; @@ -60,15 +70,23 @@ public class KieClient implements KieConfigOperation { public static final String DEFAULT_KIE_API_VERSION = "v1"; + private final Map> dimensionConfigNames = new HashMap<>(); + + private EventBus eventBus; + public KieClient(KieAddressManager addressManager, HttpTransport httpTransport, KieConfiguration kieConfiguration) { this.httpTransport = httpTransport; this.addressManager = addressManager; this.kieConfiguration = kieConfiguration; } + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + addressManager.setEventBus(eventBus); + } + @Override - public ConfigurationsResponse queryConfigurations(ConfigurationsRequest request) { - String address = addressManager.address(); + public ConfigurationsResponse queryConfigurations(ConfigurationsRequest request, String address) { String url = buildUrl(request, address); try { if (kieConfiguration.isEnableLongPolling()) { @@ -77,15 +95,16 @@ public ConfigurationsResponse queryConfigurations(ConfigurationsRequest request) HttpRequest httpRequest = new HttpRequest(url, null, null, HttpRequest.GET); HttpResponse httpResponse = httpTransport.doRequest(httpRequest); + recordAndSendUnAuthorizedEvent(httpResponse, address); ConfigurationsResponse configurationsResponse = new ConfigurationsResponse(); if (httpResponse.getStatusCode() == HttpStatus.SC_OK) { revision = httpResponse.getHeader("X-Kie-Revision"); KVResponse allConfigList = HttpUtils.deserialize(httpResponse.getContent(), KVResponse.class); + logConfigurationNames(request.getLabelsQuery(), allConfigList.getData()); Map configurations = getConfigByLabel(allConfigList); configurationsResponse.setConfigurations(configurations); configurationsResponse.setChanged(true); configurationsResponse.setRevision(revision); - addressManager.recordSuccessState(address); return configurationsResponse; } if (httpResponse.getStatusCode() == HttpStatus.SC_BAD_REQUEST) { @@ -93,19 +112,70 @@ public ConfigurationsResponse queryConfigurations(ConfigurationsRequest request) } if (httpResponse.getStatusCode() == HttpStatus.SC_NOT_MODIFIED) { configurationsResponse.setChanged(false); - addressManager.recordSuccessState(address); return configurationsResponse; } - addressManager.recordFailState(address); + if (httpResponse.getStatusCode() == HttpStatus.SC_TOO_MANY_REQUESTS) { + LOGGER.warn("rate limited, keep the local dimension [{}] configs unchanged.", request.getLabelsQuery()); + configurationsResponse.setChanged(false); + return configurationsResponse; + } throw new OperationException( "read response failed. status:" + httpResponse.getStatusCode() + "; message:" + httpResponse.getMessage() + "; content:" + httpResponse.getContent()); } catch (Exception e) { + addressManager.recordFailState(address); LOGGER.error("query configuration from {} failed, message={}", url, e.getMessage()); throw new OperationException("read response failed. ", e); } } + private void recordAndSendUnAuthorizedEvent(HttpResponse response, String address) { + if (this.eventBus != null && response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { + LOGGER.warn("query configuration unauthorized from server [{}], message [{}]", address, response.getMessage()); + addressManager.recordFailState(address); + this.eventBus.post(new UnAuthorizedOperationEvent(address)); + } else { + addressManager.recordSuccessState(address); + } + } + + /** + * Only the name of the new configuration item is printed. + * No log is printed when the configuration content is updated. + * + * @param dimension dimension + * @param data configs-data + */ + private void logConfigurationNames(String dimension, List data) { + if (CollectionUtils.isEmpty(data)) { + return; + } + List configNames = dimensionConfigNames.get(dimension); + if (configNames == null) { + configNames = new ArrayList<>(); + } + StringBuilder names = new StringBuilder(); + for (KVDoc doc : data) { + if (configNames.contains(doc.getKey())) { + continue; + } + names.append(doc.getKey()).append(","); + configNames.add(doc.getKey()); + } + if (names.isEmpty()) { + return; + } + dimensionConfigNames.put(dimension, configNames); + String fileNames = names.substring(0, names.length() - 1); + LOGGER.info("pulling dimension [{}] configurations success, get config names: [{}].", + dimension, fileNames); + } + + @Override + public void checkAddressAvailable(String address) { + ServiceCombServiceAvailableUtils.checkAddressAvailable(addressManager, address, httpTransport, ADDRESS_CHECK_PATH); + } + private String buildUrl(ConfigurationsRequest request, String currentAddress) { StringBuilder sb = new StringBuilder(); sb.append(currentAddress); @@ -126,6 +196,7 @@ private String buildUrl(ConfigurationsRequest request, String currentAddress) { private Map getConfigByLabel(KVResponse resp) { Map resultMap = new HashMap<>(); resp.getData().stream() + .sorted(Comparator.comparing(KVDoc::getUpdateTime, Comparator.nullsFirst(Comparator.naturalOrder()))) .filter(doc -> doc.getStatus() == null || ConfigConstants.STATUS_ENABLED.equalsIgnoreCase(doc.getStatus())) .map(this::processValueType) .collect(Collectors.toList()) diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigManager.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigManager.java index eaa7b93d663..ed251613530 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigManager.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigManager.java @@ -26,6 +26,7 @@ import org.apache.servicecomb.config.kie.client.model.ConfigurationsRequest; import org.apache.servicecomb.config.kie.client.model.ConfigurationsRequestFactory; import org.apache.servicecomb.config.kie.client.model.ConfigurationsResponse; +import org.apache.servicecomb.config.kie.client.model.KieAddressManager; import org.apache.servicecomb.config.kie.client.model.KieConfiguration; import org.apache.servicecomb.http.client.task.AbstractTask; import org.apache.servicecomb.http.client.task.Task; @@ -50,9 +51,10 @@ public class KieConfigManager extends AbstractTask { private final KieConfiguration kieConfiguration; + private final KieAddressManager kieAddressManager; + public KieConfigManager(KieConfigOperation configKieClient, EventBus eventBus, - KieConfiguration kieConfiguration, - ConfigConverter configConverter) { + KieConfiguration kieConfiguration, ConfigConverter configConverter, KieAddressManager kieAddressManager) { super("config-center-configuration-task"); this.configurationsRequests = ConfigurationsRequestFactory.buildConfigurationRequests(kieConfiguration); this.configurationsRequests.sort(ConfigurationsRequest::compareTo); @@ -60,27 +62,44 @@ public KieConfigManager(KieConfigOperation configKieClient, EventBus eventBus, this.eventBus = eventBus; this.configConverter = configConverter; this.kieConfiguration = kieConfiguration; + this.kieAddressManager = kieAddressManager; } public void firstPull() { + Map data = new HashMap<>(); try { - Map data = new HashMap<>(); - this.configurationsRequests.forEach(r -> { - r.setRevision(ConfigurationsRequest.INITIAL_REVISION); - ConfigurationsResponse response = configKieClient.queryConfigurations(r); - if (response.isChanged()) { - r.setRevision(response.getRevision()); - r.setLastRawData(response.getConfigurations()); - data.putAll(response.getConfigurations()); - } - }); - this.configConverter.updateData(data); - } catch (RuntimeException e) { + firstQueryConfigurations(data); + } catch (Exception e) { if (this.kieConfiguration.isFirstPullRequired()) { throw e; } else { - LOGGER.warn("first pull failed, and ignore {}", e.getMessage()); + LOGGER.warn("first pull failed!"); + } + } + } + + private void firstQueryConfigurations(Map data) { + for (int i = 0; i < 3;) { + String address = kieAddressManager.address(); + try { + this.configurationsRequests.forEach(r -> { + r.setRevision(ConfigurationsRequest.INITIAL_REVISION); + ConfigurationsResponse response = configKieClient.queryConfigurations(r, address); + if (response.isChanged()) { + r.setRevision(response.getRevision()); + r.setLastRawData(response.getConfigurations()); + data.putAll(response.getConfigurations()); + } + }); + this.configConverter.updateData(data); + break; + } catch (Exception e) { + if (i == 2) { + throw e; + } + LOGGER.warn("firstQueryConfigurations failed, config address {} and ignore {}", address, e.getMessage()); } + i++; } } @@ -105,6 +124,8 @@ protected void initTaskPool(String taskName) { public void startConfigKieManager() { this.configurationsRequests.forEach((t) -> this.startTask(new PollConfigurationTask(0, t))); + schedulerCheckAddressAvailable("kie-addr-check", new CheckKieAddressTask(), + kieConfiguration.getRefreshIntervalInMillis()); } class PollConfigurationTask implements Task { @@ -120,7 +141,8 @@ public PollConfigurationTask(int failCount, ConfigurationsRequest configurations @Override public void execute() { try { - ConfigurationsResponse response = configKieClient.queryConfigurations(configurationsRequest); + ConfigurationsResponse response = configKieClient.queryConfigurations(configurationsRequest, + kieAddressManager.address()); if (response.isChanged()) { configurationsRequest.setRevision(response.getRevision()); configurationsRequest.setLastRawData(response.getConfigurations()); @@ -134,10 +156,24 @@ public void execute() { new PollConfigurationTask(0, this.configurationsRequest))); } } catch (Exception e) { - LOGGER.error("get configurations from KieConfigCenter failed, and will try again.", e); + LOGGER.warn("get configurations from KieConfigCenter failed, and will try again, cause message: {}. current " + + "fail does not affect the obtained historical configuration.", e.getCause().getMessage()); startTask( new BackOffSleepTask(failCount + 1, new PollConfigurationTask(failCount + 1, this.configurationsRequest))); } } } + + class CheckKieAddressTask implements Runnable { + @Override + public void run() { + List isolationAddresses = kieAddressManager.getIsolationAddresses(); + if (isolationAddresses.isEmpty()) { + return; + } + for (String address : isolationAddresses) { + configKieClient.checkAddressAvailable(address); + } + } + } } diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigOperation.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigOperation.java index 364774fd22a..135ad0c220a 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigOperation.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/KieConfigOperation.java @@ -26,8 +26,16 @@ public interface KieConfigOperation { /** * 根据查询条件查询配置项。 * @param request 查询的维度(project, application, serviceName, version) 和 revision 信息。 + * @param address 查询的配置中心地址。 * @return 如果存在配置变更,返回全量的配置项, changed = true。 如果没有变更, 返回 null, changed = false, * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ - ConfigurationsResponse queryConfigurations(ConfigurationsRequest request); + ConfigurationsResponse queryConfigurations(ConfigurationsRequest request, String address); + + /** + * Check kie isolation address available + * + * @param address isolation address + */ + void checkAddressAvailable(String address); } diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVDoc.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVDoc.java index 4078557d3be..3059a581c61 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVDoc.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KVDoc.java @@ -44,6 +44,9 @@ public class KVDoc { private String status; + @JsonAlias("update_time") + private long updateTime; + public String getStatus() { return status; } @@ -115,4 +118,12 @@ public void setValue(String value) { public String getValueType() { return valueType; } + + public long getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(long updateTime) { + this.updateTime = updateTime; + } } diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java index 0742c11fb10..d8c069ee9b1 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/KieAddressManager.java @@ -27,8 +27,8 @@ public class KieAddressManager extends AbstractAddressManager { - public KieAddressManager(List addresses, EventBus eventBus) { - super(addresses); + public KieAddressManager(List addresses, EventBus eventBus, String region, String availableZone) { + super(addresses, region, availableZone); eventBus.register(this); } diff --git a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ValueType.java b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ValueType.java index b38a1e28f8e..23cbce3b367 100644 --- a/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ValueType.java +++ b/clients/config-kie-client/src/main/java/org/apache/servicecomb/config/kie/client/model/ValueType.java @@ -24,5 +24,6 @@ public enum ValueType { string, text, json, - properties + properties, + xml } diff --git a/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java b/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java index 6533fc60e57..c8bf1f57a29 100644 --- a/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java +++ b/clients/config-kie-client/src/test/java/org/apache/servicecomb/config/kie/client/model/KieAddressManagerTest.java @@ -40,7 +40,7 @@ class KieAddressManagerTest { public void kieAddressManagerTest() throws NoSuchFieldException, IllegalAccessException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); - addressManager1 = new KieAddressManager(addresses, new EventBus()); + addressManager1 = new KieAddressManager(addresses, new EventBus(), "", ""); Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager1, 0); @@ -64,7 +64,7 @@ public void onRefreshEndpointEvent() { Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); - addressManager1 = new KieAddressManager(addresses, new EventBus()); + addressManager1 = new KieAddressManager(addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "KIE"); addressManager1.refreshEndpoint(event, "KIE"); diff --git a/clients/dashboard-client/pom.xml b/clients/dashboard-client/pom.xml index 57bacd97d6a..fb40407b82a 100644 --- a/clients/dashboard-client/pom.xml +++ b/clients/dashboard-client/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardAddressManager.java b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardAddressManager.java index 9dd8c6a3448..6897e02acd4 100644 --- a/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardAddressManager.java +++ b/clients/dashboard-client/src/main/java/org/apache/servicecomb/dashboard/client/DashboardAddressManager.java @@ -29,8 +29,8 @@ public class DashboardAddressManager extends AbstractAddressManager { - public DashboardAddressManager(List addresses, EventBus eventBus) { - super(addresses); + public DashboardAddressManager(List addresses, EventBus eventBus, String region, String availableZone) { + super(addresses, region, availableZone); eventBus.register(this); } diff --git a/clients/dashboard-client/src/test/java/org/apache/servicecomb/dashboard/client/AddressManagerTest.java b/clients/dashboard-client/src/test/java/org/apache/servicecomb/dashboard/client/AddressManagerTest.java index 71e5deedaf7..fdb7a23922b 100644 --- a/clients/dashboard-client/src/test/java/org/apache/servicecomb/dashboard/client/AddressManagerTest.java +++ b/clients/dashboard-client/src/test/java/org/apache/servicecomb/dashboard/client/AddressManagerTest.java @@ -39,7 +39,7 @@ class AddressManagerTest { public void kieAddressManagerTest() throws IllegalAccessException, NoSuchFieldException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); - addressManager1 = new DashboardAddressManager(addresses, new EventBus()); + addressManager1 = new DashboardAddressManager(addresses, new EventBus(), "", ""); Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager1, 0); @@ -63,7 +63,7 @@ public void onRefreshEndpointEvent() { Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); - addressManager1 = new DashboardAddressManager(addresses, new EventBus()); + addressManager1 = new DashboardAddressManager(addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseMonitoring"); addressManager1.refreshEndpoint(event, "CseMonitoring"); diff --git a/clients/http-client-common/pom.xml b/clients/http-client-common/pom.xml index ae58108fb83..51d982e470c 100644 --- a/clients/http-client-common/pom.xml +++ b/clients/http-client-common/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java index e5f6ee251ca..3c5d6214be8 100644 --- a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/AbstractAddressManager.java @@ -17,32 +17,23 @@ package org.apache.servicecomb.http.client.common; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.http.client.event.EngineConnectChangedEvent; import org.apache.servicecomb.http.client.event.RefreshEndpointEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; import com.google.common.annotations.VisibleForTesting; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.google.common.eventbus.EventBus; public class AbstractAddressManager { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAddressManager.class); @@ -53,70 +44,118 @@ public class AbstractAddressManager { private static final String V3_PREFIX = "/v3/"; - private static final int DEFAULT_METRICS_WINDOW_TIME = 1; + private static final String ZONE = "availableZone"; + + private static final String REGION = "region"; private static final int ISOLATION_THRESHOLD = 3; - private List addresses = new ArrayList<>(); + private volatile List addresses = new ArrayList<>(); + + // when all addresses are isolation, it will use this for polling. + private final List defaultAddress = new ArrayList<>(); + + private final List defaultIsolationAddress = new ArrayList<>(); private int index; private String projectName; - // if address in same zone will be true; others will be false. - private final Map addressCategory = new HashMap<>(); - // recording continuous times of failure of an address. private final Map addressFailureStatus = new ConcurrentHashMap<>(); - // recording address isolation status, if isolated will be false - private final Map addressIsolated = new ConcurrentHashMap<>(); - - // recording address isolation status, if isolated will be false - private Cache addressIsolationStatus = CacheBuilder.newBuilder() - .maximumSize(100) - .expireAfterWrite(1, TimeUnit.MINUTES) - .build(); - private volatile List availableZone = new ArrayList<>(); + private final List isolationZoneAddress = new ArrayList<>(); + private volatile List availableRegion = new ArrayList<>(); - private final List defaultAddress = new ArrayList<>(); + private final List isolationRegionAddress = new ArrayList<>(); - private boolean addressAutoRefreshed = false; + private volatile boolean addressAutoRefreshed = false; private final Object lock = new Object(); - private Random random = new Random(); + private final Random random = new Random(); - private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1, - new ThreadFactoryBuilder() - .setNameFormat("check-available-address-%d") - .build()); + private EventBus eventBus; - public AbstractAddressManager(List addresses) { + public AbstractAddressManager(List addresses, String ownRegion, String ownAvailableZone) { this.projectName = DEFAULT_PROJECT; - this.addresses.addAll(addresses); - this.defaultAddress.addAll(addresses); - this.index = addresses.size() > 0 ? random.nextInt(addresses.size()) : 0; + parseAndInitAddresses(addresses, ownRegion, ownAvailableZone, false); + this.index = !addresses.isEmpty() ? getRandomIndex() : 0; } - public AbstractAddressManager(String projectName, List addresses) { + /** + * address support config with region/availableZone info, to enable engine affinity calls during startup + * address may be like: + * https://192.168.20.13:30110?region=region1&availableZone=az + * https://192.168.20.13:30100?region=region1&availableZone=az + * When address have no datacenter information, roundRobin using address + * + * @param addresses engine addresses + * @param ownRegion microservice region + * @param ownAvailableZone microservice zone + * @param isFormat is need format + */ + private void parseAndInitAddresses(List addresses, String ownRegion, String ownAvailableZone, + boolean isFormat) { + if (CollectionUtils.isEmpty(addresses)) { + return; + } + List tempList = new ArrayList<>(); + addressAutoRefreshed = addresses.stream().anyMatch(addr -> addr.contains(ZONE) || addr.contains(REGION)); + for (String address : addresses) { + // Compatible IpPortManager init address is 127.0.0.1:30100 + if (!address.startsWith("http")) { + tempList.add(address); + continue; + } + URLEndPoint endpoint = new URLEndPoint(address); + tempList.add(endpoint.toString()); + buildAffinityAddress(endpoint, ownRegion, ownAvailableZone); + } + this.addresses.addAll(isFormat ? this.transformAddress(tempList) : tempList); + this.defaultAddress.addAll(isFormat ? this.transformAddress(tempList) : tempList); + } + + private void buildAffinityAddress(URLEndPoint endpoint, String ownRegion, String ownAvailableZone) { + if (addressAutoRefreshed) { + if (regionAndAZMatch(ownRegion, ownAvailableZone, endpoint.getFirst(REGION), endpoint.getFirst(ZONE))) { + availableZone.add(endpoint.toString()); + } else { + availableRegion.add(endpoint.toString()); + } + } + } + + private boolean regionAndAZMatch(String ownRegion, String ownAvailableZone, String engineRegion, + String engineAvailableZone) { + return ownRegion.equalsIgnoreCase(engineRegion) && ownAvailableZone.equals(engineAvailableZone); + } + + public AbstractAddressManager(String projectName, List addresses, String ownRegion, String ownAvailableZone) { this.projectName = StringUtils.isEmpty(projectName) ? DEFAULT_PROJECT : projectName; - this.addresses = this.transformAddress(addresses); - this.defaultAddress.addAll(this.addresses); - this.index = addresses.size() > 0 ? random.nextInt(addresses.size()) : 0; + parseAndInitAddresses(addresses, ownRegion, ownAvailableZone, true); + this.index = !addresses.isEmpty() ? getRandomIndex() : 0; } - @VisibleForTesting - Cache getAddressIsolationStatus() { - return addressIsolationStatus; + private int getRandomIndex() { + return random.nextInt(addresses.size()); } - @VisibleForTesting - void setAddressIsolationStatus(Cache addressIsolationStatus) { - this.addressIsolationStatus = addressIsolationStatus; + public void refreshEndpoint(RefreshEndpointEvent event, String key) { + if (null == event || !event.getName().equals(key)) { + return; + } + + availableZone = event.getSameZone().stream().map(this::normalizeUri).collect(Collectors.toList()); + availableRegion = event.getSameRegion().stream().map(this::normalizeUri).collect(Collectors.toList()); + addressAutoRefreshed = true; + } + + protected String normalizeUri(String endpoint) { + return new URLEndPoint(endpoint).toString(); } @VisibleForTesting @@ -136,25 +175,10 @@ public List getAvailableRegion() { return availableRegion; } - private void startCheck() { - executorService.scheduleAtFixedRate(this::checkHistory, - 0, - DEFAULT_METRICS_WINDOW_TIME, - TimeUnit.MINUTES); - } - public String formatUrl(String url, boolean absoluteUrl, String address) { return absoluteUrl ? address + url : formatAddress(address) + url; } - public String address() { - if (!addressAutoRefreshed) { - return getDefaultAddress(); - } else { - return getAvailableZoneAddress(); - } - } - public boolean sslEnabled() { return address().startsWith("https://"); } @@ -163,10 +187,6 @@ protected List transformAddress(List addresses) { return addresses.stream().map(this::formatAddress).collect(Collectors.toList()); } - protected String getUrlPrefix(String address) { - return address + V3_PREFIX; - } - protected String formatAddress(String address) { try { return getUrlPrefix(address) + HttpUtils.encodeURLParam(this.projectName); @@ -175,27 +195,34 @@ protected String formatAddress(String address) { } } - private String getDefaultAddress() { - List addresses = getAvailableAddress(defaultAddress); - if (!addresses.isEmpty()) { - return getCurrentAddress(addresses); + protected String getUrlPrefix(String address) { + return address + V3_PREFIX; + } + + public String address() { + if (!addressAutoRefreshed) { + return getDefaultAddress(); + } else { + return getAvailableZoneAddress(); } - return getInitAddress(); } - private String getAvailableZoneAddress() { - List addresses = getAvailableZoneIpPorts(); + private String getDefaultAddress() { if (!addresses.isEmpty()) { return getCurrentAddress(addresses); } - return getInitAddress(); + LOGGER.warn("all addresses are isolation, please check server status."); + // when all addresses are isolation, it will use all default address for polling. + return getCurrentAddress(defaultAddress); } - // when all available address is fail, it will use all the initial addresses for polling. - private String getInitAddress() { - if (addresses.isEmpty()) { - return null; + private String getAvailableZoneAddress() { + List zoneOrRegionAddress = getZoneOrRegionAddress(); + if (!zoneOrRegionAddress.isEmpty()) { + return getCurrentAddress(zoneOrRegionAddress); } + LOGGER.warn("all auto discovery addresses are isolation, please check server status."); + // when all available address are isolation, it will use config addresses for polling. return getCurrentAddress(addresses); } @@ -209,36 +236,41 @@ private String getCurrentAddress(List addresses) { } } - private List getAvailableZoneIpPorts() { + private List getZoneOrRegionAddress() { List results = new ArrayList<>(); if (!availableZone.isEmpty()) { - results.addAll(getAvailableAddress(availableZone)); + results.addAll(availableZone); } else { - results.addAll(getAvailableAddress(availableRegion)); + results.addAll(availableRegion); } return results; } - private List getAvailableAddress(List endpoints) { - return endpoints.stream().filter(uri -> !addressIsolated.containsKey(uri) || addressIsolated.get(uri)) - .collect(Collectors.toList()); - } - - protected String normalizeUri(String endpoint) { - return new URLEndPoint(endpoint).toString(); - } - - public void refreshEndpoint(RefreshEndpointEvent event, String key) { - if (null == event || !event.getName().equals(key)) { + public void recordSuccessState(String address) { + resetFailureStatus(address); + if (addressAutoRefreshed) { + if (isolationZoneAddress.remove(address)) { + LOGGER.warn("restore same region address [{}]", address); + if (eventBus != null && availableZone.isEmpty()) { + eventBus.post(new EngineConnectChangedEvent()); + } + availableZone.add(address); + return; + } + if (isolationRegionAddress.remove(address)) { + LOGGER.warn("restore same zone address [{}]", address); + availableRegion.add(address); + } return; } + if (defaultIsolationAddress.remove(address)) { + LOGGER.warn("restore default address [{}]", address); + addresses.add(address); + } + } - availableZone = event.getSameZone().stream().map(this::normalizeUri).collect(Collectors.toList()); - availableRegion = event.getSameRegion().stream().map(this::normalizeUri).collect(Collectors.toList()); - availableZone.forEach(address -> addressCategory.put(address, true)); - availableRegion.forEach(address -> addressCategory.put(address, false)); - startCheck(); - addressAutoRefreshed = true; + public void resetFailureStatus(String address) { + addressFailureStatus.put(address, 0); } public void recordFailState(String address) { @@ -256,96 +288,61 @@ public void recordFailState(String address) { } } - public void recordSuccessState(String address) { - addressFailureStatus.put(address, 0); - } - + //Query whether the current address belongs to the same AZ or the same region through AZMap, + // and delete it from the record. At the same time, add records in history and cache @VisibleForTesting - protected void checkHistory() { - addressIsolated.keySet().stream().filter(this::judgeIsolation).forEach(s -> { - if (telnetTest(s)) { - rejoinAddress(s); - } else { - addressIsolationStatus.put(s, false); + void removeAddress(String address) { + if (!addressAutoRefreshed) { + if (addresses.remove(address)) { + LOGGER.warn("isolation default address [{}]", address); + defaultIsolationAddress.add(address); } - }); - } - - private Boolean judgeIsolation(String address) { - try { - return addressIsolationStatus.get(address, () -> true); - } catch (ExecutionException e) { - return true; + return; } - } - - protected boolean telnetTest(String address) { - URI ipPort = parseIpPortFromURI(address); - try (Socket s = new Socket()) { - s.connect(new InetSocketAddress(ipPort.getHost(), ipPort.getPort()), 3000); - return true; - } catch (IOException e) { - LOGGER.warn("ping endpoint {} failed, It will be quarantined again.", address); + if (availableZone.remove(address)) { + LOGGER.warn("isolation same zone address [{}]", address); + isolationZoneAddress.add(address); + if (eventBus != null && availableZone.isEmpty() && !availableRegion.isEmpty()) { + eventBus.post(new EngineConnectChangedEvent()); + } + return; } - return false; - } - - private URI parseIpPortFromURI(String uri) { - try { - return new URI(uri); - } catch (URISyntaxException e) { - return null; + if (availableRegion.remove(address)) { + LOGGER.warn("isolation same region address [{}]", address); + isolationRegionAddress.add(address); } } - //Query whether the current address belongs to the same AZ or region through azmap, - // add it to the sequence of, and delete the record in history - @VisibleForTesting - void rejoinAddress(String address) { - if (!addressAutoRefreshed) { - defaultAddress.add(address); - addressFailureStatus.put(address, 0); - addressIsolated.remove(address); - return; - } - - if (addressCategory.get(address) == null) { - LOGGER.warn("may not happen {}-{}", addressCategory.size(), address); - return; - } + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + } - if (addressCategory.get(address)) { - availableZone.add(address); - } else { - availableRegion.add(address); - } - addressFailureStatus.put(address, 0); - addressIsolated.remove(address); + public List getIsolationAddresses() { + List isolationAddresses = new ArrayList<>(defaultIsolationAddress); + isolationAddresses.addAll(isolationZoneAddress); + isolationAddresses.addAll(isolationRegionAddress); + return isolationAddresses; } - //Query whether the current address belongs to the same AZ or the same region through AZMap, - // and delete it from the record. At the same time, add records in history and cache - @VisibleForTesting - void removeAddress(String address) { - if (!addressAutoRefreshed) { - defaultAddress.remove(address); - addressIsolated.put(address, false); - addressIsolationStatus.put(address, false); - return; + public String compareAndGetAddress(String host) { + for (String address : defaultAddress) { + if (isAddressHostSame(address, host)) { + return address; + } } + return ""; + } - if (addressCategory.get(address) == null) { - LOGGER.warn("may not happen {}-{}", addressCategory.size(), address); - return; + private boolean isAddressHostSame(String address, String host) { + if (StringUtils.isEmpty(host)) { + return false; } - - if (addressCategory.get(address)) { - availableZone.remove(address); - } else { - availableRegion.remove(address); + try { + URI uri = new URI(address); + return host.equals(uri.getHost()); + } catch (Exception e) { + LOGGER.warn("Exception occurred while constructing URI using the address [{}]", address); } - - addressIsolated.put(address, false); - addressIsolationStatus.put(address, false); + return false; } } diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportImpl.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportImpl.java index da87892929c..507d8d1c556 100644 --- a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportImpl.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpTransportImpl.java @@ -18,17 +18,21 @@ package org.apache.servicecomb.http.client.common; import java.io.IOException; +import java.net.URI; import java.util.Map; import org.apache.http.client.HttpClient; import org.apache.http.util.EntityUtils; import org.apache.servicecomb.foundation.auth.SignRequest; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Created by on 2019/10/16. */ public class HttpTransportImpl implements HttpTransport { + private static final Logger LOGGER = LoggerFactory.getLogger(HttpTransportImpl.class); private static final String HEADER_CONTENT_TYPE = "Content-Type"; @@ -87,7 +91,7 @@ public HttpResponse doRequest(HttpRequest httpRequest) throws IOException { globalHeaders.forEach(httpRequest::addHeader); } - httpRequest.getHeaders().putAll(requestAuthHeaderProvider.loadAuthHeader(createSignRequest())); + httpRequest.getHeaders().putAll(requestAuthHeaderProvider.loadAuthHeader(createSignRequest(httpRequest.getUrl()))); //get Http response org.apache.http.HttpResponse response = httpClient.execute(httpRequest.getRealRequest()); @@ -98,9 +102,16 @@ public HttpResponse doRequest(HttpRequest httpRequest) throws IOException { response.getAllHeaders()); } - private static SignRequest createSignRequest() { - // Now the implementations do not process SignRequest, so return null. Maybe future will use it. - return null; + private static SignRequest createSignRequest(String url) { + try { + URI uri = URI.create(url); + SignRequest signRequest = new SignRequest(); + signRequest.setEndpoint(uri); + return signRequest; + } catch (Exception e) { + LOGGER.error("create signRequest failed!", e); + return null; + } } @Override diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpUtils.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpUtils.java index 37d5c207473..5f259cc3053 100644 --- a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpUtils.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/HttpUtils.java @@ -18,6 +18,7 @@ package org.apache.servicecomb.http.client.common; import java.io.IOException; +import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -55,7 +56,14 @@ public static String encodeURLParam(String value) throws IOException { if (value == null) { return ""; } - return URLEncoder.encode(value, "UTF-8"); + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } + + public static String decodeURLParam(String value) throws IOException { + if (value == null) { + return null; + } + return URLDecoder.decode(value, StandardCharsets.UTF_8); } public static String sha256Encode(String key, String data) throws Exception { diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/URLEndPoint.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/URLEndPoint.java index ba295c35363..e3e53bad06a 100644 --- a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/URLEndPoint.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/common/URLEndPoint.java @@ -36,7 +36,7 @@ public class URLEndPoint { private final boolean sslEnabled; - private final Map> querys; + private final Map> queries; private final String hostOrIp; @@ -49,7 +49,7 @@ public URLEndPoint(String endpoint) { throw new IllegalArgumentException("port not specified."); } port = uri.getPort(); - querys = splitQuery(uri); + queries = splitQuery(uri); if (endpoint.contains(HTTPS_KEY)) { sslEnabled = true; } else { @@ -68,7 +68,7 @@ public static Map> splitQuery(URI uri) { } public String getFirst(String key) { - List values = querys.get(key); + List values = queries.get(key); if (values == null) { return null; } diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/EngineConnectChangedEvent.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/EngineConnectChangedEvent.java new file mode 100644 index 00000000000..b2029f5c38c --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/EngineConnectChangedEvent.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.http.client.event; + +public class EngineConnectChangedEvent { +} diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/OperationEvents.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/OperationEvents.java similarity index 79% rename from clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/OperationEvents.java rename to clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/OperationEvents.java index 62274519ea4..69896f750ec 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/OperationEvents.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/event/OperationEvents.java @@ -15,10 +15,18 @@ * limitations under the License. */ -package org.apache.servicecomb.service.center.client; +package org.apache.servicecomb.http.client.event; public abstract class OperationEvents { public static class UnAuthorizedOperationEvent extends OperationEvents { + private final String address; + public UnAuthorizedOperationEvent(String address) { + this.address = address; + } + + public String getAddress() { + return address; + } } } diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/AbstractTask.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/AbstractTask.java index 3199eea082c..cc10ae8e142 100644 --- a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/AbstractTask.java +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/task/AbstractTask.java @@ -20,6 +20,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -30,7 +31,7 @@ public class AbstractTask { public class BackOffSleepTask implements Task { private static final long BASE = 3000; - private static final long MAX = 10 * 60 * 10000; + private static final long MAX = 10 * 60 * 1000; long waitTime; @@ -66,6 +67,8 @@ public void execute() { public static AtomicInteger taskCounter = new AtomicInteger(0); + private ScheduledExecutorService addrCheckExecutor; + protected AbstractTask(String taskName) { initTaskPool(taskName); Runtime.getRuntime().addShutdownHook(new Thread(AbstractTask.this::stop, taskName + "-shutdown-hook")); @@ -76,6 +79,13 @@ protected void initTaskPool(String taskName) { new Thread(task, taskName + "-" + taskCounter.getAndIncrement())); } + protected void schedulerCheckAddressAvailable(String taskName, Runnable task, long delayTime) { + if (addrCheckExecutor == null) { + addrCheckExecutor = Executors.newScheduledThreadPool(1, (t) -> new Thread(t, taskName)); + } + addrCheckExecutor.scheduleWithFixedDelay(task, delayTime, delayTime, TimeUnit.MILLISECONDS); + } + protected void startTask(Task task) { if (!running) { return; @@ -99,6 +109,10 @@ public void stop() { running = false; this.taskPool.shutdown(); this.taskPool.awaitTermination(10, TimeUnit.SECONDS); + if (addrCheckExecutor != null) { + this.addrCheckExecutor.shutdown(); + this.addrCheckExecutor.awaitTermination(10, TimeUnit.SECONDS); + } } catch (InterruptedException e) { LOGGER.warn("tasks not shutdown in time {}", e.getMessage()); } diff --git a/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/utils/ServiceCombServiceAvailableUtils.java b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/utils/ServiceCombServiceAvailableUtils.java new file mode 100644 index 00000000000..9139e65f96a --- /dev/null +++ b/clients/http-client-common/src/main/java/org/apache/servicecomb/http/client/utils/ServiceCombServiceAvailableUtils.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.http.client.utils; + +import org.apache.http.HttpStatus; +import org.apache.servicecomb.http.client.common.AbstractAddressManager; +import org.apache.servicecomb.http.client.common.HttpRequest; +import org.apache.servicecomb.http.client.common.HttpResponse; +import org.apache.servicecomb.http.client.common.HttpTransport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; + +public class ServiceCombServiceAvailableUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCombServiceAvailableUtils.class); + + public static void checkAddressAvailable(AbstractAddressManager addressManager, String address, + HttpTransport httpTransport, String path) { + String formatUrl = addressManager.formatUrl(path, true, address); + HttpRequest httpRequest = new HttpRequest(formatUrl, null, null, HttpRequest.GET); + try { + HttpResponse response = httpTransport.doRequest(httpRequest); + if (response.getStatusCode() == HttpStatus.SC_OK) { + addressManager.recordSuccessState(address); + return; + } + + // old server does not provide the check api, using TCP checks whether the server is ready. + if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND && telnetCheckAddress(address)) { + LOGGER.warn("[{}] path does not provide, tcp check address ready!", path); + addressManager.recordSuccessState(address); + } + } catch (IOException e) { + LOGGER.error("check isolation address [{}] available error!", address); + } + } + + private static boolean telnetCheckAddress(String address) { + URI ipPort = parseIpPortFromURI(address); + if (ipPort == null) { + return false; + } + try (Socket s = new Socket()) { + s.connect(new InetSocketAddress(ipPort.getHost(), ipPort.getPort()), 3000); + return true; + } catch (IOException e) { + LOGGER.warn("ping endpoint {} failed, It will be quarantined again.", address); + } + return false; + } + + private static URI parseIpPortFromURI(String address) { + try { + return new URI(address); + } catch (URISyntaxException e) { + LOGGER.error("build uri error with address [{}].", address); + return null; + } + } +} diff --git a/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java b/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java index 222a831d59a..b14d6827cb8 100644 --- a/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java +++ b/clients/http-client-common/src/test/java/org/apache/servicecomb/http/client/common/AbstractAddressManagerTest.java @@ -32,9 +32,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; - public class AbstractAddressManagerTest { private static final List addresses = new ArrayList<>(); @@ -49,9 +46,9 @@ public class AbstractAddressManagerTest { public void setUp() throws NoSuchFieldException, IllegalAccessException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); - addressManager1 = new AbstractAddressManager(addresses); - addressManager2 = new AbstractAddressManager("project", addresses); - addressManager3 = new AbstractAddressManager(null, addresses); + addressManager1 = new AbstractAddressManager(addresses, "", ""); + addressManager2 = new AbstractAddressManager("project", addresses, "", ""); + addressManager3 = new AbstractAddressManager(null, addresses, "", ""); Field addressManagerField = addressManager1.getClass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager1, 0); @@ -91,12 +88,7 @@ public void recordStateTest() throws ExecutionException { zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "TEST"); - AbstractAddressManager addressManager = new AbstractAddressManager(addresses) { - @Override - protected boolean telnetTest(String address) { - return true; - } - }; + AbstractAddressManager addressManager = new AbstractAddressManager(addresses, "", "") {}; addressManager.refreshEndpoint(event, "TEST"); @@ -109,7 +101,7 @@ protected boolean telnetTest(String address) { Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); // test fail 2 times ,it will not be isolated - addressManager.recordSuccessState(address); + addressManager.resetFailureStatus(address); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); // test recodeStatus times @@ -122,20 +114,8 @@ protected boolean telnetTest(String address) { addressManager.recordFailState(address); Assertions.assertEquals("http://127.0.0.4:30100", addressManager.address()); - // mock cacheAddress status refresh after 10 minute - Cache cache = CacheBuilder.newBuilder() - .maximumSize(100) - .expireAfterWrite(10, TimeUnit.MINUTES) - .build(); - cache.put("http://127.0.0.3:30100", true); - - addressManager.setAddressIsolationStatus(cache); - Cache result = addressManager.getAddressIsolationStatus(); - Assertions.assertEquals(true, result.get("http://127.0.0.3:30100", () -> false)); - // test restore isolation - addressManager.checkHistory(); - addressManager.rejoinAddress("http://127.0.0.3:30100"); + addressManager.recordSuccessState("http://127.0.0.3:30100"); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); Assertions.assertEquals("http://127.0.0.3:30100", addressManager.address()); } @@ -144,7 +124,7 @@ protected boolean telnetTest(String address) { @Test public void testMultipleThread() throws Exception { - AbstractAddressManager addressManager = new AbstractAddressManager(addresses); + AbstractAddressManager addressManager = new AbstractAddressManager(addresses, "", ""); String address = "http://127.0.0.3:30100"; CountDownLatch latch = new CountDownLatch(2); @@ -322,4 +302,27 @@ public void normalizeIPV6Test() { uri = addressManager1.normalizeUri("rest://[2008::7:957f:b2d6:1af4:a1f8]:30100"); Assertions.assertEquals("http://[2008::7:957f:b2d6:1af4:a1f8]:30100", uri); } + + @Test + public void compareAndGetAddressTest() { + List testAddr = new ArrayList<>(); + testAddr.add("https://192.168.20.160:30100"); + testAddr.add("https://127.0.0.1:30100"); + testAddr.add("https://127.0.0.3:30100"); + AbstractAddressManager manager = new AbstractAddressManager(testAddr, "", ""); + Assertions.assertTrue(manager.compareAndGetAddress("192.168.20.16").isEmpty()); + Assertions.assertEquals("https://192.168.20.160:30100", manager.compareAndGetAddress("192.168.20.160")); + } + + @Test + public void AddressAffinityTest() { + List testAddr = new ArrayList<>(); + testAddr.add("https://192.168.20.160:30100?region=region1&availableZone=zone1"); + testAddr.add("https://127.0.0.1:30100"); + AbstractAddressManager manager = new AbstractAddressManager(testAddr, "region1", "zone1"); + Assertions.assertEquals("https://192.168.20.160:30100", manager.address()); + + AbstractAddressManager manager2 = new AbstractAddressManager("default", testAddr, "region1", "zone1"); + Assertions.assertEquals("https://192.168.20.160:30100", manager2.address()); + } } diff --git a/clients/pom.xml b/clients/pom.xml index df660f6c051..49f481934cc 100644 --- a/clients/pom.xml +++ b/clients/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 diff --git a/clients/service-center-client/README.md b/clients/service-center-client/README.md index b3a933bf9a3..a6b4c6d4936 100644 --- a/clients/service-center-client/README.md +++ b/clients/service-center-client/README.md @@ -1,15 +1,15 @@ ## ServiceComb-Service-Center Client for Java - Sample Java client for ServiceComb-Service-Center HTTP API. + Sample Java client for ServiceComb-Service-Center HTTP API. If you want more information about the ServiceComb-Service-Center HTTP API, go [here](https://github.com/apache/servicecomb-service-center/blob/master/docs/openapi/v4.yaml). - + ### Build & Install - + local Build from source : ``` maven clean install ``` - + add dependency to maven ``` @@ -79,14 +79,14 @@ MicroservicesResponse services = client.getMicroserviceList(); for(Microservice microservice : services.getServices()) { for (MicroserviceInstance instance: client.getMicroserviceInstanceList(microservice.getServiceId()).getInstances()) - { - client.sendHeartBeats(new HeartbeatsRequest(microservice.getServiceId(),instance.getInstanceId())); + { + client.sendHeartBeats(new HeartbeatsRequest(microservice.getServiceId(),instance.getInstanceId())); } } ``` #### Other API -You can see client API code and tests, go [here](https://github.com/apache/servicecomb-java-chassis/blob/master/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java) +You can see client API code and tests, go [here](https://github.com/apache/servicecomb-java-chassis/blob/master/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java) ### More development diff --git a/clients/service-center-client/pom.xml b/clients/service-center-client/pom.xml index 4eb051fda9f..ccc3900e9db 100755 --- a/clients/service-center-client/pom.xml +++ b/clients/service-center-client/pom.xml @@ -22,7 +22,7 @@ clients org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManager.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManager.java index a5fd128ab1e..b19db919e81 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManager.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManager.java @@ -26,8 +26,9 @@ import com.google.common.eventbus.Subscribe; public class ServiceCenterAddressManager extends AbstractAddressManager { - public ServiceCenterAddressManager(String projectName, List addresses, EventBus eventBus) { - super(projectName, addresses); + public ServiceCenterAddressManager(String projectName, List addresses, EventBus eventBus, String region, + String availableZone) { + super(projectName, addresses, region, availableZone); eventBus.register(this); } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java index 09f30dc93da..97aeaa9931a 100755 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterClient.java @@ -25,6 +25,7 @@ import java.util.Map; import org.apache.http.HttpStatus; +import org.apache.http.client.config.RequestConfig; import org.apache.http.client.utils.URIBuilder; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; @@ -32,13 +33,13 @@ import org.apache.servicecomb.http.client.common.HttpTransport; import org.apache.servicecomb.http.client.common.HttpTransportFactory; import org.apache.servicecomb.http.client.common.HttpUtils; -import org.apache.servicecomb.service.center.client.OperationEvents.UnAuthorizedOperationEvent; import org.apache.servicecomb.service.center.client.exception.OperationException; import org.apache.servicecomb.service.center.client.model.CreateMicroserviceInstanceRequest; import org.apache.servicecomb.service.center.client.model.CreateMicroserviceRequest; import org.apache.servicecomb.service.center.client.model.CreateSchemaRequest; import org.apache.servicecomb.service.center.client.model.ErrorMessage; import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse; +import org.apache.servicecomb.service.center.client.model.Framework; import org.apache.servicecomb.service.center.client.model.GetSchemaListResponse; import org.apache.servicecomb.service.center.client.model.GetSchemaResponse; import org.apache.servicecomb.service.center.client.model.HeartbeatsRequest; @@ -58,6 +59,7 @@ import org.apache.servicecomb.service.center.client.model.UpdatePropertiesRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; @@ -65,16 +67,24 @@ public class ServiceCenterClient implements ServiceCenterOperation { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterClient.class); + private static final String CLIENT_CONNECT_TIMEOUT = "servicecomb.registry.sc.client.timeout.connect"; + + private static final String CLIENT_REQUEST_TIMEOUT = "servicecomb.registry.sc.client.timeout.request"; + + private static final String CLIENT_SOCKET_TIMEOUT = "servicecomb.registry.sc.client.timeout.socket"; + private final ServiceCenterRawClient httpClient; - private EventBus eventBus; + private final ServiceCenterAddressManager addressManager; - public ServiceCenterClient(ServiceCenterRawClient httpClient) { + public ServiceCenterClient(ServiceCenterRawClient httpClient, ServiceCenterAddressManager addressManager) { this.httpClient = httpClient; + this.addressManager = addressManager; } public ServiceCenterClient setEventBus(EventBus eventBus) { - this.eventBus = eventBus; + addressManager.setEventBus(eventBus); + this.httpClient.setEventBus(eventBus); return this; } @@ -82,14 +92,28 @@ public ServiceCenterClient(ServiceCenterAddressManager addressManager, SSLProperties sslProperties, RequestAuthHeaderProvider requestAuthHeaderProvider, String tenantName, - Map extraGlobalHeaders) { - HttpTransport httpTransport = HttpTransportFactory.createHttpTransport(sslProperties, requestAuthHeaderProvider); + Map extraGlobalHeaders, + Environment environment) { + HttpTransport httpTransport = HttpTransportFactory.createHttpTransport(sslProperties, requestAuthHeaderProvider, + buildRequestConfig(environment)); httpTransport.addHeaders(extraGlobalHeaders); this.httpClient = new ServiceCenterRawClient.Builder() .setTenantName(tenantName) .setAddressManager(addressManager) .setHttpTransport(httpTransport).build(); + this.addressManager = addressManager; + } + + private RequestConfig buildRequestConfig(Environment environment) { + RequestConfig.Builder builder = HttpTransportFactory.defaultRequestConfig(); + if (environment == null) { + return builder.build(); + } + builder.setConnectTimeout(environment.getProperty(CLIENT_CONNECT_TIMEOUT, int.class, 5000)); + builder.setConnectionRequestTimeout(environment.getProperty(CLIENT_REQUEST_TIMEOUT, int.class, 5000)); + builder.setSocketTimeout(environment.getProperty(CLIENT_SOCKET_TIMEOUT, int.class, 5000)); + return builder.build(); } @Override @@ -99,7 +123,6 @@ public MicroserviceInstancesResponse getServiceCenterInstances() { if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), MicroserviceInstancesResponse.class); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service-center instances fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -120,7 +143,6 @@ public RegisteredMicroserviceResponse registerMicroservice(Microservice microser if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceResponse.class); } - sendUnAuthorizedEvent(response); throw new OperationException( "register service fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -138,7 +160,6 @@ public MicroservicesResponse getMicroserviceList() { if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), MicroservicesResponse.class); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service List fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -163,7 +184,6 @@ public RegisteredMicroserviceResponse queryServiceId(Microservice microservice) if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceResponse.class); } - sendUnAuthorizedEvent(response); LOGGER.info("Query serviceId fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); @@ -186,7 +206,6 @@ public Microservice getMicroserviceByServiceId(String serviceId) { .deserialize(response.getContent(), MicroserviceResponse.class); return microserviceResponse.getService(); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service message fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -208,7 +227,6 @@ public RegisteredMicroserviceInstanceResponse registerMicroserviceInstance(Micro if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), RegisteredMicroserviceInstanceResponse.class); } - sendUnAuthorizedEvent(response); throw new OperationException( "register service instance fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -244,7 +262,11 @@ public FindMicroserviceInstancesResponse findMicroserviceInstance(String consume result.setModified(false); return result; } - sendUnAuthorizedEvent(response); + if (response.getStatusCode() == HttpStatus.SC_TOO_MANY_REQUESTS) { + LOGGER.warn("rate limited, keep the local service {}#{} instance cache unchanged.", appId, serviceName); + result.setModified(false); + return result; + } throw new OperationException( "get service instances list fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -263,7 +285,6 @@ public MicroserviceInstancesResponse getMicroserviceInstanceList(String serviceI if (response.getStatusCode() == HttpStatus.SC_OK) { return HttpUtils.deserialize(response.getContent(), MicroserviceInstancesResponse.class); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service instances list fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -284,7 +305,6 @@ public MicroserviceInstance getMicroserviceInstance(String serviceId, String ins .deserialize(response.getContent(), MicroserviceInstanceResponse.class); return instanceResponse.getInstance(); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service instance message fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -304,7 +324,6 @@ public void deleteMicroserviceInstance(String serviceId, String instanceId) { LOGGER.info("Delete service instance successfully."); return; } - sendUnAuthorizedEvent(response); throw new OperationException( "delete service instance fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -324,7 +343,6 @@ public boolean updateMicroserviceInstanceStatus(String serviceId, String instanc if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( "update service instance status fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -343,7 +361,6 @@ public void sendHeartBeats(HeartbeatsRequest heartbeatsRequest) { if (response.getStatusCode() == HttpStatus.SC_OK) { return; } - sendUnAuthorizedEvent(response); throw new OperationException( "heartbeats fails, statusCode = " + response.getStatusCode() + "; message = " + response.getMessage() + "; content = " + response.getContent()); @@ -363,7 +380,6 @@ public boolean sendHeartBeat(String serviceId, String instanceId) { if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( "heartbeats fails, statusCode = " + response.getStatusCode() + "; message = " + response.getMessage() + "; content = " + response.getContent()); @@ -388,7 +404,6 @@ public List getServiceSchemasList(String serviceId, boolean withCont .deserialize(response.getContent(), GetSchemaListResponse.class); return getSchemaResponse.getSchemas(); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service schemas list fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -415,7 +430,6 @@ public String getServiceSchemaContext(String serviceId, String schemaId) { GetSchemaResponse getSchemaResponse = HttpUtils.deserialize(response.getContent(), GetSchemaResponse.class); return getSchemaResponse.getSchema(); } - sendUnAuthorizedEvent(response); throw new OperationException( "get service schema context fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -435,7 +449,6 @@ public boolean registerSchema(String serviceId, String schemaId, CreateSchemaReq if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -458,7 +471,6 @@ public boolean updateServiceSchemaContext(String serviceId, SchemaInfo schemaInf if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -478,7 +490,6 @@ public boolean batchUpdateServiceSchemaContext(String serviceId, ModifySchemasRe if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( "update service schema fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() @@ -489,18 +500,12 @@ public boolean batchUpdateServiceSchemaContext(String serviceId, ModifySchemasRe } } - private void sendUnAuthorizedEvent(HttpResponse response) { - if (this.eventBus != null && response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) { - this.eventBus.post(new UnAuthorizedOperationEvent()); - } - } - @Override - public RbacTokenResponse queryToken(RbacTokenRequest request) { + public RbacTokenResponse queryToken(RbacTokenRequest request, String host) { try { + String queryAddress = addressManager.compareAndGetAddress(host); HttpResponse response = httpClient - .postHttpRequestAbsoluteUrl("/v4/token", null, - HttpUtils.serialize(request)); + .postHttpRequestAbsoluteUrl("/v4/token", null, HttpUtils.serialize(request), queryAddress); if (response.getStatusCode() == HttpStatus.SC_OK) { RbacTokenResponse result = HttpUtils.deserialize(response.getContent(), RbacTokenResponse.class); result.setStatusCode(HttpStatus.SC_OK); @@ -530,23 +535,35 @@ public RbacTokenResponse queryToken(RbacTokenRequest request) { } @Override - public boolean updateMicroserviceProperties(String serviceId, Map serviceProperties) { + public boolean updateMicroserviceProperties(String serviceId, Map serviceProperties, + Framework framework) { try { UpdatePropertiesRequest request = new UpdatePropertiesRequest(); request.setProperties(serviceProperties); + request.setFramework(framework); HttpResponse response = httpClient.putHttpRequest( "/registry/microservices/" + serviceId + "/properties", null, HttpUtils.serialize(request)); if (response.getStatusCode() == HttpStatus.SC_OK) { return true; } - sendUnAuthorizedEvent(response); throw new OperationException( - "update service instance status fails, statusCode = " + response.getStatusCode() + "; message = " + response + "update service properties fails, statusCode = " + response.getStatusCode() + "; message = " + response .getMessage() + "; content = " + response.getContent()); } catch (IOException e) { throw new OperationException( - "update service instance status fails", e); + "update service properties fails", e); + } + } + + @Override + public void checkIsolationAddressAvailable() { + List isolationAddresses = addressManager.getIsolationAddresses(); + if (isolationAddresses.isEmpty()) { + return; + } + for (String address : isolationAddresses) { + httpClient.checkAddressAvailable(address); } } } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java index 808c13daf3d..a1ab524c187 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterDiscovery.java @@ -17,12 +17,12 @@ package org.apache.servicecomb.service.center.client; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import org.apache.servicecomb.http.client.task.AbstractTask; @@ -101,6 +101,8 @@ public static class SubscriptionValue { private final Object lock = new Object(); + private final Random random = new Random(); + public ServiceCenterDiscovery(ServiceCenterClient serviceCenterClient, EventBus eventBus) { super("service-center-discovery-task"); this.serviceCenterClient = serviceCenterClient; @@ -153,13 +155,12 @@ public void onPullInstanceEvent(PullInstanceEvent event) { startTask(new PullInstanceOnceTask()); } - private List pullInstance(SubscriptionKey k, SubscriptionValue v, boolean sendChangedEvent) { + private void pullInstance(SubscriptionKey k, SubscriptionValue v, boolean sendChangedEvent) { if (myselfServiceId == null) { // registration not ready - return Collections.emptyList(); + return; } - List failedKeys = new ArrayList<>(); try { FindMicroserviceInstancesResponse instancesResponse = serviceCenterClient .findMicroserviceInstance(myselfServiceId, k.appId, k.serviceName, ALL_VERSION, v.revision); @@ -186,14 +187,9 @@ private List pullInstance(SubscriptionKey k, SubscriptionValue } } } catch (Exception e) { - LOGGER.warn("find service {}#{} instance failed.", k.appId, k.serviceName, e); - if (!(e.getCause() instanceof IOException)) { - // for IOException, do not remove cache, or when service center - // not available, invocation between microservices will fail. - failedKeys.add(k); - } + LOGGER.warn("find service {}#{} instance failed, remaining local instances cache [{}], cause message: {}", + k.appId, k.serviceName, instanceToString(v.instancesCache), e.getMessage()); } - return failedKeys; } private void setMicroserviceInfo(List instances) { @@ -216,10 +212,17 @@ class PullInstanceTask implements Task { public void execute() { pullAllInstance(); - startTask(new BackOffSleepTask(pollInterval, new PullInstanceTask())); + startTask(new BackOffSleepTask(buildPollIntervalWithSalt(), new PullInstanceTask())); } } + private long buildPollIntervalWithSalt() { + int positive = random.nextInt(5); + int sign = random.nextBoolean() ? 1 : -1; + long currentPollInterval = pollInterval + sign * positive * 1000; + return currentPollInterval > 0 ? currentPollInterval : pollInterval; + } + class PullInstanceOnceTask implements Task { @Override public void execute() { @@ -232,13 +235,15 @@ public void execute() { } private synchronized void pullAllInstance() { - List failedInstances = new ArrayList<>(); - instancesCache.forEach((k, v) -> failedInstances.addAll(pullInstance(k, v, true))); - if (failedInstances.isEmpty()) { - return; - } - failedInstances.forEach(instancesCache::remove); - failedInstances.clear(); + List emptyInstancesCache = new ArrayList<>(); + instancesCache.forEach((k, v) -> { + pullInstance(k, v, true); + if (v.instancesCache.isEmpty()) { + emptyInstancesCache.add(k); + } + }); + emptyInstancesCache.forEach(instancesCache::remove); + emptyInstancesCache.clear(); } private static String instanceToString(List instances) { @@ -252,6 +257,8 @@ private static String instanceToString(List instances) { sb.append(endpoint.length() > 64 ? endpoint.substring(0, 64) : endpoint); sb.append("|"); } + sb.append(instance.getStatus()); + sb.append("|"); } return sb.toString(); } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterOperation.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterOperation.java index 5521a732244..fd39a4ce963 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterOperation.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterOperation.java @@ -23,6 +23,7 @@ import org.apache.servicecomb.service.center.client.exception.OperationException; import org.apache.servicecomb.service.center.client.model.CreateSchemaRequest; import org.apache.servicecomb.service.center.client.model.FindMicroserviceInstancesResponse; +import org.apache.servicecomb.service.center.client.model.Framework; import org.apache.servicecomb.service.center.client.model.HeartbeatsRequest; import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; @@ -159,7 +160,6 @@ boolean updateMicroserviceInstanceStatus(String serviceId, String instanceId, * Batch send heartbeats to service-center * * @param heartbeatsRequest - * @return * @throws OperationException */ void sendHeartBeats(HeartbeatsRequest heartbeatsRequest); @@ -184,7 +184,7 @@ boolean updateMicroserviceInstanceStatus(String serviceId, String instanceId, * @return if heartbeat is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ - RbacTokenResponse queryToken(RbacTokenRequest request); + RbacTokenResponse queryToken(RbacTokenRequest request, String host); /** * Update properties of microservice @@ -192,5 +192,11 @@ boolean updateMicroserviceInstanceStatus(String serviceId, String instanceId, * @return if update is successful * @throws OperationException If some problems happened to contact service center or non http 200 returned. */ - boolean updateMicroserviceProperties(String microserviceId, Map serviceProperties); + boolean updateMicroserviceProperties(String microserviceId, Map serviceProperties, + Framework framework); + + /** + * Check serviceCenter isolation address available + */ + void checkIsolationAddressAvailable(); } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java index 6c293004cc0..e233f15fb52 100755 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClient.java @@ -21,23 +21,33 @@ import java.util.HashMap; import java.util.Map; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; import org.apache.servicecomb.http.client.common.HttpRequest; import org.apache.servicecomb.http.client.common.HttpResponse; import org.apache.servicecomb.http.client.common.HttpTransport; +import org.apache.servicecomb.http.client.event.OperationEvents.UnAuthorizedOperationEvent; +import org.apache.servicecomb.http.client.utils.ServiceCombServiceAvailableUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.eventbus.EventBus; + public class ServiceCenterRawClient { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCenterRawClient.class); private static final String HEADER_TENANT_NAME = "x-domain-name"; + private static final String ADDRESS_CHECK_PATH = "/v4/default/registry/health/readiness"; + private final String tenantName; private final HttpTransport httpTransport; private final ServiceCenterAddressManager addressManager; + private EventBus eventBus; + private ServiceCenterRawClient(String tenantName, HttpTransport httpTransport, ServiceCenterAddressManager addressManager) { this.httpTransport = httpTransport; @@ -45,41 +55,40 @@ private ServiceCenterRawClient(String tenantName, HttpTransport httpTransport, this.addressManager = addressManager; } + public void setEventBus(EventBus eventBus) { + this.eventBus = eventBus; + } + public HttpResponse getHttpRequest(String url, Map headers, String content) throws IOException { - return doHttpRequest(url, false, headers, content, HttpRequest.GET); + return doHttpRequest(url, false, headers, content, HttpRequest.GET, ""); } - public HttpResponse postHttpRequestAbsoluteUrl(String url, Map headers, String content) - throws IOException { - return doHttpRequest(url, true, headers, content, HttpRequest.POST); + public HttpResponse postHttpRequestAbsoluteUrl(String url, Map headers, String content, + String address) throws IOException { + return doHttpRequest(url, true, headers, content, HttpRequest.POST, address); } public HttpResponse postHttpRequest(String url, Map headers, String content) throws IOException { - return doHttpRequest(url, false, headers, content, HttpRequest.POST); + return doHttpRequest(url, false, headers, content, HttpRequest.POST, ""); } public HttpResponse putHttpRequest(String url, Map headers, String content) throws IOException { - return doHttpRequest(url, false, headers, content, HttpRequest.PUT); + return doHttpRequest(url, false, headers, content, HttpRequest.PUT, ""); } public HttpResponse deleteHttpRequest(String url, Map headers, String content) throws IOException { - return doHttpRequest(url, false, headers, content, HttpRequest.DELETE); + return doHttpRequest(url, false, headers, content, HttpRequest.DELETE, ""); } private HttpResponse doHttpRequest(String url, boolean absoluteUrl, Map headers, String content, - String method) - throws IOException { - String address = addressManager.address(); + String method, String queryAddress) throws IOException { + String address = StringUtils.isEmpty(queryAddress) ? addressManager.address() : queryAddress; String formatUrl = addressManager.formatUrl(url, absoluteUrl, address); - if (headers == null) { - headers = new HashMap<>(); - } - headers.put(HEADER_TENANT_NAME, tenantName); - HttpRequest httpRequest = new HttpRequest(formatUrl, headers, content, method); - + HttpRequest httpRequest = buildHttpRequest(formatUrl, headers, content, method); + HttpResponse httpResponse; try { - HttpResponse httpResponse = httpTransport.doRequest(httpRequest); - addressManager.recordSuccessState(address); + httpResponse = httpTransport.doRequest(httpRequest); + recordAndSendUnAuthorizedEvent(httpResponse, address); return httpResponse; } catch (IOException e) { addressManager.recordFailState(address); @@ -88,7 +97,9 @@ private HttpResponse doHttpRequest(String url, boolean absoluteUrl, Map headers, String content, String method) { + if (headers == null) { + headers = new HashMap<>(); + } + headers.put(HEADER_TENANT_NAME, tenantName); + return new HttpRequest(url, headers, content, method); + } + public static class Builder { private String tenantName; diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRegistration.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRegistration.java index 555c739d4c9..93988ea8948 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRegistration.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterRegistration.java @@ -114,6 +114,7 @@ public List getSchemaInfos() { public void startRegistration() { startTask(new RegisterMicroserviceTask(0)); + schedulerCheckAddressAvailable("sc-addr-check", new CheckAddressTask(), heartBeatInterval); } class RegisterMicroserviceTask implements Task { @@ -147,13 +148,13 @@ public void execute() { microservice.setProperties(newMicroservice.getProperties()); microservice.getProperties().putAll(propertiesTemp); if (serviceCenterClient.updateMicroserviceProperties(serviceResponse.getServiceId(), - microservice.getProperties())) { + microservice.getProperties(), microservice.getFramework())) { LOGGER.info( - "microservice is already registered. Update microservice properties successfully. properties=[{}]", - microservice.getProperties()); + "microservice is already registered. Update microservice properties successfully. properties=[{}], " + + "frameworkVersion [{}]", microservice.getProperties(), microservice.getFramework().getVersion()); } else { - LOGGER.error("microservice is already registered. Update microservice properties failed. properties=[{}]", - microservice.getProperties()); + LOGGER.error("microservice is already registered. Update microservice properties failed. properties=[{}], " + + "frameworkVersion [{}]", microservice.getProperties(), microservice.getFramework().getVersion()); } microservice.setServiceId(serviceResponse.getServiceId()); @@ -286,7 +287,7 @@ public void execute() { } if (!serviceCenterClient.sendHeartBeat(microservice.getServiceId(), microserviceInstance.getInstanceId())) { - LOGGER.error("send heart failed, and will try again."); + LOGGER.warn("send heart failed, and will try again."); eventBus.post(new HeartBeatEvent(false, microservice, microserviceInstance)); startTask(new BackOffSleepTask(failedCount + 1, new SendHeartBeatTask(failedCount + 1))); } else { @@ -296,10 +297,22 @@ public void execute() { new BackOffSleepTask(Math.max(heartBeatInterval, heartBeatRequestTimeout), new SendHeartBeatTask(0))); } } catch (Exception e) { - LOGGER.error("send heart failed, and will try again.", e); + // If heartbeat failures three times, log error stack help troubleshooting. Others just log message as warn. + if (failedCount == 2) { + LOGGER.error("send heart failed, and will try again.", e); + } else { + LOGGER.warn("send heart failed, and will try again. message [{}]", e.getMessage()); + } eventBus.post(new HeartBeatEvent(false, microservice, microserviceInstance)); startTask(new BackOffSleepTask(failedCount + 1, new SendHeartBeatTask(failedCount + 1))); } } } + + class CheckAddressTask implements Runnable { + @Override + public void run() { + serviceCenterClient.checkIsolationAddressAvailable(); + } + } } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java index a8be8b98b78..9f24a74a55a 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/ServiceCenterWatch.java @@ -17,12 +17,15 @@ package org.apache.servicecomb.service.center.client; +import java.net.URI; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.servicecomb.foundation.auth.SignRequest; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; import org.apache.servicecomb.http.client.common.WebSocketListener; @@ -67,7 +70,7 @@ public class ServiceCenterWatch implements WebSocketListener { private String serviceId; - private int continuousError = 0; + private AtomicInteger continuousError = new AtomicInteger(0); private final AtomicBoolean reconnecting = new AtomicBoolean(false); @@ -107,7 +110,7 @@ private void startWatch() { Map headers = new HashMap<>(); headers.put("x-domain-name", this.tenantName); headers.putAll(this.extraGlobalHeaders); - headers.putAll(this.requestAuthHeaderProvider.loadAuthHeader(null)); + headers.putAll(this.requestAuthHeaderProvider.loadAuthHeader(createSignRequest(address))); currentServerUri = convertAddress(address); LOGGER.info("start watch to address {}", currentServerUri); webSocketTransport = new WebSocketTransport(currentServerUri, sslProperties, @@ -121,6 +124,17 @@ private void startWatch() { }); } + private SignRequest createSignRequest(String url) { + try { + URI uri = URI.create(url); + SignRequest signRequest = new SignRequest(); + signRequest.setEndpoint(uri); + return signRequest; + } catch (Exception e) { + return null; + } + } + private String convertAddress(String address) { String url = String.format(WATCH, project, serviceId); if (address.startsWith(HTTP)) { @@ -143,7 +157,7 @@ private void reconnect() { if (reconnecting.getAndSet(true)) { return; } - continuousError++; + continuousError.incrementAndGet(); if (webSocketTransport != null) { webSocketTransport.close(); } @@ -151,11 +165,11 @@ private void reconnect() { } private void backOff() { - if (this.continuousError <= 0) { + if (this.continuousError.get() <= 0) { return; } try { - Thread.sleep(Math.min(SLEEP_MAX, this.continuousError * this.continuousError * SLEEP_BASE)); + Thread.sleep(Math.min(SLEEP_MAX, this.continuousError.get() * this.continuousError.get() * SLEEP_BASE)); } catch (InterruptedException e) { // do not care } @@ -183,7 +197,7 @@ public void onOpen(ServerHandshake serverHandshake) { LOGGER.info("web socket connected to server {}, status={}, message={}", currentServerUri, serverHandshake.getHttpStatus(), serverHandshake.getHttpStatusMessage()); - continuousError = 0; + continuousError.set(0); reconnecting.set(false); } } diff --git a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/UpdatePropertiesRequest.java b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/UpdatePropertiesRequest.java index b10586208a8..d7d4b093930 100644 --- a/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/UpdatePropertiesRequest.java +++ b/clients/service-center-client/src/main/java/org/apache/servicecomb/service/center/client/model/UpdatePropertiesRequest.java @@ -22,6 +22,8 @@ public class UpdatePropertiesRequest { private Map properties; + private Framework framework; + public Map getProperties() { return properties; } @@ -29,4 +31,12 @@ public Map getProperties() { public void setProperties(Map properties) { this.properties = properties; } + + public Framework getFramework() { + return framework; + } + + public void setFramework(Framework framework) { + this.framework = framework; + } } diff --git a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManagerTest.java b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManagerTest.java index f5c316cd848..a2888cfad1b 100644 --- a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManagerTest.java +++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterAddressManagerTest.java @@ -41,7 +41,7 @@ class ServiceCenterAddressManagerTest { @Test public void getUrlPrefix() { addresses.add("http://127.0.0.1:30103"); - addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus()); + addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); Assertions.assertNotNull(addressManager1); @@ -55,7 +55,7 @@ public void getUrlPrefix() { @Test public void formatUrlTest() { addresses.add("http://127.0.0.1:30103"); - addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus()); + addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); Assertions.assertNotNull(addressManager1); String address = addressManager1.address(); @@ -76,7 +76,7 @@ public void onRefreshEndpointEvent() { Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); - addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus()); + addressManager1 = new ServiceCenterAddressManager("project", addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "SERVICECENTER"); addressManager1.refreshEndpoint(event, "SERVICECENTER"); diff --git a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterClientTest.java b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterClientTest.java index 12ad9cda3ab..c486b2bd6ce 100755 --- a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterClientTest.java +++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterClientTest.java @@ -19,10 +19,12 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.apache.servicecomb.http.client.common.HttpResponse; +import org.apache.servicecomb.service.center.client.model.Framework; import org.apache.servicecomb.service.center.client.model.HeartbeatsRequest; import org.apache.servicecomb.service.center.client.model.InstancesRequest; import org.apache.servicecomb.service.center.client.model.Microservice; @@ -41,11 +43,18 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.google.common.eventbus.EventBus; /** * Created by on 2019/10/17. */ public class ServiceCenterClientTest { + private final ServiceCenterAddressManager addressManager; + + public ServiceCenterClientTest() { + this.addressManager = new ServiceCenterAddressManager("default", Arrays.asList("http://127.0.0.1:30100"), + new EventBus(), "", ""); + } @Test public void TestGetServiceCenterInstances() throws IOException { @@ -92,7 +101,7 @@ public void TestGetServiceCenterInstances() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/health", null, null)).thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroserviceInstancesResponse serviceCenterInstances = serviceCenterClient.getServiceCenterInstances(); Assertions.assertNotNull(serviceCenterInstances); @@ -121,7 +130,7 @@ public void TestRegistryService() throws IOException { Mockito.anyString())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); RegisteredMicroserviceResponse actualResponse = serviceCenterClient.registerMicroservice(microservice); Assertions.assertNotNull(actualResponse); @@ -179,7 +188,7 @@ public void TestGetServiceMessage() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/microservices/111111", null, null)) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); Microservice microservices = serviceCenterClient.getMicroserviceByServiceId("111111"); Assertions.assertNotNull(microservices); @@ -207,7 +216,7 @@ public void TestGetServiceList() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroservicesResponse actualMicroservicesResponse = serviceCenterClient.getMicroserviceList(); Assertions.assertNotNull(actualMicroservicesResponse); @@ -228,7 +237,7 @@ public void TestQueryServiceId() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); Microservice microservice = new Microservice("Test111"); RegisteredMicroserviceResponse actualServiceId = serviceCenterClient.queryServiceId(microservice); @@ -257,7 +266,7 @@ public void TestRegisterServiceInstance() throws IOException { Mockito.anyString())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); RegisteredMicroserviceInstanceResponse actualResponse = serviceCenterClient.registerMicroserviceInstance(instance); Assertions.assertNotNull(actualResponse); @@ -276,7 +285,7 @@ public void TestDeleteServiceInstance() throws IOException { Mockito.when(serviceCenterRawClient.deleteHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); serviceCenterClient.deleteMicroserviceInstance("111", "222"); } @@ -310,7 +319,7 @@ public void TestGetServiceInstanceList() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest("/registry/microservices/222222/instances", null, null)) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroserviceInstancesResponse serviceCenterInstances = serviceCenterClient .getMicroserviceInstanceList("222222"); @@ -364,7 +373,7 @@ public void TestGetServiceInstanceMessage() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); MicroserviceInstance responseInstance = serviceCenterClient .getMicroserviceInstance("111", "222"); @@ -394,7 +403,7 @@ public void TestSendHeartBeats() throws IOException { Mockito.anyString())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); serviceCenterClient.sendHeartBeats(heartbeatsRequest); } @@ -410,7 +419,7 @@ public void TestUpdateServicesInstanceStatus() throws IOException { Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); Boolean result = serviceCenterClient .updateMicroserviceInstanceStatus("111", "222", MicroserviceInstanceStatus.UP); @@ -440,7 +449,7 @@ public void TestGetServiceSchemas() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); List schemaResponse = serviceCenterClient .getServiceSchemasList("111", false); @@ -468,7 +477,7 @@ public void TestGetServiceSchemasContext() throws IOException { Mockito.when(serviceCenterRawClient.getHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); String schemaContext = serviceCenterClient .getServiceSchemaContext("111", "222"); @@ -488,7 +497,7 @@ public void TestUpdateServiceSchema() throws IOException { Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); boolean result = serviceCenterClient .updateServiceSchemaContext("111", new SchemaInfo()); @@ -506,9 +515,9 @@ public void testUpdateMicroserviceProperties() throws IOException { Mockito.when(serviceCenterRawClient.putHttpRequest(Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(httpResponse); - ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient); + ServiceCenterClient serviceCenterClient = new ServiceCenterClient(serviceCenterRawClient, addressManager); boolean result = serviceCenterClient - .updateMicroserviceProperties("111", new HashMap()); + .updateMicroserviceProperties("111", new HashMap(), new Framework()); Assertions.assertTrue(result); } diff --git a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java index 29337e31a44..48f3bd914c6 100755 --- a/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java +++ b/clients/service-center-client/src/test/java/org/apache/servicecomb/service/center/client/ServiceCenterRawClientTest.java @@ -41,7 +41,8 @@ public class ServiceCenterRawClientTest { public void TestDefaultParameter() throws IOException { HttpTransport httpTransport = Mockito.mock(HttpTransport.class); - ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager(PROJECT_NAME, Arrays.asList("http://127.0.0.1:30100"), new EventBus()); + ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager(PROJECT_NAME, + Arrays.asList("http://127.0.0.1:30100"), new EventBus(), "", ""); ServiceCenterRawClient client = new ServiceCenterRawClient.Builder() .setHttpTransport(httpTransport) .setAddressManager(addressManager) diff --git a/common/common-access-log/pom.xml b/common/common-access-log/pom.xml index 1a12f622367..e572e6bfd94 100644 --- a/common/common-access-log/pom.xml +++ b/common/common-access-log/pom.xml @@ -21,7 +21,7 @@ common org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -41,6 +41,10 @@ org.apache.servicecomb common-rest + + org.apache.servicecomb + transport-rest-client + io.vertx vertx-codegen diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfiguration.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfiguration.java index 8de504201b7..abbb90adc19 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfiguration.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/AccessLogConfiguration.java @@ -17,10 +17,12 @@ package org.apache.servicecomb.common.accessLog; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +@Configuration public class AccessLogConfiguration { @Bean - public AccessLogBootListener accessLogBootListener() { + public AccessLogBootListener scbAccessLogBootListener() { return new AccessLogBootListener(); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/client/ClientDefaultInitializer.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/client/ClientDefaultInitializer.java index 658cea9fa55..43e5ad9b191 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/client/ClientDefaultInitializer.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/client/ClientDefaultInitializer.java @@ -21,7 +21,6 @@ import org.apache.servicecomb.common.accessLog.AccessLogInitializer; import org.apache.servicecomb.common.accessLog.core.AccessLogGenerator; import org.apache.servicecomb.core.event.InvocationFinishEvent; - import org.apache.servicecomb.swagger.invocation.InvocationType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,7 +30,7 @@ import com.google.common.eventbus.Subscribe; public class ClientDefaultInitializer implements AccessLogInitializer { - private static final Logger LOGGER = LoggerFactory.getLogger("requestlog"); + private static final Logger LOGGER = LoggerFactory.getLogger("scb-access"); private AccessLogGenerator accessLogGenerator; @@ -48,7 +47,7 @@ public void init(EventBus eventBus, AccessLogConfig accessLogConfig) { @AllowConcurrentEvents public void onRequestOut(InvocationFinishEvent finishEvent) { if (InvocationType.CONSUMER.equals(finishEvent.getInvocation().getInvocationType())) { - LOGGER.info(accessLogGenerator.generateClientLog(finishEvent)); + LOGGER.info(accessLogGenerator.generateClientLog(finishEvent)); } } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ConfigurableDatetimeAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ConfigurableDatetimeAccessItem.java index d21ee86a3fa..9c759ca2cb5 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ConfigurableDatetimeAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/ConfigurableDatetimeAccessItem.java @@ -58,7 +58,7 @@ public ConfigurableDatetimeAccessItem() { * @param config the format of configuration is "PATTERN|TIMEZONE|LOCALE" or "PATTERN". It depends on whether the config contains the separator "|" */ public ConfigurableDatetimeAccessItem(String config) { - String[] configArr = null; + String[] configArr; if (config.contains("|")) { configArr = splitConfig(config); } else { @@ -71,14 +71,14 @@ public ConfigurableDatetimeAccessItem(String config) { "wrong format of configuration, \"PATTERN|TIMEZONE|LOCALE\" is expected, but actually is \"" + config + "\""); } - setConfigruations(configArr); + setConfigurations(configArr); } private String[] splitConfig(String config) { return config.split("\\|{1}?", -1); } - private void setConfigruations(String[] configArr) { + private void setConfigurations(String[] configArr) { this.pattern = StringUtils.isEmpty(configArr[0]) ? DEFAULT_DATETIME_PATTERN : configArr[0]; this.timezone = StringUtils.isEmpty(configArr[1]) ? TimeZone.getDefault() : TimeZone.getTimeZone(configArr[1]); this.locale = StringUtils.isEmpty(configArr[2]) ? DEFAULT_LOCALE : Locale.forLanguageTag(configArr[2]); @@ -91,10 +91,9 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - long milliDuration = (finishEvent.getInvocation().getInvocationStageTrace().getStartSend() - - finishEvent.getInvocation().getInvocationStageTrace().getStart()) / 1000_000; + long milliDuration = finishEvent.getInvocation().getInvocationStageTrace().calcTotal() / 1000_000; doAppendFormattedItem( - finishEvent.getInvocation().getInvocationStageTrace().getStartTimeMillis() + milliDuration, builder); + finishEvent.getInvocation().getInvocationStageTrace().getStartInMillis() + milliDuration, builder); } private void doAppendFormattedItem(long milliStartTime, StringBuilder builder) { diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieAccessItem.java index 37f7dc5564e..e15179acc69 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieAccessItem.java @@ -22,9 +22,9 @@ import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.Cookie; import io.vertx.ext.web.RoutingContext; @@ -57,8 +57,8 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() - .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation().getHandlerContext() + .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); if (null == restRequestImpl || null == restRequestImpl.getCookieMap()) { builder.append(RESULT_NOT_FOUND); return; diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondAccessItem.java index e0bde4ad306..49ecda73d94 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondAccessItem.java @@ -31,7 +31,6 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - builder.append((finishEvent.getInvocation().getInvocationStageTrace().getFinish() - - finishEvent.getInvocation().getInvocationStageTrace().getStartSend()) / 1000_000); + builder.append(finishEvent.getInvocation().getInvocationStageTrace().calcTotal() / 1000_000); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondAccessItem.java index 2da34026d5e..68d2ba0d411 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondAccessItem.java @@ -33,7 +33,6 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - builder.append((finishEvent.getInvocation().getInvocationStageTrace().getFinish() - - finishEvent.getInvocation().getInvocationStageTrace().getStartSend()) / 1000_000_000); + builder.append(finishEvent.getInvocation().getInvocationStageTrace().calcTotal() / 1000_000_000); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodAccessItem.java index 8a59112b89a..8c8b61583e3 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodAccessItem.java @@ -21,7 +21,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; +import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; @@ -53,13 +53,13 @@ public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringB builder.append(operationMeta.getHttpMethod()); return; } - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() + RestClientRequest restRequestImpl = (RestClientRequest) finishEvent.getInvocation().getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || null == restRequestImpl.getRequest().getMethod()) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || null == restRequestImpl.getHttpClientRequest().getMethod()) { builder.append(EMPTY_RESULT); return; } - builder.append(restRequestImpl.getRequest().getMethod().toString()); + builder.append(restRequestImpl.getHttpClientRequest().getMethod().toString()); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusAccessItem.java index f4e8770bd06..61be5a790a6 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpStatusAccessItem.java @@ -21,15 +21,11 @@ import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.RoutingContext; public class HttpStatusAccessItem implements AccessLogItem { - private static final Logger LOGGER = LoggerFactory.getLogger(HttpStatusAccessItem.class); - public static final String EMPTY_RESULT = "-"; @Override @@ -40,9 +36,6 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin return; } if (response.closed() && !response.ended()) { - LOGGER.warn( - "Response is closed before sending any data. " - + "Please check idle connection timeout for provider is properly configured."); builder.append(EMPTY_RESULT); return; } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostAccessItem.java index ef070c685d3..cfa2068b297 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostAccessItem.java @@ -21,7 +21,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; +import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; @@ -41,16 +41,16 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin */ @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() + RestClientRequest restRequestImpl = (RestClientRequest) finishEvent.getInvocation().getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || null == restRequestImpl.getRequest().connection() - || null == restRequestImpl.getRequest().connection().localAddress() - || StringUtils.isEmpty(restRequestImpl.getRequest().connection().localAddress().host())) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || null == restRequestImpl.getHttpClientRequest().connection() + || null == restRequestImpl.getHttpClientRequest().connection().localAddress() + || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().connection().localAddress().host())) { builder.append(EMPTY_RESULT); return; } - builder.append(restRequestImpl.getRequest().connection().localAddress().host()); + builder.append(restRequestImpl.getHttpClientRequest().connection().localAddress().host()); } public static String getLocalAddress(RoutingContext context) { diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortAccessItem.java index ffbc983abaf..def97c325be 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortAccessItem.java @@ -20,9 +20,9 @@ import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; @@ -43,14 +43,15 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() + .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || null == restRequestImpl.getRequest().connection() - || null == restRequestImpl.getRequest().connection().localAddress()) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || null == restRequestImpl.getHttpClientRequest().connection() + || null == restRequestImpl.getHttpClientRequest().connection().localAddress()) { builder.append(EMPTY_RESULT); return; } - builder.append(restRequestImpl.getRequest().connection().localAddress().port()); + builder.append(restRequestImpl.getHttpClientRequest().connection().localAddress().port()); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringAccessItem.java index 6ab7eb4a60d..e2493bd3c09 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringAccessItem.java @@ -20,9 +20,9 @@ import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; @@ -42,13 +42,14 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringBuilder builder) { - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() + .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || StringUtils.isEmpty(restRequestImpl.getRequest().query())) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().query())) { builder.append(EMPTY_RESULT); return; } - builder.append(restRequestImpl.getRequest().query()); + builder.append(restRequestImpl.getHttpClientRequest().query()); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderAccessItem.java index 61b3f88f9da..54099d3b4b1 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderAccessItem.java @@ -20,9 +20,9 @@ import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.MultiMap; import io.vertx.ext.web.RoutingContext; @@ -48,15 +48,16 @@ public void appendServerFormattedItem(ServerAccessLogEvent accessLogEvent, Strin @Override public void appendClientFormattedItem(InvocationFinishEvent clientLogEvent, StringBuilder builder) { - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) clientLogEvent.getInvocation().getHandlerContext() + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) clientLogEvent.getInvocation() + .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || null == restRequestImpl.getRequest().headers() - || StringUtils.isEmpty(restRequestImpl.getRequest().headers().get(varName))) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || null == restRequestImpl.getHttpClientRequest().headers() + || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().headers().get(varName))) { builder.append(RESULT_NOT_FOUND); return; } - builder.append(restRequestImpl.getRequest().headers().get(varName)); + builder.append(restRequestImpl.getHttpClientRequest().headers().get(varName)); } public String getVarName() { diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathAccessItem.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathAccessItem.java index 8d88c693b84..5435a22cfb1 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathAccessItem.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathAccessItem.java @@ -21,12 +21,11 @@ import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.common.accessLog.core.element.AccessLogItem; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; -import org.apache.servicecomb.swagger.SwaggerUtils; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; @@ -50,16 +49,17 @@ public void appendClientFormattedItem(InvocationFinishEvent finishEvent, StringB OperationMeta operationMeta = finishEvent.getInvocation().getOperationMeta(); SchemaMeta schemaMeta = finishEvent.getInvocation().getSchemaMeta(); if (operationMeta != null && schemaMeta != null && schemaMeta.getSwagger() != null) { - builder.append(SwaggerUtils.getBasePath(schemaMeta.getSwagger())).append(operationMeta.getOperationPath()); + builder.append(operationMeta.getOperationPath()); return; } - RestClientRequestImpl restRequestImpl = (RestClientRequestImpl) finishEvent.getInvocation().getHandlerContext() + RestClientRequestParameters restRequestImpl = (RestClientRequestParameters) finishEvent.getInvocation() + .getHandlerContext() .get(RestConst.INVOCATION_HANDLER_REQUESTCLIENT); - if (null == restRequestImpl || null == restRequestImpl.getRequest() - || StringUtils.isEmpty(restRequestImpl.getRequest().path())) { + if (null == restRequestImpl || null == restRequestImpl.getHttpClientRequest() + || StringUtils.isEmpty(restRequestImpl.getHttpClientRequest().path())) { builder.append(EMPTY_RESULT); return; } - builder.append(restRequestImpl.getRequest().path()); + builder.append(restRequestImpl.getHttpClientRequest().path()); } } diff --git a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/server/ServerDefaultInitializer.java b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/server/ServerDefaultInitializer.java index c528b67f9b9..7baf69dd8aa 100644 --- a/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/server/ServerDefaultInitializer.java +++ b/common/common-access-log/src/main/java/org/apache/servicecomb/common/accessLog/server/ServerDefaultInitializer.java @@ -31,7 +31,7 @@ public class ServerDefaultInitializer implements AccessLogInitializer { - private static final Logger LOGGER = LoggerFactory.getLogger("accesslog"); + private static final Logger LOGGER = LoggerFactory.getLogger("scb-access"); private AccessLogGenerator accessLogGenerator; diff --git a/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta b/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta index 78f937b61fe..f793ca0f575 100644 --- a/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta +++ b/common/common-access-log/src/main/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.common.accessLog.core.parser.impl.DefaultCompositeVertxRestAccessLogItemMeta \ No newline at end of file +org.apache.servicecomb.common.accessLog.core.parser.impl.DefaultCompositeVertxRestAccessLogItemMeta diff --git a/common/common-access-log/src/main/resources/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/common/common-access-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports similarity index 100% rename from common/common-access-log/src/main/resources/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports rename to common/common-access-log/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java index ee26be04cee..e8c7cf38500 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/AccessLogGeneratorTest.java @@ -81,10 +81,8 @@ public void testClientLog() { InvocationStageTrace stageTrace = Mockito.mock(InvocationStageTrace.class); OperationMeta operationMeta = Mockito.mock(OperationMeta.class); long startMillisecond = 1416863450581L; - when(stageTrace.getStartSend()).thenReturn(0L); - when(stageTrace.getStart()).thenReturn(0L); - when(stageTrace.getFinish()).thenReturn(0L); - when(stageTrace.getStartTimeMillis()).thenReturn(startMillisecond); + when(stageTrace.getStartInMillis()).thenReturn(startMillisecond); + when(stageTrace.calcTotal()).thenReturn(0L); when(invocation.getOperationMeta()).thenReturn(operationMeta); when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieItemTest.java index 5f96b175628..f1d37e9681f 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/CookieItemTest.java @@ -23,18 +23,18 @@ import java.util.HashSet; import java.util.Map; -import io.vertx.core.http.HttpServerRequest; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.vertx.core.http.Cookie; +import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.impl.CookieImpl; import io.vertx.ext.web.RoutingContext; @@ -58,7 +58,7 @@ public class CookieItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; @BeforeEach public void initStrBuilder() { @@ -66,7 +66,7 @@ public void initStrBuilder() { httpServerRequest = Mockito.mock(HttpServerRequest.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); accessLogEvent = new ServerAccessLogEvent(); strBuilder = new StringBuilder(); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java index 750ebde7e39..8f55eb0006c 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DatetimeConfigurableItemTest.java @@ -30,10 +30,6 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnJre; -import org.junit.jupiter.api.condition.EnabledOnOs; -import org.junit.jupiter.api.condition.JRE; -import org.junit.jupiter.api.condition.OS; import org.mockito.Mockito; public class DatetimeConfigurableItemTest { @@ -58,61 +54,19 @@ public void initStrBuilder() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); - when(invocationStageTrace.getStartSend()).thenReturn(0L); - when(invocationStageTrace.getStart()).thenReturn(0L); - when(invocationStageTrace.getStartTimeMillis()).thenReturn(START_MILLISECOND); + when(invocationStageTrace.getStartInMillis()).thenReturn(START_MILLISECOND); + when(invocationStageTrace.calcTotal()).thenReturn(0L); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setMilliStartTime(START_MILLISECOND); strBuilder = new StringBuilder(); } - @Test - @EnabledOnOs({OS.LINUX, OS.WINDOWS}) - @EnabledOnJre(JRE.JAVA_8) - public void serverFormattedElement() { - ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "EEE, yyyy MMM dd HH:mm:ss zzz|GMT-08|zh-CN"); - element.appendServerFormattedItem(accessLogEvent, strBuilder); - Assertions.assertEquals("星期一, 2014 十一月 24 13:10:50 GMT-08:00", strBuilder.toString()); - } - - @Test - @EnabledOnOs({OS.LINUX, OS.WINDOWS}) - @EnabledOnJre(JRE.JAVA_8) - public void clientFormattedElement() { - ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "EEE, yyyy MMM dd HH:mm:ss zzz|GMT-08|zh-CN"); - element.appendClientFormattedItem(finishEvent, strBuilder); - Assertions.assertEquals("星期一, 2014 十一月 24 13:10:50 GMT-08:00", strBuilder.toString()); - } - - @Test - @EnabledOnOs({OS.LINUX, OS.WINDOWS}) - @EnabledOnJre(JRE.JAVA_8) - public void serverFormattedElementOnNoPattern() { - ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "|GMT+08|zh-CN"); - - element.appendServerFormattedItem(accessLogEvent, strBuilder); - Assertions.assertEquals("星期二, 25 十一月 2014 05:10:50 GMT+08:00", strBuilder.toString()); - } - - @Test - @EnabledOnOs({OS.LINUX, OS.WINDOWS}) - @EnabledOnJre(JRE.JAVA_8) - public void clientFormattedElementOnNoPattern() { - ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "|GMT+08|zh-CN"); - - element.appendClientFormattedItem(finishEvent, strBuilder); - Assertions.assertEquals("星期二, 25 十一月 2014 05:10:50 GMT+08:00", strBuilder.toString()); - } @Test public void getFormattedElementOnNoTimezone() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "yyyy/MM/dd zzz||zh-CN"); + "yyyy/MM/dd zzz||zh-CN"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd zzz", Locale.forLanguageTag("zh-CN")); simpleDateFormat.setTimeZone(TimeZone.getDefault()); @@ -123,7 +77,7 @@ public void getFormattedElementOnNoTimezone() { @Test public void clientFormattedElementOnNoTimezone() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "yyyy/MM/dd zzz||zh-CN"); + "yyyy/MM/dd zzz||zh-CN"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd zzz", Locale.forLanguageTag("zh-CN")); simpleDateFormat.setTimeZone(TimeZone.getDefault()); @@ -134,7 +88,7 @@ public void clientFormattedElementOnNoTimezone() { @Test public void serverFormattedElementOnNoLocale() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|"); + "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|"); element.appendServerFormattedItem(accessLogEvent, strBuilder); Assertions.assertEquals("Tue, 25 Nov 2014 05:10:50 GMT+08:00", strBuilder.toString()); @@ -143,7 +97,7 @@ public void serverFormattedElementOnNoLocale() { @Test public void clientFormattedElementOnNoLocale() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|"); + "EEE, dd MMM yyyy HH:mm:ss zzz|GMT+08|"); element.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("Tue, 25 Nov 2014 05:10:50 GMT+08:00", strBuilder.toString()); @@ -152,9 +106,9 @@ public void clientFormattedElementOnNoLocale() { @Test public void serverFormattedElementOnNoConfig() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "||"); + "||"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, - Locale.US); + Locale.US); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendServerFormattedItem(accessLogEvent, strBuilder); @@ -164,9 +118,9 @@ public void serverFormattedElementOnNoConfig() { @Test public void clientFormattedElementOnNoConfig() { ConfigurableDatetimeAccessItem element = new ConfigurableDatetimeAccessItem( - "||"); + "||"); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(ConfigurableDatetimeAccessItem.DEFAULT_DATETIME_PATTERN, - Locale.US); + Locale.US); simpleDateFormat.setTimeZone(TimeZone.getDefault()); element.appendClientFormattedItem(finishEvent, strBuilder); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondItemTest.java index 7ad32b23f87..1fdd3b4e0d0 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationMillisecondItemTest.java @@ -50,8 +50,7 @@ public void initStrBuilder() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); - when(invocationStageTrace.getStartSend()).thenReturn(0L); - when(invocationStageTrace.getFinish()).thenReturn(1000_000L); + when(invocationStageTrace.calcTotal()).thenReturn(1000_000L); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setMilliStartTime(1L); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.java index 3081ce51368..9cbe7f28da5 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/DurationSecondItemTest.java @@ -50,7 +50,7 @@ public void initStrBuilder() { when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); - when(invocationStageTrace.getStartSend()).thenReturn(1000_000L); + when(invocationStageTrace.calcTotal()).thenReturn(1L); accessLogEvent = new ServerAccessLogEvent(); accessLogEvent.setMilliStartTime(1L); @@ -66,7 +66,7 @@ public void serverFormattedElementOn999ms() { @Test public void clientFormattedElementOn999ms() { - when(invocationStageTrace.getFinish()).thenReturn(1000_000_000L); + when(invocationStageTrace.calcTotal()).thenReturn(0L); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("0", strBuilder.toString()); } @@ -80,7 +80,7 @@ public void serverFormattedElementOn1000ms() { @Test public void clientFormattedElementOn1000ms() { - when(invocationStageTrace.getFinish()).thenReturn(1001_000_000L); + when(invocationStageTrace.calcTotal()).thenReturn(1000_000_000L); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("1", strBuilder.toString()); } @@ -94,7 +94,7 @@ public void serverFormattedElementOn1001ms() { @Test public void clientFormattedElementOn1001ms() { - when(invocationStageTrace.getFinish()).thenReturn(1002_000_000L); + when(invocationStageTrace.calcTotal()).thenReturn(1000_000_000L); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("1", strBuilder.toString()); } diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestItemTest.java index 4d75a0befd5..54e1fccef33 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/FirstLineOfRequestItemTest.java @@ -23,12 +23,12 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -54,7 +54,7 @@ public class FirstLineOfRequestItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @@ -67,7 +67,7 @@ public void initStrBuilder() { mockContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); endpoint = Mockito.mock(Endpoint.class); urlEndpoint = Mockito.mock(URIEndpointObject.class); @@ -75,7 +75,7 @@ public void initStrBuilder() { handlerMap.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerMap); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(invocation.getEndpoint()).thenReturn(endpoint); when(endpoint.getAddress()).thenReturn(urlEndpoint); accessLogEvent = new ServerAccessLogEvent(); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodItemTest.java index 39a4212df96..1dbeba59e9a 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/HttpMethodItemTest.java @@ -23,12 +23,12 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -52,7 +52,7 @@ public class HttpMethodItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @@ -65,7 +65,7 @@ public void initStrBuilder() { routingContext = Mockito.mock(RoutingContext.class); finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); endpoint = Mockito.mock(Endpoint.class); urlEndpoint = Mockito.mock(URIEndpointObject.class); @@ -92,7 +92,7 @@ public void serverFormattedElement() { @Test public void clientFormattedElement() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.getMethod()).thenReturn(HttpMethod.DELETE); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("DELETE", strBuilder.toString()); @@ -108,7 +108,7 @@ public void serverFormattedElementOnRequestIsNull() { @Test public void clientFormattedElementOnRequestIsNull() { - when(restClientRequest.getRequest()).thenReturn(null); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostItemTest.java index e4b936807ef..d1669f0316c 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalHostItemTest.java @@ -23,10 +23,10 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -55,7 +55,7 @@ public class LocalHostItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @@ -69,7 +69,7 @@ public void initStrBuilder() { serverRequest = Mockito.mock(HttpServerRequest.class); socketAddress = Mockito.mock(SocketAddress.class); invocation = Mockito.mock(Invocation.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); connection = Mockito.mock(HttpConnection.class); Map handlerMap = new HashMap<>(); @@ -83,7 +83,7 @@ public void initStrBuilder() { @Test public void clientFormattedItem() { String localAddress = "192.168.0.1"; - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(localAddress); @@ -119,7 +119,7 @@ public void serverLocalAddressOnRequestIsNull() { @Test public void clientLocalAddressOnRequestIsNull() { - when(restClientRequest.getRequest()).thenReturn(null); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @@ -134,7 +134,7 @@ public void serverLocalAddressOnLocalAddressIsNull() { @Test public void clientLocalAddressOnLocalAddressIsNull() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); @@ -153,7 +153,7 @@ public void serverLocalAddressOnHostIsNull() { @Test public void clientLocalAddressOnHostIsNull() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(null); @@ -174,7 +174,7 @@ public void serverLocalAddressIsEmpty() { @Test public void clientLocalAddressIsEmpty() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.host()).thenReturn(""); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.java index 430b5147daa..f77c51fbe74 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/LocalPortItemTest.java @@ -23,10 +23,10 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -55,7 +55,7 @@ public class LocalPortItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @@ -69,7 +69,7 @@ public void initStrBuilder() { serverRequest = Mockito.mock(HttpServerRequest.class); socketAddress = Mockito.mock(SocketAddress.class); invocation = Mockito.mock(Invocation.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); connection = Mockito.mock(HttpConnection.class); Map handlerMap = new HashMap<>(); @@ -92,7 +92,7 @@ public void serverFormattedElement() { @Test public void clientFormattedElement() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(socketAddress); when(socketAddress.port()).thenReturn(8080); @@ -109,7 +109,7 @@ public void serverFormattedElementOnRequestIsNull() { @Test public void clientFormattedElementOnRequestIsNull() { - when(restClientRequest.getRequest()).thenReturn(null); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @@ -124,7 +124,7 @@ public void serverFormattedElementOnLocalAddressIsNull() { @Test public void clientFormattedElementOnLocalAddressIsNull() { - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.connection()).thenReturn(connection); when(connection.localAddress()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringItemTest.java index d2af7f57710..ef472623d43 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/QueryStringItemTest.java @@ -23,10 +23,10 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,7 +49,7 @@ public class QueryStringItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpServerRequest serverRequest; @@ -61,7 +61,7 @@ public void initStrBuilder() { finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); serverRequest = Mockito.mock(HttpServerRequest.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); accessLogEvent = new ServerAccessLogEvent(); @@ -85,7 +85,7 @@ public void clientFormattedElement() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.query()).thenReturn(query); ITEM.appendClientFormattedItem(finishEvent, strBuilder); @@ -105,7 +105,7 @@ public void clientFormattedElementOnRequestIsNull() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(null); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); @@ -125,7 +125,7 @@ public void clientFormattedElementOnQueryIsNull() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.query()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); @@ -148,7 +148,7 @@ public void clientFormattedElementOnQueryIsEmpty() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.query()).thenReturn(query); ITEM.appendClientFormattedItem(finishEvent, strBuilder); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.java index ed425d1f801..f2ae7004251 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/RequestHeaderItemTest.java @@ -23,10 +23,10 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -53,7 +53,7 @@ public class RequestHeaderItemTest { private Invocation invocation; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpServerRequest serverRequest; @@ -67,7 +67,7 @@ public void initStrBuilder() { finishEvent = Mockito.mock(InvocationFinishEvent.class); invocation = Mockito.mock(Invocation.class); serverRequest = Mockito.mock(HttpServerRequest.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); headers = Mockito.mock(MultiMap.class); @@ -78,7 +78,7 @@ public void initStrBuilder() { @Test public void serverFormattedElement() { - HeadersMultiMap headers = new HeadersMultiMap(); + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String testValue = "testValue"; headers.add(VAR_NAME, testValue); when(routingContext.request()).thenReturn(serverRequest); @@ -97,7 +97,7 @@ public void clientFormattedElement() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.headers()).thenReturn(headers); when(headers.get(VAR_NAME)).thenReturn(testValue); @@ -120,7 +120,7 @@ public void clientFormattedElementIfHeaderIsNull() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.headers()).thenReturn(null); ELEMENT.appendClientFormattedItem(finishEvent, strBuilder); @@ -129,7 +129,7 @@ public void clientFormattedElementIfHeaderIsNull() { @Test public void serverFormattedElementIfNotFound() { - HeadersMultiMap headers = new HeadersMultiMap(); + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String testValue = "testValue"; headers.add("anotherKey", testValue); when(routingContext.request()).thenReturn(serverRequest); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderItemTest.java index a2e283e4bc8..a8708172945 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/ResponseHeaderItemTest.java @@ -64,7 +64,7 @@ public void initStrBuilder() { @Test public void serverFormattedElement() { - HeadersMultiMap headers = new HeadersMultiMap(); + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String headerValue = "headerValue"; headers.add(VAR_NAME, headerValue); when(routingContext.response()).thenReturn(serverResponse); @@ -122,7 +122,7 @@ public void clientFormattedElementOnResponseIsNull() { @Test public void serverFormattedElementOnNotFound() { - HeadersMultiMap headers = new HeadersMultiMap(); + HeadersMultiMap headers = HeadersMultiMap.httpHeaders(); String headerValue = "headerValue"; headers.add("anotherHeader", headerValue); when(routingContext.response()).thenReturn(serverResponse); diff --git a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java index e62b91e39e3..d1601609a28 100644 --- a/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java +++ b/common/common-access-log/src/test/java/org/apache/servicecomb/common/accessLog/core/element/impl/UrlPathItemTest.java @@ -19,24 +19,22 @@ import static org.mockito.Mockito.when; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.ServerAccessLogEvent; +import org.apache.servicecomb.transport.rest.client.RestClientRequestParameters; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.servers.Server; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpServerRequest; import io.vertx.ext.web.RoutingContext; @@ -62,7 +60,7 @@ public class UrlPathItemTest { private OpenAPI swagger; - private RestClientRequestImpl restClientRequest; + private RestClientRequestParameters restClientRequest; private HttpClientRequest clientRequest; @@ -76,7 +74,7 @@ public void initStrBuilder() { operationMeta = Mockito.mock(OperationMeta.class); schemaMeta = Mockito.mock(SchemaMeta.class); swagger = Mockito.mock(OpenAPI.class); - restClientRequest = Mockito.mock(RestClientRequestImpl.class); + restClientRequest = Mockito.mock(RestClientRequestParameters.class); clientRequest = Mockito.mock(HttpClientRequest.class); accessLogEvent.setRoutingContext(routingContext); @@ -85,14 +83,11 @@ public void initStrBuilder() { @Test public void clientFormattedElement() { - String uri = "/base/test"; + String uri = "/test"; when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getOperationMeta()).thenReturn(operationMeta); when(invocation.getSchemaMeta()).thenReturn(schemaMeta); when(schemaMeta.getSwagger()).thenReturn(swagger); - Server server = Mockito.mock(Server.class); - when(server.getUrl()).thenReturn("/base"); - when(swagger.getServers()).thenReturn(Arrays.asList(server)); when(operationMeta.getOperationPath()).thenReturn("/test"); ITEM.appendClientFormattedItem(finishEvent, strBuilder); @@ -105,7 +100,7 @@ public void clientFormattedElement() { when(invocation.getSchemaMeta()).thenReturn(null); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.path()).thenReturn(uri); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals(uri, strBuilder.toString()); @@ -133,7 +128,7 @@ public void clientFormattedElementOnRequestIsNull() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(null); + when(restClientRequest.getHttpClientRequest()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); } @@ -152,7 +147,7 @@ public void clientFormattedElementOnMethodIsNull() { handlerContext.put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, restClientRequest); when(finishEvent.getInvocation()).thenReturn(invocation); when(invocation.getHandlerContext()).thenReturn(handlerContext); - when(restClientRequest.getRequest()).thenReturn(clientRequest); + when(restClientRequest.getHttpClientRequest()).thenReturn(clientRequest); when(clientRequest.path()).thenReturn(null); ITEM.appendClientFormattedItem(finishEvent, strBuilder); Assertions.assertEquals("-", strBuilder.toString()); diff --git a/common/common-access-log/src/test/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta b/common/common-access-log/src/test/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta index 501ac43ee58..74f9ee4cb24 100644 --- a/common/common-access-log/src/test/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta +++ b/common/common-access-log/src/test/resources/META-INF/services/org.apache.servicecomb.common.accessLog.core.parser.VertxRestAccessLogItemMeta @@ -16,4 +16,4 @@ # org.apache.servicecomb.common.accessLog.core.parser.impl.TestCompositeExtendedAccessLogItemMeta -org.apache.servicecomb.common.accessLog.core.parser.impl.TestSingleExtendedAccessLogItemMeta \ No newline at end of file +org.apache.servicecomb.common.accessLog.core.parser.impl.TestSingleExtendedAccessLogItemMeta diff --git a/common/common-protobuf/pom.xml b/common/common-protobuf/pom.xml index b94386e0370..5b7ac5fcf7c 100644 --- a/common/common-protobuf/pom.xml +++ b/common/common-protobuf/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb common - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT common-protobuf Java Chassis::Common::Protobuf @@ -36,6 +36,12 @@ org.apache.servicecomb foundation-protobuf + + + com.google.protobuf + protobuf-java + test + org.apache.logging.log4j @@ -56,11 +62,6 @@ org.apache.servicecomb foundation-test-scaffolding - - javax.xml.bind - jaxb-api - test - @@ -78,7 +79,7 @@ - generate-sources + generate-test-sources test-compile diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/OperationProtobuf.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/OperationProtobuf.java index 2218207617d..6c12da844de 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/OperationProtobuf.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/OperationProtobuf.java @@ -33,8 +33,7 @@ import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status.Family; -@SuppressWarnings("rawtypes") -public class OperationProtobuf { +public final class OperationProtobuf { private RequestRootSerializer requestRootSerializer; private RequestRootDeserializer requestRootDeserializer; @@ -117,7 +116,7 @@ private void initRequestCodec(ScopedProtobufSchemaManager scopedProtobufSchemaMa ProtoMapper mapper = scopedProtobufSchemaManager.getOrCreateProtoMapper(invocation.getSchemaMeta()); Message requestMessage = mapper.getRequestMessage(invocation.getOperationMeta().getOperationId()); - if (!invocation.isConsumer()) { + if (invocation.isProducer()) { initProducerRequestCodec(invocation, requestMessage, mapper); } else { initConsumerRequestCodec(invocation, requestMessage, mapper); @@ -167,7 +166,7 @@ private void initResponseCodec(ScopedProtobufSchemaManager scopedProtobufSchemaM Message responseMessage = mapper.getResponseMessage(invocation.getOperationMeta().getOperationId()); JavaType responseType = invocation.findResponseType(Status.OK.getStatusCode()); - if (!invocation.isConsumer()) { + if (invocation.isProducer()) { initProviderResponseCode(responseMessage, mapper, responseType); } else { initConsumerResponseCode(responseMessage, mapper, responseType); diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootSerializer.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootSerializer.java index 263e2d786ea..ac5763d9b57 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootSerializer.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/RequestRootSerializer.java @@ -30,10 +30,10 @@ public class RequestRootSerializer { private final boolean isWrap; - public RequestRootSerializer(RootSerializer serializer, boolean isWrapp, boolean noTypesInfo) { + public RequestRootSerializer(RootSerializer serializer, boolean isWrap, boolean noTypesInfo) { this.rootSerializer = serializer; this.noTypesInfo = noTypesInfo; - this.isWrap = isWrapp; + this.isWrap = isWrap; } @SuppressWarnings("unchecked") diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootSerializer.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootSerializer.java index 07c2e1d6392..8af18a4a016 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootSerializer.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/definition/ResponseRootSerializer.java @@ -29,10 +29,10 @@ public class ResponseRootSerializer { private final boolean isWrap; - public ResponseRootSerializer(RootSerializer serializer, boolean isWrapp, boolean noTypesInfo) { + public ResponseRootSerializer(RootSerializer serializer, boolean isWrap, boolean noTypesInfo) { this.rootSerializer = serializer; this.noTypesInfo = noTypesInfo; - this.isWrap = isWrapp; + this.isWrap = isWrap; } public byte[] serialize(Object value) throws IOException { diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/schema/SchemaToProtoGenerator.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/schema/SchemaToProtoGenerator.java index 9c2cf94d4a9..0220cd96b30 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/schema/SchemaToProtoGenerator.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/schema/SchemaToProtoGenerator.java @@ -22,6 +22,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -354,8 +355,10 @@ public void createMessage(Schema schema) { messages.add(schema.getName()); appendLine(msgStringBuilder, "message %s {", schema.getName()); + List> sortedProperties = new ArrayList<>(schema.getProperties().entrySet()); + sortedProperties.sort(Comparator.comparing(Map.Entry::getKey)); int tag = 1; - for (Entry entry : schema.getProperties().entrySet()) { + for (Entry entry : sortedProperties) { Schema property = entry.getValue(); String propertyType = findSchemaType(property); diff --git a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java index e1f08b0dd60..3c7e5adbb64 100644 --- a/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java +++ b/common/common-protobuf/src/main/java/org/apache/servicecomb/codec/protobuf/utils/ScopedProtobufSchemaManager.java @@ -42,12 +42,15 @@ public class ScopedProtobufSchemaManager { static class SchemaKey { String schemaId; + String rootMessage; + Schema schema; int hashCode = -1; - SchemaKey(String schemaId, Schema schema) { + SchemaKey(String schemaId, String rootMessage, Schema schema) { this.schemaId = schemaId; + this.rootMessage = rootMessage; this.schema = schema; } @@ -60,7 +63,7 @@ public boolean equals(Object o) { return false; } SchemaKey other = (SchemaKey) o; - return StringUtils.equals(schemaId, other.schemaId) + return StringUtils.equals(schemaId, other.schemaId) && StringUtils.equals(rootMessage, other.rootMessage) && SwaggerUtils.schemaEquals(schema, other.schema); } @@ -69,7 +72,7 @@ public int hashCode() { if (hashCode != -1) { return hashCode; } - hashCode = schemaId.hashCode() ^ SwaggerUtils.schemaHashCode(schema); + hashCode = schemaId.hashCode() ^ rootMessage.hashCode() ^ SwaggerUtils.schemaHashCode(schema); return hashCode; } } @@ -100,8 +103,9 @@ public ProtoMapper getOrCreateProtoMapper(SchemaMeta schemaMeta) { /** * get the ProtoMapper from Schema */ - public ProtoMapper getOrCreateProtoMapper(OpenAPI openAPI, String schemaId, String rootMessageName, Schema schema) { - SchemaKey schemaKey = new SchemaKey(schemaId, schema); + public ProtoMapper getOrCreateProtoMapper(OpenAPI openAPI, String schemaId, String rootMessageName, + Schema schema) { + SchemaKey schemaKey = new SchemaKey(schemaId, rootMessageName, schema); return schemaMapperCache.computeIfAbsent(schemaKey, key -> { SchemaToProtoGenerator generator = new SchemaToProtoGenerator("scb.schema", openAPI, key.schema, rootMessageName); diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodec.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodec.java index 17efb6eb514..141b7cf4b2c 100644 --- a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodec.java +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodec.java @@ -118,18 +118,18 @@ public void testProtoSchemaOperationUserPOJO() throws Exception { private Invocation mockInvocation(String operation, InvocationType invocationType) { OperationMeta operationMeta; - boolean isConsumer; + boolean isProvider; Invocation invocation = Mockito.mock(Invocation.class); InvocationRuntimeType invocationRuntimeType; if (InvocationType.CONSUMER == invocationType) { operationMeta = consumerSchemaMeta.getOperations().get(operation); - isConsumer = true; + isProvider = false; Mockito.when(invocation.getSchemaMeta()).thenReturn(consumerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); } else { operationMeta = providerSchemaMeta.getOperations().get(operation); - isConsumer = false; + isProvider = true; Mockito.when(invocation.getSchemaMeta()).thenReturn(providerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); } @@ -143,7 +143,7 @@ private Invocation mockInvocation(String operation, InvocationType invocationTyp Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); Mockito.when(invocation.getMicroserviceMeta()).thenReturn(microserviceMeta); - Mockito.when(invocation.isConsumer()).thenReturn(isConsumer); + Mockito.when(invocation.isProducer()).thenReturn(isProvider); return invocation; } diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodecRestTemplate.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodecRestTemplate.java index 985fcd62cef..2a861d202be 100644 --- a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodecRestTemplate.java +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/internal/converter/TestSchemaMetaCodecRestTemplate.java @@ -99,18 +99,18 @@ public void setUp() { private Invocation mockInvocation(String operation, InvocationType invocationType) { OperationMeta operationMeta; - boolean isConsumer; + boolean isProvider; Invocation invocation = Mockito.mock(Invocation.class); InvocationRuntimeType invocationRuntimeType; if (InvocationType.CONSUMER == invocationType) { operationMeta = consumerSchemaMeta.getOperations().get(operation); - isConsumer = true; + isProvider = false; Mockito.when(invocation.getSchemaMeta()).thenReturn(consumerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); } else { operationMeta = providerSchemaMeta.getOperations().get(operation); - isConsumer = false; + isProvider = true; Mockito.when(invocation.getSchemaMeta()).thenReturn(providerSchemaMeta); invocationRuntimeType = operationMeta.buildBaseProviderRuntimeType(); } @@ -124,7 +124,7 @@ private Invocation mockInvocation(String operation, InvocationType invocationTyp Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); Mockito.when(invocation.getMicroserviceMeta()).thenReturn(microserviceMeta); - Mockito.when(invocation.isConsumer()).thenReturn(isConsumer); + Mockito.when(invocation.isProducer()).thenReturn(isProvider); return invocation; } diff --git a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaToProtoGenerator.java b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaToProtoGenerator.java index 930de8284d2..23ac41135ad 100644 --- a/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaToProtoGenerator.java +++ b/common/common-protobuf/src/test/java/org/apache/servicecomb/codec/protobuf/schema/TestSchemaToProtoGenerator.java @@ -51,8 +51,7 @@ public void test_string_schema_is_correct() { message input { string value = 1; } - - """, new ProtoToStringGenerator(proto).protoToString()); + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); } @Test @@ -83,8 +82,7 @@ public void test_object_schema_is_correct() { message input { User value = 1; } - - """, new ProtoToStringGenerator(proto).protoToString()); + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); } static class Model { @@ -115,7 +113,7 @@ public void test_springmvc_int_schema_correct() { assertEquals(""" syntax = "proto3"; package test.int; - + //@WrapProperty message testIntRequest { sint32 value = 1; @@ -131,7 +129,7 @@ public void test_springmvc_int_schema_correct() { assertEquals(""" syntax = "proto3"; package test.int; - + //@WrapProperty message testIntResponse { sint32 value = 1; @@ -154,12 +152,12 @@ public void test_springmvc_model_schema_correct() { assertEquals(""" syntax = "proto3"; package test.model; - + message Model { - string name = 1; - sint32 age = 2; + sint32 age = 1; + string name = 2; } - + //@WrapProperty message testModelRequest { Model value = 1; @@ -175,12 +173,12 @@ public void test_springmvc_model_schema_correct() { assertEquals(""" syntax = "proto3"; package test.model; - + message Model { - string name = 1; - sint32 age = 2; + sint32 age = 1; + string name = 2; } - + //@WrapProperty message testIntResponse { Model value = 1; @@ -201,53 +199,53 @@ public void testNestedModelCorrect() { assertEquals(""" syntax = "proto3"; package test.model; - + //@WrapProperty message MapString { map value = 1; } - + //@WrapProperty message ListListString { repeated ListString value = 1; } - + //@WrapProperty message ListString { repeated string value = 1; } - + message ScoreInfo { sint32 type = 1; } - + message DeptInfo { - string name = 1; - string code = 2; + string code = 1; + string name = 2; repeated ScoreInfo scores = 3; } - + message UserInfo { - repeated DeptInfo subDeptInfos = 1; - repeated MapString extraInfos = 2; - repeated ListListString nestedLists = 3; + repeated MapString extraInfos = 1; + repeated ListListString nestedLists = 2; + repeated DeptInfo subDeptInfos = 3; } - + //@WrapProperty message ListScoreInfo { repeated ScoreInfo value = 1; } - + //@WrapProperty message ListDeptInfo { repeated DeptInfo value = 1; } - + //@WrapProperty message request { UserInfo value = 1; } - """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); } @Test @@ -263,32 +261,32 @@ public void testListMapTypeCorrect() { assertEquals(""" syntax = "proto3"; package test.model; - + message ScoreInfo { sint32 type = 1; } - + message DeptInfo { - string name = 1; - string code = 2; + string code = 1; + string name = 2; repeated ScoreInfo scores = 3; } - + //@WrapProperty message ListScoreInfo { repeated ScoreInfo value = 1; } - + //@WrapProperty message ListDeptInfo { repeated DeptInfo value = 1; } - + //@WrapProperty message request { repeated DeptInfo value = 1; } - """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); + """.trim(), new ProtoToStringGenerator(proto).protoToString().trim()); } @Test diff --git a/common/common-protobuf/src/test/proto/ModelProtobuf.proto b/common/common-protobuf/src/test/proto/ModelProtobuf.proto index ec2a5d803a9..c1fb2ed34dd 100644 --- a/common/common-protobuf/src/test/proto/ModelProtobuf.proto +++ b/common/common-protobuf/src/test/proto/ModelProtobuf.proto @@ -5,9 +5,9 @@ The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/common/common-rest/pom.xml b/common/common-rest/pom.xml index d9202f5be14..f1f2c84b45b 100644 --- a/common/common-rest/pom.xml +++ b/common/common-rest/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb common - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT common-rest diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/CommonRestConfiguration.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/CommonRestConfiguration.java index 5fb174b723f..a642265ae0f 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/CommonRestConfiguration.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/CommonRestConfiguration.java @@ -26,48 +26,54 @@ import org.apache.servicecomb.common.rest.codec.query.QueryCodecs; import org.apache.servicecomb.common.rest.codec.query.QueryCodecsUtils; import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter; +import org.apache.servicecomb.common.rest.filter.inner.WebSocketServerCodecFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CommonRestConfiguration { @Bean - public QueryCodecCsv queryCodecCsv() { + public QueryCodecCsv scbQueryCodecCsv() { return new QueryCodecCsv(); } @Bean - public QueryCodecSsv queryCodecSsv() { + public QueryCodecSsv scbQueryCodecSsv() { return new QueryCodecSsv(); } @Bean - public QueryCodecPipes queryCodecPipes() { + public QueryCodecPipes scbQueryCodecPipes() { return new QueryCodecPipes(); } @Bean - public QueryCodecMulti queryCodecMulti() { + public QueryCodecMulti scbQueryCodecMulti() { return new QueryCodecMulti(); } @Bean - public QueryCodecsUtils queryCodecsUtils(QueryCodecs queryCodecs) { + public QueryCodecsUtils scbQueryCodecsUtils(QueryCodecs queryCodecs) { return new QueryCodecsUtils(queryCodecs); } @Bean - public RestServerCodecFilter restServerCodecFilter() { + public RestServerCodecFilter scbRestServerCodecFilter() { return new RestServerCodecFilter(); } @Bean - public QueryCodecs queryCodecs(List orderedCodecs) { + public WebSocketServerCodecFilter scbWebSocketServerCodecFilter() { + return new WebSocketServerCodecFilter(); + } + + @Bean + public QueryCodecs scbQueryCodecs(List orderedCodecs) { return new QueryCodecs(orderedCodecs); } @Bean - public RestEngineSchemaListener restEngineSchemaListener() { + public RestEngineSchemaListener scbRestEngineSchemaListener() { return new RestEngineSchemaListener(); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/EdgeServerWebSocketInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/EdgeServerWebSocketInvocationCreator.java new file mode 100644 index 00000000000..ff0b0f38174 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/EdgeServerWebSocketInvocationCreator.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest; + +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.common.rest.locator.OperationLocator; +import org.apache.servicecomb.common.rest.locator.ServicePathManager; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.core.provider.consumer.MicroserviceReferenceConfig; +import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; + +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.ServerWebSocket; + +public class EdgeServerWebSocketInvocationCreator extends ProviderServerWebSocketInvocationCreator { + private final String microserviceName; + + protected MicroserviceReferenceConfig microserviceReferenceConfig; + + protected final String path; + + public EdgeServerWebSocketInvocationCreator(String microserviceName, String path, + Endpoint endpoint, ServerWebSocket webSocket) { + super(null, endpoint, webSocket); + this.microserviceName = microserviceName; + this.path = path; + } + + @Override + public CompletableFuture createAsync() { + return createMicroserviceReferenceConfig() + .thenCompose(v -> super.createAsync()); + } + + protected CompletableFuture createMicroserviceReferenceConfig() { + return SCBEngine.getInstance() + .getOrCreateReferenceConfigAsync(microserviceName) + .thenAccept(mrc -> { + this.microserviceReferenceConfig = mrc; + this.microserviceMeta = mrc.getMicroserviceMeta(); + }); + } + + @Override + protected OperationLocator locateOperation(ServicePathManager servicePathManager) { + return servicePathManager.consumerLocateOperation(path, HttpMethod.POST.name()); + } + + @Override + protected Invocation createInstance() { + ReferenceConfig referenceConfig = microserviceReferenceConfig + .createReferenceConfig(restOperationMeta.getOperationMeta()); + + Invocation invocation = InvocationFactory.forConsumer(referenceConfig, + restOperationMeta.getOperationMeta(), + restOperationMeta.getOperationMeta().buildBaseConsumerRuntimeType(), + null); + invocation.setSync(false); + invocation.setEdge(); + invocation.setEndpoint(endpoint); // ensure transport name is correct + + return invocation; + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ProviderServerWebSocketInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ProviderServerWebSocketInvocationCreator.java new file mode 100644 index 00000000000..bcd4fc827b3 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ProviderServerWebSocketInvocationCreator.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest; + +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.MicroserviceMeta; + +import io.vertx.core.http.ServerWebSocket; + +public class ProviderServerWebSocketInvocationCreator extends ServerWebSocketInvocationCreator { + public ProviderServerWebSocketInvocationCreator(MicroserviceMeta microserviceMeta, + Endpoint endpoint, ServerWebSocket webSocket) { + super(microserviceMeta, endpoint, webSocket); + } + + @Override + protected void initTransportContext(Invocation invocation) { + WebSocketTransportContext transportContext = new WebSocketTransportContext(websocket); + invocation.setTransportContext(transportContext); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestConst.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestConst.java index d1788a42bb7..bc598f973f6 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestConst.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestConst.java @@ -44,13 +44,13 @@ private RestConst() { public static final String BODY_PARAMETER = "servicecomb-body"; //in invocation response - public static final String INVOCATION_HANDLER_RESPONSE = "servicecomb-invocation-hanlder-response"; + public static final String INVOCATION_HANDLER_RESPONSE = "servicecomb-invocation-handler-response"; //in invocation response - public static final String INVOCATION_HANDLER_PROCESSOR = "servicecomb-invocation-hanlder-processor"; + public static final String INVOCATION_HANDLER_PROCESSOR = "servicecomb-invocation-handler-processor"; //in invocation response - public static final String INVOCATION_HANDLER_REQUESTCLIENT = "servicecomb-invocation-hanlder-requestclient"; + public static final String INVOCATION_HANDLER_REQUESTCLIENT = "servicecomb-invocation-handler-requestclient"; public static final String REST_PRODUCER_INVOCATION = "servicecomb-rest-producer-invocation"; @@ -83,4 +83,6 @@ private RestConst() { public static final String HEADER_CONTEXT_MAPPER = "servicecomb.context.headerContextMapper"; public static final String QUERY_CONTEXT_MAPPER = "servicecomb.context.queryContextMapper"; + + public static final String DECODE_INVOCATION_CONTEXT = "servicecomb.context.decodeInvocationContext"; } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java index 60eb56c6fb2..5a6c09d3912 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreator.java @@ -86,6 +86,10 @@ protected Invocation createInstance() { } protected void initInvocationContext(Invocation invocation) { + if (!LegacyPropertyFactory.getBooleanProperty(RestConst.DECODE_INVOCATION_CONTEXT, true)) { + return; + } + String strCseContext = requestEx.getHeader(CoreConst.CSE_CONTEXT); if (StringUtils.isEmpty(strCseContext)) { return; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java index 70c3a5a3a01..727346c82d5 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/RestProducerInvocationFlow.java @@ -16,8 +16,6 @@ */ package org.apache.servicecomb.common.rest; -import static org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter.isDownloadFileResponseType; - import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor; import org.apache.servicecomb.common.rest.codec.produce.ProduceProcessorManager; import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter; @@ -25,7 +23,6 @@ import org.apache.servicecomb.core.exception.Exceptions; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.core.invocation.ProducerInvocationFlow; -import org.apache.servicecomb.foundation.common.utils.PartUtils; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.Response; @@ -47,40 +44,42 @@ public RestProducerInvocationFlow(InvocationCreator invocationCreator, protected Invocation sendCreateInvocationException(Throwable throwable) { try { Response response = Exceptions.toProducerResponse(null, throwable); - RestServerCodecFilter.encodeResponse(response, false, DEFAULT_PRODUCE_PROCESSOR, responseEx); + RestServerCodecFilter.encodeResponse(null, response, DEFAULT_PRODUCE_PROCESSOR, responseEx); } catch (Throwable e) { LOGGER.error("Failed to send response when prepare invocation failed, request uri:{}", requestEx.getRequestURI(), e); } - flushResponse("UNKNOWN_OPERATION"); + endResponse(null); return null; } @Override - protected void sendResponse(Invocation invocation, Response response) { - if (isDownloadFileResponseType(invocation, response)) { - responseEx.sendPart(PartUtils.getSinglePart(null, response.getResult())) - .whenComplete((r, e) -> flushResponse(invocation.getMicroserviceQualifiedName())); - return; - } + protected void endResponse(Invocation invocation, Response response) { + invocation.getInvocationStageTrace().startProviderSendResponse(); - flushResponse(invocation.getMicroserviceQualifiedName()); + endResponse(invocation); } - private void flushResponse(String operationName) { + private void endResponse(Invocation invocation) { try { - responseEx.flushBuffer(); + responseEx.endResponse(); } catch (Throwable flushException) { LOGGER.error("Failed to flush rest response, operation:{}, request uri:{}", - operationName, requestEx.getRequestURI(), flushException); + invocation == null ? "NA" : + invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), flushException); } try { requestEx.getAsyncContext().complete(); } catch (Throwable completeException) { LOGGER.error("Failed to complete async rest response, operation:{}, request uri:{}", - operationName, requestEx.getRequestURI(), completeException); + invocation == null ? "NA" : + invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), completeException); + } + + if (invocation != null) { + invocation.getInvocationStageTrace().finishProviderSendResponse(); } } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ServerWebSocketInvocationCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ServerWebSocketInvocationCreator.java new file mode 100644 index 00000000000..46d8b705138 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/ServerWebSocketInvocationCreator.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest; + +import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; +import static org.apache.servicecomb.core.exception.ExceptionCodes.NOT_DEFINED_ANY_SCHEMA; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.common.rest.locator.OperationLocator; +import org.apache.servicecomb.common.rest.locator.ServicePathManager; +import org.apache.servicecomb.config.YAMLUtil; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.core.invocation.InvocationCreator; +import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.http.HttpMethod; +import io.vertx.core.http.ServerWebSocket; +import io.vertx.core.json.Json; + +public abstract class ServerWebSocketInvocationCreator implements InvocationCreator { + private static final Logger LOGGER = LoggerFactory.getLogger(RestVertxProducerInvocationCreator.class); + + protected MicroserviceMeta microserviceMeta; + + protected final Endpoint endpoint; + + protected final ServerWebSocket websocket; + + protected RestOperationMeta restOperationMeta; + + public ServerWebSocketInvocationCreator(MicroserviceMeta microserviceMeta, Endpoint endpoint, + ServerWebSocket websocket) { + this.microserviceMeta = microserviceMeta; + this.endpoint = endpoint; + this.websocket = websocket; + } + + @Override + public CompletableFuture createAsync() { + initRestOperation(); + + Invocation invocation = createInstance(); + initInvocationContext(invocation); + addParameterContext(invocation); + initTransportContext(invocation); + + return CompletableFuture.completedFuture(invocation); + } + + protected Invocation createInstance() { + return InvocationFactory.forProvider(endpoint, restOperationMeta.getOperationMeta(), null); + } + + protected void initInvocationContext(Invocation invocation) { + if (!LegacyPropertyFactory.getBooleanProperty(RestConst.DECODE_INVOCATION_CONTEXT, true)) { + return; + } + + String strCseContext = websocket.headers().get(CoreConst.CSE_CONTEXT); + if (StringUtils.isEmpty(strCseContext)) { + return; + } + + @SuppressWarnings("unchecked") + Map invocationContext = Json.decodeValue(strCseContext, Map.class); + invocation.mergeContext(invocationContext); + } + + // No queries for websocket + protected void addParameterContext(Invocation invocation) { + String headerContextMapper = LegacyPropertyFactory + .getStringProperty(RestConst.HEADER_CONTEXT_MAPPER); + + Map headerContextMappers; + if (headerContextMapper != null) { + headerContextMappers = YAMLUtil.yaml2Properties(headerContextMapper); + } else { + headerContextMappers = new HashMap<>(); + } + + headerContextMappers.forEach((k, v) -> { + if (v instanceof String && websocket.headers().get(k) != null) { + invocation.addContext((String) v, websocket.headers().get(k)); + } + }); + } + + protected abstract void initTransportContext(Invocation invocation); + + protected void initRestOperation() { + OperationLocator locator = locateOperation(microserviceMeta); + restOperationMeta = locator.getOperation(); + } + + protected OperationLocator locateOperation(MicroserviceMeta microserviceMeta) { + ServicePathManager servicePathManager = ServicePathManager.getServicePathManager(microserviceMeta); + if (servicePathManager == null) { + LOGGER.error("No schema defined for {}:{}.", this.microserviceMeta.getAppId(), + this.microserviceMeta.getMicroserviceName()); + throw Exceptions.create(NOT_FOUND, NOT_DEFINED_ANY_SCHEMA, NOT_FOUND.getReasonPhrase()); + } + + return locateOperation(servicePathManager); + } + + protected OperationLocator locateOperation(ServicePathManager servicePathManager) { + return servicePathManager.producerLocateOperation(websocket.path(), HttpMethod.POST.name()); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/WebSocketTransportContext.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/WebSocketTransportContext.java new file mode 100644 index 00000000000..738b09788ff --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/WebSocketTransportContext.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest; + +import org.apache.servicecomb.swagger.invocation.context.TransportContext; + +import io.vertx.core.http.ServerWebSocket; + +public class WebSocketTransportContext implements TransportContext { + private final ServerWebSocket serverWebSocket; + + public WebSocketTransportContext(ServerWebSocket serverWebSocket) { + this.serverWebSocket = serverWebSocket; + } + + public ServerWebSocket getServerWebSocket() { + return this.serverWebSocket; + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java index 62d768847e6..74967694a90 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/RestClientRequest.java @@ -20,6 +20,7 @@ import io.vertx.core.Future; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClientRequest; /** * vertx的HttpClientRequest没有getHeader的能力 @@ -41,4 +42,6 @@ public interface RestClientRequest { Buffer getBodyBuffer() throws Exception; void attach(String name, Object partOrList); + + HttpClientRequest getHttpClientRequest(); } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodec.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodec.java new file mode 100644 index 00000000000..6b6e06f41e1 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodec.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; + +import jakarta.servlet.http.HttpServletRequest; + +public interface HeaderCodec { + static String encodeValue(Object value) throws UnsupportedEncodingException { + return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8.name()); + } + + // can not be replaced by value.toString() because of date serialize + static String convertToString(Object value) throws Exception { + return RestObjectMapperFactory.getRestObjectMapper().convertToString(value); + } + + String getCodecName(); + + void encode(RestClientRequest clientRequest, String name, Object value) throws Exception; + + Object decode(HeaderProcessor processor, HttpServletRequest request); +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecCsv.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecCsv.java new file mode 100644 index 00000000000..26d86bbf2b7 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecCsv.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +public class HeaderCodecCsv extends HeaderCodecWithDelimiter { + public static final String CODEC_NAME = "form:0"; + + public static final String DELIMITER = ","; + + public HeaderCodecCsv() { + super(CODEC_NAME, DELIMITER, DELIMITER); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecMulti.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecMulti.java new file mode 100644 index 00000000000..7a61e0fa125 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecMulti.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response.Status; + +public class HeaderCodecMulti implements HeaderCodec { + public static final String NAME = "form:1"; + + @Override + public String getCodecName() { + return NAME; + } + + @Override + public void encode(RestClientRequest clientRequest, String name, Object value) throws Exception { + if (null == value) { + // if value is empty, header should not be set to clientRequest to avoid NullPointerException in Netty. + return; + } + if (!(value instanceof Collection)) { + throw new InvocationException(Status.BAD_REQUEST, + new CommonExceptionData("Array type of header should be Collection")); + } + for (Object item : ((Collection) value)) { + clientRequest.putHeader(name, + RestObjectMapperFactory.getConsumerWriterMapper().convertToString(item)); + } + } + + @Override + public Object decode(HeaderProcessor processor, HttpServletRequest request) { + Enumeration headerValues = request.getHeaders(processor.getParameterPath()); + if (headerValues == null) { + //Even if the paramPath does not exist, headerValues won't be null at now + return null; + } + return processor.convertValue(Collections.list(headerValues), processor.getTargetType()); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecPipes.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecPipes.java new file mode 100644 index 00000000000..f458946ba61 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecPipes.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +public class HeaderCodecPipes extends HeaderCodecWithDelimiter { + public static final String CODEC_NAME = "pipeDelimited:0"; + + public static final String JOIN_DELIMITER = "|"; + + public static final String SPLIT_DELIMITER = "\\|"; + + public HeaderCodecPipes() { + super(CODEC_NAME, JOIN_DELIMITER, SPLIT_DELIMITER); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSimple.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSimple.java new file mode 100644 index 00000000000..0dd46a86b90 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSimple.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; + +import jakarta.servlet.http.HttpServletRequest; + +public class HeaderCodecSimple implements HeaderCodec { + public static final String NAME = "simple"; + + @Override + public String getCodecName() { + return NAME; + } + + @Override + public void encode(RestClientRequest clientRequest, String name, Object value) throws Exception { + if (null == value) { + // if value is empty, header should not be set to clientRequest to avoid NullPointerException in Netty. + return; + } + clientRequest.putHeader(name, + RestObjectMapperFactory.getConsumerWriterMapper().convertToString(value)); + } + + @Override + public Object decode(HeaderProcessor processor, HttpServletRequest request) { + Object value = request.getHeader(processor.getParameterPath()); + if (value == null) { + value = processor.checkRequiredAndDefaultValue(); + } + return processor.convertValue(value, processor.getTargetType()); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSsv.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSsv.java new file mode 100644 index 00000000000..a9266f043ef --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecSsv.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +public class HeaderCodecSsv extends HeaderCodecWithDelimiter { + public static final String CODEC_NAME = "spaceDelimited:0"; + + public static final String DELIMITER = " "; + + public HeaderCodecSsv() { + super(CODEC_NAME, DELIMITER, DELIMITER); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecWithDelimiter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecWithDelimiter.java new file mode 100644 index 00000000000..c85742b5549 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecWithDelimiter.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +import java.util.Collection; +import java.util.List; +import java.util.StringJoiner; + +import org.apache.servicecomb.common.rest.codec.RestClientRequest; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.param.HeaderProcessorCreator.HeaderProcessor; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.ws.rs.core.Response.Status; + +public abstract class HeaderCodecWithDelimiter implements HeaderCodec { + private final String name; + + private final String joinDelimiter; + + private final String splitDelimiter; + + public HeaderCodecWithDelimiter(String name, String joinDelimiter, String splitDelimiter) { + this.name = name; + this.joinDelimiter = joinDelimiter; + this.splitDelimiter = splitDelimiter; + } + + + @Override + public String getCodecName() { + return name; + } + + @Override + public void encode(RestClientRequest clientRequest, String name, Object value) throws Exception { + if (null == value) { + // if value is empty, header should not be set to clientRequest to avoid NullPointerException in Netty. + return; + } + if (!(value instanceof Collection)) { + throw new InvocationException(Status.BAD_REQUEST, + new CommonExceptionData("Array type of header should be Collection")); + } + clientRequest.putHeader(name, join((Collection) value)); + } + + protected String join(Collection values) throws Exception { + StringJoiner joiner = new StringJoiner(joinDelimiter); + for (Object value : values) { + String strValue = RestObjectMapperFactory.getConsumerWriterMapper().convertToString(value); + joiner.add(strValue); + } + + return joiner.toString(); + } + + @Override + public Object decode(HeaderProcessor processor, HttpServletRequest request) { + String headerValues = request.getHeader(processor.getParameterPath()); + if (headerValues == null) { + headerValues = (String) processor.checkRequiredAndDefaultValue(); + } + + return processor.convertValue(List.of(headerValues.split(splitDelimiter)), processor.getTargetType()); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecsUtils.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecsUtils.java new file mode 100644 index 00000000000..fb9509a1f90 --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/header/HeaderCodecsUtils.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.header; + +import java.util.HashMap; +import java.util.Map; + +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; + +public class HeaderCodecsUtils { + private static final Map CODECS; + + static { + CODECS = new HashMap<>(); + CODECS.put(HeaderCodecSimple.NAME, new HeaderCodecSimple()); + CODECS.put(HeaderCodecMulti.NAME, new HeaderCodecMulti()); + CODECS.put(HeaderCodecCsv.CODEC_NAME, new HeaderCodecCsv()); + CODECS.put(HeaderCodecPipes.CODEC_NAME, new HeaderCodecPipes()); + CODECS.put(HeaderCodecSsv.CODEC_NAME, new HeaderCodecSsv()); + } + + private HeaderCodecsUtils() { + } + + public static HeaderCodec find(Parameter.StyleEnum styleEnum, Boolean explode) { + return CODECS.get(formatName(styleEnum, explode)); + } + + private static String formatName(StyleEnum styleEnum, Boolean explode) { + if (styleEnum == null) { + return HeaderCodecSimple.NAME; + } + return styleEnum + ":" + + (explode != null && explode ? "1" : "0"); + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java index c59815a94b9..e789038c467 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/BodyProcessorCreator.java @@ -62,8 +62,8 @@ import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response.Status; -@SuppressWarnings("rawtypes") public class BodyProcessorCreator implements ParamValueProcessorCreator { + private static final Logger LOGGER = LoggerFactory.getLogger(BodyProcessorCreator.class); public static final String REQUEST_BODY_NAME = "X_REQUEST"; @@ -74,12 +74,18 @@ public class BodyProcessorCreator implements ParamValueProcessorCreator> deserializer = protoMapper.getDeserializerSchemaManager() - .createRootDeserializer(protoMapper.getProto().getMessage(REQUEST_BODY_NAME), targetType); + .createRootDeserializer(protoMapper.getProto().getMessage(REQUEST_BODY_NAME), + targetType == null ? OBJECT_TYPE : targetType); PropertyWrapper result = deserializer.deserialize(inputStream.readAllBytes()); return result.getValue(); } - // For application/json and text/plain - try { - if (MediaType.TEXT_PLAIN.equals(contentType) && - targetType != null && String.class.equals(targetType.getRawClass())) { - return IOUtils.toString(inputStream, StandardCharsets.UTF_8); - } - ObjectReader reader = serialViewClass != null - ? RestObjectMapperFactory.getRestObjectMapper().readerWithView(serialViewClass) - : RestObjectMapperFactory.getRestObjectMapper().reader(); - if (decodeAsObject) { - return reader.forType(OBJECT_TYPE).readValue(inputStream); - } - return reader.forType(targetType == null ? OBJECT_TYPE : targetType) - .readValue(inputStream); - } catch (MismatchedInputException e) { - // there is no way to detect InputStream is empty, so have to catch the exception - if (!isRequired && e.getMessage().contains("No content to map due to end-of-input")) { - LOGGER.info("Empty content and required is false, taken as null"); - return null; + if (StringUtils.equalsIgnoreCase(contentType, MediaType.TEXT_PLAIN)) { + try { + if (targetType != null && String.class.equals(targetType.getRawClass())) { + return IOUtils.toString(inputStream, StandardCharsets.UTF_8); + } + ObjectReader reader = serialViewClass != null + ? RestObjectMapperFactory.getRestObjectMapper().readerWithView(serialViewClass) + : RestObjectMapperFactory.getRestObjectMapper().reader(); + if (decodeAsObject()) { + return reader.forType(OBJECT_TYPE).readValue(inputStream); + } + return reader.forType(targetType == null ? OBJECT_TYPE : targetType) + .readValue(inputStream); + } catch (MismatchedInputException e) { + // there is no way to detect InputStream is empty, so have to catch the exception + if (!isRequired && e.getMessage().contains("No content to map due to end-of-input")) { + LOGGER.info("Empty content and required is false, taken as null"); + return null; + } + throw e; } - throw e; } + + throw new IllegalArgumentException(String.format("operation %s not support content-type %s", + operationMeta.getSchemaQualifiedName(), contentType)); } private String validContentType(String type) { @@ -221,8 +248,8 @@ private String validContentType(String type) { if (supportedContentTypes.size() == 0) { throw new IllegalArgumentException("operation do not have any content type support."); } - if (supportedContentTypes.contains(MediaType.APPLICATION_JSON)) { - return MediaType.APPLICATION_JSON; + if (supportedContentTypes.contains(clientEncodingDefault())) { + return clientEncodingDefault(); } return supportedContentTypes.get(0); } @@ -244,9 +271,16 @@ public void setValue(RestClientRequest clientRequest, Object arg) throws Excepti } /** - * Deserialize body object into body buffer, according to the Content-Type. + * Serialize body object into body buffer, according to the Content-Type. */ private Buffer createBodyBuffer(String contentType, Object arg) throws IOException { + if (MediaType.APPLICATION_JSON.equals(contentType)) { + try (BufferOutputStream output = new BufferOutputStream()) { + RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, arg); + return output.getBuffer(); + } + } + if (SwaggerConst.PROTOBUF_TYPE.equals(contentType)) { ProtoMapper protoMapper = scopedProtobufSchemaManager .getOrCreateProtoMapper(openAPI, operationMeta.getSchemaId(), @@ -260,9 +294,9 @@ private Buffer createBodyBuffer(String contentType, Object arg) throws IOExcepti return new BufferImpl().appendBytes(serializer.serialize(bodyArg)); } - // For application/json and text/plain + // For text/plain try (BufferOutputStream output = new BufferOutputStream()) { - if (MediaType.TEXT_PLAIN.equals(contentType) && (arg instanceof String)) { + if (arg instanceof String) { output.write(((String) arg).getBytes(StandardCharsets.UTF_8)); } else { RestObjectMapperFactory.getConsumerWriterMapper().writeValue(output, arg); @@ -361,4 +395,20 @@ public ParamValueProcessor create(OperationMeta operationMeta, String parameterN return new BodyProcessor(operationMeta, targetType, parameter); } + + private static boolean decodeAsObject() { + if (decodeAsObject == null) { + decodeAsObject = LegacyPropertyFactory + .getBooleanProperty(PARAM_DECODE_AS_OBJECT, false); + } + return decodeAsObject; + } + + private static String clientEncodingDefault() { + if (defaultRequestEncoding == null) { + defaultRequestEncoding = LegacyPropertyFactory + .getStringProperty(PARAM_DEFAULT_REQUEST_ENCODING, MediaType.APPLICATION_JSON); + } + return defaultRequestEncoding; + } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java index 0f7bf6a632b..740f57e039f 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/FormProcessorCreator.java @@ -26,6 +26,7 @@ import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.common.rest.codec.RestClientRequest; import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.invocation.converter.Converter; @@ -33,7 +34,6 @@ import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.google.inject.util.Types; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.MediaType; @@ -120,14 +120,18 @@ private boolean isPart(RequestBody parameter, String paramName) { MediaType file = parameter.getContent().get(SwaggerConst.FILE_MEDIA_TYPE); if (file != null) { Schema schema = (Schema) file.getSchema().getProperties().get(paramName); - return schema instanceof ArraySchema || - ("string".equals(schema.getType()) && "binary".equals(schema.getFormat())); + if (schema instanceof ArraySchema) { + return "string".equals(schema.getItems().getType()) && + "binary".equals(schema.getItems().getFormat()); + } else { + return ("string".equals(schema.getType()) && "binary".equals(schema.getFormat())); + } } return false; } public static class PartProcessor extends AbstractParamProcessor { - private static final Type partListType = Types.newParameterizedType(List.class, Part.class); + private static final Type partListType = ParameterizedTypeUtil.make(List.class, Part.class); // key is target type private static final Map partsToTargetConverters = SPIServiceUtils.getSortedService( @@ -174,7 +178,7 @@ private void initNormalConverter(Type genericParamType) { private void initRepeatedConverter(Type genericParamType) { if (genericParamType instanceof JavaType) { - genericParamType = Types.newParameterizedType(((JavaType) genericParamType).getRawClass(), + genericParamType = ParameterizedTypeUtil.make(((JavaType) genericParamType).getRawClass(), ((JavaType) genericParamType).getContentType()); } converter = partsToTargetConverters.get(genericParamType); diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java index a3b72403ba8..3bfb2675046 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/HeaderProcessorCreator.java @@ -18,16 +18,13 @@ package org.apache.servicecomb.common.rest.codec.param; import java.lang.reflect.Type; -import java.util.Collections; -import java.util.Enumeration; import org.apache.servicecomb.common.rest.codec.RestClientRequest; -import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; +import org.apache.servicecomb.common.rest.codec.header.HeaderCodec; +import org.apache.servicecomb.common.rest.codec.header.HeaderCodecsUtils; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; @@ -35,12 +32,11 @@ import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.parameters.HeaderParameter; import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.Parameter.StyleEnum; import jakarta.servlet.http.HttpServletRequest; import jakarta.ws.rs.core.Response.Status; public class HeaderProcessorCreator implements ParamValueProcessorCreator { - private static final Logger LOGGER = LoggerFactory.getLogger(HeaderProcessorCreator.class); - public static final String PARAMTYPE = "header"; public static class HeaderProcessor extends AbstractParamProcessor { @@ -48,34 +44,26 @@ public static class HeaderProcessor extends AbstractParamProcessor { private final boolean ignoreRequiredCheck = LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.parameter.header.ignoreRequiredCheck", false); - private final boolean repeatedType; + private final HeaderCodec headerCodec; public HeaderProcessor(HeaderParameter headerParameter, JavaType targetType) { super(headerParameter.getName(), targetType, headerParameter.getSchema().getDefault(), headerParameter.getRequired() != null && headerParameter.getRequired()); - this.repeatedType = headerParameter.getSchema() instanceof ArraySchema; + if ((headerParameter.getSchema() instanceof ArraySchema) && headerParameter.getStyle() == null) { + // compatible to default settings + this.headerCodec = HeaderCodecsUtils.find(StyleEnum.FORM, true); + } else { + this.headerCodec = HeaderCodecsUtils.find(headerParameter.getStyle(), headerParameter.getExplode()); + } } @Override public Object getValue(HttpServletRequest request) { - if (repeatedType) { - Enumeration headerValues = request.getHeaders(paramPath); - if (headerValues == null) { - //Even if the paramPath does not exist, headerValues won't be null at now - return null; - } - return convertValue(Collections.list(headerValues), targetType); - } - - Object value = request.getHeader(paramPath); - if (value == null) { - value = checkRequiredAndDefaultValue(); - } - return convertValue(value, targetType); + return headerCodec.decode(this, request); } - private Object checkRequiredAndDefaultValue() { + public Object checkRequiredAndDefaultValue() { if (!ignoreRequiredCheck && isRequired()) { throw new InvocationException(Status.BAD_REQUEST, "Parameter is required."); } @@ -84,13 +72,7 @@ private Object checkRequiredAndDefaultValue() { @Override public void setValue(RestClientRequest clientRequest, Object arg) throws Exception { - if (null == arg) { - // null header should not be set to clientRequest to avoid NullPointerException in Netty. - LOGGER.debug("Header arg is null, will not be set into clientRequest. paramPath = [{}]", paramPath); - return; - } - clientRequest.putHeader(paramPath, - RestObjectMapperFactory.getConsumerWriterMapper().convertToString(arg)); + headerCodec.encode(clientRequest, paramPath, arg); } @Override diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java deleted file mode 100644 index adf5cfbb44e..00000000000 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/param/RestClientRequestImpl.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.servicecomb.common.rest.codec.param; - -import static jakarta.ws.rs.core.HttpHeaders.CONTENT_TYPE; -import static jakarta.ws.rs.core.MediaType.MULTIPART_FORM_DATA; - -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; - -import jakarta.servlet.http.Part; -import jakarta.ws.rs.core.MediaType; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.servicecomb.common.rest.codec.RestClientRequest; -import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; -import org.apache.servicecomb.foundation.common.utils.PartUtils; -import org.apache.servicecomb.foundation.vertx.stream.BufferInputStream; -import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; -import org.apache.servicecomb.foundation.vertx.stream.InputStreamToReadStream; -import org.apache.servicecomb.foundation.vertx.stream.PumpFromPart; -import org.apache.servicecomb.swagger.invocation.AsyncResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - -import io.vertx.core.Context; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.MultiMap; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClientRequest; -import io.vertx.core.http.HttpHeaders; - -public class RestClientRequestImpl implements RestClientRequest { - private static final Logger LOGGER = LoggerFactory.getLogger(RestClientRequestImpl.class); - - protected Context context; - - protected AsyncResponse asyncResp; - - @VisibleForTesting - final Multimap uploads = ArrayListMultimap.create(); - - protected HttpClientRequest request; - - protected Map cookieMap; - - protected Map formMap; - - protected Buffer bodyBuffer; - - private final Handler throwableHandler; - - public RestClientRequestImpl(HttpClientRequest request, Context context, AsyncResponse asyncResp) { - this(request, context, asyncResp, null); - } - - public RestClientRequestImpl(HttpClientRequest request, Context context, AsyncResponse asyncResp, - Handler throwableHandler) { - this.context = context; - this.asyncResp = asyncResp; - this.request = request; - this.throwableHandler = throwableHandler; - } - - @Override - public void write(Buffer bodyBuffer) { - this.bodyBuffer = bodyBuffer; - } - - @Override - public Buffer getBodyBuffer() throws Exception { - genBodyBuffer(); - return bodyBuffer; - } - - @Override - @SuppressWarnings("unchecked") - public void attach(String name, Object partOrList) { - if (null == partOrList) { - LOGGER.debug("null file is ignored, file name = [{}]", name); - return; - } - - if (partOrList.getClass().isArray()) { - for (Object part : (Object[]) partOrList) { - uploads.put(name, PartUtils.getSinglePart(name, part)); - } - } - - if (List.class.isAssignableFrom(partOrList.getClass())) { - for (Object part : (List) partOrList) { - uploads.put(name, PartUtils.getSinglePart(name, part)); - } - return; - } - - uploads.put(name, PartUtils.getSinglePart(name, partOrList)); - } - - @Override - public Future end() { - writeCookies(); - - if (!uploads.isEmpty()) { - return doEndWithUpload(); - } - - return doEndNormal(); - } - - protected Future doEndWithUpload() { - request.setChunked(true); - - String boundary = "boundary" + UUID.randomUUID(); - putHeader(CONTENT_TYPE, MULTIPART_FORM_DATA + "; charset=UTF-8; boundary=" + boundary); - - return genBodyForm(boundary).onSuccess(v -> attachFiles(boundary)).onFailure(e -> asyncResp.consumerFail(e)); - } - - private Future genBodyForm(String boundary) { - if (formMap == null) { - return Future.succeededFuture(); - } - - try { - try (BufferOutputStream output = new BufferOutputStream()) { - for (Entry entry : formMap.entrySet()) { - output.write(bytesOf("\r\n")); - output.write(bytesOf("--" + boundary + "\r\n")); - output.write(bytesOf("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"\r\n\r\n")); - if (entry.getValue() != null) { - String value = RestObjectMapperFactory.getRestObjectMapper().convertToString(entry.getValue()); - output.write(value.getBytes(StandardCharsets.UTF_8)); - } - } - - return writeBuffer(output.getBuffer()); - } - } catch (Exception e) { - return Future.failedFuture(e); - } - } - - private byte[] bytesOf(String string) { - return string.getBytes(StandardCharsets.UTF_8); - } - - protected Future doEndNormal() { - try { - genBodyBuffer(); - } catch (Exception e) { - asyncResp.consumerFail(e); - return Future.succeededFuture(); - } - - if (bodyBuffer == null) { - return request.end(); - } - - return request.end(bodyBuffer); - } - - private void attachFiles(String boundary) { - Iterator> uploadsIterator = uploads.entries().iterator(); - attachFile(boundary, uploadsIterator); - } - - private void attachFile(String boundary, Iterator> uploadsIterator) { - if (!uploadsIterator.hasNext()) { - writeBuffer(boundaryEndInfo(boundary)).onSuccess(v -> request.end()).onFailure(e -> asyncResp.consumerFail(e)); - return; - } - - Entry entry = uploadsIterator.next(); - // do not use part.getName() to get parameter name - // because pojo consumer not easy to set name to part - String name = entry.getKey(); - Part part = entry.getValue(); - String filename = part.getSubmittedFileName(); - - LOGGER.debug("Start attach file [{}:{}].", name, filename); - writeBuffer(fileBoundaryInfo(boundary, name, part)).onSuccess(r -> - new PumpFromPart(context, part).toWriteStream(request, throwableHandler).whenComplete((v, e) -> { - if (e != null) { - LOGGER.warn("Failed attach file [{}:{}].", name, filename, e); - asyncResp.consumerFail(e); - return; - } - - LOGGER.debug("Finish attach file [{}:{}].", name, filename); - attachFile(boundary, uploadsIterator); - })).onFailure(e -> asyncResp.consumerFail(e)); - } - - private Buffer boundaryEndInfo(String boundary) { - return Buffer.buffer() - .appendString("\r\n") - .appendString("--" + boundary + "--\r\n"); - } - - protected Buffer fileBoundaryInfo(String boundary, String name, Part part) { - Buffer buffer = Buffer.buffer(); - buffer.appendString("\r\n"); - buffer.appendString("--" + boundary + "\r\n"); - buffer.appendString("Content-Disposition: form-data; name=\"") - .appendString(name) - .appendString("\"; filename=\"") - .appendString(part.getSubmittedFileName() != null ? part.getSubmittedFileName() : "null") - .appendString("\"\r\n"); - buffer.appendString("Content-Type: ").appendString(part.getContentType()).appendString("\r\n"); - buffer.appendString("Content-Transfer-Encoding: binary\r\n"); - buffer.appendString("\r\n"); - return buffer; - } - - protected Future writeBuffer(Buffer buffer) { - return new InputStreamToReadStream(context, - new BufferInputStream(buffer.getByteBuf()), true).pipe().endOnComplete(false).to(request); - } - - private void genBodyBuffer() throws Exception { - if (bodyBuffer != null) { - return; - } - - if (formMap == null) { - return; - } - - request.putHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED); - try (BufferOutputStream output = new BufferOutputStream()) { - for (Entry entry : formMap.entrySet()) { - output.write(entry.getKey().getBytes(StandardCharsets.UTF_8)); - output.write('='); - if (entry.getValue() != null) { - String value = RestObjectMapperFactory.getRestObjectMapper().convertToString(entry.getValue()); - value = URLEncoder.encode(value, StandardCharsets.UTF_8.name()); - output.write(value.getBytes(StandardCharsets.UTF_8)); - } - output.write('&'); - } - bodyBuffer = output.getBuffer(); - } - } - - private void writeCookies() { - if (cookieMap == null) { - return; - } - - StringBuilder builder = new StringBuilder(); - for (Entry entry : cookieMap.entrySet()) { - builder.append(entry.getKey()) - .append('=') - .append(entry.getValue()) - .append("; "); - } - request.putHeader(HttpHeaders.COOKIE, builder.toString()); - } - - public Context getContext() { - return context; - } - - public HttpClientRequest getRequest() { - return request; - } - - public Map getCookieMap() { - return cookieMap; - } - - @Override - public void addCookie(String name, String value) { - if (cookieMap == null) { - cookieMap = new HashMap<>(); - } - - cookieMap.put(name, value); - } - - @Override - public void addForm(String name, Object value) { - if (formMap == null) { - formMap = new HashMap<>(); - } - - if (value != null) { - formMap.put(name, value); - } - } - - @Override - public void putHeader(String name, String value) { - request.putHeader(name, value); - } - - @Override - public MultiMap getHeaders() { - return request.headers(); - } -} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceEventStreamProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceEventStreamProcessor.java new file mode 100644 index 00000000000..64795bf817d --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceEventStreamProcessor.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.codec.produce; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; + +import com.fasterxml.jackson.databind.JavaType; + +import jakarta.ws.rs.core.MediaType; + +public class ProduceEventStreamProcessor implements ProduceProcessor { + private int writeIndex = 0; + + @Override + public String getName() { + return MediaType.SERVER_SENT_EVENTS; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void doEncodeResponse(OutputStream output, Object result) throws Exception { + String buffer = "id: " + (writeIndex++) + "\n" + + "data: " + + RestObjectMapperFactory.getRestObjectMapper().writeValueAsString(result) + + "\n\n"; + output.write(buffer.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public Object doDecodeResponse(InputStream input, JavaType type) throws Exception { + String buffer = new String(input.readAllBytes(), StandardCharsets.UTF_8); + for (String line : buffer.split("\n")) { + if (line.startsWith("data: ")) { + return RestObjectMapperFactory.getRestObjectMapper().readValue(line.substring(5), type); + } + } + return null; + } +} diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java index bcf7cb74b2c..574c1e1b630 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessor.java @@ -72,7 +72,7 @@ default Object decodeResponse(Buffer buffer, JavaType type) throws Exception { return null; } - try (BufferInputStream input = new BufferInputStream(buffer.getByteBuf())) { + try (BufferInputStream input = new BufferInputStream(buffer)) { return doDecodeResponse(input, type); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java index 36b9a8c3e99..3fdcfec135f 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/produce/ProduceProcessorManager.java @@ -23,6 +23,7 @@ import org.apache.http.entity.ContentType; import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.RegisterManager; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; @@ -45,6 +46,10 @@ public final class ProduceProcessorManager extends RegisterManager jsonProcessorMap; private final Map plainProcessorMap; @@ -77,6 +82,14 @@ private static ProduceProcessor cloneNewProduceProcessor(Class serialViewClas return produceViewMap.get(DEFAULT_SERIAL_CLASS); } + private static String defaultResponseEncoding() { + if (defaultResponseEncoding == null) { + defaultResponseEncoding = LegacyPropertyFactory + .getStringProperty(PARAM_DEFAULT_RESPONSE_ENCODING, MediaType.APPLICATION_JSON); + } + return defaultResponseEncoding; + } + public ProduceProcessor findJsonProcessorByViewClass(Class serialViewClass) { if (serialViewClass == null) { return jsonProcessorMap.get(DEFAULT_SERIAL_CLASS); @@ -107,42 +120,56 @@ public ProduceProcessor findDefaultPlainProcessor() { public ProduceProcessor createProduceProcessor(OperationMeta operationMeta, int statusCode, String accept, Class serialViewClass) { + // If no produces defined, using default processor ApiResponses responses = operationMeta.getSwaggerOperation().getResponses(); ApiResponse response = responses.get(String.valueOf(statusCode)); if (response == null || response.getContent() == null || response.getContent().size() == 0) { return findDefaultProcessor(); } - String actualAccept = accept; - if (actualAccept == null) { - if (response.getContent().get(MediaType.APPLICATION_JSON) != null) { - actualAccept = MediaType.APPLICATION_JSON; + + // check intersection of `Accept` and `Produces` + if (accept == null) { + if (response.getContent().get(defaultResponseEncoding()) != null) { + accept = defaultResponseEncoding(); } else { - actualAccept = response.getContent().keySet().iterator().next(); + accept = response.getContent().keySet().iterator().next(); } } - ContentType contentType = ContentType.parse(actualAccept); - actualAccept = contentType.getMimeType(); - if (MediaType.WILDCARD.equals(contentType.getMimeType()) || - MediaType.MEDIA_TYPE_WILDCARD.equals(contentType.getMimeType())) { - if (response.getContent().get(MediaType.APPLICATION_JSON) != null) { - actualAccept = MediaType.APPLICATION_JSON; - } else { - actualAccept = response.getContent().keySet().iterator().next(); + + String actualAccept = null; + for (String item : accept.split(",")) { + ContentType contentType = ContentType.parse(item); + if (MediaType.WILDCARD.equals(contentType.getMimeType()) || + MediaType.MEDIA_TYPE_WILDCARD.equals(contentType.getMimeType())) { + if (response.getContent().get(defaultResponseEncoding()) != null) { + actualAccept = defaultResponseEncoding(); + } else { + actualAccept = response.getContent().keySet().iterator().next(); + } + break; + } + if (response.getContent().get(contentType.getMimeType()) != null) { + actualAccept = contentType.getMimeType(); + break; } } - if (response.getContent().get(actualAccept) == null) { - LOGGER.warn("Operation do not support accept type {}/{}", accept, actualAccept); + + if (actualAccept == null) { + LOGGER.warn("Operation {} do not support accept type {}", operationMeta.getSchemaQualifiedName(), accept); return findDefaultProcessor(); } + if (MediaType.APPLICATION_JSON.equals(actualAccept)) { + return findJsonProcessorByViewClass(serialViewClass); + } if (SwaggerConst.PROTOBUF_TYPE.equals(actualAccept)) { return new ProduceProtoBufferProcessor(operationMeta, operationMeta.getSchemaMeta().getSwagger(), response.getContent().get(actualAccept).getSchema()); } - if (MediaType.TEXT_PLAIN.equals(actualAccept)) { - return findPlainProcessorByViewClass(serialViewClass); + if (MediaType.SERVER_SENT_EVENTS.equals(actualAccept)) { + return new ProduceEventStreamProcessor(); } - // json - return findJsonProcessorByViewClass(serialViewClass); + // text plain + return findPlainProcessorByViewClass(serialViewClass); } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/AbstractQueryCodec.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/AbstractQueryCodec.java index 5e0d196302f..0ba395bb43f 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/AbstractQueryCodec.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/AbstractQueryCodec.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.Collections; +import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder.URLPathStringBuilder; public abstract class AbstractQueryCodec implements QueryCodec { @@ -43,6 +44,10 @@ public void encode(URLPathStringBuilder builder, String name, Object value) thro } if (value.getClass().isArray()) { + if (!(value instanceof Object[])) { + value = RestObjectMapperFactory.getRestObjectMapper() + .convertValue(value, Object[].class); + } encode(builder, name, Arrays.asList((Object[]) value)); return; } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodec.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodec.java index 3282cc1906c..b9bfd1b7130 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodec.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodec.java @@ -35,7 +35,7 @@ static String encodeValue(Object value) throws UnsupportedEncodingException { return URLEncoder.encode(value.toString(), StandardCharsets.UTF_8.name()); } - // can not replaced by value.toString() because of date serialize + // can not be replaced by value.toString() because of date serialize static String convertToString(Object value) throws Exception { return RestObjectMapperFactory.getRestObjectMapper().convertToString(value); } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMulti.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMulti.java index 551ccee01ae..4c2f4287916 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMulti.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/codec/query/QueryCodecMulti.java @@ -25,9 +25,6 @@ import jakarta.servlet.http.HttpServletRequest; -/** - * ?query=x1&query=x2 - */ @SuppressWarnings("unchecked") public class QueryCodecMulti extends AbstractQueryCodec { public static final String CODEC_NAME = "form:1"; diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java index 42e2db348ca..580175ec680 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/definition/RestOperationMeta.java @@ -23,8 +23,6 @@ import java.util.List; import java.util.Map; -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.common.rest.codec.param.FormProcessorCreator.PartProcessor; import org.apache.servicecomb.common.rest.definition.path.PathRegExp; import org.apache.servicecomb.common.rest.definition.path.URLPathBuilder; @@ -35,15 +33,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.type.TypeFactory; - import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.media.MapSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; -import io.swagger.v3.oas.models.parameters.RequestBody; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponses; import jakarta.ws.rs.core.MediaType; @@ -59,6 +52,8 @@ public class RestOperationMeta { // make sure if response is file protected boolean downloadFile; + protected boolean serverSendEvents; + protected List paramList = new ArrayList<>(); // key为参数名 @@ -80,6 +75,7 @@ public void init(OperationMeta operationMeta) { Operation operation = operationMeta.getSwaggerOperation(); this.downloadFile = checkDownloadFileFlag(); + this.serverSendEvents = checkServerSendEvents(); if (operation.getParameters() != null) { for (int swaggerParameterIdx = 0; swaggerParameterIdx < operation.getParameters().size(); swaggerParameterIdx++) { @@ -106,13 +102,12 @@ public void init(OperationMeta operationMeta) { } } - setAbsolutePath(concatPath(SwaggerUtils.getBasePath(swagger), operationMeta.getOperationPath())); + setAbsolutePath(SwaggerUtils.concatAbsolutePath(swagger, operationMeta.getOperationPath())); } private void addRestParamByName(OperationMeta operationMeta, String name, Operation operation) { Type type = operationMeta.getSwaggerProducerOperation() != null ? operationMeta.getSwaggerProducerOperation() .getSwaggerParameterTypes().get(name) : null; - type = correctFormBodyType(operation.getRequestBody(), type); RestParam param = new RestParam(operationMeta, name, operation.getRequestBody(), formData, type); addParam(param); } @@ -129,39 +124,24 @@ private Schema formSchemas(Operation operation) { return operation.getRequestBody().getContent().get(SwaggerConst.FILE_MEDIA_TYPE).getSchema(); } - /** - * EdgeService cannot recognize the map type form body whose value type is String, - * so there should be this additional setting. - * @param parameter the swagger information of the parameter - * @param type the resolved param type - * @return the corrected param type - */ - private Type correctFormBodyType(RequestBody parameter, Type type) { - if (null != type || parameter == null) { - return type; - } - if (parameter.getContent().get(MediaType.APPLICATION_JSON) == null - || !(parameter.getContent().get(MediaType.APPLICATION_JSON).getSchema() instanceof MapSchema)) { - return null; - } - String className = SwaggerUtils.getClassName(parameter.getExtensions()); - if (!StringUtils.isEmpty(className)) { - try { - JavaType javaType = TypeFactory.defaultInstance().constructFromCanonical(className); - return RestObjectMapperFactory.getRestObjectMapper().getTypeFactory() - .constructMapType(Map.class, String.class, javaType.getRawClass()); - } catch (Throwable e) { - // ignore - } - } - return RestObjectMapperFactory.getRestObjectMapper().getTypeFactory() - .constructMapType(Map.class, String.class, Object.class); - } - public boolean isDownloadFile() { return downloadFile; } + public boolean isServerSendEvents() { + return serverSendEvents; + } + + private boolean checkServerSendEvents() { + ApiResponses responses = operationMeta.getSwaggerOperation().getResponses(); + if (responses == null) { + return false; + } + ApiResponse response = responses.get(SwaggerConst.SUCCESS_KEY); + return response != null && response.getContent() != null + && response.getContent().get(MediaType.SERVER_SENT_EVENTS) != null; + } + private boolean checkDownloadFileFlag() { ApiResponses responses = operationMeta.getSwaggerOperation().getResponses(); if (responses == null) { @@ -188,21 +168,6 @@ public void setOperationMeta(OperationMeta operationMeta) { this.operationMeta = operationMeta; } - /** - * Concat the two paths to an absolute path, end of '/' added. - * - * e.g. "/" + "/ope" = /ope/ - * e.g. "/prefix" + "/ope" = /prefix/ope/ - */ - private String concatPath(String basePath, String operationPath) { - return ("/" + nonNullify(basePath) + "/" + nonNullify(operationPath)) - .replaceAll("/{2,}", "/"); - } - - private String nonNullify(String path) { - return path == null ? "" : path; - } - public String getAbsolutePath() { return this.absolutePath; } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java index 2d74f4c8e05..1d21e9c8bbd 100644 --- a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilter.java @@ -21,6 +21,7 @@ import static com.google.common.net.HttpHeaders.TRANSFER_ENCODING; import static org.apache.servicecomb.core.exception.Exceptions.toProducerResponse; +import java.io.IOException; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; @@ -34,25 +35,30 @@ import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; -import org.apache.servicecomb.foundation.common.utils.AsyncUtils; +import org.apache.servicecomb.foundation.common.utils.PartUtils; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.context.TransportContext; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.netty.buffer.Unpooled; import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; -public class RestServerCodecFilter implements ProviderFilter { +public class RestServerCodecFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(RestServerCodecFilter.class); public static final String NAME = "rest-server-codec"; @@ -63,7 +69,7 @@ public String getName() { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { // almost time, should be the first filter. return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 2000; } @@ -83,61 +89,149 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne } protected CompletableFuture invokeNext(Invocation invocation, FilterNode nextNode) { + if (invocation.isEdge()) { + TransportContext transportContext = invocation.getTransportContext(); + return nextNode.onFilter(invocation).whenComplete((r, e) -> invocation.setTransportContext(transportContext)); + } return nextNode.onFilter(invocation); } protected void decodeRequest(Invocation invocation) { + invocation.getInvocationStageTrace().startProviderDecodeRequest(); HttpServletRequestEx requestEx = invocation.getRequestEx(); OperationMeta operationMeta = invocation.getOperationMeta(); RestOperationMeta restOperationMeta = operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); Map swaggerArguments = RestCodec.restToArgs(requestEx, restOperationMeta); invocation.setSwaggerArguments(swaggerArguments); + invocation.getInvocationStageTrace().finishProviderDecodeRequest(); } protected CompletableFuture encodeResponse(Invocation invocation, Response response) { invocation.onEncodeResponseStart(response); - HttpTransportContext transportContext = invocation.getTransportContext(); - + HttpServletResponseEx responseEx = transportContext.getResponseEx(); // TODO: response support JsonView ProduceProcessor produceProcessor = ProduceProcessorManager.INSTANCE .createProduceProcessor(invocation.getOperationMeta(), response.getStatusCode(), invocation.getRequestEx().getHeader(HttpHeaders.ACCEPT), null); - HttpServletResponseEx responseEx = transportContext.getResponseEx(); - boolean download = isDownloadFileResponseType(invocation, response); - return encodeResponse(response, download, produceProcessor, responseEx); + return encodeResponse(invocation, response, produceProcessor, responseEx) + .whenComplete((r, e) -> invocation.onEncodeResponseFinish()); + } + + private static boolean isFailedResponse(Response response) { + return response.getResult() instanceof InvocationException; + } + + private static CompletableFuture writePart( + HttpServletResponseEx responseEx, Object data, Response response) { + CompletableFuture result = new CompletableFuture<>(); + responseEx.sendPart(PartUtils.getSinglePart(null, data)) + .whenComplete((r, e) -> { + if (e != null) { + result.completeExceptionally(e); + return; + } + result.complete(response); + }); + return result; } - public static CompletableFuture encodeResponse(Response response, boolean download, + private static CompletableFuture writeResponse( + HttpServletResponseEx responseEx, ProduceProcessor produceProcessor, Object data, Response response, + boolean commit) { + try (BufferOutputStream output = new BufferOutputStream(Buffer.buffer())) { + produceProcessor.encodeResponse(output, data); + + CompletableFuture result = new CompletableFuture<>(); + responseEx.setBodyBuffer(output.getBuffer()); // For extensions usage + if (commit) { + responseEx.setContentLength(output.getBuffer().length()); + } + responseEx.sendBuffer(output.getBuffer()).whenComplete((v, e) -> { + if (e != null) { + result.completeExceptionally(e); + return; + } + if (!commit) { + try { + responseEx.flushBuffer(); + } catch (IOException ex) { + LOGGER.warn("Failed to flush buffer for Server Send Events", ex); + } + } + result.complete(response); + }); + return result; + } catch (Throwable e) { + LOGGER.error("internal service error must be fixed.", e); + responseEx.setStatus(500); + return CompletableFuture.failedFuture(e); + } + } + + public static CompletableFuture encodeResponse(Invocation invocation, Response response, ProduceProcessor produceProcessor, HttpServletResponseEx responseEx) { responseEx.setStatus(response.getStatusCode()); - copyHeadersToHttpResponse(response.getHeaders(), responseEx); + copyHeadersToHttpResponse(invocation, response.getHeaders(), responseEx); - if (download) { - return CompletableFuture.completedFuture(response); + if (isFailedResponse(response)) { + responseEx.setContentType(produceProcessor.getName()); + return writeResponse(responseEx, produceProcessor, ((InvocationException) response.getResult()).getErrorData(), + response, true); + } + + if (isDownloadFileResponseType(invocation, response)) { + return writePart(responseEx, response.getResult(), response); + } + + if (isServerSendEvent(response)) { + responseEx.setContentType(produceProcessor.getName()); + return writeServerSendEvent(response, produceProcessor, responseEx); } responseEx.setContentType(produceProcessor.getName()); - try (BufferOutputStream output = new BufferOutputStream(Unpooled.compositeBuffer())) { - produceProcessor.encodeResponse(output, response.getResult()); + return writeResponse(responseEx, produceProcessor, response.getResult(), response, true); + } - responseEx.setBodyBuffer(output.getBuffer()); + private static CompletableFuture writeServerSendEvent(Response response, ProduceProcessor produceProcessor, + HttpServletResponseEx responseEx) { + responseEx.setChunked(true); + CompletableFuture result = new CompletableFuture<>(); + Publisher publisher = response.getResult(); + publisher.subscribe(new Subscriber() { + Subscription subscription; - return CompletableFuture.completedFuture(response); - } catch (Throwable e) { - LOGGER.error("internal service error must be fixed.", e); - try (BufferOutputStream output = new BufferOutputStream(Unpooled.compositeBuffer())) { - responseEx.setStatus(500); - produceProcessor.encodeResponse(output, new CommonExceptionData("500", "Internal Server Error")); - responseEx.setBodyBuffer(output.getBuffer()); - return CompletableFuture.completedFuture(response); - } catch (Throwable e1) { - // we have no idea how to do, no response given to client. - return AsyncUtils.completeExceptionally(e); + @Override + public void onSubscribe(Subscription s) { + s.request(1); + subscription = s; } - } + + @Override + public void onNext(Object o) { + writeResponse(responseEx, produceProcessor, o, response, false).whenComplete((r, e) -> { + if (e != null) { + subscription.cancel(); + result.completeExceptionally(e); + return; + } + subscription.request(1); + }); + } + + @Override + public void onError(Throwable t) { + result.completeExceptionally(t); + } + + @Override + public void onComplete() { + result.complete(response); + } + }); + return result; } /** @@ -151,15 +245,22 @@ public static boolean isDownloadFileResponseType(Invocation invocation, Response invocation.findResponseType(response.getStatusCode()).getRawClass()); } - public static void copyHeadersToHttpResponse(MultiMap headers, HttpServletResponseEx responseEx) { - if (headers == null) { - return; + public static boolean isServerSendEvent(Response response) { + return response.getResult() instanceof Publisher; + } + + public static void copyHeadersToHttpResponse(Invocation invocation, MultiMap headers, + HttpServletResponseEx responseEx) { + if (headers != null) { + headers.remove(CONTENT_LENGTH); + headers.remove(TRANSFER_ENCODING); + for (Entry entry : headers.entries()) { + responseEx.addHeader(entry.getKey(), entry.getValue()); + } } - headers.remove(CONTENT_LENGTH); - headers.remove(TRANSFER_ENCODING); - for (Entry entry : headers.entries()) { - responseEx.addHeader(entry.getKey(), entry.getValue()); + if (invocation != null && responseEx.getHeader(CoreConst.TRACE_ID_NAME) == null) { + responseEx.addHeader(CoreConst.TRACE_ID_NAME, invocation.getTraceId()); } } } diff --git a/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/WebSocketServerCodecFilter.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/WebSocketServerCodecFilter.java new file mode 100644 index 00000000000..79e7368520b --- /dev/null +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/filter/inner/WebSocketServerCodecFilter.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.common.rest.filter.inner; + +import static org.apache.servicecomb.core.exception.Exceptions.toProducerResponse; + +import java.util.HashMap; +import java.util.concurrent.CompletableFuture; + +import org.apache.servicecomb.common.rest.WebSocketTransportContext; +import org.apache.servicecomb.common.rest.codec.produce.ProduceJsonProcessor; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.foundation.vertx.stream.BufferOutputStream; +import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.context.TransportContext; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.ServerWebSocket; + +public class WebSocketServerCodecFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketServerCodecFilter.class); + + public static final String NAME = "websocket-codec"; + + @Override + public String getName() { + return NAME; + } + + @Override + public int getOrder() { + // almost time, should be the first filter. + return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 2000; + } + + @Override + public boolean enabledForTransport(String transport) { + return CoreConst.WEBSOCKET.equals(transport); + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + return CompletableFuture.completedFuture(invocation) + .thenCompose(this::decodeRequest) + .thenCompose(v -> invokeNext(invocation, nextNode)) + .exceptionally(exception -> toProducerResponse(invocation, exception)) + .thenCompose(response -> encodeResponse(invocation, response)); + } + + protected CompletableFuture invokeNext(Invocation invocation, FilterNode nextNode) { + if (invocation.isEdge()) { + TransportContext transportContext = invocation.getTransportContext(); + return nextNode.onFilter(invocation).whenComplete((r, e) -> invocation.setTransportContext(transportContext)); + } + return nextNode.onFilter(invocation); + } + + protected CompletableFuture decodeRequest(Invocation invocation) { + invocation.getInvocationStageTrace().startProviderDecodeRequest(); + invocation.setSwaggerArguments(new HashMap<>()); // set context parameters and do nothing else. + invocation.getInvocationStageTrace().finishProviderDecodeRequest(); + return CompletableFuture.completedFuture(null); + } + + protected CompletableFuture encodeResponse(Invocation invocation, Response response) { + invocation.onEncodeResponseStart(response); + WebSocketTransportContext context = invocation.getTransportContext(); + + return encodeResponse(response, context.getServerWebSocket()) + .whenComplete((r, e) -> invocation.onEncodeResponseFinish()); + } + + private static boolean isFailedResponse(Response response) { + return response.getResult() instanceof InvocationException; + } + + private static CompletableFuture writeResponse( + ServerWebSocket webSocket, Object data, Response response) { + try (BufferOutputStream output = new BufferOutputStream(Buffer.buffer())) { + ProduceJsonProcessor produceProcessor = new ProduceJsonProcessor(); + produceProcessor.encodeResponse(output, data); + CompletableFuture result = new CompletableFuture<>(); + webSocket.write(output.getBuffer()).onComplete(v -> + result.complete(response), result::completeExceptionally); + + return result; + } catch (Throwable e) { + LOGGER.error("internal service error must be fixed.", e); + return CompletableFuture.failedFuture(e); + } + } + + public static CompletableFuture encodeResponse(Response response, ServerWebSocket webSocket) { + if (isFailedResponse(response)) { + return writeResponse(webSocket, ((InvocationException) response.getResult()).getErrorData(), + response); + } + return CompletableFuture.completedFuture(response); + } +} diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationItem.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationItem.java similarity index 97% rename from edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationItem.java rename to common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationItem.java index 2fa38461faa..d884e49e453 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationItem.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationItem.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.edge.core; +package org.apache.servicecomb.common.rest.route; import java.util.regex.Pattern; diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationLoader.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationLoader.java similarity index 98% rename from edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationLoader.java rename to common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationLoader.java index ae93c134950..5c3df2c3355 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedConfigurationLoader.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/URLMappedConfigurationLoader.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.edge.core; +package org.apache.servicecomb.common.rest.route; import java.util.HashMap; import java.util.Map; diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/Utils.java b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/Utils.java similarity index 96% rename from edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/Utils.java rename to common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/Utils.java index 9bbefb9f0d1..0e1f64291c9 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/Utils.java +++ b/common/common-rest/src/main/java/org/apache/servicecomb/common/rest/route/Utils.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.servicecomb.edge.core; +package org.apache.servicecomb.common.rest.route; /** * Commonly used methods in this package. diff --git a/common/common-rest/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module b/common/common-rest/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module index 70251186a5a..65a6c38a587 100644 --- a/common/common-rest/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module +++ b/common/common-rest/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.foundation.common.utils.json.PartModule \ No newline at end of file +org.apache.servicecomb.foundation.common.utils.json.PartModule diff --git a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor index 9353b9dce7c..c13dc4dac85 100644 --- a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor +++ b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor @@ -16,4 +16,4 @@ # org.apache.servicecomb.common.rest.codec.produce.ProduceJsonProcessor -org.apache.servicecomb.common.rest.codec.produce.ProduceTextPlainProcessor \ No newline at end of file +org.apache.servicecomb.common.rest.codec.produce.ProduceTextPlainProcessor diff --git a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider index cf4b594be98..94be435a96b 100644 --- a/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider +++ b/common/common-rest/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.common.rest.filter.RestFilterProvider \ No newline at end of file +org.apache.servicecomb.common.rest.filter.RestFilterProvider diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java index cc62f4c2e60..50b3e8329d5 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/RestProducerInvocationCreatorTest.java @@ -18,6 +18,7 @@ package org.apache.servicecomb.common.rest; import static jakarta.ws.rs.core.Response.Status.NOT_FOUND; +import static org.apache.servicecomb.common.rest.RestConst.DECODE_INVOCATION_CONTEXT; import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; import static org.assertj.core.api.Assertions.assertThat; @@ -80,9 +81,11 @@ public class RestProducerInvocationCreatorTest { static SCBEngine engine; + static Environment environment; + @BeforeAll public static void beforeClass() { - Environment environment = Mockito.mock(Environment.class); + environment = Mockito.mock(Environment.class); engine = SCBBootstrap.createSCBEngineForTest(environment); engine.setStatus(SCBStatus.UP); LegacyPropertyFactory.setEnvironment(environment); @@ -158,6 +161,8 @@ public void should_save_path_var_map_in_requestEx() { @Test public void should_merge_invocation_context_from_request() { + Mockito.when(environment.getProperty(DECODE_INVOCATION_CONTEXT, boolean.class, true)) + .thenReturn(true); try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) .thenReturn(servicePathManager); @@ -174,4 +179,25 @@ public void should_merge_invocation_context_from_request() { assertThat(invocation.getContext("k")).isEqualTo("v"); } } + + @Test + public void should_not_merge_invocation_context_from_request() { + Mockito.when(environment.getProperty(DECODE_INVOCATION_CONTEXT, boolean.class, true)) + .thenReturn(false); + try (MockedStatic mockedStatic = Mockito.mockStatic(ServicePathManager.class)) { + mockedStatic.when(() -> ServicePathManager.getServicePathManager(microserviceMeta)) + .thenReturn(servicePathManager); + Mockito.when(creator.locateOperation(microserviceMeta)).thenReturn(locator); + Mockito.when(locator.getOperation()).thenReturn(restOperationMeta); + Mockito.when(restOperationMeta.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); + Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); + Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); + Mockito.when(requestEx.getHeader(CoreConst.CSE_CONTEXT)).thenReturn("{\"k\":\"v\"}"); + + Invocation invocation = creator.createAsync().join(); + + assertThat(invocation.getContext("k")).isNull(); + } + } } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java index 6939974ffd3..c134cb46558 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestCodec.java @@ -18,12 +18,10 @@ package org.apache.servicecomb.common.rest.codec; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.servicecomb.common.rest.codec.param.ParamValueProcessor; -import org.apache.servicecomb.common.rest.codec.param.RestClientRequestImpl; import org.apache.servicecomb.common.rest.definition.RestOperationMeta; import org.apache.servicecomb.common.rest.definition.RestParam; import org.apache.servicecomb.core.definition.OperationMeta; @@ -47,14 +45,6 @@ public class TestRestCodec { private static RestOperationMeta restOperation; - private static final Map header = new HashMap<>(); - - private static RestClientRequest clientRequest = new RestClientRequestImpl(null, null, null) { - public void putHeader(String name, String value) { - header.put(name, value); - } - }; - private static List paramList = null; static Environment environment = Mockito.mock(Environment.class); @@ -75,7 +65,7 @@ public static void beforeClass() { Parameter hp = new HeaderParameter(); hp.setName("header"); - hp.setSchema(new Schema()); + hp.setSchema(new Schema<>()); RestParam restParam = new RestParam(null, hp, int.class); restOperation = Mockito.mock(RestOperationMeta.class); @@ -89,23 +79,9 @@ public static void beforeClass() { @AfterAll public static void afterClass() { restOperation = null; - clientRequest = null; paramList.clear(); } - @Test - public void testArgsToRest() { - try { - Map args = new HashMap<>(); - args.put("header", "abc"); - RestCodec.argsToRest(args, restOperation, clientRequest); - Assertions.assertEquals("abc", header.get("header")); - } catch (Exception e) { - e.printStackTrace(); - Assertions.fail(); - } - } - @Test public void testRestToArgs() throws Exception { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestObjectMapper.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestObjectMapper.java index d2f17f5fefe..57da9440e4f 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestObjectMapper.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/TestRestObjectMapper.java @@ -39,7 +39,7 @@ public class TestRestObjectMapper { @Test - public void testFormateDate() throws Exception { + public void testFormatDate() throws Exception { RestObjectMapper mapper = new RestObjectMapper(); // must read/write ISO 8061 dates Date date = mapper.readValue("\"2017-07-21T17:32:28Z\"".getBytes(), Date.class); diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/fix/TestDoSFix.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/fix/TestDoSFix.java index db0f04e863b..361a3844d4c 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/fix/TestDoSFix.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/fix/TestDoSFix.java @@ -79,7 +79,7 @@ void fastFail(Callable callable, Class eCls) { long start = System.currentTimeMillis(); try { Object ret = callable.call(); - Assertions.fail("expect failed, but succes to be " + ret); + Assertions.fail("expect failed, but success to be " + ret); } catch (AssertionError e) { throw e; } catch (Throwable e) { diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java index 3debd16455c..ab71e3a9e99 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestBodyProcessor.java @@ -41,8 +41,6 @@ import com.fasterxml.jackson.databind.type.TypeFactory; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.media.Content; import io.swagger.v3.oas.models.parameters.RequestBody; @@ -66,7 +64,7 @@ public class TestBodyProcessor { ParamValueProcessor processor; - final ByteBuf inputBodyByteBuf = Unpooled.buffer(); + final Buffer inputBodyByteBuf = Buffer.buffer(); final BufferInputStream inputStream = new BufferInputStream(inputBodyByteBuf); @@ -118,7 +116,7 @@ public void before() { LegacyPropertyFactory.setEnvironment(environment); Mockito.when(environment.getProperty("servicecomb.rest.parameter.decodeAsObject", boolean.class, false)) .thenReturn(false); - headers = new HeadersMultiMap(); + headers = HeadersMultiMap.httpHeaders(); value = "value"; } @@ -142,7 +140,7 @@ public void testGetValueNoAttrNoStream() throws Exception { @Test public void testGetValueTextPlain() throws Exception { setupGetValue(String.class); - inputBodyByteBuf.writeCharSequence("abc", StandardCharsets.UTF_8); + inputBodyByteBuf.appendString("abc", StandardCharsets.UTF_8.toString()); Mockito.when(request.getContentType()).thenReturn(MediaType.TEXT_PLAIN); @@ -152,7 +150,7 @@ public void testGetValueTextPlain() throws Exception { @Test public void testGetValueContextTypeJson() throws Exception { setupGetValue(Integer.class); - inputBodyByteBuf.writeCharSequence("\"1\"", StandardCharsets.UTF_8); + inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); Mockito.when(request.getContentType()).thenReturn(MediaType.APPLICATION_JSON); @@ -162,7 +160,7 @@ public void testGetValueContextTypeJson() throws Exception { @Test public void testGetValueDefaultJson() throws Exception { setupGetValue(Integer.class); - inputBodyByteBuf.writeCharSequence("\"1\"", StandardCharsets.UTF_8); + inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); Assertions.assertEquals(1, processor.getValue(request)); } @@ -220,7 +218,7 @@ public void testGetProcessorType() { public void testGetValueRawJson() throws Exception { createRawJsonProcessor(); initInputStream(); - inputBodyByteBuf.writeCharSequence("\"1\"", StandardCharsets.UTF_8); + inputBodyByteBuf.appendString("\"1\"", StandardCharsets.UTF_8.toString()); Assertions.assertEquals("\"1\"", processor.getValue(request)); } diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessorCreator.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessorCreator.java index 7cae38cd00a..d3769dcb2c5 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessorCreator.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestCookieProcessorCreator.java @@ -18,13 +18,24 @@ package org.apache.servicecomb.common.rest.codec.param; import org.apache.servicecomb.common.rest.codec.param.CookieProcessorCreator.CookieProcessor; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.CookieParameter; public class TestCookieProcessorCreator { + static Environment environment = Mockito.mock(Environment.class); + + @BeforeAll + public static void beforeClass() { + LegacyPropertyFactory.setEnvironment(environment); + } + @Test public void testCreate() { ParamValueProcessorCreator creator = diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java deleted file mode 100644 index 410515f53c3..00000000000 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/codec/param/TestRestClientRequestImpl.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.common.rest.codec.param; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledForJreRange; -import org.junit.jupiter.api.condition.JRE; -import org.mockito.MockedStatic; -import org.mockito.Mockito; - -import com.google.common.collect.Multimap; - -import io.vertx.core.Context; -import io.vertx.core.MultiMap; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpClientRequest; -import jakarta.servlet.http.Part; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.MediaType; - -public class TestRestClientRequestImpl { - private HttpClientRequest request; - - private final Context context = Mockito.mock(Context.class); - - @BeforeEach - public void before() { - request = Mockito.mock(HttpClientRequest.class); - } - - @AfterEach - public void after() { - Mockito.reset(request); - } - - @Test - public void testForm() throws Exception { - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - restClientRequest.addForm("abc", "Hello"); - restClientRequest.addForm("def", "world"); - restClientRequest.addForm("ghi", null); - Buffer buffer = restClientRequest.getBodyBuffer(); - assertThat(buffer.toString()).isIn("def=world&abc=Hello&", "abc=Hello&def=world&"); - } - - @Test - public void testCookie() throws Exception { - final MultiMap map = MultiMap.caseInsensitiveMultiMap(); - Mockito.doAnswer(invocation -> { - map.add(io.vertx.core.http.HttpHeaders.COOKIE, "sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; "); - return null; - }).when(request) - .putHeader(io.vertx.core.http.HttpHeaders.COOKIE, "sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; "); - Mockito.doAnswer(invocation -> { - map.add(io.vertx.core.http.HttpHeaders.COOKIE, "sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; "); - return null; - }).when(request) - .putHeader(io.vertx.core.http.HttpHeaders.COOKIE, "region=china-north; sessionid=abcdefghijklmnopqrstuvwxyz; "); - Mockito.when(request.headers()).thenReturn(map); - - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - restClientRequest.addCookie("sessionid", "abcdefghijklmnopqrstuvwxyz"); - restClientRequest.addCookie("region", "china-north"); - restClientRequest.write(Buffer.buffer("I love servicecomb")); - restClientRequest.end(); - Buffer buffer = restClientRequest.getBodyBuffer(); - Assertions.assertEquals("I love servicecomb", buffer.toString()); - Assertions.assertEquals("sessionid=abcdefghijklmnopqrstuvwxyz; region=china-north; ", - restClientRequest.request.headers().get(HttpHeaders.COOKIE)); - } - - @Test - public void fileBoundaryInfo_nullSubmittedFileName() { - Part part = Mockito.mock(Part.class); - Mockito.when(part.getSubmittedFileName()).thenReturn(null); - Mockito.when(part.getContentType()).thenReturn("abc"); - - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - Buffer buffer = restClientRequest.fileBoundaryInfo("boundary", "name", part); - Assertions.assertEquals("\r\n" + - "--boundary\r\n" + - "Content-Disposition: form-data; name=\"name\"; filename=\"null\"\r\n" + - "Content-Type: abc\r\n" + - "Content-Transfer-Encoding: binary\r\n" + - "\r\n", buffer.toString()); - } - - @Test - public void fileBoundaryInfo_validSubmittedFileName() { - Part part = Mockito.mock(Part.class); - Mockito.when(part.getSubmittedFileName()).thenReturn("a.txt"); - Mockito.when(part.getContentType()).thenReturn(MediaType.TEXT_PLAIN); - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - Buffer buffer = restClientRequest.fileBoundaryInfo("boundary", "name", part); - Assertions.assertEquals("\r\n" + - "--boundary\r\n" + - "Content-Disposition: form-data; name=\"name\"; filename=\"a.txt\"\r\n" + - "Content-Type: text/plain\r\n" + - "Content-Transfer-Encoding: binary\r\n" + - "\r\n", buffer.toString()); - } - - @Test - public void testAttach() { - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - Part part = Mockito.mock(Part.class); - String fileName = "fileName"; - - restClientRequest.attach(fileName, part); - - Multimap uploads = restClientRequest.uploads; - Assertions.assertEquals(1, uploads.size()); - MatcherAssert.assertThat(uploads.asMap(), Matchers.hasEntry(fileName, Arrays.asList(part))); - } - - @Test - public void testAttachOnPartIsNull() { - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, null, null); - - restClientRequest.attach("fileName", null); - - Multimap uploads = restClientRequest.uploads; - Assertions.assertTrue(uploads.isEmpty()); - } - - @Test - @EnabledForJreRange(min = JRE.JAVA_9) - public void doEndWithUploadAfterJre8() { - Map headers = new HashMap<>(); - Mockito.doAnswer(invocation -> { - headers.put(HttpHeaders.CONTENT_TYPE, - "multipart/form-data; charset=UTF-8; boundary=boundary00000000-0000-0000-0000-000000000000"); - return null; - }).when(request).putHeader(HttpHeaders.CONTENT_TYPE, - "multipart/form-data; charset=UTF-8; boundary=boundary00000000-0000-0000-0000-000000000000"); - - UUID uuid = new UUID(0, 0); - try (MockedStatic mockedStatic = Mockito.mockStatic(UUID.class)) { - mockedStatic.when(UUID::randomUUID).thenReturn(uuid); - RestClientRequestImpl restClientRequest = new RestClientRequestImpl(request, context, null); - restClientRequest.doEndWithUpload(); - - Assertions.assertEquals( - "multipart/form-data; charset=UTF-8; boundary=boundary00000000-0000-0000-0000-000000000000", - headers.get(HttpHeaders.CONTENT_TYPE)); - } - } -} diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java index 41f4826fca0..0decec28bd9 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/filter/inner/RestServerCodecFilterTest.java @@ -23,6 +23,7 @@ import static org.apache.servicecomb.core.SCBEngine.CFG_KEY_TURN_DOWN_STATUS_WAIT_SEC; import static org.apache.servicecomb.core.SCBEngine.DEFAULT_TURN_DOWN_STATUS_WAIT_SEC; +import java.io.ByteArrayInputStream; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -38,8 +39,10 @@ import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.invocation.InvocationFactory; +import org.apache.servicecomb.foundation.common.part.InputStreamPart; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; @@ -87,10 +90,20 @@ public class RestServerCodecFilterTest { final MultiMap headers = MultiMap.caseInsensitiveMultiMap(); - final FilterNode nextNode = new FilterNode((invocation, next) -> { - Response response = Response.ok("ok"); - response.setHeaders(headers); - return CompletableFuture.completedFuture(response); + final Part part = new InputStreamPart("ok", new ByteArrayInputStream(new byte[] {1, 2})); + + final FilterNode nextNode = new FilterNode(new AbstractFilter() { + @Override + public String getName() { + return "f2"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + Response response = Response.ok(part); + response.setHeaders(headers); + return CompletableFuture.completedFuture(response); + } }); static SCBEngine engine; @@ -116,6 +129,7 @@ public void setUp() { Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); Mockito.when(operationMeta.buildBaseProviderRuntimeType()).thenReturn(invocationRuntimeType); invocation = Mockito.spy(InvocationFactory.forProvider(endpoint, operationMeta, null)); + Mockito.when(responseEx.sendPart(part)).thenReturn(CompletableFuture.completedFuture(null)); } private void mockDecodeRequestFail() { diff --git a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/resource/TestClassPathStaticResourceHandler.java b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/resource/TestClassPathStaticResourceHandler.java index 37ff95bb642..f95468f4f38 100644 --- a/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/resource/TestClassPathStaticResourceHandler.java +++ b/common/common-rest/src/test/java/org/apache/servicecomb/common/rest/resource/TestClassPathStaticResourceHandler.java @@ -20,10 +20,6 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; -import jakarta.servlet.http.Part; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.Response.Status; - import org.apache.commons.io.IOUtils; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; @@ -35,6 +31,10 @@ import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import jakarta.servlet.http.Part; +import jakarta.ws.rs.core.HttpHeaders; +import jakarta.ws.rs.core.Response.Status; + public class TestClassPathStaticResourceHandler { static ClassPathStaticResourceHandler handler = new ClassPathStaticResourceHandler(); @@ -49,7 +49,7 @@ public void normal() throws IOException { Part part = response.getResult(); try (InputStream is = part.getInputStream()) { - Assertions.assertTrue(IOUtils.toString(is, StandardCharsets.UTF_8).endsWith("")); + Assertions.assertTrue(IOUtils.toString(is, StandardCharsets.UTF_8).trim().endsWith("")); } Assertions.assertEquals("text/html", part.getContentType()); Assertions.assertEquals("text/html", response.getHeader(HttpHeaders.CONTENT_TYPE)); @@ -84,7 +84,7 @@ public void attack() { public void readContentFailed() throws IOException { handler = Mockito.spy(TestClassPathStaticResourceHandler.handler); Mockito.when(handler.findResource("web-root/index.html")) - .thenThrow(new RuntimeExceptionWithoutStackTrace("read content failed.")); + .thenThrow(new RuntimeExceptionWithoutStackTrace("read content failed.")); try (LogCollector logCollector = new LogCollector()) { Response response = handler.handle("index.html"); diff --git a/common/common-rest/src/test/resources/web-root/index.html b/common/common-rest/src/test/resources/web-root/index.html index 6aed06f6ed3..8fe33dc3cfa 100644 --- a/common/common-rest/src/test/resources/web-root/index.html +++ b/common/common-rest/src/test/resources/web-root/index.html @@ -14,4 +14,4 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> - \ No newline at end of file + diff --git a/common/pom.xml b/common/pom.xml index 8a63166ed8f..c39c89ff4ce 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default common diff --git a/core/pom.xml b/core/pom.xml index 0b40ab80eb7..af73c36dff9 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default java-chassis-core @@ -40,6 +40,10 @@ org.apache.servicecomb swagger-invocation-core + + org.apache.servicecomb + swagger-generator-core + io.zipkin.brave brave diff --git a/core/src/main/java/org/apache/servicecomb/core/BootListener.java b/core/src/main/java/org/apache/servicecomb/core/BootListener.java index 507b3e32d43..ea8314e2e1d 100644 --- a/core/src/main/java/org/apache/servicecomb/core/BootListener.java +++ b/core/src/main/java/org/apache/servicecomb/core/BootListener.java @@ -115,14 +115,6 @@ default void onBootEvent(BootEvent event) { } } - default void onBeforeHandler(BootEvent event) { - - } - - default void onAfterHandler(BootEvent event) { - - } - default void onBeforeFilter(BootEvent event) { } diff --git a/core/src/main/java/org/apache/servicecomb/core/CoreConst.java b/core/src/main/java/org/apache/servicecomb/core/CoreConst.java index 651f06c4a3c..37c34b9c96d 100644 --- a/core/src/main/java/org/apache/servicecomb/core/CoreConst.java +++ b/core/src/main/java/org/apache/servicecomb/core/CoreConst.java @@ -25,10 +25,14 @@ private CoreConst() { public static final String CSE_CONTEXT = "x-cse-context"; + public static final String TRANSPORT_NAME = "x-transport-name"; + public static final String RESTFUL = "rest"; public static final String HIGHWAY = "highway"; + public static final String WEBSOCKET = "websocket"; + public static final String ANY_TRANSPORT = ""; public static final String VERSION_RULE_LATEST = DefinitionConst.VERSION_RULE_LATEST; diff --git a/core/src/main/java/org/apache/servicecomb/core/Invocation.java b/core/src/main/java/org/apache/servicecomb/core/Invocation.java index 6cddd9c934a..067fe6558f4 100644 --- a/core/src/main/java/org/apache/servicecomb/core/Invocation.java +++ b/core/src/main/java/org/apache/servicecomb/core/Invocation.java @@ -32,13 +32,9 @@ import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.event.InvocationBusinessFinishEvent; -import org.apache.servicecomb.core.event.InvocationBusinessMethodFinishEvent; import org.apache.servicecomb.core.event.InvocationBusinessMethodStartEvent; import org.apache.servicecomb.core.event.InvocationEncodeResponseStartEvent; import org.apache.servicecomb.core.event.InvocationFinishEvent; -import org.apache.servicecomb.core.event.InvocationHandlersStartEvent; -import org.apache.servicecomb.core.event.InvocationRunInExecutorFinishEvent; -import org.apache.servicecomb.core.event.InvocationRunInExecutorStartEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.core.event.InvocationStartSendRequestEvent; import org.apache.servicecomb.core.event.InvocationTimeoutCheckEvent; @@ -88,19 +84,15 @@ static Collection loadTraceIdGenerators() { // 同步模式:避免应答在网络线程中处理解码等等业务级逻辑 private Executor responseExecutor; - private boolean sync = true; + private volatile boolean sync = true; private final InvocationStageTrace invocationStageTrace = new InvocationStageTrace(this); private HttpServletRequestEx requestEx; - private boolean finished; + private volatile boolean finished; - // not extend InvocationType - // because isEdge() only affect to apm/metrics output, no need to change so many logic - private boolean edge; - - private long invocationId; + private volatile long invocationId; private TraceIdLogger traceIdLogger; @@ -261,7 +253,22 @@ public String getOperationName() { return operationMeta.getOperationId(); } + public String getProviderTransportName() { + if (operationMeta.getSwaggerOperation().getExtensions() != null && + operationMeta.getSwaggerOperation().getExtensions().get(CoreConst.TRANSPORT_NAME) != null) { + return (String) operationMeta.getSwaggerOperation().getExtensions().get(CoreConst.TRANSPORT_NAME); + } + if (schemaMeta.getSwagger().getExtensions() != null && + schemaMeta.getSwagger().getExtensions().get(CoreConst.TRANSPORT_NAME) != null) { + return (String) schemaMeta.getSwagger().getExtensions().get(CoreConst.TRANSPORT_NAME); + } + return null; + } + public String getConfigTransportName() { + if (getProviderTransportName() != null) { + return getProviderTransportName(); + } return referenceConfig.getTransport(); } @@ -332,55 +339,37 @@ protected void initTraceId(TraceIdGenerator traceIdGenerator) { addContext(traceIdGenerator.getTraceIdKeyName(), traceIdGenerator.generate()); } - public void onStart(long start) { - invocationStageTrace.start(start); + public void onStart() { initTraceId(); EventManager.post(new InvocationStartEvent(this)); } - public void onStart(HttpServletRequestEx requestEx, long start) { + public void onStart(HttpServletRequestEx requestEx) { this.requestEx = requestEx; - onStart(start); - } - - public void onExecuteStart() { - invocationStageTrace.startExecution(); - EventManager.post(new InvocationRunInExecutorStartEvent(this)); - } - - public void onExecuteFinish() { - EventManager.post(new InvocationRunInExecutorFinishEvent(this)); - } - - public void onStartHandlersRequest() { - invocationStageTrace.startHandlersRequest(); - EventManager.post(new InvocationHandlersStartEvent(this)); + onStart(); } public void onStartSendRequest() { - invocationStageTrace.startSend(); EventManager.post(new InvocationStartSendRequestEvent(this)); } - @Override public void onBusinessMethodStart() { - invocationStageTrace.startBusinessMethod(); + invocationStageTrace.startBusinessExecute(); EventManager.post(new InvocationBusinessMethodStartEvent(this)); } - @Override - public void onBusinessMethodFinish() { - EventManager.post(new InvocationBusinessMethodFinishEvent(this)); - } - public void onEncodeResponseStart(Response response) { + invocationStageTrace.startProviderEncodeResponse(); EventManager.post(new InvocationEncodeResponseStartEvent(this, response)); } - @Override + public void onEncodeResponseFinish() { + invocationStageTrace.finishProviderEncodeResponse(); + } + public void onBusinessFinish() { - invocationStageTrace.finishBusiness(); + invocationStageTrace.finishBusinessExecute(); EventManager.post(new InvocationBusinessFinishEvent(this)); } @@ -390,9 +379,9 @@ public void onFinish(Response response) { return; } + finished = true; invocationStageTrace.finish(); EventManager.post(new InvocationFinishEvent(this, response)); - finished = true; } // for retry, reset invocation and try it again @@ -421,11 +410,11 @@ public boolean isProducer() { } public boolean isEdge() { - return edge; + return InvocationType.EDGE.equals(invocationType); } - public void setEdge(boolean edge) { - this.edge = edge; + public void setEdge() { + this.invocationType = InvocationType.EDGE; } public long getInvocationId() { diff --git a/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java b/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java index d1f97d77c12..e7f903e7552 100644 --- a/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java +++ b/core/src/main/java/org/apache/servicecomb/core/NonSwaggerInvocation.java @@ -29,12 +29,12 @@ public NonSwaggerInvocation(String appId, String microserviceName) { @Override public String getSchemaId() { - throw new UnsupportedOperationException(); + return "third-schema"; } @Override public String getOperationName() { - throw new UnsupportedOperationException(); + return "third-operation"; } @Override diff --git a/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java b/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java index d290a604a05..3bd5bd5d8f7 100644 --- a/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java +++ b/core/src/main/java/org/apache/servicecomb/core/SCBApplicationListener.java @@ -80,8 +80,6 @@ public void onApplicationEvent(ApplicationEvent event) { scbEngine.setPriorityPropertyManager(applicationContext.getBean(PriorityPropertyManager.class)); scbEngine.setFilterChainsManager(applicationContext.getBean(FilterChainsManager.class)); - scbEngine.getConsumerProviderManager().getConsumerProviderList() - .addAll(applicationContext.getBeansOfType(ConsumerProvider.class).values()); scbEngine.getProducerProviderManager().getProducerProviderList() .addAll(applicationContext.getBeansOfType(ProducerProvider.class).values()); diff --git a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java index 081f479b724..c69521698af 100644 --- a/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java +++ b/core/src/main/java/org/apache/servicecomb/core/SCBEngine.java @@ -76,7 +76,7 @@ public class SCBEngine { private ProducerProviderManager producerProviderManager; - private ConsumerProviderManager consumerProviderManager = new ConsumerProviderManager(); + private ConsumerProviderManager consumerProviderManager; private MicroserviceMeta producerMicroserviceMeta; @@ -174,6 +174,11 @@ public void setOpenAPIRegistryManager(OpenAPIRegistryManager openAPIRegistryMana this.openAPIRegistryManager = openAPIRegistryManager; } + @Autowired + public void setConsumerProviderManager(ConsumerProviderManager consumerProviderManager) { + this.consumerProviderManager = consumerProviderManager; + } + @Autowired public void setExecutorManager(ExecutorManager executorManager) { this.executorManager = executorManager; @@ -246,11 +251,6 @@ public ConsumerProviderManager getConsumerProviderManager() { return consumerProviderManager; } - public SCBEngine setConsumerProviderManager(ConsumerProviderManager consumerProviderManager) { - this.consumerProviderManager = consumerProviderManager; - return this; - } - public TransportManager getTransportManager() { return transportManager; } @@ -370,8 +370,6 @@ private void doRun() throws Exception { triggerEvent(EventType.BEFORE_REGISTRY); registrationManager.run(); discoveryManager.run(); - // ensure can invoke services in AFTER_REGISTRY - registrationManager.updateMicroserviceInstanceStatus(MicroserviceInstanceStatus.UP); status = SCBStatus.UP; triggerEvent(EventType.AFTER_REGISTRY); @@ -383,7 +381,7 @@ private void createProducerMicroserviceMeta() { String microserviceName = BootStrapProperties.readServiceName(environment); producerMicroserviceMeta = new MicroserviceMeta(this, BootStrapProperties.readApplication(environment), microserviceName, false); - producerMicroserviceMeta.setFilterChain(filterChainsManager.findProducerChain( + producerMicroserviceMeta.setProviderFilterChain(filterChainsManager.findProducerChain( BootStrapProperties.readApplication(environment), microserviceName)); producerMicroserviceMeta.setMicroserviceVersionsMeta(new MicroserviceVersionsMeta(this)); } diff --git a/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java index b18d86bf85c..8abaceef9b5 100644 --- a/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java +++ b/core/src/main/java/org/apache/servicecomb/core/ServiceCombCoreConfiguration.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.core; +import org.apache.servicecomb.core.bootup.ConfigurationProblemsCollector; import org.apache.servicecomb.core.bootup.FilterChainCollector; import org.apache.servicecomb.core.bootup.ServiceInformationCollector; import org.apache.servicecomb.core.executor.ExecutorManager; @@ -23,6 +24,7 @@ import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; import org.apache.servicecomb.core.provider.RegistryOpenAPIRegistry; +import org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager; import org.apache.servicecomb.core.provider.consumer.ReferenceConfigManager; import org.apache.servicecomb.core.provider.producer.ProducerBootListener; import org.apache.servicecomb.core.transport.TransportManager; @@ -47,54 +49,65 @@ public SCBEngine scbEngine() { } @Bean - public ReferenceConfigManager referenceConfigManager() { + public ConsumerProviderManager scbConsumerProviderManager(Environment environment, + OpenAPIRegistryManager openAPIRegistryManager) { + return new ConsumerProviderManager(environment, openAPIRegistryManager); + } + + @Bean + public ReferenceConfigManager scbReferenceConfigManager() { return new ReferenceConfigManager(); } @Bean - public OpenAPIRegistryManager openAPIRegistryManager() { + public OpenAPIRegistryManager scbOpenAPIRegistryManager() { return new OpenAPIRegistryManager(); } @Bean - public LocalOpenAPIRegistry localOpenAPIRegistry(Environment environment) { + public LocalOpenAPIRegistry scbLocalOpenAPIRegistry(Environment environment) { return new LocalOpenAPIRegistry(environment); } @Bean - public RegistryOpenAPIRegistry registryOpenAPIRegistry() { + public RegistryOpenAPIRegistry scbRegistryOpenAPIRegistry() { return new RegistryOpenAPIRegistry(); } @Bean - public ProducerBootListener producerBootListener() { + public ProducerBootListener scbProducerBootListener() { return new ProducerBootListener(); } @Bean - public FilterChainCollector filterChainCollector() { + public FilterChainCollector scbFilterChainCollector() { return new FilterChainCollector(); } @Bean - public ServiceInformationCollector serviceInformationCollector() { + public ConfigurationProblemsCollector scbConfigurationProblemsCollector() { + return new ConfigurationProblemsCollector(); + } + + @Bean + public ServiceInformationCollector scbServiceInformationCollector() { return new ServiceInformationCollector(); } @Bean - public ExecutorManager executorManager() { + public ExecutorManager scbExecutorManager() { return new ExecutorManager(); } @Bean(value = {"cse.executor.groupThreadPool", "cse.executor.default", "servicecomb.executor.groupThreadPool"}) - public GroupExecutor groupExecutor(Environment environment) { + public GroupExecutor scbGroupExecutor(Environment environment) { GroupExecutor groupExecutor = new GroupExecutor(environment); groupExecutor.init(); return groupExecutor; } @Bean - public TransportManager transportManager() { + public TransportManager scbTransportManager() { return new TransportManager(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/Transport.java b/core/src/main/java/org/apache/servicecomb/core/Transport.java index 4777a5ca0e1..1c40303fce8 100644 --- a/core/src/main/java/org/apache/servicecomb/core/Transport.java +++ b/core/src/main/java/org/apache/servicecomb/core/Transport.java @@ -19,7 +19,6 @@ import org.springframework.core.env.Environment; -// TODO:感觉要拆成显式的client、server才好些 public interface Transport { String getName(); diff --git a/core/src/main/java/org/apache/servicecomb/core/annotation/Transport.java b/core/src/main/java/org/apache/servicecomb/core/annotation/Transport.java new file mode 100644 index 00000000000..c81055c7b46 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/annotation/Transport.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core.annotation; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import org.springframework.stereotype.Component; + +@Inherited +@Documented +@Retention(RUNTIME) +@Target({TYPE, METHOD, ANNOTATION_TYPE}) +@Component +public @interface Transport { + /** + * Transport name. e.g. rest, highway. + */ + String name(); +} diff --git a/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBBootstrap.java b/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBBootstrap.java index ce5cedcb1db..21040a3410f 100644 --- a/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBBootstrap.java +++ b/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBBootstrap.java @@ -23,6 +23,7 @@ import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; +import org.apache.servicecomb.core.provider.consumer.ConsumerProviderManager; import org.apache.servicecomb.core.transport.TransportManager; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.registry.DiscoveryManager; @@ -48,6 +49,7 @@ public static SCBEngine createSCBEngineForTest(Environment environment) { result.setEnvironment(environment); OpenAPIRegistryManager openAPIRegistryManager = new OpenAPIRegistryManager(); openAPIRegistryManager.setOpenAPIRegistries(List.of(new LocalOpenAPIRegistry(environment))); + result.setConsumerProviderManager(new ConsumerProviderManager(environment, openAPIRegistryManager)); result.setOpenAPIRegistryManager(openAPIRegistryManager); return result; } diff --git a/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBEngineForTest.java b/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBEngineForTest.java index 48d3fe01ddf..46ccd954155 100644 --- a/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBEngineForTest.java +++ b/core/src/main/java/org/apache/servicecomb/core/bootstrap/SCBEngineForTest.java @@ -17,14 +17,12 @@ package org.apache.servicecomb.core.bootstrap; -import java.util.Arrays; import java.util.List; import org.apache.servicecomb.config.priority.ConfigObjectFactory; import org.apache.servicecomb.config.priority.PriorityPropertyFactory; import org.apache.servicecomb.config.priority.PriorityPropertyManager; import org.apache.servicecomb.core.SCBEngine; -import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterChainsManager; import org.apache.servicecomb.core.filter.impl.EmptyFilter; import org.apache.servicecomb.foundation.common.event.EventManager; @@ -37,11 +35,12 @@ */ public class SCBEngineForTest extends SCBEngine { public SCBEngineForTest(Environment environment) { - List filters = Arrays.asList( - new EmptyFilter() - ); + EmptyFilter emptyFilter = new EmptyFilter(); + emptyFilter.setEnvironment(environment); setFilterChainsManager(new FilterChainsManager() - .addFilters(filters)); + .setProviderFilters(List.of(emptyFilter)) + .setConsumerFilters(List.of(emptyFilter)) + .setEdgeFilters(List.of(emptyFilter))); PriorityPropertyFactory propertyFactory = new PriorityPropertyFactory(environment); ConfigObjectFactory configObjectFactory = new ConfigObjectFactory(propertyFactory); diff --git a/core/src/main/java/org/apache/servicecomb/core/bootup/BootUpInformationCollector.java b/core/src/main/java/org/apache/servicecomb/core/bootup/BootUpInformationCollector.java index 62a9ac5ce2b..4ebae5180f4 100644 --- a/core/src/main/java/org/apache/servicecomb/core/bootup/BootUpInformationCollector.java +++ b/core/src/main/java/org/apache/servicecomb/core/bootup/BootUpInformationCollector.java @@ -18,13 +18,12 @@ package org.apache.servicecomb.core.bootup; import org.apache.servicecomb.core.SCBEngine; +import org.springframework.core.Ordered; -public interface BootUpInformationCollector { +public interface BootUpInformationCollector extends Ordered { default String collect(SCBEngine engine) { return collect(); } String collect(); - - int getOrder(); } diff --git a/foundations/foundation-common/src/main/java/com/fasterxml/jackson/databind/ObjectMapperUtils.java b/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsAlarmEvent.java similarity index 68% rename from foundations/foundation-common/src/main/java/com/fasterxml/jackson/databind/ObjectMapperUtils.java rename to core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsAlarmEvent.java index 5be6d73c710..631cc361235 100644 --- a/foundations/foundation-common/src/main/java/com/fasterxml/jackson/databind/ObjectMapperUtils.java +++ b/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsAlarmEvent.java @@ -14,15 +14,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.fasterxml.jackson.databind; +package org.apache.servicecomb.core.bootup; -import com.fasterxml.jackson.core.JsonParser; +import org.apache.servicecomb.foundation.common.event.AlarmEvent; -public final class ObjectMapperUtils { - private ObjectMapperUtils() { +public class ConfigurationProblemsAlarmEvent extends AlarmEvent { + private final String problems; + + public ConfigurationProblemsAlarmEvent(Type type, String problems) { + super(type); + this.problems = problems; } - public static DeserializationContext createDeserializationContext(ObjectMapper mapper, JsonParser jp) { - return mapper.createDeserializationContext(jp, mapper.getDeserializationConfig()); + public String getProblems() { + return problems; } } diff --git a/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsCollector.java b/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsCollector.java new file mode 100644 index 00000000000..415516f6fea --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/bootup/ConfigurationProblemsCollector.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.bootup; + +import java.util.Arrays; +import java.util.Set; +import java.util.regex.Pattern; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.foundation.common.event.AlarmEvent.Type; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.springframework.core.env.Environment; + +/** + * Detect deprecated and wrong usages of configurations + * and print warning messages + * and sending ConfigurationProblemsAlarmEvent. + */ +public class ConfigurationProblemsCollector implements BootUpInformationCollector { + private static final String SERVICE_NAME_PATTERN_STRING = "([A-Za-z])|([A-Za-z][A-Za-z0-9_\\-.]*[A-Za-z0-9])"; + + private static final Pattern SERVICE_NAME_PATTERN = Pattern.compile(SERVICE_NAME_PATTERN_STRING); + + @Override + public String collect(SCBEngine engine) { + StringBuilder result = new StringBuilder(); + collectCsePrefix(engine.getEnvironment(), result); + collectServiceDefinition(engine.getEnvironment(), result); + collectServiceDefinitionValidation(engine.getEnvironment(), result); + collectTimeoutConfiguration(engine.getEnvironment(), result); + collectIsolationConfiguration(engine.getEnvironment(), result); + if (result.isEmpty()) { + return null; + } + String warnings = "[WARN]Configurations warnings:\n" + result; + EventManager.post(new ConfigurationProblemsAlarmEvent(Type.OPEN, warnings)); + return warnings; + } + + private void collectServiceDefinitionValidation(Environment environment, StringBuilder result) { + String application = BootStrapProperties.readApplication(environment); + if (!SERVICE_NAME_PATTERN.matcher(application).matches()) { + result.append("application does not match pattern ").append(SERVICE_NAME_PATTERN_STRING).append("."); + } + String serviceName = BootStrapProperties.readServiceName(environment); + if (!SERVICE_NAME_PATTERN.matcher(serviceName).matches()) { + result.append("service name does not match pattern ").append(SERVICE_NAME_PATTERN_STRING).append("."); + } + } + + private void collectIsolationConfiguration(Environment environment, StringBuilder result) { + int percentage = environment.getProperty( + "servicecomb.loadbalance.isolation.errorThresholdPercentage", int.class, -1); + int continuous = environment.getProperty( + "servicecomb.loadbalance.isolation.continuousFailureThreshold", int.class, -1); + if (percentage == -1 && continuous == -1) { + return; + } + result.append("Configuration `servicecomb.loadbalance.isolation.*` is removed, use governance instead. " + + "See https://servicecomb.apache.org/references/java-chassis/" + + "zh_CN/references-handlers/governance-best-practise.html"); + } + + private void collectTimeoutConfiguration(Environment environment, StringBuilder result) { + int keepAliveTimeoutInSeconds = environment.getProperty( + "servicecomb.rest.client.connection.keepAliveTimeoutInSeconds", int.class, 60); + int idleTimeoutInSeconds = environment.getProperty( + "servicecomb.rest.client.connection.idleTimeoutInSeconds", int.class, 150); + if (keepAliveTimeoutInSeconds >= idleTimeoutInSeconds) { + result.append("Configuration `servicecomb.rest.client.connection.keepAliveTimeoutInSeconds` is longer than " + + "servicecomb.rest.client.connection.idleTimeoutInSeconds."); + result.append("[").append(keepAliveTimeoutInSeconds).append(",").append(idleTimeoutInSeconds).append("]\n"); + } + keepAliveTimeoutInSeconds = environment.getProperty( + "servicecomb.rest.client.http2.connection.keepAliveTimeoutInSeconds", int.class, 60); + idleTimeoutInSeconds = environment.getProperty( + "servicecomb.rest.client.http2.connection.idleTimeoutInSeconds", int.class, 150); + if (keepAliveTimeoutInSeconds >= idleTimeoutInSeconds) { + result.append("Configuration `servicecomb.rest.client.http2.connection.keepAliveTimeoutInSeconds` is longer than " + + "servicecomb.rest.client.http2.connection.idleTimeoutInSeconds."); + result.append("[").append(keepAliveTimeoutInSeconds).append(",").append(idleTimeoutInSeconds).append("]\n"); + } + } + + private void collectServiceDefinition(Environment environment, StringBuilder result) { + if (environment.getProperty("APPLICATION_ID") != null) { + result.append("Configurations `APPLICATION_ID` is deprecated, " + + "use `servicecomb.service.application` instead.\n"); + } + Set names = ConfigUtil.propertiesWithPrefix(environment, "service_description."); + if (!names.isEmpty()) { + result.append("Configurations with prefix `service_description` is deprecated, " + + "use `servicecomb.service` instead. Find keys "); + result.append(Arrays.toString(names.toArray())); + result.append("\n"); + } + } + + private void collectCsePrefix(Environment environment, StringBuilder result) { + Set names = ConfigUtil.propertiesWithPrefix(environment, "cse."); + if (!names.isEmpty()) { + result.append("Configurations with prefix `cse` is deprecated, use `servicecomb` instead. Find keys "); + result.append(Arrays.toString(names.toArray())); + result.append("\n"); + } + } + + @Override + public String collect() { + return null; + } + + @Override + public int getOrder() { + return 1000; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceMeta.java b/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceMeta.java index a9b29d5dbaa..9476a34229b 100644 --- a/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceMeta.java +++ b/core/src/main/java/org/apache/servicecomb/core/definition/MicroserviceMeta.java @@ -26,10 +26,6 @@ import io.swagger.v3.oas.models.OpenAPI; -/** - * should named MicroserviceVersionMeta
- * but for compatible reason, keep the old name - */ public class MicroserviceMeta { private final SCBEngine scbEngine; @@ -45,9 +41,14 @@ public class MicroserviceMeta { // key is OperationMeta.getMicroserviceQualifiedName() private final Map operationMetas = new HashMap<>(); + // Used to indicate configuration items type. EDGE & CONSUMER chain are all consumer. private final boolean consumer; - private FilterNode filterChain = FilterNode.EMPTY; + private FilterNode consumerFilterChain = FilterNode.EMPTY; + + private FilterNode providerFilterChain = FilterNode.EMPTY; + + private FilterNode edgeFilterChain = FilterNode.EMPTY; private final VendorExtensions vendorExtensions = new VendorExtensions(); @@ -132,11 +133,27 @@ public T getExtData(String key) { return vendorExtensions.get(key); } - public FilterNode getFilterChain() { - return filterChain; + public FilterNode getConsumerFilterChain() { + return consumerFilterChain; + } + + public void setConsumerFilterChain(FilterNode consumerFilterChain) { + this.consumerFilterChain = consumerFilterChain; + } + + public FilterNode getProviderFilterChain() { + return providerFilterChain; + } + + public void setProviderFilterChain(FilterNode providerFilterChain) { + this.providerFilterChain = providerFilterChain; + } + + public FilterNode getEdgeFilterChain() { + return edgeFilterChain; } - public void setFilterChain(FilterNode filterChain) { - this.filterChain = filterChain; + public void setEdgeFilterChain(FilterNode edgeFilterChain) { + this.edgeFilterChain = edgeFilterChain; } } diff --git a/core/src/main/java/org/apache/servicecomb/core/definition/OperationMeta.java b/core/src/main/java/org/apache/servicecomb/core/definition/OperationMeta.java index 015096eb094..003daaffab1 100644 --- a/core/src/main/java/org/apache/servicecomb/core/definition/OperationMeta.java +++ b/core/src/main/java/org/apache/servicecomb/core/definition/OperationMeta.java @@ -47,8 +47,6 @@ public class OperationMeta { private final ResponsesMeta responsesMeta = new ResponsesMeta(); - private OperationConfig config; - private final VendorExtensions vendorExtensions = new VendorExtensions(); public OperationMeta init(SchemaMeta schemaMeta, SwaggerOperation swaggerOperation) { @@ -60,7 +58,6 @@ public OperationMeta init(SchemaMeta schemaMeta, SwaggerOperation swaggerOperati this.operationPath = swaggerOperation.getPath(); this.swaggerOperation = swaggerOperation.getOperation(); this.executor = schemaMeta.getMicroserviceMeta().getScbEngine().getExecutorManager().findExecutor(this); - this.config = schemaMeta.getMicroserviceMeta().getMicroserviceVersionsMeta().getOrCreateOperationConfig(this); this.responsesMeta.init(schemaMeta.getSwagger(), swaggerOperation.getOperation()); return this; @@ -75,7 +72,7 @@ public SwaggerProducerOperation getSwaggerProducerOperation() { } public OperationConfig getConfig() { - return config; + return schemaMeta.getMicroserviceMeta().getMicroserviceVersionsMeta().getOrCreateOperationConfig(this); } public String getHttpMethod() { diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationFinishEvent.java b/core/src/main/java/org/apache/servicecomb/core/event/InvocationFinishEvent.java index d1d96b4d1b5..21a20226d01 100644 --- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationFinishEvent.java +++ b/core/src/main/java/org/apache/servicecomb/core/event/InvocationFinishEvent.java @@ -20,14 +20,7 @@ import org.apache.servicecomb.swagger.invocation.Response; public class InvocationFinishEvent extends InvocationWithResponseEvent { - private final long nanoCurrent; - public InvocationFinishEvent(Invocation invocation, Response response) { super(invocation, response); - this.nanoCurrent = invocation.getInvocationStageTrace().getFinish(); - } - - public long getNanoCurrent() { - return nanoCurrent; } } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/CoreExceptionConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/exception/CoreExceptionConfiguration.java index f10692bd5b9..856888764a1 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/CoreExceptionConfiguration.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/CoreExceptionConfiguration.java @@ -22,12 +22,12 @@ @Configuration public class CoreExceptionConfiguration { @Bean - public Exceptions exceptions() { + public Exceptions scbExceptions() { return new Exceptions(); } @Bean - public DefaultExceptionProcessor defaultExceptionProcessor() { + public DefaultExceptionProcessor scbDefaultExceptionProcessor() { return new DefaultExceptionProcessor(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java b/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java index 6eb43d069cc..e8817af521d 100644 --- a/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java +++ b/core/src/main/java/org/apache/servicecomb/core/exception/DefaultExceptionProcessor.java @@ -32,6 +32,7 @@ import org.apache.servicecomb.config.inject.InjectProperties; import org.apache.servicecomb.config.inject.InjectProperty; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.tracing.TraceIdLogger; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.common.utils.ExceptionUtils; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; @@ -60,7 +61,7 @@ public class DefaultExceptionProcessor implements ExceptionProcessor { .map(converter -> (ExceptionConverter) converter) .collect(Collectors.toList()); - @InjectProperty(keys = "print-stack-trace", defaultValue = "false") + @InjectProperty(keys = "print-stack-trace", defaultValue = "true") protected boolean printStackTrace; @InjectProperty(keys = "print-rate-limit", defaultValue = "false") @@ -113,9 +114,17 @@ public InvocationException convert(Invocation invocation, Throwable throwable, S try { ExceptionConverter converter = converterCache.computeIfAbsent(unwrapped.getClass(), clazz -> findConverter(unwrapped)); - LOGGER.warn("convert operation {} exception using {}.", - invocation == null ? "NA" : invocation.getMicroserviceQualifiedName(), - converter.getClass().getSimpleName(), throwable); + if (invocation == null) { + LOGGER.warn("Convert unknown operation exception {}/{} using {}.", + throwable.getClass().getSimpleName(), unwrapped.getClass().getSimpleName(), + converter.getClass().getSimpleName()); + } else { + invocation.getTraceIdLogger().warn("{} Convert operation {} exception {}/{} using {}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), + invocation.getMicroserviceQualifiedName(), + throwable.getClass().getSimpleName(), unwrapped.getClass().getSimpleName(), + converter.getClass().getSimpleName()); + } return converter.convert(invocation, unwrapped, genericStatus); } catch (Exception e) { LOGGER.error("BUG: ExceptionConverter.convert MUST not throw exception, please fix it.\n" @@ -152,18 +161,18 @@ public void logConsumerException(Invocation invocation, InvocationException exce } if (isPrintStackTrace()) { - LOGGER.error("failed to invoke {}, endpoint={}, trace id={}.", + invocation.getTraceIdLogger().error("{} Failed to invoke {}, endpoint={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getMicroserviceQualifiedName(), invocation.getEndpoint(), - invocation.getTraceId(), exception); return; } - LOGGER.error("failed to invoke {}, endpoint={}, trace id={}, message={}.", + invocation.getTraceIdLogger().error("{} Failed to invoke {}, endpoint={}, message={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getMicroserviceQualifiedName(), invocation.getEndpoint(), - invocation.getTraceId(), ExceptionUtils.getExceptionMessageWithoutTrace(exception)); } @@ -182,8 +191,7 @@ public Response toProducerResponse(Invocation invocation, Throwable exception) { if (invocation != null) { logProducerException(invocation, invocationException); } - return Response.status(invocationException.getStatus()) - .entity(invocationException.getErrorData()); + return Response.createFail(invocationException); } @Override @@ -193,12 +201,14 @@ public void logProducerException(Invocation invocation, InvocationException exce } if (isPrintStackTrace()) { - LOGGER.error("failed to process {} invocation, operation={}.", + invocation.getTraceIdLogger().error("{} Failed to process {} invocation, operation={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getInvocationType(), invocation.getMicroserviceQualifiedName(), exception); return; } - LOGGER.error("failed to process {} invocation, operation={}, message={}.", + invocation.getTraceIdLogger().error("{} Failed to process {} invocation, operation={}, message={}.", + TraceIdLogger.constructSource(DefaultExceptionProcessor.class.getSimpleName()), invocation.getInvocationType(), invocation.getMicroserviceQualifiedName(), ExceptionUtils.getExceptionMessageWithoutTrace(exception)); } diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/IllegalArgumentExceptionToProducerResponseConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/IllegalArgumentExceptionToProducerResponseConverter.java deleted file mode 100644 index 532c38b630f..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/exception/IllegalArgumentExceptionToProducerResponseConverter.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.servicecomb.core.exception; - -import jakarta.ws.rs.core.Response.Status; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; - -public class IllegalArgumentExceptionToProducerResponseConverter implements - ExceptionToProducerResponseConverter { - @Override - public Class getExceptionClass() { - return IllegalArgumentException.class; - } - - @Override - public int getOrder() { - return 10000; - } - - @Override - public Response convert(SwaggerInvocation swaggerInvocation, IllegalArgumentException e) { - InvocationException invocationException = new InvocationException(Status.BAD_REQUEST.getStatusCode(), "", - new CommonExceptionData("Parameters not valid or types not match."), e); - return Response.failResp(invocationException); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/exception/ThrowableExceptionToProducerResponseConverter.java b/core/src/main/java/org/apache/servicecomb/core/exception/ThrowableExceptionToProducerResponseConverter.java deleted file mode 100644 index 501846426de..00000000000 --- a/core/src/main/java/org/apache/servicecomb/core/exception/ThrowableExceptionToProducerResponseConverter.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.servicecomb.core.exception; - -import jakarta.ws.rs.core.Response.Status; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; - -public class ThrowableExceptionToProducerResponseConverter implements ExceptionToProducerResponseConverter { - @Override - public Class getExceptionClass() { - return Throwable.class; - } - - @Override - public int getOrder() { - return 20000; - } - - @Override - public Response convert(SwaggerInvocation swaggerInvocation, Throwable e) { - InvocationException invocationException = new InvocationException(Status.INTERNAL_SERVER_ERROR.getStatusCode(), "", - new CommonExceptionData("Unexpected exception when processing the request."), e); - return Response.failResp(invocationException); - } -} diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java b/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java index 557ad73e16f..f03547c171a 100644 --- a/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/executor/ExecutorManager.java @@ -26,7 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; -public class ExecutorManager { +public final class ExecutorManager { public static final String KEY_EXECUTORS_PREFIX = "servicecomb.executors.Provider."; public static final String KEY_EXECUTORS_DEFAULT = "servicecomb.executors.default"; @@ -111,7 +111,7 @@ public Executor findExecutor(OperationMeta operationMeta, Executor defaultOperat return findExecutorById(EXECUTOR_DEFAULT); } - protected Executor findByKey(String configKey) { + private Executor findByKey(String configKey) { String id = environment.getProperty(configKey); if (id == null) { return null; diff --git a/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java b/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java index 24e41d407cd..13562914fac 100644 --- a/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java +++ b/core/src/main/java/org/apache/servicecomb/core/executor/GroupExecutor.java @@ -100,7 +100,7 @@ public GroupExecutor init(String groupName) { return this; } - public void initConfig() { + public synchronized void initConfig() { if (LOG_PRINTED.compareAndSet(false, true)) { LOGGER.info("thread pool rules:\n" + "1.use core threads.\n" diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/AbstractFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/AbstractFilter.java new file mode 100644 index 00000000000..17d555fc8d1 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/AbstractFilter.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter; + +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; + +public abstract class AbstractFilter implements Filter, EnvironmentAware { + private static final String ORDER_KEY = "servicecomb.filter.%s.%s.%s.order"; + + private static final String ENABLE_KEY = "servicecomb.filter.%s.%s.%s.enabled"; + + protected Environment environment; + + private String nameWithOrder; + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public int getOrder(String application, String serviceName) { + Integer custom = environment.getProperty(String.format(ORDER_KEY, getName(), application, serviceName), + Integer.class); + if (custom != null) { + return custom; + } + return getOrder(); + } + + @Override + public boolean enabledForMicroservice(String application, String serviceName) { + Boolean custom = environment.getProperty(String.format(ENABLE_KEY, getName(), application, serviceName), + Boolean.class); + if (custom != null) { + return custom; + } + return true; + } + + @Override + public String getNameWithOrder() { + if (nameWithOrder == null) { + nameWithOrder = String.format("F(%1$06d)-%2$s", getOrder(), getName()); + } + return nameWithOrder; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java index bd658be71cf..de13aa0ac2e 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/ConsumerFilter.java @@ -16,11 +16,6 @@ */ package org.apache.servicecomb.core.filter; -import org.apache.servicecomb.swagger.invocation.InvocationType; - public interface ConsumerFilter extends Filter { - @Override - default boolean enabledForInvocationType(InvocationType invocationType) { - return invocationType == InvocationType.CONSUMER; - } + } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java index 23bc14eac53..0d9ce402624 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/CoreFilterConfiguration.java @@ -16,31 +16,45 @@ */ package org.apache.servicecomb.core.filter; +import org.apache.servicecomb.core.filter.impl.ContextMapperFilter; import org.apache.servicecomb.core.filter.impl.ParameterValidatorFilter; import org.apache.servicecomb.core.filter.impl.ProviderOperationFilter; +import org.apache.servicecomb.core.filter.impl.RetryFilter; import org.apache.servicecomb.core.filter.impl.ScheduleFilter; +import org.apache.servicecomb.governance.handler.MapperHandler; +import org.apache.servicecomb.governance.handler.RetryHandler; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CoreFilterConfiguration { @Bean - public ProviderFilter producerOperationFilter() { + public ProviderOperationFilter scbProducerOperationFilter() { return new ProviderOperationFilter(); } @Bean - public ProviderFilter scheduleFilter() { + public ScheduleFilter scbScheduleFilter() { return new ScheduleFilter(); } @Bean - public FilterChainsManager filterChainsManager() { + public RetryFilter scbRetryFilter(RetryHandler retryHandler) { + return new RetryFilter(retryHandler); + } + + @Bean + public ContextMapperFilter scbContextMapperFilter(MapperHandler mapperHandler) { + return new ContextMapperFilter(mapperHandler); + } + + @Bean + public FilterChainsManager scbFilterChainsManager() { return new FilterChainsManager(); } @Bean - public ProviderFilter parameterValidatorFilter() { + public ParameterValidatorFilter scbParameterValidatorFilter() { return new ParameterValidatorFilter(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/EdgeFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/EdgeFilter.java new file mode 100644 index 00000000000..70d8bef4ef5 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/EdgeFilter.java @@ -0,0 +1,21 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter; + +public interface EdgeFilter extends Filter { + +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java b/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java index 48e4e8ffc32..99ed798e686 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/Filter.java @@ -19,15 +19,15 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; +import org.springframework.core.Ordered; /** *
  *  Filters are the basics of how an invocation is executed.
  *
  * thread rule:
- *   assume a producer filter chains is: f1, f2, schedule, f3, f4
+ *   assume a provider filter chains is: f1, f2, schedule, f3, f4
  *
  *   schedule is a builtIn filter, which will dispatch invocations to operation related threadPool
  *
@@ -47,15 +47,11 @@
  *        (reactive golden rule)
  * 
*/ -public interface Filter { +public interface Filter extends Ordered { int PROVIDER_SCHEDULE_FILTER_ORDER = 0; int CONSUMER_LOAD_BALANCE_ORDER = 0; - default boolean enabledForInvocationType(InvocationType invocationType) { - return true; - } - default boolean enabledForTransport(String transport) { return true; } @@ -64,7 +60,11 @@ default boolean enabledForMicroservice(String application, String serviceName) { return true; } - default int getOrder(InvocationType invocationType, String application, String serviceName) { + default int getOrder(String application, String serviceName) { + return 0; + } + + default int getOrder() { return 0; } @@ -72,6 +72,8 @@ default String getName() { throw new IllegalStateException("must provide unique filter name."); } + String getNameWithOrder(); + /** * * @param invocation invocation diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java b/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java index 4f7392f861a..2cc6281d018 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterChainsManager.java @@ -22,26 +22,30 @@ import java.util.List; import java.util.stream.Collectors; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.springframework.beans.factory.annotation.Autowired; public class FilterChainsManager { - private final InvocationFilterChains consumerChains = new InvocationFilterChains(InvocationType.CONSUMER); + private InvocationFilterChains consumerChains; - private final InvocationFilterChains producerChains = new InvocationFilterChains(InvocationType.PROVIDER); + private InvocationFilterChains providerChains; + + private InvocationFilterChains edgeChains; @Autowired - public FilterChainsManager addFilters(List filters) { - for (Filter filter : filters) { - if (filter.enabledForInvocationType(InvocationType.CONSUMER)) { - consumerChains.addFilter(filter); - } + public FilterChainsManager setEdgeFilters(List filters) { + edgeChains = new InvocationFilterChains(filters); + return this; + } - if (filter.enabledForInvocationType(InvocationType.PROVIDER)) { - producerChains.addFilter(filter); - } - } + @Autowired + public FilterChainsManager setConsumerFilters(List filters) { + consumerChains = new InvocationFilterChains(filters); + return this; + } + @Autowired + public FilterChainsManager setProviderFilters(List filters) { + providerChains = new InvocationFilterChains(filters); return this; } @@ -54,24 +58,31 @@ public FilterNode findConsumerChain(String application, String serviceName) { } public FilterNode findProducerChain(String application, String serviceName) { - return producerChains.findChain(application, serviceName); + return providerChains.findChain(application, serviceName); + } + + public FilterNode findEdgeChain(String application, String serviceName) { + return edgeChains.findChain(application, serviceName); } public String collectResolvedChains() { StringBuilder sb = new StringBuilder(); appendLine(sb, "consumer: "); - appendLine(sb, " filters: %s", collectFilterNames(consumerChains, InvocationType.CONSUMER)); + appendLine(sb, " filters: %s", collectFilterNames(consumerChains)); appendLine(sb, "producer: "); - appendLine(sb, " filters: %s", collectFilterNames(producerChains, InvocationType.PROVIDER)); + appendLine(sb, " filters: %s", collectFilterNames(providerChains)); + + appendLine(sb, "edge: "); + appendLine(sb, " filters: %s", collectFilterNames(edgeChains)); return deleteLast(sb, 1).toString(); } - private List collectFilterNames(InvocationFilterChains chains, InvocationType invocationType) { + private List collectFilterNames(InvocationFilterChains chains) { return chains.getFilters().stream() - .map(filter -> filter.getName() + "(" + filter.getOrder(invocationType, null, null) + ")") + .map(filter -> filter.getName() + "(" + filter.getOrder() + ")") .collect(Collectors.toList()); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java b/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java index b5cb63c0195..44a5b34d901 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/FilterNode.java @@ -62,19 +62,14 @@ private void setNextNode(FilterNode nextNode) { } public CompletableFuture onFilter(Invocation invocation) { - if (!filter.enabledForTransport(invocation.getTransportName())) { + // When transport name is empty, maybe edge transport filters need to be executed. + // Can't set Endpoint before load balance in edge. + if (invocation.getTransportName() != null && !filter.enabledForTransport(invocation.getTransportName())) { return nextNode.onFilter(invocation); } - return AsyncUtils.tryCatchSupplierFuture(() -> filter.onFilter(invocation, nextNode)) - .thenApply(this::rethrowExceptionInResponse); - } - - private Response rethrowExceptionInResponse(Response response) { - if (response.isFailed() && response.getResult() instanceof Throwable) { - throw AsyncUtils.rethrow(response.getResult()); - } - - return response; + String stage = invocation.getInvocationStageTrace().recordStageBegin(this.filter.getNameWithOrder()); + return AsyncUtils.tryCatchSupplierFuture(() -> filter.onFilter(invocation, nextNode) + .whenComplete((r, e) -> invocation.getInvocationStageTrace().recordStageEnd(stage))); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java b/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java index 064e4471b8a..6dde66c3354 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/InvocationFilterChains.java @@ -14,47 +14,38 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.servicecomb.core.filter; -import java.util.Collection; import java.util.Comparator; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; public class InvocationFilterChains { - private final Map filters = new HashMap<>(); - - private final Map microserviceChains = new HashMap<>(); - private final InvocationType invocationType; + private final List filters; - public InvocationFilterChains(InvocationType invocationType) { - this.invocationType = invocationType; - } + private final Map> microserviceChains = new ConcurrentHashMapEx<>(); - public Collection getFilters() { - return filters.values(); + public InvocationFilterChains(List filters) { + this.filters = filters; } - public void addFilter(Filter filter) { - filters.put(filter.getName(), filter); + public List getFilters() { + return filters; } public FilterNode findChain(String application, String serviceName) { - FilterNode filterNode = microserviceChains.get(serviceName); - if (filterNode == null) { - List serviceFilters = filters.entrySet().stream() - .filter(e -> e.getValue().enabledForMicroservice(application, serviceName)) - .map(e -> e.getValue()) - .collect(Collectors.toList()); - serviceFilters.sort(Comparator.comparingInt(a -> a.getOrder(invocationType, application, serviceName))); - filterNode = FilterNode.buildChain(serviceFilters); - microserviceChains.put(serviceName, filterNode); - } - return filterNode; + return microserviceChains.computeIfAbsent(application, key -> new ConcurrentHashMapEx<>()) + .computeIfAbsent(serviceName, (serviceNameInner) -> { + List serviceFilters = filters.stream() + .filter(e -> e.enabledForMicroservice(application, serviceName)) + .sorted(Comparator.comparingInt(a -> a.getOrder(application, serviceName))) + .collect(Collectors.toList()); + return FilterNode.buildChain(serviceFilters); + }); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java index 7e1dbc318cb..6d5edd7814e 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/ProviderFilter.java @@ -16,11 +16,6 @@ */ package org.apache.servicecomb.core.filter; -import org.apache.servicecomb.swagger.invocation.InvocationType; - public interface ProviderFilter extends Filter { - @Override - default boolean enabledForInvocationType(InvocationType invocationType) { - return invocationType == InvocationType.PROVIDER; - } + } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ContextMapperFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ContextMapperFilter.java new file mode 100644 index 00000000000..a14e41dd660 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ContextMapperFilter.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter.impl; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; +import org.apache.servicecomb.core.governance.MatchType; +import org.apache.servicecomb.governance.handler.MapperHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.processor.mapping.Mapper; +import org.apache.servicecomb.swagger.invocation.Response; +import org.springframework.util.CollectionUtils; + +public class ContextMapperFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { + private final MapperHandler mapperHandler; + + public ContextMapperFilter(MapperHandler mapperHandler) { + this.mapperHandler = mapperHandler; + } + + @Override + public boolean enabledForTransport(String transport) { + return CoreConst.RESTFUL.equals(transport); + } + + @Override + public int getOrder() { + return -1995; + } + + @Override + public String getName() { + return "context-mapper"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); + Mapper mapper = mapperHandler.getActuator(request); + if (mapper == null || CollectionUtils.isEmpty(mapper.target())) { + return nextNode.onFilter(invocation); + } + Map properties = mapper.target(); + properties.forEach((k, v) -> { + if (StringUtils.isEmpty(v)) { + return; + } + if ("$U".equals(v)) { + invocation.addContext(k, request.apiPath()); + } else if ("$M".equals(v)) { + invocation.addContext(k, request.method()); + } else if (v.startsWith("$H{") && v.endsWith("}")) { + invocation.addContext(k, request.header(v.substring(3, v.length() - 1))); + } else if (v.startsWith("$Q{") && v.endsWith("}")) { + invocation.addContext(k, request.query(v.substring(3, v.length() - 1))); + } else { + invocation.addContext(k, v); + } + }); + return nextNode.onFilter(invocation); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java index f611616b4e3..d2b8eb82319 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/EmptyFilter.java @@ -19,12 +19,15 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.swagger.invocation.Response; // just for test -public class EmptyFilter implements Filter { +public class EmptyFilter extends AbstractFilter implements ProviderFilter, ConsumerFilter, EdgeFilter { @Override public String getName() { return "empty"; @@ -34,4 +37,9 @@ public String getName() { public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { return CompletableFuture.completedFuture(Response.ok(null)); } + + @Override + public int getOrder() { + return 0; + } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java index bce1b014de9..b4bf065792f 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilter.java @@ -17,16 +17,18 @@ package org.apache.servicecomb.core.filter.impl; import java.lang.reflect.Method; +import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; +import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.hibernate.validator.HibernateValidator; import org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator; @@ -35,39 +37,56 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; +import jakarta.validation.Configuration; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Valid; import jakarta.validation.Validation; import jakarta.validation.ValidatorFactory; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotNull; import jakarta.validation.executable.ExecutableValidator; import jakarta.validation.groups.Default; -public class ParameterValidatorFilter implements ProviderFilter, InitializingBean { +public class ParameterValidatorFilter extends AbstractFilter implements ProviderFilter, InitializingBean { + private static class Service { + @SuppressWarnings("unused") + public void service(@Valid Model model) { + + } + } + + private static class Model { + @NotNull + String name; + + @Min(10) + int age; + + Model(String name, int age) { + this.name = name; + this.age = age; + } + } + private static final Logger LOGGER = LoggerFactory.getLogger(ParameterValidatorFilter.class); public static final String NAME = "validator"; + public static final String HIBERNATE_VALIDATE_PREFIX = "hibernate.validator"; + public static final String ENABLE_EL = "servicecomb.filters.validation.useResourceBundleMessageInterpolator"; protected ExecutableValidator validator; - private Environment environment; - - @Autowired - public void setEnvironment(Environment environment) { - this.environment = environment; - } - @Override public String getName() { return NAME; } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 1000; } @@ -75,14 +94,35 @@ public int getOrder(InvocationType invocationType, String application, String se public void afterPropertiesSet() { validator = createValidatorFactory() .getValidator().forExecutables(); + initValidate(); + } + + private void initValidate() { + // This method is intended to make first rpc call faster + // Because validation cache bean class, this way only make first rpc call a little faster. + try { + Model model = new Model("foo", 23); + Service instance = new Service(); + Method method = Service.class.getMethod("service", Model.class); + Object[] args = new Object[] {model}; + validator.validateParameters(instance, method, args, Default.class); + } catch (Throwable e) { + throw new IllegalStateException(e); + } } protected ValidatorFactory createValidatorFactory() { - return Validation.byProvider(HibernateValidator.class) + Configuration validatorConfiguration = Validation.byProvider(HibernateValidator.class) .configure() .propertyNodeNameProvider(new JacksonPropertyNodeNameProvider()) - .messageInterpolator(messageInterpolator()) - .buildValidatorFactory(); + .messageInterpolator(messageInterpolator()); + Map properties = ConfigUtil.stringPropertiesWithPrefix(environment, HIBERNATE_VALIDATE_PREFIX); + if (!properties.isEmpty()) { + for (Map.Entry entry : properties.entrySet()) { + validatorConfiguration.addProperty(entry.getKey(), entry.getValue()); + } + } + return validatorConfiguration.buildValidatorFactory(); } protected AbstractMessageInterpolator messageInterpolator() { @@ -109,9 +149,7 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne protected Set> doValidate(Invocation invocation) { SwaggerProducerOperation producerOperation = invocation.getOperationMeta().getSwaggerProducerOperation(); - Object instance = producerOperation.getProducerInstance(); - Method method = producerOperation.getProducerMethod(); - Object[] args = invocation.toProducerArguments(); - return validator.validateParameters(instance, method, args, Default.class); + return validator.validateParameters(producerOperation.getProducerInstance(), producerOperation.getProducerMethod(), + invocation.toProducerArguments(), Default.class); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java index bfd06f80eea..c8f757bf3e2 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ProviderOperationFilter.java @@ -21,16 +21,20 @@ import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -public class ProviderOperationFilter implements ProviderFilter { +import jakarta.ws.rs.core.Response.Status; + +public class ProviderOperationFilter extends AbstractFilter implements ProviderFilter { public static final String NAME = "producer-operation"; @Override @@ -39,13 +43,17 @@ public String getName() { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { // almost time, should be the last filter. return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 2000; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + if (!transportAccessAllowed(invocation)) { + return CompletableFuture.failedFuture(new InvocationException(Status.UNAUTHORIZED, + new CommonExceptionData("transport access not allowed."))); + } invocation.onBusinessMethodStart(); SwaggerProducerOperation producerOperation = invocation.getOperationMeta().getSwaggerProducerOperation(); @@ -57,6 +65,13 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne .whenComplete((response, throwable) -> processMetrics(invocation)); } + private boolean transportAccessAllowed(Invocation invocation) { + if (invocation.getProviderTransportName() == null) { + return true; + } + return invocation.getProviderTransportName().equals(invocation.getTransportName()); + } + @SuppressWarnings("unchecked") protected CompletableFuture invoke(Invocation invocation, Object instance, Method method, Object[] args) { ContextUtils.setInvocationContext(invocation); @@ -81,7 +96,6 @@ protected Response convertResultToResponse(Invocation invocation, SwaggerProduce } protected void processMetrics(Invocation invocation) { - invocation.onBusinessMethodFinish(); invocation.onBusinessFinish(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/RetryFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/RetryFilter.java new file mode 100644 index 00000000000..e423b8f7ebb --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/RetryFilter.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.core.filter.impl; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.core.governance.GovernanceConfiguration; +import org.apache.servicecomb.core.governance.MatchType; +import org.apache.servicecomb.core.governance.RetryContext; +import org.apache.servicecomb.governance.handler.RetryHandler; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.swagger.invocation.Response; +import org.springframework.beans.factory.annotation.Autowired; + +import io.github.resilience4j.decorators.Decorators; +import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; +import io.github.resilience4j.retry.Retry; + +public class RetryFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private static final Object LOCK = new Object(); + + private static volatile ScheduledExecutorService reactiveRetryPool; + + private static ScheduledExecutorService getOrCreateRetryPool() { + if (reactiveRetryPool == null) { + synchronized (LOCK) { + if (reactiveRetryPool == null) { + reactiveRetryPool = Executors.newScheduledThreadPool(2, new ThreadFactory() { + private final AtomicInteger count = new AtomicInteger(0); + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "reactive-retry-pool-thread-" + count.getAndIncrement()); + // avoid block shutdown + thread.setDaemon(true); + return thread; + } + }); + } + } + } + return reactiveRetryPool; + } + + private final RetryHandler retryHandler; + + @Autowired + public RetryFilter(RetryHandler retryHandler) { + this.retryHandler = retryHandler; + } + + @Override + public String getName() { + return "retry"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); + Retry retry = retryHandler.getActuator(request); + if (retry == null) { + return nextNode.onFilter(invocation); + } + + Supplier> next = createBusinessCompletionStageSupplier(invocation, nextNode); + DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); + dcs.withRetry(retry, getOrCreateRetryPool()); + CompletableFuture future = new CompletableFuture<>(); + dcs.get().whenComplete((r, e) -> { + if (e == null) { + future.complete(r); + return; + } + + future.completeExceptionally(e); + }); + + return future; + } + + private Supplier> createBusinessCompletionStageSupplier(Invocation invocation, + FilterNode nextNode) { + return () -> { + updateRetryStatus(invocation); + return nextNode.onFilter(invocation); + }; + } + + private static void updateRetryStatus(Invocation invocation) { + if (invocation.getLocalContext(RetryContext.RETRY_CONTEXT) != null) { + if (invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE) != null + && (boolean) invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE)) { + // clear last server to avoid using user defined endpoint + invocation.setEndpoint(null); + } + RetryContext retryContext = invocation.getLocalContext(RetryContext.RETRY_CONTEXT); + retryContext.incrementRetry(); + return; + } + + invocation.addLocalContext(RetryContext.RETRY_CONTEXT, + new RetryContext(GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()))); + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1990; + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ScheduleFilter.java b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ScheduleFilter.java index eaec70975d1..341c01fa877 100644 --- a/core/src/main/java/org/apache/servicecomb/core/filter/impl/ScheduleFilter.java +++ b/core/src/main/java/org/apache/servicecomb/core/filter/impl/ScheduleFilter.java @@ -20,15 +20,18 @@ import java.util.concurrent.Executor; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; -import org.apache.servicecomb.core.invocation.InvocationStageTrace; -import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.core.tracing.TraceIdLogger; import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.MDC; -public class ScheduleFilter implements ProviderFilter { +import jakarta.ws.rs.core.Response.Status; + +public class ScheduleFilter extends AbstractFilter implements ProviderFilter { public static final String NAME = "schedule"; @Override @@ -37,46 +40,31 @@ public String getName() { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode next) { - invocation.getInvocationStageTrace().startSchedule(); + invocation.getInvocationStageTrace().startProviderQueue(); Executor executor = invocation.getOperationMeta().getExecutor(); return CompletableFuture.completedFuture(null) .thenComposeAsync(response -> runInExecutor(invocation, next), executor); } protected CompletableFuture runInExecutor(Invocation invocation, FilterNode next) { - invocation.onExecuteStart(); - - try { - InvocationStageTrace trace = invocation.getInvocationStageTrace(); - trace.startServerFiltersRequest(); - invocation.onStartHandlersRequest(); - - checkInQueueTimeout(invocation); - - return next.onFilter(invocation) - .whenComplete((response, throwable) -> whenComplete(invocation)); - } finally { - invocation.onExecuteFinish(); - } + invocation.getInvocationStageTrace().finishProviderQueue(); + MDC.put(TraceIdLogger.KEY_TRACE_ID, invocation.getTraceId()); + checkInQueueTimeout(invocation); + return next.onFilter(invocation); } private void checkInQueueTimeout(Invocation invocation) { long nanoTimeout = invocation.getOperationMeta().getConfig() .getNanoRequestWaitInPoolTimeout(invocation.getTransport().getName()); - if (System.nanoTime() - invocation.getInvocationStageTrace().getStart() > nanoTimeout) { - throw Exceptions.genericProducer("Request in the queue timed out."); + if (invocation.getInvocationStageTrace().calcQueue() > nanoTimeout) { + throw new InvocationException(Status.REQUEST_TIMEOUT, "Request in the queue timed out."); } } - - private void whenComplete(Invocation invocation) { - invocation.getInvocationStageTrace().finishHandlersResponse(); - invocation.getInvocationStageTrace().finishServerFiltersResponse(); - } } diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java b/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java index 5b7679cdbf9..15d58d66610 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/MatchType.java @@ -20,6 +20,7 @@ import java.util.Map; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.swagger.SwaggerUtils; @@ -34,8 +35,8 @@ private GovernanceRequestExtractorImpl(Invocation invocation) { @Override public String apiPath() { if (MatchType.REST.equalsIgnoreCase(invocation.getOperationMeta().getConfig().getGovernanceMatchType())) { - if (invocation.isConsumer()) { - return concatAbsolutePath(SwaggerUtils.getBasePath(invocation.getSchemaMeta().getSwagger()), + if (!invocation.isProducer()) { + return SwaggerUtils.concatAbsolutePath(invocation.getSchemaMeta().getSwagger(), invocation.getOperationMeta().getOperationPath()); } // not highway @@ -44,7 +45,7 @@ public String apiPath() { } } - if (invocation.isConsumer()) { + if (!invocation.isProducer()) { return invocation.getOperationMeta().getMicroserviceQualifiedName(); } return invocation.getOperationMeta().getSchemaQualifiedName(); @@ -73,9 +74,18 @@ public String header(String key) { return null; } + @Override + public String query(String key) { + HttpServletRequestEx requestEx = invocation.getRequestEx(); + if (requestEx == null) { + return null; + } + return requestEx.getParameter(key); + } + @Override public String instanceId() { - if (invocation.isConsumer()) { + if (!invocation.isProducer()) { if (invocation.getEndpoint() != null && invocation.getEndpoint().getMicroserviceInstance() != null) { return invocation.getEndpoint().getMicroserviceInstance().getInstanceId(); } @@ -85,7 +95,7 @@ public String instanceId() { @Override public String serviceName() { - if (invocation.isConsumer()) { + if (!invocation.isProducer()) { return invocation.getMicroserviceName(); } return null; @@ -104,19 +114,4 @@ public Object sourceRequest() { public static GovernanceRequestExtractor createGovHttpRequest(Invocation invocation) { return new GovernanceRequestExtractorImpl(invocation); } - - /** - * Concat the two paths to an absolute path, without end of '/'. - * - * e.g. "/" + "/ope" = /ope - * e.g. "/prefix" + "/ope" = /prefix/ope - */ - private static String concatAbsolutePath(String basePath, String operationPath) { - return ("/" + nonNullify(basePath) + "/" + nonNullify(operationPath)) - .replaceAll("/{2,}", "/"); - } - - private static String nonNullify(String path) { - return path == null ? "" : path; - } } diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java b/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java index 058982a425a..47792792e14 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/RetryContext.java @@ -19,6 +19,7 @@ public class RetryContext { public static final String RETRY_CONTEXT = "x-context-retry"; + // weather need reset Endpoint in retry public static final String RETRY_LOAD_BALANCE = "x-context-retry-loadbalance"; private boolean retry; diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java index cb85ecfe1a9..9177ca71820 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombCircuitBreakerExtension.java @@ -17,11 +17,10 @@ package org.apache.servicecomb.core.governance; -import jakarta.ws.rs.core.Response.Status; +import java.util.List; import org.apache.servicecomb.governance.handler.ext.AbstractCircuitBreakerExtension; import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; public class ServiceCombCircuitBreakerExtension extends AbstractCircuitBreakerExtension { @@ -41,15 +40,11 @@ protected String extractStatusCode(Object result) { } @Override - public boolean isFailedResult(Throwable e) { + public boolean isFailedResult(List statusList, Throwable e) { if (e instanceof InvocationException) { InvocationException invocationException = (InvocationException) e; - if (invocationException.getStatusCode() == Status.SERVICE_UNAVAILABLE.getStatusCode() || - invocationException.getStatusCode() == Status.BAD_GATEWAY.getStatusCode() || - invocationException.getStatusCode() == ExceptionFactory.PRODUCER_INNER_STATUS_CODE) { - return true; - } + return statusList.contains(String.valueOf(invocationException.getStatusCode())); } - return super.isFailedResult(e); + return super.isFailedResult(statusList, e); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java index f3b9075eaef..2bbf6d3c87d 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombInstanceIsolationExtension.java @@ -17,10 +17,12 @@ package org.apache.servicecomb.core.governance; +import java.util.List; + import org.apache.servicecomb.governance.handler.ext.AbstractInstanceIsolationExtension; public class ServiceCombInstanceIsolationExtension extends AbstractInstanceIsolationExtension { - private ServiceCombRetryExtension retryExtension = new ServiceCombRetryExtension(); + private final ServiceCombRetryExtension retryExtension = new ServiceCombRetryExtension(); @Override protected String extractStatusCode(Object result) { @@ -28,7 +30,7 @@ protected String extractStatusCode(Object result) { } @Override - public boolean isFailedResult(Throwable e) { - return retryExtension.isFailedResult(e); + public boolean isFailedResult(List statusList, Throwable e) { + return retryExtension.isFailedResult(statusList, e); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java index ccd7f6f8465..871a03bfa30 100644 --- a/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java +++ b/core/src/main/java/org/apache/servicecomb/core/governance/ServiceCombRetryExtension.java @@ -17,11 +17,10 @@ package org.apache.servicecomb.core.governance; -import jakarta.ws.rs.core.Response.Status; +import java.util.List; import org.apache.servicecomb.governance.handler.ext.AbstractRetryExtension; import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; public class ServiceCombRetryExtension extends AbstractRetryExtension { @@ -41,15 +40,11 @@ protected String extractStatusCode(Object result) { } @Override - public boolean isFailedResult(Throwable e) { + public boolean isFailedResult(List statusList, Throwable e) { if (e instanceof InvocationException) { InvocationException invocationException = (InvocationException) e; - if (invocationException.getStatusCode() == Status.SERVICE_UNAVAILABLE.getStatusCode() || - invocationException.getStatusCode() == Status.BAD_GATEWAY.getStatusCode() || - invocationException.getStatusCode() == ExceptionFactory.CONSUMER_INNER_STATUS_CODE) { - return true; - } + return statusList.contains(String.valueOf(invocationException.getStatusCode())); } - return super.isFailedResult(e); + return super.isFailedResult(statusList, e); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/CoreInvocationConfiguration.java b/core/src/main/java/org/apache/servicecomb/core/invocation/CoreInvocationConfiguration.java index 8fadf19d2b0..abfe2f05330 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/CoreInvocationConfiguration.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/CoreInvocationConfiguration.java @@ -29,19 +29,19 @@ @Configuration public class CoreInvocationConfiguration { @Bean - public InvocationTimeoutBootListener invocationTimeoutBootListener(EventBus eventBus, + public InvocationTimeoutBootListener scbInvocationTimeoutBootListener(EventBus eventBus, List strategies, Environment environment) { return new InvocationTimeoutBootListener(eventBus, strategies, environment); } @Bean - public PassingTimeStrategy passingTimeStrategy() { + public PassingTimeStrategy scbPassingTimeStrategy() { return new PassingTimeStrategy(); } @Bean - public ProcessingTimeStrategy processingTimeStrategy() { + public ProcessingTimeStrategy scbProcessingTimeStrategy() { return new ProcessingTimeStrategy(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java index a413fce1016..323ad924a09 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationCreator.java @@ -20,9 +20,6 @@ import org.apache.servicecomb.core.Invocation; -/** - * better to named InvocationFactory, but already be used by old version - */ public interface InvocationCreator { CompletableFuture createAsync(); } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationStageTrace.java b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationStageTrace.java index 3f06bb8f01a..f9d0166a458 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationStageTrace.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationStageTrace.java @@ -16,362 +16,336 @@ */ package org.apache.servicecomb.core.invocation; +import java.util.HashMap; +import java.util.Map; + import org.apache.servicecomb.core.Invocation; /** *
- * important:
- *   all time point is about invocation stage, not java method
- *   all time is nanoTime
- *   not all stage relate to a event, currently, we only have 4 event:
- *     start/finish/startBusiness/finishBusiness
- *
  * for consumer:
- *         (prepare)                      (handlerReq)                                       (clientFilterReq)
- *   start --------> startHandlersRequest -----------> startClientFiltersRequest --------------------------------------
- *                                                        <----------------(sendRequest)----------------->            |
- *                        (receiveResponse)              (writeToBuffer)                   (getConnection)            |
- *    ---finishReceiveResponse <----- finishWriteToBuffer <----------- finishGetConnection <-------------- startSend <-
- *   | (wakeConsumer)            (clientFiltersResponse)               (handlersResponse)
- *   |-----> startClientFiltersResponse -------> finishClientFiltersResponse -------> finishHandlersResponse --> finish
  *
- * for producer:
- *       (prepare)       (threadPoolQueue)                                (serverFiltersRequest)
- *   start ----> startSchedule -----> startExecution -> startServerFiltersRequest -------> startHandlersRequest -------
- *                          (handlersResponse)          <-------------(business)------------->      (handlersRequest) |
- *   -----finishHandlersResponse <------ finishBusiness <------- finishBusinessMethod <------ startBusinessMethod------
- *   | (serverFiltersResponse)     (sendResponse)
- *   |---> finishServerFiltersResponse ------> finish
+ *  1. total: start create invocation -> all filters finished
+ *  2. prepare: start create invocation -> finish create invocation
+ *  3. filters-(filter-name): start call on filter -> on complete
+ *  4. connection: start get connection -> finish get connection
+ *  5. consumer-encode: start encode request -> finish encode request
+ *  6. consumer-decode: start decode response -> finish decode response
+ *  7. consumer-send: start send request -> finish send request
+ *  8. wait: finish send request -> start decode response
+ *
+ * for provider:
+ *  1. total: start create invocation -> all filters finished
+ *  2. prepare: start create invocation -> finish create invocation
+ *  3. filters-(filter-name): start call on filter -> on complete
+ *  4. queue: add in queue -> execute in thread
+ *  5. provider-decode: start decode request -> finish decode request
+ *  6. provider-encode: start encode response -> finish encode response
+ *  7. provider-send: start send response -> finish send response
+ *  8. execute: start business execute -> finish business execute
  *
  * for edge:
- *      (prepare)         (threadPoolQueue)                               (serverFiltersRequest)
- *   start ----> startSchedule -----> startExecution -> startServerFiltersRequest ----> startHandlersRequest ----------
- *                           <----------(sendRequest)---------->                                                      |
- *                       (writeToBuffer)              (getConnection)  (clientFilterReq)            (handlersRequest) |
- *   --- finishWriteToBuffer <------ finishGetConnection <------ startSend <------ startClientFiltersRequest <---------
- *   | (receiveResponse)     (wakeConsumer)                 (clientFiltersResponse)
- *   ---> finishReceiveResponse ------> startClientFiltersResponse ------> finishClientFiltersResponse ----------------
- *                                   (sendResponse)                  (serverFiltersResponse)       (handlersResponse) |
- *                              finish <------ finishServerFiltersResponse <------ finishHandlersResponse <------------
+ *
+ *  *  1. total: start create invocation -> all filters finished
+ *  *  2. prepare: start create invocation -> finish create invocation
+ *  *  3. filters-(filter-name): start call on filter -> on complete
+ *  *  4. connection: start get connection -> finish get connection
+ *  *  5. provider-decode: start decode request -> finish decode request
+ *  *  6. provider-encode: start encode response -> finish encode response
+ *  *  7. consumer-encode: start encode request -> finish encode request
+ *  *  8. consumer-decode: start decode response -> finish decode response
+ *  *  9. consumer-send: start send request -> finish send request
+ *  *  10. provider-send: start send response -> finish send response
+ *  *  11. wait: finish send request -> start decode response
  *
  * 
*/ public class InvocationStageTrace { - public static final String PREPARE = "prepare"; + public static class Stage { + private long beginTime; + + private long endTime; + + public long getBeginTime() { + return beginTime; + } - public static final String HANDLERS_REQUEST = "handlers request"; + public long getEndTime() { + return endTime; + } + } - public static final String HANDLERS_RESPONSE = "handlers response"; + public static final String STAGE_TOTAL = "total"; - public static final String CLIENT_FILTERS_REQUEST = "client filters request"; + public static final String STAGE_PREPARE = "prepare"; - public static final String CONSUMER_SEND_REQUEST = "send request"; + public static final String STAGE_PROVIDER_QUEUE = "queue"; - public static final String CONSUMER_GET_CONNECTION = "get connection"; + public static final String STAGE_PROVIDER_DECODE_REQUEST = "provider-decode"; - public static final String CONSUMER_WRITE_TO_BUF = "write to buf"; + public static final String STAGE_PROVIDER_ENCODE_RESPONSE = "provider-encode"; - public static final String CONSUMER_WAIT_RESPONSE = "wait response"; + public static final String STAGE_PROVIDER_SEND = "provider-send"; - public static final String CONSUMER_WAKE_CONSUMER = "wake consumer"; + public static final String STAGE_PROVIDER_BUSINESS = "execute"; - public static final String CLIENT_FILTERS_RESPONSE = "client filters response"; + public static final String STAGE_CONSUMER_CONNECTION = "connection"; - public static final String THREAD_POOL_QUEUE = "threadPoolQueue"; + public static final String STAGE_CONSUMER_ENCODE_REQUEST = "consumer-encode"; - public static final String SERVER_FILTERS_REQUEST = "server filters request"; + public static final String STAGE_CONSUMER_DECODE_RESPONSE = "consumer-decode"; - public static final String SERVER_FILTERS_RESPONSE = "server filters response"; + public static final String STAGE_CONSUMER_SEND = "consumer-send"; - public static final String PRODUCER_SEND_RESPONSE = "send response"; + public static final String STAGE_CONSUMER_WAIT = "wait"; private final Invocation invocation; - // current time for start invocation - private long startTimeMillis; + // invocation start time in millis, for passing strategy use only + private long startInMillis; + // invocation start time in nanos, for passing strategy use only private long start; - private long startHandlersRequest; + private long finish; - private long startClientFiltersRequest; + private long startCreateInvocation; - // only for consumer - private long startSend; + private long finishCreateInvocation; - // only for consumer - private long startGetConnection; + private long startProviderQueue; - // only for consumer - private long finishGetConnection; + private long finishProviderQueue; - // only for consumer - private long finishWriteToBuffer; + private long startConsumerConnection; - // only for consumer - private long finishReceiveResponse; + private long finishConsumerConnection; - private long startClientFiltersResponse; + private long startProviderDecodeRequest; - private long finishClientFiltersResponse; + private long finishProviderDecodeRequest; - private long finishHandlersResponse; + private long startProviderEncodeResponse; - private long finish; + private long finishProviderEncodeResponse; - // only for producer: put producer task to thread pool - private long startSchedule; + private long startConsumerEncodeRequest; - private long startServerFiltersRequest; + private long finishConsumerEncodeRequest; - private long finishServerFiltersResponse; + private long startConsumerDecodeResponse; - // only for producer: start execute in work thread - // for reactive mode, work thread is eventloop - private long startExecution; + private long finishConsumerDecodeResponse; - // only for producer - private long startBusinessMethod; + private long startProviderSendResponse; - // only for producer - private long finishBusiness; + private long finishProviderSendResponse; - public InvocationStageTrace(Invocation invocation) { - this.invocation = invocation; - } + private long startConsumerSendRequest; - public void start(long start) { - // remember the current time to start invocation - this.startTimeMillis = System.currentTimeMillis(); - this.start = start; - } + private long finishConsumerSendRequest; - public long getStart() { - return start; - } + private long startBusinessExecute; - public long getStartTimeMillis() { - return startTimeMillis; - } + private long finishBusinessExecute; - public InvocationStageTrace setStartTimeMillis(long startTimeMillis) { - this.startTimeMillis = startTimeMillis; - return this; - } + private long startWaitResponse; - public long getStartHandlersRequest() { - return startHandlersRequest; - } + private long finishWaitResponse; - public void startHandlersRequest() { - this.startHandlersRequest = System.nanoTime(); - } - - public long getStartClientFiltersRequest() { - return startClientFiltersRequest; - } + // invocation stage can not be used in concurrent access + private final Map stages = new HashMap<>(); - public void startClientFiltersRequest() { - this.startClientFiltersRequest = System.nanoTime(); - } - - public long getStartSchedule() { - return startSchedule; - } - - public void startSchedule() { - this.startSchedule = System.nanoTime(); + public InvocationStageTrace(Invocation invocation) { + this.invocation = invocation; } - public long getStartExecution() { - return startExecution; + public String recordStageBegin(String stageName) { + String realStageName = stageName; + while (stages.get(realStageName) != null) { + realStageName = realStageName + "@"; + } + Stage stage = new Stage(); + stage.beginTime = System.nanoTime(); + stages.put(realStageName, stage); + return realStageName; } - public void startExecution() { - this.startExecution = System.nanoTime(); + public void recordStageEnd(String realStageName) { + Stage stage = stages.get(realStageName); + stage.endTime = nanoTime(); } - public long getStartSend() { - return startSend; + public Map getStages() { + return stages; } - public void startSend() { - this.startSend = System.nanoTime(); + public void finish() { + this.finish = nanoTime(); } - public long getFinishGetConnection() { - return finishGetConnection; + public void startCreateInvocation(long nano) { + this.startCreateInvocation = nano; + this.startInMillis = millisTime(); + this.start = nanoTime(); } - public void startGetConnection() { - this.startGetConnection = System.nanoTime(); + public void finishCreateInvocation() { + this.finishCreateInvocation = nanoTime(); } - public void finishGetConnection() { - this.finishGetConnection = System.nanoTime(); + public long calcPrepare() { + return calc(finishCreateInvocation, startCreateInvocation); } - public long getFinishWriteToBuffer() { - return finishWriteToBuffer; + public void startProviderQueue() { + this.startProviderQueue = nanoTime(); } - public void finishWriteToBuffer(long finishWriteToBuffer) { - this.finishWriteToBuffer = finishWriteToBuffer; + public void finishProviderQueue() { + this.finishProviderQueue = nanoTime(); } - public long getFinishReceiveResponse() { - return finishReceiveResponse; + public long calcQueue() { + return calc(finishProviderQueue, startProviderQueue); } - public void finishReceiveResponse() { - this.finishReceiveResponse = System.nanoTime(); + public void startProviderDecodeRequest() { + this.startProviderDecodeRequest = nanoTime(); } - public long getStartClientFiltersResponse() { - return startClientFiltersResponse; + public void finishProviderDecodeRequest() { + this.finishProviderDecodeRequest = nanoTime(); } - public void startClientFiltersResponse() { - this.startClientFiltersResponse = System.nanoTime(); + public long calcProviderDecodeRequest() { + return calc(finishProviderDecodeRequest, startProviderDecodeRequest); } - public long getFinishClientFiltersResponse() { - return finishClientFiltersResponse; + public void startProviderEncodeResponse() { + this.startProviderEncodeResponse = nanoTime(); } - public void finishClientFiltersResponse() { - this.finishClientFiltersResponse = System.nanoTime(); + public void finishProviderEncodeResponse() { + this.finishProviderEncodeResponse = nanoTime(); } - public long getFinishHandlersResponse() { - return finishHandlersResponse; + public long calcProviderEncodeResponse() { + return calc(finishProviderEncodeResponse, startProviderEncodeResponse); } - public void finishHandlersResponse() { - this.finishHandlersResponse = System.nanoTime(); + public void startConsumerEncodeRequest() { + this.startConsumerEncodeRequest = nanoTime(); } - public long getStartServerFiltersRequest() { - return startServerFiltersRequest; + public void finishConsumerEncodeRequest() { + this.finishConsumerEncodeRequest = nanoTime(); } - public void startServerFiltersRequest() { - this.startServerFiltersRequest = System.nanoTime(); + public long calcConsumerEncodeRequest() { + return calc(finishConsumerEncodeRequest, startConsumerEncodeRequest); } - public long getFinishServerFiltersResponse() { - return finishServerFiltersResponse; + public void startConsumerDecodeResponse() { + this.startConsumerDecodeResponse = nanoTime(); } - public void finishServerFiltersResponse() { - this.finishServerFiltersResponse = System.nanoTime(); + public void finishConsumerDecodeResponse() { + this.finishConsumerDecodeResponse = nanoTime(); } - public long getStartBusinessMethod() { - return startBusinessMethod; + public long calcConsumerDecodeResponse() { + return calc(finishConsumerDecodeResponse, startConsumerDecodeResponse); } - public void startBusinessMethod() { - this.startBusinessMethod = System.nanoTime(); + public void startProviderSendResponse() { + this.startProviderSendResponse = nanoTime(); } - public long getFinishBusiness() { - return finishBusiness; + public void finishProviderSendResponse() { + this.finishProviderSendResponse = nanoTime(); } - public void finishBusiness() { - this.finishBusiness = System.nanoTime(); + public long calcProviderSendResponse() { + return calc(finishProviderSendResponse, startProviderSendResponse); } - public long getFinish() { - return finish; + public void startBusinessExecute() { + this.startBusinessExecute = nanoTime(); } - public void finish() { - this.finish = System.nanoTime(); + public void finishBusinessExecute() { + this.finishBusinessExecute = nanoTime(); } - private double calc(long finish, long start) { - if (finish == 0 || start == 0) { - return Double.NaN; - } - - return finish - start; + public long calcBusinessExecute() { + return calc(finishBusinessExecute, startBusinessExecute); } - public double calcTotalTime() { - return calc(finish, start); + public void startConsumerConnection() { + this.startConsumerConnection = nanoTime(); } - public double calcInvocationPrepareTime() { - if (invocation.isConsumer() && !invocation.isEdge()) { - return calc(startHandlersRequest, start); - } - - return calc(startSchedule, start); + public void finishConsumerConnection() { + this.finishConsumerConnection = nanoTime(); } - public double calcHandlersRequestTime() { - if (invocation.isConsumer()) { - return calc(startClientFiltersRequest, startHandlersRequest); - } - - return calc(startBusinessMethod, startHandlersRequest); + public long calcConnection() { + return calc(finishConsumerConnection, startConsumerConnection); } - public double calcClientFiltersRequestTime() { - return calc(startSend, startClientFiltersRequest); + public void startConsumerSendRequest() { + this.startConsumerSendRequest = nanoTime(); } - public double calcServerFiltersRequestTime() { - return calc(startHandlersRequest, startServerFiltersRequest); + public void finishConsumerSendRequest() { + this.finishConsumerSendRequest = nanoTime(); } - public double calcSendRequestTime() { - return calc(finishWriteToBuffer, startSend); + public long calcConsumerSendRequest() { + return calc(finishConsumerSendRequest, startConsumerSendRequest); } - public double calcGetConnectionTime() { - return calc(finishGetConnection, startGetConnection); + public void startWaitResponse() { + this.startWaitResponse = nanoTime(); } - public double calcWriteToBufferTime() { - return calc(finishWriteToBuffer, finishGetConnection); + public void finishWaitResponse() { + this.finishWaitResponse = nanoTime(); } - public double calcReceiveResponseTime() { - return calc(finishReceiveResponse, finishWriteToBuffer); + public long calcWait() { + return calc(finishWaitResponse, startWaitResponse); } - public double calcWakeConsumer() { - return calc(startClientFiltersResponse, finishReceiveResponse); + public long calcTotal() { + return calc(finish, this.startCreateInvocation); } - public double calcClientFiltersResponseTime() { - return calc(finishClientFiltersResponse, startClientFiltersResponse); + public long getStartInMillis() { + return this.startInMillis; } - public double calcServerFiltersResponseTime() { - return calc(finishServerFiltersResponse, finishHandlersResponse); + public long getStart() { + return this.start; } - public double calcHandlersResponseTime() { - if (invocation.isConsumer()) { - return calc(finishHandlersResponse, finishClientFiltersResponse); + public static long calc(long finish, long start) { + if (finish == 0 || start == 0) { + return 0; } - return calc(finishHandlersResponse, finishBusiness); - } - - public double calcThreadPoolQueueTime() { - return calc(startExecution, startSchedule); + return finish - start; } - public double calcBusinessTime() { - return calc(finishBusiness, startBusinessMethod); + /* + * Holder for testing purpose + */ + protected long nanoTime() { + return System.nanoTime(); } - public double calcSendResponseTime() { - return calc(finish, finishServerFiltersResponse); + protected long millisTime() { + return System.currentTimeMillis(); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutBootListener.java b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutBootListener.java index 93429e3c7e3..6a5e929a8bd 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutBootListener.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutBootListener.java @@ -21,8 +21,6 @@ import org.apache.servicecomb.core.event.InvocationBusinessFinishEvent; import org.apache.servicecomb.core.event.InvocationBusinessMethodStartEvent; -import org.apache.servicecomb.core.event.InvocationHandlersStartEvent; -import org.apache.servicecomb.core.event.InvocationRunInExecutorStartEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.core.event.InvocationStartSendRequestEvent; import org.apache.servicecomb.core.event.InvocationTimeoutCheckEvent; @@ -35,8 +33,8 @@ import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; -@SuppressWarnings({"UnstableApiUsage", "unused"}) -public class InvocationTimeoutBootListener { +@SuppressWarnings({"unused"}) +public final class InvocationTimeoutBootListener { private static final Logger LOGGER = LoggerFactory.getLogger(InvocationTimeoutBootListener.class); public static final String PREFIX = "servicecomb.invocation.timeout.check"; @@ -74,18 +72,6 @@ public void onInvocationStartEvent(InvocationStartEvent event) { strategy.start(event.getInvocation()); } - @Subscribe - @EnableExceptionPropagation - public void onInvocationRunInExecutorStartEvent(InvocationRunInExecutorStartEvent event) { - strategy.startRunInExecutor(event.getInvocation()); - } - - @Subscribe - @EnableExceptionPropagation - public void onInvocationHandlersStartEvent(InvocationHandlersStartEvent event) { - strategy.startHandlers(event.getInvocation()); - } - @Subscribe @EnableExceptionPropagation public void onInvocationBusinessMethodStartEvent(InvocationBusinessMethodStartEvent event) { diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutStrategy.java b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutStrategy.java index 24b48619d2c..c530acfca29 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutStrategy.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/InvocationTimeoutStrategy.java @@ -37,10 +37,6 @@ default void startRunInExecutor(Invocation invocation) { checkTimeout(invocation); } - default void startHandlers(Invocation invocation) { - checkTimeout(invocation); - } - default void startBusinessMethod(Invocation invocation) { checkTimeout(invocation); } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java index 6f4f8e2ff2d..ec589f37733 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlow.java @@ -19,14 +19,16 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.exception.Exceptions; -import org.apache.servicecomb.foundation.common.utils.ExceptionUtils; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jakarta.ws.rs.core.Response.Status; + public abstract class ProducerInvocationFlow { private static final Logger LOGGER = LoggerFactory.getLogger(ProducerInvocationFlow.class); @@ -60,57 +62,49 @@ private void tryRunInvocation(Invocation invocation) { if (invocation == null) { return; } - - invocation.onStart(requestEx, startTime); - invocation.getMicroserviceMeta().getFilterChain() - .onFilter(invocation) - .whenComplete((response, Throwable) -> sendResponse(invocation, response)) - .whenComplete((response, Throwable) -> finishInvocation(invocation, response, Throwable)); - } - - private void finishInvocation(Invocation invocation, Response response, Throwable throwable) { - invocation.onFinish(response); - - tryLogException(invocation, throwable); - } - - private void tryLogException(Invocation invocation, Throwable throwable) { - if (throwable == null) { - return; - } - - throwable = Exceptions.unwrap(throwable); - if (requestEx == null) { - logException(invocation, throwable); + invocation.getInvocationStageTrace().startCreateInvocation(this.startTime); + invocation.getInvocationStageTrace().finishCreateInvocation(); + invocation.onStart(requestEx); + if (invocation.isEdge()) { + invocation.getMicroserviceMeta().getEdgeFilterChain() + .onFilter(invocation) + .whenComplete((response, throwable) -> { + if (throwable != null) { + // Server codec operates on Response. So the filter chain result should be Response and + // will never throw exception. + // Sometimes Server Codec will throw exception, e.g. Connection Closed. + LOGGER.error("Maybe a fatal bug that should be addressed or codec exception for {}.", + invocation.getInvocationQualifiedName(), throwable); + response = Response.createFail(new InvocationException(Status.INTERNAL_SERVER_ERROR, + new CommonExceptionData("Internal error, check logs for details."))); + } + endResponse(invocation, response); + finishInvocation(invocation, response); + }); return; } - - logException(invocation, requestEx, throwable); - } - - private void logException(Invocation invocation, Throwable throwable) { - if (Exceptions.isPrintInvocationStackTrace()) { - LOGGER.error("Failed to finish invocation, operation:{}.", invocation.getMicroserviceQualifiedName(), throwable); - return; - } - - LOGGER.error("Failed to finish invocation, operation:{}, message={}.", invocation.getMicroserviceQualifiedName(), - ExceptionUtils.getExceptionMessageWithoutTrace(throwable)); + invocation.getMicroserviceMeta().getProviderFilterChain() + .onFilter(invocation) + .whenComplete((response, throwable) -> { + if (throwable != null) { + // Server codec operates on Response. So the filter chain result should be Response and + // will never throw exception. + // Sometimes Server Codec will throw exception, e.g. Connection Closed. + LOGGER.error("Maybe a fatal bug that should be addressed or codec exception for {}.", + invocation.getInvocationQualifiedName(), throwable); + response = Response.createFail(new InvocationException(Status.INTERNAL_SERVER_ERROR, + new CommonExceptionData("Internal error, check logs for details."))); + } + endResponse(invocation, response); + finishInvocation(invocation, response); + }); } - private void logException(Invocation invocation, HttpServletRequestEx requestEx, Throwable throwable) { - if (Exceptions.isPrintInvocationStackTrace()) { - LOGGER.error("Failed to finish invocation, operation:{}, request uri:{}.", - invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), throwable); - return; - } - - LOGGER.error("Failed to finish invocation, operation:{}, request uri:{}, message={}.", - invocation.getMicroserviceQualifiedName(), requestEx.getRequestURI(), - ExceptionUtils.getExceptionMessageWithoutTrace(throwable)); + private void finishInvocation(Invocation invocation, Response response) { + invocation.onFinish(response); } protected abstract Invocation sendCreateInvocationException(Throwable throwable); - protected abstract void sendResponse(Invocation invocation, Response response); + protected abstract void endResponse(Invocation invocation, Response response); } diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtils.java b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtils.java index d906de63ba5..2d8b37c997a 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtils.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/endpoint/EndpointUtils.java @@ -88,7 +88,7 @@ public static Endpoint parse(String uriEndpoint) { } /** - *
+   * {@code
    *   http://xxx  -> rest://xxx
    *   https://xxx -> rest://xxx?sslEnabled=true
    *
@@ -98,7 +98,7 @@ public static Endpoint parse(String uriEndpoint) {
    *   xxx         -> rest://xxx:port?protocol=http2
    *   xxx?a=a1    -> rest://xxx:port?a=a1&protocol=http2
    *   other://xxx -> other://xxx
-   * 
+ * } * * This method provided for convenience of handling user input endpoints, and do not have a strict meaning. Make sure all unit test cases * work before change. diff --git a/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategy.java b/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategy.java index 986c085b394..6d55c7a7f51 100644 --- a/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategy.java +++ b/core/src/main/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategy.java @@ -78,7 +78,7 @@ public void start(Invocation invocation) { return; } - long startTimeMillis = invocation.getInvocationStageTrace().getStartTimeMillis(); + long startTimeMillis = invocation.getInvocationStageTrace().getStartInMillis(); String contextChainStartTime = invocation.getContext(CHAIN_START_TIME); if (StringUtils.isEmpty(contextChainStartTime)) { invocation.addContext(CHAIN_START_TIME, String.valueOf(startTimeMillis)); diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/LocalOpenAPIRegistry.java b/core/src/main/java/org/apache/servicecomb/core/provider/LocalOpenAPIRegistry.java index 4707a0e121e..14a27530c92 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/LocalOpenAPIRegistry.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/LocalOpenAPIRegistry.java @@ -16,24 +16,12 @@ */ package org.apache.servicecomb.core.provider; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; import java.util.Map; -import java.util.Optional; -import java.util.Set; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.foundation.common.utils.JvmUtils; -import org.apache.servicecomb.foundation.common.utils.ResourceUtil; -import org.apache.servicecomb.swagger.SwaggerUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; @@ -64,33 +52,6 @@ public boolean enabled() { return true; } - @Override - public Set getSchemaIds(String application, String serviceName) { - Set result = new HashSet<>(); - if (apps.get(application) != null && apps.get(application).get(serviceName) != null) { - result.addAll(apps.get(application).get(serviceName).keySet()); - } - - String swaggersLocation; - if (application.equals(BootStrapProperties.readApplication(environment))) { - swaggersLocation = String.format("microservices/%s", serviceName); - } else { - swaggersLocation = String.format("applications/%s/%s", application, serviceName); - } - try { - List resourceUris = ResourceUtil.findResourcesBySuffix(swaggersLocation, ".yaml"); - for (URI uri : resourceUris) { - String path = uri.toURL().getPath(); - path = path.substring(path.lastIndexOf("/") + 1); - path = path.substring(0, path.indexOf(".yaml")); - result.add(path); - } - } catch (IOException | URISyntaxException e) { - LOGGER.error("Load schema ids failed from location {}. {}.", swaggersLocation, e.getMessage()); - } - return result; - } - @Override public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { apps.computeIfAbsent(application, k -> new ConcurrentHashMapEx<>()) @@ -101,27 +62,20 @@ public void registerOpenAPI(String application, String serviceName, String schem application, serviceName, schemaId); } + /** + * Method for retrieve myself schema contents. + */ + public Map loadOpenAPI() { + return loadOpenAPI(BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment)); + } + @Override - public Map loadOpenAPI(String application, String serviceName, Set schemaIds) { - Map result = new HashMap<>(schemaIds.size()); - for (String schemaId : schemaIds) { - OpenAPI api = loadFromMemory(application, serviceName, schemaId); - if (api == null) { - api = loadFromResource(application, serviceName, schemaId); - } - if (api != null) { - result.put(schemaId, api); - } - } - if (result.isEmpty()) { - return result; - } - if (result.size() != schemaIds.size()) { - LOGGER.warn("Local OpenAPI registry contains only partial schemas for {}/{}, this is not allowed.", - application, serviceName); - return Collections.emptyMap(); + public Map loadOpenAPI(String application, String serviceName) { + if (apps.get(application) != null && apps.get(application).get(serviceName) != null) { + return apps.get(application).get(serviceName); } - return result; + return Collections.emptyMap(); } @Override @@ -129,32 +83,6 @@ public void setOpenAPIChangeListener(OpenAPIChangeListener listener) { this.openAPIChangeListener = listener; } - protected OpenAPI loadFromResource(String application, String serviceName, String schemaId) { - if (application.equals(BootStrapProperties.readApplication(environment))) { - OpenAPI swagger = loadFromResource(String.format("microservices/%s/%s.yaml", serviceName, schemaId)); - if (swagger != null) { - return swagger; - } - } - return loadFromResource(String.format("applications/%s/%s/%s.yaml", application, serviceName, schemaId)); - } - - protected OpenAPI loadFromResource(String path) { - URL url = JvmUtils.findClassLoader().getResource(path); - if (url == null) { - return null; - } - - return SwaggerUtils.parseAndValidateSwagger(url); - } - - protected OpenAPI loadFromMemory(String application, String serviceName, String schemaId) { - return Optional.ofNullable(apps.get(application)) - .map(microservices -> microservices.get(serviceName)) - .map(schemas -> schemas.get(schemaId)) - .orElse(null); - } - @Override public int getOrder() { return -10000; diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistry.java b/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistry.java index 1a5367bf585..440c297d7ae 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistry.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistry.java @@ -17,7 +17,6 @@ package org.apache.servicecomb.core.provider; import java.util.Map; -import java.util.Set; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; import org.springframework.core.Ordered; @@ -32,11 +31,9 @@ public interface OpenAPIRegistry extends Ordered { boolean enabled(); - Set getSchemaIds(String application, String serviceName); - void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api); - Map loadOpenAPI(String application, String serviceName, Set schemaIds); + Map loadOpenAPI(String application, String serviceName); void setOpenAPIChangeListener(OpenAPIChangeListener listener); } diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistryManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistryManager.java index e19d0830a23..c357e6a24ae 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistryManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/OpenAPIRegistryManager.java @@ -22,7 +22,6 @@ import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Set; import org.apache.commons.io.FilenameUtils; import org.apache.servicecomb.foundation.common.utils.ResourceUtil; @@ -75,16 +74,6 @@ public void onOpenAPIChanged(String application, String serviceName) { } } - public Set getSchemaIds(String application, String serviceName) { - for (OpenAPIRegistry registry : this.openAPIRegistries) { - Set content = registry.getSchemaIds(application, serviceName); - if (!CollectionUtils.isEmpty(content)) { - return content; - } - } - return Collections.emptySet(); - } - public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { for (OpenAPIRegistry registry : this.openAPIRegistries) { registry.registerOpenAPI(application, serviceName, schemaId, api); @@ -115,10 +104,10 @@ public void registerOpenAPIInLocation(String application, String serviceName, St } } - public Map loadOpenAPI(String appId, String microserviceName, Set schemaIds) { + public Map loadOpenAPI(String appId, String microserviceName) { for (OpenAPIRegistry registry : this.openAPIRegistries) { - Map result = registry.loadOpenAPI(appId, microserviceName, schemaIds); - if (!CollectionUtils.isEmpty(result) && result.size() == schemaIds.size()) { + Map result = registry.loadOpenAPI(appId, microserviceName); + if (!CollectionUtils.isEmpty(result)) { return result; } } diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/RegistryOpenAPIRegistry.java b/core/src/main/java/org/apache/servicecomb/core/provider/RegistryOpenAPIRegistry.java index e2ff664046b..cc5e6fe22dd 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/RegistryOpenAPIRegistry.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/RegistryOpenAPIRegistry.java @@ -17,10 +17,8 @@ package org.apache.servicecomb.core.provider; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import org.apache.servicecomb.core.provider.OpenAPIRegistryManager.OpenAPIChangeListener; import org.apache.servicecomb.registry.DiscoveryManager; @@ -59,34 +57,13 @@ public boolean enabled() { return environment.getProperty(OpenAPIRegistry.CONFIG_PREFIX + ".registry.enabled", boolean.class, false); } - @Override - public Set getSchemaIds(String application, String serviceName) { - List discoveryInstances = - discoveryManager.findServiceInstances(application, serviceName); - if (discoveryInstances.isEmpty()) { - throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "no instances"); - } - discoveryInstances.sort((a, b) -> VersionCompareUtil.compareVersion(b.getVersion(), a.getVersion())); - - Set result = new HashSet<>(); - String version = null; - for (DiscoveryInstance instance : discoveryInstances) { - if (version != null && !version.equals(instance.getVersion())) { - break; - } - version = instance.getVersion(); - result.addAll(instance.getSchemas().keySet()); - } - return result; - } - @Override public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { // do noting } @Override - public Map loadOpenAPI(String application, String serviceName, Set schemaIds) { + public Map loadOpenAPI(String application, String serviceName) { List discoveryInstances = discoveryManager.findServiceInstances(application, serviceName); if (discoveryInstances.isEmpty()) { diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.java index b1a6f8ec956..615063bd3b4 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ConsumerProviderManager.java @@ -17,23 +17,70 @@ package org.apache.servicecomb.core.provider.consumer; -import java.util.ArrayList; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.List; -import org.apache.servicecomb.core.ConsumerProvider; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.core.provider.OpenAPIRegistryManager; +import org.apache.servicecomb.foundation.common.utils.ResourceUtil; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; public class ConsumerProviderManager { - private final List consumerProviderList = new ArrayList<>( - SPIServiceUtils.getOrLoadSortedService(ConsumerProvider.class)); + private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerProviderManager.class); - public List getConsumerProviderList() { - return consumerProviderList; + private final OpenAPIRegistryManager openAPIRegistryManager; + + private final Environment environment; + + public ConsumerProviderManager(Environment environment, OpenAPIRegistryManager openAPIRegistryManager) { + this.environment = environment; + this.openAPIRegistryManager = openAPIRegistryManager; } public void init() throws Exception { - for (ConsumerProvider provider : consumerProviderList) { - provider.init(); + registerSwaggerFromApplications(); + registerSwaggerFromMicroservices(); + } + + private void registerSwaggerFromApplications() { + try { + List resourceUris = ResourceUtil.findResourcesBySuffix("applications", ".yaml"); + for (URI uri : resourceUris) { + String path = uri.toURL().getPath(); + String[] segments = path.split("/"); + if (segments.length < 4 || !"applications".equals(segments[segments.length - 4])) { + continue; + } + openAPIRegistryManager.registerOpenAPI(segments[segments.length - 3], segments[segments.length - 2], + segments[segments.length - 1].substring(0, segments[segments.length - 1].indexOf(".yaml")), + SwaggerUtils.parseAndValidateSwagger(uri.toURL())); + } + } catch (IOException | URISyntaxException e) { + LOGGER.error("Load schema ids failed from applications. {}.", e.getMessage()); + } + } + + private void registerSwaggerFromMicroservices() { + try { + List resourceUris = ResourceUtil.findResourcesBySuffix("microservices", ".yaml"); + for (URI uri : resourceUris) { + String path = uri.toURL().getPath(); + String[] segments = path.split("/"); + if (segments.length < 3 || !"microservices".equals(segments[segments.length - 3])) { + continue; + } + openAPIRegistryManager.registerOpenAPI(BootStrapProperties.readApplication(environment), + segments[segments.length - 2], + segments[segments.length - 1].substring(0, segments[segments.length - 1].indexOf(".yaml")), + SwaggerUtils.parseAndValidateSwagger(uri.toURL())); + } + } catch (IOException | URISyntaxException e) { + LOGGER.error("Load schema ids failed from microservices. {}.", e.getMessage()); } } } diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java index 65d78c49012..152f43a5034 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/InvokerUtils.java @@ -18,22 +18,14 @@ package org.apache.servicecomb.core.provider.consumer; import static org.apache.servicecomb.core.exception.Exceptions.toConsumerResponse; -import static org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory.CONSUMER_INNER_STATUS_CODE; import java.lang.reflect.Method; import java.lang.reflect.Type; -import java.time.Duration; import java.util.Map; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; @@ -41,62 +33,18 @@ import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.definition.SchemaMeta; -import org.apache.servicecomb.core.governance.GovernanceConfiguration; -import org.apache.servicecomb.core.governance.MatchType; -import org.apache.servicecomb.core.governance.RetryContext; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; -import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.governance.handler.RetryHandler; -import org.apache.servicecomb.governance.handler.ext.FailurePredictor; -import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.swagger.invocation.AsyncResponse; import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.google.common.annotations.VisibleForTesting; - -import io.github.resilience4j.decorators.Decorators; -import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; -import io.github.resilience4j.retry.Retry; -import io.github.resilience4j.retry.RetryConfig; -import io.github.resilience4j.retry.RetryRegistry; import io.vertx.core.Context; import jakarta.ws.rs.core.Response.Status; public final class InvokerUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(InvokerUtils.class); - - private static final Object LOCK = new Object(); - - private static volatile ScheduledExecutorService reactiveRetryPool; - - private static ScheduledExecutorService getOrCreateRetryPool() { - if (reactiveRetryPool == null) { - synchronized (LOCK) { - if (reactiveRetryPool == null) { - reactiveRetryPool = Executors.newScheduledThreadPool(2, new ThreadFactory() { - private final AtomicInteger count = new AtomicInteger(0); - - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r, "reactive-retry-pool-thread-" + count.getAndIncrement()); - // avoid block shutdown - thread.setDaemon(true); - return thread; - } - }); - } - } - } - return reactiveRetryPool; - } - @SuppressWarnings({"unchecked"}) public static T syncInvoke(String microserviceName, String transport, String schemaId, String operationId, Map swaggerArguments, Type responseType) { @@ -128,6 +76,7 @@ public static void reactiveInvoke(String microserviceName, String schemaId, Stri public static Invocation createInvocation(String microserviceName, String transport, String schemaId, String operationId, Map swaggerArguments, Type responseType) { + long startCreateInvocation = System.nanoTime(); MicroserviceReferenceConfig microserviceReferenceConfig = SCBEngine.getInstance() .getOrCreateReferenceConfig(microserviceName); if (microserviceReferenceConfig == null) { @@ -142,8 +91,11 @@ public static Invocation createInvocation(String microserviceName, String transp ReferenceConfig referenceConfig = microserviceReferenceConfig.createReferenceConfig(transport, operationMeta); InvocationRuntimeType invocationRuntimeType = operationMeta.buildBaseConsumerRuntimeType(); invocationRuntimeType.setSuccessResponseType(responseType); - return InvocationFactory + Invocation result = InvocationFactory .forConsumer(referenceConfig, operationMeta, invocationRuntimeType, swaggerArguments); + result.getInvocationStageTrace().startCreateInvocation(startCreateInvocation); + result.getInvocationStageTrace().finishCreateInvocation(); + return result; } /** @@ -187,43 +139,6 @@ public static Response innerSyncInvoke(Invocation invocation) { return toSync(invoke(invocation), invocation.getWaitTime()); } - private static void updateRetryStatus(Invocation invocation) { - if (invocation.isFinished()) { - invocation.reset(); - if (invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE) != null - && (boolean) invocation.getLocalContext(RetryContext.RETRY_LOAD_BALANCE)) { - // clear last server to avoid using user defined endpoint - invocation.setEndpoint(null); - } - RetryContext retryContext = invocation.getLocalContext(RetryContext.RETRY_CONTEXT); - retryContext.incrementRetry(); - return; - } - - invocation.addLocalContext(RetryContext.RETRY_CONTEXT, - new RetryContext(GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()))); - } - - private static boolean isCompatibleRetryEnabled(Invocation invocation) { - // maxAttempts must be greater than or equal to 1 - return GovernanceConfiguration.isRetryEnabled(invocation.getMicroserviceName()) - && GovernanceConfiguration.getRetryNextServer(invocation.getMicroserviceName()) - + GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()) > 0; - } - - private static Retry getOrCreateCompatibleRetry(Invocation invocation) { - RetryConfig retryConfig = RetryConfig.custom() - // max attempts include the first call - .maxAttempts(GovernanceConfiguration.getRetryNextServer(invocation.getMicroserviceName()) - + GovernanceConfiguration.getRetrySameServer(invocation.getMicroserviceName()) + 1) - .retryOnResult(InvokerUtils::canRetryForStatusCode) - .retryOnException(InvokerUtils::canRetryForException) - .waitDuration(Duration.ofMillis(1)) - .build(); - RetryRegistry retryRegistry = RetryRegistry.of(retryConfig); - return retryRegistry.retry(invocation.getMicroserviceName()); - } - /** * This is an internal API, caller make sure already invoked SCBEngine.ensureStatusUp */ @@ -237,22 +152,6 @@ public static void reactiveInvoke(Invocation invocation, AsyncResponse asyncResp }); } - private static void decorateReactiveRetry(Invocation invocation, DecorateCompletionStage dcs, - GovernanceRequestExtractor request) { - // governance implementations. - RetryHandler retryHandler = BeanUtils.getBean(RetryHandler.class); - Retry retry = retryHandler.getActuator(request); - if (retry != null) { - dcs.withRetry(retry, getOrCreateRetryPool()); - } - - if (isCompatibleRetryEnabled(invocation)) { - // compatible implementation for retry in load balance module in old versions. - retry = getOrCreateCompatibleRetry(invocation); - dcs.withRetry(retry, getOrCreateRetryPool()); - } - } - public static boolean isSyncMethod(Method method) { return !isAsyncMethod(method); } @@ -283,85 +182,10 @@ public static T toSync(CompletableFuture future, long waitInMillis) { * NOTE: this method should never throw exception directly */ public static CompletableFuture invoke(Invocation invocation) { - Supplier> next = invokeImpl(invocation); - DecorateCompletionStage dcs = Decorators.ofCompletionStage(next); - GovernanceRequestExtractor request = MatchType.createGovHttpRequest(invocation); - - decorateReactiveRetry(invocation, dcs, request); - - CompletableFuture result = new CompletableFuture<>(); - dcs.get().whenComplete((r, e) -> { - ContextUtils.setInvocationContext(invocation.getParentContext()); - - if (e == null) { - result.complete(r); - return; - } - - String message = String.format("invoke failed, operation %s, trace id %s", - invocation.getMicroserviceQualifiedName(), - invocation.getTraceId()); - LOGGER.error(message, e); - Response response = Response.createConsumerFail(e, message); - invocation.onFinish(response); - result.complete(response); - }); - return result; - } - - private static Supplier> invokeImpl(Invocation invocation) { - return () -> { - invocation.onStart(null, System.nanoTime()); - updateRetryStatus(invocation); - invocation.onStartHandlersRequest(); - return invocation.getMicroserviceMeta().getFilterChain() - .onFilter(invocation) - .exceptionally(throwable -> toConsumerResponse(invocation, throwable)) - .whenComplete((response, throwable) -> finishInvocation(invocation, response)); - }; - } - - private static void finishInvocation(Invocation invocation, Response ar) { - invocation.getInvocationStageTrace().finishHandlersResponse(); - invocation.onFinish(ar); - - if (ar.isFailed()) { - // re-throw exception to make sure retry based on exception - // for InvocationException, users can configure status code for retry - // for 490, details error are wrapped, need re-throw - - if (!(ar.getResult() instanceof InvocationException)) { - throw AsyncUtils.rethrow(ar.getResult()); - } - - if (((InvocationException) ar.getResult()).getStatusCode() == CONSUMER_INNER_STATUS_CODE) { - throw AsyncUtils.rethrow(ar.getResult()); - } - } - } - - @VisibleForTesting - static boolean canRetryForException(Throwable e) { - if (e instanceof InvocationException && ((InvocationException) e).getStatusCode() == Status.SERVICE_UNAVAILABLE - .getStatusCode()) { - return true; - } - return FailurePredictor.canRetryForException(FailurePredictor.STRICT_RETRIABLE, e); - } - - @VisibleForTesting - static boolean canRetryForStatusCode(Object response) { - // retry on status code 503 - if (!(response instanceof Response resp)) { - return false; - } - if (!resp.isFailed()) { - return false; - } - if (resp.getResult() instanceof InvocationException) { - InvocationException e = resp.getResult(); - return e.getStatusCode() == 503; - } - return false; + invocation.onStart(null); + return invocation.getMicroserviceMeta().getConsumerFilterChain() + .onFilter(invocation) + .exceptionally(throwable -> toConsumerResponse(invocation, throwable)) + .whenComplete((response, throwable) -> invocation.onFinish(response)); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java index 5a8d2775be6..dffecfd62bf 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/ReferenceConfigManager.java @@ -17,7 +17,7 @@ package org.apache.servicecomb.core.provider.consumer; import java.util.Map; -import java.util.Set; +import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.SCBEngine; @@ -74,16 +74,20 @@ private void onOpenAPIChanged(String application, String serviceName) { executor.execute(() -> { synchronized (referenceConfigsLocks.computeIfAbsent(parser.getAppId(), key -> new ConcurrentHashMapEx<>()) .computeIfAbsent(parser.getMicroserviceName(), key -> new Object())) { - MicroserviceReferenceConfig temp = referenceConfigs.get(parser.getAppId()) - .get(parser.getMicroserviceName()); - if (temp != null) { + try { + MicroserviceReferenceConfig temp = referenceConfigs.get(parser.getAppId()) + .get(parser.getMicroserviceName()); + if (temp != null) { + result.complete(temp); + return; + } + temp = buildMicroserviceReferenceConfig(scbEngine, parser.getAppId(), + parser.getMicroserviceName()); + referenceConfigs.get(parser.getAppId()).put(parser.getMicroserviceName(), temp); result.complete(temp); - return; + } catch (Exception e) { + result.completeExceptionally(e); } - temp = buildMicroserviceReferenceConfig(scbEngine, parser.getAppId(), - parser.getMicroserviceName()); - referenceConfigs.get(parser.getAppId()).put(parser.getMicroserviceName(), temp); - result.complete(temp); } }); return result; @@ -131,20 +135,22 @@ private MicroserviceReferenceConfig buildMicroserviceReferenceConfig(SCBEngine e String application, String microserviceName) { ConsumerMicroserviceVersionsMeta microserviceVersionsMeta = new ConsumerMicroserviceVersionsMeta(engine); MicroserviceMeta microserviceMeta = new MicroserviceMeta(engine, application, microserviceName, true); - microserviceMeta.setFilterChain(engine.getFilterChainsManager().findConsumerChain(application, microserviceName)); + microserviceMeta.setConsumerFilterChain(engine.getFilterChainsManager() + .findConsumerChain(application, microserviceName)); + microserviceMeta.setEdgeFilterChain(engine.getFilterChainsManager() + .findEdgeChain(application, microserviceName)); microserviceMeta.setMicroserviceVersionsMeta(microserviceVersionsMeta); - Set schemaIds = this.openAPIRegistryManager.getSchemaIds(application, microserviceName); - Map schemas = this.openAPIRegistryManager.loadOpenAPI(application, microserviceName, schemaIds); - for (String schemaId : schemaIds) { - OpenAPI swagger = schemas.get(schemaId); + Map schemas = this.openAPIRegistryManager.loadOpenAPI(application, microserviceName); + for (Entry entry : schemas.entrySet()) { + OpenAPI swagger = entry.getValue(); if (swagger != null) { - microserviceMeta.registerSchemaMeta(schemaId, swagger); + microserviceMeta.registerSchemaMeta(entry.getKey(), entry.getValue()); continue; } throw new InvocationException(Status.INTERNAL_SERVER_ERROR, String.format("Swagger %s/%s/%s can not be empty or load swagger failed.", - application, microserviceName, schemaId)); + application, microserviceName, entry.getKey())); } EventManager.getEventBus().post(new CreateMicroserviceMetaEvent(microserviceMeta)); diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/SyncResponseExecutor.java b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/SyncResponseExecutor.java index b0351ac6461..8e86d9d7f90 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/SyncResponseExecutor.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/consumer/SyncResponseExecutor.java @@ -98,8 +98,7 @@ private long getWaitTime(Invocation invocation) { // In invocation handlers, may call other microservices, invocation // timeout may be much longer than request timeout. - // But this is quite rare, for simplicity, default two times of request timeout. - // If users need longer timeout, can configure invocation timeout. - return invocation.getOperationMeta().getConfig().getMsRequestTimeout() * 2; + // For simplicity, default 30000 or two times of request timeout. + return Math.max(invocation.getOperationMeta().getConfig().getMsRequestTimeout() * 2, 30000); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java index 8dac12f4c77..391bc6a4723 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java +++ b/core/src/main/java/org/apache/servicecomb/core/provider/producer/ProducerProviderManager.java @@ -120,8 +120,8 @@ public SchemaMeta registerSchema(String schemaId, Class schemaInterface, Obje } // This is special requirement by users: When service deployed in tomcat,user want to use RestTemplate to - // call REST service by the full url. e.g. restTemplate.getForObejct("cse://serviceName/root/prefix/health") - // By default, user's do not need context prefix, e.g. restTemplate.getForObejct("cse://serviceName/health") + // call REST service by the full url. e.g. restTemplate.getForObject("cse://serviceName/root/prefix/health") + // By default, user's do not need context prefix, e.g. restTemplate.getForObject("cse://serviceName/health") private void registerUrlPrefixToSwagger(OpenAPI swagger) { String urlPrefix = ClassLoaderScopeContext.getClassLoaderScopeProperty(DefinitionConst.URL_PREFIX); if (!StringUtils.isEmpty(urlPrefix) && !SwaggerUtils.getBasePath(swagger).startsWith(urlPrefix) diff --git a/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdLogger.java b/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdLogger.java index 72fd77a3909..3819bb90218 100644 --- a/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdLogger.java +++ b/core/src/main/java/org/apache/servicecomb/core/tracing/TraceIdLogger.java @@ -18,18 +18,19 @@ import org.apache.servicecomb.core.Invocation; import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.slf4j.MDC; import org.slf4j.Marker; public class TraceIdLogger { + private static final Logger LOGGER = LoggerFactory.getLogger("scb-trace-id"); + private static final Marker MARKER = new ScbMarker(); - private static final String KEY_TRACE_ID = "SERVICECOMB_TRACE_ID"; + public static final String KEY_TRACE_ID = "SERVICECOMB_TRACE_ID"; private final Invocation invocation; - private String name; - public TraceIdLogger(Invocation invocation) { this.invocation = invocation; } @@ -38,34 +39,36 @@ public Invocation getInvocation() { return invocation; } + public static String constructSource(String source) { + return "[" + source + "(" + + Thread.currentThread().getStackTrace()[2].getLineNumber() + ")]"; + } + public final String getName() { - if (name == null) { - name = invocation.getTraceId() + "-" + invocation.getInvocationId(); - } - return name; + return invocation.getTraceId(); } - public void error(Logger logger, String format, Object... arguments) { + public void error(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); - logger.error(MARKER, format, arguments); + LOGGER.error(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } - public void warn(Logger logger, String format, Object... arguments) { + public void warn(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); - logger.warn(MARKER, format, arguments); + LOGGER.warn(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } - public void info(Logger logger, String format, Object... arguments) { + public void info(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); - logger.info(MARKER, format, arguments); + LOGGER.info(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } - public void debug(Logger logger, String format, Object... arguments) { + public void debug(String format, Object... arguments) { MDC.put(KEY_TRACE_ID, getName()); - logger.debug(MARKER, format, arguments); + LOGGER.debug(MARKER, format, arguments); MDC.remove(KEY_TRACE_ID); } } diff --git a/core/src/main/java/org/apache/servicecomb/core/transport/TransportClassAnnotationProcessor.java b/core/src/main/java/org/apache/servicecomb/core/transport/TransportClassAnnotationProcessor.java new file mode 100644 index 00000000000..5af6705abc7 --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/transport/TransportClassAnnotationProcessor.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core.transport; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import io.swagger.v3.oas.models.OpenAPI; + +public class TransportClassAnnotationProcessor implements ClassAnnotationProcessor { + @Override + public Type getProcessType() { + return Transport.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, Transport transport) { + OpenAPI swagger = swaggerGenerator.getOpenAPI(); + if (StringUtils.isNotEmpty(transport.name())) { + swagger.addExtension(CoreConst.TRANSPORT_NAME, transport.name()); + } + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/transport/TransportMethodAnnotationProcessor.java b/core/src/main/java/org/apache/servicecomb/core/transport/TransportMethodAnnotationProcessor.java new file mode 100644 index 00000000000..617f4b87d5e --- /dev/null +++ b/core/src/main/java/org/apache/servicecomb/core/transport/TransportMethodAnnotationProcessor.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.core.transport; + +import java.lang.reflect.Type; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor; +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; + +import io.swagger.v3.oas.models.Operation; + +public class TransportMethodAnnotationProcessor implements MethodAnnotationProcessor { + @Override + public Type getProcessType() { + return Transport.class; + } + + @Override + public void process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Transport transport) { + Operation operation = operationGenerator.getOperation(); + if (StringUtils.isNotEmpty(transport.name())) { + operation.addExtension(CoreConst.TRANSPORT_NAME, transport.name()); + } + } +} diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider index 51f9e53fd2f..d21d7c1fe15 100644 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.filter.FilterProvider @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.core.filter.impl.DefaultFilterProvider \ No newline at end of file +org.apache.servicecomb.core.filter.impl.DefaultFilterProvider diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.tracing.TraceIdGenerator b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.tracing.TraceIdGenerator index 3a57a7db661..8e61e1f2b81 100644 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.tracing.TraceIdGenerator +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.core.tracing.TraceIdGenerator @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.core.tracing.BraveTraceIdGenerator \ No newline at end of file +org.apache.servicecomb.core.tracing.BraveTraceIdGenerator diff --git a/core/src/test/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor similarity index 91% rename from core/src/test/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter rename to core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor index ff338ab9d23..57d906d8131 100644 --- a/core/src/test/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.core.handler.impl.ErrorToProducerResponseConverter \ No newline at end of file +org.apache.servicecomb.core.transport.TransportClassAnnotationProcessor diff --git a/swagger/swagger-invocation/invocation-core/src/test/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor similarity index 90% rename from swagger/swagger-invocation/invocation-core/src/test/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter rename to core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor index a3afa7714b3..a003fcbd204 100644 --- a/swagger/swagger-invocation/invocation-core/src/test/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.invocation.exception.ErrorToProducerResponseConverter \ No newline at end of file +org.apache.servicecomb.core.transport.TransportMethodAnnotationProcessor diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister index 7fb23d9bc91..1356e33ecf8 100644 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.core.invocation.endpoint.EndpointContextRegister \ No newline at end of file +org.apache.servicecomb.core.invocation.endpoint.EndpointContextRegister diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory index 094ac2c2d11..6eb082fc473 100644 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory +++ b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.consumer.ConsumerContextArgumentMapperFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.core.invocation.endpoint.EndpointMapperFactory \ No newline at end of file +org.apache.servicecomb.core.invocation.endpoint.EndpointMapperFactory diff --git a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter b/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter deleted file mode 100644 index f703b9026fc..00000000000 --- a/core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter +++ /dev/null @@ -1,19 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -org.apache.servicecomb.core.exception.IllegalArgumentExceptionToProducerResponseConverter -org.apache.servicecomb.core.exception.ThrowableExceptionToProducerResponseConverter \ No newline at end of file diff --git a/core/src/test/java/org/apache/servicecomb/core/TestException.java b/core/src/test/java/org/apache/servicecomb/core/TestException.java index be6dc4519ba..58bce6bce43 100644 --- a/core/src/test/java/org/apache/servicecomb/core/TestException.java +++ b/core/src/test/java/org/apache/servicecomb/core/TestException.java @@ -25,32 +25,32 @@ public class TestException { @Test public void testCseException() { - CseException oExeception = new CseException("500", "InternalServerError"); - Assertions.assertEquals("500", oExeception.getCode()); + CseException oException = new CseException("500", "InternalServerError"); + Assertions.assertEquals("500", oException.getCode()); Assertions.assertEquals("ServiceDefinitionException Code:500, Message:InternalServerError", - oExeception.toString()); + oException.toString()); - oExeception = new CseException("503", "OwnException", new Throwable()); - Assertions.assertEquals("503", oExeception.getCode()); + oException = new CseException("503", "OwnException", new Throwable()); + Assertions.assertEquals("503", oException.getCode()); } @Test public void testExceptionUtils() { - CseException oExeception = ExceptionUtils + CseException oException = ExceptionUtils .createCseException("servicecomb.handler.ref.not.exist", new String("test")); - Assertions.assertEquals("servicecomb.handler.ref.not.exist", oExeception.getCode()); + Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); - oExeception = + oException = ExceptionUtils.createCseException("servicecomb.handler.ref.not.exist", new Throwable(), new String("test")); - Assertions.assertEquals("servicecomb.handler.ref.not.exist", oExeception.getCode()); + Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); - oExeception = ExceptionUtils.producerOperationNotExist("servicecomb.error", "unit-testing"); - Assertions.assertEquals("servicecomb.producer.operation.not.exist", oExeception.getCode()); + oException = ExceptionUtils.producerOperationNotExist("servicecomb.error", "unit-testing"); + Assertions.assertEquals("servicecomb.producer.operation.not.exist", oException.getCode()); - oExeception = ExceptionUtils.handlerRefNotExist("servicecomb.double.error"); - Assertions.assertEquals("servicecomb.handler.ref.not.exist", oExeception.getCode()); + oException = ExceptionUtils.handlerRefNotExist("servicecomb.double.error"); + Assertions.assertEquals("servicecomb.handler.ref.not.exist", oException.getCode()); - oExeception = ExceptionUtils.lbAddressNotFound("microServiceName", "my rule my world", "transportChannel"); - Assertions.assertEquals("servicecomb.lb.no.available.address", oExeception.getCode()); + oException = ExceptionUtils.lbAddressNotFound("microServiceName", "my rule my world", "transportChannel"); + Assertions.assertEquals("servicecomb.lb.no.available.address", oException.getCode()); } } diff --git a/core/src/test/java/org/apache/servicecomb/core/TestInvocation.java b/core/src/test/java/org/apache/servicecomb/core/TestInvocation.java index 96910a4ca4e..80b6bbda461 100644 --- a/core/src/test/java/org/apache/servicecomb/core/TestInvocation.java +++ b/core/src/test/java/org/apache/servicecomb/core/TestInvocation.java @@ -17,12 +17,10 @@ package org.apache.servicecomb.core; import java.util.Arrays; -import java.util.Map; import org.apache.servicecomb.core.definition.InvocationRuntimeType; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.event.InvocationBaseEvent; -import org.apache.servicecomb.core.event.InvocationBusinessMethodFinishEvent; import org.apache.servicecomb.core.event.InvocationBusinessMethodStartEvent; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.event.InvocationStartEvent; @@ -39,6 +37,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; @@ -48,10 +47,7 @@ import mockit.MockUp; import mockit.Mocked; -import org.junit.jupiter.api.Assertions; - public class TestInvocation { - Invocation invocation; @Mocked Endpoint endpoint; @@ -62,9 +58,6 @@ public class TestInvocation { @Mocked InvocationRuntimeType invocationRuntimeType; - @Mocked - Map arguments; - static long nanoTime = 123; @BeforeClass @@ -88,8 +81,6 @@ public static void classTeardown() { @Test public void onStart() { - mockNonaTime(); - Holder result = new Holder<>(); Object subscriber = new Object() { @Subscribe @@ -99,25 +90,13 @@ public void onStart(InvocationStartEvent event) { }; EventManager.register(subscriber); - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); - invocation.onStart(nanoTime); + Invocation invocation = new Invocation(endpoint, operationMeta, null); + invocation.onStart(); Assertions.assertSame(invocation, result.value); - Assertions.assertEquals(nanoTime, invocation.getInvocationStageTrace().getStart()); - EventManager.unregister(subscriber); } - @Test - public void onStartExecute() { - mockNonaTime(); - - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); - invocation.onExecuteStart(); - - Assertions.assertEquals(nanoTime, invocation.getInvocationStageTrace().getStartExecution()); - } - @Test public void onFinish() { mockNonaTime(); @@ -131,12 +110,11 @@ public void onStart(InvocationFinishEvent event) { }; EventManager.register(subscriber); - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); + Invocation invocation = new Invocation(endpoint, operationMeta, null); Assertions.assertFalse(invocation.isFinished()); Response response = Response.succResp(null); invocation.onFinish(response); - Assertions.assertEquals(nanoTime, result.value.getNanoCurrent()); Assertions.assertSame(invocation, result.value.getInvocation()); Assertions.assertSame(response, result.value.getResponse()); Assertions.assertTrue(invocation.isFinished()); @@ -151,19 +129,19 @@ public void onStart(InvocationFinishEvent event) { @Test public void isConsumer_yes() { - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); + Invocation invocation = new Invocation(endpoint, operationMeta, null); Assertions.assertFalse(invocation.isConsumer()); } @Test public void isConsumer_no(@Mocked ReferenceConfig referenceConfig) { - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); Assertions.assertTrue(invocation.isConsumer()); } @Test public void localContext(@Mocked ReferenceConfig referenceConfig) { - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); invocation.addLocalContext("k", 1); Assertions.assertSame(invocation.getHandlerContext(), invocation.getLocalContext()); @@ -172,10 +150,10 @@ public void localContext(@Mocked ReferenceConfig referenceConfig) { @Test public void traceId_fromContext(@Mocked ReferenceConfig referenceConfig) { - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); - invocation.onStart(0); + invocation.onStart(); Assertions.assertEquals("abc", invocation.getTraceId()); Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); @@ -190,9 +168,9 @@ public void traceId_consumerCreateTraceId(@Mocked ReferenceConfig referenceConfi result = "abc"; } }; - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); - invocation.onStart(0); + invocation.onStart(); Assertions.assertEquals("abc", invocation.getTraceId()); Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); @@ -206,9 +184,9 @@ public void traceId_fromRequest(@Mocked Endpoint endpoint, @Mocked HttpServletRe result = "abc"; } }; - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); + Invocation invocation = new Invocation(endpoint, operationMeta, null); - invocation.onStart(requestEx, 0); + invocation.onStart(requestEx); Assertions.assertEquals("abc", invocation.getTraceId()); Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); @@ -216,6 +194,7 @@ public void traceId_fromRequest(@Mocked Endpoint endpoint, @Mocked HttpServletRe @Test public void traceId_producerCreateTraceId(@Mocked Endpoint endpoint, @Mocked HttpServletRequestEx requestEx) { + TraceIdGenerator generator = SPIServiceUtils.getTargetService(TraceIdGenerator.class, BraveTraceIdGenerator.class); new Expectations(generator) { { @@ -223,9 +202,9 @@ public void traceId_producerCreateTraceId(@Mocked Endpoint endpoint, @Mocked Htt result = "abc"; } }; - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); + Invocation invocation = new Invocation(endpoint, operationMeta, null); - invocation.onStart(requestEx, 0); + invocation.onStart(requestEx); Assertions.assertEquals("abc", invocation.getTraceId()); Assertions.assertEquals("abc", invocation.getTraceId(CoreConst.TRACE_ID_NAME)); @@ -259,7 +238,7 @@ public void traceIdGeneratorInit(@Mocked TraceIdGenerator gen1, @Mocked TraceIdG InvocationBaseEvent invocationBaseEvent; @Test - public void onBusinessMethodStart() { + public void test_business_execute_time_correct() { Object listener = new Object() { @Subscribe public void onBusinessMethodStart(InvocationBusinessMethodStartEvent event) { @@ -267,52 +246,29 @@ public void onBusinessMethodStart(InvocationBusinessMethodStartEvent event) { } }; EventManager.getEventBus().register(listener); - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); + Invocation invocation = new Invocation(endpoint, operationMeta, null); mockNonaTime(); invocation.onBusinessMethodStart(); EventManager.getEventBus().unregister(listener); Assertions.assertSame(invocation, invocationBaseEvent.getInvocation()); - Assertions.assertEquals(nanoTime, invocation.getInvocationStageTrace().getStartBusinessMethod()); - } - - @Test - public void onBusinessMethodFinish() { - Object listener = new Object() { - @Subscribe - public void onBusinessMethodStart(InvocationBusinessMethodFinishEvent event) { - invocationBaseEvent = event; - } - }; - EventManager.getEventBus().register(listener); - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); - invocation.onBusinessMethodFinish(); - EventManager.getEventBus().unregister(listener); - - Assertions.assertSame(invocation, invocationBaseEvent.getInvocation()); - } - - @Test - public void onBusinessFinish() { - Invocation invocation = new Invocation(endpoint, operationMeta, arguments); - mockNonaTime(); + nanoTime++; invocation.onBusinessFinish(); - - Assertions.assertEquals(nanoTime, invocation.getInvocationStageTrace().getFinishBusiness()); + Assertions.assertEquals(1, invocation.getInvocationStageTrace().calcBusinessExecute()); } @Test public void marker(@Mocked ReferenceConfig referenceConfig) { Invocation.INVOCATION_ID.set(0); - Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); + Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); - invocation.onStart(0); - Assertions.assertEquals("abc-0", invocation.getTraceIdLogger().getName()); + invocation.onStart(); + Assertions.assertEquals("abc", invocation.getTraceIdLogger().getName()); - invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, arguments); + invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, null); invocation.addContext(CoreConst.TRACE_ID_NAME, "abc"); - invocation.onStart(0); - Assertions.assertEquals("abc-1", invocation.getTraceIdLogger().getName()); + invocation.onStart(); + Assertions.assertEquals("abc", invocation.getTraceIdLogger().getName()); } } diff --git a/core/src/test/java/org/apache/servicecomb/core/event/TestInvocationFinishEvent.java b/core/src/test/java/org/apache/servicecomb/core/event/TestInvocationFinishEvent.java deleted file mode 100644 index 63380368d4a..00000000000 --- a/core/src/test/java/org/apache/servicecomb/core/event/TestInvocationFinishEvent.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.core.event; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.invocation.InvocationStageTrace; -import org.apache.servicecomb.swagger.invocation.Response; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; - -public class TestInvocationFinishEvent { - InvocationFinishEvent event; - - @Test - public void construct(@Mocked Invocation invocation, @Mocked Response response) { - InvocationStageTrace stageTrace = new InvocationStageTrace(invocation); - long time = 123; - new MockUp() { - @Mock - long nanoTime() { - return time; - } - }; - new Expectations() { - { - invocation.getInvocationStageTrace(); - result = stageTrace; - } - }; - stageTrace.finish(); - - event = new InvocationFinishEvent(invocation, response); - - Assertions.assertEquals(time, event.getNanoCurrent()); - Assertions.assertSame(invocation, event.getInvocation()); - Assertions.assertSame(response, event.getResponse()); - } -} diff --git a/core/src/test/java/org/apache/servicecomb/core/exception/ExceptionsTest.java b/core/src/test/java/org/apache/servicecomb/core/exception/ExceptionsTest.java index 90ffc3c940e..0594084e30c 100644 --- a/core/src/test/java/org/apache/servicecomb/core/exception/ExceptionsTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/exception/ExceptionsTest.java @@ -20,6 +20,7 @@ import static jakarta.ws.rs.core.Response.Status.BAD_REQUEST; import static jakarta.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static org.assertj.core.api.Assertions.assertThat; +import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Collections; @@ -34,6 +35,9 @@ import jakarta.ws.rs.core.Response.StatusType; class ExceptionsTest { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + @Test void should_not_convert_invocation_exception() { InvocationException exception = Exceptions.genericConsumer("msg"); @@ -51,8 +55,13 @@ void should_convert_unknown_client_exception_to_invocation_exception() { assertThat(invocationException).hasCause(exception); assertThat(invocationException.getStatus()).isEqualTo(BAD_REQUEST); assertThat(invocationException.getErrorData()).isInstanceOf(CommonExceptionData.class); - assertThat(Json.encode(invocationException.getErrorData())) - .isEqualTo("{\"code\":\"SCB.00000000\",\"message\":\"Unexpected exception when processing none. msg\"}"); + + try { + assertThat(objectMapper.readTree(Json.encode(invocationException.getErrorData()))) + .isEqualTo(objectMapper.readTree("{\"code\":\"SCB.00000000\",\"message\":\"Unexpected exception when processing none. msg\"}")); + } catch (Exception e) { + throw new AssertionError("Error during JSON comparison: " + e.getMessage(), e); + } } @Test @@ -64,8 +73,13 @@ void should_convert_unknown_server_exception_to_invocation_exception() { assertThat(invocationException).hasCause(exception); assertThat(invocationException.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR); assertThat(invocationException.getErrorData()).isInstanceOf(CommonExceptionData.class); - assertThat(Json.encode(invocationException.getErrorData())) - .isEqualTo("{\"code\":\"SCB.50000000\",\"message\":\"Unexpected exception when processing none. msg\"}"); + + try { + assertThat(objectMapper.readTree(Json.encode(invocationException.getErrorData()))) + .isEqualTo(objectMapper.readTree("{\"code\":\"SCB.50000000\",\"message\":\"Unexpected exception when processing none. msg\"}")); + } catch (Exception e) { + throw new AssertionError("Error during JSON comparison: " + e.getMessage(), e); + } } static class ThrowExceptionWhenConvert implements ExceptionConverter { diff --git a/core/src/test/java/org/apache/servicecomb/core/executor/TestGroupExecutor.java b/core/src/test/java/org/apache/servicecomb/core/executor/TestGroupExecutor.java index d92ca3bb2ce..dd95105992b 100644 --- a/core/src/test/java/org/apache/servicecomb/core/executor/TestGroupExecutor.java +++ b/core/src/test/java/org/apache/servicecomb/core/executor/TestGroupExecutor.java @@ -111,7 +111,7 @@ public void maxThreads() { Assertions.assertEquals( "servicecomb.executor.default.thread-per-group is deprecated, recommended to use servicecomb.executor.default.maxThreads-per-group.", collector.getEvents().get(collector.getEvents().size() - 2).getMessage().getFormattedMessage()); - collector.teardown(); + collector.tearDown(); Mockito.when(environment.getProperty(KEY_MAX_THREADS, int.class, -1)).thenReturn(300); groupExecutor.initConfig(); @@ -127,7 +127,7 @@ public void adjustCoreThreads() { Assertions.assertEquals( "coreThreads is bigger than maxThreads, change from 25 to 10.", collector.getEvents().get(collector.getEvents().size() - 2).getMessage().getFormattedMessage()); - collector.teardown(); + collector.tearDown(); } @Test diff --git a/core/src/test/java/org/apache/servicecomb/core/filter/FilterChainTest.java b/core/src/test/java/org/apache/servicecomb/core/filter/FilterChainTest.java index 0903cc03c53..5a5dc1cb05e 100644 --- a/core/src/test/java/org/apache/servicecomb/core/filter/FilterChainTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/filter/FilterChainTest.java @@ -21,12 +21,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.catchThrowable; -import java.io.IOException; -import java.time.Duration; import java.util.List; import java.util.Vector; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -58,19 +55,35 @@ public class FilterChainTest { List msg = new Vector<>(); - Filter recordThreadFilter = (invocation, nextNode) -> { - msg.add(Thread.currentThread().getName()); - if (nextNode == null) { - return CompletableFuture.completedFuture(Response.ok(null)); + Filter recordThreadFilter = new AbstractFilter() { + @Override + public String getName() { + return "f2"; } - return nextNode.onFilter(invocation); + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + msg.add(Thread.currentThread().getName()); + if (nextNode == null) { + return CompletableFuture.completedFuture(Response.ok(null)); + } + + return nextNode.onFilter(invocation); + } }; Filter scheduler = new ScheduleFilter(); - Filter exceptionFilter = (invocation, nextNode) -> { - throw new IllegalStateException("e1"); + Filter exceptionFilter = new AbstractFilter() { + @Override + public String getName() { + return "f1"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + throw new IllegalStateException("e1"); + } }; @BeforeClass @@ -133,20 +146,4 @@ public void should_stop_chain_when_middle_filter_throw_exception() { .isInstanceOf(IllegalStateException.class) .hasMessage("e1"); } - - @Test - public void should_support_retry_logic() { - Filter exceptionFilter = (invocation, nextNode) -> { - throw new CompletionException(new IOException("net error")); - }; - SimpleRetryFilter retryFilter = new SimpleRetryFilter().setMaxRetry(3); - - CompletableFuture future = buildChain(retryFilter, recordThreadFilter, exceptionFilter) - .onFilter(invocation); - assertThat(future) - .failsWithin(Duration.ofSeconds(1)) - .withThrowableOfType(ExecutionException.class) - .withCauseExactlyInstanceOf(IOException.class) - .withMessage("java.io.IOException: net error"); - } } diff --git a/core/src/test/java/org/apache/servicecomb/core/filter/SimpleRetryFilter.java b/core/src/test/java/org/apache/servicecomb/core/filter/SimpleRetryFilter.java deleted file mode 100644 index 4eeb730260f..00000000000 --- a/core/src/test/java/org/apache/servicecomb/core/filter/SimpleRetryFilter.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.core.filter; - -import java.io.IOException; -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.exception.Exceptions; -import org.apache.servicecomb.swagger.invocation.InvocationType; -import org.apache.servicecomb.swagger.invocation.Response; - -public class SimpleRetryFilter implements ConsumerFilter { - protected int maxRetry = 3; - - @Override - public String getName() { - return "simple-retry"; - } - - @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { - return Filter.CONSUMER_LOAD_BALANCE_ORDER - 900; - } - - public SimpleRetryFilter setMaxRetry(int maxRetry) { - this.maxRetry = maxRetry; - return this; - } - - @Override - public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - return new RetrySession(invocation, nextNode).run(); - } - - protected Throwable unwrapException(Throwable throwable) { - return Exceptions.unwrapIncludeInvocationException(throwable); - } - - protected boolean isRetryException(Throwable throwable) { - return !(throwable instanceof IOException); - } - - class RetrySession { - Invocation invocation; - - FilterNode nextNode; - - int retryCount; - - CompletableFuture future = new CompletableFuture<>(); - - RetrySession(Invocation invocation, FilterNode nextNode) { - this.invocation = invocation; - this.nextNode = nextNode; - } - - CompletableFuture run() { - nextNode.onFilter(invocation) - .whenComplete(this::whenNextComplete); - return future; - } - - private void whenNextComplete(Response response, Throwable throwable) { - if (throwable == null) { - future.complete(response); - return; - } - - Throwable unwrapped = unwrapException(throwable); - if (isRetryException(unwrapped)) { - future.completeExceptionally(throwable); - return; - } - - retryCount++; - if (retryCount >= maxRetry) { - future.completeExceptionally(throwable); - return; - } - - run(); - } - } -} diff --git a/core/src/test/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilterTest.java b/core/src/test/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilterTest.java index f80490978b3..4afed2d7dde 100644 --- a/core/src/test/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilterTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/filter/impl/ParameterValidatorFilterTest.java @@ -42,7 +42,8 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import org.springframework.core.env.Environment; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MutablePropertySources; import com.fasterxml.jackson.annotation.JsonProperty; @@ -96,14 +97,16 @@ public void op(@NotNull(message = "not null") String query, @Valid Model model) @Mocked SwaggerProducerOperation operation; - Environment environment; + ConfigurableEnvironment environment; @Before public void setUp() throws Exception { - environment = Mockito.mock(Environment.class); + environment = Mockito.mock(ConfigurableEnvironment.class); SCBEngine engine = SCBBootstrap.createSCBEngineForTest(environment); + MutablePropertySources sources = new MutablePropertySources(); Mockito.when(environment.getProperty(ENABLE_EL, boolean.class, false)).thenReturn(false); + Mockito.when(environment.getPropertySources()).thenReturn(sources); filter.setEnvironment(environment); filter.afterPropertiesSet(); engine.setEnvironment(environment); diff --git a/core/src/test/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilterTest.java b/core/src/test/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilterTest.java index 4a072a2bad3..dd46b17d28b 100644 --- a/core/src/test/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilterTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/filter/impl/ProducerOperationFilterTest.java @@ -83,9 +83,6 @@ public void should_record_invocation_trace_time() throws NoSuchMethodException { invocation.onBusinessMethodStart(); times = 1; - invocation.onBusinessMethodFinish(); - times = 1; - invocation.onBusinessFinish(); times = 1; } @@ -139,11 +136,11 @@ public void should_catch_sync_business_exception() throws NoSuchMethodException CompletableFuture future = filter.onFilter(invocation, FilterNode.EMPTY); assertThat(future) - .failsWithin(Duration.ofSeconds(1)) - .withThrowableOfType(ExecutionException.class) - .withCauseExactlyInstanceOf(RuntimeExceptionWithoutStackTrace.class) - .withMessage("org.apache.servicecomb.foundation.test.scaffolding.exception" + - ".RuntimeExceptionWithoutStackTrace: syncException"); + .failsWithin(Duration.ofSeconds(1)) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(RuntimeExceptionWithoutStackTrace.class) + .withMessage("org.apache.servicecomb.foundation.test.scaffolding.exception" + + ".RuntimeExceptionWithoutStackTrace: syncException"); } @Test @@ -171,11 +168,11 @@ public void should_catch_async_business_exception() throws NoSuchMethodException CompletableFuture future = filter.onFilter(invocation, FilterNode.EMPTY); assertThat(future) - .failsWithin(Duration.ofSeconds(1)) - .withThrowableOfType(ExecutionException.class) - .withCauseExactlyInstanceOf(RuntimeExceptionWithoutStackTrace.class) - .withMessage("org.apache.servicecomb.foundation.test.scaffolding" + - ".exception.RuntimeExceptionWithoutStackTrace: asyncException"); + .failsWithin(Duration.ofSeconds(1)) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(RuntimeExceptionWithoutStackTrace.class) + .withMessage("org.apache.servicecomb.foundation.test.scaffolding" + + ".exception.RuntimeExceptionWithoutStackTrace: asyncException"); } @Test @@ -190,10 +187,9 @@ public void should_unify_IllegalArgumentException_message_when_convert_exception CompletableFuture future = filter.onFilter(invocation, FilterNode.EMPTY); assertThat(future) - .failsWithin(Duration.ofSeconds(1)) - .withThrowableOfType(ExecutionException.class) - .withCauseExactlyInstanceOf(IllegalArgumentException.class) - .withMessage("java.lang.IllegalArgumentException: wrong number of arguments"); + .failsWithin(Duration.ofSeconds(1)) + .withThrowableOfType(ExecutionException.class) + .withCauseExactlyInstanceOf(IllegalArgumentException.class); InvocationException throwable = Exceptions .convert(invocation, catchThrowable(future::get), INTERNAL_SERVER_ERROR); diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java b/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java index 1cc4b4db84a..e2e3b2c421d 100644 --- a/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/ProducerInvocationFlowTest.java @@ -20,9 +20,12 @@ import static java.util.concurrent.CompletableFuture.completedFuture; import static org.assertj.core.api.Assertions.assertThat; +import java.util.concurrent.CompletableFuture; + import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; @@ -47,7 +50,7 @@ protected Invocation sendCreateInvocationException(Throwable throwable) { } @Override - protected void sendResponse(Invocation invocation, Response response) { + protected void endResponse(Invocation invocation, Response response) { sendInvocation = invocation; } } @@ -79,7 +82,7 @@ public void should_send_exception_response_when_failed_to_create_invocation() { private void mockFilterChain() { new Expectations() { { - microserviceMeta.getFilterChain(); + microserviceMeta.getProviderFilterChain(); result = filterNode; } }; @@ -94,7 +97,7 @@ public void should_start_invocation_when_succeed_to_create_invocation() { new Verifications() { { - invocation.onStart((HttpServletRequestEx) any, anyLong); + invocation.onStart((HttpServletRequestEx) any); times = 1; } }; @@ -126,8 +129,16 @@ public void should_finish_invocation_when_invocation_success() { } private void mockInvocationFailed() { - filterNode = new FilterNode((_invocation, _node) -> { - throw new RuntimeExceptionWithoutStackTrace(); + filterNode = new FilterNode(new AbstractFilter() { + @Override + public String getName() { + return "test"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + throw new RuntimeExceptionWithoutStackTrace(); + } }); mockFilterChain(); } diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/TestInvocationStageTrace.java b/core/src/test/java/org/apache/servicecomb/core/invocation/TestInvocationStageTrace.java index 5d81bfe06ca..5b9c7040caa 100644 --- a/core/src/test/java/org/apache/servicecomb/core/invocation/TestInvocationStageTrace.java +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/TestInvocationStageTrace.java @@ -26,11 +26,11 @@ import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; -import org.junit.jupiter.api.Assertions; public class TestInvocationStageTrace { Invocation invocation; @@ -68,51 +68,34 @@ public void consumer() { invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, args); stageTrace = new InvocationStageTrace(invocation); - stageTrace.start(1); + stageTrace.startCreateInvocation(1); nanoTime = 2; - stageTrace.startHandlersRequest(); + stageTrace.finishCreateInvocation(); + stageTrace.startConsumerConnection(); nanoTime = 3; - stageTrace.startClientFiltersRequest(); + stageTrace.finishConsumerConnection(); + stageTrace.startConsumerEncodeRequest(); nanoTime = 4; - stageTrace.startGetConnection(); - stageTrace.startSend(); + stageTrace.finishConsumerEncodeRequest(); + stageTrace.startConsumerSendRequest(); nanoTime = 5; - stageTrace.finishGetConnection(); - stageTrace.finishWriteToBuffer(6); + stageTrace.finishConsumerSendRequest(); + stageTrace.startWaitResponse(); + nanoTime = 6; + stageTrace.finishWaitResponse(); + stageTrace.startConsumerDecodeResponse(); nanoTime = 7; - stageTrace.finishReceiveResponse(); - nanoTime = 8; - stageTrace.startClientFiltersResponse(); - nanoTime = 9; - stageTrace.finishClientFiltersResponse(); - nanoTime = 10; - stageTrace.finishHandlersResponse(); - nanoTime = 11; + stageTrace.finishConsumerDecodeResponse(); + stageTrace.finish(); - Assertions.assertEquals(1, stageTrace.getStart()); - Assertions.assertEquals(2, stageTrace.getStartHandlersRequest()); - Assertions.assertEquals(3, stageTrace.getStartClientFiltersRequest()); - Assertions.assertEquals(4, stageTrace.getStartSend()); - Assertions.assertEquals(5, stageTrace.getFinishGetConnection()); - Assertions.assertEquals(6, stageTrace.getFinishWriteToBuffer()); - Assertions.assertEquals(7, stageTrace.getFinishReceiveResponse()); - Assertions.assertEquals(8, stageTrace.getStartClientFiltersResponse()); - Assertions.assertEquals(9, stageTrace.getFinishClientFiltersResponse()); - Assertions.assertEquals(10, stageTrace.getFinishHandlersResponse()); - Assertions.assertEquals(11, stageTrace.getFinish()); - - Assertions.assertEquals(1f, stageTrace.calcInvocationPrepareTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcHandlersRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcClientFiltersRequestTime(), 0.1f); - Assertions.assertEquals(2f, stageTrace.calcSendRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcGetConnectionTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcWriteToBufferTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcReceiveResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcWakeConsumer(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcClientFiltersResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcHandlersResponseTime(), 0.1f); - Assertions.assertEquals(10f, stageTrace.calcTotalTime(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcPrepare(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConnection(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConsumerEncodeRequest(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConsumerSendRequest(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcWait(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConsumerDecodeResponse(), 0.1f); + Assertions.assertEquals(6f, stageTrace.calcTotal(), 0.1f); } @Test @@ -120,116 +103,81 @@ public void producer() { invocation = new Invocation(endpoint, operationMeta, args); stageTrace = new InvocationStageTrace(invocation); - stageTrace.start(1); + stageTrace.startCreateInvocation(1); nanoTime = 2; - stageTrace.startSchedule(); + stageTrace.finishCreateInvocation(); + stageTrace.startProviderDecodeRequest(); nanoTime = 3; - stageTrace.startExecution(); + stageTrace.finishProviderDecodeRequest(); + stageTrace.startProviderQueue(); nanoTime = 4; - stageTrace.startServerFiltersRequest(); + stageTrace.finishProviderQueue(); + stageTrace.startBusinessExecute(); nanoTime = 5; - stageTrace.startHandlersRequest(); + stageTrace.finishBusinessExecute(); + stageTrace.startProviderEncodeResponse(); nanoTime = 6; - stageTrace.startBusinessMethod(); + stageTrace.finishProviderEncodeResponse(); + stageTrace.startProviderSendResponse(); nanoTime = 7; - stageTrace.finishBusiness(); - nanoTime = 8; - stageTrace.finishHandlersResponse(); - nanoTime = 9; - stageTrace.finishServerFiltersResponse(); - nanoTime = 10; + stageTrace.finishProviderSendResponse(); + stageTrace.finish(); - Assertions.assertEquals(1, stageTrace.getStart()); - Assertions.assertEquals(2, stageTrace.getStartSchedule()); - Assertions.assertEquals(3, stageTrace.getStartExecution()); - Assertions.assertEquals(4, stageTrace.getStartServerFiltersRequest()); - Assertions.assertEquals(5, stageTrace.getStartHandlersRequest()); - Assertions.assertEquals(6, stageTrace.getStartBusinessMethod()); - Assertions.assertEquals(7, stageTrace.getFinishBusiness()); - Assertions.assertEquals(8, stageTrace.getFinishHandlersResponse()); - Assertions.assertEquals(9, stageTrace.getFinishServerFiltersResponse()); - Assertions.assertEquals(10, stageTrace.getFinish()); - - Assertions.assertEquals(1f, stageTrace.calcInvocationPrepareTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcThreadPoolQueueTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcServerFiltersRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcHandlersRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcBusinessTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcHandlersResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcServerFiltersResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcSendResponseTime(), 0.1f); - Assertions.assertEquals(9f, stageTrace.calcTotalTime(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcPrepare(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcQueue(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcProviderDecodeRequest(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcProviderEncodeResponse(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcBusinessExecute(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcProviderSendResponse(), 0.1f); + Assertions.assertEquals(6f, stageTrace.calcTotal(), 0.1f); } @Test public void edge() { invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, args); stageTrace = new InvocationStageTrace(invocation); - invocation.setEdge(true); + invocation.setEdge(); - stageTrace.start(1); + stageTrace.startCreateInvocation(1); nanoTime = 2; - stageTrace.startSchedule(); + stageTrace.finishCreateInvocation(); + stageTrace.startProviderDecodeRequest(); nanoTime = 3; - stageTrace.startExecution(); + stageTrace.finishProviderDecodeRequest(); + stageTrace.startConsumerConnection(); nanoTime = 4; - stageTrace.startServerFiltersRequest(); + stageTrace.finishConsumerConnection(); + stageTrace.startConsumerEncodeRequest(); nanoTime = 5; - stageTrace.startHandlersRequest(); + stageTrace.finishConsumerEncodeRequest(); + stageTrace.startConsumerSendRequest(); nanoTime = 6; - stageTrace.startClientFiltersRequest(); + stageTrace.finishConsumerSendRequest(); + stageTrace.startWaitResponse(); nanoTime = 7; - stageTrace.startGetConnection(); - stageTrace.startSend(); + stageTrace.finishWaitResponse(); + stageTrace.startConsumerDecodeResponse(); nanoTime = 8; - stageTrace.finishGetConnection(); - stageTrace.finishWriteToBuffer(9); + stageTrace.finishConsumerDecodeResponse(); + stageTrace.startProviderEncodeResponse(); nanoTime = 10; - stageTrace.finishReceiveResponse(); + stageTrace.finishProviderEncodeResponse(); + stageTrace.startProviderSendResponse(); nanoTime = 11; - stageTrace.startClientFiltersResponse(); - nanoTime = 12; - stageTrace.finishClientFiltersResponse(); - nanoTime = 13; - stageTrace.finishHandlersResponse(); - nanoTime = 14; - stageTrace.finishServerFiltersResponse(); - nanoTime = 15; + stageTrace.finishProviderSendResponse(); stageTrace.finish(); - Assertions.assertEquals(1, stageTrace.getStart()); - Assertions.assertEquals(2, stageTrace.getStartSchedule()); - Assertions.assertEquals(3, stageTrace.getStartExecution()); - Assertions.assertEquals(4, stageTrace.getStartServerFiltersRequest()); - Assertions.assertEquals(5, stageTrace.getStartHandlersRequest()); - Assertions.assertEquals(6, stageTrace.getStartClientFiltersRequest()); - Assertions.assertEquals(7, stageTrace.getStartSend()); - Assertions.assertEquals(8, stageTrace.getFinishGetConnection()); - Assertions.assertEquals(9, stageTrace.getFinishWriteToBuffer()); - Assertions.assertEquals(10, stageTrace.getFinishReceiveResponse()); - Assertions.assertEquals(11, stageTrace.getStartClientFiltersResponse()); - Assertions.assertEquals(12, stageTrace.getFinishClientFiltersResponse()); - Assertions.assertEquals(13, stageTrace.getFinishHandlersResponse()); - Assertions.assertEquals(14, stageTrace.getFinishServerFiltersResponse()); - Assertions.assertEquals(15, stageTrace.getFinish()); - - Assertions.assertEquals(1f, stageTrace.calcInvocationPrepareTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcThreadPoolQueueTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcServerFiltersRequestTime(), 0.1f); - - Assertions.assertEquals(1f, stageTrace.calcHandlersRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcClientFiltersRequestTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcGetConnectionTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcWriteToBufferTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcReceiveResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcWakeConsumer(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcClientFiltersResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcHandlersResponseTime(), 0.1f); - - Assertions.assertEquals(1f, stageTrace.calcServerFiltersResponseTime(), 0.1f); - Assertions.assertEquals(1f, stageTrace.calcSendResponseTime(), 0.1f); - - Assertions.assertEquals(14f, stageTrace.calcTotalTime(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcPrepare(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConnection(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConsumerEncodeRequest(), 0.1f); + + Assertions.assertEquals(1f, stageTrace.calcConsumerSendRequest(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcWait(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcConsumerDecodeResponse(), 0.1f); + Assertions.assertEquals(2f, stageTrace.calcProviderEncodeResponse(), 0.1f); + Assertions.assertEquals(1f, stageTrace.calcProviderSendResponse(), 0.1f); + + Assertions.assertEquals(10f, stageTrace.calcTotal(), 0.1f); } } diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointTest.java b/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointTest.java index 44b63c837a6..b3c0733bb48 100644 --- a/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/endpoint/EndpointTest.java @@ -26,7 +26,6 @@ import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.Transport; import org.apache.servicecomb.foundation.common.Holder; -import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.engine.SwaggerConsumer; import org.apache.servicecomb.swagger.engine.SwaggerConsumerOperation; import org.apache.servicecomb.swagger.engine.SwaggerEnvironment; @@ -44,27 +43,12 @@ public interface TestSchema { @Test void should_ignore_endpoint_when_generate_swagger() { SwaggerGenerator generator = SwaggerGenerator.create(TestSchema.class); - generator.getSwaggerGeneratorFeature() - .setExtJavaInterfaceInVendor(false) - .setExtJavaClassInVendor(false); OpenAPI swagger = generator.generate(); - assertThat(SwaggerUtils.swaggerToString(swagger)) - .isEqualTo("openapi: 3.0.1\n" - + "info:\n" - + " title: swagger definition for org.apache.servicecomb.core.invocation.endpoint.EndpointTest$TestSchema\n" - + " version: 1.0.0\n" - + "servers:\n" - + "- url: /TestSchema\n" - + "paths:\n" - + " /say:\n" - + " post:\n" - + " operationId: say\n" - + " responses:\n" - + " \"200\":\n" - + " description: response of 200\n" - + "components: {}\n" - + ""); + assertThat(swagger.getInfo().getTitle()).contains("EndpointTest$TestSchema"); + assertThat(swagger.getPaths()).containsKey("/say"); + assertThat(swagger.getPaths().get("/say").getPost().getOperationId()).isEqualTo("say"); + assertThat(swagger.getPaths().get("/say").getPost().getResponses()).containsKey("200"); } @Test diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategyTest.java b/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategyTest.java index 9481fd19e38..58de7e9d2a0 100644 --- a/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategyTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/PassingTimeStrategyTest.java @@ -32,17 +32,6 @@ class PassingTimeStrategyTest { PassingTimeStrategy strategy = new PassingTimeStrategy(); - @Test - void should_init_when_start_as_first_chain_node() { - Invocation invocation = new Invocation(); - invocation.getInvocationStageTrace().setStartTimeMillis(10); - - strategy.start(invocation); - - assertThat(invocation.getContext(CHAIN_START_TIME)).isEqualTo("10"); - assertThat(invocation.getLocalContext(CHAIN_START_TIME)).isEqualTo(10L); - } - @Test void should_init_when_start_as_first_node_of_a_process_but_not_first_of_a_chain() { Invocation invocation = new Invocation(); diff --git a/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategyTest.java b/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategyTest.java index 07d84e5b3e4..05d744e15db 100644 --- a/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategyTest.java +++ b/core/src/test/java/org/apache/servicecomb/core/invocation/timeout/ProcessingTimeStrategyTest.java @@ -31,17 +31,6 @@ class ProcessingTimeStrategyTest { ProcessingTimeStrategy strategy = new ProcessingTimeStrategy(); - @Test - void should_init_when_start_as_first_chain_node() { - Invocation invocation = new Invocation(); - invocation.getInvocationStageTrace().start(10); - - strategy.start(invocation); - - assertThat(invocation.getLocalContext(CHAIN_START_TIME)).isEqualTo(10L); - assertThat(invocation.getLocalContext(CHAIN_PROCESSING)).isEqualTo(0L); - } - @Test void should_do_nothing_when_not_first_node_of_a_process() { Invocation invocation = new Invocation(); diff --git a/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java b/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java index 9eba671f305..3b3e56092c3 100644 --- a/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java +++ b/core/src/test/java/org/apache/servicecomb/core/provider/consumer/TestInvokerUtils.java @@ -17,16 +17,11 @@ package org.apache.servicecomb.core.provider.consumer; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class TestInvokerUtils { @Test public void testRetryInvocation503() { - InvocationException root = new InvocationException(503, "Service Unavailable", "Error"); - boolean canRetry = InvokerUtils.canRetryForStatusCode(Response.failResp(root)); - Assertions.assertTrue(canRetry); + } } diff --git a/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java b/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java index 934381fe455..65c44b12560 100644 --- a/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java +++ b/core/src/test/java/org/apache/servicecomb/core/transport/TestAbstractTransport.java @@ -32,7 +32,7 @@ import org.mockito.Mockito; import org.springframework.core.env.Environment; -import io.vertx.core.file.impl.FileResolverImpl; +import io.vertx.core.impl.SysProps; public class TestAbstractTransport { Environment environment = Mockito.mock(Environment.class); @@ -56,7 +56,7 @@ public void setUp() { Mockito.when(environment.getProperty("servicecomb.my.publishPort", int.class, 0)).thenReturn(0); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); } diff --git a/core/src/test/resources/microservice.yaml b/core/src/test/resources/microservice.yaml index 0baa251fd13..b17313cd44c 100644 --- a/core/src/test/resources/microservice.yaml +++ b/core/src/test/resources/microservice.yaml @@ -30,4 +30,4 @@ servicecomb: zq: - tlist: a - tlist: b - - tlist: 1 \ No newline at end of file + - tlist: 1 diff --git a/core/src/test/resources/test/test/microservice.yaml b/core/src/test/resources/test/test/microservice.yaml index a82dcd3f036..7ec47b07b63 100644 --- a/core/src/test/resources/test/test/microservice.yaml +++ b/core/src/test/resources/test/test/microservice.yaml @@ -18,7 +18,7 @@ service_description: name: default version: 0.0.1 - propertyExtentedClass: org.apache.servicecomb.serviceregistry.MicroServicePropertyExtendedStub + propertyExtendedClass: org.apache.servicecomb.serviceregistry.MicroServicePropertyExtendedStub servicecomb: service: registry: diff --git a/coverage-reports/pom.xml b/coverage-reports/pom.xml index ee32cc38a63..97f795fcbf5 100644 --- a/coverage-reports/pom.xml +++ b/coverage-reports/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 diff --git a/demo/README.md b/demo/README.md index c646b9abd9f..18153ce7a57 100644 --- a/demo/README.md +++ b/demo/README.md @@ -1,9 +1,9 @@ -This module contains integrated test cases. Running these test cases, should include profile +This module contains integrated test cases. Running these test cases, should include profile ```shell script -Pdocker ``` [java-chassis-samples][java-chassis-samples] project provides a lot of samples. - + [java-chassis-samples]: https://github.com/apache/servicecomb-samples/tree/master/java-chassis-samples diff --git a/demo/demo-consul/README.md b/demo/demo-consul/README.md new file mode 100644 index 00000000000..872a1d2e93a --- /dev/null +++ b/demo/demo-consul/README.md @@ -0,0 +1,5 @@ +# Notice + +This integration tests is designed for Consul registry and configuration. And extra test cases include: + +* Test cases related to SpringMVC annotations that demo-springmvc can not cover. diff --git a/demo/demo-consul/consumer/pom.xml b/demo/demo-consul/consumer/pom.xml new file mode 100644 index 00000000000..eccc75e41b1 --- /dev/null +++ b/demo/demo-consul/consumer/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-consul + 3.4.0-SNAPSHOT + + + consul-consumer + Java Chassis::Demo::Consul::CONSUMER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-consul + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java new file mode 100644 index 00000000000..f71738a9ed6 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; +import io.vertx.core.http.WebSocket; + +@RestSchema(schemaId = "ClientWebsocketController") +@RequestMapping(path = "/ws") +public class ClientWebsocketController { + interface ProviderService { + WebSocket websocket(); + } + + @RpcReference(schemaId = "WebsocketController", microserviceName = "provider") + private ProviderService providerService; + + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + WebSocket providerWebSocket = providerService.websocket(); + providerWebSocket.closeHandler(v -> serverWebsocket.close()); + providerWebSocket.textMessageHandler(m -> { + serverWebsocket.writeTextMessage(m); + }); + serverWebsocket.textMessageHandler(m -> { + providerWebSocket.writeTextMessage(m); + }); + } +} diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsulConsumerApplication.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsulConsumerApplication.java new file mode 100644 index 00000000000..da243836198 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsulConsumerApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ConsulConsumerApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsulConsumerApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java new file mode 100644 index 00000000000..e66fb2cb3d0 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +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.ResponseBody; + +@RestSchema(schemaId = "ConsumerController") +@RequestMapping(path = "/") +public class ConsumerController { + @RpcReference(schemaId = "ProviderController", microserviceName = "provider") + private ProviderService providerService; + + // consumer service which delegate the implementation to provider service. + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return providerService.sayHello(name); + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return providerService.getConfig(key); + } + + @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public User testContentType(@RequestBody User user) { + return providerService.testContentType(user); + } +} diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java new file mode 100644 index 00000000000..f0e894d6745 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") + private IHeaderParamWithListSchemaSpringMvc provider; + + @Override + public String headerListDefault(List headerList) { + return provider.headerListDefault(headerList); + } + + @Override + public String headerListCSV(List headerList) { + return provider.headerListCSV(headerList); + } + + @Override + public String headerListMULTI(List headerList) { + return provider.headerListMULTI(headerList); + } + + @Override + public String headerListSSV(List headerList) { + return provider.headerListSSV(headerList); + } + + @Override + public String headerListPIPES(List headerList) { + return provider.headerListPIPES(headerList); + } +} diff --git a/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java new file mode 100644 index 00000000000..aa169801cf7 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +public class ConsumerReactiveStreamController { + interface ProviderReactiveStreamController { + Publisher sseString(); + + Publisher sseModel(); + } + + @RpcReference(microserviceName = "provider", schemaId = "ReactiveStreamController") + ProviderReactiveStreamController controller; + + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseString() { + return controller.sseString(); + } + + @GetMapping("/sseModel") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseModel() { + return controller.sseModel(); + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/AbstractConsumerProvider.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java similarity index 78% rename from core/src/main/java/org/apache/servicecomb/core/provider/consumer/AbstractConsumerProvider.java rename to demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java index c9093c330dc..2899546c752 100644 --- a/core/src/main/java/org/apache/servicecomb/core/provider/consumer/AbstractConsumerProvider.java +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java @@ -15,12 +15,12 @@ * limitations under the License. */ -package org.apache.servicecomb.core.provider.consumer; +package org.apache.servicecomb.samples; -import org.apache.servicecomb.core.ConsumerProvider; +public interface ProviderService { + String sayHello(String name); -public abstract class AbstractConsumerProvider implements ConsumerProvider { - @Override - public void init() { - } + String getConfig(String key); + + User testContentType(User user); } diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestConsumerProvider.java b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/User.java similarity index 74% rename from providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestConsumerProvider.java rename to demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/User.java index 31877baf45e..ff421930ef2 100644 --- a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/RestConsumerProvider.java +++ b/demo/demo-consul/consumer/src/main/java/org/apache/servicecomb/samples/User.java @@ -14,15 +14,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.servicecomb.samples; -package org.apache.servicecomb.provider.rest.common; -import org.apache.servicecomb.common.rest.RestConst; -import org.apache.servicecomb.core.provider.consumer.AbstractConsumerProvider; +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } -public class RestConsumerProvider extends AbstractConsumerProvider { - @Override public String getName() { - return RestConst.REST; + return name; + } + + public void setName(String name) { + this.name = name; } } diff --git a/demo/demo-consul/consumer/src/main/resources/application.yml b/demo/demo-consul/consumer/src/main/resources/application.yml new file mode 100644 index 00000000000..ef0d45c1dcb --- /dev/null +++ b/demo/demo-consul/consumer/src/main/resources/application.yml @@ -0,0 +1,40 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: demo-consul + version: 0.0.1 + name: consumer + properties: + group: green + + registry: + consul: + enabled: true + host: 127.0.0.1 + scheme: http + discovery: + enabled: true + acl-token: '' + watch-seconds: 8 + + rest: + address: 0.0.0.0:9092?websocketEnabled=true + server: + websocket-prefix: /ws diff --git a/demo/demo-consul/consumer/src/main/resources/log4j2.xml b/demo/demo-consul/consumer/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-consul/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-consul/gateway/pom.xml b/demo/demo-consul/gateway/pom.xml new file mode 100644 index 00000000000..1dad3860e7f --- /dev/null +++ b/demo/demo-consul/gateway/pom.xml @@ -0,0 +1,107 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-consul + 3.4.0-SNAPSHOT + + + consul-gateway + Java Chassis::Demo::Consul::GATEWAY + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + edge-core + + + org.apache.servicecomb + registry-consul + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-consul/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java b/demo/demo-consul/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java new file mode 100644 index 00000000000..7d58caafd9d --- /dev/null +++ b/demo/demo-consul/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class GatewayApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-consul/gateway/src/main/resources/application.yml b/demo/demo-consul/gateway/src/main/resources/application.yml new file mode 100644 index 00000000000..9855a87a1de --- /dev/null +++ b/demo/demo-consul/gateway/src/main/resources/application.yml @@ -0,0 +1,60 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: demo-consul + version: 0.0.1 + name: gateway + + registry: + consul: + enabled: true + host: 127.0.0.1 + scheme: http + discovery: + enabled: true + acl-token: '' + watch-seconds: 8 + + rest: + address: 0.0.0.0:9090?websocketEnabled=true + server: + websocket-prefix: /ws + + http: + dispatcher: + edge: + default: + enabled: false + url: + enabled: true + pattern: /(.*) + mappings: + consumer: + prefixSegmentCount: 0 + path: "/.*" + microserviceName: consumer + versionRule: 0.0.0+ + websocket: + mappings: + consumer: + prefixSegmentCount: 0 + path: "/ws/.*" + microserviceName: consumer + versionRule: 0.0.0+ diff --git a/demo/demo-consul/gateway/src/main/resources/log4j2.xml b/demo/demo-consul/gateway/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-consul/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-consul/pom.xml b/demo/demo-consul/pom.xml new file mode 100644 index 00000000000..aaa8f3b91ce --- /dev/null +++ b/demo/demo-consul/pom.xml @@ -0,0 +1,62 @@ + + + + + 4.0.0 + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + + demo-consul + Java Chassis::Demo::Consul + pom + + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + solution-basic + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + + + + + provider + consumer + gateway + test-client + + diff --git a/demo/demo-consul/provider/pom.xml b/demo/demo-consul/provider/pom.xml new file mode 100644 index 00000000000..0986f9f4342 --- /dev/null +++ b/demo/demo-consul/provider/pom.xml @@ -0,0 +1,114 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-consul + 3.4.0-SNAPSHOT + + + consul-provider + Java Chassis::Demo::Consul::PROVIDER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-consul + + + org.apache.servicecomb + config-consul + + + + io.reactivex.rxjava3 + rxjava + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ConsulProviderApplication.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ConsulProviderApplication.java new file mode 100644 index 00000000000..b84da167943 --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ConsulProviderApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ConsulProviderApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsulProviderApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java new file mode 100644 index 00000000000..8a773f91c72 --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class HeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @Override + public String headerListDefault(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListCSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListMULTI(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListSSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListPIPES(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } +} diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java new file mode 100644 index 00000000000..677c20ed106 --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +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.ResponseBody; + +@RestSchema(schemaId = "ProviderController") +@RequestMapping(path = "/") +public class ProviderController implements InitializingBean { + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + // a very simple service to echo the request parameter + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { +// return "Hello " + environment.getProperty("servicecomb.rest.address"); + return "Hello " + name; + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return environment.getProperty(key); + } + + @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public User testContentType(@RequestBody User user) { + return user; + } + + @Override + public void afterPropertiesSet() throws Exception { + } +} diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java new file mode 100644 index 00000000000..8108d15fd46 --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.reactivex.rxjava3.core.Flowable; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +@Transport(name = CoreConst.RESTFUL) +public class ReactiveStreamController { + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + public Publisher sseString() { + return Flowable.fromArray("a", "b", "c"); + } + + @GetMapping("/sseModel") + public Publisher sseModel() { + return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) + .map(item -> new Model("jack", item.intValue())); + } +} diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ca12402d672 --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/User.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java new file mode 100644 index 00000000000..6381ef1d54c --- /dev/null +++ b/demo/demo-consul/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; + +@RestSchema(schemaId = "WebsocketController") +@RequestMapping(path = "/ws") +public class WebsocketController { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + AtomicInteger receiveCount = new AtomicInteger(0); + CountDownLatch startSend = new CountDownLatch(1); + serverWebsocket.textMessageHandler(s -> { + if ("start".equals(s)) { + startSend.countDown(); + serverWebsocket.writeTextMessage("started"); + return; + } + receiveCount.getAndIncrement(); + }); + serverWebsocket.closeHandler((v) -> System.out.println("closed")); + + new Thread(() -> { + try { + startSend.await(30, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + serverWebsocket.writeTextMessage("hello"); + + for (int i = 0; i < 5; i++) { + serverWebsocket.writeTextMessage("hello " + i); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + serverWebsocket.writeTextMessage("total " + receiveCount.get()); + serverWebsocket.close(); + }).start(); + } +} diff --git a/demo/demo-consul/provider/src/main/resources/application.yml b/demo/demo-consul/provider/src/main/resources/application.yml new file mode 100644 index 00000000000..31bbfa5d4c7 --- /dev/null +++ b/demo/demo-consul/provider/src/main/resources/application.yml @@ -0,0 +1,67 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +# spring boot configurations +servicecomb: + service: + application: demo-consul + version: 0.0.1 + name: provider + properties: + group: green + + registry: + consul: + enabled: true + host: 127.0.0.1 + scheme: http + discovery: + enabled: true + acl-token: '' + watch-seconds: 8 + + config: + consul: + enabled: true + host: 127.0.0.1 + scheme: http + acl-token: '' + watch-seconds: 8 + + rest: + address: 0.0.0.0:9094?websocketEnabled=true + server: + websocket-prefix: /ws + + cors: + enabled: true + origin: "*" + allowCredentials: false + allowedMethod: "*" + maxAge: 3600 + + +key1: 1 +key2: 3 +key3: 5 + +test1: env +test2: applition +test3: service +test4: version +test5: tag diff --git a/demo/demo-consul/provider/src/main/resources/log4j2.xml b/demo/demo-consul/provider/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-consul/provider/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-consul/test-client/pom.xml b/demo/demo-consul/test-client/pom.xml new file mode 100644 index 00000000000..791ac474886 --- /dev/null +++ b/demo/demo-consul/test-client/pom.xml @@ -0,0 +1,220 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-consul + 3.4.0-SNAPSHOT + + + consul-test-client + Java Chassis::Demo::Consul::TEST-CLIENT + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + registry-local + + + org.kiwiproject + consul-client + + + com.google.code.gson + gson + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + hashicorp/consul:1.21 + consul + + alias + + consulServer + + + 8500 + + + + + + 8500:8500 + + + yes + + + + + consul-provider:${project.version} + consul-provider + + alias + + + -Dservicecomb.registry.consul.enabled=true + -Dservicecomb.registry.consul.scheme=http + -Dservicecomb.registry.consul.host=consul + -Dservicecomb.registry.consul.port=8500 + -Dservicecomb.config.consul.enabled=true + -Dservicecomb.config.consul.scheme=http + -Dservicecomb.config.consul.host=consul + -Dservicecomb.config.consul.port=8500 + + /maven/maven/consul-provider-${project.version}.jar + + + consul:consul + + + ServiceComb is ready + + + 9094 + + + + + + 9094:9094 + + + + + consul-consumer:${project.version} + consul-consumer + + alias + + + -Dservicecomb.registry.consul.enabled=true + -Dservicecomb.registry.consul.scheme=http + -Dservicecomb.registry.consul.host=consul + -Dservicecomb.registry.consul.port=8500 + + /maven/maven/consul-consumer-${project.version}.jar + + + consul:consul + + + ServiceComb is ready + + + 9092 + + + + + + 9092:9092 + + + + + consul-gateway:${project.version} + consul-gateway + + alias + + + -Dservicecomb.registry.consul.enabled=true + -Dservicecomb.registry.consul.scheme=http + -Dservicecomb.registry.consul.host=consul + -Dservicecomb.registry.consul.port=8500 + + /maven/maven/consul-gateway-${project.version}.jar + + + consul:consul + + + ServiceComb is ready + + + 9090 + + + + + + 9090:9090 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/Config.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/Config.java new file mode 100644 index 00000000000..2e9105cdd4a --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/Config.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +public interface Config { + String GATEWAY_URL = "http://localhost:9090"; +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ConsulConfigIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ConsulConfigIT.java new file mode 100644 index 00000000000..1a7be4b874d --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ConsulConfigIT.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import com.google.common.net.HostAndPort; +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.ConditionWaiter.SleepUtil; +import org.kiwiproject.consul.Consul; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +import java.util.concurrent.TimeUnit; + +@Component +public class ConsulConfigIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulConfigIT.class); + + @Override + public void testRestTransport() throws Exception { + + testEnvironment(); + testApplication(); + testService(); + testVersion(); + testTag(); + testOverride(); + } + + private void testOverride() { + + putValue("/servicecomb/config/environment/production/application2.properties", + "testValue=t1"); + putValue("/servicecomb/config/application/production/demo-consul/application2.properties", + "testValue=t2"); + testGetConfig("testValue", "t2"); + putValue("/servicecomb/config/service/production/demo-consul/provider/application2.properties", + "testValue=t3"); + testGetConfig("testValue", "t3"); + putValue("/servicecomb/config/version/production/demo-consul/provider/0.0.1/application2.properties", + "testValue=t4"); + testGetConfig("testValue", "t4"); + putValue("/servicecomb/config/tag/production/demo-consul/provider/0.0.1/tag1/application2.properties", + "testValue=t5"); + testGetConfig("testValue", "t5"); + } + + private void testEnvironment() { + + putValue("/servicecomb/config/environment/production/application.properties", + "test1=env"); + putValue("/servicecomb/config/environment/production/application.properties", + "test1=env1"); + + testGetConfig("test1", "env1"); + } + + + private void testApplication() { + + putValue("/servicecomb/config/application/production/demo-consul/application.properties", + "test2=applition"); + putValue("/servicecomb/config/application/production/demo-consul/application.properties", + "test2=applition2"); + testGetConfig("test2", "applition2"); + } + + private void testService() { + + putValue("/servicecomb/config/service/production/demo-consul/provider/application.properties", + "test3=service"); + putValue("/servicecomb/config/service/production/demo-consul/provider/application.properties", + "test3=service3"); + testGetConfig("test3", "service3"); + } + + private void testVersion() { + + putValue("/servicecomb/config/version/production/demo-consul/provider/0.0.1/application.properties", + "test3=version"); + putValue("/servicecomb/config/version/production/demo-consul/provider/0.0.1/application.properties", + "test4=version4"); + testGetConfig("test4", "version4"); + } + + private void testTag() { + + putValue("/servicecomb/config/tag/production/demo-consul/provider/0.0.1/tag1/application.properties", + "test5=tag"); + putValue("/servicecomb/config/tag/production/demo-consul/provider/0.0.1/tag1/application.properties", + "test5=tag5"); + testGetConfig("test5", "tag5"); + } + + public void putValue(String key, String value) { + try { + Consul.Builder builder = Consul.builder().withHostAndPort(HostAndPort.fromParts("127.0.0.1", 8500)); + Consul consulClient = builder.build(); + consulClient.keyValueClient().putValue(key, value); + LOGGER.info("Value set successfully:{}", value); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void testGetConfig(String key, String expectValue) { + + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=" + key, String.class); + + for (int i = 0; i < 4; i++) { + if (StringUtils.equals(expectValue, result)) { + TestMgr.check(expectValue, result); + break; + } + SleepUtil.sleep(500, TimeUnit.MILLISECONDS); + } + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java new file mode 100644 index 00000000000..1b129acc2c6 --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HeaderParamWithListSchemaIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHeaderListDefault(); + testHeaderListMulti(); + testHeaderListCSV(); + testHeaderListSSV(); + testHeaderListPipes(); + } + + // default to multi + private void testHeaderListDefault() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListPipes() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a|b|c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListSSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a b c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListCSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a,b,c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + + headers.add("headerList", "a, b, c"); + entity = new HttpEntity<>(headers); + result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListMulti() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java new file mode 100644 index 00000000000..97e883fb45f --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HelloWorldIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHelloWorld(); + testGetConfig(); + } + + private void testGetConfig() { + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key1", String.class); + TestMgr.check("1", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key2", String.class); + TestMgr.check("3", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key3", String.class); + TestMgr.check("5", result); + } + + private void testHelloWorld() { + String result = template + .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); + TestMgr.check("Hello World", result); + + // test trace id added + MultiValueMap headers = new HttpHeaders(); + headers.add("X-B3-TraceId", "81de2eb7691c2bbb"); + HttpEntity entity = new HttpEntity(headers); + ResponseEntity response = + template.exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class); + TestMgr.check(1, response.getHeaders().get("X-B3-TraceId").size()); + TestMgr.check("81de2eb7691c2bbb", response.getHeaders().getFirst("X-B3-TraceId")); + TestMgr.check("Hello World", response.getBody()); + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java new file mode 100644 index 00000000000..2bdd0ca42ca --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class ProviderIT implements CategorizedTestCase { + + RestOperations template = new RestTemplate(); + + private static final Logger LOGGER = LoggerFactory.getLogger(ProviderIT.class); + + @Override + public void testRestTransport() throws Exception { + + User user = getUser("Application/json"); + TestMgr.check(1L, user.getId()); + TestMgr.check("czd", user.getName()); + + User user2 = getUser("application/json"); + TestMgr.check(1L, user2.getId()); + TestMgr.check("czd", user2.getName()); + + User user3 = getUser("APPLICATION/JSON"); + TestMgr.check(1L, user3.getId()); + TestMgr.check("czd", user3.getName()); + } + + private User getUser(String contentType) throws IOException { + HttpHeaders headers = new HttpHeaders(); + headers.set("Content-Type", contentType); + + String requestBody = """ + { + "id": 1, + "name": "czd" + } + """; + + HttpEntity entity = new HttpEntity<>(requestBody, headers); + + String url = Config.GATEWAY_URL + "/testContentType"; + ResponseEntity response = template.exchange( + url, HttpMethod.POST, entity, String.class); + + User user = JsonUtils.readValue(response.getBody().getBytes(StandardCharsets.UTF_8), User.class); + return user; + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java new file mode 100644 index 00000000000..488a6cf712a --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient.Model; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +public class ReactiveStreamIT implements CategorizedTestCase { + @Autowired + @Qualifier("reactiveStreamProvider") + ReactiveStreamClient reactiveStreamProvider; + + @Autowired + @Qualifier("reactiveStreamGateway") + ReactiveStreamClient reactiveStreamGateway; + + @Override + public void testRestTransport() throws Exception { + testSseString(reactiveStreamProvider); + testSseModel(reactiveStreamProvider); + testSseString(reactiveStreamGateway); + testSseModel(reactiveStreamGateway); + } + + private void testSseModel(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseModel(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(Model s) { + buffer.append(s.getName()).append(s.getAge()); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); + } + + private void testSseString(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseString(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(String s) { + buffer.append(s); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("abc", buffer.toString()); + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java new file mode 100644 index 00000000000..26a2a491bbe --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class TestClientApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); + + run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java new file mode 100644 index 00000000000..abe96a4f10b --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.localregistry.RegistryBean; +import org.apache.servicecomb.localregistry.RegistryBean.Instance; +import org.apache.servicecomb.localregistry.RegistryBean.Instances; +import org.apache.servicecomb.provider.pojo.Invoker; +import org.reactivestreams.Publisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.WebSocket; + +@Configuration +public class ThirdSvcConfiguration { + @RequestMapping(path = "/ws") + public interface WebsocketClient { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + WebSocket websocket(); + } + + @RequestMapping(path = "/") + public interface ReactiveStreamClient { + class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + Publisher sseString(); + + @GetMapping("/sseModel") + Publisher sseModel(); + } + + @Bean + public RegistryBean providerServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .setAppId("demo-consul") + .setServiceName("provider") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9094"))))); + } + + @Bean + public RegistryBean gatewayServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .addSchemaInterface("WebsocketController", WebsocketClient.class) + .setAppId("demo-consul") + .setServiceName("gateway") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9090?websocketEnabled=true"))))); + } + + @Bean("reactiveStreamProvider") + public ReactiveStreamClient reactiveStreamProvider() { + return Invoker.createProxy("provider", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean("reactiveStreamGateway") + public ReactiveStreamClient reactiveStreamGateway() { + return Invoker.createProxy("gateway", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean + public WebsocketClient gatewayWebsocketClient() { + return Invoker.createProxy("gateway", "WebsocketController", WebsocketClient.class); + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ff421930ef2 --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/User.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java new file mode 100644 index 00000000000..c36f2d16d28 --- /dev/null +++ b/demo/demo-consul/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.WebsocketClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import io.vertx.core.http.WebSocket; + +@Component +public class WebsocketIT implements CategorizedTestCase { + @Autowired + private WebsocketClient websocketClient; + + @Override + public void testRestTransport() throws Exception { + StringBuffer sb = new StringBuffer(); + AtomicBoolean closed = new AtomicBoolean(false); + CountDownLatch latchStarted = new CountDownLatch(1); + CountDownLatch latch = new CountDownLatch(1); + + WebSocket webSocket = websocketClient.websocket(); + webSocket.textMessageHandler(s -> { + if ("started".equals(s)) { + latchStarted.countDown(); + return; + } + sb.append(s); + sb.append(" "); + webSocket.writeTextMessage(s); + }); + webSocket.closeHandler(v -> { + closed.set(true); + latch.countDown(); + }); + + webSocket.writeTextMessage("start"); + int i = 0; + for (; i < 10; i++) { + if (!latchStarted.await(3, TimeUnit.SECONDS)) { + webSocket.writeTextMessage("start"); + continue; + } + break; + } + TestMgr.check(i < 10, true); + latch.await(30, TimeUnit.SECONDS); + TestMgr.check(sb.toString(), "hello hello 0 hello 1 hello 2 hello 3 hello 4 total 6 "); + TestMgr.check(closed.get(), true); + } +} diff --git a/demo/demo-consul/test-client/src/main/resources/application.yml b/demo/demo-consul/test-client/src/main/resources/application.yml new file mode 100644 index 00000000000..718fbd9cc84 --- /dev/null +++ b/demo/demo-consul/test-client/src/main/resources/application.yml @@ -0,0 +1,40 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +servicecomb: + service: + application: demo-consul + name: test-client + version: 0.0.1 + + rest: + address: 0.0.0.0:9097 # should be same with server.port to use web container + + config: + consul: + enabled: true + host: 127.0.0.1 + scheme: http + acl-token: '' + instance-tag: tag1 + +test1: env +test2: applition +test3: service +test4: version +test5: tag diff --git a/demo/demo-consul/test-client/src/main/resources/log4j2.xml b/demo/demo-consul/test-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-consul/test-client/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-consul/test-client/src/test/java/org/apache/servicecomb/samples/ConsulIT.java b/demo/demo-consul/test-client/src/test/java/org/apache/servicecomb/samples/ConsulIT.java new file mode 100644 index 00000000000..62862743e27 --- /dev/null +++ b/demo/demo-consul/test-client/src/test/java/org/apache/servicecomb/samples/ConsulIT.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = TestClientApplication.class) +public class ConsulIT { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulIT.class); + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() { + try { + TestClientApplication.run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-crossapp/crossapp-client/pom.xml b/demo/demo-crossapp/crossapp-client/pom.xml index 2099ef0e5bc..fa2e051cd0f 100644 --- a/demo/demo-crossapp/crossapp-client/pom.xml +++ b/demo/demo-crossapp/crossapp-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-crossapp - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT crossapp-client Java Chassis::Demo::CrossApp::Client @@ -42,24 +42,88 @@ crossapp-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappClient.java b/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappClient.java index cfdaec541dd..7d78648963a 100644 --- a/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappClient.java +++ b/demo/demo-crossapp/crossapp-client/src/main/java/org/apache/servicecomb/demo/crossapp/CrossappClient.java @@ -32,6 +32,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestOperations; import org.springframework.web.client.RestTemplate; @@ -62,28 +63,84 @@ public static void run() { result = helloWorld.sayHello(); TestMgr.check("hello world", result); - testCorsHandler(); + testCorsHandlerOptions(); + testCorsHandlerGet(); TestMgr.summary(); System.setProperty("sun.net.http.allowRestrictedHeaders", "false"); } - private static void testCorsHandler() { + private static void testCorsHandlerOptions() { + // first domain RestOperations springRestTemplate = new RestTemplate(); MultiValueMap requestHeaders = new LinkedMultiValueMap<>(); - requestHeaders.put("Origin", Collections.singletonList("http://localhost:8080")); + requestHeaders.put("Origin", Collections.singletonList("http://test.domain:8080")); requestHeaders.put("Access-Control-Request-Method", Collections.singletonList("PUT")); - HttpEntity requestEntity = new HttpEntity<>(requestHeaders); ResponseEntity responseEntity = springRestTemplate .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.OPTIONS, requestEntity, String.class); - TestMgr.check("204", responseEntity.getStatusCode().value()); TreeSet sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Methods")); TestMgr.check("[DELETE,POST,GET,PUT]", sortedSet); sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Headers")); TestMgr.check("[abc,def]", sortedSet); - TestMgr.check("*", responseEntity.getHeaders().getFirst("Access-Control-Allow-Origin")); + TestMgr.check("http://test.domain:8080", + responseEntity.getHeaders().getFirst("Access-Control-Allow-Origin")); + + // second domain + requestHeaders = new LinkedMultiValueMap<>(); + requestHeaders.put("Origin", Collections.singletonList("http://test.domain:9090")); + requestHeaders.put("Access-Control-Request-Method", Collections.singletonList("PUT")); + requestEntity = new HttpEntity<>(requestHeaders); + responseEntity = springRestTemplate + .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.OPTIONS, requestEntity, + String.class); + TestMgr.check("204", responseEntity.getStatusCode().value()); + sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Methods")); + TestMgr.check("[DELETE,POST,GET,PUT]", sortedSet); + sortedSet = new TreeSet<>(responseEntity.getHeaders().get("Access-Control-Allow-Headers")); + TestMgr.check("[abc,def]", sortedSet); + TestMgr.check("http://test.domain:9090", + responseEntity.getHeaders().getFirst("Access-Control-Allow-Origin")); + } + + private static void testCorsHandlerGet() { + // allowed origin + RestOperations springRestTemplate = new RestTemplate(); + MultiValueMap requestHeaders = new LinkedMultiValueMap<>(); + requestHeaders.put("Origin", Collections.singletonList("http://test.domain:8080")); + HttpEntity requestEntity = new HttpEntity<>(requestHeaders); + ResponseEntity responseEntity = springRestTemplate + .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.GET, requestEntity, + String.class); + + TestMgr.check("200", responseEntity.getStatusCode().value()); + TestMgr.check("hello world", responseEntity.getBody()); + + // allowed origin + requestHeaders = new LinkedMultiValueMap<>(); + requestHeaders.put("Origin", Collections.singletonList("http://test.domain:9090")); + requestEntity = new HttpEntity<>(requestHeaders); + responseEntity = springRestTemplate + .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.GET, requestEntity, + String.class); + + TestMgr.check("200", responseEntity.getStatusCode().value()); + TestMgr.check("hello world", responseEntity.getBody()); + + // not allowed origin + try { + requestHeaders = new LinkedMultiValueMap<>(); + requestHeaders.put("Origin", Collections.singletonList("http://test.domain:7070")); + requestEntity = new HttpEntity<>(requestHeaders); + springRestTemplate + .exchange("http://127.0.0.1:8080/helloworld/hello", HttpMethod.GET, requestEntity, + String.class); + TestMgr.fail("must throw"); + } catch (HttpServerErrorException e) { + TestMgr.check(500, e.getStatusCode().value()); + TestMgr.check(true, e.getMessage().contains("500 CORS Rejected")); + } } } diff --git a/demo/demo-crossapp/crossapp-server/pom.xml b/demo/demo-crossapp/crossapp-server/pom.xml index 1abf8b86ef1..0207bdb7b20 100644 --- a/demo/demo-crossapp/crossapp-server/pom.xml +++ b/demo/demo-crossapp/crossapp-server/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-crossapp - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT crossapp-server Java Chassis::Demo::CrossApp::Server @@ -47,28 +47,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml b/demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml index a3b4d2241dd..0f1bbdc8853 100644 --- a/demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml +++ b/demo/demo-crossapp/crossapp-server/src/main/resources/microservice.yaml @@ -30,7 +30,7 @@ servicecomb: address: 0.0.0.0:8080 cors: enabled: true - origin: "*" + origin: "http://test.domain:8080,http://test.domain:9090" allowedHeader: abc,def allowedMethod: GET,PUT,POST,DELETE exposedHeader: abc,def diff --git a/demo/demo-crossapp/pom.xml b/demo/demo-crossapp/pom.xml index f24f4559dbf..08c2055d357 100644 --- a/demo/demo-crossapp/pom.xml +++ b/demo/demo-crossapp/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-crossapp Java Chassis::Demo::CrossApp diff --git a/demo/demo-cse-v1/consumer/pom.xml b/demo/demo-cse-v1/consumer/pom.xml index 63fca453f6b..78a7bf75a23 100644 --- a/demo/demo-cse-v1/consumer/pom.xml +++ b/demo/demo-cse-v1/consumer/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-cse-v1 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v1-consumer @@ -44,4 +44,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/consumer/src/main/resources/application.yml b/demo/demo-cse-v1/consumer/src/main/resources/application.yml index e56621ef678..11f739de444 100644 --- a/demo/demo-cse-v1/consumer/src/main/resources/application.yml +++ b/demo/demo-cse-v1/consumer/src/main/resources/application.yml @@ -59,6 +59,71 @@ servicecomb: - weight: 100 tags: version: 0.0.1 + - precedence: 3 + match: + headers: + canary: + exact: fallback + route: + - weight: 100 + tags: + version: 0.0.3 + fallback: + - weight: 20 + tags: + version: 0.0.1 + - weight: 80 + tags: + version: 0.0.2 + - precedence: 4 + emptyProtection: false + match: + headers: + canary: + exact: emptyProtectionClose100 + route: + - weight: 100 + tags: + version: 0.0.3 + - precedence: 5 + emptyProtection: false + match: + headers: + canary: + exact: emptyProtectionCloseLess100 + route: + - weight: 50 + tags: + version: 0.0.3 + - precedence: 6 + emptyProtection: false + match: + headers: + canary: + exact: emptyProtectionCloseFallback + route: + - weight: 100 + tags: + version: 0.0.3 + fallback: + - weight: 100 + tags: + version: 0.0.1 + - precedence: 7 + emptyProtection: false + match: + headers: + canary: + exact: emptyProtectionClose100-2 + route: + - weight: 50 + tags: + version: 0.0.1 + - weight: 50 + tags: + version: 0.0.3 + + router: type: router header: canary @@ -70,5 +135,3 @@ servicecomb: secretKey: ${CREDENTIALS_SK} akskCustomCipher: default project: cn-south-1 - - diff --git a/demo/demo-cse-v1/consumer/src/main/resources/log4j2.xml b/demo/demo-cse-v1/consumer/src/main/resources/log4j2.xml index 313d1fcec1f..4e54783856a 100644 --- a/demo/demo-cse-v1/consumer/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v1/consumer/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/gateway/pom.xml b/demo/demo-cse-v1/gateway/pom.xml index b130e1ed8bc..49241e669cf 100644 --- a/demo/demo-cse-v1/gateway/pom.xml +++ b/demo/demo-cse-v1/gateway/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-cse-v1 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v1-gateway @@ -47,4 +47,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/gateway/src/main/resources/application.yml b/demo/demo-cse-v1/gateway/src/main/resources/application.yml index 73545ed3370..52f065c4f62 100644 --- a/demo/demo-cse-v1/gateway/src/main/resources/application.yml +++ b/demo/demo-cse-v1/gateway/src/main/resources/application.yml @@ -50,9 +50,16 @@ servicecomb: microserviceName: consumer versionRule: 0.0.0+ - router: - type: router - header: canary + matchGroup: + canary-header: | + matches: + - headers: + canary: + prefix: "" + mapper: + canary-header: | + target: + canary: $H{canary} # Configure AK/SK credentials if needed. Default not enabled. credentials: @@ -60,4 +67,4 @@ servicecomb: accessKey: ${CREDENTIALS_AK} secretKey: ${CREDENTIALS_SK} akskCustomCipher: default - project: cn-south-1 \ No newline at end of file + project: cn-south-1 diff --git a/demo/demo-cse-v1/gateway/src/main/resources/log4j2.xml b/demo/demo-cse-v1/gateway/src/main/resources/log4j2.xml index e9bb2656f8f..165cdf7e0a3 100644 --- a/demo/demo-cse-v1/gateway/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v1/gateway/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/pom.xml b/demo/demo-cse-v1/pom.xml index b49ea7f29a5..72e70e58c15 100644 --- a/demo/demo-cse-v1/pom.xml +++ b/demo/demo-cse-v1/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-cse-v1 Java Chassis::Demo::CSE-V1 @@ -75,4 +75,4 @@ test-client - \ No newline at end of file + diff --git a/demo/demo-cse-v1/provider-canary/pom.xml b/demo/demo-cse-v1/provider-canary/pom.xml index d1d0726857d..34c393b9e8d 100644 --- a/demo/demo-cse-v1/provider-canary/pom.xml +++ b/demo/demo-cse-v1/provider-canary/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-cse-v1 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v1-provider-canary @@ -48,4 +48,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/provider-canary/src/main/resources/log4j2.xml b/demo/demo-cse-v1/provider-canary/src/main/resources/log4j2.xml index 56fc9a0a676..9ac74305e0c 100644 --- a/demo/demo-cse-v1/provider-canary/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v1/provider-canary/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/provider/pom.xml b/demo/demo-cse-v1/provider/pom.xml index cfc6fe55af4..12015bd6b7a 100644 --- a/demo/demo-cse-v1/provider/pom.xml +++ b/demo/demo-cse-v1/provider/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-cse-v1 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v1-provider @@ -48,4 +48,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml b/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml index 56fc9a0a676..9ac74305e0c 100644 --- a/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v1/provider/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/test-client/pom.xml b/demo/demo-cse-v1/test-client/pom.xml index 3996dd2f7d6..697c28fb64e 100644 --- a/demo/demo-cse-v1/test-client/pom.xml +++ b/demo/demo-cse-v1/test-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-cse-v1 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v1-test-client @@ -52,4 +52,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java index 64cc9cc302a..2aff8c25966 100644 --- a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java +++ b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java @@ -38,11 +38,11 @@ public void testRestTransport() throws Exception { @SuppressWarnings("unchecked") private void testConfig() { String result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v1.test.foo", String.class); - TestMgr.check("\"foo\"", result); + TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v1.test.bar", String.class); - TestMgr.check("\"bar\"", result); + TestMgr.check("bar", result); result = template.getForObject(Config.GATEWAY_URL + "/dynamicString?key=cse.v1.test.dynamicString", String.class); - TestMgr.check("\"a,b\"", result); + TestMgr.check("a,b", result); List listResult = template .getForObject(Config.GATEWAY_URL + "/dynamicArray", List.class); @@ -53,8 +53,8 @@ private void testConfig() { private void testFooBar() { String result = template.getForObject(Config.GATEWAY_URL + "/foo", String.class); - TestMgr.check("\"foo\"", result); + TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/bar", String.class); - TestMgr.check("\"bar\"", result); + TestMgr.check("bar", result); } } diff --git a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java index f744d4bcad9..691ee47a162 100644 --- a/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java +++ b/demo/demo-cse-v1/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java @@ -34,8 +34,14 @@ public class HelloWorldIT implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { + testHelloWorldFallback(); + testHelloWorldNoHeader(); testHelloWorld(); testHelloWorldCanary(); + testHelloWorldEmptyProtectionCloseWeight100(); + testHelloWorldeEmptyProtectionCloseWeightLess100(); + testHelloWorldEmptyProtectionCloseFallback(); + testHelloWorldEmptyProtectionCloseWeight100Two(); } private void testHelloWorld() { @@ -45,7 +51,7 @@ private void testHelloWorld() { HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class).getBody(); - TestMgr.check("\"Hello World\"", result); + TestMgr.check("Hello World", result); } } @@ -57,6 +63,30 @@ private void testHelloWorldCanary() { MultiValueMap headers = new HttpHeaders(); headers.add("canary", "new"); HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, entity, String.class).getBody(); + if (result.equals("Hello Canary World")) { + oldCount++; + } else if (result.equals("Hello Canary in canary World")) { + newCount++; + } else { + TestMgr.fail("not expected result testHelloWorldCanary"); + return; + } + } + + double ratio = oldCount / (float) (oldCount + newCount); + TestMgr.check(ratio > 0.1 && ratio < 0.3, true); + } + + private void testHelloWorldFallback() { + int oldCount = 0; + int newCount = 0; + + for (int i = 0; i < 20; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "fallback"); + HttpEntity entity = new HttpEntity<>(headers); String result = template .exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, entity, String.class).getBody(); if (result.equals("\"Hello Canary World\"")) { @@ -70,6 +100,105 @@ private void testHelloWorldCanary() { } double ratio = oldCount / (float) (oldCount + newCount); - TestMgr.check(ratio > 0.1 && ratio < 0.3, true); + TestMgr.check(Math.abs(ratio - 0.2) <= 0.1, true); + } + + private void testHelloWorldNoHeader() { + int oldCount = 0; + int newCount = 0; + + for (int i = 0; i < 20; i++) { + String result = template + .getForObject(Config.GATEWAY_URL + "/sayHelloCanary?name=World", String.class); + if (result.equals("\"Hello Canary World\"")) { + oldCount++; + } else if (result.equals("\"Hello Canary in canary World\"")) { + newCount++; + } else { + TestMgr.fail("not expected result testHelloWorldCanary"); + return; + } + } + + double ratio = oldCount / (float) (oldCount + newCount); + TestMgr.check(Double.compare(ratio, 0.5) == 0, true); + } + + private void testHelloWorldEmptyProtectionCloseWeight100() { + int failCount = 0; + + for (int i = 0; i < 20; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "emptyProtectionClose100"); + HttpEntity entity = new HttpEntity<>(headers); + try { + template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, + entity, String.class); + } catch (Exception e) { + failCount++; + } + } + + TestMgr.check(failCount == 20, true); + } + + private void testHelloWorldeEmptyProtectionCloseWeightLess100() { + int failCount = 0; + int succCount = 0; + + for (int i = 0; i < 20; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "emptyProtectionCloseLess100"); + HttpEntity entity = new HttpEntity<>(headers); + try { + template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, + entity, String.class); + succCount++; + } catch (Exception e) { + failCount++; + } + } + + TestMgr.check(succCount == 20, true); + } + + private void testHelloWorldEmptyProtectionCloseFallback() { + int failCount = 0; + int succCount = 0; + + for (int i = 0; i < 20; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "emptyProtectionCloseFallback"); + HttpEntity entity = new HttpEntity<>(headers); + try { + template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, + entity, String.class); + succCount++; + } catch (Exception e) { + failCount++; + } + } + + TestMgr.check(succCount == 20, true); + } + + private void testHelloWorldEmptyProtectionCloseWeight100Two() { + int failCount = 0; + int succCount = 0; + + for (int i = 0; i < 20; i++) { + MultiValueMap headers = new HttpHeaders(); + headers.add("canary", "emptyProtectionClose100-2"); + HttpEntity entity = new HttpEntity<>(headers); + try { + template.exchange(Config.GATEWAY_URL + "/sayHelloCanary?name=World", HttpMethod.GET, + entity, String.class); + succCount++; + } catch (Exception e) { + failCount++; + } + } + + TestMgr.check(failCount == succCount, true); } } diff --git a/demo/demo-cse-v1/test-client/src/main/resources/application.yml b/demo/demo-cse-v1/test-client/src/main/resources/application.yml index 0bdd11ac161..13ffddc60b8 100644 --- a/demo/demo-cse-v1/test-client/src/main/resources/application.yml +++ b/demo/demo-cse-v1/test-client/src/main/resources/application.yml @@ -40,4 +40,4 @@ servicecomb: accessKey: ${CREDENTIALS_AK} secretKey: ${CREDENTIALS_SK} akskCustomCipher: default - project: cn-south-1 \ No newline at end of file + project: cn-south-1 diff --git a/demo/demo-cse-v1/test-client/src/main/resources/log4j2.xml b/demo/demo-cse-v1/test-client/src/main/resources/log4j2.xml index 56fc9a0a676..9ac74305e0c 100644 --- a/demo/demo-cse-v1/test-client/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v1/test-client/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/README.md b/demo/demo-cse-v2/README.md index 001b59da912..dda7dee702e 100644 --- a/demo/demo-cse-v2/README.md +++ b/demo/demo-cse-v2/README.md @@ -9,7 +9,7 @@ * 依次启动 provider、consumer、gateway * 在配置中心增加如下配置: -* 应用级配置:consumer.yaml。类型为 yaml。 +* 应用级配置:consumer.yaml。类型为 yaml。 ```yaml cse: @@ -18,7 +18,7 @@ cse: foo: foo ``` - * 自定义配置:priority1.yaml。label信息: public=default 。类型为 yaml。 + * 自定义配置:priority1.yaml。label信息: public=default 。类型为 yaml。 ```yaml cse: v2: @@ -27,7 +27,7 @@ cse: common: common ``` - * 自定义配置:priority1.yaml。label信息: public=default,extra=default 。类型为 yaml。 + * 自定义配置:priority1.yaml。label信息: public=default,extra=default 。类型为 yaml。 ```yaml cse: v2: @@ -36,7 +36,7 @@ cse: extra: common ``` - * 应用级配置:priority2.yaml。类型为 yaml。 + * 应用级配置:priority2.yaml。类型为 yaml。 ```yaml cse: v2: @@ -44,7 +44,7 @@ cse: priority: v2 ``` - * 服务级配置:priority3.yaml,微服务性选择consumer。类型为 yaml。 + * 服务级配置:priority3.yaml,微服务性选择consumer。类型为 yaml。 ```yaml cse: v2: @@ -52,14 +52,6 @@ cse: priority: v3 ``` - * 自定义配置:priority3.yaml,labels: app=demo-java-chassis-cse-v2,environment=,service=consumer,extra=。类型为 yaml。 -```yaml -cse: - v2: - test: - priority: v4 -``` - * 应用级配置:consumerApp.yaml,应用选择demo-java-chassis-cse-v2。类型为 yaml。 ```yaml cse: @@ -68,7 +60,7 @@ cse: priority1: v1 ``` -* 服务级配置:consumerSer.yaml,微服务性选择consumer。类型为 yaml。 +* 服务级配置:consumerService.yaml,微服务性选择consumer。类型为 yaml。 ```yaml cse: v2: @@ -76,12 +68,12 @@ cse: priority1: v2 ``` - * 应用级配置: cse.v2.test.bar: bar 。 类型为 text。 - + * 应用级配置: cse.v2.test.bar: bar 。 类型为 text。 + * 执行 tests-client 里面的集成测试用例 (成功) * 修改 - * priority1.yaml。label信息: public=default 。类型为 yaml。 + * priority1.yaml。label信息: public=default 。类型为 yaml。 ```yaml cse: v2: @@ -89,10 +81,10 @@ cse: priority: v4 ``` -* 执行 tests-client 里面的集成测试用例 (成功) +* 执行 tests-client 里面的集成测试用例 (成功) * 修改 - * 应用级priority3.yaml。 + * 应用级priority3.yaml。 ```yaml cse: v2: @@ -100,10 +92,10 @@ cse: priority: v5 ``` -* 执行 tests-client 里面的集成测试用例 (失败) +* 执行 tests-client 里面的集成测试用例 (失败) * 修改 - * 应用级priority3.yaml。label信息: + * 应用级priority3.yaml。label信息: ```yaml cse: v2: @@ -111,7 +103,7 @@ cse: priority: v3 ``` -* 执行 tests-client 里面的集成测试用例 (成功) +* 执行 tests-client 里面的集成测试用例 (成功) * 修改 * 应用级consumerApp.yaml。 ```yaml @@ -121,9 +113,9 @@ cse: priority1: v10 ``` -* 执行 tests-client 里面的集成测试用例 (成功) +* 执行 tests-client 里面的集成测试用例 (成功) * 修改 - * 服务级配置:consumerSer.yaml。 + * 服务级配置:consumerService.yaml。 ```yaml cse: v2: diff --git a/demo/demo-cse-v2/consumer/pom.xml b/demo/demo-cse-v2/consumer/pom.xml index ed18ec7ddbd..0fe6e05f4d0 100644 --- a/demo/demo-cse-v2/consumer/pom.xml +++ b/demo/demo-cse-v2/consumer/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-cse-v2 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v2-consumer @@ -44,4 +44,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/consumer/src/main/resources/application.yml b/demo/demo-cse-v2/consumer/src/main/resources/application.yml index c7f104e9dbe..5982bb668b5 100644 --- a/demo/demo-cse-v2/consumer/src/main/resources/application.yml +++ b/demo/demo-cse-v2/consumer/src/main/resources/application.yml @@ -35,4 +35,3 @@ servicecomb: rest: address: 0.0.0.0:9092 # should be same with server.port to use web container - diff --git a/demo/demo-cse-v2/consumer/src/main/resources/log4j2.xml b/demo/demo-cse-v2/consumer/src/main/resources/log4j2.xml index 313d1fcec1f..4e54783856a 100644 --- a/demo/demo-cse-v2/consumer/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v2/consumer/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/gateway/pom.xml b/demo/demo-cse-v2/gateway/pom.xml index d71ad70073d..1d04a248410 100644 --- a/demo/demo-cse-v2/gateway/pom.xml +++ b/demo/demo-cse-v2/gateway/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-cse-v2 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v2-gateway @@ -47,4 +47,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml b/demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml index e9bb2656f8f..165cdf7e0a3 100644 --- a/demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v2/gateway/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/pom.xml b/demo/demo-cse-v2/pom.xml index 49c233ad4da..35a623930da 100644 --- a/demo/demo-cse-v2/pom.xml +++ b/demo/demo-cse-v2/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-cse-v2 Java Chassis::Demo::CSE-V2 @@ -37,10 +37,22 @@ org.apache.servicecomb registry-service-center + + org.apache.servicecomb + config-kie + org.apache.logging.log4j log4j-slf4j-impl + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + @@ -50,4 +62,4 @@ test-client - \ No newline at end of file + diff --git a/demo/demo-cse-v2/provider/pom.xml b/demo/demo-cse-v2/provider/pom.xml index 5dcc140d22a..4308b1169d9 100644 --- a/demo/demo-cse-v2/provider/pom.xml +++ b/demo/demo-cse-v2/provider/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-cse-v2 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v2-provider @@ -48,4 +48,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/provider/src/main/resources/application.yml b/demo/demo-cse-v2/provider/src/main/resources/application.yml index a822e124248..bb55de45abc 100644 --- a/demo/demo-cse-v2/provider/src/main/resources/application.yml +++ b/demo/demo-cse-v2/provider/src/main/resources/application.yml @@ -37,4 +37,3 @@ servicecomb: rest: address: 0.0.0.0:9093 # should be same with server.port to use web container - diff --git a/demo/demo-cse-v2/provider/src/main/resources/log4j2.xml b/demo/demo-cse-v2/provider/src/main/resources/log4j2.xml index 56fc9a0a676..9ac74305e0c 100644 --- a/demo/demo-cse-v2/provider/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v2/provider/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/test-client/pom.xml b/demo/demo-cse-v2/test-client/pom.xml index 117d520f87d..78917aa7e5c 100644 --- a/demo/demo-cse-v2/test-client/pom.xml +++ b/demo/demo-cse-v2/test-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-cse-v2 - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT cse-v2-test-client @@ -52,4 +52,4 @@ - \ No newline at end of file + diff --git a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java index 3e10063a51f..16b1388641a 100644 --- a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java +++ b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/ConsumerConfigIT.java @@ -35,29 +35,29 @@ public void testRestTransport() throws Exception { private void testConfig() { String result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.foo", String.class); - TestMgr.check("\"foo\"", result); + TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.bar", String.class); - TestMgr.check("\"bar\"", result); + TestMgr.check("bar", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.priority", String.class); - TestMgr.check("\"v1\"", result); + TestMgr.check("v1", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.common", String.class); - TestMgr.check("\"common\"", result); + TestMgr.check("common", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.extra", String.class); - TestMgr.check("\"common\"", result); + TestMgr.check("common", result); result = template.getForObject(Config.GATEWAY_URL + "/config?key=cse.v2.test.priority1", String.class); - TestMgr.check("\"v2\"", result); + TestMgr.check("v2", result); } private void testFooBar() { String result = template.getForObject(Config.GATEWAY_URL + "/bar", String.class); - TestMgr.check("\"bar\"", result); + TestMgr.check("bar", result); result = template.getForObject(Config.GATEWAY_URL + "/foo", String.class); - TestMgr.check("\"foo\"", result); + TestMgr.check("foo", result); result = template.getForObject(Config.GATEWAY_URL + "/priority", String.class); - TestMgr.check("\"v1\"", result); + TestMgr.check("v1", result); result = template.getForObject(Config.GATEWAY_URL + "/common", String.class); - TestMgr.check("\"common\"", result); + TestMgr.check("common", result); result = template.getForObject(Config.GATEWAY_URL + "/extra", String.class); - TestMgr.check("\"common\"", result); + TestMgr.check("common", result); } } diff --git a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java index e667095b511..59d06bb1e01 100644 --- a/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java +++ b/demo/demo-cse-v2/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java @@ -35,6 +35,6 @@ public void testRestTransport() throws Exception { private void testHelloWorld() { String result = template .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); - TestMgr.check("\"Hello World\"", result); + TestMgr.check("Hello World", result); } } diff --git a/demo/demo-cse-v2/test-client/src/main/resources/log4j2.xml b/demo/demo-cse-v2/test-client/src/main/resources/log4j2.xml index 56fc9a0a676..9ac74305e0c 100644 --- a/demo/demo-cse-v2/test-client/src/main/resources/log4j2.xml +++ b/demo/demo-cse-v2/test-client/src/main/resources/log4j2.xml @@ -42,4 +42,4 @@ - \ No newline at end of file + diff --git a/demo/demo-edge/README.md b/demo/demo-edge/README.md new file mode 100644 index 00000000000..b5a552e0668 --- /dev/null +++ b/demo/demo-edge/README.md @@ -0,0 +1,17 @@ +# About edge service API compatibility + +* Edge service use the latest version of the microservice meta. e.g. For business 1.0.0, 1.1.0, 2.0.0 have the following APIs: + + * 1.0.0: /business/v1/add + * 1.1.0: /business/v1/add, /business/v1/dec + * 2.0.0: /business/v2/add, /business/v2/dec + + If users invoke /business/v1/add, edge service will give NOT FOUND, because 2.0.0 microservice meta do not have this API. Even using router to route all /business/v1/* requests to 1.1.0, path locating happens before load balance. + +* It's very important to keep your API compatibility cross versions if these versions need work together. e.g. + + * 1.0.0: /business/v1/add + * 1.1.0: /business/v1/add, /business/v1/dec + * 2.0.0: /business/v1/add, /business/v1/dec, /business/v2/add, /business/v2/dec + + Together with router, /business/v1/add will go correctly to 1.0.0 or 1.1.0, and /business/v2/add will go correctly to 2.0.0. Without router, /business/v2/add may route to 1.0.0 or 1.1.0 and NOT FOUND is reported. diff --git a/demo/demo-edge/authentication/pom.xml b/demo/demo-edge/authentication/pom.xml index cef8cf293f9..4849faf28fe 100644 --- a/demo/demo-edge/authentication/pom.xml +++ b/demo/demo-edge/authentication/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT authentication Java Chassis::Demo::Edge::Authentication @@ -43,28 +43,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-edge/business-1-1-0/pom.xml b/demo/demo-edge/business-1-1-0/pom.xml index da4f353aff4..1cef9b01cab 100644 --- a/demo/demo-edge/business-1-1-0/pom.xml +++ b/demo/demo-edge/business-1-1-0/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT business-1-1-0 Java Chassis::Demo::Edge::Business 1.1.0 @@ -49,28 +49,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-edge/business-1.0.0/pom.xml b/demo/demo-edge/business-1.0.0/pom.xml index 0f38d7a2907..d7e35748fef 100644 --- a/demo/demo-edge/business-1.0.0/pom.xml +++ b/demo/demo-edge/business-1.0.0/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT business-1-0-0 Java Chassis::Demo::Edge::Business 1.0.0 @@ -51,28 +51,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-edge/business-2.0.0/pom.xml b/demo/demo-edge/business-2.0.0/pom.xml index 4ffd5e5aff7..3500be47d75 100644 --- a/demo/demo-edge/business-2.0.0/pom.xml +++ b/demo/demo-edge/business-2.0.0/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT business-2-0-0 Java Chassis::Demo::Edge::Business 2.0.0 @@ -51,28 +51,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java index 942133ece03..fde945e50bf 100644 --- a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/Impl.java @@ -17,39 +17,18 @@ package org.apache.servicecomb.demo.edge.business; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.RandomAccessFile; -import java.nio.charset.StandardCharsets; - -import org.apache.commons.io.FileUtils; import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; -import org.apache.servicecomb.demo.edge.model.DependTypeA; -import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; import org.apache.servicecomb.demo.edge.model.ResultWithInstance; -import org.apache.servicecomb.demo.edge.model.User; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -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.RequestMethod; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; - -@RestSchema(schemaId = "news-v2") -@RequestMapping(path = "/business/v2") +@RestSchema(schemaId = "news-v1") +@RequestMapping(path = "/business/v1") public class Impl { private Environment environment; @@ -58,12 +37,6 @@ public void setEnvironment(Environment environment) { this.environment = environment; } - File tempDir = new File("target/downloadTemp"); - - public Impl() throws IOException { - FileUtils.forceMkdir(tempDir); - } - @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { AppClientDataRsp response = new AppClientDataRsp(); @@ -81,45 +54,4 @@ public ResultWithInstance add(int x, int y) { public ResultWithInstance dec(int x, int y) { return ResultWithInstance.create(x - y, environment); } - - @GetMapping(path = "/download") - @ApiResponses({ - @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = File.class)), description = ""), - }) - public ResponseEntity download() throws IOException { - return ResponseEntity - .ok() - .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=download.txt") - .body(new ByteArrayInputStream("download".getBytes(StandardCharsets.UTF_8))); - } - - protected File createBigFile() throws IOException { - File file = new File(tempDir, "bigFile.txt"); - file.delete(); - RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); - randomAccessFile.setLength(10 * 1024 * 1024); - randomAccessFile.close(); - return file; - } - - @GetMapping(path = "/bigFile") - public File bigFile() throws IOException { - return createBigFile(); - } - - @PostMapping(path = "recursiveSelf") - public RecursiveSelfType recursiveSelf(@RequestBody RecursiveSelfType value) { - return value; - } - - @PostMapping(path = "dependType") - public DependTypeA dependType(@RequestBody DependTypeA value) { - return value; - } - - @PostMapping(path = "encrypt") - public User encrypt(@RequestBody User value) { - return value; - } } diff --git a/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/ImplV2.java b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/ImplV2.java new file mode 100644 index 00000000000..0e5eabf290f --- /dev/null +++ b/demo/demo-edge/business-2.0.0/src/main/java/org/apache/servicecomb/demo/edge/business/ImplV2.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.edge.business; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; +import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; +import org.apache.servicecomb.demo.edge.model.DependTypeA; +import org.apache.servicecomb.demo.edge.model.RecursiveSelfType; +import org.apache.servicecomb.demo.edge.model.ResultWithInstance; +import org.apache.servicecomb.demo.edge.model.User; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +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.RequestMethod; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; + +@RestSchema(schemaId = "news-v2") +@RequestMapping(path = "/business/v2") +public class ImplV2 { + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + File tempDir = new File("target/downloadTemp"); + + public ImplV2() throws IOException { + FileUtils.forceMkdir(tempDir); + } + + @RequestMapping(path = "/channel/news/subscribe", method = RequestMethod.POST) + public AppClientDataRsp subscribeNewsColumn(@RequestBody ChannelRequestBase request) { + AppClientDataRsp response = new AppClientDataRsp(); + String rsp = "result from 2.0.0"; + response.setRsp(rsp); + return response; + } + + @RequestMapping(path = "/add", method = RequestMethod.GET) + public ResultWithInstance add(int x, int y) { + return ResultWithInstance.create(x + y, environment); + } + + @RequestMapping(path = "/dec", method = RequestMethod.GET) + public ResultWithInstance dec(int x, int y) { + return ResultWithInstance.create(x - y, environment); + } + + @GetMapping(path = "/download") + @ApiResponses({ + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = File.class)), description = ""), + }) + public ResponseEntity download() throws IOException { + return ResponseEntity + .ok() + .header(HttpHeaders.CONTENT_TYPE, MediaType.TEXT_PLAIN_VALUE) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=download.txt") + .body(new ByteArrayInputStream("download".getBytes(StandardCharsets.UTF_8))); + } + + protected File createBigFile() throws IOException { + File file = new File(tempDir, "bigFile.txt"); + file.delete(); + RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); + randomAccessFile.setLength(10 * 1024 * 1024); + randomAccessFile.close(); + return file; + } + + @GetMapping(path = "/bigFile") + public File bigFile() throws IOException { + return createBigFile(); + } + + @PostMapping(path = "recursiveSelf") + public RecursiveSelfType recursiveSelf(@RequestBody RecursiveSelfType value) { + return value; + } + + @PostMapping(path = "dependType") + public DependTypeA dependType(@RequestBody DependTypeA value) { + return value; + } + + @PostMapping(path = "encrypt") + public User encrypt(@RequestBody User value) { + return value; + } +} diff --git a/demo/demo-edge/consumer/pom.xml b/demo/demo-edge/consumer/pom.xml index 2b0647e778c..c26ca048528 100644 --- a/demo/demo-edge/consumer/pom.xml +++ b/demo/demo-edge/consumer/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT consumer Java Chassis::Demo::Edge::Consumer @@ -47,24 +47,212 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + authentication:${project.version} + authentication + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/authentication-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 7070 + + + + + + 7070:7070 + + + service-center + + + + + business-1-0-0:${project.version} + business-1-0-0 + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/business-1-0-0-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 8080:8080 + + + service-center + + + + + business-1-1-0:${project.version} + business-1-1-0 + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/business-1-1-0-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8090 + + + + + + 8090:8090 + + + service-center + + + + + business-2-0-0:${project.version} + business-2-0-0 + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/business-2-0-0-${project.version}.jar + + + service-center:sc.servicecomb.io + + + Register microservice instance success + + + 8091 + + + + + + 8091:8091 + + + service-center + + + + + edge-service:${project.version} + edge-service + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/edge-service-${project.version}.jar + + + service-center:sc.servicecomb.io + + + Register microservice instance success + + + + 18090 + + + + + + 18090:18090 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config-edge - ${project.version} - - - - diff --git a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java index 9fea0a2663b..ab10a7c3713 100644 --- a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/Consumer.java @@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.demo.edge.model.AppClientDataRsp; import org.apache.servicecomb.demo.edge.model.ChannelRequestBase; import org.apache.servicecomb.demo.edge.model.DependTypeA; import org.apache.servicecomb.demo.edge.model.DependTypeB; @@ -42,6 +43,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -56,9 +58,9 @@ public class Consumer { String edgePrefix; -// List addV1Result = new ArrayList<>(); + List addV1Result = new ArrayList<>(); -// List decV1Result = new ArrayList<>(); + List decV1Result = new ArrayList<>(); List addV2Result = new ArrayList<>(); @@ -94,14 +96,14 @@ public void run(String prefix) { testDownload(); testDownloadBigFile(); testErrorCode(); - // TODO: we use router to test this feature. -// invoke("/v1/add", 2, 1, addV1Result); -// invoke("/v1/add", 3, 1, addV1Result); -// invoke("/v1/add", 4, 1, addV1Result); -// invoke("/v1/add", 5, 1, addV1Result); -// -// invoke("/v1/dec", 2, 1, decV1Result); -// invoke("/v1/dec", 3, 1, decV1Result); + + invoke("/v1/add", 2, 1, addV1Result); + invoke("/v1/add", 3, 1, addV1Result); + invoke("/v1/add", 4, 1, addV1Result); + invoke("/v1/add", 5, 1, addV1Result); + + invoke("/v1/dec", 2, 1, decV1Result); + invoke("/v1/dec", 3, 1, decV1Result); invoke("/v2/add", 2, 1, addV2Result); invoke("/v2/add", 3, 1, addV2Result); @@ -109,14 +111,13 @@ public void run(String prefix) { invoke("/v2/dec", 2, 1, decV2Result); invoke("/v2/dec", 3, 1, decV2Result); - // TODO: we use router to test this feature. -// printResults("v1/add", addV1Result); -// printResults("v1/dec", decV1Result); + printResults("v1/add", addV1Result); + printResults("v1/dec", decV1Result); printResults("v2/add", addV2Result); printResults("v2/dec", decV2Result); -// checkResult("v1/add", addV1Result, "1.0.0", "1.1.0"); -// checkResult("v1/dec", decV1Result, "1.1.0"); + checkResult("v1/add", addV1Result, "1.0.0", "1.1.0"); + checkResult("v1/dec", decV1Result, "1.1.0"); checkResult("v2/add", addV2Result, "2.0.0"); checkResult("v2/dec", decV2Result, "2.0.0"); } @@ -282,21 +283,20 @@ private URIEndpointObject prepareEdge(String prefix) { } protected void invokeBusiness(String urlPrefix, ChannelRequestBase request) { - // since 3.0.0, do not support this feature. - // after 3.0.0, the client will load schema once after startup and will never change, and there - // isn't version rule concept. - - // TODO: we use router to test this feature. -// for (int i = 0; i < 3; i++) { -// String url = urlPrefix + "/channel/news/subscribe"; -// -// HttpHeaders headers = new HttpHeaders(); -// headers.setContentType(MediaType.APPLICATION_JSON); -// -// HttpEntity entity = new HttpEntity<>(request, headers); -// -// ResponseEntity response = template.postForEntity(url, entity, AppClientDataRsp.class); -// Assert.isTrue(response.getBody().getRsp().equals("result from 1.1.0"), response.getBody().getRsp()); -// } + List result = new ArrayList<>(6); + for (int i = 0; i < 6; i++) { + String url = urlPrefix + "/channel/news/subscribe"; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity entity = new HttpEntity<>(request, headers); + + ResponseEntity response = template.postForEntity(url, entity, AppClientDataRsp.class); + result.add(response.getBody().getRsp()); + } + Assert.isTrue(result.contains("result from 2.0.0"), "invokeBusiness not balance"); + Assert.isTrue(result.contains("result from 1.1.0"), "invokeBusiness not balance"); + Assert.isTrue(result.contains("result from 1.0.0"), "invokeBusiness not balance"); } } diff --git a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java index f360912be2e..ae462eb6fd1 100644 --- a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/ConsumerMain.java @@ -42,8 +42,9 @@ public static void runTest() throws Exception { new Consumer().run("rest"); System.out.println("Running url dispatcher."); new Consumer().run("url"); - System.out.println("Running http dispatcher."); - new Consumer().run("http"); +// Common Http Dispatcher do not have OperationMeta, can not use router. +// System.out.println("Running http dispatcher."); +// new Consumer().run("http"); System.out.println("All test case finished."); diff --git a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java index 613ba8a0274..ad774433957 100644 --- a/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java +++ b/demo/demo-edge/consumer/src/main/java/org/apache/servicecomb/demo/edge/consumer/EdgeServiceGovernanceTest.java @@ -31,6 +31,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.RestOperations; @@ -51,9 +52,9 @@ public void testRestTransport() throws Exception { prepareEdge("url"); // edge service do not support retry // testEdgeServiceRetry(); - - testEdgeServiceInstanceIsolation(); testEdgeServiceInstanceBulkhead(); + testEdgeServiceInstanceIsolation(); // may isolate instance for 5 seconds. + Thread.sleep(6000); // ensure isolation is open for new requests } private void testEdgeServiceInstanceBulkhead() throws Exception { @@ -98,6 +99,7 @@ private void testEdgeServiceInstanceIsolation() throws Exception { String url = edgePrefix + "/business/v2/testEdgeServiceInstanceIsolation"; CountDownLatch latch = new CountDownLatch(100); + AtomicBoolean expectedFailed404 = new AtomicBoolean(false); AtomicBoolean expectedFailed502 = new AtomicBoolean(false); AtomicBoolean expectedFailed503 = new AtomicBoolean(false); AtomicBoolean notExpectedFailed = new AtomicBoolean(false); @@ -113,15 +115,24 @@ public void run() { notExpectedFailed.set(true); } } catch (Exception e) { - if (!(e instanceof HttpServerErrorException)) { - notExpectedFailed.set(true); - } else { + if (e instanceof HttpClientErrorException) { + // isolate 2.0.0 and other instance will give 404 + if (((HttpClientErrorException) e).getStatusCode().value() == 404) { + expectedFailed404.set(true); + } else { + notExpectedFailed.set(true); + } + } else if (e instanceof HttpServerErrorException) { + // instance isolated and return 503 if (((HttpServerErrorException) e).getStatusCode().value() == 503) { expectedFailed503.set(true); } + // provider throw 502 exception to trigger instance isolation if (((HttpServerErrorException) e).getStatusCode().value() == 502) { expectedFailed502.set(true); } + } else { + notExpectedFailed.set(true); } } latch.countDown(); @@ -132,6 +143,7 @@ public void run() { } latch.await(20, TimeUnit.SECONDS); + TestMgr.check(true, expectedFailed404.get()); TestMgr.check(true, expectedFailed502.get()); TestMgr.check(true, expectedFailed503.get()); TestMgr.check(false, notExpectedFailed.get()); diff --git a/demo/demo-edge/consumer/src/main/resources/microservice.yaml b/demo/demo-edge/consumer/src/main/resources/microservice.yaml index 893ace0961c..953b1e567e4 100644 --- a/demo/demo-edge/consumer/src/main/resources/microservice.yaml +++ b/demo/demo-edge/consumer/src/main/resources/microservice.yaml @@ -29,17 +29,3 @@ servicecomb: chain: Consumer: default: loadbalance - -# TODO: use router to replace version-rule -# routeRule: -# business: | -# - precedence: 2 -# match: -# headers: -# x-url: -# prefix: "/business/v1" -# route: -# - weight: 100 -# tags: -# version: 1.1.0 - diff --git a/demo/demo-edge/edge-service/pom.xml b/demo/demo-edge/edge-service/pom.xml index 71ddba38440..31fdcff3874 100644 --- a/demo/demo-edge/edge-service/pom.xml +++ b/demo/demo-edge/edge-service/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT edge-service Java Chassis::Demo::Edge::Service @@ -49,28 +49,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptEdgeDispatcher.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptEdgeDispatcher.java index 0cacd364f0e..76ea1840402 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptEdgeDispatcher.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/EncryptEdgeDispatcher.java @@ -24,6 +24,7 @@ import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; +import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.edge.core.AbstractEdgeDispatcher; import org.apache.servicecomb.edge.core.EdgeInvocationCreator; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; @@ -122,7 +123,7 @@ public CompletableFuture createAsync() { return result.whenComplete((invocation, throwable) -> { if (throwable == null) { - invocation.getHandlerContext().put("encryptContext", encryptContext); + invocation.getHandlerContext().put(EdgeConst.ENCRYPT_CONTEXT, encryptContext); } }); } diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/DecodeBodyFilter.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/DecodeBodyFilter.java index 997ed53e686..d9e0f332318 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/DecodeBodyFilter.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/DecodeBodyFilter.java @@ -21,13 +21,13 @@ import org.apache.servicecomb.common.rest.codec.RestObjectMapperFactory; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.stereotype.Component; @@ -35,11 +35,11 @@ import com.fasterxml.jackson.databind.type.TypeFactory; @Component -public class DecodeBodyFilter implements ConsumerFilter { +public class DecodeBodyFilter extends AbstractFilter implements EdgeFilter { private JavaType bodyType = TypeFactory.defaultInstance().constructMapType(Map.class, String.class, String[].class); @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1790; } diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureRequestFilter.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureRequestFilter.java index 4e33f979afd..42443b555b4 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureRequestFilter.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureRequestFilter.java @@ -23,13 +23,13 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,11 +39,11 @@ import com.google.common.hash.Hashing; @Component -public class EdgeSignatureRequestFilter implements ConsumerFilter { +public class EdgeSignatureRequestFilter extends AbstractFilter implements EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(EdgeSignatureRequestFilter.class); @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1800; } diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureResponseFilter.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureResponseFilter.java index 0b0ae444a34..a3909d75540 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureResponseFilter.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/EdgeSignatureResponseFilter.java @@ -20,13 +20,13 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.authentication.encrypt.Hcr; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,17 +38,17 @@ import io.vertx.core.buffer.Buffer; @Component -public class EdgeSignatureResponseFilter implements ConsumerFilter { +public class EdgeSignatureResponseFilter extends AbstractFilter implements EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(EdgeSignatureResponseFilter.class); @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1991; } @Override public String getName() { - return "test-edge-signature-request"; + return "test-edge-signature-response"; } @Override diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/UserIdFilter.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/UserIdFilter.java index f6a54cacbc0..fd20c0e6d7e 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/UserIdFilter.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/encrypt/filter/UserIdFilter.java @@ -19,19 +19,19 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.demo.edge.service.encrypt.EncryptContext; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.stereotype.Component; @Component -public class UserIdFilter implements ConsumerFilter { +public class UserIdFilter extends AbstractFilter implements EdgeFilter { @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1790; } diff --git a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/AuthHandler.java b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/AuthHandler.java index ebe97f3bf85..4aeb44be08a 100644 --- a/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/AuthHandler.java +++ b/demo/demo-edge/edge-service/src/main/java/org/apache/servicecomb/demo/edge/service/handler/AuthHandler.java @@ -20,12 +20,12 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.demo.edge.service.EdgeConst; import org.apache.servicecomb.provider.pojo.Invoker; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; @@ -35,7 +35,7 @@ import jakarta.ws.rs.core.Response.Status; @Component -public class AuthHandler implements ConsumerFilter { +public class AuthHandler extends AbstractFilter implements EdgeFilter { private static Logger LOGGER = LoggerFactory.getLogger(AuthHandler.class); private static Auth auth; @@ -45,7 +45,7 @@ public class AuthHandler implements ConsumerFilter { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1980; } diff --git a/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper b/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper index d8a88a70544..dcc25e0a838 100644 --- a/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper +++ b/demo/demo-edge/edge-service/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.ResponseMetaMapper @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.demo.edge.service.CustomResponseMetaMapper \ No newline at end of file +org.apache.servicecomb.demo.edge.service.CustomResponseMetaMapper diff --git a/demo/demo-edge/edge-service/src/main/resources/microservice.yaml b/demo/demo-edge/edge-service/src/main/resources/microservice.yaml index 251b32ca010..56403677e38 100644 --- a/demo/demo-edge/edge-service/src/main/resources/microservice.yaml +++ b/demo/demo-edge/edge-service/src/main/resources/microservice.yaml @@ -57,14 +57,10 @@ servicecomb: prefixSegmentCount: 1 path: "/url/business/v1/.*" microserviceName: business - # not supported since 3.0.0, use router - # versionRule: 1.0.0-2.0.0 businessV2: prefixSegmentCount: 1 path: "/url/business/v2/.*" microserviceName: business - # not supported since 3.0.0, use router - # versionRule: 2.0.0-3.0.0 http: enabled: true mappings: @@ -72,22 +68,16 @@ servicecomb: prefixSegmentCount: 1 path: "/http/business/v2/.*" microserviceName: business - # not supported since 3.0.0, use router - # versionRule: 2.0.0 businessV1: prefixSegmentCount: 1 path: "/http/business/v1/add.*" microserviceName: business - # not supported since 3.0.0, use router - # versionRule: 1.0.0-1.2.0 businessV1_1: prefixSegmentCount: 1 path: "/http/business/v1/dec.*" microserviceName: business - # not supported since 3.0.0, use router - # versionRule: 1.1.0 -# 服务治理配置 + # 服务治理配置 matchGroup: testEdgeServiceRetry: | matches: @@ -101,11 +91,11 @@ servicecomb: matches: - apiPath: exact: "/business/v2/testEdgeServiceInstanceBulkhead" -# retry not supported now -# retry: -# testEdgeServiceRetry: | -# maxAttempts: 2 -# retryOnSame: 0 + # retry not supported now + # retry: + # testEdgeServiceRetry: | + # maxAttempts: 2 + # retryOnSame: 0 instanceIsolation: testEdgeServiceInstanceIsolation: | minimumNumberOfCalls: 10 @@ -114,9 +104,34 @@ servicecomb: failureRateThreshold: 50 slowCallRateThreshold: 100 slowCallDurationThreshold: 3000 - waitDurationInOpenState: 10000 + waitDurationInOpenState: 3000 permittedNumberOfCallsInHalfOpenState: 10 instanceBulkhead: testEdgeServiceInstanceBulkhead: | maxConcurrentCalls: 1 maxWaitDuration: 1 + + # enable router for edge service + router: + type: router + routeRule: + business: | + - precedence: 2 + match: + apiPath: + prefix: "/business/v2" + route: + - weight: 100 + tags: + version: 2.0.0 + - precedence: 1 + match: + apiPath: + prefix: "/business/v1/dec" + route: + - weight: 50 + tags: + version: 1.1.0 + - weight: 50 + tags: + version: 2.0.0 diff --git a/demo/demo-edge/model/pom.xml b/demo/demo-edge/model/pom.xml index 55e3a19e0e0..3adda2c13e4 100644 --- a/demo/demo-edge/model/pom.xml +++ b/demo/demo-edge/model/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT model Java Chassis::Demo::Edge::Model diff --git a/demo/demo-edge/pom.xml b/demo/demo-edge/pom.xml index 0164da9b7cd..34cdcd1fccc 100644 --- a/demo/demo-edge/pom.xml +++ b/demo/demo-edge/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-edge Java Chassis::Demo::Edge diff --git a/demo/demo-etcd/README.md b/demo/demo-etcd/README.md new file mode 100644 index 00000000000..046bec6c968 --- /dev/null +++ b/demo/demo-etcd/README.md @@ -0,0 +1,5 @@ +# Notice + +This integration tests is designed for Etcd registry and configuration. And extra test cases include: + +* Test cases related to SpringMVC annotations that demo-springmvc can not cover. diff --git a/demo/demo-etcd/consumer/pom.xml b/demo/demo-etcd/consumer/pom.xml new file mode 100644 index 00000000000..20a03319d43 --- /dev/null +++ b/demo/demo-etcd/consumer/pom.xml @@ -0,0 +1,114 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.4.0-SNAPSHOT + + + etcd-consumer + Java Chassis::Demo::Etcd::CONSUMER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + com.google.protobuf + protobuf-java + 3.25.5 + runtime + + + org.apache.servicecomb + registry-etcd + + + org.apache.servicecomb + config-etcd + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java new file mode 100644 index 00000000000..f71738a9ed6 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; +import io.vertx.core.http.WebSocket; + +@RestSchema(schemaId = "ClientWebsocketController") +@RequestMapping(path = "/ws") +public class ClientWebsocketController { + interface ProviderService { + WebSocket websocket(); + } + + @RpcReference(schemaId = "WebsocketController", microserviceName = "provider") + private ProviderService providerService; + + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + WebSocket providerWebSocket = providerService.websocket(); + providerWebSocket.closeHandler(v -> serverWebsocket.close()); + providerWebSocket.textMessageHandler(m -> { + serverWebsocket.writeTextMessage(m); + }); + serverWebsocket.textMessageHandler(m -> { + providerWebSocket.writeTextMessage(m); + }); + } +} diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java new file mode 100644 index 00000000000..e66fb2cb3d0 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +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.ResponseBody; + +@RestSchema(schemaId = "ConsumerController") +@RequestMapping(path = "/") +public class ConsumerController { + @RpcReference(schemaId = "ProviderController", microserviceName = "provider") + private ProviderService providerService; + + // consumer service which delegate the implementation to provider service. + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return providerService.sayHello(name); + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return providerService.getConfig(key); + } + + @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public User testContentType(@RequestBody User user) { + return providerService.testContentType(user); + } +} diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java new file mode 100644 index 00000000000..f0e894d6745 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") + private IHeaderParamWithListSchemaSpringMvc provider; + + @Override + public String headerListDefault(List headerList) { + return provider.headerListDefault(headerList); + } + + @Override + public String headerListCSV(List headerList) { + return provider.headerListCSV(headerList); + } + + @Override + public String headerListMULTI(List headerList) { + return provider.headerListMULTI(headerList); + } + + @Override + public String headerListSSV(List headerList) { + return provider.headerListSSV(headerList); + } + + @Override + public String headerListPIPES(List headerList) { + return provider.headerListPIPES(headerList); + } +} diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java new file mode 100644 index 00000000000..aa169801cf7 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +public class ConsumerReactiveStreamController { + interface ProviderReactiveStreamController { + Publisher sseString(); + + Publisher sseModel(); + } + + @RpcReference(microserviceName = "provider", schemaId = "ReactiveStreamController") + ProviderReactiveStreamController controller; + + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseString() { + return controller.sseString(); + } + + @GetMapping("/sseModel") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseModel() { + return controller.sseModel(); + } +} diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java new file mode 100644 index 00000000000..2101e6512f8 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/EtcdConsumerApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class EtcdConsumerApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(EtcdConsumerApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/core/src/main/java/org/apache/servicecomb/core/ConsumerProvider.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java similarity index 77% rename from core/src/main/java/org/apache/servicecomb/core/ConsumerProvider.java rename to demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java index b4b69a3cc51..2899546c752 100644 --- a/core/src/main/java/org/apache/servicecomb/core/ConsumerProvider.java +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java @@ -15,14 +15,12 @@ * limitations under the License. */ -package org.apache.servicecomb.core; +package org.apache.servicecomb.samples; -/** - * consumer端对业务的接口,不同场景是完全不同的 - * 所以这里只定义consumer对core的接口 - */ -public interface ConsumerProvider { - String getName(); +public interface ProviderService { + String sayHello(String name); + + String getConfig(String key); - void init(); + User testContentType(User user); } diff --git a/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ff421930ef2 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/java/org/apache/servicecomb/samples/User.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-etcd/consumer/src/main/resources/application.yml b/demo/demo-etcd/consumer/src/main/resources/application.yml new file mode 100644 index 00000000000..e0db5f1434e --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/resources/application.yml @@ -0,0 +1,35 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-etcd + version: 0.0.1 + name: consumer + properties: + group: red + registry: + etcd: + connectString: http://127.0.0.1:2379 + config: + etcd: + connectString: http://127.0.0.1:2379 + + rest: + address: 0.0.0.0:9092?websocketEnabled=true + server: + websocket-prefix: /ws diff --git a/demo/demo-etcd/consumer/src/main/resources/log4j2.xml b/demo/demo-etcd/consumer/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-etcd/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-etcd/gateway/pom.xml b/demo/demo-etcd/gateway/pom.xml new file mode 100644 index 00000000000..0b52381a492 --- /dev/null +++ b/demo/demo-etcd/gateway/pom.xml @@ -0,0 +1,113 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.4.0-SNAPSHOT + + + etcd-gateway + Java Chassis::Demo::Etcd::GATEWAY + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + edge-core + + + com.google.protobuf + protobuf-java + 3.25.5 + runtime + + + org.apache.servicecomb + registry-etcd + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java b/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java new file mode 100644 index 00000000000..7d58caafd9d --- /dev/null +++ b/demo/demo-etcd/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class GatewayApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-etcd/gateway/src/main/resources/application.yml b/demo/demo-etcd/gateway/src/main/resources/application.yml new file mode 100644 index 00000000000..f526eb3de70 --- /dev/null +++ b/demo/demo-etcd/gateway/src/main/resources/application.yml @@ -0,0 +1,53 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-etcd + version: 0.0.1 + name: gateway + registry: + etcd: + enabled: true + connectString: http://127.0.0.1:2379 + + rest: + address: 0.0.0.0:9090?websocketEnabled=true + server: + websocket-prefix: /ws + + http: + dispatcher: + edge: + default: + enabled: false + url: + enabled: true + pattern: /(.*) + mappings: + consumer: + prefixSegmentCount: 0 + path: "/.*" + microserviceName: consumer + versionRule: 0.0.0+ + websocket: + mappings: + consumer: + prefixSegmentCount: 0 + path: "/ws/.*" + microserviceName: consumer + versionRule: 0.0.0+ diff --git a/demo/demo-etcd/gateway/src/main/resources/log4j2.xml b/demo/demo-etcd/gateway/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-etcd/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-etcd/pom.xml b/demo/demo-etcd/pom.xml new file mode 100644 index 00000000000..c615edd88b1 --- /dev/null +++ b/demo/demo-etcd/pom.xml @@ -0,0 +1,61 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-etcd + Java Chassis::Demo::Etcd + pom + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + solution-basic + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + + + + + provider + consumer + gateway + test-client + + + diff --git a/demo/demo-etcd/provider/pom.xml b/demo/demo-etcd/provider/pom.xml new file mode 100644 index 00000000000..8d8c005a1e1 --- /dev/null +++ b/demo/demo-etcd/provider/pom.xml @@ -0,0 +1,123 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.4.0-SNAPSHOT + + + etcd-provider + Java Chassis::Demo::Etcd::PROVIDER + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + com.google.protobuf + protobuf-java + 3.25.5 + runtime + + + org.apache.servicecomb + registry-etcd + + + org.apache.servicecomb + config-etcd + + + + io.reactivex.rxjava3 + rxjava + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java new file mode 100644 index 00000000000..388b962dc61 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/EtcdProviderApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class EtcdProviderApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(EtcdProviderApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java new file mode 100644 index 00000000000..8a773f91c72 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class HeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @Override + public String headerListDefault(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListCSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListMULTI(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListSSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListPIPES(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } +} diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java new file mode 100644 index 00000000000..677c20ed106 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +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.ResponseBody; + +@RestSchema(schemaId = "ProviderController") +@RequestMapping(path = "/") +public class ProviderController implements InitializingBean { + private Environment environment; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + // a very simple service to echo the request parameter + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { +// return "Hello " + environment.getProperty("servicecomb.rest.address"); + return "Hello " + name; + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return environment.getProperty(key); + } + + @PostMapping(path = "/testContentType", consumes = MediaType.APPLICATION_JSON_VALUE) + @ResponseBody + public User testContentType(@RequestBody User user) { + return user; + } + + @Override + public void afterPropertiesSet() throws Exception { + } +} diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java new file mode 100644 index 00000000000..8108d15fd46 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.reactivex.rxjava3.core.Flowable; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +@Transport(name = CoreConst.RESTFUL) +public class ReactiveStreamController { + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + public Publisher sseString() { + return Flowable.fromArray("a", "b", "c"); + } + + @GetMapping("/sseModel") + public Publisher sseModel() { + return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) + .map(item -> new Model("jack", item.intValue())); + } +} diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ca12402d672 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/User.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java new file mode 100644 index 00000000000..3bc88c64ac5 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; + +@RestSchema(schemaId = "WebsocketController") +@RequestMapping(path = "/ws") +public class WebsocketController { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + // Client may have not registered message handler, and messages sent may get lost. + // So we sleep for a while to send message. + AtomicInteger receiveCount = new AtomicInteger(0); + serverWebsocket.textMessageHandler(s -> { + receiveCount.getAndIncrement(); + }); + serverWebsocket.closeHandler((v) -> System.out.println("closed")); + + new Thread(() -> { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + serverWebsocket.writeTextMessage("hello"); + + for (int i = 0; i < 5; i++) { + serverWebsocket.writeTextMessage("hello " + i); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + serverWebsocket.writeTextMessage("total " + receiveCount.get()); + serverWebsocket.close(); + }).start(); + } +} diff --git a/demo/demo-etcd/provider/src/main/resources/application.yml b/demo/demo-etcd/provider/src/main/resources/application.yml new file mode 100644 index 00000000000..da2051cd77e --- /dev/null +++ b/demo/demo-etcd/provider/src/main/resources/application.yml @@ -0,0 +1,55 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- +# spring boot configurations +servicecomb: + service: + application: demo-etcd + version: 0.0.1 + name: provider + properties: + group: green + registry: + etcd: + connectString: http://127.0.0.1:2379 + config: + etcd: + connectString: http://127.0.0.1:2379 + instance-tag: tag1 + + rest: + address: 0.0.0.0:9094?websocketEnabled=true + server: + websocket-prefix: /ws + + cors: + enabled: true + origin: "*" + allowCredentials: false + allowedMethod: "*" + maxAge: 3600 + + +key1: 1 +key2: 3 +key3: 5 + +test1: env +test2: applition +test3: service +test4: version +test5: tag diff --git a/demo/demo-etcd/provider/src/main/resources/log4j2.xml b/demo/demo-etcd/provider/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-etcd/provider/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-etcd/test-client/pom.xml b/demo/demo-etcd/test-client/pom.xml new file mode 100644 index 00000000000..ab839d762f5 --- /dev/null +++ b/demo/demo-etcd/test-client/pom.xml @@ -0,0 +1,211 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-etcd + 3.4.0-SNAPSHOT + + + etcd-test-client + Java Chassis::Demo::Etcd::TEST-CLIENT + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + registry-local + + + io.etcd + jetcd-core + + + com.google.protobuf + protobuf-java + 3.25.5 + runtime + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + bitnami/etcd:3.5.16 + etcd + + alias + + etcdserver + + + 2379 + + + + + + 2379:2379 + + + yes + + + + + etcd-provider:${project.version} + etcd-provider + + alias + + + -Dservicecomb.registry.etcd.connectString=http://etcd:2379 + -Dservicecomb.config.etcd.connectString=http://etcd:2379 + + /maven/maven/etcd-provider-${project.version}.jar + + + etcd:etcd + + + ServiceComb is ready + + + 9094 + + + + + + 9094:9094 + + + + + etcd-consumer:${project.version} + etcd-consumer + + alias + + + -Dservicecomb.registry.etcd.connectString=http://etcd:2379 + -Dservicecomb.config.etcd.connectString=http://etcd:2379 + + /maven/maven/etcd-consumer-${project.version}.jar + + + etcd:etcd + + + ServiceComb is ready + + + 9092 + + + + + + 9092:9092 + + + + + etcd-gateway:${project.version} + etcd-gateway + + alias + + + -Dservicecomb.registry.etcd.connectString=http://etcd:2379 + + /maven/maven/etcd-gateway-${project.version}.jar + + + etcd:etcd + + + ServiceComb is ready + + + 9090 + + + + + + 9090:9090 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java new file mode 100644 index 00000000000..2e9105cdd4a --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/Config.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +public interface Config { + String GATEWAY_URL = "http://localhost:9090"; +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java new file mode 100644 index 00000000000..92432e01f5f --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/EtcdConfigIT.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.ConditionWaiter.SleepUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +import io.etcd.jetcd.ByteSequence; +import io.etcd.jetcd.Client; + +@Component +public class EtcdConfigIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdConfigIT.class); + + @Override + public void testRestTransport() throws Exception { + + testEnvironment(); + testApplication(); + testService(); + testVersion(); + testTag(); + testOverride(); + } + + private void testOverride() { + + putValue("/servicecomb/config/environment/production/application2.properties", + "testValue=t1"); + putValue("/servicecomb/config/application/production/demo-etcd/application2.properties", + "testValue=t2"); + testGetConfig("testValue", "t2"); + putValue("/servicecomb/config/service/production/demo-etcd/provider/application2.properties", + "testValue=t3"); + testGetConfig("testValue", "t3"); + putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application2.properties", + "testValue=t4"); + testGetConfig("testValue", "t4"); + putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application2.properties", + "testValue=t5"); + testGetConfig("testValue", "t5"); + } + + private void testEnvironment() { + + putValue("/servicecomb/config/environment/production/application.properties", + "test1=env"); + putValue("/servicecomb/config/environment/production/application.properties", + "test1=env1"); + + testGetConfig("test1", "env1"); + } + + + private void testApplication() { + + putValue("/servicecomb/config/application/production/demo-etcd/application.properties", + "test2=applition"); + putValue("/servicecomb/config/application/production/demo-etcd/application.properties", + "test2=applition2"); + testGetConfig("test2", "applition2"); + } + + private void testService() { + + putValue("/servicecomb/config/service/production/demo-etcd/provider/application.properties", + "test3=service"); + putValue("/servicecomb/config/service/production/demo-etcd/provider/application.properties", + "test3=service3"); + testGetConfig("test3", "service3"); + } + + private void testVersion() { + + putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application.properties", + "test3=version"); + putValue("/servicecomb/config/version/production/demo-etcd/provider/0.0.1/application.properties", + "test4=version4"); + testGetConfig("test4", "version4"); + } + + private void testTag() { + + putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application.properties", + "test5=tag"); + putValue("/servicecomb/config/tag/production/demo-etcd/provider/0.0.1/tag1/application.properties", + "test5=tag5"); + testGetConfig("test5", "tag5"); + } + + + public void putValue(String key, String value) { + try (Client client = Client.builder().endpoints("http://localhost:2379").build()) { + + client.getKVClient().put( + ByteSequence.from(key, StandardCharsets.UTF_8), + ByteSequence.from(value, StandardCharsets.UTF_8) + ).get(); + + LOGGER.info("Value set successfully:{}", value); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void testGetConfig(String key, String expectValue) { + + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=" + key, String.class); + + for (int i = 0; i < 4; i++) { + if (StringUtils.equals(expectValue, result)) { + TestMgr.check(expectValue, result); + break; + } + SleepUtil.sleep(500, TimeUnit.MILLISECONDS); + } + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java new file mode 100644 index 00000000000..1b129acc2c6 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HeaderParamWithListSchemaIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHeaderListDefault(); + testHeaderListMulti(); + testHeaderListCSV(); + testHeaderListSSV(); + testHeaderListPipes(); + } + + // default to multi + private void testHeaderListDefault() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListPipes() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a|b|c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListSSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a b c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListCSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a,b,c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + + headers.add("headerList", "a, b, c"); + entity = new HttpEntity<>(headers); + result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListMulti() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java new file mode 100644 index 00000000000..97e883fb45f --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HelloWorldIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHelloWorld(); + testGetConfig(); + } + + private void testGetConfig() { + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key1", String.class); + TestMgr.check("1", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key2", String.class); + TestMgr.check("3", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key3", String.class); + TestMgr.check("5", result); + } + + private void testHelloWorld() { + String result = template + .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); + TestMgr.check("Hello World", result); + + // test trace id added + MultiValueMap headers = new HttpHeaders(); + headers.add("X-B3-TraceId", "81de2eb7691c2bbb"); + HttpEntity entity = new HttpEntity(headers); + ResponseEntity response = + template.exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class); + TestMgr.check(1, response.getHeaders().get("X-B3-TraceId").size()); + TestMgr.check("81de2eb7691c2bbb", response.getHeaders().getFirst("X-B3-TraceId")); + TestMgr.check("Hello World", response.getBody()); + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java new file mode 100644 index 00000000000..2bdd0ca42ca --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ProviderIT.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class ProviderIT implements CategorizedTestCase { + + RestOperations template = new RestTemplate(); + + private static final Logger LOGGER = LoggerFactory.getLogger(ProviderIT.class); + + @Override + public void testRestTransport() throws Exception { + + User user = getUser("Application/json"); + TestMgr.check(1L, user.getId()); + TestMgr.check("czd", user.getName()); + + User user2 = getUser("application/json"); + TestMgr.check(1L, user2.getId()); + TestMgr.check("czd", user2.getName()); + + User user3 = getUser("APPLICATION/JSON"); + TestMgr.check(1L, user3.getId()); + TestMgr.check("czd", user3.getName()); + } + + private User getUser(String contentType) throws IOException { + HttpHeaders headers = new HttpHeaders(); + headers.set("Content-Type", contentType); + + String requestBody = """ + { + "id": 1, + "name": "czd" + } + """; + + HttpEntity entity = new HttpEntity<>(requestBody, headers); + + String url = Config.GATEWAY_URL + "/testContentType"; + ResponseEntity response = template.exchange( + url, HttpMethod.POST, entity, String.class); + + User user = JsonUtils.readValue(response.getBody().getBytes(StandardCharsets.UTF_8), User.class); + return user; + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java new file mode 100644 index 00000000000..488a6cf712a --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient.Model; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +public class ReactiveStreamIT implements CategorizedTestCase { + @Autowired + @Qualifier("reactiveStreamProvider") + ReactiveStreamClient reactiveStreamProvider; + + @Autowired + @Qualifier("reactiveStreamGateway") + ReactiveStreamClient reactiveStreamGateway; + + @Override + public void testRestTransport() throws Exception { + testSseString(reactiveStreamProvider); + testSseModel(reactiveStreamProvider); + testSseString(reactiveStreamGateway); + testSseModel(reactiveStreamGateway); + } + + private void testSseModel(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseModel(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(Model s) { + buffer.append(s.getName()).append(s.getAge()); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); + } + + private void testSseString(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseString(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(String s) { + buffer.append(s); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("abc", buffer.toString()); + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java new file mode 100644 index 00000000000..26a2a491bbe --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class TestClientApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); + + run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java new file mode 100644 index 00000000000..3ee5db8c346 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.localregistry.RegistryBean; +import org.apache.servicecomb.localregistry.RegistryBean.Instance; +import org.apache.servicecomb.localregistry.RegistryBean.Instances; +import org.apache.servicecomb.provider.pojo.Invoker; +import org.reactivestreams.Publisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.WebSocket; + +@Configuration +public class ThirdSvcConfiguration { + @RequestMapping(path = "/ws") + public interface WebsocketClient { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + WebSocket websocket(); + } + + @RequestMapping(path = "/") + public interface ReactiveStreamClient { + class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + Publisher sseString(); + + @GetMapping("/sseModel") + Publisher sseModel(); + } + + @Bean + public RegistryBean providerServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .setAppId("demo-etcd") + .setServiceName("provider") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9094"))))); + } + + @Bean + public RegistryBean gatewayServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .addSchemaInterface("WebsocketController", WebsocketClient.class) + .setAppId("demo-etcd") + .setServiceName("gateway") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9090?websocketEnabled=true"))))); + } + + @Bean("reactiveStreamProvider") + public ReactiveStreamClient reactiveStreamProvider() { + return Invoker.createProxy("provider", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean("reactiveStreamGateway") + public ReactiveStreamClient reactiveStreamGateway() { + return Invoker.createProxy("gateway", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean + public WebsocketClient gatewayWebsocketClient() { + return Invoker.createProxy("gateway", "WebsocketController", WebsocketClient.class); + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/User.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/User.java new file mode 100644 index 00000000000..ff421930ef2 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/User.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +public class User { + + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java new file mode 100644 index 00000000000..ea7a7f45b15 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.WebsocketClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import io.vertx.core.http.WebSocket; + +@Component +public class WebsocketIT implements CategorizedTestCase { + @Autowired + private WebsocketClient websocketClient; + + @Override + public void testRestTransport() throws Exception { + StringBuffer sb = new StringBuffer(); + AtomicBoolean closed = new AtomicBoolean(false); + CountDownLatch latch = new CountDownLatch(1); + + WebSocket webSocket = websocketClient.websocket(); + webSocket.textMessageHandler(s -> { + sb.append(s); + sb.append(" "); + webSocket.writeTextMessage(s); + }); + webSocket.closeHandler(v -> { + closed.set(true); + latch.countDown(); + }); + latch.await(30, TimeUnit.SECONDS); + TestMgr.check(sb.toString(), "hello hello 0 hello 1 hello 2 hello 3 hello 4 total 6 "); + TestMgr.check(closed.get(), true); + } +} diff --git a/demo/demo-etcd/test-client/src/main/resources/application.yml b/demo/demo-etcd/test-client/src/main/resources/application.yml new file mode 100644 index 00000000000..adcf259e984 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/resources/application.yml @@ -0,0 +1,35 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-etcd + name: test-client + version: 0.0.1 + + rest: + address: 0.0.0.0:9097 # should be same with server.port to use web container + + config: + etcd: + instance-tag: tag1 + +test1: env +test2: applition +test3: service +test4: version +test5: tag diff --git a/demo/demo-etcd/test-client/src/main/resources/log4j2.xml b/demo/demo-etcd/test-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-etcd/test-client/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java b/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java new file mode 100644 index 00000000000..833feff87c0 --- /dev/null +++ b/demo/demo-etcd/test-client/src/test/java/org/apache/servicecomb/samples/EtcdIT.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = TestClientApplication.class) +public class EtcdIT { + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdIT.class); + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() { + try { + TestClientApplication.run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/demo-filter/filter-client/pom.xml b/demo/demo-filter/filter-client/pom.xml index f83de4e4cf4..6c39fb4f5ad 100644 --- a/demo/demo-filter/filter-client/pom.xml +++ b/demo/demo-filter/filter-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-filter - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT filter-client Java Chassis::Demo::Filter::Client @@ -56,28 +56,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/FilterClient.java b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/FilterClient.java index e5c0cf07eb5..ab454f7c9e2 100644 --- a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/FilterClient.java +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/FilterClient.java @@ -44,6 +44,6 @@ public static void main(String[] args) throws Exception { } public static void run() throws Exception { - CategorizedTestCaseRunner.runCategorizedTestCase("filterServer"); + CategorizedTestCaseRunner.runCategorizedTestCase("com.servicecomb.filterServer"); } } diff --git a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/ClientExceptionSchema.java b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/ClientExceptionSchema.java index 387d2c25dda..3d64b25b426 100644 --- a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/ClientExceptionSchema.java +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/ClientExceptionSchema.java @@ -40,7 +40,7 @@ interface IExceptionSchema { private RestOperations restTemplate = RestTemplateBuilder.create(); - @RpcReference(microserviceName = "filterServer", schemaId = "ExceptionSchema") + @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "ExceptionSchema") public void setExceptionSchema(IExceptionSchema exceptionSchema) { this.exceptionSchema = exceptionSchema; } @@ -48,7 +48,7 @@ public void setExceptionSchema(IExceptionSchema exceptionSchema) { @GetMapping(path = "/blockingExceptionRestTemplate") public boolean blockingExceptionRestTemplate() { return restTemplate.getForObject( - "servicecomb://filterServer/exception/blockingException", boolean.class); + "servicecomb://com.servicecomb.filterServer/exception/blockingException", boolean.class); } @GetMapping(path = "/blockingExceptionReference") @@ -58,7 +58,7 @@ public boolean blockingExceptionReference() { @GetMapping(path = "/blockingExceptionInvoker") public boolean blockingExceptionInvoker() { - return InvokerUtils.syncInvoke("filterServer", + return InvokerUtils.syncInvoke("com.servicecomb.filterServer", "ExceptionSchema", "blockingException", null, boolean.class); } diff --git a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/GovernanceConsumerSchema.java b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/GovernanceConsumerSchema.java new file mode 100644 index 00000000000..733d3ab1f54 --- /dev/null +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/GovernanceConsumerSchema.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.filter.client; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "GovernanceConsumerSchema") +@RequestMapping(path = "/govern", produces = MediaType.APPLICATION_JSON_VALUE) +public class GovernanceConsumerSchema { + interface GovernanceProviderSchemaInf { + boolean providerFlowControl(); + } + + @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "GovernanceProviderSchema") + private GovernanceProviderSchemaInf retrySchemaInf; + + + @GetMapping(path = "/edgeFlowControl") + public boolean edgeFlowControl() { + return true; + } + + @GetMapping(path = "/providerFlowControl") + public boolean providerFlowControl() { + return retrySchemaInf.providerFlowControl(); + } +} diff --git a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java index e101db5a86f..159c52fe340 100644 --- a/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java +++ b/demo/demo-filter/filter-client/src/main/java/org/apache/servicecomb/demo/filter/client/RetryClientSchema.java @@ -17,15 +17,19 @@ package org.apache.servicecomb.demo.filter.client; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.client.RestOperations; +import jakarta.ws.rs.core.Response.Status; + @RestSchema(schemaId = "RetryClientSchema") @RequestMapping(path = "/retry", produces = MediaType.APPLICATION_JSON_VALUE) public class RetryClientSchema { @@ -33,14 +37,35 @@ interface RetrySchemaInf { CompletableFuture successWhenRetryAsync(); } - @RpcReference(microserviceName = "filterServer", schemaId = "RetrySchema") + @RpcReference(microserviceName = "com.servicecomb.filterServer", schemaId = "RetrySchema") private RetrySchemaInf retrySchemaInf; RestOperations restTemplate = RestTemplateBuilder.create(); + private AtomicLong counter = new AtomicLong(0); + + @GetMapping(path = "/governance/edgeSuccessWhenRetry") + public boolean edgeSuccessWhenRetry() { + if (counter.getAndIncrement() % 3 != 0) { + throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later."); + } + return true; + } + + @GetMapping(path = "/governance/edgeSuccessWhenRetryAsync") + public CompletableFuture edgeSuccessWhenRetryAsync() { + CompletableFuture result = new CompletableFuture<>(); + if (counter.getAndIncrement() % 2 == 0) { + result.completeExceptionally(new InvocationException(Status.INTERNAL_SERVER_ERROR, "try again later.")); + } else { + result.complete(true); + } + return result; + } + @GetMapping(path = "/governance/successWhenRetry") public boolean successWhenRetry() { - return restTemplate.getForObject("servicecomb://filterServer/retry/governance/successWhenRetry", + return restTemplate.getForObject("servicecomb://com.servicecomb.filterServer/retry/governance/successWhenRetry", Boolean.class); } diff --git a/demo/demo-filter/filter-client/src/main/resources/microservice.yaml b/demo/demo-filter/filter-client/src/main/resources/microservice.yaml index 05e6f506d6c..72f90936c83 100644 --- a/demo/demo-filter/filter-client/src/main/resources/microservice.yaml +++ b/demo/demo-filter/filter-client/src/main/resources/microservice.yaml @@ -18,7 +18,8 @@ servicecomb: service: application: filtertest - name: filterClient + # test service name with dot + name: com.servicecomb.filterClient version: 0.0.1 registry: @@ -40,6 +41,7 @@ servicecomb: matches: - apiPath: prefix: "/retry/governance/" + retry: retry-governance: | maxAttempts: 2 diff --git a/demo/demo-filter/filter-edge/pom.xml b/demo/demo-filter/filter-edge/pom.xml index 8b5c8fe6ef2..798f7b7697e 100644 --- a/demo/demo-filter/filter-edge/pom.xml +++ b/demo/demo-filter/filter-edge/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-filter - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT filter-edge Java Chassis::Demo::Filter::Edge @@ -60,28 +60,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml b/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml index 623cb450258..128a825e6d6 100644 --- a/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml +++ b/demo/demo-filter/filter-edge/src/main/resources/microservice.yaml @@ -18,7 +18,8 @@ servicecomb: service: application: filtertest - name: filterEdge + # test service name with dot + name: com.servicecomb.filterEdge version: 0.0.1 registry: @@ -30,6 +31,11 @@ servicecomb: server: compression: true + edge: + filter: + addHeader: + allowedHeaders: X-B3-TraceId + invocation: exception: print-stack-trace: true @@ -48,4 +54,26 @@ servicecomb: filterClient: prefixSegmentCount: 0 path: "/retry/.*" - microserviceName: filterClient + microserviceName: com.servicecomb.filterClient + filterClient2: + prefixSegmentCount: 0 + path: "/govern/.*" + microserviceName: com.servicecomb.filterClient + matchGroup: + edgeFlowControl: | + matches: + - apiPath: + exact: "/govern/edgeFlowControl" + retry-governance: | + matches: + - apiPath: + prefix: "/retry/governance/" + rateLimiting: + edgeFlowControl: | + timeoutDuration: 0 + limitRefreshPeriod: 1000 + rate: 1 + retry: + retry-governance: | + maxAttempts: 2 + retryOnResponseStatus: [500] diff --git a/demo/demo-filter/filter-server/pom.xml b/demo/demo-filter/filter-server/pom.xml index 1122b26e15f..b3f257fd875 100644 --- a/demo/demo-filter/filter-server/pom.xml +++ b/demo/demo-filter/filter-server/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-filter - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT filter-server Java Chassis::Demo::Filter::Server @@ -51,28 +51,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/GovernanceProviderSchema.java b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/GovernanceProviderSchema.java new file mode 100644 index 00000000000..c2b5f37cadf --- /dev/null +++ b/demo/demo-filter/filter-server/src/main/java/org/apache/servicecomb/demo/filter/server/GovernanceProviderSchema.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.filter.server; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "GovernanceProviderSchema") +@RequestMapping(path = "/govern", produces = MediaType.APPLICATION_JSON_VALUE) +public class GovernanceProviderSchema { + @GetMapping(path = "/providerFlowControl") + public boolean providerFlowControl() { + return true; + } +} diff --git a/demo/demo-filter/filter-server/src/main/resources/microservice.yaml b/demo/demo-filter/filter-server/src/main/resources/microservice.yaml index e453353ce2a..bb96cf5f576 100644 --- a/demo/demo-filter/filter-server/src/main/resources/microservice.yaml +++ b/demo/demo-filter/filter-server/src/main/resources/microservice.yaml @@ -18,7 +18,8 @@ servicecomb: service: application: filtertest - name: filterServer + # test service name with dot + name: com.servicecomb.filterServer version: 0.0.1 registry: @@ -32,6 +33,17 @@ servicecomb: highway: address: 0.0.0.0:7070 + matchGroup: + providerFlowControl: | + matches: + - apiPath: + exact: "/govern/providerFlowControl" + rateLimiting: + providerFlowControl: | + timeoutDuration: 0 + limitRefreshPeriod: 1000 + rate: 1 + #########SSL options ssl.protocols: TLSv1.2 ssl.authPeer: true @@ -48,4 +60,3 @@ ssl.crl: revoke.crl ssl.sslCustomClass: org.apache.servicecomb.demo.DemoSSLCustom vertx.disableFileCPResolving: false # false: create the .vertx directory, true: do not create - diff --git a/demo/demo-filter/filter-tests/pom.xml b/demo/demo-filter/filter-tests/pom.xml index 4ade04f5ca3..3936a85f517 100644 --- a/demo/demo-filter/filter-tests/pom.xml +++ b/demo/demo-filter/filter-tests/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-filter - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT filter-tests Java Chassis::Demo::Filter::Tests @@ -184,19 +184,6 @@ io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestExceptionSchemaFromClient.java b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestExceptionSchemaFromClient.java index 9f9a211a334..e404b38b8a9 100644 --- a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestExceptionSchemaFromClient.java +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestExceptionSchemaFromClient.java @@ -29,7 +29,7 @@ public class TestExceptionSchemaFromClient implements CategorizedTestCase { RestOperations restTemplate = RestTemplateBuilder.create(); - private static final String SERVER = "servicecomb://filterClient"; + private static final String SERVER = "servicecomb://com.servicecomb.filterClient"; @Override public String getMicroserviceName() { diff --git a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestGovernanceSchemaFromEdge.java b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestGovernanceSchemaFromEdge.java new file mode 100644 index 00000000000..cd9958b7624 --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestGovernanceSchemaFromEdge.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.filter.tests; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class TestGovernanceSchemaFromEdge implements CategorizedTestCase { + interface GovernanceEdgeSchemaInf { + boolean edgeFlowControl(); + + boolean providerFlowControl(); + } + + @RpcReference(microserviceName = "com.servicecomb.filterEdge", schemaId = "GovernanceConsumerSchema") + private GovernanceEdgeSchemaInf retrySchemaInf; + + @Override + public String getMicroserviceName() { + return "com.servicecomb.filterEdge"; + } + + @Override + public void testRestTransport() throws Exception { + testEdgeFlowControl(); + testConsumerFlowControl(); + } + + private void testConsumerFlowControl() throws Exception { + CountDownLatch latch = new CountDownLatch(100); + AtomicBoolean expectedFailed = new AtomicBoolean(false); + AtomicBoolean notExpectedFailed = new AtomicBoolean(false); + + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + String name = "t-" + i + "-" + j; + new Thread(name) { + public void run() { + try { + boolean result = retrySchemaInf.providerFlowControl(); + if (!result) { + notExpectedFailed.set(true); + } + } catch (Exception e) { + if (!e.getMessage().contains("rate limited")) { + notExpectedFailed.set(true); + } + expectedFailed.set(true); + } + latch.countDown(); + } + }.start(); + } + Thread.sleep(100); + } + + latch.await(20, TimeUnit.SECONDS); + TestMgr.check(expectedFailed.get(), true); + TestMgr.check(notExpectedFailed.get(), false); + } + + private void testEdgeFlowControl() throws Exception { + CountDownLatch latch = new CountDownLatch(100); + AtomicBoolean expectedFailed = new AtomicBoolean(false); + AtomicBoolean notExpectedFailed = new AtomicBoolean(false); + + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + String name = "t-" + i + "-" + j; + new Thread(name) { + public void run() { + try { + boolean result = retrySchemaInf.edgeFlowControl(); + if (!result) { + notExpectedFailed.set(true); + } + } catch (Exception e) { + if (!e.getMessage().contains("rate limited")) { + notExpectedFailed.set(true); + } + expectedFailed.set(true); + } + latch.countDown(); + } + }.start(); + } + Thread.sleep(100); + } + + latch.await(20, TimeUnit.SECONDS); + TestMgr.check(expectedFailed.get(), true); + TestMgr.check(notExpectedFailed.get(), false); + } +} diff --git a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromClient.java b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromClient.java index dea3fe8e9ab..944f7831910 100644 --- a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromClient.java +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromClient.java @@ -33,16 +33,16 @@ interface RetrySchemaInf { CompletableFuture successWhenRetryAsync(); } - @RpcReference(microserviceName = "filterClient", schemaId = "RetryClientSchema") + @RpcReference(microserviceName = "com.servicecomb.filterClient", schemaId = "RetryClientSchema") private RetrySchemaInf retrySchemaInf; RestOperations restTemplate = RestTemplateBuilder.create(); - private static final String SERVER = "servicecomb://filterClient"; + private static final String SERVER = "servicecomb://com.servicecomb.filterClient"; @Override public String getMicroserviceName() { - return "filterClient"; + return "com.servicecomb.filterClient"; } @Override diff --git a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java index 422297296b1..b28eaff3cbc 100644 --- a/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java +++ b/demo/demo-filter/filter-tests/src/main/java/org/apache/servicecomb/demo/filter/tests/TestRetrySchemaFromEdge.java @@ -29,25 +29,29 @@ @Component public class TestRetrySchemaFromEdge implements CategorizedTestCase { interface RetrySchemaInf { + boolean edgeSuccessWhenRetry(); + boolean successWhenRetry(); + CompletableFuture edgeSuccessWhenRetryAsync(); + CompletableFuture successWhenRetryAsync(); } - @RpcReference(microserviceName = "filterEdge", schemaId = "RetryClientSchema") + @RpcReference(microserviceName = "com.servicecomb.filterEdge", schemaId = "RetryClientSchema") private RetrySchemaInf retrySchemaInf; RestOperations restTemplate = RestTemplateBuilder.create(); RestTemplate springRestTemplate = new RestTemplate(); - private static final String SERVER = "servicecomb://filterEdge"; + private static final String SERVER = "servicecomb://com.servicecomb.filterEdge"; private static final String EDGE_SERVER = "http://127.0.0.1:9090"; @Override public String getMicroserviceName() { - return "filterEdge"; + return "com.servicecomb.filterEdge"; } @Override @@ -55,6 +59,7 @@ public void testRestTransport() throws Exception { testRetryGovernanceFromEdgeDefaultDispatcher(); testRetryGovernanceRestTemplate(); testRetryGovernanceRpc(); + testEdgeRetryGovernanceRpc(); } private void testRetryGovernanceRpc() throws Exception { @@ -65,6 +70,14 @@ private void testRetryGovernanceRpc() throws Exception { TestMgr.check(retrySchemaInf.successWhenRetryAsync().get(), true); } + private void testEdgeRetryGovernanceRpc() throws Exception { + TestMgr.check(retrySchemaInf.edgeSuccessWhenRetry(), true); + TestMgr.check(retrySchemaInf.edgeSuccessWhenRetry(), true); + + TestMgr.check(retrySchemaInf.edgeSuccessWhenRetryAsync().get(), true); + TestMgr.check(retrySchemaInf.edgeSuccessWhenRetryAsync().get(), true); + } + private void testRetryGovernanceRestTemplate() { TestMgr.check(restTemplate.getForObject( SERVER + "/retry/governance/successWhenRetry", boolean.class), true); @@ -74,8 +87,8 @@ private void testRetryGovernanceRestTemplate() { private void testRetryGovernanceFromEdgeDefaultDispatcher() { TestMgr.check(springRestTemplate.getForObject( - EDGE_SERVER + "/service/filterClient/retry/governance/successWhenRetry", boolean.class), true); + EDGE_SERVER + "/service/com.servicecomb.filterClient/retry/governance/successWhenRetry", boolean.class), true); TestMgr.check(springRestTemplate.getForObject( - EDGE_SERVER + "/service/filterClient/retry/governance/successWhenRetry", boolean.class), true); + EDGE_SERVER + "/service/com.servicecomb.filterClient/retry/governance/successWhenRetry", boolean.class), true); } } diff --git a/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml b/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml index 75a286db44b..5694d54ac56 100644 --- a/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml +++ b/demo/demo-filter/filter-tests/src/main/resources/microservice.yaml @@ -18,7 +18,8 @@ servicecomb: service: application: filtertest - name: filterTests + # test service name with dot + name: com.servicecomb.filterTests version: 0.0.1 registry: @@ -28,13 +29,3 @@ servicecomb: invocation: exception: print-stack-trace: true - # test governance retry - matchGroup: - retry-governance: | - matches: - - apiPath: - prefix: "/retry/governance/" - retry: - retry-governance: | - maxAttempts: 2 - retryOnResponseStatus: [500] diff --git a/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/GovernanceConsumerSchema.yaml b/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/GovernanceConsumerSchema.yaml new file mode 100644 index 00000000000..b29502270ab --- /dev/null +++ b/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/GovernanceConsumerSchema.yaml @@ -0,0 +1,45 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.demo.filter.client.GovernanceConsumerSchema + version: 1.0.0 +servers: +- url: /govern +paths: + /edgeFlowControl: + get: + operationId: edgeFlowControl + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean + /providerFlowControl: + get: + operationId: providerFlowControl + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean +components: {} diff --git a/demo/demo-filter/filter-tests/src/main/resources/microservices/filterEdge/RetryClientSchema.yaml b/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/RetryClientSchema.yaml similarity index 75% rename from demo/demo-filter/filter-tests/src/main/resources/microservices/filterEdge/RetryClientSchema.yaml rename to demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/RetryClientSchema.yaml index 3b837600273..77517becf68 100644 --- a/demo/demo-filter/filter-tests/src/main/resources/microservices/filterEdge/RetryClientSchema.yaml +++ b/demo/demo-filter/filter-tests/src/main/resources/microservices/com.servicecomb.filterEdge/RetryClientSchema.yaml @@ -22,6 +22,26 @@ info: servers: - url: /retry paths: + /governance/edgeSuccessWhenRetry: + get: + operationId: edgeSuccessWhenRetry + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean + /governance/edgeSuccessWhenRetryAsync: + get: + operationId: edgeSuccessWhenRetryAsync + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: boolean /governance/successWhenRetry: get: operationId: successWhenRetry @@ -42,5 +62,4 @@ paths: application/json: schema: type: boolean -components: - schemas: {} \ No newline at end of file +components: {} diff --git a/demo/demo-filter/filter-tests/src/main/resources/registry.yaml b/demo/demo-filter/filter-tests/src/main/resources/registry.yaml index caad0222370..1e09e18eea2 100644 --- a/demo/demo-filter/filter-tests/src/main/resources/registry.yaml +++ b/demo/demo-filter/filter-tests/src/main/resources/registry.yaml @@ -14,12 +14,10 @@ ## See the License for the specific language governing permissions and ## limitations under the License. ## --------------------------------------------------------------------------- -filterEdge: +com.servicecomb.filterEdge: - id: "001" version: "1.0" appid: filtertest - schemaIds: - - RetryClientSchema instances: - endpoints: - rest://127.0.0.1:9090 diff --git a/demo/demo-filter/pom.xml b/demo/demo-filter/pom.xml index e53ec7a9434..45e0206faf1 100644 --- a/demo/demo-filter/pom.xml +++ b/demo/demo-filter/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-filter Java Chassis::Demo::Filter diff --git a/demo/demo-jaxrs/jaxrs-client/pom.xml b/demo/demo-jaxrs/jaxrs-client/pom.xml index cc907355746..d25a3cdecca 100644 --- a/demo/demo-jaxrs/jaxrs-client/pom.xml +++ b/demo/demo-jaxrs/jaxrs-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-jaxrs - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT jaxrs-client Java Chassis::Demo::JAXRS::Client @@ -45,24 +45,88 @@ jaxrs-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsClient.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsClient.java index 853b29232f3..2428e654774 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsClient.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/JaxrsClient.java @@ -388,7 +388,7 @@ private static void testValidatorAddFail(RestOperations template, String cseUrlP isExcep = true; TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); - // Message dependends on locale, so just check the short part. + // Message depended on locale, so just check the short part. // 'must be greater than or equal to 20', propertyPath=add.arg1, rootBeanClass=class org.apache.servicecomb.demo.jaxrs.server.Validator, messageTemplate='{jakarta.validation.constraints.Min.message}'}]] // ignored if (e.getErrorData() instanceof CommonExceptionData) { @@ -426,7 +426,7 @@ private static void testValidatorSayHiFail(RestOperations template, String cseUr isExcep = true; TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); - // Message dependends on locale, so just check the short part. + // Message depended on locale, so just check the short part. if (e.getErrorData() instanceof CommonExceptionData) { // highway decode/encode 'Object' with special type information, got runtime type CommonExceptionData data = (CommonExceptionData) e.getErrorData(); @@ -468,7 +468,7 @@ private static void testValidatorExchangeFail(RestOperations template, String cs isExcep = true; TestMgr.check(400, e.getStatus().getStatusCode()); TestMgr.check(Status.BAD_REQUEST, e.getReasonPhrase()); - // Message dependends on locale, so just check the short part. + // Message depended on locale, so just check the short part. if (e.getErrorData() instanceof CommonExceptionData) { // highway decode/encode 'Object' with special type information, got runtime type CommonExceptionData data = (CommonExceptionData) e.getErrorData(); diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java index 5d919a5bfab..66c5ab958b4 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/CodeFirstRestTemplateJaxrs.java @@ -57,6 +57,6 @@ private void test404(RestOperations template) { } } TestMgr.check(404, exception.getStatusCode().value()); - TestMgr.check("404 Not Found: \"{\"message\":\"Not Found\"}\"", exception.getMessage()); + TestMgr.check(true, exception.getMessage().contains("404 Not Found")); } } diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java index 104e5b8e46c..5eba5b94c95 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/MultiErrorCodeServiceClient.java @@ -70,7 +70,6 @@ private static void testErrorCodeWrongType() { headers.setContentType(MediaType.APPLICATION_JSON); String body = "{\"message\":\"hello\",\"code\":\"wrongType\""; HttpEntity entity = new HttpEntity<>(body, headers); - ResponseEntity result; try { template .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); @@ -83,7 +82,7 @@ private static void testErrorCodeWrongType() { try { template .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); - TestMgr.check(590, 200); + TestMgr.fail("expect failed."); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 400); } @@ -94,7 +93,7 @@ private static void testErrorCodeWrongType() { try { template .postForEntity(SERVER + "/MultiErrorCodeService/errorCode", entity, MultiResponse200.class); - TestMgr.check(590, 200); + TestMgr.fail("expect failed."); } catch (InvocationException e) { TestMgr.check(e.getStatusCode(), 400); } diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFormRequestSchema.java b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFormRequestSchema.java index 22163e2658e..591302058de 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFormRequestSchema.java +++ b/demo/demo-jaxrs/jaxrs-client/src/main/java/org/apache/servicecomb/demo/jaxrs/client/TestFormRequestSchema.java @@ -37,7 +37,7 @@ public class TestFormRequestSchema implements CategorizedTestCase { private static final Logger LOGGER = LoggerFactory.getLogger(TestFormRequestSchema.class); - private RestOperations restTemplate = RestTemplateBuilder.create(); + private final RestOperations restTemplate = RestTemplateBuilder.create(); @Override public void testRestTransport() throws Exception { @@ -45,19 +45,16 @@ public void testRestTransport() throws Exception { // testFormRequestFail会关闭连接,防止下个测试用例失败,睡眠2s Thread.sleep(2000); testFormRequestSuccess(); + testFormRequestBufferSize(); } // formSize is less than default maxFormAttributeSize , success - private void testFormRequestSuccess() throws Exception { + private void testFormRequestSuccess() { try { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap formData = new LinkedMultiValueMap<>(); - StringBuilder stringBuffer = new StringBuilder(); - for (int i = 0; i < 512; i++) { - stringBuffer.append("a"); - } - formData.add("formData", stringBuffer.toString()); + formData.add("formData", "a".repeat(512)); HttpEntity> requestEntity = new HttpEntity<>(formData, headers); ResponseEntity responseEntity = restTemplate .postForEntity("cse://jaxrs/form/formRequest", requestEntity, String.class); @@ -69,15 +66,11 @@ private void testFormRequestSuccess() throws Exception { } // formSize is greater than default maxFormAttributeSize , throw exception - private void testFormRequestFail() throws Exception { + private void testFormRequestFail() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap formData = new LinkedMultiValueMap<>(); - StringBuilder stringBuffer = new StringBuilder(); - for (int i = 0; i < 1688; i++) { - stringBuffer.append("a"); - } - formData.add("formData", String.valueOf(stringBuffer)); + formData.add("formData", "a".repeat(1688)); HttpEntity> requestEntity = new HttpEntity<>(formData, headers); try { restTemplate.postForEntity("cse://jaxrs/form/formRequest", requestEntity, String.class); @@ -86,4 +79,26 @@ private void testFormRequestFail() throws Exception { TestMgr.check(e.getMessage().contains("Internal Server Error"), true); } } + + private void testFormRequestBufferSize() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap formData = new LinkedMultiValueMap<>(); + // we can not test a situation for form exceed max buffer size, because the netty buffer is very + // big and the trunc can always be decoded and cached buffer size is always 0. + formData.add("F0123456789001234567890012345678900123456789001234567890" + + "0123456789001234567890012345678900123456789001234567890", "a".repeat(1020) + // we can not test a situation for form exceed max buffer size, because the netty buffer is very + // big and the trunc can always be decoded and cached buffer size is always 0. + ); + HttpEntity> requestEntity = new HttpEntity<>(formData, headers); + try { + ResponseEntity responseEntity = + restTemplate.postForEntity("cse://jaxrs/form/formLongName", requestEntity, String.class); + TestMgr.check(responseEntity.getBody(), "formRequest success : 1020"); + } catch (Throwable e) { + LOGGER.error("testFormRequestBufferSize-->", e); + TestMgr.failed("", e); + } + } } diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml b/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml +++ b/demo/demo-jaxrs/jaxrs-client/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml b/demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml index e375233af81..07493f07ef8 100644 --- a/demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml +++ b/demo/demo-jaxrs/jaxrs-client/src/main/resources/microservice.yaml @@ -91,4 +91,4 @@ jaxrstest: low-2.a.high-1.b: 3 low-2.a.high-2.b: 4 -# test configurations. \ No newline at end of file +# test configurations. diff --git a/demo/demo-jaxrs/jaxrs-server/pom.xml b/demo/demo-jaxrs/jaxrs-server/pom.xml index c153e3e255d..cc80ad1521c 100644 --- a/demo/demo-jaxrs/jaxrs-server/pom.xml +++ b/demo/demo-jaxrs/jaxrs-server/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-jaxrs - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT jaxrs-server Java Chassis::Demo::JAXRS::Server @@ -61,28 +61,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FormRequestSchema.java b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FormRequestSchema.java index f36e1791189..2670681217a 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FormRequestSchema.java +++ b/demo/demo-jaxrs/jaxrs-server/src/main/java/org/apache/servicecomb/demo/jaxrs/server/FormRequestSchema.java @@ -17,6 +17,8 @@ package org.apache.servicecomb.demo.jaxrs.server; +import org.apache.servicecomb.provider.rest.common.RestSchema; + import jakarta.ws.rs.Consumes; import jakarta.ws.rs.FormParam; import jakarta.ws.rs.POST; @@ -24,8 +26,6 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; -import org.apache.servicecomb.provider.rest.common.RestSchema; - @RestSchema(schemaId = "FormRequestSchema") @Path("/form") @Produces(MediaType.APPLICATION_JSON) @@ -38,4 +38,11 @@ public String formRequestSuccess(@FormParam("formData") String formData) throws return "formRequest success : " + formData.length(); } + @Path("/formLongName") + @POST + @Consumes(MediaType.APPLICATION_FORM_URLENCODED) + public String formLongName(@FormParam("F0123456789001234567890012345678900123456789001234567890" + + "0123456789001234567890012345678900123456789001234567890") String formData) throws Exception { + return "formRequest success : " + formData.length(); + } } diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher b/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher index afff0839503..a3a19c72aa3 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher +++ b/demo/demo-jaxrs/jaxrs-server/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.demo.jaxrs.server.injectBean.InjectBeanVertxHttpDispatcher \ No newline at end of file +org.apache.servicecomb.demo.jaxrs.server.injectBean.InjectBeanVertxHttpDispatcher diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/resources/log4j2.xml b/demo/demo-jaxrs/jaxrs-server/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/resources/log4j2.xml +++ b/demo/demo-jaxrs/jaxrs-server/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml b/demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml index 40cb567a9bb..475708a55b1 100644 --- a/demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml +++ b/demo/demo-jaxrs/jaxrs-server/src/main/resources/microservice.yaml @@ -27,7 +27,8 @@ servicecomb: rest: address: 0.0.0.0:8080 server: - maxFormAttributeSize: 1024 + maxFormAttributeSize: 1024 # for testing, and bigger than netty buffer allocator + maxFormBufferedBytes: 100 highway: address: 0.0.0.0:7070 uploads: diff --git a/demo/demo-jaxrs/pom.xml b/demo/demo-jaxrs/pom.xml index 14feb54d70d..014baac0318 100644 --- a/demo/demo-jaxrs/pom.xml +++ b/demo/demo-jaxrs/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-jaxrs Java Chassis::Demo::JAXRS diff --git a/demo/demo-local-registry/README.md b/demo/demo-local-registry/README.md index 7ace9bd1d27..a585a17b7ce 100644 --- a/demo/demo-local-registry/README.md +++ b/demo/demo-local-registry/README.md @@ -1,6 +1,5 @@ This demo is an integration test for testing local registration an discovery. -Local registration and discovery is a mechanism that do not using Service Center, -`Microservice` and `MicroserviceInstance` are constructed from configuration file. - +Local registration and discovery is a mechanism that do not using Service Center, +`Microservice` and `MicroserviceInstance` are constructed from configuration file. diff --git a/demo/demo-local-registry/demo-local-registry-client/pom.xml b/demo/demo-local-registry/demo-local-registry-client/pom.xml index c05982df19b..352e0cf26aa 100644 --- a/demo/demo-local-registry/demo-local-registry-client/pom.xml +++ b/demo/demo-local-registry/demo-local-registry-client/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-local-registry - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -45,24 +45,60 @@ demo-local-registry-server + + + + io.fabric8 + docker-maven-plugin + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + /maven/maven/${demo.service.name}-${project.version}.jar + + + ServiceComb is ready + + + 8080 + + + + + + 8080:8080 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config-local - ${project.version} - - - - diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/application.yml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/application.yml index ceb616b7ddf..fcf4bb18309 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/application.yml +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/application.yml @@ -22,4 +22,4 @@ servicecomb: service: application: demo-local-registry name: demo-local-registry-client - version: 0.0.1 \ No newline at end of file + version: 0.0.1 diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean/ServerEndpoint.yaml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean/ServerEndpoint.yaml index 91350734c85..2adeb24781c 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean/ServerEndpoint.yaml +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean/ServerEndpoint.yaml @@ -39,4 +39,4 @@ paths: schema: type: string components: - schemas: {} \ No newline at end of file + schemas: {} diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean2/ServerEndpoint.yaml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean2/ServerEndpoint.yaml index 91350734c85..2adeb24781c 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean2/ServerEndpoint.yaml +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server-bean2/ServerEndpoint.yaml @@ -39,4 +39,4 @@ paths: schema: type: string components: - schemas: {} \ No newline at end of file + schemas: {} diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server/ServerEndpoint.yaml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server/ServerEndpoint.yaml index 91350734c85..2adeb24781c 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server/ServerEndpoint.yaml +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/microservices/demo-local-registry-server/ServerEndpoint.yaml @@ -39,4 +39,4 @@ paths: schema: type: string components: - schemas: {} \ No newline at end of file + schemas: {} diff --git a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml index 2ad3c23269f..cb5a3bcd4d9 100644 --- a/demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml +++ b/demo/demo-local-registry/demo-local-registry-client/src/main/resources/registry.yaml @@ -24,4 +24,4 @@ demo-local-registry-server: - CodeFirstEndpoint instances: - endpoints: - - rest://localhost:8080 \ No newline at end of file + - rest://localhost:8080 diff --git a/demo/demo-local-registry/demo-local-registry-server/pom.xml b/demo/demo-local-registry/demo-local-registry-server/pom.xml index 0c0c8a2398b..8832ac2dc8a 100644 --- a/demo/demo-local-registry/demo-local-registry-server/pom.xml +++ b/demo/demo-local-registry/demo-local-registry-server/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-local-registry - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -55,28 +55,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/CodeFirstEndpoint.java b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/CodeFirstEndpoint.java index 7a01fcbddff..06fdf6b7939 100644 --- a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/CodeFirstEndpoint.java +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/CodeFirstEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.localRegistryServer; -import jakarta.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "CodeFirstEndpoint") @RequestMapping(path = "/register/url/codeFirst", produces = MediaType.APPLICATION_JSON) public class CodeFirstEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(CodeFirstEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerEndpoint.java b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerEndpoint.java index be39f3a7989..2db9d87dd05 100644 --- a/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerEndpoint.java +++ b/demo/demo-local-registry/demo-local-registry-server/src/main/java/org/apache/servicecomb/demo/localRegistryServer/ServerEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.localRegistryServer; -import jakarta.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-local-registry/pom.xml b/demo/demo-local-registry/pom.xml index c849e9b8310..c33eebf22e7 100644 --- a/demo/demo-local-registry/pom.xml +++ b/demo/demo-local-registry/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-local-registry Java Chassis::Demo::Local Registry diff --git a/demo/demo-multi-registries/README.md b/demo/demo-multi-registries/README.md index deafa597407..762bd27eba3 100644 --- a/demo/demo-multi-registries/README.md +++ b/demo/demo-multi-registries/README.md @@ -1,4 +1,4 @@ # About * This demo enables both local-registry and sc-registry. -* This demo using local-registry to call service center api. +* This demo using local-registry to call service center api. diff --git a/demo/demo-multi-registries/demo-multi-registries-client/pom.xml b/demo/demo-multi-registries/demo-multi-registries-client/pom.xml index 3ef75747c5e..3577950317f 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/pom.xml +++ b/demo/demo-multi-registries/demo-multi-registries-client/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-multi-registries - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -45,24 +45,88 @@ demo-multi-registries-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/microservices/thirdParty-service-center/ServiceCenterEndpoint.yaml b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/microservices/thirdParty-service-center/ServiceCenterEndpoint.yaml index 6e88a8b56bf..88332e3259a 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/microservices/thirdParty-service-center/ServiceCenterEndpoint.yaml +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/microservices/thirdParty-service-center/ServiceCenterEndpoint.yaml @@ -59,4 +59,4 @@ paths: schema: type: object components: - schemas: {} \ No newline at end of file + schemas: {} diff --git a/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/registry.yaml b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/registry.yaml index 377cc4e17a7..9d39b1b8bd1 100644 --- a/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/registry.yaml +++ b/demo/demo-multi-registries/demo-multi-registries-client/src/main/resources/registry.yaml @@ -32,4 +32,4 @@ thirdParty-no-schema-server: - ServerEndpoint # not in local , discovery from demo-multi-registries-server. Not supported since 3.0.0 instances: - endpoints: - - rest://localhost:8080 # actually will invoke to demo-multi-registries-server \ No newline at end of file + - rest://localhost:8080 # actually will invoke to demo-multi-registries-server diff --git a/demo/demo-multi-registries/demo-multi-registries-server/pom.xml b/demo/demo-multi-registries/demo-multi-registries-server/pom.xml index 27e1f77f0c4..4a0b37f4433 100644 --- a/demo/demo-multi-registries/demo-multi-registries-server/pom.xml +++ b/demo/demo-multi-registries/demo-multi-registries-server/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-multi-registries - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -55,28 +55,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerEndpoint.java b/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerEndpoint.java index 03ed503e630..eb594aaba57 100644 --- a/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerEndpoint.java +++ b/demo/demo-multi-registries/demo-multi-registries-server/src/main/java/org/apache/servicecomb/demo/registry/ServerEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.registry; -import jakarta.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-multi-registries/pom.xml b/demo/demo-multi-registries/pom.xml index 2ef1efe1cfc..13f34c9c7af 100644 --- a/demo/demo-multi-registries/pom.xml +++ b/demo/demo-multi-registries/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-multi-registries Java Chassis::Demo::Multi Registry diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml b/demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml index eabb0dd8058..2669f28a874 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-multi-service-center - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -177,21 +177,6 @@ io.fabric8 docker-maven-plugin - - org.apache.maven.plugins - maven-failsafe-plugin - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java index 85f041034cf..3b11e0582d8 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/RegistryClientTest.java @@ -42,6 +42,8 @@ import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.SchemaInfo; import org.apache.servicecomb.service.center.client.model.ServiceCenterConfiguration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import com.google.common.base.Charsets; @@ -58,14 +60,21 @@ public class RegistryClientTest implements CategorizedTestCase { // auto test only tests 'hasRegistered=false', can run this client many times to test 'hasRegistered=true' private boolean hasRegistered = true; + private final Environment environment; + + @Autowired + public RegistryClientTest(Environment environment) { + this.environment = environment; + } + @Override public void testRestTransport() throws Exception { - ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager("default", Arrays.asList("http://127.0.0.1:30100"), - new EventBus()); + ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager("default", + Arrays.asList("http://127.0.0.1:30100"), new EventBus(), "", ""); SSLProperties sslProperties = new SSLProperties(); sslProperties.setEnabled(false); ServiceCenterClient serviceCenterClient = new ServiceCenterClient(addressManager, sslProperties, - new DefaultRequestAuthHeaderProvider(), "default", null); + new DefaultRequestAuthHeaderProvider(), "default", null, environment); EventBus eventBus = new SimpleEventBus(); ServiceCenterConfiguration serviceCenterConfiguration = new ServiceCenterConfiguration(); ServiceCenterRegistration serviceCenterRegistration = new ServiceCenterRegistration(serviceCenterClient, diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Configuration.java b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Configuration.java index 754134c4a5c..db7fc11df06 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Configuration.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/java/org/apache/servicecomb/demo/multiServiceCenterClient/SC2Configuration.java @@ -48,8 +48,8 @@ public ServiceCenterWatch serviceCenterWatch2( @Bean public ServiceCenterClient serviceCenterClient2( @Qualifier("scConfigurationProperties2") SCConfigurationProperties scConfigurationProperties2, - List authHeaderProviders, Environment environment) { - return SCClientUtils.serviceCenterClient(scConfigurationProperties2, authHeaderProviders, environment); + Environment environment) { + return SCClientUtils.serviceCenterClient(scConfigurationProperties2, environment); } @Bean diff --git a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/application.yml b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/application.yml index c07025d4cc5..6daa76e8dee 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/application.yml +++ b/demo/demo-multi-service-center/demo-multi-service-center-client/src/main/resources/application.yml @@ -28,4 +28,4 @@ servicecomb: transport: eventloop: - size: 6 \ No newline at end of file + size: 6 diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverA/pom.xml b/demo/demo-multi-service-center/demo-multi-service-center-serverA/pom.xml index af71275cbd8..ddfd3570a43 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverA/pom.xml +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-multi-service-center - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -55,28 +55,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerEndpoint.java b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerEndpoint.java index d789dab7cb4..16fdb41bdde 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerEndpoint.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverA/src/main/java/org/apache/servicecomb/demo/multiServiceCenter/ServerEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.multiServiceCenter; -import jakarta.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/pom.xml b/demo/demo-multi-service-center/demo-multi-service-center-serverB/pom.xml index 51ad2bf944c..66dfd58d9b4 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverB/pom.xml +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-multi-service-center - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -55,28 +55,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerEndpoint.java b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerEndpoint.java index 3822ffd5fbb..d14c1d4a7b0 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerEndpoint.java +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/java/org/apache/servicecomb/demo/multiServiceCenterServerB/ServerEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.multiServiceCenterServerB; -import jakarta.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.yml b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.yml index 17da23d103c..e9850bc8c23 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.yml +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/application.yml @@ -33,4 +33,3 @@ demo.multi.service.center.serverB: key1: key1-boot key4: key4-boot key5: key5 - diff --git a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/microservice.yaml b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/microservice.yaml index 52cd6c8eab1..2164cf6ac9f 100644 --- a/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/microservice.yaml +++ b/demo/demo-multi-service-center/demo-multi-service-center-serverB/src/main/resources/microservice.yaml @@ -26,4 +26,4 @@ demo.multi.service.center.serverB: key6: ${demo.multi.service.center.serverB.actual.key6} key7: - ${demo.multi.service.center.serverB.actual.key7.1} - - ${demo.multi.service.center.serverB.actual.key7.2} \ No newline at end of file + - ${demo.multi.service.center.serverB.actual.key7.2} diff --git a/demo/demo-multi-service-center/pom.xml b/demo/demo-multi-service-center/pom.xml index b6acef542aa..cf9c101e496 100644 --- a/demo/demo-multi-service-center/pom.xml +++ b/demo/demo-multi-service-center/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-multi-service-center Java Chassis::Demo::Multi Service Centers diff --git a/demo/demo-multiple/a-client/pom.xml b/demo/demo-multiple/a-client/pom.xml index dd0dd2d5009..84131a7909c 100644 --- a/demo/demo-multiple/a-client/pom.xml +++ b/demo/demo-multiple/a-client/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT a-client Java Chassis::Demo::Multiple::A client diff --git a/demo/demo-multiple/a-server/pom.xml b/demo/demo-multiple/a-server/pom.xml index b11afad4c21..d38b12d8d62 100644 --- a/demo/demo-multiple/a-server/pom.xml +++ b/demo/demo-multiple/a-server/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT a-server Java Chassis::Demo::Multiple::A server diff --git a/demo/demo-multiple/b-client/pom.xml b/demo/demo-multiple/b-client/pom.xml index 00386e0ee37..4ab02a66f1b 100644 --- a/demo/demo-multiple/b-client/pom.xml +++ b/demo/demo-multiple/b-client/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT b-client Java Chassis::Demo::Multiple::B client diff --git a/demo/demo-multiple/b-server/pom.xml b/demo/demo-multiple/b-server/pom.xml index 7b02ee27808..e33e750d8e4 100644 --- a/demo/demo-multiple/b-server/pom.xml +++ b/demo/demo-multiple/b-server/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT b-server diff --git a/demo/demo-multiple/multiple-client/pom.xml b/demo/demo-multiple/multiple-client/pom.xml index bd6902e0ae9..7dcbb6f14f2 100644 --- a/demo/demo-multiple/multiple-client/pom.xml +++ b/demo/demo-multiple/multiple-client/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT multiple-client Java Chassis::Demo::Multiple::Client @@ -40,7 +40,7 @@ org.apache.servicecomb.demo.multiple.client.MultipleClient - + docker @@ -48,24 +48,88 @@ multiple-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml b/demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml index 74fd194d19f..1ece43eac98 100644 --- a/demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/multiple-client/src/main/resources/microservice.yaml @@ -23,4 +23,4 @@ servicecomb: service: application: multiple name: merged-client - version: 0.0.1 \ No newline at end of file + version: 0.0.1 diff --git a/demo/demo-multiple/multiple-server/pom.xml b/demo/demo-multiple/multiple-server/pom.xml index 62293637cea..18c7ae19069 100644 --- a/demo/demo-multiple/multiple-server/pom.xml +++ b/demo/demo-multiple/multiple-server/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-multiple - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT multiple-server Java Chassis::Demo::Multiple::Server @@ -54,28 +54,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml b/demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml index db24f716ede..b1eeade93d7 100644 --- a/demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml +++ b/demo/demo-multiple/multiple-server/src/main/resources/microservice.yaml @@ -21,4 +21,4 @@ servicecomb: service: application: multiple name: merged-server - version: 0.0.1 \ No newline at end of file + version: 0.0.1 diff --git a/demo/demo-multiple/pom.xml b/demo/demo-multiple/pom.xml index a16a7a6beaf..7496a2050d7 100644 --- a/demo/demo-multiple/pom.xml +++ b/demo/demo-multiple/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-multiple Java Chassis::Demo::Multiple diff --git a/demo/demo-nacos/README.md b/demo/demo-nacos/README.md new file mode 100644 index 00000000000..9165cac5687 --- /dev/null +++ b/demo/demo-nacos/README.md @@ -0,0 +1,5 @@ +# Notice + +This integration tests is designed for Nacos registry and configuration. And extra test cases include: + +* Test cases related to JAX-RS annotations that demo-jaxrs can not cover. diff --git a/demo/demo-nacos/consumer/pom.xml b/demo/demo-nacos/consumer/pom.xml index 5a223fdb83a..6dfe326f6a5 100644 --- a/demo/demo-nacos/consumer/pom.xml +++ b/demo/demo-nacos/consumer/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-nacos - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT nacos-consumer @@ -53,30 +53,52 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - - \ No newline at end of file + diff --git a/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java index 4ed0ecd85fe..437f88bfafd 100644 --- a/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java +++ b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java @@ -34,4 +34,9 @@ public class ConsumerController { public String sayHello(@RequestParam("name") String name) { return providerService.sayHello(name); } + + @GetMapping("/authIncludePath") + public String authIncludePath() { + return providerService.authIncludePath(); + } } diff --git a/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java new file mode 100644 index 00000000000..aed6894f941 --- /dev/null +++ b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchema; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchema.class) +public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchema { + @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") + private IHeaderParamWithListSchema provider; + + @Override + public String headerListDefault(List headerList) { + return provider.headerListDefault(headerList); + } + + @Override + public String headerListCSV(List headerList) { + return provider.headerListCSV(headerList); + } + + @Override + public String headerListMULTI(List headerList) { + return provider.headerListMULTI(headerList); + } + + @Override + public String headerListSSV(List headerList) { + return provider.headerListSSV(headerList); + } + + @Override + public String headerListPIPES(List headerList) { + return provider.headerListPIPES(headerList); + } +} diff --git a/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java index f327412b759..ee62a110625 100644 --- a/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java +++ b/demo/demo-nacos/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java @@ -19,4 +19,6 @@ public interface ProviderService { String sayHello(String name); + + String authIncludePath(); } diff --git a/demo/demo-nacos/consumer/src/main/resources/application.yml b/demo/demo-nacos/consumer/src/main/resources/application.yml index 0110e6e2748..ce6025722a5 100644 --- a/demo/demo-nacos/consumer/src/main/resources/application.yml +++ b/demo/demo-nacos/consumer/src/main/resources/application.yml @@ -29,5 +29,7 @@ servicecomb: rest: address: 0.0.0.0:9092 - - + publicKey: + accessControl: + enabled: true + excludePathPatterns: '/*' diff --git a/demo/demo-nacos/consumer/src/main/resources/log4j2.xml b/demo/demo-nacos/consumer/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-nacos/consumer/src/main/resources/log4j2.xml +++ b/demo/demo-nacos/consumer/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-nacos/gateway/pom.xml b/demo/demo-nacos/gateway/pom.xml index f1135e3ab11..f76d407973a 100644 --- a/demo/demo-nacos/gateway/pom.xml +++ b/demo/demo-nacos/gateway/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-nacos - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT nacos-gateway @@ -56,30 +56,52 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - - \ No newline at end of file + diff --git a/demo/demo-nacos/gateway/src/main/resources/log4j2.xml b/demo/demo-nacos/gateway/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-nacos/gateway/src/main/resources/log4j2.xml +++ b/demo/demo-nacos/gateway/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-nacos/pom.xml b/demo/demo-nacos/pom.xml index 20c38c7d88d..a37e292de1d 100644 --- a/demo/demo-nacos/pom.xml +++ b/demo/demo-nacos/pom.xml @@ -23,12 +23,16 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-nacos Java Chassis::Demo::NACOS pom + + org.apache.servicecomb.demo + demo-schema + org.apache.servicecomb solution-basic @@ -41,6 +45,10 @@ org.apache.logging.log4j log4j-core + + org.apache.logging.log4j + log4j-api + @@ -50,4 +58,4 @@ test-client - \ No newline at end of file + diff --git a/demo/demo-nacos/provider/pom.xml b/demo/demo-nacos/provider/pom.xml index d80ae394c40..a23bc1bb06f 100644 --- a/demo/demo-nacos/provider/pom.xml +++ b/demo/demo-nacos/provider/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-nacos - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT nacos-provider @@ -57,30 +57,52 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - - \ No newline at end of file + diff --git a/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java b/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java new file mode 100644 index 00000000000..a4475d80bda --- /dev/null +++ b/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchema; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchema.class) +public class HeaderParamWithListSchema implements IHeaderParamWithListSchema { + @Override + public String headerListDefault(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListCSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListMULTI(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListSSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListPIPES(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } +} diff --git a/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java index 6eea7776a6b..0076440d232 100644 --- a/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java +++ b/demo/demo-nacos/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java @@ -30,4 +30,9 @@ public class ProviderController { public String sayHello(@RequestParam("name") String name) { return "Hello " + name; } + + @GetMapping("/authIncludePath") + public String authIncludePath() { + return "success."; + } } diff --git a/demo/demo-nacos/provider/src/main/resources/application.yml b/demo/demo-nacos/provider/src/main/resources/application.yml index b24d281cd65..ae6e029b701 100644 --- a/demo/demo-nacos/provider/src/main/resources/application.yml +++ b/demo/demo-nacos/provider/src/main/resources/application.yml @@ -31,3 +31,12 @@ servicecomb: rest: address: 0.0.0.0:9094 + publicKey: + accessControl: + enabled: true + includePathPatterns: '/authIncludePath' + black: + authPath: + category: property + propertyName: serviceName + rule: consumer* diff --git a/demo/demo-nacos/provider/src/main/resources/log4j2.xml b/demo/demo-nacos/provider/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-nacos/provider/src/main/resources/log4j2.xml +++ b/demo/demo-nacos/provider/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-nacos/test-client/pom.xml b/demo/demo-nacos/test-client/pom.xml index 1ad589a02f6..103aeb24d94 100644 --- a/demo/demo-nacos/test-client/pom.xml +++ b/demo/demo-nacos/test-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-nacos - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT nacos-test-client @@ -193,21 +193,8 @@ io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - - \ No newline at end of file + diff --git a/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java new file mode 100644 index 00000000000..1b129acc2c6 --- /dev/null +++ b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HeaderParamWithListSchemaIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHeaderListDefault(); + testHeaderListMulti(); + testHeaderListCSV(); + testHeaderListSSV(); + testHeaderListPipes(); + } + + // default to multi + private void testHeaderListDefault() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListPipes() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a|b|c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListSSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a b c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListCSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a,b,c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + + headers.add("headerList", "a, b, c"); + entity = new HttpEntity<>(headers); + result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListMulti() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } +} diff --git a/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java index 59d06bb1e01..ad9460753a8 100644 --- a/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java +++ b/demo/demo-nacos/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java @@ -30,6 +30,7 @@ public class HelloWorldIT implements CategorizedTestCase { @Override public void testRestTransport() throws Exception { testHelloWorld(); + testAuthIncludePath(); } private void testHelloWorld() { @@ -37,4 +38,14 @@ private void testHelloWorld() { .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); TestMgr.check("Hello World", result); } + + private void testAuthIncludePath() { + String result = ""; + try { + result = template.getForObject(Config.GATEWAY_URL + "/authIncludePath", String.class); + } catch (Exception e) { + result = "failed"; + } + TestMgr.check("failed", result); + } } diff --git a/demo/demo-nacos/test-client/src/main/resources/log4j2.xml b/demo/demo-nacos/test-client/src/main/resources/log4j2.xml index 6e6586771bb..c51f7ad5036 100644 --- a/demo/demo-nacos/test-client/src/main/resources/log4j2.xml +++ b/demo/demo-nacos/test-client/src/main/resources/log4j2.xml @@ -38,4 +38,4 @@ - \ No newline at end of file + diff --git a/demo/demo-pojo/pojo-client/pom.xml b/demo/demo-pojo/pojo-client/pom.xml index 712a1f0c358..582c8c8f511 100644 --- a/demo/demo-pojo/pojo-client/pom.xml +++ b/demo/demo-pojo/pojo-client/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-pojo - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT pojo-client Java Chassis::Demo::POJO::Client @@ -54,24 +54,88 @@ pojo-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClient.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClient.java index dbc1bc2eb1e..601901e5884 100644 --- a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClient.java +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/CodeFirstPojoClient.java @@ -94,7 +94,7 @@ private void testCodeFirstCompletableFuture(CodeFirstPojoIntf codeFirst) { return; } - Vertx vertx = VertxUtils.getOrCreateVertxByName("transport", null); + Vertx vertx = VertxUtils.getOrCreateVertxByName("transport", null, null); CountDownLatch latch = new CountDownLatch(1); // vertx.runOnContext in normal thread is not a good practice // here just a test, not care for this. @@ -107,7 +107,9 @@ private void testCodeFirstCompletableFuture(CodeFirstPojoIntf codeFirst) { .thenCompose(result -> { TestMgr.check("someone sayhi, context k: v", result); - TestMgr.check(true, context == ContextUtils.getInvocationContext()); + // new call need set invocation context implicitly or will not + // inherit parent context + ContextUtils.setInvocationContext(context); return ((CodeFirstPojoClientIntf) codeFirst).sayHiAsync("someone 1"); }) diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/PojoClient.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/PojoClient.java index 01be34c2a39..03fafced0ae 100644 --- a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/PojoClient.java +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/PojoClient.java @@ -18,7 +18,6 @@ package org.apache.servicecomb.demo.pojo.client; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -33,7 +32,6 @@ import org.apache.servicecomb.demo.DemoConst; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.demo.server.Test; -import org.apache.servicecomb.demo.server.TestRequest; import org.apache.servicecomb.demo.server.User; import org.apache.servicecomb.demo.smartcare.Application; import org.apache.servicecomb.demo.smartcare.Group; @@ -43,7 +41,6 @@ import org.apache.servicecomb.provider.pojo.RpcReference; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; import org.apache.servicecomb.swagger.invocation.context.InvocationContext; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.WebApplicationType; @@ -56,8 +53,6 @@ public class PojoClient { private static final Logger LOGGER = LoggerFactory.getLogger(PojoClient.class); - public static final byte[] buffer = new byte[1024]; - // reference a not exist a microservice, and never use it // this should not cause problems @RpcReference(microserviceName = "notExist") @@ -70,10 +65,6 @@ public class PojoClient { private static SmartCare smartcare; - static { - Arrays.fill(buffer, (byte) 1); - } - public static void setTestFromXml(Test testFromXml) { PojoClient.testFromXml = testFromXml; } @@ -120,14 +111,6 @@ public static void run() throws Exception { testContextClassLoaderIsNull(); testNull(testFromXml); - testNull(test); - - // This test case shows destroy of WeightedResponseTimeRule timer task. after test finished will not print: - // "Weight adjusting job started" and thread "NFLoadBalancer-serverWeightTimer-unknown" destroyed. - InMemoryDynamicPropertiesSource.update("servicecomb.loadbalance.strategy.name", "WeightedResponse"); - testStringArray(test); - InMemoryDynamicPropertiesSource.update("servicecomb.loadbalance.strategy.name", "RoundRobin"); - testStringArray(test); boolean checkerDestroyed = true; // Timer cancel may not destroy thread very fast so check for 3 times. @@ -143,28 +126,13 @@ public static void run() throws Exception { } TestMgr.check(checkerDestroyed, true); - testChinese(test); - testStringHaveSpace(test); - testWrapParam(test); - testSplitParam(test); - testInputArray(test); - - testException(test); - testSmartCare(smartcare); testCommonInvoke(transport); if ("rest".equals(transport)) { testTraceIdOnNotSetBefore(); - testNullRest(test); - testExceptionRest(test); - testEmptyRest(test); - } else if ("highway".equals(transport)) { - testNullHighway(test); - testEmptyHighway(test); } - testTraceIdOnContextContainsTraceId(); } } @@ -216,58 +184,6 @@ private static void testSmartCare(SmartCare smartCare) { smartCare.delApplication("app0")); } - private static void testExceptionRest(Test test) { - try { - test.testException(456); - } catch (InvocationException e) { - TestMgr.check("456 error", e.getErrorData()); - } - - try { - test.testException(556); - } catch (InvocationException e) { - TestMgr.check("[556 error]", e.getErrorData()); - } - - try { - test.testException(557); - } catch (InvocationException e) { - TestMgr.check("[[557 error]]", e.getErrorData()); - } - } - - private static void testException(Test test) { - try { - test.testException(456); - } catch (InvocationException e) { - TestMgr.check("456 error", e.getErrorData()); - } - - try { - test.testException(556); - } catch (InvocationException e) { - TestMgr.check("[556 error]", e.getErrorData()); - } - - try { - test.testException(557); - } catch (InvocationException e) { - TestMgr.check("[[557 error]]", e.getErrorData()); - } - } - - private static void testInputArray(Test test) { - String result = test.addString(new String[] {"a", "b"}); - LOGGER.info("input array result:{}", result); - TestMgr.check("[a, b]", result); - } - - private static void testSplitParam(Test test) { - User result = test.splitParam(1, new User()); - LOGGER.info("split param result:{}", result); - TestMgr.check("User [name=nameA, users count:0, age=100, index=1]", result); - } - @SuppressWarnings("rawtypes") private static void testCommonInvoke(String transport) { Map arguments = new HashMap<>(); @@ -293,58 +209,9 @@ private static void testCommonInvoke(String transport) { TestMgr.check("User [name=nameA, users count:0, age=100, index=3]", result); } - private static void testEmptyHighway(Test test) { - TestMgr.check("code is ''", test.getTestString("")); - } - - private static void testEmptyRest(Test test) { - TestMgr.check("code is ''", test.getTestString("")); - } - - private static void testNullRest(Test test) { - TestMgr.check(null, test.wrapParam(null)); - } - - private static void testNullHighway(Test test) { - TestMgr.check("nameA", test.wrapParam(null).getName()); - } - private static void testNull(Test test) { TestMgr.check("code is 'null'", test.getTestString(null)); TestMgr.check(null, test.postTestStatic(2)); TestMgr.check(null, test.patchTestStatic(2)); } - - private static void testChinese(Test test) { - TestMgr.check("code is '测试'", test.getTestString("测试")); - - User user = new User(); - user.setName("名字"); - User result = test.splitParam(1, user); - TestMgr.check("名字, users count:0", result.getName()); - } - - private static void testStringHaveSpace(Test test) { - TestMgr.check("code is 'a b'", test.getTestString("a b")); - } - - private static void testStringArray(Test test) { - // TestMgr.check("arr is '[a, , b]'", test.testStringArray(new String[] {"a", null, "b"})); - TestMgr.check("arr is '[a, b]'", test.testStringArray(new String[] {"a", "b"})); - } - - private static void testWrapParam(Test test) { - User user = new User(); - - TestRequest request = new TestRequest(); - request.setUser(user); - request.setIndex(0); - request.setData(buffer); - request.getUsers().add(user); - - User result = test.wrapParam(request); - LOGGER.info("wrap param result:{}", result); - - TestMgr.check("User [name=nameA, users count:1, age=100, index=0]", result); - } } diff --git a/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestTestImpl.java b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestTestImpl.java new file mode 100644 index 00000000000..8b8fabde08e --- /dev/null +++ b/demo/demo-pojo/pojo-client/src/main/java/org/apache/servicecomb/demo/pojo/client/TestTestImpl.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.pojo.client; + +import java.util.Arrays; + +import org.apache.servicecomb.config.InMemoryDynamicPropertiesSource; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.server.Test; +import org.apache.servicecomb.demo.server.TestRequest; +import org.apache.servicecomb.demo.server.User; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +@Component +public class TestTestImpl implements CategorizedTestCase { + private static final Logger LOGGER = LoggerFactory.getLogger(TestTestImpl.class); + + private static final byte[] buffer = new byte[1024]; + + static { + Arrays.fill(buffer, (byte) 1); + } + + @RpcReference(microserviceName = "pojo", schemaId = "server") + private Test test; + + @Override + public void testAllTransport() throws Exception { + testNull(test); + + // This test case shows destroy of WeightedResponseTimeRule timer task. after test finished will not print: + // "Weight adjusting job started" and thread "NFLoadBalancer-serverWeightTimer-unknown" destroyed. + InMemoryDynamicPropertiesSource.update("servicecomb.loadbalance.strategy.name", "WeightedResponse"); + testStringArray(test); + InMemoryDynamicPropertiesSource.update("servicecomb.loadbalance.strategy.name", "RoundRobin"); + testStringArray(test); + + testChinese(test); + testStringHaveSpace(test); + testWrapParam(test); + + testSplitParam(test); + testInputArray(test); + + testException(test); + + testIntArray(test); + } + + @Override + public void testRestTransport() throws Exception { + testNullRest(test); + testExceptionRest(test); + testEmptyRest(test); + } + + @Override + public void testHighwayTransport() throws Exception { + testNullHighway(test); + testEmptyHighway(test); + } + + private static void testIntArray(Test test) { + int[] request = new int[] {5, 11, 4}; + int[] result = test.testIntArray(request); + TestMgr.check(request.length, result.length); + TestMgr.check(request[1], result[1]); + } + + private static void testEmptyHighway(Test test) { + TestMgr.check("code is ''", test.getTestString("")); + } + + private static void testEmptyRest(Test test) { + TestMgr.check("code is ''", test.getTestString("")); + } + + private static void testNullRest(Test test) { + TestMgr.check(null, test.wrapParam(null)); + } + + private static void testNullHighway(Test test) { + TestMgr.check("nameA", test.wrapParam(null).getName()); + } + + private static void testNull(Test test) { + TestMgr.check("code is 'null'", test.getTestString(null)); + TestMgr.check(null, test.postTestStatic(2)); + TestMgr.check(null, test.patchTestStatic(2)); + } + + private static void testChinese(Test test) { + TestMgr.check("code is '测试'", test.getTestString("测试")); + + User user = new User(); + user.setName("名字"); + User result = test.splitParam(1, user); + TestMgr.check("名字, users count:0", result.getName()); + } + + private static void testStringHaveSpace(Test test) { + TestMgr.check("code is 'a b'", test.getTestString("a b")); + } + + private static void testStringArray(Test test) { + // TestMgr.check("arr is '[a, , b]'", test.testStringArray(new String[] {"a", null, "b"})); + TestMgr.check("arr is '[a, b]'", test.testStringArray(new String[] {"a", "b"})); + } + + private static void testWrapParam(Test test) { + User user = new User(); + + TestRequest request = new TestRequest(); + request.setUser(user); + request.setIndex(0); + request.setData(buffer); + request.getUsers().add(user); + + User result = test.wrapParam(request); + LOGGER.info("wrap param result:{}", result); + + TestMgr.check("User [name=nameA, users count:1, age=100, index=0]", result); + } + + + private static void testExceptionRest(Test test) { + try { + test.testException(456); + } catch (InvocationException e) { + TestMgr.check("456 error", e.getErrorData()); + } + + try { + test.testException(556); + } catch (InvocationException e) { + TestMgr.check("[556 error]", e.getErrorData()); + } + + try { + test.testException(557); + } catch (InvocationException e) { + TestMgr.check("[[557 error]]", e.getErrorData()); + } + } + + private static void testException(Test test) { + try { + test.testException(456); + } catch (InvocationException e) { + TestMgr.check("456 error", e.getErrorData()); + } + + try { + test.testException(556); + } catch (InvocationException e) { + TestMgr.check("[556 error]", e.getErrorData()); + } + + try { + test.testException(557); + } catch (InvocationException e) { + TestMgr.check("[[557 error]]", e.getErrorData()); + } + } + + private static void testInputArray(Test test) { + String result = test.addString(new String[] {"a", "b"}); + LOGGER.info("input array result:{}", result); + TestMgr.check("[a, b]", result); + } + + private static void testSplitParam(Test test) { + User result = test.splitParam(1, new User()); + LOGGER.info("split param result:{}", result); + TestMgr.check("User [name=nameA, users count:0, age=100, index=1]", result); + } +} diff --git a/demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml b/demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml index 1f9805d8ccd..45c62dffcf1 100644 --- a/demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml +++ b/demo/demo-pojo/pojo-client/src/main/resources/microservice.yaml @@ -37,6 +37,7 @@ servicecomb: window_time: 12000 publisher.defaultLog.enabled: false # when in testing , can turn on flowcontrol: + enabled: true Consumer: qps: limit: diff --git a/demo/demo-pojo/pojo-server/pom.xml b/demo/demo-pojo/pojo-server/pom.xml index dfc78bf00af..b0b673ef47e 100644 --- a/demo/demo-pojo/pojo-server/pom.xml +++ b/demo/demo-pojo/pojo-server/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-pojo - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT pojo-server Java Chassis::Demo::POJO::Server @@ -36,7 +36,9 @@ org.apache.servicecomb.demo.pojo.server.PojoServer + true + @@ -49,28 +51,51 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/TestImpl.java b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/TestImpl.java index 87ffd0e7c3d..230dfd804fe 100644 --- a/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/TestImpl.java +++ b/demo/demo-pojo/pojo-server/src/main/java/org/apache/servicecomb/demo/pojo/server/TestImpl.java @@ -105,4 +105,9 @@ public String addString(String[] strArr) { public String testTraceId() { return ContextUtils.getInvocationContext().getContext(CoreConst.TRACE_ID_NAME); } + + @Override + public int[] testIntArray(int[] request) { + return request; + } } diff --git a/demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml b/demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml index 1a9d2715a49..0fe723a06ff 100644 --- a/demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml +++ b/demo/demo-pojo/pojo-server/src/main/resources/microservice.yaml @@ -34,6 +34,7 @@ servicecomb: disableDataTypeCheck: true flowcontrol: + enabled: true Provider: qps: limit: @@ -41,4 +42,3 @@ servicecomb: FlowControlSchema: foo: 3 bar: 3000 - diff --git a/demo/demo-pojo/pom.xml b/demo/demo-pojo/pom.xml index 4bf09de5263..96217173a03 100644 --- a/demo/demo-pojo/pom.xml +++ b/demo/demo-pojo/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-pojo Java Chassis::Demo::POJO diff --git a/demo/demo-register-url-prefix/README.md b/demo/demo-register-url-prefix/README.md index 416a098a41b..e0b2df4db0b 100644 --- a/demo/demo-register-url-prefix/README.md +++ b/demo/demo-register-url-prefix/README.md @@ -1,4 +1,4 @@ -This demo is an integration test for testing +This demo is an integration test for testing ```yaml servicecomb: @@ -7,7 +7,7 @@ servicecomb: registerUrlPrefix: true ``` -When this configuration is enabled, web context path is added to swagger, and consumer can +When this configuration is enabled, web context path is added to swagger, and consumer can invoke with context path ```yaml diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-client/pom.xml b/demo/demo-register-url-prefix/demo-register-url-prefix-client/pom.xml index 574d57e339c..1b7fc782cd1 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-client/pom.xml +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-client/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-register-url-prefix - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -45,24 +45,88 @@ demo-register-url-prefix-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/application.yml b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/application.yml index e9056295577..65b87064740 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/application.yml +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-client/src/main/resources/application.yml @@ -29,4 +29,4 @@ servicecomb: handler: chain: Consumer: - default: loadbalance \ No newline at end of file + default: loadbalance diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-server/pom.xml b/demo/demo-register-url-prefix/demo-register-url-prefix-server/pom.xml index a3f719ddeee..2d34ae62051 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-server/pom.xml +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-register-url-prefix - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -50,28 +50,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixEndpoint.java b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixEndpoint.java index 86b1d40026b..dae91a50f49 100644 --- a/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixEndpoint.java +++ b/demo/demo-register-url-prefix/demo-register-url-prefix-server/src/main/java/org/apache/servicecomb/demo/prefix/RegisterUrlPrefixEndpoint.java @@ -17,26 +17,21 @@ package org.apache.servicecomb.demo.prefix; -import jakarta.ws.rs.core.MediaType; - import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "RegisterUrlPrefixEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class RegisterUrlPrefixEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(RegisterUrlPrefixEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-register-url-prefix/pom.xml b/demo/demo-register-url-prefix/pom.xml index 2cea341f1a2..e51630b5397 100644 --- a/demo/demo-register-url-prefix/pom.xml +++ b/demo/demo-register-url-prefix/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-register-url-prefix Java Chassis::Demo::Register URL Prefix diff --git a/demo/demo-schema/pom.xml b/demo/demo-schema/pom.xml index 49316dc5848..b17f345111b 100644 --- a/demo/demo-schema/pom.xml +++ b/demo/demo-schema/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-schema Java Chassis::Demo::Schema @@ -37,10 +37,6 @@ foundation-test-scaffolding compile - - javax.xml.bind - jaxb-api - diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/TestMgr.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/TestMgr.java index 9cfffaa5c2f..c90fe8bc1a5 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/TestMgr.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/TestMgr.java @@ -32,7 +32,7 @@ public class TestMgr { private static String msg = ""; - private static final AtomicLong checkes = new AtomicLong(0); + private static final AtomicLong checks = new AtomicLong(0); public static void setMsg(String msg) { TestMgr.msg = msg; @@ -47,7 +47,7 @@ public static void check(Object expect, Object real) { } public static void check(Object expect, Object real, Throwable error) { - checkes.incrementAndGet(); + checks.incrementAndGet(); if (expect == real) { return; @@ -66,7 +66,7 @@ public static void check(Object expect, Object real, Throwable error) { } public static void checkNotEmpty(String real) { - checkes.incrementAndGet(); + checks.incrementAndGet(); if (StringUtils.isEmpty(real)) { errorList.add(new Error(msg + " | unexpected null result, method is " + getCaller())); @@ -78,7 +78,7 @@ public static void fail(String desc) { } public static void failed(String desc, Throwable e) { - checkes.incrementAndGet(); + checks.incrementAndGet(); Error error = new Error(msg + " | " + desc + ", method is " + getCaller()); if (e != null) { @@ -94,12 +94,12 @@ public static boolean isSuccess() { public static void summary() { if (errorList.isEmpty()) { LOGGER.info("............. test finished ............"); - LOGGER.info("............. total checks : " + checkes.get()); + LOGGER.info("............. total checks : " + checks.get()); return; } LOGGER.info("............. test not finished ............"); - LOGGER.info("............. total checks : " + checkes.get()); + LOGGER.info("............. total checks : " + checks.get()); LOGGER.info("............. total errors : " + errorList.size()); LOGGER.info("............. error details: "); for (Throwable e : errorList) { diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchema.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchema.java new file mode 100644 index 00000000000..8ee60ff27dd --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchema.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.api; + +import java.util.List; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.Explode; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.enums.ParameterStyle; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.HeaderParam; +import jakarta.ws.rs.Path; + +@Path("/headerList") +public interface IHeaderParamWithListSchema { + @Path("headerListDefault") + @GET + String headerListDefault( + @HeaderParam("headerList") List headerList); + + @Path("headerListCSV") + @GET + String headerListCSV( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.FALSE) + List headerList); + + @Path("headerListMULTI") + @GET + String headerListMULTI( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.TRUE) + List headerList); + + @Path("headerListSSV") + @GET + String headerListSSV( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.SPACEDELIMITED, explode = Explode.FALSE) + List headerList); + + @Path("headerListPIPES") + @GET + String headerListPIPES( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.PIPEDELIMITED, explode = Explode.FALSE) + List headerList); +} diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchemaSpringMvc.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchemaSpringMvc.java new file mode 100644 index 00000000000..75875f8790c --- /dev/null +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/api/IHeaderParamWithListSchemaSpringMvc.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.api; + + +import java.util.List; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.Explode; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.enums.ParameterStyle; + +@RequestMapping("/headerList") +public interface IHeaderParamWithListSchemaSpringMvc { + @GetMapping("headerListDefault") + String headerListDefault( + @RequestHeader("headerList") List headerList); + + @GetMapping("headerListCSV") + String headerListCSV( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.FALSE) + List headerList); + + @GetMapping("headerListMULTI") + String headerListMULTI( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.FORM, explode = Explode.TRUE) + List headerList); + + @GetMapping("headerListSSV") + String headerListSSV( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.SPACEDELIMITED, explode = Explode.FALSE) + List headerList); + + @GetMapping("headerListPIPES") + String headerListPIPES( + @Parameter(name = "headerList", in = ParameterIn.HEADER, style = ParameterStyle.PIPEDELIMITED, explode = Explode.FALSE) + List headerList); +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConsumerProvider.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/PersonAlias.java similarity index 75% rename from providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConsumerProvider.java rename to demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/PersonAlias.java index f3bae5b0ca7..a1766eb29c2 100644 --- a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoConsumerProvider.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/controller/PersonAlias.java @@ -15,15 +15,21 @@ * limitations under the License. */ -package org.apache.servicecomb.provider.pojo; +package org.apache.servicecomb.demo.controller; -import static org.apache.servicecomb.provider.pojo.PojoConst.POJO; +public class PersonAlias { + private String name; -import org.apache.servicecomb.core.provider.consumer.AbstractConsumerProvider; + public void setName(String name) { + this.name = name; + } -public class PojoConsumerProvider extends AbstractConsumerProvider { - @Override public String getName() { - return POJO; + return this.name; + } + + @Override + public String toString() { + return name; } } diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java index 620879803ab..797833437a7 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBJob.java @@ -16,11 +16,12 @@ */ package org.apache.servicecomb.demo.jaxbbean; -import javax.xml.bind.annotation.XmlAccessOrder; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorOrder; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; + +import jakarta.xml.bind.annotation.XmlAccessOrder; +import jakarta.xml.bind.annotation.XmlAccessType; +import jakarta.xml.bind.annotation.XmlAccessorOrder; +import jakarta.xml.bind.annotation.XmlAccessorType; +import jakarta.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "job") @XmlAccessorType(XmlAccessType.FIELD) diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java index bd66e9dbd9f..450ebacabe8 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/jaxbbean/JAXBPerson.java @@ -18,13 +18,13 @@ import java.io.Serializable; -import javax.xml.bind.annotation.XmlAccessOrder; -import javax.xml.bind.annotation.XmlAccessorOrder; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlTransient; -import javax.xml.bind.annotation.XmlType; +import jakarta.xml.bind.annotation.XmlAccessOrder; +import jakarta.xml.bind.annotation.XmlAccessorOrder; +import jakarta.xml.bind.annotation.XmlAttribute; +import jakarta.xml.bind.annotation.XmlElement; +import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.XmlTransient; +import jakarta.xml.bind.annotation.XmlType; @XmlType(propOrder = {"name", "role", "job"}) @XmlRootElement(name = "person") diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/Test.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/Test.java index 0afa62a6434..0369d576073 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/Test.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/server/Test.java @@ -35,4 +35,6 @@ public interface Test { String addString(String[] strArr); String testTraceId(); + + int[] testIntArray(int[] request); } diff --git a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java index 3df68dc5533..7ff2518e8a3 100644 --- a/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java +++ b/demo/demo-schema/src/main/java/org/apache/servicecomb/demo/utils/JAXBUtils.java @@ -20,9 +20,6 @@ import java.io.StringWriter; import java.nio.charset.StandardCharsets; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Source; import javax.xml.transform.sax.SAXSource; @@ -33,6 +30,10 @@ import com.fasterxml.jackson.databind.JavaType; +import jakarta.xml.bind.JAXBContext; +import jakarta.xml.bind.Marshaller; +import jakarta.xml.bind.Unmarshaller; + public class JAXBUtils { private static final Logger LOGGER = LoggerFactory.getLogger(JAXBUtils.class); diff --git a/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor b/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor index ff9a6cb9a54..acd03890620 100644 --- a/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor +++ b/demo/demo-schema/src/main/resources/META-INF/services/org.apache.servicecomb.common.rest.codec.produce.ProduceProcessor @@ -16,4 +16,4 @@ # org.apache.servicecomb.demo.produceprocessor.ProduceAppXmlProcessor -org.apache.servicecomb.demo.produceprocessor.override.ProduceAppXmlProcessor \ No newline at end of file +org.apache.servicecomb.demo.produceprocessor.override.ProduceAppXmlProcessor diff --git a/demo/demo-schema/src/main/resources/microservice.yaml b/demo/demo-schema/src/main/resources/microservice.yaml index 8c0b909beb5..28df63a4323 100644 --- a/demo/demo-schema/src/main/resources/microservice.yaml +++ b/demo/demo-schema/src/main/resources/microservice.yaml @@ -27,4 +27,3 @@ servicecomb: exception: print-stack-trace: true print-rate-limit: true - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/pom.xml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/pom.xml index bcdb324aa71..372d3a8ccc3 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/pom.xml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-spring-boot-transport - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -39,24 +39,88 @@ demo-spring-boot-pojo-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientTest.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientTest.java index e087b0b6223..4e0525a1230 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientTest.java +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/java/org/apache/servicecomb/demo/springboot/pojo/client/PojoClientTest.java @@ -38,7 +38,7 @@ public class PojoClientTest { @RpcReference(microserviceName = "spring-boot-pojo-server", schemaId = "server") public static Test test; - public static Test testFromXml; + private static Test testFromXml; public static final byte[] buffer = new byte[1024]; diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml index fd741def221..1d901a66c99 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-client/src/main/resources/application.yml @@ -35,4 +35,4 @@ servicecomb: enabled: false references: pojo: - version-rule: 0.0.1 \ No newline at end of file + version-rule: 0.0.1 diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/pom.xml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/pom.xml index ecc9cfd0626..60761704af9 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/pom.xml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-spring-boot-transport - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -52,28 +52,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/handler/MyHandler.java b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/handler/MyHandler.java index 1ec59f40b5a..09d2e2eb06d 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/handler/MyHandler.java +++ b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/java/org/apache/servicecomb/demo/springboot/pojo/server/handler/MyHandler.java @@ -20,25 +20,25 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.demo.springboot.pojo.server.schema.server.User; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component -public class MyHandler implements ProviderFilter { +public class MyHandler extends AbstractFilter implements ProviderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(MyHandler.class); public static final String SPLITPARAM_RESPONSE_USER_SUFFIX = "(modified by MyHandler)"; @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 100; } diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/helloworld.Greeter.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/helloworld.Greeter.yaml deleted file mode 100644 index d503f154ce0..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/helloworld.Greeter.yaml +++ /dev/null @@ -1,74 +0,0 @@ -## --------------------------------------------------------------------------- -## Licensed to the Apache Software Foundation (ASF) under one or more -## contributor license agreements. See the NOTICE file distributed with -## this work for additional information regarding copyright ownership. -## The ASF licenses this file to You under the Apache License, Version 2.0 -## (the "License"); you may not use this file except in compliance with -## the License. You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: helloworld.Greeter - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.helloworld.greeter.Hello - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest/helloworld.Greeter -produces: - - application/json - -paths: - /SayHello: - post: - operationId: SayHello - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - "200": - description: asdf - schema: - type: string - default: - description: asf - schema: - type: string - /SayHelloAgain: - post: - operationId: SayHelloAgain - parameters: - - name: name - in: body - required: true - schema: - type: string - responses: - "200": - description: asdf - schema: - type: string - default: - description: asf - schema: - type: string diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/server.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/server.yaml deleted file mode 100644 index 4717f8e483e..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/server.yaml +++ /dev/null @@ -1,197 +0,0 @@ -## --------------------------------------------------------------------------- -## Licensed to the Apache Software Foundation (ASF) under one or more -## contributor license agreements. See the NOTICE file distributed with -## this work for additional information regarding copyright ownership. -## The ASF licenses this file to You under the Apache License, Version 2.0 -## (the "License"); you may not use this file except in compliance with -## the License. You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: cse test - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.springboot.pojo.server.schema.server.Test - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest -produces: - - application/json - -paths: - /testStringArray: - get: - operationId: testStringArray - parameters: - - name: arr - in: query - required: true - type: array - items: - type: string - responses: - "200": - description: asdf - schema: - type: string - /testStatic: - get: - operationId: getTestString - parameters: - - name: code - in: query - required: false - type: string - responses: - "200": - description: asdf - schema: - type: string - post: - operationId: postTestStatic - parameters: - - name: code - in: query - required: true - type: integer - format: int32 - responses: - "200": - description: asdf - schema: - type: string - /testException/{code}: - get: - operationId: testException - parameters: - - name: code - in: path - required: true - type: integer - format: int32 - responses: - "200": - description: asdf - schema: - type: string - 456: - description: asdf - schema: - type: string - 556: - description: asdf - schema: - type: array - items: - type: string - 557: - description: asdf - schema: - type: array - items: - type: array - items: - type: string - /splitParam: - post: - operationId: splitParam - parameters: - - name: index - in: query - required: true - type: number - - name: user - in: body - schema: - $ref: '#/definitions/User' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - /wrapParam: - post: - operationId: wrapParam - parameters: - - name: request - in: body - required: false - schema: - $ref: '#/definitions/TestRequest' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - /addstring: - delete: - operationId: addString - parameters: - - name: strArr - in: query - required: true - type: array - items: - type: string - responses: - "200": - description: add string - schema: - type: string - -definitions: - Error: - type: object - x-java-class: org.apache.servicecomb.core.exception.CommonExceptionData - properties: - message: - type: string - TestRequest: - type: object - properties: - index: - type: number - user: - $ref: '#/definitions/User' - users: - type: array - items: - $ref: '#/definitions/User' - data: - type: string - format: byte - User: - type: object - properties: - name: - type: string - age: - type: number - index: - type: number - x-java-class: "org.apache.servicecomb.demo.springboot.pojo.server.schema.server.User" \ No newline at end of file diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/smartcare.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/smartcare.yaml deleted file mode 100644 index 49a8ac93e72..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/smartcare.yaml +++ /dev/null @@ -1,135 +0,0 @@ -## --------------------------------------------------------------------------- -## Licensed to the Apache Software Foundation (ASF) under one or more -## contributor license agreements. See the NOTICE file distributed with -## this work for additional information regarding copyright ownership. -## The ASF licenses this file to You under the Apache License, Version 2.0 -## (the "License"); you may not use this file except in compliance with -## the License. You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: smartcare test - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.smartcare.SmartCare - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest/plat/meta/v1 -produces: - - application/json - -paths: - /applications: - post: - operationId: addApplication - parameters: - - name: application - in: body - required: true - schema: - $ref: '#/definitions/Application' - responses: - 201: - description: success - schema: - $ref: '#/definitions/Response' - 400: - description: error - schema: - $ref: '#/definitions/Response' - 500: - description: error - schema: - $ref: '#/definitions/Response' - - /applications/{appName}: - delete: - operationId: delApplication - parameters: - - name: appName - in: path - required: true - type: string - responses: - 201: - description: success - schema: - $ref: '#/definitions/Response' - 400: - description: error - schema: - $ref: '#/definitions/Response' - 500: - description: error - schema: - $ref: '#/definitions/Response' - - -definitions: - Application: - type: object - properties: - name: - type: string - labelEN: - type: string - labelCH: - type: string - defaultGroup: - type: string - version: - type: string - dynamicFlag: - type: boolean - groups: - type: array - items: - $ref: '#/definitions/Group' - required: - - name - - defaultGroup - - version - - dynamicFlag - - groups - - Group: - type: object - properties: - name: - type: string - labelEN: - type: string - labelCH: - type: string - required: - - name - - Response: - type: object - x-java-class: org.apache.servicecomb.demo.smartcare.Response - properties: - resultCode: - type: integer - format: int32 - resultMessage: - type: string - required: - - resultCode - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/tcc-server.yaml b/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/tcc-server.yaml deleted file mode 100644 index 4af831b88d8..00000000000 --- a/demo/demo-spring-boot-transport/demo-spring-boot-pojo-server/src/main/resources/microservices/pojo/tcc-server.yaml +++ /dev/null @@ -1,108 +0,0 @@ -## --------------------------------------------------------------------------- -## Licensed to the Apache Software Foundation (ASF) under one or more -## contributor license agreements. See the NOTICE file distributed with -## this work for additional information regarding copyright ownership. -## The ASF licenses this file to You under the Apache License, Version 2.0 -## (the "License"); you may not use this file except in compliance with -## the License. You may obtain a copy of the License at -## -## http://www.apache.org/licenses/LICENSE-2.0 -## -## Unless required by applicable law or agreed to in writing, software -## distributed under the License is distributed on an "AS IS" BASIS, -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -## See the License for the specific language governing permissions and -## limitations under the License. -## --------------------------------------------------------------------------- - -# this is an example of the Uber API -# as a demonstration of an API spec in YAML -swagger: '2.0' -info: - title: cse test - version: 1.0.0 - x-java-interface: org.apache.servicecomb.demo.tccserver.TestTcc - -# the domain of the service -#host: api.uber.com - -# array of all schemes that your API supports -#schemes: -# - https - -# will be prefixed to all paths -basePath: /pojo/rest/tcc-server -produces: - - application/json - -paths: - /splitParam: - post: - operationId: splitParam - parameters: - - name: index - in: query - required: true - type: number - - name: user - in: body - schema: - $ref: '#/definitions/User' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - /wrapParam: - post: - operationId: wrapParam - parameters: - - name: TestRequest - in: body - required: true - schema: - $ref: '#/definitions/TestRequest' - responses: - "200": - description: asdf - schema: - $ref: '#/definitions/User' - default: - description: asf - schema: - $ref: '#/definitions/Error' - -definitions: - Error: - type: object - x-java-class: org.apache.servicecomb.core.exception.CommonExceptionData - properties: - message: - type: string - TestRequest: - type: object - properties: - index: - type: number - user: - $ref: '#/definitions/User' - users: - type: array - items: - $ref: '#/definitions/User' - data: - type: string - format: byte - User: - type: object - properties: - name: - type: string - age: - type: number - index: - type: number diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/pom.xml b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/pom.xml index 353e81bf9b1..edd73c1428b 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/pom.xml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/pom.xml @@ -26,7 +26,7 @@ org.apache.servicecomb.demo demo-spring-boot-transport - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -42,24 +42,88 @@ demo-spring-boot-springmvc-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ReactiveStreamIT.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ReactiveStreamIT.java new file mode 100644 index 00000000000..c9c1957ddf5 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ReactiveStreamIT.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.springboot.springmvc.client; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.springboot.springmvc.client.ThirdSvcConfiguration.ReactiveStreamClient; +import org.apache.servicecomb.springboot.springmvc.client.ThirdSvcConfiguration.ReactiveStreamClient.Model; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +public class ReactiveStreamIT implements CategorizedTestCase { + @Autowired + @Qualifier("reactiveStreamProvider") + ReactiveStreamClient reactiveStreamProvider; + + @Override + public void testRestTransport() throws Exception { + testSseString(reactiveStreamProvider); + testSseModel(reactiveStreamProvider); + } + + private void testSseModel(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseModel(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(Model s) { + buffer.append(s.getName()).append(s.getAge()); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); + } + + private void testSseString(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseString(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(String s) { + buffer.append(s); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("abc", buffer.toString()); + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/TestAnnotationsSchema.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/TestAnnotationsSchema.java new file mode 100644 index 00000000000..7d77fa1be35 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/TestAnnotationsSchema.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.springboot.springmvc.client; + +import org.apache.http.HttpStatus; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.controller.Person; +import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; +import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Component +public class TestAnnotationsSchema implements CategorizedTestCase { + private static final String microserviceName = "springmvc"; + + private static final RestTemplate templateUrlWithServiceName = new CseRestTemplate(); + + @Override + public void testRestTransport() throws Exception { + templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); + testRequiredBody(templateUrlWithServiceName, microserviceName); + testRegExpPath(); + } + + private void testRegExpPath() { + String prefix = "cse://" + microserviceName; + String result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a?name={name}", + String.class, "a"); + TestMgr.check("a", result); + result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b?name={name}", + String.class, "ab"); + TestMgr.check("ab", result); + result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b/c?name={name}", + String.class, "abc"); + TestMgr.check("abc", result); + } + + private static void testRequiredBody(RestTemplate template, String microserviceName) { + String prefix = "cse://" + microserviceName; + Person user = new Person(); + + TestMgr.check("No user data found", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + user, + String.class, + "ha")); + + user.setName("world"); + TestMgr.check("ha world", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + user, + String.class, + "ha")); + + TestMgr.check("No user data found", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + null, + String.class, + "ha")); + + TestMgr.check("No user name found", + template.postForObject(prefix + "/annotations/say", + "", + String.class, + "ha")); + TestMgr.check("test", + template.postForObject(prefix + "/annotations/say", + "test", + String.class, + "ha")); + + try { + template.postForObject(prefix + "/annotations/testRequiredBody", + null, + String.class); + TestMgr.fail("should fail"); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ThirdSvcConfiguration.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ThirdSvcConfiguration.java new file mode 100644 index 00000000000..098a36828ed --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/ThirdSvcConfiguration.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.springboot.springmvc.client; + +import org.apache.servicecomb.provider.pojo.Invoker; +import org.reactivestreams.Publisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@Configuration +public class ThirdSvcConfiguration { + @RequestMapping(path = "/") + public interface ReactiveStreamClient { + class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + Publisher sseString(); + + @GetMapping("/sseModel") + Publisher sseModel(); + } + + @Bean("reactiveStreamProvider") + public ReactiveStreamClient reactiveStreamProvider() { + return Invoker.createProxy("springmvc", "ReactiveStreamController", ReactiveStreamClient.class); + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/UploadDownloadSchemaTest.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/UploadDownloadSchemaTest.java new file mode 100644 index 00000000000..18054cf4f4a --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-client/src/main/java/org/apache/servicecomb/springboot/springmvc/client/UploadDownloadSchemaTest.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.springboot.springmvc.client; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestOperations; + +@Component +public class UploadDownloadSchemaTest implements CategorizedTestCase { + RestOperations restOperations = RestTemplateBuilder.create(); + + @Override + public void testRestTransport() throws Exception { + testEmptyFileUploadWork(); + testNonEmptyFileUploadWork(); + } + + private void testNonEmptyFileUploadWork() throws Exception { + String file2Content = " bonjour"; + File someFile = File.createTempFile("upload2", ".txt"); + FileUtils.writeStringToFile(someFile, file2Content, StandardCharsets.UTF_8, false); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + Map params = new HashMap<>(); + params.put("name", "test"); + params.put("file", someFile); + HttpEntity> entity = new HttpEntity<>(params, headers); + String url = "servicecomb://springmvc/up/down/upload"; + String result = restOperations.postForObject(url, entity, String.class); + TestMgr.check("test; bonjour", result); + } + + private void testEmptyFileUploadWork() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + Map params = new HashMap<>(); + params.put("name", "test"); + HttpEntity> entity = new HttpEntity<>(params, headers); + String url = "servicecomb://springmvc/up/down/upload"; + String result = restOperations.postForObject(url, entity, String.class); + TestMgr.check("test;", result); + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/pom.xml b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/pom.xml index 490a61bf8c1..efa0723b59a 100644 --- a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/pom.xml +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/pom.xml @@ -26,7 +26,7 @@ org.apache.servicecomb.demo demo-spring-boot-transport - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -47,28 +47,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/AnnotationsSchema.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/AnnotationsSchema.java new file mode 100644 index 00000000000..b1bcaba83ed --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/AnnotationsSchema.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.springboot.springmvc.server; + +import org.apache.servicecomb.demo.controller.Person; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; + +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "annotations") +@RequestMapping(path = "/springmvc/annotations", produces = MediaType.APPLICATION_JSON) +public class AnnotationsSchema { + @GetMapping(path = "/add") + public int add(@RequestParam(name = "a", defaultValue = "10") int a, + @RequestParam(name = "b", defaultValue = "10") int b) { + return a + b; + } + + @RequestMapping(path = "/sayhei", method = RequestMethod.GET) + public String sayHei(@RequestHeader(name = "name", defaultValue = "test") String name) { + return "hei " + name; + } + + @GetMapping(path = "/sayhi") + @Parameters({ + @Parameter(name = "name", in = ParameterIn.QUERY, schema = @Schema(type = "string", defaultValue = "test")), + @Parameter(name = "age", in = ParameterIn.QUERY, schema = @Schema(type = "integer", defaultValue = "20")) + }) + public String sayHi(String name, int age) { + return "hi " + name + " your age is : " + age; + } + + @RequestMapping(path = "/saysomething", method = RequestMethod.POST) + public String saySomething(String prefix, @RequestBody(required = false) Person user) { + if (user == null || user.getName() == null || user.getName().isEmpty()) { + return "No user data found"; + } + return prefix + " " + user.getName(); + } + + @RequestMapping(path = "/say", method = RequestMethod.POST) + public String say(@RequestBody(required = false) String user) { + if (user == null || user.isEmpty()) { + return "No user name found"; + } + return user; + } + + @RequestMapping(path = "/testRequiredBody", method = RequestMethod.POST) + public String testRequiredBody(@RequestBody(required = true) Person user) { + if (user == null) { + return "Should not happen"; + } + return user.getName(); + } + + @RequestMapping(path = "/testRegExpPath/{path: .+}", method = RequestMethod.GET) + public String testRegExpPath(@RequestParam("name") String name) { + return name; + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/ReactiveStreamController.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/ReactiveStreamController.java new file mode 100644 index 00000000000..e82028d1286 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/ReactiveStreamController.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.springboot.springmvc.server; + + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.reactivex.rxjava3.core.Flowable; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +public class ReactiveStreamController { + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + public Publisher sseString() { + return Flowable.fromArray("a", "b", "c"); + } + + @GetMapping("/sseModel") + public Publisher sseModel() { + return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) + .map(item -> new Model("jack", item.intValue())); + } +} diff --git a/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/UploadDownloadSchema.java b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/UploadDownloadSchema.java new file mode 100644 index 00000000000..ec80bedbec4 --- /dev/null +++ b/demo/demo-spring-boot-transport/demo-spring-boot-springmvc-server/src/main/java/org/apache/servicecomb/springboot/springmvc/server/UploadDownloadSchema.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.springboot.springmvc.server; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import org.apache.commons.io.IOUtils; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.multipart.MultipartFile; + +import jakarta.ws.rs.core.MediaType; + +@RestSchema(schemaId = "UploadDownloadSchema") +@RequestMapping(path = "/up/down") +public class UploadDownloadSchema { + @PostMapping(path = "/upload", consumes = MediaType.MULTIPART_FORM_DATA) + public String fileUpload(@RequestPart MultipartFile file, @RequestPart(value = "name") String name) throws Exception { + StringBuilder result = new StringBuilder(); + result.append(name).append(";"); + if (file == null || file.isEmpty()) { + return result.toString(); + } + try (InputStream is = file.getInputStream()) { + result.append(IOUtils.toString(is, StandardCharsets.UTF_8)); + } + return result.toString(); + } +} diff --git a/demo/demo-spring-boot-transport/pom.xml b/demo/demo-spring-boot-transport/pom.xml index f4624ce9455..bd7650a3f26 100644 --- a/demo/demo-spring-boot-transport/pom.xml +++ b/demo/demo-spring-boot-transport/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-spring-boot-transport Java Chassis::Demo::Spring Boot::Transport diff --git a/demo/demo-springmvc/pom.xml b/demo/demo-springmvc/pom.xml index bd1d520deb6..c307aa7741b 100644 --- a/demo/demo-springmvc/pom.xml +++ b/demo/demo-springmvc/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-springmvc Java Chassis::Demo::Spring MVC diff --git a/demo/demo-springmvc/springmvc-client/pom.xml b/demo/demo-springmvc/springmvc-client/pom.xml index 6deecc1725c..17b7bbcd709 100644 --- a/demo/demo-springmvc/springmvc-client/pom.xml +++ b/demo/demo-springmvc/springmvc-client/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-springmvc - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT springmvc-client Java Chassis::Demo::Spring MVC::Client @@ -42,24 +42,88 @@ springmvc-server + + + + io.fabric8 + docker-maven-plugin + + + + servicecomb/service-center + service-center + + + server is ready + + + 30100 + + + + + + 30100:30100 + + + + + ${demo.service.name}:${project.version} + ${demo.service.name} + + + + -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 + + /maven/maven/${demo.service.name}-${project.version}.jar + + + service-center:sc.servicecomb.io + + + ServiceComb is ready + + + 8080 + + + + + + 7070:7070 + 8080:8080 + + + service-center + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + io.fabric8 docker-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-run-config - ${project.version} - - - - diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java index 72d6b845b32..b827884d323 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcClient.java @@ -17,7 +17,6 @@ package org.apache.servicecomb.demo.springmvc; -import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -26,10 +25,8 @@ import org.apache.servicecomb.demo.CategorizedTestCaseRunner; import org.apache.servicecomb.demo.DemoConst; import org.apache.servicecomb.demo.TestMgr; -import org.apache.servicecomb.demo.controller.Controller; import org.apache.servicecomb.demo.controller.Person; import org.apache.servicecomb.demo.springmvc.client.CodeFirstRestTemplateSpringmvc; -import org.apache.servicecomb.demo.springmvc.client.ThirdSvc.ThirdSvcClient; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; @@ -66,13 +63,20 @@ public class SpringmvcClient { private static RestOperations restTemplate; - private static Controller controller; + public static void main(String[] args) { + new SpringApplicationBuilder(SpringmvcClient.class).web(WebApplicationType.NONE).run(args); - public static void main(String[] args) throws Exception { - try { - new SpringApplicationBuilder(SpringmvcClient.class).web(WebApplicationType.NONE).run(args); + run(); + } + + private static void changeTransport(String microserviceName, String transport) { + InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); + TestMgr.setMsg(microserviceName, transport); + } - run(); + public static void run() { + try { + runImpl(); } catch (Throwable e) { TestMgr.check("success", "failed"); LOGGER.error("-------------- test failed -------------"); @@ -80,32 +84,34 @@ public static void main(String[] args) throws Exception { LOGGER.error("-------------- test failed -------------"); } TestMgr.summary(); - LOGGER.info("-------------- last time updated checks(maybe more/less): 1313 -------------"); + LOGGER.info("-------------- last time updated checks(maybe more/less): 1344 -------------"); } - private static void changeTransport(String microserviceName, String transport) { - InMemoryDynamicPropertiesSource.update("servicecomb.references.transport." + microserviceName, transport); - TestMgr.setMsg(microserviceName, transport); - } - - public static void run() throws Exception { + private static void runImpl() throws Exception { testHttpClientsIsOk(); templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); restTemplate = RestTemplateBuilder.create(); templateUrlWithProviderPrefix.setRequestFactory(new UrlWithProviderPrefixClientHttpRequestFactory("/pojo/rest")); - controller = BeanUtils.getBean("controller"); String prefix = "cse://springmvc"; String microserviceName = "springmvc"; try { - // this test class is intended for retry hanging issue JAV-127 + // only works in rest, highway does not have name param, because not defined. + templateUrlWithServiceName.getForObject(prefix + "/controller/sayhi?name=throwexception", String.class); + TestMgr.check("true", "false"); + } catch (Exception e) { + TestMgr.check("true", "true"); + } + try { + // only works in rest, highway does not have name param, because not defined. templateUrlWithServiceName.getForObject(prefix + "/controller/sayhi?name=throwexception", String.class); TestMgr.check("true", "false"); } catch (Exception e) { TestMgr.check("true", "true"); } + testHandler(microserviceName); CodeFirstRestTemplateSpringmvc codeFirstClient = BeanUtils.getContext().getBean(CodeFirstRestTemplateSpringmvc.class); @@ -215,22 +221,11 @@ private static void testAllTransport(String microserviceName) { testControllerAllTransport(templateUrlWithServiceName, microserviceName); - testController(); - testRequiredBody(templateUrlWithServiceName, microserviceName); testSpringMvcDefaultValuesAllTransport(templateUrlWithServiceName, microserviceName); testSpringMvcDefaultValuesJavaPrimitiveAllTransport(templateUrlWithServiceName, microserviceName); - testThirdService(); } } - private static void testThirdService() { - ThirdSvcClient client = BeanUtils.getContext().getBean(ThirdSvcClient.class); - - Date date = new Date(); - ResponseEntity responseEntity = client.responseEntity(date); - TestMgr.check(date, responseEntity.getBody()); - } - private static void testControllerRest(RestTemplate template, String microserviceName) { String prefix = "cse://" + microserviceName; @@ -334,56 +329,6 @@ private static void testControllerAllTransport(RestTemplate template, String mic "ha")); } - private static void testController() { - Person user = new Person(); - user.setName("world"); - TestMgr.check("ha world", controller.saySomething("ha", user)); - } - - private static void testRequiredBody(RestTemplate template, String microserviceName) { - String prefix = "cse://" + microserviceName; - Person user = new Person(); - - TestMgr.check("No user data found", - template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", - user, - String.class, - "ha")); - - user.setName("world"); - TestMgr.check("ha world", - template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", - user, - String.class, - "ha")); - - TestMgr.check("No user data found", - template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", - null, - String.class, - "ha")); - - TestMgr.check("No user name found", - template.postForObject(prefix + "/annotations/say", - "", - String.class, - "ha")); - TestMgr.check("test", - template.postForObject(prefix + "/annotations/say", - "test", - String.class, - "ha")); - - try { - template.postForObject(prefix + "/annotations/testRequiredBody", - null, - String.class); - TestMgr.fail("should fail"); - } catch (InvocationException e) { - TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); - } - } - private static void testSpringMvcDefaultValuesRest(RestTemplate template, String microserviceName) { String cseUrlPrefix = "cse://" + microserviceName + "/SpringMvcDefaultValues/"; String result = template.getForObject(cseUrlPrefix + "/query?d=10", String.class); diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java index f561b954fdb..d30edc6b495 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/CodeFirstRestTemplateSpringmvc.java @@ -213,12 +213,12 @@ private void testCodeFirstTestForm(RestOperations template, String cseUrlPrefix) Map map = new HashMap<>(); String code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); - HttpEntity> formEntiry = new HttpEntity<>(map, formHeaders); + HttpEntity> formEntry = new HttpEntity<>(map, formHeaders); TestMgr.check(code + "null", - template.postForEntity(cseUrlPrefix + "/testform", formEntiry, String.class).getBody()); + template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); map.put("form2", "hello"); TestMgr - .check(code + "hello", template.postForEntity(cseUrlPrefix + "/testform", formEntiry, String.class).getBody()); + .check(code + "hello", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); } private void testCodeFirstTestFormHighway(RestOperations template, String cseUrlPrefix) { @@ -228,16 +228,16 @@ private void testCodeFirstTestFormHighway(RestOperations template, String cseUrl String code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); map.put("form2", ""); - HttpEntity> formEntiry = new HttpEntity<>(map, formHeaders); - TestMgr.check(code + "", template.postForEntity(cseUrlPrefix + "/testform", formEntiry, String.class).getBody()); + HttpEntity> formEntry = new HttpEntity<>(map, formHeaders); + TestMgr.check(code + "", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); map = new HashMap<>(); code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); map.put("form2", null); - formEntiry = new HttpEntity<>(map, formHeaders); + formEntry = new HttpEntity<>(map, formHeaders); TestMgr.check(code + "null", - template.postForEntity(cseUrlPrefix + "/testform", formEntiry, String.class).getBody()); + template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); } private void testCodeFirstTestFormRest(RestOperations template, String cseUrlPrefix) { @@ -247,8 +247,8 @@ private void testCodeFirstTestFormRest(RestOperations template, String cseUrlPre String code = "servicecomb%2bwelcome%40%23%24%25%5e%26*()%3d%3d"; map.put("form1", code); map.put("form2", ""); - HttpEntity> formEntiry = new HttpEntity<>(map, formHeaders); + HttpEntity> formEntry = new HttpEntity<>(map, formHeaders); // Rest will have empty string, but users will try to avoid depend on this, This is different from highway - TestMgr.check(code + "", template.postForEntity(cseUrlPrefix + "/testform", formEntiry, String.class).getBody()); + TestMgr.check(code + "", template.postForEntity(cseUrlPrefix + "/testform", formEntry, String.class).getBody()); } } diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/ErrorToProducerResponseConverter.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ResponseOKData.java similarity index 57% rename from swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/ErrorToProducerResponseConverter.java rename to demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ResponseOKData.java index 490f5d498b5..df2bf9c5f42 100644 --- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/ErrorToProducerResponseConverter.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ResponseOKData.java @@ -14,21 +14,35 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.swagger.invocation.exception; +package org.apache.servicecomb.demo.springmvc.client; -import jakarta.ws.rs.core.Response.Status; +public class ResponseOKData { + private String errorCode; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; + private String errorMessage; -public class ErrorToProducerResponseConverter implements ExceptionToProducerResponseConverter { - @Override - public Class getExceptionClass() { - return Error.class; + public ResponseOKData() { + + } + + public ResponseOKData(String errorCode, String errorMessage) { + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMessage() { + return errorMessage; } - @Override - public Response convert(SwaggerInvocation swaggerInvocation, Error e) { - return Response.create(Status.OK, "response from error: " + e.getMessage()); + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestAnnotationsSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestAnnotationsSchema.java new file mode 100644 index 00000000000..89f7862384b --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestAnnotationsSchema.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.springmvc.client; + +import org.apache.http.HttpStatus; +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.controller.Person; +import org.apache.servicecomb.provider.springmvc.reference.CseRestTemplate; +import org.apache.servicecomb.provider.springmvc.reference.UrlWithServiceNameClientHttpRequestFactory; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +@Component +public class TestAnnotationsSchema implements CategorizedTestCase { + private static final String microserviceName = "springmvc"; + + private static final RestTemplate templateUrlWithServiceName = new CseRestTemplate(); + + @Override + public void testAllTransport() throws Exception { + templateUrlWithServiceName.setRequestFactory(new UrlWithServiceNameClientHttpRequestFactory()); + testRequiredBody(templateUrlWithServiceName, microserviceName); + testRegExpPath(); + } + + private void testRegExpPath() { + String prefix = "cse://" + microserviceName; + String result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a?name={name}", + String.class, "a"); + TestMgr.check("a", result); + result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b?name={name}", + String.class, "ab"); + TestMgr.check("ab", result); + result = templateUrlWithServiceName.getForObject(prefix + "/annotations/testRegExpPath/a/b/c?name={name}", + String.class, "abc"); + TestMgr.check("abc", result); + } + + private static void testRequiredBody(RestTemplate template, String microserviceName) { + String prefix = "cse://" + microserviceName; + Person user = new Person(); + + TestMgr.check("No user data found", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + user, + String.class, + "ha")); + + user.setName("world"); + TestMgr.check("ha world", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + user, + String.class, + "ha")); + + TestMgr.check("No user data found", + template.postForObject(prefix + "/annotations/saysomething?prefix={prefix}", + null, + String.class, + "ha")); + + TestMgr.check("No user name found", + template.postForObject(prefix + "/annotations/say", + "", + String.class, + "ha")); + TestMgr.check("test", + template.postForObject(prefix + "/annotations/say", + "test", + String.class, + "ha")); + + try { + template.postForObject(prefix + "/annotations/testRequiredBody", + null, + String.class); + TestMgr.fail("should fail"); + } catch (InvocationException e) { + TestMgr.check(e.getStatusCode(), HttpStatus.SC_BAD_REQUEST); + } + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestBigNumberSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestBigNumberSchema.java new file mode 100644 index 00000000000..3b70709e6d2 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestBigNumberSchema.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class TestBigNumberSchema implements CategorizedTestCase { + interface IBigNumberSchema { + BigInteger bigInteger(BigInteger intHeader, BigInteger intQuery, BigInteger intForm); + + BigDecimal bigDecimal(BigDecimal decimalHeader, BigDecimal decimalQuery, BigDecimal decimalForm); + } + + @RpcReference(microserviceName = "springmvc", schemaId = "BigNumberSchema") + private IBigNumberSchema schema; + + @Override + public void testAllTransport() throws Exception { + testBigInteger(); + testBigDecimal(); + } + + public void testBigInteger() { + BigInteger result = schema.bigInteger(BigInteger.valueOf(100L), BigInteger.valueOf(3000000000000000000L), + BigInteger.valueOf(200L)); + TestMgr.check("3000000000000000300", result.toString()); + } + + public void testBigDecimal() { + BigDecimal a = BigDecimal.valueOf(100.1D); + BigDecimal b = BigDecimal.valueOf(300000000000000000.1D); + BigDecimal c = BigDecimal.valueOf(200.1D); + BigDecimal expected = a.add(b).add(c); + BigDecimal result = schema.bigDecimal(a, b, c); + TestMgr.check(expected, result); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestControllerImpl.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestControllerImpl.java index 7e74b18fc81..3264caa9fa9 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestControllerImpl.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestControllerImpl.java @@ -18,20 +18,62 @@ import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.controller.Controller; +import org.apache.servicecomb.demo.controller.Person; +import org.apache.servicecomb.demo.controller.PersonAlias; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.http.client.common.HttpUtils; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; - @Component public class TestControllerImpl implements CategorizedTestCase { - RestOperations restTemplate = RestTemplateBuilder.create(); - private static final String SERVER = "servicecomb://springmvc"; + @Autowired + Controller controller; + + RestOperations restTemplate = RestTemplateBuilder.create(); + + @Override public void testRestTransport() throws Exception { testQueryParamSpecial(); + testResponseModel(); + } + + @Override + public void testAllTransport() throws Exception { + testControllerBodyModel(); + testControllerHeaderModel(); + } + + private void testControllerHeaderModel() throws Exception { + // HTTP header只允许 ASCII 字符。 + // 在Header参数中使用特殊字符,需要在发送请求前进行编码,在收到响应后进行解码,具体编码方式可以业务自行选择。 + // 如果不进行编码,则可能会出现乱码(HTTP协议历史上只标准化了ASCII字符,其他字符集可能工作,可能不工作)。 + String encodedResult = controller.sayHei(HttpUtils.encodeURLParam("中文HTTP Header")); + TestMgr.check("hei 中文HTTP Header", HttpUtils.decodeURLParam(encodedResult)); + + // 普通ASCII字符不需要编码 + TestMgr.check("hei ~!@#$%^&*()_+-={}[]|\\:\";''<>?,./AL340", + controller.sayHei("~!@#$%^&*()_+-={}[]|\\:\";''<>?,./AL340")); + } + + private void testControllerBodyModel() { + Person user = new Person(); + user.setName("world"); + TestMgr.check("ha world", controller.saySomething("ha", user)); + } + + private void testResponseModel() { + Person person = restTemplate.getForObject(SERVER + "/springmvc/controller/testResponseModel", Person.class); + TestMgr.check("jack", person.getName()); + + PersonAlias personAlias = restTemplate.getForObject(SERVER + "/springmvc/controller/testResponseModel", + PersonAlias.class); + TestMgr.check("jack", personAlias.getName()); } private void testQueryParamSpecial() { diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDataTypesAnnotationsSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDataTypesAnnotationsSchema.java new file mode 100644 index 00000000000..6234b0a99f6 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDataTypesAnnotationsSchema.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.springmvc.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class TestDataTypesAnnotationsSchema implements CategorizedTestCase { + public interface DataTypesAnnotationsItf { + int[] testIntArrayQuery(int[] param); + + Integer[] testIntegerArrayQuery(Integer[] param); + } + + @RpcReference(schemaId = "DataTypesAnnotationsSchema", microserviceName = "springmvc") + private DataTypesAnnotationsItf client; + + @Override + public void testAllTransport() throws Exception { + testIntArrayQuery(); + testIntegerArrayQuery(); + } + + private void testIntArrayQuery() { + int[] request = new int[] {5, 11, 4}; + int[] result = client.testIntArrayQuery(request); + TestMgr.check(request.length, result.length); + TestMgr.check(request[1], result[1]); + } + + private void testIntegerArrayQuery() { + Integer[] request = new Integer[] {5, 11, 4}; + Integer[] result = client.testIntegerArrayQuery(request); + TestMgr.check(request.length, result.length); + TestMgr.check(request[1], result[1]); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDateTimeSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDateTimeSchema.java index df632549093..580192c71a1 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDateTimeSchema.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDateTimeSchema.java @@ -95,16 +95,6 @@ public TestDateTimeSchema() { } - @Override - public void testRestTransport() throws Exception { - - } - - @Override - public void testHighwayTransport() throws Exception { - - } - @Override public void testAllTransport() throws Exception { testDateTimeSchema(); @@ -141,9 +131,9 @@ private void testDateTimeSchema() { private void testDateTimeSchemaMulticast() throws Exception { DiscoveryContext context = new DiscoveryContext(); VersionedCache serversVersionedCache = discoveryTree.discovery(context, "springmvctest", "springmvc"); - List enpoints = serversVersionedCache.data(); + List endpoints = serversVersionedCache.data(); - for (String endpoint : enpoints) { + for (String endpoint : endpoints) { InvocationContext invocationContext = new InvocationContext(); invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, endpoint); Date date = new Date(); @@ -165,11 +155,11 @@ private Endpoint parseEndpoint(String endpointUri) throws Exception { private void testDateTimeSchemaMulticastRestTemplate() throws Exception { DiscoveryContext context = new DiscoveryContext(); VersionedCache serversVersionedCache = discoveryTree.discovery(context, "springmvctest", "springmvc"); - List enpoints = serversVersionedCache.data(); + List endpoints = serversVersionedCache.data(); RestOperations restTemplate = RestTemplateBuilder.create(); - for (String endpoint : enpoints) { + for (String endpoint : endpoints) { CseHttpEntity entity = new CseHttpEntity<>(null); InvocationContext invocationContext = new InvocationContext(); invocationContext.addLocalContext(LoadBalanceFilter.SERVICECOMB_SERVER_ENDPOINT, endpoint); diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java index 20a920a63a1..f1ba4b87095 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestDownloadSchema.java @@ -17,25 +17,73 @@ package org.apache.servicecomb.demo.springmvc.client; +import java.net.URI; import java.util.Collections; +import java.util.List; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart; import org.apache.servicecomb.provider.springmvc.reference.RestTemplateBuilder; +import org.apache.servicecomb.registry.DiscoveryManager; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; @Component public class TestDownloadSchema implements CategorizedTestCase { + @Autowired + DiscoveryManager discoveryManager; + @Override public void testRestTransport() throws Exception { testDownloadFileAndDeleted(); + testDownloadFileAndDeletedCN(); testDownloadFileNotDeleted(); testDownloadFileWithNull(); testSetContentTypeByResponseEntity(); + testResponseOKException(); + } + + private void testResponseOKException() { + List instances = + discoveryManager.findServiceInstances("springmvctest", "springmvc"); + String endpoint = instances.get(0).getEndpoints().stream() + .filter(item -> item.startsWith("rest")).findFirst().get(); + URI endpointItem = URI.create(endpoint); + RestTemplate template = new RestTemplate(); + + // This is for compatible usage. For best practise, any status code + // should have only one type of response. + ResponseEntity resultFail = template.getForEntity( + "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + + "/api/download/testResponseOKExceptionBean?exception=true", ResponseOKData.class); + TestMgr.check(200, resultFail.getStatusCode().value()); + TestMgr.check("code-005", resultFail.getBody().getErrorCode()); + TestMgr.check("error-005", resultFail.getBody().getErrorMessage()); + ResponseEntity resultOK = template.getForEntity( + "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + + "/api/download/testResponseOKExceptionBean?exception=false", boolean.class); + TestMgr.check(true, resultOK.getBody()); + + resultFail = template.getForEntity( + "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + + "/api/download/testResponseOKExceptionDownload?exception=true&content=ddd&contentType=plain/text", + ResponseOKData.class); + TestMgr.check(200, resultFail.getStatusCode().value()); + TestMgr.check("code-005", resultFail.getBody().getErrorCode()); + TestMgr.check("error-005", resultFail.getBody().getErrorMessage()); + + ResponseEntity resultPartOK = template.getForEntity( + "http://" + endpointItem.getHost() + ":" + endpointItem.getPort() + + "/api/download/testResponseOKExceptionDownload?exception=false&content=ddd&contentType=plain/text", + String.class); + TestMgr.check(200, resultPartOK.getStatusCode().value()); + TestMgr.check("ddd", resultPartOK.getBody()); } private void testDownloadFileAndDeleted() throws Exception { @@ -50,6 +98,19 @@ private void testDownloadFileAndDeleted() throws Exception { TestMgr.check(exists, false); } + private void testDownloadFileAndDeletedCN() throws Exception { + RestOperations restTemplate = RestTemplateBuilder.create(); + ReadStreamPart readStreamPart = restTemplate + .getForObject("servicecomb://springmvc/download/deleteAfterFinished?content={1}&fileName={2}", + ReadStreamPart.class, "hello", "中文"); + String hello = readStreamPart.saveAsString().get(); + TestMgr.check(hello, "hello"); + + boolean exists = restTemplate + .getForObject("servicecomb://springmvc/download/assertLastFileDeleted", boolean.class); + TestMgr.check(exists, false); + } + private void testDownloadFileWithNull() throws Exception { RestOperations restTemplate = RestTemplateBuilder.create(); ReadStreamPart readStreamPart = restTemplate diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestFactoryBean.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestFactoryBean.java new file mode 100644 index 00000000000..78295f47a8a --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestFactoryBean.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.springmvc.client.factory.ServiceBean; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +@Component +public class TestFactoryBean implements CategorizedTestCase, ApplicationContextAware { + ApplicationContext applicationContext; + + @Override + public void testRestTransport() throws Exception { + ServiceBean serviceBean = (ServiceBean) this.applicationContext.getBean("ServiceFactoryBean"); + TestMgr.check("a-3", serviceBean.getName()); + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestManagementEndpoint.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestManagementEndpoint.java index 07c04729db3..edac80715a8 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestManagementEndpoint.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestManagementEndpoint.java @@ -16,9 +16,7 @@ */ package org.apache.servicecomb.demo.springmvc.client; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import org.apache.servicecomb.demo.CategorizedTestCase; import org.apache.servicecomb.demo.TestMgr; @@ -149,10 +147,7 @@ private void testSchemeInterfaceSpringmvcPathNonSlashCorrect() { } private void testSchemeInterfaceSpringmvcContentCorrect() { - Set schemaIds = new HashSet<>(); - schemaIds.add("SchemeInterfaceSpringmvc"); - Map contents = managementEndpoint.schemaContents(schemaIds); - TestMgr.check(1, contents.size()); + Map contents = managementEndpoint.schemaContents(); TestMgr.check(CONTENT_SCHEMA, contents.get("SchemeInterfaceSpringmvc")); } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestMaxHttpUrlLength.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestMaxHttpUrlLength.java index 5d3422ace32..08f9cdda1ee 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestMaxHttpUrlLength.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestMaxHttpUrlLength.java @@ -54,14 +54,4 @@ private void testUrlNotLongerThan4096() { TestMgr.check(REQUEST_URI_TOO_LONG.getStatusCode(), e.getStatusCode()); } } - - @Override - public void testHighwayTransport() throws Exception { - - } - - @Override - public void testAllTransport() throws Exception { - - } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdSvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdSvc.java new file mode 100644 index 00000000000..98592c9bce2 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestThirdSvc.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client; + +import java.util.Date; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.demo.springmvc.client.ThirdSvc.ThirdSvcClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; + +@Component +public class TestThirdSvc implements CategorizedTestCase { + private ThirdSvcClient client; + + @Autowired + public void setThirdSvcClient(ThirdSvcClient client) { + this.client = client; + } + + @Override + public void testAllTransport() throws Exception { + testResponseEntity(); + + testDifferentParameterName(); + } + + private void testDifferentParameterName() { + String result = client.getAuthorization("test", "param", "auth"); + TestMgr.check("testparamauth", result); + } + + private void testResponseEntity() { + Date date = new Date(); + ResponseEntity responseEntity = client.responseEntity(date); + TestMgr.check(date, responseEntity.getBody()); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestTransportSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestTransportSchema.java new file mode 100644 index 00000000000..d818ed439d4 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestTransportSchema.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; + +@Component +public class TestTransportSchema implements CategorizedTestCase { + interface TransportClient { + boolean restTransport(); + + boolean highwayTransport(); + } + + @RpcReference(microserviceName = "springmvc", schemaId = "TransportSchema") + private TransportClient transportClient; + + @Override + public void testRestTransport() throws Exception { + testTransportSchema(); + } + + @Override + public void testHighwayTransport() throws Exception { + testTransportSchema(); + } + + @Override + public void testAllTransport() throws Exception { + testTransportSchema(); + } + + private void testTransportSchema() { + TestMgr.check(true, transportClient.highwayTransport()); + TestMgr.check(true, transportClient.restTransport()); + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java index 45b5d4c66b6..0e6d810d4a5 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestUploadSchema.java @@ -59,7 +59,9 @@ public void testRestTransport() throws Exception { testUploadMultiBigFiles(); testFileUploadMultiRpc(); testUploadFileAndAttribute(); + testUploadFileAndAttributeCN(); testUploadFileRequestPartAttribute(); + testUploadFileRequestPartAttributeList(); } private void testServerStartupSuccess() { @@ -126,6 +128,23 @@ private void testUploadFileAndAttribute() throws Exception { TestMgr.check("hi test", result); } + private void testUploadFileAndAttributeCN() throws Exception { + RestOperations template = RestTemplateBuilder.create(); + Map map = new HashMap<>(); + String message = "hi"; + File file = File.createTempFile("中文名称", ".txt"); + FileUtils.writeStringToFile(file, "test", StandardCharsets.UTF_8, false); + + map.put("file", new FileSystemResource(file)); + map.put("attribute", message); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); + String result = template.postForObject("servicecomb://springmvc/upload/uploadFileAndAttribute", + new HttpEntity<>(map, headers), String.class); + TestMgr.check("hi test", result); + } + + private void testUploadFileRequestPartAttribute() throws Exception { RestOperations template = RestTemplateBuilder.create(); Map map = new HashMap<>(); @@ -141,4 +160,25 @@ private void testUploadFileRequestPartAttribute() throws Exception { new HttpEntity<>(map, headers), String.class); TestMgr.check("hi test", result); } + + private void testUploadFileRequestPartAttributeList() throws Exception { + RestOperations template = RestTemplateBuilder.create(); + MultiValueMap map = new LinkedMultiValueMap<>(); + String message1 = "msg1"; + String message2 = "msg2"; + File file1 = File.createTempFile("file1", ".txt"); + FileUtils.writeStringToFile(file1, "test1", StandardCharsets.UTF_8, false); + File file2 = File.createTempFile("file2", ".txt"); + FileUtils.writeStringToFile(file2, "test2", StandardCharsets.UTF_8, false); + + map.add("files", new FileSystemResource(file1)); + map.add("files", new FileSystemResource(file2)); + map.add("attributes", message1); + map.add("attributes", message2); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(org.springframework.http.MediaType.MULTIPART_FORM_DATA); + String result = template.postForObject("servicecomb://springmvc/upload/uploadFileRequestPartAttributeList", + new HttpEntity<>(map, headers), String.class); + TestMgr.check("test1test2msg1msg2", result); + } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestWeakSpringmvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestWeakSpringmvc.java index b6d6e768003..df9f8236215 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestWeakSpringmvc.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/TestWeakSpringmvc.java @@ -120,16 +120,6 @@ public class TestWeakSpringmvc implements CategorizedTestCase { private RestOperations restTemplate = RestTemplateBuilder.create(); - @Override - public void testRestTransport() throws Exception { - - } - - @Override - public void testHighwayTransport() throws Exception { - - } - @Override public void testAllTransport() throws Exception { testDiffName(); @@ -138,10 +128,10 @@ public void testAllTransport() throws Exception { testGenericsModel(); - testSpecailNameModel(); + testSpecialNameModel(); } - private void testSpecailNameModel() { + private void testSpecialNameModel() { SpecialNameModel model = new SpecialNameModel(); model.setaIntName(30); SpecialNameModel result = specialNameModelInf.specialNameModel(0, model); diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdSvc.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdSvc.java index 070ca101eba..3cbd8dd97dd 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdSvc.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/ThirdSvc.java @@ -29,9 +29,12 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; @@ -51,6 +54,12 @@ public interface ThirdSvcClient { @Header(name = "h2", schema = @Schema(implementation = String.class))}) @RequestMapping(path = "/responseEntity", method = RequestMethod.POST) ResponseEntity responseEntity(@RequestAttribute("date") Date date); + + @GetMapping(value = "/getAuthorization", produces = {"application/json"}) + String getAuthorization( + @RequestHeader(value = "test") String test, + @RequestParam(value = "param") String param, + @RequestHeader("Authorization") String authorization); } @Bean diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorStartEvent.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceBean.java similarity index 75% rename from core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorStartEvent.java rename to demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceBean.java index 3779bde03f5..1a25838d083 100644 --- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorStartEvent.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceBean.java @@ -14,12 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.core.event; -import org.apache.servicecomb.core.Invocation; +package org.apache.servicecomb.demo.springmvc.client.factory; -public class InvocationRunInExecutorStartEvent extends InvocationBaseEvent { - public InvocationRunInExecutorStartEvent(Invocation invocation) { - super(invocation); +public class ServiceBean { + private final String name; + + public ServiceBean(String name) { + this.name = name; + } + + public String getName() { + return name; } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceFactoryBean.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceFactoryBean.java new file mode 100644 index 00000000000..9bee6805c48 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceFactoryBean.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client.factory; + +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component("ServiceFactoryBean") +public class ServiceFactoryBean implements FactoryBean { + + ServiceWithReference serviceWithReference; + + @Autowired + public ServiceFactoryBean(ServiceWithReference serviceWithReference) { + this.serviceWithReference = serviceWithReference; + } + + @Override + public ServiceBean getObject() throws Exception { + return new ServiceBean(serviceWithReference.test("a")); + } + + @Override + public Class getObjectType() { + return ServiceBean.class; + } + + @Override + public boolean isSingleton() { + return true; + } +} diff --git a/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReference.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReference.java new file mode 100644 index 00000000000..f371bf35148 --- /dev/null +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReference.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.client.factory; + +public interface ServiceWithReference { + String test(String name); +} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/AbstractPeriodMeter.java b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReferenceImpl.java similarity index 60% rename from foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/AbstractPeriodMeter.java rename to demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReferenceImpl.java index d9e2f46d583..a5f8d7c6bb1 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/AbstractPeriodMeter.java +++ b/demo/demo-springmvc/springmvc-client/src/main/java/org/apache/servicecomb/demo/springmvc/client/factory/ServiceWithReferenceImpl.java @@ -14,29 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.foundation.metrics.meter; -import java.util.Collections; -import java.util.List; +package org.apache.servicecomb.demo.springmvc.client.factory; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; +import org.apache.servicecomb.demo.springmvc.client.SchemeInterfaceSpringmvc; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.springframework.stereotype.Component; -public abstract class AbstractPeriodMeter implements PeriodMeter { - protected Id id; - - protected List allMeasurements = Collections.emptyList(); - - public Id id() { - return id; - } +@Component +public class ServiceWithReferenceImpl implements ServiceWithReference { + @RpcReference(schemaId = "SchemeInterfaceSpringmvc", microserviceName = "springmvc") + private SchemeInterfaceSpringmvc springmvc; @Override - public Iterable measure() { - return allMeasurements; - } - - public boolean hasExpired() { - return false; + public String test(String name) { + return name + "-" + springmvc.add(1, 2); } } diff --git a/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml b/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml index eec6b301cb7..2fe0f881f55 100644 --- a/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml +++ b/demo/demo-springmvc/springmvc-client/src/main/resources/microservice.yaml @@ -54,9 +54,6 @@ servicecomb: userDefinedEndpoint.enabled: true strategy: name: WeightedResponse - retryEnabled: true - retryOnSame: 1 - retryOnNext: 1 filter.status.enabled: false fallbackpolicy: Consumer: @@ -113,10 +110,17 @@ servicecomb: matches: - apiPath: prefix: "/retry/governance/" + retry-success: | + matches: + - apiPath: + prefix: "/codeFirstSpringmvc/retrySuccess" retry: retry-governance: | maxAttempts: 2 retryOnResponseStatus: [500] + retry-success: | + maxAttempts: 2 + retryOnResponseStatus: [503] #########SSL options # open jdk 8 now TLSv1.3 not available diff --git a/demo/demo-springmvc/springmvc-client/src/test/java/org/apache/servicecomb/demo/springmvc/SpringMvcIT.java b/demo/demo-springmvc/springmvc-client/src/test/java/org/apache/servicecomb/demo/springmvc/SpringMvcIT.java index e39f55eb2fb..c445ce1a647 100644 --- a/demo/demo-springmvc/springmvc-client/src/test/java/org/apache/servicecomb/demo/springmvc/SpringMvcIT.java +++ b/demo/demo-springmvc/springmvc-client/src/test/java/org/apache/servicecomb/demo/springmvc/SpringMvcIT.java @@ -28,16 +28,14 @@ @ExtendWith(SpringExtension.class) @SpringBootTest(classes = SpringmvcClient.class) public class SpringMvcIT { - @BeforeEach public void setUp() { TestMgr.errors().clear(); } @Test - public void clientGetsNoError() throws Exception { + public void clientGetsNoError() { SpringmvcClient.run(); - - Assertions.assertTrue(TestMgr.errors().isEmpty()); + Assertions.assertTrue(TestMgr.isSuccess()); } } diff --git a/demo/demo-springmvc/springmvc-server/pom.xml b/demo/demo-springmvc/springmvc-server/pom.xml index 15deaf81710..f88876261d6 100644 --- a/demo/demo-springmvc/springmvc-server/pom.xml +++ b/demo/demo-springmvc/springmvc-server/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb.demo demo-springmvc - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT springmvc-server Java Chassis::Demo::Spring MVC::Server @@ -51,28 +51,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcServer.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcServer.java index aa91890c344..52ede2792d8 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcServer.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/SpringmvcServer.java @@ -17,6 +17,8 @@ package org.apache.servicecomb.demo.springmvc; +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -25,5 +27,20 @@ public class SpringmvcServer { public static void main(String[] args) throws Exception { new SpringApplicationBuilder(SpringmvcServer.class).web(WebApplicationType.NONE).run(args); + + runTests(); + + TestMgr.summary(); + if (!TestMgr.isSuccess()) { + System.exit(1); + } + } + + private static void runTests() { + try { + CategorizedTestCaseRunner.runCategorizedTestCase("springmvc"); + } catch (Exception e) { + TestMgr.failed("runCategorizedTestCase failed", e); + } } } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/filter/ProviderTestFilter.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/filter/ProviderTestFilter.java index 8c0e1f3f4ac..310f68e0ca0 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/filter/ProviderTestFilter.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/filter/ProviderTestFilter.java @@ -20,17 +20,17 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.stereotype.Component; @Component -public class ProviderTestFilter implements ProviderFilter { +public class ProviderTestFilter extends AbstractFilter implements ProviderFilter { @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1800; } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsTest.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsSchema.java similarity index 94% rename from demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsTest.java rename to demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsSchema.java index a6bd7595747..45f2c01e4b3 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsTest.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/AnnotationsSchema.java @@ -34,7 +34,7 @@ @RestSchema(schemaId = "annotations") @RequestMapping(path = "/springmvc/annotations", produces = MediaType.APPLICATION_JSON) -public class AnnotationsTest { +public class AnnotationsSchema { @GetMapping(path = "/add") public int add(@RequestParam(name = "a", defaultValue = "10") int a, @RequestParam(name = "b", defaultValue = "10") int b) { @@ -78,4 +78,9 @@ public String testRequiredBody(@RequestBody(required = true) Person user) { } return user.getName(); } + + @RequestMapping(path = "/testRegExpPath/{path: .+}", method = RequestMethod.GET) + public String testRegExpPath(@RequestParam("name") String name) { + return name; + } } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/BigNumberSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/BigNumberSchema.java new file mode 100644 index 00000000000..1190fde43f8 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/BigNumberSchema.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.server; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestAttribute; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "BigNumberSchema") +@RequestMapping("/bigNumber") +public class BigNumberSchema { + @PostMapping(path = "/integer", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public BigInteger bigInteger(@RequestHeader("intHeader") BigInteger intHeader, + @RequestParam("intQuery") BigInteger intQuery, @RequestAttribute("intForm") BigInteger intForm) { + return intHeader.add(intQuery).add(intForm); + } + + @PostMapping(path = "/decimal", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public BigDecimal bigDecimal(@RequestHeader("decimalHeader") BigDecimal decimalHeader, + @RequestParam("decimalQuery") BigDecimal decimalQuery, @RequestAttribute("decimalForm") BigDecimal decimalForm) { + return decimalHeader.add(decimalQuery).add(decimalForm); + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java index 8ea395a3ee8..36f8518afe4 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/CodeFirstSpringmvc.java @@ -148,6 +148,15 @@ public ResponseEntity responseEntity(InvocationContext c1, @RequestAttribu return new ResponseEntity<>(date, headers, HttpStatus.ACCEPTED); } + @GetMapping(value = "/getAuthorization", produces = {"application/json"}) + public String getAuthorization( + @RequestHeader(value = "test") String test, + @RequestParam(value = "param") String param, + @Parameter(description = "Authorization header", required = true, in = ParameterIn.HEADER, + name = "Authorization") @RequestHeader("Authorization") String authorization) { + return test + param + authorization; + } + @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = Date.class)) , description = "", headers = {@Header(name = "h1", schema = @Schema(implementation = String.class)), @@ -293,7 +302,7 @@ public String addString(@RequestParam(name = "s") List s) { } // Using 490, 590 error code, the response type should be CommonExceptionData. Or we need - // complex ExceptionConverters to deal with exceptions thrown by Hanlders, etc. + // complex ExceptionConverters to deal with exceptions thrown by Handlers, etc. @RequestMapping(path = "/fallback/returnnull/{name}", method = RequestMethod.GET) @ApiResponses(value = {@ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = String.class)), description = "xxx"), @@ -486,7 +495,7 @@ public Generic> testGenericGenericUser(@RequestBody Generic deleteAfterFinished(@RequestParam("content") String content) throws IOException { - File file = createTempFile(content); + public ResponseEntity deleteAfterFinished(@RequestParam("content") String content, + @RequestParam(value = "fileName", required = false) String fileName) throws IOException { + File file; + + if (StringUtils.isNotEmpty(fileName)) { + file = createTempFile(fileName, content); + } else { + file = createTempFile(content); + } return ResponseEntity .ok() @@ -93,18 +100,45 @@ public ResponseEntity notDeleteAfterFinished(@RequestParam("content") Stri } @GetMapping(path = "/setContentTypeByResponseEntity") - public ResponseEntity setContentTypeByResponseEntity(@RequestParam("content") String content, @RequestParam("contentType") String contentType) throws IOException { + public ResponseEntity setContentTypeByResponseEntity(@RequestParam("content") String content, + @RequestParam("contentType") String contentType) throws IOException { File file = createTempFile(content); return ResponseEntity - .ok() - .header(HttpHeaders.CONTENT_TYPE, contentType) - .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") - .body(new FilePart(null, file)); + .ok() + .header(HttpHeaders.CONTENT_TYPE, contentType) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") + .body(new FilePart(null, file)); } @GetMapping(path = "/assertLastFileDeleted") public boolean assertLastFileDeleted() { return lastFile.exists(); } + + @GetMapping(path = "/testResponseOKExceptionDownload") + public ResponseEntity testResponseOKExceptionDownload( + @RequestParam("exception") boolean exception, + @RequestParam("content") String content, + @RequestParam("contentType") String contentType) throws IOException { + if (exception) { + throw new ResponseOKException(); + } + + File file = createTempFile(content); + + return ResponseEntity + .ok() + .header(HttpHeaders.CONTENT_TYPE, contentType) + .header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=tempFileEntity.txt") + .body(new FilePart(null, file)); + } + + @GetMapping(path = "/testResponseOKExceptionBean") + public boolean testResponseOKExceptionBean(@RequestParam("exception") boolean exception) { + if (exception) { + throw new ResponseOKException(); + } + return true; + } } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java index 580bb793c02..ddf56fb5a3f 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ProducerTestsAfterBootup.java @@ -75,4 +75,3 @@ public void onBootEvent(BootEvent event) { } } } - diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKData.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKData.java new file mode 100644 index 00000000000..aeadbbeda90 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKData.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.springmvc.server; + +public class ResponseOKData { + private String errorCode; + + private String errorMessage; + + public ResponseOKData(String errorCode, String errorMessage) { + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKException.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKException.java new file mode 100644 index 00000000000..6dcafb2db99 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKException.java @@ -0,0 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.demo.springmvc.server; + +public class ResponseOKException extends RuntimeException { +} diff --git a/swagger/swagger-invocation/invocation-validator/src/main/java/org/apache/servicecomb/swagger/invocation/validator/ConstraintViolationExceptionToProducerResponseConverter.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKExceptionConverter.java similarity index 52% rename from swagger/swagger-invocation/invocation-validator/src/main/java/org/apache/servicecomb/swagger/invocation/validator/ConstraintViolationExceptionToProducerResponseConverter.java rename to demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKExceptionConverter.java index 552c97000ba..7358b72171c 100644 --- a/swagger/swagger-invocation/invocation-validator/src/main/java/org/apache/servicecomb/swagger/invocation/validator/ConstraintViolationExceptionToProducerResponseConverter.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/ResponseOKExceptionConverter.java @@ -14,30 +14,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.swagger.invocation.validator; +package org.apache.servicecomb.demo.springmvc.server; -import jakarta.validation.ConstraintViolationException; -import jakarta.ws.rs.core.Response.Status; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.exception.ExceptionConverter; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -public class ConstraintViolationExceptionToProducerResponseConverter - implements ExceptionToProducerResponseConverter { - @Override - public Class getExceptionClass() { - return ConstraintViolationException.class; - } +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; + +public class ResponseOKExceptionConverter implements + ExceptionConverter { @Override - public Response convert(SwaggerInvocation swaggerInvocation, ConstraintViolationException e) { - return Response.createFail(new InvocationException(Status.BAD_REQUEST, e.getConstraintViolations().toString())); + public boolean canConvert(Throwable throwable) { + return throwable instanceof ResponseOKException; } @Override - public int getOrder() { - return -100; + public InvocationException convert(Invocation invocation, ResponseOKException throwable, StatusType genericStatus) { + // This is for compatible usage. For best practise, any status code + // should have only one type of response. + return new InvocationException(Status.OK, new ResponseOKData("code-005", "error-005")); } } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCutomized.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCustomized.java similarity index 94% rename from demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCutomized.java rename to demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCustomized.java index 445c6a35b07..a3858541825 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCutomized.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/RestProducersCustomized.java @@ -24,5 +24,5 @@ @Component @Primary // This example shows user's how to customize RestProducers by @Primary annotation -public class RestProducersCutomized extends RestProducers { +public class RestProducersCustomized extends RestProducers { } diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/TransportSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/TransportSchema.java new file mode 100644 index 00000000000..158cd806188 --- /dev/null +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/TransportSchema.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.demo.springmvc.server; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "TransportSchema") +@RequestMapping(path = "/transport") +public class TransportSchema { + @GetMapping(path = "/restTransport") + @Transport(name = CoreConst.RESTFUL) + public boolean restTransport(InvocationContext invocation) { + return CoreConst.RESTFUL.equals(((Invocation) invocation).getTransportName()); + } + + @GetMapping(path = "/highwayTransport") + @Transport(name = CoreConst.HIGHWAY) + public boolean highwayTransport(InvocationContext invocation) { + return CoreConst.HIGHWAY.equals(((Invocation) invocation).getTransportName()); + } +} diff --git a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java index 494a4d663df..1a3a737d592 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java +++ b/demo/demo-springmvc/springmvc-server/src/main/java/org/apache/servicecomb/demo/springmvc/server/UploadSchema.java @@ -78,6 +78,21 @@ public String uploadFileRequestPartAttribute(@RequestPart(name = "file") Multipa } } + @PostMapping(path = "/uploadFileRequestPartAttributeList", produces = MediaType.TEXT_PLAIN_VALUE) + public String uploadFileRequestPartAttributeList(@RequestPart(name = "files") List files, + @RequestPart(name = "attributes") List attributes) throws IOException { + StringBuilder result = new StringBuilder(); + for (MultipartFile file : files) { + try (InputStream is = file.getInputStream()) { + result.append(IOUtils.toString(is, StandardCharsets.UTF_8)); + } + } + for (String attribute : attributes) { + result.append(attribute); + } + return result.toString(); + } + @PostMapping(path = "/uploadFileAndAttribute", produces = MediaType.TEXT_PLAIN_VALUE) public String uploadFileAndAttribute(@RequestPart(name = "file") MultipartFile file, @RequestAttribute(name = "attribute") String attribute) throws IOException { diff --git a/swagger/swagger-invocation/invocation-validator/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter b/demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter similarity index 88% rename from swagger/swagger-invocation/invocation-validator/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter rename to demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter index f6d6fd90616..f25ea28e1c1 100644 --- a/swagger/swagger-invocation/invocation-validator/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter +++ b/demo/demo-springmvc/springmvc-server/src/main/resources/META-INF/services/org.apache.servicecomb.core.exception.ExceptionConverter @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.invocation.validator.ConstraintViolationExceptionToProducerResponseConverter \ No newline at end of file +org.apache.servicecomb.demo.springmvc.server.ResponseOKExceptionConverter diff --git a/demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml b/demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml index 39302e15575..c5c10c5b41b 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml +++ b/demo/demo-springmvc/springmvc-server/src/main/resources/microservice.yaml @@ -15,8 +15,11 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -instance_description: +# test not supported configuration +# service_description.initialStatus +service_description: initialStatus: TESTING + servicecomb: service: application: springmvctest @@ -70,6 +73,11 @@ servicecomb: region: my-Region availableZone: my-Zone codec.printErrorMessage: true + + # For old testing + loadbalance: + isolation: + errorThresholdPercentage: 20 #########SSL options # open jdk 8 now TLSv1.3 not available # ssl.protocols: TLSv1.3 @@ -89,4 +97,3 @@ ssl.crl: revoke.crl ssl.sslCustomClass: org.apache.servicecomb.demo.DemoSSLCustom vertx.disableFileCPResolving: false # false: create the .vertx directory, true: do not create - diff --git a/demo/demo-springmvc/springmvc-server/src/main/resources/schemas/CodeFirstSpringmvcForSchema.yaml b/demo/demo-springmvc/springmvc-server/src/main/resources/schemas/CodeFirstSpringmvcForSchema.yaml index 59f89315261..2263730b56a 100644 --- a/demo/demo-springmvc/springmvc-server/src/main/resources/schemas/CodeFirstSpringmvcForSchema.yaml +++ b/demo/demo-springmvc/springmvc-server/src/main/resources/schemas/CodeFirstSpringmvcForSchema.yaml @@ -21,7 +21,7 @@ info: title: swagger definition for org.apache.servicecomb.demo.springmvc.server.CodeFirstSpringmvcForSchema version: 1.0.0 servers: -- url: /forScheam +- url: /forSchema paths: /uploadFile: post: @@ -53,4 +53,4 @@ paths: application/json: schema: type: boolean -components: {} \ No newline at end of file +components: {} diff --git a/demo/demo-zeroconfig-registry/README.md b/demo/demo-zeroconfig-registry/README.md index 44cd5539ce9..c2b53cf780c 100644 --- a/demo/demo-zeroconfig-registry/README.md +++ b/demo/demo-zeroconfig-registry/README.md @@ -1,11 +1,11 @@ # Introduction -This demo tests zeroconfig. +This demo tests zeroconfig. * server run in docker - + * client run in docker, and find server instances through zeroconfig, find schemas through @@ -14,8 +14,8 @@ This demo tests zeroconfig. * edge run in docker, and find server instances through zeroconfig, find schemas through schema discovery - + * tests - integration tests run in host machine, find client/edge instances through local, - and schemas from schema discovery. \ No newline at end of file + integration tests run in host machine, find client/edge instances through local, + and schemas from schema discovery. diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/pom.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/pom.xml index 442caca5204..385bb4950a5 100644 --- a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/pom.xml +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-zeroconfig-registry - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -62,28 +62,50 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/application.yml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/application.yml index 192d48653cc..af6c2946515 100644 --- a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/application.yml +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-client/src/main/resources/application.yml @@ -52,7 +52,7 @@ servicecomb: matches: - apiPath: exact: "/governance/retry" - demo-retry-no-prefex: | + demo-retry-no-prefix: | matches: - apiPath: exact: "/noPrefixRetry" @@ -82,7 +82,7 @@ servicecomb: maxAttempts: 3 ## services is optional in configuration file services: demo-zeroconfig-registry-client - demo-retry-no-prefex: | + demo-retry-no-prefix: | maxAttempts: 3 ## services is optional in configuration file services: demo-zeroconfig-registry-client diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/pom.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/pom.xml index b2eda319383..c8dcb06893a 100644 --- a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/pom.xml +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-zeroconfig-registry - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -67,30 +67,52 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 docker-maven-plugin - - org.commonjava.maven.plugins - directory-maven-plugin - - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - - \ No newline at end of file + diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java index 8b57fc65272..71f259366ae 100644 --- a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-edge/src/main/java/org/apache/servicecomb/demo/zeroconfig/edge/ServerEndpoint.java @@ -17,25 +17,21 @@ package org.apache.servicecomb.demo.zeroconfig.edge; -import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/edge/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/pom.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/pom.xml index 933a9d1ffbd..f69f1118a0a 100644 --- a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/pom.xml +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-zeroconfig-registry - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -63,6 +63,45 @@ docker + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + io.fabric8 @@ -72,21 +111,8 @@ org.commonjava.maven.plugins directory-maven-plugin - - com.github.odavid.maven.plugins - mixin-maven-plugin - - - - org.apache.servicecomb.demo - docker-build-config - ${project.version} - - - - - \ No newline at end of file + diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java index f13ef264583..b0dd4ef5d97 100644 --- a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-server/src/main/java/org/apache/servicecomb/demo/zeroconfig/server/ServerEndpoint.java @@ -17,25 +17,21 @@ package org.apache.servicecomb.demo.zeroconfig.server; -import jakarta.ws.rs.core.MediaType; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.swagger.invocation.context.ContextUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import jakarta.ws.rs.core.MediaType; + @RestSchema(schemaId = "ServerEndpoint") @RequestMapping(path = "/register/url/prefix", produces = MediaType.APPLICATION_JSON) public class ServerEndpoint { - private static final Logger LOGGER - = LoggerFactory.getLogger(ServerEndpoint.class); - @GetMapping(path = "/getName") public String getName(@RequestParam(name = "name") String name) { - ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info(LOGGER, "get name invoked."); + ((Invocation) ContextUtils.getInvocationContext()).getTraceIdLogger().info("get name invoked."); return name; } } diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/pom.xml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/pom.xml index 6d0f30c2708..8c57041ea1d 100644 --- a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/pom.xml +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/pom.xml @@ -25,7 +25,7 @@ org.apache.servicecomb.demo demo-zeroconfig-registry - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT @@ -45,7 +45,7 @@ demo-schema - + docker @@ -151,25 +151,8 @@ io.fabric8 docker-maven-plugin - - org.apache.maven.plugins - maven-failsafe-plugin - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - - - diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/ClientServerEndpoint.yaml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/ClientServerEndpoint.yaml index 80e14863b20..d8e19afb43c 100644 --- a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/ClientServerEndpoint.yaml +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/microservices/demo-zeroconfig-registry-edge/ClientServerEndpoint.yaml @@ -153,4 +153,3 @@ components: type: string format: date-time x-java-class: org.apache.servicecomb.demo.zeroconfig.client.ClientModel - diff --git a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/registry.yaml b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/registry.yaml index 932de0d895e..07360eabfde 100644 --- a/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/registry.yaml +++ b/demo/demo-zeroconfig-registry/demo-zeroconfig-registry-tests/src/main/resources/registry.yaml @@ -35,4 +35,4 @@ demo-zeroconfig-registry-edge: - SchemaDiscoveryEndpoint instances: - endpoints: - - rest://localhost:8888 \ No newline at end of file + - rest://localhost:8888 diff --git a/demo/demo-zeroconfig-registry/pom.xml b/demo/demo-zeroconfig-registry/pom.xml index 585538a3c0d..bc02525e483 100644 --- a/demo/demo-zeroconfig-registry/pom.xml +++ b/demo/demo-zeroconfig-registry/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb.demo demo-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT demo-zeroconfig-registry Java Chassis::Demo::ZeroConfig Registry diff --git a/demo/demo-zookeeper/README.md b/demo/demo-zookeeper/README.md new file mode 100644 index 00000000000..48a74913f83 --- /dev/null +++ b/demo/demo-zookeeper/README.md @@ -0,0 +1,5 @@ +# Notice + +This integration tests is designed for Zookeeper registry and configuration. And extra test cases include: + +* Test cases related to SpringMVC annotations that demo-springmvc can not cover. diff --git a/demo/demo-zookeeper/consumer/pom.xml b/demo/demo-zookeeper/consumer/pom.xml new file mode 100644 index 00000000000..73fb82a25a3 --- /dev/null +++ b/demo/demo-zookeeper/consumer/pom.xml @@ -0,0 +1,104 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-zookeeper + 3.4.0-SNAPSHOT + + + zookeeper-consumer + Java Chassis::Demo::Zookeeper::CONSUMER + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-zookeeper + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java new file mode 100644 index 00000000000..76fb2fb17db --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ClientWebsocketController.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; +import io.vertx.core.http.WebSocket; + +@RestSchema(schemaId = "ClientWebsocketController") +@RequestMapping(path = "/ws") +public class ClientWebsocketController { + interface ProviderService { + WebSocket websocket(); + } + + @RpcReference(schemaId = "WebsocketController", microserviceName = "provider") + private ProviderService providerService; + + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + WebSocket providerWebSocket = providerService.websocket(); + providerWebSocket.closeHandler(v -> serverWebsocket.close()); + providerWebSocket.textMessageHandler(m -> { + System.out.println("send message " + m); + serverWebsocket.writeTextMessage(m); + }); + serverWebsocket.textMessageHandler(m -> { + System.out.println("receive message " + m); + providerWebSocket.writeTextMessage(m); + }); + } +} diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java new file mode 100644 index 00000000000..3e7a2a0e285 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ConsumerApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ConsumerApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java new file mode 100644 index 00000000000..e5dd86d9d64 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerController.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@RestSchema(schemaId = "ConsumerController") +@RequestMapping(path = "/") +public class ConsumerController { + @RpcReference(schemaId = "ProviderController", microserviceName = "provider") + private ProviderService providerService; + + // consumer service which delegate the implementation to provider service. + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return providerService.sayHello(name); + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return providerService.getConfig(key); + } +} diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java new file mode 100644 index 00000000000..f0e894d6745 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerHeaderParamWithListSchema.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "ConsumerHeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class ConsumerHeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @RpcReference(microserviceName = "provider", schemaId = "HeaderParamWithListSchema") + private IHeaderParamWithListSchemaSpringMvc provider; + + @Override + public String headerListDefault(List headerList) { + return provider.headerListDefault(headerList); + } + + @Override + public String headerListCSV(List headerList) { + return provider.headerListCSV(headerList); + } + + @Override + public String headerListMULTI(List headerList) { + return provider.headerListMULTI(headerList); + } + + @Override + public String headerListSSV(List headerList) { + return provider.headerListSSV(headerList); + } + + @Override + public String headerListPIPES(List headerList) { + return provider.headerListPIPES(headerList); + } +} diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java new file mode 100644 index 00000000000..aa169801cf7 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ConsumerReactiveStreamController.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.pojo.RpcReference; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +public class ConsumerReactiveStreamController { + interface ProviderReactiveStreamController { + Publisher sseString(); + + Publisher sseModel(); + } + + @RpcReference(microserviceName = "provider", schemaId = "ReactiveStreamController") + ProviderReactiveStreamController controller; + + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseString() { + return controller.sseString(); + } + + @GetMapping("/sseModel") + @Transport(name = CoreConst.RESTFUL) + public Publisher sseModel() { + return controller.sseModel(); + } +} diff --git a/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java new file mode 100644 index 00000000000..fe71314c9f4 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/java/org/apache/servicecomb/samples/ProviderService.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +public interface ProviderService { + String sayHello(String name); + + String getConfig(String key); +} diff --git a/demo/demo-zookeeper/consumer/src/main/resources/application.yml b/demo/demo-zookeeper/consumer/src/main/resources/application.yml new file mode 100644 index 00000000000..100797f83b4 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/resources/application.yml @@ -0,0 +1,36 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-zookeeper + version: 0.0.1 + name: consumer + properties: + group: red + registry: + zk: + enabled: true + connectString: 127.0.0.1:2181 + + rest: + address: 0.0.0.0:9092?websocketEnabled=true + server: + websocket-prefix: /ws + + highway: + address: 0.0.0.0:7092 diff --git a/demo/demo-zookeeper/consumer/src/main/resources/log4j2.xml b/demo/demo-zookeeper/consumer/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-zookeeper/consumer/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-zookeeper/gateway/pom.xml b/demo/demo-zookeeper/gateway/pom.xml new file mode 100644 index 00000000000..f2676b1ed6d --- /dev/null +++ b/demo/demo-zookeeper/gateway/pom.xml @@ -0,0 +1,107 @@ + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-zookeeper + 3.4.0-SNAPSHOT + + + zookeeper-gateway + Java Chassis::Demo::Zookeeper::GATEWAY + jar + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + edge-core + + + org.apache.servicecomb + registry-zookeeper + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zookeeper/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java b/demo/demo-zookeeper/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java new file mode 100644 index 00000000000..7d58caafd9d --- /dev/null +++ b/demo/demo-zookeeper/gateway/src/main/java/org/apache/servicecomb/samples/GatewayApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class GatewayApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(GatewayApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-zookeeper/gateway/src/main/resources/application.yml b/demo/demo-zookeeper/gateway/src/main/resources/application.yml new file mode 100644 index 00000000000..ea13ef73d91 --- /dev/null +++ b/demo/demo-zookeeper/gateway/src/main/resources/application.yml @@ -0,0 +1,53 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-zookeeper + version: 0.0.1 + name: gateway + registry: + zk: + enabled: true + connectString: 127.0.0.1:2181 + + rest: + address: 0.0.0.0:9090?websocketEnabled=true + server: + websocket-prefix: /ws + + http: + dispatcher: + edge: + default: + enabled: false + url: + enabled: true + pattern: /(.*) + mappings: + consumer: + prefixSegmentCount: 0 + path: "/.*" + microserviceName: consumer + versionRule: 0.0.0+ + websocket: + mappings: + consumer: + prefixSegmentCount: 0 + path: "/ws/.*" + microserviceName: consumer + versionRule: 0.0.0+ diff --git a/demo/demo-zookeeper/gateway/src/main/resources/log4j2.xml b/demo/demo-zookeeper/gateway/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-zookeeper/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-zookeeper/pom.xml b/demo/demo-zookeeper/pom.xml new file mode 100644 index 00000000000..aa1030de1ec --- /dev/null +++ b/demo/demo-zookeeper/pom.xml @@ -0,0 +1,61 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-parent + 3.4.0-SNAPSHOT + + demo-zookeeper + Java Chassis::Demo::Zookeeper + pom + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + solution-basic + + + org.apache.logging.log4j + log4j-slf4j-impl + + + org.apache.logging.log4j + log4j-core + + + org.apache.logging.log4j + log4j-api + + + + + provider + consumer + gateway + test-client + + + diff --git a/demo/demo-zookeeper/provider/pom.xml b/demo/demo-zookeeper/provider/pom.xml new file mode 100644 index 00000000000..3b008bfb000 --- /dev/null +++ b/demo/demo-zookeeper/provider/pom.xml @@ -0,0 +1,116 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-zookeeper + 3.4.0-SNAPSHOT + + + zookeeper-provider + Java Chassis::Demo::Zookeeper::PROVIDER + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb + registry-zookeeper + + + org.apache.servicecomb + config-zookeeper + + + io.reactivex.rxjava3 + rxjava + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + ${project.artifactId}:${project.version} + ${project.artifactId} + + eclipse-temurin:17-jre-jammy + + 7070 + 8080 + + + tar + ${root.basedir}/assembly/assembly.xml + + + java -Xmx128m $JAVA_OPTS -jar $JAR_PATH + + + + + + + + build + package + + build + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java new file mode 100644 index 00000000000..8a773f91c72 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchema.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.demo.api.IHeaderParamWithListSchemaSpringMvc; +import org.apache.servicecomb.provider.rest.common.RestSchema; + +@RestSchema(schemaId = "HeaderParamWithListSchema", schemaInterface = IHeaderParamWithListSchemaSpringMvc.class) +public class HeaderParamWithListSchema implements IHeaderParamWithListSchemaSpringMvc { + @Override + public String headerListDefault(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListCSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListMULTI(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListSSV(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } + + @Override + public String headerListPIPES(List headerList) { + return headerList == null ? "null" : headerList.size() + ":" + headerList; + } +} diff --git a/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java new file mode 100644 index 00000000000..fde6f36b2e3 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderApplication.java @@ -0,0 +1,33 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class ProviderApplication { + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(ProviderApplication.class).run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java new file mode 100644 index 00000000000..a238d77be24 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ProviderController.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.zookeeper.ZookeeperClient; +import org.apache.servicecomb.config.zookeeper.ZookeeperConfig; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.beans.factory.InitializingBean; +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.RequestParam; + +@RestSchema(schemaId = "ProviderController") +@RequestMapping(path = "/") +public class ProviderController implements InitializingBean { + private Environment environment; + + private ZookeeperConfig zookeeperConfig; + + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + this.zookeeperConfig = new ZookeeperConfig(environment); + } + + // a very simple service to echo the request parameter + @GetMapping("/sayHello") + public String sayHello(@RequestParam("name") String name) { + return "Hello " + name; + } + + @GetMapping("/getConfig") + public String getConfig(@RequestParam("key") String key) { + return environment.getProperty(key); + } + + @Override + public void afterPropertiesSet() throws Exception { + CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConfig.getConnectString(), + zookeeperConfig.getSessionTimeoutMillis(), zookeeperConfig.getConnectionTimeoutMillis(), + new ExponentialBackoffRetry(1000, 3)); + client.start(); + client.blockUntilConnected(10, TimeUnit.SECONDS); + + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ZookeeperConfig.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + + String path = String.format(ZookeeperClient.PATH_ENVIRONMENT, env); + if (client.checkExists().forPath(path + "/config.properties") != null) { + client.delete().forPath(path + "/config.properties"); + } + client.create().creatingParentsIfNeeded(). + forPath(path + "/config.properties", "key1=1\nkey2=2".getBytes(StandardCharsets.UTF_8)); + + path = String.format(ZookeeperClient.PATH_VERSION, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment)); + if (client.checkExists().forPath(path + "/config.properties") != null) { + client.delete().forPath(path + "/config.properties"); + } + client.create().creatingParentsIfNeeded(). + forPath(path + "/config.properties", "key2=3\nkey3=4".getBytes(StandardCharsets.UTF_8)); + + path = String.format(ZookeeperClient.PATH_TAG, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment), + zookeeperConfig.getInstanceTag()); + if (client.checkExists().forPath(path + "/config.properties") != null) { + client.delete().forPath(path + "/config.properties"); + } + client.create().creatingParentsIfNeeded(). + forPath(path + "/config.properties", "key2=3\nkey3=5".getBytes(StandardCharsets.UTF_8)); + + client.close(); + } +} diff --git a/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java new file mode 100644 index 00000000000..8108d15fd46 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/ReactiveStreamController.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.samples; + + +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.reactivestreams.Publisher; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.reactivex.rxjava3.core.Flowable; + +@RestSchema(schemaId = "ReactiveStreamController") +@RequestMapping(path = "/") +@Transport(name = CoreConst.RESTFUL) +public class ReactiveStreamController { + public static class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + public Publisher sseString() { + return Flowable.fromArray("a", "b", "c"); + } + + @GetMapping("/sseModel") + public Publisher sseModel() { + return Flowable.intervalRange(0, 5, 0, 1, TimeUnit.SECONDS) + .map(item -> new Model("jack", item.intValue())); + } +} diff --git a/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java new file mode 100644 index 00000000000..6381ef1d54c --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/java/org/apache/servicecomb/samples/WebsocketController.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.provider.rest.common.RestSchema; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.ServerWebSocket; + +@RestSchema(schemaId = "WebsocketController") +@RequestMapping(path = "/ws") +public class WebsocketController { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + public void websocket(ServerWebSocket serverWebsocket) { + AtomicInteger receiveCount = new AtomicInteger(0); + CountDownLatch startSend = new CountDownLatch(1); + serverWebsocket.textMessageHandler(s -> { + if ("start".equals(s)) { + startSend.countDown(); + serverWebsocket.writeTextMessage("started"); + return; + } + receiveCount.getAndIncrement(); + }); + serverWebsocket.closeHandler((v) -> System.out.println("closed")); + + new Thread(() -> { + try { + startSend.await(30, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + serverWebsocket.writeTextMessage("hello"); + + for (int i = 0; i < 5; i++) { + serverWebsocket.writeTextMessage("hello " + i); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + serverWebsocket.writeTextMessage("total " + receiveCount.get()); + serverWebsocket.close(); + }).start(); + } +} diff --git a/demo/demo-zookeeper/provider/src/main/resources/application.yml b/demo/demo-zookeeper/provider/src/main/resources/application.yml new file mode 100644 index 00000000000..dd777236bd8 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/resources/application.yml @@ -0,0 +1,47 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- +# spring boot configurations +servicecomb: + service: + application: demo-zookeeper + version: 0.0.1 + name: provider + properties: + group: green + registry: + zk: + connectString: 127.0.0.1:2181 + config: + zk: + connectString: 127.0.0.1:2181 + instance-tag: config-demo + + rest: + address: 0.0.0.0:9094?websocketEnabled=true + server: + websocket-prefix: /ws + + highway: + address: 0.0.0.0:7094 + + cors: + enabled: true + origin: "*" + allowCredentials: false + allowedMethod: "*" + maxAge: 3600 diff --git a/demo/demo-zookeeper/provider/src/main/resources/log4j2.xml b/demo/demo-zookeeper/provider/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-zookeeper/provider/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-zookeeper/test-client/pom.xml b/demo/demo-zookeeper/test-client/pom.xml new file mode 100644 index 00000000000..e527b630005 --- /dev/null +++ b/demo/demo-zookeeper/test-client/pom.xml @@ -0,0 +1,196 @@ + + + + + 4.0.0 + + + org.apache.servicecomb.demo + demo-zookeeper + 3.4.0-SNAPSHOT + + + zookeeper-test-client + Java Chassis::Demo::Zookeeper::TEST-CLIENT + jar + + + + + + + org.apache.servicecomb + java-chassis-spring-boot-starter-standalone + + + org.apache.servicecomb.demo + demo-schema + + + org.apache.servicecomb + registry-local + + + + + + docker + + + + + io.fabric8 + docker-maven-plugin + + + + zookeeper:3.8.3 + zookeeper + + alias + + binding to port + + + 2181 + + + + + + zookeeper.port:2181 + + + + + zookeeper-provider:${project.version} + zookeeper-provider + + alias + + + -Dservicecomb.registry.zk.connectString=zookeeper:2181 -Dservicecomb.config.zk.connectString=zookeeper:2181 + + /maven/maven/zookeeper-provider-${project.version}.jar + + + zookeeper:zookeeper + + + ServiceComb is ready + + + 9094 + + + + + + 9094:9094 + + + + + zookeeper-consumer:${project.version} + zookeeper-consumer + + alias + + + -Dservicecomb.registry.zk.connectString=zookeeper:2181 + + /maven/maven/zookeeper-consumer-${project.version}.jar + + + zookeeper:zookeeper + + + ServiceComb is ready + + + 9092 + + + + + + 9092:9092 + + + + + zookeeper-gateway:${project.version} + zookeeper-gateway + + alias + + + -Dservicecomb.registry.zk.connectString=zookeeper:2181 + + /maven/maven/zookeeper-gateway-${project.version}.jar + + + zookeeper:zookeeper + + + ServiceComb is ready + + + 9090 + + + + + + 9090:9090 + + + + + + + + start + pre-integration-test + + start + + + + stop + post-integration-test + + stop + + + + + + + + + + io.fabric8 + docker-maven-plugin + + + + + + diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/Config.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/Config.java new file mode 100644 index 00000000000..2e9105cdd4a --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/Config.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +public interface Config { + String GATEWAY_URL = "http://localhost:9090"; +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java new file mode 100644 index 00000000000..1b129acc2c6 --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HeaderParamWithListSchemaIT.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HeaderParamWithListSchemaIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHeaderListDefault(); + testHeaderListMulti(); + testHeaderListCSV(); + testHeaderListSSV(); + testHeaderListPipes(); + } + + // default to multi + private void testHeaderListDefault() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListDefault", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListPipes() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a|b|c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListPIPES", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListSSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a b c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListSSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListCSV() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a,b,c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + + headers.add("headerList", "a, b, c"); + entity = new HttpEntity<>(headers); + result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListCSV", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } + + private void testHeaderListMulti() { + MultiValueMap headers = new HttpHeaders(); + headers.add("headerList", "a"); + headers.add("headerList", "b"); + headers.add("headerList", "c"); + HttpEntity entity = new HttpEntity<>(headers); + String result = template + .exchange(Config.GATEWAY_URL + "/headerList/headerListMULTI", HttpMethod.GET, entity, String.class).getBody(); + TestMgr.check("3:[a, b, c]", result); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java new file mode 100644 index 00000000000..97e883fb45f --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/HelloWorldIT.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestOperations; +import org.springframework.web.client.RestTemplate; + +@Component +public class HelloWorldIT implements CategorizedTestCase { + RestOperations template = new RestTemplate(); + + @Override + public void testRestTransport() throws Exception { + testHelloWorld(); + testGetConfig(); + } + + private void testGetConfig() { + String result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key1", String.class); + TestMgr.check("1", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key2", String.class); + TestMgr.check("3", result); + result = template + .getForObject(Config.GATEWAY_URL + "/getConfig?key=key3", String.class); + TestMgr.check("5", result); + } + + private void testHelloWorld() { + String result = template + .getForObject(Config.GATEWAY_URL + "/sayHello?name=World", String.class); + TestMgr.check("Hello World", result); + + // test trace id added + MultiValueMap headers = new HttpHeaders(); + headers.add("X-B3-TraceId", "81de2eb7691c2bbb"); + HttpEntity entity = new HttpEntity(headers); + ResponseEntity response = + template.exchange(Config.GATEWAY_URL + "/sayHello?name=World", HttpMethod.GET, entity, String.class); + TestMgr.check(1, response.getHeaders().get("X-B3-TraceId").size()); + TestMgr.check("81de2eb7691c2bbb", response.getHeaders().getFirst("X-B3-TraceId")); + TestMgr.check("Hello World", response.getBody()); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java new file mode 100644 index 00000000000..488a6cf712a --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ReactiveStreamIT.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.ReactiveStreamClient.Model; +import org.reactivestreams.Publisher; +import org.reactivestreams.Subscriber; +import org.reactivestreams.Subscription; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +public class ReactiveStreamIT implements CategorizedTestCase { + @Autowired + @Qualifier("reactiveStreamProvider") + ReactiveStreamClient reactiveStreamProvider; + + @Autowired + @Qualifier("reactiveStreamGateway") + ReactiveStreamClient reactiveStreamGateway; + + @Override + public void testRestTransport() throws Exception { + testSseString(reactiveStreamProvider); + testSseModel(reactiveStreamProvider); + testSseString(reactiveStreamGateway); + testSseModel(reactiveStreamGateway); + } + + private void testSseModel(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseModel(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(Model s) { + buffer.append(s.getName()).append(s.getAge()); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("jack0jack1jack2jack3jack4", buffer.toString()); + } + + private void testSseString(ReactiveStreamClient client) throws Exception { + Publisher result = client.sseString(); + StringBuilder buffer = new StringBuilder(); + CountDownLatch countDownLatch = new CountDownLatch(1); + result.subscribe(new Subscriber<>() { + Subscription subscription; + + @Override + public void onSubscribe(Subscription s) { + subscription = s; + subscription.request(1); + } + + @Override + public void onNext(String s) { + buffer.append(s); + subscription.request(1); + } + + @Override + public void onError(Throwable t) { + subscription.cancel(); + countDownLatch.countDown(); + } + + @Override + public void onComplete() { + countDownLatch.countDown(); + } + }); + countDownLatch.await(10, TimeUnit.SECONDS); + TestMgr.check("abc", buffer.toString()); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java new file mode 100644 index 00000000000..26a2a491bbe --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/TestClientApplication.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.CategorizedTestCaseRunner; +import org.apache.servicecomb.demo.TestMgr; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; + +@SpringBootApplication +public class TestClientApplication { + private static final Logger LOGGER = LoggerFactory.getLogger(TestClientApplication.class); + + public static void main(String[] args) throws Exception { + try { + new SpringApplicationBuilder().web(WebApplicationType.NONE).sources(TestClientApplication.class).run(args); + + run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + } + + public static void run() throws Exception { + CategorizedTestCaseRunner.runCategorizedTestCase("consumer"); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java new file mode 100644 index 00000000000..5f23c66cdac --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/ThirdSvcConfiguration.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.List; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.annotation.Transport; +import org.apache.servicecomb.localregistry.RegistryBean; +import org.apache.servicecomb.localregistry.RegistryBean.Instance; +import org.apache.servicecomb.localregistry.RegistryBean.Instances; +import org.apache.servicecomb.provider.pojo.Invoker; +import org.reactivestreams.Publisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import io.vertx.core.http.WebSocket; + +@Configuration +public class ThirdSvcConfiguration { + @RequestMapping(path = "/ws") + public interface WebsocketClient { + @PostMapping("/websocket") + @Transport(name = CoreConst.WEBSOCKET) + WebSocket websocket(); + } + + @RequestMapping(path = "/") + public interface ReactiveStreamClient { + class Model { + private String name; + + private int age; + + public Model() { + + } + + public Model(String name, int age) { + this.name = name; + this.age = age; + } + + public int getAge() { + return age; + } + + public Model setAge(int age) { + this.age = age; + return this; + } + + public String getName() { + return name; + } + + public Model setName(String name) { + this.name = name; + return this; + } + } + + @GetMapping("/sseString") + Publisher sseString(); + + @GetMapping("/sseModel") + Publisher sseModel(); + } + + @Bean + public RegistryBean providerServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .setAppId("demo-zookeeper") + .setServiceName("provider") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9094"))))); + } + + @Bean + public RegistryBean gatewayServiceBean() { + return new RegistryBean() + .addSchemaInterface("ReactiveStreamController", ReactiveStreamClient.class) + .addSchemaInterface("WebsocketController", WebsocketClient.class) + .setAppId("demo-zookeeper") + .setServiceName("gateway") + .setVersion("0.0.1") + .setInstances(new Instances().setInstances(List.of( + new Instance().setEndpoints(List.of("rest://localhost:9090?websocketEnabled=true"))))); + } + + @Bean("reactiveStreamProvider") + public ReactiveStreamClient reactiveStreamProvider() { + return Invoker.createProxy("provider", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean("reactiveStreamGateway") + public ReactiveStreamClient reactiveStreamGateway() { + return Invoker.createProxy("gateway", "ReactiveStreamController", ReactiveStreamClient.class); + } + + @Bean + public WebsocketClient gatewayWebsocketClient() { + return Invoker.createProxy("gateway", "WebsocketController", WebsocketClient.class); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java new file mode 100644 index 00000000000..c36f2d16d28 --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/java/org/apache/servicecomb/samples/WebsocketIT.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.apache.servicecomb.demo.CategorizedTestCase; +import org.apache.servicecomb.demo.TestMgr; +import org.apache.servicecomb.samples.ThirdSvcConfiguration.WebsocketClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import io.vertx.core.http.WebSocket; + +@Component +public class WebsocketIT implements CategorizedTestCase { + @Autowired + private WebsocketClient websocketClient; + + @Override + public void testRestTransport() throws Exception { + StringBuffer sb = new StringBuffer(); + AtomicBoolean closed = new AtomicBoolean(false); + CountDownLatch latchStarted = new CountDownLatch(1); + CountDownLatch latch = new CountDownLatch(1); + + WebSocket webSocket = websocketClient.websocket(); + webSocket.textMessageHandler(s -> { + if ("started".equals(s)) { + latchStarted.countDown(); + return; + } + sb.append(s); + sb.append(" "); + webSocket.writeTextMessage(s); + }); + webSocket.closeHandler(v -> { + closed.set(true); + latch.countDown(); + }); + + webSocket.writeTextMessage("start"); + int i = 0; + for (; i < 10; i++) { + if (!latchStarted.await(3, TimeUnit.SECONDS)) { + webSocket.writeTextMessage("start"); + continue; + } + break; + } + TestMgr.check(i < 10, true); + latch.await(30, TimeUnit.SECONDS); + TestMgr.check(sb.toString(), "hello hello 0 hello 1 hello 2 hello 3 hello 4 total 6 "); + TestMgr.check(closed.get(), true); + } +} diff --git a/demo/demo-zookeeper/test-client/src/main/resources/application.yml b/demo/demo-zookeeper/test-client/src/main/resources/application.yml new file mode 100644 index 00000000000..6f8a74f5c9d --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/resources/application.yml @@ -0,0 +1,25 @@ +# +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- +servicecomb: + service: + application: demo-zookeeper + name: test-client + version: 0.0.1 + + rest: + address: 0.0.0.0:9097 # should be same with server.port to use web container diff --git a/demo/demo-zookeeper/test-client/src/main/resources/log4j2.xml b/demo/demo-zookeeper/test-client/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..c51f7ad5036 --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/main/resources/log4j2.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + diff --git a/demo/demo-zookeeper/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java b/demo/demo-zookeeper/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java new file mode 100644 index 00000000000..549cfe2a300 --- /dev/null +++ b/demo/demo-zookeeper/test-client/src/test/java/org/apache/servicecomb/samples/ZookeeperIT.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.samples; + +import org.apache.servicecomb.demo.TestMgr; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = TestClientApplication.class) +public class ZookeeperIT { + private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperIT.class); + + @BeforeEach + public void setUp() { + TestMgr.errors().clear(); + } + + @Test + public void clientGetsNoError() throws Exception { + try { + TestClientApplication.run(); + } catch (Exception e) { + TestMgr.failed("test case run failed", e); + LOGGER.error("-------------- test failed -------------"); + LOGGER.error("", e); + LOGGER.error("-------------- test failed -------------"); + } + TestMgr.summary(); + Assertions.assertTrue(TestMgr.errors().isEmpty()); + } +} diff --git a/demo/docker-build-config/pom.xml b/demo/docker-build-config/pom.xml deleted file mode 100644 index fd55e817cb7..00000000000 --- a/demo/docker-build-config/pom.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - demo-parent - org.apache.servicecomb.demo - 3.0.0-SNAPSHOT - - 4.0.0 - - docker-build-config - Java Chassis::Demo::Docker Build Config - pom - - - - - - io.fabric8 - docker-maven-plugin - - - - ${project.artifactId}:${project.version} - ${project.artifactId} - - openjdk:17-alpine - - 7070 - 8080 - - - tar - ${root.basedir}/assembly/assembly.xml - - - java -Xmx128m $JAVA_OPTS -jar $JAR_PATH - - - - - - - - build - package - - build - - - - - - org.commonjava.maven.plugins - directory-maven-plugin - - - directories - - directory-of - - initialize - - root.basedir - - org.apache.servicecomb.demo - demo-parent - - - - - - - - - diff --git a/demo/docker-run-config-edge/pom.xml b/demo/docker-run-config-edge/pom.xml deleted file mode 100644 index 58314f9d7fb..00000000000 --- a/demo/docker-run-config-edge/pom.xml +++ /dev/null @@ -1,271 +0,0 @@ - - - - - demo-parent - org.apache.servicecomb.demo - 3.0.0-SNAPSHOT - - 4.0.0 - - docker-run-config-edge - Java Chassis::Demo::Docker Run Config - - pom - - - - - - io.fabric8 - docker-maven-plugin - - - - servicecomb/service-center - service-center - - - server is ready - - - 30100 - - - - - - 30100:30100 - - - - - authentication:${project.version} - authentication - - - - -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 - - /maven/maven/authentication-${project.version}.jar - - - service-center:sc.servicecomb.io - - - ServiceComb is ready - - - 7070 - - - - - - 7070:7070 - - - service-center - - - - - - - business-2-0-0:${project.version} - business-2-0-0 - - - - -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 - - /maven/maven/business-2-0-0-${project.version}.jar - - - service-center:sc.servicecomb.io - - - Register microservice instance success - - - 8091 - - - - - - 8091:8091 - - - service-center - - - - - edge-service:${project.version} - edge-service - - - - -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 - - /maven/maven/edge-service-${project.version}.jar - - - service-center:sc.servicecomb.io - - - Register microservice instance success - - - - 18090 - - - - - - 18090:18090 - - - service-center - - - - - - - - start - pre-integration-test - - start - - - - stop - post-integration-test - - stop - - - - - - - - - - - docker - - - - org.apache.maven.plugins - maven-failsafe-plugin - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - - - - - - docker-machine - - - - - - - diff --git a/demo/docker-run-config-local/pom.xml b/demo/docker-run-config-local/pom.xml deleted file mode 100644 index f18cdeb64ad..00000000000 --- a/demo/docker-run-config-local/pom.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - - demo-parent - org.apache.servicecomb.demo - 3.0.0-SNAPSHOT - - 4.0.0 - - docker-run-config-local - Java Chassis::Demo::Docker Run Config Local - - pom - - - - - - io.fabric8 - docker-maven-plugin - - - - ${demo.service.name}:${project.version} - ${demo.service.name} - - - /maven/maven/${demo.service.name}-${project.version}.jar - - - ServiceComb is ready - - - 8080 - - - - - - 8080:8080 - - - - - - - - start - pre-integration-test - - start - - - - stop - post-integration-test - - stop - - - - - - - - - - - docker - - - - org.apache.maven.plugins - maven-failsafe-plugin - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - - - - - - docker-machine - - - - - - - diff --git a/demo/docker-run-config/pom.xml b/demo/docker-run-config/pom.xml deleted file mode 100644 index 1aa274d0afe..00000000000 --- a/demo/docker-run-config/pom.xml +++ /dev/null @@ -1,144 +0,0 @@ - - - - - demo-parent - org.apache.servicecomb.demo - 3.0.0-SNAPSHOT - - 4.0.0 - - docker-run-config - Java Chassis::Demo::Docker Run Config - - pom - - - - - - io.fabric8 - docker-maven-plugin - - - - servicecomb/service-center - service-center - - - server is ready - - - 30100 - - - - - - 30100:30100 - - - - - ${demo.service.name}:${project.version} - ${demo.service.name} - - - - -Dservicecomb.registry.sc.address=http://sc.servicecomb.io:30100 - - /maven/maven/${demo.service.name}-${project.version}.jar - - - service-center:sc.servicecomb.io - - - ServiceComb is ready - - - 8080 - - - - - - 7070:7070 - 8080:8080 - - - service-center - - - - - - - - start - pre-integration-test - - start - - - - stop - post-integration-test - - stop - - - - - - - - - - - docker - - - - org.apache.maven.plugins - maven-failsafe-plugin - - ${jacoco.failsafe.argLine} - - - - - integration-test - verify - - - - - - - - - docker-machine - - - - - - - diff --git a/demo/pom.xml b/demo/pom.xml index 1a2c71fbe66..d0376883c6b 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default org.apache.servicecomb.demo @@ -35,10 +35,6 @@ pom - docker-build-config - docker-run-config - docker-run-config-edge - docker-run-config-local demo-schema demo-pojo demo-filter @@ -56,6 +52,10 @@ demo-cse-v1 demo-cse-v2 demo-nacos + + + demo-zookeeper + demo-consul @@ -139,12 +139,6 @@ - - com.github.odavid.maven.plugins - mixin-maven-plugin - 0.1-alpha-40 - true - org.commonjava.maven.plugins directory-maven-plugin @@ -181,7 +175,59 @@ + + org.commonjava.maven.plugins + directory-maven-plugin + + + directories + + directory-of + + initialize + + root.basedir + + org.apache.servicecomb.demo + demo-parent + + + + + + + + org.commonjava.maven.plugins + directory-maven-plugin + + + + + + docker + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + ${jacoco.failsafe.argLine} + + + + + integration-test + verify + + + + + + + + diff --git a/dependencies/bom/pom.xml b/dependencies/bom/pom.xml index 244b9f89865..ed5e1bc91a2 100644 --- a/dependencies/bom/pom.xml +++ b/dependencies/bom/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb java-chassis-dependencies-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT java-chassis-bom @@ -111,6 +111,16 @@ config-nacos ${project.version} + + org.apache.servicecomb + config-zookeeper + ${project.version} + + + org.apache.servicecomb + config-consul + ${project.version} + @@ -268,6 +278,26 @@ registry-nacos ${project.version} + + org.apache.servicecomb + registry-zookeeper + ${project.version} + + + org.apache.servicecomb + registry-etcd + ${project.version} + + + org.apache.servicecomb + config-etcd + ${project.version} + + + org.apache.servicecomb + registry-consul + ${project.version} + org.apache.servicecomb @@ -381,4 +411,4 @@ - \ No newline at end of file + diff --git a/dependencies/default/pom.xml b/dependencies/default/pom.xml index 8d0fb65ccb1..22f2190ddd1 100644 --- a/dependencies/default/pom.xml +++ b/dependencies/default/pom.xml @@ -17,12 +17,12 @@ --> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.apache.servicecomb java-chassis-dependencies-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT java-chassis-dependencies @@ -32,71 +32,64 @@ 1.2.2 - 2.5.3 - 1.9.19 - 3.24.2 - 4.2.0 - 5.16.0 - 1.9.4 - 2.11.0 + 3.27.7 + 4.3.0 + 6.3.0 + 1.11.0 + 2.21.0 2.6 - 3.12.0 - 1.2 - 1.10.0 - 1.5.1 - 4.0.3 - 1.0.1 + 3.20.0 + 1.3.5 + 1.15.0 + 5.9.0 + 1.0.3 3.0.2 1.17.12 1.17.13 - 32.1.3-jre + 33.5.0-jre 5.1.0 1.3 - 2.1.12 - 8.0.0.Final + 2.2.2 + 9.1.0.Final 4.5.14 1.5.18 - 2.15.3 - 1.5.4 + 2.20.1 + 1.6.0 1.3.2 1 1.5 - 2.3.1 - 3.1.2 + 4.0.2 1.34 4.13.2 - 5.10.0 + 5.13.4 1.2.17 - 2.20.0 - 1.2.11 - 3.9.1 - 1.11.4 + 2.25.3 + 3.9.12 + 1.16.4 + 5.21.0 + 5.2.0 5.15.0 - 2.2.3 + 3.1.1 0.3.0 - 4.1.97.Final - 4.12.0 0.16.0 3.23.4 1.8.0 2.2.27 + 1.0.4 1.7.0 2.7.18 - 1.3.8 - 0.5.1 + 3.1.12 1.0.0 0.13.2 6.0.0 1.7.36 - 1.33 - 1.6.11 - 6.0.9 - 3.0.7 - 2.2.15 - 1.3.3 - 4.4.4 - 2.24.3 - 2.16.4 + 2.5 + 2.2.41 + 5.0.8 + 3.5.1 + 3.4.0 + 0.8.6 + 1.8.0 ${basedir}/../.. @@ -104,29 +97,6 @@ - - ch.qos.logback - logback-classic - ${logback.version} - - - ch.qos.logback - logback-core - ${logback.version} - - - - com.esotericsoftware - kryo-shaded - ${esotericsoftware.version} - - - - com.fasterxml - classmate - ${classmate.version} - - com.fasterxml.jackson jackson-bom @@ -135,6 +105,20 @@ pom + + org.mockito + mockito-bom + ${mockito.version} + import + pom + + + org.mockito + mockito-inline + ${mockito-inline.version} + test + + com.github.seanyinx unit-scaffolding @@ -142,6 +126,22 @@ test + + org.apache.curator + curator-x-discovery + ${curator.version} + + + org.apache.curator + curator-framework + ${curator.version} + + + org.apache.curator + curator-recipes + ${curator.version} + + com.google.code.findbugs jsr305 @@ -183,28 +183,12 @@ ${guice.version} + com.google.protobuf protobuf-java ${protobuf.version} - - - - com.netflix.spectator - spectator-reg-servo - ${spectator.version} - - - - com.squareup.okhttp3 - okhttp - ${okhttp3.version} - - - - com.squareup.okhttp3 - mockwebserver - ${okhttp3.version} + test @@ -231,56 +215,6 @@ ${commons-logging.version} - - io.github.swagger2markup - swagger2markup - ${swagger2markup.version} - - - io.swagger - swagger-compat-spec-parser - - - commons-collections - commons-collections - - - - - - io.netty - netty-bom - import - ${netty.version} - pom - - - - io.reactivex - rxjava - ${rxjava.version} - - - io.reactivex - rxnetty - ${rxnetty.version} - - - - io.netty - netty-codec-http - - - io.netty - netty-handler - - - io.netty - netty-transport-native-epoll - - - - org.apache.maven maven-model @@ -335,65 +269,27 @@ - io.vertx - vertx-codegen - ${vertx.version} + io.reactivex.rxjava3 + rxjava + ${rxjava.version} - io.vertx - vertx-core - ${vertx.version} - - - io.vertx - vertx-web - ${vertx.version} + org.reactivestreams + reactive-streams + ${reactive-streams.version} io.zipkin.brave - brave - ${brave.version} - - - io.zipkin.brave - brave-context-slf4j - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-jaxrs2 - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-servlet - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-spring-web - ${brave.version} - - - io.zipkin.brave - brave-instrumentation-spring-webmvc - ${brave.version} - - - io.zipkin.brave - brave-spring-beans + brave-bom ${brave.version} + pom + import io.zipkin.reporter2 - zipkin-reporter - ${zipkin-reporter.version} - - - io.zipkin.reporter2 - zipkin-sender-okhttp3 + zipkin-reporter-bom ${zipkin-reporter.version} @@ -431,12 +327,6 @@ ${javax-resource.version} - - javax.xml.bind - jaxb-api - ${jaxb-api.version} - - junit junit @@ -485,24 +375,6 @@ ${log4j2.version} - - org.asciidoctor - asciidoctorj - ${asciidoctorj.version} - - - com.jcraft - jzlib - - - - - - org.aspectj - aspectjweaver - ${aspectj.version} - - org.awaitility awaitility @@ -566,13 +438,6 @@ test - - org.mock-server - mockserver-netty - ${mock-server.version} - test - - org.slf4j slf4j-api @@ -580,9 +445,9 @@ - org.springframework - spring-framework-bom - ${spring.version} + io.vertx + vertx-dependencies + ${vertx.version} pom import @@ -628,6 +493,18 @@ pom import + + + io.etcd + jetcd-core + ${jetcd-core.version} + + + + org.kiwiproject + consul-client + ${consul-client.version} + diff --git a/dependencies/pom.xml b/dependencies/pom.xml index 8cbf8e890fb..4ee9c9ea071 100644 --- a/dependencies/pom.xml +++ b/dependencies/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb java-chassis - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT java-chassis-dependencies-parent diff --git a/distribution/pom.xml b/distribution/pom.xml index 1cdad0e4ebc..ddd1e3de25d 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default apache-servicecomb-java-chassis-distribution diff --git a/distribution/src/release/NOTICE b/distribution/src/release/NOTICE index d1821a3491c..5ed927047d5 100644 --- a/distribution/src/release/NOTICE +++ b/distribution/src/release/NOTICE @@ -1,6 +1,5 @@ Apache ServiceComb Java Chassis -Copyright 2017-2019 The Apache Software Foundation +Copyright 2017-2024 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). - diff --git a/distribution/src/release/licenses/LICENSE-abego b/distribution/src/release/licenses/LICENSE-abego index a4fe241d454..1620af43b30 100644 --- a/distribution/src/release/licenses/LICENSE-abego +++ b/distribution/src/release/licenses/LICENSE-abego @@ -2,26 +2,26 @@ Copyright (c) 2011, abego Software GmbH, Germany (http://www.abego.org) All rights reserved. -Redistribution and use in source and binary forms, with or without +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -3. Neither the name of the abego Software GmbH nor the names of its - contributors may be used to endorse or promote products derived from this +3. Neither the name of the abego Software GmbH nor the names of its + contributors may be used to endorse or promote products derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/distribution/src/release/licenses/LICENSE-animalsniffer b/distribution/src/release/licenses/LICENSE-animalsniffer index 719e6d7f0fd..4e28c7bc322 100644 --- a/distribution/src/release/licenses/LICENSE-animalsniffer +++ b/distribution/src/release/licenses/LICENSE-animalsniffer @@ -18,4 +18,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. + THE SOFTWARE. diff --git a/distribution/src/release/licenses/LICENSE-checkerqual b/distribution/src/release/licenses/LICENSE-checkerqual index f6743bb6980..7f197e96dec 100644 --- a/distribution/src/release/licenses/LICENSE-checkerqual +++ b/distribution/src/release/licenses/LICENSE-checkerqual @@ -17,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/distribution/src/release/licenses/LICENSE-edl-v10 b/distribution/src/release/licenses/LICENSE-edl-v10 index a4a267ef07f..90202907fd2 100644 --- a/distribution/src/release/licenses/LICENSE-edl-v10 +++ b/distribution/src/release/licenses/LICENSE-edl-v10 @@ -9,4 +9,4 @@ Redistribution and use in source and binary forms, with or without modification, Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/distribution/src/release/licenses/LICENSE-epl-v10 b/distribution/src/release/licenses/LICENSE-epl-v10 index 3fa00836fa4..b0476ee93e8 100644 --- a/distribution/src/release/licenses/LICENSE-epl-v10 +++ b/distribution/src/release/licenses/LICENSE-epl-v10 @@ -83,4 +83,4 @@ All Recipient's rights under this Agreement shall terminate if it fails to compl Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. -This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. \ No newline at end of file +This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. diff --git a/distribution/src/release/licenses/LICENSE-jcodings b/distribution/src/release/licenses/LICENSE-jcodings index f8031949305..f85e365de02 100644 --- a/distribution/src/release/licenses/LICENSE-jcodings +++ b/distribution/src/release/licenses/LICENSE-jcodings @@ -15,4 +15,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/distribution/src/release/licenses/LICENSE-jsonp b/distribution/src/release/licenses/LICENSE-jsonp index 07368a285ae..75372e3d2c9 100644 --- a/distribution/src/release/licenses/LICENSE-jsonp +++ b/distribution/src/release/licenses/LICENSE-jsonp @@ -1,5 +1,5 @@ 1. Definitions. - + 1.1. "Contributor" means each individual or entity that creates or contributes to the creation of Modifications. diff --git a/distribution/src/release/licenses/LICENSE-logback b/distribution/src/release/licenses/LICENSE-logback index 8953762a3cd..1f99506bfa4 100644 --- a/distribution/src/release/licenses/LICENSE-logback +++ b/distribution/src/release/licenses/LICENSE-logback @@ -11,4 +11,4 @@ the Eclipse Foundation or (per the licensee's choosing) under the terms of the GNU Lesser General Public License version 2.1 -as published by the Free Software Foundation. \ No newline at end of file +as published by the Free Software Foundation. diff --git a/distribution/src/release/licenses/NOTICE-protostuff b/distribution/src/release/licenses/NOTICE-protostuff index e1c9c379777..1f95779a7fb 100644 --- a/distribution/src/release/licenses/NOTICE-protostuff +++ b/distribution/src/release/licenses/NOTICE-protostuff @@ -3,13 +3,13 @@ Copyright 2009 David Yu dyuproject@gmail.com ============================================================== -protobuf is copyright Google inc unless otherwise noted. +protobuf is copyright Google inc unless otherwise noted. It is licensed under the BSD license. -jackson-core-asl is copyright FasterXml unless otherwise noted. +jackson-core-asl is copyright FasterXml unless otherwise noted. It is licensed under the apache 2.0 license. -antlr is copyright Terence Parr unless otherwise noted. +antlr is copyright Terence Parr unless otherwise noted. It is licensed under the BSD license. stringtemplate is copyright Terence Parr unless otherwise noted. @@ -17,10 +17,10 @@ It is licensed under the BSD license. velocity is licensed under the apache 2.0 license. -B64Code.java is copyright Mort Bay Consulting Pty Ltd unless otherwise noted. +B64Code.java is copyright Mort Bay Consulting Pty Ltd unless otherwise noted. It is licensed under the apache 2.0 license. -jarjar is copyright Google inc unless otherwise noted. +jarjar is copyright Google inc unless otherwise noted. It is licensed under the apache 2.0 license. guava is copyright Google inc unless otherwise noted. diff --git a/doap_ServiceComb_Java-Chassis.rdf b/doap_ServiceComb_Java-Chassis.rdf deleted file mode 100644 index f5dbe8a1ced..00000000000 --- a/doap_ServiceComb_Java-Chassis.rdf +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - 2021-03-25 - - Apache ServiceComb Java-Chassis - - - ServiceComb Java Chassis is a Software Development Kit (SDK) for rapid development of microservices in Java, providing service registration, service discovery, dynamic routing, and service management features - ServiceComb Java Chassis is a Software Development Kit (SDK) for rapid development of microservices in Java, providing service registration, service discovery, dynamic routing, and service management features - - - - Java - - - - ServiceComb Java-Chassis 2.2.0 - 2021-02-28 - 2.2.0 - - - - - - - - - - - liubao68 - - - - - - diff --git a/dynamic-config/config-apollo/pom.xml b/dynamic-config/config-apollo/pom.xml index 65114c20034..7cc03d182dd 100644 --- a/dynamic-config/config-apollo/pom.xml +++ b/dynamic-config/config-apollo/pom.xml @@ -22,7 +22,7 @@ dynamic-config org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 config-apollo diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java index 4fe0cc92b50..61c6a9c5c7d 100644 --- a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java +++ b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloClient.java @@ -163,4 +163,3 @@ void compareChangedConfig(Map before, Map after) } } } - diff --git a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloDynamicPropertiesSource.java b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloDynamicPropertiesSource.java index 935877fcc4c..4a1113283e0 100644 --- a/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloDynamicPropertiesSource.java +++ b/dynamic-config/config-apollo/src/main/java/org/apache/servicecomb/config/apollo/ApolloDynamicPropertiesSource.java @@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; public class ApolloDynamicPropertiesSource implements DynamicPropertiesSource { public static final String SOURCE_NAME = "apollo"; @@ -75,7 +76,7 @@ public void handle(ConfigurationAction action, Map config) { } @Override - public MapPropertySource create(Environment environment) { + public PropertySource create(Environment environment) { init(environment); return new MapPropertySource(SOURCE_NAME, valueCache); } diff --git a/dynamic-config/config-cc/pom.xml b/dynamic-config/config-cc/pom.xml index 42786bce1fe..0e8f6addcc6 100644 --- a/dynamic-config/config-cc/pom.xml +++ b/dynamic-config/config-cc/pom.xml @@ -21,7 +21,7 @@ dynamic-config org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfig.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfig.java index eaa047e98bb..c1cf7c1b683 100644 --- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfig.java +++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterConfig.java @@ -39,6 +39,12 @@ public final class ConfigCenterConfig { private static final int DEFAULT_REFRESH_INTERVAL = 15000; + private static final String CLIENT_CONNECT_TIMEOUT = "servicecomb.config.client.timeout.connect"; + + private static final String CLIENT_REQUEST_TIMEOUT = "servicecomb.config.client.timeout.request"; + + private static final String CLIENT_SOCKET_TIMEOUT = "servicecomb.config.client.timeout.socket"; + private final Environment environment; public ConfigCenterConfig(Environment environment) { @@ -86,4 +92,16 @@ public String getProxyPasswd() { public List getServerUri() { return ConfigUtil.parseArrayValue(environment.getProperty(ADDRESS, "")); } + + public int getConnectTimeout() { + return environment.getProperty(CLIENT_CONNECT_TIMEOUT, int.class, 5000); + } + + public int getConnectionRequestTimeout() { + return environment.getProperty(CLIENT_REQUEST_TIMEOUT, int.class, 5000); + } + + public int getSocketTimeout() { + return environment.getProperty(CLIENT_SOCKET_TIMEOUT, int.class, 5000); + } } diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterDynamicPropertiesSource.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterDynamicPropertiesSource.java index 614c44d39c6..23c0a7af196 100644 --- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterDynamicPropertiesSource.java +++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/ConfigCenterDynamicPropertiesSource.java @@ -49,6 +49,7 @@ import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; import com.google.common.eventbus.Subscribe; @@ -68,33 +69,41 @@ private void init(Environment environment) { ConfigCenterConfig configCenterConfig = new ConfigCenterConfig(environment); configConverter = new ConfigConverter(configCenterConfig.getFileSources()); - ConfigCenterAddressManager kieAddressManager = configKieAddressManager(configCenterConfig); + ConfigCenterAddressManager configCenterAddressManager = configCenterAddressManager(configCenterConfig, environment); - HttpTransport httpTransport = createHttpTransport(kieAddressManager, - HttpTransportFactory.defaultRequestConfig().build(), - environment, configCenterConfig); - ConfigCenterClient configCenterClient = new ConfigCenterClient(kieAddressManager, httpTransport); + HttpTransport httpTransport = createHttpTransport(configCenterAddressManager, + buildRequestConfig(configCenterConfig), environment, configCenterConfig); + ConfigCenterClient configCenterClient = new ConfigCenterClient(configCenterAddressManager, httpTransport); EventManager.register(this); ConfigCenterConfiguration configCenterConfiguration = createConfigCenterConfiguration(configCenterConfig); QueryConfigurationsRequest queryConfigurationsRequest = firstPull(configCenterConfig, configCenterClient, - environment); + environment, configCenterAddressManager); ConfigCenterManager configCenterManager = new ConfigCenterManager(configCenterClient, EventManager.getEventBus(), - configConverter, configCenterConfiguration); + configConverter, configCenterConfiguration, configCenterAddressManager); configCenterManager.setQueryConfigurationsRequest(queryConfigurationsRequest); configCenterManager.startConfigCenterManager(); data.putAll(configConverter.getCurrentData()); } + private RequestConfig buildRequestConfig(ConfigCenterConfig config) { + RequestConfig.Builder builder = HttpTransportFactory.defaultRequestConfig(); + builder.setConnectTimeout(config.getConnectTimeout()); + builder.setConnectionRequestTimeout(config.getConnectionRequestTimeout()); + builder.setSocketTimeout(config.getSocketTimeout()); + return builder.build(); + } + private QueryConfigurationsRequest firstPull(ConfigCenterConfig configCenterConfig, - ConfigCenterClient configCenterClient, Environment environment) { + ConfigCenterClient configCenterClient, Environment environment, + ConfigCenterAddressManager configCenterAddressManager) { QueryConfigurationsRequest queryConfigurationsRequest = createQueryConfigurationsRequest(environment); try { QueryConfigurationsResponse response = configCenterClient - .queryConfigurations(queryConfigurationsRequest); + .queryConfigurations(queryConfigurationsRequest, configCenterAddressManager.address()); if (response.isChanged()) { configConverter.updateData(response.getConfigurations()); queryConfigurationsRequest.setRevision(response.getRevision()); @@ -134,7 +143,7 @@ private ConfigCenterConfiguration createConfigCenterConfiguration( return new ConfigCenterConfiguration().setRefreshIntervalInMillis(configCenterConfig.getRefreshInterval()); } - private HttpTransport createHttpTransport(ConfigCenterAddressManager kieAddressManager, + private HttpTransport createHttpTransport(ConfigCenterAddressManager configCenterAddressManager, RequestConfig requestConfig, Environment environment, ConfigCenterConfig configCenterConfig) { List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); @@ -154,33 +163,35 @@ private HttpTransport createHttpTransport(ConfigCenterAddressManager kieAddressM return HttpTransportFactory .createHttpTransport( TransportUtils - .createSSLProperties(kieAddressManager.sslEnabled(), environment, ConfigCenterConfig.SSL_TAG), + .createSSLProperties(configCenterAddressManager.sslEnabled(), environment, ConfigCenterConfig.SSL_TAG), getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); } return HttpTransportFactory .createHttpTransport( TransportUtils - .createSSLProperties(kieAddressManager.sslEnabled(), environment, ConfigCenterConfig.SSL_TAG), + .createSSLProperties(configCenterAddressManager.sslEnabled(), environment, ConfigCenterConfig.SSL_TAG), getRequestAuthHeaderProvider(authHeaderProviders), requestConfig); } private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { return signRequest -> { + String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; Map headers = new HashMap<>(); - authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders())); + authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); return headers; }; } - private ConfigCenterAddressManager configKieAddressManager(ConfigCenterConfig configCenterConfig) { + private ConfigCenterAddressManager configCenterAddressManager(ConfigCenterConfig configCenterConfig, Environment environment) { + String region = environment.getProperty("servicecomb.datacenter.region"); + String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); return new ConfigCenterAddressManager(configCenterConfig.getDomainName(), - configCenterConfig.getServerUri(), - EventManager.getEventBus()); + configCenterConfig.getServerUri(), EventManager.getEventBus(), region, availableZone); } @Override - public MapPropertySource create(Environment environment) { + public PropertySource create(Environment environment) { init(environment); return new MapPropertySource(SOURCE_NAME, data); } diff --git a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/TransportUtils.java b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/TransportUtils.java index 6a91ea6a22c..25a4dac0543 100644 --- a/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/TransportUtils.java +++ b/dynamic-config/config-cc/src/main/java/org/apache/servicecomb/config/cc/TransportUtils.java @@ -61,10 +61,10 @@ public static SSLProperties createSSLProperties(boolean sslEnabled, Environment DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".checkCN.white.file", "ssl.checkCN.white.file")); - option.setAllowRenegociate(getBooleanProperty(environment, - DEFAULT_OPTION.isAllowRenegociate(), - "ssl." + tag + ".allowRenegociate", - "ssl.allowRenegociate")); + option.setAllowRenegotiate(getBooleanProperty(environment, + DEFAULT_OPTION.isAllowRenegotiate(), + "ssl." + tag + ".allowRenegotiate", + "ssl.allowRenegotiate")); option.setStorePath( getStringProperty(environment, DEFAULT_OPTION.getStorePath(), diff --git a/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector b/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector index f49c5aacfbd..9b7c91cf3d9 100644 --- a/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector +++ b/dynamic-config/config-cc/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -org.apache.servicecomb.config.cc.ConfigCenterInformationCollector \ No newline at end of file +org.apache.servicecomb.config.cc.ConfigCenterInformationCollector diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterAddressManagerTest.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterAddressManagerTest.java index 1642d11ff55..50d39bc19db 100644 --- a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterAddressManagerTest.java +++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterAddressManagerTest.java @@ -41,8 +41,8 @@ class ConfigCenterAddressManagerTest { public void addressManagerTest() throws NoSuchFieldException, IllegalAccessException { addresses.add("http://127.0.0.1:30103"); addresses.add("https://127.0.0.2:30103"); - addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus()); - addressManager2 = new ConfigCenterAddressManager(null, addresses, new EventBus()); + addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus(), "", ""); + addressManager2 = new ConfigCenterAddressManager(null, addresses, new EventBus(), "", ""); Field addressManagerField = addressManager1.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager1, 0); @@ -71,7 +71,7 @@ public void onRefreshEndpointEvent() { Map> zoneAndRegion = new HashMap<>(); zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", addressRG); - addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus()); + addressManager1 = new ConfigCenterAddressManager("project", addresses, new EventBus(), "", ""); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseConfigCenter"); addressManager1.refreshEndpoint(event, "CseConfigCenter"); diff --git a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java index f9424c9a2f2..3298d95bb19 100644 --- a/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java +++ b/dynamic-config/config-cc/src/test/java/org/apache/servicecomb/config/cc/ConfigCenterConfigurationSourceImplTest.java @@ -36,7 +36,8 @@ void configAddressManagerTest() throws IllegalAccessException, NoSuchFieldExcept List addresses = new ArrayList<>(); addresses.add("http://127.0.0.1:30103"); addresses.add("http://127.0.0.2:30103"); - ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, EventManager.getEventBus()); + ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, + EventManager.getEventBus(), "", ""); Field addressManagerField = addressManager.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager, 0); @@ -47,7 +48,7 @@ void configAddressManagerTest() throws IllegalAccessException, NoSuchFieldExcept address = addressManager.address(); Assertions.assertEquals("http://127.0.0.1:30103/v3/test", address); - addressManager = new ConfigCenterAddressManager(null, addresses, EventManager.getEventBus()); + addressManager = new ConfigCenterAddressManager(null, addresses, EventManager.getEventBus(), "", ""); addressManagerField = addressManager.getClass().getSuperclass().getDeclaredField("index"); addressManagerField.setAccessible(true); addressManagerField.set(addressManager, 0); @@ -65,7 +66,8 @@ void onRefreshEndpointEventTest() { zoneAndRegion.put("sameZone", addressAZ); zoneAndRegion.put("sameRegion", new ArrayList<>()); RefreshEndpointEvent event = new RefreshEndpointEvent(zoneAndRegion, "CseConfigCenter"); - ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, EventManager.getEventBus()); + ConfigCenterAddressManager addressManager = new ConfigCenterAddressManager("test", addresses, + EventManager.getEventBus(), "", ""); addressManager.onRefreshEndpointEvent(event); List availableAZ = addressManager.getAvailableZone(); diff --git a/dynamic-config/config-consul/pom.xml b/dynamic-config/config-consul/pom.xml new file mode 100644 index 00000000000..af890c0e6ec --- /dev/null +++ b/dynamic-config/config-consul/pom.xml @@ -0,0 +1,54 @@ + + + + + 4.0.0 + + org.apache.servicecomb + dynamic-config + 3.4.0-SNAPSHOT + + + config-consul + Java Chassis::Dynamic Config::Consul + + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + org.kiwiproject + consul-client + + + com.google.code.gson + gson + + + diff --git a/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfig.java b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfig.java new file mode 100644 index 00000000000..57613037eb4 --- /dev/null +++ b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfig.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.consul; + +import org.springframework.core.env.Environment; + +public class ConsulConfig { + + public static final String CONSUL_CONFIG_PREFIX = "servicecomb.config.consul"; + + public static final String CONSUL_DEFAULT_ENVIRONMENT = "production"; + + public static final String PROPERTY_CONSUL_HOST = "servicecomb.config.consul.host"; + + public static final String PROPERTY_CONSUL_PORT = "servicecomb.config.consul.port"; + + public static final String PROPERTY_CONSUL_SCHEME = "servicecomb.config.consul.scheme"; + + public static final String PROPERTY_CONSUL_ACL_TOKEN = "servicecomb.config.consul.acl-token"; + + public static final String PROPERTY_CONSUL_WATCH_SECONDS = "servicecomb.config.consul.watch-seconds"; + + public static final String PROPERTY_INSTANCE_TAG = "servicecomb.config.consul.instance-tag"; + + public static final String PATH_ENVIRONMENT = "/servicecomb/config/environment/%s"; + + public static final String PATH_APPLICATION = "/servicecomb/config/application/%s/%s"; + + public static final String PATH_SERVICE = "/servicecomb/config/service/%s/%s/%s"; + + public static final String PATH_VERSION = "/servicecomb/config/version/%s/%s/%s/%s"; + + public static final String PATH_TAG = "/servicecomb/config/tag/%s/%s/%s/%s/%s"; + + private final Environment environment; + + public ConsulConfig(Environment environment) { + this.environment = environment; + } + + public String getConsulHost() { + return environment.getProperty(PROPERTY_CONSUL_HOST, String.class, "127.0.0.1"); + } + + public int getConsulPort() { + return environment.getProperty(PROPERTY_CONSUL_PORT, int.class, 8500); + } + + public String getConsulScheme() { + return environment.getProperty(PROPERTY_CONSUL_SCHEME); + } + + public String getConsulAclToken() { + return environment.getProperty(PROPERTY_CONSUL_ACL_TOKEN); + } + + public int getConsulWatchSeconds() { + return environment.getProperty(PROPERTY_CONSUL_WATCH_SECONDS, int.class, 8); + } + + public String getInstanceTag() { + return environment.getProperty(PROPERTY_INSTANCE_TAG); + } +} diff --git a/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigClient.java b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigClient.java new file mode 100644 index 00000000000..ae18e11ff48 --- /dev/null +++ b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigClient.java @@ -0,0 +1,305 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.consul; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.jetbrains.annotations.NotNull; +import org.kiwiproject.consul.Consul; +import org.kiwiproject.consul.KeyValueClient; +import org.kiwiproject.consul.cache.KVCache; +import org.kiwiproject.consul.model.kv.Value; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ByteArrayResource; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; + +import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_APPLICATION; +import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_ENVIRONMENT; +import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_SERVICE; +import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_TAG; +import static org.apache.servicecomb.config.consul.ConsulConfig.PATH_VERSION; + +public class ConsulConfigClient { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulConfigClient.class); + + public class GetDataRunnable implements Runnable { + + private Map dataMap; + + private String path; + + public GetDataRunnable(String path, Map dataMap) { + this.dataMap = dataMap; + this.path = path; + } + + @Override + public void run() { + try { + if (path.equals("tagData")) { + tagData = dataMap; + } else if (path.equals("versionData")) { + versionData = dataMap; + } else if (path.equals("serviceData")) { + serviceData = dataMap; + } else if (path.equals("applicationData")) { + applicationData = dataMap; + } else { + environmentData = dataMap; + } + refreshConfigItems(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private ConsulDynamicPropertiesSource.UpdateHandler updateHandler; + + private ConsulConfig consulConfig; + + private Environment environment; + + private final Object lock = new Object(); + + private Consul consulClient; + + private KeyValueClient kvClient; + + private ConsulConfigProperties consulConfigProperties; + + private Map environmentData = new HashMap<>(); + + private Map applicationData = new HashMap<>(); + + private Map serviceData = new HashMap<>(); + + private Map versionData = new HashMap<>(); + + private Map tagData = new HashMap<>(); + + private Map allLast = new HashMap<>(); + + public ConsulConfigClient(ConsulDynamicPropertiesSource.UpdateHandler updateHandler, Environment environment, ConsulConfigProperties consulConfigProperties, Consul consulClient) { + this.updateHandler = updateHandler; + this.consulConfig = new ConsulConfig(environment); + this.environment = environment; + this.consulConfigProperties = consulConfigProperties; + this.consulClient = consulClient; + this.kvClient = consulClient.keyValueClient(); + } + + public void refreshConsulConfig() { + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ConsulConfig.CONSUL_DEFAULT_ENVIRONMENT; + } + addEnvironmentConfig(env); + addApplicationConfig(env); + addServiceConfig(env); + addVersionConfig(env); + addTagConfig(env); + + refreshConfigItems(); + } + + private void addTagConfig(String env) { + if (StringUtils.isBlank(consulConfig.getInstanceTag())) { + return; + } + String path = String.format(PATH_TAG, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment), + consulConfig.getInstanceTag()); + + this.tagData = parseData(path); + try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { + cache.addListener(newValues -> { + Optional newValue = newValues.values().stream() + .filter(value -> value.getKey().equals(path)) + .findAny(); + + newValue.ifPresent(value -> { + Optional decodedValue = newValue.get().getValueAsString(); + decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("tagData", getValues(path))).start()); + }); + }); + cache.start(); + } + } + + private void addVersionConfig(String env) { + String path = String.format(PATH_VERSION, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment)); + + this.versionData = parseData(path); + try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { + cache.addListener(newValues -> { + Optional newValue = newValues.values().stream() + .filter(value -> value.getKey().equals(path)) + .findAny(); + + newValue.ifPresent(value -> { + Optional decodedValue = newValue.get().getValueAsString(); + decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("versionData", getValues(path))).start()); + }); + }); + cache.start(); + } + } + + private void addServiceConfig(String env) { + String path = String.format(PATH_SERVICE, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment)); + + this.serviceData = parseData(path); + try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { + cache.addListener(newValues -> { + Optional newValue = newValues.values().stream() + .filter(value -> value.getKey().equals(path)) + .findAny(); + + newValue.ifPresent(value -> { + Optional decodedValue = newValue.get().getValueAsString(); + decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("serviceData", getValues(path))).start()); + }); + }); + cache.start(); + } + } + + private void addApplicationConfig(String env) { + String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); + this.applicationData = parseData(path); + try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { + cache.addListener(newValues -> { + Optional newValue = newValues.values().stream() + .filter(value -> value.getKey().equals(path)) + .findAny(); + + newValue.ifPresent(value -> { + Optional decodedValue = newValue.get().getValueAsString(); + decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("applicationData", getValues(path))).start()); + }); + }); + cache.start(); + } + } + + private void addEnvironmentConfig(String env) { + String path = String.format(PATH_ENVIRONMENT, env); + + this.environmentData = parseData(path); + try (KVCache cache = KVCache.newCache(kvClient, path, consulConfig.getConsulWatchSeconds())) { + cache.addListener(newValues -> { + Optional newValue = newValues.values().stream() + .filter(value -> value.getKey().equals(path)) + .findAny(); + + newValue.ifPresent(value -> { + Optional decodedValue = newValue.get().getValueAsString(); + decodedValue.ifPresent(v -> new Thread(new GetDataRunnable("environmentData", getValues(path))).start()); + }); + }); + cache.start(); + } + } + + public Map parseData(String path) { + try { + return getValues(path); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + } + return new HashMap<>(); + } + + private Map getValues(String path) { + Map values = new HashMap<>(); + KeyValueClient keyValueClient = consulClient.keyValueClient(); + String decodedValue = keyValueClient.getValueAsString(path).orElseThrow(); + if (StringUtils.isBlank(decodedValue)) { + return values; + } + return getValues(path, decodedValue); + } + + private @NotNull Map getValues(String path, String decodedValue) { + Map values = new HashMap<>(); + if (path.endsWith(".yaml") || path.endsWith(".yml")) { + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(decodedValue.getBytes(StandardCharsets.UTF_8))); + values.putAll(toMap(yamlFactory.getObject())); + } else if (path.endsWith(".properties")) { + Properties properties = new Properties(); + try { + properties.load(new StringReader(decodedValue)); + } catch (IOException e) { + LOGGER.error(e.getMessage(), e); + } + values.putAll(toMap(properties)); + } else { + values.put(path, decodedValue); + } + return values; + } + + private void refreshConfigItems() { + synchronized (lock) { + Map all = new HashMap<>(); + all.putAll(environmentData); + all.putAll(applicationData); + all.putAll(serviceData); + all.putAll(versionData); + all.putAll(tagData); + updateHandler.handle(all, allLast); + this.allLast = all; + } + } + + @SuppressWarnings("unchecked") + private Map toMap(Properties properties) { + if (properties == null) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + Enumeration keys = (Enumeration) properties.propertyNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + Object value = properties.getProperty(key); + result.put(key, value); + } + return result; + } +} diff --git a/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigProperties.java b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigProperties.java new file mode 100644 index 00000000000..fe3a278678a --- /dev/null +++ b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulConfigProperties.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.consul; + +public class ConsulConfigProperties { + + private String host = "localhost"; + + private Integer port = 8500; + + private String scheme = "http"; + + private String aclToken; + + private Integer watchSeconds = 8; + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getScheme() { + return scheme; + } + + public void setScheme(String scheme) { + this.scheme = scheme; + } + + public String getAclToken() { + return aclToken; + } + + public void setAclToken(String aclToken) { + this.aclToken = aclToken; + } + + public Integer getWatchSeconds() { + return watchSeconds; + } + + public void setWatchSeconds(Integer watchSeconds) { + this.watchSeconds = watchSeconds; + } +} diff --git a/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulDynamicPropertiesSource.java b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulDynamicPropertiesSource.java new file mode 100644 index 00000000000..9f0b3649249 --- /dev/null +++ b/dynamic-config/config-consul/src/main/java/org/apache/servicecomb/config/consul/ConsulDynamicPropertiesSource.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.config.consul; + +import com.google.common.net.HostAndPort; +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.kiwiproject.consul.Consul; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class ConsulDynamicPropertiesSource implements DynamicPropertiesSource { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulDynamicPropertiesSource.class); + + public static final String SOURCE_NAME = "consul"; + + private final Map valueCache = new ConcurrentHashMap<>(); + + private ConsulConfigClient consulConfigClient; + + public ConsulDynamicPropertiesSource() { + } + + private final UpdateHandler updateHandler = new UpdateHandler(); + public class UpdateHandler { + public void handle(Map current, Map last) { + ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); + LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); + valueCache.putAll(event.getAdded()); + valueCache.putAll(event.getUpdated()); + event.getDeleted().forEach((k, v) -> valueCache.remove(k)); + EventManager.post(event); + } + } + + private ConsulConfigProperties consulConfigProperties(Environment environment) { + ConsulConfig consulConfig = new ConsulConfig(environment); + ConsulConfigProperties consulConfigProperties = new ConsulConfigProperties(); + consulConfigProperties.setHost(consulConfig.getConsulHost()); + consulConfigProperties.setPort(consulConfig.getConsulPort()); + consulConfigProperties.setScheme(consulConfig.getConsulScheme()); + consulConfigProperties.setAclToken(consulConfig.getConsulAclToken()); + consulConfigProperties.setWatchSeconds(consulConfig.getConsulWatchSeconds()); + return consulConfigProperties; + } + + private Consul consulClient(ConsulConfigProperties consulConfigProperties) { + Consul.Builder builder = Consul.builder().withHostAndPort(HostAndPort.fromParts(consulConfigProperties.getHost(), consulConfigProperties.getPort())); + if (StringUtils.isNotBlank(consulConfigProperties.getAclToken())) { + builder.withAclToken(consulConfigProperties.getAclToken()); + } + return builder.build(); + } + + private ConsulConfigClient consulConfigClient(Environment environment) { + ConsulConfigProperties consulConfigProperties = consulConfigProperties(environment); + Consul consulClient = consulClient(consulConfigProperties); + return new ConsulConfigClient(updateHandler, environment, consulConfigProperties, consulClient); + } + + @Override + public PropertySource create(Environment environment) { + try { + consulConfigClient = consulConfigClient(environment); + consulConfigClient.refreshConsulConfig(); + } catch (Exception e) { + throw new IllegalStateException("Set up consul config failed.", e); + } + return new MapPropertySource(SOURCE_NAME, valueCache); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/swagger/swagger-invocation/invocation-validator/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.extension.ProducerInvokeExtension b/dynamic-config/config-consul/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource similarity index 91% rename from swagger/swagger-invocation/invocation-validator/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.extension.ProducerInvokeExtension rename to dynamic-config/config-consul/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource index 215fc569235..7b1c73e1195 100644 --- a/swagger/swagger-invocation/invocation-validator/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.extension.ProducerInvokeExtension +++ b/dynamic-config/config-consul/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.invocation.validator.ParameterValidator +org.apache.servicecomb.config.consul.ConsulDynamicPropertiesSource diff --git a/dynamic-config/config-etcd/pom.xml b/dynamic-config/config-etcd/pom.xml new file mode 100644 index 00000000000..8aa6b2cf963 --- /dev/null +++ b/dynamic-config/config-etcd/pom.xml @@ -0,0 +1,44 @@ + + + + + + + dynamic-config + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + config-etcd + Java Chassis::Dynamic Config::Zookeeper + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-vertx + + + io.etcd + jetcd-core + + + diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java new file mode 100644 index 00000000000..9a7743add07 --- /dev/null +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdClient.java @@ -0,0 +1,275 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.etcd; + +import java.io.IOException; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.etcd.EtcdDynamicPropertiesSource.UpdateHandler; +import org.apache.servicecomb.foundation.common.utils.MuteExceptionUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ByteArrayResource; + +import io.etcd.jetcd.ByteSequence; +import io.etcd.jetcd.Client; +import io.etcd.jetcd.KeyValue; +import io.etcd.jetcd.Watch; +import io.etcd.jetcd.kv.GetResponse; +import io.etcd.jetcd.options.GetOption; +import io.etcd.jetcd.options.WatchOption; + +public class EtcdClient { + + public class GetDataRunnable implements Runnable { + + private Map dataMap; + + private EtcdClient etcdClient; + + private String path; + + public GetDataRunnable(Map dataMap, EtcdClient etcdClient, String path) { + this.dataMap = dataMap; + this.etcdClient = etcdClient; + this.path = path; + } + + @Override + public void run() { + try { + dataMap.clear(); + dataMap.putAll(etcdClient.parseData(path)); + refreshConfigItems(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdClient.class); + + public static final String PATH_ENVIRONMENT = "/servicecomb/config/environment/%s"; + + public static final String PATH_APPLICATION = "/servicecomb/config/application/%s/%s"; + + public static final String PATH_SERVICE = "/servicecomb/config/service/%s/%s/%s"; + + public static final String PATH_VERSION = "/servicecomb/config/version/%s/%s/%s/%s"; + + public static final String PATH_TAG = "/servicecomb/config/tag/%s/%s/%s/%s/%s"; + + private final UpdateHandler updateHandler; + + private final EtcdConfig etcdConfig; + + private final Environment environment; + + private final Object lock = new Object(); + + private Map environmentData = new HashMap<>(); + + private Map applicationData = new HashMap<>(); + + private Map serviceData = new HashMap<>(); + + private Map versionData = new HashMap<>(); + + private Map tagData = new HashMap<>(); + + private Map allLast = new HashMap<>(); + + private Client client; + + public EtcdClient(UpdateHandler updateHandler, Environment environment) { + this.updateHandler = updateHandler; + this.etcdConfig = new EtcdConfig(environment); + this.environment = environment; + } + + public void getClient() { + if (StringUtils.isEmpty(etcdConfig.getAuthInfo())) { + this.client = Client.builder().endpoints(etcdConfig.getConnectString()).build(); + } else { + String[] authInfo = etcdConfig.getAuthInfo().split(":"); + this.client = Client.builder().endpoints(etcdConfig.getConnectString()) + .user(ByteSequence.from(authInfo[0], StandardCharsets.UTF_8)) + .password(ByteSequence.from(authInfo[1], StandardCharsets.UTF_8)).build(); + } + } + + public void refreshEtcdConfig() throws Exception { + + getClient(); + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = EtcdConfig.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + addEnvironmentConfig(env); + addApplicationConfig(env); + addServiceConfig(env); + addVersionConfig(env); + addTagConfig(env); + + refreshConfigItems(); + } + + private void addTagConfig(String env) throws Exception { + if (StringUtils.isEmpty(etcdConfig.getInstanceTag())) { + return; + } + String path = String.format(PATH_TAG, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment), + etcdConfig.getInstanceTag()); + + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunnable(tagData, this, path)).start()); + this.tagData = parseData(path); + } + + private void addVersionConfig(String env) throws Exception { + String path = String.format(PATH_VERSION, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment)); + + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunnable(versionData, this, path)).start()); + this.versionData = parseData(path); + } + + private void addServiceConfig(String env) throws Exception { + String path = String.format(PATH_SERVICE, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment)); + + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunnable(serviceData, this, path)).start()); + this.serviceData = parseData(path); + } + + private void addApplicationConfig(String env) throws Exception { + String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); + + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunnable(applicationData, this, path)).start()); + this.applicationData = parseData(path); + } + + private void addEnvironmentConfig(String env) throws Exception { + String path = String.format(PATH_ENVIRONMENT, env); + + ByteSequence prefixByteSeq = ByteSequence.from(path, StandardCharsets.UTF_8); + Watch watchClient = client.getWatchClient(); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> new Thread(new GetDataRunnable(environmentData, this, path)).start()); + this.environmentData = parseData(path); + } + + public Map parseData(String path) throws Exception { + + List endpointKv = getValuesByPrefix(path); + return getValues(path, endpointKv); + } + + private Map getValues(String path, List endpointKv) { + Map values = new HashMap<>(); + for (KeyValue keyValue : endpointKv) { + String key = new String(keyValue.getKey().getBytes(), StandardCharsets.UTF_8); + String value = new String(keyValue.getValue().getBytes(), StandardCharsets.UTF_8); + if (key.equals(path)) { + continue; + } + if (key.endsWith(".yaml") || key.endsWith(".yml")) { + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(value.getBytes(StandardCharsets.UTF_8))); + values.putAll(toMap(yamlFactory.getObject())); + } else if (key.endsWith(".properties")) { + Properties properties = new Properties(); + try { + properties.load(new StringReader(value)); + } catch (IOException e) { + LOGGER.error("load error"); + } + values.putAll(toMap(properties)); + } else { + values.put(key, value); + } + } + return values; + } + + private List getValuesByPrefix(String prefix) { + + CompletableFuture getFuture = client.getKVClient() + .get(ByteSequence.from(prefix, StandardCharsets.UTF_8), + GetOption.builder().withPrefix(ByteSequence.from(prefix, StandardCharsets.UTF_8)).build()); + GetResponse response = MuteExceptionUtil.builder().withLog("get kv by prefix error") + .executeCompletableFuture(getFuture); + return response.getKvs(); + } + + private void refreshConfigItems() { + synchronized (lock) { + Map all = new HashMap<>(); + all.putAll(environmentData); + all.putAll(applicationData); + all.putAll(serviceData); + all.putAll(versionData); + all.putAll(tagData); + updateHandler.handle(all, allLast); + this.allLast = all; + } + } + + @SuppressWarnings("unchecked") + private Map toMap(Properties properties) { + if (properties == null) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + Enumeration keys = (Enumeration) properties.propertyNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + Object value = properties.getProperty(key); + result.put(key, value); + } + return result; + } +} diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.java new file mode 100644 index 00000000000..ee04204c28a --- /dev/null +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdConfig.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.etcd; + +import org.springframework.core.env.Environment; + +public class EtcdConfig { + public static final String ZOOKEEPER_DEFAULT_ENVIRONMENT = "production"; + + public static final String PROPERTY_CONNECT_STRING = "servicecomb.config.etcd.connectString"; + + public static final String PROPERTY_SESSION_TIMEOUT = "servicecomb.config.etcd.sessionTimeoutMillis"; + + public static final String PROPERTY_CONNECTION_TIMEOUT = "servicecomb.config.etcd.connectionTimeoutMills"; + + public static final String PROPERTY_AUTH_SCHEMA = "servicecomb.config.etcd.authenticationSchema"; + + public static final String PROPERTY_AUTH_INFO = "servicecomb.config.etcd.authenticationInfo"; + + public static final String PROPERTY_INSTANCE_TAG = "servicecomb.config.etcd.instanceTag"; + + private final Environment environment; + + public EtcdConfig(Environment environment) { + this.environment = environment; + } + + public String getConnectString() { + return environment.getProperty(PROPERTY_CONNECT_STRING, "http://127.0.0.1:2379"); + } + + public int getSessionTimeoutMillis() { + return environment.getProperty(PROPERTY_SESSION_TIMEOUT, int.class, 60000); + } + + public int getConnectionTimeoutMillis() { + return environment.getProperty(PROPERTY_CONNECTION_TIMEOUT, int.class, 1000); + } + + public String getAuthSchema() { + return environment.getProperty(PROPERTY_AUTH_SCHEMA); + } + + public String getAuthInfo() { + return environment.getProperty(PROPERTY_AUTH_INFO); + } + + public String getInstanceTag() { + return environment.getProperty(PROPERTY_INSTANCE_TAG); + } +} diff --git a/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java new file mode 100644 index 00000000000..a86de892180 --- /dev/null +++ b/dynamic-config/config-etcd/src/main/java/org/apache/servicecomb/config/etcd/EtcdDynamicPropertiesSource.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.etcd; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +public class EtcdDynamicPropertiesSource implements DynamicPropertiesSource { + public static final String SOURCE_NAME = "etcd"; + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDynamicPropertiesSource.class); + + private final Map valueCache = new ConcurrentHashMap<>(); + + public EtcdDynamicPropertiesSource() { + } + + private final UpdateHandler updateHandler = new UpdateHandler(); + + private void init(Environment environment) { + EtcdClient etcdClient = new EtcdClient(updateHandler, environment); + try { + etcdClient.refreshEtcdConfig(); + } catch (Exception e) { + throw new IllegalStateException("Set up etcd config failed.", e); + } + } + + public class UpdateHandler { + public void handle(Map current, Map last) { + ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); + LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); + valueCache.putAll(event.getAdded()); + valueCache.putAll(event.getUpdated()); + event.getDeleted().forEach((k, v) -> valueCache.remove(k)); + EventManager.post(event); + } + } + + @Override + public PropertySource create(Environment environment) { + init(environment); + return new MapPropertySource(SOURCE_NAME, valueCache); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..d10124395fa --- /dev/null +++ b/dynamic-config/config-etcd/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.apache.servicecomb.config.etcd.EtcdDynamicPropertiesSource diff --git a/dynamic-config/config-kie/pom.xml b/dynamic-config/config-kie/pom.xml index 4a3a7f2cda2..4bf61b5801f 100644 --- a/dynamic-config/config-kie/pom.xml +++ b/dynamic-config/config-kie/pom.xml @@ -23,7 +23,7 @@ dynamic-config org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -62,4 +62,4 @@ config-kie-client - \ No newline at end of file + diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfig.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfig.java index 564b5c8ad17..79e40786f8f 100644 --- a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfig.java +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieConfig.java @@ -66,6 +66,12 @@ public class KieConfig { private static final String CUSTOM_LABEL_VALUE_DEFAULT = ""; + private static final String CLIENT_CONNECT_TIMEOUT = "servicecomb.kie.client.timeout.connect"; + + private static final String CLIENT_REQUEST_TIMEOUT = "servicecomb.kie.client.timeout.request"; + + private static final String CLIENT_SOCKET_TIMEOUT = "servicecomb.kie.client.timeout.socket"; + private final Environment environment; public KieConfig(Environment environment) { @@ -148,4 +154,16 @@ public String getProxyUsername() { public String getProxyPasswd() { return environment.getProperty(VertxConst.PROXY_PASSWD); } + + public int getConnectTimeout() { + return environment.getProperty(CLIENT_CONNECT_TIMEOUT, int.class, 5000); + } + + public int getConnectionRequestTimeout() { + return environment.getProperty(CLIENT_REQUEST_TIMEOUT, int.class, 5000); + } + + public int getSocketTimeout() { + return environment.getProperty(CLIENT_SOCKET_TIMEOUT, int.class, 5000); + } } diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieDynamicPropertiesSource.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieDynamicPropertiesSource.java index 13a25be6f1f..6683b84fb09 100644 --- a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieDynamicPropertiesSource.java +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/KieDynamicPropertiesSource.java @@ -26,6 +26,7 @@ import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.config.RequestConfig.Builder; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.servicecomb.config.BootStrapProperties; @@ -48,6 +49,7 @@ import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; import com.google.common.eventbus.Subscribe; @@ -69,9 +71,9 @@ public KieDynamicPropertiesSource() { private void init(Environment environment) { KieConfig kieConfig = new KieConfig(environment); configConverter = new ConfigConverter(kieConfig.getFileSources()); - KieAddressManager kieAddressManager = configKieAddressManager(kieConfig); + KieAddressManager kieAddressManager = configKieAddressManager(kieConfig, environment); - RequestConfig.Builder requestBuilder = HttpTransportFactory.defaultRequestConfig(); + RequestConfig.Builder requestBuilder = buildRequestConfigBuilder(kieConfig); if (kieConfig.enableLongPolling() && kieConfig.getPollingWaitTime() >= 0) { requestBuilder.setConnectionRequestTimeout(kieConfig.getPollingWaitTime() * 2 * 1000); @@ -82,12 +84,21 @@ private void init(Environment environment) { KieConfiguration kieConfiguration = createKieConfiguration(kieConfig, environment); KieClient kieClient = new KieClient(kieAddressManager, httpTransport, kieConfiguration); EventManager.register(this); - kieConfigManager = new KieConfigManager(kieClient, EventManager.getEventBus(), kieConfiguration, configConverter); + kieConfigManager = new KieConfigManager(kieClient, EventManager.getEventBus(), kieConfiguration, configConverter, + kieAddressManager); kieConfigManager.firstPull(); kieConfigManager.startConfigKieManager(); data.putAll(configConverter.getCurrentData()); } + private Builder buildRequestConfigBuilder(KieConfig kieConfig) { + Builder builder = HttpTransportFactory.defaultRequestConfig(); + builder.setConnectTimeout(kieConfig.getConnectTimeout()); + builder.setConnectionRequestTimeout(kieConfig.getConnectionRequestTimeout()); + builder.setSocketTimeout(kieConfig.getSocketTimeout()); + return builder; + } + @Subscribe public void onConfigurationChangedEvent(KieConfigurationChangedEvent event) { LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); @@ -150,19 +161,22 @@ private HttpTransport createHttpTransport(KieAddressManager kieAddressManager, private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { return signRequest -> { + String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; Map headers = new HashMap<>(); - authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders())); + authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); return headers; }; } - private KieAddressManager configKieAddressManager(KieConfig kieConfig) { + private KieAddressManager configKieAddressManager(KieConfig kieConfig, Environment environment) { + String region = environment.getProperty("servicecomb.datacenter.region"); + String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); return new KieAddressManager( - Arrays.asList(kieConfig.getServerUri().split(",")), EventManager.getEventBus()); + Arrays.asList(kieConfig.getServerUri().split(",")), EventManager.getEventBus(), region, availableZone); } @Override - public MapPropertySource create(Environment environment) { + public PropertySource create(Environment environment) { init(environment); return new MapPropertySource(SOURCE_NAME, data); } diff --git a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/TransportUtils.java b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/TransportUtils.java index 8cabafec633..8be7ec00c1a 100644 --- a/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/TransportUtils.java +++ b/dynamic-config/config-kie/src/main/java/org/apache/servicecomb/config/kie/TransportUtils.java @@ -61,10 +61,10 @@ public static SSLProperties createSSLProperties(boolean sslEnabled, Environment DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".checkCN.white.file", "ssl.checkCN.white.file")); - option.setAllowRenegociate(getBooleanProperty(environment, - DEFAULT_OPTION.isAllowRenegociate(), - "ssl." + tag + ".allowRenegociate", - "ssl.allowRenegociate")); + option.setAllowRenegotiate(getBooleanProperty(environment, + DEFAULT_OPTION.isAllowRenegotiate(), + "ssl." + tag + ".allowRenegotiate", + "ssl.allowRenegotiate")); option.setStorePath( getStringProperty(environment, DEFAULT_OPTION.getStorePath(), diff --git a/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector index cb357c9c3f8..4ff2984a1b2 100644 --- a/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector +++ b/dynamic-config/config-kie/src/main/resources/META-INF/services/org.apache.servicecomb.core.bootup.BootUpInformationCollector @@ -14,4 +14,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -org.apache.servicecomb.config.kie.collect.KieClientInformationCollector \ No newline at end of file +org.apache.servicecomb.config.kie.collect.KieClientInformationCollector diff --git a/dynamic-config/config-nacos/README.md b/dynamic-config/config-nacos/README.md index 8e817fc043a..e09d8db761d 100644 --- a/dynamic-config/config-nacos/README.md +++ b/dynamic-config/config-nacos/README.md @@ -1,5 +1,4 @@ # Dynamic configurations implementation with [Nacos](https://github.com/alibaba/nacos) Read [developers guide](https://docs.servicecomb.io/java-chassis/zh_CN/config/general-config/) -for details. - +for details. diff --git a/dynamic-config/config-nacos/pom.xml b/dynamic-config/config-nacos/pom.xml index a30caa4e6c1..395603f9fd6 100644 --- a/dynamic-config/config-nacos/pom.xml +++ b/dynamic-config/config-nacos/pom.xml @@ -22,7 +22,7 @@ dynamic-config org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 config-nacos diff --git a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosClient.java b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosClient.java index 2fd17523c2e..de9527b17ab 100644 --- a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosClient.java +++ b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosClient.java @@ -259,4 +259,3 @@ private static Properties nacosProperties(Environment environment, NacosConfig n return properties; } } - diff --git a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosDynamicPropertiesSource.java b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosDynamicPropertiesSource.java index f170408d42f..308b6337615 100644 --- a/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosDynamicPropertiesSource.java +++ b/dynamic-config/config-nacos/src/main/java/org/apache/servicecomb/config/nacos/NacosDynamicPropertiesSource.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; public class NacosDynamicPropertiesSource implements DynamicPropertiesSource { public static final String SOURCE_NAME = "nacos"; @@ -60,7 +61,7 @@ public void handle(Map current, Map last) { } @Override - public MapPropertySource create(Environment environment) { + public PropertySource create(Environment environment) { init(environment); return new MapPropertySource(SOURCE_NAME, valueCache); } diff --git a/dynamic-config/config-zookeeper/pom.xml b/dynamic-config/config-zookeeper/pom.xml new file mode 100644 index 00000000000..35fabec9591 --- /dev/null +++ b/dynamic-config/config-zookeeper/pom.xml @@ -0,0 +1,49 @@ + + + + + + + dynamic-config + org.apache.servicecomb + 3.4.0-SNAPSHOT + + 4.0.0 + config-zookeeper + Java Chassis::Dynamic Config::Zookeeper + + + org.apache.servicecomb + foundation-config + + + org.apache.servicecomb + foundation-vertx + + + + org.apache.curator + curator-framework + + + org.apache.curator + curator-recipes + + + diff --git a/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperClient.java b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperClient.java new file mode 100644 index 00000000000..407c577d4e5 --- /dev/null +++ b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperClient.java @@ -0,0 +1,281 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.zookeeper; + +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; + +import org.apache.commons.lang3.StringUtils; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.recipes.cache.CuratorCache; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.zookeeper.ZookeeperDynamicPropertiesSource.UpdateHandler; +import org.apache.zookeeper.server.auth.DigestLoginModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.core.env.Environment; +import org.springframework.core.io.ByteArrayResource; + +public class ZookeeperClient { + static class ZookeeperSASLConfig extends Configuration { + AppConfigurationEntry entry; + + public ZookeeperSASLConfig(String username, + String password) { + Map options = new HashMap<>(); + options.put("username", username); + options.put("password", password); + this.entry = new AppConfigurationEntry( + DigestLoginModule.class.getName(), + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options + ); + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + AppConfigurationEntry[] array = new AppConfigurationEntry[1]; + array[0] = entry; + return array; + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperClient.class); + + public static final String PATH_ENVIRONMENT = "/servicecomb/config/environment/%s"; + + public static final String PATH_APPLICATION = "/servicecomb/config/application/%s/%s"; + + public static final String PATH_SERVICE = "/servicecomb/config/service/%s/%s/%s"; + + public static final String PATH_VERSION = "/servicecomb/config/version/%s/%s/%s/%s"; + + public static final String PATH_TAG = "/servicecomb/config/tag/%s/%s/%s/%s/%s"; + + private final UpdateHandler updateHandler; + + private final ZookeeperConfig zookeeperConfig; + + private final Environment environment; + + private final Object lock = new Object(); + + private Map environmentData = new HashMap<>(); + + private Map applicationData = new HashMap<>(); + + private Map serviceData = new HashMap<>(); + + private Map versionData = new HashMap<>(); + + private Map tagData = new HashMap<>(); + + private Map allLast = new HashMap<>(); + + + public ZookeeperClient(UpdateHandler updateHandler, Environment environment) { + this.updateHandler = updateHandler; + this.zookeeperConfig = new ZookeeperConfig(environment); + this.environment = environment; + } + + public void refreshZookeeperConfig() throws Exception { + CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() + .connectString(zookeeperConfig.getConnectString()) + .sessionTimeoutMs(zookeeperConfig.getSessionTimeoutMillis()) + .retryPolicy(new ExponentialBackoffRetry(1000, 3)); + String authSchema = zookeeperConfig.getAuthSchema(); + if (StringUtils.isNotEmpty(authSchema)) { + if (!"digest".equals(authSchema)) { + throw new IllegalStateException("Not supported schema now. " + authSchema); + } + if (zookeeperConfig.getAuthInfo() == null) { + throw new IllegalStateException("Auth info can not be empty. "); + } + + String[] authInfo = zookeeperConfig.getAuthInfo().split(":"); + Configuration.setConfiguration(new ZookeeperSASLConfig(authInfo[0], authInfo[1])); + } + CuratorFramework client = builder.build(); + client.start(); + + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ZookeeperConfig.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + addEnvironmentConfig(env, client); + addApplicationConfig(env, client); + addServiceConfig(env, client); + addVersionConfig(env, client); + addTagConfig(env, client); + + refreshConfigItems(); + } + + private void addTagConfig(String env, CuratorFramework client) throws Exception { + if (StringUtils.isEmpty(zookeeperConfig.getInstanceTag())) { + return; + } + String path = String.format(PATH_TAG, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment), + zookeeperConfig.getInstanceTag()); + CuratorCache cache = CuratorCache.builder(client, path).build(); + cache.listenable().addListener((type, oldData, newData) -> { + try { + this.tagData = parseData(client, path); + refreshConfigItems(); + } catch (Exception e) { + LOGGER.error("process event failed", e); + } + }); + cache.start(); + this.tagData = parseData(client, path); + } + + private void addVersionConfig(String env, CuratorFramework client) throws Exception { + String path = String.format(PATH_VERSION, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment), + BootStrapProperties.readServiceVersion(environment)); + CuratorCache cache = CuratorCache.builder(client, path).build(); + cache.listenable().addListener((type, oldData, newData) -> { + try { + this.versionData = parseData(client, path); + refreshConfigItems(); + } catch (Exception e) { + LOGGER.error("process event failed", e); + } + }); + cache.start(); + this.versionData = parseData(client, path); + } + + private void addServiceConfig(String env, CuratorFramework client) throws Exception { + String path = String.format(PATH_SERVICE, env, + BootStrapProperties.readApplication(environment), + BootStrapProperties.readServiceName(environment)); + CuratorCache cache = CuratorCache.builder(client, path).build(); + cache.listenable().addListener((type, oldData, newData) -> { + try { + this.serviceData = parseData(client, path); + refreshConfigItems(); + } catch (Exception e) { + LOGGER.error("process event failed", e); + } + }); + cache.start(); + this.serviceData = parseData(client, path); + } + + private void addApplicationConfig(String env, CuratorFramework client) throws Exception { + String path = String.format(PATH_APPLICATION, env, BootStrapProperties.readApplication(environment)); + CuratorCache cache = CuratorCache.builder(client, path).build(); + cache.listenable().addListener((type, oldData, newData) -> { + try { + this.applicationData = parseData(client, path); + refreshConfigItems(); + } catch (Exception e) { + LOGGER.error("process event failed", e); + } + }); + cache.start(); + this.applicationData = parseData(client, path); + } + + private void addEnvironmentConfig(String env, CuratorFramework client) throws Exception { + String path = String.format(PATH_ENVIRONMENT, env); + CuratorCache cache = CuratorCache.builder(client, path).build(); + cache.listenable().addListener((type, oldData, newData) -> { + try { + this.environmentData = parseData(client, path); + refreshConfigItems(); + } catch (Exception e) { + LOGGER.error("process event failed", e); + } + }); + cache.start(); + this.environmentData = parseData(client, path); + } + + private Map parseData(CuratorFramework client, String path) throws Exception { + Map values = new HashMap<>(); + + if (client.checkExists().forPath(path) != null) { + client.getChildren().forPath(path).stream().sorted().forEach(item -> { + try { + byte[] data = client.getData().forPath(path + "/" + item); + if (item.endsWith(".yaml") || item.endsWith(".yml")) { + YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean(); + yamlFactory.setResources(new ByteArrayResource(data)); + values.putAll(toMap(yamlFactory.getObject())); + } else if (item.endsWith(".properties")) { + Properties properties = new Properties(); + properties.load(new StringReader(new String(data, StandardCharsets.UTF_8))); + values.putAll(toMap(properties)); + } else { + values.put(item, new String(data, StandardCharsets.UTF_8)); + } + } catch (Exception e) { + LOGGER.error("", e); + } + }); + } + + return values; + } + + private void refreshConfigItems() { + synchronized (lock) { + Map all = new HashMap<>(); + all.putAll(environmentData); + all.putAll(applicationData); + all.putAll(serviceData); + all.putAll(versionData); + all.putAll(tagData); + updateHandler.handle(all, allLast); + this.allLast = all; + } + } + + @SuppressWarnings("unchecked") + private Map toMap(Properties properties) { + if (properties == null) { + return Collections.emptyMap(); + } + Map result = new HashMap<>(); + Enumeration keys = (Enumeration) properties.propertyNames(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + Object value = properties.getProperty(key); + result.put(key, value); + } + return result; + } +} diff --git a/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperConfig.java b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperConfig.java new file mode 100644 index 00000000000..307ef7f0d55 --- /dev/null +++ b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperConfig.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.zookeeper; + +import org.springframework.core.env.Environment; + +public class ZookeeperConfig { + public static final String ZOOKEEPER_DEFAULT_ENVIRONMENT = "production"; + + public static final String PROPERTY_CONNECT_STRING = "servicecomb.config.zk.connect-string"; + + public static final String PROPERTY_SESSION_TIMEOUT = "servicecomb.config.zk.session-timeout-millis"; + + public static final String PROPERTY_CONNECTION_TIMEOUT = "servicecomb.config.zk.connection-timeout-mills"; + + public static final String PROPERTY_AUTH_SCHEMA = "servicecomb.config.zk.authentication-schema"; + + public static final String PROPERTY_AUTH_INFO = "servicecomb.config.zk.authentication-info"; + + public static final String PROPERTY_INSTANCE_TAG = "servicecomb.config.zk.instance-tag"; + + private final Environment environment; + + public ZookeeperConfig(Environment environment) { + this.environment = environment; + } + + public String getConnectString() { + return environment.getProperty(PROPERTY_CONNECT_STRING, "127.0.0.1:2181"); + } + + public int getSessionTimeoutMillis() { + return environment.getProperty(PROPERTY_SESSION_TIMEOUT, int.class, 60000); + } + + public int getConnectionTimeoutMillis() { + return environment.getProperty(PROPERTY_CONNECTION_TIMEOUT, int.class, 1000); + } + + public String getAuthSchema() { + return environment.getProperty(PROPERTY_AUTH_SCHEMA); + } + + public String getAuthInfo() { + return environment.getProperty(PROPERTY_AUTH_INFO); + } + + public String getInstanceTag() { + return environment.getProperty(PROPERTY_INSTANCE_TAG); + } +} diff --git a/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperDynamicPropertiesSource.java b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperDynamicPropertiesSource.java new file mode 100644 index 00000000000..a971ea5845f --- /dev/null +++ b/dynamic-config/config-zookeeper/src/main/java/org/apache/servicecomb/config/zookeeper/ZookeeperDynamicPropertiesSource.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.config.zookeeper; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.config.DynamicPropertiesSource; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; + +public class ZookeeperDynamicPropertiesSource implements DynamicPropertiesSource { + public static final String SOURCE_NAME = "zookeeper"; + + private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperDynamicPropertiesSource.class); + + private final Map valueCache = new ConcurrentHashMap<>(); + + public ZookeeperDynamicPropertiesSource() { + } + + private final UpdateHandler updateHandler = new UpdateHandler(); + + private void init(Environment environment) { + ZookeeperClient zookeeperClient = new ZookeeperClient(updateHandler, environment); + try { + zookeeperClient.refreshZookeeperConfig(); + } catch (Exception e) { + throw new IllegalStateException("Set up zookeeper config failed.", e); + } + } + + public class UpdateHandler { + public void handle(Map current, Map last) { + ConfigurationChangedEvent event = ConfigurationChangedEvent.createIncremental(current, last); + LOGGER.info("Dynamic configuration changed: {}", event.getChanged()); + valueCache.putAll(event.getAdded()); + valueCache.putAll(event.getUpdated()); + event.getDeleted().forEach((k, v) -> valueCache.remove(k)); + EventManager.post(event); + } + } + + @Override + public PropertySource create(Environment environment) { + init(environment); + return new MapPropertySource(SOURCE_NAME, valueCache); + } + + @Override + public int getOrder() { + return 0; + } +} diff --git a/dynamic-config/config-zookeeper/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource b/dynamic-config/config-zookeeper/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource new file mode 100644 index 00000000000..7b3b32c721d --- /dev/null +++ b/dynamic-config/config-zookeeper/src/main/resources/META-INF/services/org.apache.servicecomb.config.DynamicPropertiesSource @@ -0,0 +1,18 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.apache.servicecomb.config.zookeeper.ZookeeperDynamicPropertiesSource diff --git a/dynamic-config/pom.xml b/dynamic-config/pom.xml index fac881fc0fb..d5777cd654c 100644 --- a/dynamic-config/pom.xml +++ b/dynamic-config/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 @@ -36,6 +36,9 @@ config-cc config-nacos config-kie + config-zookeeper + config-etcd + config-consul diff --git a/edge/edge-core/pom.xml b/edge/edge-core/pom.xml index a194b334f82..0a75f8e33c6 100644 --- a/edge/edge-core/pom.xml +++ b/edge/edge-core/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb edge - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT edge-core Java Chassis::Edge::Core diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java index d771fa80a3b..0f7e59bb0d2 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/CommonHttpEdgeDispatcher.java @@ -20,6 +20,9 @@ import java.util.HashMap; import java.util.Map; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationLoader; +import org.apache.servicecomb.common.rest.route.Utils; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.Invocation; diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java index 08c33b2a8d5..56108a8cf1a 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/DefaultEdgeDispatcher.java @@ -18,6 +18,7 @@ package org.apache.servicecomb.edge.core; import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; +import org.apache.servicecomb.common.rest.route.Utils; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java index 5ba63b04a53..92594e6157a 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeAddHeaderFilter.java @@ -20,42 +20,34 @@ import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.function.BiConsumer; import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.transport.rest.client.RestClientTransportContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import com.google.common.eventbus.Subscribe; -public class EdgeAddHeaderFilter implements ConsumerFilter { - private static final Logger LOGGER = LoggerFactory.getLogger(EdgeAddHeaderFilter.class); - +public class EdgeAddHeaderFilter extends AbstractFilter implements EdgeFilter { public static final String NAME = "edge-add-headers"; private static final String PREFIX = "servicecomb.edge.filter.addHeader"; - private static final String KEY_ENABLED = PREFIX + ".enabled"; - private static final String KEY_HEADERS = PREFIX + ".allowedHeaders"; private final Environment environment; private List publicHeaders = new ArrayList<>(); - private boolean enabled = false; - public EdgeAddHeaderFilter(Environment environment) { this.environment = environment; init(); @@ -63,6 +55,7 @@ public EdgeAddHeaderFilter(Environment environment) { } @Subscribe + @SuppressWarnings("unused") public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { for (String changed : event.getChanged()) { if (changed.startsWith(PREFIX)) { @@ -73,12 +66,11 @@ public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { } private void init() { - enabled = environment.getProperty(KEY_ENABLED, boolean.class, false); - String publicHeaderStr = environment.getProperty(KEY_HEADERS, ""); - String[] split = publicHeaderStr.split(","); - if (split.length > 0) { - publicHeaders = Arrays.asList(split); + String publicHeaderStr = environment.getProperty(KEY_HEADERS); + if (StringUtils.isEmpty(publicHeaderStr)) { + return; } + publicHeaders = Arrays.asList(publicHeaderStr.split(",")); } @Override @@ -88,37 +80,29 @@ public String getName() { @Override public boolean enabledForTransport(String transport) { - return enabled; + return CoreConst.RESTFUL.equals(transport); } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { - return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1991; + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1995; } @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - RestClientTransportContext transportContext = invocation.getTransportContext(); - if (transportContext != null) { - return CompletableFuture.completedFuture(null) - .thenAccept(v -> addHeaders(invocation, transportContext.getHttpClientRequest()::putHeader)) - .thenCompose(v -> nextNode.onFilter(invocation)); - } - // normal consumer in edge process - return nextNode.onFilter(invocation); - } - - public void addHeaders(Invocation invocation, BiConsumer headerAdder) { - if (!invocation.isEdge()) { - return; + if (publicHeaders.isEmpty()) { + return nextNode.onFilter(invocation); } + RestClientTransportContext transportContext = invocation.getTransportContext(); HttpServletRequestEx oldRequest = invocation.getRequestEx(); publicHeaders.forEach(key -> { String value = oldRequest.getHeader(key); if (StringUtils.isNotEmpty(value)) { - headerAdder.accept(key, value); + transportContext.getHttpClientRequest().putHeader(key, value); } }); + + return nextNode.onFilter(invocation); } } diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeBootListener.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeBootListener.java index 538c83b8b12..144dc2c6224 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeBootListener.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeBootListener.java @@ -20,21 +20,8 @@ import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.core.executor.ExecutorManager; import org.apache.servicecomb.transport.rest.vertx.TransportConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; public class EdgeBootListener implements BootListener { - private static final Logger LOGGER = LoggerFactory.getLogger(EdgeBootListener.class); - - private Environment environment; - - @Autowired - public void setEnvironment(Environment environment) { - this.environment = environment; - } - @Override public void onBootEvent(BootEvent event) { if (!EventType.BEFORE_PRODUCER_PROVIDER.equals(event.getEventType())) { diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeCoreConfiguration.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeCoreConfiguration.java index 45333b7d04f..6fa0457c7fc 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeCoreConfiguration.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeCoreConfiguration.java @@ -23,17 +23,12 @@ @Configuration public class EdgeCoreConfiguration { @Bean - public EdgeBootListener edgeBootListener() { + public EdgeBootListener scbEdgeBootListener() { return new EdgeBootListener(); } @Bean - public EdgeServerCodecFilter edgeServerCodecFilter() { - return new EdgeServerCodecFilter(); - } - - @Bean - public EdgeAddHeaderFilter edgeAddHeaderFilter(Environment environment) { + public EdgeAddHeaderFilter scbEdgeAddHeaderFilter(Environment environment) { return new EdgeAddHeaderFilter(environment); } } diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java index 82e47283be1..309548a5b38 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeInvocationCreator.java @@ -21,6 +21,7 @@ import org.apache.servicecomb.common.rest.RestVertxProducerInvocationCreator; import org.apache.servicecomb.common.rest.locator.OperationLocator; import org.apache.servicecomb.common.rest.locator.ServicePathManager; +import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.invocation.InvocationFactory; @@ -44,7 +45,10 @@ public class EdgeInvocationCreator extends RestVertxProducerInvocationCreator { public EdgeInvocationCreator(RoutingContext routingContext, HttpServletRequestEx requestEx, HttpServletResponseEx responseEx, String microserviceName, String path) { - super(routingContext, null, null, requestEx, responseEx); + // Set endpoint before load balance because edge service will use RESTFUL transport filters. + super(routingContext, null, + SCBEngine.getInstance().getTransportManager().findTransport(CoreConst.RESTFUL).getEndpoint(), + requestEx, responseEx); this.microserviceName = microserviceName; this.path = path; @@ -70,11 +74,6 @@ protected OperationLocator locateOperation(ServicePathManager servicePathManager return servicePathManager.consumerLocateOperation(path, requestEx.getMethod()); } - @Override - protected void initInvocationContext(Invocation invocation) { - // do not read InvocationContext from HTTP header, for security reason - } - @Override protected Invocation createInstance() { ReferenceConfig referenceConfig = microserviceReferenceConfig @@ -85,7 +84,8 @@ protected Invocation createInstance() { restOperationMeta.getOperationMeta().buildBaseConsumerRuntimeType(), null); invocation.setSync(false); - invocation.setEdge(true); + invocation.setEdge(); + invocation.setEndpoint(endpoint); // ensure transport name is correct invocation.addLocalContext(EDGE_INVOCATION_CONTEXT, Vertx.currentContext()); return invocation; diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeServerCodecFilter.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeServerCodecFilter.java deleted file mode 100644 index cef9257b3cb..00000000000 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/EdgeServerCodecFilter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.edge.core; - -import java.util.concurrent.CompletableFuture; - -import org.apache.servicecomb.common.rest.filter.inner.RestServerCodecFilter; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.Filter; -import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.context.TransportContext; - -public class EdgeServerCodecFilter extends RestServerCodecFilter { - public static final String NAME = "edge-server-codec"; - - @Override - public String getName() { - return NAME; - } - - @Override - public boolean enabledForInvocationType(InvocationType invocationType) { - return invocationType == InvocationType.CONSUMER; - } - - @Override - public boolean enabledForTransport(String transport) { - // For edge service, this filter executed before load balancer and transport is always null. - return true; - } - - @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { - return Filter.CONSUMER_LOAD_BALANCE_ORDER - 2000; - } - - @Override - public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - if (invocation.getRequestEx() == null) { - // to support normal consumer invocation in edge process - return nextNode.onFilter(invocation); - } - - return super.onFilter(invocation, nextNode); - } - - // save and restore transportContext to support edge invocation - @Override - protected CompletableFuture invokeNext(Invocation invocation, FilterNode nextNode) { - TransportContext transportContext = invocation.getTransportContext(); - return nextNode.onFilter(invocation) - .whenComplete((r, e) -> invocation.setTransportContext(transportContext)); - } -} diff --git a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java index 195513714b5..d4e456c098f 100644 --- a/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java +++ b/edge/edge-core/src/main/java/org/apache/servicecomb/edge/core/URLMappedEdgeDispatcher.java @@ -21,16 +21,18 @@ import java.util.Map; import org.apache.servicecomb.common.rest.RestProducerInvocationFlow; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationLoader; +import org.apache.servicecomb.common.rest.route.Utils; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.invocation.InvocationCreator; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; import org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest; import org.apache.servicecomb.foundation.vertx.http.VertxServerResponseToHttpServletResponse; import org.apache.servicecomb.transport.rest.vertx.RestBodyHandler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; @@ -45,8 +47,6 @@ * Provide a URL mapping based dispatcher. Users configure witch URL patterns dispatch to a target service. */ public class URLMappedEdgeDispatcher extends AbstractEdgeDispatcher { - private static final Logger LOG = LoggerFactory.getLogger(URLMappedEdgeDispatcher.class); - public static final String CONFIGURATION_ITEM = "URLMappedConfigurationItem"; private static final String PATTERN_ANY = "/(.*)"; @@ -64,6 +64,7 @@ public class URLMappedEdgeDispatcher extends AbstractEdgeDispatcher { private Environment environment; public URLMappedEdgeDispatcher() { + EventManager.register(this); } // though this is an SPI, but add as beans. diff --git a/edge/edge-core/src/main/resources/META-INF/microservice.yaml b/edge/edge-core/src/main/resources/META-INF/microservice.yaml new file mode 100644 index 00000000000..a456c2868b2 --- /dev/null +++ b/edge/edge-core/src/main/resources/META-INF/microservice.yaml @@ -0,0 +1,23 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +servicecomb-config-order: -500 + +servicecomb: + context: + # do not decode invocation context for edge service by default + decodeInvocationContext: false diff --git a/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher b/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher index 0f1d5d6dea7..6a672cefffd 100644 --- a/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher +++ b/edge/edge-core/src/main/resources/META-INF/services/org.apache.servicecomb.transport.rest.vertx.VertxHttpDispatcher @@ -17,4 +17,4 @@ org.apache.servicecomb.edge.core.DefaultEdgeDispatcher org.apache.servicecomb.edge.core.URLMappedEdgeDispatcher -org.apache.servicecomb.edge.core.CommonHttpEdgeDispatcher \ No newline at end of file +org.apache.servicecomb.edge.core.CommonHttpEdgeDispatcher diff --git a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java index 2e23ab673e2..046adfaa3a2 100644 --- a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java +++ b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestURLMappedEdgeDispatcher.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.Map; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.transport.rest.vertx.RestBodyHandler; import org.junit.jupiter.api.AfterEach; diff --git a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestUtils.java b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestUtils.java index f7fcfa18c8e..38e8fa837d5 100644 --- a/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestUtils.java +++ b/edge/edge-core/src/test/java/org/apache/servicecomb/edge/core/TestUtils.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.edge.core; +import org.apache.servicecomb.common.rest.route.Utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/edge/pom.xml b/edge/pom.xml index 1ef9644f852..e11f4e088b6 100644 --- a/edge/pom.xml +++ b/edge/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default edge diff --git a/foundations/foundation-common/pom.xml b/foundations/foundation-common/pom.xml index 709915470ae..2f5a18878b4 100644 --- a/foundations/foundation-common/pom.xml +++ b/foundations/foundation-common/pom.xml @@ -20,7 +20,7 @@ 4.0.0 org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundations foundation-common @@ -54,6 +54,12 @@ org.apache.httpcomponents httpclient + + + commons-logging + commons-logging + + jakarta.ws.rs @@ -99,7 +105,7 @@ io.vertx vertx-core - + org.apache.logging.log4j log4j-slf4j-impl diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/FoundationCommonAuthConfiguration.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/FoundationCommonAuthConfiguration.java index 19ea1e752e3..7faea74a3b5 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/FoundationCommonAuthConfiguration.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/auth/FoundationCommonAuthConfiguration.java @@ -20,7 +20,7 @@ @Configuration public class FoundationCommonAuthConfiguration { - public ShaAKSKCipher shaAKSKCipher() { + public ShaAKSKCipher scbShaAKSKCipher() { return new ShaAKSKCipher(); } } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/NamedThreadFactory.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/NamedThreadFactory.java index c6be9c1e0db..3b79c9c8e1a 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/NamedThreadFactory.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/NamedThreadFactory.java @@ -43,7 +43,9 @@ public NamedThreadFactory(String prefix) { */ @Override public Thread newThread(Runnable r) { - return new Thread(r, prefix + "-" + threadNumber.getAndIncrement()); + Thread thread = new Thread(r, prefix + "-" + threadNumber.getAndIncrement()); + thread.setDaemon(true); + return thread; } public String getPrefix() { diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/ParameterizedTypeUtil.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/ParameterizedTypeUtil.java new file mode 100644 index 00000000000..025c08acbf3 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/ParameterizedTypeUtil.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.common; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * Utility class for creating ParameterizedType. + */ +public class ParameterizedTypeUtil implements ParameterizedType { + private final Type[] actualTypeArguments; + + private final Class rawType; + + private ParameterizedTypeUtil(Class rawType, Type[] actualTypeArguments) { + this.actualTypeArguments = actualTypeArguments; + this.rawType = rawType; + } + + public static ParameterizedType make(Class rawType, Type... actualTypeArguments) { + return new ParameterizedTypeUtil(rawType, actualTypeArguments); + } + + @Override + public Type[] getActualTypeArguments() { + return this.actualTypeArguments; + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return null; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ParameterizedType that) { + + if (this == that) { + return true; + } + + Type thatRawType = that.getRawType(); + + return Objects.equals(rawType, thatRawType) && + Arrays.equals(actualTypeArguments, that.getActualTypeArguments()); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Arrays.hashCode(actualTypeArguments) ^ + Objects.hashCode(rawType); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append(rawType.getName()); + + if (actualTypeArguments != null) { + StringJoiner sj = new StringJoiner(", ", "<", ">"); + sj.setEmptyValue(""); + for (Type t : actualTypeArguments) { + sj.add(t.getTypeName()); + } + sb.append(sj); + } + + return sb.toString(); + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/ServiceCombConstants.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/ServiceCombConstants.java index 8cf6db4cd42..aedae7c1a86 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/ServiceCombConstants.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/base/ServiceCombConstants.java @@ -30,7 +30,7 @@ public interface ServiceCombConstants { String DEFAULT_TRACING_COLLECTOR_ADDRESS = "http://127.0.0.1:9411"; - String CONFIG_KEY_SPLITER = "_"; + String CONFIG_KEY_SPLITTER = "_"; String CONFIG_FRAMEWORK_DEFAULT_NAME = "servicecomb-java-chassis"; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/cache/VersionedCache.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/cache/VersionedCache.java index 6580174861e..b30e13862b4 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/cache/VersionedCache.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/cache/VersionedCache.java @@ -30,7 +30,7 @@ interface IsEmpty { boolean isEmpty(); } - protected int cacheVersion; + protected volatile int cacheVersion; // an optional name protected String name; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/PaaSResourceUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/PaaSResourceUtils.java index 77270ec2d07..fe11a3fe763 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/PaaSResourceUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/PaaSResourceUtils.java @@ -118,7 +118,7 @@ public static List getSortedResources(String locationPattern, String s return resList; } - public static List getSortedPorperties(String locationPattern) { + public static List getSortedProperties(String locationPattern) { return getSortedResources(locationPattern, PROPERTIES_SUFFIX); } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PropertiesLoader.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PropertiesLoader.java index 4f8827a6748..ae4500e8aef 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PropertiesLoader.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/config/impl/PropertiesLoader.java @@ -40,7 +40,7 @@ public List getFoundResList() { public T load() throws Exception { Properties props = new Properties(); for (String locationPattern : locationPatternList) { - List resList = PaaSResourceUtils.getSortedPorperties(locationPattern); + List resList = PaaSResourceUtils.getSortedProperties(locationPattern); foundResList.addAll(resList); PaaSPropertiesLoaderUtils.fillAllProperties(props, resList); } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java index aad5e69fbdf..50b474050d9 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/event/EventManager.java @@ -56,7 +56,7 @@ public static void unregister(Object listener) { } @Bean - public EventBus eventBus() { + public EventBus scbEventBus() { return eventBus; } } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/IpPort.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/IpPort.java index d8515eea13a..66aae3ebca7 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/IpPort.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/IpPort.java @@ -22,9 +22,9 @@ import com.google.common.base.Objects; public class IpPort { - private String hostOrIp; + private volatile String hostOrIp; - private int port; + private volatile int port; private volatile InetSocketAddress socketAddress; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java index 34d2d413549..fd8cc1f7a29 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/NetUtils.java @@ -262,7 +262,7 @@ public static IpPort parseIpPort(String scheme, String authority) { /** * 对于配置为0.0.0.0的地址,let it go * schema, e.g. http - * adddress, e.g 0.0.0.0:8080 + * address, e.g 0.0.0.0:8080 * return 实际监听的地址 */ public static String getRealListenAddress(String schema, String address) { diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/URIEndpointObject.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/URIEndpointObject.java index 03b0ffc9647..78b633c1183 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/URIEndpointObject.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/net/URIEndpointObject.java @@ -35,24 +35,32 @@ public class URIEndpointObject extends IpPort { private static final String PROTOCOL_KEY = "protocol"; + private static final String WEBSOCKET_ENABLED_KEY = "websocketEnabled"; + private static final String HTTP2 = "http2"; private final boolean sslEnabled; private boolean http2Enabled; - private final Map> querys; + private boolean websocketEnabled; + + private final Map> queries; + + private final String schema; public URIEndpointObject(String endpoint) { URI uri = URI.create(endpoint); + schema = uri.getScheme(); setHostOrIp(uri.getHost()); if (uri.getPort() < 0) { // do not use default port throw new IllegalArgumentException("port not specified."); } setPort(uri.getPort()); - querys = splitQuery(uri); + queries = splitQuery(uri); sslEnabled = Boolean.parseBoolean(getFirst(SSL_ENABLED_KEY)); + websocketEnabled = Boolean.parseBoolean(getFirst(WEBSOCKET_ENABLED_KEY)); String httpVersion = getFirst(PROTOCOL_KEY); if (HTTP2.equals(httpVersion)) { http2Enabled = true; @@ -73,16 +81,24 @@ public boolean isSslEnabled() { return sslEnabled; } + public boolean isWebsocketEnabled() { + return websocketEnabled; + } + public boolean isHttp2Enabled() { return http2Enabled; } + public String getSchema() { + return this.schema; + } + public List getQuery(String key) { - return querys.get(key); + return queries.get(key); } public String getFirst(String key) { - List values = querys.get(key); + List values = queries.get(key); // it's impossible that values is not null and size is 0 if (values == null) { return null; diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePart.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePart.java index d44c40b92d5..1bd9ddfd952 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePart.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/part/FilePart.java @@ -18,9 +18,9 @@ package org.apache.servicecomb.foundation.common.part; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import org.apache.commons.io.FileUtils; @@ -41,7 +41,7 @@ public FilePart(String name, File file) { @Override public InputStream getInputStream() throws IOException { - return new FileInputStream(file); + return Files.newInputStream(file.toPath()); } @Override diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ConditionWaiter.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ConditionWaiter.java new file mode 100644 index 00000000000..4641ca3d233 --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/ConditionWaiter.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.common.utils; + +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +public class ConditionWaiter { + private final AtomicReference dataReference; + + private final AtomicBoolean isComplete; + + private final long sleepDuration; + + private final TimeUnit timeUnit; + + private final ExecutorService executorService; + + public ConditionWaiter(T initialData, long sleepDuration, TimeUnit timeUnit) { + this.dataReference = new AtomicReference<>(initialData); + this.isComplete = new AtomicBoolean(false); + this.sleepDuration = sleepDuration; + this.timeUnit = timeUnit; + this.executorService = Executors.newSingleThreadExecutor(); + } + + public T waitForCompletion() { + while (!isComplete.get()) { + SleepUtil.sleep(sleepDuration, timeUnit); + } + return dataReference.get(); + } + + public void setData(T newData) { + dataReference.set(newData); + } + + public void executeTaskAsync(Callable task) { + CompletableFuture.supplyAsync(() -> { + try { + return task.call(); + } catch (Exception e) { + throw new RuntimeException("Task execution failed", e); + } + }, executorService).thenAccept(result -> { + setData(result); + isComplete.set(true); + }); + } + + public static class SleepUtil { + public static void sleep(long duration, TimeUnit timeUnit) { + try { + timeUnit.sleep(duration); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + System.out.println("Thread was interrupted during sleep!"); + } + } + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/GenericsUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/GenericsUtils.java index 0429b82da70..d872236082e 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/GenericsUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/GenericsUtils.java @@ -24,14 +24,14 @@ public final class GenericsUtils { /** * check if XXX.class is generic type. see TestGenericsUtils for details meaning. * This method is provided for API compatibility for RestTemplate. Following code: - * - * + *

+ *{@code * List>> response = consumers.getSCBRestTemplate() * postForObject("/testListObjectParam", request, List.class); - * + * } * * should work for versions of 1.*. This is because java-chassis can read type info from swaggers. - * + *

* Start from 2.x, the best practise to write this code is to use ParameterizedTypeReference provided by RestTemplate * exchange method. */ diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairUtils.java index d77067df88f..91fed2ce1c8 100644 --- a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairUtils.java +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/KeyPairUtils.java @@ -72,7 +72,7 @@ public static KeyPairEntry generateALGKeyPair() { PrivateKey privKey = keyPair.getPrivate(); return new KeyPairEntry(privKey, pubKey, encoder.encodeToString(pubKey.getEncoded())); } catch (NoSuchAlgorithmException e) { - LOGGER.error("generate rsa keypair faild"); + LOGGER.error("generate rsa keypair failed"); throw new IllegalStateException("perhaps error occurred on jre"); } } diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaUtils.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaUtils.java new file mode 100644 index 00000000000..09265d57e2e --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/LambdaUtils.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.common.utils; + +import java.util.function.Consumer; + +public class LambdaUtils { + + public static Consumer ignoreException(Consumer consumer) { + return item -> { + try { + consumer.accept(item); + } catch (Throwable e) { + } + }; + } +} diff --git a/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MuteExceptionUtil.java b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MuteExceptionUtil.java new file mode 100644 index 00000000000..334ae13808f --- /dev/null +++ b/foundations/foundation-common/src/main/java/org/apache/servicecomb/foundation/common/utils/MuteExceptionUtil.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.foundation.common.utils; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MuteExceptionUtil { + + public interface FunctionWithException { + R apply(T t) throws Exception; + } + + public interface FunctionWithDoubleParam { + R apply(T1 t1, T2 t2) throws Exception; + } + + private static final Logger LOGGER = LoggerFactory.getLogger(MuteExceptionUtil.class); + + public static class MuteExceptionUtilBuilder { + + private String logMessage; + + private Object[] customMessageParams; + + public MuteExceptionUtilBuilder withLog(String message, Object... params) { + this.logMessage = message; + this.customMessageParams = params; + return this; + } + + private String getLogMessage(String defaultMessage) { + return logMessage != null ? logMessage : defaultMessage; + } + + public R executeFunction(FunctionWithException function, T t) { + try { + return function.apply(t); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute Function failure..."), customMessageParams, e); + return null; + } + } + + public T executeSupplier(Supplier supplier) { + try { + return supplier.get(); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute Supplier failure..."), customMessageParams, e); + return null; + } + } + + public T executeCompletableFuture(CompletableFuture completableFuture) { + try { + return completableFuture.get(); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute CompletableFuture failure..."), customMessageParams, e); + return null; + } + } + + public R executeFunctionWithDoubleParam(FunctionWithDoubleParam function, T1 t1, T2 t2) { + try { + return function.apply(t1, t2); + } catch (Exception e) { + LOGGER.error(getLogMessage("execute FunctionWithDoubleParam failure..."), customMessageParams, e); + return null; + } + } + } + + public static MuteExceptionUtilBuilder builder() { + return new MuteExceptionUtilBuilder(); + } +} diff --git a/foundations/foundation-common/src/main/resources/META-INF/mime.types b/foundations/foundation-common/src/main/resources/META-INF/mime.types index 3e85536f857..eec38ecd549 100644 --- a/foundations/foundation-common/src/main/resources/META-INF/mime.types +++ b/foundations/foundation-common/src/main/resources/META-INF/mime.types @@ -154,4 +154,3 @@ application/x-x509-ca-cert cacert application/x-x509-server-cert scert application/x-x509-user-cert ucert application/x-x509-email-cert ecert - diff --git a/foundations/foundation-common/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.common.encrypt.Encryption b/foundations/foundation-common/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.common.encrypt.Encryption index 0e40feb3d0f..f954930aa71 100644 --- a/foundations/foundation-common/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.common.encrypt.Encryption +++ b/foundations/foundation-common/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.common.encrypt.Encryption @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.foundation.common.encrypt.NoEncryption \ No newline at end of file +org.apache.servicecomb.foundation.common.encrypt.NoEncryption diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventManager.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventManager.java index 6b6b1caac80..736a933159b 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventManager.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/event/TestEventManager.java @@ -48,7 +48,7 @@ public void normalListener() { test(this); Assertions.assertTrue(collector.getEvents().isEmpty()); // ensure no warning logs - collector.teardown(); + collector.tearDown(); } @Test @@ -72,7 +72,7 @@ void onInt(Integer obj) { Assertions.assertTrue(true); } - collector.teardown(); + collector.tearDown(); } @Test @@ -98,7 +98,7 @@ public void onInt(Integer obj) { // ensure logs: "LOGGER.warn("Failed to create lambda for method: {}, fallback to reflect.", method, throwable);" Assertions.assertFalse(collector.getEvents().isEmpty()); - collector.teardown(); + collector.tearDown(); } @Subscribe @@ -111,4 +111,3 @@ public void onInt(Integer obj) { iCount++; } } - diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestURIEndpointObject.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestURIEndpointObject.java index 7651a0d3965..dc3323fecc3 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestURIEndpointObject.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/net/TestURIEndpointObject.java @@ -65,8 +65,8 @@ public void testQueryChineseAndSpaceAndEmpty() throws UnsupportedEncodingExcepti "cse://1.1.1.1:1234/abc?a=1&b=&country=" + URLEncoder.encode("中 国", StandardCharsets.UTF_8.name()); URIEndpointObject ep = new URIEndpointObject(strUri); - Map> querys = Deencapsulation.getField(ep, "querys"); - Assertions.assertEquals(3, querys.size()); + Map> queries = Deencapsulation.getField(ep, "queries"); + Assertions.assertEquals(3, queries.size()); Assertions.assertEquals(1, ep.getQuery("a").size()); Assertions.assertEquals("1", ep.getFirst("a")); diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFileNameTooLong.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFileNameTooLong.java index 7ad60568d2b..0ccf049ffba 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFileNameTooLong.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestFileNameTooLong.java @@ -55,15 +55,15 @@ public void assertFileNotTooLong() { Assertions.assertTrue(names.isEmpty()); } - private static void findLongFileName(File folder, List holder, int baseLenght) { + private static void findLongFileName(File folder, List holder, int baseLength) { if (folder.isFile()) { - if (folder.getAbsolutePath().length() >= MAN_FILE_SIZE + baseLenght) { + if (folder.getAbsolutePath().length() >= MAN_FILE_SIZE + baseLength) { holder.add(folder.getAbsolutePath()); } } else if (folder.isDirectory() && !"target".equals(folder.getName())) { File[] children = folder.listFiles(); for (File child : children) { - findLongFileName(child, holder, baseLenght); + findLongFileName(child, holder, baseLength); } } else { return; diff --git a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java index 89da7bd52b3..10054efe4e8 100644 --- a/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java +++ b/foundations/foundation-common/src/test/java/org/apache/servicecomb/foundation/common/utils/TestJvmUtils.java @@ -56,7 +56,7 @@ public void findMainClass_invalid() { Assertions.assertNull(JvmUtils.findMainClass()); Assertions.assertEquals("\"invalidCls\" is not a valid class.", logCollector.getEvents().get(0).getMessage()); - logCollector.teardown(); + logCollector.tearDown(); } @Test diff --git a/foundations/foundation-config/pom.xml b/foundations/foundation-config/pom.xml index 465bda3602f..12c4a54c5be 100644 --- a/foundations/foundation-config/pom.xml +++ b/foundations/foundation-config/pom.xml @@ -21,7 +21,7 @@ foundations org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -74,6 +74,12 @@ org.apache.httpcomponents httpclient + + + commons-logging + commons-logging + + io.netty diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigEnvironmentPostProcessor.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigEnvironmentPostProcessor.java index 0edb84541bc..8462da9057d 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigEnvironmentPostProcessor.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/ConfigEnvironmentPostProcessor.java @@ -23,9 +23,8 @@ import org.apache.servicecomb.config.file.MicroserviceConfigLoader; import org.apache.servicecomb.foundation.bootstrap.BootStrapService; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.context.annotation.Conditional; @@ -39,14 +38,13 @@ * Initialize configuration. */ public class ConfigEnvironmentPostProcessor implements EnvironmentPostProcessor { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigEnvironmentPostProcessor.class); - public static final String MICROSERVICE_PROPERTY_SOURCE_NAME = "microservice.yaml"; public static final String MAPPING_PROPERTY_SOURCE_NAME = "mapping.yaml"; @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + LegacyPropertyFactory.setEnvironment(environment); addMicroserviceDefinitions(environment); startupBootStrapService(environment); addDynamicConfigurationToSpring(environment); @@ -133,14 +131,11 @@ private void addDynamicConfigurationToSpring(Environment environment) { if (!(environment instanceof ConfigurableEnvironment)) { return; } - try { - for (DynamicPropertiesSource dynamicPropertiesSource : - SPIServiceUtils.getOrLoadSortedService(DynamicPropertiesSource.class)) { - ((ConfigurableEnvironment) environment).getPropertySources() - .addFirst(dynamicPropertiesSource.create(environment)); - } - } catch (Exception e) { - LOGGER.warn("set up dynamic property source to spring failed.", e); + + for (DynamicPropertiesSource dynamicPropertiesSource : + SPIServiceUtils.getOrLoadSortedService(DynamicPropertiesSource.class)) { + ((ConfigurableEnvironment) environment).getPropertySources() + .addFirst(dynamicPropertiesSource.create(environment)); } } } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesImpl.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesImpl.java index 84f623d74d9..323e84d80aa 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesImpl.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesImpl.java @@ -17,11 +17,11 @@ package org.apache.servicecomb.config; -import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.DoubleConsumer; import java.util.function.IntConsumer; @@ -44,17 +44,17 @@ private static class Holder { } } - private final Map, String>>> stringCallbacks = new HashMap<>(); + private final Map, String>>> stringCallbacks = new ConcurrentHashMap<>(); - private final Map>> intCallbacks = new HashMap<>(); + private final Map>> intCallbacks = new ConcurrentHashMap<>(); - private final Map>> longCallbacks = new HashMap<>(); + private final Map>> longCallbacks = new ConcurrentHashMap<>(); - private final Map>> floatCallbacks = new HashMap<>(); + private final Map>> floatCallbacks = new ConcurrentHashMap<>(); - private final Map>> doubleCallbacks = new HashMap<>(); + private final Map>> doubleCallbacks = new ConcurrentHashMap<>(); - private final Map, Boolean>>> booleanCallbacks = new HashMap<>(); + private final Map, Boolean>>> booleanCallbacks = new ConcurrentHashMap<>(); private final Environment environment; diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesSource.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesSource.java index 87af7f66f28..8634bff29f3 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesSource.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/DynamicPropertiesSource.java @@ -18,11 +18,11 @@ import org.springframework.core.Ordered; import org.springframework.core.env.Environment; -import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; /** * SPI to add new DynamicPropertiesSource. */ public interface DynamicPropertiesSource extends Ordered { - MapPropertySource create(Environment environment); + PropertySource create(Environment environment); } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.java index 9cb7c231a25..1f74e897dd6 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/FoundationConfigConfiguration.java @@ -20,53 +20,50 @@ import org.apache.servicecomb.config.priority.ConfigObjectFactory; import org.apache.servicecomb.config.priority.PriorityPropertyFactory; import org.apache.servicecomb.config.priority.PriorityPropertyManager; -import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.core.env.Environment; @Configuration @SuppressWarnings("unused") public class FoundationConfigConfiguration { @Bean - public InjectBeanPostProcessor injectBeanPostProcessor(PriorityPropertyManager priorityPropertyManager) { + public static InjectBeanPostProcessor scbInjectBeanPostProcessor( + @Autowired @Lazy PriorityPropertyManager priorityPropertyManager) { return new InjectBeanPostProcessor(priorityPropertyManager); } @Bean - public PriorityPropertyManager priorityPropertyManager(ConfigObjectFactory configObjectFactory) { + public PriorityPropertyManager scbPriorityPropertyManager(ConfigObjectFactory configObjectFactory) { return new PriorityPropertyManager(configObjectFactory); } @Bean - public PriorityPropertyFactory priorityPropertyFactory(Environment environment) { + public PriorityPropertyFactory scbPriorityPropertyFactory(Environment environment) { return new PriorityPropertyFactory(environment); } @Bean - public DynamicPropertiesImpl dynamicProperties(Environment environment) { + public DynamicPropertiesImpl scbDynamicProperties(Environment environment) { return new DynamicPropertiesImpl(environment); } @Bean - public ConfigObjectFactory configObjectFactory(PriorityPropertyFactory propertyFactory) { + public ConfigObjectFactory scbConfigObjectFactory(PriorityPropertyFactory propertyFactory) { return new ConfigObjectFactory(propertyFactory); } @Bean @ConfigurationProperties(prefix = DataCenterProperties.PREFIX) - public DataCenterProperties dataCenterProperties() { + public DataCenterProperties scbDataCenterProperties() { return new DataCenterProperties(); } @Bean - public LegacyPropertyFactory legacyPropertyFactory(Environment environment) { - return new LegacyPropertyFactory(environment); - } - - @Bean - public InMemoryDynamicPropertiesSource inMemoryDynamicPropertiesSource() { + public InMemoryDynamicPropertiesSource scbInMemoryDynamicPropertiesSource() { return new InMemoryDynamicPropertiesSource(); } } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/InMemoryDynamicPropertiesSource.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/InMemoryDynamicPropertiesSource.java index 8ce73d7274b..f6139af0a24 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/InMemoryDynamicPropertiesSource.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/InMemoryDynamicPropertiesSource.java @@ -22,6 +22,7 @@ import org.apache.servicecomb.foundation.common.event.EventManager; import org.springframework.core.env.Environment; import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.PropertySource; /** * This DynamicPropertiesSource is created for easier system tests. @@ -32,7 +33,7 @@ public class InMemoryDynamicPropertiesSource implements DynamicPropertiesSource private static final Map DYNAMIC = new HashMap<>(); @Override - public MapPropertySource create(Environment environment) { + public PropertySource create(Environment environment) { return new MapPropertySource(SOURCE_NAME, DYNAMIC); } diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java index a83dd03eab1..2dc116a304b 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/YAMLUtil.java @@ -98,13 +98,13 @@ public static T parserObject(String yamlContent, Class clazz) { } @SuppressWarnings("unchecked") - public static Map retrieveItems(String prefix, Map propertieMap) { + public static Map retrieveItems(String prefix, Map propertiesMap) { Map result = new LinkedHashMap<>(); if (!prefix.isEmpty()) { prefix += "."; } - for (Map.Entry entry : propertieMap.entrySet()) { + for (Map.Entry entry : propertiesMap.entrySet()) { if (entry.getValue() instanceof Map) { result.putAll(retrieveItems(prefix + entry.getKey(), (Map) entry.getValue())); } else { diff --git a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityProperty.java b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityProperty.java index 7ebbcfd8daf..c1c30896159 100644 --- a/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityProperty.java +++ b/foundations/foundation-config/src/main/java/org/apache/servicecomb/config/priority/PriorityProperty.java @@ -30,7 +30,7 @@ * or register to PriorityPropertyManager manually * @param */ -public class PriorityProperty { +public final class PriorityProperty { private static final Logger LOGGER = LoggerFactory.getLogger(PriorityProperty.class); private final PriorityPropertyType propertyType; @@ -89,32 +89,32 @@ private Function collectReader(Type type) { } @SuppressWarnings("unchecked") - protected T readInt(DynamicProperty property) { + private T readInt(DynamicProperty property) { return (T) property.getInteger(); } @SuppressWarnings("unchecked") - protected T readLong(DynamicProperty property) { + private T readLong(DynamicProperty property) { return (T) property.getLong(); } @SuppressWarnings("unchecked") - protected T readString(DynamicProperty property) { + private T readString(DynamicProperty property) { return (T) property.getString(); } @SuppressWarnings("unchecked") - protected T readBoolean(DynamicProperty property) { + private T readBoolean(DynamicProperty property) { return (T) property.getBoolean(); } @SuppressWarnings("unchecked") - protected T readDouble(DynamicProperty property) { + private T readDouble(DynamicProperty property) { return (T) property.getDouble(); } @SuppressWarnings("unchecked") - protected T readFloat(DynamicProperty property) { + private T readFloat(DynamicProperty property) { return (T) property.getFloat(); } diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/BootStrapPropertiesTest.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/BootStrapPropertiesTest.java index 996cbcdb8a1..38a8a4a8b5d 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/BootStrapPropertiesTest.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/BootStrapPropertiesTest.java @@ -20,15 +20,12 @@ import static org.apache.servicecomb.foundation.test.scaffolding.AssertUtils.assertPrettyJson; import static org.assertj.core.api.Assertions.assertThat; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; @@ -37,14 +34,12 @@ private Map readInstanceProperties(String yaml) { Map properties = YAMLUtil.yaml2Properties(yaml); ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); MutablePropertySources mutablePropertySources = new MutablePropertySources(); - EnumerablePropertySource propertySource = new MapPropertySource("yaml", properties); + MapPropertySource propertySource = new MapPropertySource("yaml", properties); mutablePropertySources.addLast(propertySource); Mockito.when(environment.getPropertySources()).thenReturn(mutablePropertySources); - List keys = new ArrayList<>(); for (Entry entry : properties.entrySet()) { Mockito.when(environment.getProperty(entry.getKey())).thenReturn(entry.getValue().toString()); - keys.add(entry.getKey()); } return BootStrapProperties.readServiceInstanceProperties(environment); @@ -59,62 +54,62 @@ void should_be_empty_when_old_and_new_key_not_exists() { @Test void should_read_boolean_to_string() { - Map properties = readInstanceProperties("" - + "servicecomb:\n" - + " instance:\n" - + " properties:\n" - + " k: true"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k\" : \"true\"\n" - + "}"); + Map properties = readInstanceProperties(""" + servicecomb: + instance: + properties: + k: true"""); + + assertPrettyJson(properties).isEqualTo(""" + { + "k" : "true" + }"""); } @Test void should_read_number_to_string() { - Map properties = readInstanceProperties("" - + "servicecomb:\n" - + " instance:\n" - + " properties:\n" - + " k: 1"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k\" : \"1\"\n" - + "}"); + Map properties = readInstanceProperties(""" + servicecomb: + instance: + properties: + k: 1"""); + + assertPrettyJson(properties).isEqualTo(""" + { + "k" : "1" + }"""); } @Test void should_read_by_old_prefix_when_new_prefix_not_exists() { - Map properties = readInstanceProperties("" - + "instance_description:\n" - + " properties:\n" - + " k: v\n" - + " k1: v1"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k1\" : \"v1\",\n" - + " \"k\" : \"v\"\n" - + "}"); + Map properties = readInstanceProperties(""" + instance_description: + properties: + k: v + k1: v1"""); + + assertPrettyJson(properties).isEqualTo(""" + { + "k1" : "v1", + "k" : "v" + }"""); } @Test void should_ignore_old_prefix_when_new_prefix_exists() { - Map properties = readInstanceProperties("" - + "instance_description:\n" - + " properties:\n" - + " k: v\n" - + " k1: v1\n" - + "servicecomb:\n" - + " instance:\n" - + " properties:\n" - + " k: new"); - - assertPrettyJson(properties).isEqualTo("" - + "{\n" - + " \"k\" : \"new\"\n" - + "}"); + Map properties = readInstanceProperties(""" + instance_description: + properties: + k: v + k1: v1 + servicecomb: + instance: + properties: + k: new"""); + + assertPrettyJson(properties).isEqualTo(""" + { + "k" : "new" + }"""); } } diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java index 6694ace8644..4abafe8765e 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/TestYAMLUtil.java @@ -51,19 +51,6 @@ public void setName(String name) { public void testSafeParser() { Person person = YAMLUtil.parserObject("name: hello", Person.class); Assertions.assertEquals("hello", person.getName()); - - person = YAMLUtil.parserObject("!!org.apache.servicecomb.config.TestYAMLUtil$Person\n" - + "name: hello", Person.class); - Assertions.assertEquals("hello", person.getName()); - - person = YAMLUtil.parserObject("!!org.apache.servicecomb.config.TestYAMLUtil$UnsafePerson\n" - + "name: hello", Person.class); - Assertions.assertEquals("hello", person.getName()); - - // using Object.class is not safe, do not used in product code. - Object object = YAMLUtil.parserObject("!!org.apache.servicecomb.config.TestYAMLUtil$UnsafePerson\n" - + "name: hello", Object.class); - Assertions.assertEquals("hello", ((UnsafePerson) object).getName()); } @Test diff --git a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestPlaceholderResolver.java b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestPlaceholderResolver.java index 2f7020628f4..d6407785f65 100644 --- a/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestPlaceholderResolver.java +++ b/foundations/foundation-config/src/test/java/org/apache/servicecomb/config/inject/TestPlaceholderResolver.java @@ -20,18 +20,22 @@ import java.util.HashMap; import java.util.Map; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; public class TestPlaceholderResolver { static Map parameters = new HashMap<>(); static PlaceholderResolver resolver = new PlaceholderResolver(); - @BeforeClass - public static void setup() { + @BeforeAll + public static void setupClass() { parameters.put("key", "value"); parameters.put("varOfVar", Arrays.asList("${key}")); @@ -42,6 +46,14 @@ public static void setup() { parameters.put("high-list", Arrays.asList("high-1", "high-2")); } + protected Environment environment; + + @BeforeEach + public void setup() { + environment = Mockito.mock(Environment.class); + LegacyPropertyFactory.setEnvironment(environment); + } + @Test public void unknown() { MatcherAssert.assertThat(resolver.replace("prefix${xxx}suffix", parameters), @@ -106,7 +118,8 @@ public void nested() { @Test public void mixed() { - MatcherAssert.assertThat(resolver.replace("prefix.${${priority}-list}.${key}.${high-list}.suffix ${xxx}", parameters), + MatcherAssert.assertThat( + resolver.replace("prefix.${${priority}-list}.${key}.${high-list}.suffix ${xxx}", parameters), Matchers.contains( "prefix.low-1.value.high-1.suffix ${xxx}", "prefix.low-2.value.high-1.suffix ${xxx}", diff --git a/foundations/foundation-config/src/test/resources/mapping.yaml b/foundations/foundation-config/src/test/resources/mapping.yaml index b9e0d97a3ef..66d6b0d08e1 100644 --- a/foundations/foundation-config/src/test/resources/mapping.yaml +++ b/foundations/foundation-config/src/test/resources/mapping.yaml @@ -22,4 +22,4 @@ MY_SERVICES_ENDPOINT: - servicecomb.service1.mapping.address SERVICECOMB_ENV: - service_description.environment - - service_description.environment.old \ No newline at end of file + - service_description.environment.old diff --git a/foundations/foundation-config/src/test/resources/microservice.yaml b/foundations/foundation-config/src/test/resources/microservice.yaml index f8f6825d889..43a9078fba0 100644 --- a/foundations/foundation-config/src/test/resources/microservice.yaml +++ b/foundations/foundation-config/src/test/resources/microservice.yaml @@ -66,4 +66,4 @@ test: commonSeparatedStringHolder: ${holder} stringArray: - m - - n \ No newline at end of file + - n diff --git a/foundations/foundation-config/src/test/resources/test1.yaml b/foundations/foundation-config/src/test/resources/test1.yaml index 708d0e82f8b..e171487901f 100644 --- a/foundations/foundation-config/src/test/resources/test1.yaml +++ b/foundations/foundation-config/src/test/resources/test1.yaml @@ -38,11 +38,11 @@ shutDownHandler: enabled: true timeLimit: 30000 -hsa: +has: manager: url: 127.0.0.1 - serializer: - default: protostuff + serializer: + default: protostuff provider: service: name: ${spring.application.name} @@ -52,7 +52,7 @@ hsa: rpc: serviceLevel: interface dsf: -# ip: "10.57.65.225" +# ip: "10.57.65.225" port: 7450 eureka: diff --git a/foundations/foundation-metrics/pom.xml b/foundations/foundation-metrics/pom.xml index ba0193e7013..d893011c4d4 100644 --- a/foundations/foundation-metrics/pom.xml +++ b/foundations/foundation-metrics/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb foundations - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundation-metrics Java Chassis::Foundations::Metrics @@ -32,8 +32,8 @@ foundation-common - com.netflix.spectator - spectator-reg-servo + io.micrometer + micrometer-core org.apache.logging.log4j diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java index 5b014afe8d9..86529a36e41 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsBootstrap.java @@ -22,34 +22,32 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import io.micrometer.core.instrument.MeterRegistry; + public class MetricsBootstrap { private static final Logger LOGGER = LoggerFactory.getLogger(MetricsBootstrap.class); - private GlobalRegistry globalRegistry; + private final MetricsBootstrapConfig config; - private EventBus eventBus; + private MeterRegistry meterRegistry; - private MetricsBootstrapConfig config; + private EventBus eventBus; private ScheduledExecutorService executorService; private List metricsInitializers; - private Environment environment; - @Autowired - public void setEnvironment(Environment environment) { - this.environment = environment; - config = new MetricsBootstrapConfig(environment); + public MetricsBootstrap(MetricsBootstrapConfig config) { + this.config = config; } @Autowired @@ -57,15 +55,19 @@ public void setMetricsInitializers(List metricsInitializers) this.metricsInitializers = metricsInitializers; } - public void start(GlobalRegistry globalRegistry, EventBus eventBus) { - this.globalRegistry = globalRegistry; + @Autowired + public void setMeterRegistry(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + public void start(EventBus eventBus) { this.eventBus = eventBus; this.executorService = Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder() - .setNameFormat("spectator-poller-%d") + .setNameFormat("metrics-poller-%d") .build()); - metricsInitializers.forEach(initializer -> initializer.init(globalRegistry, eventBus, config)); + metricsInitializers.forEach(initializer -> initializer.init(this.meterRegistry, eventBus, config)); startPoll(); } @@ -86,9 +88,13 @@ protected void startPoll() { } public synchronized void pollMeters() { + metricsInitializers.forEach(initializer -> { + if (initializer instanceof PeriodMeter) { + ((PeriodMeter) initializer).poll(System.currentTimeMillis(), config.getMsPollInterval()); + } + }); try { - long secondInterval = TimeUnit.MILLISECONDS.toSeconds(config.getMsPollInterval()); - PolledEvent polledEvent = globalRegistry.poll(secondInterval); + PolledEvent polledEvent = new PolledEvent(meterRegistry.getMeters()); eventBus.post(polledEvent); } catch (Throwable e) { LOGGER.error("poll meters error. ", e); diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java index 88201dcb2cc..3cb0b8f95f0 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/MetricsInitializer.java @@ -16,19 +16,16 @@ */ package org.apache.servicecomb.foundation.metrics; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; - import com.google.common.eventbus.EventBus; +import io.micrometer.core.instrument.MeterRegistry; + public interface MetricsInitializer { default int getOrder() { return 0; } - /** - * if create new registry, must add to globalRegistry - */ - void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config); + void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config); default void destroy() { diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/PolledEvent.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/PolledEvent.java index 6b7c3018c68..0814f96149f 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/PolledEvent.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/PolledEvent.java @@ -18,17 +18,14 @@ import java.util.List; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; +import io.micrometer.core.instrument.Meter; + public class PolledEvent { private List meters; - private List measurements; - - public PolledEvent(List meters, List measurements) { + public PolledEvent(List meters) { this.meters = meters; - this.measurements = measurements; } public List getMeters() { @@ -38,12 +35,4 @@ public List getMeters() { public void setMeters(List meters) { this.meters = meters; } - - public List getMeasurements() { - return measurements; - } - - public void setMeasurements(List measurements) { - this.measurements = measurements; - } } diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java index 2a341cfa19b..d464697024f 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionConfig.java @@ -24,6 +24,8 @@ import org.slf4j.LoggerFactory; public class LatencyDistributionConfig { + public static final Long MAX_LATENCY = 60 * 60 * 1000L; + private static final Logger LOGGER = LoggerFactory.getLogger(LatencyDistributionConfig.class); private final List scopeConfigs = new ArrayList<>(); @@ -36,7 +38,7 @@ public LatencyDistributionConfig(String config) { if (StringUtils.isEmpty(config)) { return; } - config = config.trim() + "," + Long.MAX_VALUE; + config = config.trim() + "," + MAX_LATENCY; String[] array = config.split("\\s*,+\\s*"); try { for (int idx = 0; idx < array.length - 1; idx++) { diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionMeter.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionMeter.java deleted file mode 100644 index 175c06b8eed..00000000000 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyDistributionMeter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.metrics.meter; - -import java.util.ArrayList; -import java.util.List; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; - -public class LatencyDistributionMeter extends AbstractPeriodMeter { - private final List latencyScopeMeters = new ArrayList<>(); - - public LatencyDistributionMeter(Id id, String config) { - this.id = id; - - LatencyDistributionConfig latencyDistributionConfig = new LatencyDistributionConfig(config); - for (LatencyScopeConfig scopeConfig : latencyDistributionConfig.getScopeConfigs()) { - latencyScopeMeters.add(new LatencyScopeMeter(id, scopeConfig)); - } - } - - public void record(long nanoLatency) { - if (nanoLatency < 0) { - return; - } - - for (LatencyScopeMeter latencyScopeMeter : latencyScopeMeters) { - if (latencyScopeMeter.update(nanoLatency)) { - return; - } - } - } - - @Override - public void calcMeasurements(long msNow, long secondInterval) { - List measurements = new ArrayList<>(); - calcMeasurements(measurements, msNow, secondInterval); - allMeasurements = measurements; - } - - @Override - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - latencyScopeMeters.forEach(latencyScopeMeter -> measurements.add(latencyScopeMeter.createMeasurement(msNow))); - } -} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyScopeMeter.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyScopeMeter.java deleted file mode 100644 index 114f930f084..00000000000 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/LatencyScopeMeter.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.metrics.meter; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.LongAdder; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; - -public class LatencyScopeMeter { - private final Id scopeId; - - private final LongAdder times = new LongAdder(); - - private long lastTimes = 0L; - - private final long nanoMin; - - private final long nanoMax; - - public LatencyScopeMeter(Id latencyDistributionId, LatencyScopeConfig config) { - nanoMin = TimeUnit.MILLISECONDS.toNanos(config.getMsMin()); - nanoMax = TimeUnit.MILLISECONDS.toNanos(config.getMsMax()); - scopeId = latencyDistributionId.withTag("scope", - String.format("[%d,%s)", config.getMsMin(), config.getMsMax() == Long.MAX_VALUE ? "" : config.getMsMax())); - } - - public boolean update(long nanoLatency) { - if (nanoMin <= nanoLatency && nanoMax > nanoLatency) { - times.increment(); - return true; - } - - return false; - } - - public Measurement createMeasurement(long msNow) { - long currentTimes = times.longValue(); - long deltaTimes = currentTimes - lastTimes; - this.lastTimes = currentTimes; - - return new Measurement(scopeId, msNow, deltaTimes); - } -} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/PeriodMeter.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/PeriodMeter.java index f91d781e17a..a846d08179d 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/PeriodMeter.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/PeriodMeter.java @@ -16,13 +16,6 @@ */ package org.apache.servicecomb.foundation.metrics.meter; -import java.util.List; - -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; - -public interface PeriodMeter extends Meter { - void calcMeasurements(long msNow, long secondInterval); - - void calcMeasurements(List measurements, long msNow, long secondInterval); +public interface PeriodMeter { + void poll(long msNow, long secondInterval); } diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/SimpleTimer.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/SimpleTimer.java deleted file mode 100644 index 09803a4718d..00000000000 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/meter/SimpleTimer.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.metrics.meter; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.LongAdder; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Statistic; -import com.netflix.spectator.impl.AtomicDouble; - -/** - * ServoTimer is too slow - * this is a faster timer - */ -public class SimpleTimer extends AbstractPeriodMeter { - private static final double CNV_SECONDS = 1.0 / TimeUnit.SECONDS.toNanos(1L); - - private final Id idCount; - - private final Id idTotalTime; - - private final Id idMax; - - private final LongAdder count = new LongAdder(); - - private final LongAdder totalTime = new LongAdder(); - - private final AtomicDouble max = new AtomicDouble(); - - private long lastCount = 0; - - private long lastTotalTime = 0; - - public SimpleTimer(Id id) { - this.id = id; - this.idCount = id.withTag(Statistic.count); - this.idTotalTime = id.withTag(Statistic.totalTime); - this.idMax = id.withTag(Statistic.max); - } - - public void record(long nanoAmount) { - if (nanoAmount >= 0) { - totalTime.add(nanoAmount); - count.increment(); - max.max(nanoAmount); - } - } - - private Measurement newMeasurement(Id id, long msNow, Number n) { - return new Measurement(id, msNow, n.doubleValue()); - } - - @Override - public void calcMeasurements(long msNow, long secondInterval) { - List measurements = new ArrayList<>(3); - calcMeasurements(measurements, msNow, secondInterval); - allMeasurements = measurements; - } - - @Override - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - long currentCount = count.longValue(); - long currentTotalTime = totalTime.longValue(); - - measurements.add(newMeasurement(idCount, msNow, (double) (currentCount - lastCount) / secondInterval)); - measurements - .add(newMeasurement(idTotalTime, msNow, (currentTotalTime - lastTotalTime) / secondInterval * CNV_SECONDS)); - measurements.add(newMeasurement(idMax, msNow, max.get() * CNV_SECONDS)); - - lastCount = currentCount; - lastTotalTime = currentTotalTime; - // maybe lost some max value, but not so important? - max.set(0); - } -} diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/DefaultTagFinder.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/DefaultTagFinder.java similarity index 90% rename from foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/DefaultTagFinder.java rename to foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/DefaultTagFinder.java index 17d3d9a7370..f29227c6a72 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/DefaultTagFinder.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/DefaultTagFinder.java @@ -14,9 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.foundation.metrics.publish.spectator; +package org.apache.servicecomb.foundation.metrics.publish; -import com.netflix.spectator.api.Tag; + +import io.micrometer.core.instrument.Tag; public class DefaultTagFinder implements TagFinder { private final String tagKey; @@ -45,7 +46,7 @@ public String getTagKey() { @Override public Tag find(Iterable tags) { for (Tag tag : tags) { - if (tag.key().equals(tagKey)) { + if (tag.getKey().equals(tagKey)) { return tag; } } diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementGroupConfig.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementGroupConfig.java similarity index 95% rename from foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementGroupConfig.java rename to foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementGroupConfig.java index 0942baf2666..cec6db25eb4 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementGroupConfig.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementGroupConfig.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.foundation.metrics.publish.spectator; +package org.apache.servicecomb.foundation.metrics.publish; import java.util.Arrays; import java.util.HashMap; diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementNode.java similarity index 79% rename from foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java rename to foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementNode.java index c138bea15eb..8ff19a15345 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementNode.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementNode.java @@ -14,29 +14,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.foundation.metrics.publish.spectator; +package org.apache.servicecomb.foundation.metrics.publish; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter.Id; public class MeasurementNode implements Comparable { private final String name; + private final Id id; + private final List measurements = new ArrayList<>(); private Map children; - public MeasurementNode(String name, Map children) { + public MeasurementNode(String name, Id id, Map children) { this.name = name; + this.id = id; this.children = children; } public String getName() { - return name; + return this.name; + } + + public Id getId() { + return id; } public Map getChildren() { @@ -62,13 +70,16 @@ public MeasurementNode findChild(String... childNames) { return node; } - public MeasurementNode addChild(String childName, Measurement measurement) { + public MeasurementNode addChild(String childName, Id id, Measurement measurement) { if (children == null) { children = new LinkedHashMap<>(); } - MeasurementNode node = children.computeIfAbsent(childName, name -> new MeasurementNode(name, null)); - node.addMeasurement(measurement); + MeasurementNode node = children.computeIfAbsent(childName, name -> new MeasurementNode(name, id, null)); + + if (measurement != null) { + node.addMeasurement(measurement); + } return node; } @@ -84,7 +95,7 @@ public void addMeasurement(Measurement measurement) { public double summary() { double result = 0; for (Measurement measurement : measurements) { - result += measurement.value(); + result += measurement.getValue(); } return result; diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementTree.java similarity index 52% rename from foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java rename to foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementTree.java index 79101fa166e..a03c4739ef7 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/MeasurementTree.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/MeasurementTree.java @@ -14,21 +14,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.foundation.metrics.publish.spectator; +package org.apache.servicecomb.foundation.metrics.publish; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; -import com.netflix.spectator.api.Tag; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.Meter.Id; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.distribution.CountAtBucket; +import io.micrometer.core.instrument.distribution.HistogramSnapshot; // like select * from meters group by ...... // but output a tree not a table public class MeasurementTree extends MeasurementNode { public MeasurementTree() { - super(null, null); + super(null, null, null); } // groupConfig: @@ -37,23 +42,37 @@ public MeasurementTree() { // only id name exists in groupConfig will accept, others will be ignored public void from(Iterator meters, MeasurementGroupConfig groupConfig) { meters.forEachRemaining(meter -> { + // This code snip is not very good design. But DistributionSummary is quite special. + if (meter instanceof DistributionSummary distributionSummary) { + HistogramSnapshot snapshot = distributionSummary.takeSnapshot(); + CountAtBucket[] countAtBuckets = snapshot.histogramCounts(); + List distributions = new ArrayList<>(countAtBuckets.length); + for (CountAtBucket countAtBucket : countAtBuckets) { + final double value = countAtBucket.count(); + distributions.add(new Measurement(() -> value, + Statistic.COUNT)); + } + + from(meter.getId(), distributions, groupConfig); + return; + } + Iterable measurements = meter.measure(); - from(measurements, groupConfig); + from(meter.getId(), measurements, groupConfig); }); } - public void from(Iterable measurements, MeasurementGroupConfig groupConfig) { + public void from(Id id, Iterable measurements, MeasurementGroupConfig groupConfig) { for (Measurement measurement : measurements) { - Id id = measurement.id(); - MeasurementNode node = addChild(id.name(), measurement); + MeasurementNode node = addChild(id.getName(), id, measurement); - List tagFinders = groupConfig.findTagFinders(id.name()); + List tagFinders = groupConfig.findTagFinders(id.getName()); if (tagFinders == null) { continue; } for (TagFinder tagFinder : tagFinders) { - Tag tag = tagFinder.find(id.tags()); + Tag tag = tagFinder.find(id.getTags()); if (tag == null) { if (tagFinder.skipOnNull()) { break; @@ -61,11 +80,13 @@ public void from(Iterable measurements, MeasurementGroupConfig grou throw new IllegalStateException( String.format("tag key \"%s\" not exist in %s", tagFinder.getTagKey(), - measurement)); + id)); } - node = node.addChild(tag.value(), measurement); + node = node.addChild(tag.getValue(), id, measurement); } + + node.addChild(measurement.getStatistic().name(), id, measurement); } } } diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TagFinder.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/TagFinder.java similarity index 92% rename from foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TagFinder.java rename to foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/TagFinder.java index 0762afb8de4..e1f3c8b402c 100644 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TagFinder.java +++ b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/publish/TagFinder.java @@ -14,9 +14,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.foundation.metrics.publish.spectator; +package org.apache.servicecomb.foundation.metrics.publish; -import com.netflix.spectator.api.Tag; +import io.micrometer.core.instrument.Tag; public interface TagFinder { static TagFinder build(Object obj) { diff --git a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/registry/GlobalRegistry.java b/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/registry/GlobalRegistry.java deleted file mode 100644 index 25e85b134e5..00000000000 --- a/foundations/foundation-metrics/src/main/java/org/apache/servicecomb/foundation/metrics/registry/GlobalRegistry.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.metrics.registry; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.apache.servicecomb.foundation.metrics.PolledEvent; -import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; - -import com.netflix.spectator.api.Clock; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.SpectatorUtils; - -public class GlobalRegistry { - private final Clock clock; - - private final List registries = new CopyOnWriteArrayList<>(); - - private Registry defaultRegistry; - - public GlobalRegistry() { - this(Clock.SYSTEM); - } - - public GlobalRegistry(Clock clock) { - this.clock = clock; - } - - public Clock getClock() { - return clock; - } - - public List getRegistries() { - return registries; - } - - public Registry getDefaultRegistry() { - return defaultRegistry; - } - - public synchronized void add(Registry registry) { - if (registries.isEmpty()) { - defaultRegistry = registry; - } - registries.add(registry); - } - - public synchronized void remove(Registry registry) { - registries.remove(registry); - if (registry != defaultRegistry) { - return; - } - - if (registries.isEmpty()) { - defaultRegistry = null; - return; - } - - defaultRegistry = registries.get(0); - } - - public synchronized void removeAll() { - registries.clear(); - defaultRegistry = null; - } - - @SuppressWarnings("unchecked") - public T find(Class cls) { - for (Registry registry : registries) { - if (cls.isAssignableFrom(registry.getClass())) { - return (T) registry; - } - } - return null; - } - - public PolledEvent poll(long secondInterval) { - long msNow = clock.wallTime(); - List meters = new ArrayList<>(); - List measurements = new ArrayList<>(); - for (Registry registry : registries) { - SpectatorUtils.removeExpiredMeters(registry); - - for (Meter meter : registry) { - if (meter instanceof PeriodMeter) { - ((PeriodMeter) meter).calcMeasurements(msNow, secondInterval); - } - - meters.add(meter); - meter.measure().forEach(measurements::add); - } - } - - return new PolledEvent(meters, measurements); - } -} diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java index 466a0c5f100..231b0099ed4 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/TestMetricsBootstrap.java @@ -25,7 +25,6 @@ import java.util.List; import java.util.concurrent.ScheduledExecutorService; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; @@ -36,12 +35,11 @@ import com.google.common.eventbus.EventBus; +import io.micrometer.core.instrument.MeterRegistry; import mockit.Deencapsulation; public class TestMetricsBootstrap { - MetricsBootstrap bootstrap = new MetricsBootstrap(); - - GlobalRegistry globalRegistry = new GlobalRegistry(); + MetricsBootstrap bootstrap; EventBus eventBus = new EventBus(); @@ -49,13 +47,15 @@ public class TestMetricsBootstrap { @BeforeEach public void setUp() { - bootstrap.setMetricsInitializers(List.of()); Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) .thenReturn(DEFAULT_METRICS_WINDOW_TIME); Mockito.when(environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); - bootstrap.setEnvironment(environment); + + MetricsBootstrapConfig config = new MetricsBootstrapConfig(environment); + bootstrap = new MetricsBootstrap(config); + bootstrap.setMetricsInitializers(List.of()); } @Test @@ -63,14 +63,14 @@ public void loadMetricsInitializers() { List initList = new ArrayList<>(); MetricsInitializer metricsInitializer = new MetricsInitializer() { @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { initList.add(this); } }; bootstrap.setMetricsInitializers(Arrays.asList(metricsInitializer, metricsInitializer)); - bootstrap.start(globalRegistry, eventBus); + bootstrap.start(eventBus); bootstrap.shutdown(); MatcherAssert.assertThat(initList, Matchers.contains(metricsInitializer, metricsInitializer)); @@ -87,7 +87,7 @@ public int getOrder() { } @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { } @Override @@ -103,7 +103,7 @@ public int getOrder() { } @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { } @Override diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestLatencyDistributionMeter.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestLatencyDistributionMeter.java deleted file mode 100644 index d5cd260749c..00000000000 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestLatencyDistributionMeter.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.metrics.meter; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -public class TestLatencyDistributionMeter { - - @Test - public void testMeasure() { - Id id = Mockito.mock(Id.class); - LatencyDistributionMeter latencyDistributionMeter = new LatencyDistributionMeter(id, "0,1,3,10"); - latencyDistributionMeter.record(TimeUnit.MILLISECONDS.toNanos(1L)); - latencyDistributionMeter.record(TimeUnit.MILLISECONDS.toNanos(5L)); - latencyDistributionMeter.record(TimeUnit.MILLISECONDS.toNanos(2L)); - List measurements = new ArrayList<>(); - latencyDistributionMeter.calcMeasurements(measurements, 0L, 0L); - Assertions.assertEquals(4, measurements.size()); - Assertions.assertEquals(0, ((int) (measurements.get(0).value()))); - Assertions.assertEquals(2, ((int) (measurements.get(1).value()))); - Assertions.assertEquals(1, ((int) (measurements.get(2).value()))); - Assertions.assertEquals(0, ((int) (measurements.get(3).value()))); - } -} diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestSimpleTimer.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestSimpleTimer.java deleted file mode 100644 index 47b60ec4748..00000000000 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/meter/TestSimpleTimer.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.metrics.meter; - -import com.netflix.spectator.api.SpectatorUtils; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class TestSimpleTimer { - SimpleTimer timer = new SimpleTimer(SpectatorUtils.createDefaultId("name")); - - @Test - public void measure() { - timer.record(2); - timer.record(4); - - Assertions.assertFalse(timer.measure().iterator().hasNext()); - - timer.calcMeasurements(1, 2); - Assertions.assertEquals( - "[Measurement(name:statistic=count,1,1.0), Measurement(name:statistic=totalTime,1,3.0000000000000004E-9), Measurement(name:statistic=max,1,4.0E-9)]", - timer.measure().toString()); - Assertions.assertFalse(timer.hasExpired()); - Assertions.assertEquals("name", timer.id().name()); - } -} diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestDefaultTagFinder.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestDefaultTagFinder.java index c892b6a8b8c..46220069b97 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestDefaultTagFinder.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestDefaultTagFinder.java @@ -19,11 +19,13 @@ import java.util.Arrays; import java.util.List; -import com.netflix.spectator.api.BasicTag; -import com.netflix.spectator.api.Tag; +import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; +import org.apache.servicecomb.foundation.metrics.publish.TagFinder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import io.micrometer.core.instrument.Tag; + public class TestDefaultTagFinder { TagFinder finder = new DefaultTagFinder("key"); @@ -34,8 +36,8 @@ public void getTagKey() { @Test public void readSucc() { - Tag tag = new BasicTag("key", "value"); - List tags = Arrays.asList(new BasicTag("t1", "t1v"), + Tag tag = Tag.of("key", "value"); + List tags = Arrays.asList(Tag.of("t1", "t1v"), tag); Assertions.assertSame(tag, finder.find(tags)); @@ -43,7 +45,7 @@ public void readSucc() { @Test public void readFail() { - List tags = Arrays.asList(new BasicTag("t1", "t1v")); + List tags = Arrays.asList(Tag.of("t1", "t1v")); Assertions.assertNull(finder.find(tags)); } diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java index 03374009621..60d77b4c75a 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementGroupConfig.java @@ -19,6 +19,8 @@ import java.util.List; import java.util.Map; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.TagFinder; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; @@ -51,7 +53,7 @@ public void addGroup() { config.addGroup("id1", "tag1.1", "tag1.2"); config.addGroup("id2", "tag2.1", "tag2.2"); - MatcherAssert.assertThat(groups.keySet(), Matchers.contains("id2", "id1")); + MatcherAssert.assertThat(groups.keySet(), Matchers.containsInAnyOrder("id2", "id1")); MatcherAssert.assertThat(groups.get("id1").stream().map(TagFinder::getTagKey).toArray(), Matchers.arrayContaining("tag1.1", "tag1.2")); MatcherAssert.assertThat(groups.get("id2").stream().map(TagFinder::getTagKey).toArray(), Matchers.arrayContaining("tag2.1", "tag2.2")); } diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java index 7c5691f929a..be7681ae5cd 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementNode.java @@ -19,18 +19,23 @@ import java.util.HashMap; import java.util.Map; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; +import org.junit.jupiter.api.Assertions; -import com.netflix.spectator.api.Measurement; - +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter.Id; +import io.micrometer.core.instrument.Meter.Type; +import io.micrometer.core.instrument.Tags; import mockit.Expectations; import mockit.Mocked; -import org.junit.jupiter.api.Assertions; public class TestMeasurementNode { - MeasurementNode node = new MeasurementNode("name", null); + Id id = new Id("name", Tags.empty(), null, null, Type.OTHER); + + MeasurementNode node = new MeasurementNode("name", id, null); @Test public void getName() { @@ -40,7 +45,7 @@ public void getName() { @Test public void getChildren() { Map children = new HashMap<>(); - node = new MeasurementNode("name", children); + node = new MeasurementNode("name", id, children); Assertions.assertSame(children, node.getChildren()); } @@ -52,24 +57,24 @@ public void findChild_noChildren() { @Test public void findChild_multiLevel_noMiddleChildren(@Mocked Measurement measurement) { - MeasurementNode c1 = node.addChild("c1", measurement); - c1.addChild("c2", measurement); + MeasurementNode c1 = node.addChild("c1", id, measurement); + c1.addChild("c2", id, measurement); Assertions.assertNull(node.findChild("c1_notExist", "c2")); } @Test public void findChild_multiLevel_ok(@Mocked Measurement measurement) { - MeasurementNode c1 = node.addChild("c1", measurement); - MeasurementNode c2 = c1.addChild("c2", measurement); + MeasurementNode c1 = node.addChild("c1", id, measurement); + MeasurementNode c2 = c1.addChild("c2", id, measurement); Assertions.assertSame(c2, node.findChild("c1", "c2")); } @Test public void addChild(@Mocked Measurement measurement) { - MeasurementNode c1 = node.addChild("c1", measurement); - MeasurementNode c2 = node.addChild("c2", measurement); + MeasurementNode c1 = node.addChild("c1", id, measurement); + MeasurementNode c2 = node.addChild("c2", id, measurement); Assertions.assertSame(c1, node.findChild("c1")); Assertions.assertSame(c2, node.findChild("c2")); @@ -86,7 +91,7 @@ public void getMeasurements(@Mocked Measurement measurement) { public void summary(@Mocked Measurement measurement) { new Expectations() { { - measurement.value(); + measurement.getValue(); result = 10; } }; diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java index 108831d0b70..d21354be5b2 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestMeasurementTree.java @@ -18,22 +18,23 @@ import java.util.concurrent.TimeUnit; +import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.junit.Before; import org.junit.Test; import org.junit.jupiter.api.Assertions; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.Statistic; -import com.netflix.spectator.api.Timer; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.Timer; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; public class TestMeasurementTree { MeasurementTree tree = new MeasurementTree(); - ManualClock clock = new ManualClock(); - - Registry registry = new DefaultRegistry(clock); + MeterRegistry registry = new SimpleMeterRegistry(); Timer timer; @@ -53,17 +54,19 @@ public void setup() { @Test public void from() { - timer.record(10, TimeUnit.NANOSECONDS); - timer.record(2, TimeUnit.NANOSECONDS); + timer.record(10, TimeUnit.SECONDS); + timer.record(2, TimeUnit.SECONDS); - MeasurementGroupConfig config = new MeasurementGroupConfig("id", "g1", "g2", Statistic.count.key()); - tree.from(registry.iterator(), config); + MeasurementGroupConfig config = new MeasurementGroupConfig("id", "g1", "g2"); + tree.from(registry.getMeters().iterator(), config); Assertions.assertEquals(2, tree.getChildren().size()); MeasurementNode node = tree.findChild("id", "g1v", "g2v"); - Assertions.assertEquals(2d, node.findChild(Statistic.count.value()).getMeasurements().get(0).value(), 0); - Assertions.assertEquals(12d, node.findChild(Statistic.totalTime.value()).getMeasurements().get(0).value(), 0); + Assertions.assertEquals(2d, + node.findChild(Statistic.COUNT.name()).getMeasurements().get(0).getValue(), 0); + Assertions.assertEquals(12d, + node.findChild(Statistic.TOTAL_TIME.name()).getMeasurements().get(0).getValue(), 0); Assertions.assertEquals(0d, tree.findChild("id_notCare").summary(), 0); } @@ -71,7 +74,7 @@ public void from() { public void from_withSkipOnNull() { try { MeasurementGroupConfig config = new MeasurementGroupConfig("id", new DefaultTagFinder("notExist", true)); - tree.from(registry.iterator(), config); + tree.from(registry.getMeters().iterator(), config); } catch (Exception e) { Assertions.fail("should not throw exception"); } @@ -81,8 +84,10 @@ public void from_withSkipOnNull() { public void from_failed() { IllegalStateException exception = Assertions.assertThrows(IllegalStateException.class, () -> { MeasurementGroupConfig config = new MeasurementGroupConfig("id", "notExist"); - tree.from(registry.iterator(), config); + tree.from(registry.getMeters().iterator(), config); }); - Assertions.assertEquals("tag key \"notExist\" not exist in Measurement(id:g1=g1v:g2=g2v:statistic=count:t3=t3v:t4=t4v,0,0.0)", exception.getMessage()); + Assertions.assertEquals( + "tag key \"notExist\" not exist in MeterId{name='id', tags=[tag(g1=g1v),tag(g2=g2v),tag(t3=t3v),tag(t4=t4v)]}", + exception.getMessage()); } } diff --git a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestTagFinder.java b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestTagFinder.java index a4c40320e6a..2a0d960637a 100644 --- a/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestTagFinder.java +++ b/foundations/foundation-metrics/src/test/java/org/apache/servicecomb/foundation/metrics/publish/spectator/TestTagFinder.java @@ -16,6 +16,8 @@ */ package org.apache.servicecomb.foundation.metrics.publish.spectator; +import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; +import org.apache.servicecomb.foundation.metrics.publish.TagFinder; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; diff --git a/foundations/foundation-protobuf/README.md b/foundations/foundation-protobuf/README.md index 8f3526373c7..c225e89dc87 100644 --- a/foundations/foundation-protobuf/README.md +++ b/foundations/foundation-protobuf/README.md @@ -1,9 +1,9 @@ # Overview -There are many existing protobuf codecs, but all of them are not suit for us. -ServiceComb data model is just POJO, not bind to any codec mechanism, so we had to create new one: +There are many existing protobuf codecs, but all of them are not suit for us. +ServiceComb data model is just POJO, not bind to any codec mechanism, so we had to create new one: -| | ServiceComb | protobuf | protostuff | jackson | -| -------------------------------------------------------- | :-----------: | :------: | :------------: | :-----: | +| | ServiceComb | protobuf | protostuff | jackson | +| -------------------------------------------------------- | :-----------: | :------: | :------------: | :-----: | | generate code | no need
just POJO | must
100+ lines IDL, generate 10000+ java code | no need
POJO with Annotation(eg:@Tag) | no need | | support null element in repeated field | no | no | not compatible | no | | support "oneOf" | no | yes | no | no | @@ -21,25 +21,25 @@ ServiceComb data model is just POJO, not bind to any codec mechanism, so we had | serialize not repeated number field ignore default value | yes | yes | no | no | | serialize from map model | yes | no | no | no | | deserialize to map model | yes | no | no | no | - + # Usage -## Create factory - one factory instance globally is enough +## Create factory + one factory instance globally is enough ```java ProtoMapperFactory factory = new ProtoMapperFactory(); ``` ## Load proto definition - create mapper instance for each proto definition -- load from classpath + create mapper instance for each proto definition +- load from classpath ```java ProtoMapper protoMapper = factory.createFromName("protobuf.proto"); ``` -- load from proto content +- load from proto content ```java ProtoMapper protoMapper = factory.createFromContent(protoContent); ``` ## Serialize -serializer is reusable and thread safe +serializer is reusable and thread safe Assuming you have a proto definition ```proto message User { @@ -50,10 +50,9 @@ and a POJO class ```java public class User { private String name; - // getter and setter + // getter and setter } ``` -```java RootSerializer serializer = protoMapper.createRootSerializer("User", User.class); User user = new User(); @@ -63,11 +62,13 @@ byte[] pojoBytes= serializer.serialize(user); Map map = new HashMap<>(); map.put("name", "userName"); byte[] mapBytes = serializer.serialize(map); +```java + // pojoBytes equals mapBytes ``` ## Deserialize -deserializer is reusable and thread safe +deserializer is reusable and thread safe ```java RootDeserializer pojoDeserializer = protoMapper.createRootDeserializer("User", User.class); RootDeserializer> mapDeserializer = protoMapper.createRootDeserializer("User", Map.class); @@ -86,67 +87,67 @@ Map map = mapDeserializer.deserialize(bytes); 2.protoStuff some scenes, there is no field but have getter or setter, so we can not use unsafe to access field so we disable protoStuff unsafe feature - + for repeated fields, protoStuff have better performance, but not compatible to protobuf - + 3.jackson not support map/any/recursive, ignore related fields 4.serialize result size ScbStrong/ScbWeak/Protobuf have the same and smaller size, because skip all default/null value -Empty: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 519 515 240 288 1242 -ser len : 36 0 0 0 56 -deser time(ms): 161 69 10 516 486 -deser->ser len: 36 0 0 0 56 -ser+deser(ms) : 680 584 250 804 1728 - -Scalars: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 557 529 328 262 1357 -ser len : 56 24 24 24 76 -deser time(ms): 181 141 115 527 504 -deser->ser len: 56 24 24 24 76 -ser+deser(ms) : 738 670 443 789 1861 - -Pojo: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 571 574 276 309 1304 -ser len : 46 10 10 10 66 -deser time(ms): 230 69 112 668 537 -deser->ser len: 46 10 10 10 66 -ser+deser(ms) : 801 643 388 977 1841 - -SimpleList: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 590 609 296 637 1320 -ser len : 68 32 32 32 88 -deser time(ms): 233 105 122 2226 541 -deser->ser len: 68 32 32 32 88 -ser+deser(ms) : 823 714 418 2863 1861 - -PojoList: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 609 632 319 2777 1407 -ser len : 56 20 20 20 76 -deser time(ms): 244 134 173 2287 679 -deser->ser len: 56 20 20 20 76 -ser+deser(ms) : 853 766 492 5064 2086 - -Map: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 746 772 491 1079 1298 -ser len : 92 54 54 54 56 -deser time(ms): 522 427 468 1031 422 -deser->ser len: 92 54 54 54 56 -ser+deser(ms) : 1268 1199 959 2110 1720 - -Mixed: - Protostuff ScbStrong ScbWeak Protobuf Jackson -ser time(ms) : 1686 1999 2034 2112 2537 -ser len : 479 505 505 505 489 -deser time(ms): 1969 2154 2923 2984 3316 -deser->ser len: 479 505 505 505 489 -ser+deser(ms) : 3655 4153 4957 5096 5853 -``` \ No newline at end of file +Empty: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 519 515 240 288 1242 +serialize len : 36 0 0 0 56 +deserialize time(ms): 161 69 10 516 486 +deserialize->serialize len: 36 0 0 0 56 +serialize+deserialize(ms) : 680 584 250 804 1728 + +Scalars: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 557 529 328 262 1357 +serialize len : 56 24 24 24 76 +deserialize time(ms): 181 141 115 527 504 +deserialize->serialize len: 56 24 24 24 76 +serialize+deserialize(ms) : 738 670 443 789 1861 + +Pojo: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 571 574 276 309 1304 +serialize len : 46 10 10 10 66 +deserialize time(ms): 230 69 112 668 537 +deserialize->serialize len: 46 10 10 10 66 +serialize+deserialize(ms) : 801 643 388 977 1841 + +SimpleList: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 590 609 296 637 1320 +serialize len : 68 32 32 32 88 +deserialize time(ms): 233 105 122 2226 541 +deserialize->serialize len: 68 32 32 32 88 +serialize+deserialize(ms) : 823 714 418 2863 1861 + +PojoList: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 609 632 319 2777 1407 +serialize len : 56 20 20 20 76 +deserialize time(ms): 244 134 173 2287 679 +deserialize->serialize len: 56 20 20 20 76 +serialize+deserialize(ms) : 853 766 492 5064 2086 + +Map: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 746 772 491 1079 1298 +serialize len : 92 54 54 54 56 +deserialize time(ms): 522 427 468 1031 422 +deserialize->serialize len: 92 54 54 54 56 +serialize+deserialize(ms) : 1268 1199 959 2110 1720 + +Mixed: + Protostuff ScbStrong ScbWeak Protobuf Jackson +serialize time(ms) : 1686 1999 2034 2112 2537 +serialize len : 479 505 505 505 489 +deserialize time(ms): 1969 2154 2923 2984 3316 +deserialize->serialize len: 479 505 505 505 489 +serialize+deserialize(ms) : 3655 4153 4957 5096 5853 +``` diff --git a/foundations/foundation-protobuf/pom.xml b/foundations/foundation-protobuf/pom.xml index d542e63514b..cb8662c7bfb 100644 --- a/foundations/foundation-protobuf/pom.xml +++ b/foundations/foundation-protobuf/pom.xml @@ -21,7 +21,7 @@ foundations org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -50,10 +50,11 @@ jsr305
- + com.google.protobuf protobuf-java + test @@ -88,7 +89,7 @@ - generate-sources + generate-test-sources test-compile @@ -97,5 +98,5 @@ - + diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ContentFileReader.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ContentFileReader.java index 3a1aad2640f..89bf2b43282 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ContentFileReader.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/parser/ContentFileReader.java @@ -26,7 +26,7 @@ public class ContentFileReader implements FileReader { private final String content; - private boolean contentReaded; + private boolean contentRead; public ContentFileReader(FileReader importReader, String content) { this.importReader = importReader; @@ -35,8 +35,8 @@ public ContentFileReader(FileReader importReader, String content) { @Override public CharStream read(String contentOrName) { - if (!contentReaded) { - contentReaded = true; + if (!contentRead) { + contentRead = true; return CharStreams.fromString(this.content); } diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java index 3beaa0074fb..5d583954ca0 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/SchemaManager.java @@ -55,15 +55,14 @@ public abstract class SchemaManager { // key is canonical message name + ":" + canonical type name protected final Map> canonicalSchemas = new ConcurrentHashMapEx<>(); + // key is canonical message name + ":" + canonical type name + protected final Map> canonicalSchemasCreated = new ConcurrentHashMapEx<>(); + public SchemaManager(ProtoMapper protoMapper) { this.protoMapper = protoMapper; this.proto = protoMapper.getProto(); } - public Map> getCanonicalSchemas() { - return canonicalSchemas; - } - protected String generateCacheKey(Message message, JavaType javaType) { return message.getCanonicalName() + ":" + javaType.toCanonical(); } @@ -72,44 +71,25 @@ protected String generateCacheKey(Message message, JavaType javaType) { protected abstract SchemaEx newMessageSchema(Message message, Map types); - /** - * - * @param protoField - * @param propertyDescriptor provide getter/setter/javaType - * @return - */ protected abstract FieldSchema createScalarField(Field protoField, PropertyDescriptor propertyDescriptor); @SuppressWarnings("unchecked") protected SchemaEx getOrCreateMessageSchema(Message message, Map types) { String cacheKey = generateCacheKey(message, ProtoConst.MAP_TYPE); - SchemaEx messageSchema = (SchemaEx) canonicalSchemas.get(cacheKey); - if (messageSchema == null) { - // messageSchema already put into canonicalSchemas inside createMessageSchema - messageSchema = createMessageSchema(message, types); - } - return messageSchema; + return (SchemaEx) canonicalSchemasCreated.computeIfAbsent(cacheKey, key -> createMessageSchema(message, types)); } @SuppressWarnings("unchecked") protected SchemaEx getOrCreateMessageSchema(Message message, JavaType javaType) { String cacheKey = generateCacheKey(message, javaType); - SchemaEx messageSchema = (SchemaEx) canonicalSchemas.get(cacheKey); - if (messageSchema == null) { - // messageSchema already put into canonicalSchemas inside createMessageSchema - messageSchema = createMessageSchema(message, javaType); - } - return messageSchema; + return (SchemaEx) canonicalSchemasCreated.computeIfAbsent(cacheKey, + key -> createMessageSchema(message, javaType)); } @SuppressWarnings("unchecked") - protected SchemaEx findSchema(String key) { - return (SchemaEx) canonicalSchemas.get(key); - } - protected SchemaEx createMessageSchema(Message message, Map types) { String cacheKey = generateCacheKey(message, ProtoConst.MAP_TYPE); - SchemaEx schema = findSchema(cacheKey); + SchemaEx schema = (SchemaEx) canonicalSchemas.get(cacheKey); if (schema != null) { return schema; } @@ -121,9 +101,10 @@ protected SchemaEx createMessageSchema(Message message, Map return schema; } + @SuppressWarnings("unchecked") protected SchemaEx createMessageSchema(Message message, JavaType javaType) { String cacheKey = generateCacheKey(message, javaType); - SchemaEx schema = findSchema(cacheKey); + SchemaEx schema = (SchemaEx) canonicalSchemas.get(cacheKey); if (schema != null) { return schema; } @@ -144,7 +125,7 @@ protected FieldSchema createMapFieldSchema(Field protoField, PropertyDesc JavaType entryType = TypeFactory.defaultInstance().constructParametricType(MapEntry.class, javaType.getKeyType(), javaType.getContentType()); - SchemaEx> entrySchema = getOrCreateMessageSchema((Message) protoField.getType(), + SchemaEx> entrySchema = createMessageSchema((Message) protoField.getType(), entryType); return new MapSchema<>(protoField, propertyDescriptor, entrySchema); } @@ -185,14 +166,14 @@ public FieldMapEx> createMapFields(Message message, Map types, String perameterName) { + private JavaType getParameterType(Map types, String parameterName) { - if (types.get(perameterName) != null) { - return TypeFactory.defaultInstance().constructType(types.get(perameterName)); + if (types.get(parameterName) != null) { + return TypeFactory.defaultInstance().constructType(types.get(parameterName)); } throw new IllegalArgumentException( - String.format("not found type info for parameter name [%s]", perameterName)); + String.format("not found type info for parameter name [%s]", parameterName)); } public FieldSchema createSchemaField(Field protoField, PropertyDescriptor propertyDescriptor) { @@ -215,7 +196,7 @@ public FieldSchema createSchemaField(Field protoField, PropertyDescriptor // message if (protoField.getType().isMessage()) { - SchemaEx messageSchema = getOrCreateMessageSchema((Message) protoField.getType(), + SchemaEx messageSchema = createMessageSchema((Message) protoField.getType(), propertyDescriptor.getJavaType()); if (isWrapProperty((Message) protoField.getType())) { return new PropertyWrapperAsFieldSchema<>(protoField, propertyDescriptor, messageSchema); diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java index a86d2aa4fdc..23b9d85a01d 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/deserializer/DeserializerSchemaManager.java @@ -249,7 +249,7 @@ protected FieldSchema createRepeatedSchema(Field protoField, PropertyDesc if (contentType == null) { contentType = ProtoConst.OBJECT_TYPE; } - SchemaEx contentSchema = getOrCreateMessageSchema((Message) protoField.getType(), contentType); + SchemaEx contentSchema = createMessageSchema((Message) protoField.getType(), contentType); if (isWrapProperty((Message) protoField.getType())) { return PropertyWrapperRepeatedReadSchemas.create(protoField, propertyDescriptor, contentSchema); } diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java index 49b2154cbbc..ccab4d81f82 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/MessageWriteSchema.java @@ -101,6 +101,8 @@ public FieldMapEx getMainPojoFieldMaps() { @Override public void init() { + this.mapFieldMaps = protoMapper.getSerializerSchemaManager().createMapFields(message); + if (ProtoUtils.isWrapProperty(message)) { this.mainPojoFieldMaps = createPropertyWrapperFields(javaType); return; @@ -178,10 +180,6 @@ private void writeDynamicPojo(OutputEx output, Object dynamicValue) throws I } protected final void writeFromMap(OutputEx output, Map map) throws IOException { - if (mapFieldMaps == null) { - mapFieldMaps = protoMapper.getSerializerSchemaManager().createMapFields(message); - } - for (Entry entry : map.entrySet()) { if (entry.getValue() == null) { continue; diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java index 97ea2af2b1d..d63944e3423 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/internal/schema/serializer/SerializerSchemaManager.java @@ -226,7 +226,7 @@ protected FieldSchema createRepeatedSchema(Field protoField, PropertyDesc if (contentType == null) { contentType = ProtoConst.OBJECT_TYPE; } - SchemaEx contentSchema = getOrCreateMessageSchema((Message) protoField.getType(), contentType); + SchemaEx contentSchema = createMessageSchema((Message) protoField.getType(), contentType); if (isWrapProperty((Message) protoField.getType())) { return PropertyWrapperRepeatedWriteSchemas.create(protoField, propertyDescriptor, contentSchema); } diff --git a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/notice.txt b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/notice.txt index 870ad1c79bb..0b6783a90c6 100644 --- a/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/notice.txt +++ b/foundations/foundation-protobuf/src/main/java/org/apache/servicecomb/foundation/protobuf/notice.txt @@ -15,4 +15,4 @@ 1.数组编码规则从protoStuff变成标准的protobuf 不确定: - 1.List/List/Map>/Map规则应该也有变化,是否有变化,从protoStuff变成标准protobuf \ No newline at end of file + 1.List/List/Map>/Map规则应该也有变化,是否有变化,从protoStuff变成标准protobuf diff --git a/foundations/foundation-protobuf/src/main/resources/google/protobuf/any.proto b/foundations/foundation-protobuf/src/main/resources/google/protobuf/any.proto new file mode 100644 index 00000000000..c89243178af --- /dev/null +++ b/foundations/foundation-protobuf/src/main/resources/google/protobuf/any.proto @@ -0,0 +1,161 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/known/anypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "AnyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; + +// `Any` contains an arbitrary serialized protocol buffer message along with a +// URL that describes the type of the serialized message. +// +// Protobuf library provides support to pack/unpack Any values in the form +// of utility functions or additional generated methods of the Any type. +// +// Example 1: Pack and unpack a message in C++. +// +// Foo foo = ...; +// Any any; +// any.PackFrom(foo); +// ... +// if (any.UnpackTo(&foo)) { +// ... +// } +// +// Example 2: Pack and unpack a message in Java. +// +// Foo foo = ...; +// Any any = Any.pack(foo); +// ... +// if (any.is(Foo.class)) { +// foo = any.unpack(Foo.class); +// } +// // or ... +// if (any.isSameTypeAs(Foo.getDefaultInstance())) { +// foo = any.unpack(Foo.getDefaultInstance()); +// } +// +// Example 3: Pack and unpack a message in Python. +// +// foo = Foo(...) +// any = Any() +// any.Pack(foo) +// ... +// if any.Is(Foo.DESCRIPTOR): +// any.Unpack(foo) +// ... +// +// Example 4: Pack and unpack a message in Go +// +// foo := &pb.Foo{...} +// any, err := anypb.New(foo) +// if err != nil { +// ... +// } +// ... +// foo := &pb.Foo{} +// if err := any.UnmarshalTo(foo); err != nil { +// ... +// } +// +// The pack methods provided by protobuf library will by default use +// 'type.googleapis.com/full.type.name' as the type URL and the unpack +// methods only use the fully qualified type name after the last '/' +// in the type URL, for example "foo.bar.com/x/y.z" will yield type +// name "y.z". +// +// JSON +// ==== +// The JSON representation of an `Any` value uses the regular +// representation of the deserialized, embedded message, with an +// additional field `@type` which contains the type URL. Example: +// +// package google.profile; +// message Person { +// string first_name = 1; +// string last_name = 2; +// } +// +// { +// "@type": "type.googleapis.com/google.profile.Person", +// "firstName": , +// "lastName": +// } +// +// If the embedded message type is well-known and has a custom JSON +// representation, that representation will be embedded adding a field +// `value` which holds the custom JSON in addition to the `@type` +// field. Example (for message [google.protobuf.Duration][]): +// +// { +// "@type": "type.googleapis.com/google.protobuf.Duration", +// "value": "1.212s" +// } +// +message Any { + // A URL/resource name that uniquely identifies the type of the serialized + // protocol buffer message. This string must contain at least + // one "/" character. The last segment of the URL's path must represent + // the fully qualified name of the type (as in + // `path/google.protobuf.Duration`). The name should be in a canonical form + // (e.g., leading "." is not accepted). + // + // In practice, teams usually precompile into the binary all types that they + // expect it to use in the context of Any. However, for URLs which use the + // scheme `http`, `https`, or no scheme, one can optionally set up a type + // server that maps type URLs to message definitions as follows: + // + // * If no scheme is provided, `https` is assumed. + // * An HTTP GET on the URL must yield a [google.protobuf.Type][] + // value in binary format, or produce an error. + // * Applications are allowed to cache lookup results based on the + // URL, or have them precompiled into a binary to avoid any + // lookup. Therefore, binary compatibility needs to be preserved + // on changes to types. (Use versioned type names to manage + // breaking changes.) + // + // Note: this functionality is not currently available in the official + // protobuf release, and it is not used for type URLs beginning with + // type.googleapis.com. + // + // Schemes other than `http`, `https` (or the empty scheme) might be + // used with implementation specific semantics. + // + string type_url = 1; + + // Must be a valid serialized protocol buffer of the above specified type. + bytes value = 2; +} diff --git a/foundations/foundation-protobuf/src/main/resources/google/protobuf/empty.proto b/foundations/foundation-protobuf/src/main/resources/google/protobuf/empty.proto new file mode 100644 index 00000000000..b87c89dcfce --- /dev/null +++ b/foundations/foundation-protobuf/src/main/resources/google/protobuf/empty.proto @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option go_package = "google.golang.org/protobuf/types/known/emptypb"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +message Empty {} diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBoolSchema.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBoolSchema.java index a82fc971c34..5862c723bfc 100644 --- a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBoolSchema.java +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/internal/schema/scalar/TestBoolSchema.java @@ -36,7 +36,7 @@ public void testTrue() throws Throwable { // equalsIgnoreCase doTestFromString("true"); - doTestFromString("trUe"); + doTestFromString("true"); } @Test @@ -83,4 +83,3 @@ public void type_invalid() throws Throwable { exception.getMessage()); } } - diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestBase.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestBase.java index c8c810c6505..c73caf9a327 100644 --- a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestBase.java +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestBase.java @@ -79,25 +79,25 @@ private TestEngineResult runOneEngine(ProtubufCodecEngine engine, Object model, { long msStart = System.currentTimeMillis(); for (int idx = 0; idx < count; idx++) { - engineResult.serBytes = engine.serialize(model); + engineResult.serializeBytes = engine.serialize(model); } long msEnd = System.currentTimeMillis(); - engineResult.msSerTime = msEnd - msStart; + engineResult.msSerializeTime = msEnd - msStart; } // deserialize { long msStart = System.currentTimeMillis(); for (int idx = 0; idx < count; idx++) { - engineResult.deserResult = engine.deserialize(engineResult.serBytes); + engineResult.deserializeResult = engine.deserialize(engineResult.serializeBytes); } long msEnd = System.currentTimeMillis(); - engineResult.msDeserTime = msEnd - msStart; + engineResult.msDeserializeTime = msEnd - msStart; } - engineResult.deserResultBytes = engine.serialize(engineResult.deserResult); + engineResult.deserializeResultBytes = engine.serialize(engineResult.deserializeResult); return engineResult; } } diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestEngineResult.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestEngineResult.java index ffa08506df7..f02ee12092a 100644 --- a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestEngineResult.java +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestEngineResult.java @@ -20,14 +20,14 @@ public class TestEngineResult { public String engineName; // serialize - public long msSerTime; + public long msSerializeTime; - public byte[] serBytes; + public byte[] serializeBytes; // deserialize - public long msDeserTime; + public long msDeserializeTime; - public Object deserResult; + public Object deserializeResult; - public byte[] deserResultBytes; + public byte[] deserializeResultBytes; } diff --git a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestProtoPerformance.java b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestProtoPerformance.java index eea36041283..98e2f5b7855 100644 --- a/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestProtoPerformance.java +++ b/foundations/foundation-protobuf/src/test/java/org/apache/servicecomb/foundation/protobuf/performance/TestProtoPerformance.java @@ -65,17 +65,17 @@ private static void printResult(TestResult result) { System.out.printf(" " + strFmt + "\n", result.engineResults.stream().map(r -> r.engineName).toArray()); - System.out.printf("ser time(ms) : " + numberFmt + "\n", - result.engineResults.stream().map(r -> r.msSerTime).toArray()); - System.out.printf("ser len : " + numberFmt + "\n", - result.engineResults.stream().map(r -> r.serBytes.length).toArray()); + System.out.printf("serialize time(ms) : " + numberFmt + "\n", + result.engineResults.stream().map(r -> r.msSerializeTime).toArray()); + System.out.printf("serialize len : " + numberFmt + "\n", + result.engineResults.stream().map(r -> r.serializeBytes.length).toArray()); - System.out.printf("deser time(ms): " + numberFmt + "\n", - result.engineResults.stream().map(r -> r.msDeserTime).toArray()); - System.out.printf("deser->ser len: " + numberFmt + "\n", - result.engineResults.stream().map(r -> r.deserResultBytes.length).toArray()); + System.out.printf("deserialize time(ms): " + numberFmt + "\n", + result.engineResults.stream().map(r -> r.msDeserializeTime).toArray()); + System.out.printf("deserialize->serialize len: " + numberFmt + "\n", + result.engineResults.stream().map(r -> r.deserializeResultBytes.length).toArray()); - System.out.printf("ser+deser(ms) : " + numberFmt + "\n\n", - result.engineResults.stream().map(r -> r.msSerTime + r.msDeserTime).toArray()); + System.out.printf("serialize+deserialize(ms) : " + numberFmt + "\n\n", + result.engineResults.stream().map(r -> r.msSerializeTime + r.msDeserializeTime).toArray()); } } diff --git a/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto b/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto index 1a0e33938bd..b1804922666 100644 --- a/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto +++ b/foundations/foundation-protobuf/src/test/resources/jacksonRoot.proto @@ -5,9 +5,9 @@ The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -95,4 +95,4 @@ enum Color { message User { optional string name = 1; -} \ No newline at end of file +} diff --git a/foundations/foundation-protobuf/src/test/resources/method.proto b/foundations/foundation-protobuf/src/test/resources/method.proto index 08a948ca03f..414b7643880 100644 --- a/foundations/foundation-protobuf/src/test/resources/method.proto +++ b/foundations/foundation-protobuf/src/test/resources/method.proto @@ -42,4 +42,4 @@ message Response { message User { string name = 1; repeated User friends = 2; -} \ No newline at end of file +} diff --git a/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto b/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto index 0972a3133fa..1e9f7f41076 100644 --- a/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto +++ b/foundations/foundation-protobuf/src/test/resources/protobufRoot.proto @@ -5,9 +5,9 @@ The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 - + Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -109,4 +109,4 @@ message User { string name = 1; Root typeRecursive = 2; -} \ No newline at end of file +} diff --git a/foundations/foundation-registry/pom.xml b/foundations/foundation-registry/pom.xml index e524790f355..d4cca8111c4 100644 --- a/foundations/foundation-registry/pom.xml +++ b/foundations/foundation-registry/pom.xml @@ -21,7 +21,7 @@ foundations org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/DiscoveryManager.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/DiscoveryManager.java index 4025e6f126c..edcd3b3caef 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/DiscoveryManager.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/DiscoveryManager.java @@ -27,8 +27,10 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.servicecomb.foundation.common.NamedThreadFactory; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.utils.LambdaUtils; import org.apache.servicecomb.registry.api.Discovery; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.api.LifeCycle; @@ -56,6 +58,8 @@ void onInstancesChanged(String registryName, String application, String serviceN private final InstancePing ping; + private final HealthCheckTask healthCheckTask = new HealthCheckTask(); + // application:serviceName:instanceId private final Map>> allInstances = new ConcurrentHashMapEx<>(); @@ -75,74 +79,11 @@ public DiscoveryManager(List> discoveryLi discovery.setInstanceChangedListener(this::onInstancesChanged); } this.ping = pings.get(0); - task = Executors.newScheduledThreadPool(1, (runnable) -> { - Thread thread = new Thread(runnable, "discovery-manager-task") { - @Override - public void run() { - try { - runnable.run(); - } catch (Throwable e) { - LOGGER.error("discovery manager task error, not allowed please fix. ", e); - } - } - }; - thread.setPriority(Thread.MIN_PRIORITY); - return thread; - }); + task = Executors.newScheduledThreadPool(1, new NamedThreadFactory("discovery-manager-task")); } - private void doTask() { - Map>> removed = new HashMap<>(); - for (Entry>> apps : allInstances.entrySet()) { - for (Entry> services : apps.getValue().entrySet()) { - boolean changed = false; - for (StatefulDiscoveryInstance instance : services.getValue().values()) { - // check isolated time - if (instance.getIsolationStatus() == IsolationStatus.ISOLATED && - instance.getIsolatedTime() + instance.getIsolateDuration() < System.currentTimeMillis()) { - instance.setIsolationStatus(IsolationStatus.NORMAL); - changed = true; - } - // check ping status - if (System.currentTimeMillis() - instance.getPingTime() > 180_000L) { - boolean pingResult = ping.ping(instance); - if (pingResult && instance.getPingStatus() != PingStatus.OK) { - instance.setPingStatus(PingStatus.OK); - changed = true; - } else if (!pingResult && instance.getPingStatus() != PingStatus.FAIL) { - instance.setPingStatus(PingStatus.FAIL); - changed = true; - } - instance.setPingTime(System.currentTimeMillis()); - } - // check unused - if (instance.getHistoryStatus() == HistoryStatus.HISTORY) { - if (instance.getStatus() != MicroserviceInstanceStatus.UP || - instance.getPingStatus() == PingStatus.FAIL || - instance.getIsolationStatus() == IsolationStatus.ISOLATED) { - removed.computeIfAbsent(apps.getKey(), k -> new HashMap<>()) - .computeIfAbsent(services.getKey(), k -> new ArrayList<>()).add(instance.getInstanceId()); - LOGGER.info("Remove instance {}/{}/{}/{}/{}/{}/{}/{}", - apps.getKey(), services.getKey(), instance.getRegistryName(), - instance.getInstanceId(), instance.getHistoryStatus(), - instance.getStatus(), instance.getPingStatus(), instance.getIsolationStatus()); - changed = true; - } - } - } - if (changed) { - rebuildVersionCache(apps.getKey(), services.getKey()); - } - } - } - // remove unused - for (Entry>> apps : removed.entrySet()) { - for (Entry> services : apps.getValue().entrySet()) { - for (String instance : services.getValue()) { - allInstances.get(apps.getKey()).get(services.getKey()).remove(instance); - } - } - } + public Discovery getPrimaryDiscovery() { + return this.discoveryList.get(0); } private void onInstancesChanged(String application, String serviceName, @@ -152,10 +93,6 @@ private void onInstancesChanged(String application, String serviceName, private void onInstancesChanged(String registryName, String application, String serviceName, List instances) { - for (InstanceChangeListener listener : this.instanceChangeListeners) { - listener.onInstancesChanged(registryName, application, serviceName, instances); - } - Map statefulInstances = allInstances.computeIfAbsent(application, key -> new ConcurrentHashMapEx<>()).computeIfAbsent(serviceName, key -> new ConcurrentHashMapEx<>()); @@ -195,6 +132,10 @@ private void onInstancesChanged(String registryName, String application, String application, serviceName, instances.size(), instanceInfo); rebuildVersionCache(application, serviceName); + + for (InstanceChangeListener listener : this.instanceChangeListeners) { + listener.onInstancesChanged(registryName, application, serviceName, instances); + } } public void addInstanceChangeListener(InstanceChangeListener instanceChangeListener) { @@ -242,7 +183,7 @@ private VersionedCache calcAvailableInstance(String application, String serviceN continue; } if (instance.getHistoryStatus() == HistoryStatus.HISTORY - && instance.getMicroserviceInstanceStatus() == MicroserviceInstanceStatus.UP + && instance.getStatus() == MicroserviceInstanceStatus.UP && instance.getPingStatus() == PingStatus.OK && instance.getIsolationStatus() == IsolationStatus.NORMAL) { result.add(instance); @@ -303,14 +244,14 @@ public List findServiceInstances(String application @Override public void destroy() { - discoveryList.forEach(LifeCycle::destroy); + discoveryList.forEach(LambdaUtils.ignoreException(LifeCycle::destroy)); task.shutdownNow(); } @Override public void run() { discoveryList.forEach(LifeCycle::run); - task.scheduleWithFixedDelay(this::doTask, 3, 3, TimeUnit.SECONDS); + task.schedule(healthCheckTask, 3, TimeUnit.SECONDS); } @Override @@ -329,4 +270,68 @@ public String info() { }); return result.toString(); } + + class HealthCheckTask implements Runnable { + + @Override + public void run() { + try { + Map>> removed = new HashMap<>(); + for (Entry>> apps : DiscoveryManager.this.allInstances.entrySet()) { + for (Entry> services : apps.getValue().entrySet()) { + boolean changed = false; + for (StatefulDiscoveryInstance instance : services.getValue().values()) { + // check isolated time + if (instance.getIsolationStatus() == IsolationStatus.ISOLATED && + instance.getIsolatedTime() + instance.getIsolateDuration() < System.currentTimeMillis()) { + instance.setIsolationStatus(IsolationStatus.NORMAL); + changed = true; + } + // check ping status + if (System.currentTimeMillis() - instance.getPingTime() > 60_000L) { + boolean pingResult = DiscoveryManager.this.ping.ping(instance); + if (pingResult && instance.getPingStatus() != PingStatus.OK) { + instance.setPingStatus(PingStatus.OK); + changed = true; + } else if (!pingResult && instance.getPingStatus() != PingStatus.FAIL) { + instance.setPingStatus(PingStatus.FAIL); + changed = true; + } + instance.setPingTime(System.currentTimeMillis()); + } + // check unused + if (instance.getHistoryStatus() == HistoryStatus.HISTORY) { + if (instance.getStatus() != MicroserviceInstanceStatus.UP || + instance.getPingStatus() == PingStatus.FAIL || + instance.getIsolationStatus() == IsolationStatus.ISOLATED) { + removed.computeIfAbsent(apps.getKey(), k -> new HashMap<>()) + .computeIfAbsent(services.getKey(), k -> new ArrayList<>()).add(instance.getInstanceId()); + LOGGER.info("Remove instance {}/{}/{}/{}/{}/{}/{}/{}", + apps.getKey(), services.getKey(), instance.getRegistryName(), + instance.getInstanceId(), instance.getHistoryStatus(), + instance.getStatus(), instance.getPingStatus(), instance.getIsolationStatus()); + changed = true; + } + } + } + if (changed) { + rebuildVersionCache(apps.getKey(), services.getKey()); + } + } + } + // remove unused + for (Entry>> apps : removed.entrySet()) { + for (Entry> services : apps.getValue().entrySet()) { + for (String instance : services.getValue()) { + DiscoveryManager.this.allInstances.get(apps.getKey()).get(services.getKey()).remove(instance); + } + } + } + } catch (Throwable e) { + LOGGER.error("discovery manager task error. ", e); + } finally { + DiscoveryManager.this.task.schedule(this, 3, TimeUnit.SECONDS); + } + } + } } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/InvocationExceptionToProducerResponseConverter.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationId.java similarity index 59% rename from swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/InvocationExceptionToProducerResponseConverter.java rename to foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationId.java index 2c3ddfd4b5d..9932f5fdfc9 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/InvocationExceptionToProducerResponseConverter.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationId.java @@ -14,20 +14,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.swagger.invocation.exception; +package org.apache.servicecomb.registry; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; +import java.util.UUID; -public class InvocationExceptionToProducerResponseConverter implements - ExceptionToProducerResponseConverter { - @Override - public Class getExceptionClass() { - return InvocationException.class; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RegistrationId { + private static final Logger LOGGER = LoggerFactory.getLogger(RegistrationId.class); + + private final String instanceId; + + public RegistrationId() { + this.instanceId = buildInstanceId(); + LOGGER.info("initialized global registration id {}", this.instanceId); + } + + public String getInstanceId() { + return instanceId; } - @Override - public Response convert(SwaggerInvocation swaggerInvocation, InvocationException e) { - return Response.failResp(e); + private static String buildInstanceId() { + return UUID.randomUUID().toString(); } } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationManager.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationManager.java index 3d07a9122b9..9c135d380c8 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationManager.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistrationManager.java @@ -23,6 +23,8 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; +import org.apache.servicecomb.foundation.common.utils.LambdaUtils; +import org.apache.servicecomb.foundation.common.utils.SPIEnabled; import org.apache.servicecomb.registry.api.LifeCycle; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; @@ -39,7 +41,7 @@ public RegistrationManager(List> re this.registrationList = Collections.emptyList(); return; } - this.registrationList = registrationList; + this.registrationList = registrationList.stream().filter(SPIEnabled::enabled).collect(Collectors.toList()); } /** @@ -74,6 +76,10 @@ public String getServiceId(String registryName) { return registration.get().getMicroserviceInstance().getServiceId(); } + public Registration getPrimaryRegistration() { + return registrationList.get(0); + } + public void updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { registrationList .forEach(registration -> registration.updateMicroserviceInstanceStatus(status)); @@ -95,7 +101,7 @@ public void addEndpoint(String endpoint) { } public void destroy() { - registrationList.forEach(LifeCycle::destroy); + registrationList.forEach(LambdaUtils.ignoreException(LifeCycle::destroy)); } public void run() { diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistryConfiguration.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistryConfiguration.java index b3c83b64a8c..7a1d72460cc 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistryConfiguration.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/RegistryConfiguration.java @@ -35,35 +35,40 @@ @SuppressWarnings("unused") public class RegistryConfiguration { @Bean - public RegistrationManager registrationManager( + public RegistrationManager scbRegistrationManager( List> registrationList) { return new RegistrationManager(registrationList); } @Bean - public TelnetInstancePing telnetInstancePing() { + public TelnetInstancePing scbTelnetInstancePing() { return new TelnetInstancePing(); } @Bean - public DiscoveryManager discoveryManager( + public DiscoveryManager scbDiscoveryManager( List> discoveryList, List pingList) { return new DiscoveryManager(discoveryList, pingList); } @Bean - public DiscoveryTree discoveryTree(DiscoveryManager discoveryManager) { + public DiscoveryTree scbDiscoveryTree(DiscoveryManager discoveryManager) { return new DiscoveryTree(discoveryManager); } @Bean - public InstanceStatusDiscoveryFilter instanceStatusDiscoveryFilter() { + public InstanceStatusDiscoveryFilter scbInstanceStatusDiscoveryFilter() { return new InstanceStatusDiscoveryFilter(); } @Bean - public MicroserviceInstanceCache microserviceInstanceCache(DiscoveryManager discoveryManager) { + public MicroserviceInstanceCache scbMicroserviceInstanceCache(DiscoveryManager discoveryManager) { return new MicroserviceInstanceCache(discoveryManager); } + + @Bean + public RegistrationId scbRegistrationId() { + return new RegistrationId(); + } } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Discovery.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Discovery.java index 6fb382835ad..0c57ed6b605 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Discovery.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Discovery.java @@ -55,6 +55,14 @@ interface InstanceChangedListener { */ List findServiceInstances(String application, String serviceName); + /** + * Find all services of this application. + * + * @param application application + * @return all services match the criteria. + */ + List findServices(String application); + /** * Discovery can call InstanceChangedListener when instance get changed. */ diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstance.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstance.java index 0dd6d0237c0..c5c872beefd 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstance.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/MicroserviceInstance.java @@ -93,4 +93,10 @@ public interface MicroserviceInstance { default String getServiceId() { return ""; } + + /** + * Service status(Required): status of this microservice. + * + */ + MicroserviceInstanceStatus getStatus(); } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Registration.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Registration.java index c5529619f56..014da5f147b 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Registration.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/Registration.java @@ -22,42 +22,42 @@ /** - * This is the core service registration interface.
+ * This is the core service registration interface. */ public interface Registration extends SPIEnabled, SPIOrder, LifeCycle { String name(); /** - * get MicroserviceInstance
- * + * get MicroserviceInstance + *

* Life Cycle:This method is called anytime after run. */ R getMicroserviceInstance(); /** - * update MicroserviceInstance status
- * + * update MicroserviceInstance status + *

* Life Cycle:This method is called anytime after run. */ boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status); /** - * adding schemas to Microservice
- * + * adding schemas to Microservice + *

* Life Cycle:This method is called after init and before run. */ void addSchema(String schemaId, String content); /** - * adding endpoints to MicroserviceInstance
- * + * adding endpoints to MicroserviceInstance + *

* Life Cycle:This method is called after init and before run. */ void addEndpoint(String endpoint); /** - * adding property to MicroserviceInstance
- * + * adding property to MicroserviceInstance + *

* Life Cycle:This method is called after init and before run. */ void addProperty(String key, String value); diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/RegistrationInstance.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/RegistrationInstance.java index afe2bc7a878..a7d7cafda88 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/RegistrationInstance.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/api/RegistrationInstance.java @@ -20,15 +20,4 @@ * Microserivce instance registration object. */ public interface RegistrationInstance extends MicroserviceInstance { - /** - * Initial status when registering(Optional). This method is - * implementation specific and may be not used. - */ - MicroserviceInstanceStatus getInitialStatus(); - - /** - * Ready status when microservice instance is ready for service(Optional).This method is - * implementation specific and may be not used. - */ - MicroserviceInstanceStatus getReadyStatus(); } diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractDiscoveryFilter.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractDiscoveryFilter.java index 5883ec51bf3..f274799c30e 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractDiscoveryFilter.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractDiscoveryFilter.java @@ -19,19 +19,21 @@ import java.util.ArrayList; +import org.apache.servicecomb.config.DynamicProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; public abstract class AbstractDiscoveryFilter implements DiscoveryFilter { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDiscoveryFilter.class); - protected Environment environment; + protected Boolean enabled; + + protected DynamicProperties dynamicProperties; @Autowired - public void setEnvironment(Environment environment) { - this.environment = environment; + public void setDynamicProperties(DynamicProperties dynamicProperties) { + this.dynamicProperties = dynamicProperties; } @Override diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractEndpointDiscoveryFilter.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractEndpointDiscoveryFilter.java index 45a20e74aba..f4b464f0097 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractEndpointDiscoveryFilter.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/AbstractEndpointDiscoveryFilter.java @@ -17,10 +17,10 @@ package org.apache.servicecomb.registry.discovery; -import java.net.URI; import java.util.ArrayList; import java.util.List; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +29,10 @@ public abstract class AbstractEndpointDiscoveryFilter implements DiscoveryFilter private static final String ALL_TRANSPORT = ""; + private static final String WEBSOCKET_TRANSPORT = "websocket"; + + private static final String REST_TRANSPORT = "rest"; + @Override public boolean isGroupingFilter() { return true; @@ -48,13 +52,16 @@ protected DiscoveryTreeNode createDiscoveryTreeNode(String expectTransportName, for (StatefulDiscoveryInstance instance : instances) { for (String endpoint : instance.getEndpoints()) { try { - URI uri = URI.create(endpoint); - String transportName = uri.getScheme(); - if (!isTransportNameMatch(transportName, expectTransportName)) { + URIEndpointObject endpointObject = new URIEndpointObject(endpoint); + String transPortName = endpointObject.getSchema(); + if (endpointObject.isWebsocketEnabled() && WEBSOCKET_TRANSPORT.equals(expectTransportName)) { + transPortName = WEBSOCKET_TRANSPORT; + } + if (!isTransportNameMatch(transPortName, expectTransportName)) { continue; } - Object objEndpoint = createEndpoint(context, transportName, endpoint, instance); + Object objEndpoint = createEndpoint(context, transPortName, endpoint, instance); if (objEndpoint == null) { continue; } @@ -72,7 +79,10 @@ protected DiscoveryTreeNode createDiscoveryTreeNode(String expectTransportName, } protected boolean isTransportNameMatch(String transportName, String expectTransportName) { - return ALL_TRANSPORT.equals(expectTransportName) || transportName.equals(expectTransportName); + if (ALL_TRANSPORT.equals(expectTransportName)) { + return true; + } + return transportName.equals(expectTransportName); } protected abstract String findTransportName(DiscoveryContext context, DiscoveryTreeNode parent); diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTree.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTree.java index d040cf447b1..c7b70f606b9 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTree.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTree.java @@ -92,10 +92,14 @@ public void setDiscoveryFilters(List filters) { } private void log() { + StringBuilder sb = new StringBuilder(); + sb.append("DiscoveryFilters(name, enabled, order, group):"); for (DiscoveryFilter filter : filters) { - LOGGER.info("DiscoveryFilter {}, enabled {}, order {}.", - filter.getClass().getName(), filter.enabled(), filter.getOrder()); + sb.append("(").append(filter.getClass().getName()).append(",") + .append(filter.enabled()).append(",").append(filter.getOrder()).append(",") + .append(filter.isGroupingFilter()).append(")"); } + LOGGER.info(sb.toString()); } boolean isMatch(VersionedCache existing, VersionedCache inputCache) { @@ -181,7 +185,7 @@ protected DiscoveryTreeNode doDiscovery(DiscoveryContext context, DiscoveryTreeN } // no rerun support, go on even result is empty - // because maybe some filter use other mechanism to create a instance(eg:domain name) + // because maybe some filter use other mechanism to create an instance(eg:domain name) } parent = child; diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTreeNode.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTreeNode.java index 3dda15f6c4b..e0f7c828c10 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTreeNode.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/DiscoveryTreeNode.java @@ -25,7 +25,7 @@ public class DiscoveryTreeNode extends VersionedCache { private volatile boolean childrenInited; - private int level; + private volatile int level; protected Map attributes = new ConcurrentHashMapEx<>(); diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstanceStatusDiscoveryFilter.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstanceStatusDiscoveryFilter.java index ef60b4a3eff..e7a4029bdeb 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstanceStatusDiscoveryFilter.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/InstanceStatusDiscoveryFilter.java @@ -32,6 +32,8 @@ public class InstanceStatusDiscoveryFilter extends AbstractGroupDiscoveryFilter public static final String GROUP_SIZE = "status_group_size"; + public static final String SERVICECOMB_LOADBALANCE_FILTER_STATUS_ENABLED = "servicecomb.loadbalance.filter.status.enabled"; + @Override public int getOrder() { return -10000; @@ -39,7 +41,13 @@ public int getOrder() { @Override public boolean enabled() { - return environment.getProperty("servicecomb.loadbalance.filter.status.enabled", Boolean.class, true); + + if (enabled == null) { + enabled = dynamicProperties.getBooleanProperty(SERVICECOMB_LOADBALANCE_FILTER_STATUS_ENABLED, + value -> enabled = value, + true); + } + return enabled; } @Override @@ -69,21 +77,21 @@ public void init(DiscoveryContext context, DiscoveryTreeNode parent) { for (StatefulDiscoveryInstance instance : instances) { if (HistoryStatus.CURRENT == instance.getHistoryStatus() && - MicroserviceInstanceStatus.UP == instance.getMicroserviceInstanceStatus() && + MicroserviceInstanceStatus.UP == instance.getStatus() && PingStatus.OK == instance.getPingStatus() && IsolationStatus.NORMAL == instance.getIsolationStatus()) { level0.add(instance); continue; } if (HistoryStatus.CURRENT == instance.getHistoryStatus() && - MicroserviceInstanceStatus.UP == instance.getMicroserviceInstanceStatus() && + MicroserviceInstanceStatus.UP == instance.getStatus() && PingStatus.UNKNOWN == instance.getPingStatus() && IsolationStatus.NORMAL == instance.getIsolationStatus()) { level1.add(instance); continue; } if (HistoryStatus.HISTORY == instance.getHistoryStatus() && - MicroserviceInstanceStatus.UP == instance.getMicroserviceInstanceStatus() && + MicroserviceInstanceStatus.UP == instance.getStatus() && PingStatus.OK == instance.getPingStatus() && IsolationStatus.NORMAL == instance.getIsolationStatus()) { level2.add(instance); diff --git a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/StatefulDiscoveryInstance.java b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/StatefulDiscoveryInstance.java index fbcb1614330..394b2b03ccc 100644 --- a/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/StatefulDiscoveryInstance.java +++ b/foundations/foundation-registry/src/main/java/org/apache/servicecomb/registry/discovery/StatefulDiscoveryInstance.java @@ -46,8 +46,6 @@ public enum HistoryStatus { private final DiscoveryInstance discoveryInstance; - private MicroserviceInstanceStatus microserviceInstanceStatus = MicroserviceInstanceStatus.UP; - private IsolationStatus isolationStatus = IsolationStatus.NORMAL; private long isolatedTime; @@ -56,7 +54,7 @@ public enum HistoryStatus { private PingStatus pingStatus = PingStatus.UNKNOWN; - private long pingTime = 0; + private long pingTime = System.currentTimeMillis(); private HistoryStatus historyStatus = HistoryStatus.CURRENT; @@ -64,15 +62,6 @@ public StatefulDiscoveryInstance(DiscoveryInstance discoveryInstance) { this.discoveryInstance = discoveryInstance; } - public MicroserviceInstanceStatus getMicroserviceInstanceStatus() { - return microserviceInstanceStatus; - } - - public void setMicroserviceInstanceStatus( - MicroserviceInstanceStatus microserviceInstanceStatus) { - this.microserviceInstanceStatus = microserviceInstanceStatus; - } - public IsolationStatus getIsolationStatus() { return isolationStatus; } diff --git a/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.consumer.MicroserviceInstancePing b/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.consumer.MicroserviceInstancePing index 8493c8a6e17..56169c4507c 100644 --- a/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.consumer.MicroserviceInstancePing +++ b/foundations/foundation-registry/src/main/resources/META-INF/services/org.apache.servicecomb.registry.consumer.MicroserviceInstancePing @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.registry.consumer.SimpleMicroserviceInstancePing \ No newline at end of file +org.apache.servicecomb.registry.consumer.SimpleMicroserviceInstancePing diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/TestDiscoveryManager.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/TestDiscoveryManager.java index 77762b0fcd4..bd50cc85748 100644 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/TestDiscoveryManager.java +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/TestDiscoveryManager.java @@ -112,6 +112,11 @@ public List findServiceInstances(String application, String return null; } + @Override + public List findServices(String application) { + return null; + } + @Override public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTree.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTree.java index a30b892f8b4..d726d1b59c0 100644 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTree.java +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestDiscoveryTree.java @@ -24,6 +24,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.registry.DiscoveryManager; @@ -32,7 +33,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.springframework.core.env.Environment; public class TestDiscoveryTree { DiscoveryContext context = new DiscoveryContext(); @@ -238,11 +238,12 @@ public void test_one_service_concurrent_correct() throws Exception { DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); DiscoveryContext discoveryContext = new DiscoveryContext(); InstanceStatusDiscoveryFilter filter = new InstanceStatusDiscoveryFilter(); - Environment environment = Mockito.mock(Environment.class); - Mockito.when(environment - .getProperty("servicecomb.loadbalance.filter.status.enabled", Boolean.class, true)) + DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); + Mockito.when(dynamicProperties.getBooleanProperty(Mockito.eq("servicecomb.loadbalance.filter.status.enabled"), + Mockito.any(), + Mockito.eq(true))) .thenReturn(true); - filter.setEnvironment(environment); + filter.setDynamicProperties(dynamicProperties); discoveryTree.setDiscoveryFilters(List.of(filter)); StatefulDiscoveryInstance instance1 = Mockito.mock(StatefulDiscoveryInstance.class); diff --git a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestInstanceStatusDiscoveryFilter.java b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestInstanceStatusDiscoveryFilter.java index eeecc420096..8e7307098b9 100644 --- a/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestInstanceStatusDiscoveryFilter.java +++ b/foundations/foundation-registry/src/test/java/org/apache/servicecomb/registry/discovery/TestInstanceStatusDiscoveryFilter.java @@ -36,22 +36,22 @@ public void test_all_group_correct_init() { List instances = new ArrayList<>(); StatefulDiscoveryInstance instance1 = Mockito.mock(StatefulDiscoveryInstance.class); Mockito.when(instance1.getHistoryStatus()).thenReturn(HistoryStatus.CURRENT); - Mockito.when(instance1.getMicroserviceInstanceStatus()).thenReturn(MicroserviceInstanceStatus.UP); + Mockito.when(instance1.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); Mockito.when(instance1.getPingStatus()).thenReturn(PingStatus.OK); Mockito.when(instance1.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); StatefulDiscoveryInstance instance2 = Mockito.mock(StatefulDiscoveryInstance.class); Mockito.when(instance2.getHistoryStatus()).thenReturn(HistoryStatus.CURRENT); - Mockito.when(instance2.getMicroserviceInstanceStatus()).thenReturn(MicroserviceInstanceStatus.UP); + Mockito.when(instance2.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); Mockito.when(instance2.getPingStatus()).thenReturn(PingStatus.UNKNOWN); Mockito.when(instance2.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); StatefulDiscoveryInstance instance3 = Mockito.mock(StatefulDiscoveryInstance.class); Mockito.when(instance3.getHistoryStatus()).thenReturn(HistoryStatus.HISTORY); - Mockito.when(instance3.getMicroserviceInstanceStatus()).thenReturn(MicroserviceInstanceStatus.UP); + Mockito.when(instance3.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); Mockito.when(instance3.getPingStatus()).thenReturn(PingStatus.OK); Mockito.when(instance3.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); StatefulDiscoveryInstance instance4 = Mockito.mock(StatefulDiscoveryInstance.class); Mockito.when(instance4.getHistoryStatus()).thenReturn(HistoryStatus.CURRENT); - Mockito.when(instance4.getMicroserviceInstanceStatus()).thenReturn(MicroserviceInstanceStatus.UP); + Mockito.when(instance4.getStatus()).thenReturn(MicroserviceInstanceStatus.UP); Mockito.when(instance4.getPingStatus()).thenReturn(PingStatus.FAIL); Mockito.when(instance4.getIsolationStatus()).thenReturn(IsolationStatus.NORMAL); instances.addAll(Arrays.asList(instance1, instance2, instance3, instance4)); diff --git a/foundations/foundation-registry/src/test/resources/log4j2.xml b/foundations/foundation-registry/src/test/resources/log4j2.xml index 8cee07220ec..3125ebd11e4 100644 --- a/foundations/foundation-registry/src/test/resources/log4j2.xml +++ b/foundations/foundation-registry/src/test/resources/log4j2.xml @@ -16,14 +16,14 @@ under the License. --> - - - - - - - - - - + + + + + + + + + + diff --git a/foundations/foundation-spi/pom.xml b/foundations/foundation-spi/pom.xml index d5184a62f47..aa38214b3ee 100644 --- a/foundations/foundation-spi/pom.xml +++ b/foundations/foundation-spi/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb foundations - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundation-spi Java Chassis::Foundations::SSL @@ -41,19 +41,4 @@ test - diff --git a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderLoader.java b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderLoader.java index 39e0bfa1a5d..668e77faa38 100644 --- a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderLoader.java +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderLoader.java @@ -29,6 +29,10 @@ public class AuthHeaderLoader { private static final AuthHeaderLoader INSTANCE = new AuthHeaderLoader(); + private AuthHeaderLoader() { + + } + public static AuthHeaderLoader getInstance() { return INSTANCE; } diff --git a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderProvider.java b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderProvider.java index 313c19ee5ba..0e036e21a58 100644 --- a/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderProvider.java +++ b/foundations/foundation-spi/src/main/java/org/apache/servicecomb/foundation/auth/AuthHeaderProvider.java @@ -21,11 +21,21 @@ import java.util.Map; public interface AuthHeaderProvider { - default Map authHeaders() { + /** + * Obtain RBAC authentication request header, host is the key of cache + * + * @param host engine address ip + * @return auth headers + */ + default Map authHeaders(String host) { return new HashMap<>(0); } default Map getSignAuthHeaders(SignRequest request) { - return authHeaders(); + String host = ""; + if (request != null && request.getEndpoint() != null) { + host = request.getEndpoint().getHost(); + } + return authHeaders(host); } } diff --git a/foundations/foundation-spi/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.common.utils.SPIServiceDef b/foundations/foundation-spi/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.common.utils.SPIServiceDef index eebf462d01b..7d8b6d492a4 100644 --- a/foundations/foundation-spi/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.common.utils.SPIServiceDef +++ b/foundations/foundation-spi/src/test/resources/META-INF/services/org.apache.servicecomb.foundation.common.utils.SPIServiceDef @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.foundation.common.utils.SPIServiceDefImpl \ No newline at end of file +org.apache.servicecomb.foundation.common.utils.SPIServiceDefImpl diff --git a/foundations/foundation-ssl/pom.xml b/foundations/foundation-ssl/pom.xml index 342dce13d28..3cf7cdcb485 100644 --- a/foundations/foundation-ssl/pom.xml +++ b/foundations/foundation-ssl/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb foundations - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundation-ssl Java Chassis::Foundations::SSL diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLManager.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLManager.java index af7a4351e5a..b7136f7752e 100644 --- a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLManager.java +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLManager.java @@ -171,7 +171,7 @@ public static SSLServerSocket createSSLServerSocket(SSLOption option, setClientAuth(option, socket); return socket; } catch (UnknownHostException e) { - throw new IllegalArgumentException("unkown host"); + throw new IllegalArgumentException("unknown host"); } catch (IOException e) { throw new IllegalArgumentException("unable create socket"); } @@ -185,7 +185,7 @@ public static SSLSocket createSSLSocket(SSLOption option, SSLCustom custom) { (SSLSocket) factory.createSocket(); socket.setEnabledProtocols(option.getProtocols().split(",")); String[] supported = socket.getSupportedCipherSuites(); - String[] enabled = option.getCiphers().split(","); + String[] enabled = option.getCiphers().split("\\s*,\\s*"); socket.setEnabledCipherSuites(getEnabledCiphers(supported, enabled)); return socket; } catch (UnknownHostException e) { diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOption.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOption.java index 77c17788351..19160bb3927 100644 --- a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOption.java +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/SSLOption.java @@ -47,7 +47,7 @@ public final class SSLOption { DEFAULT_OPTION.setCheckCNHost(false); DEFAULT_OPTION.setCheckCNWhite(false); DEFAULT_OPTION.setCheckCNWhiteFile("white.list"); - DEFAULT_OPTION.setAllowRenegociate(true); + DEFAULT_OPTION.setAllowRenegotiate(true); DEFAULT_OPTION.setStorePath("internal"); DEFAULT_OPTION.setTrustStore("trust.jks"); DEFAULT_OPTION.setTrustStoreType("JKS"); @@ -72,7 +72,7 @@ public final class SSLOption { private String checkCNWhiteFile; - private boolean allowRenegociate; + private boolean allowRenegotiate; private String clientAuth; @@ -126,8 +126,8 @@ public void setCheckCNWhiteFile(String checkCNWhiteFile) { this.checkCNWhiteFile = checkCNWhiteFile; } - public void setAllowRenegociate(boolean allowRenegociate) { - this.allowRenegociate = allowRenegociate; + public void setAllowRenegotiate(boolean allowRenegotiate) { + this.allowRenegotiate = allowRenegotiate; } public void setStorePath(String storePath) { @@ -186,8 +186,8 @@ public String getCheckCNWhiteFile() { return checkCNWhiteFile; } - public boolean isAllowRenegociate() { - return allowRenegociate; + public boolean isAllowRenegotiate() { + return allowRenegotiate; } public String getStorePath() { @@ -314,10 +314,10 @@ public static SSLOption build(String tag, Environment environment) { DEFAULT_OPTION.getCiphers(), "ssl." + tag + ".checkCN.white.file", "ssl.checkCN.white.file"); - option.allowRenegociate = getBooleanProperty(environment, - DEFAULT_OPTION.isAllowRenegociate(), - "ssl." + tag + ".allowRenegociate", - "ssl.allowRenegociate"); + option.allowRenegotiate = getBooleanProperty(environment, + DEFAULT_OPTION.isAllowRenegotiate(), + "ssl." + tag + ".allowRenegotiate", + "ssl.allowRenegotiate"); option.storePath = getStringProperty(environment, DEFAULT_OPTION.getStorePath(), @@ -365,7 +365,7 @@ private void fromProperty(Properties props) { this.checkCNHost = propBoolean(props, "ssl.checkCN.host"); this.checkCNWhite = propBoolean(props, "ssl.checkCN.white"); this.checkCNWhiteFile = propString(props, "ssl.checkCN.white.file"); - this.allowRenegociate = propBoolean(props, "ssl.allowRenegociate"); + this.allowRenegotiate = propBoolean(props, "ssl.allowRenegotiate"); this.storePath = propString(props, "ssl.storePath"); this.clientAuth = propString(props, "ssl.clientAuth", false); this.trustStore = propString(props, "ssl.trustStore"); diff --git a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java index 1177089097a..597894571c5 100644 --- a/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java +++ b/foundations/foundation-ssl/src/main/java/org/apache/servicecomb/foundation/ssl/TrustManagerExt.java @@ -189,7 +189,7 @@ private void checkCNHost(X509Certificate[] chain, String ip) throws CertificateE } } } catch (SocketException e) { - throw new CertificateException("Get local adrress fail."); + throw new CertificateException("Get local address fail."); } } else if (cnValid(cns, ipTmp)) { return; diff --git a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtilTest.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtilTest.java index f7d046f6562..54955139087 100644 --- a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtilTest.java +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/KeyStoreUtilTest.java @@ -26,13 +26,12 @@ import java.security.cert.CertificateFactory; import java.util.Collection; -import org.apache.commons.lang3.SystemUtils; import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.mockito.Mockito; import mockit.Mock; import mockit.MockUp; +import org.mockito.Mockito; public class KeyStoreUtilTest { final String strFilePath = Thread.currentThread().getContextClassLoader().getResource("").getPath(); @@ -78,14 +77,9 @@ public void testCreateKeyManagersException() { try { KeyStoreUtil.createKeyManagers(keystore, storeKeyValue); } catch (IllegalArgumentException e) { - if (SystemUtils.JAVA_SPECIFICATION_VERSION.startsWith("17")) { Assertions.assertEquals("Bad key store.Get Key failed:" + " Cannot read the array length because \"password\" is null", e.getMessage()); - } else { - Assertions.assertEquals("Bad key store.Get Key failed: null", - e.getMessage()); - } } } diff --git a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLOptionTest.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLOptionTest.java index 558fa255cc5..ae91fcbb697 100644 --- a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLOptionTest.java +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/SSLOptionTest.java @@ -47,7 +47,7 @@ public void setUp() throws Exception { Mockito.when(environment.getProperty("ssl.checkCN.host")).thenReturn("true"); Mockito.when(environment.getProperty("ssl.checkCN.white")).thenReturn("true"); Mockito.when(environment.getProperty("ssl.checkCN.white.file")).thenReturn("white.list"); - Mockito.when(environment.getProperty("ssl.allowRenegociate")).thenReturn("false"); + Mockito.when(environment.getProperty("ssl.allowRenegotiate")).thenReturn("false"); Mockito.when(environment.getProperty("ssl.storePath")).thenReturn("internal"); Mockito.when(environment.getProperty("ssl.trustStore")).thenReturn("trust.jks"); Mockito.when(environment.getProperty("ssl.trustStoreType")).thenReturn("JKS"); @@ -91,9 +91,9 @@ public void testSSLOption() { option.setCheckCNWhiteFile(checkCNWhiteFile); Assertions.assertEquals("white.list", checkCNWhiteFile); - boolean allowRenegociate = option.isAllowRenegociate(); - option.setAllowRenegociate(allowRenegociate); - Assertions.assertFalse(allowRenegociate); + boolean allowRenegotiate = option.isAllowRenegotiate(); + option.setAllowRenegotiate(allowRenegotiate); + Assertions.assertFalse(allowRenegotiate); String storePath = option.getStorePath(); option.setStorePath(storePath); @@ -158,9 +158,9 @@ public void testSSLOptionYaml() { option.setCheckCNWhiteFile(checkCNWhiteFile); Assertions.assertEquals("white.list", checkCNWhiteFile); - boolean allowRenegociate = option.isAllowRenegociate(); - option.setAllowRenegociate(allowRenegociate); - Assertions.assertFalse(allowRenegociate); + boolean allowRenegotiate = option.isAllowRenegotiate(); + option.setAllowRenegotiate(allowRenegotiate); + Assertions.assertFalse(allowRenegotiate); String storePath = option.getStorePath(); option.setStorePath(storePath); @@ -213,7 +213,7 @@ public void testSSLOptionYamlOption2() throws Exception { } @Test - public void testSSLOptionYamlOptionWithProperyFalse() throws Exception { + public void testSSLOptionYamlOptionWithPropertyFalse() throws Exception { Mockito.when(environment.getProperty("ssl.authPeer")).thenReturn("false"); SSLOption option = SSLOption.build("server", environment); @@ -225,7 +225,7 @@ public void testSSLOptionYamlOptionWithProperyFalse() throws Exception { } @Test - public void testSSLOptionYamlOptionWithProperyTrue() throws Exception { + public void testSSLOptionYamlOptionWithPropertyTrue() throws Exception { SSLOption option = SSLOption.build("server", environment); boolean authPeer = option.isAuthPeer(); @@ -264,9 +264,9 @@ public void testSSLOptionYamlOption() throws Exception { option.setCheckCNWhiteFile(checkCNWhiteFile); Assertions.assertEquals("white.list", checkCNWhiteFile); - boolean allowRenegociate = option.isAllowRenegociate(); - option.setAllowRenegociate(allowRenegociate); - Assertions.assertFalse(allowRenegociate); + boolean allowRenegotiate = option.isAllowRenegotiate(); + option.setAllowRenegotiate(allowRenegotiate); + Assertions.assertFalse(allowRenegotiate); String storePath = option.getStorePath(); option.setStorePath(storePath); diff --git a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLOptionFactory.java b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLOptionFactory.java index ca65b057e94..1d3b1b8b229 100644 --- a/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLOptionFactory.java +++ b/foundations/foundation-ssl/src/test/java/org/apache/servicecomb/foundation/ssl/TestSSLOptionFactory.java @@ -51,7 +51,7 @@ public void testSSLOptionFactoryWrong(@Mocked SSLOption option) { } @Test - public void testSSLOptionFactoryCorrent() { + public void testSSLOptionFactoryCurrent() { new MockUp() { @Mock public String getStringProperty(Environment environment, String defaultValue, diff --git a/foundations/foundation-ssl/src/test/resources/client.ssl.properties b/foundations/foundation-ssl/src/test/resources/client.ssl.properties index 4d25cd495bb..87808468196 100644 --- a/foundations/foundation-ssl/src/test/resources/client.ssl.properties +++ b/foundations/foundation-ssl/src/test/resources/client.ssl.properties @@ -17,12 +17,13 @@ #########SSL options ssl.protocols=TLSv1.3,TLSv1.2,TLSv1.1,TLSv1,SSLv2Hello -ssl.ciphers=TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA +## test with extra blank +ssl.ciphers=TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_DSS_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_128_CBC_SHA ssl.authPeer=true ssl.checkCN.host=false ssl.checkCN.white=true ssl.checkCN.white.file=white.list -ssl.allowRenegociate=false +ssl.allowRenegotiate=false #########certificates config ssl.storePath=internal diff --git a/foundations/foundation-ssl/src/test/resources/microservice.yaml b/foundations/foundation-ssl/src/test/resources/microservice.yaml index 41ca3326e51..482c8e48ea7 100644 --- a/foundations/foundation-ssl/src/test/resources/microservice.yaml +++ b/foundations/foundation-ssl/src/test/resources/microservice.yaml @@ -23,7 +23,7 @@ ssl: checkCN.host: true checkCN.white: true checkCN.white.file: white.list - allowRenegociate: false + allowRenegotiate: false storePath: internal trustStore: trust.jks trustStoreType: JKS diff --git a/foundations/foundation-ssl/src/test/resources/server.ssl.properties b/foundations/foundation-ssl/src/test/resources/server.ssl.properties index aec677c2387..d2a8180ab51 100644 --- a/foundations/foundation-ssl/src/test/resources/server.ssl.properties +++ b/foundations/foundation-ssl/src/test/resources/server.ssl.properties @@ -22,7 +22,7 @@ ssl.authPeer=true ssl.checkCN.host=true ssl.checkCN.white=true ssl.checkCN.white.file=white.list -ssl.allowRenegociate=false +ssl.allowRenegotiate=false #########certificates config ssl.storePath=internal diff --git a/foundations/foundation-ssl/src/test/resources/server.ssl.resource.properties b/foundations/foundation-ssl/src/test/resources/server.ssl.resource.properties index f1327eeca56..01f7380ba71 100644 --- a/foundations/foundation-ssl/src/test/resources/server.ssl.resource.properties +++ b/foundations/foundation-ssl/src/test/resources/server.ssl.resource.properties @@ -22,7 +22,7 @@ ssl.authPeer=true ssl.checkCN.host=true ssl.checkCN.white=true ssl.checkCN.white.file=white.list -ssl.allowRenegociate=false +ssl.allowRenegotiate=false #########certificates config ssl.storePath=internal diff --git a/foundations/foundation-ssl/src/test/resources/ssl/server.cer b/foundations/foundation-ssl/src/test/resources/ssl/server.cer index 79a62b21dea..fa0810bf80f 100644 --- a/foundations/foundation-ssl/src/test/resources/ssl/server.cer +++ b/foundations/foundation-ssl/src/test/resources/ssl/server.cer @@ -50,18 +50,18 @@ Certificate: 53:08:33 Exponent: 65537 (0x10001) X509v3 extensions: - X509v3 Basic Constraints: + X509v3 Basic Constraints: CA:FALSE X509v3 Key Usage: critical Digital Signature, Non Repudiation, Key Encipherment - X509v3 Subject Key Identifier: + X509v3 Subject Key Identifier: 27:15:69:5B:D5:13:60:C4:5C:62:54:75:55:A1:1D:4D:57:27:03:1F - X509v3 Authority Key Identifier: + X509v3 Authority Key Identifier: keyid:D5:4E:82:08:70:16:63:3D:DF:87:25:0B:7D:2E:EB:83:21:6A:BA:5B DirName:/C=CN/ST=GuangDong/L=ShenZhen/O=Huawei Technologies Co., Ltd/OU=OSS & Service Tools Dept/CN=OSS3.0 CA serial:7D:FD:C5:D8:70:DA:6E:0F:00:00:00:00:00:00:00:00 - X509v3 Subject Alternative Name: + X509v3 Subject Alternative Name: DNS:10.57.65.225, IP Address:10.57.65.225 Signature Algorithm: sha256WithRSAEncryption 38:1b:40:c9:3c:7f:63:a0:2d:64:59:2b:d9:4a:4b:db:c4:70: diff --git a/foundations/foundation-ssl/src/test/resources/ssl/trust.cer b/foundations/foundation-ssl/src/test/resources/ssl/trust.cer index a41bd779130..f010f4bf058 100644 --- a/foundations/foundation-ssl/src/test/resources/ssl/trust.cer +++ b/foundations/foundation-ssl/src/test/resources/ssl/trust.cer @@ -50,18 +50,18 @@ Certificate: 05:60:4b Exponent: 65537 (0x10001) X509v3 extensions: - X509v3 Basic Constraints: + X509v3 Basic Constraints: CA:TRUE X509v3 Key Usage: critical Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: + X509v3 Subject Key Identifier: D5:4E:82:08:70:16:63:3D:DF:87:25:0B:7D:2E:EB:83:21:6A:BA:5B - X509v3 Authority Key Identifier: + X509v3 Authority Key Identifier: keyid:D5:4E:82:08:70:16:63:3D:DF:87:25:0B:7D:2E:EB:83:21:6A:BA:5B DirName:/C=CN/ST=GuangDong/L=ShenZhen/O=Huawei Technologies Co., Ltd/OU=OSS & Service Tools Dept/CN=OSS3.0 CA serial:7D:FD:C5:D8:70:DA:6E:0F:00:00:00:00:00:00:00:00 - X509v3 Subject Alternative Name: + X509v3 Subject Alternative Name: email:oss3ca@huawei.com Signature Algorithm: sha256WithRSAEncryption 8a:52:5f:99:d0:7b:a0:8c:2d:e8:6c:ff:7a:bc:89:64:03:d4: diff --git a/foundations/foundation-ssl/src/test/resources/ssl/white.list b/foundations/foundation-ssl/src/test/resources/ssl/white.list index fe1d44d8e85..5719754cff9 100644 --- a/foundations/foundation-ssl/src/test/resources/ssl/white.list +++ b/foundations/foundation-ssl/src/test/resources/ssl/white.list @@ -1,4 +1,4 @@ 127.0.0.1 10.67.190.60 10.67.147.115 -10.57.65.225 \ No newline at end of file +10.57.65.225 diff --git a/foundations/foundation-test-scaffolding/pom.xml b/foundations/foundation-test-scaffolding/pom.xml index a149ef9def3..f3feba62148 100644 --- a/foundations/foundation-test-scaffolding/pom.xml +++ b/foundations/foundation-test-scaffolding/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb foundations - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundation-test-scaffolding Java Chassis::Foundations::Test Scaffolding @@ -41,8 +41,8 @@ provided - com.netflix.spectator - spectator-reg-servo + com.google.guava + guava org.apache.logging.log4j @@ -50,6 +50,12 @@ provided true + + org.apache.logging.log4j + log4j-api + provided + true + com.fasterxml.jackson.core jackson-annotations diff --git a/foundations/foundation-test-scaffolding/src/main/java/io/vertx/core/impl/SyncContext.java b/foundations/foundation-test-scaffolding/src/main/java/io/vertx/core/impl/SyncContext.java deleted file mode 100644 index eb25a08f56f..00000000000 --- a/foundations/foundation-test-scaffolding/src/main/java/io/vertx/core/impl/SyncContext.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package io.vertx.core.impl; - -import io.vertx.core.AsyncResult; -import io.vertx.core.Future; -import io.vertx.core.Handler; -import io.vertx.core.Promise; - -public class SyncContext extends EventLoopContext { - protected VertxInternal owner; - - public SyncContext() { - this(null); - } - - public SyncContext(VertxInternal vertx) { - super(vertx, null, null, null, null, null, null); - } - - @Override - public VertxInternal owner() { - return owner; - } - - public void setOwner(VertxInternal owner) { - this.owner = owner; - } - - @Override - protected void runOnContext(ContextInternal ctx, Handler action) { - action.handle(null); - } - - public static void syncExecuteBlocking(Handler> blockingCodeHandler, - Handler> asyncResultHandler) { - Promise res = Promise.promise(); - - try { - blockingCodeHandler.handle(res); - } catch (Throwable e) { - res.fail(e); - return; - } - - res.future().onComplete(asyncResultHandler); - } - - private static Future syncExecuteBlocking(Handler> blockingCodeHandler) { - Promise res = Promise.promise(); - - try { - blockingCodeHandler.handle(res); - } catch (Throwable e) { - res.fail(e); - return res.future(); - } - - res.complete(); - return res.future(); - } - - @Override - public Future executeBlockingInternal(Handler> action) { - return syncExecuteBlocking(action); - } - - @Override - public void executeBlocking(Handler> blockingCodeHandler, boolean ordered, - Handler> asyncResultHandler) { - syncExecuteBlocking(blockingCodeHandler, asyncResultHandler); - } -} diff --git a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java index 691e6d611d8..5b435b89829 100644 --- a/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java +++ b/foundations/foundation-test-scaffolding/src/main/java/org/apache/servicecomb/foundation/test/scaffolding/log/LogCollector.java @@ -16,6 +16,12 @@ */ package org.apache.servicecomb.foundation.test.scaffolding.log; +import java.io.Closeable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Appender; @@ -23,27 +29,28 @@ import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.AbstractAppender; import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.layout.PatternLayout; -import java.io.Closeable; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - public class LogCollector implements Closeable { List events = new ArrayList<>(); - Appender appender = new AbstractAppender("LogCollector", null, PatternLayout.createDefaultLayout()) { - @Override - public void append(LogEvent event) { - events.add(event); - } - }; + Appender appender; + + String appenderName; public LogCollector() { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); + appenderName = "LogCollector" + System.nanoTime(); + appender = new AbstractAppender(appenderName, null, PatternLayout.createDefaultLayout(), + true, + Property.EMPTY_ARRAY) { + @Override + public void append(LogEvent event) { + events.add(event); + } + }; appender.start(); config.getRootLogger().addAppender(appender, Level.ALL, null); ctx.updateLoggers(config); @@ -82,11 +89,11 @@ public List getThrowableMessages() { .collect(Collectors.toList()); } - public void teardown() { + public void tearDown() { LoggerContext ctx = (LoggerContext) LogManager.getContext(false); Configuration config = ctx.getConfiguration(); appender.stop(); - config.getRootLogger().removeAppender("LogCollector"); + config.getRootLogger().removeAppender(appenderName); ctx.updateLoggers(config); } @@ -96,6 +103,6 @@ public void clear() { @Override public void close() { - teardown(); + tearDown(); } } diff --git a/foundations/foundation-vertx/pom.xml b/foundations/foundation-vertx/pom.xml index d670812a782..24556f590f7 100644 --- a/foundations/foundation-vertx/pom.xml +++ b/foundations/foundation-vertx/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb foundations - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT foundation-vertx Java Chassis::Foundations::Vertx @@ -49,7 +49,7 @@ vertx-codegen provided - + org.apache.logging.log4j log4j-slf4j-impl diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AddressResolverConfig.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AddressResolverConfig.java index afb2a656aad..2d4ee389642 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AddressResolverConfig.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/AddressResolverConfig.java @@ -79,7 +79,7 @@ public static AddressResolverOptions getAddressResolverOptions(String tag) { "addressResolver.rdFlag")); addressResolverOptions .setSearchDomains(getStringListProperty( - AddressResolverOptions.DEFAULT_SEACH_DOMAINS, + AddressResolverOptions.DEFAULT_SEARCH_DOMAINS, "addressResolver." + tag + ".searchDomains", "addressResolver.searchDomains")); addressResolverOptions @@ -109,7 +109,7 @@ private static int getPositiveIntProperty(int defaultValue, String... keys) { for (String key : keys) { Integer val = LegacyPropertyFactory.getProperty(key, Integer.class); if (val != null && val <= 0) { - LOGGER.warn("Address resover key:{}'s value:{} is not positive, please check!", key, val); + LOGGER.warn("Address resolver key:{}'s value:{} is not positive, please check!", key, val); continue; } if (val != null) { diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SharedVertxFactory.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SharedVertxFactory.java index 9803582459c..9f2d3af797b 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SharedVertxFactory.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/SharedVertxFactory.java @@ -64,7 +64,7 @@ public static Vertx getSharedVertx(Environment environment) { private static Vertx createSharedVertx(Environment environment, String name) { SharedVertxInfo info = new SharedVertxInfo(environment); - Vertx vertx = VertxUtils.init(name, info.vertxOptions); + Vertx vertx = VertxUtils.init(name, info.vertxOptions, info.metricsFactory); info.metricsFactory.setVertx(vertx, info.vertxOptions); vertx.sharedData().getLocalMap(LOCAL_MAP_NAME).put(INFO, info); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxTLSBuilder.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxTLSBuilder.java index 99a36c2e772..ba2203338e6 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxTLSBuilder.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxTLSBuilder.java @@ -33,6 +33,7 @@ import io.vertx.core.http.ClientAuth; import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.WebSocketClientOptions; import io.vertx.core.net.ClientOptionsBase; import io.vertx.core.net.JksOptions; import io.vertx.core.net.NetServerOptions; @@ -87,6 +88,18 @@ public static void buildHttpClientOptions(String sslKey, HttpClientOptions httpC buildHttpClientOptions(sslOption, sslCustom, httpClientOptions); } + public static void buildWebSocketClientOptions(String sslKey, WebSocketClientOptions webSocketClientOptions) { + SSLOptionFactory factory = SSLOptionFactory.createSSLOptionFactory(sslKey, LegacyPropertyFactory.getEnvironment()); + SSLOption sslOption; + if (factory == null) { + sslOption = SSLOption.build(sslKey, LegacyPropertyFactory.getEnvironment()); + } else { + sslOption = factory.createSSLOption(); + } + SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); + buildWebSocketClientOptions(sslOption, sslCustom, webSocketClientOptions); + } + public static HttpClientOptions buildHttpClientOptions(SSLOption sslOption, SSLCustom sslCustom, HttpClientOptions httpClientOptions) { buildClientOptionsBase(sslOption, sslCustom, httpClientOptions); @@ -94,6 +107,13 @@ public static HttpClientOptions buildHttpClientOptions(SSLOption sslOption, SSLC return httpClientOptions; } + public static WebSocketClientOptions buildWebSocketClientOptions(SSLOption sslOption, SSLCustom sslCustom, + WebSocketClientOptions webSocketClientOptions) { + buildClientOptionsBase(sslOption, sslCustom, webSocketClientOptions); + webSocketClientOptions.setVerifyHost(sslOption.isCheckCNHost()); + return webSocketClientOptions; + } + public static ClientOptionsBase buildClientOptionsBase(SSLOption sslOption, SSLCustom sslCustom, ClientOptionsBase clientOptionsBase) { buildTCPSSLOptions(sslOption, sslCustom, clientOptionsBase); @@ -111,7 +131,7 @@ private static TCPSSLOptions buildTCPSSLOptions(SSLOption sslOption, SSLCustom s tcpClientOptions.setSsl(true); if (sslOption.getEngine().equalsIgnoreCase("openssl")) { - tcpClientOptions.setOpenSslEngineOptions(new OpenSSLEngineOptions()); + tcpClientOptions.setSslEngineOptions(new OpenSSLEngineOptions()); } String fullKeyStore = sslCustom.getFullPath(sslOption.getKeyStore()); if (isFileExists(fullKeyStore)) { @@ -119,12 +139,12 @@ private static TCPSSLOptions buildTCPSSLOptions(SSLOption sslOption, SSLCustom s PfxOptions keyPfxOptions = new PfxOptions(); keyPfxOptions.setPath(fullKeyStore); keyPfxOptions.setPassword(new String(sslCustom.decode(sslOption.getKeyStoreValue().toCharArray()))); - tcpClientOptions.setPfxKeyCertOptions(keyPfxOptions); + tcpClientOptions.setKeyCertOptions(keyPfxOptions); } else if (STORE_JKS.equalsIgnoreCase(sslOption.getKeyStoreType())) { JksOptions keyJksOptions = new JksOptions(); keyJksOptions.setPath(fullKeyStore); keyJksOptions.setPassword(new String(sslCustom.decode(sslOption.getKeyStoreValue().toCharArray()))); - tcpClientOptions.setKeyStoreOptions(keyJksOptions); + tcpClientOptions.setKeyCertOptions(keyJksOptions); } else { throw new IllegalArgumentException("invalid key store type."); } @@ -138,13 +158,13 @@ private static TCPSSLOptions buildTCPSSLOptions(SSLOption sslOption, SSLCustom s trustPfxOptions.setPath(fullTrustStore); trustPfxOptions .setPassword(new String(sslCustom.decode(sslOption.getTrustStoreValue().toCharArray()))); - tcpClientOptions.setPfxTrustOptions(trustPfxOptions); + tcpClientOptions.setTrustOptions(trustPfxOptions); } else if (STORE_JKS.equalsIgnoreCase(sslOption.getTrustStoreType())) { JksOptions trustJksOptions = new JksOptions(); trustJksOptions.setPath(fullTrustStore); trustJksOptions .setPassword(new String(sslCustom.decode(sslOption.getTrustStoreValue().toCharArray()))); - tcpClientOptions.setTrustStoreOptions(trustJksOptions); + tcpClientOptions.setTrustOptions(trustJksOptions); } else { throw new IllegalArgumentException("invalid trust store type."); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxUtils.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxUtils.java index bc398616f82..5eceac0b24d 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxUtils.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/VertxUtils.java @@ -20,13 +20,13 @@ import java.io.IOException; import java.io.InputStream; import java.lang.management.ManagementFactory; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; -import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; @@ -38,14 +38,17 @@ import io.netty.buffer.ByteBuf; import io.vertx.core.AbstractVerticle; import io.vertx.core.DeploymentOptions; +import io.vertx.core.Future; import io.vertx.core.Verticle; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.buffer.Buffer; -import io.vertx.core.file.impl.FileResolverImpl; -import io.vertx.core.impl.VertxBuilder; +import io.vertx.core.impl.SysProps; import io.vertx.core.impl.VertxThread; +import io.vertx.core.internal.VertxBootstrap; +import io.vertx.core.spi.VertxMetricsFactory; import io.vertx.core.spi.VertxThreadFactory; +import io.vertx.core.transport.Transport; /** * VertxUtils @@ -84,17 +87,19 @@ public static DeploymentOptions createClientDeployOptions( } // deploy Verticle and wait for its success. do not call this method in event-loop thread - public static boolean blockDeploy(Vertx vertx, + public static Map blockDeploy(Vertx vertx, Class cls, DeploymentOptions options) throws InterruptedException { - Holder result = new Holder<>(); + Map result = new HashMap<>(); CountDownLatch latch = new CountDownLatch(1); - vertx.deployVerticle(cls.getName(), options, ar -> { - result.value = ar.succeeded(); + Future future = vertx.deployVerticle(cls.getName(), options); + future.onComplete((success, failure) -> { + result.put("code", failure == null); - if (ar.failed()) { - LOGGER.error("deploy vertx failed, cause ", ar.cause()); + if (failure != null) { + result.put("message", failure.getMessage()); + LOGGER.error("deploy vertx failed, cause ", failure); } latch.countDown(); @@ -102,14 +107,15 @@ public static boolean blockDeploy(Vertx vertx, latch.await(); - return result.value; + return result; } - public static Vertx getOrCreateVertxByName(String name, VertxOptions vertxOptions) { - return vertxMap.computeIfAbsent(name, vertxName -> init(vertxName, vertxOptions)); + public static Vertx getOrCreateVertxByName(String name, VertxOptions vertxOptions, + VertxMetricsFactory metricsFactory) { + return vertxMap.computeIfAbsent(name, vertxName -> init(name, vertxOptions, metricsFactory)); } - public static Vertx init(String name, VertxOptions vertxOptions) { + public static Vertx init(String name, VertxOptions vertxOptions, VertxMetricsFactory metricsFactory) { if (vertxOptions == null) { vertxOptions = new VertxOptions(); } @@ -122,14 +128,31 @@ public static Vertx init(String name, VertxOptions vertxOptions) { configureVertxFileCaching(vertxOptions); - return new VertxBuilder(vertxOptions).threadFactory(new VertxThreadFactory() { - @Override - public VertxThread newVertxThread(Runnable target, String threadName, boolean worker, long maxExecTime, - TimeUnit maxExecTimeUnit) { - return VertxThreadFactory.super - .newVertxThread(target, name + "-" + threadName, worker, maxExecTime, maxExecTimeUnit); - } - }).init().vertx(); + VertxBootstrap bootstrap = bootstrap(vertxOptions, metricsFactory) + .threadFactory(new VertxThreadFactory() { + @Override + public VertxThread newVertxThread(Runnable target, String threadName, boolean worker, long maxExecTime, + TimeUnit maxExecTimeUnit) { + return VertxThreadFactory.super + .newVertxThread(target, name + "-" + threadName, worker, maxExecTime, maxExecTimeUnit); + } + }); + + return bootstrap.init().vertx(); + } + + private static VertxBootstrap bootstrap(VertxOptions options, VertxMetricsFactory metricsFactory) { + VertxBootstrap bootstrap = VertxBootstrap.create(); + bootstrap.options(options); + bootstrap.metricsFactory(metricsFactory); + Transport tr; + if (options.getPreferNativeTransport()) { + tr = Transport.nativeTransport(); + } else { + tr = Transport.NIO; + } + bootstrap.transport(tr.implementation()); + return bootstrap; } /** @@ -137,7 +160,7 @@ public VertxThread newVertxThread(Runnable target, String threadName, boolean wo */ private static void configureVertxFileCaching(VertxOptions vertxOptions) { boolean disableFileCPResolving = LegacyPropertyFactory - .getBooleanProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, true); + .getBooleanProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, true); vertxOptions.getFileSystemOptions().setClassPathResolvingEnabled(!disableFileCPResolving); } @@ -152,8 +175,9 @@ public static byte[] getBytesFast(InputStream inputStream) throws IOException { } public static byte[] getBytesFast(Buffer buffer) { - ByteBuf byteBuf = buffer.getByteBuf(); - return getBytesFast(byteBuf); + byte[] arr = new byte[buffer.length()]; + buffer.getBytes(arr, 0); + return arr; } public static byte[] getBytesFast(ByteBuf byteBuf) { @@ -176,15 +200,17 @@ public static CompletableFuture closeVertxByName(String name) { return future; } - vertx.close(ar -> { - if (ar.succeeded()) { + Future closeFuture = vertx.close(); + closeFuture.onComplete((succ, fail) -> { + if (fail == null) { LOGGER.info("Success to close vertx {}.", name); future.complete(null); return; } - future.completeExceptionally(ar.cause()); + future.completeExceptionally(fail); }); + return future; } @@ -199,8 +225,9 @@ public static void blockCloseVertxByName(String name) { public static void blockCloseVertx(Vertx vertx) { CountDownLatch latch = new CountDownLatch(1); - vertx.close(ar -> { - if (ar.succeeded()) { + Future closeFuture = vertx.close(); + closeFuture.onComplete((succ, fail) -> { + if (fail == null) { LOGGER.info("Success to close vertx {}.", vertx); } else { LOGGER.info("Failed to close vertx {}.", vertx); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java index 48902dfb18a..83bf28db9ca 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientOptionsSPI.java @@ -22,6 +22,8 @@ import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpVersion; +import io.vertx.core.http.WebSocketClientOptions; +import io.vertx.core.net.ClientOptionsBase; import io.vertx.core.net.ProxyOptions; /** @@ -74,6 +76,8 @@ public interface HttpClientOptionsSPI { int getKeepAliveTimeout(); + boolean enableLogActivity(); + /***************** http 2 settings ****************************/ int getHttp2MultiplexingLimit(); @@ -95,19 +99,10 @@ public interface HttpClientOptionsSPI { /***************** ssl settings ***************************/ boolean isSsl(); - static HttpClientOptions createHttpClientOptions(HttpClientOptionsSPI spi) { - HttpClientOptions httpClientOptions = new HttpClientOptions(); - - httpClientOptions.setProtocolVersion(spi.getHttpVersion()); + static void buildClientOptionsBase(HttpClientOptionsSPI spi, ClientOptionsBase httpClientOptions) { httpClientOptions.setConnectTimeout(spi.getConnectTimeoutInMillis()); httpClientOptions.setIdleTimeout(spi.getIdleTimeoutInSeconds()); - httpClientOptions.setReadIdleTimeout(spi.getIdleTimeoutInSeconds()); - httpClientOptions.setWriteIdleTimeout(spi.getIdleTimeoutInSeconds()); - httpClientOptions.setTryUseCompression(spi.isTryUseCompression()); - httpClientOptions.setMaxWaitQueueSize(spi.getMaxWaitQueueSize()); - httpClientOptions.setMaxPoolSize(spi.getMaxPoolSize()); - httpClientOptions.setKeepAlive(spi.isKeepAlive()); - httpClientOptions.setMaxHeaderSize(spi.getMaxHeaderSize()); + httpClientOptions.setLogActivity(spi.enableLogActivity()); if (spi.isProxyEnable()) { ProxyOptions proxy = new ProxyOptions(); @@ -121,9 +116,21 @@ static HttpClientOptions createHttpClientOptions(HttpClientOptionsSPI spi) { if (spi.getHttpVersion() == HttpVersion.HTTP_2) { httpClientOptions.setUseAlpn(spi.isUseAlpn()); + } + } + + static HttpClientOptions createHttpClientOptions(HttpClientOptionsSPI spi) { + HttpClientOptions httpClientOptions = new HttpClientOptions(); + buildClientOptionsBase(spi, httpClientOptions); + + httpClientOptions.setProtocolVersion(spi.getHttpVersion()); + httpClientOptions.setDecompressionSupported(spi.isTryUseCompression()); + httpClientOptions.setKeepAlive(spi.isKeepAlive()); + httpClientOptions.setMaxHeaderSize(spi.getMaxHeaderSize()); + + if (spi.getHttpVersion() == HttpVersion.HTTP_2) { httpClientOptions.setHttp2ClearTextUpgrade(false); httpClientOptions.setHttp2MultiplexingLimit(spi.getHttp2MultiplexingLimit()); - httpClientOptions.setHttp2MaxPoolSize(spi.getHttp2MaxPoolSize()); httpClientOptions.setHttp2KeepAliveTimeout(spi.getKeepAliveTimeout()); } else { httpClientOptions.setKeepAliveTimeout(spi.getKeepAliveTimeout()); @@ -135,4 +142,15 @@ static HttpClientOptions createHttpClientOptions(HttpClientOptionsSPI spi) { return httpClientOptions; } + + static WebSocketClientOptions createWebSocketClientOptions(HttpClientOptionsSPI spi, boolean sslEnabled) { + WebSocketClientOptions webSocketClientOptions = new WebSocketClientOptions(); + buildClientOptionsBase(spi, webSocketClientOptions); + + if (sslEnabled) { + VertxTLSBuilder.buildWebSocketClientOptions(spi.getConfigTag(), webSocketClientOptions); + } + + return webSocketClientOptions; + } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientPoolFactory.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientPoolFactory.java index c8752949ddc..69f15e6e397 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientPoolFactory.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClientPoolFactory.java @@ -5,9 +5,7 @@ * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at - * * http://www.apache.org/licenses/LICENSE-2.0 - * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,6 +22,7 @@ import io.vertx.core.Context; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.PoolOptions; // execute in vertx context public class HttpClientPoolFactory implements ClientPoolFactory { @@ -31,25 +30,30 @@ public class HttpClientPoolFactory implements ClientPoolFactory { - LOGGER.debug("http connection connected, local:{}, remote:{}.", - connection.localAddress(), connection.remoteAddress()); - connection.closeHandler(v -> - LOGGER.debug("http connection closed, local:{}, remote:{}.", - connection.localAddress(), connection.remoteAddress()) - ); - connection.exceptionHandler(e -> - LOGGER.info("http connection exception, local:{}, remote:{}.", - connection.localAddress(), connection.remoteAddress(), e) - ); - }); + HttpClient httpClient = context.owner().httpClientBuilder() + .with(httpClientOptions) + .with(poolOptions) + .withConnectHandler(connection -> { + LOGGER.debug("http connection connected, local:{}, remote:{}.", + connection.localAddress(), connection.remoteAddress()); + connection.closeHandler(v -> + LOGGER.debug("http connection closed, local:{}, remote:{}.", + connection.localAddress(), connection.remoteAddress()) + ); + connection.exceptionHandler(e -> + LOGGER.error("http connection exception, local:{}, remote:{}.", + connection.localAddress(), connection.remoteAddress(), e) + ); + }).build(); return new HttpClientWithContext(httpClient, context); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java index c3c898c8d01..b1ef46436ec 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/http/HttpClients.java @@ -33,9 +33,12 @@ import io.vertx.core.Context; import io.vertx.core.DeploymentOptions; +import io.vertx.core.ThreadingModel; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.dns.AddressResolverOptions; +import io.vertx.core.http.PoolOptions; +import io.vertx.core.http.WebSocketClient; /** * load and manages a set of HttpClient at boot up. @@ -68,12 +71,16 @@ public static void destroy() { private static ClientPoolManager createClientPoolManager(HttpClientOptionsSPI option) { Vertx vertx = getOrCreateVertx(option); + PoolOptions poolOptions = new PoolOptions(); + poolOptions.setMaxWaitQueueSize(option.getMaxWaitQueueSize()); + poolOptions.setHttp1MaxSize(option.getMaxPoolSize()); + poolOptions.setHttp2MaxSize(option.getHttp2MaxPoolSize()); ClientPoolManager clientPoolManager = new ClientPoolManager<>(vertx, - new HttpClientPoolFactory(HttpClientOptionsSPI.createHttpClientOptions(option))); + new HttpClientPoolFactory(HttpClientOptionsSPI.createHttpClientOptions(option), poolOptions)); DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientPoolManager, option.getInstanceCount()) - .setWorker(option.isWorker()) + .setThreadingModel(option.isWorker() ? ThreadingModel.WORKER : ThreadingModel.EVENT_LOOP) .setWorkerPoolName(option.getWorkerPoolName()) .setWorkerPoolSize(option.getWorkerPoolSize()); try { @@ -84,6 +91,11 @@ private static ClientPoolManager createClientPoolManager( } } + public static WebSocketClient createWebSocketClient(HttpClientOptionsSPI option, boolean sslEnabled) { + Vertx vertx = getOrCreateVertx(option); + return vertx.createWebSocketClient(HttpClientOptionsSPI.createWebSocketClientOptions(option, sslEnabled)); + } + private static Vertx getOrCreateVertx(HttpClientOptionsSPI option) { if (option.useSharedVertx()) { return SharedVertxFactory.getSharedVertx(LegacyPropertyFactory.getEnvironment()); @@ -96,7 +108,7 @@ private static Vertx getOrCreateVertx(HttpClientOptionsSPI option) { .setEventLoopPoolSize(option.getEventLoopPoolSize()); // Maybe we can deploy only one vert.x for the application. However this has did it like this. - return VertxUtils.getOrCreateVertxByName(option.clientName(), vertxOptions); + return VertxUtils.getOrCreateVertxByName(option.clientName(), vertxOptions, null); } /** diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java index 763ba5e3daf..ef6717f8968 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/AbstractTcpClientPackage.java @@ -27,9 +27,9 @@ public static long getAndIncRequestId() { return reqId.getAndIncrement(); } - private long msRequestTimeout; + private volatile long msRequestTimeout; - private long finishWriteToBuffer; + private volatile long finishWriteToBuffer; protected long msgId = getAndIncRequestId(); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/NetClientWrapper.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/NetClientWrapper.java index a36d80c246c..ce47126ee47 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/NetClientWrapper.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/NetClientWrapper.java @@ -16,8 +16,7 @@ */ package org.apache.servicecomb.foundation.vertx.client.tcp; -import io.vertx.core.AsyncResult; -import io.vertx.core.Handler; +import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.net.NetClient; import io.vertx.core.net.NetSocket; @@ -50,12 +49,11 @@ public TcpClientConfig getClientConfig(boolean ssl) { return normalClientConfig; } - public void connect(boolean ssl, int port, String host, Handler> connectHandler) { + public Future connect(boolean ssl, int port, String host) { if (ssl) { - sslNetClient.connect(port, host, connectHandler); - return; + return sslNetClient.connect(port, host); } - normalNetClient.connect(port, host, connectHandler); + return normalNetClient.connect(port, host); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java index 5d363e874d9..db071e3c34f 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/client/tcp/TcpClientConnection.java @@ -39,6 +39,7 @@ import io.vertx.core.AsyncResult; import io.vertx.core.Context; +import io.vertx.core.Future; import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetSocket; import io.vertx.core.net.impl.NetSocketImpl; @@ -61,7 +62,7 @@ enum Status { private final InetSocketAddress socketAddress; - private boolean localSupportLogin = false; + private volatile boolean localSupportLogin = false; private final boolean remoteSupportLogin; @@ -144,7 +145,7 @@ private boolean writeToBufferQueue(AbstractTcpClientPackage tcpClientPackage) { if (Status.WORKING.equals(status)) { // encode in sender thread try (TcpOutputStream os = tcpClientPackage.createStream()) { - write(os.getByteBuf()); + write(os.getBuffer()); tcpClientPackage.finishWriteToBuffer(); } return true; @@ -180,17 +181,17 @@ protected void connect() { this.status = Status.CONNECTING; LOGGER.info("connecting to address {}", socketAddress.toString()); - netClientWrapper.connect(endpoint.isSslEnabled(), + Future result = netClientWrapper.connect(endpoint.isSslEnabled(), socketAddress.getPort(), - socketAddress.getHostString(), - ar -> { - if (ar.succeeded()) { - onConnectSuccess(ar.result()); - return; - } - - onConnectFailed(ar.cause()); - }); + socketAddress.getHostString()); + result.onComplete((socket, fail) -> { + if (fail == null) { + onConnectSuccess(socket); + return; + } + + onConnectFailed(fail); + }); } private void onConnectSuccess(NetSocket socket) { diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java index 7f19f91139e..e3334a79f1b 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/executor/VertxWorkerExecutor.java @@ -25,8 +25,9 @@ public class VertxWorkerExecutor implements Executor { @Override public void execute(Runnable command) { - Vertx.currentContext().owner().executeBlocking(future -> command.run(), - false, - null); + Vertx.currentContext().owner().executeBlocking(() -> { + command.run(); + return null; + }, false); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java index 2a0559baba6..bd7899b471c 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/AbstractHttpServletResponse.java @@ -25,6 +25,7 @@ import java.util.Map; import java.util.concurrent.CompletableFuture; +import io.vertx.core.buffer.Buffer; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.Part; @@ -85,6 +86,11 @@ public int getBufferSize() { @Override public void flushBuffer() throws IOException { + // for vert.x do noting + } + + @Override + public void endResponse() throws IOException { throw new Error("not supported method"); } @@ -222,4 +228,9 @@ public Object getAttribute(String key) { public CompletableFuture sendPart(Part body) { throw new Error("not supported method"); } + + @Override + public CompletableFuture sendBuffer(Buffer buffer) { + throw new Error("not supported method"); + } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/FileUploadPart.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/FileUploadPart.java index a444495f675..9e72d64d289 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/FileUploadPart.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/FileUploadPart.java @@ -18,9 +18,9 @@ package org.apache.servicecomb.foundation.vertx.http; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import org.apache.commons.io.FileUtils; import org.apache.servicecomb.foundation.common.part.AbstractPart; @@ -36,7 +36,7 @@ public FileUploadPart(FileUpload fileUpload) { @Override public InputStream getInputStream() throws IOException { - return new FileInputStream(fileUpload.uploadedFileName()); + return Files.newInputStream(new File(fileUpload.uploadedFileName()).toPath()); } @Override diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java index 265ee658ec1..a565ed7791e 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/HttpServletResponseEx.java @@ -17,14 +17,17 @@ package org.apache.servicecomb.foundation.vertx.http; +import java.io.IOException; +import java.io.PrintWriter; import java.util.concurrent.CompletableFuture; +import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpHeaders; +import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.Response.StatusType; -import io.vertx.core.http.HttpHeaders; - public interface HttpServletResponseEx extends HttpServletResponse, BodyBufferSupport { StatusType getStatusType(); @@ -34,7 +37,21 @@ public interface HttpServletResponseEx extends HttpServletResponse, BodyBufferSu CompletableFuture sendPart(Part body); + CompletableFuture sendBuffer(Buffer buffer); + default void setChunked(boolean chunked) { setHeader(HttpHeaders.TRANSFER_ENCODING.toString(), HttpHeaders.CHUNKED.toString()); } + + void endResponse() throws IOException; + + @Override + default ServletOutputStream getOutputStream() throws IOException { + throw new IOException("Not allowed"); + } + + @Override + default PrintWriter getWriter() throws IOException { + throw new IOException("Not allowed"); + } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/ReadStreamPart.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/ReadStreamPart.java index e6a7947eb2b..acd2fc1978a 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/ReadStreamPart.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/ReadStreamPart.java @@ -27,8 +27,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.vertx.core.AsyncResult; import io.vertx.core.Context; +import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.core.file.AsyncFile; @@ -82,15 +82,13 @@ public ReadStream getReadStream() { } /** - * - * @param writeStream * @return future of save action
* * important: WriteStream did not provide endHandler, so we can not know when will really finished write. * so the return future only means finished read from readStream. */ public CompletableFuture saveToWriteStream(WriteStream writeStream) { - return new PumpCommon().pump(context, readStream, writeStream, null); + return new PumpCommon().pump(readStream, writeStream, null); } public CompletableFuture saveAsBytes() { @@ -120,8 +118,6 @@ public CompletableFuture saveAs(Function converter) { } /** - * - * @param fileName * @return future of save to file, future complete means write to file finished */ public CompletableFuture saveToFile(String fileName) { @@ -132,9 +128,6 @@ public CompletableFuture saveToFile(String fileName) { } /** - * - * @param file - * @param openOptions * @return future of save to file, future complete means write to file finished */ public CompletableFuture saveToFile(File file, OpenOptions openOptions) { @@ -142,34 +135,37 @@ public CompletableFuture saveToFile(File file, OpenOptions openOptions) { context.runOnContext((v) -> { Vertx vertx = context.owner(); - vertx.fileSystem().open(file.getAbsolutePath(), openOptions, ar -> onFileOpened(file, ar, future)); + Future openFuture = vertx.fileSystem().open(file.getAbsolutePath(), openOptions); + openFuture.onComplete((s, f) -> onFileOpened(file, s, f, future)); }); return future; } - protected void onFileOpened(File file, AsyncResult ar, CompletableFuture future) { - if (ar.failed()) { - future.completeExceptionally(ar.cause()); + protected void onFileOpened(File file, AsyncFile asyncFile, Throwable failure, CompletableFuture future) { + if (failure != null) { + future.completeExceptionally(failure); return; } - AsyncFile asyncFile = ar.result(); CompletableFuture saveFuture = saveToWriteStream(asyncFile); - saveFuture.whenComplete((v, saveException) -> asyncFile.close(closeAr -> { - if (closeAr.failed()) { - LOGGER.error("Failed to close file {}.", file); - } - - // whatever close success or failed - // will not affect to result - // result just only related to write - if (saveException == null) { - future.complete(file); - return; - } - - future.completeExceptionally(saveException); - })); + saveFuture.whenComplete((v, saveException) -> { + Future result = asyncFile.close(); + result.onComplete((s, f) -> { + if (f != null) { + LOGGER.error("Failed to close file {}.", file); + } + + // whatever close success or failed + // will not affect to result + // result just only related to write + if (saveException == null) { + future.complete(file); + return; + } + + future.completeExceptionally(saveException); + }); + }); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletRequestEx.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletRequestEx.java index 21fd453090c..82b46b47597 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletRequestEx.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletRequestEx.java @@ -38,8 +38,6 @@ import com.google.common.annotations.VisibleForTesting; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.vertx.core.buffer.Buffer; import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.HttpServletRequest; @@ -77,9 +75,9 @@ public ServletInputStream getInputStream() throws IOException { if (this.inputStream == null) { if (cacheRequest) { byte[] inputBytes = IOUtils.toByteArray(getRequest().getInputStream()); - ByteBuf byteBuf = Unpooled.wrappedBuffer(inputBytes); + Buffer byteBuf = Buffer.buffer(inputBytes); this.inputStream = new BufferInputStream(byteBuf); - setBodyBuffer(Buffer.buffer(Unpooled.wrappedBuffer(byteBuf))); + setBodyBuffer(byteBuf); } else { this.inputStream = getRequest().getInputStream(); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java index 430f2ace8e3..38db248efdb 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/StandardHttpServletResponseEx.java @@ -82,11 +82,7 @@ public StatusType getStatusType() { } @Override - public void flushBuffer() throws IOException { - byte[] bytes = getBodyBytes(); - if (bytes != null) { - getOutputStream().write(bytes, 0, getBodyBytesLength()); - } + public void endResponse() throws IOException { super.flushBuffer(); } @@ -121,4 +117,16 @@ public CompletableFuture sendPart(Part part) { Context context = Vertx.currentContext(); return new PumpFromPart(context, part).toOutputStream(outputStream, false); } + + @Override + public CompletableFuture sendBuffer(Buffer buffer) { + CompletableFuture future = new CompletableFuture<>(); + try { + getOutputStream().write(buffer.getBytes(), 0, buffer.length()); + future.complete(null); + } catch (IOException e) { + future.completeExceptionally(e); + } + return future; + } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java index 737e400bdb6..da55f5ff613 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerRequestToHttpServletRequest.java @@ -268,7 +268,7 @@ public ServletInputStream getInputStream() { if (context.body().buffer() == null) { return null; } - inputStream = new BufferInputStream(context.body().buffer().getByteBuf()); + inputStream = new BufferInputStream(context.body().buffer()); return inputStream; } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java index 7e28e8bb70c..81785e9698c 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/http/VertxServerResponseToHttpServletResponse.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.foundation.vertx.http; +import java.io.IOException; import java.util.Collection; import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -26,6 +27,7 @@ import io.vertx.core.Context; import io.vertx.core.Vertx; +import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerResponse; import jakarta.servlet.http.Part; import jakarta.ws.rs.core.HttpHeaders; @@ -50,6 +52,11 @@ public void setContentType(String type) { serverResponse.headers().set(HttpHeaders.CONTENT_TYPE, type); } + @Override + public void setContentLength(int len) { + serverResponse.headers().set(HttpHeaders.CONTENT_LENGTH, String.valueOf(len)); + } + @Override public void setStatus(int sc) { @@ -100,7 +107,7 @@ public Collection getHeaderNames() { } @Override - public void flushBuffer() { + public void endResponse() { if (context == Vertx.currentContext()) { internalFlushBuffer(); return; @@ -110,12 +117,10 @@ public void flushBuffer() { } public void internalFlushBuffer() { - if (bodyBuffer == null) { - serverResponse.end(); + if (serverResponse.closed()) { return; } - - serverResponse.end(bodyBuffer); + serverResponse.end(); } @Override @@ -127,6 +132,23 @@ public CompletableFuture sendPart(Part part) { return new PumpFromPart(context, part).toWriteStream(serverResponse, null); } + @Override + public CompletableFuture sendBuffer(Buffer buffer) { + if (serverResponse.closed()) { + return CompletableFuture.failedFuture(new IOException("Response is closed before sending any data. " + + "Maybe client is timeout or check idle connection timeout for provider is properly configured.")); + } + CompletableFuture future = new CompletableFuture<>(); + serverResponse.write(buffer).onComplete(result -> { + if (result.failed()) { + future.completeExceptionally(result.cause()); + } else { + future.complete(null); + } + }); + return future; + } + @Override public void setChunked(boolean chunked) { serverResponse.setChunked(chunked); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultClientMetrics.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultClientMetrics.java index c3893a2e60d..2bc9288857b 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultClientMetrics.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultClientMetrics.java @@ -17,16 +17,14 @@ package org.apache.servicecomb.foundation.vertx.metrics; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientTaskMetric; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; - import io.vertx.core.spi.metrics.ClientMetrics; import io.vertx.core.spi.observability.HttpRequest; import io.vertx.core.spi.observability.HttpResponse; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; public class DefaultClientMetrics implements - ClientMetrics { + ClientMetrics { private final DefaultClientEndpointMetric clientEndpointMetric; public DefaultClientMetrics(DefaultClientEndpointMetric clientEndpointMetric) { @@ -37,18 +35,6 @@ public DefaultClientEndpointMetric getClientEndpointMetric() { return this.clientEndpointMetric; } - @Override - public DefaultClientTaskMetric enqueueRequest() { - DefaultClientTaskMetric taskMetric = new DefaultClientTaskMetric(clientEndpointMetric); - taskMetric.enqueueRequest(); - return taskMetric; - } - - @Override - public void dequeueRequest(DefaultClientTaskMetric endpointMetric) { - endpointMetric.dequeueRequest(); - } - @Override public DefaultRequestMetric requestBegin(String uri, HttpRequest request) { DefaultRequestMetric requestMetric = new DefaultRequestMetric(this.clientEndpointMetric); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpClientMetrics.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpClientMetrics.java index 59649948348..1279a4f8566 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpClientMetrics.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultHttpClientMetrics.java @@ -18,7 +18,6 @@ import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientTaskMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultRequestMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultTcpSocketMetric; @@ -26,7 +25,7 @@ import io.vertx.core.spi.metrics.HttpClientMetrics; public class DefaultHttpClientMetrics implements - HttpClientMetrics { + HttpClientMetrics { private static final String PROTOCOL = "http://"; private final DefaultClientEndpointMetricManager clientEndpointMetricManager; diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetrics.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetrics.java index ee345ed4e2b..c0b89a42eea 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetrics.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetrics.java @@ -18,23 +18,22 @@ import java.util.Map; -import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; - import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpServerOptions; -import io.vertx.core.metrics.impl.DummyVertxMetrics; import io.vertx.core.net.NetClientOptions; import io.vertx.core.net.NetServerOptions; import io.vertx.core.net.SocketAddress; import io.vertx.core.spi.metrics.HttpClientMetrics; import io.vertx.core.spi.metrics.HttpServerMetrics; import io.vertx.core.spi.metrics.TCPMetrics; +import io.vertx.core.spi.metrics.VertxMetrics; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; -public class DefaultVertxMetrics extends DummyVertxMetrics { +public class DefaultVertxMetrics implements VertxMetrics { // to support listen multiple addresses, must use a map to manage the metric private final Map serverEndpointMetricMap = new ConcurrentHashMapEx<>(); @@ -61,7 +60,7 @@ public Map getServerEndpointMetricMap() { } @Override - public HttpClientMetrics createHttpClientMetrics(HttpClientOptions options) { + public HttpClientMetrics createHttpClientMetrics(HttpClientOptions options) { return new DefaultHttpClientMetrics(clientEndpointMetricManager); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetricsFactory.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetricsFactory.java index dca7cecca92..540a644b1e1 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetricsFactory.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/DefaultVertxMetricsFactory.java @@ -55,7 +55,6 @@ public synchronized VertxMetrics metrics(VertxOptions options) { @Override public MetricsOptions newOptions() { MetricsOptionsEx metricsOptions = new MetricsOptionsEx(); - metricsOptions.setFactory(this); metricsOptions.setEnabled(true); return metricsOptions; } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetric.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetric.java index cedd6ec6b32..ce618078957 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetric.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetric.java @@ -29,7 +29,7 @@ public class DefaultClientEndpointMetric extends DefaultEndpointMetric { // control if the metric instance will be expired // all invoker about incRefCount/isExpired, must lock: DefaultClientEndpointMetricManager // decRefCount no need to lock, because that only cause to be expired later. - private long lastNanoTime = System.nanoTime(); + private volatile long lastNanoTime = System.nanoTime(); public DefaultClientEndpointMetric(String address) { super(address); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java index bdd5a1e52f8..17977a468ca 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/metrics/metric/DefaultClientEndpointMetricManager.java @@ -50,7 +50,10 @@ public DefaultClientEndpointMetricManager(MetricsOptionsEx metricsOptionsEx) { public DefaultClientEndpointMetric getOrCreateEndpointMetric(String address) { rwlock.readLock().lock(); try { - return clientEndpointMetricMap.computeIfAbsent(address, DefaultClientEndpointMetric::new); + if (clientEndpointMetricMap.get(address) == null) { + clientEndpointMetricMap.put(address, new DefaultClientEndpointMetric(address)); + } + return clientEndpointMetricMap.get(address); } finally { rwlock.readLock().unlock(); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpParser.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpParser.java index a59004d62a1..4e79f69fca3 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpParser.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpParser.java @@ -19,16 +19,10 @@ import java.nio.charset.StandardCharsets; -import io.netty.buffer.ByteBuf; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.parsetools.RecordParser; -/** - * TcpParser - * - * - */ public class TcpParser implements Handler { public static final byte[] TCP_MAGIC; @@ -86,16 +80,17 @@ public boolean firstNEqual(byte[] a, byte[] b, int n) { protected void onParse(Buffer buffer) { switch (status) { case TCP_HEADER: - ByteBuf buf = buffer.getByteBuf(); - if (!firstNEqual(TCP_MAGIC, buf.array(), TCP_MAGIC.length)) { + if (!firstNEqual(TCP_MAGIC, buffer.getBytes(), TCP_MAGIC.length)) { reset(); return; } - buf.skipBytes(TCP_MAGIC.length); - msgId = buf.readLong(); - totalLen = buf.readInt(); - headerLen = buf.readInt(); + int index = TCP_MAGIC.length; + msgId = buffer.getLong(index); + index = index + 8; + totalLen = buffer.getInt(index); + index = index + 4; + headerLen = buffer.getInt(index); if (headerLen > totalLen || headerLen <= 0) { throw new IllegalStateException("possibly attack."); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServer.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServer.java index c672e47f6cb..8a960175ad8 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServer.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/server/TcpServer.java @@ -21,7 +21,6 @@ import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; -import org.apache.servicecomb.foundation.common.utils.ExceptionUtils; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; @@ -32,6 +31,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.core.net.NetServer; import io.vertx.core.net.NetServerOptions; @@ -80,17 +80,18 @@ public void init(Vertx vertx, String sslKey, AsyncResultCallback LOGGER.error("Unexpected error in server.{}", ExceptionUtils.getExceptionMessageWithoutTrace(e))); + e -> LOGGER.error("Unexpected error in server.", e)); InetSocketAddress socketAddress = endpointObject.getSocketAddress(); - netServer.listen(socketAddress.getPort(), socketAddress.getHostString(), ar -> { - if (ar.succeeded()) { + Future result = netServer.listen(socketAddress.getPort(), socketAddress.getHostString()); + result.onComplete((s, f) -> { + if (f == null) { callback.success(socketAddress); return; } // 监听失败 String msg = String.format("listen failed, address=%s", socketAddress); - callback.fail(new Exception(msg, ar.cause())); + callback.fail(new Exception(msg, f)); }); } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferInputStream.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferInputStream.java index 9eede6180d7..af0d4acf185 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferInputStream.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferInputStream.java @@ -20,58 +20,69 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; +import io.vertx.core.buffer.Buffer; import jakarta.servlet.ReadListener; import jakarta.servlet.ServletInputStream; -import io.netty.buffer.ByteBuf; - public class BufferInputStream extends ServletInputStream { - private final ByteBuf byteBuf; + private int readIndex = 0; + + private final Buffer byteBuf; - public BufferInputStream(ByteBuf buffer) { + public BufferInputStream(Buffer buffer) { this.byteBuf = buffer; } @Override public long skip(long len) { int skipLen = Math.min((int) len, available()); - byteBuf.skipBytes(skipLen); + this.readIndex += skipLen; return skipLen; } public byte readByte() { - return byteBuf.readByte(); + int index = readIndex; + readIndex = index + 1; + return byteBuf.getByte(index); } @Override public int read() { - return byteBuf.readUnsignedByte(); + return this.readByte() & 255; } public boolean readBoolean() { - return byteBuf.readBoolean(); + return this.readByte() != 0; } public short readShort() { - return byteBuf.readShort(); + int index = readIndex; + readIndex = index + 2; + return byteBuf.getShort(index); } public int readInt() { - return byteBuf.readInt(); + int index = readIndex; + readIndex = index + 4; + return byteBuf.getInt(index); } public long readLong() { - return byteBuf.readLong(); + int index = readIndex; + readIndex = index + 8; + return byteBuf.getLong(index); } public int getIndex() { - return byteBuf.readerIndex(); + return readIndex; } public String readString() { int length = readInt(); byte[] bytes = new byte[length]; - byteBuf.readBytes(bytes); + int index = readIndex; + readIndex = index + length; + byteBuf.getBytes(index, readIndex, bytes); return new String(bytes, StandardCharsets.UTF_8); } @@ -83,40 +94,47 @@ public int read(byte[] b) { @Override public int read(byte[] b, int off, int len) { int avail = available(); - if (len > avail) { - len = avail; + if (avail <= 0) { + return -1; } if (len == 0) { - return -1; + return 0; } - byteBuf.readBytes(b, off, len); + if (len > avail) { + len = avail; + } + + int index = readIndex; + readIndex = index + len; + + byteBuf.getBytes(index, readIndex, b); return len; } @Override public int available() { - return byteBuf.readableBytes(); + return byteBuf.length() - readIndex; } @Override public void close() { - byteBuf.release(); + // nothing to do } @Override public void reset() throws IOException { - byteBuf.resetReaderIndex(); + readIndex = 0; } - public ByteBuf getByteBuf() { + public Buffer getByteBuf() { return byteBuf; } @Override public boolean isFinished() { - return !byteBuf.isReadable(); + return byteBuf.length() > readIndex; } @Override diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferOutputStream.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferOutputStream.java index f063d5d0656..42c9b31261d 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferOutputStream.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/BufferOutputStream.java @@ -20,9 +20,7 @@ import java.io.OutputStream; import java.nio.charset.StandardCharsets; -import io.netty.buffer.ByteBuf; import io.vertx.core.buffer.Buffer; -import io.vertx.core.buffer.impl.VertxByteBufAllocator; /** * BufferOutputStream. @@ -32,40 +30,36 @@ public class BufferOutputStream extends OutputStream { private static final int DIRECT_BUFFER_SIZE = 1024; - protected ByteBuf byteBuf; + protected Buffer byteBuf; public BufferOutputStream() { - this(VertxByteBufAllocator.DEFAULT.heapBuffer(DIRECT_BUFFER_SIZE, Integer.MAX_VALUE)); + this(Buffer.buffer(DIRECT_BUFFER_SIZE)); } - public BufferOutputStream(ByteBuf buffer) { + public BufferOutputStream(Buffer buffer) { this.byteBuf = buffer; } - public ByteBuf getByteBuf() { - return byteBuf; - } - public Buffer getBuffer() { - return Buffer.buffer(byteBuf); + return byteBuf; } public int length() { - return byteBuf.readableBytes(); + return byteBuf.length(); } public void writeByte(byte value) { - byteBuf.writeByte(value); + byteBuf.appendByte(value); } // 实际是写byte @Override public void write(int byteValue) { - byteBuf.writeByte((byte) byteValue); + byteBuf.appendByte((byte) byteValue); } public void write(boolean value) { - byteBuf.writeBoolean(value); + byteBuf.appendByte(value ? (byte) 1 : (byte) 0); } public void writeInt(int pos, int value) { @@ -73,20 +67,20 @@ public void writeInt(int pos, int value) { } public void writeShort(short value) { - byteBuf.writeShort(value); + byteBuf.appendShort(value); } public void writeInt(int value) { - byteBuf.writeInt(value); + byteBuf.appendInt(value); } public void writeLong(long value) { - byteBuf.writeLong(value); + byteBuf.appendLong(value); } public void writeString(String value) { - byteBuf.writeInt(value.length()); - byteBuf.writeCharSequence(value, StandardCharsets.UTF_8); + writeInt(value.length()); + byteBuf.appendString(value, StandardCharsets.UTF_8.toString()); } @Override @@ -96,7 +90,7 @@ public void write(byte[] b) { @Override public void write(byte[] bytes, int offset, int len) { - byteBuf.writeBytes(bytes, offset, len); + byteBuf.appendBytes(bytes, offset, len); } @Override @@ -105,6 +99,6 @@ public void close() { } public int writerIndex() { - return byteBuf.writerIndex(); + return byteBuf.length(); } } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/InputStreamToReadStream.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/InputStreamToReadStream.java index 8ca0a6a4ee5..580d460e5c0 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/InputStreamToReadStream.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/InputStreamToReadStream.java @@ -19,12 +19,11 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.netty.buffer.Unpooled; -import io.vertx.core.AsyncResult; import io.vertx.core.Context; import io.vertx.core.Handler; import io.vertx.core.Promise; @@ -40,7 +39,7 @@ public class InputStreamToReadStream implements ReadStream { private final InputStream inputStream; - private boolean closed; + private volatile boolean closed; private boolean paused; @@ -97,26 +96,29 @@ public synchronized InputStreamToReadStream handler(Handler handler) { } class ReadResult { - int readed; + int read; byte[] bytes = new byte[readBufferSize]; void doRead() throws IOException { - readed = inputStream.read(bytes); + read = inputStream.read(bytes); } Buffer toBuffer() { - return Buffer.buffer(Unpooled.wrappedBuffer(bytes).writerIndex(readed)); + return Buffer.buffer(Arrays.copyOf(bytes, read)); } } private synchronized void doRead() { if (!readInProgress) { readInProgress = true; - - context.executeBlocking(this::readInWorker, - true, - this::afterReadInEventloop); + Promise future = Promise.promise(); + context.executeBlocking(() -> { + readInWorker(future); + return null; + }, + true); + future.future().onComplete(this::afterReadInEventloop); } } @@ -135,15 +137,14 @@ public void handleException(Throwable e) { exceptionHandler.handle(e); } - private synchronized void afterReadInEventloop(AsyncResult ar) { - if (ar.failed()) { - handleException(ar.cause()); + private synchronized void afterReadInEventloop(ReadResult readResult, Throwable failure) { + if (failure != null) { + handleException(failure); return; } readInProgress = false; - ReadResult readResult = ar.result(); - if (readResult.readed < 0) { + if (readResult.read < 0) { handleEnd(); return; } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/OutputStreamToWriteStream.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/OutputStreamToWriteStream.java index 95ea0b5076d..35681a723a8 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/OutputStreamToWriteStream.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/OutputStreamToWriteStream.java @@ -21,10 +21,10 @@ import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.foundation.common.io.AsyncCloseable; -import io.vertx.core.AsyncResult; import io.vertx.core.Context; import io.vertx.core.Future; import io.vertx.core.Handler; @@ -60,7 +60,7 @@ public class OutputStreamToWriteStream implements WriteStream, AsyncClos // we just need to flow control by pump, so use another size private final Queue buffers = new ConcurrentLinkedQueue<>(); - private int currentBufferCount; + private final AtomicInteger currentBufferCount = new AtomicInteger(); // just indicate if buffers is full, not control add logic // must >= SMALLEST_MAX_BUFFERS @@ -92,22 +92,20 @@ private void handleException(Throwable t) { @Override public synchronized Future write(Buffer data) { Promise result = Promise.promise(); - write(data, ar -> { - if (ar.failed()) { - handleException(ar.cause()); - } - result.complete(); - }); + write(data, result); return result.future(); } - @Override - public void write(Buffer data, Handler> handler) { - currentBufferCount++; + private void write(Buffer data, Promise future) { + currentBufferCount.incrementAndGet(); buffers.add(data); - context.executeBlocking(this::writeInWorker, - true, - handler); + context.executeBlocking( + () -> { + writeInWorker(future); + return null; + }, + true + ); } protected void writeInWorker(Promise future) { @@ -122,12 +120,13 @@ protected void writeInWorker(Promise future) { outputStream.write(buffer.getBytes()); synchronized (OutputStreamToWriteStream.this) { - currentBufferCount--; - Runnable action = (currentBufferCount == 0 && closedDeferred != null) ? closedDeferred : this::checkDrained; + currentBufferCount.decrementAndGet(); + Runnable action = (currentBufferCount.get() == 0 && closedDeferred != null) + ? closedDeferred : this::checkDrained; action.run(); } } catch (IOException e) { - currentBufferCount--; + currentBufferCount.decrementAndGet(); future.fail(e); return; } @@ -135,8 +134,8 @@ protected void writeInWorker(Promise future) { } @Override - public void end(Handler> handler) { - close(); + public Future end() { + return Future.fromCompletionStage(close()); } @Override @@ -148,7 +147,7 @@ public WriteStream setWriteQueueMaxSize(int maxSize) { @Override public synchronized boolean writeQueueFull() { - return currentBufferCount >= maxBuffers; + return currentBufferCount.get() >= maxBuffers; } @Override @@ -158,7 +157,7 @@ public synchronized WriteStream drainHandler(Handler handler) { } private synchronized void checkDrained() { - if (drainHandler != null && currentBufferCount <= drainMark) { + if (drainHandler != null && currentBufferCount.get() <= drainMark) { Handler handler = drainHandler; drainHandler = null; handler.handle(null); @@ -186,7 +185,7 @@ private synchronized CompletableFuture closeInternal() { closed = true; CompletableFuture future = new CompletableFuture<>(); - if (currentBufferCount == 0) { + if (currentBufferCount.get() == 0) { doClose(future); } else { closedDeferred = () -> doClose(future); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpCommon.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpCommon.java index 5b70f72766e..ae4b5bac727 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpCommon.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpCommon.java @@ -20,27 +20,23 @@ import org.apache.servicecomb.foundation.common.io.AsyncCloseable; -import io.vertx.core.Context; +import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientResponse; -import io.vertx.core.streams.Pump; import io.vertx.core.streams.ReadStream; import io.vertx.core.streams.WriteStream; +import io.vertx.core.streams.impl.PipeImpl; public class PumpCommon { /** - * - * @param context - * @param readStream - * @param writeStream * @return future of save action
*

important: *

if writeStream is AsyncCloseable, future means write complete *

if writeStream is not AsyncCloseable, future only means read complete */ @SuppressWarnings("unchecked") - public CompletableFuture pump(Context context, ReadStream readStream, WriteStream writeStream, + public CompletableFuture pump(ReadStream readStream, WriteStream writeStream, Handler throwableHandler) { CompletableFuture readFuture = new CompletableFuture<>(); @@ -62,17 +58,18 @@ public CompletableFuture pump(Context context, ReadStream readStre readFuture.completeExceptionally(e); }); readStream.exceptionHandler(readFuture::completeExceptionally); - // just means read finished, not means write finished - readStream.endHandler(readFuture::complete); - // if readStream(HttpClientResponse) and writeStream(HttpServerResponse) - // belongs to difference eventloop - // maybe will cause deadlock - // if happened, vertx will print deadlock stacks - Pump.pump(readStream, writeStream).start(); - context.runOnContext(v -> readStream.resume()); + Future pipeResult = new PipeImpl<>(readStream).endOnComplete(false).to(writeStream); - if (!AsyncCloseable.class.isInstance(writeStream)) { + pipeResult.onComplete((s) -> readFuture.complete(null), + (f) -> { + if (throwableHandler != null) { + throwableHandler.handle(f); + } + readFuture.completeExceptionally(f); + }); + + if (!(writeStream instanceof AsyncCloseable)) { return readFuture; } diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java index ba7b3182087..2c5284e12dc 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/stream/PumpFromPart.java @@ -69,7 +69,7 @@ private CompletableFuture> prepareReadStream() { public CompletableFuture toWriteStream(WriteStream writeStream, Handler throwableHandler) { return prepareReadStream() - .thenCompose(readStream -> new PumpCommon().pump(context, readStream, writeStream, throwableHandler)) + .thenCompose(readStream -> new PumpCommon().pump(readStream, writeStream, throwableHandler)) .whenComplete((v, e) -> { if (e != null) { LOGGER.error("to write stream failed.", e); diff --git a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConnection.java b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConnection.java index 95f460013d2..fcc22bfd7c9 100644 --- a/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConnection.java +++ b/foundations/foundation-vertx/src/main/java/org/apache/servicecomb/foundation/vertx/tcp/TcpConnection.java @@ -20,9 +20,6 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicLong; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.CompositeByteBuf; import io.vertx.core.Context; import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetSocket; @@ -49,7 +46,7 @@ public class TcpConnection { // so this optimization: // 1.avoid vertx's lock // 2.reduce netty's task schedule - private final Queue writeQueue = new ConcurrentLinkedQueue<>(); + private final Queue writeQueue = new ConcurrentLinkedQueue<>(); private final AtomicLong writeQueueSize = new AtomicLong(); @@ -80,10 +77,10 @@ public NetSocket getNetSocket() { public void initNetSocket(NetSocketImpl netSocket) { this.netSocket = netSocket; - this.context = netSocket.getContext(); + this.context = netSocket.context(); } - public void write(ByteBuf buf) { + public void write(Buffer buf) { writeQueue.add(buf); long oldSize = writeQueueSize.getAndIncrement(); if (oldSize == 0) { @@ -97,25 +94,15 @@ protected void scheduleWrite() { } protected void writeInContext() { - CompositeByteBuf cbb = ByteBufAllocator.DEFAULT.compositeBuffer(); for (; ; ) { - ByteBuf buf = writeQueue.poll(); + Buffer buf = writeQueue.poll(); if (buf == null) { break; } writeQueueSize.decrementAndGet(); - cbb.addComponent(true, buf); - if (cbb.numComponents() == cbb.maxNumComponents()) { - CompositeByteBuf last = cbb; - netSocket.write(Buffer.buffer(last)).onComplete(r -> last.release()); - cbb = ByteBufAllocator.DEFAULT.compositeBuffer(); - } - } - if (cbb.isReadable()) { - CompositeByteBuf last = cbb; - netSocket.write(Buffer.buffer(last)).onComplete(r -> last.release()); + netSocket.write(buf); } } } diff --git a/foundations/foundation-vertx/src/test/java/io/vertx/ext/web/impl/TestHttpServerRequestUtils.java b/foundations/foundation-vertx/src/test/java/io/vertx/ext/web/impl/TestHttpServerRequestUtils.java deleted file mode 100644 index ec8255fdcc9..00000000000 --- a/foundations/foundation-vertx/src/test/java/io/vertx/ext/web/impl/TestHttpServerRequestUtils.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.vertx.ext.web.impl; - -import io.vertx.ext.web.RequestBody; -import org.apache.servicecomb.foundation.vertx.http.VertxServerRequestToHttpServletRequest; - -import io.vertx.core.http.impl.HttpServerRequestInternal; -import io.vertx.ext.web.AllowForwardHeaders; -import io.vertx.ext.web.RoutingContext; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -// HttpServerRequestWrapper is a package visible class, so put this test in package io.vertx.ext.web.impl -public class TestHttpServerRequestUtils { - @Test - public void testVertxServerRequestToHttpServletRequest() { - RoutingContext context = Mockito.mock(RoutingContext.class); - HttpServerRequestInternal request = Mockito.mock(HttpServerRequestInternal.class); - HttpServerRequestWrapper wrapper = new HttpServerRequestWrapper(request, AllowForwardHeaders.NONE); - Mockito.when(request.scheme()).thenReturn("http"); - Mockito.when(context.request()).thenReturn(wrapper); - RequestBody requestBody = Mockito.mock(RequestBody.class); - Mockito.when(context.body()).thenReturn(requestBody); - - VertxServerRequestToHttpServletRequest reqEx = new VertxServerRequestToHttpServletRequest(context, "abc"); - Assertions.assertEquals("abc", reqEx.getRequestURI()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSharedVertxFactory.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSharedVertxFactory.java index 10cc0827a0f..8e77a256251 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSharedVertxFactory.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestSharedVertxFactory.java @@ -23,7 +23,7 @@ import org.mockito.Mockito; import org.springframework.core.env.Environment; -import io.vertx.core.file.impl.FileResolverImpl; +import io.vertx.core.impl.SysProps; public class TestSharedVertxFactory { Environment environment = Mockito.mock(Environment.class); @@ -32,7 +32,7 @@ public class TestSharedVertxFactory { public void setUp() { Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); } diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestStream.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestStream.java index 2c9fdc895cc..af3e2078c90 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestStream.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestStream.java @@ -22,8 +22,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import io.netty.buffer.ByteBuf; -import io.vertx.core.buffer.impl.VertxByteBufAllocator; +import io.vertx.core.buffer.Buffer; public class TestStream { @@ -31,16 +30,21 @@ public class TestStream { @Test public void testBufferInputStream() { - ByteBuf obuf = VertxByteBufAllocator.DEFAULT.heapBuffer(DIRECT_BUFFER_SIZE, Integer.MAX_VALUE); - obuf.writeBytes(("testss").getBytes()); + Buffer obuf = Buffer.buffer(DIRECT_BUFFER_SIZE); + obuf.appendBytes(("tests1").getBytes()); @SuppressWarnings("resource") BufferInputStream oBufferInputStream = new BufferInputStream(obuf); - Assertions.assertNotEquals(1234, oBufferInputStream.skip(0)); - Assertions.assertNotEquals(obuf.readByte(), oBufferInputStream.readByte()); - Assertions.assertEquals(obuf.readByte() + 1, oBufferInputStream.read()); - Assertions.assertEquals(obuf.readBoolean(), oBufferInputStream.readBoolean()); - Assertions.assertEquals(obuf.readerIndex(), oBufferInputStream.getIndex()); - Assertions.assertEquals(obuf.readableBytes(), oBufferInputStream.available()); + + byte test = oBufferInputStream.readByte(); + Assertions.assertEquals((byte) 't', test); + Assertions.assertEquals((byte) 'e', oBufferInputStream.read()); + + Assertions.assertEquals(2, oBufferInputStream.skip(2)); + Assertions.assertEquals((byte) 's', oBufferInputStream.read()); + Assertions.assertTrue(oBufferInputStream.readBoolean()); + + Assertions.assertEquals(6, oBufferInputStream.getIndex()); + Assertions.assertEquals(0, oBufferInputStream.available()); } @Test @@ -53,9 +57,9 @@ public void testBufferOutputStream() { Assertions.assertTrue((1 < oBufferOutputStream.length())); @SuppressWarnings("resource") - BufferInputStream oBufferInputStream = new BufferInputStream(oBufferOutputStream.getByteBuf()); + BufferInputStream oBufferInputStream = new BufferInputStream(oBufferOutputStream.getBuffer()); Assertions.assertEquals("test", oBufferInputStream.readString()); Assertions.assertEquals(1, oBufferInputStream.readByte()); - Assertions.assertEquals(true, oBufferInputStream.readBoolean()); + Assertions.assertTrue(oBufferInputStream.readBoolean()); } } diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxUtils.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxUtils.java index 399d0b1f611..9f966cbcbde 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxUtils.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/TestVertxUtils.java @@ -38,14 +38,14 @@ import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.buffer.Buffer; -import io.vertx.core.file.impl.FileResolverImpl; +import io.vertx.core.impl.SysProps; public class TestVertxUtils { Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); @@ -53,7 +53,7 @@ public void setUp() { @Test public void testGetOrCreateVertx() throws InterruptedException { - Vertx vertx = VertxUtils.getOrCreateVertxByName("ut", null); + Vertx vertx = VertxUtils.getOrCreateVertxByName("ut", null, null); Holder name = new Holder<>(); CountDownLatch latch = new CountDownLatch(1); @@ -70,25 +70,25 @@ public void testGetOrCreateVertx() throws InterruptedException { @Test public void testCreateVertxWithFileCPResolving() { // create .vertx folder - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(false); deleteCacheFile(); - VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingFalse", null); + VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingFalse", null, null); Assertions.assertTrue(isCacheFileExists()); VertxUtils.blockCloseVertxByName("testCreateVertxWithFileCPResolvingFalse"); // don't create .vertx folder deleteCacheFile(); Assertions.assertFalse(isCacheFileExists()); - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); - VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingTrue", null); + VertxUtils.getOrCreateVertxByName("testCreateVertxWithFileCPResolvingTrue", null, null); Assertions.assertFalse(isCacheFileExists()); VertxUtils.blockCloseVertxByName("testCreateVertxWithFileCPResolvingTrue"); } private void deleteCacheFile() { - String cacheDirBase = System.getProperty(FileResolverImpl.CACHE_DIR_BASE_PROP_NAME, + String cacheDirBase = System.getProperty(SysProps.FILE_CACHE_DIR.name, System.getProperty("java.io.tmpdir", ".")); File folder = new File(cacheDirBase); File[] files = folder.listFiles(); @@ -100,7 +100,7 @@ private void deleteCacheFile() { } private boolean isCacheFileExists() { - String cacheDirBase = System.getProperty(FileResolverImpl.CACHE_DIR_BASE_PROP_NAME, + String cacheDirBase = System.getProperty(SysProps.FILE_CACHE_DIR.name, System.getProperty("java.io.tmpdir", ".")); File folder = new File(cacheDirBase); File[] files = folder.listFiles(); @@ -114,7 +114,7 @@ private boolean isCacheFileExists() { @Test public void testVertxUtilsInitNullOptions() { - Vertx vertx = VertxUtils.init(null, null); + Vertx vertx = VertxUtils.init(null, null, null); Assertions.assertNotEquals(null, vertx); VertxUtils.blockCloseVertx(vertx); } @@ -123,7 +123,7 @@ public void testVertxUtilsInitNullOptions() { public void testVertxUtilsInitWithOptions() { VertxOptions oOptions = new VertxOptions(); - Vertx vertx = VertxUtils.init(null, oOptions); + Vertx vertx = VertxUtils.init(null, oOptions, null); Assertions.assertNotEquals(null, vertx); VertxUtils.blockCloseVertx(vertx); } @@ -131,11 +131,12 @@ public void testVertxUtilsInitWithOptions() { @Test public void testgetBytesFastBufferInputStream() throws IOException { byte[] bytes = new byte[] {1}; - ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes); + Buffer byteBuf = Buffer.buffer(bytes); try (BufferInputStream inputStream = new BufferInputStream(byteBuf)) { byte[] result = VertxUtils.getBytesFast(inputStream); - Assertions.assertSame(bytes, result); + Assertions.assertEquals(bytes.length, result.length); + Assertions.assertEquals(bytes[0], result[0]); } } diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/http/TestHttpClientPoolFactory.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/http/TestHttpClientPoolFactory.java deleted file mode 100644 index cf57fe619d1..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/http/TestHttpClientPoolFactory.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.vertx.client.http; - -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpClientOptions; -import io.vertx.core.impl.ContextInternal; -import io.vertx.core.impl.VertxInternal; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -public class TestHttpClientPoolFactory { - private final HttpClientOptions httpClientOptions = new HttpClientOptions(); - - HttpClientPoolFactory factory = new HttpClientPoolFactory(httpClientOptions); - - @Test - public void createClientPool() { - VertxInternal vertx = Mockito.mock(VertxInternal.class); - ContextInternal context = Mockito.mock(ContextInternal.class); - HttpClient httpClient = Mockito.mock(HttpClient.class); - Mockito.when(context.owner()).thenReturn(vertx); - Mockito.when(vertx.createHttpClient(httpClientOptions)).thenReturn(httpClient); - - HttpClientWithContext pool = factory.createClientPool(context); - - Assertions.assertSame(context, pool.context()); - Assertions.assertSame(httpClient, pool.getHttpClient()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestNetClientWrapper.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestNetClientWrapper.java deleted file mode 100644 index adbcd3e8138..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestNetClientWrapper.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.vertx.client.tcp; - - -import java.util.ArrayList; -import java.util.List; - -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.Test; - -import io.vertx.core.AsyncResult; -import io.vertx.core.Handler; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; -import io.vertx.core.net.NetClient; -import io.vertx.core.net.NetSocket; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; - -public class TestNetClientWrapper { - @Mocked - Vertx vertx; - - @Mocked - TcpClientConfig normalClientConfig; - - @Mocked - NetClient normalNetClient; - - @Mocked - TcpClientConfig sslClientConfig; - - @Mocked - NetClient sslNetClient; - - NetClientWrapper netClientWrapper; - - @Before - public void setup() { - new Expectations() { - { - vertx.createNetClient(normalClientConfig); - result = normalNetClient; - vertx.createNetClient(sslClientConfig); - result = sslNetClient; - } - }; - netClientWrapper = new NetClientWrapper(vertx, normalClientConfig, sslClientConfig); - } - - @Test - public void getClientConfig() { - Assertions.assertSame(normalClientConfig, netClientWrapper.getClientConfig(false)); - Assertions.assertSame(sslClientConfig, netClientWrapper.getClientConfig(true)); - } - - @Test - public void connect(@Mocked NetSocket normalSocket, @Mocked NetSocket sslSocket) { - int port = 8000; - String host = "localhost"; - - Promise promiseConnect = Promise.promise(); - new MockUp(normalNetClient) { - @Mock - NetClient connect(int port, String host, Handler> connectHandler) { - promiseConnect.complete(normalSocket); - connectHandler.handle(promiseConnect.future()); - return null; - } - }; - - Promise sslPromiseConnect = Promise.promise(); - new MockUp(sslNetClient) { - @Mock - NetClient connect(int port, String host, Handler> connectHandler) { - sslPromiseConnect.complete(sslSocket); - connectHandler.handle(sslPromiseConnect.future()); - return null; - } - }; - - List socks = new ArrayList<>(); - netClientWrapper.connect(false, port, host, asyncSocket -> socks.add(asyncSocket.result())); - netClientWrapper.connect(true, port, host, asyncSocket -> socks.add(asyncSocket.result())); - - MatcherAssert.assertThat(socks, Matchers.contains(normalSocket, sslSocket)); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnection.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnection.java index 0767898f2d4..f072d962000 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnection.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnection.java @@ -25,13 +25,14 @@ import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; import org.junit.Before; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.vertx.core.AsyncResult; import io.vertx.core.Context; +import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Promise; +import io.vertx.core.buffer.Buffer; import io.vertx.core.net.NetSocket; import io.vertx.core.net.impl.NetSocketImpl; import mockit.Deencapsulation; @@ -39,7 +40,6 @@ import mockit.Mock; import mockit.MockUp; import mockit.Mocked; -import org.junit.jupiter.api.Assertions; public class TestTcpClientConnection { @Mocked @@ -90,14 +90,14 @@ public void send_inWorkingStatus(@Mocked AbstractTcpClientPackage tcpClientPacka Deencapsulation.setField(tcpClientConnection, "status", Status.WORKING); long msgId = 1; - ByteBuf byteBuf = Unpooled.buffer(); + Buffer byteBuf = Buffer.buffer(); new Expectations(tcpClientConnection) { { tcpClientPackage.getMsgId(); result = msgId; tcpClientPackage.createStream(); result = tcpOutputStream; - tcpOutputStream.getByteBuf(); + tcpOutputStream.getBuffer(); result = byteBuf; } }; @@ -172,7 +172,7 @@ public void send_disconnectedToWorking(@Mocked AbstractTcpClientPackage tcpClien { tcpClientPackage.getMsgId(); result = msgId; - tcpClientConnection.write((ByteBuf) any); + tcpClientConnection.write((Buffer) any); } }; new MockUp(context) { @@ -195,9 +195,9 @@ public void connect_success(@Mocked NetSocketImpl netSocket) { Promise promise = Promise.promise(); new MockUp(netClientWrapper) { @Mock - void connect(boolean ssl, int port, String host, Handler> connectHandler) { + public Future connect(boolean ssl, int port, String host) { promise.complete(netSocket); - connectHandler.handle(promise.future()); + return promise.future(); } }; @@ -216,9 +216,9 @@ public void connect_failed() { RuntimeException error = new RuntimeExceptionWithoutStackTrace(); new MockUp(netClientWrapper) { @Mock - void connect(boolean ssl, int port, String host, Handler> connectHandler) { + public Future connect(boolean ssl, int port, String host) { promise.fail(error); - connectHandler.handle(promise.future()); + return promise.future(); } }; diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnectionPool.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnectionPool.java deleted file mode 100644 index dfaf8ec60a2..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/client/tcp/TestTcpClientConnectionPool.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.vertx.client.tcp; - -import io.vertx.core.impl.ContextInternal; -import io.vertx.core.impl.VertxInternal; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -public class TestTcpClientConnectionPool { - - TcpClientConnectionPool pool; - - @BeforeEach - public void setup() { - ContextInternal context = Mockito.mock(ContextInternal.class); - VertxInternal vertx = Mockito.mock(VertxInternal.class); - Mockito.when(context.owner()).thenReturn(vertx); - NetClientWrapper netClientWrapper = Mockito.mock(NetClientWrapper.class); - pool = new TcpClientConnectionPool(context, netClientWrapper); - } - - @Test - public void create() { - Assertions.assertTrue(pool.create("rest://localhost:8765") instanceof TcpClientConnection); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java index e6083edd0f8..053cc56e7de 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestAbstractHttpServletResponse.java @@ -91,8 +91,7 @@ public void testGetBufferSize() { @Test public void testFlushBuffer() { - Error error = Assertions.assertThrows(Error.class, () -> response.flushBuffer()); - checkError(error); + Assertions.assertDoesNotThrow(() -> response.flushBuffer()); } @Test diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestFileUploadPart.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestFileUploadPart.java index f7d132ed397..ed2e331da71 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestFileUploadPart.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestFileUploadPart.java @@ -20,8 +20,8 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.UUID; import org.apache.commons.io.FileUtils; @@ -33,8 +33,8 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - +import org.mockito.Mockito; + public class TestFileUploadPart { FileUpload fileUpload; diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestReadStreamPart.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestReadStreamPart.java deleted file mode 100644 index 03d425e353c..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestReadStreamPart.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.vertx.http; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.UUID; -import java.util.concurrent.ExecutionException; - -import jakarta.ws.rs.core.HttpHeaders; - -import org.apache.commons.io.FileUtils; -import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; -import org.apache.servicecomb.foundation.vertx.stream.InputStreamToReadStream; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; - -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.file.FileSystemException; -import io.vertx.core.file.OpenOptions; -import io.vertx.core.http.HttpClientResponse; -import io.vertx.core.impl.SyncContext; -import io.vertx.core.impl.VertxInternal; -import io.vertx.core.streams.WriteStream; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestReadStreamPart { - static Vertx vertx = Vertx.vertx(); - - static SyncContext context = new SyncContext(); - - static String src = "src"; - - static InputStream inputStream = new ByteArrayInputStream(src.getBytes()); - - InputStreamToReadStream readStream = new InputStreamToReadStream(context, inputStream, true); - - ReadStreamPart part = new ReadStreamPart(context, readStream); - - @Before - public void setup() throws IOException { - context.setOwner((VertxInternal) vertx); - inputStream.reset(); - } - - @AfterClass - public static void teardown() { - vertx.close(); - } - - @Test - public void constructFromHttpClientResponse_noContentType(@Mocked HttpClientResponse httpClientResponse) { - new Expectations() { - { - httpClientResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION); - result = "xx;filename=name.txt"; - httpClientResponse.getHeader(HttpHeaders.CONTENT_TYPE); - result = null; - } - }; - - part = new ReadStreamPart(context, httpClientResponse); - - Assertions.assertEquals("name.txt", part.getSubmittedFileName()); - Assertions.assertEquals("text/plain", part.getContentType()); - } - - @Test - public void constructFromHttpClientResponse_hasContentType(@Mocked HttpClientResponse httpClientResponse) { - new Expectations() { - { - httpClientResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION); - result = "xx;filename=name.txt"; - httpClientResponse.getHeader(HttpHeaders.CONTENT_TYPE); - result = "type"; - } - }; - - part = new ReadStreamPart(context, httpClientResponse); - - Assertions.assertEquals("name.txt", part.getSubmittedFileName()); - Assertions.assertEquals("type", part.getContentType()); - } - - @Test - public void saveToWriteStream() throws InterruptedException, ExecutionException { - Buffer buf = Buffer.buffer(); - WriteStream writeStream = new MockUp>() { - @Mock - WriteStream write(Buffer data) { - buf.appendBuffer(data); - return null; - } - }.getMockInstance(); - - part.saveToWriteStream(writeStream).get(); - - Assertions.assertEquals(src, buf.toString()); - } - - @Test - public void saveToWriteStream_writeException() throws InterruptedException, ExecutionException { - RuntimeException error = new RuntimeExceptionWithoutStackTrace(); - WriteStream writeStream = new MockUp>() { - Handler exceptionHandler; - - @Mock - WriteStream exceptionHandler(Handler handler) { - this.exceptionHandler = handler; - return null; - } - - @Mock - WriteStream write(Buffer data) { - exceptionHandler.handle(error); - return null; - } - }.getMockInstance(); - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> part.saveToWriteStream(writeStream).get()); - Assertions.assertTrue(exception.getCause() instanceof RuntimeException); - } - - @Test - public void saveToWrite_readException(@Mocked WriteStream writeStream) { - RuntimeException error = new RuntimeExceptionWithoutStackTrace(); - new MockUp(inputStream) { - @Mock - int read(byte[] b) throws IOException { - throw error; - } - }; - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> part.saveToWriteStream(writeStream).get()); - Assertions.assertTrue(exception.getCause() instanceof RuntimeException); - } - - @Test - public void saveAsBytes() throws InterruptedException, ExecutionException { - Assertions.assertArrayEquals(src.getBytes(), part.saveAsBytes().get()); - } - - @Test - public void saveAsString() throws InterruptedException, ExecutionException { - Assertions.assertEquals(src, part.saveAsString().get()); - } - - @Test - public void saveToFile() throws InterruptedException, ExecutionException, IOException { - File dir = new File("target/notExist-" + UUID.randomUUID()); - File file = new File(dir, "a.txt"); - - Assertions.assertFalse(dir.exists()); - - part.saveToFile(file.getAbsolutePath()).get(); - - Assertions.assertEquals(src, FileUtils.readFileToString(file, StandardCharsets.UTF_8)); - - FileUtils.forceDelete(dir); - Assertions.assertFalse(dir.exists()); - } - - @Test - public void saveToFile_notExist_notCreate() throws InterruptedException, ExecutionException, IOException { - File dir = new File("target/notExist-" + UUID.randomUUID()); - File file = new File(dir, "a.txt"); - - Assertions.assertFalse(dir.exists()); - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> { - OpenOptions openOptions = new OpenOptions().setCreateNew(false); - part.saveToFile(file, openOptions).get(); - }); - Assertions.assertTrue(exception.getCause() instanceof FileSystemException); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletRequestEx.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletRequestEx.java index d0adef2ce47..15e52aa5f71 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletRequestEx.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletRequestEx.java @@ -117,7 +117,7 @@ public void parameterMap_merge() throws IOException { inherited.put("p1", v1); Buffer buffer = Buffer.buffer("p1=v1-3;p2=v2"); - BufferInputStream inputStream = new BufferInputStream(buffer.getByteBuf()); + BufferInputStream inputStream = new BufferInputStream(buffer); new Expectations() { { request.getParameterMap(); diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java index c672fb28879..289d5b15226 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestStandardHttpServletResponseEx.java @@ -103,7 +103,7 @@ public void write(int b) { Buffer body = Buffer.buffer().appendString("body"); responseEx.setBodyBuffer(body); responseEx.flushBuffer(); - Assertions.assertEquals("body", buffer.toString()); + Assertions.assertEquals(0, buffer.length()); } @Test diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java deleted file mode 100644 index 66c67547515..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/http/TestVertxServerResponseToHttpServletResponse.java +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.servicecomb.foundation.vertx.http; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; - -import org.apache.commons.io.FileUtils; -import org.apache.servicecomb.foundation.common.http.HttpStatus; -import org.apache.servicecomb.foundation.common.part.FilePart; -import org.apache.servicecomb.foundation.vertx.stream.PumpFromPart; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Before; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; - -import io.vertx.core.AsyncResult; -import io.vertx.core.Context; -import io.vertx.core.Handler; -import io.vertx.core.MultiMap; -import io.vertx.core.Promise; -import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import io.vertx.core.http.HttpServerResponse; -import io.vertx.core.impl.SyncContext; -import io.vertx.core.streams.WriteStream; -import jakarta.servlet.http.Part; -import jakarta.ws.rs.core.HttpHeaders; -import jakarta.ws.rs.core.Response.StatusType; -import mockit.Deencapsulation; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; - -public class TestVertxServerResponseToHttpServletResponse { - MultiMap headers = MultiMap.caseInsensitiveMultiMap(); - - HttpStatus httpStatus = new HttpStatus(123, "default"); - - HttpServerResponse serverResponse; - - VertxServerResponseToHttpServletResponse response; - - boolean flushWithBody; - - boolean runOnContextInvoked; - - @Mocked - Vertx vertx; - - @Mocked - Context context; - - boolean chunked; - - @Before - public void setup() { - serverResponse = new MockUp() { - @Mock - HttpServerResponse setStatusCode(int statusCode) { - Deencapsulation.setField(httpStatus, "statusCode", statusCode); - return serverResponse; - } - - @Mock - HttpServerResponse setStatusMessage(String statusMessage) { - Deencapsulation.setField(httpStatus, "reason", statusMessage); - return serverResponse; - } - - @Mock - int getStatusCode() { - return httpStatus.getStatusCode(); - } - - @Mock - String getStatusMessage() { - return httpStatus.getReasonPhrase(); - } - - @Mock - MultiMap headers() { - return headers; - } - - @Mock - HttpServerResponse putHeader(String name, String value) { - headers.set(name, value); - return serverResponse; - } - - @Mock - void end() { - flushWithBody = false; - } - - @Mock - void end(Buffer chunk) { - flushWithBody = true; - } - - @Mock - HttpServerResponse setChunked(boolean chunked) { - TestVertxServerResponseToHttpServletResponse.this.chunked = chunked; - return serverResponse; - } - - @Mock - boolean isChunked() { - return chunked; - } - }.getMockInstance(); - - new Expectations() { - { - Vertx.currentContext(); - result = context; - } - }; - - new MockUp(context) { - @Mock - void runOnContext(Handler action) { - runOnContextInvoked = true; - action.handle(null); - } - - @Mock - void executeBlocking(Handler> blockingCodeHandler, boolean ordered, - Handler> resultHandler) { - SyncContext.syncExecuteBlocking(blockingCodeHandler, resultHandler); - } - - @Mock - Vertx owner() { - return vertx; - } - }; - - response = new VertxServerResponseToHttpServletResponse(serverResponse); - } - - @Test - public void construct_invalid() throws IOException { - new Expectations() { - { - Vertx.currentContext(); - result = null; - } - }; - - NullPointerException exception = Assertions.assertThrows(NullPointerException.class, - () -> new VertxServerResponseToHttpServletResponse(serverResponse)); - Assertions.assertEquals("must run in vertx context.", exception.getMessage()); - } - - @Test - public void setContentType() { - response.setContentType("json"); - Assertions.assertEquals("json", headers.get(HttpHeaders.CONTENT_TYPE)); - } - - @Test - public void getStatusType() { - StatusType status = response.getStatusType(); - - Assertions.assertSame(status, response.getStatusType()); - Assertions.assertEquals(123, httpStatus.getStatusCode()); - Assertions.assertEquals("default", httpStatus.getReasonPhrase()); - } - - @Test - public void addHeader() { - response.addHeader("n1", "v1_1"); - response.addHeader("n1", "v1_2"); - response.addHeader("n2", "v2"); - - Assertions.assertEquals(2, headers.size()); - MatcherAssert.assertThat(headers.getAll("n1"), Matchers.contains("v1_1", "v1_2")); - MatcherAssert.assertThat(headers.getAll("n2"), Matchers.contains("v2")); - } - - @Test - public void setHeader() { - response.setHeader("n1", "v1_1"); - response.setHeader("n1", "v1_2"); - response.setHeader("n2", "v2"); - - Assertions.assertEquals(2, headers.size()); - MatcherAssert.assertThat(headers.getAll("n1"), Matchers.contains("v1_2")); - MatcherAssert.assertThat(headers.getAll("n2"), Matchers.contains("v2")); - } - - @Test - public void getStatus() { - Assertions.assertEquals(123, response.getStatus()); - } - - @Test - public void getContentType() { - headers.set(HttpHeaders.CONTENT_TYPE, "json"); - Assertions.assertEquals("json", response.getContentType()); - } - - @Test - public void getHeader() { - headers.set(HttpHeaders.CONTENT_TYPE, "json"); - Assertions.assertEquals("json", response.getHeader(HttpHeaders.CONTENT_TYPE)); - } - - @Test - public void getHeaders() { - headers.add("h1", "h1_1"); - headers.add("h1", "h1_2"); - - MatcherAssert.assertThat(response.getHeaders("h1"), Matchers.contains("h1_1", "h1_2")); - } - - @Test - public void getHeaderNames() { - headers.add("h1", "h1"); - headers.add("h2", "h2"); - - MatcherAssert.assertThat(response.getHeaderNames(), Matchers.contains("h1", "h2")); - } - - @Test - public void flushBuffer_sameContext() throws IOException { - response.flushBuffer(); - - Assertions.assertFalse(runOnContextInvoked); - } - - @Test - public void flushBuffer_diffContext() throws IOException { - new Expectations() { - { - Vertx.currentContext(); - result = null; - } - }; - response.flushBuffer(); - - Assertions.assertTrue(runOnContextInvoked); - } - - @Test - public void internalFlushBufferNoBody() throws IOException { - response.internalFlushBuffer(); - - Assertions.assertFalse(flushWithBody); - } - - @Test - public void internalFlushBufferWithBody() throws IOException { - response.setBodyBuffer(Buffer.buffer()); - response.internalFlushBuffer(); - - Assertions.assertTrue(flushWithBody); - } - - @Test - public void prepareSendPartHeader_update(@Mocked Part part) { - new Expectations() { - { - part.getContentType(); - result = "type"; - part.getSubmittedFileName(); - result = "测 试"; - } - }; - DownloadUtils.prepareDownloadHeader(response, part); - - Assertions.assertTrue(serverResponse.isChunked()); - Assertions.assertEquals("type", response.getHeader(HttpHeaders.CONTENT_TYPE)); - Assertions.assertEquals( - "attachment;filename=%E6%B5%8B%20%20%20%20%20%E8%AF%95;filename*=utf-8''%E6%B5%8B%20%20%20%20%20%E8%AF%95", - response.getHeader(HttpHeaders.CONTENT_DISPOSITION)); - } - - @Test - public void prepareSendPartHeader_notUpdate(@Mocked Part part) { - headers.add(HttpHeaders.CONTENT_LENGTH, "10"); - headers.add(HttpHeaders.CONTENT_TYPE, "type"); - headers.add(HttpHeaders.CONTENT_DISPOSITION, "disposition"); - - DownloadUtils.prepareDownloadHeader(response, part); - - Assertions.assertFalse(serverResponse.isChunked()); - Assertions.assertEquals("type", response.getHeader(HttpHeaders.CONTENT_TYPE)); - Assertions.assertEquals("disposition", response.getHeader(HttpHeaders.CONTENT_DISPOSITION)); - } - - @Test - public void sendPart_openInputStreamFailed(@Mocked Part part) - throws IOException { - IOException ioException = new IOException("forbid open stream"); - new Expectations() { - { - part.getInputStream(); - result = ioException; - } - }; - - CompletableFuture future = response.sendPart(part); - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, future::get); - Assertions.assertTrue(exception.getCause() instanceof IOException); - } - - @Test - public void sendPart_testPartIsNull(@Mocked Part part) throws InterruptedException, ExecutionException { - CompletableFuture future1 = response.sendPart(null); - Assertions.assertNull(future1.get()); - } - - @Test - public void sendPart_inputStreamBreak(@Mocked Part part, @Mocked InputStream inputStream) - throws IOException { - IOException ioException = new IOException("forbid read"); - new Expectations() { - { - part.getInputStream(); - result = inputStream; - inputStream.read((byte[]) any); - result = ioException; - } - }; - - CompletableFuture future = response.sendPart(part); - - ExecutionException exception = Assertions.assertThrows(ExecutionException.class, future::get); - Assertions.assertTrue(exception.getCause() instanceof IOException); - } - - @Test - public void sendPart_ReadStreamPart(@Mocked ReadStreamPart part) { - CompletableFuture future = new CompletableFuture<>(); - new MockUp() { - @Mock - CompletableFuture toWriteStream(WriteStream writeStream, Handler throwableHandler) { - return future; - } - }; - - Assertions.assertSame(future, response.sendPart(part)); - } - - @Test - public void sendPart_succ(@Mocked Part part, @Mocked InputStream inputStream) - throws IOException, InterruptedException, ExecutionException { - new Expectations() { - { - part.getInputStream(); - result = inputStream; - inputStream.read((byte[]) any); - result = -1; - } - }; - - CompletableFuture future = response.sendPart(part); - - Assertions.assertNull(future.get()); - } - - @Test - public void clearPartResource_deleteFile() throws IOException { - File file = new File("target", UUID.randomUUID() + ".txt"); - FileUtils.write(file, "content", StandardCharsets.UTF_8); - FilePart part = new FilePart(null, file).setDeleteAfterFinished(true); - - Assertions.assertTrue(file.exists()); - DownloadUtils.clearPartResource(part); - Assertions.assertFalse(file.exists()); - } - - @Test - public void clearPartResource_notDeleteFile() throws IOException { - File file = new File("target", UUID.randomUUID() + ".txt"); - FileUtils.write(file, "content", StandardCharsets.UTF_8); - FilePart part = new FilePart(null, file); - - Assertions.assertTrue(file.exists()); - DownloadUtils.clearPartResource(part); - Assertions.assertTrue(file.exists()); - - file.delete(); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultVertxMetricsFactory.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultVertxMetricsFactory.java index 1ae56ebee4a..ccc67374c82 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultVertxMetricsFactory.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/metrics/TestDefaultVertxMetricsFactory.java @@ -29,14 +29,12 @@ public class TestDefaultVertxMetricsFactory { DefaultVertxMetricsFactory factory = new DefaultVertxMetricsFactory(); - @SuppressWarnings("deprecation") @Test public void metrics() { MetricsOptions metricsOptions = factory.newOptions(); options.setMetricsOptions(metricsOptions); VertxMetrics vertxMetrics = factory.metrics(options); - Assertions.assertSame(factory, metricsOptions.getFactory()); Assertions.assertTrue(metricsOptions.isEnabled()); Assertions.assertSame(factory.getVertxMetrics(), vertxMetrics); diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpServer.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpServer.java deleted file mode 100644 index 2e1446c9e73..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/server/TestTcpServer.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.servicecomb.foundation.vertx.server; - -import java.net.InetSocketAddress; - -import org.apache.servicecomb.foundation.common.net.URIEndpointObject; -import org.apache.servicecomb.foundation.vertx.AsyncResultCallback; -import org.apache.servicecomb.foundation.vertx.metrics.DefaultTcpServerMetrics; -import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; -import org.junit.Test; - -import io.vertx.core.Handler; -import io.vertx.core.Vertx; -import io.vertx.core.net.NetServer; -import io.vertx.core.net.NetServerOptions; -import io.vertx.core.net.NetSocket; -import io.vertx.core.net.impl.NetSocketImpl; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; - -public class TestTcpServer { - static class TcpServerForTest extends TcpServer { - public TcpServerForTest(URIEndpointObject endpointObject) { - super(endpointObject); - } - - @Override - protected TcpServerConnection createTcpServerConnection() { - return new TcpServerConnection() { - @Override - public void init(NetSocket netSocket) { - super.init(netSocket); - } - }; - } - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Test - public void testTcpServerNonSSL(@Mocked Vertx vertx, @Mocked AsyncResultCallback callback, - @Mocked NetServer netServer) { - new Expectations() { - { - vertx.createNetServer(); - result = netServer; - netServer.connectHandler((Handler) any); - netServer.listen(anyInt, anyString, (Handler) any); - } - }; - URIEndpointObject endpointObject = new URIEndpointObject("highway://127.0.0.1:6663"); - TcpServer server = new TcpServerForTest(endpointObject); - // assert done in Expectations - server.init(vertx, "", callback); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Test - public void testTcpServerSSL(@Mocked Vertx vertx, @Mocked AsyncResultCallback callback, - @Mocked NetServer netServer) { - new Expectations() { - { - vertx.createNetServer((NetServerOptions) any); - result = netServer; - netServer.connectHandler((Handler) any); - netServer.listen(anyInt, anyString, (Handler) any); - } - }; - URIEndpointObject endpointObject = new URIEndpointObject("highway://127.0.0.1:6663?sslEnabled=true"); - TcpServer server = new TcpServerForTest(endpointObject); - // assert done in Expectations - server.init(vertx, "", callback); - } - - Handler connectHandler; - - boolean netSocketClosed; - - @SuppressWarnings({"rawtypes", "unchecked"}) - @Test - public void testConnectionLimit(@Mocked Vertx vertx, @Mocked AsyncResultCallback callback, - @Mocked NetServer netServer, @Mocked NetSocketImpl netSocket) { - DefaultServerEndpointMetric endpointMetric = new DefaultServerEndpointMetric(null); - DefaultTcpServerMetrics tcpServerMetrics = new DefaultTcpServerMetrics(endpointMetric); - - new MockUp(netServer) { - @Mock - NetServer connectHandler(Handler handler) { - connectHandler = handler; - return netServer; - } - }; - new MockUp(netSocket) { - @Mock - void close() { - netSocketClosed = true; - } - }; - new Expectations() { - { - vertx.createNetServer((NetServerOptions) any); - result = netServer; - netServer.listen(anyInt, anyString, (Handler) any); - netSocket.metrics(); - result = tcpServerMetrics; - } - }; - URIEndpointObject endpointObject = new URIEndpointObject("highway://127.0.0.1:6663?sslEnabled=true"); - TcpServer server = new TcpServerForTest(endpointObject) { - @Override - protected int getConnectionLimit() { - return 2; - } - }; - // assert done in Expectations - server.init(vertx, "", callback); - - // no problem - endpointMetric.onConnect(); - endpointMetric.onConnect(); - connectHandler.handle(netSocket); - - // reject - endpointMetric.onConnect(); - connectHandler.handle(netSocket); - Assertions.assertTrue(netSocketClosed); - Assertions.assertEquals(1, endpointMetric.getRejectByConnectionLimitCount()); - } -} diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestBufferInputStream.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestBufferInputStream.java index 070fe207d6d..40be5d48642 100644 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestBufferInputStream.java +++ b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestBufferInputStream.java @@ -29,8 +29,7 @@ import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; +import io.vertx.core.buffer.Buffer; public class TestBufferInputStream { @@ -38,7 +37,7 @@ public class TestBufferInputStream { @Before public void setUp() throws Exception { - ByteBuf buffer = Mockito.mock(ByteBuf.class); + Buffer buffer = Mockito.mock(Buffer.class); instance = new BufferInputStream(buffer); } @@ -60,8 +59,8 @@ public void testReadDecorate() throws IOException { gzipOutputStream.write(text.getBytes()); gzipOutputStream.close(); - ByteBuf buffer = Unpooled.buffer(); - buffer.writeBytes(out.toByteArray()); + Buffer buffer = Buffer.buffer(); + buffer.appendBytes(out.toByteArray()); out.close(); BufferInputStream bufferInputStream = new BufferInputStream(buffer); GZIPInputStream gzipInputStream = new GZIPInputStream(bufferInputStream); diff --git a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestPumpFromPart.java b/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestPumpFromPart.java deleted file mode 100644 index dafa8e7f07c..00000000000 --- a/foundations/foundation-vertx/src/test/java/org/apache/servicecomb/foundation/vertx/stream/TestPumpFromPart.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.foundation.vertx.stream; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.concurrent.ExecutionException; - -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.RandomStringUtils; -import org.apache.servicecomb.foundation.common.part.InputStreamPart; -import org.apache.servicecomb.foundation.vertx.stream.InputStreamToReadStream.ReadResult; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; - -import io.vertx.core.Context; -import io.vertx.core.Promise; -import io.vertx.core.impl.SyncContext; -import jakarta.servlet.http.Part; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; - -public class TestPumpFromPart { - String src = RandomStringUtils.random(100, true, true); - - boolean inputStreamClosed; - - InputStream inputStream = new ByteArrayInputStream(src.getBytes()) { - @Override - public void close() throws IOException { - super.close(); - inputStreamClosed = true; - } - }; - - Part part; - - boolean outputStreamClosed; - - BufferOutputStream outputStream; - - IOException error = new IOException(); - - Context context = new SyncContext(); - - private void run(Context context, boolean closeOutput) throws Throwable { - inputStream.reset(); - part = new InputStreamPart("name", inputStream); - - outputStream = new BufferOutputStream() { - @Override - public void close() { - super.close(); - outputStreamClosed = true; - } - }; - - new PumpFromPart(context, part).toOutputStream(outputStream, closeOutput).get(); - } - - public void do_pump_succ(Context context) throws Throwable { - run(context, true); - - Assertions.assertEquals(src, outputStream.getBuffer().toString()); - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - } - - @Test - public void pump_succ() throws Throwable { - do_pump_succ(null); - do_pump_succ(context); - } - - public void do_pump_outputNotClose(Context context) throws Throwable { - run(context, false); - - Assertions.assertEquals(src, outputStream.getBuffer().toString()); - Assertions.assertFalse(outputStreamClosed); - } - - @Test - public void pump_outputNotClose() throws Throwable { - do_pump_outputNotClose(null); - do_pump_outputNotClose(context); - } - - public void pump_error(Context context) { - try { - run(context, true); - Assertions.fail("must throw exception"); - } catch (Throwable e) { - MatcherAssert.assertThat(e, Matchers.instanceOf(ExecutionException.class)); - MatcherAssert.assertThat(e.getCause(), Matchers.sameInstance(error)); - } - - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - } - - @Test - public void pump_read_error() throws IOException { - new MockUp() { - @Mock - void readInWorker(Promise future) { - future.fail(error); - } - }; - new Expectations(IOUtils.class) { - { - IOUtils.copyLarge((InputStream) any, (OutputStream) any); - result = error; - } - }; - - pump_error(null); - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - - inputStreamClosed = false; - outputStreamClosed = false; - pump_error(context); - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - } - - @Test - public void pump_write_error() throws IOException { - new MockUp() { - @Mock - void write(byte[] b) throws IOException { - throw error; - } - }; - new Expectations(IOUtils.class) { - { - IOUtils.copyLarge((InputStream) any, (OutputStream) any); - result = error; - } - }; - - pump_error(null); - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - - inputStreamClosed = false; - outputStreamClosed = false; - pump_error(context); - Assertions.assertTrue(inputStreamClosed); - Assertions.assertTrue(outputStreamClosed); - } -} diff --git a/foundations/pom.xml b/foundations/pom.xml index ad724a691b6..7b3f55d334a 100644 --- a/foundations/pom.xml +++ b/foundations/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default foundations diff --git a/governance/pom.xml b/governance/pom.xml index 5bcf7560875..3f78c36ade5 100644 --- a/governance/pom.xml +++ b/governance/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 @@ -46,14 +46,6 @@ io.micrometer micrometer-core - - org.aspectj - aspectjweaver - - - org.aspectj - aspectjrt - org.apache.commons commons-lang3 diff --git a/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java b/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java index bd3929ad60d..b5fd042a723 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/GovernanceCommonConfiguration.java @@ -42,10 +42,9 @@ import org.apache.servicecomb.governance.marker.operator.PrefixOperator; import org.apache.servicecomb.governance.marker.operator.SuffixOperator; import org.apache.servicecomb.governance.properties.BulkheadProperties; -import org.apache.servicecomb.governance.properties.TimeLimiterProperties; -import org.apache.servicecomb.governance.properties.GovernanceCacheProperties; import org.apache.servicecomb.governance.properties.CircuitBreakerProperties; import org.apache.servicecomb.governance.properties.FaultInjectionProperties; +import org.apache.servicecomb.governance.properties.GovernanceCacheProperties; import org.apache.servicecomb.governance.properties.IdentifierRateLimitProperties; import org.apache.servicecomb.governance.properties.InstanceBulkheadProperties; import org.apache.servicecomb.governance.properties.InstanceIsolationProperties; @@ -54,6 +53,7 @@ import org.apache.servicecomb.governance.properties.MatchProperties; import org.apache.servicecomb.governance.properties.RateLimitProperties; import org.apache.servicecomb.governance.properties.RetryProperties; +import org.apache.servicecomb.governance.properties.TimeLimiterProperties; import org.apache.servicecomb.governance.service.MatchersService; import org.apache.servicecomb.governance.service.MatchersServiceImpl; import org.springframework.beans.factory.ObjectProvider; @@ -66,175 +66,175 @@ public class GovernanceCommonConfiguration { // properties configuration @Bean - public BulkheadProperties bulkheadProperties() { + public BulkheadProperties scbBulkheadProperties() { return new BulkheadProperties(); } @Bean - public InstanceBulkheadProperties instanceBulkheadProperties() { + public InstanceBulkheadProperties scbInstanceBulkheadProperties() { return new InstanceBulkheadProperties(); } @Bean - public CircuitBreakerProperties circuitBreakerProperties() { + public CircuitBreakerProperties scbCircuitBreakerProperties() { return new CircuitBreakerProperties(); } @Bean - public InstanceIsolationProperties instanceIsolationProperties() { + public InstanceIsolationProperties scbInstanceIsolationProperties() { return new InstanceIsolationProperties(); } @Bean - public MatchProperties matchProperties() { + public MatchProperties scbMatchProperties() { return new MatchProperties(); } @Bean - public RateLimitProperties rateLimitProperties() { + public RateLimitProperties scbRateLimitProperties() { return new RateLimitProperties(); } @Bean - public IdentifierRateLimitProperties identifierRateLimitProperties() { + public IdentifierRateLimitProperties scbIdentifierRateLimitProperties() { return new IdentifierRateLimitProperties(); } @Bean - public RetryProperties retryProperties() { + public RetryProperties scbRetryProperties() { return new RetryProperties(); } @Bean - public TimeLimiterProperties timeLimiterProperties() { + public TimeLimiterProperties scbTimeLimiterProperties() { return new TimeLimiterProperties(); } @Bean - public GovernanceCacheProperties cacheProperties() { + public GovernanceCacheProperties scbCacheProperties() { return new GovernanceCacheProperties(); } @Bean - public FaultInjectionProperties faultInjectionProperties() { + public FaultInjectionProperties scbFaultInjectionProperties() { return new FaultInjectionProperties(); } @Bean - public LoadBalanceProperties loadBalanceProperties() { + public LoadBalanceProperties scbLoadBalanceProperties() { return new LoadBalanceProperties(); } @Bean - public MapperProperties mapperProperties() { + public MapperProperties scbMapperProperties() { return new MapperProperties(); } // handlers configuration @Bean - public BulkheadHandler bulkheadHandler(BulkheadProperties bulkheadProperties) { - return new BulkheadHandler(bulkheadProperties); + public BulkheadHandler scbBulkheadHandler(BulkheadProperties scbBulkheadProperties) { + return new BulkheadHandler(scbBulkheadProperties); } @Bean - public InstanceBulkheadHandler instanceBulkheadHandler(InstanceBulkheadProperties instanceBulkheadProperties) { - return new InstanceBulkheadHandler(instanceBulkheadProperties); + public InstanceBulkheadHandler scbInstanceBulkheadHandler(InstanceBulkheadProperties scbInstanceBulkheadProperties) { + return new InstanceBulkheadHandler(scbInstanceBulkheadProperties); } @Bean - public LoadBalanceHandler loadBalanceHandler(LoadBalanceProperties loadBalanceProperties) { + public LoadBalanceHandler scbLoadBalanceHandler(LoadBalanceProperties loadBalanceProperties) { return new LoadBalanceHandler(loadBalanceProperties); } @Bean - public CircuitBreakerHandler circuitBreakerHandler(CircuitBreakerProperties circuitBreakerProperties, + public CircuitBreakerHandler scbCircuitBreakerHandler(CircuitBreakerProperties scbCircuitBreakerProperties, AbstractCircuitBreakerExtension circuitBreakerExtension) { - return new CircuitBreakerHandler(circuitBreakerProperties, circuitBreakerExtension); + return new CircuitBreakerHandler(scbCircuitBreakerProperties, circuitBreakerExtension); } @Bean - public InstanceIsolationHandler instanceIsolationHandler(InstanceIsolationProperties instanceIsolationProperties, + public InstanceIsolationHandler scbInstanceIsolationHandler(InstanceIsolationProperties scbInstanceIsolationProperties, AbstractInstanceIsolationExtension isolationExtension, ObjectProvider meterRegistry) { - return new InstanceIsolationHandler(instanceIsolationProperties, isolationExtension, meterRegistry); + return new InstanceIsolationHandler(scbInstanceIsolationProperties, isolationExtension, meterRegistry); } @Bean - public RateLimitingHandler rateLimitingHandler(RateLimitProperties rateLimitProperties) { + public RateLimitingHandler scbRateLimitingHandler(RateLimitProperties rateLimitProperties) { return new RateLimitingHandler(rateLimitProperties); } @Bean - public IdentifierRateLimitingHandler identifierRateLimitingHandler( + public IdentifierRateLimitingHandler scbIdentifierRateLimitingHandler( IdentifierRateLimitProperties identifierRateLimitProperties) { return new IdentifierRateLimitingHandler(identifierRateLimitProperties); } @Bean - public RetryHandler retryHandler(RetryProperties retryProperties, AbstractRetryExtension retryExtension) { + public RetryHandler scbRetryHandler(RetryProperties retryProperties, AbstractRetryExtension retryExtension) { return new RetryHandler(retryProperties, retryExtension); } @Bean - public TimeLimiterHandler timeLimiterHandler(TimeLimiterProperties timeLimiterProperties) { + public TimeLimiterHandler scbTimeLimiterHandler(TimeLimiterProperties timeLimiterProperties) { return new TimeLimiterHandler(timeLimiterProperties); } @Bean - public GovernanceCacheHandler governanceCacheHandler(GovernanceCacheProperties cacheProperties) { + public GovernanceCacheHandler scbGovernanceCacheHandler(GovernanceCacheProperties cacheProperties) { return new GovernanceCacheHandler(cacheProperties); } @Bean - public FaultInjectionHandler faultInjectionHandler(FaultInjectionProperties faultInjectionProperties) { - return new FaultInjectionHandler(faultInjectionProperties); + public FaultInjectionHandler scbFaultInjectionHandler(FaultInjectionProperties scbFaultInjectionProperties) { + return new FaultInjectionHandler(scbFaultInjectionProperties); } @Bean - public MapperHandler mapperHandler(MapperProperties mapperProperties) { - return new MapperHandler(mapperProperties); + public MapperHandler scbMapperHandler(MapperProperties scbMapperProperties) { + return new MapperHandler(scbMapperProperties); } // request processor @Bean - public RequestProcessor requestProcessor(Map operatorMap) { + public RequestProcessor scbRequestProcessor(Map operatorMap) { return new RequestProcessor(operatorMap); } // matchers @Bean - public MatchersService matchersService(RequestProcessor requestProcessor, MatchProperties matchProperties) { - return new MatchersServiceImpl(requestProcessor, matchProperties); + public MatchersService scbMatchersService(RequestProcessor requestProcessor, MatchProperties scbMatchProperties) { + return new MatchersServiceImpl(requestProcessor, scbMatchProperties); } @Bean - public MatchersManager matchersManager(MatchersService matchersService) { + public MatchersManager scbMatchersManager(MatchersService matchersService) { return new MatchersManager(matchersService); } // operators @Bean - public CompareOperator compareOperator() { + public CompareOperator scbCompareOperator() { return new CompareOperator(); } @Bean - public ContainsOperator containsOperator() { + public ContainsOperator scbContainsOperator() { return new ContainsOperator(); } @Bean - public ExactOperator exactOperator() { + public ExactOperator scbExactOperator() { return new ExactOperator(); } @Bean - public PrefixOperator prefixOperator() { + public PrefixOperator scbPrefixOperator() { return new PrefixOperator(); } @Bean - public SuffixOperator suffixOperator() { + public SuffixOperator scbSuffixOperator() { return new SuffixOperator(); } } diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java index fdeb10a5a59..39082661b24 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/CircuitBreakerHandler.java @@ -72,7 +72,7 @@ private Disposable getCircuitBreaker(String key, CircuitBreakerP .minimumNumberOfCalls(policy.getMinimumNumberOfCalls()) .slidingWindowType(policy.getSlidingWindowTypeEnum()) .slidingWindowSize(Integer.parseInt(policy.getSlidingWindowSize())) - .recordException(circuitBreakerExtension::isFailedResult) + .recordException(e -> circuitBreakerExtension.isFailedResult(policy.getRecordFailureStatus(), e)) .recordResult(r -> circuitBreakerExtension.isFailedResult(policy.getRecordFailureStatus(), r)) .build(); CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java index 5be1978dd4b..894dd170e99 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/InstanceIsolationHandler.java @@ -35,7 +35,7 @@ import io.github.resilience4j.micrometer.tagged.TaggedCircuitBreakerMetrics; import io.micrometer.core.instrument.MeterRegistry; -public class InstanceIsolationHandler extends AbstractGovernanceHandler { +public final class InstanceIsolationHandler extends AbstractGovernanceHandler { private static final Logger LOGGER = LoggerFactory.getLogger(InstanceIsolationHandler.class); private final InstanceIsolationProperties instanceIsolationProperties; @@ -103,7 +103,7 @@ private Disposable getCircuitBreaker(String key, CircuitBreakerP .minimumNumberOfCalls(policy.getMinimumNumberOfCalls()) .slidingWindowType(policy.getSlidingWindowTypeEnum()) .slidingWindowSize(Integer.parseInt(policy.getSlidingWindowSize())) - .recordException(isolationExtension::isFailedResult) + .recordException(e -> isolationExtension.isFailedResult(policy.getRecordFailureStatus(), e)) .recordResult(r -> isolationExtension.isFailedResult(policy.getRecordFailureStatus(), r)) .build(); CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java index 8b4c819b05d..f7f2476f667 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/RetryHandler.java @@ -68,7 +68,7 @@ private Disposable getRetry(String key, RetryPolicy retryPolicy) { RetryConfig config = RetryConfig.custom() .maxAttempts(retryPolicy.getMaxAttempts() + 1) .retryOnResult(response -> retryExtension.isFailedResult(retryPolicy.getRetryOnResponseStatus(), response)) - .retryOnException(retryExtension::isFailedResult) + .retryOnException(exception -> retryExtension.isFailedResult(retryPolicy.getRetryOnResponseStatus(), exception)) .intervalFunction(getIntervalFunction(retryPolicy)) .failAfterMaxAttempts(retryPolicy.isFailAfterMaxAttempts()) .build(); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java index 31ae8eca16f..c07ebe39bdd 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/ext/FailurePredictor.java @@ -29,8 +29,8 @@ import com.google.common.collect.ImmutableMap; -import io.vertx.core.VertxException; import io.netty.handler.ssl.SslHandshakeTimeoutException; +import io.vertx.core.VertxException; public interface FailurePredictor { Map, List> STRICT_RETRIABLE = @@ -49,7 +49,7 @@ public interface FailurePredictor { boolean isFailedResult(List statusList, Object result); - default boolean isFailedResult(Throwable e) { + default boolean isFailedResult(List statusList, Throwable e) { return canRetryForException(STRICT_RETRIABLE, e); } diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java index 2a454e5d11a..e3ac0b2abce 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequest.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.governance.marker; +import java.util.Collections; import java.util.Map; import org.springframework.util.LinkedCaseInsensitiveMap; @@ -26,7 +27,14 @@ public class GovernanceRequest implements GovernanceRequestExtractor { * For provider: headers indicates the request headers to me. * For consumer: headers indicates the request headers to the target. */ - private Map headers; + private Map headers = Collections.emptyMap(); + + /** + * Queries with this request, maybe null. + * For provider: Queries indicates the request params to me. + * For consumer: Queries indicates the request params to the target. + */ + private Map queries = Collections.emptyMap(); /** * api path with this request, maybe null. For REST, e.g. /foo/bar; For RPC, e.g. MySchema.sayHello @@ -70,16 +78,31 @@ public String header(String key) { return headers.get(key); } + @Override + public String query(String key) { + return queries.get(key); + } + public Map getHeaders() { return headers; } + public Map getQueries() { + return queries; + } + public void setHeaders(Map headers) { Map temp = new LinkedCaseInsensitiveMap<>(); temp.putAll(headers); this.headers = temp; } + public void setQueries(Map queries) { + Map temp = new LinkedCaseInsensitiveMap<>(); + temp.putAll(queries); + this.queries = temp; + } + @Override public String apiPath() { return apiPath; diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequestExtractor.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequestExtractor.java index 873f78665ed..28beb8e8384 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequestExtractor.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/GovernanceRequestExtractor.java @@ -23,6 +23,8 @@ public interface GovernanceRequestExtractor { String header(String key); + String query(String key); + String instanceId(); String serviceName(); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/Matcher.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/Matcher.java index 1cff86523b0..6fb18fada3d 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/Matcher.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/Matcher.java @@ -26,6 +26,8 @@ public class Matcher { private Map headers; + private Map queries; + private RawOperator apiPath; private List method; @@ -46,11 +48,18 @@ public Map getHeaders() { return headers; } - public void setHeaders( - Map headers) { + public void setHeaders(Map headers) { this.headers = headers; } + public Map getQueries() { + return queries; + } + + public void setQueries(Map queries) { + this.queries = queries; + } + public RawOperator getApiPath() { return apiPath; } diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/RequestProcessor.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/RequestProcessor.java index a17d3f72b40..9651d8a7740 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/RequestProcessor.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/RequestProcessor.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.governance.marker; +import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; @@ -44,14 +45,13 @@ public class RequestProcessor implements ApplicationContextAware { public static final String infoMessageForCreatingClass = "is not in spring container, create one and register it to spring container"; - private static final String OPERATOR_SUFFIX = "Operator"; - private final Map operatorMap; private ApplicationContext applicationContext; public RequestProcessor(Map operatorMap) { - this.operatorMap = operatorMap; + this.operatorMap = new HashMap<>(operatorMap.size()); + operatorMap.forEach((k, v) -> this.operatorMap.put(v.name(), v)); } public boolean match(GovernanceRequestExtractor request, Matcher matcher) { @@ -64,6 +64,9 @@ public boolean match(GovernanceRequestExtractor request, Matcher matcher) { if (!headersMatch(request, matcher)) { return false; } + if (!queriesMatch(request, matcher)) { + return false; + } if (!serviceNameMatch(request, matcher)) { return false; } @@ -90,6 +93,19 @@ private boolean headersMatch(GovernanceRequestExtractor request, Matcher matcher return true; } + private boolean queriesMatch(GovernanceRequestExtractor request, Matcher matcher) { + if (matcher.getQueries() == null) { + return true; + } + for (Entry entry : matcher.getQueries().entrySet()) { + if (request.query(entry.getKey()) == null || + !operatorMatch(request.query(entry.getKey()), entry.getValue())) { + return false; + } + } + return true; + } + private boolean apiPathMatch(GovernanceRequestExtractor request, Matcher matcher) { if (matcher.getApiPath() == null) { return true; @@ -110,7 +126,7 @@ private boolean operatorMatch(String str, RawOperator rawOperator) { } for (Entry entry : rawOperator.entrySet()) { - MatchOperator operator = operatorMap.get(entry.getKey() + OPERATOR_SUFFIX); + MatchOperator operator = operatorMap.get(entry.getKey()); if (operator == null) { LOGGER.error("unsupported operator:" + entry.getKey() + ", please use one of :" + operatorMap.keySet()); return false; diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/CompareOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/CompareOperator.java index ff491ecefe5..5b73b9aa939 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/CompareOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/CompareOperator.java @@ -32,8 +32,13 @@ public CompareOperator() { charSet.add('!'); } + @Override + public String name() { + return "compare"; + } + /** - * 支持 > < = >= <= ! 后面加数字 + * 支持 {@code > < = >= <= !} 后面加数字 * * @param targetStr * @param patternStr diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ContainsOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ContainsOperator.java index a5bb935f9b1..faa40521441 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ContainsOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ContainsOperator.java @@ -18,6 +18,11 @@ public class ContainsOperator implements MatchOperator { + @Override + public String name() { + return "contains"; + } + @Override public boolean match(String targetStr, String patternStr) { return targetStr.contains(patternStr); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ExactOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ExactOperator.java index 139d2f8cd99..cee8d48a27e 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ExactOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/ExactOperator.java @@ -20,6 +20,11 @@ public class ExactOperator implements MatchOperator { + @Override + public String name() { + return "exact"; + } + @Override public boolean match(String targetStr, String patternStr) { return StringUtils.equals(targetStr, patternStr); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/MatchOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/MatchOperator.java index db92907455a..28abc1b8df5 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/MatchOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/MatchOperator.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.governance.marker.operator; public interface MatchOperator { + String name(); boolean match(String targetStr, String patternStr); } diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/PrefixOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/PrefixOperator.java index e211d0d605f..6bb84c45a53 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/PrefixOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/PrefixOperator.java @@ -20,6 +20,11 @@ import org.apache.commons.lang3.StringUtils; public class PrefixOperator implements MatchOperator { + @Override + public String name() { + return "prefix"; + } + @Override public boolean match(String requestValue, String patternValue) { return StringUtils.startsWith(requestValue, patternValue); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/SuffixOperator.java b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/SuffixOperator.java index 712b00b6bb0..99adf9fed1e 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/SuffixOperator.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/marker/operator/SuffixOperator.java @@ -20,6 +20,11 @@ import org.apache.commons.lang3.StringUtils; public class SuffixOperator implements MatchOperator { + @Override + public String name() { + return "suffix"; + } + @Override public boolean match(String requestValue, String patternValue) { return StringUtils.endsWith(requestValue, patternValue); diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceProperties.java index f8bda95510e..05f42dfa5b1 100644 --- a/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceProperties.java +++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/GovernanceProperties.java @@ -199,8 +199,8 @@ private boolean servicesMatch(String services) { return true; } - return Arrays.stream(services.split(",")).anyMatch(ser -> { - String[] serviceAndVersion = ser.split(":"); + return Arrays.stream(services.split(",")).anyMatch(service -> { + String[] serviceAndVersion = service.split(":"); if (serviceAndVersion.length == 1) { return microserviceMeta.getName().equals(serviceAndVersion[0]); } else if (serviceAndVersion.length == 2) { diff --git a/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java b/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java index 4b11c5481a3..9aa881ee8df 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java +++ b/governance/src/main/java/org/apache/servicecomb/router/RouterCommonConfiguration.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.router; +import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.match.RouterRuleMatcher; import org.springframework.context.annotation.Bean; @@ -25,17 +26,17 @@ @Configuration public class RouterCommonConfiguration { @Bean - public RouterRuleCache routerRuleCache(Environment environment) { + public RouterRuleCache scbRouterRuleCache(Environment environment) { return new RouterRuleCache(environment); } @Bean - public RouterRuleMatcher routerRuleMatcher(RouterRuleCache routerRuleCache) { - return new RouterRuleMatcher(routerRuleCache); + public RouterRuleMatcher scbRouterRuleMatcher(RouterRuleCache routerRuleCache, RequestProcessor requestProcessor) { + return new RouterRuleMatcher(routerRuleCache, requestProcessor); } @Bean - public RouterFilter routerFilter(RouterRuleMatcher routerRuleMatcher, RouterRuleCache routerRuleCache) { + public RouterFilter scbRouterFilter(RouterRuleMatcher routerRuleMatcher, RouterRuleCache routerRuleCache) { return new RouterFilter(routerRuleMatcher, routerRuleCache); } } diff --git a/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java b/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java index f544dd71c35..ab742e8613d 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java +++ b/governance/src/main/java/org/apache/servicecomb/router/RouterFilter.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.router; import java.util.List; -import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; import org.apache.servicecomb.router.match.RouterRuleMatcher; @@ -42,7 +42,7 @@ public RouterFilter(RouterRuleMatcher routerRuleMatcher, RouterRuleCache routerR } public List getFilteredListOfServers(List list, - String targetServiceName, Map headers, RouterDistributor distributor) { + String targetServiceName, GovernanceRequestExtractor extractor, RouterDistributor distributor) { if (CollectionUtils.isEmpty(list)) { return list; } @@ -55,7 +55,7 @@ public List getFilteredListOfServers(List list, return list; } // 2.match rule - PolicyRuleItem invokeRule = routerRuleMatcher.match(targetServiceName, headers); + PolicyRuleItem invokeRule = routerRuleMatcher.match(targetServiceName, extractor); if (invokeRule == null) { LOGGER.debug("route management match rule failed"); diff --git a/governance/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java b/governance/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java index de3734a2235..7828c82d3a7 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java +++ b/governance/src/main/java/org/apache/servicecomb/router/cache/RouterRuleCache.java @@ -119,6 +119,8 @@ private boolean addAllRule(String targetServiceName) { } ServiceInfoCache serviceInfoCache = new ServiceInfoCache(policyRuleItemList); serviceInfoCacheMap.put(targetServiceName, serviceInfoCache); + LOGGER.info("Route management serialization service {} rules success, content: {}", targetServiceName, + serviceInfoCache.getAllrule()); return true; } diff --git a/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java b/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java index 731d05d9764..d3905dfb133 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java +++ b/governance/src/main/java/org/apache/servicecomb/router/distribute/AbstractRouterDistributor.java @@ -17,6 +17,7 @@ package org.apache.servicecomb.router.distribute; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,34 +53,55 @@ public void setRouterRuleCache(RouterRuleCache routerRuleCache) { protected AbstractRouterDistributor() { } + /** + * distribute logic: + * 1、First according to the set route rules to choose target instances, if have just return. + * 2、if route rules not match instance, check if fallback rules are set, if set and match instances then return. + * 3、if route and fallback routes all have not match instance, then if route rules weight count less 100, return + * unset instances, otherwise return all instances. + * @param targetServiceName + * @param list + * @param invokeRule + * @return + */ @Override public List distribute(String targetServiceName, List list, PolicyRuleItem invokeRule) { - invokeRule.check(); // unSetTags instance list List unSetTagInstances = new ArrayList<>(); - // get tag list - Map> versionServerMap = getDistributList(targetServiceName, list, invokeRule, unSetTagInstances); + // record fallback router targItem instance + Map> fallbackVersionServerMap = new HashMap<>(); - if (CollectionUtils.isEmpty(versionServerMap)) { - LOGGER.debug("route management can not match any rule and route the latest version"); - // rule note matched instance babel, all instance return, select instance for load balancing later - return list; - } + // get tag instance map, fallbackVersionServerMap, unSetTagInstances + Map> versionServerMap = getDistributList(targetServiceName, list, invokeRule, + unSetTagInstances, fallbackVersionServerMap); // weight calculation to obtain the next tags instance - TagItem targetTag = getFiltedServerTagItem(invokeRule, targetServiceName); + TagItem targetTag = getFilteredServerTagItem(invokeRule, targetServiceName); if (targetTag != null && versionServerMap.containsKey(targetTag)) { return versionServerMap.get(targetTag); } - // has weightLess situation - if (invokeRule.isWeightLess() && unSetTagInstances.size() > 0) { + if (!fallbackVersionServerMap.isEmpty()) { + // weight calculation to obtain the next fallback tags instance + TagItem fallbackTargetTag = getFallbackFilteredServerTagItem(invokeRule, targetServiceName); + if (fallbackTargetTag != null && fallbackVersionServerMap.containsKey(fallbackTargetTag)) { + return fallbackVersionServerMap.get(fallbackTargetTag); + } + } + + // has weightLess situation and unSetTagInstances has values + if (invokeRule.isWeightLess() && !unSetTagInstances.isEmpty()) { return unSetTagInstances; } - return list; + if (invokeRule.isEmptyProtection()) { + return list; + } + + // weight set 100 but not matched any instance, then return empty when emptyProtection close + return Collections.emptyList(); } @Override @@ -91,38 +113,31 @@ public void init(Function getVersion, this.getProperties = getProperties; } - public TagItem getFiltedServerTagItem(PolicyRuleItem rule, String targetServiceName) { + public TagItem getFilteredServerTagItem(PolicyRuleItem rule, String targetServiceName) { return routerRuleCache.getServiceInfoCacheMap().get(targetServiceName) .getNextInvokeVersion(rule); } + public TagItem getFallbackFilteredServerTagItem(PolicyRuleItem rule, String targetServiceName) { + return routerRuleCache.getServiceInfoCacheMap().get(targetServiceName) + .getFallbackNextInvokeVersion(rule); + } + /** - * 1.filter targetService + * 1.filter set route rules targetService, build fallback targetService map and unSetTagInstances list. * 2.establish map is a more complicate way than direct traversal, because of multiple matches. * * the method getProperties() contains other field that we don't need. */ - private Map> getDistributList(String serviceName, - List list, PolicyRuleItem invokeRule, List unSetTagInstances) { + private Map> getDistributList(String serviceName, List list, + PolicyRuleItem invokeRule, List unSetTagInstances, Map> fallbackVersionMap) { Map> versionServerMap = new HashMap<>(); for (INSTANCE instance : list) { //get server if (getServerName.apply(instance).equals(serviceName)) { - //most matching TagItem tagItem = new TagItem(getVersion.apply(instance), getProperties.apply(instance)); - TagItem targetTag = null; - int maxMatch = 0; - // obtain the rule with the most parameter matches - for (RouteItem entry : invokeRule.getRoute()) { - if (entry.getTagitem() == null){ - continue; - } - int nowMatch = entry.getTagitem().matchNum(tagItem); - if (nowMatch > maxMatch) { - maxMatch = nowMatch; - targetTag = entry.getTagitem(); - } - } + // route most matching TagItem + TagItem targetTag = buildTargetTag(invokeRule.getRoute(), tagItem); if (targetTag != null) { if (!versionServerMap.containsKey(targetTag)) { versionServerMap.put(targetTag, new ArrayList<>()); @@ -132,8 +147,34 @@ private Map> getDistributList(String serviceName, // not matched, placed in the unset tag instances collection unSetTagInstances.add(instance); } + // ensure the tags can build when set for both route and fallback at the same time + if (!CollectionUtils.isEmpty(invokeRule.getFallback())) { + // fallback most matching TagItem + TagItem targetTagFallback = buildTargetTag(invokeRule.getFallback(), tagItem); + if (!fallbackVersionMap.containsKey(targetTagFallback)) { + fallbackVersionMap.put(targetTagFallback, new ArrayList<>()); + } + fallbackVersionMap.get(targetTagFallback).add(instance); + } } } return versionServerMap; } + + private TagItem buildTargetTag(List route, TagItem tagItem) { + int maxMatch = 0; + TagItem targetTag = null; + // obtain the rule with the most parameter matches + for (RouteItem entry : route) { + if (entry.getTagitem() == null){ + continue; + } + int nowMatch = entry.getTagitem().matchNum(tagItem); + if (nowMatch > maxMatch) { + maxMatch = nowMatch; + targetTag = entry.getTagitem(); + } + } + return targetTag; + } } diff --git a/governance/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java b/governance/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java index fa9af8fd16f..5f5babdec34 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java +++ b/governance/src/main/java/org/apache/servicecomb/router/distribute/RouterDistributor.java @@ -23,8 +23,8 @@ import org.apache.servicecomb.router.model.PolicyRuleItem; /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public interface RouterDistributor { diff --git a/governance/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java b/governance/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java index 61fda32c8e4..9647a853ea8 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java +++ b/governance/src/main/java/org/apache/servicecomb/router/exception/RouterIllegalParamException.java @@ -17,8 +17,8 @@ package org.apache.servicecomb.router.exception; /** - * @Author GuoYl123 - * @Date 2019/11/4 + * @author GuoYl123 + * @since 2019/11/4 **/ public class RouterIllegalParamException extends RuntimeException { diff --git a/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java b/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java index a7fcb0f2811..dd2ba78d7d3 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java +++ b/governance/src/main/java/org/apache/servicecomb/router/match/RouterRuleMatcher.java @@ -16,22 +16,25 @@ */ package org.apache.servicecomb.router.match; -import java.util.Map; - +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; +import org.apache.servicecomb.governance.marker.RequestProcessor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.model.PolicyRuleItem; public class RouterRuleMatcher { private final RouterRuleCache routerRuleCache; - public RouterRuleMatcher(RouterRuleCache routerRuleCache) { + private final RequestProcessor requestProcessor; + + public RouterRuleMatcher(RouterRuleCache routerRuleCache, RequestProcessor requestProcessor) { this.routerRuleCache = routerRuleCache; + this.requestProcessor = requestProcessor; } - public PolicyRuleItem match(String serviceName, Map invokeHeader) { + public PolicyRuleItem match(String serviceName, GovernanceRequestExtractor request) { for (PolicyRuleItem rule : routerRuleCache.getServiceInfoCacheMap().get(serviceName) .getAllrule()) { - if (rule.getMatch() == null || rule.getMatch().match(invokeHeader)) { + if (rule.getMatch() == null || requestProcessor.match(request, rule.getMatch())) { return rule; } } diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java b/governance/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java index 931fcee2273..7a8afeda77f 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/HeaderRule.java @@ -21,8 +21,8 @@ import org.slf4j.LoggerFactory; /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public class HeaderRule { diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/Matcher.java b/governance/src/main/java/org/apache/servicecomb/router/model/Matcher.java deleted file mode 100644 index 7db815dd267..00000000000 --- a/governance/src/main/java/org/apache/servicecomb/router/model/Matcher.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.router.model; - -import java.util.Map; -import org.springframework.util.CollectionUtils; - -/** - * @Author GuoYl123 - * @Date 2019/10/17 - **/ -public class Matcher { - - private String source; - - private Map sourceTags; - - private Map headers; - - private String refer; - - public Matcher() { - } - - public boolean match(Map realHeaders) { - if (CollectionUtils.isEmpty(headers)) { - return true; - } - for (Map.Entry entry : headers.entrySet()) { - if (!realHeaders.containsKey(entry.getKey()) || !entry.getValue() - .match(realHeaders.get(entry.getKey()))) { - return false; - } - } - return true; - } - - public String getSource() { - return source; - } - - public void setSource(String source) { - this.source = source; - } - - public Map getSourceTags() { - return sourceTags; - } - - public void setSourceTags(Map sourceTags) { - this.sourceTags = sourceTags; - } - - public Map getHeaders() { - return headers; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public String getRefer() { - return refer; - } - - public void setRefer(String refer) { - this.refer = refer; - } - - @Override - public String toString() { - return "Matcher{" + - "source='" + source + '\'' + - ", sourceTags=" + sourceTags + - ", headers=" + headers + - ", refer='" + refer + '\'' + - '}'; - } -} diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java b/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java index 1006abfec1b..0700a2618d8 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/PolicyRuleItem.java @@ -18,14 +18,15 @@ import java.util.List; +import org.apache.servicecomb.governance.marker.Matcher; import org.apache.servicecomb.router.exception.RouterIllegalParamException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public class PolicyRuleItem implements Comparable { @@ -35,13 +36,18 @@ public class PolicyRuleItem implements Comparable { private Matcher match; - // any match private List route; private Integer total; private boolean weightLess = false; + private List fallback; + + private Integer fallbackTotal; + + private boolean emptyProtection = true; + public PolicyRuleItem() { } @@ -113,6 +119,30 @@ public void setWeightLess(boolean weightLess) { this.weightLess = weightLess; } + public List getFallback() { + return fallback; + } + + public void setFallback(List fallback) { + this.fallback = fallback; + } + + public Integer getFallbackTotal() { + return fallbackTotal; + } + + public void setFallbackTotal(Integer fallbackTotal) { + this.fallbackTotal = fallbackTotal; + } + + public boolean isEmptyProtection() { + return emptyProtection; + } + + public void setEmptyProtection(boolean emptyProtection) { + this.emptyProtection = emptyProtection; + } + @Override public String toString() { return "PolicyRuleItem{" + @@ -121,6 +151,9 @@ public String toString() { ", route=" + route + ", total=" + total + ", weightLess=" + weightLess + + ", fallback=" + fallback + + ", fallbackTotal=" + fallbackTotal + + ", emptyProtection=" + emptyProtection + '}'; } } diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java b/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java index f2017a923e0..5f1d6e9f8df 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/RouteItem.java @@ -19,8 +19,8 @@ import java.util.Map; /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public class RouteItem implements Comparable { diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java b/governance/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java index 15cc08c949b..232f1da6338 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/ServiceInfoCache.java @@ -19,9 +19,11 @@ import java.util.List; import java.util.stream.Collectors; +import org.springframework.util.CollectionUtils; + /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public class ServiceInfoCache { private final List allrule; @@ -34,9 +36,12 @@ public class ServiceInfoCache { public ServiceInfoCache(List policyRuleItemList) { this.allrule = policyRuleItemList.stream().sorted().collect(Collectors.toList()); - this.getAllrule().forEach(rule -> - rule.getRoute().forEach(RouteItem::initTagItem) - ); + this.getAllrule().forEach(rule -> { + rule.getRoute().forEach(RouteItem::initTagItem); + if (!CollectionUtils.isEmpty(rule.getFallback())) { + rule.getFallback().forEach(RouteItem::initTagItem); + } + }); } public TagItem getNextInvokeVersion(PolicyRuleItem policyRuleItem) { @@ -44,7 +49,19 @@ public TagItem getNextInvokeVersion(PolicyRuleItem policyRuleItem) { if (policyRuleItem.getTotal() == null) { policyRuleItem.setTotal(rule.stream().mapToInt(RouteItem::getWeight).sum()); } - rule.stream().forEach(RouteItem::addCurrentWeight); + return calculateWeight(rule, policyRuleItem.getTotal()); + } + + public TagItem getFallbackNextInvokeVersion(PolicyRuleItem policyRuleItem) { + List rule = policyRuleItem.getFallback(); + if (policyRuleItem.getFallbackTotal() == null) { + policyRuleItem.setFallbackTotal(rule.stream().mapToInt(RouteItem::getWeight).sum()); + } + return calculateWeight(rule, policyRuleItem.getFallbackTotal()); + } + + private TagItem calculateWeight(List rule, int total) { + rule.forEach(RouteItem::addCurrentWeight); int maxIndex = 0, maxWeight = -1; for (int i = 0; i < rule.size(); i++) { if (maxWeight < rule.get(i).getCurrentWeight()) { @@ -52,7 +69,7 @@ public TagItem getNextInvokeVersion(PolicyRuleItem policyRuleItem) { maxWeight = rule.get(i).getCurrentWeight(); } } - rule.get(maxIndex).reduceCurrentWeight(policyRuleItem.getTotal()); + rule.get(maxIndex).reduceCurrentWeight(total); return rule.get(maxIndex).getTagitem(); } diff --git a/governance/src/main/java/org/apache/servicecomb/router/model/TagItem.java b/governance/src/main/java/org/apache/servicecomb/router/model/TagItem.java index 0fc610df769..a7a20fab7ea 100644 --- a/governance/src/main/java/org/apache/servicecomb/router/model/TagItem.java +++ b/governance/src/main/java/org/apache/servicecomb/router/model/TagItem.java @@ -21,8 +21,8 @@ import java.util.Objects; /** - * @Author GuoYl123 - * @Date 2019/10/17 + * @author GuoYl123 + * @since 2019/10/17 **/ public class TagItem { diff --git a/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java b/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java index 9c84287ac5c..dedeba80053 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/AbstractFailurePredictorTest.java @@ -35,8 +35,8 @@ protected String extractStatusCode(Object result) { } @Override - public boolean isFailedResult(Throwable e) { - return super.isFailedResult(e); + public boolean isFailedResult(List statusList, Throwable e) { + return super.isFailedResult(statusList, e); } } diff --git a/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java index 4e77fa900b3..59703d4ee7a 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/BulkheadHandlerTest.java @@ -32,8 +32,8 @@ public class BulkheadHandlerTest { private BulkheadHandler bulkheadHandler; @Autowired - public void setInstanceIsolationHandler(BulkheadHandler bulkheadHandler) { - this.bulkheadHandler = bulkheadHandler; + public void setInstanceIsolationHandler(BulkheadHandler scbBulkheadHandler) { + this.bulkheadHandler = scbBulkheadHandler; } @Test diff --git a/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java b/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java index 92890baa663..a0e0c2d8ab9 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/CustomMatchTest.java @@ -90,7 +90,7 @@ public void test_should_pass_when_multiple_value() { public void test_should_throw_exception_when_not_implements_interface() { GovernanceRequest request = new GovernanceRequest(); try { - Matcher mockMatcher = generateMatcher("classNotImplments", "bill,bill2"); + Matcher mockMatcher = generateMatcher("classNotImplements", "bill,bill2"); this.requestProcessor.match(request, mockMatcher); Assertions.fail("an exception is expected!"); } catch (Exception e) { diff --git a/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java b/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java index 919f260d0b1..7733b8ed44f 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java @@ -42,9 +42,9 @@ public class FaultInjectionTest { private FaultInjectionHandler faultInjectionHandler2; @Autowired - public void setFaultInjectionHandler(FaultInjectionHandler faultInjectionHandler, + public void setFaultInjectionHandler(FaultInjectionHandler scbFaultInjectionHandler, @Qualifier("faultInjectionHandler2") FaultInjectionHandler faultInjectionHandler2) { - this.faultInjectionHandler = faultInjectionHandler; + this.faultInjectionHandler = scbFaultInjectionHandler; this.faultInjectionHandler2 = faultInjectionHandler2; } diff --git a/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java b/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java index 4f12dec62f2..5b71a2ac3b8 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java @@ -89,49 +89,49 @@ public void setPropertiesList(List queries = new HashMap<>(); + queries.put("name", "bob"); + request.setQueries(queries); + Mapper mapper = mapperHandler.getActuator(request); + Assertions.assertEquals(1, mapper.target().size()); + Assertions.assertEquals("$Q{name}", mapper.target().get("user-id")); + } + @Test public void test_mapper2_work() { GovernanceRequest request = new GovernanceRequest(); diff --git a/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java b/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java index b75d9cdb942..85c04f39590 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/MockConfiguration.java @@ -30,8 +30,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import io.micrometer.prometheus.PrometheusConfig; -import io.micrometer.prometheus.PrometheusMeterRegistry; +import io.micrometer.prometheusmetrics.PrometheusConfig; +import io.micrometer.prometheusmetrics.PrometheusMeterRegistry; + @Configuration public class MockConfiguration { diff --git a/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java b/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java index 1dd272ca187..2badc9c1548 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/MockRetryExtension.java @@ -34,7 +34,7 @@ protected String extractStatusCode(Object result) { } @Override - public boolean isFailedResult(Throwable e) { + public boolean isFailedResult(List statusList, Throwable e) { return false; } } diff --git a/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java b/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java index 1223bfe7465..073cfc44534 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/RetryHandlerTest.java @@ -35,7 +35,7 @@ public void testNotFailAfterMaxAttemptsWhenThrow() { AbstractRetryExtension retryExtension = Mockito.mock(AbstractRetryExtension.class); RetryProperties retryProperties = Mockito.mock(RetryProperties.class); GovernanceRequest governanceRequest = Mockito.mock(GovernanceRequest.class); - Mockito.when(retryExtension.isFailedResult(Mockito.any())).thenReturn(true); + Mockito.when(retryExtension.isFailedResult(Mockito.any(), Mockito.any())).thenReturn(true); RetryPolicy retryPolicy = new RetryPolicy(); retryPolicy.setName("test"); @@ -53,7 +53,7 @@ public void testFailAfterMaxAttemptsWhenThrow() { AbstractRetryExtension retryExtension = Mockito.mock(AbstractRetryExtension.class); RetryProperties retryProperties = Mockito.mock(RetryProperties.class); GovernanceRequest governanceRequest = Mockito.mock(GovernanceRequest.class); - Mockito.when(retryExtension.isFailedResult(Mockito.any())).thenReturn(true); + Mockito.when(retryExtension.isFailedResult(Mockito.any(), Mockito.any())).thenReturn(true); RetryPolicy retryPolicy = new RetryPolicy(); retryPolicy.setName("test"); @@ -71,7 +71,7 @@ public void testFailAfterMaxAttemptsOnResult() { AbstractRetryExtension retryExtension = Mockito.mock(AbstractRetryExtension.class); RetryProperties retryProperties = Mockito.mock(RetryProperties.class); GovernanceRequest governanceRequest = Mockito.mock(GovernanceRequest.class); - Mockito.when(retryExtension.isFailedResult(Mockito.any(), Mockito.any())).thenReturn(true); + Mockito.when(retryExtension.isFailedResult(Mockito.any(), (Object) Mockito.any())).thenReturn(true); RetryPolicy retryPolicy = new RetryPolicy(); retryPolicy.setName("test"); diff --git a/governance/src/test/java/org/apache/servicecomb/governance/handler/ext/RetryExtensionTest.java b/governance/src/test/java/org/apache/servicecomb/governance/handler/ext/RetryExtensionTest.java index 15a0c5489d9..41157a9b079 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/handler/ext/RetryExtensionTest.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/handler/ext/RetryExtensionTest.java @@ -24,16 +24,16 @@ import java.util.Arrays; import java.util.List; -import org.junit.Test; + import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; import io.vertx.core.VertxException; -@RunWith(SpringRunner.class) +@SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RetryExtensionTest { @@ -134,7 +134,7 @@ public void testRetryNoRouteToHostException() { @Test public void testRetryEqualTen() { - Exception target = new ConnectException("connectin refused"); + Exception target = new ConnectException("connection refused"); for (int i = 0; i < 8; i++) { target = new Exception("Level" + i, target); } @@ -145,7 +145,7 @@ public void testRetryEqualTen() { @Test public void testRetryOverTen() { - Exception target = new ConnectException("connectin refused"); + Exception target = new ConnectException("connection refused"); for (int i = 0; i < 9; i++) { target = new Exception("Level" + i, target); } diff --git a/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplments.java b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplements.java similarity index 96% rename from governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplments.java rename to governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplements.java index 85d14fe0834..51336e60ed5 100644 --- a/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplments.java +++ b/governance/src/test/java/org/apache/servicecomb/governance/mockclasses/ClassNotImplements.java @@ -21,7 +21,7 @@ import org.apache.servicecomb.governance.marker.GovernanceRequest; @Component -public class ClassNotImplments { +public class ClassNotImplements { public boolean matchRequest(GovernanceRequest request, String parameters) { return true; } diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java index c3901878bdd..cc8cc9ce96c 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfig2Test.java @@ -26,21 +26,23 @@ import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.junit.Before; -import org.junit.Test; + import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) +@SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorDynamicConfig2Test { private static final String TARGET_SERVICE_NAME = "test_server"; @@ -89,7 +91,7 @@ public void setTestDistributor(RouterDistributor testDistributor) { private final Map dynamicValues = new HashMap<>(); - @Before + @BeforeEach public void setUp() { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; @@ -124,9 +126,11 @@ private void postConfigurationChangedEvent() { @Test public void testMatchPrecedenceHigher() { + GovernanceRequest governanceRequest = new GovernanceRequest(); Map headers = new HashMap<>(); headers.put("userId", "01"); headers.put("appId", "01"); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -136,7 +140,7 @@ public void testMatchPrecedenceHigher() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); } @@ -164,6 +168,8 @@ public void testCaseSensitiveNotMatch() { Map headers = new HashMap<>(); headers.put("userId", "User01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -173,7 +179,7 @@ public void testCaseSensitiveNotMatch() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); Assertions.assertEquals("2.0", resultServerList.get(0).getVersion()); @@ -207,6 +213,8 @@ public void testNoneMatch() { Map headers = new HashMap<>(); headers.put("userId", "User01"); headers.put("appId", "App01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -216,7 +224,7 @@ public void testNoneMatch() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(2, resultServerList.size()); } @@ -248,6 +256,8 @@ public void testOneMatchButNoInstance() { Map headers = new HashMap<>(); headers.put("userId", "user01"); headers.put("appId", "app02"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -257,13 +267,13 @@ public void testOneMatchButNoInstance() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(2, resultServerList.size()); } - private List mainFilter(List serverList, Map headers) { + private List mainFilter(List serverList, GovernanceRequestExtractor requestExtractor) { return routerFilter - .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, headers, + .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, requestExtractor, testDistributor); } } diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java index 47228c7b82b..4418a30829b 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorDynamicConfigTest.java @@ -18,7 +18,6 @@ package org.apache.servicecomb.router; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -27,21 +26,22 @@ import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.junit.Before; -import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) +@SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorDynamicConfigTest { private static final String TARGET_SERVICE_NAME = "test_server"; @@ -51,10 +51,9 @@ public class RouterDistributorDynamicConfigTest { + " match: #匹配策略\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 01\n" + + " exact: \"01\"\n" + " route: #路由规则\n" + " - weight: 50\n" + " tags:\n" @@ -63,10 +62,9 @@ public class RouterDistributorDynamicConfigTest { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 02\n" + + " exact: \"02\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -75,10 +73,9 @@ public class RouterDistributorDynamicConfigTest { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 03\n" + + " exact: \"03\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -110,7 +107,7 @@ public RouterDistributorDynamicConfigTest() { private final Map dynamicValues = new HashMap<>(); - @Before + @BeforeEach public void setUp() { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; @@ -143,7 +140,7 @@ public String[] getPropertyNames() { @Test public void testHeaderIsEmpty() { List list = getMockList(); - List serverList = mainFilter(list, Collections.emptyMap()); + List serverList = mainFilter(list, new GovernanceRequest()); Assertions.assertEquals(2, serverList.size()); } @@ -152,9 +149,11 @@ public void testVersionNotMatch() { Map headerMap = new HashMap<>(); headerMap.put("userId", "02"); headerMap.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headerMap); List list = getMockList(); list.remove(1); - List serverList = mainFilter(list, headerMap); + List serverList = mainFilter(list, governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("01", serverList.get(0).getId()); } @@ -164,7 +163,9 @@ public void testVersionMatch() { Map headers = new HashMap<>(); headers.put("userId", "01"); headers.put("appId", "01"); - List serverList = mainFilter(getMockList(), headers); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); + List serverList = mainFilter(getMockList(), governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("02", serverList.get(0).getId()); } @@ -174,7 +175,9 @@ public void testMatchPrecedenceLower() { Map headers = new HashMap<>(); headers.put("userId", "02"); headers.put("appId", "01"); - List serverList = mainFilter(getMockList(), headers); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); + List serverList = mainFilter(getMockList(), governanceRequest); Assertions.assertEquals(1, serverList.size()); Assertions.assertEquals("01", serverList.get(0).getId()); } @@ -184,6 +187,8 @@ public void testMatchPrecedenceHigher() { Map headers = new HashMap<>(); headers.put("userId", "03"); headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -194,7 +199,7 @@ public void testMatchPrecedenceHigher() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("02", resultServerList.get(0).getId()); } @@ -210,9 +215,10 @@ private List getMockList() { return serverList; } - private List mainFilter(List serverList, Map headers) { + private List mainFilter(List serverList, + GovernanceRequestExtractor governanceRequestExtractor) { return routerFilter - .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, headers, + .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, governanceRequestExtractor, testDistributor); } } diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java index 19f0f47f35f..54ebcb05823 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileConfigTest.java @@ -16,20 +16,20 @@ */ package org.apache.servicecomb.router; +import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -@RunWith(SpringRunner.class) +@SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorFileConfigTest { private static final String TARGET_SERVICE_NAME = "test_server1"; @@ -52,8 +52,11 @@ public void setRouterDistributor(RouterDistributor routerDistributor public void testDistribute() { List list = initServiceList(); HashMap header = new HashMap<>(); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); + List listOfServers = routerFilter - .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor); + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); Assertions.assertNotNull(listOfServers); for (ServiceIns server : listOfServers) { Assertions.assertEquals(TARGET_SERVICE_NAME, server.getServerName()); @@ -63,7 +66,7 @@ public void testDistribute() { for (int i = 0; i < 10; i++) { List serverList = routerFilter - .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor); + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); for (ServiceIns serviceIns : serverList) { if ("01".equals(serviceIns.getId())) { serverNum1++; diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java index 763e2eeca09..68b04bc9cdb 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWeightLessTest.java @@ -20,16 +20,16 @@ import java.util.HashMap; import java.util.List; +import org.apache.servicecomb.governance.marker.GovernanceRequest; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) +@SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorFileWeightLessTest { private static final String TARGET_SERVICE_NAME = "test_server2"; @@ -52,8 +52,10 @@ public void setRouterDistributor(RouterDistributor routerDistributor public void testDistribute() { List list = initServiceList(); HashMap header = new HashMap<>(); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); List listOfServers = routerFilter - .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor); + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); Assertions.assertNotNull(listOfServers); for (ServiceIns server : listOfServers) { Assertions.assertEquals(TARGET_SERVICE_NAME, server.getServerName()); @@ -63,7 +65,7 @@ public void testDistribute() { for (int i = 0; i < 10; i++) { List serverList = routerFilter - .getFilteredListOfServers(list, TARGET_SERVICE_NAME, header, routerDistributor); + .getFilteredListOfServers(list, TARGET_SERVICE_NAME, governanceRequest, routerDistributor); for (ServiceIns serviceIns : serverList) { if ("01".equals(serviceIns.getId())) { serverNum1++; diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWithFallbackTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWithFallbackTest.java new file mode 100644 index 00000000000..2d7a1064557 --- /dev/null +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorFileWithFallbackTest.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.router; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.router.distribute.RouterDistributor; +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.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) +public class RouterDistributorFileWithFallbackTest { + private static final String TARGET_SERVICE_NAME_WITHOUT_FALLBACK = "test_server3"; + + private static final String TARGET_SERVICE_NAME_WITH_FALLBACK = "test_server4"; + + private static final String TARGET_SERVICE_NAME_ROUTE_FALLBACK = "test_server5"; + + private RouterFilter routerFilter; + + private RouterDistributor routerDistributor; + + @Autowired + public void setRouterFilter(RouterFilter routerFilter) { + this.routerFilter = routerFilter; + } + + @Autowired + public void setRouterDistributor(RouterDistributor routerDistributor) { + this.routerDistributor = routerDistributor; + } + + @Test + public void testDistributeWithoutFallback() { + List list = initServiceList(TARGET_SERVICE_NAME_WITHOUT_FALLBACK); + HashMap header = new HashMap<>(); + header.put("canary", "canary"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); + List listOfServers = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITHOUT_FALLBACK, governanceRequest, routerDistributor); + Assertions.assertNotNull(listOfServers); + for (ServiceIns server : listOfServers) { + Assertions.assertEquals(TARGET_SERVICE_NAME_WITHOUT_FALLBACK, server.getServerName()); + } + int serverNum1 = 0; + int serverNum2 = 0; + + for (int i = 0; i < 10; i++) { + List serverList = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITHOUT_FALLBACK, governanceRequest, routerDistributor); + for (ServiceIns serviceIns : serverList) { + if ("01".equals(serviceIns.getId())) { + serverNum1++; + } else if ("02".equals(serviceIns.getId())) { + serverNum2++; + } + } + } + Assertions.assertTrue(serverNum2 == serverNum1); + } + + @Test + public void testDistributeWithFallback() { + List list = initServiceList(TARGET_SERVICE_NAME_WITH_FALLBACK); + HashMap header = new HashMap<>(); + header.put("canary", "canary"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); + List listOfServers = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITH_FALLBACK, governanceRequest, routerDistributor); + Assertions.assertNotNull(listOfServers); + for (ServiceIns server : listOfServers) { + Assertions.assertEquals(TARGET_SERVICE_NAME_WITH_FALLBACK, server.getServerName()); + } + int serverNum1 = 0; + int serverNum2 = 0; + + for (int i = 0; i < 10; i++) { + List serverList = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_WITH_FALLBACK, governanceRequest, routerDistributor); + for (ServiceIns serviceIns : serverList) { + if ("01".equals(serviceIns.getId())) { + serverNum1++; + } else if ("02".equals(serviceIns.getId())) { + serverNum2++; + } + } + } + Assertions.assertTrue((serverNum2 + serverNum1) == serverNum1); + } + + @Test + public void testDistributeRouteAndFallbackHaveSame() { + List list = initServiceList(TARGET_SERVICE_NAME_ROUTE_FALLBACK); + HashMap header = new HashMap<>(); + header.put("canary", "canary"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(header); + List listOfServers = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_ROUTE_FALLBACK, governanceRequest, routerDistributor); + Assertions.assertNotNull(listOfServers); + for (ServiceIns server : listOfServers) { + Assertions.assertEquals(TARGET_SERVICE_NAME_ROUTE_FALLBACK, server.getServerName()); + } + int serverNum1 = 0; + int serverNum2 = 0; + + for (int i = 0; i < 20; i++) { + List serverList = routerFilter + .getFilteredListOfServers(list, TARGET_SERVICE_NAME_ROUTE_FALLBACK, governanceRequest, routerDistributor); + for (ServiceIns serviceIns : serverList) { + if ("01".equals(serviceIns.getId())) { + serverNum1++; + } else if ("02".equals(serviceIns.getId())) { + serverNum2++; + } + } + } + boolean flag = false; + if (Math.round(serverNum1 * 1.0 / serverNum2) == 3) { + flag = true; + } + Assertions.assertTrue(flag); + } + + List initServiceList(String serviceName) { + ServiceIns serviceIns1 = new ServiceIns("01", serviceName); + ServiceIns serviceIns2 = new ServiceIns("02", serviceName); + serviceIns1.setVersion("1.0"); + serviceIns2.setVersion("2.0"); + serviceIns1.addTags("x-group", "red"); + serviceIns2.addTags("x-group", "green"); + List list = new ArrayList<>(); + list.add(serviceIns1); + list.add(serviceIns2); + return list; + } +} diff --git a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java index 587e7fc7d58..89a54d76979 100644 --- a/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java +++ b/governance/src/test/java/org/apache/servicecomb/router/RouterDistributorGlobalConfigTest.java @@ -26,21 +26,22 @@ import org.apache.servicecomb.governance.event.GovernanceConfigurationChangedEvent; import org.apache.servicecomb.governance.event.GovernanceEventManager; +import org.apache.servicecomb.governance.marker.GovernanceRequest; +import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.router.cache.RouterRuleCache; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.junit.Before; -import org.junit.Test; import org.junit.jupiter.api.Assertions; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.ConfigDataApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.Environment; import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) +@SpringBootTest @ContextConfiguration(locations = "classpath:META-INF/spring/*.xml", initializers = ConfigDataApplicationContextInitializer.class) public class RouterDistributorGlobalConfigTest { private static final String TARGET_SERVICE_NAME = "test_server"; @@ -52,10 +53,9 @@ public class RouterDistributorGlobalConfigTest { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 02\n" + + " exact: \"02\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -64,10 +64,9 @@ public class RouterDistributorGlobalConfigTest { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 03\n" + + " exact: \"03\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -99,7 +98,7 @@ public RouterDistributorGlobalConfigTest() { private final Map dynamicValues = new HashMap<>(); - @Before + @BeforeEach public void setUp() { ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; @@ -129,6 +128,8 @@ public void testUseGlobalRouteRule() { Map headers = new HashMap<>(); headers.put("userId", "03"); headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -139,7 +140,7 @@ public void testUseGlobalRouteRule() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("01", resultServerList.get(0).getId()); } @@ -151,10 +152,9 @@ public void testUseProviderRouteRule() { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 03\n" + + " exact: \"03\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -163,10 +163,9 @@ public void testUseProviderRouteRule() { + " match:\n" + " headers: #header匹配\n" + " appId:\n" - + " regex: 01\n" - + " caseInsensitive: false # 是否区分大小写,默认为false,区分大小写\n" + + " exact: \"01\"\n" + " userId:\n" - + " exact: 02\n" + + " exact: \"02\"\n" + " route:\n" + " - weight: 100\n" + " tags:\n" @@ -177,6 +176,8 @@ public void testUseProviderRouteRule() { Map headers = new HashMap<>(); headers.put("userId", "03"); headers.put("appId", "01"); + GovernanceRequest governanceRequest = new GovernanceRequest(); + governanceRequest.setHeaders(headers); List serverList = new ArrayList<>(); ServiceIns ins1 = new ServiceIns("01", TARGET_SERVICE_NAME); @@ -187,14 +188,15 @@ public void testUseProviderRouteRule() { serverList.add(ins1); serverList.add(ins2); - List resultServerList = mainFilter(serverList, headers); + List resultServerList = mainFilter(serverList, governanceRequest); Assertions.assertEquals(1, resultServerList.size()); Assertions.assertEquals("02", resultServerList.get(0).getId()); } - private List mainFilter(List serverList, Map headers) { + private List mainFilter(List serverList, + GovernanceRequestExtractor governanceRequestExtractor) { return routerFilter - .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, headers, + .getFilteredListOfServers(serverList, TARGET_SERVICE_NAME, governanceRequestExtractor, testDistributor); } diff --git a/governance/src/test/resources/META-INF/spring/bean.xml b/governance/src/test/resources/META-INF/spring/bean.xml index f9382441770..5ff6c6e05cc 100644 --- a/governance/src/test/resources/META-INF/spring/bean.xml +++ b/governance/src/test/resources/META-INF/spring/bean.xml @@ -23,4 +23,5 @@ http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> + diff --git a/governance/src/test/resources/application.yaml b/governance/src/test/resources/application.yaml index 95eb39f61c8..ce981ac1ea2 100644 --- a/governance/src/test/resources/application.yaml +++ b/governance/src/test/resources/application.yaml @@ -93,12 +93,17 @@ servicecomb: matches: - apiPath: prefix: "/mapper" + demo-mapper-query: | + matches: + - queries: + name: + exact: "bob" rateLimiting: demo-rateLimiting: | rate: 1 demo-rateLimiting-servicename: | rate: 1 - wrongIngored: | + wrongIgnored: | rate: 0 identifierRateLimiting: demo-rateLimiting: | @@ -126,7 +131,7 @@ servicecomb: retry: demo-retry: | maxAttempts: 3 - wrongIngored: | + wrongIgnored: | maxAttempts: -1 circuitBreaker: demo-circuitBreaker: | @@ -134,7 +139,7 @@ servicecomb: slidingWindowSize: 2 slidingWindowType: COUNT_BASED services: myself:1.0,other - wrongIngored: | + wrongIgnored: | rules: match: demo-circuitBreaker.xx minimumNumberOfCalls: -1 @@ -153,7 +158,7 @@ servicecomb: maxConcurrentCalls: 1 maxWaitDuration: 3000 services: other:1.0 - wrongIngored: | + wrongIgnored: | rules: match: demo-bulkhead.xx maxWaitDuration: -1 @@ -186,7 +191,7 @@ servicecomb: type: abort percentage: 50 errorCode: 500 - wrongIngored: | + wrongIgnored: | delayTime: -1 type: ERROR faultInjection2: @@ -203,6 +208,9 @@ servicecomb: target: host: 127.0.0.1 port: 8080 + demo-mapper-query: | + target: + user-id: $Q{name} mapper2: demo-mapper: | target: @@ -215,10 +223,8 @@ servicecomb: headers: # header 匹配 region: # 如果配置了多个 header,那么所有的 header 规则都必须和请求匹配 exact: 'providerRegion' - caseInsensitive: false # 不区分大小写 type: regex: gray_[a-z]+ # java 正则表达式匹配 - caseInsensitive: true # 区分大小写 route: # 路由规则 - weight: 20 # 权重值 tags: @@ -241,4 +247,52 @@ servicecomb: route: - weight: 20 tags: - x-group: red \ No newline at end of file + x-group: red + + test_server3: | # 服务名 + - precedence: 2 + match: + headers: + canary: + exact: 'canary' + route: + - weight: 20 + tags: + version: 1.0.0 + - weight: 80 + tags: + version: 2.0.0 + test_server4: | # 服务名 + - precedence: 2 + match: + headers: + canary: + exact: 'canary' + route: + - weight: 20 + tags: + version: 1.0.0 + - weight: 80 + tags: + version: 2.0.0 + fallback: + - weight: 100 + tags: + x-group: red + test_server5: | # 服务名 + - precedence: 2 + match: + headers: + canary: + exact: 'canary' + route: + - weight: 50 + tags: + x-group: red + fallback: + - weight: 50 + tags: + x-group: red + - weight: 50 + tags: + x-group: green diff --git a/handlers/handler-fault-injection/pom.xml b/handlers/handler-fault-injection/pom.xml index 1896581a3a7..825c63c2270 100755 --- a/handlers/handler-fault-injection/pom.xml +++ b/handlers/handler-fault-injection/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb handlers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT handler-fault-injection Java Chassis::Handlers::Fault Injection diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerAbortFaultFilter.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerAbortFaultFilter.java index c13f1b8c2c0..e4eef91ef2f 100644 --- a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerAbortFaultFilter.java +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerAbortFaultFilter.java @@ -19,17 +19,18 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ConsumerAbortFaultFilter implements ConsumerFilter { +public class ConsumerAbortFaultFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerAbortFaultFilter.class); private static final String SUCCESS_RESPONSE = "success"; @@ -37,7 +38,7 @@ public class ConsumerAbortFaultFilter implements ConsumerFilter { public static final String ABORTED_ERROR_MSG = "aborted by fault inject"; @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1020; } diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerDelayFaultFilter.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerDelayFaultFilter.java index 248ed748cfb..e8c0f6ffd67 100644 --- a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerDelayFaultFilter.java +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/ConsumerDelayFaultFilter.java @@ -19,10 +19,11 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,11 +31,11 @@ import io.vertx.core.Context; import io.vertx.core.Vertx; -public class ConsumerDelayFaultFilter implements ConsumerFilter { +public class ConsumerDelayFaultFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerDelayFaultFilter.class); @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1030; } diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfiguration.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfiguration.java index d954ab336bf..600ed5dc9f6 100644 --- a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfiguration.java +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionConfiguration.java @@ -29,12 +29,12 @@ public class FaultInjectionConfiguration { public static final String FAULT_INJECTION_ENABLED = FAULT_INJECTION_PREFIX + ".enabled"; @Bean - public ConsumerAbortFaultFilter consumerAbortFaultFilter() { + public ConsumerAbortFaultFilter scbConsumerAbortFaultFilter() { return new ConsumerAbortFaultFilter(); } @Bean - public ConsumerDelayFaultFilter consumerDelayFaultFilter() { + public ConsumerDelayFaultFilter scbConsumerDelayFaultFilter() { return new ConsumerDelayFaultFilter(); } } diff --git a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java index 9d96fcd1d31..67c3ab86939 100755 --- a/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java +++ b/handlers/handler-fault-injection/src/main/java/org/apache/servicecomb/faultinjection/FaultInjectionUtil.java @@ -39,7 +39,7 @@ public class FaultInjectionUtil { private FaultInjectionUtil() { } - // key is config paramter + // key is config parameter private static final Map configCenterValue = new ConcurrentHashMapEx<>(); /** diff --git a/handlers/handler-flowcontrol-qps/pom.xml b/handlers/handler-flowcontrol-qps/pom.xml index 7001cb8d5bc..eb5d63a0993 100644 --- a/handlers/handler-flowcontrol-qps/pom.xml +++ b/handlers/handler-flowcontrol-qps/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb handlers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT handler-flowcontrol-qps Java Chassis::Handlers::Flow Control QPS diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerFlowControlFilter.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerFlowControlFilter.java index b5fe884d93a..72828b35ccf 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerFlowControlFilter.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ConsumerFlowControlFilter.java @@ -20,10 +20,11 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -31,7 +32,7 @@ import com.google.common.annotations.VisibleForTesting; -public class ConsumerFlowControlFilter implements ConsumerFilter { +public class ConsumerFlowControlFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private final QpsControllerManager qpsControllerMgr; public ConsumerFlowControlFilter(Environment environment) { @@ -44,7 +45,7 @@ public QpsControllerManager getQpsControllerMgr() { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER - 1990; } diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/FlowControlQpsConfiguration.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/FlowControlQpsConfiguration.java index 27bef6dcb1a..d5fd7702cd1 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/FlowControlQpsConfiguration.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/FlowControlQpsConfiguration.java @@ -23,19 +23,19 @@ @Configuration @ConditionalOnProperty(value = FlowControlQpsConfiguration.FLOW_CONTROL_ENABLED, - havingValue = "true", matchIfMissing = true) + havingValue = "true") public class FlowControlQpsConfiguration { public static final String FLOW_CONTROL_PREFIX = "servicecomb.flowcontrol"; public static final String FLOW_CONTROL_ENABLED = FLOW_CONTROL_PREFIX + ".enabled"; @Bean - public ProviderFlowControlFilter providerFlowControlFilter(Environment environment) { + public ProviderFlowControlFilter scbProviderFlowControlFilter(Environment environment) { return new ProviderFlowControlFilter(environment); } @Bean - public ConsumerFlowControlFilter consumerFlowControlFilter(Environment environment) { + public ConsumerFlowControlFilter scbConsumerFlowControlFilter(Environment environment) { return new ConsumerFlowControlFilter(environment); } } diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderFlowControlFilter.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderFlowControlFilter.java index ef9610d30ce..00f78f126f1 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderFlowControlFilter.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/ProviderFlowControlFilter.java @@ -21,10 +21,10 @@ import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -32,7 +32,7 @@ import com.google.common.annotations.VisibleForTesting; -public class ProviderFlowControlFilter implements ProviderFilter { +public class ProviderFlowControlFilter extends AbstractFilter implements ProviderFilter { private final QpsControllerManager qpsControllerMgr; public ProviderFlowControlFilter(Environment environment) { @@ -40,7 +40,7 @@ public ProviderFlowControlFilter(Environment environment) { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1990; } diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java index 0ef5b27167c..e4cf5e18457 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/QpsControllerManager.java @@ -36,7 +36,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.Subscribe; -public class QpsControllerManager { +public final class QpsControllerManager { private static final Logger LOGGER = LoggerFactory.getLogger(QpsControllerManager.class); public static final String SEPARATOR = "."; diff --git a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/FixedWindowStrategy.java b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/FixedWindowStrategy.java index 65204c3a304..c95c1ab2cba 100644 --- a/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/FixedWindowStrategy.java +++ b/handlers/handler-flowcontrol-qps/src/main/java/org/apache/servicecomb/qps/strategy/FixedWindowStrategy.java @@ -52,7 +52,7 @@ public boolean isLimitNewRequest() { msCycleBegin = msNow; } - // Configuration update and use is at the situation of multi-threaded concurrency + // Configuration update and use is at the situation of multi-thread concurrency // It is possible that operation level updated to null,but schema level or microservice level does not updated boolean isLimitRequest = newCount - lastRequestCount >= this.getQpsLimit(); if (isLimitRequest){ diff --git a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.java b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.java index 23c228fab2e..f34b6c5a261 100644 --- a/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.java +++ b/handlers/handler-flowcontrol-qps/src/test/java/org/apache/servicecomb/qps/TestQpsStrategy.java @@ -24,8 +24,8 @@ import org.junit.jupiter.api.Test; /** - * @Author GuoYl123 - * @Date 2020/7/16 + * @author GuoYl123 + * @since 2020/7/16 **/ public class TestQpsStrategy { diff --git a/handlers/handler-governance/README.md b/handlers/handler-governance/README.md index 3a9db2ff26a..b6a237f6510 100644 --- a/handlers/handler-governance/README.md +++ b/handlers/handler-governance/README.md @@ -1,3 +1,3 @@ -Enable governance features by handler. +Enable governance features by handler. -Governance can be enabled in any different process layer, enable it in handler is most portable way. \ No newline at end of file +Governance can be enabled in any different process layer, enable it in handler is most portable way. diff --git a/handlers/handler-governance/pom.xml b/handlers/handler-governance/pom.xml index b039e206156..7db533e562e 100644 --- a/handlers/handler-governance/pom.xml +++ b/handlers/handler-governance/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb handlers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT handler-governance diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceBulkheadFilter.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceBulkheadFilter.java index 8d8fd4c94c7..1b007e111ad 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceBulkheadFilter.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceBulkheadFilter.java @@ -22,13 +22,14 @@ import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.InstanceBulkheadHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -42,7 +43,7 @@ import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; import jakarta.ws.rs.core.Response.Status; -public class ConsumerInstanceBulkheadFilter implements ConsumerFilter { +public class ConsumerInstanceBulkheadFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerInstanceBulkheadFilter.class); private final InstanceBulkheadHandler instanceBulkheadHandler; @@ -53,7 +54,7 @@ public ConsumerInstanceBulkheadFilter(InstanceBulkheadHandler instanceBulkheadHa } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1060; } diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationFilter.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationFilter.java index 875e53b75d4..e60d52fab18 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationFilter.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationFilter.java @@ -23,7 +23,9 @@ import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.governance.MatchType; @@ -31,7 +33,6 @@ import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy; import org.apache.servicecomb.registry.DiscoveryManager; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -45,7 +46,7 @@ import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; import jakarta.ws.rs.core.Response.Status; -public class ConsumerInstanceIsolationFilter implements ConsumerFilter { +public class ConsumerInstanceIsolationFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerInstanceIsolationFilter.class); private final InstanceIsolationHandler instanceIsolationHandler; @@ -63,7 +64,7 @@ public void setDiscoveryManager(DiscoveryManager discoveryManager) { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1050; } diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/HandlerGovernanceConfiguration.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/HandlerGovernanceConfiguration.java index b4d8f487256..9223146a759 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/HandlerGovernanceConfiguration.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/HandlerGovernanceConfiguration.java @@ -34,29 +34,29 @@ public class HandlerGovernanceConfiguration { public static final String GOVERNANCE_ENABLED = GOVERNANCE_PREFIX + ".enabled"; @Bean - public ConsumerInstanceBulkheadFilter consumerInstanceBulkheadFilter( + public ConsumerInstanceBulkheadFilter scbConsumerInstanceBulkheadFilter( InstanceBulkheadHandler instanceBulkheadHandler) { return new ConsumerInstanceBulkheadFilter(instanceBulkheadHandler); } @Bean - public ConsumerInstanceIsolationFilter consumerInstanceIsolationFilter( + public ConsumerInstanceIsolationFilter scbConsumerInstanceIsolationFilter( InstanceIsolationHandler instanceIsolationHandler) { return new ConsumerInstanceIsolationFilter(instanceIsolationHandler); } @Bean - public ProviderBulkheadFilter providerBulkheadFilter(BulkheadHandler bulkheadHandler) { + public ProviderBulkheadFilter scbProviderBulkheadFilter(BulkheadHandler bulkheadHandler) { return new ProviderBulkheadFilter(bulkheadHandler); } @Bean - public ProviderCircuitBreakerFilter providerCircuitBreakerFilter(CircuitBreakerHandler circuitBreakerHandler) { + public ProviderCircuitBreakerFilter scbProviderCircuitBreakerFilter(CircuitBreakerHandler circuitBreakerHandler) { return new ProviderCircuitBreakerFilter(circuitBreakerHandler); } @Bean - public ProviderRateLimitingFilter providerRateLimitingFilter(RateLimitingHandler rateLimitingHandler) { + public ProviderRateLimitingFilter scbProviderRateLimitingFilter(RateLimitingHandler rateLimitingHandler) { return new ProviderRateLimitingFilter(rateLimitingHandler); } } diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderBulkheadFilter.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderBulkheadFilter.java index c526916b0aa..a68ebf08fb1 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderBulkheadFilter.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderBulkheadFilter.java @@ -22,13 +22,13 @@ import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.BulkheadHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -41,7 +41,7 @@ import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; -public class ProviderBulkheadFilter implements ProviderFilter { +public class ProviderBulkheadFilter extends AbstractFilter implements ProviderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ProviderBulkheadFilter.class); private final BulkheadHandler bulkheadHandler; @@ -52,7 +52,7 @@ public ProviderBulkheadFilter(BulkheadHandler bulkheadHandler) { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1880; } diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderCircuitBreakerFilter.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderCircuitBreakerFilter.java index 0c5e2ed33de..dbc0b8978c6 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderCircuitBreakerFilter.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderCircuitBreakerFilter.java @@ -22,13 +22,13 @@ import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.CircuitBreakerHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -41,7 +41,7 @@ import io.github.resilience4j.decorators.Decorators; import io.github.resilience4j.decorators.Decorators.DecorateCompletionStage; -public class ProviderCircuitBreakerFilter implements ProviderFilter { +public class ProviderCircuitBreakerFilter extends AbstractFilter implements ProviderFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ProviderCircuitBreakerFilter.class); private final CircuitBreakerHandler circuitBreakerHandler; @@ -52,7 +52,7 @@ public ProviderCircuitBreakerFilter(CircuitBreakerHandler circuitBreakerHandler) } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1890; } diff --git a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderRateLimitingFilter.java b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderRateLimitingFilter.java index 63db4cd7d93..732fcf48ab5 100644 --- a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderRateLimitingFilter.java +++ b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ProviderRateLimitingFilter.java @@ -22,13 +22,14 @@ import java.util.function.Supplier; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.governance.handler.RateLimitingHandler; import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -41,7 +42,7 @@ import io.github.resilience4j.ratelimiter.RateLimiter; import io.github.resilience4j.ratelimiter.RequestNotPermitted; -public class ProviderRateLimitingFilter implements ProviderFilter { +public class ProviderRateLimitingFilter extends AbstractFilter implements ProviderFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(ProviderRateLimitingFilter.class); private final RateLimitingHandler rateLimitingHandler; @@ -52,13 +53,13 @@ public ProviderRateLimitingFilter(RateLimitingHandler rateLimitingHandler) { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 1900; } @Override public String getName() { - return "provider-rate-limiting"; + return "rate-limiting"; } @Override diff --git a/handlers/handler-loadbalance/pom.xml b/handlers/handler-loadbalance/pom.xml index 157ab3faafa..0fd42dce838 100644 --- a/handlers/handler-loadbalance/pom.xml +++ b/handlers/handler-loadbalance/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb handlers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT handler-loadbalance diff --git a/handlers/handler-loadbalance/readme.MD b/handlers/handler-loadbalance/readme.MD index 335e494ba88..53b4ef30d91 100644 --- a/handlers/handler-loadbalance/readme.MD +++ b/handlers/handler-loadbalance/readme.MD @@ -1,6 +1,6 @@ 参考: https://github.com/Netflix/ribbon/wiki/Working-with-load-balancers LoadBalancer -Rule -Ping +Rule +Ping ServerList ServerListFilter diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/Configuration.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/Configuration.java index 61f7a2eee98..87a527c9aff 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/Configuration.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/Configuration.java @@ -20,65 +20,72 @@ import java.util.Map; import org.apache.servicecomb.config.ConfigUtil; +import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; /** * configuration items - * */ public final class Configuration { //// 2.1 configuration items public static final String ROOT = "servicecomb.loadbalance."; - public static final String SERVER_EXPIRED_IN_SECONDS = "servicecomb.loadbalance.stats.serverExpiredInSeconds"; - - public static final String TIMER_INTERVAL_IN_MILLIS = "servicecomb.loadbalance.stats.timerIntervalInMillis"; + public static final String RULE_STRATEGY_GLOBAL = "servicecomb.loadbalance.strategy.name"; public static final String RULE_STRATEGY_NAME = "strategy.name"; // 2.0 configuration items public static final String ROOT_20 = "ribbon."; - // retry configurations - public static final String RETRY_HANDLER = "retryHandler"; - - // SessionStickinessRule configruation + // SessionStickinessRule configuration public static final String SESSION_TIMEOUT_IN_SECONDS = "SessionStickinessRule.sessionTimeoutInSeconds"; public static final String SUCCESSIVE_FAILED_TIMES = "SessionStickinessRule.successiveFailedTimes"; - private static final double PERCENT = 100; - public static final String FILTER_ISOLATION = "isolation."; - public static final String FILTER_OPEN = "enabled"; - - public static final String FILTER_ERROR_PERCENTAGE = "errorThresholdPercentage"; - - public static final String FILTER_ENABLE_REQUEST = "enableRequestThreshold"; - - public static final String FILTER_RECOVER_IMMEDIATELY_WHEN_SUCCESS = "recoverImmediatelyWhenSuccess"; - - public static final String FILTER_SINGLE_TEST = "singleTestTime"; - public static final String FILTER_MAX_SINGLE_TEST_WINDOW = "maxSingleTestWindow"; - public static final String FILTER_MIN_ISOLATION_TIME = "minIsolationTime"; - - public static final String FILTER_CONTINUOUS_FAILURE_THRESHOLD = "continuousFailureThreshold"; - public static final String TRANSACTIONCONTROL_OPTIONS_PREFIX_PATTERN = "servicecomb.loadbalance.%s.transactionControl.options"; public static final Configuration INSTANCE = new Configuration(); + public record RuleType(int type, String value) { + public static final int TYPE_SCHEMA = 1; + + public static final int TYPE_OPERATION = 2; + + public String getValue() { + return value; + } + + public int getType() { + return type; + } + } + private Configuration() { } - public String getRuleStrategyName(String microservice) { - return getStringProperty(null, - ROOT + microservice + "." + RULE_STRATEGY_NAME, - ROOT + RULE_STRATEGY_NAME); + public RuleType getRuleStrategyName(Invocation invocation) { + String value = getStringProperty(null, ROOT + invocation.getMicroserviceName() + "." + + invocation.getSchemaId() + "." + invocation.getOperationName() + "." + RULE_STRATEGY_NAME); + if (value != null) { + return new RuleType(RuleType.TYPE_OPERATION, value); + } + value = getStringProperty(null, ROOT + invocation.getMicroserviceName() + "." + + invocation.getSchemaId() + "." + RULE_STRATEGY_NAME); + if (value != null) { + return new RuleType(RuleType.TYPE_SCHEMA, value); + } + value = getStringProperty(null, ROOT + invocation.getMicroserviceName() + "." + + RULE_STRATEGY_NAME); + if (value != null) { + return new RuleType(RuleType.TYPE_SCHEMA, value); + } + return new RuleType(RuleType.TYPE_SCHEMA, + getStringProperty("RoundRobin", RULE_STRATEGY_GLOBAL)); } public int getSessionTimeoutInSeconds(String microservice) { @@ -105,49 +112,6 @@ public int getSuccessiveFailedTimes(String microservice) { } } - public boolean isIsolationFilterOpen(String microservice) { - String p = getStringProperty("true", - ROOT + microservice + "." + FILTER_ISOLATION + FILTER_OPEN, - ROOT + FILTER_ISOLATION + FILTER_OPEN); - return Boolean.parseBoolean(p); - } - - public int getErrorThresholdPercentage(String microservice) { - final int defaultValue = 0; - String p = getStringProperty("0", - ROOT + microservice + "." + FILTER_ISOLATION + FILTER_ERROR_PERCENTAGE, - ROOT + FILTER_ISOLATION + FILTER_ERROR_PERCENTAGE); - try { - int result = Integer.parseInt(p); - if (result <= PERCENT && result > 0) { - return result; - } - return defaultValue; - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public int getEnableRequestThreshold(String microservice) { - return getThreshold(microservice, FILTER_ENABLE_REQUEST); - } - - public int getSingleTestTime(String microservice) { - final int defaultValue = 60000; - String p = getStringProperty("60000", - ROOT + microservice + "." + FILTER_ISOLATION + FILTER_SINGLE_TEST, - ROOT + FILTER_ISOLATION + FILTER_SINGLE_TEST); - try { - int result = Integer.parseInt(p); - if (result >= 0) { - return result; - } - return defaultValue; - } catch (NumberFormatException e) { - return defaultValue; - } - } - public int getMaxSingleTestWindow() { final int defaultValue = 60000; String p = getStringProperty(Integer.toString(defaultValue), @@ -163,29 +127,6 @@ public int getMaxSingleTestWindow() { } } - public int getMinIsolationTime(String microservice) { - final int defaultValue = 3000; // 3 seconds - String p = getStringProperty("3000", - ROOT + microservice + "." + FILTER_ISOLATION + FILTER_MIN_ISOLATION_TIME, - ROOT + FILTER_ISOLATION + FILTER_MIN_ISOLATION_TIME); - try { - int result = Integer.parseInt(p); - if (result >= 0) { - return result; - } - return defaultValue; - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public boolean isRecoverImmediatelyWhenSuccess(String microservice) { - String p = getStringProperty("true", - ROOT + microservice + "." + FILTER_ISOLATION + FILTER_RECOVER_IMMEDIATELY_WHEN_SUCCESS, - ROOT + FILTER_ISOLATION + FILTER_RECOVER_IMMEDIATELY_WHEN_SUCCESS); - return Boolean.parseBoolean(p); - } - public Map getFlowsplitFilterOptions(String microservice) { String keyPrefix = String.format(TRANSACTIONCONTROL_OPTIONS_PREFIX_PATTERN, microservice); return ConfigUtil.stringPropertiesWithPrefix(LegacyPropertyFactory.getEnvironment(), keyPrefix); @@ -201,20 +142,4 @@ public static String getStringProperty(String defaultValue, String... keys) { } return defaultValue; } - - public int getContinuousFailureThreshold(String microservice) { - return getThreshold(microservice, FILTER_CONTINUOUS_FAILURE_THRESHOLD); - } - - private int getThreshold(String microservice, String threshold) { - final int defaultValue = 5; - String p = getStringProperty("5", - ROOT + microservice + "." + FILTER_ISOLATION + threshold, - ROOT + FILTER_ISOLATION + threshold); - try { - return Integer.parseInt(p); - } catch (NumberFormatException e) { - return defaultValue; - } - } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsFactory.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsFactory.java index 342bb8cd6ad..dc4cd6b6082 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsFactory.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsFactory.java @@ -25,8 +25,4 @@ public interface ExtensionsFactory { default RuleExt createLoadBalancerRule(String ruleName) { return null; } - - default ServerListFilterExt createServerListFilter(String key, String value, Object... args) { - return null; - } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsManager.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsManager.java index 0d0245e2d22..2187f59e789 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsManager.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ExtensionsManager.java @@ -18,26 +18,19 @@ import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - public class ExtensionsManager { - private static final Logger LOGGER = LoggerFactory.getLogger(ExtensionsManager.class); - private final List extensionsFactories; public ExtensionsManager(List extensionsFactories) { this.extensionsFactories = extensionsFactories; } - public RuleExt createLoadBalancerRule(String microservice) { + public RuleExt createLoadBalancerRule(String ruleStrategyName) { RuleExt rule = null; for (ExtensionsFactory factory : extensionsFactories) { - if (factory.isSupport(Configuration.RULE_STRATEGY_NAME, - Configuration.INSTANCE.getRuleStrategyName(microservice))) { - rule = factory.createLoadBalancerRule( - Configuration.INSTANCE.getRuleStrategyName(microservice)); + if (factory.isSupport(Configuration.RULE_STRATEGY_NAME, ruleStrategyName)) { + rule = factory.createLoadBalancerRule(ruleStrategyName); break; } } @@ -46,7 +39,6 @@ public RuleExt createLoadBalancerRule(String microservice) { rule = new RoundRobinRuleExt(); } - LOGGER.info("Using load balance rule {} for microservice {}.", rule.getClass().getName(), microservice); return rule; } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceConfiguration.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceConfiguration.java index d0b2f087b5d..d65726e0ada 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceConfiguration.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceConfiguration.java @@ -19,7 +19,6 @@ import java.util.List; import org.apache.servicecomb.core.SCBEngine; -import org.apache.servicecomb.core.filter.ConsumerFilter; import org.apache.servicecomb.loadbalance.filter.InstancePropertyDiscoveryFilter; import org.apache.servicecomb.loadbalance.filter.PriorityInstancePropertyDiscoveryFilter; import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter; @@ -38,38 +37,38 @@ public class LoadBalanceConfiguration { public static final String LOAD_BALANCE_ENABLED = LOAD_BALANCE_PREFIX + ".enabled"; @Bean - public ConsumerFilter loadBalanceFilter(ExtensionsManager extensionsManager, DiscoveryTree discoveryTree, + public LoadBalanceFilter scbLoadBalanceFilter(ExtensionsManager extensionsManager, DiscoveryTree discoveryTree, SCBEngine scbEngine) { return new LoadBalanceFilter(extensionsManager, discoveryTree, scbEngine); } @Bean - public RuleNameExtentionsFactory ruleNameExtentionsFactory() { - return new RuleNameExtentionsFactory(); + public RuleNameExtensionsFactory scbRuleNameExtensionsFactory() { + return new RuleNameExtensionsFactory(); } @Bean - public ExtensionsManager extensionsManager(List extensionsFactories) { + public ExtensionsManager scbExtensionsManager(List extensionsFactories) { return new ExtensionsManager(extensionsFactories); } @Bean - public PriorityInstancePropertyDiscoveryFilter priorityInstancePropertyDiscoveryFilter() { + public PriorityInstancePropertyDiscoveryFilter scbPriorityInstancePropertyDiscoveryFilter() { return new PriorityInstancePropertyDiscoveryFilter(); } @Bean - public InstancePropertyDiscoveryFilter instancePropertyDiscoveryFilter() { + public InstancePropertyDiscoveryFilter scbInstancePropertyDiscoveryFilter() { return new InstancePropertyDiscoveryFilter(); } @Bean - public ServerDiscoveryFilter serverDiscoveryFilter() { + public ServerDiscoveryFilter scbServerDiscoveryFilter() { return new ServerDiscoveryFilter(); } @Bean - public ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter() { + public ZoneAwareDiscoveryFilter scbZoneAwareDiscoveryFilter() { return new ZoneAwareDiscoveryFilter(); } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceFilter.java index e7d32844cc3..e97263ac465 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceFilter.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalanceFilter.java @@ -18,24 +18,28 @@ import java.net.URI; import java.util.Map; -import java.util.Objects; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.ConfigurationChangedEvent; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.governance.RetryContext; import org.apache.servicecomb.foundation.common.cache.VersionedCache; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.loadbalance.Configuration.RuleType; import org.apache.servicecomb.registry.discovery.DiscoveryContext; import org.apache.servicecomb.registry.discovery.DiscoveryTree; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; @@ -44,11 +48,12 @@ import org.springframework.beans.factory.annotation.Autowired; import com.google.common.annotations.VisibleForTesting; +import com.google.common.eventbus.Subscribe; import io.github.resilience4j.core.metrics.Metrics.Outcome; import jakarta.ws.rs.core.Response.Status; -public class LoadBalanceFilter implements ConsumerFilter { +public class LoadBalanceFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { public static final String CONTEXT_KEY_LAST_SERVER = "x-context-last-server"; // Enough times to make sure to choose a different server in high volume. @@ -69,8 +74,6 @@ public class LoadBalanceFilter implements ConsumerFilter { private final ExtensionsManager extensionsManager; - private String strategy = null; - private final SCBEngine scbEngine; // set endpoint in invocation.localContext @@ -87,6 +90,22 @@ public LoadBalanceFilter(ExtensionsManager extensionsManager, boolean.class, false); this.extensionsManager = extensionsManager; this.discoveryTree = discoveryTree; + EventManager.register(this); + } + + @Subscribe + @SuppressWarnings("unused") + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + Set changedKeys = event.getChanged(); + for (String key : changedKeys) { + if (key.startsWith(Configuration.ROOT)) { + synchronized (lock) { + clearLoadBalancer(); + } + LOGGER.info("clear load balance rule for configuration changed, {}", key); + break; + } + } } private void preCheck(SCBEngine scbEngine) { @@ -109,7 +128,7 @@ private void preCheck(SCBEngine scbEngine) { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER; } @@ -131,23 +150,17 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne invocation.addLocalContext(RetryContext.RETRY_LOAD_BALANCE, true); - String strategy = Configuration.INSTANCE.getRuleStrategyName(invocation.getMicroserviceName()); - if (!Objects.equals(strategy, this.strategy)) { - //配置变化,需要重新生成所有的lb实例 - synchronized (lock) { - clearLoadBalancer(); - } - } - this.strategy = strategy; - LoadBalancer loadBalancer = getOrCreateLoadBalancer(invocation); return send(invocation, nextNode, loadBalancer); } // user's can invoke a service by supplying target Endpoint. - // in this case, we do not using load balancer, and no stats of server calculated, no retrying. + // in this case, do not use load balancer, and no stats of server calculated, no retrying. private boolean handleSuppliedEndpoint(Invocation invocation) throws Exception { + if (invocation.isEdge()) { + return false; + } if (invocation.getEndpoint() != null) { return true; } @@ -235,7 +248,7 @@ private ServiceCombServer chooseServer(Invocation invocation, LoadBalancer chose } } - LOGGER.info("operation failed {}, retry to instance [{}], last instance [{}], trace id {}", + LOGGER.warn("operation failed {}, retry to instance [{}], last instance [{}], trace id {}", invocation.getMicroserviceQualifiedName(), nextServer == null ? "" : nextServer.getEndpoint().getEndpoint(), lastServer == null ? "" : nextServer.getEndpoint().getEndpoint(), @@ -267,12 +280,22 @@ protected LoadBalancer getOrCreateLoadBalancer(Invocation invocation) { invocation.getMicroserviceName()); invocation.addLocalContext(CONTEXT_KEY_SERVER_LIST, serversVersionedCache.data()); - return loadBalancerMap - .computeIfAbsent(serversVersionedCache.name(), name -> createLoadBalancer(invocation.getMicroserviceName())); + RuleType ruleType = Configuration.INSTANCE.getRuleStrategyName(invocation); + String cacheKey; + if (ruleType.getType() == RuleType.TYPE_SCHEMA) { + cacheKey = invocation.getAppId() + "-" + + invocation.getMicroserviceName() + "-" + invocation.getSchemaId(); + } else { + cacheKey = invocation.getAppId() + "-" + + invocation.getMicroserviceName() + "-" + invocation.getSchemaId() + "-" + invocation.getOperationName(); + } + return loadBalancerMap.computeIfAbsent(cacheKey, + key -> createLoadBalancer(ruleType, key, invocation.getMicroserviceName())); } - private LoadBalancer createLoadBalancer(String microserviceName) { - RuleExt rule = extensionsManager.createLoadBalancerRule(microserviceName); + private LoadBalancer createLoadBalancer(RuleType ruleType, String cacheKey, String microserviceName) { + RuleExt rule = extensionsManager.createLoadBalancerRule(ruleType.getValue()); + LOGGER.info("Using load balance rule {} for microservice {}.", rule.getClass().getName(), cacheKey); return new LoadBalancer(rule, microserviceName); } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RandomRuleExt.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RandomRuleExt.java index 5f8d26c0ff8..318b45f3839 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RandomRuleExt.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RandomRuleExt.java @@ -31,7 +31,7 @@ public ServiceCombServer choose(List servers, Invocation invo if (servers.isEmpty()) { return null; } - int index = Math.abs(ThreadLocalRandom.current().nextInt()) % servers.size(); + int index = ThreadLocalRandom.current().nextInt(servers.size()); return servers.get(index); } } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtentionsFactory.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtensionsFactory.java similarity index 97% rename from handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtentionsFactory.java rename to handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtensionsFactory.java index 9315928eae4..9d9b0e0da1b 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtentionsFactory.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/RuleNameExtensionsFactory.java @@ -20,7 +20,7 @@ import com.google.common.collect.Lists; -public class RuleNameExtentionsFactory implements ExtensionsFactory { +public class RuleNameExtensionsFactory implements ExtensionsFactory { private static final Collection ACCEPT_KEYS = Lists.newArrayList( Configuration.RULE_STRATEGY_NAME); diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/SessionStickinessRule.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/SessionStickinessRule.java index c3c250b9e48..37d94bc0fa1 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/SessionStickinessRule.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/SessionStickinessRule.java @@ -40,7 +40,7 @@ public class SessionStickinessRule implements RuleExt { private volatile ServiceCombServer lastServer = null; - private long lastAccessedTime = 0; + private volatile long lastAccessedTime = 0; private volatile boolean errorThresholdMet = false; diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/InstancePropertyDiscoveryFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/InstancePropertyDiscoveryFilter.java index d3811d3a725..2b4c3eb6b20 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/InstancePropertyDiscoveryFilter.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/InstancePropertyDiscoveryFilter.java @@ -30,11 +30,14 @@ import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; /** - * Instance property based filter + * Instance property based filter */ public class InstancePropertyDiscoveryFilter extends AbstractDiscoveryFilter { + private static final String MATCHED = "matched"; + public static final String SERVICECOMB_LOADBALANCE_FILTER_INSTANCE_PROPERTY_ENABLED = "servicecomb.loadbalance.filter.instanceProperty.enabled"; + @Override public int getOrder() { return 400; @@ -42,8 +45,13 @@ public int getOrder() { @Override public boolean enabled() { - return environment.getProperty("servicecomb.loadbalance.filter.instanceProperty.enabled", - boolean.class, true); + + if (enabled == null) { + enabled = dynamicProperties.getBooleanProperty(SERVICECOMB_LOADBALANCE_FILTER_INSTANCE_PROPERTY_ENABLED, + value -> enabled = value, + true); + } + return enabled; } @Override diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilter.java index 7dbe070237d..d890b7ea229 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilter.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilter.java @@ -47,6 +47,8 @@ public class PriorityInstancePropertyDiscoveryFilter extends AbstractDiscoveryFi private static final String ALL_INSTANCE = "allInstance"; + public static final String SERVICECOMB_LOADBALANCE_FILTER_PRIORITY_INSTANCE_PROPERTY_KEY = "servicecomb.loadbalance.filter.priorityInstanceProperty.key"; + private String propertyKey; private Environment environment; @@ -58,8 +60,10 @@ public void setEnvironment(Environment environment) { @Override protected void init(DiscoveryContext context, DiscoveryTreeNode parent) { - propertyKey = environment.getProperty("servicecomb.loadbalance.filter.priorityInstanceProperty.key", - String.class, "environment"); + + propertyKey = dynamicProperties.getStringProperty(SERVICECOMB_LOADBALANCE_FILTER_PRIORITY_INSTANCE_PROPERTY_KEY, + value -> propertyKey = value, + "environment"); // group all instance by property List instances = parent.data(); @@ -115,8 +119,13 @@ protected String findChildName(DiscoveryContext context, DiscoveryTreeNode paren @Override public boolean enabled() { - return environment.getProperty("servicecomb.loadbalance.filter.priorityInstanceProperty.enabled", - boolean.class, false); + + if (enabled == null) { + enabled = dynamicProperties.getBooleanProperty("servicecomb.loadbalance.filter.priorityInstanceProperty.enabled", + value -> enabled = value, + false); + } + return enabled; } @Override @@ -136,7 +145,7 @@ static class PriorityInstanceProperty { /** * Constructor * - * @param key property key + * @param key property key * @param value property value */ public PriorityInstanceProperty(@NotNull String key, String value) { @@ -153,7 +162,7 @@ public PriorityInstanceProperty(@NotNull String key, String value) { /** * Constructor * - * @param key property key + * @param key property key * @param microserviceInstance instance */ public PriorityInstanceProperty(@NotNull String key, @NotNull StatefulDiscoveryInstance microserviceInstance) { diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ZoneAwareDiscoveryFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ZoneAwareDiscoveryFilter.java index 62eee1b3bd4..0e521397f13 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ZoneAwareDiscoveryFilter.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filter/ZoneAwareDiscoveryFilter.java @@ -34,8 +34,19 @@ public class ZoneAwareDiscoveryFilter extends AbstractGroupDiscoveryFilter { public static final String GROUP_SIZE = "zone_aware_group_size"; + public static final String CONFIG_ENABLED = "servicecomb.loadbalance.filter.zoneaware.enabled"; + + public static final String CONFIG_RATIO = "servicecomb.loadbalance.filter.zoneaware.ratio"; + + public static final String CONFIG_RATIO_CEILING = "servicecomb.loadbalance.filter.zoneaware.ratioCeiling"; + private DataCenterProperties dataCenterProperties; + private Integer ratio; + + private Integer ratioCeiling; + + @Autowired @SuppressWarnings("unused") public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { @@ -49,8 +60,33 @@ public int getOrder() { @Override public boolean enabled() { - return environment.getProperty("servicecomb.loadbalance.filter.zoneaware.enabled", - Boolean.class, true); + + if (enabled == null) { + enabled = dynamicProperties.getBooleanProperty(CONFIG_ENABLED, + value -> enabled = value, + true); + } + return enabled; + } + + private int getRatio() { + + if (ratio == null) { + ratio = dynamicProperties.getIntProperty(CONFIG_RATIO, + value -> ratio = value, + 30); + } + return ratio; + } + + private int getRatioCeiling(int defaultValue) { + + if (ratioCeiling == null) { + ratioCeiling = dynamicProperties.getIntProperty(CONFIG_RATIO_CEILING, + value -> ratioCeiling = value, + defaultValue); + } + return ratioCeiling; } @Override @@ -87,16 +123,23 @@ public void init(DiscoveryContext context, DiscoveryTreeNode parent) { } } - if (!instancesRegionAndAZMatch.isEmpty()) { + int ratio = getRatio(); + int ratioCeiling = getRatioCeiling(100 - ratio); + + if (hasEnoughMembers(instances.size(), instancesRegionAndAZMatch.size(), ratio, ratioCeiling)) { parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() .subName(parent, GROUP_PREFIX + groups).data(instancesRegionAndAZMatch)); groups++; + } else { + instancesAZMatch.addAll(instancesRegionAndAZMatch); } - if (!instancesAZMatch.isEmpty()) { + if (hasEnoughMembers(instances.size(), instancesAZMatch.size(), ratio, ratioCeiling)) { parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() .subName(parent, GROUP_PREFIX + groups).data(instancesAZMatch)); groups++; + } else { + instancesNoMatch.addAll(instancesAZMatch); } parent.child(GROUP_PREFIX + groups, new DiscoveryTreeNode() @@ -105,6 +148,14 @@ public void init(DiscoveryContext context, DiscoveryTreeNode parent) { parent.attribute(GROUP_SIZE, groups); } + private boolean hasEnoughMembers(int totalSize, int groupSize, int ratio, int ratioCeiling) { + if (totalSize == 0 || groupSize == 0) { + return false; + } + int actual = Math.floorDiv(groupSize * 100, totalSize); + return actual >= ratio && actual <= ratioCeiling; + } + private boolean regionAndAZMatch(StatefulDiscoveryInstance target) { if (dataCenterProperties.getRegion() != null && dataCenterProperties.getAvailableZone() != null && target.getDataCenterInfo() != null) { diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestExtensionsManager.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestExtensionsManager.java index 7ad8057a631..2b159d38339 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestExtensionsManager.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestExtensionsManager.java @@ -41,22 +41,15 @@ public void tearDown() { @Test public void testRuleName() { - Mockito.when(environment.getProperty("servicecomb.loadbalance.mytest1.strategy.name")) - .thenReturn("RoundRobin"); - Mockito.when(environment.getProperty("servicecomb.loadbalance.mytest2.strategy.name")) - .thenReturn("Random"); - Mockito.when(environment.getProperty("servicecomb.loadbalance.mytest3.strategy.name")) - .thenReturn("WeightedResponse"); - List extensionsFactories = new ArrayList<>(); - extensionsFactories.add(new RuleNameExtentionsFactory()); + extensionsFactories.add(new RuleNameExtensionsFactory()); ExtensionsManager extensionsManager = new ExtensionsManager(extensionsFactories); Assertions.assertEquals(RoundRobinRuleExt.class.getName(), - extensionsManager.createLoadBalancerRule("mytest1").getClass().getName()); + extensionsManager.createLoadBalancerRule("RoundRobin").getClass().getName()); Assertions.assertEquals(RandomRuleExt.class.getName(), - extensionsManager.createLoadBalancerRule("mytest2").getClass().getName()); + extensionsManager.createLoadBalancerRule("Random").getClass().getName()); Assertions.assertEquals(WeightedResponseTimeRuleExt.class.getName(), - extensionsManager.createLoadBalancerRule("mytest3").getClass().getName()); + extensionsManager.createLoadBalancerRule("WeightedResponse").getClass().getName()); } } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter.java index dc61706f568..123f23ac514 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter.java @@ -114,7 +114,7 @@ Transport findTransport(String transportName) { }; List extensionsFactories = new ArrayList<>(); - extensionsFactories.add(new RuleNameExtentionsFactory()); + extensionsFactories.add(new RuleNameExtensionsFactory()); ExtensionsManager extensionsManager = new ExtensionsManager(extensionsFactories); DiscoveryTree discoveryTree = new DiscoveryTree( diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter2.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter2.java index 5e4c47e9c19..4c2fb5f5b45 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter2.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceFilter2.java @@ -25,6 +25,7 @@ import java.util.List; import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.NonSwaggerInvocation; import org.apache.servicecomb.core.SCBEngine; @@ -35,6 +36,7 @@ import org.apache.servicecomb.core.definition.SchemaMeta; import org.apache.servicecomb.core.provider.consumer.ReferenceConfig; import org.apache.servicecomb.core.transport.TransportManager; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter; import org.apache.servicecomb.loadbalance.filter.ZoneAwareDiscoveryFilter; import org.apache.servicecomb.registry.DiscoveryManager; @@ -50,13 +52,34 @@ import org.mockito.Mockito; import org.springframework.core.env.Environment; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; + public class TestLoadBalanceFilter2 { + DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); + Environment environment = Mockito.mock(Environment.class); @BeforeEach public void setUp() { + Mockito.when(environment.getProperty("servicecomb.loadbalance.userDefinedEndpoint.enabled", boolean.class, false)).thenReturn(false); + + Mockito.when(dynamicProperties.getBooleanProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.enabled"), + Mockito.any(), + Mockito.eq(true))) + .thenReturn(true); + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratio"), + Mockito.any(), + Mockito.eq(30))) + .thenReturn(0); + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratioCeiling"), + Mockito.any(), + Mockito.eq(100))) + .thenReturn(100); + + LegacyPropertyFactory.setEnvironment(environment); } @Test @@ -66,6 +89,12 @@ public void testZoneAwareFilterWorks() { InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); + Operation operation = Mockito.mock(Operation.class); + when(operationMeta.getSwaggerOperation()).thenReturn(operation); + when(operation.getExtensions()).thenReturn(null); + OpenAPI openAPI = Mockito.mock(OpenAPI.class); + when(schemaMeta.getSwagger()).thenReturn(openAPI); + when(openAPI.getExtensions()).thenReturn(null); MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); when(schemaMeta.getMicroserviceName()).thenReturn("testMicroserviceName"); @@ -133,12 +162,10 @@ public void testZoneAwareFilterWorks() { .thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); - zoneAwareDiscoveryFilter.setEnvironment(environment); + zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); zoneAwareDiscoveryFilter.setDataCenterProperties(myself); ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); serverDiscoveryFilter.setScbEngine(scbEngine); - Mockito.when(environment.getProperty("servicecomb.loadbalance.filter.zoneaware.enabled", - Boolean.class, true)).thenReturn(true); discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, serverDiscoveryFilter)); handler = new LoadBalanceFilter(new ExtensionsManager(new ArrayList<>()), @@ -176,6 +203,12 @@ public void testIsolationEventWithEndpoint() { InvocationRuntimeType invocationRuntimeType = Mockito.mock(InvocationRuntimeType.class); SchemaMeta schemaMeta = Mockito.mock(SchemaMeta.class); when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); + Operation operation = Mockito.mock(Operation.class); + when(operationMeta.getSwaggerOperation()).thenReturn(operation); + when(operation.getExtensions()).thenReturn(null); + OpenAPI openAPI = Mockito.mock(OpenAPI.class); + when(schemaMeta.getSwagger()).thenReturn(openAPI); + when(openAPI.getExtensions()).thenReturn(null); MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); when(schemaMeta.getMicroserviceName()).thenReturn("testMicroserviceName"); @@ -220,10 +253,8 @@ public void testIsolationEventWithEndpoint() { .thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); - zoneAwareDiscoveryFilter.setEnvironment(environment); + zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); zoneAwareDiscoveryFilter.setDataCenterProperties(myself); - Mockito.when(environment.getProperty("servicecomb.loadbalance.filter.zoneaware.enabled", - Boolean.class, true)).thenReturn(true); ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); serverDiscoveryFilter.setScbEngine(scbEngine); discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, @@ -251,6 +282,12 @@ public void testZoneAwareFilterWorksEmptyInstanceProtectionEnabled() { MicroserviceMeta microserviceMeta = Mockito.mock(MicroserviceMeta.class); when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); when(schemaMeta.getMicroserviceName()).thenReturn("testMicroserviceName"); + Operation operation = Mockito.mock(Operation.class); + when(operationMeta.getSwaggerOperation()).thenReturn(operation); + when(operation.getExtensions()).thenReturn(null); + OpenAPI openAPI = Mockito.mock(OpenAPI.class); + when(schemaMeta.getSwagger()).thenReturn(openAPI); + when(openAPI.getExtensions()).thenReturn(null); when(microserviceMeta.getAppId()).thenReturn("testApp"); when(referenceConfig.getTransport()).thenReturn("rest"); Invocation invocation = new Invocation(referenceConfig, operationMeta, invocationRuntimeType, new HashMap<>()); @@ -316,10 +353,8 @@ public void testZoneAwareFilterWorksEmptyInstanceProtectionEnabled() { .thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); - zoneAwareDiscoveryFilter.setEnvironment(environment); + zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); zoneAwareDiscoveryFilter.setDataCenterProperties(myself); - Mockito.when(environment.getProperty("servicecomb.loadbalance.filter.zoneaware.enabled", - Boolean.class, true)).thenReturn(true); ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); serverDiscoveryFilter.setScbEngine(scbEngine); discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, @@ -416,11 +451,8 @@ public void testZoneAwareFilterUsingMockedInvocationWorks() { .thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); - Environment environment = Mockito.mock(Environment.class); - zoneAwareDiscoveryFilter.setEnvironment(environment); + zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); zoneAwareDiscoveryFilter.setDataCenterProperties(myself); - Mockito.when(environment.getProperty("servicecomb.loadbalance.filter.zoneaware.enabled", - Boolean.class, true)).thenReturn(true); ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); serverDiscoveryFilter.setScbEngine(scbEngine); discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, @@ -516,11 +548,8 @@ public void testStatusFilterUsingMockedInvocationWorks() { .thenReturn(parent); DiscoveryTree discoveryTree = new DiscoveryTree(discoveryManager); ZoneAwareDiscoveryFilter zoneAwareDiscoveryFilter = new ZoneAwareDiscoveryFilter(); - Environment environment = Mockito.mock(Environment.class); - zoneAwareDiscoveryFilter.setEnvironment(environment); + zoneAwareDiscoveryFilter.setDynamicProperties(dynamicProperties); zoneAwareDiscoveryFilter.setDataCenterProperties(myself); - Mockito.when(environment.getProperty("servicecomb.loadbalance.filter.zoneaware.enabled", - Boolean.class, true)).thenReturn(true); ServerDiscoveryFilter serverDiscoveryFilter = new ServerDiscoveryFilter(); serverDiscoveryFilter.setScbEngine(scbEngine); discoveryTree.setDiscoveryFilters(Arrays.asList(zoneAwareDiscoveryFilter, diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionSticknessRule.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionStickinessRule.java similarity index 99% rename from handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionSticknessRule.java rename to handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionStickinessRule.java index 0b8c904503b..9a60b18657c 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionSticknessRule.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestSessionStickinessRule.java @@ -37,7 +37,7 @@ import mockit.Mock; import mockit.MockUp; -public class TestSessionSticknessRule { +public class TestSessionStickinessRule { @Test public void testServerWithoutTimeoutAndWithThreshold() { diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilterTest.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilterTest.java index 23f7c92f636..7c78388dcdc 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilterTest.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/PriorityInstancePropertyDiscoveryFilterTest.java @@ -27,6 +27,7 @@ import java.util.Set; import java.util.stream.Collectors; +import org.apache.servicecomb.config.DynamicProperties; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.registry.api.DiscoveryInstance; import org.apache.servicecomb.registry.discovery.DiscoveryContext; @@ -59,6 +60,8 @@ public class PriorityInstancePropertyDiscoveryFilterTest { EnumerablePropertySource propertySource; + DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); + @Before public void setUp() { propertySource = Mockito.mock(EnumerablePropertySource.class); @@ -71,8 +74,10 @@ public void setUp() { filter = new PriorityInstancePropertyDiscoveryFilter(); filter.setEnvironment(environment); - Mockito.when(environment.getProperty("servicecomb.loadbalance.filter.priorityInstanceProperty.key", - String.class, "environment")).thenReturn("environment"); + filter.setDynamicProperties(dynamicProperties); + Mockito.when(dynamicProperties.getStringProperty(Mockito.eq("servicecomb.loadbalance.filter.priorityInstanceProperty.key"), + Mockito.any(), + Mockito.eq("environment"))).thenReturn("environment"); instances = new ArrayList<>(); filter.setEnvironment(environment); DiscoveryInstance discoveryInstance1 = Mockito.mock(DiscoveryInstance.class); diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestZoneAwareDiscoveryFilter.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestZoneAwareDiscoveryFilter.java new file mode 100644 index 00000000000..05b6d5432ca --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/TestZoneAwareDiscoveryFilter.java @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.loadbalance.filter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.config.DynamicProperties; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +import org.apache.servicecomb.registry.discovery.DiscoveryContext; +import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode; +import org.apache.servicecomb.registry.discovery.StatefulDiscoveryInstance; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +public class TestZoneAwareDiscoveryFilter { + DynamicProperties dynamicProperties = Mockito.mock(DynamicProperties.class); + + @BeforeEach + public void setUp() { + + Mockito.when(dynamicProperties.getBooleanProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.enabled"), + Mockito.any(), + Mockito.eq(true))) + .thenReturn(true); + } + + @Test + public void test_not_enough_instance() { + + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratio"), + Mockito.any(), + Mockito.eq(30))) + .thenReturn(50); + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratioCeiling"), + Mockito.any(), + Mockito.eq(50))) + .thenReturn(70); + + ZoneAwareDiscoveryFilter filter = new ZoneAwareDiscoveryFilter(); + filter.setDynamicProperties(dynamicProperties); + + // set up data + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); + filter.setDataCenterProperties(myself); + + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); + DataCenterInfo info = new DataCenterInfo(); + info.setName("test"); + info.setRegion("test-Region"); + info.setAvailableZone("test-zone"); + List allMatchEndpoint = new ArrayList<>(); + allMatchEndpoint.add("rest://localhost:9090"); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); + Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); + + DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); + DataCenterInfo regionMatchInfo = new DataCenterInfo(); + regionMatchInfo.setName("test"); + regionMatchInfo.setRegion("test-Region"); + regionMatchInfo.setAvailableZone("test-zone2"); + List regionMatchEndpoint = new ArrayList<>(); + regionMatchEndpoint.add("rest://localhost:9091"); + Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); + Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); + Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); + + DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); + DataCenterInfo noneMatchInfo = new DataCenterInfo(); + noneMatchInfo.setName("test"); + noneMatchInfo.setRegion("test-Region2"); + noneMatchInfo.setAvailableZone("test-zone2"); + List noMatchEndpoint = new ArrayList<>(); + noMatchEndpoint.add("rest://localhost:9092"); + Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); + Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); + Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); + + // run test + List data = Arrays.asList(allmatchInstance, regionMatchInstance, noneMatchInstance); + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); + DiscoveryContext context = new DiscoveryContext(); + DiscoveryTreeNode result = filter.discovery(context, parent); + + // check result + Integer level = context.getContextParameter(filter.contextParameter()); + Integer groups = parent.attribute(filter.groupsSizeParameter()); + List resultData = result.data(); + Assertions.assertEquals(1, level); + Assertions.assertEquals(2, groups); + Assertions.assertEquals(2, resultData.size()); + Assertions.assertEquals("regionMatchInstance", resultData.get(0).getInstanceId()); + Assertions.assertEquals("allmatchInstance", resultData.get(1).getInstanceId()); + } + + @Test + public void test_not_enough_instance_both_ceiling_floor() { + + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratio"), + Mockito.any(), + Mockito.eq(30))) + .thenReturn(40); + Mockito.when(dynamicProperties.getIntProperty(Mockito.eq("servicecomb.loadbalance.filter.zoneaware.ratioCeiling"), + Mockito.any(), + Mockito.eq(60))) + .thenReturn(60); + + ZoneAwareDiscoveryFilter filter = new ZoneAwareDiscoveryFilter(); + filter.setDynamicProperties(dynamicProperties); + + // set up data + DataCenterProperties myself = new DataCenterProperties(); + myself.setName("test"); + myself.setRegion("test-Region"); + myself.setAvailableZone("test-zone"); + filter.setDataCenterProperties(myself); + + DiscoveryInstance discoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance allmatchInstance = new StatefulDiscoveryInstance(discoveryInstance); + DataCenterInfo info = new DataCenterInfo(); + info.setName("test"); + info.setRegion("test-Region"); + info.setAvailableZone("test-zone"); + List allMatchEndpoint = new ArrayList<>(); + allMatchEndpoint.add("rest://localhost:9090"); + Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint); + Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info); + Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance"); + + DiscoveryInstance regionMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance regionMatchInstance = new StatefulDiscoveryInstance(regionMatchDiscoveryInstance); + DataCenterInfo regionMatchInfo = new DataCenterInfo(); + regionMatchInfo.setName("test"); + regionMatchInfo.setRegion("test-Region"); + regionMatchInfo.setAvailableZone("test-zone2"); + List regionMatchEndpoint = new ArrayList<>(); + regionMatchEndpoint.add("rest://localhost:9091"); + Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint); + Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo); + Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance"); + + DiscoveryInstance noneMatchDiscoveryInstance = Mockito.mock(DiscoveryInstance.class); + StatefulDiscoveryInstance noneMatchInstance = new StatefulDiscoveryInstance(noneMatchDiscoveryInstance); + DataCenterInfo noneMatchInfo = new DataCenterInfo(); + noneMatchInfo.setName("test"); + noneMatchInfo.setRegion("test-Region2"); + noneMatchInfo.setAvailableZone("test-zone2"); + List noMatchEndpoint = new ArrayList<>(); + noMatchEndpoint.add("rest://localhost:9092"); + Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint); + Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo); + Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance"); + + // run test + List data = Arrays.asList(allmatchInstance, regionMatchInstance, noneMatchInstance); + DiscoveryTreeNode parent = new DiscoveryTreeNode().name("parent").data(data); + DiscoveryContext context = new DiscoveryContext(); + DiscoveryTreeNode result = filter.discovery(context, parent); + + // check result + Integer level = context.getContextParameter(filter.contextParameter()); + Integer groups = parent.attribute(filter.groupsSizeParameter()); + List resultData = result.data(); + Assertions.assertEquals(null, level); + Assertions.assertEquals(1, groups); + Assertions.assertEquals(3, resultData.size()); + Assertions.assertEquals("noneMatchInstance", resultData.get(0).getInstanceId()); + Assertions.assertEquals("regionMatchInstance", resultData.get(1).getInstanceId()); + Assertions.assertEquals("allmatchInstance", resultData.get(2).getInstanceId()); + } +} diff --git a/handlers/handler-loadbalance/src/test/resources/microservice.yaml b/handlers/handler-loadbalance/src/test/resources/microservice.yaml index 4db1321ba99..5ff7ed9ae41 100644 --- a/handlers/handler-loadbalance/src/test/resources/microservice.yaml +++ b/handlers/handler-loadbalance/src/test/resources/microservice.yaml @@ -20,4 +20,4 @@ servicecomb: consumer: default: empty producer: - default: empty \ No newline at end of file + default: empty diff --git a/handlers/handler-publickey-auth/pom.xml b/handlers/handler-publickey-auth/pom.xml index 03735e95665..9f13b26f232 100644 --- a/handlers/handler-publickey-auth/pom.xml +++ b/handlers/handler-publickey-auth/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb handlers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT handler-publickey-auth diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java index 172a640a800..a7f8ea308ee 100644 --- a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/AuthenticationConfiguration.java @@ -35,32 +35,32 @@ public class AuthenticationConfiguration { public static final String ACCESS_CONTROL_ENABLED = ACCESS_CONTROL_PREFIX + ".enabled"; @Bean - public ConsumerAuthFilter consumerAuthFilter() { + public ConsumerAuthFilter scbConsumerAuthFilter() { return new ConsumerAuthFilter(); } @Bean - public ProviderAuthFilter providerAuthFilter() { + public ProviderAuthFilter scbProviderAuthFilter() { return new ProviderAuthFilter(); } @Bean - public AuthenticationBootListener authenticationBootListener() { + public AuthenticationBootListener scbAuthenticationBootListener() { return new AuthenticationBootListener(); } @Bean - public ConsumerTokenManager consumerTokenManager() { + public ConsumerTokenManager scbConsumerTokenManager() { return new ConsumerTokenManager(); } @Bean - public ProviderTokenManager providerTokenManager() { + public ProviderTokenManager scbProviderTokenManager() { return new ProviderTokenManager(); } @Bean - public AccessController accessController(Environment environment) { + public AccessController scbAccessController(Environment environment) { return new AccessController(environment); } } diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerAuthFilter.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerAuthFilter.java index 6957dc1b786..e26f24d936a 100644 --- a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerAuthFilter.java +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/consumer/ConsumerAuthFilter.java @@ -21,17 +21,18 @@ import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.beans.factory.annotation.Autowired; import jakarta.ws.rs.core.Response.Status; -public class ConsumerAuthFilter implements ConsumerFilter { +public class ConsumerAuthFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private ConsumerTokenManager authenticationTokenManager; @Autowired @@ -40,7 +41,7 @@ public void setConsumerTokenManager(ConsumerTokenManager consumerTokenManager) { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1010; } diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/PathCheckUtils.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/PathCheckUtils.java new file mode 100644 index 00000000000..c587195d238 --- /dev/null +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/PathCheckUtils.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.authentication.provider; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.env.Environment; + +public class PathCheckUtils { + private static final String KEY_INCLUDE_PATH = "servicecomb.publicKey.accessControl.includePathPatterns"; + + private static final String KEY_EXCLUDE_PATH = "servicecomb.publicKey.accessControl.excludePathPatterns"; + + /** + * first determine configured non-authentication path is matched requestPath, if match not needed auth. + * second determine whether of configured authentication path, if not configured, default all path need auth; + * if configured, then check whether of matched requestPath, if match needed auth, otherwise not needed auth. + * + * @param requestPath path + * @param env environment + * @return notRequiredAuth + */ + public static boolean isNotRequiredAuth(String requestPath, Environment env) { + if (excludePathMatchPath(requestPath, env)) { + return true; + } + return includePathMatchPath(requestPath, env); + } + + private static boolean excludePathMatchPath(String requestPath, Environment env) { + String excludePathPattern = env.getProperty(KEY_EXCLUDE_PATH, ""); + if (StringUtils.isEmpty(excludePathPattern)) { + return false; + } + return isPathMather(requestPath, excludePathPattern); + } + + private static boolean includePathMatchPath(String requestPath, Environment env) { + String includePathPattern = env.getProperty(KEY_INCLUDE_PATH, ""); + if (StringUtils.isEmpty(includePathPattern)) { + return false; + } + return !isPathMather(requestPath, includePathPattern); + } + + private static boolean isPathMather(String requestPath, String pathPattern) { + for (String pattern : pathPattern.split(",")) { + if (!pattern.isEmpty() && isPatternMatch(requestPath, pattern)) { + return true; + } + } + return false; + } + + public static boolean isPatternMatch(String value, String pattern) { + if (pattern.startsWith("*") || pattern.startsWith("/*")) { + int index = 0; + for (int i = 0; i < pattern.length(); i++) { + if (pattern.charAt(i) != '*' && pattern.charAt(i) != '/') { + break; + } + index++; + } + return value.endsWith(pattern.substring(index)); + } + if (pattern.endsWith("*")) { + int index = pattern.length() - 1; + for (int i = pattern.length() - 1; i >= 0; i--) { + if (pattern.charAt(i) != '*' && pattern.charAt(i) != '/') { + break; + } + index--; + } + return value.startsWith(pattern.substring(0, index + 1)); + } + return value.equals(pattern); + } +} diff --git a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java index 05374501653..f525ae88cf8 100644 --- a/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java +++ b/handlers/handler-publickey-auth/src/main/java/org/apache/servicecomb/authentication/provider/ProviderAuthFilter.java @@ -20,26 +20,31 @@ import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; -import org.apache.servicecomb.swagger.invocation.InvocationType; +import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import jakarta.ws.rs.core.Response.Status; -public class ProviderAuthFilter implements ProviderFilter { +public class ProviderAuthFilter extends AbstractFilter implements ProviderFilter { private ProviderTokenManager authenticationTokenManager; + private Environment env; + @Autowired - public void setProviderTokenManager(ProviderTokenManager providerTokenManager) { + public void setProviderTokenManager(ProviderTokenManager providerTokenManager, Environment env) { this.authenticationTokenManager = providerTokenManager; + this.env = env; } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.PROVIDER_SCHEDULE_FILTER_ORDER + 1010; } @@ -50,6 +55,11 @@ public String getName() { @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + String requestFullPath = SwaggerUtils.concatAbsolutePath(invocation.getSchemaMeta().getSwagger(), + invocation.getOperationMeta().getOperationPath()); + if (PathCheckUtils.isNotRequiredAuth(requestFullPath, env)) { + return nextNode.onFilter(invocation); + } String token = invocation.getContext(CoreConst.AUTH_TOKEN); if (null != token && authenticationTokenManager.valid(token)) { return nextNode.onFilter(invocation); diff --git a/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestPathCheckUtils.java b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestPathCheckUtils.java new file mode 100644 index 00000000000..b8ff4623081 --- /dev/null +++ b/handlers/handler-publickey-auth/src/test/java/org/apache/servicecomb/authentication/provider/TestPathCheckUtils.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.authentication.provider; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.mockito.Mockito.when; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.servers.Server; +import org.apache.servicecomb.swagger.SwaggerUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.core.env.Environment; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class TestPathCheckUtils { + private static final String KEY_INCLUDE_PATH = "servicecomb.publicKey.accessControl.includePathPatterns"; + + private static final String KEY_EXCLUDE_PATH = "servicecomb.publicKey.accessControl.excludePathPatterns"; + + private static final String BASE_PATH = "/api/v1"; + + private Environment environment; + + private OpenAPI swagger; + + @BeforeEach + public void setUp() { + environment = Mockito.mock(Environment.class); + swagger = new OpenAPI(); + swagger.setServers(new ArrayList<>()); + swagger.getServers().add(new Server().url(BASE_PATH)); + } + + @Test + public void testExcludePathWithBasePathAndExactMatch() { + String operationPath = "/public"; + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + operationPath); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, operationPath); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for excluded path with exact match"); + } + + @Test + public void testExcludePathWithBasePathAndWildcard() { + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + "/public/*"); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/test"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for excluded path with wildcard"); + + fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/nested/path"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for excluded nested path with wildcard"); + } + + @Test + public void testIncludePathWithBasePath() { + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(""); + when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(BASE_PATH + "/private/*"); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/private/resource"); + assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should require auth for included path"); + + fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public/resource"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for non-included path"); + } + + @Test + public void testExcludeOverrideIncludePath() { + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(BASE_PATH + "/resource"); + when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(BASE_PATH + "/*"); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/resource"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Exclude patterns should override include patterns"); + } + + @Test + public void testMultipleExcludePaths() { + List operationPaths = List.of("/public", "/health", "/metrics/*"); + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(concatPath(BASE_PATH, operationPaths)); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/public"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for first exclude path"); + + fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/health"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for second exclude path"); + + fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/metrics/jvm"); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth for wildcard exclude path"); + } + + @Test + public void testDifferentBasePath() { + String basePath = "/different/base"; + String publicOperationPath = "/public"; + String privateOperationPath = "/private"; + swagger.getServers().clear(); + swagger.getServers().add(new Server().url(basePath)); + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(basePath + publicOperationPath); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, publicOperationPath); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth with different base path"); + + fullPath = SwaggerUtils.concatAbsolutePath(swagger, privateOperationPath); + assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should require auth for non-excluded path with different base path"); + } + + @Test + public void testNoBasePath() { + String operationPath = "/public"; + swagger.setServers(null); + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(operationPath); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, operationPath); + assertTrue(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should not require auth when no base path is set"); + } + + @Test + public void testEmptyConfiguration() { + when(environment.getProperty(KEY_EXCLUDE_PATH, "")).thenReturn(""); + when(environment.getProperty(KEY_INCLUDE_PATH, "")).thenReturn(""); + + String fullPath = SwaggerUtils.concatAbsolutePath(swagger, "/any/path"); + assertFalse(PathCheckUtils.isNotRequiredAuth(fullPath, environment), + "Should require auth by default when no patterns are configured"); + } + + private String concatPath(String basePath, List paths) { + return paths.stream().map(path -> basePath + path).collect(Collectors.joining(",")); + } +} diff --git a/handlers/handler-publickey-auth/src/test/resources/microservice.yaml b/handlers/handler-publickey-auth/src/test/resources/microservice.yaml index 4db1321ba99..5ff7ed9ae41 100644 --- a/handlers/handler-publickey-auth/src/test/resources/microservice.yaml +++ b/handlers/handler-publickey-auth/src/test/resources/microservice.yaml @@ -20,4 +20,4 @@ servicecomb: consumer: default: empty producer: - default: empty \ No newline at end of file + default: empty diff --git a/handlers/handler-router/pom.xml b/handlers/handler-router/pom.xml index 367c5e08c44..0068002eb7d 100644 --- a/handlers/handler-router/pom.xml +++ b/handlers/handler-router/pom.xml @@ -22,7 +22,7 @@ handlers org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -59,4 +59,4 @@ handler-loadbalance - \ No newline at end of file + diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterAddHeaderFilter.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterAddHeaderFilter.java deleted file mode 100644 index bf7dbeb7eda..00000000000 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterAddHeaderFilter.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.router.custom; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.filter.Filter; -import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.core.filter.ProviderFilter; -import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; -import org.apache.servicecomb.foundation.common.utils.JsonUtils; -import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; -import org.apache.servicecomb.swagger.invocation.InvocationType; -import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.JsonProcessingException; - -public class RouterAddHeaderFilter implements Filter { - - private static final Logger LOGGER = LoggerFactory.getLogger(RouterAddHeaderFilter.class); - - private static final String SERVICECOMB_ROUTER_HEADER = "servicecomb.router.header"; - - private List allHeader = new ArrayList<>(); - - public RouterAddHeaderFilter() { - loadHeaders(); - } - - /** - * read config and get Header - */ - private void loadHeaders() { - addAllHeaders(LegacyPropertyFactory.getStringProperty(SERVICECOMB_ROUTER_HEADER)); - } - - private void addAllHeaders(String str) { - if (StringUtils.isEmpty(str)) { - return; - } - try { - allHeader = Arrays.asList(str.split(",")); - } catch (Exception e) { - LOGGER.error("route management Serialization failed: {}", e.getMessage()); - } - } - - private Map getHeaderMap(HttpServletRequestEx httpServletRequestEx) { - Map headerMap = new HashMap<>(); - allHeader.forEach(headerKey -> { - String val = httpServletRequestEx.getHeader(headerKey); - if (!StringUtils.isEmpty(val)) { - headerMap.put(headerKey, httpServletRequestEx.getHeader(headerKey)); - } - }); - return headerMap; - } - - @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { - return ProviderFilter.PROVIDER_SCHEDULE_FILTER_ORDER - 1970; - } - - @Override - public boolean enabledForInvocationType(InvocationType invocationType) { - // enable for both edge and producer - return true; - } - - @Override - public String getName() { - return "router-add-header"; - } - - @Override - public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - if (!invocation.isEdge() && !invocation.isProducer()) { - return nextNode.onFilter(invocation); - } - - if (!StringUtils.isEmpty(invocation.getContext(RouterServerListFilter.ROUTER_HEADER))) { - return nextNode.onFilter(invocation); - } - - if (allHeader.isEmpty()) { - return nextNode.onFilter(invocation); - } - - Map headerMap = getHeaderMap(invocation.getRequestEx()); - try { - invocation.addContext(RouterServerListFilter.ROUTER_HEADER, - JsonUtils.OBJ_MAPPER.writeValueAsString(headerMap)); - } catch (JsonProcessingException e) { - LOGGER.error("canary context serialization failed"); - } - return nextNode.onFilter(invocation); - } -} diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java index 3ebe527b760..fdae82e1445 100644 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java +++ b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterServerListFilter.java @@ -16,35 +16,23 @@ */ package org.apache.servicecomb.router.custom; -import java.util.HashMap; import java.util.List; -import java.util.Map; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.governance.MatchType; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.foundation.common.utils.JsonUtils; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.loadbalance.ServerListFilterExt; import org.apache.servicecomb.loadbalance.ServiceCombServer; import org.apache.servicecomb.router.RouterFilter; import org.apache.servicecomb.router.distribute.RouterDistributor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; public class RouterServerListFilter implements ServerListFilterExt { - private static final Logger LOGGER = LoggerFactory.getLogger(RouterServerListFilter.class); - private static final String ENABLE = "servicecomb.router.type"; private static final String TYPE_ROUTER = "router"; - public static final String ROUTER_HEADER = "X-RouterContext"; - @SuppressWarnings("unchecked") private final RouterDistributor routerDistributor = BeanUtils .getBean(RouterDistributor.class); @@ -61,42 +49,7 @@ public boolean enabled() { public List getFilteredListOfServers(List list, Invocation invocation) { String targetServiceName = invocation.getMicroserviceName(); - Map headers = addHeaders(invocation); - headers = filterHeaders(headers); - return routerFilter - .getFilteredListOfServers(list, targetServiceName, headers, - routerDistributor); - } - - private Map filterHeaders(Map headers) { - List filters = SPIServiceUtils - .getOrLoadSortedService(RouterHeaderFilterExt.class); - for (RouterHeaderFilterExt filterExt : filters) { - if (filterExt.enabled()) { - headers = filterExt.doFilter(headers); - } - } - return headers; - } - - private Map addHeaders(Invocation invocation) { - Map headers = new HashMap<>(); - if (invocation.getContext(ROUTER_HEADER) != null) { - Map canaryContext = null; - try { - canaryContext = JsonUtils.OBJ_MAPPER - .readValue(invocation.getContext(ROUTER_HEADER), - new TypeReference>() { - }); - } catch (JsonProcessingException e) { - LOGGER.error("canary context serialization failed"); - } - if (canaryContext != null) { - headers.putAll(canaryContext); - } - } - invocation.getInvocationArguments().forEach((k, v) -> headers.put(k, v == null ? null : v.toString())); - headers.putAll(invocation.getContext()); - return headers; + return routerFilter.getFilteredListOfServers(list, targetServiceName, + MatchType.createGovHttpRequest(invocation), routerDistributor); } } diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java index 7d66001c118..ff6952d24c8 100644 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java +++ b/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/ServiceCombRouterConfiguration.java @@ -29,12 +29,7 @@ public class ServiceCombRouterConfiguration { public static final String ROUTER_ENABLED = ROUTER_PREFIX + ".enabled"; @Bean - public ServiceCombRouterDistributor serviceCombRouterDistributor() { + public ServiceCombRouterDistributor scbServiceCombRouterDistributor() { return new ServiceCombRouterDistributor(); } - - @Bean - public RouterAddHeaderFilter routerAddHeaderFilter() { - return new RouterAddHeaderFilter(); - } } diff --git a/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt b/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt index f5a6ffa008e..0e6f5d9a65b 100644 --- a/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt +++ b/handlers/handler-router/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.router.custom.RouterServerListFilter \ No newline at end of file +org.apache.servicecomb.router.custom.RouterServerListFilter diff --git a/handlers/handler-tracing-zipkin/pom.xml b/handlers/handler-tracing-zipkin/pom.xml index eb287f25931..3644d209bab 100644 --- a/handlers/handler-tracing-zipkin/pom.xml +++ b/handlers/handler-tracing-zipkin/pom.xml @@ -22,7 +22,7 @@ handlers org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -50,10 +50,6 @@ io.zipkin.brave brave - - io.zipkin.reporter2 - zipkin-sender-okhttp3 - io.zipkin.brave brave-context-slf4j @@ -61,7 +57,14 @@ io.zipkin.zipkin2 zipkin - compile + + + io.zipkin.reporter2 + zipkin-sender-okhttp3 + + + io.zipkin.reporter2 + zipkin-reporter-brave diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpRequestParser.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpRequestParser.java new file mode 100644 index 00000000000..32fb8752028 --- /dev/null +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpRequestParser.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.tracing.zipkin; + +import org.apache.servicecomb.core.Invocation; + +import brave.SpanCustomizer; +import brave.http.HttpRequest; +import brave.http.HttpRequestParser; +import brave.http.HttpTags; +import brave.propagation.TraceContext; + +public record CustomHttpRequestParser() implements HttpRequestParser { + @Override + public void parse(HttpRequest req, TraceContext context, SpanCustomizer span) { + Invocation invocation = ((InvocationAware) req).getInvocation(); + span.name(invocation.getInvocationQualifiedName()); + HttpTags.METHOD.tag(req, context, span); + HttpTags.PATH.tag(req, context, span); + if (!invocation.isConsumer()) { // server or edge + HttpTags.ROUTE.tag(req, context, span); + } + } +} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpResponseParser.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpResponseParser.java new file mode 100644 index 00000000000..bb59f7e9371 --- /dev/null +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/CustomHttpResponseParser.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.tracing.zipkin; + +import org.apache.servicecomb.core.Invocation; + +import brave.SpanCustomizer; +import brave.Tags; +import brave.http.HttpResponse; +import brave.http.HttpResponseParser; +import brave.http.HttpTags; +import brave.propagation.TraceContext; + +public record CustomHttpResponseParser() implements HttpResponseParser { + @Override + public void parse(HttpResponse response, TraceContext context, SpanCustomizer span) { + Invocation invocation = ((InvocationAware) response).getInvocation(); + HttpTags.STATUS_CODE.tag(response, context, span); + if (response.error() != null) { + span.tag(Tags.ERROR.key(), response.error().getMessage()); + } + if (!invocation.isProducer()) { // client or edge + span.tag(HttpTags.ROUTE.key(), response.route()); + } + } +} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientRequestWrapper.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientRequestWrapper.java index b8a157b06f0..cc758d68a27 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientRequestWrapper.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientRequestWrapper.java @@ -17,31 +17,17 @@ package org.apache.servicecomb.tracing.zipkin; -import brave.http.HttpClientRequest; - import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class HttpClientRequestWrapper extends HttpClientRequest { - private static final Logger LOG = LoggerFactory.getLogger(HttpClientRequestWrapper.class); - private Invocation invocation; +import brave.http.HttpClientRequest; - HttpClientRequestWrapper() { - } +class HttpClientRequestWrapper extends HttpClientRequest implements InvocationAware { + private final Invocation invocation; HttpClientRequestWrapper(Invocation invocation) { this.invocation = invocation; } - HttpClientRequestWrapper invocation(Invocation invocation) { - this.invocation = invocation; - return this; - } - - @Override public void header(String name, String value) { invocation.addContext(name, value); @@ -52,30 +38,33 @@ public String method() { return invocation.getOperationMeta().getHttpMethod(); } + @Override + public String route() { + return invocation.getEndpoint().getEndpoint(); + } + @Override public String path() { - if (LegacyPropertyFactory.getBooleanProperty(TracingConfiguration.TRACING_WORK_WITH_THIRDPARTY, false)) { - try { - return TracingConfiguration.createRequestPath(invocation); - } catch (Exception e) { - LOG.warn("generate rest path failed: {}", e.getMessage()); - } - } - return invocation.getOperationMeta().getOperationPath(); + return TracingConfiguration.createRequestPath(invocation); } @Override public String url() { - return invocation.getEndpoint().getEndpoint(); + return invocation.getInvocationQualifiedName(); } @Override public String header(String name) { - return invocation.getContext().get(name); + return invocation.getContext(name); } @Override public Object unwrap() { return invocation; } + + @Override + public Invocation getInvocation() { + return invocation; + } } diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientResponseWrapper.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientResponseWrapper.java index 3cc070505aa..b939efaa610 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientResponseWrapper.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpClientResponseWrapper.java @@ -17,43 +17,23 @@ package org.apache.servicecomb.tracing.zipkin; -import brave.http.HttpClientRequest; -import brave.http.HttpClientResponse; -import brave.internal.Nullable; - +import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; -class HttpClientResponseWrapper extends HttpClientResponse { - @Nullable - HttpClientRequest request; - - @Nullable - Response response; +import brave.http.HttpClientRequest; +import brave.http.HttpClientResponse; - @Nullable - Throwable error; +class HttpClientResponseWrapper extends HttpClientResponse implements InvocationAware { + final HttpClientRequest request; - HttpClientResponseWrapper() { - } + final Response response; - HttpClientResponseWrapper(@Nullable Response response, @Nullable Throwable error) { - this.response = response; - this.error = error; - } + final Invocation invocation; - HttpClientResponseWrapper response(Response response) { + HttpClientResponseWrapper(Invocation invocation, Response response, HttpClientRequestWrapper request) { this.response = response; - return this; - } - - HttpClientResponseWrapper throwable(Throwable error) { - this.error = error; - return this; - } - - HttpClientResponseWrapper request(HttpClientRequest request) { this.request = request; - return this; + this.invocation = invocation; } @Override @@ -73,6 +53,11 @@ public HttpClientRequest request() { @Override public Throwable error() { - return error; + return response.isFailed() ? response.getResult() : null; + } + + @Override + public Invocation getInvocation() { + return invocation; } } diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServeRequestWrapper.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServeRequestWrapper.java index 5abea79a498..45725539b46 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServeRequestWrapper.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServeRequestWrapper.java @@ -17,30 +17,18 @@ package org.apache.servicecomb.tracing.zipkin; -import brave.http.HttpServerRequest; - +import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class HttpServeRequestWrapper extends HttpServerRequest { - private static final Logger LOG = LoggerFactory.getLogger(HttpServeRequestWrapper.class); - private Invocation invocation; +import brave.http.HttpServerRequest; - HttpServeRequestWrapper() { - } +class HttpServeRequestWrapper extends HttpServerRequest implements InvocationAware { + private final Invocation invocation; HttpServeRequestWrapper(Invocation invocation) { this.invocation = invocation; } - HttpServeRequestWrapper invocation(Invocation invocation) { - this.invocation = invocation; - return this; - } - @Override public String method() { return invocation.getOperationMeta().getHttpMethod(); @@ -48,28 +36,35 @@ public String method() { @Override public String path() { - if (LegacyPropertyFactory.getBooleanProperty(TracingConfiguration.TRACING_WORK_WITH_THIRDPARTY, false)) { - try { - return TracingConfiguration.createRequestPath(invocation); - } catch (Exception e) { - LOG.warn("generate rest path failed: {}", e.getMessage()); - } - } - return invocation.getOperationMeta().getOperationPath(); + return TracingConfiguration.createRequestPath(invocation); } @Override public String url() { - return invocation.getEndpoint().getEndpoint(); + return invocation.getInvocationQualifiedName(); } @Override - public String header(String key) { - return invocation.getContext().get(key); + public String route() { + return invocation.getRequestEx() != null ? invocation.getRequestEx().getRemoteAddr() : null; + } + + @Override + public String header(String name) { + String result = invocation.getContext(name); + if (StringUtils.isEmpty(result) && invocation.getRequestEx() != null) { + result = invocation.getRequestEx().getHeader(name); + } + return result; } @Override public Object unwrap() { return invocation; } + + @Override + public Invocation getInvocation() { + return invocation; + } } diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServerResponseWrapper.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServerResponseWrapper.java index ea01bea418a..9f384bdfffd 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServerResponseWrapper.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/HttpServerResponseWrapper.java @@ -17,43 +17,23 @@ package org.apache.servicecomb.tracing.zipkin; -import brave.http.HttpServerRequest; -import brave.http.HttpServerResponse; -import brave.internal.Nullable; - +import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.swagger.invocation.Response; -class HttpServerResponseWrapper extends HttpServerResponse { - @Nullable - private Response response; - - @Nullable - private Throwable error; +import brave.http.HttpServerRequest; +import brave.http.HttpServerResponse; - @Nullable - private HttpServerRequest request; +class HttpServerResponseWrapper extends HttpServerResponse implements InvocationAware { + private final Response response; - HttpServerResponseWrapper() { - } + private final HttpServerRequest request; - HttpServerResponseWrapper(Response response, Throwable error) { - this.response = response; - this.error = error; - } + private final Invocation invocation; - HttpServerResponseWrapper response(Response response) { + HttpServerResponseWrapper(Invocation invocation, Response response, HttpServeRequestWrapper request) { this.response = response; - return this; - } - - HttpServerResponseWrapper request(HttpServerRequest request) { this.request = request; - return this; - } - - HttpServerResponseWrapper error(Throwable error) { - this.error = error; - return this; + this.invocation = invocation; } @Override @@ -73,6 +53,11 @@ public HttpServerRequest request() { @Override public Throwable error() { - return error; + return response.isFailed() ? response.getResult() : null; + } + + @Override + public Invocation getInvocation() { + return invocation; } } diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationHandlersStartEvent.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/InvocationAware.java similarity index 80% rename from core/src/main/java/org/apache/servicecomb/core/event/InvocationHandlersStartEvent.java rename to handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/InvocationAware.java index 55d57001b89..ddb4e8365ae 100644 --- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationHandlersStartEvent.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/InvocationAware.java @@ -14,13 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.apache.servicecomb.core.event; +package org.apache.servicecomb.tracing.zipkin; import org.apache.servicecomb.core.Invocation; -public class InvocationHandlersStartEvent extends InvocationBaseEvent { - public InvocationHandlersStartEvent(Invocation invocation) { - super(invocation); - } +public interface InvocationAware { + Invocation getInvocation(); } diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/LogSpanHandler.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/LogSpanHandler.java new file mode 100644 index 00000000000..93499ec9209 --- /dev/null +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/LogSpanHandler.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.tracing.zipkin; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brave.handler.MutableSpan; +import brave.handler.SpanHandler; +import brave.propagation.TraceContext; + +public class LogSpanHandler extends SpanHandler { + private static final Logger LOGGER = LoggerFactory.getLogger("scb-trace"); + + public boolean end(TraceContext context, MutableSpan span, Cause cause) { + if (!LOGGER.isInfoEnabled()) { + return false; + } else { + LOGGER.info(span.toString()); + return true; + } + } + + public String toString() { + return "LogSpanHandler{name=scb-trace}"; + } +} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/TracingConfiguration.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/TracingConfiguration.java index 144f2f3d766..79dbc2857b6 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/TracingConfiguration.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/TracingConfiguration.java @@ -34,6 +34,7 @@ import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -45,87 +46,108 @@ import brave.propagation.CurrentTraceContext; import brave.propagation.ThreadLocalCurrentTraceContext; import zipkin2.Span; -import zipkin2.codec.SpanBytesEncoder; import zipkin2.reporter.AsyncReporter; +import zipkin2.reporter.BytesMessageSender; import zipkin2.reporter.Reporter; import zipkin2.reporter.Sender; +import zipkin2.reporter.SpanBytesEncoder; import zipkin2.reporter.brave.AsyncZipkinSpanHandler; import zipkin2.reporter.okhttp3.OkHttpSender; @Configuration @ConditionalOnProperty(value = TracingConfiguration.TRACING_ENABLED, - havingValue = "true") + havingValue = "true", matchIfMissing = true) public class TracingConfiguration { - public static final String TRACING_PREFIX = "servicecomb.tracing.zipkin"; - - public static final String TRACING_WORK_WITH_THIRDPARTY = "servicecomb.tracing.workWithThirdParty"; + public static final String TRACING_PREFIX = "servicecomb.tracing"; public static final String TRACING_ENABLED = TRACING_PREFIX + ".enabled"; - private String apiVersion = CONFIG_TRACING_COLLECTOR_API_V2; - - @Bean - Sender sender(DynamicProperties dynamicProperties) { - apiVersion = dynamicProperties.getStringProperty(CONFIG_TRACING_COLLECTOR_API_VERSION, - CONFIG_TRACING_COLLECTOR_API_V2).toLowerCase(); - // use default value if the user set value is invalid - if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) != 0) { - apiVersion = CONFIG_TRACING_COLLECTOR_API_V2; + public static final String TRACING_REPORTER_LOG_ENABLED = TRACING_PREFIX + ".reporter.log.enabled"; + + public static final String TRACING_REPORTER_ZIPKIN_ENABLED = TRACING_PREFIX + ".reporter.zipkin.enabled"; + + @Configuration + @ConditionalOnProperty(value = TracingConfiguration.TRACING_REPORTER_ZIPKIN_ENABLED, + havingValue = "true") + static class ZipkinReporterConfiguration { + @Bean + BytesMessageSender scbOkHttpSender(DynamicProperties dynamicProperties) { + String apiVersion = dynamicProperties.getStringProperty(CONFIG_TRACING_COLLECTOR_API_VERSION, + CONFIG_TRACING_COLLECTOR_API_V2).toLowerCase(); + // use default value if the user set value is invalid + if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) != 0) { + apiVersion = CONFIG_TRACING_COLLECTOR_API_V2; + } + + String path = MessageFormat.format(CONFIG_TRACING_COLLECTOR_PATH, apiVersion); + return OkHttpSender.create( + dynamicProperties.getStringProperty( + CONFIG_TRACING_COLLECTOR_ADDRESS, + DEFAULT_TRACING_COLLECTOR_ADDRESS) + .trim() + .replaceAll("/+$", "") + .concat(path)); } - String path = MessageFormat.format(CONFIG_TRACING_COLLECTOR_PATH, apiVersion); - return OkHttpSender.create( - dynamicProperties.getStringProperty( - CONFIG_TRACING_COLLECTOR_ADDRESS, - DEFAULT_TRACING_COLLECTOR_ADDRESS) - .trim() - .replaceAll("/+$", "") - .concat(path)); - } + @Bean + Reporter scbZipkinReporter(DynamicProperties dynamicProperties, BytesMessageSender sender) { + String apiVersion = dynamicProperties.getStringProperty(CONFIG_TRACING_COLLECTOR_API_VERSION, + CONFIG_TRACING_COLLECTOR_API_V2).toLowerCase(); + if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) == 0) { + return AsyncReporter.builder(sender).build(SpanBytesEncoder.JSON_V1); + } - @Bean - Reporter zipkinReporter(Sender sender) { - if (apiVersion.compareTo(CONFIG_TRACING_COLLECTOR_API_V1) == 0) { - return AsyncReporter.builder(sender).build(SpanBytesEncoder.JSON_V1); + return AsyncReporter.builder(sender).build(); } - - return AsyncReporter.builder(sender).build(); } - @Bean - Tracing tracing(Sender sender, DynamicProperties dynamicProperties, - CurrentTraceContext currentTraceContext, Environment environment) { - return Tracing.newBuilder() - .localServiceName(BootStrapProperties.readServiceName(environment)) - .currentTraceContext(currentTraceContext) // puts trace IDs into logs - .addSpanHandler(AsyncZipkinSpanHandler.create(sender)) + CurrentTraceContext scbCurrentTraceContext() { + return ThreadLocalCurrentTraceContext.newBuilder() + .addScopeDecorator(MDCScopeDecorator.newBuilder().build()) .build(); } @Bean - CurrentTraceContext currentTraceContext() { - return ThreadLocalCurrentTraceContext.newBuilder().addScopeDecorator(MDCScopeDecorator.newBuilder().build()) - .build(); + Tracing scbTracing(@Autowired(required = false) Sender sender, + CurrentTraceContext currentTraceContext, Environment environment, DynamicProperties dynamicProperties) { + Tracing.Builder builder = Tracing.newBuilder() + .localServiceName(BootStrapProperties.readServiceName(environment)) + .currentTraceContext(currentTraceContext); // puts trace IDs into logs + if (dynamicProperties.getBooleanProperty(TRACING_REPORTER_LOG_ENABLED, true)) { + builder.addSpanHandler(new LogSpanHandler()); + } + if (dynamicProperties.getBooleanProperty(TRACING_REPORTER_ZIPKIN_ENABLED, false)) { + builder.addSpanHandler(AsyncZipkinSpanHandler.create(sender)); + } + return builder.build(); } @Bean - HttpTracing httpTracing(Tracing tracing) { - return HttpTracing.create(tracing); + HttpTracing scbHttpTracing(Tracing tracing) { + return HttpTracing.newBuilder(tracing) + .clientRequestParser(new CustomHttpRequestParser()) + .clientResponseParser(new CustomHttpResponseParser()) + .serverRequestParser(new CustomHttpRequestParser()) + .serverResponseParser(new CustomHttpResponseParser()).build(); } @Bean - ZipkinTracingFilter zipkinTracingFilter() { + ZipkinTracingFilter scbZipkinTracingFilter() { return new ZipkinTracingFilter(); } - public static String createRequestPath(Invocation invocation) throws Exception { + public static String createRequestPath(Invocation invocation) { URIEndpointObject address = (URIEndpointObject) invocation.getEndpoint().getAddress(); String urlPrefix = address.getFirst(DefinitionConst.URL_PREFIX); RestOperationMeta swaggerRestOperation = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); String path = (String) invocation.getHandlerContext().get(RestConst.REST_CLIENT_REQUEST_PATH); if (path == null) { - path = swaggerRestOperation.getPathBuilder().createRequestPath(invocation.getSwaggerArguments()); + try { + path = swaggerRestOperation.getPathBuilder().createRequestPath(invocation.getSwaggerArguments()); + } catch (Exception e) { + path = invocation.getOperationMeta().getOperationPath(); + } } if (StringUtils.isEmpty(urlPrefix) || path.startsWith(urlPrefix)) { diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinConsumerDelegate.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinConsumerDelegate.java deleted file mode 100644 index 149dc8c6e6a..00000000000 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinConsumerDelegate.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.servicecomb.tracing.zipkin; - -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.swagger.invocation.Response; - -import brave.Span; -import brave.Tracing; -import brave.http.HttpClientRequest; -import brave.http.HttpClientResponse; -import brave.http.HttpClientHandler; -import brave.http.HttpTracing; -import brave.propagation.Propagation.Setter; -import brave.propagation.TraceContext.Injector; - -class ZipkinConsumerDelegate implements ZipkinTracingDelegate { - - private final HttpClientHandler handler; - - private final HttpTracing httpTracing; - - private final Injector injector; - - private final HttpClientResponseWrapper responseWrapper; - - private final HttpClientRequestWrapper requestWrapper; - - ZipkinConsumerDelegate(HttpTracing httpTracing) { - this.httpTracing = httpTracing; - this.injector = httpTracing.tracing().propagation().injector(injector()); - this.handler = HttpClientHandler.create(httpTracing); - this.responseWrapper = new HttpClientResponseWrapper(); - this.requestWrapper = new HttpClientRequestWrapper(); - } - - @Override - public Span createSpan(Invocation invocation) { - return handler.handleSend(requestWrapper.invocation(invocation)); - } - - @Override - public void onResponse(Span span, Response response, Throwable error) { - handler.handleReceive(responseWrapper.response(response).throwable(error).request(requestWrapper), span); - } - - @Override - public String name() { - return "Zipkin consumer"; - } - - @Override - public Tracing tracer() { - return httpTracing.tracing(); - } - - private Setter injector() { - return (invocation, key, value) -> invocation.getContext().put(key, value); - } -} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegate.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegate.java deleted file mode 100644 index b17370979e3..00000000000 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegate.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.servicecomb.tracing.zipkin; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.core.CoreConst; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.swagger.invocation.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import brave.Span; -import brave.Tracing; -import brave.http.HttpServerRequest; -import brave.http.HttpServerResponse; -import brave.http.HttpServerHandler; -import brave.http.HttpTracing; -import brave.propagation.Propagation.Getter; -import brave.propagation.TraceContext.Extractor; - -class ZipkinProviderDelegate implements ZipkinTracingDelegate { - private static final Logger LOG = LoggerFactory.getLogger(ZipkinProviderDelegate.class); - - private final HttpServerHandler handler; - - private final HttpTracing httpTracing; - - private final Extractor extractor; - - private final HttpServeRequestWrapper requestWrapper; - - private final HttpServerResponseWrapper responseWrapper; - - public static final String SPAN_ID_HEADER_NAME = "X-B3-SpanId"; - - public static final String TRACE_ID_HEADER_NAME = CoreConst.TRACE_ID_NAME; - - private static final Getter INVOCATION_STRING_GETTER = (invocation, key) -> { - String extracted = invocation.getContext().get(key); - if (StringUtils.isEmpty(extracted) && SPAN_ID_HEADER_NAME.equals(key)) { - // use traceId as spanId to avoid brave's recreating traceId - extracted = invocation.getContext().get(TRACE_ID_HEADER_NAME); - LOG.debug("try to extract X-B3-SpanId, but the value is empty, replace with TraceId = [{}]", extracted); - } - return extracted; - }; - - @VisibleForTesting - static Getter getInvocationStringGetter() { - return INVOCATION_STRING_GETTER; - } - - ZipkinProviderDelegate(HttpTracing httpTracing) { - this.httpTracing = httpTracing; - this.extractor = httpTracing.tracing().propagation().extractor(extractor()); - this.handler = HttpServerHandler.create(httpTracing); - this.requestWrapper = new HttpServeRequestWrapper(); - this.responseWrapper = new HttpServerResponseWrapper(); - } - - @Override - public Tracing tracer() { - return httpTracing.tracing(); - } - - @Override - public Span createSpan(Invocation invocation) { - return handler.handleReceive(requestWrapper.invocation(invocation)); - } - - @Override - public void onResponse(Span span, Response response, Throwable error) { - handler.handleSend(responseWrapper.response(response).error(error).request(requestWrapper), span); - } - - @Override - public String name() { - return "Zipkin provider"; - } - - private Getter extractor() { - return INVOCATION_STRING_GETTER; - } -} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingFilter.java b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingFilter.java index b7c62e44223..f88532adf5e 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingFilter.java +++ b/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingFilter.java @@ -17,56 +17,113 @@ package org.apache.servicecomb.tracing.zipkin; -import static org.apache.servicecomb.swagger.invocation.InvocationType.PROVIDER; - -import java.util.concurrent.CompletableFuture; - import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.exception.Exceptions; -import org.apache.servicecomb.core.filter.Filter; -import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.event.InvocationStartEvent; +import org.apache.servicecomb.foundation.common.event.EventManager; import org.springframework.beans.factory.annotation.Autowired; +import com.google.common.eventbus.Subscribe; + import brave.Span; -import brave.Tracer.SpanInScope; +import brave.http.HttpClientHandler; +import brave.http.HttpClientRequest; +import brave.http.HttpClientResponse; +import brave.http.HttpServerHandler; +import brave.http.HttpServerRequest; +import brave.http.HttpServerResponse; import brave.http.HttpTracing; -public class ZipkinTracingFilter implements Filter { - public static final String NAME = "zipkin"; +public class ZipkinTracingFilter { + public static final String CONTEXT_TRACE_REQUEST = "x-trace-request"; + + public static final String CONTEXT_TRACE_HANDLER = "x-trace-handler"; + + public static final String CONTEXT_TRACE_SPAN = "x-trace-span"; - private ZipkinConsumerDelegate consumer; + public static final String CONTEXT_TRACE_EDGE_REQUEST = "x-trace-edge-request"; - private ZipkinProviderDelegate producer; + public static final String CONTEXT_TRACE_EDGE_HANDLER = "x-trace-edge-handler"; - @Override - public String getName() { - return NAME; + public static final String CONTEXT_TRACE_EDGE_SPAN = "x-trace-edge-span"; + + private HttpTracing httpTracing; + + public ZipkinTracingFilter() { + EventManager.register(this); } @Autowired public void setHttpTracing(HttpTracing httpTracing) { - this.consumer = new ZipkinConsumerDelegate(httpTracing); - this.producer = new ZipkinProviderDelegate(httpTracing); + this.httpTracing = httpTracing; } - @SuppressWarnings({"try", "unused"}) - @Override - public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - ZipkinTracingDelegate tracing = collectTracing(invocation); + @Subscribe + public void onInvocationStartEvent(InvocationStartEvent event) { + Invocation invocation = event.getInvocation(); - Span span = tracing.createSpan(invocation); - try (SpanInScope scope = tracing.tracer().tracer().withSpanInScope(span)) { - return nextNode.onFilter(invocation) - .whenComplete((response, exception) -> tracing.onResponse(span, response, Exceptions.unwrap(exception))); + if (invocation.isProducer()) { + HttpServerHandler handler = HttpServerHandler.create(httpTracing); + HttpServeRequestWrapper request = new HttpServeRequestWrapper(invocation); + Span span = handler.handleReceive(request); + invocation.addLocalContext(CONTEXT_TRACE_SPAN, span); + invocation.addLocalContext(CONTEXT_TRACE_HANDLER, handler); + invocation.addLocalContext(CONTEXT_TRACE_REQUEST, request); + } else if (invocation.isConsumer()) { + Span parentSpan = invocation.getLocalContext(CONTEXT_TRACE_SPAN); + HttpClientHandler handler = HttpClientHandler.create(httpTracing); + HttpClientRequestWrapper request = new HttpClientRequestWrapper(invocation); + Span span = handler.handleSendWithParent(request, parentSpan == null ? null : parentSpan.context()); + invocation.addLocalContext(CONTEXT_TRACE_HANDLER, handler); + invocation.addLocalContext(CONTEXT_TRACE_REQUEST, request); + invocation.addLocalContext(CONTEXT_TRACE_SPAN, span); + } else { + // edge as server + HttpServerHandler serverHandler = HttpServerHandler.create(httpTracing); + HttpServeRequestWrapper serverRequest = new HttpServeRequestWrapper(invocation); + Span serverSpan = serverHandler.handleReceive(serverRequest); + invocation.addLocalContext(CONTEXT_TRACE_EDGE_SPAN, serverSpan); + invocation.addLocalContext(CONTEXT_TRACE_EDGE_HANDLER, serverHandler); + invocation.addLocalContext(CONTEXT_TRACE_EDGE_REQUEST, serverRequest); + + // edge as client + HttpClientHandler clientHandler = HttpClientHandler.create(httpTracing); + HttpClientRequestWrapper clientRequest = new HttpClientRequestWrapper(invocation); + Span clientSpan = clientHandler.handleSendWithParent(clientRequest, serverSpan.context()); + invocation.addLocalContext(CONTEXT_TRACE_HANDLER, clientHandler); + invocation.addLocalContext(CONTEXT_TRACE_REQUEST, clientRequest); + invocation.addLocalContext(CONTEXT_TRACE_SPAN, clientSpan); } } - private ZipkinTracingDelegate collectTracing(Invocation invocation) { - if (PROVIDER.equals(invocation.getInvocationType())) { - return producer; + @Subscribe + public void onInvocationFinishEvent(InvocationFinishEvent event) { + Invocation invocation = event.getInvocation(); + if (invocation.isProducer()) { + HttpServerHandler handler + = invocation.getLocalContext(CONTEXT_TRACE_HANDLER); + Span span = invocation.getLocalContext(CONTEXT_TRACE_SPAN); + handler.handleSend(new HttpServerResponseWrapper(invocation, event.getResponse(), + invocation.getLocalContext(CONTEXT_TRACE_REQUEST)), span); + } else if (invocation.isConsumer()) { + HttpClientHandler handler + = invocation.getLocalContext(CONTEXT_TRACE_HANDLER); + Span span = invocation.getLocalContext(CONTEXT_TRACE_SPAN); + handler.handleReceive(new HttpClientResponseWrapper(invocation, event.getResponse(), + invocation.getLocalContext(CONTEXT_TRACE_REQUEST)), span); + } else { + // edge as client + HttpClientHandler clientHandler + = invocation.getLocalContext(CONTEXT_TRACE_HANDLER); + Span clientSpan = invocation.getLocalContext(CONTEXT_TRACE_SPAN); + clientHandler.handleReceive(new HttpClientResponseWrapper(invocation, event.getResponse(), + invocation.getLocalContext(CONTEXT_TRACE_REQUEST)), clientSpan); + // edge as server + HttpServerHandler serverHandler + = invocation.getLocalContext(CONTEXT_TRACE_EDGE_HANDLER); + Span serverSpan = invocation.getLocalContext(CONTEXT_TRACE_EDGE_SPAN); + serverHandler.handleSend(new HttpServerResponseWrapper(invocation, event.getResponse(), + invocation.getLocalContext(CONTEXT_TRACE_EDGE_REQUEST)), serverSpan); } - - return consumer; } } diff --git a/handlers/handler-tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegateTest.java b/handlers/handler-tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegateTest.java deleted file mode 100644 index 68fd31bbe90..00000000000 --- a/handlers/handler-tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinProviderDelegateTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.servicecomb.tracing.zipkin; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.core.Invocation; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import brave.propagation.Propagation.Getter; - -public class ZipkinProviderDelegateTest { - @Test - public void testGetterOnGetSpanId() { - Getter getter = ZipkinProviderDelegate.getInvocationStringGetter(); - - Invocation invocation = Mockito.mock(Invocation.class); - Map context = new HashMap<>(); - - Mockito.when(invocation.getContext()).thenReturn(context); - - // if there is no spanId or traceId, then result is null - String spanId = getter.get(invocation, ZipkinProviderDelegate.SPAN_ID_HEADER_NAME); - Assertions.assertNull(spanId); - - // if there is no spanId but traceId, then traceId will be returned as result - final String testTraceId = "testTraceId"; - context.put(ZipkinProviderDelegate.TRACE_ID_HEADER_NAME, testTraceId); - spanId = getter.get(invocation, ZipkinProviderDelegate.SPAN_ID_HEADER_NAME); - Assertions.assertEquals(testTraceId, spanId); - - // if there is spanId, then spanId will be returned - final String testSpanId = "testSpanId"; - context.put(ZipkinProviderDelegate.SPAN_ID_HEADER_NAME, testSpanId); - spanId = getter.get(invocation, ZipkinProviderDelegate.SPAN_ID_HEADER_NAME); - Assertions.assertEquals(testSpanId, spanId); - } - - @Test - public void testGetterOnGetOtherContent() { - Getter getter = ZipkinProviderDelegate.getInvocationStringGetter(); - - Invocation invocation = Mockito.mock(Invocation.class); - Map context = new HashMap<>(); - - Mockito.when(invocation.getContext()).thenReturn(context); - - final String key = "key"; - String value = getter.get(invocation, key); - Assertions.assertNull(value); - - final String expectedValue = "value"; - context.put(key, expectedValue); - value = getter.get(invocation, key); - Assertions.assertEquals(expectedValue, value); - } -} diff --git a/handlers/pom.xml b/handlers/pom.xml index 800c25d6c1d..e74e2c95134 100644 --- a/handlers/pom.xml +++ b/handlers/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 diff --git a/huawei-cloud/darklaunch/pom.xml b/huawei-cloud/darklaunch/pom.xml index 2675fd5eb26..b4396f87a64 100644 --- a/huawei-cloud/darklaunch/pom.xml +++ b/huawei-cloud/darklaunch/pom.xml @@ -21,7 +21,7 @@ huawei-cloud org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRule.java b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRule.java index 6dbb621b65e..988cd9e401f 100644 --- a/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRule.java +++ b/huawei-cloud/darklaunch/src/main/java/org/apache/servicecomb/darklaunch/DarklaunchRule.java @@ -89,4 +89,3 @@ public void addRuleItem(DarklaunchRuleItem ruleItem) { this.ruleItems.add(ruleItem); } } - diff --git a/huawei-cloud/darklaunch/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt b/huawei-cloud/darklaunch/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt index 6b7ea02f089..1104043c1cc 100644 --- a/huawei-cloud/darklaunch/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt +++ b/huawei-cloud/darklaunch/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.darklaunch.DarklaunchServerListFilter \ No newline at end of file +org.apache.servicecomb.darklaunch.DarklaunchServerListFilter diff --git a/huawei-cloud/dashboard/pom.xml b/huawei-cloud/dashboard/pom.xml index 5d9f67982c0..015a43a240a 100644 --- a/huawei-cloud/dashboard/pom.xml +++ b/huawei-cloud/dashboard/pom.xml @@ -21,7 +21,7 @@ huawei-cloud org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DashboardConfiguration.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DashboardConfiguration.java index 885b8bbf26a..328b6bf6e02 100644 --- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DashboardConfiguration.java +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DashboardConfiguration.java @@ -24,32 +24,32 @@ @Configuration public class DashboardConfiguration { @Bean - public MonitorInformationCollector monitorInformationCollector() { + public MonitorInformationCollector scbMonitorInformationCollector() { return new MonitorInformationCollector(); } @Bean - public MonitorConstant monitorConstant(Environment environment) { + public MonitorConstant scbMonitorConstant(Environment environment) { return new MonitorConstant(environment); } @Bean - public MonitorBootListener monitorBootListener() { + public MonitorBootListener scbMonitorBootListener() { return new MonitorBootListener(); } @Bean - public DataFactory dataFactory() { + public DataFactory scbDataFactory() { return new DataFactory(); } @Bean - public MetricsMonitorDataProvider metricsMonitorDataProvider() { + public MetricsMonitorDataProvider scbMetricsMonitorDataProvider() { return new MetricsMonitorDataProvider(); } @Bean - public DefaultMonitorDataPublisher defaultMonitorDataPublisher() { + public DefaultMonitorDataPublisher scbDefaultMonitorDataPublisher() { return new DefaultMonitorDataPublisher(); } } diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java index cd7d2fc10fe..1209d9d8532 100644 --- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/DefaultMonitorDataPublisher.java @@ -84,8 +84,10 @@ private DashboardAddressManager createDashboardAddressManager() { if (addresses.isEmpty()) { throw new IllegalStateException("dashboard address is not configured."); } + String region = environment.getProperty("servicecomb.datacenter.region"); + String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); - return new DashboardAddressManager(addresses, EventManager.getEventBus()); + return new DashboardAddressManager(addresses, EventManager.getEventBus(), region, availableZone); } private HttpTransport createHttpTransport(DashboardAddressManager addressManager, RequestConfig requestConfig, @@ -120,8 +122,9 @@ private HttpTransport createHttpTransport(DashboardAddressManager addressManager private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { return signRequest -> { + String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; Map headers = new HashMap<>(); - authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders())); + authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); return headers; }; } diff --git a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java index 5379c668429..ed54eef341e 100644 --- a/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java +++ b/huawei-cloud/dashboard/src/main/java/org/apache/servicecomb/huaweicloud/dashboard/monitor/MetricsMonitorDataProvider.java @@ -20,13 +20,13 @@ import java.util.List; import java.util.Map; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.dashboard.client.model.InterfaceInfo; import org.apache.servicecomb.dashboard.client.model.MonitorData; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.metrics.PolledEvent; import org.apache.servicecomb.huaweicloud.dashboard.monitor.data.MonitorConstant; import org.apache.servicecomb.huaweicloud.dashboard.monitor.model.MonitorDataProvider; -import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.metrics.core.publish.PublishModelFactory; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf; @@ -38,7 +38,8 @@ import org.springframework.core.env.Environment; import com.google.common.eventbus.Subscribe; -import com.netflix.spectator.api.Meter; + +import io.micrometer.core.instrument.Meter; /** * Monitor data based on metrics-core module. @@ -129,7 +130,7 @@ private void extractProviderInfo(DefaultPublishModel model, Map { @@ -139,16 +140,16 @@ private void extractProviderInfo(DefaultPublishModel model, Map { @@ -175,16 +176,17 @@ private void extractEdgeInfo(DefaultPublishModel model, Map { @@ -211,16 +213,17 @@ private void extractConsumerInfo(DefaultPublishModel model, Map org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default @@ -36,4 +36,4 @@ darklaunch - \ No newline at end of file + diff --git a/huawei-cloud/servicestage/pom.xml b/huawei-cloud/servicestage/pom.xml index 5b47e81ba68..d92064951f9 100644 --- a/huawei-cloud/servicestage/pom.xml +++ b/huawei-cloud/servicestage/pom.xml @@ -21,7 +21,7 @@ huawei-cloud org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 servicestage @@ -47,4 +47,4 @@ - \ No newline at end of file + diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/AKSKAuthHeaderProvider.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/AKSKAuthHeaderProvider.java index 3469df6a70e..e80e867cab4 100644 --- a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/AKSKAuthHeaderProvider.java +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/AKSKAuthHeaderProvider.java @@ -17,9 +17,9 @@ package org.apache.servicecomb.huaweicloud.servicestage; -import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -33,12 +33,13 @@ import org.apache.servicecomb.foundation.auth.Cipher; import org.apache.servicecomb.foundation.auth.DefaultCipher; import org.apache.servicecomb.foundation.auth.ShaAKSKCipher; +import org.apache.servicecomb.foundation.bootstrap.BootStrapService; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; -public class AKSKAuthHeaderProvider implements AuthHeaderProvider { +public class AKSKAuthHeaderProvider implements AuthHeaderProvider, BootStrapService { private static final Logger LOGGER = LoggerFactory.getLogger(AKSKAuthHeaderProvider.class); private static final String CONFIG_AKSK_ENABLED = "servicecomb.credentials.akskEnabled"; @@ -61,30 +62,25 @@ public class AKSKAuthHeaderProvider implements AuthHeaderProvider { private static final String X_SERVICE_PROJECT = "X-Service-Project"; - private final Map headers = new HashMap<>(); - - private boolean enabled; + private static Environment environment; - private final boolean loaded = false; + private final Map headers = new HashMap<>(); - private final Environment environment; + private boolean loaded = false; - public AKSKAuthHeaderProvider(Environment environment) { - this.environment = environment; - this.enabled = environment.getProperty(CONFIG_AKSK_ENABLED, boolean.class, true); + public AKSKAuthHeaderProvider() { } public Map authHeaders() { - if (!enabled) { - return headers; + if (!environment.getProperty(CONFIG_AKSK_ENABLED, boolean.class, true)) { + return Collections.emptyMap(); } if (StringUtils.isEmpty(getAccessKey())) { LOGGER.warn("ak sk auth enabled but access key is not configured, disable it at runtime. " + "Config [{}] to false to disable it implicitly.", CONFIG_AKSK_ENABLED); - enabled = false; - return headers; + return Collections.emptyMap(); } if (!loaded) { @@ -98,6 +94,7 @@ private synchronized void load() { headers.put(X_SERVICE_AK, getAccessKey()); headers.put(X_SERVICE_SHAAKSK, getSecretKey()); headers.put(X_SERVICE_PROJECT, getProject()); + loaded = true; } } @@ -126,11 +123,7 @@ private String getProject() { if (StringUtils.isEmpty(project)) { return project; } - try { - return URLEncoder.encode(project, "UTF-8"); - } catch (UnsupportedEncodingException e) { - return project; - } + return URLEncoder.encode(project, StandardCharsets.UTF_8); } private Cipher findCipher() { @@ -154,4 +147,9 @@ public static String sha256Encode(String key, String data) { throw new IllegalArgumentException("Can not encode ak sk. Please check the value is correct.", e); } } + + @Override + public void startup(Environment environment) { + AKSKAuthHeaderProvider.environment = environment; + } } diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/RBACBootStrapService.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/RBACBootStrapService.java new file mode 100644 index 00000000000..007e3e56c96 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/RBACBootStrapService.java @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.huaweicloud.servicestage; + +import static org.apache.servicecomb.foundation.ssl.SSLOption.DEFAULT_OPTION; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.servicecomb.foundation.auth.Cipher; +import org.apache.servicecomb.foundation.auth.DefaultCipher; +import org.apache.servicecomb.foundation.bootstrap.BootStrapService; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.foundation.ssl.SSLCustom; +import org.apache.servicecomb.foundation.ssl.SSLOption; +import org.apache.servicecomb.foundation.vertx.VertxConst; +import org.apache.servicecomb.http.client.auth.DefaultRequestAuthHeaderProvider; +import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; +import org.apache.servicecomb.http.client.common.HttpTransport; +import org.apache.servicecomb.http.client.common.HttpTransportFactory; +import org.apache.servicecomb.registry.sc.SCClientUtils; +import org.apache.servicecomb.service.center.client.ServiceCenterAddressManager; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.ServiceCenterRawClient; +import org.springframework.core.env.Environment; + +import com.google.common.annotations.VisibleForTesting; + +public class RBACBootStrapService implements BootStrapService { + private static final String RBAC_ADDRESS = "servicecomb.service.registry.address"; + + public static final String DEFAULT_REGISTRY_NAME = "default"; + + public static final String RBAC_ENABLED = "servicecomb.credentials.rbac.enabled"; + + public static final String ACCOUNT_NAME_KEY = "servicecomb.credentials.account.name"; + + public static final String PASSWORD_KEY = "servicecomb.credentials.account.password"; + + public static final String CIPHER_KEY = "servicecomb.credentials.cipher"; + + public static final String PROJECT_KEY = "servicecomb.credentials.project"; + + private static final String SSL_TAG = "sc.consumer"; + + @Override + public void startup(Environment environment) { + if (!getBooleanProperty(environment, false, RBAC_ENABLED)) { + return; + } + + ServiceCenterAddressManager addressManager = createAddressManager(environment); + addressManager.setEventBus(EventManager.getEventBus()); + SSLProperties sslProperties = createSSLProperties(environment); + sslProperties.setEnabled(addressManager.sslEnabled()); + + ServiceCenterClient serviceCenterClient = + new ServiceCenterClient(new ServiceCenterRawClient.Builder() + .setTenantName("default") + .setAddressManager(addressManager) + .setHttpTransport(createHttpTransport(environment, sslProperties)).build(), addressManager); + + Map clients = new HashMap<>(1); + clients.put(DEFAULT_REGISTRY_NAME, serviceCenterClient); + TokenCacheManager.getInstance().setServiceCenterClients(clients); + TokenCacheManager.getInstance().addTokenCache( + DEFAULT_REGISTRY_NAME, + getStringProperty(environment, null, ACCOUNT_NAME_KEY), + getStringProperty(environment, null, PASSWORD_KEY), + getCipher(getStringProperty(environment, DefaultCipher.CIPHER_NAME, CIPHER_KEY))); + } + + private static HttpTransport createHttpTransport(Environment environment, SSLProperties sslProperties) { + if (isProxyEnable(environment)) { + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). + setDefaultRequestConfig(HttpTransportFactory.defaultRequestConfig().build()); + HttpHost proxy = new HttpHost(getProxyHost(environment), + getProxyPort(environment), "http"); // now only support http proxy + httpClientBuilder.setProxy(proxy); + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(new AuthScope(proxy), + new UsernamePasswordCredentials(getProxyUsername(environment), + getProxyPasswd(environment))); + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + + return HttpTransportFactory + .createHttpTransport(sslProperties, + new DefaultRequestAuthHeaderProvider(), httpClientBuilder); + } + + return HttpTransportFactory + .createHttpTransport(sslProperties, + new DefaultRequestAuthHeaderProvider(), HttpTransportFactory.defaultRequestConfig().build()); + } + + @VisibleForTesting + Cipher getCipher(String cipherName) { + if (DefaultCipher.CIPHER_NAME.equals(cipherName)) { + return DefaultCipher.getInstance(); + } + + List ciphers = SPIServiceUtils.getOrLoadSortedService(Cipher.class); + return ciphers.stream().filter(c -> c.name().equals(cipherName)).findFirst() + .orElseThrow(() -> new IllegalArgumentException("failed to find cipher named " + cipherName)); + } + + private ServiceCenterAddressManager createAddressManager(Environment environment) { + return SCClientUtils.createAddressManager(getProjectName(environment), getRBACAddressList(environment), + environment); + } + + private SSLProperties createSSLProperties(Environment environment) { + SSLProperties sslProperties = new SSLProperties(); + + SSLOption option = new SSLOption(); + option.setEngine(getStringProperty(environment, + DEFAULT_OPTION.getEngine(), + "ssl." + SSL_TAG + ".engine", + "ssl.engine")); + option.setProtocols( + getStringProperty(environment, + DEFAULT_OPTION.getProtocols(), + "ssl." + SSL_TAG + ".protocols", + "ssl.protocols")); + option.setCiphers( + getStringProperty(environment, DEFAULT_OPTION.getCiphers(), "ssl." + SSL_TAG + ".ciphers", "ssl.ciphers")); + option.setAuthPeer( + getBooleanProperty(environment, DEFAULT_OPTION.isAuthPeer(), "ssl." + SSL_TAG + ".authPeer", "ssl.authPeer")); + option.setCheckCNHost( + getBooleanProperty(environment, + DEFAULT_OPTION.isCheckCNHost(), + "ssl." + SSL_TAG + ".checkCN.host", + "ssl.checkCN.host")); + option.setCheckCNWhite( + getBooleanProperty(environment, + DEFAULT_OPTION.isCheckCNWhite(), + "ssl." + SSL_TAG + ".checkCN.white", + "ssl.checkCN.white")); + option.setCheckCNWhiteFile(getStringProperty(environment, + DEFAULT_OPTION.getCiphers(), + "ssl." + SSL_TAG + ".checkCN.white.file", + "ssl.checkCN.white.file")); + option.setAllowRenegotiate(getBooleanProperty(environment, + DEFAULT_OPTION.isAllowRenegotiate(), + "ssl." + SSL_TAG + ".allowRenegotiate", + "ssl.allowRenegotiate")); + option.setStorePath( + getStringProperty(environment, + DEFAULT_OPTION.getStorePath(), + "ssl." + SSL_TAG + ".storePath", + "ssl.storePath")); + option.setClientAuth( + getStringProperty(environment, + DEFAULT_OPTION.getClientAuth(), + "ssl." + SSL_TAG + ".clientAuth", + "ssl.clientAuth")); + option.setTrustStore( + getStringProperty(environment, + DEFAULT_OPTION.getTrustStore(), + "ssl." + SSL_TAG + ".trustStore", + "ssl.trustStore")); + option.setTrustStoreType(getStringProperty(environment, + DEFAULT_OPTION.getTrustStoreType(), + "ssl." + SSL_TAG + ".trustStoreType", + "ssl.trustStoreType")); + option.setTrustStoreValue(getStringProperty(environment, + DEFAULT_OPTION.getTrustStoreValue(), + "ssl." + SSL_TAG + ".trustStoreValue", + "ssl.trustStoreValue")); + option.setKeyStore( + getStringProperty(environment, DEFAULT_OPTION.getKeyStore(), "ssl." + SSL_TAG + ".keyStore", "ssl.keyStore")); + option.setKeyStoreType( + getStringProperty(environment, + DEFAULT_OPTION.getKeyStoreType(), + "ssl." + SSL_TAG + ".keyStoreType", + "ssl.keyStoreType")); + option.setKeyStoreValue(getStringProperty(environment, + DEFAULT_OPTION.getKeyStoreValue(), + "ssl." + SSL_TAG + ".keyStoreValue", + "ssl.keyStoreValue")); + option.setCrl(getStringProperty(environment, DEFAULT_OPTION.getCrl(), "ssl." + SSL_TAG + ".crl", "ssl.crl")); + option.setSslCustomClass( + getStringProperty(environment, null, "ssl." + SSL_TAG + ".sslCustomClass", "ssl.sslCustomClass")); + + sslProperties.setSslOption(option); + sslProperties.setSslCustom(SSLCustom.createSSLCustom(option.getSslCustomClass())); + return sslProperties; + } + + private String getStringProperty(Environment environment, String defaultValue, String... keys) { + for (String key : keys) { + if (environment.getProperty(key) != null) { + return environment.getProperty(key); + } + } + return defaultValue; + } + + private boolean getBooleanProperty(Environment environment, boolean defaultValue, String... keys) { + for (String key : keys) { + if (environment.getProperty(key) != null) { + return Boolean.parseBoolean(environment.getProperty(key)); + } + } + return defaultValue; + } + + private String getProjectName(Environment environment) { + return getStringProperty(environment, "default", PROJECT_KEY); + } + + private List getRBACAddressList(Environment environment) { + String address = environment.getProperty(RBAC_ADDRESS, "http://127.0.0.1:30100"); + return Arrays.asList(address.split(",")); + } + + + public static Boolean isProxyEnable(Environment environment) { + return environment.getProperty(VertxConst.PROXY_ENABLE, boolean.class, false); + } + + public static String getProxyHost(Environment environment) { + return environment.getProperty(VertxConst.PROXY_HOST, "127.0.0.1"); + } + + public static int getProxyPort(Environment environment) { + return environment.getProperty(VertxConst.PROXY_PORT, int.class, 8080); + } + + public static String getProxyUsername(Environment environment) { + return environment.getProperty(VertxConst.PROXY_USERNAME); + } + + public static String getProxyPasswd(Environment environment) { + return environment.getProperty(VertxConst.PROXY_PASSWD); + } +} diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/ServiceStageConfiguration.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/ServiceStageConfiguration.java index 543507aa2c0..5a369b70bf9 100644 --- a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/ServiceStageConfiguration.java +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/ServiceStageConfiguration.java @@ -18,15 +18,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; @Configuration public class ServiceStageConfiguration { - @Bean - public AKSKAuthHeaderProvider akskAuthHeaderProvider(Environment environment) { - return new AKSKAuthHeaderProvider(environment); - } - @Bean public CasEnvVariablesAdapter casEnvVariablesAdapter() { return new CasEnvVariablesAdapter(); diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenAuthHeaderProvider.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenAuthHeaderProvider.java new file mode 100644 index 00000000000..19bd550602b --- /dev/null +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenAuthHeaderProvider.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.huaweicloud.servicestage; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; + +public class TokenAuthHeaderProvider implements AuthHeaderProvider { + @Override + public Map authHeaders(String host) { + String token = TokenCacheManager.getInstance().getToken(host); + if (StringUtils.isEmpty(token)) { + return new HashMap<>(); + } + + HashMap header = new HashMap<>(); + header.put("Authorization", "Bearer " + token); + return Collections.unmodifiableMap(header); + } +} diff --git a/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenCacheManager.java b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenCacheManager.java new file mode 100644 index 00000000000..b4510bb4238 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/java/org/apache/servicecomb/huaweicloud/servicestage/TokenCacheManager.java @@ -0,0 +1,206 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.huaweicloud.servicestage; + +import java.net.URI; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.foundation.auth.Cipher; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.http.client.event.OperationEvents; +import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.model.RbacTokenRequest; +import org.apache.servicecomb.service.center.client.model.RbacTokenResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.eventbus.Subscribe; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import jakarta.ws.rs.core.Response.Status; + +public final class TokenCacheManager { + private static final Logger LOGGER = LoggerFactory.getLogger(TokenCacheManager.class); + + // special token used for special conditions + // e.g. un-authorized: will query token after token expired period + // e.g. not found: will query token after token expired period + public static final String INVALID_TOKEN = ""; + + private static final TokenCacheManager INSTANCE = new TokenCacheManager(); + + private Map serviceCenterClients; + + private TokenCache tokenCache; + + public static TokenCacheManager getInstance() { + return INSTANCE; + } + + private TokenCacheManager() { + } + + public void setServiceCenterClients(Map serviceCenterClients) { + this.serviceCenterClients = serviceCenterClients; + } + + public void addTokenCache(String registryName, String accountName, String password, Cipher cipher) { + tokenCache = new TokenCache(registryName, accountName, password, cipher); + } + + public String getToken(String host) { + if (tokenCache == null) { + return null; + } + return tokenCache.getToken(host); + } + + public class TokenCache { + private static final long TOKEN_REFRESH_TIME_IN_SECONDS = 20 * 60 * 1000; + + private final String registryName; + + private final String accountName; + + private final String password; + + private ExecutorService executorService; + + private LoadingCache cache; + + private final Cipher cipher; + + public TokenCache(String registryName, String accountName, String password, + Cipher cipher) { + this.registryName = registryName; + this.accountName = accountName; + this.password = password; + this.cipher = cipher; + + if (enabled()) { + executorService = Executors.newFixedThreadPool(1, t -> new Thread(t, "rbac-executor-" + this.registryName) { + @Override + public void run() { + try { + super.run(); + } catch (Throwable e) { + LOGGER.error("", e); + } + } + }); + cache = CacheBuilder.newBuilder() + .maximumSize(10) + .refreshAfterWrite(refreshTime(), TimeUnit.MILLISECONDS) + .build(new CacheLoader() { + @Override + public String load(String key) throws Exception { + return createHeaders(key); + } + + @Override + public ListenableFuture reload(String key, String oldValue) throws Exception { + return Futures.submit(() -> createHeaders(key), executorService); + } + }); + EventManager.getEventBus().register(this); + } + } + + @Subscribe + public void onNotPermittedEvent(OperationEvents.UnAuthorizedOperationEvent event) { + LOGGER.warn("address {} unAuthorized, refresh cache token!", event.getAddress()); + cache.refresh(getHostByAddress(event.getAddress())); + } + + private String getHostByAddress(String address) { + try { + URI uri = URI.create(address); + return uri.getHost(); + } catch (Exception e) { + LOGGER.error("get host by address [{}] error!", address, e); + return registryName; + } + } + + private String createHeaders(String host) { + LOGGER.info("start to create RBAC headers for host: {}", host); + + ServiceCenterClient serviceCenterClient = serviceCenterClients.get(this.registryName); + + RbacTokenRequest request = new RbacTokenRequest(); + request.setName(accountName); + request.setPassword(new String(cipher.decrypt(password.toCharArray()))); + + RbacTokenResponse rbacTokenResponse = serviceCenterClient.queryToken(request, host); + + if (Status.UNAUTHORIZED.getStatusCode() == rbacTokenResponse.getStatusCode() + || Status.FORBIDDEN.getStatusCode() == rbacTokenResponse.getStatusCode()) { + // password wrong, do not try anymore + LOGGER.warn("username or password may be wrong, stop trying to query tokens."); + return INVALID_TOKEN; + } + if (Status.NOT_FOUND.getStatusCode() == rbacTokenResponse.getStatusCode()) { + // service center not support, do not try + LOGGER.warn("service center do not support RBAC token, you should not config account info"); + return INVALID_TOKEN; + } + if (Status.INTERNAL_SERVER_ERROR.getStatusCode() == rbacTokenResponse.getStatusCode()) { + // return null for server_error, so the token information can be re-fetched on the next call. + // It will prompt 'CacheLoader returned null for key xxx' + LOGGER.warn("service center query RBAC token error!"); + return null; + } + + LOGGER.info("refresh host [{}] token successfully {}", host, rbacTokenResponse.getStatusCode()); + return rbacTokenResponse.getToken(); + } + + protected long refreshTime() { + return TOKEN_REFRESH_TIME_IN_SECONDS; + } + + public String getToken(String host) { + if (!enabled()) { + return null; + } + String address = host; + if (StringUtils.isEmpty(address)) { + address = registryName; + } + try { + return cache.get(address); + } catch (Exception e) { + LOGGER.error("failed to create token", e); + return null; + } + } + + private boolean enabled() { + return !StringUtils.isEmpty(this.accountName) + && !StringUtils.isEmpty(this.password); + } + } +} diff --git a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider new file mode 100644 index 00000000000..a78d2c2e33b --- /dev/null +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.AuthHeaderProvider @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.apache.servicecomb.huaweicloud.servicestage.AKSKAuthHeaderProvider +org.apache.servicecomb.huaweicloud.servicestage.TokenAuthHeaderProvider diff --git a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.Cipher b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.Cipher index e242e7a854a..8d0efa25aef 100644 --- a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.Cipher +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.auth.Cipher @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.foundation.auth.ShaAKSKCipher \ No newline at end of file +org.apache.servicecomb.foundation.auth.ShaAKSKCipher diff --git a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService new file mode 100644 index 00000000000..90c339ba065 --- /dev/null +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.bootstrap.BootStrapService @@ -0,0 +1,19 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.apache.servicecomb.huaweicloud.servicestage.RBACBootStrapService +org.apache.servicecomb.huaweicloud.servicestage.AKSKAuthHeaderProvider diff --git a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.adapter.EnvAdapter b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.adapter.EnvAdapter index a7eb97b3bc3..2aa5b486121 100644 --- a/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.adapter.EnvAdapter +++ b/huawei-cloud/servicestage/src/main/resources/META-INF/services/org.apache.servicecomb.serviceregistry.adapter.EnvAdapter @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.huaweicloud.servicestage.CasEnvVariablesAdapter \ No newline at end of file +org.apache.servicecomb.huaweicloud.servicestage.CasEnvVariablesAdapter diff --git a/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestAKSKAuthHeaderProvider.java b/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestAKSKAuthHeaderProvider.java index 2495ef64db2..2b82672c77c 100644 --- a/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestAKSKAuthHeaderProvider.java +++ b/huawei-cloud/servicestage/src/test/java/org/apache/servicecomb/huaweicloud/servicestage/TestAKSKAuthHeaderProvider.java @@ -37,7 +37,8 @@ public void test_project_name_properly_encoded_en() { Mockito.when(environment.getProperty("servicecomb.credentials.akskCustomCipher", "default")).thenReturn("default"); - AKSKAuthHeaderProvider provider = new AKSKAuthHeaderProvider(environment); + AKSKAuthHeaderProvider provider = new AKSKAuthHeaderProvider(); + provider.startup(environment); Assertions.assertEquals("hello", provider.authHeaders().get("X-Service-Project")); } @@ -54,7 +55,8 @@ public void test_project_name_properly_encoded_cn() { "")).thenReturn("secret key"); Mockito.when(environment.getProperty("servicecomb.credentials.akskCustomCipher", "default")).thenReturn("default"); - AKSKAuthHeaderProvider provider = new AKSKAuthHeaderProvider(environment); + AKSKAuthHeaderProvider provider = new AKSKAuthHeaderProvider(); + provider.startup(environment); Assertions.assertEquals("%E6%B5%8B%E8%AF%95", provider.authHeaders().get("X-Service-Project")); } } diff --git a/metrics/metrics-core/pom.xml b/metrics/metrics-core/pom.xml index 9d467b727fd..c68b483fde9 100644 --- a/metrics/metrics-core/pom.xml +++ b/metrics/metrics-core/pom.xml @@ -21,7 +21,7 @@ metrics org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -60,13 +60,8 @@ provided - org.apache.logging.log4j - log4j-slf4j-impl - test - - - org.apache.logging.log4j - log4j-core + org.mockito + mockito-inline test diff --git a/metrics/metrics-core/src/main/java/com/netflix/spectator/api/patterns/ThreadPoolMonitorPublishModelFactory.java b/metrics/metrics-core/src/main/java/com/netflix/spectator/api/patterns/ThreadPoolMonitorPublishModelFactory.java deleted file mode 100644 index 3b8ba946f37..00000000000 --- a/metrics/metrics-core/src/main/java/com/netflix/spectator/api/patterns/ThreadPoolMonitorPublishModelFactory.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.netflix.spectator.api.patterns; - -import java.util.Map; - -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree; -import org.apache.servicecomb.metrics.core.ThreadPoolMetersInitializer; -import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; - -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Utils; - -public class ThreadPoolMonitorPublishModelFactory { - interface Setter { - void set(ThreadPoolPublishModel model, Measurement measurement); - } - - private final MeasurementTree tree; - - private final Map threadPools; - - public ThreadPoolMonitorPublishModelFactory(MeasurementTree tree, - Map threadPools) { - this.tree = tree; - this.threadPools = threadPools; - } - - public static void create(MeasurementTree tree, - Map threadPools) { - new ThreadPoolMonitorPublishModelFactory(tree, threadPools).create(); - } - - public void create() { - readMeasurement(ThreadPoolMonitor.TASK_COUNT, - (model, measurement) -> model.setAvgTaskCount(measurement.value())); - readMeasurement(ThreadPoolMonitor.COMPLETED_TASK_COUNT, - (model, measurement) -> model.setAvgCompletedTaskCount(measurement.value())); - readMeasurement(ThreadPoolMonitor.CURRENT_THREADS_BUSY, - (model, measurement) -> model.setCurrentThreadsBusy((int) measurement.value())); - readMeasurement(ThreadPoolMonitor.MAX_THREADS, - (model, measurement) -> model.setMaxThreads((int) measurement.value())); - readMeasurement(ThreadPoolMonitor.POOL_SIZE, - (model, measurement) -> model.setPoolSize((int) measurement.value())); - readMeasurement(ThreadPoolMonitor.CORE_POOL_SIZE, - (model, measurement) -> model.setCorePoolSize((int) measurement.value())); - readMeasurement(ThreadPoolMonitor.QUEUE_SIZE, - (model, measurement) -> model.setQueueSize((int) measurement.value())); - readMeasurement(ThreadPoolMetersInitializer.REJECTED_COUNT, - (model, measurement) -> model.setRejected(measurement.value())); - } - - protected void readMeasurement(String name, Setter setter) { - MeasurementNode node = tree.findChild(name); - if (node == null) { - return; - } - - for (Measurement measurement : node.getMeasurements()) { - String threadPoolName = Utils.getTagValue(measurement.id(), ThreadPoolMonitor.ID_TAG_NAME); - if (threadPoolName == null) { - continue; - } - - ThreadPoolPublishModel model = threadPools.computeIfAbsent(threadPoolName, tpn -> new ThreadPoolPublishModel()); - setter.set(model, measurement); - } - } -} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/DefaultRegistryInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/DefaultRegistryInitializer.java deleted file mode 100644 index d48898c6b98..00000000000 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/DefaultRegistryInitializer.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.metrics.core; - -import java.time.Duration; - -import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import org.apache.servicecomb.foundation.metrics.MetricsInitializer; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; - -import com.google.common.eventbus.EventBus; -import com.netflix.servo.DefaultMonitorRegistry; - -public class DefaultRegistryInitializer implements MetricsInitializer { - public static final String SERVO_POLLERS = "servo.pollers"; - - private GlobalRegistry globalRegistry; - - @SuppressWarnings("deprecation") - private com.netflix.spectator.servo.ServoRegistry registry; - - // create registry before init meters - @Override - public int getOrder() { - return -10; - } - - @Override - @SuppressWarnings("deprecation") - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { - this.globalRegistry = globalRegistry; - - // spectator move poll gauges from inline to background executor - // we need to set the interval to unify value - System.setProperty("spectator.api.gaugePollingFrequency", Duration.ofMillis(config.getMsPollInterval()).toString()); - - System.setProperty(SERVO_POLLERS, String.valueOf(config.getMsPollInterval())); - registry = new com.netflix.spectator.servo.ServoRegistry(); - globalRegistry.add(registry); - } - - @Override - public void destroy() { - if (registry != null) { - DefaultMonitorRegistry.getInstance().unregister(registry); - globalRegistry.remove(registry); - } - } -} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/InvocationMetersInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/InvocationMetersInitializer.java index 33912e98785..25bc9b62e71 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/InvocationMetersInitializer.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/InvocationMetersInitializer.java @@ -21,7 +21,6 @@ import org.apache.servicecomb.core.event.InvocationStartEvent; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; import org.apache.servicecomb.metrics.core.meter.ConsumerMeters; import org.apache.servicecomb.metrics.core.meter.EdgeMeters; import org.apache.servicecomb.metrics.core.meter.ProducerMeters; @@ -30,7 +29,8 @@ import com.google.common.eventbus.AllowConcurrentEvents; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; -import com.netflix.spectator.api.Registry; + +import io.micrometer.core.instrument.MeterRegistry; public class InvocationMetersInitializer implements MetricsInitializer { private ConsumerMeters consumerMeters; @@ -40,23 +40,20 @@ public class InvocationMetersInitializer implements MetricsInitializer { private EdgeMeters edgeMeters; @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { - Registry registry = globalRegistry.getDefaultRegistry(); - - consumerMeters = new ConsumerMeters(registry, config); - producerMeters = new ProducerMeters(registry, config); - edgeMeters = new EdgeMeters(registry, config); + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + consumerMeters = new ConsumerMeters(meterRegistry, config); + producerMeters = new ProducerMeters(meterRegistry, config); + edgeMeters = new EdgeMeters(meterRegistry, config); eventBus.register(this); } protected AbstractInvocationMeters findInvocationMeters(Invocation invocation) { if (invocation.isConsumer()) { - if (invocation.isEdge()) { - return edgeMeters.getInvocationMeters(); - } else { - return consumerMeters.getInvocationMeters(); - } + return consumerMeters.getInvocationMeters(); + } + if (invocation.isEdge()) { + return edgeMeters.getInvocationMeters(); } return producerMeters.getInvocationMeters(); } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java index 540b10ed963..eb9e4347544 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsBootListener.java @@ -19,15 +19,10 @@ import org.apache.servicecomb.core.BootListener; import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.metrics.MetricsBootstrap; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; public class MetricsBootListener implements BootListener { private final MetricsBootstrap metricsBootstrap; - public MetricsBootstrap getMetricsBootstrap() { - return metricsBootstrap; - } - public MetricsBootListener(MetricsBootstrap metricsBootstrap) { this.metricsBootstrap = metricsBootstrap; } @@ -37,9 +32,13 @@ public void onBeforeProducerProvider(BootEvent event) { } + public MetricsBootstrap getMetricsBootstrap() { + return metricsBootstrap; + } + @Override public void onAfterRegistry(BootEvent event) { - metricsBootstrap.start(new GlobalRegistry(), EventManager.getEventBus()); + metricsBootstrap.start(EventManager.getEventBus()); } @Override diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsCoreConfiguration.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsCoreConfiguration.java index 91d6ea2a526..338b2183d02 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsCoreConfiguration.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/MetricsCoreConfiguration.java @@ -16,23 +16,51 @@ */ package org.apache.servicecomb.metrics.core; +import java.time.Duration; + import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.foundation.metrics.MetricsBootstrap; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher; import org.apache.servicecomb.metrics.core.publish.SlowInvocationLogger; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.CountingMode; @Configuration public class MetricsCoreConfiguration { + @Bean + public MetricsBootstrapConfig metricsBootstrapConfig(Environment environment) { + return new MetricsBootstrapConfig(environment); + } + + @Bean + @ConditionalOnMissingBean + public MeterRegistry meterRegistry(MetricsBootstrapConfig config) { + return new SimpleMeterRegistryExt(s -> { + if ("simple.step".equals(s)) { + return Duration.ofMillis(config.getMsPollInterval()).toString(); + } + if ("simple.mode".equals(s)) { + return CountingMode.STEP.name(); + } + return null; + }, Clock.SYSTEM); + } + @Bean public MetricsBootListener metricsBootListener(MetricsBootstrap metricsBootstrap) { return new MetricsBootListener(metricsBootstrap); } @Bean - public MetricsBootstrap metricsBootstrap() { - return new MetricsBootstrap(); + public MetricsBootstrap metricsBootstrap(MetricsBootstrapConfig config) { + return new MetricsBootstrap(config); } // Begin MetricsInitializers @@ -42,11 +70,6 @@ public DefaultLogPublisher defaultLogPublisher() { return new DefaultLogPublisher(); } - @Bean - public DefaultRegistryInitializer defaultRegistryInitializer() { - return new DefaultRegistryInitializer(); - } - @Bean public InvocationMetersInitializer invocationMetersInitializer() { return new InvocationMetersInitializer(); diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/OsMetersInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/OsMetersInitializer.java index 88f01393fa6..f5486f78089 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/OsMetersInitializer.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/OsMetersInitializer.java @@ -16,45 +16,31 @@ */ package org.apache.servicecomb.metrics.core; -import org.apache.commons.lang3.SystemUtils; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.metrics.core.meter.os.OsMeter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.google.common.annotations.VisibleForTesting; import com.google.common.eventbus.EventBus; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.SpectatorUtils; -public class OsMetersInitializer implements MetricsInitializer { - private static final Logger LOGGER = LoggerFactory.getLogger(OsMetersInitializer.class); +import io.micrometer.core.instrument.MeterRegistry; +public class OsMetersInitializer implements MetricsInitializer, PeriodMeter { private OsMeter osMeter; - private boolean isOsLinux = SystemUtils.IS_OS_LINUX; - - @VisibleForTesting - OsMetersInitializer setOsLinux(boolean osLinux) { - isOsLinux = osLinux; - return this; - } - @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { - if (!isOsLinux) { - LOGGER.info("only support linux os to collect cpu and net info"); - return; - } - - Registry registry = globalRegistry.getDefaultRegistry(); - osMeter = new OsMeter(registry); - SpectatorUtils.registerMeter(registry, osMeter); + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + osMeter = new OsMeter(meterRegistry); } public OsMeter getOsMeter() { return osMeter; } + + @Override + public void poll(long msNow, long secondInterval) { + if (osMeter != null) { + osMeter.poll(msNow, secondInterval); + } + } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SimpleMeterRegistryExt.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SimpleMeterRegistryExt.java new file mode 100644 index 00000000000..592ec2af113 --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/SimpleMeterRegistryExt.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.cumulative.CumulativeDistributionSummary; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.core.instrument.distribution.HistogramGauges; +import io.micrometer.core.instrument.distribution.StepBucketHistogram; +import io.micrometer.core.instrument.simple.SimpleConfig; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +/** + * Custom SimpleMeterRegistry to support StepDistributionSummary + */ +public class SimpleMeterRegistryExt extends SimpleMeterRegistry { + private final SimpleConfig config; + + public SimpleMeterRegistryExt(SimpleConfig config, Clock clock) { + super(config, clock); + this.config = config; + } + + @Override + protected DistributionSummary newDistributionSummary(Meter.Id id, + DistributionStatisticConfig distributionStatisticConfig, double scale) { + DistributionStatisticConfig merged = distributionStatisticConfig + .merge(DistributionStatisticConfig.builder().expiry(config.step()).build()); + + DistributionSummary summary; + switch (config.mode()) { + case CUMULATIVE: + summary = new CumulativeDistributionSummary(id, clock, merged, scale, false); + break; + case STEP: + default: + summary = new StepDistributionSummaryExt(id, clock, merged, scale, config.step().toMillis(), + new StepBucketHistogram(clock, config.step().toMillis(), distributionStatisticConfig, + false, false)); + break; + } + + HistogramGauges.registerWithCommonFormat(summary, this); + + return summary; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/StepDistributionSummaryExt.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/StepDistributionSummaryExt.java new file mode 100644 index 00000000000..519f903a76e --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/StepDistributionSummaryExt.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core; + +import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; +import io.micrometer.core.instrument.distribution.Histogram; +import io.micrometer.core.instrument.step.StepDistributionSummary; + +public class StepDistributionSummaryExt extends StepDistributionSummary { + public StepDistributionSummaryExt(Id id, Clock clock, + DistributionStatisticConfig distributionStatisticConfig, double scale, long stepMillis, + Histogram histogram) { + super(id, clock, distributionStatisticConfig, scale, stepMillis, histogram); + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java index ad01233911b..9ada122585b 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/ThreadPoolMetersInitializer.java @@ -16,6 +16,7 @@ */ package org.apache.servicecomb.metrics.core; +import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; import java.util.Map.Entry; @@ -26,27 +27,24 @@ import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.executor.GroupExecutor; -import org.apache.servicecomb.core.executor.ThreadPoolExecutorEx; import org.apache.servicecomb.foundation.common.utils.BeanUtils; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; +import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; import com.google.common.eventbus.EventBus; -import com.netflix.spectator.api.BasicTag; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.Tag; -import com.netflix.spectator.api.patterns.PolledMeter; -import com.netflix.spectator.api.patterns.ThreadPoolMonitor; -public class ThreadPoolMetersInitializer implements MetricsInitializer { - public static String REJECTED_COUNT = "threadpool.rejectedCount"; +import io.micrometer.core.instrument.MeterRegistry; - private Registry registry; +public class ThreadPoolMetersInitializer implements MetricsInitializer, PeriodMeter { + private MeterRegistry meterRegistry; + + private Map threadPoolMeters = new HashMap<>(); @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { - registry = globalRegistry.getDefaultRegistry(); + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + this.meterRegistry = meterRegistry; createThreadPoolMeters(); } @@ -62,7 +60,7 @@ public void createThreadPoolMeters() { continue; } - if (GroupExecutor.class.isInstance(executor)) { + if (executor instanceof GroupExecutor) { createThreadPoolMeters(entry.getKey(), (GroupExecutor) executor); continue; } @@ -88,20 +86,17 @@ protected void createThreadPoolMeters(String threadPoolName, GroupExecutor group } } - protected void createThreadPoolMeters(String threadPoolName, Executor executor) { - if (!ThreadPoolExecutor.class.isInstance(executor)) { + public void createThreadPoolMeters(String threadPoolName, Executor executor) { + if (!(executor instanceof ThreadPoolExecutor threadPoolExecutor)) { return; } - ThreadPoolMonitor.attach(registry, (ThreadPoolExecutor) executor, threadPoolName); - - if (executor instanceof ThreadPoolExecutorEx) { - Tag idTag = new BasicTag("id", threadPoolName); + threadPoolMeters.computeIfAbsent(threadPoolName, + (key) -> new ThreadPoolMeter(meterRegistry, threadPoolName, threadPoolExecutor)); + } - PolledMeter.using(registry) - .withName(REJECTED_COUNT) - .withTag(idTag) - .monitorMonotonicCounter((ThreadPoolExecutorEx) executor, ThreadPoolExecutorEx::getRejectedCount); - } + @Override + public void poll(long msNow, long secondInterval) { + threadPoolMeters.forEach((key, value) -> value.poll(msNow, secondInterval)); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java index dd70b38730e..8240fabd4f2 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/VertxMetersInitializer.java @@ -18,44 +18,47 @@ import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.foundation.vertx.SharedVertxFactory; import org.apache.servicecomb.metrics.core.meter.vertx.HttpClientEndpointsMeter; import org.apache.servicecomb.metrics.core.meter.vertx.ServerEndpointsMeter; -import org.apache.servicecomb.metrics.core.meter.vertx.VertxEndpointsMeter; import com.google.common.eventbus.EventBus; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.SpectatorUtils; -public class VertxMetersInitializer implements MetricsInitializer { +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public class VertxMetersInitializer implements MetricsInitializer, PeriodMeter { public static final String VERTX_ENDPOINTS = "servicecomb.vertx.endpoints"; public static final String ENDPOINTS_TYPE = "type"; - public static final String ENDPOINTS_CLINET = "client"; + public static final String ENDPOINTS_CLIENT = "client"; public static final String ENDPOINTS_SERVER = "server"; - @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { - Registry registry = globalRegistry.getDefaultRegistry(); + private HttpClientEndpointsMeter httpClientEndpointsMeter; - Id endpointsId = registry.createId(VERTX_ENDPOINTS); - VertxEndpointsMeter clientMeter = new HttpClientEndpointsMeter( - endpointsId.withTag(ENDPOINTS_TYPE, ENDPOINTS_CLINET), + private ServerEndpointsMeter serverEndpointsMeter; + + @Override + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + httpClientEndpointsMeter = new HttpClientEndpointsMeter(meterRegistry, VERTX_ENDPOINTS, + Tags.of(ENDPOINTS_TYPE, ENDPOINTS_CLIENT), SharedVertxFactory.getMetricsFactory(config.getEnvironment()) .getVertxMetrics() - .getClientEndpointMetricManager() - .getClientEndpointMetricMap()); - SpectatorUtils.registerMeter(registry, clientMeter); + .getClientEndpointMetricManager()); - VertxEndpointsMeter serverMeter = new ServerEndpointsMeter( - endpointsId.withTag(ENDPOINTS_TYPE, ENDPOINTS_SERVER), + serverEndpointsMeter = new ServerEndpointsMeter(meterRegistry, VERTX_ENDPOINTS, + Tags.of(ENDPOINTS_TYPE, ENDPOINTS_SERVER), SharedVertxFactory.getMetricsFactory(config.getEnvironment()) .getVertxMetrics() .getServerEndpointMetricMap()); - SpectatorUtils.registerMeter(registry, serverMeter); + } + + @Override + public void poll(long msNow, long secondInterval) { + httpClientEndpointsMeter.poll(msNow, secondInterval); + serverEndpointsMeter.poll(msNow, secondInterval); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.java index bea132203b9..ccd7f49f6a3 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ConsumerMeters.java @@ -20,13 +20,13 @@ import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; import org.apache.servicecomb.metrics.core.meter.invocation.ConsumerInvocationMeters; -import com.netflix.spectator.api.Registry; +import io.micrometer.core.instrument.MeterRegistry; public class ConsumerMeters { private final AbstractInvocationMeters invocationMeters; - public ConsumerMeters(Registry registry, MetricsBootstrapConfig metricsBootstrapConfig) { - invocationMeters = new ConsumerInvocationMeters(registry, metricsBootstrapConfig); + public ConsumerMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + invocationMeters = new ConsumerInvocationMeters(meterRegistry, metricsBootstrapConfig); } public AbstractInvocationMeters getInvocationMeters() { diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/EdgeMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/EdgeMeters.java index 6da929165f3..b81403e89a3 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/EdgeMeters.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/EdgeMeters.java @@ -21,14 +21,14 @@ import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; import org.apache.servicecomb.metrics.core.meter.invocation.EdgeInvocationMeters; -import com.netflix.spectator.api.Registry; +import io.micrometer.core.instrument.MeterRegistry; public class EdgeMeters { private final AbstractInvocationMeters invocationMeters; - public EdgeMeters(Registry registry, MetricsBootstrapConfig metricsBootstrapConfig) { - this.invocationMeters = new EdgeInvocationMeters(registry, metricsBootstrapConfig); + public EdgeMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + this.invocationMeters = new EdgeInvocationMeters(meterRegistry, metricsBootstrapConfig); } public AbstractInvocationMeters getInvocationMeters() { diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.java index abe41586421..1822b940f06 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ProducerMeters.java @@ -20,13 +20,13 @@ import org.apache.servicecomb.metrics.core.meter.invocation.AbstractInvocationMeters; import org.apache.servicecomb.metrics.core.meter.invocation.ProducerInvocationMeters; -import com.netflix.spectator.api.Registry; +import io.micrometer.core.instrument.MeterRegistry; public class ProducerMeters { private final AbstractInvocationMeters invocationMeters; - public ProducerMeters(Registry registry, MetricsBootstrapConfig metricsBootstrapConfig) { - invocationMeters = new ProducerInvocationMeters(registry, metricsBootstrapConfig); + public ProducerMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + invocationMeters = new ProducerInvocationMeters(meterRegistry, metricsBootstrapConfig); } public AbstractInvocationMeters getInvocationMeters() { diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ThreadPoolMonitorPublishModelFactory.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ThreadPoolMonitorPublishModelFactory.java new file mode 100644 index 00000000000..d0474142cca --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/ThreadPoolMonitorPublishModelFactory.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.meter; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; +import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; + +import io.micrometer.core.instrument.Measurement; + +public class ThreadPoolMonitorPublishModelFactory { + interface Setter { + void set(ThreadPoolPublishModel model, Measurement measurement); + } + + public static Map create(MeasurementTree tree) { + Map result = new HashMap<>(8); + readMeasurement(result, tree, ThreadPoolMeter.TASK_COUNT, + (model, measurement) -> model.setAvgTaskCount(measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.COMPLETED_TASK_COUNT, + (model, measurement) -> model.setAvgCompletedTaskCount(measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.CURRENT_THREADS_BUSY, + (model, measurement) -> model.setCurrentThreadsBusy((int) measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.MAX_THREADS, + (model, measurement) -> model.setMaxThreads((int) measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.POOL_SIZE, + (model, measurement) -> model.setPoolSize((int) measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.CORE_POOL_SIZE, + (model, measurement) -> model.setCorePoolSize((int) measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.QUEUE_SIZE, + (model, measurement) -> model.setQueueSize((int) measurement.getValue())); + readMeasurement(result, tree, ThreadPoolMeter.REJECTED_COUNT, + (model, measurement) -> model.setRejected(measurement.getValue())); + return result; + } + + protected static void readMeasurement(Map threadPools, MeasurementTree tree, + String name, Setter setter) { + MeasurementNode node = tree.findChild(ThreadPoolMeter.THREAD_POOL_METER); + if (node == null) { + return; + } + + for (String threadPoolName : node.getChildren().keySet()) { + MeasurementNode measure = node.findChild(threadPoolName, name); + if (measure == null) { + continue; + } + ThreadPoolPublishModel model = threadPools.computeIfAbsent(threadPoolName, tpn -> new ThreadPoolPublishModel()); + setter.set(model, measure.getMeasurements().get(0)); + } + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java index 1807575f06d..dbaf875ba81 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeter.java @@ -16,93 +16,90 @@ */ package org.apache.servicecomb.metrics.core.meter.invocation; -import java.util.ArrayList; -import java.util.List; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import org.apache.servicecomb.foundation.metrics.meter.AbstractPeriodMeter; -import org.apache.servicecomb.foundation.metrics.meter.LatencyDistributionMeter; -import org.apache.servicecomb.foundation.metrics.meter.SimpleTimer; +import org.apache.servicecomb.foundation.metrics.meter.LatencyDistributionConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; -public abstract class AbstractInvocationMeter extends AbstractPeriodMeter { +public abstract class AbstractInvocationMeter { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractInvocationMeter.class); + + // total time distribution + private final DistributionSummary totalSummary; //total time - private final SimpleTimer totalTimer; + private final Timer totalTimer; // prepare time - private final SimpleTimer prepareTimer; - - // handler request - private final SimpleTimer handlersRequestTimer; - - // handler response - private final SimpleTimer handlersResponseTimer; - - // latency distribution - private final LatencyDistributionMeter latencyDistributionMeter; + private final Timer prepareTimer; protected final MetricsBootstrapConfig metricsBootstrapConfig; - public AbstractInvocationMeter(Id id, MetricsBootstrapConfig metricsBootstrapConfig) { - this.id = id; + public AbstractInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, + MetricsBootstrapConfig metricsBootstrapConfig) { this.metricsBootstrapConfig = metricsBootstrapConfig; - latencyDistributionMeter = createLatencyDistribution(MeterInvocationConst.TAG_LATENCY_DISTRIBUTION); - totalTimer = createStageTimer(MeterInvocationConst.STAGE_TOTAL); - prepareTimer = createStageTimer(MeterInvocationConst.STAGE_PREPARE); - handlersRequestTimer = createStageTimer(MeterInvocationConst.STAGE_HANDLERS_REQUEST); - handlersResponseTimer = createStageTimer(MeterInvocationConst.STAGE_HANDLERS_RESPONSE); - } - - protected LatencyDistributionMeter createLatencyDistribution(String tagValue) { - return new LatencyDistributionMeter(id.withTag(MeterInvocationConst.TAG_TYPE, tagValue), - metricsBootstrapConfig.getLatencyDistribution()); - } - protected SimpleTimer createStageTimer(String stageValue) { - return createTimer(id.withTag(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE) - .withTag(MeterInvocationConst.TAG_STAGE, stageValue)); + double[] sla = toSla(metricsBootstrapConfig.getLatencyDistribution()); + if (sla != null) { + totalSummary = DistributionSummary.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_DISTRIBUTION)) + .distributionStatisticExpiry(Duration.ofMillis(metricsBootstrapConfig.getMsPollInterval())) + .serviceLevelObjectives(sla).register(meterRegistry); + } else { + totalSummary = null; + } + this.totalTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE + , MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_TOTAL)).register(meterRegistry); + this.prepareTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE + , MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PREPARE)).register(meterRegistry); } - protected SimpleTimer createTimer(String tagKey, String tagValue) { - return createTimer(id.withTag(tagKey, tagValue)); - } - - protected SimpleTimer createTimer(Id timerId) { - return new SimpleTimer(timerId); + private static double[] toSla(String config) { + if (StringUtils.isEmpty(config)) { + return null; + } + config = config.trim() + "," + LatencyDistributionConfig.MAX_LATENCY; + String[] array = config.split("\\s*,+\\s*"); + double[] result = new double[array.length]; + + for (int idx = 0; idx < array.length - 1; idx++) { + long msMin = Long.parseLong(array[idx]); + long msMax = Long.parseLong(array[idx + 1]); + if (msMin >= msMax) { + LOGGER.error("invalid latency scope, min={}, max={}.", array[idx], array[idx + 1]); + return null; + } + + result[idx] = msMin; + } + result[array.length - 1] = LatencyDistributionConfig.MAX_LATENCY; + + if (Double.compare(0, result[0]) == 0) { + double[] target = new double[result.length - 1]; + System.arraycopy(result, 1, target, 0, target.length); + return target; + } + + return result; } public void onInvocationFinish(InvocationFinishEvent event) { InvocationStageTrace stageTrace = event.getInvocation().getInvocationStageTrace(); - latencyDistributionMeter.record((long) stageTrace.calcTotalTime()); - totalTimer.record((long) stageTrace.calcTotalTime()); - handlersRequestTimer.record((long) stageTrace.calcHandlersRequestTime()); - handlersResponseTimer.record((long) stageTrace.calcHandlersResponseTime()); - prepareTimer.record((long) stageTrace.calcInvocationPrepareTime()); - } - - @Override - public void calcMeasurements(long msNow, long secondInterval) { - List measurements = new ArrayList<>(3); - calcMeasurements(measurements, msNow, secondInterval); - allMeasurements = measurements; - } - - @Override - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - latencyDistributionMeter.calcMeasurements(measurements, msNow, secondInterval); - totalTimer.calcMeasurements(measurements, msNow, secondInterval); - handlersRequestTimer.calcMeasurements(measurements, msNow, secondInterval); - handlersResponseTimer.calcMeasurements(measurements, msNow, secondInterval); - prepareTimer.calcMeasurements(measurements, msNow, secondInterval); - } - - @Override - public boolean hasExpired() { - return super.hasExpired(); + totalTimer.record(stageTrace.calcTotal(), TimeUnit.NANOSECONDS); + prepareTimer.record(stageTrace.calcPrepare(), TimeUnit.NANOSECONDS); + if (totalSummary != null) { + totalSummary.record(TimeUnit.NANOSECONDS.toMillis(stageTrace.calcTotal())); + } } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.java index bca004e6519..30e8e2987ca 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/AbstractInvocationMeters.java @@ -25,12 +25,11 @@ import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.swagger.invocation.Response; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.SpectatorUtils; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; public abstract class AbstractInvocationMeters { - protected Registry registry; + protected MeterRegistry registry; private final Map metersMap = new ConcurrentHashMapEx<>(); @@ -39,21 +38,15 @@ public abstract class AbstractInvocationMeters { protected MetricsBootstrapConfig metricsBootstrapConfig; - public AbstractInvocationMeters(Registry registry, MetricsBootstrapConfig metricsBootstrapConfig) { + public AbstractInvocationMeters(MeterRegistry registry, MetricsBootstrapConfig metricsBootstrapConfig) { this.registry = registry; this.metricsBootstrapConfig = metricsBootstrapConfig; } protected AbstractInvocationMeter getOrCreateMeters(Invocation invocation, Response response) { - // build string key is faster then use Id to locate timer directly + // build string key is faster than use Id to locate timer directly StringBuilder keyBuilder = new StringBuilder(maxKeyLen); - String invocationName; - //check edge - if (invocation.isConsumer() && invocation.isEdge()) { - invocationName = MeterInvocationConst.EDGE_INVOCATION_NAME; - } else { - invocationName = invocation.getInvocationType().name(); - } + String invocationName = invocation.getInvocationType().name(); keyBuilder .append(invocationName) @@ -65,23 +58,16 @@ protected AbstractInvocationMeter getOrCreateMeters(Invocation invocation, Respo } return metersMap.computeIfAbsent(keyBuilder.toString(), k -> { - Id id = registry.createId(MeterInvocationConst.INVOCATION_NAME, - MeterInvocationConst.TAG_ROLE, - invocationName, - MeterInvocationConst.TAG_TRANSPORT, - invocation.getRealTransportName(), - MeterInvocationConst.TAG_OPERATION, - invocation.getMicroserviceQualifiedName(), - MeterInvocationConst.TAG_STATUS, - String.valueOf(response.getStatusCode())); - - AbstractInvocationMeter meter = createMeter(id); - SpectatorUtils.registerMeter(registry, meter); + AbstractInvocationMeter meter = createMeter(MeterInvocationConst.INVOCATION_NAME, Tags.empty() + .and(MeterInvocationConst.TAG_ROLE, invocationName) + .and(MeterInvocationConst.TAG_TRANSPORT, invocation.getRealTransportName()) + .and(MeterInvocationConst.TAG_OPERATION, invocation.getMicroserviceQualifiedName()) + .and(MeterInvocationConst.TAG_STATUS, String.valueOf(response.getStatusCode()))); return meter; }); } - protected abstract AbstractInvocationMeter createMeter(Id id); + protected abstract AbstractInvocationMeter createMeter(String name, Tags tags); public void onInvocationStart(InvocationStartEvent event) { } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.java index 5701899f6fa..d0b4c08f75d 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeter.java @@ -16,40 +16,47 @@ */ package org.apache.servicecomb.metrics.core.meter.invocation; -import java.util.List; +import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import org.apache.servicecomb.foundation.metrics.meter.SimpleTimer; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; public class ConsumerInvocationMeter extends AbstractInvocationMeter { - private final SimpleTimer clientFiltersRequestTimer; + private final Timer consumerEncodeRequestTimer; - private final SimpleTimer consumerSendRequestTimer; + private final Timer consumerDecodeResponseTimer; - private final SimpleTimer consumerGetConnectionTimer; + private final Timer consumerGetConnectionTimer; - private final SimpleTimer consumerWriteToBufTimer; + private final Timer consumerSendRequestTimer; - private final SimpleTimer consumerWaitResponseTimer; + private final Timer consumerWaitResponseTimer; - private final SimpleTimer consumerWakeConsumerTimer; - - private final SimpleTimer clientFiltersResponseTimer; - - public ConsumerInvocationMeter(Id id, MetricsBootstrapConfig metricsBootstrapConfig) { - super(id, metricsBootstrapConfig); - clientFiltersRequestTimer = createStageTimer(MeterInvocationConst.STAGE_CLIENT_FILTERS_REQUEST); - consumerSendRequestTimer = createStageTimer(MeterInvocationConst.STAGE_CONSUMER_SEND_REQUEST); - consumerGetConnectionTimer = createStageTimer(MeterInvocationConst.STAGE_CONSUMER_GET_CONNECTION); - consumerWriteToBufTimer = createStageTimer(MeterInvocationConst.STAGE_CONSUMER_WRITE_TO_BUF); - consumerWakeConsumerTimer = createStageTimer(MeterInvocationConst.STAGE_CONSUMER_WAKE_CONSUMER); - clientFiltersResponseTimer = createStageTimer(MeterInvocationConst.STAGE_CLIENT_FILTERS_RESPONSE); - consumerWaitResponseTimer = createStageTimer(MeterInvocationConst.STAGE_CONSUMER_WAIT_RESPONSE); + public ConsumerInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, + MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, name, tags, metricsBootstrapConfig); + consumerSendRequestTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_SEND)).register(meterRegistry); + consumerGetConnectionTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_CONNECTION)).register(meterRegistry); + consumerEncodeRequestTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST)) + .register(meterRegistry); + consumerDecodeResponseTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE)) + .register(meterRegistry); + consumerWaitResponseTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_CONSUMER_WAIT)).register(meterRegistry); } @Override @@ -57,25 +64,10 @@ public void onInvocationFinish(InvocationFinishEvent event) { super.onInvocationFinish(event); InvocationStageTrace invocationStageTrace = event.getInvocation().getInvocationStageTrace(); - clientFiltersRequestTimer.record((long) invocationStageTrace.calcClientFiltersRequestTime()); - consumerSendRequestTimer.record((long) invocationStageTrace.calcSendRequestTime()); - consumerGetConnectionTimer.record((long) invocationStageTrace.calcGetConnectionTime()); - consumerWriteToBufTimer.record((long) invocationStageTrace.calcWriteToBufferTime()); - consumerWaitResponseTimer.record((long) invocationStageTrace.calcReceiveResponseTime()); - consumerWakeConsumerTimer.record((long) invocationStageTrace.calcWakeConsumer()); - clientFiltersResponseTimer.record((long) invocationStageTrace.calcClientFiltersResponseTime()); - } - - @Override - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - super.calcMeasurements(measurements, msNow, secondInterval); - - clientFiltersRequestTimer.calcMeasurements(measurements, msNow, secondInterval); - consumerSendRequestTimer.calcMeasurements(measurements, msNow, secondInterval); - consumerGetConnectionTimer.calcMeasurements(measurements, msNow, secondInterval); - consumerWriteToBufTimer.calcMeasurements(measurements, msNow, secondInterval); - consumerWaitResponseTimer.calcMeasurements(measurements, msNow, secondInterval); - consumerWakeConsumerTimer.calcMeasurements(measurements, msNow, secondInterval); - clientFiltersResponseTimer.calcMeasurements(measurements, msNow, secondInterval); + consumerEncodeRequestTimer.record(invocationStageTrace.calcConsumerEncodeRequest(), TimeUnit.NANOSECONDS); + consumerSendRequestTimer.record(invocationStageTrace.calcConsumerSendRequest(), TimeUnit.NANOSECONDS); + consumerGetConnectionTimer.record(invocationStageTrace.calcConnection(), TimeUnit.NANOSECONDS); + consumerWaitResponseTimer.record(invocationStageTrace.calcWait(), TimeUnit.NANOSECONDS); + consumerDecodeResponseTimer.record(invocationStageTrace.calcConsumerDecodeResponse(), TimeUnit.NANOSECONDS); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.java index fd0729ca7c4..842122e08db 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ConsumerInvocationMeters.java @@ -18,16 +18,16 @@ import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Registry; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; public class ConsumerInvocationMeters extends AbstractInvocationMeters { - public ConsumerInvocationMeters(Registry registry, MetricsBootstrapConfig metricsBootstrapConfig) { - super(registry, metricsBootstrapConfig); + public ConsumerInvocationMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, metricsBootstrapConfig); } @Override - protected AbstractInvocationMeter createMeter(Id id) { - return new ConsumerInvocationMeter(id, this.metricsBootstrapConfig); + protected AbstractInvocationMeter createMeter(String name, Tags tags) { + return new ConsumerInvocationMeter(registry, name, tags, this.metricsBootstrapConfig); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeter.java index af168a0fdb5..32fe7aa926a 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeter.java @@ -17,31 +17,37 @@ package org.apache.servicecomb.metrics.core.meter.invocation; -import java.util.List; +import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import org.apache.servicecomb.foundation.metrics.meter.SimpleTimer; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; public class EdgeInvocationMeter extends ConsumerInvocationMeter { - private final SimpleTimer executorQueueTimer; - - private final SimpleTimer serverFiltersRequestTimer; - - private final SimpleTimer serverFiltersResponseTimer; - - private final SimpleTimer sendResponseTimer; - - public EdgeInvocationMeter(Id id, MetricsBootstrapConfig metricsBootstrapConfig) { - super(id, metricsBootstrapConfig); - executorQueueTimer = createStageTimer(MeterInvocationConst.STAGE_EXECUTOR_QUEUE); - serverFiltersRequestTimer = createStageTimer(MeterInvocationConst.STAGE_SERVER_FILTERS_REQUEST); - serverFiltersResponseTimer = createStageTimer(MeterInvocationConst.STAGE_SERVER_FILTERS_RESPONSE); - sendResponseTimer = createStageTimer(MeterInvocationConst.STAGE_PRODUCER_SEND_RESPONSE); + private final Timer providerDecodeRequestTimer; + + private final Timer providerEncodeResponseTimer; + + private final Timer sendResponseTimer; + + public EdgeInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, + MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, name, tags, metricsBootstrapConfig); + providerDecodeRequestTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST)) + .register(meterRegistry); + providerEncodeResponseTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE)) + .register(meterRegistry); + sendResponseTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_SEND)) + .register(meterRegistry); } @Override @@ -49,19 +55,8 @@ public void onInvocationFinish(InvocationFinishEvent event) { super.onInvocationFinish(event); InvocationStageTrace invocationStageTrace = event.getInvocation().getInvocationStageTrace(); - executorQueueTimer.record((long) invocationStageTrace.calcThreadPoolQueueTime()); - serverFiltersRequestTimer.record((long) invocationStageTrace.calcServerFiltersRequestTime()); - serverFiltersResponseTimer.record((long) invocationStageTrace.calcServerFiltersResponseTime()); - sendResponseTimer.record((long) invocationStageTrace.calcSendResponseTime()); - } - - @Override - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - super.calcMeasurements(measurements, msNow, secondInterval); - - executorQueueTimer.calcMeasurements(measurements, msNow, secondInterval); - serverFiltersRequestTimer.calcMeasurements(measurements, msNow, secondInterval); - serverFiltersResponseTimer.calcMeasurements(measurements, msNow, secondInterval); - sendResponseTimer.calcMeasurements(measurements, msNow, secondInterval); + providerDecodeRequestTimer.record(invocationStageTrace.calcProviderDecodeRequest(), TimeUnit.NANOSECONDS); + providerEncodeResponseTimer.record(invocationStageTrace.calcProviderEncodeResponse(), TimeUnit.NANOSECONDS); + sendResponseTimer.record(invocationStageTrace.calcProviderSendResponse(), TimeUnit.NANOSECONDS); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeters.java index 2a5b0bdddef..ecdbd3670b0 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeters.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/EdgeInvocationMeters.java @@ -19,16 +19,16 @@ import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Registry; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; public class EdgeInvocationMeters extends ConsumerInvocationMeters { - public EdgeInvocationMeters(Registry registry, MetricsBootstrapConfig metricsBootstrapConfig) { - super(registry, metricsBootstrapConfig); + public EdgeInvocationMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, metricsBootstrapConfig); } @Override - protected AbstractInvocationMeter createMeter(Id id) { - return new EdgeInvocationMeter(id, metricsBootstrapConfig); + protected AbstractInvocationMeter createMeter(String name, Tags tags) { + return new EdgeInvocationMeter(registry, name, tags, metricsBootstrapConfig); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java index 94cec809a73..2a377e43260 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/MeterInvocationConst.java @@ -16,8 +16,6 @@ */ package org.apache.servicecomb.metrics.core.meter.invocation; -import com.netflix.spectator.api.Statistic; - public interface MeterInvocationConst { String INVOCATION_NAME = "servicecomb.invocation"; @@ -32,46 +30,9 @@ public interface MeterInvocationConst { String TAG_STAGE = "stage"; - String TAG_LATENCY_DISTRIBUTION = "latencyDistribution"; + String TAG_DISTRIBUTION = "distribution"; String TAG_STATUS = "status"; - String TAG_STATISTIC = Statistic.count.key(); - - String STAGE_TOTAL = "total"; - - String STAGE_PREPARE = "prepare"; - - String STAGE_EXECUTOR_QUEUE = "queue"; - - String STAGE_EXECUTION = "execution"; - String EDGE_INVOCATION_NAME = "EDGE"; - - String STAGE_HANDLERS_REQUEST = "handlers_request"; - - String STAGE_HANDLERS_RESPONSE = "handlers_response"; - - // producer only - String STAGE_SERVER_FILTERS_REQUEST = "server_filters_request"; - - String STAGE_SERVER_FILTERS_RESPONSE = "server_filters_response"; - - String STAGE_PRODUCER_SEND_RESPONSE = "producer_send_response"; - - //consumer only - - String STAGE_CLIENT_FILTERS_REQUEST = "client_filters_request"; - - String STAGE_CONSUMER_SEND_REQUEST = "consumer_send_request"; - - String STAGE_CONSUMER_GET_CONNECTION = "consumer_get_connection"; - - String STAGE_CONSUMER_WRITE_TO_BUF = "consumer_write_to_buf"; - - String STAGE_CONSUMER_WAIT_RESPONSE = "consumer_wait_response"; - - String STAGE_CONSUMER_WAKE_CONSUMER = "consumer_wake_consumer"; - - String STAGE_CLIENT_FILTERS_RESPONSE = "client_filters_response"; } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.java index 1decb98d0ba..2ed55e5e0ef 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeter.java @@ -16,35 +16,49 @@ */ package org.apache.servicecomb.metrics.core.meter.invocation; -import java.util.List; +import java.util.concurrent.TimeUnit; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import org.apache.servicecomb.foundation.metrics.meter.SimpleTimer; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.Timer; public class ProducerInvocationMeter extends AbstractInvocationMeter { - private final SimpleTimer executorQueueTimer; + private final Timer executorQueueTimer; - private final SimpleTimer executionTimer; + private final Timer executionTimer; - private final SimpleTimer serverFiltersRequestTimer; + private final Timer providerDecodeRequestTimer; - private final SimpleTimer serverFiltersResponseTimer; + private final Timer providerEncodeResponseTimer; - private final SimpleTimer sendResponseTimer; + private final Timer sendResponseTimer; - public ProducerInvocationMeter(Id id, MetricsBootstrapConfig metricsBootstrapConfig) { - super(id, metricsBootstrapConfig); - - executorQueueTimer = createStageTimer(MeterInvocationConst.STAGE_EXECUTOR_QUEUE); - executionTimer = createStageTimer(MeterInvocationConst.STAGE_EXECUTION); - serverFiltersRequestTimer = createStageTimer(MeterInvocationConst.STAGE_SERVER_FILTERS_REQUEST); - serverFiltersResponseTimer = createStageTimer(MeterInvocationConst.STAGE_SERVER_FILTERS_RESPONSE); - sendResponseTimer = createStageTimer(MeterInvocationConst.STAGE_PRODUCER_SEND_RESPONSE); + public ProducerInvocationMeter(MeterRegistry meterRegistry, String name, Tags tags, + MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, name, tags, metricsBootstrapConfig); + executorQueueTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_QUEUE)) + .register(meterRegistry); + executionTimer = Timer.builder(name).tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_BUSINESS)) + .register(meterRegistry); + providerDecodeRequestTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST)) + .register(meterRegistry); + providerEncodeResponseTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE)) + .register(meterRegistry); + sendResponseTimer = Timer.builder(name) + .tags(tags.and(MeterInvocationConst.TAG_TYPE, MeterInvocationConst.TAG_STAGE, + MeterInvocationConst.TAG_STAGE, InvocationStageTrace.STAGE_PROVIDER_SEND)) + .register(meterRegistry); } @Override @@ -52,21 +66,10 @@ public void onInvocationFinish(InvocationFinishEvent event) { super.onInvocationFinish(event); InvocationStageTrace invocationStageTrace = event.getInvocation().getInvocationStageTrace(); - executorQueueTimer.record((long) invocationStageTrace.calcThreadPoolQueueTime()); - executionTimer.record((long) invocationStageTrace.calcBusinessTime()); - serverFiltersRequestTimer.record((long) invocationStageTrace.calcServerFiltersRequestTime()); - serverFiltersResponseTimer.record((long) invocationStageTrace.calcServerFiltersResponseTime()); - sendResponseTimer.record((long) invocationStageTrace.calcSendResponseTime()); - } - - @Override - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - super.calcMeasurements(measurements, msNow, secondInterval); - - executorQueueTimer.calcMeasurements(measurements, msNow, secondInterval); - executionTimer.calcMeasurements(measurements, msNow, secondInterval); - serverFiltersRequestTimer.calcMeasurements(measurements, msNow, secondInterval); - serverFiltersResponseTimer.calcMeasurements(measurements, msNow, secondInterval); - sendResponseTimer.calcMeasurements(measurements, msNow, secondInterval); + executorQueueTimer.record(invocationStageTrace.calcQueue(), TimeUnit.NANOSECONDS); + executionTimer.record(invocationStageTrace.calcBusinessExecute(), TimeUnit.NANOSECONDS); + providerDecodeRequestTimer.record(invocationStageTrace.calcProviderDecodeRequest(), TimeUnit.NANOSECONDS); + providerEncodeResponseTimer.record(invocationStageTrace.calcProviderEncodeResponse(), TimeUnit.NANOSECONDS); + sendResponseTimer.record(invocationStageTrace.calcProviderSendResponse(), TimeUnit.NANOSECONDS); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.java index fefe5070854..bf4f84104f9 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/invocation/ProducerInvocationMeters.java @@ -18,16 +18,16 @@ import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Registry; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; public class ProducerInvocationMeters extends AbstractInvocationMeters { - public ProducerInvocationMeters(Registry registry, MetricsBootstrapConfig metricsBootstrapConfig) { - super(registry, metricsBootstrapConfig); + public ProducerInvocationMeters(MeterRegistry meterRegistry, MetricsBootstrapConfig metricsBootstrapConfig) { + super(meterRegistry, metricsBootstrapConfig); } @Override - protected AbstractInvocationMeter createMeter(Id id) { - return new ProducerInvocationMeter(id, metricsBootstrapConfig); + protected AbstractInvocationMeter createMeter(String name, Tags tags) { + return new ProducerInvocationMeter(registry, name, tags, metricsBootstrapConfig); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/CpuMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/CpuMeter.java deleted file mode 100644 index 587c11419ec..00000000000 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/CpuMeter.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.metrics.core.meter.os; - -import java.util.List; - -import org.apache.servicecomb.metrics.core.meter.os.cpu.OsCpuUsage; -import org.apache.servicecomb.metrics.core.meter.os.cpu.ProcessCpuUsage; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; - -public class CpuMeter { - private static final Logger LOGGER = LoggerFactory.getLogger(CpuMeter.class); - - // read from /proc/stat - private final OsCpuUsage allCpuUsage; - - // read from /proc/self/stat /proc/uptime - private final ProcessCpuUsage processCpuUsage; - - public CpuMeter(Id id) { - allCpuUsage = new OsCpuUsage(id.withTag(OsMeter.OS_TYPE, OsMeter.OS_TYPE_ALL_CPU)); - processCpuUsage = new ProcessCpuUsage(id.withTag(OsMeter.OS_TYPE, OsMeter.OS_TYPE_PROCESS_CPU)); - - //must refresh all first - update(); - allCpuUsage.setUsage(0); - processCpuUsage.setUsage(0); - } - - public void calcMeasurements(List measurements, long msNow) { - update(); - measurements.add(new Measurement(allCpuUsage.getId(), msNow, allCpuUsage.getUsage())); - measurements.add(new Measurement(processCpuUsage.getId(), msNow, processCpuUsage.getUsage())); - } - - public void update() { - try { - allCpuUsage.update(); - processCpuUsage.update(); - } catch (Throwable e) { - LOGGER.error("Failed to update usage", e); - } - } - - public OsCpuUsage getAllCpuUsage() { - return allCpuUsage; - } - - public ProcessCpuUsage getProcessCpuUsage() { - return processCpuUsage; - } -} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java index 5c9bc69cc70..882fb6bf5de 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/NetMeter.java @@ -19,55 +19,66 @@ import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.SystemUtils; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage; -import org.apache.servicecomb.metrics.core.meter.os.net.NetStat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.netflix.spectator.api.BasicTag; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Tag; +import com.google.common.annotations.VisibleForTesting; -public class NetMeter { +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; + +public class NetMeter implements PeriodMeter { private static final Logger LOGGER = LoggerFactory.getLogger(NetMeter.class); public static final String STATISTIC = "statistic"; public static final String INTERFACE = "interface"; - public static final Tag TAG_RECEIVE = new BasicTag(STATISTIC, "receive"); - - public static final Tag TAG_PACKETS_RECEIVE = new BasicTag(STATISTIC, "receivePackets"); + public static final Tag TAG_RECEIVE = Tag.of(STATISTIC, "receive"); - public static final Tag TAG_SEND = new BasicTag(STATISTIC, "send"); + public static final Tag TAG_PACKETS_RECEIVE = Tag.of(STATISTIC, "receivePackets"); - public static final Tag TAG_PACKETS_SEND = new BasicTag(STATISTIC, "sendPackets"); + public static final Tag TAG_SEND = Tag.of(STATISTIC, "send"); - private final Id id; + public static final Tag TAG_PACKETS_SEND = Tag.of(STATISTIC, "sendPackets"); private final Map interfaceUsageMap = new ConcurrentHashMap<>(); - public NetMeter(Id id) { - this.id = id; - // init lastRxBytes, lastRxPackets, lastTxBytes, lastTxPackets - refreshNet(1); - interfaceUsageMap.values().forEach(interfaceUsage -> interfaceUsage.getNetStats().forEach(NetStat::clearRate)); - } + private boolean isOsLinux = SystemUtils.IS_OS_LINUX; + + protected final MeterRegistry meterRegistry; + + protected final String name; + + protected final Tags tags; - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - refreshNet(secondInterval); + public NetMeter(MeterRegistry meterRegistry, String name, Tags tags) { + this.meterRegistry = meterRegistry; + this.name = name; + this.tags = tags; + poll(0, 0); + } - interfaceUsageMap.values().forEach(interfaceUsage -> interfaceUsage.calcMeasurements(measurements, msNow)); + @VisibleForTesting + public void setOsLinux(boolean osLinux) { + isOsLinux = osLinux; } + @Override + public void poll(long msNow, long secondInterval) { + if (isOsLinux) { + refreshNet(secondInterval); + } + } /* * Inter-| Receive | Transmit @@ -79,7 +90,6 @@ protected void refreshNet(long secondInterval) { try { File file = new File("/proc/net/dev"); List netInfo = FileUtils.readLines(file, StandardCharsets.UTF_8); - Set nameSet = new HashSet<>(); //the first two lines is useless for (int i = 2; i < netInfo.size(); i++) { @@ -90,19 +100,12 @@ protected void refreshNet(long secondInterval) { continue; } - String name = strings[0].trim(); - nameSet.add(name); + String interfaceName = strings[0].trim(); - InterfaceUsage interfaceUsage = interfaceUsageMap.computeIfAbsent(name, key -> new InterfaceUsage(id, key)); + InterfaceUsage interfaceUsage = interfaceUsageMap.computeIfAbsent(interfaceName, + key -> new InterfaceUsage(meterRegistry, name, tags, key)); interfaceUsage.update(strings[1], secondInterval); } - - // clear deleted interfaces - for (String interfaceName : interfaceUsageMap.keySet()) { - if (!nameSet.contains(interfaceName)) { - this.interfaceUsageMap.remove(interfaceName); - } - } } catch (IOException e) { LOGGER.error("Failed to read net info/", e); } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/OsMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/OsMeter.java index 8bc157c1734..9b74fd199de 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/OsMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/OsMeter.java @@ -16,14 +16,12 @@ */ package org.apache.servicecomb.metrics.core.meter.os; -import java.util.ArrayList; -import java.util.List; - -import org.apache.servicecomb.foundation.metrics.meter.AbstractPeriodMeter; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import com.google.common.annotations.VisibleForTesting; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Registry; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; /** * name=os type=cpu value = 0 @@ -33,43 +31,31 @@ * name=os type=net interface=eth0 statistic=sendPackets value=100 * name=os type=net interface=eth0 statistic=receivePackets value=100 */ -public class OsMeter extends AbstractPeriodMeter { +public class OsMeter implements PeriodMeter { public static final String OS_NAME = "os"; public static final String OS_TYPE = "type"; - public static final String OS_TYPE_ALL_CPU = "cpu"; - - public static final String OS_TYPE_PROCESS_CPU = "processCpu"; - public static final String OS_TYPE_NET = "net"; - private final CpuMeter cpuMeter; + private final SystemMeter systemMeter; private final NetMeter netMeter; - public OsMeter(Registry registry) { - this.id = registry.createId(OS_NAME); - cpuMeter = new CpuMeter(id); - netMeter = new NetMeter(id.withTag(OS_TYPE, OS_TYPE_NET)); - } - - @Override - public void calcMeasurements(long msNow, long secondInterval) { - List measurements = new ArrayList<>(); - calcMeasurements(measurements, msNow, secondInterval); - allMeasurements = measurements; + public OsMeter(MeterRegistry meterRegistry) { + systemMeter = new SystemMeter(meterRegistry, OS_NAME); + netMeter = new NetMeter(meterRegistry, OS_NAME, Tags.of(OS_TYPE, OS_TYPE_NET)); } @Override - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - cpuMeter.calcMeasurements(measurements, msNow); - netMeter.calcMeasurements(measurements, msNow, secondInterval); + public void poll(long msNow, long secondInterval) { + systemMeter.poll(msNow, secondInterval); + netMeter.poll(msNow, secondInterval); } @VisibleForTesting - public CpuMeter getCpuMeter() { - return cpuMeter; + public SystemMeter getCpuMeter() { + return systemMeter; } @VisibleForTesting diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/SystemMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/SystemMeter.java new file mode 100644 index 00000000000..2b9ce7d471e --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/SystemMeter.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.meter.os; + +import java.lang.management.ManagementFactory; + +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; + +import com.google.common.annotations.VisibleForTesting; +import com.sun.management.OperatingSystemMXBean; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; + +public class SystemMeter implements PeriodMeter { + public static final String SYSTEM_LOAD_AVERAGE = "sla"; + + public static final String CPU_USAGE = "cpu"; + + public static final String PROCESS_CPU_USAGE = "processCpu"; + + public static final String MEMORY_USAGE = "memory"; + + private OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class); + + public SystemMeter(MeterRegistry meterRegistry, String name) { + Gauge.builder(name, () -> getOsBean().getSystemLoadAverage()) + .tags(Tags.of(OsMeter.OS_TYPE, SYSTEM_LOAD_AVERAGE)) + .register(meterRegistry); + + Gauge.builder(name, () -> getOsBean().getCpuLoad()) + .tags(Tags.of(OsMeter.OS_TYPE, CPU_USAGE)) + .register(meterRegistry); + + Gauge.builder(name, () -> getOsBean().getProcessCpuLoad()) + .tags(Tags.of(OsMeter.OS_TYPE, PROCESS_CPU_USAGE)) + .register(meterRegistry); + + Gauge.builder(name, () -> (getOsBean().getTotalMemorySize() - getOsBean().getFreeMemorySize()) + / (double) getOsBean().getTotalMemorySize()) + .tags(Tags.of(OsMeter.OS_TYPE, MEMORY_USAGE)) + .register(meterRegistry); + } + + @VisibleForTesting + public void setOsBean(OperatingSystemMXBean mock) { + this.osBean = mock; + } + + private OperatingSystemMXBean getOsBean() { + return this.osBean; + } + + @Override + public void poll(long msNow, long secondInterval) { + + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/AbstractCpuUsage.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/AbstractCpuUsage.java deleted file mode 100644 index 375a64875b0..00000000000 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/AbstractCpuUsage.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.metrics.core.meter.os.cpu; - -import com.netflix.spectator.api.Id; - -public abstract class AbstractCpuUsage { - - protected Id id; - - protected double usage; - - protected int cpuCount = Runtime.getRuntime().availableProcessors(); - - static class Period { - double last; - - double period; - - void update(double current) { - period = current - last; - last = current; - } - } - - public AbstractCpuUsage(Id id) { - this.id = id; - } - - public Id getId() { - return id; - } - - public double getUsage() { - return usage; - } - - public void setUsage(double usage) { - this.usage = usage; - } - - protected void updateUsage(double periodBusy, double periodTotal, boolean irixMode) { - usage = periodTotal == 0 ? 0 : periodBusy / periodTotal; - if (usage > 1) { - usage = 1; - } - if (irixMode) { - usage *= cpuCount; - } - } -} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/CpuUtils.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/CpuUtils.java deleted file mode 100644 index 75eb83d9d1f..00000000000 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/CpuUtils.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.metrics.core.meter.os.cpu; - -import java.io.File; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.io.Files; - -public final class CpuUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(CpuUtils.class); - - public static final File PROC_STAT = new File("/proc/stat"); - - public static final File UPTIME = new File("/proc/uptime"); - - public static final File SELF_PROCESS = new File("/proc/self/stat"); - - private CpuUtils() { - } - - public static String[] readAndSplitFirstLine(File file) throws IOException { - return Files.asCharSource(file, StandardCharsets.UTF_8).readFirstLine().trim().split("\\s+"); - } - - public static double summary(String[] stats, int start, int len) { - double total = 0; - for (int idx = start; idx < start + len; idx++) { - total += Double.parseDouble(stats[idx]); - } - return total; - } - - public static double readProcSelfBusy() throws IOException { - String[] stats = readAndSplitFirstLine(SELF_PROCESS); - return summary(stats, 13, 2); - } - - public static double readProcStatTotal() throws IOException { - String[] stats = readAndSplitFirstLine(PROC_STAT); - return summary(stats, 1, 8); - } - - public static double readUptimeTotal() throws IOException { - String[] uptime = readAndSplitFirstLine(UPTIME); - return Double.parseDouble(uptime[0]); - } - - private static boolean isBetween(long x, long lower, long upper) { - return lower <= x && x <= upper; - } - - /** - * unit of /proc/uptime is seconds - * unit of /proc/self/stat is jiffies - * hence, we should calculate userHZ to get process cpu rate - * - * @return userHZ - */ - public static int calcHertz() { - double up1, up2, seconds; - double jiffies; - - for (; ; ) { - try { - up1 = readUptimeTotal(); - jiffies = readProcStatTotal(); - up2 = readUptimeTotal(); - } catch (Throwable e) { - LOGGER.error("Failed to calc hertz, should never happened, try again.", e); - continue; - } - - if (0 == (long) ((up2 - up1) * 1000.0 / up1)) { - break; - } - } - - seconds = (up1 + up2) / 2; - long hz = Math.round(jiffies / seconds / Runtime.getRuntime().availableProcessors()); - /* S/390 (sometimes) */ - if (isBetween(hz, 9, 11)) { - return 10; - } - - /* user-mode Linux */ - if (isBetween(hz, 18, 22)) { - return 20; - } - - /* ia64 emulator */ - if (isBetween(hz, 30, 34)) { - return 32; - } - - if (isBetween(hz, 48, 52)) { - return 50; - } - - if (isBetween(hz, 58, 61)) { - return 60; - } - - /* StrongARM /Shark */ - if (isBetween(hz, 62, 65)) { - return 64; - } - - /* normal Linux */ - if (isBetween(hz, 95, 105)) { - return 100; - } - - /* MIPS, ARM */ - if (isBetween(hz, 124, 132)) { - return 128; - } - - /* normal << 1 */ - if (isBetween(hz, 195, 204)) { - return 200; - } - - if (isBetween(hz, 247, 252)) { - return 250; - } - - if (isBetween(hz, 253, 260)) { - return 256; - } - - /* normal << 2 */ - if (isBetween(hz, 393, 408)) { - return 400; - } - - /* SMP WinNT */ - if (isBetween(hz, 410, 600)) { - return 500; - } - - /* normal << 3 */ - if (isBetween(hz, 790, 808)) { - return 800; - } - - /* ARM */ - if (isBetween(hz, 990, 1010)) { - return 1000; - } - - /* Alpha, ia64 */ - if (isBetween(hz, 1015, 1035)) { - return 1024; - } - - /* Alpha */ - if (isBetween(hz, 1180, 1220)) { - return 1200; - } - - LOGGER.warn("Unknown HZ value! ({}) Assume {}.\n", hz, 100); - return 100; - } -} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/OsCpuUsage.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/OsCpuUsage.java deleted file mode 100644 index 364ca10b474..00000000000 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/OsCpuUsage.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.metrics.core.meter.os.cpu; - -import java.io.IOException; - -import com.netflix.spectator.api.Id; - -/* - * unit : 1 jiffies - * more details : - * http://man7.org/linux/man-pages/man5/proc.5.html - * CMD : /proc/stat - * cpu 2445171 599297 353967 24490633 11242 0 10780 2993 0 0 - * cpu user nice system idle iowait irq softirq stealstolen guest guest_nice - * 0 1 2 3 4 5 6 7 8 9 10 - * total = user + nice + system + idle + iowait + irq + softirq + stealstolen - * busy = total - idle - */ -public class OsCpuUsage extends AbstractCpuUsage { - - private final Period total = new Period(); - - private final Period idle = new Period(); - - public OsCpuUsage(Id id) { - super(id); - } - - public void update() throws IOException { - String[] stats = CpuUtils.readAndSplitFirstLine(CpuUtils.PROC_STAT); - long currentIdle = Long.parseLong(stats[4]); - double totalCpu = CpuUtils.summary(stats, 1, 8); - idle.update(currentIdle); - total.update(totalCpu); - updateUsage(total.period - idle.period, total.period, false); - } -} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/ProcessCpuUsage.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/ProcessCpuUsage.java deleted file mode 100644 index 00ec8cb5ec8..00000000000 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/cpu/ProcessCpuUsage.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.metrics.core.meter.os.cpu; - -import java.io.IOException; - -import com.netflix.spectator.api.Id; - -/* - * unit : 1 jiffies - * more details : - * http://man7.org/linux/man-pages/man5/proc.5.html - * CMD : /proc/[pid]/stat - * 6754 (kubelet) S 1 995 995 0 -1 4202752 193281 592501546 0 12 1152076 907044 87991 113319 .. - * pid comm state ppid pgrp session tty_nr tpgid flags minflt cminflt majflt cmajflt utime stime cutime cstime - * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 - * busy = utime + stime - * CMD: /proc/uptime - * total = uptime[0] * cpuNum * userHZ - * - */ -public class ProcessCpuUsage extends AbstractCpuUsage { - - private final Period busy = new Period(); - - private final Period total = new Period(); - - private final int userHZ = CpuUtils.calcHertz(); - - public ProcessCpuUsage(Id id) { - super(id); - } - - public void update() throws IOException { - double processBusy = CpuUtils.readProcSelfBusy(); - double uptime = CpuUtils.readUptimeTotal(); - busy.update(processBusy); - total.update(uptime * userHZ * cpuCount); - updateUsage(busy.period, total.period, true); - } -} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java index c29d9598ff9..b3d40a1bbf5 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/InterfaceUsage.java @@ -22,49 +22,71 @@ import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_RECEIVE; import static org.apache.servicecomb.metrics.core.meter.os.NetMeter.TAG_SEND; -import java.util.ArrayList; -import java.util.List; +import com.google.common.annotations.VisibleForTesting; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; public class InterfaceUsage { - private final String name; + private final String interfaceName; - private final List netStats = new ArrayList<>(); + private final NetStat receive; - public InterfaceUsage(Id id, String name) { - this.name = name; - id = id.withTag(INTERFACE, name); - init(id); - } + private final NetStat send; + + private final NetStat packetsReceive; + + private final NetStat packetsSend; + + public InterfaceUsage(MeterRegistry meterRegistry, String name, Tags tags, String interfaceName) { + this.interfaceName = interfaceName; + tags = tags.and(Tag.of(INTERFACE, interfaceName)); - private void init(Id id) { // recv/Bps - netStats.add(new NetStat(id.withTag(TAG_RECEIVE), 0)); + receive = new NetStat(0); + Gauge.builder(name, receive::getRate).tags(tags.and(TAG_RECEIVE)).register(meterRegistry); // send/Bps - netStats.add(new NetStat(id.withTag(TAG_SEND), 8)); - + send = new NetStat(8); + Gauge.builder(name, send::getRate).tags(tags.and(TAG_SEND)).register(meterRegistry); // recv/pps - netStats.add(new NetStat(id.withTag(TAG_PACKETS_RECEIVE), 1)); + packetsReceive = new NetStat(1); + Gauge.builder(name, packetsReceive::getRate).tags(tags.and(TAG_PACKETS_RECEIVE)).register(meterRegistry); // send/pps - netStats.add(new NetStat(id.withTag(TAG_PACKETS_SEND), 9)); - } - - public void calcMeasurements(List measurements, long msNow) { - netStats.forEach(netStat -> measurements.add(new Measurement(netStat.getId(), msNow, netStat.getRate()))); + packetsSend = new NetStat(9); + Gauge.builder(name, packetsSend::getRate).tags(tags.and(TAG_PACKETS_SEND)).register(meterRegistry); } public void update(String interfaceData, long secondInterval) { String[] netInfo = interfaceData.trim().split("\\s+"); - netStats.forEach(netStat -> netStat.update(netInfo, secondInterval)); + receive.update(netInfo, secondInterval); + send.update(netInfo, secondInterval); + packetsReceive.update(netInfo, secondInterval); + packetsSend.update(netInfo, secondInterval); } - public String getName() { - return name; + @VisibleForTesting + public NetStat getReceive() { + return receive; } - public List getNetStats() { - return netStats; + @VisibleForTesting + public NetStat getSend() { + return send; + } + + @VisibleForTesting + public NetStat getPacketsReceive() { + return packetsReceive; + } + + @VisibleForTesting + public NetStat getPacketsSend() { + return packetsSend; + } + + public String getName() { + return interfaceName; } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/NetStat.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/NetStat.java index 3d3f08b706e..fac707dce6d 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/NetStat.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/os/net/NetStat.java @@ -16,21 +16,17 @@ */ package org.apache.servicecomb.metrics.core.meter.os.net; -import com.netflix.spectator.api.Id; public class NetStat { private final int index; - private final Id id; - // send/recv bytes/packets private long lastValue; // Bps/pps private double rate; - public NetStat(Id id, int index) { - this.id = id; + public NetStat(int index) { this.index = index; } @@ -55,8 +51,4 @@ public double getRate() { public int getIndex() { return index; } - - public Id getId() { - return id; - } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/pool/ThreadPoolMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/pool/ThreadPoolMeter.java new file mode 100644 index 00000000000..1267bc53e6f --- /dev/null +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/pool/ThreadPoolMeter.java @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.meter.pool; + +import java.util.concurrent.ThreadPoolExecutor; + +import org.apache.servicecomb.core.executor.ThreadPoolExecutorEx; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; + +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; + +public class ThreadPoolMeter implements PeriodMeter { + public static final String THREAD_POOL_METER = "servicecomb.threadpool"; + + public static final String ID = "id"; + + public static final String STAGE = "stage"; + + public static final String REJECTED_COUNT = "rejectedCount"; + + public static final String TASK_COUNT = "taskCount"; + + public static final String COMPLETED_TASK_COUNT = "completedTaskCount"; + + public static final String CURRENT_THREADS_BUSY = "currentThreadsBusy"; + + public static final String MAX_THREADS = "maxThreads"; + + public static final String POOL_SIZE = "poolSize"; + + public static final String CORE_POOL_SIZE = "corePoolSize"; + + public static final String QUEUE_SIZE = "queueSize"; + + private final ThreadPoolExecutor threadPoolExecutor; + + private long currentTask; + + private long lastTask; + + private long currentCompletedTask; + + private long lastCompletedTask; + + public ThreadPoolMeter(MeterRegistry meterRegistry, String threadPoolName, ThreadPoolExecutor threadPoolExecutor) { + this.threadPoolExecutor = threadPoolExecutor; + + Gauge.builder(THREAD_POOL_METER, () -> currentTask) + .tags(ID, threadPoolName, STAGE, TASK_COUNT) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, () -> currentCompletedTask) + .tags(ID, threadPoolName, STAGE, COMPLETED_TASK_COUNT) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getActiveCount) + .tags(ID, threadPoolName, STAGE, CURRENT_THREADS_BUSY) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getMaximumPoolSize) + .tags(ID, threadPoolName, STAGE, MAX_THREADS) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getPoolSize) + .tags(ID, threadPoolName, STAGE, POOL_SIZE) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, threadPoolExecutor::getCorePoolSize) + .tags(ID, threadPoolName, STAGE, CORE_POOL_SIZE) + .register(meterRegistry); + Gauge.builder(THREAD_POOL_METER, () -> threadPoolExecutor.getQueue().size()) + .tags(ID, threadPoolName, STAGE, QUEUE_SIZE) + .register(meterRegistry); + + if (threadPoolExecutor instanceof ThreadPoolExecutorEx) { + Gauge.builder(THREAD_POOL_METER, () -> ((ThreadPoolExecutorEx) (threadPoolExecutor)).getRejectedCount()) + .tags(ID, threadPoolName, STAGE, REJECTED_COUNT) + .register(meterRegistry); + } + } + + @Override + public void poll(long msNow, long secondInterval) { + long temp = threadPoolExecutor.getTaskCount(); + currentTask = temp - lastTask; + lastTask = temp; + + temp = threadPoolExecutor.getCompletedTaskCount(); + currentCompletedTask = temp - lastCompletedTask; + lastCompletedTask = temp; + } +} diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java index d0ce37bc6df..e7f960b6d6d 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/EndpointMeter.java @@ -16,15 +16,17 @@ */ package org.apache.servicecomb.metrics.core.meter.vertx; -import java.util.List; import java.util.concurrent.TimeUnit; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; -public class EndpointMeter { +public class EndpointMeter implements PeriodMeter { private static final double SNV_MILLI_SECONDS = 1.0 / TimeUnit.MILLISECONDS.toNanos(1L); public static final String ADDRESS = "address"; @@ -45,79 +47,122 @@ public class EndpointMeter { public static final String LATENCY = "latency"; - protected Id id; + protected DefaultEndpointMetric metric; - private final Id idConnect; + protected final MeterRegistry meterRegistry; - private final Id idDisconnect; + private final Gauge connectCount; - private final Id idConnections; + private final Gauge disconnectCount; - private final Id idBytesRead; + private final Gauge connections; - private final Id idBytesWritten; + private final Gauge bytesRead; - private final Id idRequests; + private final Gauge bytesWritten; - private final Id idLatency; + private final Gauge requests; - protected DefaultEndpointMetric metric; + private final Gauge latency; + + private long currentConnectCount; private long lastConnectCount; + private long currentDisconnectCount; + private long lastDisconnectCount; + private long currentBytesRead; + private long lastBytesRead; + private long currentBytesWritten; + private long lastBytesWritten; + private long currentRequests; + private long lastRequests; + private long currentRequestsForLatency; + + private long lastRequestsForLatency; + + private long currentLatency; + private long lastLatency; - public EndpointMeter(Id id, DefaultEndpointMetric metric) { - id = id.withTag(ADDRESS, metric.getAddress()); - this.id = id; - idConnect = id.withTag(STATISTIC, CONNECT_COUNT); - idDisconnect = id.withTag(STATISTIC, DISCONNECT_COUNT); - idConnections = id.withTag(STATISTIC, CONNECTIONS); - idBytesRead = id.withTag(STATISTIC, BYTES_READ); - idBytesWritten = id.withTag(STATISTIC, BYTES_WRITTEN); - idRequests = id.withTag(STATISTIC, REQUESTS); - idLatency = id.withTag(STATISTIC, LATENCY); + public EndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) { + this.meterRegistry = meterRegistry; this.metric = metric; + + tags = tags.and(Tag.of(ADDRESS, metric.getAddress())); + connectCount = Gauge.builder(name, () -> currentConnectCount) + .tags(tags.and(Tag.of(STATISTIC, CONNECT_COUNT))) + .register(meterRegistry); + disconnectCount = Gauge.builder(name, () -> currentDisconnectCount) + .tags(tags.and(Tag.of(STATISTIC, DISCONNECT_COUNT))) + .register(meterRegistry); + connections = Gauge.builder(name, () -> this.metric.getConnectCount() - this.metric.getDisconnectCount()) + .tags(tags.and(Tag.of(STATISTIC, CONNECTIONS))) + .register(meterRegistry); + bytesRead = Gauge.builder(name, () -> currentBytesRead) + .tags(tags.and(Tag.of(STATISTIC, BYTES_READ))) + .register(meterRegistry); + bytesWritten = Gauge.builder(name, () -> currentBytesWritten) + .tags(tags.and(Tag.of(STATISTIC, BYTES_WRITTEN))) + .register(meterRegistry); + requests = Gauge.builder(name, () -> currentRequests) + .tags(tags.and(Tag.of(STATISTIC, REQUESTS))) + .register(meterRegistry); + latency = Gauge.builder(name, () -> currentRequestsForLatency == 0 ? 0 : + (currentLatency) / ((double) (currentRequestsForLatency)) * SNV_MILLI_SECONDS) + .tags(tags.and(Tag.of(STATISTIC, LATENCY))) + .register(meterRegistry); } public DefaultEndpointMetric getMetric() { return metric; } - protected Measurement newMeasurement(Id id, long timestamp, Number n) { - return new Measurement(id, timestamp, n.doubleValue()); + public void destroy() { + this.meterRegistry.remove(connectCount); + this.meterRegistry.remove(disconnectCount); + this.meterRegistry.remove(connections); + this.meterRegistry.remove(bytesRead); + this.meterRegistry.remove(bytesWritten); + this.meterRegistry.remove(requests); + this.meterRegistry.remove(latency); } - public void calcMeasurements(List measurements, long msNow, double secondInterval) { - long connectCount = metric.getConnectCount(); - long disconnectCount = metric.getDisconnectCount(); - long bytesRead = metric.getBytesRead(); - long bytesWritten = metric.getBytesWritten(); - long requests = metric.getRequests(); - long latency = metric.getLatency(); - - measurements.add(newMeasurement(idConnect, msNow, connectCount - lastConnectCount)); - measurements.add(newMeasurement(idDisconnect, msNow, disconnectCount - lastDisconnectCount)); - measurements.add(newMeasurement(idConnections, msNow, connectCount - disconnectCount)); - measurements.add(newMeasurement(idBytesRead, msNow, (bytesRead - lastBytesRead) / secondInterval)); - measurements.add(newMeasurement(idBytesWritten, msNow, (bytesWritten - lastBytesWritten) / secondInterval)); - measurements.add(newMeasurement(idRequests, msNow, requests - lastRequests)); - measurements.add(newMeasurement(idLatency, msNow, - requests - lastRequests == 0 ? 0 : (latency - lastLatency) / (requests - lastRequests) * SNV_MILLI_SECONDS)); - - this.lastConnectCount = connectCount; - this.lastDisconnectCount = disconnectCount; - this.lastBytesRead = bytesRead; - this.lastBytesWritten = bytesWritten; - this.lastRequests = requests; - this.lastLatency = latency; + @Override + public void poll(long msNow, long secondInterval) { + long temp = metric.getConnectCount(); + currentConnectCount = temp - lastConnectCount; + lastConnectCount = temp; + + temp = metric.getDisconnectCount(); + currentDisconnectCount = temp - lastDisconnectCount; + lastDisconnectCount = temp; + + temp = metric.getBytesRead(); + currentBytesRead = temp - lastBytesRead; + lastBytesRead = temp; + + temp = metric.getBytesWritten(); + currentBytesWritten = temp - lastBytesWritten; + lastBytesWritten = temp; + + temp = metric.getRequests(); + currentRequests = temp - lastRequests; + lastRequests = temp; + + temp = metric.getRequests(); + currentRequestsForLatency = temp - lastRequestsForLatency; + lastRequestsForLatency = temp; + temp = metric.getLatency(); + currentLatency = temp - lastLatency; + lastLatency = temp; } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java index a960bf68023..a8e75dc013f 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointMeter.java @@ -16,29 +16,38 @@ */ package org.apache.servicecomb.metrics.core.meter.vertx; -import java.util.List; - import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; public class HttpClientEndpointMeter extends EndpointMeter { public static final String QUEUE_COUNT = "queueCount"; - private final Id idQueueCount; + private final Gauge queueCount; + + private long currentQueueCount; - public HttpClientEndpointMeter(Id id, DefaultEndpointMetric metric) { - super(id, metric); - idQueueCount = this.id.withTag(STATISTIC, QUEUE_COUNT); + public HttpClientEndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) { + super(meterRegistry, name, tags, metric); + queueCount = Gauge.builder(name, () -> currentQueueCount) + .tags(tags.and(Tag.of(STATISTIC, QUEUE_COUNT), Tag.of(ADDRESS, metric.getAddress()))) + .register(meterRegistry); + } + + @Override + public void destroy() { + super.destroy(); + this.meterRegistry.remove(queueCount); } @Override - public void calcMeasurements(List measurements, long msNow, double secondInterval) { - super.calcMeasurements(measurements, msNow, secondInterval); + public void poll(long msNow, long secondInterval) { + super.poll(msNow, secondInterval); - long queueCount = ((DefaultClientEndpointMetric) metric).getQueueCount(); - measurements.add(newMeasurement(idQueueCount, msNow, queueCount)); + currentQueueCount = ((DefaultClientEndpointMetric) metric).getQueueCount(); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java index 57430bae4f0..f68f8413cc5 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/HttpClientEndpointsMeter.java @@ -16,19 +16,20 @@ */ package org.apache.servicecomb.metrics.core.meter.vertx; -import java.util.Map; - +import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultClientEndpointMetricManager; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; -import com.netflix.spectator.api.Id; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; public class HttpClientEndpointsMeter extends VertxEndpointsMeter { - public HttpClientEndpointsMeter(Id id, Map endpointMetricMap) { - super(id, endpointMetricMap); + public HttpClientEndpointsMeter(MeterRegistry meterRegistry, String name, Tags tags, + DefaultClientEndpointMetricManager clientEndpointMetricManager) { + super(meterRegistry, name, tags, clientEndpointMetricManager.getClientEndpointMetricMap()); } @Override - protected EndpointMeter createEndpointMeter(Id id, DefaultEndpointMetric metric) { - return new HttpClientEndpointMeter(id, metric); + protected EndpointMeter createEndpointMeter(DefaultEndpointMetric metric) { + return new HttpClientEndpointMeter(meterRegistry, name, tags, metric); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java index b2e8b6f4096..bee385ef9b5 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointMeter.java @@ -16,33 +16,42 @@ */ package org.apache.servicecomb.metrics.core.meter.vertx; -import java.util.List; - import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultServerEndpointMetric; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.Gauge; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.Tags; public class ServerEndpointMeter extends EndpointMeter { public static final String REJECT_BY_CONNECTION_LIMIT = "rejectByConnectionLimit"; - private final Id idRejectByConnectionLimit; + private final Gauge rejectByConnectionLimit; private long lastRejectByConnectionLimit; - public ServerEndpointMeter(Id id, DefaultEndpointMetric metric) { - super(id, metric); - idRejectByConnectionLimit = this.id.withTag(STATISTIC, REJECT_BY_CONNECTION_LIMIT); + private long currentRejectByConnectionLimit; + + public ServerEndpointMeter(MeterRegistry meterRegistry, String name, Tags tags, DefaultEndpointMetric metric) { + super(meterRegistry, name, tags, metric); + rejectByConnectionLimit = Gauge.builder(name, () -> currentRejectByConnectionLimit) + .tags(tags.and(Tag.of(STATISTIC, REJECT_BY_CONNECTION_LIMIT), Tag.of(ADDRESS, metric.getAddress()))) + .register(meterRegistry); + } + + @Override + public void destroy() { + super.destroy(); + this.meterRegistry.remove(rejectByConnectionLimit); } @Override - public void calcMeasurements(List measurements, long msNow, double secondInterval) { - super.calcMeasurements(measurements, msNow, secondInterval); + public void poll(long msNow, long secondInterval) { + super.poll(msNow, secondInterval); - long rejectByConnectionLimit = ((DefaultServerEndpointMetric) metric).getRejectByConnectionLimitCount(); - measurements - .add(newMeasurement(idRejectByConnectionLimit, msNow, rejectByConnectionLimit - lastRejectByConnectionLimit)); - this.lastRejectByConnectionLimit = rejectByConnectionLimit; + long current = ((DefaultServerEndpointMetric) metric).getRejectByConnectionLimitCount(); + currentRejectByConnectionLimit = current - lastRejectByConnectionLimit; + lastRejectByConnectionLimit = current; } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointsMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointsMeter.java index 14c3f2cdc47..11f5a706523 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointsMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/ServerEndpointsMeter.java @@ -20,15 +20,17 @@ import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; -import com.netflix.spectator.api.Id; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; public class ServerEndpointsMeter extends VertxEndpointsMeter { - public ServerEndpointsMeter(Id id, Map endpointMetricMap) { - super(id, endpointMetricMap); + public ServerEndpointsMeter(MeterRegistry meterRegistry, String name, Tags tags, + Map endpointMetricMap) { + super(meterRegistry, name, tags, endpointMetricMap); } @Override - protected EndpointMeter createEndpointMeter(Id id, DefaultEndpointMetric metric) { - return new ServerEndpointMeter(id, metric); + protected EndpointMeter createEndpointMeter(DefaultEndpointMetric metric) { + return new ServerEndpointMeter(meterRegistry, name, tags, metric); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java index ab7f4d95dd8..fd4d10f0d41 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/meter/vertx/VertxEndpointsMeter.java @@ -16,66 +16,55 @@ */ package org.apache.servicecomb.metrics.core.meter.vertx; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; -import org.apache.servicecomb.foundation.metrics.meter.AbstractPeriodMeter; +import org.apache.servicecomb.foundation.metrics.meter.PeriodMeter; import org.apache.servicecomb.foundation.vertx.metrics.metric.DefaultEndpointMetric; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; -public class VertxEndpointsMeter extends AbstractPeriodMeter { +public class VertxEndpointsMeter implements PeriodMeter { private final Map endpointMetricMap; - private final Map endpointMeterMap = new ConcurrentHashMapEx<>(); + protected final MeterRegistry meterRegistry; - @SuppressWarnings("unchecked") - public VertxEndpointsMeter(Id id, Map endpointMetricMap) { - this.id = id; - this.endpointMetricMap = (Map) endpointMetricMap; - } + protected final String name; - @Override - public void calcMeasurements(long msNow, long secondInterval) { - List measurements = new ArrayList<>(); - calcMeasurements(measurements, msNow, secondInterval); - allMeasurements = measurements; - } + protected final Tags tags; - @Override - public void calcMeasurements(List measurements, long msNow, long secondInterval) { - syncMeters(); + private final Map endpointMeterMap = new ConcurrentHashMapEx<>(); - for (EndpointMeter meter : endpointMeterMap.values()) { - meter.calcMeasurements(measurements, msNow, secondInterval); - } + @SuppressWarnings("unchecked") + public VertxEndpointsMeter(MeterRegistry meterRegistry, String name, Tags tags, + Map endpointMetricMap) { + this.meterRegistry = meterRegistry; + this.name = name; + this.tags = tags; + this.endpointMetricMap = (Map) endpointMetricMap; } - private void syncMeters() { + private void syncMeters(long msNow, long secondInterval) { for (EndpointMeter meter : endpointMeterMap.values()) { if (!endpointMetricMap.containsKey(meter.getMetric().getAddress())) { - endpointMeterMap.remove(meter.getMetric().getAddress()); + EndpointMeter removed = endpointMeterMap.remove(meter.getMetric().getAddress()); + removed.destroy(); } } for (DefaultEndpointMetric metric : endpointMetricMap.values()) { - endpointMeterMap.computeIfAbsent(metric.getAddress(), addr -> createEndpointMeter(id, metric)); + EndpointMeter updated = endpointMeterMap.computeIfAbsent(metric.getAddress(), + address -> createEndpointMeter(metric)); + updated.poll(msNow, secondInterval); } } - protected EndpointMeter createEndpointMeter(Id id, DefaultEndpointMetric metric) { - return new EndpointMeter(id, metric); - } - - @Override - public Iterable measure() { - return allMeasurements; + protected EndpointMeter createEndpointMeter(DefaultEndpointMetric metric) { + return new EndpointMeter(meterRegistry, name, tags, metric); } @Override - public boolean hasExpired() { - return false; + public void poll(long msNow, long secondInterval) { + syncMeters(msNow, secondInterval); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/AbstractMeasurementNodeLogPublisher.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/AbstractMeasurementNodeLogPublisher.java index d5d478b0be5..cba8e334ce9 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/AbstractMeasurementNodeLogPublisher.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/AbstractMeasurementNodeLogPublisher.java @@ -16,8 +16,8 @@ */ package org.apache.servicecomb.metrics.core.publish; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; public abstract class AbstractMeasurementNodeLogPublisher { protected StringBuilder sb; diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ClientEndpointsLogPublisher.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ClientEndpointsLogPublisher.java index 57958ef9b00..0669283fc73 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ClientEndpointsLogPublisher.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ClientEndpointsLogPublisher.java @@ -22,8 +22,8 @@ import java.util.List; import org.apache.servicecomb.foundation.common.net.NetUtils; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.VertxMetersInitializer; import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; import org.apache.servicecomb.metrics.core.meter.vertx.HttpClientEndpointMeter; diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java index 47a3a7aefd6..73327df1385 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/DefaultLogPublisher.java @@ -22,20 +22,20 @@ import java.util.Map; import java.util.Map.Entry; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.common.net.NetUtils; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; import org.apache.servicecomb.foundation.metrics.PolledEvent; import org.apache.servicecomb.foundation.metrics.meter.LatencyDistributionConfig; import org.apache.servicecomb.foundation.metrics.meter.LatencyScopeConfig; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.metrics.core.VertxMetersInitializer; -import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.metrics.core.meter.os.NetMeter; import org.apache.servicecomb.metrics.core.meter.os.OsMeter; +import org.apache.servicecomb.metrics.core.meter.os.SystemMeter; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf; @@ -50,37 +50,36 @@ import com.google.common.base.Strings; import com.google.common.eventbus.EventBus; import com.google.common.eventbus.Subscribe; -import com.netflix.spectator.api.Meter; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; import io.vertx.core.Vertx; public class DefaultLogPublisher implements MetricsInitializer { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultLogPublisher.class); + private static final Logger LOGGER = LoggerFactory.getLogger("scb-metrics"); public static final String ENABLED = "servicecomb.metrics.publisher.defaultLog.enabled"; // for a client, maybe will connect to too many endpoints, so default not print detail, just print summary public static final String ENDPOINTS_CLIENT_DETAIL_ENABLED = "servicecomb.metrics.publisher.defaultLog.endpoints.client.detail.enabled"; - private static final String FIRST_LINE_SIMPLE_FORMAT = " %-11s %-8.1f %-18s %s%s\n"; + private static final String FIRST_LINE_SIMPLE_FORMAT = " %-11s\n"; private static final String SIMPLE_FORMAT = " %-8.1f %-18s %s%s\n"; //details private static final String PRODUCER_DETAILS_FORMAT = "" - + " prepare: %-18s queue : %-18s filtersReq : %-18s handlersReq: %s\n" - + " execute: %-18s handlersResp: %-18s filtersResp: %-18s sendResp : %s\n"; + + " prepare: %-18s decode-request : %-18s queue : %-18s business-execute: %s\n" + + " encode-response: %-18s send: %-18s\n"; private static final String CONSUMER_DETAILS_FORMAT = "" - + " prepare : %-18s handlersReq : %-18s cFiltersReq: %-18s sendReq : %s\n" - + " getConnect : %-18s writeBuf : %-18s waitResp : %-18s wakeConsumer: %s\n" - + " cFiltersResp: %-18s handlersResp: %s\n"; + + " prepare : %-18s connection : %-18s encode-request: %-18s send : %s\n" + + " wait : %-18s decode-response : %-18s\n"; private static final String EDGE_DETAILS_FORMAT = "" - + " prepare : %-18s queue : %-18s sFiltersReq : %-18s handlersReq : %s\n" - + " cFiltersReq : %-18s sendReq : %-18s getConnect : %-18s writeBuf : %s\n" - + " waitResp : %-18s wakeConsumer: %-18s cFiltersResp: %-18s handlersResp: %s\n" - + " sFiltersResp: %-18s sendResp : %s\n"; + + " prepare : %-18s provider-decode : %-18s connection : %-18s consumer-encode : %s\n" + + " consumer-send : %-18s wait : %-18s consumer-decode : %-18s provider-encode : %s\n" + + " provider-send : %-18s\n"; private LatencyDistributionConfig latencyDistributionConfig; @@ -104,7 +103,7 @@ public void setEnvironment(Environment environment) { } @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { if (!environment.getProperty(ENABLED, boolean.class, false)) { return; } @@ -121,7 +120,7 @@ private void initLatencyDistribution(MetricsBootstrapConfig config) { latencyDistributionConfig = new LatencyDistributionConfig(config.getLatencyDistribution()); String header; for (LatencyScopeConfig scopeConfig : latencyDistributionConfig.getScopeConfigs()) { - if (scopeConfig.getMsMax() == Long.MAX_VALUE) { + if (scopeConfig.getMsMax() == LatencyDistributionConfig.MAX_LATENCY) { header = String.format("[%d,) ", scopeConfig.getMsMin()); } else { header = String.format("[%d,%d) ", scopeConfig.getMsMin(), scopeConfig.getMsMax()); @@ -183,10 +182,10 @@ private void printNetLog(StringBuilder sb, MeasurementNode osNode) { StringBuilder tmpSb = new StringBuilder(); for (MeasurementNode interfaceNode : netNode.getChildren().values()) { - double sendRate = interfaceNode.findChild(NetMeter.TAG_SEND.value()).summary(); - double sendPacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_SEND.value()).summary(); - double receiveRate = interfaceNode.findChild(NetMeter.TAG_RECEIVE.value()).summary(); - double receivePacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_RECEIVE.value()).summary(); + double sendRate = interfaceNode.findChild(NetMeter.TAG_SEND.getValue()).summary(); + double sendPacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_SEND.getValue()).summary(); + double receiveRate = interfaceNode.findChild(NetMeter.TAG_RECEIVE.getValue()).summary(); + double receivePacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_RECEIVE.getValue()).summary(); if (sendRate == 0 && receiveRate == 0 && receivePacketsRate == 0 && sendPacketsRate == 0) { continue; } @@ -203,17 +202,14 @@ private void printNetLog(StringBuilder sb, MeasurementNode osNode) { } private void printCpuLog(StringBuilder sb, MeasurementNode osNode) { - MeasurementNode cpuNode = osNode.findChild(OsMeter.OS_TYPE_ALL_CPU); - MeasurementNode processNode = osNode.findChild(OsMeter.OS_TYPE_PROCESS_CPU); - if (cpuNode == null || cpuNode.getMeasurements().isEmpty() || - processNode == null || processNode.getMeasurements().isEmpty()) { - return; - } - double allRate = cpuNode.summary(); - double processRate = processNode.summary(); + MeasurementNode cpuNode = osNode.findChild(SystemMeter.CPU_USAGE); + MeasurementNode processNode = osNode.findChild(SystemMeter.PROCESS_CPU_USAGE); + MeasurementNode memoryNode = osNode.findChild(SystemMeter.MEMORY_USAGE); + MeasurementNode slaNode = osNode.findChild(SystemMeter.SYSTEM_LOAD_AVERAGE); appendLine(sb, " cpu:"); - appendLine(sb, " all usage: %.2f%% all idle: %.2f%% process: %.2f%%", allRate * 100, - (1 - allRate) * 100, processRate * 100); + appendLine(sb, " all usage: %.2f%% process usage: %.2f%% sla: %.2f memory usage: %.2f%%", + cpuNode.summary() * 100, processNode.summary() * 100, + slaNode.summary(), memoryNode.summary() * 100); } protected void printThreadPoolMetrics(DefaultPublishModel model, StringBuilder sb) { @@ -245,7 +241,7 @@ protected void printEdgeLog(DefaultPublishModel model, StringBuilder sb) { sb.append("" + "edge:\n" + " simple:\n" - + " status tps latency ") + + " status requests latency ") .append(latencyDistributionHeader) .append("operation\n"); StringBuilder detailsBuilder = new StringBuilder(); @@ -269,7 +265,7 @@ protected void printConsumerLog(DefaultPublishModel model, StringBuilder sb) { sb.append("" + "consumer:\n" + " simple:\n" - + " status tps latency ") + + " status requests latency ") .append(latencyDistributionHeader) .append("operation\n"); StringBuilder detailsBuilder = new StringBuilder(); @@ -293,7 +289,7 @@ protected void printProducerLog(DefaultPublishModel model, StringBuilder sb) { sb.append("" + "producer:\n" + " simple:\n" - + " status tps latency ") + + " status requests latency ") .append(latencyDistributionHeader) .append("operation\n"); // use detailsBuilder, we can traverse the map only once @@ -313,32 +309,24 @@ protected void printProducerLog(DefaultPublishModel model, StringBuilder sb) { private StringBuilder printSamplePerf(OperationPerfGroup perfGroup) { StringBuilder sb = new StringBuilder(); - boolean firstLine = true; + String status = perfGroup.getTransport() + "." + perfGroup.getStatus() + ":"; + sb.append(String.format(FIRST_LINE_SIMPLE_FORMAT, status)); + for (int i = 0; i < perfGroup.getOperationPerfs().size(); i++) { OperationPerf operationPerf = perfGroup.getOperationPerfs().get(i); - PerfInfo stageTotal = operationPerf.findStage(MeterInvocationConst.STAGE_TOTAL); - if (Double.compare(0D, stageTotal.getTps()) == 0) { + if (isIgnoreEmptyPerf(operationPerf)) { continue; } - if (firstLine) { - firstLine = false; - String status = perfGroup.getTransport() + "." + perfGroup.getStatus(); - sb.append(String.format(FIRST_LINE_SIMPLE_FORMAT, status, - stageTotal.getTps(), - getDetailsFromPerf(stageTotal), - formatLatencyDistribution(operationPerf), - operationPerf.getOperation())); - } else { - sb.append(String.format(SIMPLE_FORMAT, stageTotal.getTps(), - getDetailsFromPerf(stageTotal), - formatLatencyDistribution(operationPerf), - operationPerf.getOperation())); - } + PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); + sb.append(String.format(SIMPLE_FORMAT, stageTotal.getTotalRequests(), + getDetailsFromPerf(stageTotal), + formatLatencyDistribution(operationPerf), + operationPerf.getOperation())); } OperationPerf summaryOperation = perfGroup.getSummary(); - PerfInfo stageSummaryTotal = summaryOperation.findStage(MeterInvocationConst.STAGE_TOTAL); + PerfInfo stageSummaryTotal = summaryOperation.findStage(InvocationStageTrace.STAGE_TOTAL); //print summary - sb.append(String.format(SIMPLE_FORMAT, stageSummaryTotal.getTps(), + sb.append(String.format(SIMPLE_FORMAT, stageSummaryTotal.getTotalRequests(), getDetailsFromPerf(stageSummaryTotal), formatLatencyDistribution(summaryOperation), "(summary)")); @@ -357,32 +345,27 @@ private StringBuilder printProducerDetailsPerf(OperationPerfGroup perfGroup) { .append(".") .append(perfGroup.getStatus()) .append(":\n"); - PerfInfo prepare, queue, filtersReq, handlersReq, execute, handlersResp, filtersResp, sendResp; + PerfInfo prepare, queue, providerDecode, providerEncode, execute, sendResp; for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) { - PerfInfo stageTotal = operationPerf.findStage(MeterInvocationConst.STAGE_TOTAL); - if (Double.compare(0D, stageTotal.getTps()) == 0) { + if (isIgnoreEmptyPerf(operationPerf)) { continue; } - prepare = operationPerf.findStage(MeterInvocationConst.STAGE_PREPARE); - queue = operationPerf.findStage(MeterInvocationConst.STAGE_EXECUTOR_QUEUE); - filtersReq = operationPerf.findStage(MeterInvocationConst.STAGE_SERVER_FILTERS_REQUEST); - handlersReq = operationPerf.findStage(MeterInvocationConst.STAGE_HANDLERS_REQUEST); - execute = operationPerf.findStage(MeterInvocationConst.STAGE_EXECUTION); - handlersResp = operationPerf.findStage(MeterInvocationConst.STAGE_HANDLERS_RESPONSE); - filtersResp = operationPerf.findStage(MeterInvocationConst.STAGE_SERVER_FILTERS_RESPONSE); - sendResp = operationPerf.findStage(MeterInvocationConst.STAGE_PRODUCER_SEND_RESPONSE); + prepare = operationPerf.findStage(InvocationStageTrace.STAGE_PREPARE); + queue = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_QUEUE); + providerDecode = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST); + providerEncode = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE); + execute = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_BUSINESS); + sendResp = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_SEND); sb.append(" ") .append(operationPerf.getOperation()) .append(":\n") .append(String.format(PRODUCER_DETAILS_FORMAT, getDetailsFromPerf(prepare), + getDetailsFromPerf(providerDecode), getDetailsFromPerf(queue), - getDetailsFromPerf(filtersReq), - getDetailsFromPerf(handlersReq), getDetailsFromPerf(execute), - getDetailsFromPerf(handlersResp), - getDetailsFromPerf(filtersResp), + getDetailsFromPerf(providerEncode), getDetailsFromPerf(sendResp) )); } @@ -399,38 +382,29 @@ private StringBuilder printConsumerDetailsPerf(OperationPerfGroup perfGroup) { .append(perfGroup.getStatus()) .append(":\n"); - PerfInfo prepare, handlersReq, clientFiltersReq, sendReq, getConnect, writeBuf, - waitResp, wakeConsumer, clientFiltersResp, handlersResp; + PerfInfo prepare, encodeRequest, decodeResponse, sendReq, getConnect, + waitResp; for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) { - PerfInfo stageTotal = operationPerf.findStage(MeterInvocationConst.STAGE_TOTAL); - if (Double.compare(0D, stageTotal.getTps()) == 0) { + if (isIgnoreEmptyPerf(operationPerf)) { continue; } - prepare = operationPerf.findStage(MeterInvocationConst.STAGE_PREPARE); - handlersReq = operationPerf.findStage(MeterInvocationConst.STAGE_HANDLERS_REQUEST); - clientFiltersReq = operationPerf.findStage(MeterInvocationConst.STAGE_CLIENT_FILTERS_REQUEST); - sendReq = operationPerf.findStage(MeterInvocationConst.STAGE_CONSUMER_SEND_REQUEST); - getConnect = operationPerf.findStage(MeterInvocationConst.STAGE_CONSUMER_GET_CONNECTION); - writeBuf = operationPerf.findStage(MeterInvocationConst.STAGE_CONSUMER_WRITE_TO_BUF); - waitResp = operationPerf.findStage(MeterInvocationConst.STAGE_CONSUMER_WAIT_RESPONSE); - wakeConsumer = operationPerf.findStage(MeterInvocationConst.STAGE_CONSUMER_WAKE_CONSUMER); - clientFiltersResp = operationPerf.findStage(MeterInvocationConst.STAGE_CLIENT_FILTERS_RESPONSE); - handlersResp = operationPerf.findStage(MeterInvocationConst.STAGE_HANDLERS_RESPONSE); + prepare = operationPerf.findStage(InvocationStageTrace.STAGE_PREPARE); + encodeRequest = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST); + decodeResponse = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE); + sendReq = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_SEND); + getConnect = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_CONNECTION); + waitResp = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_WAIT); sb.append(" ") .append(operationPerf.getOperation()) .append(":\n") .append(String.format(CONSUMER_DETAILS_FORMAT, getDetailsFromPerf(prepare), - getDetailsFromPerf(handlersReq), - getDetailsFromPerf(clientFiltersReq), - getDetailsFromPerf(sendReq), getDetailsFromPerf(getConnect), - getDetailsFromPerf(writeBuf), + getDetailsFromPerf(encodeRequest), + getDetailsFromPerf(sendReq), getDetailsFromPerf(waitResp), - getDetailsFromPerf(wakeConsumer), - getDetailsFromPerf(clientFiltersResp), - getDetailsFromPerf(handlersResp) + getDetailsFromPerf(decodeResponse) )); } @@ -446,45 +420,35 @@ private StringBuilder printEdgeDetailsPerf(OperationPerfGroup perfGroup) { .append(perfGroup.getStatus()) .append(":\n"); - PerfInfo prepare, queue, serverFiltersReq, handlersReq, clientFiltersReq, sendReq, getConnect, writeBuf, - waitResp, wakeConsumer, clientFiltersResp, handlersResp, serverFiltersResp, sendResp; + PerfInfo prepare, connection, decodeProviderRequest, encodeProviderResponse, + encodeConsumerRequest, decodeConsumerResponse, sendReq, getConnect, + waitResp, sendResp; for (OperationPerf operationPerf : perfGroup.getOperationPerfs()) { - PerfInfo stageTotal = operationPerf.findStage(MeterInvocationConst.STAGE_TOTAL); - if (Double.compare(0D, stageTotal.getTps()) == 0) { + if (isIgnoreEmptyPerf(operationPerf)) { continue; } - prepare = operationPerf.findStage(MeterInvocationConst.STAGE_PREPARE); - queue = operationPerf.findStage(MeterInvocationConst.STAGE_EXECUTOR_QUEUE); - serverFiltersReq = operationPerf.findStage(MeterInvocationConst.STAGE_SERVER_FILTERS_REQUEST); - handlersReq = operationPerf.findStage(MeterInvocationConst.STAGE_HANDLERS_REQUEST); - clientFiltersReq = operationPerf.findStage(MeterInvocationConst.STAGE_CLIENT_FILTERS_REQUEST); - sendReq = operationPerf.findStage(MeterInvocationConst.STAGE_CONSUMER_SEND_REQUEST); - getConnect = operationPerf.findStage(MeterInvocationConst.STAGE_CONSUMER_GET_CONNECTION); - writeBuf = operationPerf.findStage(MeterInvocationConst.STAGE_CONSUMER_WRITE_TO_BUF); - waitResp = operationPerf.findStage(MeterInvocationConst.STAGE_CONSUMER_WAIT_RESPONSE); - wakeConsumer = operationPerf.findStage(MeterInvocationConst.STAGE_CONSUMER_WAKE_CONSUMER); - clientFiltersResp = operationPerf.findStage(MeterInvocationConst.STAGE_CLIENT_FILTERS_RESPONSE); - handlersResp = operationPerf.findStage(MeterInvocationConst.STAGE_HANDLERS_RESPONSE); - serverFiltersResp = operationPerf.findStage(MeterInvocationConst.STAGE_SERVER_FILTERS_RESPONSE); - sendResp = operationPerf.findStage(MeterInvocationConst.STAGE_PRODUCER_SEND_RESPONSE); + prepare = operationPerf.findStage(InvocationStageTrace.STAGE_PREPARE); + connection = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_CONNECTION); + decodeProviderRequest = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST); + encodeProviderResponse = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE); + encodeConsumerRequest = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST); + sendReq = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_SEND); + decodeConsumerResponse = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE); + waitResp = operationPerf.findStage(InvocationStageTrace.STAGE_CONSUMER_WAIT); + sendResp = operationPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_SEND); sb.append(" ") .append(operationPerf.getOperation()) .append(":\n") .append(String.format(EDGE_DETAILS_FORMAT, getDetailsFromPerf(prepare), - getDetailsFromPerf(queue), - getDetailsFromPerf(serverFiltersReq), - getDetailsFromPerf(handlersReq), - getDetailsFromPerf(clientFiltersReq), + getDetailsFromPerf(decodeProviderRequest), + getDetailsFromPerf(connection), + getDetailsFromPerf(encodeConsumerRequest), getDetailsFromPerf(sendReq), - getDetailsFromPerf(getConnect), - getDetailsFromPerf(writeBuf), getDetailsFromPerf(waitResp), - getDetailsFromPerf(wakeConsumer), - getDetailsFromPerf(clientFiltersResp), - getDetailsFromPerf(handlersResp), - getDetailsFromPerf(serverFiltersResp), + getDetailsFromPerf(decodeConsumerResponse), + getDetailsFromPerf(encodeProviderResponse), getDetailsFromPerf(sendResp) )); } @@ -492,6 +456,16 @@ private StringBuilder printEdgeDetailsPerf(OperationPerfGroup perfGroup) { return sb; } + private boolean isIgnoreEmptyPerf(OperationPerf operationPerf) { + PerfInfo stageTotal = operationPerf.findStage(InvocationStageTrace.STAGE_TOTAL); + // max latency is calculated in ring algorithm, maybe not 0 + if (Double.compare(0D, stageTotal.getTotalRequests()) == 0 + && Double.compare(0D, stageTotal.getMsMaxLatency()) == 0) { + return true; + } + return false; + } + protected void printVertxMetrics(MeasurementTree tree, StringBuilder sb) { appendLine(sb, "vertx:"); @@ -505,7 +479,7 @@ protected void printVertxMetrics(MeasurementTree tree, StringBuilder sb) { } ClientEndpointsLogPublisher client = new ClientEndpointsLogPublisher(tree, sb, - VertxMetersInitializer.ENDPOINTS_CLINET); + VertxMetersInitializer.ENDPOINTS_CLIENT); ServerEndpointsLogPublisher server = new ServerEndpointsLogPublisher(tree, sb, VertxMetersInitializer.ENDPOINTS_SERVER); if (client.isExists() || server.isExists()) { diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java index 69e1d7b3fa0..7fb9ad43cda 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishModelFactory.java @@ -18,21 +18,22 @@ import java.util.List; -import org.apache.servicecomb.foundation.metrics.publish.spectator.DefaultTagFinder; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementGroupConfig; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree; +import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.VertxMetersInitializer; +import org.apache.servicecomb.metrics.core.meter.ThreadPoolMonitorPublishModelFactory; import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.metrics.core.meter.os.NetMeter; import org.apache.servicecomb.metrics.core.meter.os.OsMeter; +import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; import org.apache.servicecomb.swagger.invocation.InvocationType; -import com.netflix.spectator.api.Meter; -import com.netflix.spectator.api.patterns.ThreadPoolMonitorPublishModelFactory; +import io.micrometer.core.instrument.Meter; public class PublishModelFactory { private final MeasurementTree tree; @@ -61,19 +62,19 @@ protected MeasurementGroupConfig createMeasurementGroupConfig() { MeterInvocationConst.TAG_OPERATION, MeterInvocationConst.TAG_STATUS, MeterInvocationConst.TAG_TYPE, - new DefaultTagFinder(MeterInvocationConst.TAG_STAGE, true), - MeterInvocationConst.TAG_STATISTIC); + new DefaultTagFinder(MeterInvocationConst.TAG_STAGE, true)); - //os config groupConfig.addGroup(OsMeter.OS_NAME, OsMeter.OS_TYPE, new DefaultTagFinder(NetMeter.INTERFACE, true), - NetMeter.STATISTIC); + new DefaultTagFinder(NetMeter.STATISTIC, true)); groupConfig.addGroup(VertxMetersInitializer.VERTX_ENDPOINTS, VertxMetersInitializer.ENDPOINTS_TYPE, - EndpointMeter.ADDRESS, - EndpointMeter.STATISTIC); + EndpointMeter.ADDRESS, EndpointMeter.STATISTIC); + + groupConfig.addGroup(ThreadPoolMeter.THREAD_POOL_METER, + ThreadPoolMeter.ID, ThreadPoolMeter.STAGE); return groupConfig; } @@ -108,11 +109,11 @@ public DefaultPublishModel createDefaultPublishModel() { model.getProducer() .setOperationPerfGroups(generateOperationPerfGroups(tree, InvocationType.PROVIDER.name())); - //edge + model.getEdge() .setOperationPerfGroups(generateOperationPerfGroups(tree, MeterInvocationConst.EDGE_INVOCATION_NAME)); - ThreadPoolMonitorPublishModelFactory.create(tree, model.getThreadPools()); + model.setThreadPools(ThreadPoolMonitorPublishModelFactory.create(tree)); return model; } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java index 3196120d7b4..9c761aaa5d4 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/PublishUtils.java @@ -19,14 +19,14 @@ import java.util.HashMap; import java.util.Map; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo; -import com.netflix.spectator.api.Statistic; +import io.micrometer.core.instrument.Statistic; public final class PublishUtils { private PublishUtils() { @@ -34,13 +34,9 @@ private PublishUtils() { public static PerfInfo createPerfInfo(MeasurementNode stageNode) { PerfInfo perfInfo = new PerfInfo(); - perfInfo.setTps(stageNode.findChild(Statistic.count.name()).summary()); - perfInfo.setMsTotalTime(stageNode.findChild(Statistic.totalTime.name()).summary() * 1000); - // when UT with DefaultRegistry, there is no max value - MeasurementNode maxNode = stageNode.findChild(Statistic.max.name()); - if (maxNode != null) { - perfInfo.setMsMaxLatency(maxNode.summary() * 1000); - } + perfInfo.setTotalRequests(stageNode.findChild(Statistic.COUNT.name()).summary()); + perfInfo.setMsTotalTime(stageNode.findChild(Statistic.TOTAL_TIME.name()).summary() * 1000); + perfInfo.setMsMaxLatency(stageNode.findChild(Statistic.MAX.name()).summary() * 1000); return perfInfo; } @@ -54,10 +50,10 @@ public static OperationPerf createOperationPerf(String operation, MeasurementNod operationPerf.getStages().put(mNode.getName(), perfInfo); }); - MeasurementNode latencyNode = statusNode.findChild(MeterInvocationConst.TAG_LATENCY_DISTRIBUTION); + MeasurementNode latencyNode = statusNode.findChild(MeterInvocationConst.TAG_DISTRIBUTION); if (latencyNode != null && latencyNode.getMeasurements() != null) { operationPerf.setLatencyDistribution(latencyNode.getMeasurements().stream() - .map(m -> (int) m.value()) + .map(m -> (int) m.getValue()) .toArray(Integer[]::new)); } return operationPerf; diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ServerEndpointsLogPublisher.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ServerEndpointsLogPublisher.java index 0e1ae12f466..115e9660009 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ServerEndpointsLogPublisher.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/ServerEndpointsLogPublisher.java @@ -19,8 +19,8 @@ import static org.apache.servicecomb.foundation.common.utils.StringBuilderUtils.appendLine; import org.apache.servicecomb.foundation.common.net.NetUtils; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.VertxMetersInitializer; import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; import org.apache.servicecomb.metrics.core.meter.vertx.ServerEndpointMeter; diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/SlowInvocationLogger.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/SlowInvocationLogger.java index a72f56250a2..797876aafb6 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/SlowInvocationLogger.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/SlowInvocationLogger.java @@ -16,6 +16,21 @@ */ package org.apache.servicecomb.metrics.core.publish; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_CONNECTION; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_SEND; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_CONSUMER_WAIT; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PREPARE; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_BUSINESS; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_QUEUE; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_PROVIDER_SEND; +import static org.apache.servicecomb.core.invocation.InvocationStageTrace.STAGE_TOTAL; + +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.servicecomb.common.rest.RestConst; @@ -35,7 +50,7 @@ import com.google.common.eventbus.Subscribe; public class SlowInvocationLogger { - private static final Logger LOGGER = LoggerFactory.getLogger(SlowInvocationLogger.class); + private static final Logger LOGGER = LoggerFactory.getLogger("scb-slow"); public SlowInvocationLogger(SCBEngine scbEngine) { scbEngine.getEventBus().register(this); @@ -47,12 +62,12 @@ public void onInvocationFinish(InvocationFinishEvent event) { Invocation invocation = event.getInvocation(); OperationConfig operationConfig = invocation.getOperationMeta().getConfig(); if (!operationConfig.isSlowInvocationEnabled() || - invocation.getInvocationStageTrace().calcTotalTime() < operationConfig.getNanoSlowInvocation()) { + invocation.getInvocationStageTrace().calcTotal() < operationConfig.getNanoSlowInvocation()) { return; } - if (!invocation.isConsumer()) { - logSlowProducer(invocation, event.getResponse(), operationConfig); + if (invocation.isProducer()) { + logSlowProvider(invocation, event.getResponse(), operationConfig); return; } @@ -64,142 +79,118 @@ public void onInvocationFinish(InvocationFinishEvent event) { logSlowConsumer(invocation, event.getResponse(), operationConfig); } - private String collectClientAddress(Invocation invocation) { + private static String collectClientAddress(Invocation invocation) { HttpServletRequestEx requestEx = invocation.getRequestEx(); return requestEx == null ? "unknown" : requestEx.getRemoteAddr() + ":" + requestEx.getRemotePort(); } - private String collectTargetAddress(Invocation invocation) { + private static String collectTargetAddress(Invocation invocation) { Endpoint endpoint = invocation.getEndpoint(); return endpoint == null ? "unknown" : endpoint.getEndpoint(); } - private String formatTime(double doubleNano) { - long micros = TimeUnit.NANOSECONDS.toMicros((long) doubleNano); + private static String formatTime(long doubleNano) { + long micros = TimeUnit.NANOSECONDS.toMicros(doubleNano); return micros / 1000 + "." + micros % 1000; } - private void logSlowProducer(Invocation invocation, Response response, OperationConfig operationConfig) { + private static void logSlowProvider(Invocation invocation, Response response, OperationConfig operationConfig) { RestOperationMeta restOperationMeta = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); InvocationStageTrace stageTrace = invocation.getInvocationStageTrace(); - invocation.getTraceIdLogger().warn(LOGGER, "" - + "slow({} ms) invocation, {}:\n" - + " http method: {}\n" - + " url : {}\n" - + " client : {}\n" - + " status code: {}\n" - + " total : {} ms\n" - + " prepare : {} ms\n" - + " threadPoolQueue : {} ms\n" - + " server filters request : {} ms\n" - + " handlers request : {} ms\n" - + " business execute : {} ms\n" - + " handlers response : {} ms\n" - + " server filters response: {} ms\n" - + " send response : {} ms", - operationConfig.getMsSlowInvocation(), - invocation.getInvocationQualifiedName(), - restOperationMeta.getHttpMethod(), - restOperationMeta.getAbsolutePath(), - collectClientAddress(invocation), - response.getStatusCode(), - formatTime(stageTrace.calcTotalTime()), - formatTime(stageTrace.calcInvocationPrepareTime()), - formatTime(stageTrace.calcThreadPoolQueueTime()), - formatTime(stageTrace.calcServerFiltersRequestTime()), - formatTime(stageTrace.calcHandlersRequestTime()), - formatTime(stageTrace.calcBusinessTime()), - formatTime(stageTrace.calcHandlersResponseTime()), - formatTime(stageTrace.calcServerFiltersResponseTime()), - formatTime(stageTrace.calcSendResponseTime()) - ); + StringBuilder sb = new StringBuilder(); + sb.append("Slow Provider invocation [").append(invocation.getInvocationQualifiedName()) + .append("](").append(operationConfig.getMsSlowInvocation()).append(" ms") + .append(")[").append(invocation.getTraceId()).append("]\n") + .append(formatPair(" ", "http method", restOperationMeta.getHttpMethod())) + .append(formatPair(" ", "url", restOperationMeta.getAbsolutePath())) + .append(formatPair(" ", "endpoint", collectClientAddress(invocation))) + .append(formatPair(" ", "status code", String.valueOf(response.getStatusCode()))) + + .append(formatPair(" ", STAGE_TOTAL, stageTrace.calcTotal())) + .append(formatPair(" ", STAGE_PREPARE, stageTrace.calcPrepare())) + .append(formatPair(" ", STAGE_PROVIDER_DECODE_REQUEST, stageTrace.calcProviderDecodeRequest())) + .append(formatPair(" ", STAGE_PROVIDER_QUEUE, stageTrace.calcQueue())) + .append(formatPair(" ", STAGE_PROVIDER_BUSINESS, stageTrace.calcBusinessExecute())) + .append(formatPair(" ", STAGE_PROVIDER_ENCODE_RESPONSE, stageTrace.calcProviderEncodeResponse())) + .append(formatPair(" ", STAGE_PROVIDER_SEND, stageTrace.calcProviderSendResponse())); + + List sorted = new ArrayList<>(stageTrace.getStages().keySet()); + sorted.stream().sorted().forEach(key -> { + sb.append(formatPair(" ", key, + InvocationStageTrace.calc(stageTrace.getStages().get(key).getEndTime(), + stageTrace.getStages().get(key).getBeginTime()))); + }); + + LOGGER.warn(sb.toString()); + } + + protected static String formatPair(String padding, String name, String value) { + return String.format("%-20s: %20s\n", padding + name, value); } - private void logSlowConsumer(Invocation invocation, Response response, OperationConfig operationConfig) { + protected static String formatPair(String padding, String name, long time) { + return String.format("%-20s: %8sms\n", padding + name, formatTime(time)); + } + + private static void logSlowConsumer(Invocation invocation, Response response, OperationConfig operationConfig) { RestOperationMeta restOperationMeta = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); InvocationStageTrace stageTrace = invocation.getInvocationStageTrace(); - invocation.getTraceIdLogger().warn(LOGGER, "" - + "slow({} ms) invocation, {}:\n" - + " http method: {}\n" - + " url : {}\n" - + " server : {}\n" - + " status code: {}\n" - + " total : {} ms\n" - + " prepare : {} ms\n" - + " handlers request : {} ms\n" - + " client filters request : {} ms\n" - + " send request : {} ms\n" - + " get connection : {} ms\n" - + " write to buf : {} ms\n" - + " wait response : {} ms\n" - + " wake consumer : {} ms\n" - + " client filters response: {} ms\n" - + " handlers response : {} ms", - operationConfig.getMsSlowInvocation(), - invocation.getInvocationQualifiedName(), - restOperationMeta.getHttpMethod(), - restOperationMeta.getAbsolutePath(), - collectTargetAddress(invocation), - response.getStatusCode(), - formatTime(stageTrace.calcTotalTime()), - formatTime(stageTrace.calcInvocationPrepareTime()), - formatTime(stageTrace.calcHandlersRequestTime()), - formatTime(stageTrace.calcClientFiltersRequestTime()), - formatTime(stageTrace.calcSendRequestTime()), - formatTime(stageTrace.calcGetConnectionTime()), - formatTime(stageTrace.calcWriteToBufferTime()), - formatTime(stageTrace.calcReceiveResponseTime()), - formatTime(stageTrace.calcWakeConsumer()), - formatTime(stageTrace.calcClientFiltersResponseTime()), - formatTime(stageTrace.calcHandlersResponseTime()) - ); + StringBuilder sb = new StringBuilder(); + sb.append("Slow Consumer invocation [").append(invocation.getInvocationQualifiedName()) + .append("](").append(operationConfig.getMsSlowInvocation()).append(" ms") + .append(")[").append(invocation.getTraceId()).append("]\n") + .append(formatPair(" ", "http method", restOperationMeta.getHttpMethod())) + .append(formatPair(" ", "url", restOperationMeta.getAbsolutePath())) + .append(formatPair(" ", "endpoint", collectTargetAddress(invocation))) + .append(formatPair(" ", "status code", String.valueOf(response.getStatusCode()))) + .append(formatPair(" ", STAGE_TOTAL, stageTrace.calcTotal())) + .append(formatPair(" ", STAGE_PREPARE, stageTrace.calcPrepare())) + .append(formatPair(" ", STAGE_CONSUMER_CONNECTION, stageTrace.calcConnection())) + .append(formatPair(" ", STAGE_CONSUMER_ENCODE_REQUEST, stageTrace.calcConsumerEncodeRequest())) + .append(formatPair(" ", STAGE_CONSUMER_SEND, stageTrace.calcConsumerSendRequest())) + .append(formatPair(" ", STAGE_CONSUMER_WAIT, stageTrace.calcWait())) + .append(formatPair(" ", STAGE_CONSUMER_DECODE_RESPONSE, stageTrace.calcConsumerDecodeResponse())); + + List sorted = new ArrayList<>(stageTrace.getStages().keySet()); + sorted.stream().sorted().forEach(key -> { + sb.append(formatPair(" ", key, + InvocationStageTrace.calc(stageTrace.getStages().get(key).getEndTime(), + stageTrace.getStages().get(key).getBeginTime()))); + }); + + LOGGER.warn(sb.toString()); } - private void logSlowEdge(Invocation invocation, Response response, OperationConfig operationConfig) { + private static void logSlowEdge(Invocation invocation, Response response, OperationConfig operationConfig) { RestOperationMeta restOperationMeta = invocation.getOperationMeta().getExtData(RestConst.SWAGGER_REST_OPERATION); InvocationStageTrace stageTrace = invocation.getInvocationStageTrace(); - invocation.getTraceIdLogger().warn(LOGGER, "" - + "slow({} ms) invocation, {}:\n" - + " http method: {}\n" - + " url : {}\n" - + " server : {}\n" - + " status code: {}\n" - + " total : {} ms\n" - + " prepare : {} ms\n" - + " threadPoolQueue : {} ms\n" - + " server filters request : {} ms\n" - + " handlers request : {} ms\n" - + " client filters request : {} ms\n" - + " send request : {} ms\n" - + " get connection : {} ms\n" - + " write to buf : {} ms\n" - + " wait response : {} ms\n" - + " wake consumer : {} ms\n" - + " client filters response: {} ms\n" - + " handlers response : {} ms\n" - + " server filters response: {} ms\n" - + " send response : {} ms", - operationConfig.getMsSlowInvocation(), - invocation.getInvocationQualifiedName(), - restOperationMeta.getHttpMethod(), - restOperationMeta.getAbsolutePath(), - collectTargetAddress(invocation), - response.getStatusCode(), - formatTime(stageTrace.calcTotalTime()), - formatTime(stageTrace.calcInvocationPrepareTime()), - formatTime(stageTrace.calcThreadPoolQueueTime()), - formatTime(stageTrace.calcServerFiltersRequestTime()), - formatTime(stageTrace.calcHandlersRequestTime()), - formatTime(stageTrace.calcClientFiltersRequestTime()), - formatTime(stageTrace.calcSendRequestTime()), - formatTime(stageTrace.calcGetConnectionTime()), - formatTime(stageTrace.calcWriteToBufferTime()), - formatTime(stageTrace.calcReceiveResponseTime()), - formatTime(stageTrace.calcWakeConsumer()), - formatTime(stageTrace.calcClientFiltersResponseTime()), - formatTime(stageTrace.calcHandlersResponseTime()), - formatTime(stageTrace.calcServerFiltersResponseTime()), - formatTime(stageTrace.calcSendResponseTime()) - ); + StringBuilder sb = new StringBuilder(); + sb.append("Slow Edge invocation [").append(invocation.getInvocationQualifiedName()) + .append("](").append(operationConfig.getMsSlowInvocation()).append(" ms") + .append(")[").append(invocation.getTraceId()).append("]\n") + .append(formatPair(" ", "http method", restOperationMeta.getHttpMethod())) + .append(formatPair(" ", "url", restOperationMeta.getAbsolutePath())) + .append(formatPair(" ", "endpoint", collectTargetAddress(invocation))) + .append(formatPair(" ", "status code", String.valueOf(response.getStatusCode()))) + .append(formatPair(" ", STAGE_TOTAL, stageTrace.calcTotal())) + .append(formatPair(" ", STAGE_PREPARE, stageTrace.calcPrepare())) + .append(formatPair(" ", STAGE_PROVIDER_DECODE_REQUEST, stageTrace.calcProviderDecodeRequest())) + .append(formatPair(" ", STAGE_CONSUMER_CONNECTION, stageTrace.calcConnection())) + .append(formatPair(" ", STAGE_CONSUMER_ENCODE_REQUEST, stageTrace.calcConsumerEncodeRequest())) + .append(formatPair(" ", STAGE_CONSUMER_SEND, stageTrace.calcConsumerSendRequest())) + .append(formatPair(" ", STAGE_CONSUMER_WAIT, stageTrace.calcWait())) + .append(formatPair(" ", STAGE_CONSUMER_DECODE_RESPONSE, stageTrace.calcConsumerDecodeResponse())) + .append(formatPair(" ", STAGE_PROVIDER_ENCODE_RESPONSE, stageTrace.calcProviderEncodeResponse())) + .append(formatPair(" ", STAGE_PROVIDER_SEND, stageTrace.calcProviderSendResponse())) + ; + + List sorted = new ArrayList<>(stageTrace.getStages().keySet()); + sorted.stream().sorted().forEach(key -> { + sb.append(formatPair(" ", key, + InvocationStageTrace.calc(stageTrace.getStages().get(key).getEndTime(), + stageTrace.getStages().get(key).getBeginTime()))); + }); + + LOGGER.warn(sb.toString()); } } diff --git a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.java b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.java index fef78700c88..e9e3afbee5b 100644 --- a/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.java +++ b/metrics/metrics-core/src/main/java/org/apache/servicecomb/metrics/core/publish/model/invocation/PerfInfo.java @@ -17,18 +17,18 @@ package org.apache.servicecomb.metrics.core.publish.model.invocation; public class PerfInfo { - private double tps; + private double totalRequests; private double msTotalTime; private double msMaxLatency; - public double getTps() { - return tps; + public double getTotalRequests() { + return totalRequests; } - public void setTps(double tps) { - this.tps = tps; + public void setTotalRequests(double totalRequests) { + this.totalRequests = totalRequests; } public double getMsTotalTime() { @@ -48,7 +48,7 @@ public void setMsMaxLatency(double msMaxLatency) { } public void add(PerfInfo other) { - tps += other.tps; + totalRequests += other.totalRequests; msTotalTime += other.msTotalTime; if (msMaxLatency < other.msMaxLatency) { msMaxLatency = other.msMaxLatency; @@ -56,12 +56,12 @@ public void add(PerfInfo other) { } public double calcMsLatency() { - return (tps != 0) ? msTotalTime / tps : 0; + return (totalRequests != 0) ? msTotalTime / totalRequests : 0; } @Override public String toString() { - return "PerfInfo [tps=" + tps + ", msTotalTime=" + msTotalTime + ", msLatency=" + calcMsLatency() + return "PerfInfo [tps=" + totalRequests + ", msTotalTime=" + msTotalTime + ", msLatency=" + calcMsLatency() + ", msMaxLatency=" + msMaxLatency + "]"; } diff --git a/metrics/metrics-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/metrics/metrics-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index e6424025574..87677909bd6 100644 --- a/metrics/metrics-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/metrics/metrics-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -15,4 +15,4 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -org.apache.servicecomb.metrics.core.MetricsCoreConfiguration \ No newline at end of file +org.apache.servicecomb.metrics.core.MetricsCoreConfiguration diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java index f8921629997..f154ba65449 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/AssertUtil.java @@ -16,15 +16,15 @@ */ package org.apache.servicecomb.metrics.core; -import com.netflix.spectator.api.Measurement; +import java.util.List; + import org.junit.jupiter.api.Assertions; -import java.util.List; +import io.micrometer.core.instrument.Measurement; public class AssertUtil { - public static void assertMeasure(List measurements, int index, String expected) { - Assertions.assertEquals(String.format("Measurement(%s)", expected), measurements.get(index).toString()); - } - + public static void assertMeasure(List measurements, int index, String expected) { + Assertions.assertEquals(String.format("Measurement{%s}", expected), measurements.get(index).toString()); + } } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestDefaultRegistryInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestDefaultRegistryInitializer.java deleted file mode 100644 index 35fcd4ede18..00000000000 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestDefaultRegistryInitializer.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.metrics.core; - -import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN; -import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.DEFAULT_METRICS_WINDOW_TIME; -import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME; - -import java.util.List; - -import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; -import org.mockito.Mockito; -import org.springframework.core.env.Environment; - -import com.google.common.eventbus.EventBus; -import com.netflix.servo.DefaultMonitorRegistry; -import com.netflix.spectator.api.Registry; - -import mockit.Deencapsulation; - -public class TestDefaultRegistryInitializer { - GlobalRegistry globalRegistry = new GlobalRegistry(); - - List registries = Deencapsulation.getField(globalRegistry, "registries"); - - DefaultRegistryInitializer registryInitializer = new DefaultRegistryInitializer(); - - Environment environment = Mockito.mock(Environment.class); - - @Test - @SuppressWarnings("deprecation") - public void init() { - Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) - .thenReturn(DEFAULT_METRICS_WINDOW_TIME); - Mockito.when(environment.getProperty( - CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) - .thenReturn(7); - registryInitializer.init(globalRegistry, new EventBus(), new MetricsBootstrapConfig(environment)); - - Assertions.assertEquals(-10, registryInitializer.getOrder()); - Assertions.assertTrue(globalRegistry.getDefaultRegistry() instanceof com.netflix.spectator.servo.ServoRegistry); - Assertions.assertEquals(1, registries.size()); - Assertions.assertEquals(0, DefaultMonitorRegistry.getInstance().getRegisteredMonitors().size()); - - registryInitializer.destroy(); - - Assertions.assertEquals(0, registries.size()); - Assertions.assertEquals(0, DefaultMonitorRegistry.getInstance().getRegisteredMonitors().size()); - } - - @Test - public void destroy_notInit() { - // should not throw exception - registryInitializer.destroy(); - } -} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java index 54f491242bc..316b9bf9b53 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestInvocationMetersInitializer.java @@ -25,412 +25,388 @@ import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.event.InvocationFinishEvent; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementGroupConfig; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.swagger.invocation.InvocationType; -import org.junit.Before; -import org.junit.Test; +import org.apache.servicecomb.swagger.invocation.Response; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Registry; -import mockit.Expectations; -import mockit.Mocked; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +@ExtendWith(MockitoExtension.class) public class TestInvocationMetersInitializer { EventBus eventBus = new EventBus(); - GlobalRegistry globalRegistry = new GlobalRegistry(new ManualClock()); - - Registry registry = new DefaultRegistry(globalRegistry.getClock()); + MeterRegistry registry = new SimpleMeterRegistry(); InvocationMetersInitializer invocationMetersInitializer = new InvocationMetersInitializer(); - @Mocked + @Mock Invocation invocation; Environment environment = Mockito.mock(Environment.class); - @Before + @BeforeEach public void setup() { Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) .thenReturn(DEFAULT_METRICS_WINDOW_TIME); Mockito.when(environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); - globalRegistry.add(registry); - invocationMetersInitializer.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment)); + invocationMetersInitializer.init(registry, eventBus, new MetricsBootstrapConfig(environment)); } @Test - public void consumerInvocation(@Mocked InvocationFinishEvent event) { - new Expectations() { - { - invocation.isConsumer(); - result = true; - invocation.getInvocationType(); - result = InvocationType.CONSUMER; - invocation.getRealTransportName(); - result = CoreConst.RESTFUL; - invocation.getMicroserviceQualifiedName(); - result = "m.s.o"; - invocation.getInvocationStageTrace().calcTotalTime(); - result = 9; - invocation.getInvocationStageTrace().calcClientFiltersRequestTime(); - result = 9; - invocation.getInvocationStageTrace().calcSendRequestTime(); - result = 9; - invocation.getInvocationStageTrace().calcGetConnectionTime(); - result = 4; - invocation.getInvocationStageTrace().calcWriteToBufferTime(); - result = 5; - invocation.getInvocationStageTrace().calcWakeConsumer(); - result = 9; - invocation.getInvocationStageTrace().calcReceiveResponseTime(); - result = 9; - invocation.getInvocationStageTrace().calcClientFiltersResponseTime(); - result = 9; - invocation.getInvocationStageTrace().calcInvocationPrepareTime(); - result = 9; - invocation.getInvocationStageTrace().calcHandlersRequestTime(); - result = 9; - invocation.getInvocationStageTrace().calcHandlersResponseTime(); - result = 9; - - event.getInvocation(); - result = invocation; - } - }; + public void consumerInvocation() { + InvocationFinishEvent event = Mockito.mock(InvocationFinishEvent.class); + Mockito.when(invocation.isConsumer()).thenReturn(true); + Mockito.when(invocation.getInvocationType()).thenReturn(InvocationType.CONSUMER); + Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); + InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + Mockito.when(invocationStageTrace.calcTotal()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcConnection()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcConsumerEncodeRequest()).thenReturn(4L); + Mockito.when(invocationStageTrace.calcConsumerSendRequest()).thenReturn(5L); + Mockito.when(invocationStageTrace.calcWait()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcConsumerDecodeResponse()).thenReturn(9L); + Mockito.when(event.getInvocation()).thenReturn(invocation); + Response mockResponse = Mockito.spy(Response.class); + Mockito.when(event.getResponse()).thenReturn(mockResponse); + Mockito.doReturn(0).when(mockResponse).getStatusCode(); eventBus.post(event); eventBus.post(event); - globalRegistry.poll(1); - MeasurementTree tree = new MeasurementTree(); - tree.from(registry.iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME)); - List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME).getMeasurements(); + tree.from(registry.getMeters().iterator(), + new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage")); + List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-send") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.0E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=5.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-encode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=8.0E-9"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=4.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "wait") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); AssertUtil.assertMeasure(measurements, 0, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=total:statistic=count:status=0:transport=rest:type=stage,0,2.0"); + "statistic='COUNT', value=2.0"); AssertUtil.assertMeasure(measurements, 1, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=total:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); + "statistic='TOTAL_TIME', value=1.8E-8"); AssertUtil.assertMeasure(measurements, 2, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=total:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, 3, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=handlers_request:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, 4, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=handlers_request:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, 5, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=handlers_request:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, 6, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=handlers_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, 7, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=handlers_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, 8, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=handlers_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, 9, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=prepare:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, 10, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=prepare:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, 11, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=prepare:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, 12, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=client_filters_request:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, 13, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=client_filters_request:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, 14, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=client_filters_request:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, 15, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_send_request:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, 16, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_send_request:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, 17, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_send_request:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, 18, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_get_connection:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, 19, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_get_connection:statistic=totalTime:status=0:transport=rest:type=stage,0,8.0E-9"); - AssertUtil.assertMeasure(measurements, 20, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_get_connection:statistic=max:status=0:transport=rest:type=stage,0,4.0E-9"); - AssertUtil.assertMeasure(measurements, 21, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_write_to_buf:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, 22, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_write_to_buf:statistic=totalTime:status=0:transport=rest:type=stage,0,1.0E-8"); - AssertUtil.assertMeasure(measurements, 23, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_write_to_buf:statistic=max:status=0:transport=rest:type=stage,0,5.0E-9"); - AssertUtil.assertMeasure(measurements, 24, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_wait_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, 25, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_wait_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, 26, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_wait_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, 27, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_wake_consumer:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, 28, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_wake_consumer:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, 29, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=consumer_wake_consumer:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, 30, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=client_filters_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, 31, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=client_filters_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, 32, - "servicecomb.invocation:operation=m.s.o:role=CONSUMER:stage=client_filters_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-decode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); } @Test - public void edgeInvocation(@Mocked InvocationFinishEvent event) { - new Expectations() { - { - invocation.isConsumer(); - result = true; - invocation.isEdge(); - result = true; - invocation.getRealTransportName(); - result = CoreConst.RESTFUL; - invocation.getMicroserviceQualifiedName(); - result = "m.s.o"; - invocation.getInvocationStageTrace().calcTotalTime(); - result = 9; - invocation.getInvocationStageTrace().calcThreadPoolQueueTime(); - result = 9; - invocation.getInvocationStageTrace().calcClientFiltersRequestTime(); - result = 9; - invocation.getInvocationStageTrace().calcSendRequestTime(); - result = 9; - invocation.getInvocationStageTrace().calcGetConnectionTime(); - result = 4; - invocation.getInvocationStageTrace().calcWriteToBufferTime(); - result = 5; - invocation.getInvocationStageTrace().calcWakeConsumer(); - result = 9; - invocation.getInvocationStageTrace().calcReceiveResponseTime(); - result = 9; - invocation.getInvocationStageTrace().calcClientFiltersResponseTime(); - result = 9; - invocation.getInvocationStageTrace().calcInvocationPrepareTime(); - result = 9; - invocation.getInvocationStageTrace().calcHandlersRequestTime(); - result = 9; - invocation.getInvocationStageTrace().calcHandlersResponseTime(); - result = 9; - invocation.getInvocationStageTrace().calcSendResponseTime(); - result = 9; - invocation.getInvocationStageTrace().calcServerFiltersRequestTime(); - result = 9; - invocation.getInvocationStageTrace().calcServerFiltersResponseTime(); - result = 9; - event.getInvocation(); - result = invocation; - } - }; + public void edgeInvocation() { + InvocationFinishEvent event = Mockito.mock(InvocationFinishEvent.class); + Mockito.when(invocation.getInvocationType()).thenReturn(InvocationType.EDGE); + Mockito.when(invocation.isEdge()).thenReturn(true); + Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); + InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + Mockito.when(invocationStageTrace.calcTotal()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderDecodeRequest()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcConnection()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcConsumerEncodeRequest()).thenReturn(4L); + Mockito.when(invocationStageTrace.calcConsumerSendRequest()).thenReturn(5L); + Mockito.when(invocationStageTrace.calcConsumerDecodeResponse()).thenReturn(8L); + Mockito.when(invocationStageTrace.calcWait()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderEncodeResponse()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderSendResponse()).thenReturn(9L); + Mockito.when(event.getInvocation()).thenReturn(invocation); + Response mockResponse = Mockito.spy(Response.class); + Mockito.when(event.getResponse()).thenReturn(mockResponse); + Mockito.doReturn(0).when(mockResponse).getStatusCode(); eventBus.post(event); eventBus.post(event); - globalRegistry.poll(1); - MeasurementTree tree = new MeasurementTree(); - tree.from(registry.iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME)); - List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME).getMeasurements(); - int i = 0; - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=total:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=total:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=total:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=handlers_request:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=handlers_request:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=handlers_request:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=handlers_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=handlers_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=handlers_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=prepare:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=prepare:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=prepare:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=client_filters_request:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=client_filters_request:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=client_filters_request:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_send_request:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_send_request:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_send_request:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_get_connection:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_get_connection:statistic=totalTime:status=0:transport=rest:type=stage,0,8.0E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_get_connection:statistic=max:status=0:transport=rest:type=stage,0,4.0E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_write_to_buf:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_write_to_buf:statistic=totalTime:status=0:transport=rest:type=stage,0,1.0E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_write_to_buf:statistic=max:status=0:transport=rest:type=stage,0,5.0E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_wait_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_wait_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_wait_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_wake_consumer:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_wake_consumer:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=consumer_wake_consumer:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=client_filters_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=client_filters_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=client_filters_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=queue:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=queue:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=queue:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=server_filters_request:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=server_filters_request:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=server_filters_request:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=server_filters_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=server_filters_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=server_filters_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=producer_send_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=producer_send_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i, - "servicecomb.invocation:operation=m.s.o:role=EDGE:stage=producer_send_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); + tree.from(registry.getMeters().iterator(), + new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage")); + + List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-send") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.0E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=5.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "connection") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-encode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=8.0E-9"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=4.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "wait") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "consumer-decode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.6E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=8.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-decode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-encode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-send") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); } @Test - public void producerInvocation(@Mocked InvocationFinishEvent event) { - new Expectations() { - { - invocation.isConsumer(); - result = false; - invocation.getInvocationType(); - result = InvocationType.PROVIDER; - invocation.getRealTransportName(); - result = CoreConst.RESTFUL; - invocation.getMicroserviceQualifiedName(); - result = "m.s.o"; - invocation.getInvocationStageTrace().calcTotalTime(); - result = 9; - invocation.getInvocationStageTrace().calcInvocationPrepareTime(); - result = 9; - invocation.getInvocationStageTrace().calcHandlersRequestTime(); - result = 9; - invocation.getInvocationStageTrace().calcHandlersResponseTime(); - result = 9; - invocation.getInvocationStageTrace().calcThreadPoolQueueTime(); - result = 9; - invocation.getInvocationStageTrace().calcBusinessTime(); - result = 9; - invocation.getInvocationStageTrace().calcServerFiltersRequestTime(); - result = 9; - invocation.getInvocationStageTrace().calcServerFiltersResponseTime(); - result = 9; - invocation.getInvocationStageTrace().calcSendResponseTime(); - result = 9; - event.getInvocation(); - result = invocation; - } - }; + public void producerInvocation() { + InvocationFinishEvent event = Mockito.mock(InvocationFinishEvent.class); + Mockito.when(invocation.isConsumer()).thenReturn(false); + Mockito.when(invocation.getInvocationType()).thenReturn(InvocationType.PROVIDER); + Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); + InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + Mockito.when(invocationStageTrace.calcTotal()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderDecodeRequest()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcQueue()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcBusinessExecute()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderEncodeResponse()).thenReturn(9L); + Mockito.when(invocationStageTrace.calcProviderSendResponse()).thenReturn(9L); + Mockito.when(event.getInvocation()).thenReturn(invocation); + Response mockResponse = Mockito.spy(Response.class); + Mockito.when(event.getResponse()).thenReturn(mockResponse); + Mockito.doReturn(0).when(mockResponse).getStatusCode(); eventBus.post(event); eventBus.post(event); - globalRegistry.poll(1); - MeasurementTree tree = new MeasurementTree(); - tree.from(registry.iterator(), new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME)); - List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME).getMeasurements(); - int i = 0; - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=total:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=total:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=total:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=handlers_request:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=handlers_request:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=handlers_request:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=handlers_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=handlers_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=handlers_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=prepare:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=prepare:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=prepare:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=queue:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=queue:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=queue:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=execution:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=execution:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=execution:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=server_filters_request:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=server_filters_request:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=server_filters_request:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=server_filters_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=server_filters_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=server_filters_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=producer_send_response:statistic=count:status=0:transport=rest:type=stage,0,2.0"); - AssertUtil.assertMeasure(measurements, i++, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=producer_send_response:statistic=totalTime:status=0:transport=rest:type=stage,0,1.8000000000000002E-8"); - AssertUtil.assertMeasure(measurements, i, - "servicecomb.invocation:operation=m.s.o:role=PROVIDER:stage=producer_send_response:statistic=max:status=0:transport=rest:type=stage,0,9.000000000000001E-9"); + tree.from(registry.getMeters().iterator(), + new MeasurementGroupConfig(MeterInvocationConst.INVOCATION_NAME, "stage")); + + List measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "total") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "prepare") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "queue") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "execute") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-decode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-encode") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); + + measurements = tree.findChild(MeterInvocationConst.INVOCATION_NAME, "provider-send") + .getMeasurements(); + Assertions.assertEquals(3, measurements.size()); + AssertUtil.assertMeasure(measurements, 0, + "statistic='COUNT', value=2.0"); + AssertUtil.assertMeasure(measurements, 1, + "statistic='TOTAL_TIME', value=1.8E-8"); + AssertUtil.assertMeasure(measurements, 2, + "statistic='MAX', value=9.0E-9"); } } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java index 83bc88bc271..c9461668da1 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestOsMeterInitializer.java @@ -17,129 +17,95 @@ package org.apache.servicecomb.metrics.core; import java.io.File; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; -import org.apache.servicecomb.metrics.core.meter.os.CpuMeter; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.meter.os.NetMeter; import org.apache.servicecomb.metrics.core.meter.os.OsMeter; -import org.apache.servicecomb.metrics.core.meter.os.cpu.CpuUtils; +import org.apache.servicecomb.metrics.core.meter.os.SystemMeter; import org.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Mockito; import com.google.common.eventbus.EventBus; -import com.google.common.io.Files; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Registry; +import com.sun.management.OperatingSystemMXBean; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.mockito.junit.jupiter.MockitoExtension; +@ExtendWith(MockitoExtension.class) public class TestOsMeterInitializer { - GlobalRegistry globalRegistry = new GlobalRegistry(new ManualClock()); + MeterRegistry registry = new SimpleMeterRegistry(); - Registry registry = new DefaultRegistry(globalRegistry.getClock()); - - @Mocked + @Mock EventBus eventBus; @Test - public void init(@Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean) { + public void init() { List list = new ArrayList<>(); list.add("13 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); - new MockUp() { - //Files.readFirstLine - @Mock - public String readFirstLine(File file, Charset encoding) { - return list.get(0); - } - }; - new MockUp() { - @Mock - public List readLines(File file, Charset encoding) { - return list; - } - }; - new MockUp() { - @Mock - public int calcHertz() { - return 100; - } - }; - new MockUp() { - @Mock - RuntimeMXBean getRuntimeMXBean() { - return mxBean; - } - }; - new MockUp() { - @Mock - public Runtime getRuntime() { - return runtime; - } - }; - new Expectations() { - { - runtime.availableProcessors(); - result = 2; - } - }; - globalRegistry.add(registry); - OsMetersInitializer osMetersInitializer = new OsMetersInitializer(); - osMetersInitializer.setOsLinux(true); - osMetersInitializer.init(globalRegistry, eventBus, null); - OsMeter osMeter = osMetersInitializer.getOsMeter(); - Assertions.assertNotNull(osMeter); - Assertions.assertNotNull(osMeter.getCpuMeter()); - Assertions.assertNotNull(osMeter.getNetMeter()); - CpuMeter cpuMeter = osMeter.getCpuMeter(); - NetMeter netMeter = osMeter.getNetMeter(); - Assertions.assertEquals(0.0, cpuMeter.getProcessCpuUsage().getUsage(), 0.0); + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + OsMetersInitializer osMetersInitializer = new OsMetersInitializer(); + osMetersInitializer.init(registry, eventBus, null); - Assertions.assertEquals(0.0, cpuMeter.getAllCpuUsage().getUsage(), 0.0); + OsMeter osMeter = osMetersInitializer.getOsMeter(); + SystemMeter systemMeter = osMeter.getCpuMeter(); + OperatingSystemMXBean osBean = Mockito.mock(OperatingSystemMXBean.class); + Mockito.when(osBean.getCpuLoad()).thenReturn(3.2D); + Mockito.when(osBean.getProcessCpuLoad()).thenReturn(1.2D); + systemMeter.setOsBean(osBean); + NetMeter netMeter = osMeter.getNetMeter(); + netMeter.setOsLinux(true); - Map interfaceInfoMap = netMeter.getInterfaceUsageMap(); - Assertions.assertEquals(1, interfaceInfoMap.size()); - InterfaceUsage eth0 = interfaceInfoMap.get("eth0"); - Assertions.assertEquals(4, eth0.getNetStats().size()); - // recv Bps - Assertions.assertEquals(0L, eth0.getNetStats().get(0).getLastValue()); - Assertions.assertEquals(0, eth0.getNetStats().get(0).getRate(), 0.0); - Assertions.assertEquals(0, eth0.getNetStats().get(0).getIndex()); - // send Bps - Assertions.assertEquals(0L, eth0.getNetStats().get(1).getLastValue()); - Assertions.assertEquals(0, eth0.getNetStats().get(1).getRate(), 0.0); - Assertions.assertEquals(8, eth0.getNetStats().get(1).getIndex()); + osMetersInitializer.poll(System.currentTimeMillis(), 1000); - // recv pps - Assertions.assertEquals(0L, eth0.getNetStats().get(2).getLastValue()); - Assertions.assertEquals(0, eth0.getNetStats().get(2).getRate(), 0.0); - Assertions.assertEquals(1, eth0.getNetStats().get(2).getIndex()); + MeasurementTree tree = new MeasurementTree(); + MeasurementGroupConfig group = new MeasurementGroupConfig(); + group.addGroup(OsMeter.OS_NAME, OsMeter.OS_TYPE); + tree.from(registry.getMeters().iterator(), group); - // send pps - Assertions.assertEquals(0L, eth0.getNetStats().get(3).getLastValue()); - Assertions.assertEquals(0, eth0.getNetStats().get(3).getRate(), 0.0); - Assertions.assertEquals(9, eth0.getNetStats().get(3).getIndex()); - } + Assertions.assertEquals(1.2D, + tree.findChild(OsMeter.OS_NAME, SystemMeter.PROCESS_CPU_USAGE).summary(), 0.0); + Assertions.assertEquals(3.2D, + tree.findChild(OsMeter.OS_NAME, SystemMeter.CPU_USAGE).summary(), 0.0); - @Test - public void initFail() { - OsMetersInitializer osMetersInitializer = new OsMetersInitializer(); - osMetersInitializer.setOsLinux(false); - osMetersInitializer.init(null, eventBus, null); - Assertions.assertNull(osMetersInitializer.getOsMeter()); + Map interfaceInfoMap = netMeter.getInterfaceUsageMap(); + Assertions.assertEquals(1, interfaceInfoMap.size()); + InterfaceUsage eth0 = interfaceInfoMap.get("eth0"); + // recv Bps + Assertions.assertEquals(0L, eth0.getReceive().getLastValue()); + Assertions.assertEquals(0, eth0.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, eth0.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(0L, eth0.getSend().getLastValue()); + Assertions.assertEquals(0, eth0.getSend().getRate(), 0.0); + Assertions.assertEquals(8, eth0.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(0L, eth0.getPacketsReceive().getLastValue()); + Assertions.assertEquals(0, eth0.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(0L, eth0.getPacketsSend().getLastValue()); + Assertions.assertEquals(0, eth0.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); + } } } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java index ea908c88f0c..9c9698aed2b 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestThreadPoolMetersInitializer.java @@ -16,48 +16,43 @@ */ package org.apache.servicecomb.metrics.core; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.RunnableScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.ThreadPoolExecutor; import org.apache.servicecomb.core.SCBEngine; import org.apache.servicecomb.core.definition.MicroserviceMeta; import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.executor.GroupExecutor; +import org.apache.servicecomb.core.executor.ThreadPoolExecutorEx; import org.apache.servicecomb.foundation.common.utils.BeanUtils; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; -import org.hamcrest.MatcherAssert; -import org.hamcrest.Matchers; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.metrics.core.meter.pool.ThreadPoolMeter; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import org.mockito.Mockito; import org.springframework.context.ApplicationContext; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.patterns.PolledMeter; - +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; import mockit.Mocked; public class TestThreadPoolMetersInitializer { - GlobalRegistry globalRegistry = new GlobalRegistry(); - - Registry registry = new DefaultRegistry(new ManualClock()); + MeterRegistry registry = new SimpleMeterRegistry(); ThreadPoolMetersInitializer threadPoolMetersInitializer = new ThreadPoolMetersInitializer(); - ThreadPoolExecutor threadPoolExecutor = Mockito.mock(ThreadPoolExecutor.class); + ThreadPoolExecutorEx threadPoolExecutor = Mockito.mock(ThreadPoolExecutorEx.class); @Mocked BlockingQueue queue; @@ -130,21 +125,22 @@ void delayedExecute(RunnableScheduledFuture task) { } }; - globalRegistry.add(registry); - threadPoolMetersInitializer.init(globalRegistry, null, null); - - PolledMeter.update(registry); - List result = new ArrayList<>(); - registry.iterator().forEachRemaining(meter -> result.add(meter.measure().toString())); - - MatcherAssert.assertThat(result, - Matchers.containsInAnyOrder("[Measurement(threadpool.maxThreads:id=groupExecutor-group0,0,0.0)]", - "[Measurement(threadpool.rejectedTaskCount:id=groupExecutor-group0,0,0.0)]", - "[Measurement(threadpool.completedTaskCount:id=groupExecutor-group0,0,0.0)]", - "[Measurement(threadpool.currentThreadsBusy:id=groupExecutor-group0,0,0.0)]", - "[Measurement(threadpool.corePoolSize:id=groupExecutor-group0,0,0.0)]", - "[Measurement(threadpool.poolSize:id=groupExecutor-group0,0,0.0)]", - "[Measurement(threadpool.queueSize:id=groupExecutor-group0,0,10.0)]", - "[Measurement(threadpool.taskCount:id=groupExecutor-group0,0,0.0)]")); + threadPoolMetersInitializer.init(registry, null, null); + + MeasurementTree tree = new MeasurementTree(); + MeasurementGroupConfig group = new MeasurementGroupConfig(); + group.addGroup(ThreadPoolMeter.THREAD_POOL_METER, ThreadPoolMeter.ID, + ThreadPoolMeter.STAGE); + tree.from(registry.getMeters().iterator(), group); + + MeasurementNode node = tree.findChild(ThreadPoolMeter.THREAD_POOL_METER); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "maxThreads").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "rejectedCount").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "completedTaskCount").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "currentThreadsBusy").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "corePoolSize").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "poolSize").summary(), 0, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "queueSize").summary(), 10, 0); + Assertions.assertEquals(node.findChild("groupExecutor-group0", "taskCount").summary(), 0, 0); } } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java index 85a91c9f9a0..35ba63b3bdc 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/TestVertxMetersInitializer.java @@ -21,47 +21,42 @@ import static org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig.METRICS_WINDOW_TIME; import static org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher.ENDPOINTS_CLIENT_DETAIL_ENABLED; -import java.util.ArrayList; import java.util.List; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.PolledEvent; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.apache.servicecomb.foundation.vertx.SharedVertxFactory; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; import org.apache.servicecomb.metrics.core.publish.DefaultLogPublisher; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; -import com.google.common.collect.Lists; import com.google.common.eventbus.EventBus; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; -import com.netflix.spectator.api.Registry; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.vertx.core.AbstractVerticle; import io.vertx.core.DeploymentOptions; +import io.vertx.core.Future; import io.vertx.core.Promise; -import io.vertx.core.file.impl.FileResolverImpl; import io.vertx.core.http.HttpClient; import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpClientResponse; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServer; +import io.vertx.core.impl.SysProps; import io.vertx.ext.web.Router; public class TestVertxMetersInitializer { - GlobalRegistry globalRegistry = new GlobalRegistry(new ManualClock()); - - Registry registry = new DefaultRegistry(globalRegistry.getClock()); + MeterRegistry registry = new SimpleMeterRegistry(); EventBus eventBus = new EventBus(); @@ -71,6 +66,12 @@ public class TestVertxMetersInitializer { Environment environment = Mockito.mock(Environment.class); + LogCollector logCollector = new LogCollector(); + + static HttpClient client; + + static HttpServer server; + static int port; static String body = "body"; @@ -81,16 +82,17 @@ public void start(Promise startPromise) { Router mainRouter = Router.router(vertx); mainRouter.route("/").handler(context -> context.response().end(body)); - HttpServer server = vertx.createHttpServer(); + server = vertx.createHttpServer(); server.requestHandler(mainRouter); - server.listen(0, "0.0.0.0", ar -> { - if (ar.succeeded()) { - port = ar.result().actualPort(); + Future future = server.listen(0, "0.0.0.0"); + future.onComplete((s, f) -> { + if (f == null) { + port = s.actualPort(); startPromise.complete(); return; } - startPromise.fail(ar.cause()); + startPromise.fail(f); }); } } @@ -98,13 +100,16 @@ public void start(Promise startPromise) { public static class TestClientVerticle extends AbstractVerticle { @Override public void start(Promise startPromise) { - HttpClient client = vertx.createHttpClient(); - client.request(HttpMethod.GET, port, "127.0.0.1", "/", ar -> { - if (ar.succeeded()) { - HttpClientRequest request = ar.result(); - request.send(body, resp -> { - if (resp.succeeded()) { - resp.result().bodyHandler((buffer) -> startPromise.complete()); + client = vertx.createHttpClient(); + Future future = client.request(HttpMethod.GET, port, "127.0.0.1", "/"); + future.onComplete((s, f) -> { + if (f == null) { + Future responseFuture = s.send(body); + responseFuture.onComplete((rs, rf) -> { + if (rf == null) { + rs.bodyHandler((buffer) -> startPromise.complete()); + } else { + startPromise.fail(f); } }); } @@ -112,19 +117,26 @@ public void start(Promise startPromise) { } } - @Before + @BeforeEach public void setup() { Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); HttpClients.load(); } - @After - public void teardown() { + @AfterEach + public void tearDown() { + logCollector.clear(); HttpClients.destroy(); + if (client != null) { + client.shutdown(); + } + if (server != null) { + server.shutdown(); + } } @Test @@ -135,36 +147,27 @@ public void init() throws InterruptedException { CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(false); - - globalRegistry.add(registry); - vertxMetersInitializer.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment)); - logPublisher.setEnvironment(environment); - logPublisher.init(null, eventBus, null); VertxUtils .blockDeploy(SharedVertxFactory.getSharedVertx(environment), TestServerVerticle.class, new DeploymentOptions()); VertxUtils .blockDeploy(SharedVertxFactory.getSharedVertx(environment), TestClientVerticle.class, new DeploymentOptions()); - globalRegistry.poll(1); - List meters = Lists.newArrayList(registry.iterator()); - List measurements = new ArrayList<>(); - for (Meter meter : meters) { - meter.measure().forEach(measurements::add); - } + vertxMetersInitializer.init(registry, eventBus, new MetricsBootstrapConfig(environment)); + logPublisher.setEnvironment(environment); + logPublisher.init(registry, eventBus, null); - LogCollector logCollector = new LogCollector(); + vertxMetersInitializer.poll(0, 1); + List meters = registry.getMeters(); - testLog(logCollector, meters, measurements, true); + testLog(logCollector, meters, true); logCollector.clear(); - testLog(logCollector, meters, measurements, false); - logCollector.teardown(); + testLog(logCollector, meters, false); } - private void testLog(LogCollector logCollector, List meters, List measurements, - boolean printDetail) { + private void testLog(LogCollector logCollector, List meters, boolean printDetail) { Mockito.when(environment.getProperty(ENDPOINTS_CLIENT_DETAIL_ENABLED, boolean.class, true)).thenReturn(printDetail); - logPublisher.onPolledEvent(new PolledEvent(meters, measurements)); + logPublisher.onPolledEvent(new PolledEvent(meters)); StringBuilder sb = new StringBuilder(); logCollector.getEvents().forEach(event -> sb.append(event.getMessage()).append("\n")); diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestCpuMeter.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestCpuMeter.java deleted file mode 100644 index 8295bdceed9..00000000000 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestCpuMeter.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.metrics.core.meter.os; - -import java.io.File; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -import org.apache.servicecomb.metrics.core.meter.os.cpu.CpuUtils; -import org.junit.Test; - -import com.google.common.io.CharSource; -import com.google.common.io.Files; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; - -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; - -public class TestCpuMeter { - - @Test - public void testRefreshCpuSuccess(@Mocked Id id, @Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean, - @Mocked CharSource charSource) throws IOException { - new MockUp() { - @Mock - public CharSource asCharSource(File file, Charset encoding) { - return charSource; - } - }; - new MockUp() { - @Mock - RuntimeMXBean getRuntimeMXBean() { - return mxBean; - } - }; - new MockUp() { - @Mock - public int calcHertz() { - return 4; - } - }; - new MockUp() { - @Mock - public Runtime getRuntime() { - return runtime; - } - }; - new Expectations() { - { - runtime.availableProcessors(); - result = 2; - charSource.readFirstLine(); - result = "1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1"; - } - }; - CpuMeter cpuMeter = new CpuMeter(id); - Assertions.assertEquals(0.0, cpuMeter.getAllCpuUsage().getUsage(), 0.0); - Assertions.assertEquals(0.0, cpuMeter.getProcessCpuUsage().getUsage(), 0.0); - - new Expectations() { - { - charSource.readFirstLine(); - result = "2 2 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 2 2"; - } - }; - cpuMeter.update(); - - Assertions.assertEquals(0.875, cpuMeter.getAllCpuUsage().getUsage(), 0.0); - Assertions.assertEquals(0.5, cpuMeter.getProcessCpuUsage().getUsage(), 0.0); - } - - @Test - public void testRefreshError(@Mocked Id id, @Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean, - @Mocked CharSource charSource) throws IOException { - - new MockUp() { - @Mock - public CharSource asCharSource(File file, Charset encoding) { - return charSource; - } - }; - new MockUp() { - @Mock - public int calcHertz() { - return 4; - } - }; - new MockUp() { - @Mock - RuntimeMXBean getRuntimeMXBean() { - return mxBean; - } - }; - new MockUp() { - @Mock - public Runtime getRuntime() { - return runtime; - } - }; - new Expectations() { - { - runtime.availableProcessors(); - result = 2; - charSource.readFirstLine(); - result = "1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1"; - } - }; - CpuMeter cpuMeter = new CpuMeter(id); - Assertions.assertEquals(0.0, cpuMeter.getAllCpuUsage().getUsage(), 0.0); - Assertions.assertEquals(0.0, cpuMeter.getProcessCpuUsage().getUsage(), 0.0); - new Expectations() { - { - charSource.readFirstLine(); - result = "1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1"; - } - }; - cpuMeter.update(); - - Assertions.assertEquals(0.0, cpuMeter.getAllCpuUsage().getUsage(), 0.0); - Assertions.assertEquals(0.0, cpuMeter.getProcessCpuUsage().getUsage(), 0.0); - } - - @Test - public void testCalcMeasurements(@Mocked Id id, @Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean, - @Mocked CharSource charSource) throws IOException { - List measurements = new ArrayList<>(); - - new MockUp() { - @Mock - public CharSource asCharSource(File file, Charset encoding) { - return charSource; - } - }; - new MockUp() { - @Mock - public int calcHertz() { - return 4; - } - }; - new MockUp() { - @Mock - RuntimeMXBean getRuntimeMXBean() { - return mxBean; - } - }; - new MockUp() { - @Mock - public Runtime getRuntime() { - return runtime; - } - }; - new Expectations() { - { - runtime.availableProcessors(); - result = 2; - charSource.readFirstLine(); - result = "1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1"; - } - }; - CpuMeter cpuMeter = new CpuMeter(id); - - new Expectations() { - { - charSource.readFirstLine(); - result = "2 2 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 2 2"; - } - }; - cpuMeter.calcMeasurements(measurements, 0); - Assertions.assertEquals(2, measurements.size()); - Measurement measurement = measurements.get(0); - Assertions.assertEquals(0, measurement.timestamp()); - Assertions.assertEquals(0.875, measurement.value(), 0.0); - measurement = measurements.get(1); - Assertions.assertEquals(0, measurement.timestamp()); - Assertions.assertEquals(0.5, measurement.value(), 0.0); - } -} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java index 89b8b49e4e0..8ed2a920de7 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestNetMeter.java @@ -16,6 +16,9 @@ */ package org.apache.servicecomb.metrics.core.meter.os; +import static org.apache.servicecomb.metrics.core.meter.os.OsMeter.OS_TYPE; +import static org.apache.servicecomb.metrics.core.meter.os.OsMeter.OS_TYPE_NET; + import java.io.File; import java.nio.charset.Charset; import java.util.ArrayList; @@ -23,243 +26,280 @@ import java.util.Map; import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.foundation.metrics.publish.DefaultTagFinder; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.metrics.core.meter.os.net.InterfaceUsage; -import org.junit.Test; - -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; - -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; import org.junit.jupiter.api.Assertions; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + public class TestNetMeter { @Test - public void testNetRefreshUnchanged(@Mocked Id id) { + public void testNetRefreshUnchanged() { List list = new ArrayList<>(); list.add("useless"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); - new MockUp() { - @Mock - public List readLines(File file, Charset encoding) { - return list; + + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + NetMeter netMeter = new NetMeter(meterRegistry, "os", Tags.of(OS_TYPE, OS_TYPE_NET)); + list.remove(2); + list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); + netMeter.refreshNet(1); + + Map meterInterfaceInfoMap = netMeter.getInterfaceUsageMap(); + Assertions.assertTrue(meterInterfaceInfoMap.containsKey("eth0")); + + InterfaceUsage eth0 = meterInterfaceInfoMap.get("eth0"); + + Assertions.assertEquals("eth0", eth0.getName()); + + // recv Bps + Assertions.assertEquals(1L, eth0.getReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, eth0.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(1L, eth0.getSend().getLastValue()); + Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0); + Assertions.assertEquals(8, eth0.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); + + // measurement tree test + MeasurementGroupConfig group = new MeasurementGroupConfig(); + group.addGroup(OsMeter.OS_NAME, + OS_TYPE, + new DefaultTagFinder(NetMeter.INTERFACE, true), + new DefaultTagFinder(NetMeter.STATISTIC, true)); + + MeasurementTree tree = new MeasurementTree(); + tree.from(meterRegistry.getMeters().iterator(), group); + MeasurementNode osNode = tree.findChild(OsMeter.OS_NAME); + MeasurementNode netNode = osNode.findChild(OS_TYPE_NET); + Assertions.assertEquals(1, netNode.getChildren().size()); + for (MeasurementNode interfaceNode : netNode.getChildren().values()) { + double sendRate = interfaceNode.findChild(NetMeter.TAG_SEND.getValue()).summary(); + double sendPacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_SEND.getValue()).summary(); + double receiveRate = interfaceNode.findChild(NetMeter.TAG_RECEIVE.getValue()).summary(); + double receivePacketsRate = interfaceNode.findChild(NetMeter.TAG_PACKETS_RECEIVE.getValue()).summary(); + Assertions.assertEquals(1, sendRate, 0); + Assertions.assertEquals(1, sendPacketsRate, 0); + Assertions.assertEquals(1, receiveRate, 0); + Assertions.assertEquals(1, receivePacketsRate, 0); } - }; - NetMeter netMeter = new NetMeter(id); - list.remove(2); - list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); - netMeter.refreshNet(1); - Map meterInterfaceInfoMap = netMeter.getInterfaceUsageMap(); - Assertions.assertTrue(meterInterfaceInfoMap.containsKey("eth0")); - - InterfaceUsage eth0 = meterInterfaceInfoMap.get("eth0"); - - Assertions.assertEquals("eth0", eth0.getName()); - - Assertions.assertEquals(4, eth0.getNetStats().size()); - // recv Bps - Assertions.assertEquals(1L, eth0.getNetStats().get(0).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(0).getRate(), 0.0); - Assertions.assertEquals(0, eth0.getNetStats().get(0).getIndex()); - // send Bps - Assertions.assertEquals(1L, eth0.getNetStats().get(1).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(1).getRate(), 0.0); - Assertions.assertEquals(8, eth0.getNetStats().get(1).getIndex()); - - // recv pps - Assertions.assertEquals(1L, eth0.getNetStats().get(2).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(2).getRate(), 0.0); - Assertions.assertEquals(1, eth0.getNetStats().get(2).getIndex()); - - // send pps - Assertions.assertEquals(1L, eth0.getNetStats().get(3).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(3).getRate(), 0.0); - Assertions.assertEquals(9, eth0.getNetStats().get(3).getIndex()); + } } @Test - public void testNetRefreshAdd(@Mocked Id id) { + public void testNetRefreshAdd() { List list = new ArrayList<>(); list.add("useless"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); - new MockUp() { - @Mock - public List readLines(File file, Charset encoding) { - return list; - } - }; - NetMeter netMeter = new NetMeter(id); - Map netMap = netMeter.getInterfaceUsageMap(); - Assertions.assertEquals(1, netMap.size()); - list.remove(2); - list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); - list.add("lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); - netMeter.refreshNet(1); - Assertions.assertEquals(2, netMap.size()); - InterfaceUsage eth0 = netMap.get("eth0"); - Assertions.assertEquals("eth0", eth0.getName()); - Assertions.assertEquals(4, eth0.getNetStats().size()); - // recv Bps - Assertions.assertEquals(1L, eth0.getNetStats().get(0).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(0).getRate(), 0.0); - Assertions.assertEquals(0, eth0.getNetStats().get(0).getIndex()); - // send Bps - Assertions.assertEquals(1L, eth0.getNetStats().get(1).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(1).getRate(), 0.0); - Assertions.assertEquals(8, eth0.getNetStats().get(1).getIndex()); - - // recv pps - Assertions.assertEquals(1L, eth0.getNetStats().get(2).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(2).getRate(), 0.0); - Assertions.assertEquals(1, eth0.getNetStats().get(2).getIndex()); - - // send pps - Assertions.assertEquals(1L, eth0.getNetStats().get(3).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(3).getRate(), 0.0); - Assertions.assertEquals(9, eth0.getNetStats().get(3).getIndex()); - - InterfaceUsage lo = netMap.get("lo"); - Assertions.assertEquals("lo", lo.getName()); - - Assertions.assertEquals(4, lo.getNetStats().size()); - // recv Bps - Assertions.assertEquals(0L, lo.getNetStats().get(0).getLastValue()); - Assertions.assertEquals(0, lo.getNetStats().get(0).getRate(), 0.0); - Assertions.assertEquals(0, lo.getNetStats().get(0).getIndex()); - // send Bps - Assertions.assertEquals(0L, lo.getNetStats().get(1).getLastValue()); - Assertions.assertEquals(0, lo.getNetStats().get(1).getRate(), 0.0); - Assertions.assertEquals(8, lo.getNetStats().get(1).getIndex()); - - // recv pps - Assertions.assertEquals(0L, lo.getNetStats().get(2).getLastValue()); - Assertions.assertEquals(0, lo.getNetStats().get(2).getRate(), 0.0); - Assertions.assertEquals(1, lo.getNetStats().get(2).getIndex()); - - // send pps - Assertions.assertEquals(0L, lo.getNetStats().get(3).getLastValue()); - Assertions.assertEquals(0, lo.getNetStats().get(3).getRate(), 0.0); - Assertions.assertEquals(9, lo.getNetStats().get(3).getIndex()); + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty()); + netMeter.setOsLinux(true); + netMeter.poll(0, 0); + Map netMap = netMeter.getInterfaceUsageMap(); + Assertions.assertEquals(1, netMap.size()); + list.remove(2); + list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); + list.add("lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); + netMeter.refreshNet(1); + Assertions.assertEquals(2, netMap.size()); + InterfaceUsage eth0 = netMap.get("eth0"); + Assertions.assertEquals("eth0", eth0.getName()); + + // recv Bps + Assertions.assertEquals(1L, eth0.getReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, eth0.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(1L, eth0.getSend().getLastValue()); + Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0); + Assertions.assertEquals(8, eth0.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); + + InterfaceUsage lo = netMap.get("lo"); + Assertions.assertEquals("lo", lo.getName()); + + // recv Bps + Assertions.assertEquals(0L, lo.getReceive().getLastValue()); + Assertions.assertEquals(0, lo.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, lo.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(0L, lo.getSend().getLastValue()); + Assertions.assertEquals(0, lo.getSend().getRate(), 0.0); + Assertions.assertEquals(8, lo.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(0L, lo.getPacketsReceive().getLastValue()); + Assertions.assertEquals(0, lo.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, lo.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(0L, lo.getPacketsSend().getLastValue()); + Assertions.assertEquals(0, lo.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, lo.getPacketsSend().getIndex()); + } } @Test - public void testNetRefreshRemove(@Mocked Id id) { + public void testNetRefreshRemove() { List list = new ArrayList<>(); list.add("useless"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); list.add("lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); - new MockUp() { - @Mock - public List readLines(File file, Charset encoding) { - return list; - } - }; - NetMeter netMeter = new NetMeter(id); - Map netMap = netMeter.getInterfaceUsageMap(); - Assertions.assertEquals(2, netMap.size()); - InterfaceUsage lo = netMap.get("lo"); - InterfaceUsage eth0 = netMap.get("eth0"); - Assertions.assertEquals("lo", lo.getName()); - Assertions.assertEquals(4, lo.getNetStats().size()); - // recv Bps - Assertions.assertEquals(0L, lo.getNetStats().get(0).getLastValue()); - Assertions.assertEquals(0, lo.getNetStats().get(0).getRate(), 0.0); - Assertions.assertEquals(0, lo.getNetStats().get(0).getIndex()); - // send Bps - Assertions.assertEquals(0L, lo.getNetStats().get(1).getLastValue()); - Assertions.assertEquals(0, lo.getNetStats().get(1).getRate(), 0.0); - Assertions.assertEquals(8, lo.getNetStats().get(1).getIndex()); - - // recv pps - Assertions.assertEquals(0L, lo.getNetStats().get(2).getLastValue()); - Assertions.assertEquals(0, lo.getNetStats().get(2).getRate(), 0.0); - Assertions.assertEquals(1, lo.getNetStats().get(2).getIndex()); - - // send pps - Assertions.assertEquals(0L, lo.getNetStats().get(3).getLastValue()); - Assertions.assertEquals(0, lo.getNetStats().get(3).getRate(), 0.0); - Assertions.assertEquals(9, lo.getNetStats().get(3).getIndex()); - - Assertions.assertEquals("eth0", eth0.getName()); - Assertions.assertEquals(4, eth0.getNetStats().size()); - // recv Bps - Assertions.assertEquals(0L, eth0.getNetStats().get(0).getLastValue()); - Assertions.assertEquals(0, eth0.getNetStats().get(0).getRate(), 0.0); - Assertions.assertEquals(0, eth0.getNetStats().get(0).getIndex()); - // send Bps - Assertions.assertEquals(0L, eth0.getNetStats().get(1).getLastValue()); - Assertions.assertEquals(0, eth0.getNetStats().get(1).getRate(), 0.0); - Assertions.assertEquals(8, eth0.getNetStats().get(1).getIndex()); - - // recv pps - Assertions.assertEquals(0L, eth0.getNetStats().get(2).getLastValue()); - Assertions.assertEquals(0, eth0.getNetStats().get(2).getRate(), 0.0); - Assertions.assertEquals(1, eth0.getNetStats().get(2).getIndex()); - - // send pps - Assertions.assertEquals(0L, eth0.getNetStats().get(3).getLastValue()); - Assertions.assertEquals(0, eth0.getNetStats().get(3).getRate(), 0.0); - Assertions.assertEquals(9, eth0.getNetStats().get(3).getIndex()); - list.remove(2); - list.remove(2); - list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); - netMeter.refreshNet(1); - Assertions.assertNull(netMap.get("lo")); - Assertions.assertEquals(1, netMap.size()); - Assertions.assertEquals("eth0", eth0.getName()); - Assertions.assertEquals(4, eth0.getNetStats().size()); - // recv Bps - Assertions.assertEquals(1L, eth0.getNetStats().get(0).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(0).getRate(), 0.0); - Assertions.assertEquals(0, eth0.getNetStats().get(0).getIndex()); - // send Bps - Assertions.assertEquals(1L, eth0.getNetStats().get(1).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(1).getRate(), 0.0); - Assertions.assertEquals(8, eth0.getNetStats().get(1).getIndex()); - - // recv pps - Assertions.assertEquals(1L, eth0.getNetStats().get(2).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(2).getRate(), 0.0); - Assertions.assertEquals(1, eth0.getNetStats().get(2).getIndex()); - - // send pps - Assertions.assertEquals(1L, eth0.getNetStats().get(3).getLastValue()); - Assertions.assertEquals(1, eth0.getNetStats().get(3).getRate(), 0.0); - Assertions.assertEquals(9, eth0.getNetStats().get(3).getIndex()); - } + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty()); + netMeter.setOsLinux(true); + netMeter.poll(0, 1); + + Map netMap = netMeter.getInterfaceUsageMap(); + Assertions.assertEquals(2, netMap.size()); + InterfaceUsage lo = netMap.get("lo"); + InterfaceUsage eth0 = netMap.get("eth0"); + Assertions.assertEquals("lo", lo.getName()); + // recv Bps + Assertions.assertEquals(0L, lo.getReceive().getLastValue()); + Assertions.assertEquals(0, lo.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, lo.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(0L, lo.getSend().getLastValue()); + Assertions.assertEquals(0, lo.getSend().getRate(), 0.0); + Assertions.assertEquals(8, lo.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(0L, lo.getPacketsReceive().getLastValue()); + Assertions.assertEquals(0, lo.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, lo.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(0L, lo.getPacketsSend().getLastValue()); + Assertions.assertEquals(0, lo.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, lo.getPacketsSend().getIndex()); + + Assertions.assertEquals("eth0", eth0.getName()); + // recv Bps + Assertions.assertEquals(0L, eth0.getReceive().getLastValue()); + Assertions.assertEquals(0, eth0.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, eth0.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(0L, eth0.getSend().getLastValue()); + Assertions.assertEquals(0, eth0.getSend().getRate(), 0.0); + Assertions.assertEquals(8, eth0.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(0L, eth0.getPacketsReceive().getLastValue()); + Assertions.assertEquals(0, eth0.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); + // send pps + Assertions.assertEquals(0L, eth0.getPacketsSend().getLastValue()); + Assertions.assertEquals(0, eth0.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); + list.remove(2); + list.remove(2); + list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); + netMeter.refreshNet(1); + + Assertions.assertEquals("eth0", eth0.getName()); + + // recv Bps + Assertions.assertEquals(1L, eth0.getReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getReceive().getRate(), 0.0); + Assertions.assertEquals(0, eth0.getReceive().getIndex()); + // send Bps + Assertions.assertEquals(1L, eth0.getSend().getLastValue()); + Assertions.assertEquals(1, eth0.getSend().getRate(), 0.0); + Assertions.assertEquals(8, eth0.getSend().getIndex()); + + // recv pps + Assertions.assertEquals(1L, eth0.getPacketsReceive().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsReceive().getRate(), 0.0); + Assertions.assertEquals(1, eth0.getPacketsReceive().getIndex()); + + // send pps + Assertions.assertEquals(1L, eth0.getPacketsSend().getLastValue()); + Assertions.assertEquals(1, eth0.getPacketsSend().getRate(), 0.0); + Assertions.assertEquals(9, eth0.getPacketsSend().getIndex()); + } + + } @Test - public void testCalcMeasurements(@Mocked Id id) { + public void testCalcMeasurements() { List list = new ArrayList<>(); list.add("useless"); list.add("useless"); list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); - new MockUp() { - @Mock - public List readLines(File file, Charset encoding) { - return list; - } - }; - NetMeter netMeter = new NetMeter(id); - list.remove(2); - list.add("eth0: 3 1 0 0 0 0 0 1 3 1 1 0 0 0 0 0"); - List measurements = new ArrayList<>(); - netMeter.calcMeasurements(measurements, 0L, 1); - Assertions.assertEquals(4, measurements.size()); - Measurement receive = measurements.get(0); - Measurement send = measurements.get(1); - Measurement receivePackets = measurements.get(2); - Measurement sendPackets = measurements.get(3); - Assertions.assertEquals(3.0, send.value(), 0.0); - Assertions.assertEquals(1.0, sendPackets.value(), 0.0); - Assertions.assertEquals(3.0, receive.value(), 0.0); - Assertions.assertEquals(1.0, receivePackets.value(), 0.0); + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + + NetMeter netMeter = new NetMeter(meterRegistry, "net", Tags.empty()); + netMeter.setOsLinux(true); + list.remove(2); + list.add("eth0: 3 1 0 0 0 0 0 1 3 1 1 0 0 0 0 0"); + netMeter.poll(0, 1); + MeasurementTree tree = new MeasurementTree(); + tree.from(meterRegistry.getMeters().iterator(), + new MeasurementGroupConfig("net", "statistic")); + + Measurement receive = tree.findChild("net", "receive").getMeasurements().get(0); + Measurement send = tree.findChild("net", "send").getMeasurements().get(0); + Measurement receivePackets = tree.findChild("net", "receivePackets").getMeasurements().get(0); + Measurement sendPackets = tree.findChild("net", "sendPackets").getMeasurements().get(0); + Assertions.assertEquals(3.0, send.getValue(), 0.0); + Assertions.assertEquals(1.0, sendPackets.getValue(), 0.0); + Assertions.assertEquals(3.0, receive.getValue(), 0.0); + Assertions.assertEquals(1.0, receivePackets.getValue(), 0.0); + } + } } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestOsMeter.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestOsMeter.java deleted file mode 100644 index 375627d9bef..00000000000 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestOsMeter.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.metrics.core.meter.os; - - -import java.io.File; -import java.io.IOException; -import java.lang.management.ManagementFactory; -import java.lang.management.RuntimeMXBean; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.io.FileUtils; -import org.apache.servicecomb.metrics.core.meter.os.cpu.CpuUtils; -import org.junit.Test; - -import com.google.common.collect.Lists; -import com.google.common.io.CharSource; -import com.google.common.io.Files; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Registry; - -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; - -public class TestOsMeter { - Registry registry = new DefaultRegistry(new ManualClock()); - - @Test - public void testCalcMeasurement(@Mocked Runtime runtime, @Mocked RuntimeMXBean mxBean, - @Mocked CharSource charSource) throws IOException { - List list = new ArrayList<>(); - list.add("useless"); - list.add("useless"); - list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); - new MockUp() { - @Mock - public List readLines(File file, Charset encoding) { - return list; - } - }; - new MockUp() { - @Mock - public int calcHertz() { - return 4; - } - }; - new MockUp() { - @Mock - public CharSource asCharSource(File file, Charset encoding) { - return charSource; - } - }; - new MockUp() { - @Mock - RuntimeMXBean getRuntimeMXBean() { - return mxBean; - } - }; - new MockUp() { - @Mock - public Runtime getRuntime() { - return runtime; - } - }; - new Expectations() { - { - runtime.availableProcessors(); - result = 2; - charSource.readFirstLine(); - result = "1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1"; - } - }; - OsMeter osMeter = new OsMeter(registry); - list.clear(); - list.add("useless"); - list.add("useless"); - list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); - new Expectations() { - { - charSource.readFirstLine(); - result = "2 2 2 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2 2 2"; - } - }; - osMeter.calcMeasurements(1, 1); - ArrayList measurements = Lists.newArrayList(osMeter.measure()); - Assertions.assertEquals(6, measurements.size()); - Assertions.assertEquals(0.875, measurements.get(0).value(), 0.0); - Assertions.assertEquals(0.5, measurements.get(1).value(), 0.0); - Assertions.assertEquals(1.0, measurements.get(2).value(), 0.0); - Assertions.assertEquals(1.0, measurements.get(3).value(), 0.0); - Assertions.assertEquals(1.0, measurements.get(4).value(), 0.0); - Assertions.assertEquals(1.0, measurements.get(5).value(), 0.0); - } -} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestSystemMeter.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestSystemMeter.java new file mode 100644 index 00000000000..05d32250dbf --- /dev/null +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/meter/os/TestSystemMeter.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.metrics.core.meter.os; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementGroupConfig; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import com.google.common.io.CharSource; +import com.google.common.io.Files; +import com.sun.management.OperatingSystemMXBean; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; + +public class TestSystemMeter { + @Test + public void testCalcMeasurement() throws IOException { + MeterRegistry meterRegistry = new SimpleMeterRegistry(); + CharSource charSource = Mockito.mock(CharSource.class); + List list = new ArrayList<>(); + list.add("useless"); + list.add("useless"); + list.add("eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); + + try (MockedStatic fileUtilsMockedStatic = Mockito.mockStatic(FileUtils.class); + MockedStatic filesMockedStatic = Mockito.mockStatic(Files.class)) { + fileUtilsMockedStatic.when(() -> { + FileUtils.readLines(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(list); + filesMockedStatic.when(() -> { + Files.asCharSource(Mockito.any(File.class), Mockito.any(Charset.class)); + }).thenReturn(charSource); + + OsMeter osMeter = new OsMeter(meterRegistry); + list.clear(); + list.add("useless"); + list.add("useless"); + list.add("eth0: 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0"); + + SystemMeter systemMeter = osMeter.getCpuMeter(); + OperatingSystemMXBean osBean = Mockito.mock(OperatingSystemMXBean.class); + Mockito.when(osBean.getSystemLoadAverage()).thenReturn(0.775); + Mockito.when(osBean.getCpuLoad()).thenReturn(0.875); + Mockito.when(osBean.getProcessCpuLoad()).thenReturn(0.5); + Mockito.when(osBean.getTotalMemorySize()).thenReturn(1000000000L); + Mockito.when(osBean.getFreeMemorySize()).thenReturn(300000000L); + systemMeter.setOsBean(osBean); + NetMeter netMeter = osMeter.getNetMeter(); + netMeter.setOsLinux(true); + + osMeter.poll(0, 1); + + MeasurementTree tree = new MeasurementTree(); + tree.from(meterRegistry.getMeters().iterator(), + new MeasurementGroupConfig("os", "type")); + + Assertions.assertEquals(0.875, tree.findChild("os", "cpu").getMeasurements().get(0).getValue(), 0.0); + Assertions.assertEquals(0.5, tree.findChild("os", "processCpu").getMeasurements().get(0).getValue(), 0.0); + Assertions.assertEquals(0.7, tree.findChild("os", "memory").getMeasurements().get(0).getValue(), 0.0); + Assertions.assertEquals(0.775, tree.findChild("os", "sla").getMeasurements().get(0).getValue(), 0.0); + Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(0).getValue(), 0.0); + Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(1).getValue(), 0.0); + Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(2).getValue(), 0.0); + Assertions.assertEquals(1.0, tree.findChild("os", "net").getMeasurements().get(3).getValue(), 0.0); + } + } +} diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java index 2b136e7b5ae..a1867955a60 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestDefaultLogPublisher.java @@ -26,15 +26,14 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.PolledEvent; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.apache.servicecomb.foundation.vertx.VertxUtils; -import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.metrics.core.meter.os.OsMeter; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; @@ -52,8 +51,14 @@ import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; -import com.netflix.spectator.api.Measurement; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter.Id; +import io.micrometer.core.instrument.Meter.Type; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.Tags; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.vertx.core.impl.VertxImpl; import jakarta.ws.rs.core.Response.Status; import mockit.Expectations; @@ -63,7 +68,7 @@ @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestDefaultLogPublisher { - GlobalRegistry globalRegistry = new GlobalRegistry(); + MeterRegistry meterRegistry = new SimpleMeterRegistry(); EventBus eventBus = new EventBus(); @@ -84,8 +89,8 @@ public void setup() { } @After - public void teardown() { - collector.teardown(); + public void tearDown() { + collector.tearDown(); } @Test @@ -99,7 +104,7 @@ void register(Object object) { } }; - publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment)); + publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); Assertions.assertFalse(registered.value); } @@ -114,7 +119,7 @@ void register(Object object) { } }; - publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment)); + publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); Assertions.assertTrue(registered.value); } @@ -129,7 +134,7 @@ void register(Object object) { } }; - publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment)); + publisher.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); Assertions.assertNull(registered.value); } @@ -144,12 +149,12 @@ public void onPolledEvent_failed() { @Test public void onPolledEvent(@Injectable VertxImpl vertxImpl, @Injectable MeasurementTree tree, - @Injectable GlobalRegistry globalRegistry, @Injectable EventBus eventBus) { + @Injectable EventBus eventBus) { MetricsBootstrapConfig config = Mockito.mock(MetricsBootstrapConfig.class); try { Mockito.when(environment.getProperty(DefaultLogPublisher.ENABLED, boolean.class, false)).thenReturn(true); Mockito.when(config.getLatencyDistribution()).thenReturn("0,1,100"); - publisher.init(globalRegistry, eventBus, config); + publisher.init(meterRegistry, eventBus, config); new Expectations(VertxUtils.class) { { VertxUtils.getVertxMap(); @@ -158,28 +163,24 @@ public void onPolledEvent(@Injectable VertxImpl vertxImpl, @Injectable Measureme }; DefaultPublishModel model = new DefaultPublishModel(); PerfInfo perfTotal = new PerfInfo(); - perfTotal.setTps(10_0000); + perfTotal.setTotalRequests(10_0000); perfTotal.setMsTotalTime(30000L * 1_0000); perfTotal.setMsMaxLatency(30000); OperationPerf operationPerf = new OperationPerf(); operationPerf.setOperation("op"); operationPerf.setLatencyDistribution(new Integer[] {12, 120, 1200}); - operationPerf.getStages().put(MeterInvocationConst.STAGE_TOTAL, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_EXECUTOR_QUEUE, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_EXECUTION, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_PREPARE, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_HANDLERS_REQUEST, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_HANDLERS_RESPONSE, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_CLIENT_FILTERS_REQUEST, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_CLIENT_FILTERS_RESPONSE, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_CONSUMER_SEND_REQUEST, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_PRODUCER_SEND_RESPONSE, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_CONSUMER_GET_CONNECTION, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_CONSUMER_WRITE_TO_BUF, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_CONSUMER_WAIT_RESPONSE, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_CONSUMER_WAKE_CONSUMER, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_SERVER_FILTERS_REQUEST, perfTotal); - operationPerf.getStages().put(MeterInvocationConst.STAGE_SERVER_FILTERS_RESPONSE, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_TOTAL, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_QUEUE, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_ENCODE_REQUEST, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_DECODE_RESPONSE, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_DECODE_REQUEST, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_ENCODE_RESPONSE, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_BUSINESS, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PREPARE, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_WAIT, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_SEND, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_PROVIDER_SEND, perfTotal); + operationPerf.getStages().put(InvocationStageTrace.STAGE_CONSUMER_CONNECTION, perfTotal); OperationPerfGroup operationPerfGroup = new OperationPerfGroup(CoreConst.RESTFUL, Status.OK.name()); operationPerfGroup.addOperationPerf(operationPerf); @@ -192,22 +193,27 @@ public void onPolledEvent(@Injectable VertxImpl vertxImpl, @Injectable Measureme model.getEdge().setOperationPerfGroups(operationPerfGroups); model.getThreadPools().put("test", new ThreadPoolPublishModel()); - Measurement measurement = new Measurement(null, 0L, 1.0); + Id id = new Id("test", Tags.empty(), null, null, Type.OTHER); + Measurement measurement = new Measurement(() -> 1.0, Statistic.VALUE); - MeasurementNode measurementNodeCpuAll = new MeasurementNode("allProcess", new HashMap<>()); - MeasurementNode measurementNodeCpuProcess = new MeasurementNode("currentProcess", new HashMap<>()); - MeasurementNode measurementNodeSend = new MeasurementNode("send", new HashMap<>()); - MeasurementNode measurementNodeSendPacket = new MeasurementNode("sendPackets", new HashMap<>()); - MeasurementNode measurementNodeRecv = new MeasurementNode("receive", new HashMap<>()); - MeasurementNode measurementNodeRecvPacket = new MeasurementNode("receivePackets", new HashMap<>()); - MeasurementNode measurementNodeEth0 = new MeasurementNode("eth0", new HashMap<>()); - MeasurementNode measurementNodeNet = new MeasurementNode("net", new HashMap<>()); - MeasurementNode measurementNodeOs = new MeasurementNode("os", new HashMap<>()); + MeasurementNode measurementNodeCpuAll = new MeasurementNode("allProcess", id, new HashMap<>()); + MeasurementNode measurementNodeCpuProcess = new MeasurementNode("currentProcess", id, new HashMap<>()); + MeasurementNode measurementNodeSla = new MeasurementNode("sla", id, new HashMap<>()); + MeasurementNode measurementNodeMemory = new MeasurementNode("memory", id, new HashMap<>()); + MeasurementNode measurementNodeSend = new MeasurementNode("send", id, new HashMap<>()); + MeasurementNode measurementNodeSendPacket = new MeasurementNode("sendPackets", id, new HashMap<>()); + MeasurementNode measurementNodeRecv = new MeasurementNode("receive", id, new HashMap<>()); + MeasurementNode measurementNodeRecvPacket = new MeasurementNode("receivePackets", id, new HashMap<>()); + MeasurementNode measurementNodeEth0 = new MeasurementNode("eth0", id, new HashMap<>()); + MeasurementNode measurementNodeNet = new MeasurementNode("net", id, new HashMap<>()); + MeasurementNode measurementNodeOs = new MeasurementNode("os", id, new HashMap<>()); measurementNodeSend.getMeasurements().add(measurement); measurementNodeRecv.getMeasurements().add(measurement); measurementNodeCpuAll.getMeasurements().add(measurement); measurementNodeCpuProcess.getMeasurements().add(measurement); + measurementNodeSla.getMeasurements().add(measurement); + measurementNodeMemory.getMeasurements().add(measurement); measurementNodeRecvPacket.getMeasurements().add(measurement); measurementNodeSendPacket.getMeasurements().add(measurement); @@ -219,6 +225,8 @@ public void onPolledEvent(@Injectable VertxImpl vertxImpl, @Injectable Measureme measurementNodeNet.getChildren().put("eth0", measurementNodeEth0); measurementNodeOs.getChildren().put("cpu", measurementNodeCpuAll); measurementNodeOs.getChildren().put("processCpu", measurementNodeCpuProcess); + measurementNodeOs.getChildren().put("sla", measurementNodeSla); + measurementNodeOs.getChildren().put("memory", measurementNodeMemory); measurementNodeOs.getChildren().put("net", measurementNodeNet); measurementNodeOs.getMeasurements().add(measurement); @@ -242,58 +250,60 @@ MeasurementTree getTree() { result = measurementNodeOs; } }; - publisher.onPolledEvent(new PolledEvent(Collections.emptyList(), Collections.emptyList())); + publisher.onPolledEvent(new PolledEvent(Collections.emptyList())); List events = collector.getEvents().stream() - .filter(e -> DefaultLogPublisher.class.getName().equals(e.getLoggerName())).toList(); + .filter(e -> "scb-metrics".equals(e.getLoggerName())).toList(); LogEvent event = events.get(0); - Assertions.assertEquals("\n" - + "os:\n" - + " cpu:\n" - + " all usage: 100.00% all idle: 0.00% process: 100.00%\n" - + " net:\n" - + " send(Bps) recv(Bps) send(pps) recv(pps) interface\n" - + " 1 1 1 1 eth0\n" - + "vertx:\n" - + " instances:\n" - + " name eventLoopContext-created\n" - + " v 0\n" - + "threadPool:\n" - + " coreSize maxThreads poolSize currentBusy rejected queueSize taskCount taskFinished name\n" - + " 0 0 0 0 NaN 0 0.0 0.0 test\n" - + "consumer:\n" - + " simple:\n" - + " status tps latency [0,1) [1,100) [100,) operation\n" - + " rest.OK 100000.0 3000.000/30000.000 12 120 1200 op\n" - + " 100000.0 3000.000/30000.000 12 120 1200 (summary)\n" - + " details:\n" - + " rest.OK:\n" - + " op:\n" - + " prepare : 3000.000/30000.000 handlersReq : 3000.000/30000.000 cFiltersReq: 3000.000/30000.000 sendReq : 3000.000/30000.000\n" - + " getConnect : 3000.000/30000.000 writeBuf : 3000.000/30000.000 waitResp : 3000.000/30000.000 wakeConsumer: 3000.000/30000.000\n" - + " cFiltersResp: 3000.000/30000.000 handlersResp: 3000.000/30000.000\n" - + "producer:\n" - + " simple:\n" - + " status tps latency [0,1) [1,100) [100,) operation\n" - + " rest.OK 100000.0 3000.000/30000.000 12 120 1200 op\n" - + " 100000.0 3000.000/30000.000 12 120 1200 (summary)\n" - + " details:\n" - + " rest.OK:\n" - + " op:\n" - + " prepare: 3000.000/30000.000 queue : 3000.000/30000.000 filtersReq : 3000.000/30000.000 handlersReq: 3000.000/30000.000\n" - + " execute: 3000.000/30000.000 handlersResp: 3000.000/30000.000 filtersResp: 3000.000/30000.000 sendResp : 3000.000/30000.000\n" - + "edge:\n" - + " simple:\n" - + " status tps latency [0,1) [1,100) [100,) operation\n" - + " rest.OK 100000.0 3000.000/30000.000 12 120 1200 op\n" - + " 100000.0 3000.000/30000.000 12 120 1200 (summary)\n" - + " details:\n" - + " rest.OK:\n" - + " op:\n" - + " prepare : 3000.000/30000.000 queue : 3000.000/30000.000 sFiltersReq : 3000.000/30000.000 handlersReq : 3000.000/30000.000\n" - + " cFiltersReq : 3000.000/30000.000 sendReq : 3000.000/30000.000 getConnect : 3000.000/30000.000 writeBuf : 3000.000/30000.000\n" - + " waitResp : 3000.000/30000.000 wakeConsumer: 3000.000/30000.000 cFiltersResp: 3000.000/30000.000 handlersResp: 3000.000/30000.000\n" - + " sFiltersResp: 3000.000/30000.000 sendResp : 3000.000/30000.000\n", - event.getMessage().getFormattedMessage()); + Assertions.assertEquals(""" + os: + cpu: + all usage: 100.00% process usage: 100.00% sla: 1.00 memory usage: 100.00% + net: + send(Bps) recv(Bps) send(pps) recv(pps) interface + 1 1 1 1 eth0 + vertx: + instances: + name eventLoopContext-created + v 0 + threadPool: + coreSize maxThreads poolSize currentBusy rejected queueSize taskCount taskFinished name + 0 0 0 0 NaN 0 0.0 0.0 test + consumer: + simple: + status requests latency [0,1) [1,100) [100,) operation + rest.OK: \s + 100000.0 3000.000/30000.000 12 120 1200 op + 100000.0 3000.000/30000.000 12 120 1200 (summary) + details: + rest.OK: + op: + prepare : 3000.000/30000.000 connection : 3000.000/30000.000 encode-request: 3000.000/30000.000 send : 3000.000/30000.000 + wait : 3000.000/30000.000 decode-response : 3000.000/30000.000 + producer: + simple: + status requests latency [0,1) [1,100) [100,) operation + rest.OK: \s + 100000.0 3000.000/30000.000 12 120 1200 op + 100000.0 3000.000/30000.000 12 120 1200 (summary) + details: + rest.OK: + op: + prepare: 3000.000/30000.000 decode-request : 3000.000/30000.000 queue : 3000.000/30000.000 business-execute: 3000.000/30000.000 + encode-response: 3000.000/30000.000 send: 3000.000/30000.000 + edge: + simple: + status requests latency [0,1) [1,100) [100,) operation + rest.OK: \s + 100000.0 3000.000/30000.000 12 120 1200 op + 100000.0 3000.000/30000.000 12 120 1200 (summary) + details: + rest.OK: + op: + prepare : 3000.000/30000.000 provider-decode : 3000.000/30000.000 connection : 3000.000/30000.000 consumer-encode : 3000.000/30000.000 + consumer-send : 3000.000/30000.000 wait : 3000.000/30000.000 consumer-decode : 3000.000/30000.000 provider-encode : 3000.000/30000.000 + provider-send : 3000.000/30000.000 + """.trim(), + event.getMessage().getFormattedMessage().trim()); } catch (Exception e) { e.printStackTrace(); Assertions.fail("unexpected error happen. " + e.getMessage()); diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java index b0d22f7fe24..483aa6ab75d 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestInvocationPublishModelFactory.java @@ -26,381 +26,277 @@ import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; import org.apache.servicecomb.metrics.core.InvocationMetersInitializer; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; -import com.google.common.collect.Lists; import com.google.common.eventbus.EventBus; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Registry; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.vertx.core.json.Json; -import mockit.Deencapsulation; -import mockit.Expectations; -import mockit.Mock; -import mockit.MockUp; -import mockit.Mocked; + +import org.skyscreamer.jsonassert.JSONAssert; public class TestInvocationPublishModelFactory { EventBus eventBus = new EventBus(); - GlobalRegistry globalRegistry = new GlobalRegistry(); - - Registry registry = new DefaultRegistry(new ManualClock()); + // not step mode. + MeterRegistry meterRegistry = new SimpleMeterRegistry(); InvocationMetersInitializer invocationMetersInitializer = new InvocationMetersInitializer(); - @Mocked - Invocation invocation; + Invocation invocation = Mockito.mock(Invocation.class); - InvocationStageTrace invocationStageTrace = new InvocationStageTrace(invocation); + InvocationStageTrace invocationStageTrace = Mockito.mock(InvocationStageTrace.class); - @Mocked - Response response; + Response response = Mockito.mock(Response.class); InvocationType invocationType; Environment environment = Mockito.mock(Environment.class); @Test - public void createDefaultPublishModel() { + public void createDefaultPublishModel() throws Exception { Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) .thenReturn(DEFAULT_METRICS_WINDOW_TIME); Mockito.when(environment.getProperty( CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) .thenReturn(7); Mockito.when(environment.getProperty(CONFIG_LATENCY_DISTRIBUTION, String.class)).thenReturn("0,1,100"); - globalRegistry.add(registry); - invocationMetersInitializer.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment)); + + invocationMetersInitializer.init(meterRegistry, eventBus, new MetricsBootstrapConfig(environment)); prepareInvocation(); - globalRegistry.poll(1); - PublishModelFactory factory = new PublishModelFactory(Lists.newArrayList(registry.iterator())); + PublishModelFactory factory = new PublishModelFactory(meterRegistry.getMeters()); DefaultPublishModel model = factory.createDefaultPublishModel(); - String expect = "{\n" - + " \"operationPerfGroups\" : {\n" - + " \"groups\" : {\n" - + " \"rest\" : {\n" - + " \"200\" : {\n" - + " \"transport\" : \"rest\",\n" - + " \"status\" : \"200\",\n" - + " \"operationPerfs\" : [ {\n" - + " \"operation\" : \"m.s.o\",\n" - + " \"stages\" : {\n" - + " \"prepare\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"client_filters_request\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"total\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.4000000000000001E-5,\n" - + " \"msMaxLatency\" : 1.4000000000000001E-5\n" - + " },\n" - + " \"consumer_send_request\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 2.0000000000000003E-6,\n" - + " \"msMaxLatency\" : 2.0000000000000003E-6\n" - + " },\n" - + " \"handlers_request\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"handlers_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 3.0000000000000005E-6,\n" - + " \"msMaxLatency\" : 3.0000000000000005E-6\n" - + " },\n" - + " \"consumer_wait_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"client_filters_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"consumer_get_connection\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"consumer_write_to_buf\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"consumer_wake_consumer\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " }\n" - + " },\n" - + " \"latencyDistribution\" : [ 1, 0, 0 ]\n" - + " } ],\n" - + " \"summary\" : {\n" - + " \"operation\" : \"\",\n" - + " \"stages\" : {\n" - + " \"client_filters_request\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"prepare\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"consumer_send_request\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 2.0000000000000003E-6,\n" - + " \"msMaxLatency\" : 2.0000000000000003E-6\n" - + " },\n" - + " \"total\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.4000000000000001E-5,\n" - + " \"msMaxLatency\" : 1.4000000000000001E-5\n" - + " },\n" - + " \"handlers_request\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"client_filters_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"consumer_wait_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"handlers_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 3.0000000000000005E-6,\n" - + " \"msMaxLatency\" : 3.0000000000000005E-6\n" - + " },\n" - + " \"consumer_get_connection\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"consumer_wake_consumer\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"consumer_write_to_buf\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " }\n" - + " },\n" - + " \"latencyDistribution\" : [ 1, 0, 0 ]\n" - + " }\n" - + " }\n" - + " }\n" - + " }\n" - + " }\n" - + "}"; - Assertions.assertEquals(Json.encodePrettily(Json.decodeValue(expect, Object.class)), - Json.encodePrettily(model.getConsumer())); + String expect = """ + { + "operationPerfGroups" : { + "groups" : { + "rest" : { + "200" : { + "transport" : "rest", + "status" : "200", + "operationPerfs" : [ { + "operation" : "m.s.o", + "stages" : { + "consumer-encode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "prepare" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "wait" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "total" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.4E-5, + "msMaxLatency" : 1.4E-5 + }, + "consumer-send" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "connection" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-decode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + } + }, + "latencyDistribution" : [ 1, 1, 1 ] + } ], + "summary" : { + "operation" : "", + "stages" : { + "consumer-encode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "prepare" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "total" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.4E-5, + "msMaxLatency" : 1.4E-5 + }, + "wait" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-send" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "connection" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-decode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + } + }, + "latencyDistribution" : [ 1, 1, 1 ] + } + } + } + } + } + } + """; + JSONAssert.assertEquals(Json.encodePrettily(Json.decodeValue(expect, Object.class)), + Json.encodePrettily(model.getConsumer()), false); - expect = "{\n" - + " \"operationPerfGroups\" : {\n" - + " \"groups\" : {\n" - + " \"rest\" : {\n" - + " \"200\" : {\n" - + " \"transport\" : \"rest\",\n" - + " \"status\" : \"200\",\n" - + " \"operationPerfs\" : [ {\n" - + " \"operation\" : \"m.s.o\",\n" - + " \"stages\" : {\n" - + " \"server_filters_request\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 0.0,\n" - + " \"msMaxLatency\" : 0.0\n" - + " },\n" - + " \"prepare\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 5.0E-6,\n" - + " \"msMaxLatency\" : 5.0E-6\n" - + " },\n" - + " \"execution\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 3.0000000000000005E-6,\n" - + " \"msMaxLatency\" : 3.0000000000000005E-6\n" - + " },\n" - + " \"total\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.4000000000000001E-5,\n" - + " \"msMaxLatency\" : 1.4000000000000001E-5\n" - + " },\n" - + " \"producer_send_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 2.0000000000000003E-6,\n" - + " \"msMaxLatency\" : 2.0000000000000003E-6\n" - + " },\n" - + " \"handlers_request\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 6.000000000000001E-6,\n" - + " \"msMaxLatency\" : 6.000000000000001E-6\n" - + " },\n" - + " \"handlers_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"queue\" : {\n" - + " \"tps\" : 0.0,\n" - + " \"msTotalTime\" : 0.0,\n" - + " \"msMaxLatency\" : 0.0\n" - + " },\n" - + " \"server_filters_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " }\n" - + " },\n" - + " \"latencyDistribution\" : [ 1, 0, 0 ]\n" - + " } ],\n" - + " \"summary\" : {\n" - + " \"operation\" : \"\",\n" - + " \"stages\" : {\n" - + " \"server_filters_request\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 0.0,\n" - + " \"msMaxLatency\" : 0.0\n" - + " },\n" - + " \"execution\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 3.0000000000000005E-6,\n" - + " \"msMaxLatency\" : 3.0000000000000005E-6\n" - + " },\n" - + " \"prepare\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 5.0E-6,\n" - + " \"msMaxLatency\" : 5.0E-6\n" - + " },\n" - + " \"producer_send_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 2.0000000000000003E-6,\n" - + " \"msMaxLatency\" : 2.0000000000000003E-6\n" - + " },\n" - + " \"total\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.4000000000000001E-5,\n" - + " \"msMaxLatency\" : 1.4000000000000001E-5\n" - + " },\n" - + " \"handlers_request\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 6.000000000000001E-6,\n" - + " \"msMaxLatency\" : 6.000000000000001E-6\n" - + " },\n" - + " \"handlers_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " },\n" - + " \"queue\" : {\n" - + " \"tps\" : 0.0,\n" - + " \"msTotalTime\" : 0.0,\n" - + " \"msMaxLatency\" : 0.0\n" - + " },\n" - + " \"server_filters_response\" : {\n" - + " \"tps\" : 1.0,\n" - + " \"msTotalTime\" : 1.0000000000000002E-6,\n" - + " \"msMaxLatency\" : 1.0000000000000002E-6\n" - + " }\n" - + " },\n" - + " \"latencyDistribution\" : [ 1, 0, 0 ]\n" - + " }\n" - + " }\n" - + " }\n" - + " }\n" - + " }\n" - + "}"; - Assertions.assertEquals(Json.encodePrettily(Json.decodeValue(expect, Object.class)), - Json.encodePrettily(model.getProducer())); + expect = """ + { + "operationPerfGroups" : { + "groups" : { + "rest" : { + "200" : { + "transport" : "rest", + "status" : "200", + "operationPerfs" : [ { + "operation" : "m.s.o", + "stages" : { + "consumer-encode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "prepare" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "wait" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "total" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.4E-5, + "msMaxLatency" : 1.4E-5 + }, + "consumer-send" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "connection" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-decode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + } + }, + "latencyDistribution" : [ 1, 1, 1 ] + } ], + "summary" : { + "operation" : "", + "stages" : { + "consumer-encode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "prepare" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "total" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.4E-5, + "msMaxLatency" : 1.4E-5 + }, + "wait" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-send" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "connection" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + }, + "consumer-decode" : { + "totalRequests" : 1.0, + "msTotalTime" : 1.0000000000000002E-6, + "msMaxLatency" : 1.0000000000000002E-6 + } + }, + "latencyDistribution" : [ 1, 1, 1 ] + } + } + } + } + } + } + """; + JSONAssert.assertEquals(Json.encodePrettily(Json.decodeValue(expect, Object.class)), + Json.encodePrettily(model.getProducer()), false); } protected void prepareInvocation() { - - Deencapsulation.setField(invocationStageTrace, "start", 1L); - Deencapsulation.setField(invocationStageTrace, "startHandlersRequest", 2L); - Deencapsulation.setField(invocationStageTrace, "startClientFiltersRequest", 3L); - Deencapsulation.setField(invocationStageTrace, "startSend", 4L); - Deencapsulation.setField(invocationStageTrace, "startGetConnection", 4L); - Deencapsulation.setField(invocationStageTrace, "finishGetConnection", 5L); - Deencapsulation.setField(invocationStageTrace, "finishWriteToBuffer", 6L); - Deencapsulation.setField(invocationStageTrace, "finishReceiveResponse", 7L); - Deencapsulation.setField(invocationStageTrace, "startClientFiltersResponse", 8L); - Deencapsulation.setField(invocationStageTrace, "finishClientFiltersResponse", 9L); - Deencapsulation.setField(invocationStageTrace, "finishHandlersResponse", 14L); - Deencapsulation.setField(invocationStageTrace, "finish", 15L); - Deencapsulation.setField(invocationStageTrace, "startExecution", 5L); - Deencapsulation.setField(invocationStageTrace, "startSchedule", 6L); - Deencapsulation.setField(invocationStageTrace, "startBusinessMethod", 8L); - Deencapsulation.setField(invocationStageTrace, "finishBusiness", 11L); - Deencapsulation.setField(invocationStageTrace, "finishHandlersResponse", 12L); - Deencapsulation.setField(invocationStageTrace, "finishServerFiltersResponse", 13L); - Deencapsulation.setField(invocationStageTrace, "invocation", invocation); + Mockito.when(invocationStageTrace.calcTotal()).thenReturn(14L); + Mockito.when(invocationStageTrace.calcPrepare()).thenReturn(1L); + Mockito.when(invocationStageTrace.calcConnection()).thenReturn(1L); + Mockito.when(invocationStageTrace.calcConsumerEncodeRequest()).thenReturn(1L); + Mockito.when(invocationStageTrace.calcConsumerSendRequest()).thenReturn(1L); + Mockito.when(invocationStageTrace.calcWait()).thenReturn(1L); + Mockito.when(invocationStageTrace.calcConsumerDecodeResponse()).thenReturn(1L); invocationType = InvocationType.CONSUMER; - new MockUp() { - @Mock - InvocationType getInvocationType() { - return invocationType; - } - - @Mock - boolean isConsumer() { - return InvocationType.CONSUMER.equals(invocationType); - } - - @Mock - String getRealTransportName() { - return CoreConst.RESTFUL; - } - - @Mock - String getMicroserviceQualifiedName() { - return "m.s.o"; - } - @Mock - InvocationStageTrace getInvocationStageTrace() { - return invocationStageTrace; - } - }; + Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); + Mockito.when(invocation.isConsumer()).thenReturn(InvocationType.CONSUMER.equals(invocationType)); + Mockito.when(invocation.getRealTransportName()).thenReturn(CoreConst.RESTFUL); + Mockito.when(invocation.getMicroserviceQualifiedName()).thenReturn("m.s.o"); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); + Mockito.when(response.getStatusCode()).thenReturn(200); - new Expectations() { - { - response.getStatusCode(); - result = 200; - } - }; InvocationFinishEvent finishEvent = new InvocationFinishEvent(invocation, response); eventBus.post(finishEvent); invocationType = InvocationType.PROVIDER; + Mockito.when(invocation.getInvocationType()).thenReturn(invocationType); eventBus.post(finishEvent); } } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java index 78ada56f9c2..dc8b1ec72d7 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestPublishUtils.java @@ -16,36 +16,34 @@ */ package org.apache.servicecomb.metrics.core.publish; -import java.util.HashMap; import java.util.Map; -import jakarta.ws.rs.core.Response.Status; - import org.apache.servicecomb.core.CoreConst; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode; -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementTree; -import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementTree; +import org.apache.servicecomb.metrics.core.meter.ThreadPoolMonitorPublishModelFactory; import org.apache.servicecomb.metrics.core.publish.model.ThreadPoolPublishModel; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerf; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroup; import org.apache.servicecomb.metrics.core.publish.model.invocation.OperationPerfGroups; import org.apache.servicecomb.metrics.core.publish.model.invocation.PerfInfo; import org.apache.servicecomb.metrics.core.publish.model.invocation.Utils; -import org.junit.Test; - -import com.netflix.spectator.api.patterns.ThreadPoolMonitorPublishModelFactory; import org.junit.jupiter.api.Assertions; +import jakarta.ws.rs.core.Response.Status; +import org.junit.jupiter.api.Test; + public class TestPublishUtils { String op = "op"; @Test public void createPerfInfo() { - MeasurementNode stageNode = Utils.createStageNode(MeterInvocationConst.STAGE_TOTAL, 10, 10, 100); + MeasurementNode stageNode = Utils.createStageNode(InvocationStageTrace.STAGE_TOTAL, 10, 10, 100); PerfInfo perf = PublishUtils.createPerfInfo(stageNode); - Assertions.assertEquals(10, perf.getTps(), 0); + Assertions.assertEquals(10, perf.getTotalRequests(), 0); Assertions.assertEquals(1000, perf.calcMsLatency(), 0); Assertions.assertEquals(100000, perf.getMsMaxLatency(), 0); } @@ -54,9 +52,9 @@ public void createPerfInfo() { public void createOperationPerf() { OperationPerf opPerf = Utils.createOperationPerf(op); - PerfInfo perfInfo = opPerf.findStage(MeterInvocationConst.STAGE_TOTAL); + PerfInfo perfInfo = opPerf.findStage(InvocationStageTrace.STAGE_TOTAL); Integer[] latencyDistribution = opPerf.getLatencyDistribution(); - Assertions.assertEquals(10, perfInfo.getTps(), 0); + Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); Assertions.assertEquals(2, latencyDistribution.length); @@ -75,9 +73,9 @@ public void addOperationPerfGroups() { Map statusMap = groups.getGroups().get(CoreConst.RESTFUL); OperationPerfGroup group = statusMap.get(Status.OK.name()); - PerfInfo perfInfo = group.getSummary().findStage(MeterInvocationConst.STAGE_TOTAL); + PerfInfo perfInfo = group.getSummary().findStage(InvocationStageTrace.STAGE_TOTAL); Integer[] latencyDistribution = group.getSummary().getLatencyDistribution(); - Assertions.assertEquals(10, perfInfo.getTps(), 0); + Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); Assertions.assertEquals(2, latencyDistribution.length); @@ -87,9 +85,8 @@ public void addOperationPerfGroups() { @Test public void createThreadPoolPublishModels_empty() { - Map threadPools = new HashMap<>(); - - ThreadPoolMonitorPublishModelFactory.create(new MeasurementTree(), threadPools); + Map threadPools = + ThreadPoolMonitorPublishModelFactory.create(new MeasurementTree()); Assertions.assertTrue(threadPools.isEmpty()); } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestSlowInvocationLogger.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestSlowInvocationLogger.java index dad92b73711..e09252c4e61 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestSlowInvocationLogger.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestSlowInvocationLogger.java @@ -25,38 +25,41 @@ import org.apache.servicecomb.core.definition.OperationMeta; import org.apache.servicecomb.core.event.InvocationFinishEvent; import org.apache.servicecomb.core.invocation.InvocationStageTrace; -import org.apache.servicecomb.core.tracing.TraceIdLogger; import org.apache.servicecomb.foundation.test.scaffolding.log.LogCollector; import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; import org.apache.servicecomb.swagger.invocation.Response; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; -import mockit.Expectations; -import mockit.Mocked; +import com.google.common.eventbus.EventBus; +@ExtendWith(MockitoExtension.class) public class TestSlowInvocationLogger { - @Mocked + @Mock SCBEngine scbEngine; - @Mocked + @Mock Invocation invocation; - @Mocked + @Mock OperationMeta operationMeta; - @Mocked + @Mock RestOperationMeta restOperationMeta; - @Mocked + @Mock OperationConfig operationConfig; - @Mocked + @Mock Response response; - @Mocked + @Mock InvocationStageTrace stageTrace; InvocationFinishEvent event; @@ -65,16 +68,22 @@ public class TestSlowInvocationLogger { LogCollector logCollector; - @Before + @BeforeEach public void setup() { + EventBus eventBus = Mockito.spy(EventBus.class); + Mockito.doNothing().when(eventBus).register(Mockito.any()); + Mockito.when(scbEngine.getEventBus()).thenReturn(eventBus); + Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); + Mockito.when(operationMeta.getConfig()).thenReturn(operationConfig); + logger = new SlowInvocationLogger(scbEngine); event = new InvocationFinishEvent(invocation, response); logCollector = new LogCollector(); } - @After - public void teardown() { - logCollector.teardown(); + @AfterEach + public void tearDown() { + logCollector.tearDown(); } @Test @@ -86,156 +95,110 @@ public void disable() { @Test public void enableButNotSlow() { - new Expectations() { - { - operationConfig.isSlowInvocationEnabled(); - result = true; - operationConfig.getNanoSlowInvocation(); - result = 2; - stageTrace.calcTotalTime(); - result = 1; - } - }; + + Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); + Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(2L); + Mockito.when(stageTrace.calcTotal()).thenReturn(1L); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); logger.onInvocationFinish(event); Assertions.assertTrue(logCollector.getEvents().isEmpty()); } @Test - public void consumerSlow(@Mocked Endpoint endpoint) { - new Expectations() { - { - invocation.getEndpoint(); - result = endpoint; - endpoint.getEndpoint(); - result = "rest://1.1.1.1:1234"; - invocation.isConsumer(); - result = true; - invocation.getTraceIdLogger(); - result = new TraceIdLogger(invocation); - operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); - result = restOperationMeta; - operationConfig.isSlowInvocationEnabled(); - result = true; - operationConfig.getNanoSlowInvocation(); - result = 1; - stageTrace.calcTotalTime(); - result = 1; - } - }; + public void consumerSlow() { + Endpoint endpoint = Mockito.mock(Endpoint.class); + Mockito.when(invocation.getEndpoint()).thenReturn(endpoint); + Mockito.when(endpoint.getEndpoint()).thenReturn("rest://1.1.1.1:1234"); + Mockito.when(invocation.isProducer()).thenReturn(false); + Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); + Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); + Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(1L); + Mockito.when(stageTrace.calcTotal()).thenReturn(1L); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); logger.onInvocationFinish(event); - Assertions.assertEquals("" - + "slow(0 ms) invocation, null:\n" - + " http method: null\n" - + " url : null\n" - + " server : rest://1.1.1.1:1234\n" - + " status code: 0\n" - + " total : 0.0 ms\n" - + " prepare : 0.0 ms\n" - + " handlers request : 0.0 ms\n" - + " client filters request : 0.0 ms\n" - + " send request : 0.0 ms\n" - + " get connection : 0.0 ms\n" - + " write to buf : 0.0 ms\n" - + " wait response : 0.0 ms\n" - + " wake consumer : 0.0 ms\n" - + " client filters response: 0.0 ms\n" - + " handlers response : 0.0 ms", + Assertions.assertEquals(""" + Slow Consumer invocation [null](0 ms)[null] + http method : null + url : null + endpoint : rest://1.1.1.1:1234 + status code : 0 + total : 0.0ms + prepare : 0.0ms + connection : 0.0ms + consumer-encode : 0.0ms + consumer-send : 0.0ms + wait : 0.0ms + consumer-decode : 0.0ms + """, logCollector.getEvent(0).getMessage().getFormattedMessage()); } @Test - public void edgeSlow(@Mocked Endpoint endpoint) { - new Expectations() { - { - invocation.getEndpoint(); - result = endpoint; - endpoint.getEndpoint(); - result = "rest://1.1.1.1:1234"; - invocation.isConsumer(); - result = true; - invocation.getTraceIdLogger(); - result = new TraceIdLogger(invocation); - invocation.isEdge(); - result = true; - operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); - result = restOperationMeta; - operationConfig.isSlowInvocationEnabled(); - result = true; - operationConfig.getNanoSlowInvocation(); - result = 1; - stageTrace.calcTotalTime(); - result = 1; - } - }; + public void edgeSlow() { + Endpoint endpoint = Mockito.mock(Endpoint.class); + Mockito.when(invocation.getEndpoint()).thenReturn(endpoint); + Mockito.when(endpoint.getEndpoint()).thenReturn("rest://1.1.1.1:1234"); + Mockito.when(invocation.isEdge()).thenReturn(true); + Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); + Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); + Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(1L); + Mockito.when(stageTrace.calcTotal()).thenReturn(1L); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); logger.onInvocationFinish(event); - Assertions.assertEquals("" - + "slow(0 ms) invocation, null:\n" - + " http method: null\n" - + " url : null\n" - + " server : rest://1.1.1.1:1234\n" - + " status code: 0\n" - + " total : 0.0 ms\n" - + " prepare : 0.0 ms\n" - + " threadPoolQueue : 0.0 ms\n" - + " server filters request : 0.0 ms\n" - + " handlers request : 0.0 ms\n" - + " client filters request : 0.0 ms\n" - + " send request : 0.0 ms\n" - + " get connection : 0.0 ms\n" - + " write to buf : 0.0 ms\n" - + " wait response : 0.0 ms\n" - + " wake consumer : 0.0 ms\n" - + " client filters response: 0.0 ms\n" - + " handlers response : 0.0 ms\n" - + " server filters response: 0.0 ms\n" - + " send response : 0.0 ms", + Assertions.assertEquals(""" + Slow Edge invocation [null](0 ms)[null] + http method : null + url : null + endpoint : rest://1.1.1.1:1234 + status code : 0 + total : 0.0ms + prepare : 0.0ms + provider-decode : 0.0ms + connection : 0.0ms + consumer-encode : 0.0ms + consumer-send : 0.0ms + wait : 0.0ms + consumer-decode : 0.0ms + provider-encode : 0.0ms + provider-send : 0.0ms + """, logCollector.getEvent(0).getMessage().getFormattedMessage()); } @Test - public void producerSlow(@Mocked HttpServletRequestEx requestEx) { - new Expectations() { - { - invocation.getRequestEx(); - result = requestEx; - invocation.getTraceIdLogger(); - result = new TraceIdLogger(invocation); - requestEx.getRemoteAddr(); - result = "1.1.1.1"; - requestEx.getRemotePort(); - result = 1234; - invocation.isConsumer(); - result = false; - operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION); - result = restOperationMeta; - operationConfig.isSlowInvocationEnabled(); - result = true; - operationConfig.getNanoSlowInvocation(); - result = 1; - stageTrace.calcTotalTime(); - result = 1; - } - }; + public void producerSlow() { + + HttpServletRequestEx requestEx = Mockito.mock(HttpServletRequestEx.class); + + Mockito.when(invocation.getRequestEx()).thenReturn(requestEx); + Mockito.when(requestEx.getRemoteAddr()).thenReturn("1.1.1.1"); + Mockito.when(requestEx.getRemotePort()).thenReturn(1234); + Mockito.when(invocation.isProducer()).thenReturn(true); + Mockito.when(operationMeta.getExtData(RestConst.SWAGGER_REST_OPERATION)).thenReturn(restOperationMeta); + Mockito.when(operationConfig.isSlowInvocationEnabled()).thenReturn(true); + Mockito.when(operationConfig.getNanoSlowInvocation()).thenReturn(1L); + Mockito.when(stageTrace.calcTotal()).thenReturn(1L); + Mockito.when(invocation.getInvocationStageTrace()).thenReturn(stageTrace); + logger.onInvocationFinish(event); - Assertions.assertEquals("" - + "slow(0 ms) invocation, null:\n" - + " http method: null\n" - + " url : null\n" - + " client : 1.1.1.1:1234\n" - + " status code: 0\n" - + " total : 0.0 ms\n" - + " prepare : 0.0 ms\n" - + " threadPoolQueue : 0.0 ms\n" - + " server filters request : 0.0 ms\n" - + " handlers request : 0.0 ms\n" - + " business execute : 0.0 ms\n" - + " handlers response : 0.0 ms\n" - + " server filters response: 0.0 ms\n" - + " send response : 0.0 ms", + Assertions.assertEquals(""" + Slow Provider invocation [null](0 ms)[null] + http method : null + url : null + endpoint : 1.1.1.1:1234 + status code : 0 + total : 0.0ms + prepare : 0.0ms + provider-decode : 0.0ms + queue : 0.0ms + execute : 0.0ms + provider-encode : 0.0ms + provider-send : 0.0ms + """, logCollector.getEvent(0).getMessage().getFormattedMessage()); } } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java index ef37c4f9378..f00e49180ae 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/TestThreadPoolPublishModelFactory.java @@ -17,52 +17,54 @@ package org.apache.servicecomb.metrics.core.publish; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; +import org.apache.servicecomb.core.executor.ThreadPoolExecutorEx; +import org.apache.servicecomb.foundation.common.event.EventManager; import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; +import org.apache.servicecomb.metrics.core.ThreadPoolMetersInitializer; import org.apache.servicecomb.metrics.core.publish.model.DefaultPublishModel; -import org.junit.FixMethodOrder; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; -import org.junit.runners.MethodSorters; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; -import com.google.common.collect.Lists; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.patterns.PolledMeter; -import com.netflix.spectator.api.patterns.ThreadPoolMonitor; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.mockito.junit.jupiter.MockitoExtension; -import mockit.Expectations; -import mockit.Injectable; -import mockit.Mocked; +import org.skyscreamer.jsonassert.JSONAssert; -@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) public class TestThreadPoolPublishModelFactory { - protected Registry registry = new DefaultRegistry(new ManualClock()); + MeterRegistry registry = new SimpleMeterRegistry(); - @Mocked + @Mock BlockingQueue queue; @Test - public void createDefaultPublishModel(@Injectable ThreadPoolExecutor threadPoolExecutor) throws Exception { - new Expectations() { - { - threadPoolExecutor.getQueue(); - result = queue; - queue.size(); - result = 10d; + public void createDefaultPublishModel() throws Exception { + ThreadPoolExecutorEx threadPoolExecutor = Mockito.mock(ThreadPoolExecutorEx.class); + Mockito.when(threadPoolExecutor.getQueue()).thenReturn(queue); + Mockito.doReturn(10).when(queue).size(); + + MetricsBootstrapConfig metricsBootstrapConfig = Mockito.mock(MetricsBootstrapConfig.class); + ThreadPoolMetersInitializer threadPoolMetersInitializer = new ThreadPoolMetersInitializer() { + @Override + public void createThreadPoolMeters() { + createThreadPoolMeters("test", threadPoolExecutor); } }; - - ThreadPoolMonitor.attach(registry, threadPoolExecutor, "test"); - - PolledMeter.update(registry); - PublishModelFactory factory = new PublishModelFactory(Lists.newArrayList(registry.iterator())); + threadPoolMetersInitializer.init(registry, EventManager.getEventBus(), metricsBootstrapConfig); + PublishModelFactory factory = new PublishModelFactory(registry.getMeters()); DefaultPublishModel model = factory.createDefaultPublishModel(); - Assertions.assertEquals( - "{\"test\":{\"avgTaskCount\":0.0,\"avgCompletedTaskCount\":0.0,\"currentThreadsBusy\":0,\"maxThreads\":0,\"poolSize\":0,\"corePoolSize\":0,\"queueSize\":10,\"rejected\":\"NaN\"}}", - JsonUtils.writeValueAsString(model.getThreadPools())); + JSONAssert.assertEquals( + """ + {"test":{"avgTaskCount":0.0,"avgCompletedTaskCount":0.0,"currentThreadsBusy":0,"maxThreads":0,"poolSize":0,"corePoolSize":0,"queueSize":10,"rejected":0.0}}""", + JsonUtils.writeValueAsString(model.getThreadPools()), false); } } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.java index f0fa8349185..97079bfe168 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerf.java @@ -16,9 +16,9 @@ */ package org.apache.servicecomb.metrics.core.publish.model.invocation; -import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; -import org.junit.Test; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class TestOperationPerf { String op = "op"; @@ -34,13 +34,13 @@ public void add() { Assertions.assertEquals(op, otherOpPerf.getOperation()); - PerfInfo perfInfo = opPerf.findStage(MeterInvocationConst.STAGE_TOTAL); - Assertions.assertEquals(10, perfInfo.getTps(), 0); + PerfInfo perfInfo = opPerf.findStage(InvocationStageTrace.STAGE_TOTAL); + Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); - perfInfo = opPerf.findStage(MeterInvocationConst.STAGE_EXECUTION); - Assertions.assertEquals(10, perfInfo.getTps(), 0); + perfInfo = opPerf.findStage(InvocationStageTrace.STAGE_PROVIDER_BUSINESS); + Assertions.assertEquals(10, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.java index 2306ea9dfb8..ff0191b14b5 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestOperationPerfGroup.java @@ -16,13 +16,13 @@ */ package org.apache.servicecomb.metrics.core.publish.model.invocation; -import jakarta.ws.rs.core.Response.Status; - import org.apache.servicecomb.core.CoreConst; -import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; -import org.junit.Test; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.junit.jupiter.api.Assertions; +import jakarta.ws.rs.core.Response.Status; +import org.junit.jupiter.api.Test; + public class TestOperationPerfGroup { String op = "op"; @@ -46,13 +46,13 @@ public void addOperationPerf() { OperationPerf summary = group.getSummary(); - PerfInfo perfInfo = summary.findStage(MeterInvocationConst.STAGE_TOTAL); - Assertions.assertEquals(20, perfInfo.getTps(), 0); + PerfInfo perfInfo = summary.findStage(InvocationStageTrace.STAGE_TOTAL); + Assertions.assertEquals(20, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); - perfInfo = summary.findStage(MeterInvocationConst.STAGE_EXECUTION); - Assertions.assertEquals(20, perfInfo.getTps(), 0); + perfInfo = summary.findStage(InvocationStageTrace.STAGE_PROVIDER_BUSINESS); + Assertions.assertEquals(20, perfInfo.getTotalRequests(), 0); Assertions.assertEquals(1000, perfInfo.calcMsLatency(), 0); Assertions.assertEquals(100000, perfInfo.getMsMaxLatency(), 0); } diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java index 3fec1908bf4..35c6d83e72c 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/TestPerfInfo.java @@ -24,7 +24,7 @@ public class TestPerfInfo { public void construct() { PerfInfo perf = new PerfInfo(); - Assertions.assertEquals(0, perf.getTps(), 0); + Assertions.assertEquals(0, perf.getTotalRequests(), 0); Assertions.assertEquals(0, perf.getMsTotalTime(), 0); Assertions.assertEquals(0, perf.getMsMaxLatency(), 0); Assertions.assertEquals(0, perf.calcMsLatency(), 0); @@ -35,18 +35,18 @@ public void add_changeMax() { PerfInfo sum = new PerfInfo(); PerfInfo other = new PerfInfo(); - other.setTps(10); + other.setTotalRequests(10); other.setMsTotalTime(10); other.setMsMaxLatency(100); sum.add(other); other = new PerfInfo(); - other.setTps(20); + other.setTotalRequests(20); other.setMsTotalTime(20); other.setMsMaxLatency(200); sum.add(other); - Assertions.assertEquals(30, sum.getTps(), 0); + Assertions.assertEquals(30, sum.getTotalRequests(), 0); Assertions.assertEquals(30, sum.getMsTotalTime(), 0); Assertions.assertEquals(200, sum.getMsMaxLatency(), 0); Assertions.assertEquals(1.0, sum.calcMsLatency(), 0); @@ -57,18 +57,18 @@ public void add_notChangeMax() { PerfInfo sum = new PerfInfo(); PerfInfo other = new PerfInfo(); - other.setTps(10); + other.setTotalRequests(10); other.setMsTotalTime(10); other.setMsMaxLatency(100); sum.add(other); other = new PerfInfo(); - other.setTps(20); + other.setTotalRequests(20); other.setMsTotalTime(20); other.setMsMaxLatency(50); sum.add(other); - Assertions.assertEquals(30, sum.getTps(), 0); + Assertions.assertEquals(30, sum.getTotalRequests(), 0); Assertions.assertEquals(1.0, sum.calcMsLatency(), 0); Assertions.assertEquals(100, sum.getMsMaxLatency(), 0); } @@ -76,7 +76,7 @@ public void add_notChangeMax() { @Test public void testToString() { PerfInfo perf = new PerfInfo(); - perf.setTps(10); + perf.setTotalRequests(10); perf.setMsTotalTime(10); perf.setMsMaxLatency(100); diff --git a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java index f8dcd649c77..17a62c57569 100644 --- a/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java +++ b/metrics/metrics-core/src/test/java/org/apache/servicecomb/metrics/core/publish/model/invocation/Utils.java @@ -19,50 +19,52 @@ import java.util.HashMap; import java.util.List; -import jakarta.ws.rs.core.Response.Status; - -import org.apache.servicecomb.foundation.metrics.publish.spectator.MeasurementNode; +import org.apache.servicecomb.core.invocation.InvocationStageTrace; +import org.apache.servicecomb.foundation.metrics.publish.MeasurementNode; import org.apache.servicecomb.metrics.core.meter.invocation.MeterInvocationConst; import org.apache.servicecomb.metrics.core.publish.PublishUtils; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.Statistic; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter.Id; +import io.micrometer.core.instrument.Meter.Type; +import io.micrometer.core.instrument.Statistic; +import io.micrometer.core.instrument.Tags; +import jakarta.ws.rs.core.Response.Status; public class Utils { - static Registry registry = new DefaultRegistry(); - - public static MeasurementNode totalStageNode = Utils.createStageNode(MeterInvocationConst.STAGE_TOTAL, 10, 10, 100); + public static MeasurementNode totalStageNode = Utils.createStageNode(InvocationStageTrace.STAGE_TOTAL, 10, 10, 100); public static MeasurementNode executeStageNode = - Utils.createStageNode(MeterInvocationConst.STAGE_EXECUTION, 10, 10, 100); + Utils.createStageNode(InvocationStageTrace.STAGE_PROVIDER_BUSINESS, 10, 10, 100); + + public static Id initId = new Id("id", Tags.empty(), null, null, Type.OTHER); public static MeasurementNode createStageNode(String stage, double count, double totalTime, double max) { - Id id = registry.createId("id").withTag(Statistic.count); - Measurement countMeasurement = new Measurement(id.withTag(Statistic.count), 0, count); - Measurement totalTimeMeasurement = new Measurement(id.withTag(Statistic.totalTime), 0, totalTime); - Measurement maxMeasurement = new Measurement(id.withTag(Statistic.max), 0, max); + Id id = initId; + Measurement countMeasurement = new Measurement(() -> count, Statistic.COUNT); + Measurement totalTimeMeasurement = new Measurement(() -> totalTime, Statistic.TOTAL_TIME); + Measurement maxMeasurement = new Measurement(() -> max, Statistic.MAX); - MeasurementNode stageNode = new MeasurementNode(stage, null); - stageNode.addChild(Statistic.count.name(), countMeasurement); - stageNode.addChild(Statistic.totalTime.name(), totalTimeMeasurement); - stageNode.addChild(Statistic.max.name(), maxMeasurement); + MeasurementNode stageNode = new MeasurementNode(stage, id, null); + stageNode.addChild(Statistic.COUNT.name(), id, countMeasurement); + stageNode.addChild(Statistic.TOTAL_TIME.name(), id, totalTimeMeasurement); + stageNode.addChild(Statistic.MAX.name(), id, maxMeasurement); return stageNode; } public static MeasurementNode createStatusNode(String status, MeasurementNode... stageNodes) { - MeasurementNode statusNode = new MeasurementNode(status, new HashMap<>()); - MeasurementNode typeNode = new MeasurementNode(MeterInvocationConst.TAG_STAGE, new HashMap<>()); - MeasurementNode latencyNode = new MeasurementNode(MeterInvocationConst.TAG_LATENCY_DISTRIBUTION, new HashMap<>()); + Id id = initId; + MeasurementNode statusNode = new MeasurementNode(status, id, new HashMap<>()); + MeasurementNode typeNode = new MeasurementNode(MeterInvocationConst.TAG_STAGE, id, new HashMap<>()); + MeasurementNode latencyNode = new MeasurementNode(MeterInvocationConst.TAG_DISTRIBUTION, id, + new HashMap<>()); List measurements = latencyNode.getMeasurements(); - measurements.add(new Measurement(null, 0L, 1)); - measurements.add(new Measurement(null, 0L, 2)); + measurements.add(new Measurement(() -> 1, Statistic.VALUE)); + measurements.add(new Measurement(() -> 2, Statistic.VALUE)); for (MeasurementNode stageNode : stageNodes) { typeNode.getChildren().put(stageNode.getName(), stageNode); } diff --git a/metrics/metrics-integration/metrics-prometheus/pom.xml b/metrics/metrics-integration/metrics-prometheus/pom.xml index 98de7c93e8d..20d23c9d567 100644 --- a/metrics/metrics-integration/metrics-prometheus/pom.xml +++ b/metrics/metrics-integration/metrics-prometheus/pom.xml @@ -21,7 +21,7 @@ metrics-integration org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java b/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java index 12ae5041d1f..19cd28d510b 100644 --- a/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java +++ b/metrics/metrics-integration/metrics-prometheus/src/main/java/org/apache/servicecomb/metrics/prometheus/PrometheusPublisher.java @@ -25,18 +25,17 @@ import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; -import com.netflix.spectator.api.Measurement; -import com.netflix.spectator.api.Meter; -import com.netflix.spectator.api.Registry; -import com.netflix.spectator.api.Tag; +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; import io.prometheus.client.Collector; import io.prometheus.client.Collector.MetricFamilySamples.Sample; import io.prometheus.client.CollectorRegistry; @@ -49,7 +48,7 @@ public class PrometheusPublisher extends Collector implements Collector.Describa private HTTPServer httpServer; - private GlobalRegistry globalRegistry; + private MeterRegistry meterRegistry; private Environment environment; @@ -59,8 +58,8 @@ public void setEnvironment(Environment environment) { } @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { - this.globalRegistry = globalRegistry; + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + this.meterRegistry = meterRegistry; //prometheus default port allocation is here : https://github.com/prometheus/prometheus/wiki/Default-port-allocations String address = environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696"); @@ -87,18 +86,14 @@ private InetSocketAddress getSocketAddress(String address) { @Override public List describe() { List familySamples = new ArrayList<>(); - if (globalRegistry == null) { - return familySamples; - } List samples = new ArrayList<>(); - for (Registry registry : globalRegistry.getRegistries()) { - for (Meter meter : registry) { - meter.measure().forEach(measurement -> { - Sample sample = convertMeasurementToSample(measurement); - samples.add(sample); - }); - } + + for (Meter meter : this.meterRegistry.getMeters()) { + meter.measure().forEach(measurement -> { + Sample sample = convertMeasurementToSample(meter, measurement); + samples.add(sample); + }); } familySamples.add(new MetricFamilySamples("ServiceComb_Metrics", Type.UNKNOWN, "ServiceComb Metrics", samples)); @@ -106,20 +101,20 @@ public List describe() { return familySamples; } - protected Sample convertMeasurementToSample(Measurement measurement) { - String prometheusName = measurement.id().name().replace(".", "_"); + protected Sample convertMeasurementToSample(Meter meter, Measurement measurement) { + String prometheusName = meter.getId().getName().replace(".", "_"); List labelNames = new ArrayList<>(); List labelValues = new ArrayList<>(); labelNames.add("appId"); labelValues.add(BootStrapProperties.readApplication(environment)); - for (Tag tag : measurement.id().tags()) { - labelNames.add(tag.key()); - labelValues.add(tag.value()); + for (Tag tag : meter.getId().getTags()) { + labelNames.add(tag.getKey()); + labelValues.add(tag.getValue()); } - return new Sample(prometheusName, labelNames, labelValues, measurement.value()); + return new Sample(prometheusName, labelNames, labelValues, measurement.getValue()); } @Override diff --git a/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java b/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java index 167a66b4082..bd9fdd0bfd7 100644 --- a/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java +++ b/metrics/metrics-integration/metrics-prometheus/src/test/java/org/apache/servicecomb/metrics/prometheus/TestPrometheusPublisher.java @@ -33,7 +33,6 @@ import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.foundation.common.exceptions.ServiceCombException; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -41,16 +40,15 @@ import org.mockito.Mockito; import org.springframework.core.env.Environment; -import com.netflix.spectator.api.Counter; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Registry; import com.sun.net.httpserver.HttpServer; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; import io.prometheus.client.exporter.HTTPServer; public class TestPrometheusPublisher { - GlobalRegistry globalRegistry = new GlobalRegistry(new ManualClock()); + MeterRegistry meterRegistry = new SimpleMeterRegistry(); PrometheusPublisher publisher = new PrometheusPublisher(); @@ -75,7 +73,7 @@ public void testBadPublishAddress() { Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) .thenReturn("a:b:c"); Assertions.assertThrows(ServiceCombException.class, () -> { - publisher.init(globalRegistry, null, null); + publisher.init(meterRegistry, null, null); }); } @@ -84,7 +82,7 @@ public void testBadPublishAddress_BadPort() { Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) .thenReturn("localhost:xxxx"); Assertions.assertThrows(ServiceCombException.class, () -> { - publisher.init(globalRegistry, null, null); + publisher.init(meterRegistry, null, null); }); } @@ -93,7 +91,7 @@ public void testBadPublishAddress_TooLargePort() { Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) .thenReturn("localhost:9999999"); Assertions.assertThrows(ServiceCombException.class, () -> { - publisher.init(globalRegistry, null, null); + publisher.init(meterRegistry, null, null); }); } @@ -103,12 +101,9 @@ public void collect() throws IllegalAccessException, IOException { Mockito.when(environment.getProperty(METRICS_PROMETHEUS_ADDRESS, String.class, "0.0.0.0:9696")) .thenReturn("localhost:0"); publisher.setEnvironment(environment); - publisher.init(globalRegistry, null, new MetricsBootstrapConfig(environment)); + publisher.init(meterRegistry, null, new MetricsBootstrapConfig(environment)); - Registry registry = new DefaultRegistry(new ManualClock()); - globalRegistry.add(registry); - - Counter counter = registry.counter("count.name", "tag1", "tag1v", "tag2", "tag2v"); + Counter counter = meterRegistry.counter("count.name", "tag1", "tag1v", "tag2", "tag2v"); counter.increment(); HTTPServer httpServer = (HTTPServer) FieldUtils.readField(publisher, "httpServer", true); diff --git a/metrics/metrics-integration/pom.xml b/metrics/metrics-integration/pom.xml index 893c36a0619..d989f8bc2a4 100644 --- a/metrics/metrics-integration/pom.xml +++ b/metrics/metrics-integration/pom.xml @@ -22,7 +22,7 @@ metrics org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/metrics/pom.xml b/metrics/pom.xml index 6f086a512bf..95eb85051fc 100644 --- a/metrics/pom.xml +++ b/metrics/pom.xml @@ -24,7 +24,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default diff --git a/parents/default/pom.xml b/parents/default/pom.xml index f3df8ea2732..b8954d029b8 100644 --- a/parents/default/pom.xml +++ b/parents/default/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb java-chassis-dependencies - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../../dependencies/default @@ -106,26 +106,15 @@ log4j-slf4j-impl test + + org.apache.logging.log4j + log4j-api + test + org.apache.logging.log4j log4j-core test - - - - - maven-source-plugin - - - attach-sources - - jar - - - - - - diff --git a/parents/pom.xml b/parents/pom.xml index a88aa964548..8fce5793c5c 100644 --- a/parents/pom.xml +++ b/parents/pom.xml @@ -24,7 +24,7 @@ org.apache.servicecomb java-chassis - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT java-chassis-parents diff --git a/pom.xml b/pom.xml index b9f6d13d12c..25882c4ca33 100644 --- a/pom.xml +++ b/pom.xml @@ -23,13 +23,13 @@ org.apache apache - 29 + 35 org.apache.servicecomb java-chassis - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT pom ServiceComb Java Chassis @@ -37,38 +37,38 @@ https://github.com/apache/servicecomb-java-chassis + 17 + 17 UTF-8 17 - -Dfile.encoding=UTF-8 true - - 3.3.0 + 3.6.0 4.3.0 - 8.3.1 - 0.43.3 - 3.1.0 - 3.0.1 - 0.8.10 - 3.5.0 + 12.2.0 + 0.48.0 + 3.6.3 + 3.2.8 + 0.8.14 + 3.12.0 2.5 - 3.11.0 - 3.0.0 - 3.3.0 - 3.4.3 - 3.0.0 + 3.15.0 + 3.5.4 + 3.4.2 + 3.9.0 + 3.2.0 1.7.1 0.6.1 3.19.2 1.47.0 - 10.12.3 - 2.5.3 - 3.12.1 - 3.2.1 - 4.7.3.5 - 3.1.3 - 3.0.0-M9 + 12.3.1 + 3.1.1 + 3.21.0 + 3.3.0 + 4.9.8.2 + 3.5.4 + 3.5.4 @@ -199,7 +199,7 @@ org.codehaus.mojo license-maven-plugin - 2.2.0 + 2.7.1 default-cli @@ -243,7 +243,7 @@ **/etc/eclipse-java-google-style.xml **/etc/intellij-java-google-style.xml - **/resources/ssl/** + **/resources/ssl/** **/src/release/licenses/** @@ -259,6 +259,8 @@ **/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java **/java/org/apache/servicecomb/foundation/protobuf/internal/model/ProtobufRoot.java + + **/resources/google/protobuf/**.proto @@ -306,16 +308,12 @@ ${excludesFile} - 1 + 5 + 2 true alphabetical - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin.version} - org.apache.maven.plugins maven-jar-plugin @@ -336,7 +334,7 @@ true true Medium - ci/spotbugs/exclude.xml + ${session.executionRootDirectory}/ci/spotbugs/exclude.xml @@ -418,17 +416,11 @@ ${maven-compiler-plugin.version} -parameters - 17 - 17 true true + - ${werror.properties} -Xlint:all - - -Xlint:-classfile - - -Xlint:-processing @@ -458,7 +450,7 @@ org.springframework.boot spring-boot-maven-plugin - ${spring-boot-maven-plugin.version} + ${spring-boot.version} @@ -543,7 +535,6 @@ Date created: - -Xdoclint:none diff --git a/providers/pom.xml b/providers/pom.xml index 56ebfd720a4..9bd8a52f271 100644 --- a/providers/pom.xml +++ b/providers/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default providers diff --git a/providers/provider-jaxrs/pom.xml b/providers/provider-jaxrs/pom.xml index 3307bc2f5ef..1bd923857c6 100644 --- a/providers/provider-jaxrs/pom.xml +++ b/providers/provider-jaxrs/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb providers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT provider-jaxrs Java Chassis::Providers::JAXRS diff --git a/providers/provider-pojo/pom.xml b/providers/provider-pojo/pom.xml index 9a81bedf460..922f544b9f5 100644 --- a/providers/provider-pojo/pom.xml +++ b/providers/provider-pojo/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb providers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT provider-pojo Java Chassis::Providers::POJO @@ -50,11 +50,6 @@ log4j-core test - - javax.xml.bind - jaxb-api - test - org.mockito mockito-inline diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocationCreator.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocationCreator.java index 7f34b4b1c83..54dd320fe68 100644 --- a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocationCreator.java +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/PojoInvocationCreator.java @@ -24,6 +24,7 @@ public class PojoInvocationCreator { public PojoInvocation create(Method method, PojoConsumerMetaRefresher metaRefresher, Object[] args) { + long startCreateInvocation = System.nanoTime(); PojoConsumerMeta pojoConsumerMeta = metaRefresher.getLatestMeta(); PojoConsumerOperationMeta consumerOperationMeta = pojoConsumerMeta.ensureFindOperationMeta(method); @@ -31,7 +32,8 @@ public PojoInvocation create(Method method, PojoConsumerMetaRefresher metaRefres invocation.setSuccessResponseType(consumerOperationMeta.getResponsesType()); invocation.setInvocationArguments(consumerOperationMeta.getSwaggerConsumerOperation().toInvocationArguments(args)); invocation.setSync(consumerOperationMeta.isSync()); - + invocation.getInvocationStageTrace().startCreateInvocation(startCreateInvocation); + invocation.getInvocationStageTrace().finishCreateInvocation(); return invocation; } diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/ProviderPojoConfiguration.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/ProviderPojoConfiguration.java index 14f91542fb3..81bf83a756d 100644 --- a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/ProviderPojoConfiguration.java +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/ProviderPojoConfiguration.java @@ -16,7 +16,6 @@ */ package org.apache.servicecomb.provider.pojo; -import org.apache.servicecomb.provider.pojo.reference.RpcReferenceProcessor; import org.apache.servicecomb.provider.pojo.schema.PojoProducers; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -24,17 +23,12 @@ @Configuration public class ProviderPojoConfiguration { @Bean - public PojoConsumerProvider pojoConsumerProvider() { - return new PojoConsumerProvider(); + public static RpcReferenceBeanDefinitionRegistry rpcReferenceBeanDefinitionRegistry() { + return new RpcReferenceBeanDefinitionRegistry(); } @Bean - public RpcReferenceProcessor rpcReferenceProcessor() { - return new RpcReferenceProcessor(); - } - - @Bean - public PojoProducers pojoProducers() { + public static PojoProducers pojoProducers() { return new PojoProducers(); } } diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReferenceBeanDefinitionRegistry.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReferenceBeanDefinitionRegistry.java new file mode 100644 index 00000000000..32e10077131 --- /dev/null +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/RpcReferenceBeanDefinitionRegistry.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.pojo; + +import org.apache.servicecomb.provider.pojo.reference.RpcReferenceProcessor; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; + +/** + * In order to support FactoryBean eager inject @RpcReference, add RpcReferenceProcessor + * before FactoryBean is initialized. + */ +public class RpcReferenceBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor { + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + beanFactory.addBeanPostProcessor(new RpcReferenceProcessor(beanFactory)); + } +} diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/RpcReferenceProcessor.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/RpcReferenceProcessor.java index d986194e7fd..ba5ded3f12c 100644 --- a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/RpcReferenceProcessor.java +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/reference/RpcReferenceProcessor.java @@ -23,12 +23,15 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.context.EmbeddedValueResolverAware; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringValueResolver; -public class RpcReferenceProcessor implements BeanPostProcessor, EmbeddedValueResolverAware { - private StringValueResolver resolver; +public class RpcReferenceProcessor implements BeanPostProcessor { + private final ConfigurableBeanFactory beanFactory; + + public RpcReferenceProcessor(ConfigurableBeanFactory beanFactory) { + this.beanFactory = beanFactory; + } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { @@ -60,11 +63,6 @@ protected void processConsumerField(Object bean, Field field) { handleReferenceField(bean, field, reference); } - @Override - public void setEmbeddedValueResolver(StringValueResolver resolver) { - this.resolver = resolver; - } - private void handleReferenceMethod(Object bean, String beanName, Method method, RpcReference reference) throws BeansException { try { @@ -84,7 +82,7 @@ private void handleReferenceField(Object obj, Field field, private PojoReferenceMeta createPojoReferenceMeta(RpcReference reference, Class consumerInterface) { String microserviceName = reference.microserviceName(); - microserviceName = resolver.resolveStringValue(microserviceName); + microserviceName = beanFactory.resolveEmbeddedValue(microserviceName); PojoReferenceMeta pojoReference = new PojoReferenceMeta(); pojoReference.setMicroserviceName(microserviceName); diff --git a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducerMeta.java b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducerMeta.java index 7526ec23fab..d63541cdfe3 100644 --- a/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducerMeta.java +++ b/providers/provider-pojo/src/main/java/org/apache/servicecomb/provider/pojo/schema/PojoProducerMeta.java @@ -17,14 +17,14 @@ package org.apache.servicecomb.provider.pojo.schema; -import javax.inject.Inject; - -import com.google.common.annotations.VisibleForTesting; import org.apache.servicecomb.core.provider.producer.ProducerMeta; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; + +import com.google.common.annotations.VisibleForTesting; public class PojoProducerMeta extends ProducerMeta implements InitializingBean { - @Inject + @Autowired protected PojoProducers pojoProducers; private String implementation; diff --git a/providers/provider-pojo/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider b/providers/provider-pojo/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider index c38a7493918..b5a9f25c66a 100644 --- a/providers/provider-pojo/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider +++ b/providers/provider-pojo/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.provider.pojo.PojoProducerProvider \ No newline at end of file +org.apache.servicecomb.provider.pojo.PojoProducerProvider diff --git a/providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/reference/TestRpcReferenceProcessor.java b/providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/reference/TestRpcReferenceProcessor.java index 0c37541483f..7162a8a154d 100644 --- a/providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/reference/TestRpcReferenceProcessor.java +++ b/providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/reference/TestRpcReferenceProcessor.java @@ -27,12 +27,15 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.env.Environment; public class TestRpcReferenceProcessor { static Environment environment = Mockito.mock(Environment.class); - RpcReferenceProcessor consumers = new RpcReferenceProcessor(); + static ConfigurableBeanFactory beanFactory = Mockito.mock(ConfigurableBeanFactory.class); + + RpcReferenceProcessor consumers = new RpcReferenceProcessor(beanFactory); @BeforeEach public void setUp() { @@ -56,7 +59,7 @@ public void testReference() { Assertions.assertNull(bean.person); - consumers.setEmbeddedValueResolver((strVal) -> strVal); + Mockito.when(beanFactory.resolveEmbeddedValue("test")).thenReturn("test"); Assertions.assertSame(bean, consumers.postProcessBeforeInitialization(bean, "id")); Assertions.assertNotNull(bean.person); diff --git a/providers/provider-pojo/src/test/resources/microservice.yaml b/providers/provider-pojo/src/test/resources/microservice.yaml index 4db1321ba99..5ff7ed9ae41 100644 --- a/providers/provider-pojo/src/test/resources/microservice.yaml +++ b/providers/provider-pojo/src/test/resources/microservice.yaml @@ -20,4 +20,4 @@ servicecomb: consumer: default: empty producer: - default: empty \ No newline at end of file + default: empty diff --git a/providers/provider-rest-common/pom.xml b/providers/provider-rest-common/pom.xml index b2ee0c6cf08..075a9f9146b 100644 --- a/providers/provider-rest-common/pom.xml +++ b/providers/provider-rest-common/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb providers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT provider-rest-common Java Chassis::Providers::Rest Common diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketArgMapperFactory.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketArgMapperFactory.java new file mode 100644 index 00000000000..b0f554a8466 --- /dev/null +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketArgMapperFactory.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.provider.rest.common; + +import org.apache.servicecomb.swagger.invocation.arguments.ArgumentMapper; +import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory; + +import io.vertx.core.http.ServerWebSocket; + +public class ProducerServerWebSocketArgMapperFactory implements ProducerContextArgumentMapperFactory { + + @Override + public Class getContextClass() { + return ServerWebSocket.class; + } + + @Override + public ArgumentMapper create(String invocationArgumentName, String swaggerArgumentName) { + return new ProducerServerWebSocketMapper(invocationArgumentName, swaggerArgumentName); + } +} diff --git a/core/src/test/java/org/apache/servicecomb/core/handler/impl/ErrorToProducerResponseConverter.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketMapper.java similarity index 58% rename from core/src/test/java/org/apache/servicecomb/core/handler/impl/ErrorToProducerResponseConverter.java rename to providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketMapper.java index ab537d82417..21d040b5c87 100644 --- a/core/src/test/java/org/apache/servicecomb/core/handler/impl/ErrorToProducerResponseConverter.java +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProducerServerWebSocketMapper.java @@ -14,22 +14,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.core.handler.impl; -import jakarta.ws.rs.core.Response.Status; +package org.apache.servicecomb.provider.rest.common; -import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.common.rest.WebSocketTransportContext; import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter; +import org.apache.servicecomb.swagger.invocation.arguments.producer.AbstractProducerContextArgMapper; -public class ErrorToProducerResponseConverter implements ExceptionToProducerResponseConverter { - @Override - public Class getExceptionClass() { - return Error.class; + +public class ProducerServerWebSocketMapper extends AbstractProducerContextArgMapper { + public ProducerServerWebSocketMapper(String invocationArgumentName, String swaggerArgumentName) { + super(invocationArgumentName, swaggerArgumentName); } @Override - public Response convert(SwaggerInvocation swaggerInvocation, Error e) { - return Response.create(Status.OK, "response from error: " + e.getMessage()); + public Object createContextArg(SwaggerInvocation invocation) { + WebSocketTransportContext context = invocation.getTransportContext(); + return context.getServerWebSocket(); } } diff --git a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProviderRestCommonConfiguration.java b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProviderRestCommonConfiguration.java index 7f613d10f98..662c2ffba42 100644 --- a/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProviderRestCommonConfiguration.java +++ b/providers/provider-rest-common/src/main/java/org/apache/servicecomb/provider/rest/common/ProviderRestCommonConfiguration.java @@ -22,12 +22,7 @@ @Configuration public class ProviderRestCommonConfiguration { @Bean - public RestProducers restProducers() { + public static RestProducers restProducers() { return new RestProducers(); } - - @Bean - public RestConsumerProvider restConsumerProvider() { - return new RestConsumerProvider(); - } } diff --git a/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider b/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider index fd193cbb803..ee817607b15 100644 --- a/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider +++ b/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.core.ProducerProvider @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.provider.rest.common.RestProducerProvider \ No newline at end of file +org.apache.servicecomb.provider.rest.common.RestProducerProvider diff --git a/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory b/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory index 8898a2e63e4..1561d349cf1 100644 --- a/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory +++ b/providers/provider-rest-common/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory @@ -15,4 +15,5 @@ # limitations under the License. # -org.apache.servicecomb.provider.rest.common.ProducerHttpRequestArgMapperFactory \ No newline at end of file +org.apache.servicecomb.provider.rest.common.ProducerHttpRequestArgMapperFactory +org.apache.servicecomb.provider.rest.common.ProducerServerWebSocketArgMapperFactory diff --git a/providers/provider-springmvc/pom.xml b/providers/provider-springmvc/pom.xml index 3627e2a867a..4792027cc64 100644 --- a/providers/provider-springmvc/pom.xml +++ b/providers/provider-springmvc/pom.xml @@ -23,7 +23,7 @@ org.apache.servicecomb providers - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT provider-springmvc Java Chassis::Providers::Spring MVC @@ -61,11 +61,6 @@ log4j-slf4j-impl test - - javax.xml.bind - jaxb-api - test - org.mockito mockito-inline diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java index 288e5f7f8c2..ba056e180fb 100644 --- a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpRequest.java @@ -20,6 +20,7 @@ import java.io.OutputStream; import java.lang.reflect.Type; import java.net.URI; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -146,13 +147,13 @@ public HttpMethod getMethod() { } @Override - public String getMethodValue() { - return method.name(); + public URI getURI() { + return uri; } @Override - public URI getURI() { - return uri; + public Map getAttributes() { + return Collections.emptyMap(); } @Override diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java index c2599a929cf..e3caf103b09 100644 --- a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseClientHttpResponse.java @@ -94,6 +94,8 @@ public HttpStatus getStatusCode() throws IOException { } @Override + @Deprecated(since = "6.0") + @SuppressWarnings("deprecations") public int getRawStatusCode() throws IOException { return response.getStatusCode(); } diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRequestCallback.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRequestCallback.java index e452b0e4bc2..fd1e33cb967 100644 --- a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRequestCallback.java +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRequestCallback.java @@ -26,13 +26,13 @@ public class CseRequestCallback implements RequestCallback { private final Object requestBody; - private final RequestCallback orgCallback; + private final RequestCallback targetCallback; private final Type responseType; - public CseRequestCallback(Object requestBody, RequestCallback orgCallback, Type responseType) { + public CseRequestCallback(Object requestBody, RequestCallback targetCallback, Type responseType) { this.requestBody = requestBody; - this.orgCallback = orgCallback; + this.targetCallback = targetCallback; this.responseType = responseType; } @@ -41,7 +41,7 @@ public CseRequestCallback(Object requestBody, RequestCallback orgCallback, Type */ @Override public void doWithRequest(ClientHttpRequest request) throws IOException { - orgCallback.doWithRequest(request); + targetCallback.doWithRequest(request); CseClientHttpRequest req = (CseClientHttpRequest) request; req.setResponseType(overrideResponseType()); diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRestTemplate.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRestTemplate.java index c9b98d3aa53..b4f3923b004 100644 --- a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRestTemplate.java +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseRestTemplate.java @@ -170,6 +170,13 @@ public RequestCallback httpEntityCallback(Object requestBody, Type responseT return cseCallback; } + @Override + public RequestCallback acceptHeaderRequestCallback(Class responseType) { + RequestCallback callback = super.acceptHeaderRequestCallback(responseType); + CseRequestCallback cseCallback = new CseRequestCallback(null, callback, responseType); + return cseCallback; + } + @Override public boolean isAcceptable(String uri) { return uri.startsWith(RestConst.URI_PREFIX) || uri.startsWith(RestConst.URI_PREFIX_NEW); diff --git a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.java b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.java index 9cb98b1a765..08f2b9da880 100644 --- a/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.java +++ b/providers/provider-springmvc/src/main/java/org/apache/servicecomb/provider/springmvc/reference/CseUriTemplateHandler.java @@ -24,7 +24,8 @@ import org.springframework.web.util.DefaultUriBuilderFactory; public class CseUriTemplateHandler extends DefaultUriBuilderFactory { - public static final String APP_SERVICE_SEPARATOR_INTERNAL = "."; + // Must be one of URI unreserved `ALPHA / DIGIT / "-" / "." / "_" / "~"` + public static final String APP_SERVICE_SEPARATOR_INTERNAL = "~"; public CseUriTemplateHandler() { } diff --git a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.java b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.java index de9d2db85d7..9407ded239c 100644 --- a/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.java +++ b/providers/provider-springmvc/src/test/java/org/apache/servicecomb/provider/springmvc/reference/TestCseUriTemplateHandler.java @@ -28,14 +28,14 @@ public class TestCseUriTemplateHandler { public void testCrossApp() { CseUriTemplateHandler handler = new CseUriTemplateHandler(); URI uri = handler.expand("cse://{ap}{p}:ms/{path}?q={query}", "ap", "p", "path", "query"); - Assertions.assertEquals("cse://app.ms/path?q=query", uri.toString()); + Assertions.assertEquals("cse://app~ms/path?q=query", uri.toString()); Map vars = new HashMap<>(); vars.put("app", "app"); vars.put("path", "path"); vars.put("q", "query"); uri = handler.expand("cse://{app}:ms/{path}?q={q}", vars); - Assertions.assertEquals("cse://app.ms/path?q=query", uri.toString()); + Assertions.assertEquals("cse://app~ms/path?q=query", uri.toString()); uri = handler.expand("cse://ms/{path}?q={query}", "path", "query"); Assertions.assertEquals("cse://ms/path?q=query", uri.toString()); @@ -45,14 +45,14 @@ public void testCrossApp() { public void testCrossAppServiceComb() { CseUriTemplateHandler handler = new CseUriTemplateHandler(); URI uri = handler.expand("servicecomb://{ap}{p}:ms/{path}?q={query}", "ap", "p", "path", "query"); - Assertions.assertEquals("servicecomb://app.ms/path?q=query", uri.toString()); + Assertions.assertEquals("servicecomb://app~ms/path?q=query", uri.toString()); Map vars = new HashMap<>(); vars.put("app", "app"); vars.put("path", "path"); vars.put("q", "query"); uri = handler.expand("servicecomb://{app}:ms/{path}?q={q}", vars); - Assertions.assertEquals("servicecomb://app.ms/path?q=query", uri.toString()); + Assertions.assertEquals("servicecomb://app~ms/path?q=query", uri.toString()); uri = handler.expand("servicecomb://ms/{path}?q={query}", "path", "query"); Assertions.assertEquals("servicecomb://ms/path?q=query", uri.toString()); diff --git a/providers/provider-springmvc/src/test/resources/microservice.yaml b/providers/provider-springmvc/src/test/resources/microservice.yaml index 4db1321ba99..5ff7ed9ae41 100644 --- a/providers/provider-springmvc/src/test/resources/microservice.yaml +++ b/providers/provider-springmvc/src/test/resources/microservice.yaml @@ -20,4 +20,4 @@ servicecomb: consumer: default: empty producer: - default: empty \ No newline at end of file + default: empty diff --git a/samples/README.md b/samples/README.md index 7f504e3eeae..751a1d0aa93 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1 +1 @@ -java-chassis samples are moved to [servicecomb-samples](https://github.com/apache/servicecomb-samples/tree/master/java-chassis-samples) \ No newline at end of file +java-chassis samples are moved to [servicecomb-samples](https://github.com/apache/servicecomb-samples/tree/master/java-chassis-samples) diff --git a/service-registry/pom.xml b/service-registry/pom.xml index 0099771f40d..252d89eb433 100644 --- a/service-registry/pom.xml +++ b/service-registry/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 @@ -36,5 +36,8 @@ registry-lightweight registry-zero-config registry-nacos + registry-zookeeper + registry-etcd + registry-consul diff --git a/service-registry/registry-consul/pom.xml b/service-registry/registry-consul/pom.xml new file mode 100644 index 00000000000..380b9ea45fc --- /dev/null +++ b/service-registry/registry-consul/pom.xml @@ -0,0 +1,54 @@ + + + + + 4.0.0 + + org.apache.servicecomb + service-registry-parent + 3.4.0-SNAPSHOT + + + registry-consul + Java Chassis::Service Registry::Consul + + + + org.kiwiproject + consul-client + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + com.google.code.gson + gson + + + diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConfiguration.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConfiguration.java new file mode 100644 index 00000000000..36422f0f110 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConfiguration.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.consul; + +import com.google.common.net.HostAndPort; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.consul.config.ConsulDiscoveryProperties; +import org.apache.servicecomb.registry.consul.config.ConsulProperties; +import org.kiwiproject.consul.Consul; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +@ConditionalOnProperty(prefix = "servicecomb.registry.consul", name = "enabled", matchIfMissing = true) +@Configuration +public class ConsulConfiguration { + + @Bean + @ConfigurationProperties(prefix = ConsulConst.CONSUL_REGISTRY_PREFIX) + @ConditionalOnMissingBean + public ConsulProperties consulProperties() { + return new ConsulProperties(); + } + + @Bean + @ConfigurationProperties(prefix = ConsulConst.CONSUL_DISCOVERY_REGISTRY_PREFIX) + @ConditionalOnMissingBean + public ConsulDiscoveryProperties consulDiscoveryProperties() { + return new ConsulDiscoveryProperties(); + } + + @Bean + @ConditionalOnBean(value = {ConsulProperties.class, ConsulDiscoveryProperties.class}) + @ConditionalOnMissingBean + public Consul consulClient(ConsulProperties consulProperties, ConsulDiscoveryProperties consulDiscoveryProperties) { + Consul.Builder builder = Consul.builder() + .withHostAndPort(HostAndPort.fromParts(consulProperties.getHost(), consulProperties.getPort())); + if (StringUtils.isNotBlank(consulDiscoveryProperties.getAclToken())) { + builder.withAclToken(consulDiscoveryProperties.getAclToken()); + } + return builder.build(); + } + + @Bean + @ConditionalOnBean(value = {Consul.class, Environment.class, RegistrationId.class, DataCenterProperties.class}) + @ConditionalOnMissingBean + public ConsulDiscovery consulDiscovery() { + return new ConsulDiscovery(); + } + + @Bean + @ConditionalOnBean(value = {Consul.class, Environment.class, RegistrationId.class, DataCenterProperties.class}) + @ConditionalOnMissingBean + public ConsulRegistration consulRegistration() { + return new ConsulRegistration(); + } +} diff --git a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingDelegate.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConst.java similarity index 66% rename from handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingDelegate.java rename to service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConst.java index 3761a8d8454..7074c34a3e0 100644 --- a/handlers/handler-tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingDelegate.java +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulConst.java @@ -15,21 +15,15 @@ * limitations under the License. */ -package org.apache.servicecomb.tracing.zipkin; +package org.apache.servicecomb.registry.consul; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.swagger.invocation.Response; +public class ConsulConst { -import brave.Span; -import brave.Tracing; + public static final String CONSUL_REGISTRY_NAME = "consul-registry"; -interface ZipkinTracingDelegate { + public static final String CONSUL_REGISTRY_PREFIX = "servicecomb.registry.consul"; - Tracing tracer(); + public static final String CONSUL_DISCOVERY_REGISTRY_PREFIX = "servicecomb.registry.consul.discovery"; - Span createSpan(Invocation invocation); - - void onResponse(Span span, Response response, Throwable error); - - String name(); + public static final String CONSUL_DEFAULT_ENVIRONMENT = "production"; } diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscovery.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscovery.java new file mode 100644 index 00000000000..ce16ccc47b3 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscovery.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.consul; + +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import jakarta.annotation.Resource; +import jakarta.validation.constraints.NotNull; + +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.registry.api.Discovery; +import org.apache.servicecomb.registry.consul.config.ConsulDiscoveryProperties; +import org.apache.servicecomb.registry.consul.config.ConsulProperties; +import org.kiwiproject.consul.Consul; +import org.kiwiproject.consul.HealthClient; +import org.kiwiproject.consul.cache.ServiceHealthCache; +import org.kiwiproject.consul.cache.ServiceHealthKey; +import org.kiwiproject.consul.model.health.Service; +import org.kiwiproject.consul.model.health.ServiceHealth; +import org.kiwiproject.consul.option.Options; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ConsulDiscovery implements Discovery { + + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulDiscovery.class); + + @Resource + private ConsulProperties consulProperties; + + @Resource + private ConsulDiscoveryProperties consulDiscoveryProperties; + + @Resource + private Consul consulClient; + + @Resource + private Environment environment; + + private List consulDiscoveryInstanceList; + + private InstanceChangedListener instanceChangedListener; + + private ServiceHealthCache svHealth; + + @Override + public String name() { + return ConsulConst.CONSUL_REGISTRY_NAME; + } + + @Override + public boolean enabled(String application, String serviceName) { + return consulDiscoveryProperties.isEnabled(); + } + + @Override + public List findServiceInstances(String application, String serviceName) { + LOGGER.info("findServiceInstances application:{}, serviceName:{}", application, serviceName); + consulDiscoveryInstanceList = getInstances(serviceName); + return consulDiscoveryInstanceList; + } + + @Override + public List findServices(String application) { + LOGGER.info("ConsulDiscovery findServices(application={})", application); + Map> response = consulClient.catalogClient().getServices().getResponse(); + if (!CollectionUtils.isEmpty(response)) { + return Lists.newArrayList(response.keySet()); + } + return Lists.newArrayList(); + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + this.instanceChangedListener = instanceChangedListener; + } + + @Override + public boolean enabled() { + return consulDiscoveryProperties.isEnabled(); + } + + @Override + public void init() { + LOGGER.info("ConsulDiscovery init"); + } + + @Override + public void run() { + LOGGER.info("ConsulDiscovery run"); + String serviceName = BootStrapProperties.readServiceName(environment); + HealthClient healthClient = consulClient.healthClient(); + svHealth = ServiceHealthCache.newCache(healthClient, serviceName, true, Options.BLANK_QUERY_OPTIONS, + consulDiscoveryProperties.getWatchSeconds()); + svHealth.addListener((Map newValues) -> instanceChangedListener.onInstanceChanged( + name(), BootStrapProperties.readApplication(environment), serviceName, getInstances(serviceName))); + svHealth.start(); + } + + @Override + public void destroy() { + if (svHealth != null) { + svHealth.stop(); + } + String serviceId = consulDiscoveryProperties.getServiceId(); + LOGGER.info("ConsulDiscovery destroy consul service id={}", serviceId); + if (consulClient != null) { + LOGGER.info("ConsulDiscovery consulClient destroy"); + consulClient.agentClient().deregister(serviceId); + consulClient.destroy(); + } + } + + public List getInstances(@NotNull final String serviceName) { + List instances = new ArrayList<>(); + addInstancesToList(instances, serviceName); + return instances; + } + + private void addInstancesToList(List instances, String serviceName) { + List healthServices = getHealthServices(serviceName); + if (!CollectionUtils.isEmpty(healthServices)) { + for (ServiceHealth serviceHealth : healthServices) { + Service service = serviceHealth.getService(); + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + ConsulInstance consulInstance = gson.fromJson(service.getMeta().get("meta"), ConsulInstance.class); + instances.add(new ConsulDiscoveryInstance(consulInstance)); + } + } + } + + private List getHealthServices(String serviceName) { + HealthClient healthClient = consulClient.healthClient(); + return healthClient.getHealthyServiceInstances(serviceName).getResponse(); + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscoveryInstance.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscoveryInstance.java new file mode 100644 index 00000000000..f07e4a40543 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulDiscoveryInstance.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.consul; + +import org.apache.servicecomb.registry.api.DiscoveryInstance; + +public class ConsulDiscoveryInstance extends ConsulInstance implements DiscoveryInstance { + + public ConsulDiscoveryInstance(ConsulInstance consulInstance) { + super(consulInstance); + } + + @Override + public String getRegistryName() { + return ConsulConst.CONSUL_REGISTRY_NAME; + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulInstance.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulInstance.java new file mode 100644 index 00000000000..722b3bbe28e --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulInstance.java @@ -0,0 +1,204 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.consul; + +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstance; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ConsulInstance implements MicroserviceInstance { + private String serviceId; + + private String instanceId; + + private String environment; + + private String application; + + private String serviceName; + + private String alias; + + private String version; + + private String description; + + private DataCenterInfo dataCenterInfo; + + private List endpoints = new ArrayList<>(); + + private Map schemas = new HashMap<>(); + + private Map properties = new HashMap<>(); + + private MicroserviceInstanceStatus status; + + public ConsulInstance() { + + } + + public ConsulInstance(ConsulInstance other) { + this.serviceId = other.serviceId; + this.instanceId = other.instanceId; + this.environment = other.environment; + this.application = other.application; + this.serviceName = other.serviceName; + this.alias = other.alias; + this.version = other.version; + this.description = other.description; + this.dataCenterInfo = other.dataCenterInfo; + this.endpoints = other.endpoints; + this.schemas = other.schemas; + this.properties = other.properties; + this.status = other.status; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public void setApplication(String application) { + this.application = application; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { + this.dataCenterInfo = dataCenterInfo; + } + + public void setEndpoints(List endpoints) { + this.endpoints = endpoints; + } + + public void setSchemas(Map schemas) { + this.schemas = schemas; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public void setStatus(MicroserviceInstanceStatus status) { + this.status = status; + } + + @Override + public String getEnvironment() { + return this.environment; + } + + @Override + public String getApplication() { + return this.application; + } + + @Override + public String getServiceName() { + return this.serviceName; + } + + @Override + public String getAlias() { + return alias; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return dataCenterInfo == null ? new DataCenterInfo() : dataCenterInfo; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Map getProperties() { + return properties; + } + + @Override + public Map getSchemas() { + return schemas; + } + + @Override + public List getEndpoints() { + return endpoints; + } + + public void addSchema(String schemaId, String content) { + this.schemas.put(schemaId, content); + } + + public void addEndpoint(String endpoint) { + this.endpoints.add(endpoint); + } + + public void addProperty(String key, String value) { + this.properties.put(key, value); + } + + @Override + public String getInstanceId() { + return instanceId; + } + + @Override + public String getServiceId() { + return serviceId; + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return this.status; + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistration.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistration.java new file mode 100644 index 00000000000..6458d250fe7 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistration.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.consul; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import jakarta.annotation.Resource; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.invocation.endpoint.EndpointUtils; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.Registration; +import org.apache.servicecomb.registry.consul.config.ConsulDiscoveryProperties; +import org.kiwiproject.consul.AgentClient; +import org.kiwiproject.consul.Consul; +import org.kiwiproject.consul.model.agent.ImmutableRegistration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.util.CollectionUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +public class ConsulRegistration implements Registration { + + private static final Logger LOGGER = LoggerFactory.getLogger(ConsulRegistration.class); + + private ConsulInstance consulInstance; + + @Resource + private ConsulDiscoveryProperties consulDiscoveryProperties; + + @Resource + private Consul consulClient; + + @Resource + private Environment environment; + + @Resource + private DataCenterProperties dataCenterProperties; + + @Resource + private RegistrationId registrationId; + + @Value("${servicecomb.rest.address:127.0.0.1:8080}") + private String restAddress; + + private ImmutableRegistration.Builder registrationBuilder; + + @Override + public String name() { + return ConsulConst.CONSUL_REGISTRY_NAME; + } + + @Override + public ConsulRegistrationInstance getMicroserviceInstance() { + return new ConsulRegistrationInstance(consulInstance); + } + + @Override + public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + consulInstance.setStatus(status); + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + Map meta = new HashMap<>(); + meta.put("meta", gson.toJson(consulInstance)); + registrationBuilder.meta(meta); + ImmutableRegistration newService = registrationBuilder.build(); + AgentClient agentClient = consulClient.agentClient(); + agentClient.register(newService); + return true; + } + + @Override + public void addSchema(String schemaId, String content) { + if (consulDiscoveryProperties.isEnableSwaggerRegistration()) { + consulInstance.addSchema(schemaId, content); + } + } + + @Override + public void addEndpoint(String endpoint) { + consulInstance.addEndpoint(endpoint); + } + + @Override + public void addProperty(String key, String value) { + consulInstance.addProperty(key, value); + } + + @Override + public boolean enabled() { + return consulDiscoveryProperties.isEnabled(); + } + + @Override + public void init() { + LOGGER.info("ConsulRegistration init"); + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ConsulConst.CONSUL_DEFAULT_ENVIRONMENT; + } + String instanceId = registrationId.getInstanceId(); + consulInstance = new ConsulInstance(); + consulInstance.setInstanceId(instanceId); + consulInstance.setEnvironment(env); + consulInstance.setApplication(BootStrapProperties.readApplication(environment)); + consulInstance.setServiceName(BootStrapProperties.readServiceName(environment)); + consulInstance.setAlias(BootStrapProperties.readServiceAlias(environment)); + consulInstance.setDescription(BootStrapProperties.readServiceDescription(environment)); + if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { + DataCenterInfo dataCenterInfo = new DataCenterInfo(); + dataCenterInfo.setName(dataCenterProperties.getName()); + dataCenterInfo.setRegion(dataCenterProperties.getRegion()); + dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); + consulInstance.setDataCenterInfo(dataCenterInfo); + } + consulInstance.setProperties(BootStrapProperties.readServiceProperties(environment)); + consulInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); + consulInstance.setStatus( + MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); + } + + @Override + public void run() { + LOGGER.info("ConsulRegistration run"); + registrationBuilder = ImmutableRegistration.builder() + .name(consulInstance.getServiceName()); + List endpoints = consulInstance.getEndpoints(); + for (String endpoint : endpoints) { + Endpoint temp = EndpointUtils.parse(endpoint); + if (temp.getAddress() instanceof URIEndpointObject) { + String hostOrIp = ((URIEndpointObject) temp.getAddress()).getHostOrIp(); + int port = ((URIEndpointObject) temp.getAddress()).getPort(); + String serviceId = hostOrIp + ":" + port; + consulDiscoveryProperties.setServiceId(serviceId); + consulInstance.setServiceId(serviceId); + registrationBuilder.id(serviceId); + registrationBuilder.address(hostOrIp); + registrationBuilder.port(port); + break; + } + } + List tags = consulDiscoveryProperties.getTags(); + if (CollectionUtils.isEmpty(tags)) { + tags.add(consulInstance.getServiceName()); + } + registrationBuilder.tags(tags); + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + Map meta = new HashMap<>(); + meta.put("meta", gson.toJson(consulInstance)); + registrationBuilder.meta(meta); + ImmutableRegistration newService = registrationBuilder.build(); + AgentClient agentClient = consulClient.agentClient(); + agentClient.register(newService); + LOGGER.info("ConsulRegistration newService"); + } + + @Override + public void destroy() { + LOGGER.info("ConsulRegistration destroy"); + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistrationInstance.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistrationInstance.java new file mode 100644 index 00000000000..dc0b83eaefc --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/ConsulRegistrationInstance.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.consul; + + +import org.apache.servicecomb.registry.api.RegistrationInstance; + +public class ConsulRegistrationInstance extends ConsulInstance implements RegistrationInstance { + + public ConsulRegistrationInstance(ConsulInstance instance) { + super(instance); + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulDiscoveryProperties.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulDiscoveryProperties.java new file mode 100644 index 00000000000..a6a23192eb4 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulDiscoveryProperties.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.consul.config; + +import org.springframework.core.style.ToStringCreator; + +import java.util.ArrayList; +import java.util.List; + + +public class ConsulDiscoveryProperties { + + private String aclToken; + + private List tags = new ArrayList<>(); + + private Boolean enabled = true; + + private Integer watchSeconds = 8; + + private Boolean enableSwaggerRegistration = false; + + private String serviceId; + + public String getAclToken() { + return this.aclToken; + } + + public void setAclToken(String aclToken) { + this.aclToken = aclToken; + } + + public List getTags() { + return this.tags; + } + + public void setTags(List tags) { + this.tags = tags; + } + + public Boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public Boolean isEnableSwaggerRegistration() { + return enableSwaggerRegistration; + } + + public void setEnableSwaggerRegistration(Boolean enableSwaggerRegistration) { + this.enableSwaggerRegistration = enableSwaggerRegistration; + } + + public Integer getWatchSeconds() { + return watchSeconds; + } + + public void setWatchSeconds(Integer watchSeconds) { + this.watchSeconds = watchSeconds; + } + + public String getServiceId() { + return serviceId; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + @Override + public String toString() { + return new ToStringCreator(this).append("aclToken", this.aclToken) + .append("enabled", this.enabled) + .append("serviceId", this.serviceId) + .append("tags", this.tags) + .append("watchSeconds", this.watchSeconds) + .toString(); + } +} diff --git a/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulProperties.java b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulProperties.java new file mode 100644 index 00000000000..bb04a758330 --- /dev/null +++ b/service-registry/registry-consul/src/main/java/org/apache/servicecomb/registry/consul/config/ConsulProperties.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.registry.consul.config; + +import jakarta.validation.constraints.NotNull; + +import org.springframework.validation.annotation.Validated; + + +@Validated +public class ConsulProperties { + + @NotNull + private String host = "localhost"; + + private String scheme = "http"; + + @NotNull + private Integer port = 8500; + + private Boolean enabled = true; + + public String getHost() { + return this.host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return this.port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public Boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public String getScheme() { + return this.scheme; + } + + public void setScheme(String scheme) { + this.scheme = scheme; + } + + @Override + public String toString() { + return "ConsulProperties{" + "host='" + this.host + '\'' + ", port=" + this.port + ", scheme=" + this.scheme + + ", enabled=" + this.enabled + "}"; + } +} diff --git a/service-registry/registry-consul/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-consul/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..9a0422d67c2 --- /dev/null +++ b/service-registry/registry-consul/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +org.apache.servicecomb.registry.consul.ConsulConfiguration diff --git a/service-registry/registry-etcd/pom.xml b/service-registry/registry-etcd/pom.xml new file mode 100644 index 00000000000..10c037a03aa --- /dev/null +++ b/service-registry/registry-etcd/pom.xml @@ -0,0 +1,52 @@ + + + + + + org.apache.servicecomb + service-registry-parent + 3.4.0-SNAPSHOT + + 4.0.0 + + registry-etcd + Java Chassis::Service Registry::Etcd + + + + + io.etcd + jetcd-core + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + + diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java new file mode 100644 index 00000000000..a926c7bbc11 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConfiguration.java @@ -0,0 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.etcd; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class EtcdConfiguration { + @Bean + @ConfigurationProperties(prefix = EtcdConst.ETCD_REGISTRY_PREFIX) + public EtcdRegistryProperties etcdRegistryProperties() { + return new EtcdRegistryProperties(); + } + + @Bean + public EtcdDiscovery etcdDiscovery() { + return new EtcdDiscovery(); + } + + @Bean + public EtcdRegistration etcdRegistration() { + return new EtcdRegistration(); + } +} diff --git a/providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/TestPojoConsumerProvider.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java similarity index 63% rename from providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/TestPojoConsumerProvider.java rename to service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java index 0321934ddb6..03b28f0d2ae 100644 --- a/providers/provider-pojo/src/test/java/org/apache/servicecomb/provider/pojo/TestPojoConsumerProvider.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdConst.java @@ -14,19 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.servicecomb.registry.etcd; -package org.apache.servicecomb.provider.pojo; +public class EtcdConst { -import static org.apache.servicecomb.provider.pojo.PojoConst.POJO; -import static org.hamcrest.core.Is.is; + public static final String ETCD_REGISTRY_NAME = "etcd-registry"; -import org.hamcrest.MatcherAssert; -import org.junit.jupiter.api.Test; + public static final String ETCD_DISCOVERY_ROOT = "/servicecomb/registry/%s"; -public class TestPojoConsumerProvider { - @Test - public void providerNameIsPojo() { - PojoConsumerProvider pojoConsumerProvider = new PojoConsumerProvider(); - MatcherAssert.assertThat(pojoConsumerProvider.getName(), is(POJO)); - } + public static final String ETCD_REGISTRY_PREFIX = "servicecomb.registry.etcd"; + + public static final String ETCD_DISCOVERY_ENABLED = ETCD_REGISTRY_PREFIX + ".%s.%s.enabled"; + + public static final String ETCD_DEFAULT_ENVIRONMENT = "production"; } diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java new file mode 100644 index 00000000000..b43b9bdabd9 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscovery.java @@ -0,0 +1,221 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.etcd; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.foundation.common.utils.ConditionWaiter; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.foundation.common.utils.MuteExceptionUtil; +import org.apache.servicecomb.registry.api.Discovery; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import com.google.common.collect.Lists; + +import io.etcd.jetcd.ByteSequence; +import io.etcd.jetcd.Client; +import io.etcd.jetcd.KeyValue; +import io.etcd.jetcd.Watch; +import io.etcd.jetcd.kv.GetResponse; +import io.etcd.jetcd.options.GetOption; +import io.etcd.jetcd.options.WatchOption; + +public class EtcdDiscovery implements Discovery { + + private Environment environment; + + private String basePath; + + private EtcdRegistryProperties etcdRegistryProperties; + + private Client client; + + private InstanceChangedListener instanceChangedListener; + + private static final Logger LOGGER = LoggerFactory.getLogger(EtcdDiscovery.class); + + private Map watchMap = new ConcurrentHashMapEx<>(); + + @Autowired + @SuppressWarnings("unused") + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + @SuppressWarnings("unused") + public void setEtcdRegistryProperties(EtcdRegistryProperties etcdRegistryProperties) { + this.etcdRegistryProperties = etcdRegistryProperties; + } + + @Override + public String name() { + return EtcdConst.ETCD_REGISTRY_NAME; + } + + @Override + public boolean enabled(String application, String serviceName) { + return environment.getProperty(String.format(EtcdConst.ETCD_DISCOVERY_ENABLED, application, serviceName), + boolean.class, true); + } + + @Override + public List findServiceInstances(String application, String serviceName) { + + String prefixPath = basePath + "/" + application + "/" + serviceName; + watchMap.computeIfAbsent(prefixPath, listenServiceName -> { + Watch watchClient = client.getWatchClient(); + try { + ByteSequence prefixByteSeq = ByteSequence.from(prefixPath, Charset.defaultCharset()); + watchClient.watch(prefixByteSeq, WatchOption.builder().withPrefix(prefixByteSeq).build(), + resp -> watchNode(application, serviceName, prefixPath)); + } catch (Exception e) { + LOGGER.error("Failed to add watch", e); + } + return watchClient; + }); + +// async get all instances,because sync is bad way in etcd. + ConditionWaiter> waiter = new ConditionWaiter<>(new ArrayList<>(), 50, + TimeUnit.MILLISECONDS); + waiter.executeTaskAsync(() -> { + CompletableFuture getFuture = client.getKVClient() + .get(ByteSequence.from(prefixPath, StandardCharsets.UTF_8), + GetOption.builder().withPrefix(ByteSequence.from(prefixPath, StandardCharsets.UTF_8)).build()); + GetResponse getResponse = getFuture.get(); + return convertServiceInstanceList(getResponse.getKvs()); + }); + return waiter.waitForCompletion(); + } + + private void watchNode(String application, String serviceName, String prefixPath) { + + CompletableFuture getFuture = client.getKVClient() + .get(ByteSequence.from(prefixPath, StandardCharsets.UTF_8), + GetOption.builder().withPrefix(ByteSequence.from(prefixPath, StandardCharsets.UTF_8)).build()); + getFuture.thenAcceptAsync(response -> { + List discoveryInstanceList = convertServiceInstanceList(response.getKvs()); + instanceChangedListener.onInstanceChanged(name(), application, serviceName, discoveryInstanceList); + }).exceptionally(e -> { + LOGGER.error("watchNode error", e); + return null; + }); + } + + private List getValuesByPrefix(String prefix) { + + CompletableFuture getFuture = client.getKVClient() + .get(ByteSequence.from(prefix, StandardCharsets.UTF_8), + GetOption.builder().withPrefix(ByteSequence.from(prefix, StandardCharsets.UTF_8)).build()); + GetResponse response = MuteExceptionUtil.builder().withLog("get kv by prefix error") + .executeCompletableFuture(getFuture); + return response.getKvs(); + } + + private List convertServiceInstanceList(List keyValueList) { + + List list = Lists.newArrayListWithExpectedSize(keyValueList.size()); + for (KeyValue keyValue : keyValueList) { + EtcdDiscoveryInstance etcdDiscoveryInstance = getEtcdDiscoveryInstance(keyValue); + list.add(etcdDiscoveryInstance); + } + return list; + } + + private static EtcdDiscoveryInstance getEtcdDiscoveryInstance(KeyValue keyValue) { + String valueJson = new String(keyValue.getValue().getBytes(), Charset.defaultCharset()); + + EtcdInstance etcdInstance = MuteExceptionUtil.builder() + .withLog("convert json value to obj from etcd failure, {}", valueJson) + .executeFunctionWithDoubleParam(JsonUtils::readValue, valueJson.getBytes(StandardCharsets.UTF_8), + EtcdInstance.class); + return new EtcdDiscoveryInstance(etcdInstance); + } + + @Override + public List findServices(String application) { + + ConditionWaiter> waiter = new ConditionWaiter<>(new ArrayList<>(), 50, TimeUnit.MILLISECONDS); + waiter.executeTaskAsync(() -> { + String prefixPath = basePath + "/" + application; + List endpointKv = getValuesByPrefix(prefixPath); + return endpointKv.stream() + .map(kv -> kv.getKey().toString(StandardCharsets.UTF_8)) + .map(key -> { + String[] parts = StringUtils.split(key, "/"); + return parts.length > 5 ? parts[4] : null; + }) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + }); + return waiter.waitForCompletion(); + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + this.instanceChangedListener = instanceChangedListener; + } + + @Override + public boolean enabled() { + return etcdRegistryProperties.isEnabled(); + } + + @Override + public void init() { + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = EtcdConst.ETCD_DEFAULT_ENVIRONMENT; + } + basePath = String.format(EtcdConst.ETCD_DISCOVERY_ROOT, env); + } + + @Override + public void run() { + if (StringUtils.isEmpty(etcdRegistryProperties.getAuthenticationInfo())) { + this.client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()).build(); + } else { + String[] authInfo = etcdRegistryProperties.getAuthenticationInfo().split(":"); + this.client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()) + .user(ByteSequence.from(authInfo[0], Charset.defaultCharset())) + .password(ByteSequence.from(authInfo[1], Charset.defaultCharset())).build(); + } + } + + @Override + public void destroy() { + if (client != null) { + client.close(); + watchMap = null; + } + } +} diff --git a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterHeaderFilterExt.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java similarity index 69% rename from handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterHeaderFilterExt.java rename to service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java index 7951850f54a..d75a3b04cf4 100644 --- a/handlers/handler-router/src/main/java/org/apache/servicecomb/router/custom/RouterHeaderFilterExt.java +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdDiscoveryInstance.java @@ -14,22 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.router.custom; +package org.apache.servicecomb.registry.etcd; -import java.util.Map; +import org.apache.servicecomb.registry.api.DiscoveryInstance; +public class EtcdDiscoveryInstance extends EtcdInstance implements DiscoveryInstance { -/** - * allow users to filter not necessary headers in request. - **/ -public interface RouterHeaderFilterExt { - - default int getOrder() { - return 0; + public EtcdDiscoveryInstance(EtcdInstance other) { + super(other); } - default boolean enabled() { - return true; + @Override + public String getRegistryName() { + return EtcdConst.ETCD_REGISTRY_NAME; } - - Map doFilter(Map invokeHeader); } diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java new file mode 100644 index 00000000000..071e84bf3bc --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdInstance.java @@ -0,0 +1,221 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.etcd; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstance; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; + +public class EtcdInstance implements MicroserviceInstance { + private String serviceId; + + private String instanceId; + + private String environment; + + private String application; + + private String serviceName; + + private String alias; + + private String version; + + private String description; + + private DataCenterInfo dataCenterInfo; + + private List endpoints = new ArrayList<>(); + + private Map schemas = new HashMap<>(); + + private Map properties = new HashMap<>(); + + private MicroserviceInstanceStatus status; + + public EtcdInstance() { + + } + + public EtcdInstance(EtcdInstance other) { + this.serviceId = other.serviceId; + this.instanceId = other.instanceId; + this.environment = other.environment; + this.application = other.application; + this.serviceName = other.serviceName; + this.alias = other.alias; + this.version = other.version; + this.description = other.description; + this.dataCenterInfo = other.dataCenterInfo; + this.endpoints = other.endpoints; + this.schemas = other.schemas; + this.properties = other.properties; + this.status = other.status; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public void setApplication(String application) { + this.application = application; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { + this.dataCenterInfo = dataCenterInfo; + } + + public void setEndpoints(List endpoints) { + this.endpoints = endpoints; + } + + public void setSchemas(Map schemas) { + this.schemas = schemas; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public void setStatus(MicroserviceInstanceStatus status) { + this.status = status; + } + + @Override + public String getEnvironment() { + return this.environment; + } + + @Override + public String getApplication() { + return this.application; + } + + @Override + public String getServiceName() { + return this.serviceName; + } + + @Override + public String getAlias() { + return alias; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return dataCenterInfo == null ? new DataCenterInfo() : dataCenterInfo; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Map getProperties() { + return properties; + } + + @Override + public Map getSchemas() { + return schemas; + } + + @Override + public List getEndpoints() { + return endpoints; + } + + public void addSchema(String schemaId, String content) { + this.schemas.put(schemaId, content); + } + + public void addEndpoint(String endpoint) { + this.endpoints.add(endpoint); + } + + public void addProperty(String key, String value) { + this.properties.put(key, value); + } + + @Override + public String getInstanceId() { + return instanceId; + } + + @Override + public String getServiceId() { + return serviceId; + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return this.status; + } + + @Override + public String toString() { + return "EtcdInstance{" + + "serviceId='" + serviceId + '\'' + + ", instanceId='" + instanceId + '\'' + + ", environment='" + environment + '\'' + + ", application='" + application + '\'' + + ", serviceName='" + serviceName + '\'' + + ", alias='" + alias + '\'' + + ", version='" + version + '\'' + + ", description='" + description + '\'' + + ", dataCenterInfo=" + dataCenterInfo + + ", endpoints=" + endpoints + + ", schemas=" + schemas + + ", properties=" + properties + + '}'; + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java new file mode 100644 index 00000000000..3fabfac4e23 --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistration.java @@ -0,0 +1,207 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.etcd; + +import java.nio.charset.Charset; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.foundation.common.utils.JsonUtils; +import org.apache.servicecomb.foundation.common.utils.MuteExceptionUtil; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.Registration; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +import io.etcd.jetcd.ByteSequence; +import io.etcd.jetcd.Client; +import io.etcd.jetcd.KV; +import io.etcd.jetcd.Lease; +import io.etcd.jetcd.kv.PutResponse; +import io.etcd.jetcd.options.PutOption; + +public class EtcdRegistration implements Registration { + + private EtcdInstance etcdInstance; + + private Environment environment; + + private String basePath; + + private RegistrationId registrationId; + + private DataCenterProperties dataCenterProperties; + + private EtcdRegistryProperties etcdRegistryProperties; + + private Client client; + + private ScheduledExecutorService executorService; + + private String keyPath; + + private Long leaseId; + + @Autowired + @SuppressWarnings("unused") + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + @SuppressWarnings("unused") + public void setEtcdRegistryProperties(EtcdRegistryProperties etcdRegistryProperties) { + this.etcdRegistryProperties = etcdRegistryProperties; + } + + @Autowired + public void setRegistrationId(RegistrationId registrationId) { + this.registrationId = registrationId; + } + + @Autowired + @SuppressWarnings("unused") + public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { + this.dataCenterProperties = dataCenterProperties; + } + + @Override + public String name() { + return EtcdConst.ETCD_REGISTRY_NAME; + } + + @Override + public EtcdRegistrationInstance getMicroserviceInstance() { + return new EtcdRegistrationInstance(etcdInstance); + } + + @Override + public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + this.etcdInstance.setStatus(status); + String valueJson = MuteExceptionUtil.builder().withLog("to json, key:{}, value:{}", keyPath, etcdInstance) + .executeFunction(JsonUtils::writeValueAsString, etcdInstance); + register(ByteSequence.from(keyPath, Charset.defaultCharset()), + ByteSequence.from(valueJson, Charset.defaultCharset())); + return true; + } + + @Override + public void addSchema(String schemaId, String content) { + if (etcdRegistryProperties.isEnableSwaggerRegistration()) { + etcdInstance.addSchema(schemaId, content); + } + } + + @Override + public void addEndpoint(String endpoint) { + etcdInstance.addEndpoint(endpoint); + } + + @Override + public void addProperty(String key, String value) { + etcdInstance.addProperty(key, value); + } + + @Override + public boolean enabled() { + return etcdRegistryProperties.isEnabled(); + } + + @Override + public void init() { + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = EtcdConst.ETCD_DEFAULT_ENVIRONMENT; + } + basePath = String.format(EtcdConst.ETCD_DISCOVERY_ROOT, env); + etcdInstance = new EtcdInstance(); + etcdInstance.setInstanceId(registrationId.getInstanceId()); + etcdInstance.setEnvironment(env); + etcdInstance.setApplication(BootStrapProperties.readApplication(environment)); + etcdInstance.setServiceName(BootStrapProperties.readServiceName(environment)); + etcdInstance.setAlias(BootStrapProperties.readServiceAlias(environment)); + etcdInstance.setDescription(BootStrapProperties.readServiceDescription(environment)); + if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { + DataCenterInfo dataCenterInfo = new DataCenterInfo(); + dataCenterInfo.setName(dataCenterProperties.getName()); + dataCenterInfo.setRegion(dataCenterProperties.getRegion()); + dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); + etcdInstance.setDataCenterInfo(dataCenterInfo); + } + etcdInstance.setProperties(BootStrapProperties.readServiceProperties(environment)); + etcdInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); + etcdInstance.setStatus( + MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); + } + + @Override + public void run() { + client = Client.builder().endpoints(etcdRegistryProperties.getConnectString()) + .build(); + keyPath = basePath + "/" + + BootStrapProperties.readApplication(environment) + "/" + + BootStrapProperties.readServiceName(environment) + "/" + + registrationId.getInstanceId(); + + String valueJson = MuteExceptionUtil.builder().withLog("to json, key:{}, value:{}", keyPath, etcdInstance) + .executeFunction(JsonUtils::writeValueAsString, etcdInstance); + register(ByteSequence.from(keyPath, Charset.defaultCharset()), + ByteSequence.from(valueJson, Charset.defaultCharset())); + } + + public void register(ByteSequence key, ByteSequence value) { + + Lease leaseClient = client.getLeaseClient(); + leaseId = MuteExceptionUtil.builder().withLog("get lease id, key:{}, value:{}", keyPath, etcdInstance) + .executeCompletableFuture(leaseClient.grant(60)).getID(); + KV kvClient = client.getKVClient(); + + PutOption putOption = PutOption.builder().withLeaseId(leaseId).build(); + CompletableFuture putResponse = kvClient.put(key, value, putOption); + putResponse.thenRun(() -> { + executorService = Executors.newSingleThreadScheduledExecutor(); + executorService.scheduleAtFixedRate( + () -> MuteExceptionUtil.builder().withLog("reRegister, {}, {}", keyPath, etcdInstance) + .executeFunction(leaseClient::keepAliveOnce, leaseId), 0, 5, TimeUnit.SECONDS); + }); + } + + private void unregister() { + // close job + executorService.shutdownNow(); + + // close etcd node + Lease leaseClient = client.getLeaseClient(); + leaseClient.revoke(leaseId); + client.getKVClient().delete(ByteSequence.from(keyPath, Charset.defaultCharset())); + } + + @Override + public void destroy() { + if (client != null) { + unregister(); + client.close(); + } + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java new file mode 100644 index 00000000000..3dfa78c338e --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistrationInstance.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.etcd; + +import org.apache.servicecomb.registry.api.RegistrationInstance; + +public class EtcdRegistrationInstance extends EtcdInstance implements RegistrationInstance { + public EtcdRegistrationInstance(EtcdInstance instance) { + super(instance); + } +} diff --git a/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java new file mode 100644 index 00000000000..c4d61094bfc --- /dev/null +++ b/service-registry/registry-etcd/src/main/java/org/apache/servicecomb/registry/etcd/EtcdRegistryProperties.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.etcd; + +public class EtcdRegistryProperties { + private boolean enabled = true; + + private boolean ephemeral = true; + + private String connectString = "http://127.0.0.1:2379"; + + private String authenticationSchema; + + private String authenticationInfo; + + private int connectionTimeoutMillis = 1000; + + private int sessionTimeoutMillis = 60000; + + private boolean enableSwaggerRegistration = false; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isEphemeral() { + return ephemeral; + } + + public void setEphemeral(boolean ephemeral) { + this.ephemeral = ephemeral; + } + + public String getConnectString() { + return connectString; + } + + public void setConnectString(String connectString) { + this.connectString = connectString; + } + + public int getConnectionTimeoutMillis() { + return connectionTimeoutMillis; + } + + public void setConnectionTimeoutMillis(int connectionTimeoutMillis) { + this.connectionTimeoutMillis = connectionTimeoutMillis; + } + + public int getSessionTimeoutMillis() { + return sessionTimeoutMillis; + } + + public void setSessionTimeoutMillis(int sessionTimeoutMillis) { + this.sessionTimeoutMillis = sessionTimeoutMillis; + } + + public boolean isEnableSwaggerRegistration() { + return enableSwaggerRegistration; + } + + public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) { + this.enableSwaggerRegistration = enableSwaggerRegistration; + } + + public String getAuthenticationSchema() { + return authenticationSchema; + } + + public void setAuthenticationSchema(String authenticationSchema) { + this.authenticationSchema = authenticationSchema; + } + + public String getAuthenticationInfo() { + return authenticationInfo; + } + + public void setAuthenticationInfo(String authenticationInfo) { + this.authenticationInfo = authenticationInfo; + } +} diff --git a/service-registry/registry-etcd/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-etcd/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..999ffb6b04b --- /dev/null +++ b/service-registry/registry-etcd/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +org.apache.servicecomb.registry.etcd.EtcdConfiguration diff --git a/service-registry/registry-lightweight/pom.xml b/service-registry/registry-lightweight/pom.xml index badd5863c55..43a27c5cfe6 100644 --- a/service-registry/registry-lightweight/pom.xml +++ b/service-registry/registry-lightweight/pom.xml @@ -22,7 +22,7 @@ service-registry-parent org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -49,4 +49,4 @@ test - \ No newline at end of file + diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightDiscovery.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightDiscovery.java index 897e1b7c503..f52e8345534 100644 --- a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightDiscovery.java +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightDiscovery.java @@ -25,18 +25,13 @@ import com.google.common.eventbus.EventBus; -@SuppressWarnings("UnstableApiUsage") public abstract class AbstractLightweightDiscovery implements Discovery { - public static final String ZERO_CONFIG_NAME = "zero-config-discovery"; - public static final String ZERO_DISCOVERY_ENABLED = "servicecomb.registry.zero-config.%s.%s.enabled"; protected EventBus eventBus; protected Store store; - protected String revision; - private Environment environment; @Autowired @@ -45,13 +40,13 @@ public void setEnvironment(Environment environment) { } @Autowired - public AbstractLightweightDiscovery setEventBus(EventBus eventBus) { + public AbstractLightweightDiscovery setEventBus(EventBus eventBus) { this.eventBus = eventBus; return this; } @Autowired - public AbstractLightweightDiscovery setStore(Store store) { + public AbstractLightweightDiscovery setStore(Store store) { this.store = store; return this; } diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightRegistration.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightRegistration.java index 301beb38d57..06aa7c9b73f 100644 --- a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightRegistration.java +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/AbstractLightweightRegistration.java @@ -42,19 +42,19 @@ public abstract class AbstractLightweightRegistration setEventBus(EventBus eventBus) { this.eventBus = eventBus; return this; } @Autowired - public AbstractLightweightRegistration setStoreService(StoreService storeService) { + public AbstractLightweightRegistration setStoreService(StoreService storeService) { this.storeService = storeService; return this; } @Autowired - public AbstractLightweightRegistration setSelf(Self self) { + public AbstractLightweightRegistration setSelf(Self self) { this.self = self; return this; } diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/LightWeightRegistryConfiguration.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/LightWeightRegistryConfiguration.java index 78ab582e135..862356d0dba 100644 --- a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/LightWeightRegistryConfiguration.java +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/LightWeightRegistryConfiguration.java @@ -77,4 +77,3 @@ public DiscoveryClient zeroConfigDiscoveryClient() { ZERO_CONFIG_DISCOVERY_CLIENT, DiscoveryClient.class); } } - diff --git a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java index c9b863eae17..d6acccbf737 100644 --- a/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java +++ b/service-registry/registry-lightweight/src/main/java/org/apache/servicecomb/registry/lightweight/Self.java @@ -17,9 +17,8 @@ package org.apache.servicecomb.registry.lightweight; -import java.util.UUID; - import org.apache.servicecomb.core.BootListener; +import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceFactory; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; @@ -41,11 +40,18 @@ public class Self implements InitializingBean, BootListener { private Environment environment; + private RegistrationId registrationId; + @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } + @Autowired + public void setRegistrationId(RegistrationId registrationId) { + this.registrationId = registrationId; + } + @Override public void afterPropertiesSet() { init(environment); @@ -61,7 +67,7 @@ public Self init(Environment environment) { microservice.getVersion())); instance = microservice.getInstance() - .instanceId(UUID.randomUUID().toString()) + .instanceId(registrationId.getInstanceId()) .serviceId(microservice.getServiceId()); microserviceInfo diff --git a/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/MessageTest.java b/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/MessageTest.java index 66181ce87e7..0e79f3296c3 100644 --- a/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/MessageTest.java +++ b/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/MessageTest.java @@ -18,10 +18,12 @@ package org.apache.servicecomb.registry.lightweight; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; import org.junit.jupiter.api.Test; import io.vertx.core.json.Json; +import org.skyscreamer.jsonassert.JSONAssert; class MessageTest { private String toLinuxPrettyJson(Object value) { @@ -33,7 +35,8 @@ private String toLinuxPrettyJson(Object value) { void should_encode_register_type() { Message msg = Message.of(MessageType.REGISTER, new RegisterRequest()); - assertThat(toLinuxPrettyJson(msg)).isEqualTo("" + try { + JSONAssert.assertEquals(toLinuxPrettyJson(msg), "" + "{\n" + " \"type\" : \"REGISTER\",\n" + " \"body\" : {\n" @@ -45,7 +48,11 @@ void should_encode_register_type() { + " \"status\" : null,\n" + " \"endpoints\" : null\n" + " }\n" - + "}"); + + "}", false); + } catch (Exception e) { + fail("Failed to compare JSONs: " + e.getMessage(), e); + } + } @Test @@ -60,14 +67,18 @@ void should_decode_register_type() { void should_encode_unregister_type() { Message msg = Message.of(MessageType.UNREGISTER, new UnregisterRequest()); - assertThat(toLinuxPrettyJson(msg)).isEqualTo("" + try { + JSONAssert.assertEquals(toLinuxPrettyJson(msg), "" + "{\n" + " \"type\" : \"UNREGISTER\",\n" + " \"body\" : {\n" + " \"serviceId\" : null,\n" + " \"instanceId\" : null\n" + " }\n" - + "}"); + + "}", false); + } catch (Exception e) { + fail("Failed to compare JSONs: " + e.getMessage(), e); + } } @Test diff --git a/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/StoreServiceTest.java b/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/StoreServiceTest.java index bab02249c89..efaf9665dc5 100644 --- a/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/StoreServiceTest.java +++ b/service-registry/registry-lightweight/src/test/java/org/apache/servicecomb/registry/lightweight/StoreServiceTest.java @@ -23,6 +23,7 @@ import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.foundation.test.scaffolding.time.MockTicker; +import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.store.InstanceStore; @@ -80,8 +81,9 @@ void setUp() { protected RegisterRequest createRegisterRequest() { return new MockRegisterRequest(); } - } - .init(environment) + }; + self.setRegistrationId(new RegistrationId()); + self.init(environment) .addSchema("schema-1", "s1") .addEndpoint("rest://1.1.1.1:80"); diff --git a/service-registry/registry-local/pom.xml b/service-registry/registry-local/pom.xml index 9118bde3f3a..e3ddcc5510b 100644 --- a/service-registry/registry-local/pom.xml +++ b/service-registry/registry-local/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb service-registry-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscovery.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscovery.java index a439974d3b9..4d3a0a6e822 100644 --- a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscovery.java +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscovery.java @@ -62,6 +62,11 @@ public List findServiceInstances(String application, Str return this.localRegistryStore.findServiceInstances(application, serviceName); } + @Override + public List findServices(String application) { + return this.localRegistryStore.findServices(application); + } + @Override public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscoveryInstance.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscoveryInstance.java index 8fb0e57f334..ff8e10794f1 100644 --- a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscoveryInstance.java +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalDiscoveryInstance.java @@ -56,12 +56,12 @@ public LocalDiscoveryInstance(LocalOpenAPIRegistry localOpenAPIRegistry, SwaggerGenerator generator = SwaggerGenerator.create(v); OpenAPI openAPI = generator.generate(); if (openAPI == null) { - throw new IllegalStateException(String.format("Generate schema for %s/%s/%s faild.", + throw new IllegalStateException(String.format("Generate schema for %s/%s/%s failed.", registryBean.getAppId(), registryBean.getServiceName(), k)); } String schemaContent = SwaggerUtils.swaggerToString(openAPI); if (StringUtils.isEmpty(schemaContent)) { - throw new IllegalStateException(String.format("Generate schema for %s/%s/%s faild.", + throw new IllegalStateException(String.format("Generate schema for %s/%s/%s failed.", registryBean.getAppId(), registryBean.getServiceName(), k)); } localOpenAPIRegistry.registerOpenAPI(registryBean.getAppId(), registryBean.getServiceName(), diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistrationInstance.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistrationInstance.java index 9ff0a0e4992..e07527636a9 100644 --- a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistrationInstance.java +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistrationInstance.java @@ -16,7 +16,6 @@ */ package org.apache.servicecomb.localregistry; -import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -25,6 +24,7 @@ import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; +import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.DataCenterInfo; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.RegistrationInstance; @@ -47,7 +47,9 @@ public class LocalRegistrationInstance implements RegistrationInstance { public LocalRegistrationInstance( Environment environment, - DataCenterProperties dataCenterProperties, LocalOpenAPIRegistry localOpenAPIRegistry) { + DataCenterProperties dataCenterProperties, + LocalOpenAPIRegistry localOpenAPIRegistry, + RegistrationId registrationId) { this.environment = environment; this.localOpenAPIRegistry = localOpenAPIRegistry; @@ -58,8 +60,7 @@ public LocalRegistrationInstance( this.properties.putAll(BootStrapProperties.readServiceProperties(environment)); - this.instanceId = System.currentTimeMillis() + "-" + - ManagementFactory.getRuntimeMXBean().getPid(); + this.instanceId = registrationId.getInstanceId(); } @Override @@ -118,12 +119,7 @@ public String getInstanceId() { } @Override - public MicroserviceInstanceStatus getInitialStatus() { - return MicroserviceInstanceStatus.STARTING; - } - - @Override - public MicroserviceInstanceStatus getReadyStatus() { + public MicroserviceInstanceStatus getStatus() { return MicroserviceInstanceStatus.UP; } diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConfiguration.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConfiguration.java index 5a98249daba..9ebdcca368b 100644 --- a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConfiguration.java +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryConfiguration.java @@ -18,6 +18,7 @@ import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; +import org.apache.servicecomb.registry.RegistrationId; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @@ -27,8 +28,10 @@ public class LocalRegistryConfiguration { @Bean public LocalRegistrationInstance localRegistrationInstance( Environment environment, - DataCenterProperties dataCenterProperties, LocalOpenAPIRegistry localOpenAPIRegistry) { - return new LocalRegistrationInstance(environment, dataCenterProperties, localOpenAPIRegistry); + DataCenterProperties dataCenterProperties, + LocalOpenAPIRegistry localOpenAPIRegistry, + RegistrationId registrationId) { + return new LocalRegistrationInstance(environment, dataCenterProperties, localOpenAPIRegistry, registrationId); } @Bean diff --git a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryStore.java b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryStore.java index 36218068c3b..3af782e5900 100644 --- a/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryStore.java +++ b/service-registry/registry-local/src/main/java/org/apache/servicecomb/localregistry/LocalRegistryStore.java @@ -148,4 +148,11 @@ public List findServiceInstances(String application, Str } return instances; } + + public List findServices(String application) { + if (microserviceInstanceMap.get(application) == null) { + return Collections.emptyList(); + } + return microserviceInstanceMap.get(application).keySet().stream().toList(); + } } diff --git a/service-registry/registry-nacos/pom.xml b/service-registry/registry-nacos/pom.xml index e4e8822066e..11e58275235 100644 --- a/service-registry/registry-nacos/pom.xml +++ b/service-registry/registry-nacos/pom.xml @@ -17,35 +17,35 @@ --> - - org.apache.servicecomb - service-registry-parent - 3.0.0-SNAPSHOT - - 4.0.0 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + org.apache.servicecomb + service-registry-parent + 3.4.0-SNAPSHOT + + 4.0.0 - registry-nacos - Java Chassis::Service Registry::Nacos + registry-nacos + Java Chassis::Service Registry::Nacos - - - com.alibaba.nacos - nacos-client - - - org.apache.servicecomb - foundation-common - - - org.apache.servicecomb - foundation-registry - - - org.apache.servicecomb - java-chassis-core - - - - \ No newline at end of file + + + com.alibaba.nacos + nacos-client + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + + diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConfiguration.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConfiguration.java index 2a1adbaae92..24eaf50a33d 100644 --- a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConfiguration.java +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConfiguration.java @@ -18,6 +18,7 @@ package org.apache.servicecomb.registry.nacos; import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.registry.RegistrationId; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -36,8 +37,8 @@ public NacosDiscoveryProperties nacosDiscoveryProperties() { public NacosRegistration nacosRegistration( DataCenterProperties dataCenterProperties, @Qualifier("nacosDiscoveryProperties") NacosDiscoveryProperties nacosDiscoveryProperties, - Environment environment) { - return new NacosRegistration(dataCenterProperties, nacosDiscoveryProperties, environment); + Environment environment, RegistrationId registrationId) { + return new NacosRegistration(dataCenterProperties, nacosDiscoveryProperties, environment, registrationId); } @Bean diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConst.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConst.java index b3a7a5dab37..563d1726095 100644 --- a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConst.java +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosConst.java @@ -57,4 +57,6 @@ public class NacosConst { public static final String NAMING_LOAD_CACHE_AT_START = "namingLoadCacheAtStart"; public static final String NACOS_REGISTRY_NAME = "nacos-registry"; + + public static final String NACOS_STATUS = "nacos-status"; } diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscovery.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscovery.java index 87d03fa4649..dfe2a5739eb 100644 --- a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscovery.java +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscovery.java @@ -24,6 +24,8 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; +import com.alibaba.nacos.api.exception.NacosException; + import org.apache.servicecomb.registry.api.Discovery; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; @@ -73,31 +75,40 @@ public boolean enabled(String application, String serviceName) { @Override public List findServiceInstances(String application, String serviceName) { try { + // Get the instance first, and the Listener will not be executed for the first subscribe. + List instances = namingService.getAllInstances(serviceName, application, true); + AtomicBoolean result = SUBSCRIBES.computeIfAbsent(application, k -> new HashMap<>()).computeIfAbsent(serviceName, k -> new AtomicBoolean(true)); if (result.get()) { synchronized (lock) { if (result.get()) { namingService.subscribe(serviceName, application, (event) -> { - if (result.getAndSet(false)) { - // ignore the first event. - return; - } if (event instanceof NamingEvent) { this.instanceChangedListener.onInstanceChanged(name(), application, serviceName, convertServiceInstanceList(((NamingEvent) event).getInstances(), application, serviceName)); } }); + result.set(false); } } } - List instances = namingService.getAllInstances(serviceName, application, true); return convertServiceInstanceList(instances, application, serviceName); } catch (Exception e) { throw new IllegalStateException(e); } } + @Override + public List findServices(String application) { + try { + return namingService.getServicesOfServer(0, Integer.MAX_VALUE, application) + .getData(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + private List convertServiceInstanceList( List instances, String application, String serviceName) { if (CollectionUtils.isEmpty(instances)) { @@ -127,7 +138,13 @@ public void run() { @Override public void destroy() { - + if (namingService != null) { + try { + namingService.shutDown(); + } catch (NacosException e) { + throw new IllegalStateException("destroy process is interrupted."); + } + } } @Override diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryInstance.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryInstance.java index 7104498edf7..6360d012012 100644 --- a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryInstance.java +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosDiscoveryInstance.java @@ -57,7 +57,15 @@ public NacosDiscoveryInstance(Instance instance, String application, String serv @Override public MicroserviceInstanceStatus getStatus() { - return instance.isEnabled() ? MicroserviceInstanceStatus.UP : MicroserviceInstanceStatus.DOWN; + if (instance.isEnabled()) { + String instanceStatus = instance.getMetadata().get(NacosConst.NACOS_STATUS); + if (StringUtils.isBlank(instanceStatus)) { + return MicroserviceInstanceStatus.UP; + } + return MicroserviceInstanceStatus.valueOf(instanceStatus); + } else { + return MicroserviceInstanceStatus.DOWN; + } } @Override @@ -134,6 +142,10 @@ public List getEndpoints() { private static List readEndpoints(Instance instance) { if (StringUtils.isEmpty(instance.getMetadata().get(NacosConst.PROPERTY_ENDPOINT))) { + // interoperate with spring cloud using nacos + if (StringUtils.isNotEmpty(instance.getIp()) && instance.getPort() > 0) { + return List.of("rest://" + instance.getIp() + ":" + instance.getPort()); + } return Collections.emptyList(); } return Arrays.asList(instance.getMetadata().get(NacosConst.PROPERTY_ENDPOINT) diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosMicroserviceHandler.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosMicroserviceHandler.java index c49b5cd69af..29aed07bffb 100644 --- a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosMicroserviceHandler.java +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosMicroserviceHandler.java @@ -29,6 +29,7 @@ import org.springframework.core.env.Environment; import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.utils.NamingUtils; public class NacosMicroserviceHandler { private static final String VERSION_MAPPING = "VERSION_MAPPING"; @@ -48,7 +49,8 @@ public class NacosMicroserviceHandler { public static Instance createMicroserviceInstance( DataCenterProperties dataCenterProperties, NacosDiscoveryProperties properties, Environment environment) { Instance instance = new Instance(); - instance.setServiceName(BootStrapProperties.readServiceName(environment)); + instance.setServiceName(NamingUtils.getGroupedName( + BootStrapProperties.readServiceName(environment), BootStrapProperties.readApplication(environment))); instance.setWeight(properties.getWeight()); instance.setEnabled(properties.isInstanceEnabled()); instance.setClusterName(properties.getClusterName()); diff --git a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistration.java b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistration.java index 184b4971cf8..96f41d0670e 100644 --- a/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistration.java +++ b/service-registry/registry-nacos/src/main/java/org/apache/servicecomb/registry/nacos/NacosRegistration.java @@ -17,14 +17,15 @@ package org.apache.servicecomb.registry.nacos; -import java.lang.management.ManagementFactory; import java.util.List; import java.util.Map; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.invocation.endpoint.EndpointUtils; import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; import org.springframework.beans.factory.annotation.Autowired; @@ -52,8 +53,8 @@ public class NacosRegistration implements Registration org.apache.servicecomb service-registry-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -82,10 +82,5 @@ foundation-test-scaffolding test - - javax.xml.bind - jaxb-api - test - diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/MicroserviceHandler.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/MicroserviceHandler.java index 7ff1ec74d85..7ffa409a089 100644 --- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/MicroserviceHandler.java +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/MicroserviceHandler.java @@ -142,7 +142,8 @@ public static MicroserviceInstance createMicroserviceInstance( properties.putAll(BootStrapProperties.readServiceProperties(environment)); properties.putAll(genCasProperties(environment)); microserviceInstance.setProperties(properties); - microserviceInstance.setStatus(MicroserviceInstanceStatus.valueOf(scConfigurationProperties.getInitialStatus())); + microserviceInstance.setStatus( + MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); return microserviceInstance; } diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCAddressManager.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCAddressManager.java index c9bf300a36f..1c8ec27c8a8 100644 --- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCAddressManager.java +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCAddressManager.java @@ -49,28 +49,25 @@ public enum Type { private static final Logger LOGGER = LoggerFactory.getLogger(SCAddressManager.class); - private boolean initialized = false; - private final ServiceCenterClient serviceCenterClient; private final SCRegistration scRegistration; private final SCConfigurationProperties configurationProperties; + private final Map> lastEngineEndpointsCache = new HashMap<>(); + public SCAddressManager(SCConfigurationProperties configurationProperties, ServiceCenterClient serviceCenterClient, SCRegistration scRegistration) { this.configurationProperties = configurationProperties; this.serviceCenterClient = serviceCenterClient; this.scRegistration = scRegistration; - EventManager.getEventBus().register(this); + scRegistration.getEventBus().register(this); } @Subscribe public void onHeartBeatEvent(HeartBeatEvent event) { - if (initialized) { - return; - } if (event.isSuccess() && configurationProperties.isAutoDiscovery()) { for (Type type : Type.values()) { initEndPort(type.name()); @@ -79,19 +76,30 @@ public void onHeartBeatEvent(HeartBeatEvent event) { } private void initEndPort(String key) { - List instances = findServiceInstance("default", - key, "0+"); - if ("SERVICECENTER".equals(key) && !instances.isEmpty()) { - initialized = true; - } - Map> zoneAndRegion = generateZoneAndRegionAddress(instances); + List instances = findServiceInstance("default", key, "0+"); + HashSet currentEngineEndpoints = new HashSet<>(); + Map> zoneAndRegion = generateZoneAndRegionAddress(instances, currentEngineEndpoints); if (zoneAndRegion == null) { return; } - EventManager.post(new RefreshEndpointEvent(zoneAndRegion, key)); + if (isEngineEndpointsChanged(lastEngineEndpointsCache.get(key), currentEngineEndpoints)) { + LOGGER.info("auto discovery service [{}] addresses: [{}]", key, zoneAndRegion); + lastEngineEndpointsCache.put(key, currentEngineEndpoints); + EventManager.post(new RefreshEndpointEvent(zoneAndRegion, key)); + } + } + + private boolean isEngineEndpointsChanged(Set lastEngineEndpoints, Set currentEngineEndpoints) { + if (lastEngineEndpoints == null || lastEngineEndpoints.isEmpty()) { + return true; + } + HashSet compareTemp = new HashSet<>(lastEngineEndpoints); + compareTemp.removeAll(currentEngineEndpoints); + return !compareTemp.isEmpty() || lastEngineEndpoints.size() != currentEngineEndpoints.size(); } - private Map> generateZoneAndRegionAddress(List instances) { + private Map> generateZoneAndRegionAddress(List instances, + HashSet currentEngineEndpoints) { if (instances.isEmpty()) { return null; } @@ -107,6 +115,7 @@ private Map> generateZoneAndRegionAddress(List(sameZone)); zoneAndRegion.put("sameRegion", new ArrayList<>(sameRegion)); diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCClientUtils.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCClientUtils.java index 7da39ce32bb..f9371926a1d 100644 --- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCClientUtils.java +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCClientUtils.java @@ -17,20 +17,34 @@ package org.apache.servicecomb.registry.sc; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.HttpClientBuilder; import org.apache.servicecomb.config.ConfigUtil; import org.apache.servicecomb.foundation.auth.AuthHeaderProvider; import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; +import org.apache.servicecomb.foundation.vertx.VertxConst; import org.apache.servicecomb.http.client.auth.RequestAuthHeaderProvider; import org.apache.servicecomb.http.client.common.HttpConfiguration.SSLProperties; +import org.apache.servicecomb.http.client.common.HttpTransport; +import org.apache.servicecomb.http.client.common.HttpTransportFactory; import org.apache.servicecomb.service.center.client.ServiceCenterAddressManager; import org.apache.servicecomb.service.center.client.ServiceCenterClient; +import org.apache.servicecomb.service.center.client.ServiceCenterRawClient; import org.apache.servicecomb.service.center.client.ServiceCenterWatch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,22 +53,114 @@ public class SCClientUtils { private static final Logger LOGGER = LoggerFactory.getLogger(SCClientUtils.class); - public static ServiceCenterAddressManager createAddressManager(SCConfigurationProperties discoveryProperties) { + // Compatible chassis multi-registration center + private static final Map serviceAddressManagers = new ConcurrentHashMap<>(); + + public static ServiceCenterAddressManager createAddressManager(SCConfigurationProperties discoveryProperties, + Environment environment) { List addresses = ConfigUtil.parseArrayValue(discoveryProperties.getAddress()); - LOGGER.info("initialize discovery server={}", addresses); - return new ServiceCenterAddressManager("default", addresses, EventManager.getEventBus()); + return createAddressManager("default", addresses, environment); + } + + /** + * Ensure that the ServiceCenterAddressManager in the client created for RBAC authentication and registry discovery + * is the same. This ensures that when an error is reported due to the registry center address being unavailable, + * the authentication and registry discovery remain consistent. + * + * @param projectName projectName + * @param addresses engine address + * @param environment environment + * @return Service Center Address Manager + */ + public static ServiceCenterAddressManager createAddressManager(String projectName, List addresses, + Environment environment) { + if (getServiceCenterAddressManager(addresses) == null) { + synchronized (SCClientUtils.class) { + if (getServiceCenterAddressManager(addresses) == null) { + String key = String.join(",", addresses); + LOGGER.info("initialize discovery server={}", addresses); + String region = environment.getProperty("servicecomb.datacenter.region"); + String availableZone = environment.getProperty("servicecomb.datacenter.availableZone"); + ServiceCenterAddressManager addressManager = new ServiceCenterAddressManager(projectName, addresses, + EventManager.getEventBus(), region, availableZone); + serviceAddressManagers.put(key, addressManager); + return addressManager; + } + } + } + return getServiceCenterAddressManager(addresses); + } + + private static ServiceCenterAddressManager getServiceCenterAddressManager(List addresses) { + String forwardKey = String.join(",", addresses); + List tempAddr = new ArrayList<>(addresses); + Collections.reverse(tempAddr); + String reverseKey = String.join(",", tempAddr); + if (serviceAddressManagers.get(forwardKey) != null) { + return serviceAddressManagers.get(forwardKey); + } + if (serviceAddressManagers.get(reverseKey) != null) { + return serviceAddressManagers.get(reverseKey); + } + return null; } // add other headers needed for registration by new ServiceCenterClient(...) public static ServiceCenterClient serviceCenterClient(SCConfigurationProperties discoveryProperties, - List authHeaderProviders, Environment environment) { - ServiceCenterAddressManager addressManager = createAddressManager(discoveryProperties); + Environment environment) { + ServiceCenterAddressManager addressManager = createAddressManager(discoveryProperties, environment); SSLProperties sslProperties = buildSslProperties(addressManager, environment); - return new ServiceCenterClient(addressManager, sslProperties, - getRequestAuthHeaderProvider(authHeaderProviders), - "default", new HashMap<>()).setEventBus(EventManager.getEventBus()); + return new ServiceCenterClient(new ServiceCenterRawClient.Builder() + .setTenantName("default") + .setAddressManager(addressManager) + .setHttpTransport(createHttpTransport(environment, sslProperties)).build(), addressManager); + } + + private static HttpTransport createHttpTransport(Environment environment, SSLProperties sslProperties) { + List authHeaderProviders = SPIServiceUtils.getOrLoadSortedService(AuthHeaderProvider.class); + + if (isProxyEnable(environment)) { + HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(). + setDefaultRequestConfig(HttpTransportFactory.defaultRequestConfig().build()); + HttpHost proxy = new HttpHost(getProxyHost(environment), + getProxyPort(environment), "http"); // now only support http proxy + httpClientBuilder.setProxy(proxy); + CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); + credentialsProvider.setCredentials(new AuthScope(proxy), + new UsernamePasswordCredentials(getProxyUsername(environment), + getProxyPasswd(environment))); + httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider); + + return HttpTransportFactory + .createHttpTransport(sslProperties, + getRequestAuthHeaderProvider(authHeaderProviders), httpClientBuilder); + } + + return HttpTransportFactory + .createHttpTransport(sslProperties, + getRequestAuthHeaderProvider(authHeaderProviders), HttpTransportFactory.defaultRequestConfig().build()); + } + + public static Boolean isProxyEnable(Environment environment) { + return environment.getProperty(VertxConst.PROXY_ENABLE, boolean.class, false); + } + + public static String getProxyHost(Environment environment) { + return environment.getProperty(VertxConst.PROXY_HOST, "127.0.0.1"); + } + + public static int getProxyPort(Environment environment) { + return environment.getProperty(VertxConst.PROXY_PORT, int.class, 8080); + } + + public static String getProxyUsername(Environment environment) { + return environment.getProperty(VertxConst.PROXY_USERNAME); + } + + public static String getProxyPasswd(Environment environment) { + return environment.getProperty(VertxConst.PROXY_PASSWD); } private static SSLProperties buildSslProperties(ServiceCenterAddressManager addressManager, @@ -78,7 +184,7 @@ private static SSLProperties buildSslProperties(ServiceCenterAddressManager addr public static ServiceCenterWatch serviceCenterWatch(SCConfigurationProperties discoveryProperties, List authHeaderProviders, Environment environment) { - ServiceCenterAddressManager addressManager = createAddressManager(discoveryProperties); + ServiceCenterAddressManager addressManager = createAddressManager(discoveryProperties, environment); SSLProperties sslProperties = buildSslProperties(addressManager, environment); return new ServiceCenterWatch(addressManager, sslProperties, getRequestAuthHeaderProvider(authHeaderProviders), "default", new HashMap<>(), EventManager.getEventBus()); @@ -86,8 +192,9 @@ public static ServiceCenterWatch serviceCenterWatch(SCConfigurationProperties di private static RequestAuthHeaderProvider getRequestAuthHeaderProvider(List authHeaderProviders) { return signRequest -> { + String host = signRequest != null && signRequest.getEndpoint() != null ? signRequest.getEndpoint().getHost() : ""; Map headers = new HashMap<>(); - authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders())); + authHeaderProviders.forEach(provider -> headers.putAll(provider.authHeaders(host))); return headers; }; } diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfiguration.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfiguration.java index ac7620ad203..6cd8965c8a2 100644 --- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfiguration.java +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfiguration.java @@ -39,8 +39,8 @@ public SCConfigurationProperties scConfigurationProperties() { @Bean public ServiceCenterClient serviceCenterClient( @Qualifier("scConfigurationProperties") SCConfigurationProperties scConfigurationProperties, - List authHeaderProviders, Environment environment) { - return SCClientUtils.serviceCenterClient(scConfigurationProperties, authHeaderProviders, environment); + Environment environment) { + return SCClientUtils.serviceCenterClient(scConfigurationProperties, environment); } @Bean diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfigurationProperties.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfigurationProperties.java index 784f711cf59..5ac431406a3 100644 --- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfigurationProperties.java +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCConfigurationProperties.java @@ -49,8 +49,6 @@ public class SCConfigurationProperties { private boolean autoDiscovery = false; - private String initialStatus = "STARTING"; - private boolean watch = false; private long registrationWaitTimeInMillis = 30000; @@ -140,14 +138,6 @@ public void setHealthCheckTimes(int healthCheckTimes) { this.healthCheckTimes = healthCheckTimes; } - public String getInitialStatus() { - return initialStatus; - } - - public void setInitialStatus(String initialStatus) { - this.initialStatus = initialStatus; - } - public boolean isWatch() { return watch; } diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscovery.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscovery.java index 471d0242978..8cffa504705 100644 --- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscovery.java +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscovery.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.apache.servicecomb.foundation.common.event.SimpleEventBus; import org.apache.servicecomb.registry.api.Discovery; @@ -28,6 +29,7 @@ import org.apache.servicecomb.service.center.client.ServiceCenterClient; import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery; import org.apache.servicecomb.service.center.client.ServiceCenterDiscovery.SubscriptionKey; +import org.apache.servicecomb.service.center.client.model.Microservice; import org.apache.servicecomb.service.center.client.model.MicroserviceInstance; import org.apache.servicecomb.service.center.client.model.SchemaInfo; import org.springframework.beans.factory.annotation.Autowired; @@ -96,6 +98,13 @@ public List findServiceInstances(String application, String return toDiscoveryInstances(instances); } + @Override + public List findServices(String application) { + return serviceCenterClient.getMicroserviceList().getServices().stream() + .filter(e -> e.getAppId().equals(application)).map(Microservice::getServiceName) + .collect(Collectors.toList()); + } + private List toDiscoveryInstances(List instances) { if (CollectionUtils.isEmpty(instances)) { return Collections.emptyList(); diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscoveryInstance.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscoveryInstance.java index 28ef4e37b0a..f3b7ffe4403 100644 --- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscoveryInstance.java +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCDiscoveryInstance.java @@ -76,9 +76,12 @@ public String getVersion() { @Override public DataCenterInfo getDataCenterInfo() { - return new DataCenterInfo(microserviceInstance.getDataCenterInfo().getName(), - microserviceInstance.getDataCenterInfo().getRegion(), - microserviceInstance.getDataCenterInfo().getAvailableZone()); + if (microserviceInstance.getDataCenterInfo() != null) { + return new DataCenterInfo(microserviceInstance.getDataCenterInfo().getName(), + microserviceInstance.getDataCenterInfo().getRegion(), + microserviceInstance.getDataCenterInfo().getAvailableZone()); + } + return new DataCenterInfo(); } @Override diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistration.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistration.java index c0ccec52dd3..6692f61c6c7 100644 --- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistration.java +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistration.java @@ -21,6 +21,7 @@ import org.apache.servicecomb.config.DataCenterProperties; import org.apache.servicecomb.foundation.common.event.SimpleEventBus; +import org.apache.servicecomb.registry.RegistrationId; import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; import org.apache.servicecomb.registry.api.Registration; import org.apache.servicecomb.service.center.client.RegistrationEvents.MicroserviceInstanceRegistrationEvent; @@ -60,6 +61,8 @@ public class SCRegistration implements Registration { private Environment environment; + private RegistrationId registrationId; + private CountDownLatch readyWaiter = new CountDownLatch(1); @Autowired @@ -75,12 +78,16 @@ public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { this.dataCenterProperties = dataCenterProperties; } + @Autowired + public void setRegistrationId(RegistrationId registrationId) { + this.registrationId = registrationId; + } + @Autowired public void setEnvironment(Environment environment) { this.environment = environment; } - @Override public void init() { microservice = MicroserviceHandler.createMicroservice( @@ -89,6 +96,7 @@ public void init() { environment, configurationProperties, dataCenterProperties); + microserviceInstance.setInstanceId(registrationId.getInstanceId()); serviceCenterRegistration = new ServiceCenterRegistration(serviceCenterClient, new ServiceCenterConfiguration().setCanOverwriteSwagger( this.configurationProperties.isCanOverwriteSwagger()) @@ -96,6 +104,10 @@ public void init() { eventBus); serviceCenterRegistration.setMicroservice(microservice); serviceCenterRegistration.setMicroserviceInstance(microserviceInstance); + serviceCenterRegistration.setHeartBeatInterval( + TimeUnit.SECONDS.toMillis(configurationProperties.getHealthCheckIntervalInSeconds())); + serviceCenterRegistration.setHeartBeatRequestTimeout( + configurationProperties.getHealthCheckRequestTimeoutInMillis()); registrationInstance = new SCRegistrationInstance(microservice, microserviceInstance, serviceCenterRegistration); eventBus.register(this); } @@ -186,4 +198,8 @@ public Microservice getBackendMicroservice() { public MicroserviceInstance getBackendMicroserviceInstance() { return microserviceInstance; } + + EventBus getEventBus() { + return eventBus; + } } diff --git a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistrationInstance.java b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistrationInstance.java index 36416972a73..cfe53692eeb 100644 --- a/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistrationInstance.java +++ b/service-registry/registry-service-center/src/main/java/org/apache/servicecomb/registry/sc/SCRegistrationInstance.java @@ -34,8 +34,7 @@ public class SCRegistrationInstance implements RegistrationInstance { private final ServiceCenterRegistration serviceCenterRegistration; - public SCRegistrationInstance(Microservice microservice, - MicroserviceInstance microserviceInstance, + public SCRegistrationInstance(Microservice microservice, MicroserviceInstance microserviceInstance, ServiceCenterRegistration serviceCenterRegistration) { this.microservice = microservice; this.microserviceInstance = microserviceInstance; @@ -107,15 +106,10 @@ public String getServiceId() { } @Override - public MicroserviceInstanceStatus getInitialStatus() { + public MicroserviceInstanceStatus getStatus() { return MicroserviceInstanceStatus.valueOf(microserviceInstance.getStatus().name()); } - @Override - public MicroserviceInstanceStatus getReadyStatus() { - return MicroserviceInstanceStatus.UP; - } - public Microservice getBackendMicroservice() { return microservice; } diff --git a/service-registry/registry-zero-config/pom.xml b/service-registry/registry-zero-config/pom.xml index 70ca41a94b7..c9a6569f1bc 100644 --- a/service-registry/registry-zero-config/pom.xml +++ b/service-registry/registry-zero-config/pom.xml @@ -22,7 +22,7 @@ service-registry-parent org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -35,4 +35,4 @@ registry-lightweight - \ No newline at end of file + diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/AbstractZeroConfigRegistration.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/AbstractZeroConfigRegistration.java index f0a58948043..b6e577e44a2 100644 --- a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/AbstractZeroConfigRegistration.java +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/AbstractZeroConfigRegistration.java @@ -28,7 +28,7 @@ public abstract class AbstractZeroConfigRegistration setConfig(Config config) { this.config = config; return this; } diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscovery.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscovery.java index 9615d8ec318..70b54f8a079 100644 --- a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscovery.java +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigDiscovery.java @@ -21,8 +21,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import org.apache.servicecomb.registry.lightweight.AbstractLightweightDiscovery; +import org.apache.servicecomb.registry.lightweight.model.Microservice; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstance; import org.apache.servicecomb.registry.lightweight.model.MicroserviceInstances; import org.springframework.beans.factory.annotation.Autowired; @@ -74,4 +76,11 @@ public List findServiceInstances(String application } return result; } + + @Override + public List findServices(String application) { + List microservices = store.getAllMicroservices(); + return microservices.stream().filter(e -> e.getAppId().equals(application)) + .map(Microservice::getServiceName).collect(Collectors.toList()); + } } diff --git a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistrationInstance.java b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistrationInstance.java index 61864c96495..9e1955b24a8 100644 --- a/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistrationInstance.java +++ b/service-registry/registry-zero-config/src/main/java/org/apache/servicecomb/zeroconfig/ZeroConfigRegistrationInstance.java @@ -87,12 +87,7 @@ public String getInstanceId() { } @Override - public MicroserviceInstanceStatus getInitialStatus() { - return MicroserviceInstanceStatus.STARTING; - } - - @Override - public MicroserviceInstanceStatus getReadyStatus() { + public MicroserviceInstanceStatus getStatus() { return MicroserviceInstanceStatus.UP; } diff --git a/service-registry/registry-zookeeper/pom.xml b/service-registry/registry-zookeeper/pom.xml new file mode 100644 index 00000000000..e5ba4236e18 --- /dev/null +++ b/service-registry/registry-zookeeper/pom.xml @@ -0,0 +1,55 @@ + + + + + + org.apache.servicecomb + service-registry-parent + 3.4.0-SNAPSHOT + + 4.0.0 + + registry-zookeeper + Java Chassis::Service Registry::Zookeeper + + + + org.apache.curator + curator-x-discovery + + + org.apache.curator + curator-framework + + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + foundation-registry + + + org.apache.servicecomb + java-chassis-core + + + + diff --git a/foundations/foundation-metrics/src/main/java/com/netflix/spectator/api/SpectatorUtils.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConfiguration.java similarity index 54% rename from foundations/foundation-metrics/src/main/java/com/netflix/spectator/api/SpectatorUtils.java rename to service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConfiguration.java index cccc7107f43..39c9fd176ad 100644 --- a/foundations/foundation-metrics/src/main/java/com/netflix/spectator/api/SpectatorUtils.java +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConfiguration.java @@ -14,26 +14,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.netflix.spectator.api; +package org.apache.servicecomb.registry.zookeeper; -public final class SpectatorUtils { - private SpectatorUtils() { - } +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; - public static Id createDefaultId(String name) { - return new DefaultId(name); +@Configuration +public class ZookeeperConfiguration { + @Bean + @ConfigurationProperties(prefix = ZookeeperConst.ZOOKEEPER_REGISTRY_PREFIX) + public ZookeeperRegistryProperties zookeeperRegistryProperties() { + return new ZookeeperRegistryProperties(); } - public static void removeExpiredMeters(Registry registry) { - if (registry instanceof AbstractRegistry) { - ((AbstractRegistry) registry).removeExpiredMeters(); - } + @Bean + public ZookeeperDiscovery zookeeperDiscovery() { + return new ZookeeperDiscovery(); } - public static void registerMeter(Registry registry, Meter meter) { - if (!(registry instanceof AbstractRegistry)) { - throw new IllegalStateException("registry must be a AbstractRegistry, class=" + registry.getClass().getName()); - } - ((AbstractRegistry) registry).getOrCreate(meter.id(), Meter.class, null, _id -> meter); + @Bean + public ZookeeperRegistration zookeeperRegistration() { + return new ZookeeperRegistration(); } } diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConst.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConst.java new file mode 100644 index 00000000000..1d6d5d14117 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperConst.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.zookeeper; + +public class ZookeeperConst { + public static final String ZOOKEEPER_REGISTRY_NAME = "zookeeper-registry"; + + public static final String ZOOKEEPER_DISCOVERY_ROOT = "/servicecomb/registry/%s"; + + public static final String ZOOKEEPER_REGISTRY_PREFIX = "servicecomb.registry.zk"; + + public static final String ZOOKEEPER_DISCOVERY_ENABLED = ZOOKEEPER_REGISTRY_PREFIX + ".%s.%s.enabled"; + + public static final String ZOOKEEPER_DEFAULT_ENVIRONMENT = "production"; +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscovery.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscovery.java new file mode 100644 index 00000000000..ab601e11783 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscovery.java @@ -0,0 +1,236 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.zookeeper; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; + +import org.apache.commons.lang3.StringUtils; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.state.ConnectionState; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.curator.utils.CloseableUtils; +import org.apache.curator.x.discovery.ServiceCache; +import org.apache.curator.x.discovery.ServiceDiscovery; +import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; +import org.apache.curator.x.discovery.ServiceInstance; +import org.apache.curator.x.discovery.details.JsonInstanceSerializer; +import org.apache.curator.x.discovery.details.ServiceCacheListener; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx; +import org.apache.servicecomb.registry.api.Discovery; +import org.apache.zookeeper.server.auth.DigestLoginModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +public class ZookeeperDiscovery implements Discovery { + static class ZookeeperSASLConfig extends Configuration { + AppConfigurationEntry entry; + + public ZookeeperSASLConfig(String username, + String password) { + Map options = new HashMap<>(); + options.put("username", username); + options.put("password", password); + this.entry = new AppConfigurationEntry( + DigestLoginModule.class.getName(), + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options + ); + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + AppConfigurationEntry[] array = new AppConfigurationEntry[1]; + array[0] = entry; + return array; + } + } + + private static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperDiscovery.class); + + private final Map>> serviceDiscoveries = + new ConcurrentHashMapEx<>(); + + private final Map> serviceNameDiscoveries = + new ConcurrentHashMapEx<>(); + + private Environment environment; + + private ZookeeperRegistryProperties zookeeperRegistryProperties; + + private String basePath; + + private CuratorFramework client; + + private InstanceChangedListener instanceChangedListener; + + @Autowired + @SuppressWarnings("unused") + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + @SuppressWarnings("unused") + public void setZookeeperRegistryProperties(ZookeeperRegistryProperties zookeeperRegistryProperties) { + this.zookeeperRegistryProperties = zookeeperRegistryProperties; + } + + @Override + public String name() { + return ZookeeperConst.ZOOKEEPER_REGISTRY_NAME; + } + + @Override + public boolean enabled(String application, String serviceName) { + return environment.getProperty(String.format(ZookeeperConst.ZOOKEEPER_DISCOVERY_ENABLED, application, serviceName), + boolean.class, true); + } + + @Override + public List findServiceInstances(String application, String serviceName) { + try { + ServiceCache discovery = serviceDiscoveries.computeIfAbsent(application, app -> + new ConcurrentHashMapEx<>()).computeIfAbsent(serviceName, name -> { + JsonInstanceSerializer serializer = + new JsonInstanceSerializer<>(ZookeeperInstance.class); + ServiceDiscovery dis = ServiceDiscoveryBuilder.builder(ZookeeperInstance.class) + .client(client) + .basePath(basePath + "/" + application) + .serializer(serializer) + .build(); + ServiceCache cache = + dis.serviceCacheBuilder().name(serviceName).build(); + cache.addListener(new ServiceCacheListener() { + @Override + public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) { + LOGGER.warn("zookeeper discovery state changed {}", connectionState); + } + + @Override + public void cacheChanged() { + instanceChangedListener.onInstanceChanged(name(), application, serviceName, + toDiscoveryInstances(cache.getInstances())); + } + }); + try { + CountDownLatch latch = cache.startImmediate(); + if (!latch.await(5000, TimeUnit.SECONDS)) { + throw new IllegalStateException("cache start failed."); + } + } catch (Exception e) { + throw new IllegalStateException(e); + } + return cache; + }); + List> instances = discovery.getInstances(); + return toDiscoveryInstances(instances); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + @Override + public List findServices(String application) { + try { + ServiceDiscovery discovery = serviceNameDiscoveries + .computeIfAbsent(application, app -> { + JsonInstanceSerializer serializer = + new JsonInstanceSerializer<>(ZookeeperInstance.class); + ServiceDiscovery dis = ServiceDiscoveryBuilder.builder(ZookeeperInstance.class) + .client(client) + .basePath(basePath + "/" + application) + .serializer(serializer) + .build(); + try { + dis.start(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + return dis; + }); + return discovery.queryForNames().stream().toList(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + private List toDiscoveryInstances( + List> instances) { + return instances.stream().map(instance -> + new ZookeeperDiscoveryInstance(instance.getPayload())).collect(Collectors.toList()); + } + + @Override + public void setInstanceChangedListener(InstanceChangedListener instanceChangedListener) { + this.instanceChangedListener = instanceChangedListener; + } + + @Override + public void init() { + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ZookeeperConst.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + basePath = String.format(ZookeeperConst.ZOOKEEPER_DISCOVERY_ROOT, env); + } + + @Override + public void run() { + CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() + .connectString(zookeeperRegistryProperties.getConnectString()) + .sessionTimeoutMs(zookeeperRegistryProperties.getSessionTimeoutMillis()) + .retryPolicy(new ExponentialBackoffRetry(1000, 3)); + String authSchema = zookeeperRegistryProperties.getAuthenticationSchema(); + if (StringUtils.isNotEmpty(authSchema)) { + if (!"digest".equals(authSchema)) { + throw new IllegalStateException("Not supported schema now. " + authSchema); + } + if (zookeeperRegistryProperties.getAuthenticationInfo() == null) { + throw new IllegalStateException("Auth info can not be empty. "); + } + + String[] authInfo = zookeeperRegistryProperties.getAuthenticationInfo().split(":"); + Configuration.setConfiguration(new ZookeeperSASLConfig(authInfo[0], authInfo[1])); + } + client = builder.build(); + client.start(); + } + + @Override + public void destroy() { + if (client != null) { + CloseableUtils.closeQuietly(client); + } + } + + @Override + public boolean enabled() { + return zookeeperRegistryProperties.isEnabled(); + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscoveryInstance.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscoveryInstance.java new file mode 100644 index 00000000000..b01ad50830e --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperDiscoveryInstance.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.zookeeper; + +import org.apache.servicecomb.registry.api.DiscoveryInstance; + +public class ZookeeperDiscoveryInstance extends ZookeeperInstance implements DiscoveryInstance { + public ZookeeperDiscoveryInstance(ZookeeperInstance other) { + super(other); + } + + @Override + public String getRegistryName() { + return ZookeeperConst.ZOOKEEPER_REGISTRY_NAME; + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperInstance.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperInstance.java new file mode 100644 index 00000000000..b919d97855f --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperInstance.java @@ -0,0 +1,203 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.zookeeper; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstance; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; + +public class ZookeeperInstance implements MicroserviceInstance { + private String serviceId; + + private String instanceId; + + private String environment; + + private String application; + + private String serviceName; + + private String alias; + + private String version; + + private String description; + + private DataCenterInfo dataCenterInfo; + + private List endpoints = new ArrayList<>(); + + private Map schemas = new HashMap<>(); + + private Map properties = new HashMap<>(); + + private MicroserviceInstanceStatus status; + + public ZookeeperInstance() { + + } + + public ZookeeperInstance(ZookeeperInstance other) { + this.serviceId = other.serviceId; + this.instanceId = other.instanceId; + this.environment = other.environment; + this.application = other.application; + this.serviceName = other.serviceName; + this.alias = other.alias; + this.version = other.version; + this.description = other.description; + this.dataCenterInfo = other.dataCenterInfo; + this.endpoints = other.endpoints; + this.schemas = other.schemas; + this.properties = other.properties; + this.status = other.status; + } + + public void setServiceId(String serviceId) { + this.serviceId = serviceId; + } + + public void setInstanceId(String instanceId) { + this.instanceId = instanceId; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public void setApplication(String application) { + this.application = application; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setDataCenterInfo(DataCenterInfo dataCenterInfo) { + this.dataCenterInfo = dataCenterInfo; + } + + public void setEndpoints(List endpoints) { + this.endpoints = endpoints; + } + + public void setSchemas(Map schemas) { + this.schemas = schemas; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + + public void setStatus(MicroserviceInstanceStatus status) { + this.status = status; + } + + @Override + public String getEnvironment() { + return this.environment; + } + + @Override + public String getApplication() { + return this.application; + } + + @Override + public String getServiceName() { + return this.serviceName; + } + + @Override + public String getAlias() { + return alias; + } + + @Override + public String getVersion() { + return version; + } + + @Override + public DataCenterInfo getDataCenterInfo() { + return dataCenterInfo == null ? new DataCenterInfo() : dataCenterInfo; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public Map getProperties() { + return properties; + } + + @Override + public Map getSchemas() { + return schemas; + } + + @Override + public List getEndpoints() { + return endpoints; + } + + public void addSchema(String schemaId, String content) { + this.schemas.put(schemaId, content); + } + + public void addEndpoint(String endpoint) { + this.endpoints.add(endpoint); + } + + public void addProperty(String key, String value) { + this.properties.put(key, value); + } + + @Override + public String getInstanceId() { + return instanceId; + } + + @Override + public String getServiceId() { + return serviceId; + } + + @Override + public MicroserviceInstanceStatus getStatus() { + return this.status; + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistration.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistration.java new file mode 100644 index 00000000000..55df0d42702 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistration.java @@ -0,0 +1,225 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.zookeeper; + +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; + +import org.apache.commons.lang3.StringUtils; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.curator.utils.CloseableUtils; +import org.apache.curator.x.discovery.ServiceDiscovery; +import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; +import org.apache.curator.x.discovery.ServiceInstance; +import org.apache.curator.x.discovery.details.JsonInstanceSerializer; +import org.apache.servicecomb.config.BootStrapProperties; +import org.apache.servicecomb.config.DataCenterProperties; +import org.apache.servicecomb.registry.RegistrationId; +import org.apache.servicecomb.registry.api.DataCenterInfo; +import org.apache.servicecomb.registry.api.MicroserviceInstanceStatus; +import org.apache.servicecomb.registry.api.Registration; +import org.apache.zookeeper.server.auth.DigestLoginModule; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; + +public class ZookeeperRegistration implements Registration { + static class ZookeeperSASLConfig extends Configuration { + AppConfigurationEntry entry; + + public ZookeeperSASLConfig(String username, + String password) { + Map options = new HashMap<>(); + options.put("username", username); + options.put("password", password); + this.entry = new AppConfigurationEntry( + DigestLoginModule.class.getName(), + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, + options + ); + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + AppConfigurationEntry[] array = new AppConfigurationEntry[1]; + array[0] = entry; + return array; + } + } + + private Environment environment; + + private ZookeeperRegistryProperties zookeeperRegistryProperties; + + private DataCenterProperties dataCenterProperties; + + private String basePath; + + private CuratorFramework client; + + private ServiceInstance instance; + + private RegistrationId registrationId; + + private ServiceDiscovery dis; + + @Autowired + @SuppressWarnings("unused") + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Autowired + @SuppressWarnings("unused") + public void setZookeeperRegistryProperties(ZookeeperRegistryProperties zookeeperRegistryProperties) { + this.zookeeperRegistryProperties = zookeeperRegistryProperties; + } + + @Autowired + public void setRegistrationId(RegistrationId registrationId) { + this.registrationId = registrationId; + } + + @Autowired + @SuppressWarnings("unused") + public void setDataCenterProperties(DataCenterProperties dataCenterProperties) { + this.dataCenterProperties = dataCenterProperties; + } + + @Override + public void init() { + String env = BootStrapProperties.readServiceEnvironment(environment); + if (StringUtils.isEmpty(env)) { + env = ZookeeperConst.ZOOKEEPER_DEFAULT_ENVIRONMENT; + } + basePath = String.format(ZookeeperConst.ZOOKEEPER_DISCOVERY_ROOT, env); + ZookeeperInstance zookeeperInstance = new ZookeeperInstance(); + zookeeperInstance.setInstanceId(registrationId.getInstanceId()); + zookeeperInstance.setEnvironment(env); + zookeeperInstance.setApplication(BootStrapProperties.readApplication(environment)); + zookeeperInstance.setServiceName(BootStrapProperties.readServiceName(environment)); + zookeeperInstance.setAlias(BootStrapProperties.readServiceAlias(environment)); + zookeeperInstance.setDescription(BootStrapProperties.readServiceDescription(environment)); + if (StringUtils.isNotEmpty(dataCenterProperties.getName())) { + DataCenterInfo dataCenterInfo = new DataCenterInfo(); + dataCenterInfo.setName(dataCenterProperties.getName()); + dataCenterInfo.setRegion(dataCenterProperties.getRegion()); + dataCenterInfo.setAvailableZone(dataCenterProperties.getAvailableZone()); + zookeeperInstance.setDataCenterInfo(dataCenterInfo); + } + zookeeperInstance.setProperties(BootStrapProperties.readServiceProperties(environment)); + zookeeperInstance.setVersion(BootStrapProperties.readServiceVersion(environment)); + + zookeeperInstance.setStatus( + MicroserviceInstanceStatus.valueOf(BootStrapProperties.readServiceInstanceInitialStatus(environment))); + try { + this.instance = ServiceInstance.builder().name(zookeeperInstance.getServiceName()) + .id(zookeeperInstance.getInstanceId()).payload(zookeeperInstance).build(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + @Override + public void run() { + CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder() + .connectString(zookeeperRegistryProperties.getConnectString()) + .sessionTimeoutMs(zookeeperRegistryProperties.getSessionTimeoutMillis()) + .retryPolicy(new ExponentialBackoffRetry(1000, 3)); + String authSchema = zookeeperRegistryProperties.getAuthenticationSchema(); + if (StringUtils.isNotEmpty(authSchema)) { + if (!"digest".equals(authSchema)) { + throw new IllegalStateException("Not supported schema now. " + authSchema); + } + if (zookeeperRegistryProperties.getAuthenticationInfo() == null) { + throw new IllegalStateException("Auth info can not be empty. "); + } + + String[] authInfo = zookeeperRegistryProperties.getAuthenticationInfo().split(":"); + Configuration.setConfiguration(new ZookeeperDiscovery.ZookeeperSASLConfig(authInfo[0], authInfo[1])); + } + client = builder.build(); + client.start(); + JsonInstanceSerializer serializer = + new JsonInstanceSerializer<>(ZookeeperInstance.class); + dis = ServiceDiscoveryBuilder.builder(ZookeeperInstance.class) + .client(client) + .basePath(basePath + "/" + BootStrapProperties.readApplication(environment)) + .serializer(serializer) + .thisInstance(instance) + .build(); + try { + dis.start(); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + @Override + public void destroy() { + if (client != null) { + CloseableUtils.closeQuietly(client); + } + } + + @Override + public String name() { + return ZookeeperConst.ZOOKEEPER_REGISTRY_NAME; + } + + @Override + public ZookeeperRegistrationInstance getMicroserviceInstance() { + return new ZookeeperRegistrationInstance(instance.getPayload()); + } + + @Override + public boolean updateMicroserviceInstanceStatus(MicroserviceInstanceStatus status) { + this.instance.getPayload().setStatus(status); + try { + dis.updateService(instance); + } catch (Exception e) { + throw new IllegalStateException(e); + } + return true; + } + + @Override + public void addSchema(String schemaId, String content) { + if (zookeeperRegistryProperties.isEnableSwaggerRegistration()) { + instance.getPayload().addSchema(schemaId, content); + } + } + + @Override + public void addEndpoint(String endpoint) { + instance.getPayload().addEndpoint(endpoint); + } + + @Override + public void addProperty(String key, String value) { + instance.getPayload().addProperty(key, value); + } + + @Override + public boolean enabled() { + return zookeeperRegistryProperties.isEnabled(); + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistrationInstance.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistrationInstance.java new file mode 100644 index 00000000000..e5caed18960 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistrationInstance.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.zookeeper; + +import org.apache.servicecomb.registry.api.RegistrationInstance; + +public class ZookeeperRegistrationInstance extends ZookeeperInstance implements RegistrationInstance { + public ZookeeperRegistrationInstance(ZookeeperInstance instance) { + super(instance); + } +} diff --git a/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistryProperties.java b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistryProperties.java new file mode 100644 index 00000000000..0f74278fb58 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/java/org/apache/servicecomb/registry/zookeeper/ZookeeperRegistryProperties.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.registry.zookeeper; + +public class ZookeeperRegistryProperties { + private boolean enabled = true; + + private boolean ephemeral = true; + + private String connectString = "127.0.0.1:2181"; + + private String authenticationSchema; + + private String authenticationInfo; + + private int connectionTimeoutMillis = 1000; + + private int sessionTimeoutMillis = 60000; + + private boolean enableSwaggerRegistration = false; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isEphemeral() { + return ephemeral; + } + + public void setEphemeral(boolean ephemeral) { + this.ephemeral = ephemeral; + } + + public String getConnectString() { + return connectString; + } + + public void setConnectString(String connectString) { + this.connectString = connectString; + } + + public int getConnectionTimeoutMillis() { + return connectionTimeoutMillis; + } + + public void setConnectionTimeoutMillis(int connectionTimeoutMillis) { + this.connectionTimeoutMillis = connectionTimeoutMillis; + } + + public int getSessionTimeoutMillis() { + return sessionTimeoutMillis; + } + + public void setSessionTimeoutMillis(int sessionTimeoutMillis) { + this.sessionTimeoutMillis = sessionTimeoutMillis; + } + + public boolean isEnableSwaggerRegistration() { + return enableSwaggerRegistration; + } + + public void setEnableSwaggerRegistration(boolean enableSwaggerRegistration) { + this.enableSwaggerRegistration = enableSwaggerRegistration; + } + + public String getAuthenticationSchema() { + return authenticationSchema; + } + + public void setAuthenticationSchema(String authenticationSchema) { + this.authenticationSchema = authenticationSchema; + } + + public String getAuthenticationInfo() { + return authenticationInfo; + } + + public void setAuthenticationInfo(String authenticationInfo) { + this.authenticationInfo = authenticationInfo; + } +} diff --git a/service-registry/registry-zookeeper/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/service-registry/registry-zookeeper/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000000..3fd671eac61 --- /dev/null +++ b/service-registry/registry-zookeeper/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1,18 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +org.apache.servicecomb.registry.zookeeper.ZookeeperConfiguration diff --git a/solutions/pom.xml b/solutions/pom.xml index 5cc25d394d4..4203f65309b 100644 --- a/solutions/pom.xml +++ b/solutions/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 diff --git a/solutions/solution-basic/pom.xml b/solutions/solution-basic/pom.xml index d0f758df131..1bb51a4b88c 100644 --- a/solutions/solution-basic/pom.xml +++ b/solutions/solution-basic/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb solutions - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthInstancePing.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthInstancePing.java index b1e14880bee..79db6818e61 100644 --- a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthInstancePing.java +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/HealthInstancePing.java @@ -20,6 +20,7 @@ import java.util.HashMap; import java.util.Map; +import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.Endpoint; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.SCBEngine; @@ -33,6 +34,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Lazy; +import org.springframework.core.env.Environment; import org.springframework.util.CollectionUtils; public class HealthInstancePing implements InstancePing { @@ -42,15 +44,22 @@ public class HealthInstancePing implements InstancePing { private TelnetInstancePing telnetInstancePing; + private Environment environment; + @Autowired @Lazy public void setScbEngine(SCBEngine scbEngine) { this.scbEngine = scbEngine; } + @Autowired + public void setEnvironment(Environment environment) { + this.environment = environment; + } + @Autowired @Lazy - public void setTelnetInstancePing(@Qualifier("telnetInstancePing") TelnetInstancePing telnetInstancePing) { + public void setTelnetInstancePing(@Qualifier("scbTelnetInstancePing") TelnetInstancePing telnetInstancePing) { this.telnetInstancePing = telnetInstancePing; } @@ -76,7 +85,10 @@ public boolean ping(StatefulDiscoveryInstance instance) { if (transport == null) { continue; } - Invocation invocation = InvokerUtils.createInvocation(instance.getServiceName(), transportName, + // Use myself service name instead of the target. Because can avoid create + // MicroserviceReferenceConfig for the target. + Invocation invocation = InvokerUtils.createInvocation(BootStrapProperties.readServiceName(environment), + transportName, ManagementEndpoint.NAME, "health", args, boolean.class); invocation.setEndpoint(new Endpoint(transport, endpoint, instance)); diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/InstanceOpenAPIRegistry.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/InstanceOpenAPIRegistry.java index 0bcf428bcfc..f8314d5afaa 100644 --- a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/InstanceOpenAPIRegistry.java +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/InstanceOpenAPIRegistry.java @@ -21,7 +21,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.Endpoint; @@ -76,50 +75,6 @@ public boolean enabled() { return environment.getProperty(OpenAPIRegistry.CONFIG_PREFIX + ".instance.enabled", boolean.class, true); } - @Override - @SuppressWarnings("unchecked") - public Set getSchemaIds(String application, String serviceName) { - List discoveryInstances = - discoveryManager.findServiceInstances(application, serviceName); - if (discoveryInstances.isEmpty()) { - throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "no instances"); - } - discoveryInstances.sort((a, b) -> VersionCompareUtil.compareVersion(b.getVersion(), a.getVersion())); - - String version = null; - for (DiscoveryInstance instance : discoveryInstances) { - if (version != null && !version.equals(instance.getVersion())) { - break; - } - version = instance.getVersion(); - for (String endpoint : instance.getEndpoints()) { - URI uri = URI.create(endpoint); - String transportName = uri.getScheme(); - Transport transport = transportManager.findTransport(transportName); - if (transport == null) { - continue; - } - // Use myself service name instead of the target. Because can avoid create - // MicroserviceReferenceConfig for the target. - Invocation invocation = InvokerUtils.createInvocation(BootStrapProperties.readServiceName(environment), - transportName, - ManagementEndpoint.NAME, "schemaIds", - new HashMap<>(), new TypeReference>() { - }.getType()); - invocation.setEndpoint(new Endpoint(transport, endpoint, discoveryInstances.get(0))); - try { - return (Set) InvokerUtils.syncInvoke(invocation); - } catch (InvocationException e) { - LOGGER.warn("Get schema ids {}/{}/{} from endpoint {} failed. {}", - instance.getApplication(), - instance.getServiceName(), - instance.getInstanceId(), endpoint, e.getMessage()); - } - } - } - throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "Get schema ids fail from all latest version."); - } - @Override public void registerOpenAPI(String application, String serviceName, String schemaId, OpenAPI api) { // do nothing @@ -127,9 +82,10 @@ public void registerOpenAPI(String application, String serviceName, String schem @Override @SuppressWarnings("unchecked") - public Map loadOpenAPI(String application, String serviceName, Set schemaIds) { + public Map loadOpenAPI(String application, String serviceName) { List discoveryInstances = - discoveryManager.findServiceInstances(application, serviceName); + discoveryManager.getOrCreateVersionedCache(application, serviceName).data(); + if (discoveryInstances.isEmpty()) { throw new InvocationException(Status.INTERNAL_SERVER_ERROR, "no instances"); } @@ -150,12 +106,10 @@ public Map loadOpenAPI(String application, String serviceName, } // Use myself service name instead of the target. Because can avoid create // MicroserviceReferenceConfig for the target. - Map args = new HashMap<>(); - args.put("schemaIds", schemaIds); Invocation invocation = InvokerUtils.createInvocation(BootStrapProperties.readServiceName(environment), transportName, ManagementEndpoint.NAME, "schemaContents", - args, new TypeReference>() { + new HashMap<>(), new TypeReference>() { }.getType()); invocation.setEndpoint(new Endpoint(transport, endpoint, discoveryInstances.get(0))); try { diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpoint.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpoint.java index a9c10f601ac..f41aa8a216f 100644 --- a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpoint.java +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpoint.java @@ -17,7 +17,6 @@ package org.apache.servicecomb.solution.basic.integration; import java.util.Map; -import java.util.Set; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; @@ -43,15 +42,11 @@ public interface ManagementEndpoint { boolean health(@QueryParam("instanceId") String instanceId, @QueryParam("registryName") String registryName); /** - * Schema ids of this instance. + * Schemas of this instance. * * This api is for internal schema loading usage. */ - @GET - @Path("/schema/ids") - Set schemaIds(); - @POST @Path("/schema/contents") - Map schemaContents(Set schemaIds); + Map schemaContents(); } diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpointImpl.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpointImpl.java index c68b13571f3..00852bde863 100644 --- a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpointImpl.java +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/ManagementEndpointImpl.java @@ -18,21 +18,15 @@ import java.util.HashMap; import java.util.Map; -import java.util.Set; import org.apache.commons.lang3.StringUtils; -import org.apache.servicecomb.config.BootStrapProperties; import org.apache.servicecomb.core.provider.LocalOpenAPIRegistry; import org.apache.servicecomb.provider.rest.common.RestSchema; import org.apache.servicecomb.registry.RegistrationManager; import org.apache.servicecomb.swagger.SwaggerUtils; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; -import org.springframework.util.CollectionUtils; import io.swagger.v3.oas.models.OpenAPI; -import jakarta.ws.rs.core.Response.Status; @RestSchema(schemaId = ManagementEndpoint.NAME, schemaInterface = ManagementEndpoint.class) public class ManagementEndpointImpl implements ManagementEndpoint { @@ -40,8 +34,6 @@ public class ManagementEndpointImpl implements ManagementEndpoint { private LocalOpenAPIRegistry localOpenAPIRegistry; - private Environment environment; - @Autowired public void setRegistrationManager(RegistrationManager registrationManager) { this.registrationManager = registrationManager; @@ -52,13 +44,11 @@ public void setLocalOpenAPIRegistry(LocalOpenAPIRegistry localOpenAPIRegistry) { this.localOpenAPIRegistry = localOpenAPIRegistry; } - @Autowired - public void setEnvironment(Environment environment) { - this.environment = environment; - } - @Override public boolean health(String instanceId, String registryName) { + if (StringUtils.isEmpty(instanceId) || StringUtils.isEmpty(registryName)) { + return false; + } String mySelf = registrationManager.getInstanceId(registryName); if (StringUtils.isEmpty(mySelf)) { return false; @@ -67,33 +57,10 @@ public boolean health(String instanceId, String registryName) { } @Override - public Set schemaIds() { - return localOpenAPIRegistry.getSchemaIds(BootStrapProperties.readApplication(environment), - BootStrapProperties.readServiceName(environment)); - } - - @Override - public Map schemaContents(Set schemaIds) { - if (CollectionUtils.isEmpty(schemaIds)) { - throw new InvocationException(Status.BAD_REQUEST, "invalid schemaIds parameter."); - } - checkValid(schemaIds); - - Map apis = localOpenAPIRegistry.loadOpenAPI(BootStrapProperties.readApplication(environment), - BootStrapProperties.readServiceName(environment), schemaIds); - if (apis.size() != schemaIds.size()) { - throw new InvocationException(Status.BAD_REQUEST, "Not exists schemaIds parameter."); - } + public Map schemaContents() { + Map apis = localOpenAPIRegistry.loadOpenAPI(); Map result = new HashMap<>(apis.size()); apis.forEach((k, v) -> result.put(k, SwaggerUtils.swaggerToString(v))); return result; } - - private void checkValid(Set schemaIds) { - for (String schemaId : schemaIds) { - if (schemaId.contains("/") || schemaId.contains("\\") || schemaId.contains("..")) { - throw new InvocationException(Status.BAD_REQUEST, "invalid schemaIds parameter."); - } - } - } } diff --git a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java index 6cb73136dfc..e989a5b8a76 100644 --- a/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java +++ b/solutions/solution-basic/src/main/java/org/apache/servicecomb/solution/basic/integration/MetricsEndpointImpl.java @@ -24,38 +24,36 @@ import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; import org.apache.servicecomb.foundation.metrics.MetricsInitializer; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; +import org.apache.servicecomb.metrics.core.meter.vertx.EndpointMeter; import org.apache.servicecomb.provider.rest.common.RestSchema; import com.google.common.eventbus.EventBus; -import com.netflix.spectator.api.Id; -import com.netflix.spectator.api.Meter; -import com.netflix.spectator.api.Registry; + +import io.micrometer.core.instrument.Measurement; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.Meter.Id; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Tag; @RestSchema(schemaId = MetricsEndpoint.NAME, schemaInterface = MetricsEndpoint.class) public class MetricsEndpointImpl implements MetricsInitializer, MetricsEndpoint { - private GlobalRegistry globalRegistry; + private MeterRegistry meterRegistry; @Override - public void init(GlobalRegistry globalRegistry, EventBus eventBus, MetricsBootstrapConfig config) { - this.globalRegistry = globalRegistry; + public void init(MeterRegistry meterRegistry, EventBus eventBus, MetricsBootstrapConfig config) { + this.meterRegistry = meterRegistry; } @Override public Map measure() { Map measurements = new LinkedHashMap<>(); - if (globalRegistry == null) { - return measurements; - } - StringBuilder sb = new StringBuilder(); - for (Registry registry : globalRegistry.getRegistries()) { - for (Meter meter : registry) { - meter.measure().forEach(measurement -> { - String key = idToString(measurement.id(), sb); - measurements.put(key, measurement.value()); - }); - } + + for (Meter meter : this.meterRegistry.getMeters()) { + meter.measure().forEach(measurement -> { + String key = idToString(meter.getId(), measurement, sb); + measurements.put(key, measurement.getValue()); + }); } return measurements; @@ -63,18 +61,23 @@ public Map measure() { // format id to string: // idName(tag1=value1,tag2=value2) - protected String idToString(Id id, StringBuilder sb) { + protected String idToString(Id id, Measurement measurement, StringBuilder sb) { sb.setLength(0); - sb.append(id.name()).append('('); + sb.append(id.getName()).append("(").append(EndpointMeter.STATISTIC).append("=") + .append(measurement.getStatistic().name()).append(","); sb.append(StreamSupport .stream(id - .tags() + .getTags() .spliterator(), false) - .map(Object::toString) + .map(this::tagToString) .collect( Collectors.joining(","))); sb.append(')'); return sb.toString(); } + + private String tagToString(Tag tag) { + return tag.getKey() + "=" + tag.getValue(); + } } diff --git a/solutions/solution-basic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/solutions/solution-basic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 32075e7b900..351463c398d 100644 --- a/solutions/solution-basic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/solutions/solution-basic/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -15,4 +15,4 @@ ## limitations under the License. ## --------------------------------------------------------------------------- -org.apache.servicecomb.solution.basic.integration.IntegrationConfiguration \ No newline at end of file +org.apache.servicecomb.solution.basic.integration.IntegrationConfiguration diff --git a/solutions/solution-basic/src/main/resources/microservice.yaml b/solutions/solution-basic/src/main/resources/microservice.yaml index 5ad15287d31..cead0718220 100644 --- a/solutions/solution-basic/src/main/resources/microservice.yaml +++ b/solutions/solution-basic/src/main/resources/microservice.yaml @@ -23,10 +23,12 @@ servicecomb: # metrics and access log accesslog: enabled: true + pattern: "|%SCB-traceId|%h|%r|%s|%D" + metrics: - window_time: 60000 + window_time: 300000 invocation: - latencyDistribution: 0,1,10,100,1000 + latencyDistribution: 0,10,50,100,1000 Consumer.invocation.slow: enabled: true msTime: 1000 diff --git a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestHealthEndpointImpl.java b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestHealthEndpointImpl.java index a429dd9fc1d..d0515a73105 100644 --- a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestHealthEndpointImpl.java +++ b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestHealthEndpointImpl.java @@ -22,9 +22,9 @@ import org.apache.servicecomb.foundation.metrics.health.HealthCheckResult; import org.apache.servicecomb.foundation.metrics.health.HealthChecker; import org.apache.servicecomb.foundation.metrics.health.HealthCheckerManager; -import org.junit.Before; -import org.junit.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class TestHealthEndpointImpl { private final HealthChecker good = new HealthChecker() { @@ -52,7 +52,7 @@ public HealthCheckResult check() { }; - @Before + @BeforeEach public void reset() { HealthCheckerManager.getInstance().unregister(good.getName()); HealthCheckerManager.getInstance().unregister(bad.getName()); diff --git a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestManagementEndpointImpl.java b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestManagementEndpointImpl.java index 71164bc2a90..02e06025192 100644 --- a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestManagementEndpointImpl.java +++ b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestManagementEndpointImpl.java @@ -16,31 +16,6 @@ */ package org.apache.servicecomb.solution.basic.integration; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - public class TestManagementEndpointImpl { - @Test - public void testSchemaContentsParameterNotValid() { - ManagementEndpoint endpoint = new ManagementEndpointImpl(); - Assertions.assertThrows(InvocationException.class, () -> endpoint.schemaContents(null)); - Assertions.assertThrows(InvocationException.class, () -> endpoint.schemaContents(Collections.emptySet())); - final Set ids = new HashSet<>(); - ids.add("33/33"); - Assertions.assertThrows(InvocationException.class, () -> endpoint.schemaContents(ids)); - final Set ids2 = new HashSet<>(); - ids2.add("eeee\\"); - Assertions.assertThrows(InvocationException.class, () -> endpoint.schemaContents(ids2)); - final Set ids3 = new HashSet<>(); - ids3.add("eeee3333..sss"); - Assertions.assertThrows(InvocationException.class, () -> endpoint.schemaContents(ids3)); - final Set ids4 = new HashSet<>(); - ids4.add("eeee3333/sss4"); - Assertions.assertThrows(InvocationException.class, () -> endpoint.schemaContents(ids4)); - } + } diff --git a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java index a7698d60914..cb175d9b7d8 100644 --- a/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java +++ b/solutions/solution-basic/src/test/java/org/apache/servicecomb/solution/basic/integration/TestMetricsEndpointImpl.java @@ -23,25 +23,38 @@ import java.util.Map; import org.apache.servicecomb.foundation.metrics.MetricsBootstrapConfig; -import org.apache.servicecomb.foundation.metrics.registry.GlobalRegistry; -import org.junit.Test; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.core.env.Environment; import com.google.common.eventbus.EventBus; -import com.netflix.spectator.api.Clock; -import com.netflix.spectator.api.DefaultRegistry; -import com.netflix.spectator.api.ManualClock; -import com.netflix.spectator.api.Registry; + +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; public class TestMetricsEndpointImpl { MetricsEndpointImpl publisher = new MetricsEndpointImpl(); + EventBus eventBus = new EventBus(); + Environment environment = Mockito.mock(Environment.class); + @BeforeEach + public void setUp() { + Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) + .thenReturn(DEFAULT_METRICS_WINDOW_TIME); + Mockito.when(environment.getProperty( + CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) + .thenReturn(7); + } + @Test public void measure_globalRegistryNull() { + MeterRegistry registry = new SimpleMeterRegistry(); + publisher.init(registry, eventBus, new MetricsBootstrapConfig(environment)); + Map result = publisher.measure(); Assertions.assertEquals(0, result.size()); @@ -49,25 +62,15 @@ public void measure_globalRegistryNull() { @Test public void measure_normal() { - Mockito.when(environment.getProperty(METRICS_WINDOW_TIME, int.class, DEFAULT_METRICS_WINDOW_TIME)) - .thenReturn(DEFAULT_METRICS_WINDOW_TIME); - Mockito.when(environment.getProperty( - CONFIG_LATENCY_DISTRIBUTION_MIN_SCOPE_LEN, int.class, 7)) - .thenReturn(7); - - Clock clock = new ManualClock(); - GlobalRegistry globalRegistry = new GlobalRegistry(); - Registry registry = new DefaultRegistry(clock); - registry.timer(registry.createId("name", "t1", "v1", "t2", "v2")); - globalRegistry.add(registry); - - EventBus eventBus = new EventBus(); + MeterRegistry registry = new SimpleMeterRegistry(); + registry.timer("name", "t1", "v1", "t2", "v2"); - publisher.init(globalRegistry, eventBus, new MetricsBootstrapConfig(environment)); + publisher.init(registry, eventBus, new MetricsBootstrapConfig(environment)); Map result = publisher.measure(); - Assertions.assertEquals(2, result.size()); - Assertions.assertEquals(0, result.get("name(statistic=count,t1=v1,t2=v2)"), 0); - Assertions.assertEquals(0, result.get("name(statistic=totalTime,t1=v1,t2=v2)"), 0); + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals(0, result.get("name(statistic=COUNT,t1=v1,t2=v2)"), 0); + Assertions.assertEquals(0, result.get("name(statistic=TOTAL_TIME,t1=v1,t2=v2)"), 0); + Assertions.assertEquals(0, result.get("name(statistic=MAX,t1=v1,t2=v2)"), 0); } } diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index 1f59f2c64b8..b96de480b73 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default @@ -35,4 +35,4 @@ spring-boot-starters - \ No newline at end of file + diff --git a/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/pom.xml b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/pom.xml index d7a739138b5..c3fa579baee 100644 --- a/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/pom.xml +++ b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-servlet/pom.xml @@ -22,7 +22,7 @@ java-chassis-spring-boot-starters org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-standalone/pom.xml b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-standalone/pom.xml index 314a906521e..cd8d007109f 100644 --- a/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-standalone/pom.xml +++ b/spring-boot/spring-boot-starters/java-chassis-spring-boot-starter-standalone/pom.xml @@ -22,7 +22,7 @@ java-chassis-spring-boot-starters org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -63,4 +63,4 @@ - \ No newline at end of file + diff --git a/spring-boot/spring-boot-starters/pom.xml b/spring-boot/spring-boot-starters/pom.xml index 0d3de32c47b..8c3eecd55ed 100644 --- a/spring-boot/spring-boot-starters/pom.xml +++ b/spring-boot/spring-boot-starters/pom.xml @@ -22,7 +22,7 @@ java-chassis-spring-boot org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -36,17 +36,4 @@ java-chassis-spring-boot-starter-standalone - - - - org.apache.maven.plugins - maven-compiler-plugin - - 17 - 17 - - - - - diff --git a/swagger/pom.xml b/swagger/pom.xml index ba18d328cd7..5009d6dbc8f 100644 --- a/swagger/pom.xml +++ b/swagger/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default swagger diff --git a/swagger/swagger-generator/generator-core/pom.xml b/swagger/swagger-generator/generator-core/pom.xml index 35528f79f39..768f6ecd054 100644 --- a/swagger/swagger-generator/generator-core/pom.xml +++ b/swagger/swagger-generator/generator-core/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb swagger-generator - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-generator-core Java Chassis::Swagger::Generator::Core @@ -44,13 +44,8 @@ jakarta.servlet-api - com.google.inject - guice - - - - javax.xml.bind - jaxb-api + org.reactivestreams + reactive-streams diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java index 85955c29466..c1751b728a8 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/SwaggerUtils.java @@ -22,10 +22,13 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; import java.net.URL; import java.nio.charset.StandardCharsets; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; @@ -81,8 +84,11 @@ public static String getBasePath(OpenAPI swagger) { * Only ones servers and contains only base path. */ public static void setBasePath(OpenAPI swagger, String basePath) { - if (swagger.getServers() == null || swagger.getServers().size() == 0) { - swagger.setServers(List.of(new Server())); + if (swagger.getServers() == null) { + swagger.setServers(new ArrayList<>()); + } + if (swagger.getServers().size() == 0) { + swagger.getServers().add(new Server()); } swagger.getServers().get(0).setUrl(basePath); } @@ -400,6 +406,8 @@ public static boolean isBean(Type type) { && cls != LocalDateTime.class && cls != byte[].class && cls != File.class + && cls != BigInteger.class + && cls != BigDecimal.class && !cls.getName().equals("org.springframework.web.multipart.MultipartFile") && !Part.class.isAssignableFrom(cls)); } @@ -437,4 +445,24 @@ public static boolean methodExists(PathItem pathItem, String httpMethod) { default -> false; }; } + + public static String concatAbsolutePath(OpenAPI swagger, String operationPath) { + String basePath = getBasePath(swagger); + return concatPath(basePath, operationPath); + } + + /** + * Concat the two paths to an absolute path, without end of '/'. + *

+ * e.g. "/" + "/ope" = /ope + * e.g. "/prefix" + "/ope" = /prefix/ope + */ + public static String concatPath(String basePath, String operationPath) { + return ("/" + nonNullify(basePath) + "/" + nonNullify(operationPath)) + .replaceAll("/{2,}", "/"); + } + + private static String nonNullify(String path) { + return path == null ? "" : path; + } } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/ConverterMgr.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/ConverterMgr.java index 6f160a390c0..5705f2460b9 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/ConverterMgr.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/ConverterMgr.java @@ -17,6 +17,8 @@ package org.apache.servicecomb.swagger.converter; +import java.math.BigDecimal; +import java.math.BigInteger; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.HashMap; @@ -66,10 +68,14 @@ private static void initTypeFormatMap() { TypeFactory.defaultInstance().constructType(Integer.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("integer", "int64"), TypeFactory.defaultInstance().constructType(Long.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("integer", ""), + TypeFactory.defaultInstance().constructType(BigInteger.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("number", "float"), TypeFactory.defaultInstance().constructType(Float.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("number", "double"), TypeFactory.defaultInstance().constructType(Double.class)); + TYPE_FORMAT_MAP.put(genTypeFormatKey("number", ""), + TypeFactory.defaultInstance().constructType(BigDecimal.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("string", ""), TypeFactory.defaultInstance().constructType(String.class)); TYPE_FORMAT_MAP.put(genTypeFormatKey("string", "date"), diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ObjectPropertyConverter.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ObjectPropertyConverter.java index d3317131f8d..3564ca4bd55 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ObjectPropertyConverter.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/converter/property/ObjectPropertyConverter.java @@ -30,7 +30,7 @@ public class ObjectPropertyConverter extends AbstractConverter { @Override protected Map findVendorExtensions(Object def) { - return ((Schema) def).getExtensions(); + return ((Schema) def).getExtensions(); } @Override diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java index 35d8ceb7d05..8caec2dd15e 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/ModelResolverExt.java @@ -36,7 +36,6 @@ import org.apache.servicecomb.swagger.extend.property.creator.PartPropertyCreator; import org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator; import org.apache.servicecomb.swagger.generator.SwaggerConst; -import org.apache.servicecomb.swagger.generator.SwaggerGeneratorFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; @@ -98,7 +97,7 @@ private void addPropertyCreator(PropertyCreator creator) { @Override - public Schema resolve(AnnotatedType propType, ModelConverterContext context, Iterator next) { + public Schema resolve(AnnotatedType propType, ModelConverterContext context, Iterator next) { PropertyCreator creator = propertyCreatorMap.get(propType.getType()); if (creator != null) { return creator.createProperty(); @@ -109,8 +108,7 @@ public Schema resolve(AnnotatedType propType, ModelConverterContext context, Ite } Schema result = super.resolve(propType, context, next); - if (SwaggerGeneratorFeature.isLocalExtJavaClassInVendor() - && !StringUtils.isEmpty(result.get$ref())) { + if (!StringUtils.isEmpty(result.get$ref())) { Schema referencedSchema = context.getDefinedModels() != null ? context.getDefinedModels().get(SwaggerUtils.getSchemaName(result.get$ref())) : null; if (referencedSchema != null) { diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospector.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospector.java index bf647c15b64..4e377b81a11 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospector.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/introspector/JsonPropertyIntrospector.java @@ -26,6 +26,8 @@ import io.swagger.v3.core.jackson.SwaggerAnnotationIntrospector; +import java.util.Objects; + public class JsonPropertyIntrospector extends SwaggerAnnotationIntrospector { private static final long serialVersionUID = 4157263023893695762L; @@ -47,10 +49,10 @@ public String findEnumValue(Enum value) { @Override public String findPropertyDescription(Annotated annotated) { Class enumClass = annotated.getRawType(); - if (enumClass.isEnum()) { + if (enumClass.isEnum() && Objects.nonNull(annotated.getAnnotated())) { return SwaggerEnum.JDK.findPropertyDescription(enumClass, annotated.getAnnotated().getAnnotations()); } - if (EnumUtils.isDynamicEnum(enumClass)) { + if (EnumUtils.isDynamicEnum(enumClass) && Objects.nonNull(annotated.getAnnotated())) { return SwaggerEnum.DYNAMIC.findPropertyDescription(enumClass, annotated.getAnnotated().getAnnotations()); } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/ByteArrayPropertyCreator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/ByteArrayPropertyCreator.java index 20db68f855f..768a07a411f 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/ByteArrayPropertyCreator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/ByteArrayPropertyCreator.java @@ -30,7 +30,7 @@ public class ByteArrayPropertyCreator implements PropertyCreator { private final Class[] classes = {Byte[].class, byte[].class}; @Override - public Schema createProperty() { + public Schema createProperty() { return new ByteArraySchema(); } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/BytePropertyCreator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/BytePropertyCreator.java index 65b728bfae6..66e53e67814 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/BytePropertyCreator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/BytePropertyCreator.java @@ -31,7 +31,7 @@ public class BytePropertyCreator implements PropertyCreator { private final Class[] classes = {Byte.class, byte.class}; @Override - public Schema createProperty() { + public Schema createProperty() { return new IntegerSchema(); } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/InputStreamPropertyCreator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/InputStreamPropertyCreator.java index 4231c1d6389..8a0556c145a 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/InputStreamPropertyCreator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/InputStreamPropertyCreator.java @@ -27,7 +27,7 @@ public class InputStreamPropertyCreator implements PropertyCreator { private final Class[] classes = {InputStream.class}; @Override - public Schema createProperty() { + public Schema createProperty() { return new ByteArraySchema(); } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PartPropertyCreator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PartPropertyCreator.java index 61060b13c05..3a489e5f7ef 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PartPropertyCreator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PartPropertyCreator.java @@ -17,16 +17,15 @@ package org.apache.servicecomb.swagger.extend.property.creator; -import jakarta.servlet.http.Part; - import io.swagger.v3.oas.models.media.FileSchema; import io.swagger.v3.oas.models.media.Schema; +import jakarta.servlet.http.Part; public class PartPropertyCreator implements PropertyCreator { private final Class[] classes = {Part.class}; @Override - public Schema createProperty() { + public Schema createProperty() { return new FileSchema(); } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PropertyCreator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PropertyCreator.java index 671b9dc8a3e..52629ed7871 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PropertyCreator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/extend/property/creator/PropertyCreator.java @@ -21,7 +21,7 @@ @SuppressWarnings({"rawtypes"}) public interface PropertyCreator { - Schema createProperty(); + Schema createProperty(); Class[] classes(); } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/OperationGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/OperationGenerator.java index c7aff4c80a2..358db852989 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/OperationGenerator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/OperationGenerator.java @@ -46,4 +46,10 @@ public interface OperationGenerator { * Used to check if one of operation form parameter is binary */ boolean isBinary(); + + /** + * + * Used to check if this operation is websocket + */ + boolean isWebsocket(); } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterGenerator.java index 9b2be38d55d..2547b5c7f20 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterGenerator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ParameterGenerator.java @@ -126,7 +126,7 @@ public boolean isBinary() { public void generate() { this.parameterGeneratorContext.updateConsumes( - this.operationGenerator.isForm(), this.operationGenerator.isBinary()); + this.operationGenerator.isForm(), this.operationGenerator.isBinary(), this.operationGenerator.isWebsocket()); if (this.parameterGeneratorContext.getHttpParameterType() == HttpParameterType.BODY) { if (parameterGeneratorContext.getSupportedConsumes().size() == 0) { diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ResponseTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ResponseTypeProcessor.java index 60ec44de498..a1efa5112ae 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ResponseTypeProcessor.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/ResponseTypeProcessor.java @@ -24,10 +24,6 @@ public interface ResponseTypeProcessor { Type getProcessType(); /** - * - * @param swaggerGenerator - * @param operationGenerator - * @param genericResponseType * @return if genericResponseType is CompletableFuture<String>, then return String */ Type extractResponseType(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, @@ -37,5 +33,5 @@ default Type extractResponseType(Type genericResponseType) { return extractResponseType(null, null, genericResponseType); } - Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType); + Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType); } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerConst.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerConst.java index 5b26bea7520..70990922e07 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerConst.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerConst.java @@ -35,6 +35,10 @@ private SwaggerConst() { public static final String PROTOBUF_TYPE = "application/protobuf"; + public static final String WEBSOCKET_TYPE = "application/websocket"; + + public static final String TAG_WEBSOCKET = "websocket"; + public static final String EXT_JAVA_INTF = "x-java-interface"; public static final String EXT_JAVA_CLASS = "x-java-class"; diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGenerator.java index b11d8ffa784..cc9bbaaa58b 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGenerator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGenerator.java @@ -45,8 +45,6 @@ static SwaggerGenerator create(Class cls) { throw new IllegalStateException("impossible, must be bug. can not generate swagger for " + cls.getName()); } - SwaggerGeneratorFeature getSwaggerGeneratorFeature(); - SwaggerGeneratorContext getSwaggerGeneratorContext(); /** diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorFeature.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorFeature.java deleted file mode 100644 index 398e5f546e0..00000000000 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/SwaggerGeneratorFeature.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.generator; - -public class SwaggerGeneratorFeature { - private static final ThreadLocal featureThreadLocal = new ThreadLocal<>(); - - public static ThreadLocal getFeatureThreadLocal() { - return featureThreadLocal; - } - - public static boolean isLocalExtJavaClassInVendor() { - SwaggerGeneratorFeature feature = featureThreadLocal.get(); - return feature != null ? feature.extJavaClassInVendor : true; - } - - // packageName and extJavaInVender is unnecessary, new invocation mechanism not depend them - // just remain them for compatible - private String packageName = "gen.swagger"; - - private boolean extJavaClassInVendor = true; - - private boolean extJavaInterfaceInVendor = true; - - public String getPackageName() { - return packageName; - } - - public void setPackageName(String packageName) { - this.packageName = packageName; - } - - public boolean isExtJavaClassInVendor() { - return extJavaClassInVendor; - } - - public SwaggerGeneratorFeature setExtJavaClassInVendor(boolean extJavaClassInVendor) { - this.extJavaClassInVendor = extJavaClassInVendor; - - return this; - } - - public boolean isExtJavaInterfaceInVendor() { - return extJavaInterfaceInVendor; - } - - public SwaggerGeneratorFeature setExtJavaInterfaceInVendor(boolean extJavaInterfaceInVendor) { - this.extJavaInterfaceInVendor = extJavaInterfaceInVendor; - - return this; - } -} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java index f23a4d91dda..f3c00478f7b 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractOperationGenerator.java @@ -45,6 +45,7 @@ import org.apache.servicecomb.swagger.generator.ParameterGenerator; import org.apache.servicecomb.swagger.generator.ParameterTypeProcessor; import org.apache.servicecomb.swagger.generator.ResponseTypeProcessor; +import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import org.apache.servicecomb.swagger.generator.core.utils.MethodUtils; @@ -343,6 +344,12 @@ public boolean isBinary() { return false; } + @Override + public boolean isWebsocket() { + return this.swaggerOperation.getTags() != null && + this.swaggerOperation.getTags().contains(SwaggerConst.TAG_WEBSOCKET); + } + @Override public void addOperationToSwagger() { if (StringUtils.isEmpty(httpMethod)) { @@ -410,7 +417,7 @@ public void scanResponse() { } } - protected Schema createResponseModel() { + protected Schema createResponseModel() { Type responseType = TypeToken.of(clazz) .resolveType(method.getGenericReturnType()) diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractSwaggerGenerator.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractSwaggerGenerator.java index 17b491b5214..6549418441d 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractSwaggerGenerator.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/AbstractSwaggerGenerator.java @@ -36,7 +36,6 @@ import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; -import org.apache.servicecomb.swagger.generator.SwaggerGeneratorFeature; import org.apache.servicecomb.swagger.generator.core.utils.MethodUtils; import io.swagger.v3.oas.annotations.Operation; @@ -57,8 +56,6 @@ * */ public abstract class AbstractSwaggerGenerator implements SwaggerGenerator { - protected SwaggerGeneratorFeature swaggerGeneratorFeature = new SwaggerGeneratorFeature(); - protected SwaggerGeneratorContext swaggerGeneratorContext = new SwaggerGeneratorContext(); protected Class cls; @@ -102,11 +99,6 @@ public void setHttpMethod(String httpMethod) { this.httpMethod = httpMethod.toUpperCase(Locale.US); } - @Override - public SwaggerGeneratorFeature getSwaggerGeneratorFeature() { - return swaggerGeneratorFeature; - } - @Override public SwaggerGeneratorContext getSwaggerGeneratorContext() { return swaggerGeneratorContext; @@ -116,33 +108,21 @@ public OpenAPI generate() { LOGGER.info("generate schema from [{}]", cls); scanClassAnnotation(); - ThreadLocal featureThreadLocal = SwaggerGeneratorFeature.getFeatureThreadLocal(); - featureThreadLocal.set(swaggerGeneratorFeature); - try { - scanMethods(); - addOperationsToSwagger(); + scanMethods(); + addOperationsToSwagger(); - correctSwagger(); + correctSwagger(); - return openAPI; - } finally { - featureThreadLocal.remove(); - } + return openAPI; } public void scanClassAnnotation() { - ThreadLocal featureThreadLocal = SwaggerGeneratorFeature.getFeatureThreadLocal(); - featureThreadLocal.set(swaggerGeneratorFeature); - try { - for (Annotation annotation : cls.getAnnotations()) { - ClassAnnotationProcessor processor = findClassAnnotationProcessor(annotation.annotationType()); - if (processor == null) { - continue; - } - processor.process(this, annotation); + for (Annotation annotation : cls.getAnnotations()) { + ClassAnnotationProcessor processor = findClassAnnotationProcessor(annotation.annotationType()); + if (processor == null) { + continue; } - } finally { - featureThreadLocal.remove(); + processor.process(this, annotation); } } @@ -262,11 +242,12 @@ protected void addOperationsToSwagger() { @Override public void setBasePath(String basePath) { basePath = new PlaceholderResolver().replaceFirst(basePath); - Server server = new Server(); - server.setUrl(basePath); if (openAPI.getServers() == null) { openAPI.setServers(new ArrayList<>()); } - openAPI.getServers().add(server); + if (openAPI.getServers().size() == 0) { + openAPI.getServers().add(new Server()); + } + openAPI.getServers().get(0).setUrl(basePath); } } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/ParameterGeneratorContext.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/ParameterGeneratorContext.java index 99b1eba2207..dc9f6c9c0da 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/ParameterGeneratorContext.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/ParameterGeneratorContext.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.core.model.HttpParameterType; import com.fasterxml.jackson.databind.JavaType; @@ -115,8 +116,14 @@ public void setDefaultValue(Object defaultValue) { this.defaultValue = defaultValue; } - public void updateConsumes(boolean isForm, boolean isBinary) { + public void updateConsumes(boolean isForm, boolean isBinary, boolean isWebSocket) { List removed = new ArrayList<>(); + if (isWebSocket) { + supportedConsumes.clear(); + supportedConsumes.add(SwaggerConst.WEBSOCKET_TYPE); + return; + } + if (httpParameterType == HttpParameterType.BODY) { if (isForm) { throw new IllegalArgumentException("Both form and body parameter not allowed."); @@ -127,7 +134,11 @@ public void updateConsumes(boolean isForm, boolean isBinary) { } removed.add(media); } - } else if (httpParameterType == HttpParameterType.FORM) { + supportedConsumes.removeAll(removed); + return; + } + + if (httpParameterType == HttpParameterType.FORM) { for (String media : supportedConsumes) { if (!SUPPORTED_FORM_CONTENT_TYPE.contains(media)) { removed.add(media); @@ -140,10 +151,11 @@ public void updateConsumes(boolean isForm, boolean isBinary) { removed.add(MediaType.MULTIPART_FORM_DATA); } } - } else { - supportedConsumes.clear(); + supportedConsumes.removeAll(removed); + return; } - supportedConsumes.removeAll(removed); + + supportedConsumes.clear(); } public boolean isForm() { diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java index 837499dad21..7b1eeda13e0 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/SwaggerGeneratorContext.java @@ -30,10 +30,12 @@ public class SwaggerGeneratorContext { protected static final List SUPPORTED_CONTENT_TYPE = Arrays.asList(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN, - MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_FORM_URLENCODED); + MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_FORM_URLENCODED, SwaggerConst.WEBSOCKET_TYPE, + MediaType.SERVER_SENT_EVENTS); protected static final List SUPPORTED_BODY_CONTENT_TYPE - = Arrays.asList(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, MediaType.TEXT_PLAIN); + = Arrays.asList(MediaType.APPLICATION_JSON, SwaggerConst.PROTOBUF_TYPE, + MediaType.TEXT_PLAIN); protected static final List SUPPORTED_FORM_CONTENT_TYPE = Arrays.asList(MediaType.MULTIPART_FORM_DATA, MediaType.APPLICATION_FORM_URLENCODED); diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartListParameterTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartListParameterTypeProcessor.java index 183f45df867..8d47dfdc359 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartListParameterTypeProcessor.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/PartListParameterTypeProcessor.java @@ -18,15 +18,16 @@ import java.util.List; +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; + import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.google.inject.util.Types; import jakarta.servlet.http.Part; public class PartListParameterTypeProcessor extends PartArrayParameterTypeProcessor { @Override public JavaType getProcessType() { - return TypeFactory.defaultInstance().constructType(Types.newParameterizedType(List.class, Part.class)); + return TypeFactory.defaultInstance().constructType(ParameterizedTypeUtil.make(List.class, Part.class)); } } diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ServerWebSocketContextRegister.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ServerWebSocketContextRegister.java new file mode 100644 index 00000000000..70bb4255096 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/parameter/ServerWebSocketContextRegister.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.processor.parameter; + +import java.lang.reflect.Type; + +import org.apache.servicecomb.swagger.generator.SwaggerContextRegister; + +import io.vertx.core.http.ServerWebSocket; + +public class ServerWebSocketContextRegister implements SwaggerContextRegister { + @Override + public Type getContextType() { + return ServerWebSocket.class; + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java index 6b4bee3f9aa..46788ae8ecc 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/DefaultResponseTypeProcessor.java @@ -21,8 +21,6 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import jakarta.servlet.http.Part; - import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.OperationGenerator; import org.apache.servicecomb.swagger.generator.ResponseTypeProcessor; @@ -30,6 +28,7 @@ import io.swagger.v3.core.util.ReflectionUtils; import io.swagger.v3.oas.models.media.Schema; +import jakarta.servlet.http.Part; public class DefaultResponseTypeProcessor implements ResponseTypeProcessor { protected boolean extractActualType; @@ -73,7 +72,7 @@ private Type doExtractResponseType(Type genericResponseType) { } @Override - public Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + public Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, Type genericResponseType) { Type responseType = extractResponseType(swaggerGenerator, operationGenerator, genericResponseType); if (responseType == null || ReflectionUtils.isVoid(responseType)) { diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/PublisherProcessor.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/PublisherProcessor.java new file mode 100644 index 00000000000..8388477dca5 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/processor/response/PublisherProcessor.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.servicecomb.swagger.generator.core.processor.response; + +import java.lang.reflect.Type; +import java.util.List; + +import org.apache.servicecomb.swagger.generator.OperationGenerator; +import org.apache.servicecomb.swagger.generator.SwaggerGenerator; +import org.reactivestreams.Publisher; + +import io.swagger.v3.oas.models.media.Schema; +import jakarta.ws.rs.core.MediaType; + +public class PublisherProcessor extends DefaultResponseTypeProcessor { + public PublisherProcessor() { + extractActualType = true; + } + + @Override + public Type getProcessType() { + return Publisher.class; + } + + @Override + public Schema process(SwaggerGenerator swaggerGenerator, OperationGenerator operationGenerator, + Type genericResponseType) { + operationGenerator.getOperationGeneratorContext() + .updateProduces(List.of(MediaType.SERVER_SENT_EVENTS)); + return super.process(swaggerGenerator, operationGenerator, genericResponseType); + } +} diff --git a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java index 20db8307fd4..c3d6270424e 100644 --- a/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java +++ b/swagger/swagger-generator/generator-core/src/main/java/org/apache/servicecomb/swagger/generator/core/unittest/UnitTestSwaggerUtils.java @@ -20,14 +20,16 @@ import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.Objects; import org.apache.commons.io.IOUtils; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.junit.jupiter.api.Assertions; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import io.swagger.v3.core.util.Yaml; import io.swagger.v3.oas.models.OpenAPI; @@ -71,7 +73,6 @@ public static OpenAPI parse(String content) { public static SwaggerGenerator testSwagger(String resPath, Class cls, String... methods) { SwaggerGenerator generator = SwaggerGenerator.create(cls); generator.replaceMethodWhiteList(methods); - generator.getSwaggerGeneratorFeature().setPackageName("gen.cse.ms.ut"); OpenAPI swagger = generator.generate(); String schema = pretty(swagger).trim(); @@ -82,8 +83,19 @@ public static SwaggerGenerator testSwagger(String resPath, Class cls, String. expectSchema = expectSchema.substring(offset + 4); } - if (!Objects.equals(expectSchema, schema)) { - Assertions.assertEquals(expectSchema, schema); + try { + ObjectMapper yaml = new ObjectMapper(new YAMLFactory()); + JsonNode expected = yaml.readTree(expectSchema); + JsonNode actual = yaml.readTree(schema); + + if (!actual.equals(expected)) { + ObjectMapper json = new ObjectMapper(); + String expectedPretty = json.writerWithDefaultPrettyPrinter().writeValueAsString(expected); + String actualPretty = json.writerWithDefaultPrettyPrinter().writeValueAsString(actual); + Assertions.fail("OpenAPI mismatch.\n=== EXPECTED ===\n" + expectedPretty + "\n=== ACTUAL ===\n" + actualPretty); + } + } catch (Exception e) { + Assertions.fail("Failed to parse/compare OpenAPI YAML: " + e.getMessage(), e); } return generator; diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor index 42aa6468f7b..249784556de 100644 --- a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor @@ -18,4 +18,4 @@ org.apache.servicecomb.swagger.generator.core.processor.annotation.OperationMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.core.processor.annotation.ApiResponsesMethodProcessor org.apache.servicecomb.swagger.generator.core.processor.annotation.ApiResponseMethodProcessor -org.apache.servicecomb.swagger.generator.core.processor.annotation.RequestBodyMethodAnnotationProcessor \ No newline at end of file +org.apache.servicecomb.swagger.generator.core.processor.annotation.RequestBodyMethodAnnotationProcessor diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor index be1aa8c9983..eba9ece3fd7 100644 --- a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor @@ -17,4 +17,4 @@ org.apache.servicecomb.swagger.generator.core.processor.parameter.RequestBodyParameterAnnotationProcessor org.apache.servicecomb.swagger.generator.core.processor.parameter.ParameterParameterAnnotationProcessor -org.apache.servicecomb.swagger.generator.core.processor.parameter.RawJsonRequestBodyProcessor \ No newline at end of file +org.apache.servicecomb.swagger.generator.core.processor.parameter.RawJsonRequestBodyProcessor diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor index 7f737a6ff8d..130af5d2a49 100644 --- a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterTypeProcessor @@ -18,4 +18,3 @@ org.apache.servicecomb.swagger.generator.core.processor.parameter.PartParameterTypeProcessor org.apache.servicecomb.swagger.generator.core.processor.parameter.PartArrayParameterTypeProcessor org.apache.servicecomb.swagger.generator.core.processor.parameter.PartListParameterTypeProcessor - diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor index 53678b70263..9abf37e8c9d 100644 --- a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor @@ -17,3 +17,4 @@ org.apache.servicecomb.swagger.generator.core.processor.response.CompletableFutureProcessor org.apache.servicecomb.swagger.generator.core.processor.response.OptionalProcessor +org.apache.servicecomb.swagger.generator.core.processor.response.PublisherProcessor diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister index 55d82314785..db52891e086 100644 --- a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister @@ -15,4 +15,5 @@ # limitations under the License. # -org.apache.servicecomb.swagger.generator.core.processor.parameter.HttpServletRequestContextRegister \ No newline at end of file +org.apache.servicecomb.swagger.generator.core.processor.parameter.HttpServletRequestContextRegister +org.apache.servicecomb.swagger.generator.core.processor.parameter.ServerWebSocketContextRegister diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGenerator b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGenerator index 7ea0df0595d..35c874ef2a2 100644 --- a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGenerator +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGenerator @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGenerator \ No newline at end of file +org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGenerator diff --git a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory index 0194518ddac..ba83d84d0d5 100644 --- a/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory +++ b/swagger/swagger-generator/generator-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorFactory \ No newline at end of file +org.apache.servicecomb.swagger.generator.pojo.PojoSwaggerGeneratorFactory diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExtTest.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExtTest.java index e6865b1055b..309f84f4b4a 100644 --- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExtTest.java +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/extend/module/EnumModuleExtTest.java @@ -56,7 +56,7 @@ public void testEnumModule() throws JsonProcessingException { serializeValue = mapper.writeValueAsString(TestEnum.E_F); Assertions.assertEquals("\"E.F\"", serializeValue); serializeValue = mapper.writeValueAsString(TestEnum.HI); - Assertions.assertEquals("\"HI\"", serializeValue); + Assertions.assertEquals("\"\"", serializeValue); } @Test @@ -64,6 +64,6 @@ public void testEnumModuleModel() { OpenAPI openAPI = new OpenAPI(); Schema schema = SwaggerUtils.resolveTypeSchemas(openAPI, TestEnum.class); Assertions.assertEquals(schema.getType(), "string"); - MatcherAssert.assertThat((List) schema.getEnum(), contains("AB", "C-D", "E.F", "HI")); + MatcherAssert.assertThat((List) schema.getEnum(), contains("AB", "C-D", "E.F", "")); } } diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiResponse.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiResponse.java index 58e1ebeb77f..e39b3bbf858 100644 --- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiResponse.java +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestApiResponse.java @@ -21,7 +21,9 @@ import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperations; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import io.swagger.v3.oas.annotations.headers.Header; import io.swagger.v3.oas.annotations.media.Content; @@ -30,11 +32,18 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.ws.rs.core.MediaType; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class TestApiResponse { - static SwaggerOperations swaggerOperations = SwaggerOperations.generate(ApiResponseAnnotation.class); + + SwaggerOperations swaggerOperations; + + @BeforeAll + public void setUp() { + swaggerOperations = SwaggerOperations.generate(ApiResponseAnnotation.class); + } @AfterAll - public static void teardown() { + public void tearDown() { swaggerOperations = null; } diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestArrayType.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestArrayType.java index c36e27f687c..931fe3da9e0 100644 --- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestArrayType.java +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestArrayType.java @@ -35,11 +35,11 @@ public void test() { SwaggerOperations swaggerOperations = SwaggerOperations.generate(ArrayType.class); SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testBytes"); RequestBody bodyParameter = swaggerOperation.getOperation().getRequestBody(); - Schema model = bodyParameter.getContent().get(MediaType.APPLICATION_JSON).getSchema(); + Schema model = bodyParameter.getContent().get(MediaType.APPLICATION_JSON).getSchema(); Assertions.assertEquals(Components.COMPONENTS_SCHEMAS_REF + "testBytesBody", model.get$ref()); OpenAPI openAPI = swaggerOperation.getSwagger(); - Schema schema = openAPI.getComponents().getSchemas().get("testBytesBody"); + Schema schema = openAPI.getComponents().getSchemas().get("testBytesBody"); Assertions.assertEquals(1, schema.getProperties().size()); ByteArraySchema arrayProperty = (ByteArraySchema) schema.getProperties().get("value"); diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestOperationGenerator.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestOperationGenerator.java index f238e91ff34..b46b9bb8327 100644 --- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestOperationGenerator.java +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestOperationGenerator.java @@ -27,7 +27,9 @@ import org.hamcrest.MatcherAssert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.Operation; @@ -39,11 +41,17 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class TestOperationGenerator { - static SwaggerOperations swaggerOperations = SwaggerOperations.generate(TestClass.class); + SwaggerOperations swaggerOperations; + + @BeforeAll + public void setUp() { + swaggerOperations = SwaggerOperations.generate(TestClass.class); + } @AfterAll - public static void teardown() { + public void tearDown() { swaggerOperations = null; } diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerUtils.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerUtils.java index 6a138dc42f1..7611a3f80a7 100644 --- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerUtils.java +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/TestSwaggerUtils.java @@ -28,11 +28,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.logging.Logger; import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.SwaggerConst; import org.apache.servicecomb.swagger.generator.SwaggerGeneratorUtils; +import org.apache.servicecomb.swagger.generator.core.pojo.TestTypeClass; +import org.apache.servicecomb.swagger.generator.core.pojo.TestTypeRecord; import org.apache.servicecomb.swagger.generator.core.pojo.TestType1; import org.apache.servicecomb.swagger.generator.core.pojo.TestType2; import org.apache.servicecomb.swagger.generator.core.schema.RepeatOperation; @@ -50,6 +53,7 @@ @SuppressWarnings("rawtypes") public class TestSwaggerUtils { + Logger LOGGER = Logger.getLogger(TestSwaggerUtils.class.getName()); @Test public void testSchemaMethod() { @@ -117,12 +121,24 @@ private static class AllTypeTest2 { Map t3; TestType2[] t4; + + } + + private static class AllTypeTest3{ + TestTypeRecord t5; + } + private static class AllTypeTest4{ + TestTypeClass t5; } @Test public void testAddDefinitions() { - Field[] fields1 = AllTypeTest1.class.getDeclaredFields(); - Field[] fields2 = AllTypeTest2.class.getDeclaredFields(); + testAllType(AllTypeTest1.class, AllTypeTest2.class); + } + + private void testAllType(Class clazz1, Class clazz2) { + Field[] fields1 = clazz1.getDeclaredFields(); + Field[] fields2 = clazz2.getDeclaredFields(); for (Field value : fields1) { for (Field field : fields2) { if (value.isSynthetic() || field.isSynthetic()) { @@ -132,12 +148,18 @@ public void testAddDefinitions() { testExcep(value.getGenericType(), field.getGenericType()); fail("IllegalArgumentException expected"); } catch (IllegalArgumentException e) { + LOGGER.warning(value.getGenericType() + " " + field.getGenericType() + " " + e.getMessage()); MatcherAssert.assertThat(e.getMessage(), containsString("duplicate param model:")); } } } } + @Test + public void testAddDefinitionsWithRecord() { + testAllType(AllTypeTest3.class, AllTypeTest4.class); + } + private void testExcep(Type f1, Type f2) { OpenAPI swagger = new OpenAPI(); SwaggerUtils.resolveTypeSchemas(swagger, f1); @@ -161,5 +183,13 @@ public void test_resolve_type_schemas_correct() { // should be ObjectSchema but swagger is not. //

 Assertions.assertTrue(schema instanceof ObjectSchema) 
Assertions.assertEquals("object", schema.getType()); + + openAPI = new OpenAPI(); + schema = SwaggerUtils.getSchema(openAPI, SwaggerUtils.resolveTypeSchemas(openAPI, TestTypeClass.class)); // resolve reference + Assertions.assertEquals("object", schema.getType()); + + openAPI = new OpenAPI(); + schema = SwaggerUtils.getSchema(openAPI, SwaggerUtils.resolveTypeSchemas(openAPI, TestTypeRecord.class)); // resolve reference + Assertions.assertEquals("object", schema.getType()); } } diff --git a/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestConsumerProvider.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeClass.java similarity index 64% rename from providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestConsumerProvider.java rename to swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeClass.java index 4f710994604..b00a4433480 100644 --- a/providers/provider-rest-common/src/test/java/org/apache/servicecomb/provider/rest/common/TestRestConsumerProvider.java +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeClass.java @@ -15,18 +15,20 @@ * limitations under the License. */ -package org.apache.servicecomb.provider.rest.common; +package org.apache.servicecomb.swagger.generator.core.pojo; -import org.apache.servicecomb.common.rest.RestConst; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; +import io.swagger.v3.oas.annotations.media.Schema; -public class TestRestConsumerProvider { +@Schema(name = "YYY") +public class TestTypeClass { + @Schema(description = "language") + private TestTypeEnumLang testTypeEnumLang; - @Test - public void testInit() throws Exception { - RestConsumerProvider instance = new RestConsumerProvider(); - instance.init(); - Assertions.assertEquals(RestConst.REST, instance.getName()); + public TestTypeEnumLang getTestTypeEnumLang() { + return testTypeEnumLang; + } + + public void setTestTypeEnumLang(TestTypeEnumLang testTypeEnumLang) { + this.testTypeEnumLang = testTypeEnumLang; } } diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeEnumLang.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeEnumLang.java new file mode 100644 index 00000000000..515f45c7bb1 --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeEnumLang.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.pojo; + +public enum TestTypeEnumLang { + JAVA, CHINESE, UNKNOWN; +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeRecord.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeRecord.java new file mode 100644 index 00000000000..c61cc0913eb --- /dev/null +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/pojo/TestTypeRecord.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.swagger.generator.core.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(name = "YYY") +public record TestTypeRecord( + @Schema(description = "language") TestTypeEnumLang testTypeEnumLang) { +} diff --git a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessorTest.java b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessorTest.java index b4720f15d13..fdc3cea273e 100644 --- a/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessorTest.java +++ b/swagger/swagger-generator/generator-core/src/test/java/org/apache/servicecomb/swagger/generator/core/processor/annotation/OperationMethodAnnotationProcessorTest.java @@ -28,7 +28,9 @@ import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; @@ -41,11 +43,17 @@ import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.core.MediaType; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class OperationMethodAnnotationProcessorTest { - static SwaggerOperations swaggerOperations = SwaggerOperations.generate(TestClass.class); + SwaggerOperations swaggerOperations; + + @BeforeAll + public void setUp() { + swaggerOperations = SwaggerOperations.generate(TestClass.class); + } @AfterAll - public static void teardown() { + public void tearDown() { swaggerOperations = null; } @@ -174,7 +182,7 @@ public void testBlankMediaType() { @Test public void testBodyParam() { SwaggerOperation swaggerOperation = swaggerOperations.findOperation("testBodyParam"); - io.swagger.v3.oas.models.media.Schema schema = swaggerOperation.getSwagger() + io.swagger.v3.oas.models.media.Schema schema = swaggerOperation.getSwagger() .getPaths().get("/testBodyParam").getPost().getRequestBody().getContent() .get(MediaType.APPLICATION_JSON).getSchema(); Assertions.assertEquals(Components.COMPONENTS_SCHEMAS_REF + "TestBodyBean", schema.get$ref()); diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/ParameterAnnotation.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/ParameterAnnotation.yaml index 64e81e853ab..af5f0a2e9de 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/ParameterAnnotation.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/ParameterAnnotation.yaml @@ -31,13 +31,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/differentNameBody' + $ref: "#/components/schemas/differentNameBody" application/protobuf: schema: - $ref: '#/components/schemas/differentNameBody' + $ref: "#/components/schemas/differentNameBody" text/plain: schema: - $ref: '#/components/schemas/differentNameBody' + $ref: "#/components/schemas/differentNameBody" x-name: differentNameBody responses: "200": @@ -65,4 +65,4 @@ components: format: int32 "y": type: integer - format: int32 \ No newline at end of file + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/Schema.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/Schema.yaml index 0de5a305e98..0894deeb672 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/Schema.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/Schema.yaml @@ -30,13 +30,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ignoreRequestBody' + $ref: "#/components/schemas/ignoreRequestBody" application/protobuf: schema: - $ref: '#/components/schemas/ignoreRequestBody' + $ref: "#/components/schemas/ignoreRequestBody" text/plain: schema: - $ref: '#/components/schemas/ignoreRequestBody' + $ref: "#/components/schemas/ignoreRequestBody" x-name: ignoreRequestBody responses: "200": @@ -48,13 +48,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/nestedListStringBody' + $ref: "#/components/schemas/nestedListStringBody" application/protobuf: schema: - $ref: '#/components/schemas/nestedListStringBody' + $ref: "#/components/schemas/nestedListStringBody" text/plain: schema: - $ref: '#/components/schemas/nestedListStringBody' + $ref: "#/components/schemas/nestedListStringBody" x-name: nestedListStringBody responses: "200": @@ -88,13 +88,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/AllType' + $ref: "#/components/schemas/AllType" application/protobuf: schema: - $ref: '#/components/schemas/AllType' + $ref: "#/components/schemas/AllType" text/plain: schema: - $ref: '#/components/schemas/AllType' + $ref: "#/components/schemas/AllType" x-name: obj responses: "200": @@ -132,13 +132,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testArrayBody' + $ref: "#/components/schemas/testArrayBody" application/protobuf: schema: - $ref: '#/components/schemas/testArrayBody' + $ref: "#/components/schemas/testArrayBody" text/plain: schema: - $ref: '#/components/schemas/testArrayBody' + $ref: "#/components/schemas/testArrayBody" x-name: testArrayBody responses: "200": @@ -150,13 +150,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testBooleanBody' + $ref: "#/components/schemas/testBooleanBody" application/protobuf: schema: - $ref: '#/components/schemas/testBooleanBody' + $ref: "#/components/schemas/testBooleanBody" text/plain: schema: - $ref: '#/components/schemas/testBooleanBody' + $ref: "#/components/schemas/testBooleanBody" x-name: testBooleanBody responses: "200": @@ -168,13 +168,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testByteBody' + $ref: "#/components/schemas/testByteBody" application/protobuf: schema: - $ref: '#/components/schemas/testByteBody' + $ref: "#/components/schemas/testByteBody" text/plain: schema: - $ref: '#/components/schemas/testByteBody' + $ref: "#/components/schemas/testByteBody" x-name: testByteBody responses: "200": @@ -186,13 +186,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testBytesBody' + $ref: "#/components/schemas/testBytesBody" application/protobuf: schema: - $ref: '#/components/schemas/testBytesBody' + $ref: "#/components/schemas/testBytesBody" text/plain: schema: - $ref: '#/components/schemas/testBytesBody' + $ref: "#/components/schemas/testBytesBody" x-name: testBytesBody responses: "200": @@ -204,13 +204,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testCharBody' + $ref: "#/components/schemas/testCharBody" application/protobuf: schema: - $ref: '#/components/schemas/testCharBody' + $ref: "#/components/schemas/testCharBody" text/plain: schema: - $ref: '#/components/schemas/testCharBody' + $ref: "#/components/schemas/testCharBody" x-name: testCharBody responses: "200": @@ -273,13 +273,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testDoubleBody' + $ref: "#/components/schemas/testDoubleBody" application/protobuf: schema: - $ref: '#/components/schemas/testDoubleBody' + $ref: "#/components/schemas/testDoubleBody" text/plain: schema: - $ref: '#/components/schemas/testDoubleBody' + $ref: "#/components/schemas/testDoubleBody" x-name: testDoubleBody responses: "200": @@ -291,13 +291,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testEnumBody' + $ref: "#/components/schemas/testEnumBody" application/protobuf: schema: - $ref: '#/components/schemas/testEnumBody' + $ref: "#/components/schemas/testEnumBody" text/plain: schema: - $ref: '#/components/schemas/testEnumBody' + $ref: "#/components/schemas/testEnumBody" x-name: testEnumBody responses: "200": @@ -309,13 +309,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testFloatBody' + $ref: "#/components/schemas/testFloatBody" application/protobuf: schema: - $ref: '#/components/schemas/testFloatBody' + $ref: "#/components/schemas/testFloatBody" text/plain: schema: - $ref: '#/components/schemas/testFloatBody' + $ref: "#/components/schemas/testFloatBody" x-name: testFloatBody responses: "200": @@ -327,13 +327,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testIntegerBody' + $ref: "#/components/schemas/testIntegerBody" application/protobuf: schema: - $ref: '#/components/schemas/testIntegerBody' + $ref: "#/components/schemas/testIntegerBody" text/plain: schema: - $ref: '#/components/schemas/testIntegerBody' + $ref: "#/components/schemas/testIntegerBody" x-name: testIntegerBody responses: "200": @@ -345,13 +345,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testListBody' + $ref: "#/components/schemas/testListBody" application/protobuf: schema: - $ref: '#/components/schemas/testListBody' + $ref: "#/components/schemas/testListBody" text/plain: schema: - $ref: '#/components/schemas/testListBody' + $ref: "#/components/schemas/testListBody" x-name: testListBody responses: "200": @@ -363,13 +363,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testLongBody' + $ref: "#/components/schemas/testLongBody" application/protobuf: schema: - $ref: '#/components/schemas/testLongBody' + $ref: "#/components/schemas/testLongBody" text/plain: schema: - $ref: '#/components/schemas/testLongBody' + $ref: "#/components/schemas/testLongBody" x-name: testLongBody responses: "200": @@ -381,13 +381,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testMapBody' + $ref: "#/components/schemas/testMapBody" application/protobuf: schema: - $ref: '#/components/schemas/testMapBody' + $ref: "#/components/schemas/testMapBody" text/plain: schema: - $ref: '#/components/schemas/testMapBody' + $ref: "#/components/schemas/testMapBody" x-name: testMapBody responses: "200": @@ -399,13 +399,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testMapListBody' + $ref: "#/components/schemas/testMapListBody" application/protobuf: schema: - $ref: '#/components/schemas/testMapListBody' + $ref: "#/components/schemas/testMapListBody" text/plain: schema: - $ref: '#/components/schemas/testMapListBody' + $ref: "#/components/schemas/testMapListBody" x-name: testMapListBody responses: "200": @@ -417,13 +417,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testMultiParamBody' + $ref: "#/components/schemas/testMultiParamBody" application/protobuf: schema: - $ref: '#/components/schemas/testMultiParamBody' + $ref: "#/components/schemas/testMultiParamBody" text/plain: schema: - $ref: '#/components/schemas/testMultiParamBody' + $ref: "#/components/schemas/testMultiParamBody" x-name: testMultiParamBody responses: "200": @@ -451,13 +451,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/protobuf: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" text/plain: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" x-name: user responses: "200": @@ -469,13 +469,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testOneEnumBody' + $ref: "#/components/schemas/testOneEnumBody" application/protobuf: schema: - $ref: '#/components/schemas/testOneEnumBody' + $ref: "#/components/schemas/testOneEnumBody" text/plain: schema: - $ref: '#/components/schemas/testOneEnumBody' + $ref: "#/components/schemas/testOneEnumBody" x-name: testOneEnumBody responses: "200": @@ -516,13 +516,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testSetBody' + $ref: "#/components/schemas/testSetBody" application/protobuf: schema: - $ref: '#/components/schemas/testSetBody' + $ref: "#/components/schemas/testSetBody" text/plain: schema: - $ref: '#/components/schemas/testSetBody' + $ref: "#/components/schemas/testSetBody" x-name: testSetBody responses: "200": @@ -534,13 +534,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testShortBody' + $ref: "#/components/schemas/testShortBody" application/protobuf: schema: - $ref: '#/components/schemas/testShortBody' + $ref: "#/components/schemas/testShortBody" text/plain: schema: - $ref: '#/components/schemas/testShortBody' + $ref: "#/components/schemas/testShortBody" x-name: testShortBody responses: "200": @@ -552,13 +552,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testStringBody' + $ref: "#/components/schemas/testStringBody" application/protobuf: schema: - $ref: '#/components/schemas/testStringBody' + $ref: "#/components/schemas/testStringBody" text/plain: schema: - $ref: '#/components/schemas/testStringBody' + $ref: "#/components/schemas/testStringBody" x-name: testStringBody responses: "200": @@ -570,13 +570,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testbooleanBody' + $ref: "#/components/schemas/testbooleanBody" application/protobuf: schema: - $ref: '#/components/schemas/testbooleanBody' + $ref: "#/components/schemas/testbooleanBody" text/plain: schema: - $ref: '#/components/schemas/testbooleanBody' + $ref: "#/components/schemas/testbooleanBody" x-name: testbooleanBody responses: "200": @@ -588,13 +588,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testbyteBody' + $ref: "#/components/schemas/testbyteBody" application/protobuf: schema: - $ref: '#/components/schemas/testbyteBody' + $ref: "#/components/schemas/testbyteBody" text/plain: schema: - $ref: '#/components/schemas/testbyteBody' + $ref: "#/components/schemas/testbyteBody" x-name: testbyteBody responses: "200": @@ -606,13 +606,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testbytesBody' + $ref: "#/components/schemas/testbytesBody" application/protobuf: schema: - $ref: '#/components/schemas/testbytesBody' + $ref: "#/components/schemas/testbytesBody" text/plain: schema: - $ref: '#/components/schemas/testbytesBody' + $ref: "#/components/schemas/testbytesBody" x-name: testbytesBody responses: "200": @@ -624,13 +624,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testcharBody' + $ref: "#/components/schemas/testcharBody" application/protobuf: schema: - $ref: '#/components/schemas/testcharBody' + $ref: "#/components/schemas/testcharBody" text/plain: schema: - $ref: '#/components/schemas/testcharBody' + $ref: "#/components/schemas/testcharBody" x-name: testcharBody responses: "200": @@ -642,13 +642,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testdoubleBody' + $ref: "#/components/schemas/testdoubleBody" application/protobuf: schema: - $ref: '#/components/schemas/testdoubleBody' + $ref: "#/components/schemas/testdoubleBody" text/plain: schema: - $ref: '#/components/schemas/testdoubleBody' + $ref: "#/components/schemas/testdoubleBody" x-name: testdoubleBody responses: "200": @@ -660,13 +660,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testfloatBody' + $ref: "#/components/schemas/testfloatBody" application/protobuf: schema: - $ref: '#/components/schemas/testfloatBody' + $ref: "#/components/schemas/testfloatBody" text/plain: schema: - $ref: '#/components/schemas/testfloatBody' + $ref: "#/components/schemas/testfloatBody" x-name: testfloatBody responses: "200": @@ -678,13 +678,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testintBody' + $ref: "#/components/schemas/testintBody" application/protobuf: schema: - $ref: '#/components/schemas/testintBody' + $ref: "#/components/schemas/testintBody" text/plain: schema: - $ref: '#/components/schemas/testintBody' + $ref: "#/components/schemas/testintBody" x-name: testintBody responses: "200": @@ -696,13 +696,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testlongBody' + $ref: "#/components/schemas/testlongBody" application/protobuf: schema: - $ref: '#/components/schemas/testlongBody' + $ref: "#/components/schemas/testlongBody" text/plain: schema: - $ref: '#/components/schemas/testlongBody' + $ref: "#/components/schemas/testlongBody" x-name: testlongBody responses: "200": @@ -714,13 +714,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testshortBody' + $ref: "#/components/schemas/testshortBody" application/protobuf: schema: - $ref: '#/components/schemas/testshortBody' + $ref: "#/components/schemas/testshortBody" text/plain: schema: - $ref: '#/components/schemas/testshortBody' + $ref: "#/components/schemas/testshortBody" x-name: testshortBody responses: "200": @@ -732,13 +732,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/wrapToBodyWithDescBody' + $ref: "#/components/schemas/wrapToBodyWithDescBody" application/protobuf: schema: - $ref: '#/components/schemas/wrapToBodyWithDescBody' + $ref: "#/components/schemas/wrapToBodyWithDescBody" text/plain: schema: - $ref: '#/components/schemas/wrapToBodyWithDescBody' + $ref: "#/components/schemas/wrapToBodyWithDescBody" x-name: wrapToBodyWithDescBody responses: "200": @@ -766,7 +766,7 @@ components: list: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" x-java-class: org.apache.servicecomb.swagger.generator.core.schema.AllType User: type: object @@ -776,7 +776,7 @@ components: friends: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User testArrayBody: type: object @@ -846,7 +846,7 @@ components: value: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" testLongBody: type: object properties: @@ -859,7 +859,7 @@ components: value: type: object additionalProperties: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" testMapListBody: type: object properties: @@ -868,12 +868,12 @@ components: additionalProperties: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" testMultiParamBody: type: object properties: obj: - $ref: '#/components/schemas/AllType' + $ref: "#/components/schemas/AllType" bValue: type: boolean byteValue: @@ -919,11 +919,11 @@ components: list: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" map: type: object additionalProperties: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" testOneEnumBody: type: object properties: @@ -1009,4 +1009,4 @@ components: properties: desc: type: integer - format: int32 \ No newline at end of file + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/allMethod.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/allMethod.yaml index 83bf77bb461..809dc9ccec8 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/allMethod.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/allMethod.yaml @@ -847,4 +847,4 @@ components: properties: desc: type: integer - format: int32 \ No newline at end of file + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/allType.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/allType.yaml index 1961677269d..b5b5102ae2a 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/allType.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/allType.yaml @@ -48,4 +48,4 @@ components: friends: type: array items: - $ref: '#/components/schemas/User' \ No newline at end of file + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiOperation.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiOperation.yaml index 3a5e6496af3..d2262b48918 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiOperation.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiOperation.yaml @@ -40,4 +40,4 @@ paths: "200": description: response of 200 content: - application/json: {} \ No newline at end of file + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiResponse.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiResponse.yaml index 3161826aefc..e4b523415d7 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiResponse.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/apiResponse.yaml @@ -37,4 +37,4 @@ paths: description: "" format: "" content: - application/json: {} \ No newline at end of file + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/array.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/array.yaml index a46cdbd6a6c..ed2755cde79 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/array.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/array.yaml @@ -44,4 +44,4 @@ components: value: type: array items: - type: string \ No newline at end of file + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/boolean.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/boolean.yaml index 743549d5f98..d2dd735f18c 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/boolean.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/boolean.yaml @@ -43,4 +43,4 @@ components: type: object properties: value: - type: boolean \ No newline at end of file + type: boolean diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/booleanObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/booleanObject.yaml index a4345f09d39..f31f4e6fbe8 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/booleanObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/booleanObject.yaml @@ -42,4 +42,4 @@ components: type: object properties: value: - type: boolean \ No newline at end of file + type: boolean diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/byte.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/byte.yaml index 0b7957741f1..c12584a8cda 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/byte.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/byte.yaml @@ -43,4 +43,4 @@ components: properties: value: type: string - format: byte \ No newline at end of file + format: byte diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/byteObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/byteObject.yaml index b292b91dea5..3e57a34434a 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/byteObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/byteObject.yaml @@ -43,4 +43,4 @@ components: properties: value: type: string - format: byte \ No newline at end of file + format: byte diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytes.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytes.yaml index fc8d3fc82ac..76180f8753c 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytes.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytes.yaml @@ -45,4 +45,4 @@ components: type: array items: type: string - format: byte \ No newline at end of file + format: byte diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytesObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytesObject.yaml index 40327664f76..ed55ed0ca2a 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytesObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/bytesObject.yaml @@ -45,4 +45,4 @@ components: type: array items: type: string - format: byte \ No newline at end of file + format: byte diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/char.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/char.yaml index 86b191d6375..5e1ce2a0132 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/char.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/char.yaml @@ -42,4 +42,4 @@ components: type: object properties: value: - type: string \ No newline at end of file + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/charObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/charObject.yaml index 1daed195e72..22e2ced3400 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/charObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/charObject.yaml @@ -42,4 +42,4 @@ components: type: object properties: value: - type: string \ No newline at end of file + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/completableFuture.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/completableFuture.yaml index 9167bc73bfd..449ec2cda52 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/completableFuture.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/completableFuture.yaml @@ -34,4 +34,4 @@ paths: schema: type: string components: - schemas: {} \ No newline at end of file + schemas: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/date.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/date.yaml index f3d27b04778..52572264ec5 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/date.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/date.yaml @@ -35,4 +35,4 @@ paths: type: string format: date-time components: - schemas: {} \ No newline at end of file + schemas: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/double.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/double.yaml index ddb2380fce7..65584bbf279 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/double.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/double.yaml @@ -43,4 +43,4 @@ components: properties: value: type: number - format: double \ No newline at end of file + format: double diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/doubleObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/doubleObject.yaml index e7a098c3783..e9ff78b1434 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/doubleObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/doubleObject.yaml @@ -43,4 +43,4 @@ components: properties: value: type: number - format: double \ No newline at end of file + format: double diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/enum.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/enum.yaml index b026b73141e..b7a6d5248e5 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/enum.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/enum.yaml @@ -52,4 +52,4 @@ components: enum: - RED - YELLOW - - BLUE \ No newline at end of file + - BLUE diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/float.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/float.yaml index 85124d3e622..6839634c7c9 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/float.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/float.yaml @@ -43,4 +43,4 @@ components: properties: value: type: number - format: float \ No newline at end of file + format: float diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/floatObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/floatObject.yaml index b892a4c3d11..112958f2281 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/floatObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/floatObject.yaml @@ -43,4 +43,4 @@ components: properties: value: type: number - format: float \ No newline at end of file + format: float diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/ignoreRequest.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/ignoreRequest.yaml index 4738a68d972..091b0168add 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/ignoreRequest.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/ignoreRequest.yaml @@ -43,4 +43,4 @@ components: properties: value: type: integer - format: int32 \ No newline at end of file + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/int.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/int.yaml index 30db886cc24..19bd7678928 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/int.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/int.yaml @@ -43,4 +43,4 @@ components: properties: value: type: integer - format: int32 \ No newline at end of file + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/intObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/intObject.yaml index 586b01defbe..da11ea13e2e 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/intObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/intObject.yaml @@ -43,4 +43,4 @@ components: properties: value: type: integer - format: int32 \ No newline at end of file + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/list.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/list.yaml index 28bf27cc5f0..e53bdd0ed49 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/list.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/list.yaml @@ -53,4 +53,4 @@ components: value: type: array items: - $ref: '#/components/schemas/User' \ No newline at end of file + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/long.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/long.yaml index 6830ae7cef4..17f716eecac 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/long.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/long.yaml @@ -43,4 +43,4 @@ components: properties: value: type: integer - format: int64 \ No newline at end of file + format: int64 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/longObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/longObject.yaml index 4c0a21f4b24..9916d768c52 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/longObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/longObject.yaml @@ -43,4 +43,4 @@ components: properties: value: type: integer - format: int64 \ No newline at end of file + format: int64 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/map.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/map.yaml index 224e3a7851c..918305a1b2c 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/map.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/map.yaml @@ -53,4 +53,4 @@ components: value: type: object additionalProperties: - $ref: '#/components/schemas/User' \ No newline at end of file + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/mapList.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/mapList.yaml index 223530e0e94..17cc9c53029 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/mapList.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/mapList.yaml @@ -55,4 +55,4 @@ components: additionalProperties: type: array items: - $ref: '#/components/schemas/User' \ No newline at end of file + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/multiParam.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/multiParam.yaml index 9ad5e8c00f8..79f69bcdbd7 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/multiParam.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/multiParam.yaml @@ -114,4 +114,4 @@ components: map: type: object additionalProperties: - $ref: '#/components/schemas/User' \ No newline at end of file + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/nestedListString.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/nestedListString.yaml index 1430a10e044..a4f39dc9ccf 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/nestedListString.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/nestedListString.yaml @@ -52,4 +52,4 @@ components: items: type: array items: - type: string \ No newline at end of file + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/object.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/object.yaml index dd0d0713b23..b64684d360c 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/object.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/object.yaml @@ -41,4 +41,4 @@ components: friends: type: array items: - $ref: '#/components/schemas/User' \ No newline at end of file + $ref: '#/components/schemas/User' diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/oneEnum.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/oneEnum.yaml index d0067f03476..e678748c974 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/oneEnum.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/oneEnum.yaml @@ -46,4 +46,4 @@ components: enum: - RED - YELLOW - - BLUE \ No newline at end of file + - BLUE diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/part.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/part.yaml index 9c945ccea45..dcc3438e6cf 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/part.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/part.yaml @@ -36,4 +36,4 @@ paths: "200": description: response of 200 content: - application/json: {} \ No newline at end of file + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/partArray.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/partArray.yaml index 78ae64f2ff5..b308b794694 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/partArray.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/partArray.yaml @@ -38,4 +38,4 @@ paths: "200": description: response of 200 content: - application/json: {} \ No newline at end of file + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/partList.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/partList.yaml index 3cfddcf6040..359f1f00341 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/partList.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/partList.yaml @@ -38,4 +38,4 @@ paths: "200": description: response of 200 content: - application/json: {} \ No newline at end of file + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/pojoExample1.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/pojoExample1.yaml index 1ba1c76fca3..9dcf23ee148 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/pojoExample1.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/pojoExample1.yaml @@ -30,13 +30,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/testMultiParameterBody' + $ref: "#/components/schemas/testMultiParameterBody" application/protobuf: schema: - $ref: '#/components/schemas/testMultiParameterBody' + $ref: "#/components/schemas/testMultiParameterBody" text/plain: schema: - $ref: '#/components/schemas/testMultiParameterBody' + $ref: "#/components/schemas/testMultiParameterBody" x-name: testMultiParameterBody responses: "200": @@ -44,13 +44,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/XXX' + $ref: "#/components/schemas/XXX" application/protobuf: schema: - $ref: '#/components/schemas/XXX' + $ref: "#/components/schemas/XXX" text/plain: schema: - $ref: '#/components/schemas/XXX' + $ref: "#/components/schemas/XXX" /testOneParameter: post: operationId: testOneParameter @@ -58,13 +58,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/XXX' + $ref: "#/components/schemas/XXX" application/protobuf: schema: - $ref: '#/components/schemas/XXX' + $ref: "#/components/schemas/XXX" text/plain: schema: - $ref: '#/components/schemas/XXX' + $ref: "#/components/schemas/XXX" x-name: testType1 responses: "200": @@ -72,13 +72,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/XXX' + $ref: "#/components/schemas/XXX" application/protobuf: schema: - $ref: '#/components/schemas/XXX' + $ref: "#/components/schemas/XXX" text/plain: schema: - $ref: '#/components/schemas/XXX' + $ref: "#/components/schemas/XXX" components: schemas: XXX: @@ -92,6 +92,6 @@ components: type: object properties: testType1: - $ref: '#/components/schemas/XXX' + $ref: "#/components/schemas/XXX" testString: - type: string \ No newline at end of file + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/responseHeader.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/responseHeader.yaml index b053ac91ecb..a6c0498f54e 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/responseHeader.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/responseHeader.yaml @@ -40,4 +40,4 @@ paths: "200": description: response of 200 content: - application/json: {} \ No newline at end of file + application/json: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/set.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/set.yaml index 74a6dc5116b..b1286c94782 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/set.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/set.yaml @@ -45,4 +45,4 @@ components: uniqueItems: true type: array items: - type: string \ No newline at end of file + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/short.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/short.yaml index 38ee8a4f26e..794f5988caa 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/short.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/short.yaml @@ -43,4 +43,4 @@ components: properties: value: type: integer - format: int32 \ No newline at end of file + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/shortObject.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/shortObject.yaml index 73ad8219441..9fe72b7a15f 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/shortObject.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/shortObject.yaml @@ -43,4 +43,4 @@ components: properties: value: type: integer - format: int32 \ No newline at end of file + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/string.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/string.yaml index 34d042215c6..4a6fc326df8 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/string.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/string.yaml @@ -42,4 +42,4 @@ components: type: object properties: value: - type: string \ No newline at end of file + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/testCompletableFutureOptional.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/testCompletableFutureOptional.yaml index 3ab7fa8cf52..f2cbe41c1df 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/testCompletableFutureOptional.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/testCompletableFutureOptional.yaml @@ -34,4 +34,4 @@ paths: schema: type: string components: - schemas: {} \ No newline at end of file + schemas: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/testOptional.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/testOptional.yaml index ac7bc2d9514..dcad961f2a9 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/testOptional.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/testOptional.yaml @@ -34,4 +34,4 @@ paths: schema: type: string components: - schemas: {} \ No newline at end of file + schemas: {} diff --git a/swagger/swagger-generator/generator-core/src/test/resources/schemas/wrapToBodyWithDesc.yaml b/swagger/swagger-generator/generator-core/src/test/resources/schemas/wrapToBodyWithDesc.yaml index 3819ab98299..7897158842a 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/schemas/wrapToBodyWithDesc.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/schemas/wrapToBodyWithDesc.yaml @@ -43,4 +43,4 @@ components: properties: desc: type: integer - format: int32 \ No newline at end of file + format: int32 diff --git a/swagger/swagger-generator/generator-core/src/test/resources/swagger1.yaml b/swagger/swagger-generator/generator-core/src/test/resources/swagger1.yaml index ef32763e948..add83d161c5 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/swagger1.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/swagger1.yaml @@ -36,4 +36,4 @@ paths: "200": description: say hello schema: - type: string \ No newline at end of file + type: string diff --git a/swagger/swagger-generator/generator-core/src/test/resources/swagger2.yaml b/swagger/swagger-generator/generator-core/src/test/resources/swagger2.yaml index 329ec357999..628b6582558 100644 --- a/swagger/swagger-generator/generator-core/src/test/resources/swagger2.yaml +++ b/swagger/swagger-generator/generator-core/src/test/resources/swagger2.yaml @@ -37,4 +37,4 @@ paths: "200": description: say hello schema: - type: string \ No newline at end of file + type: string diff --git a/swagger/swagger-generator/generator-jaxrs/pom.xml b/swagger/swagger-generator/generator-jaxrs/pom.xml index b60f1fa40b2..1a82b059f8a 100644 --- a/swagger/swagger-generator/generator-jaxrs/pom.xml +++ b/swagger/swagger-generator/generator-jaxrs/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb swagger-generator - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-generator-jaxrs Java Chassis::Swagger::Generator::Jaxrs @@ -45,10 +45,5 @@ org.apache.servicecomb foundation-test-scaffolding - - javax.xml.bind - jaxb-api - test - diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor index d65e2e62826..647fe860ace 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor +++ b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor @@ -22,4 +22,4 @@ org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.GetMethodAnn org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PostMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PutMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.DeleteMethodAnnotationProcessor -org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PatchMethodAnnotationProcessor \ No newline at end of file +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PatchMethodAnnotationProcessor diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor index 85ed0342426..e20efd576e7 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor +++ b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ParameterAnnotationProcessor @@ -19,4 +19,4 @@ org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.PathParamPar org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.QueryParamParameterAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.HeaderParamParameterAnnotationProcessor org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.CookieParamParameterAnnotationProcessor -org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.FormParamParameterAnnotationProcessor \ No newline at end of file +org.apache.servicecomb.swagger.generator.jaxrs.processor.annotation.FormParamParameterAnnotationProcessor diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor index 254f2dcf096..137ee9359bc 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor +++ b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.generator.jaxrs.processor.response.JaxrsResponseProcessor \ No newline at end of file +org.apache.servicecomb.swagger.generator.jaxrs.processor.response.JaxrsResponseProcessor diff --git a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory index d79d9d3e11d..c96c1805872 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory +++ b/swagger/swagger-generator/generator-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerGeneratorFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.generator.jaxrs.JaxrsSwaggerGeneratorFactory \ No newline at end of file +org.apache.servicecomb.swagger.generator.jaxrs.JaxrsSwaggerGeneratorFactory diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/Echo.java b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/Echo.java index 2bae2974c36..68820b8c6b9 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/Echo.java +++ b/swagger/swagger-generator/generator-jaxrs/src/test/java/org/apache/servicecomb/swagger/generator/jaxrs/Echo.java @@ -102,8 +102,8 @@ public String query(@QueryParam(value = "query") String query) { @Path(value = "queryComplex") @GET - public String queryComplex(@QueryParam(value = "querys") List querys) { - return String.format("%s", querys); + public String queryComplex(@QueryParam(value = "queries") List queries) { + return String.format("%s", queries); } @Operation(summary = "") diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/ClassAnnotation.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/ClassAnnotation.yaml index 46b0d244948..5f8df8369d6 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/ClassAnnotation.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/ClassAnnotation.yaml @@ -30,7 +30,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" x-name: user responses: "200": @@ -38,10 +38,10 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" text/plain: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" /testForm: post: operationId: testForm @@ -115,5 +115,5 @@ components: friends: type: array items: - $ref: '#/components/schemas/User' - x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User \ No newline at end of file + $ref: "#/components/schemas/User" + x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/FullSwaggerService.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/FullSwaggerService.yaml index 3e7cfa134a8..39ae7960507 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/FullSwaggerService.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/FullSwaggerService.yaml @@ -138,4 +138,4 @@ paths: text/plain: schema: type: string -components: {} \ No newline at end of file +components: {} diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/consumes.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/consumes.yaml index 9cb7f9377c7..8da5da55a28 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/consumes.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/consumes.yaml @@ -87,4 +87,4 @@ components: type: object properties: input: - type: string \ No newline at end of file + type: string diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/dynamicStatusEnum.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/dynamicStatusEnum.yaml index 5c3d0753b38..c3c5263661d 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/dynamicStatusEnum.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/dynamicStatusEnum.yaml @@ -62,4 +62,4 @@ components: properties: status: type: integer - format: int32 \ No newline at end of file + format: int32 diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/echo.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/echo.yaml index d5227f9169a..7e0fe3b59c4 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/echo.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/echo.yaml @@ -78,7 +78,7 @@ paths: - name: q in: query schema: - $ref: '#/components/schemas/AggregatedParam' + $ref: "#/components/schemas/AggregatedParam" responses: "200": description: response of 200 @@ -89,7 +89,7 @@ paths: - name: h in: header schema: - $ref: '#/components/schemas/AggregatedParam' + $ref: "#/components/schemas/AggregatedParam" responses: "200": description: response of 200 @@ -188,13 +188,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/DynamicStatusModel' + $ref: "#/components/schemas/DynamicStatusModel" application/protobuf: schema: - $ref: '#/components/schemas/DynamicStatusModel' + $ref: "#/components/schemas/DynamicStatusModel" text/plain: schema: - $ref: '#/components/schemas/DynamicStatusModel' + $ref: "#/components/schemas/DynamicStatusModel" x-name: model responses: "200": @@ -233,13 +233,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/protobuf: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" text/plain: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" x-name: srcUser responses: "200": @@ -340,13 +340,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/JdkStatusModel' + $ref: "#/components/schemas/JdkStatusModel" application/protobuf: schema: - $ref: '#/components/schemas/JdkStatusModel' + $ref: "#/components/schemas/JdkStatusModel" text/plain: schema: - $ref: '#/components/schemas/JdkStatusModel' + $ref: "#/components/schemas/JdkStatusModel" x-name: model responses: "200": @@ -453,12 +453,12 @@ paths: get: operationId: queryComplex parameters: - - name: querys + - name: queries in: query schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" responses: "200": description: response of 200 @@ -550,7 +550,7 @@ components: friends: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User JdkStatusModel: type: object @@ -560,4 +560,4 @@ components: enum: - BAD_REQUEST - NOT_FOUND - x-java-class: org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatusModel \ No newline at end of file + x-java-class: org.apache.servicecomb.swagger.generator.jaxrs.model.enums.JdkStatusModel diff --git a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/nestedListString.yaml b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/nestedListString.yaml index 10800782089..338ccdb8d67 100644 --- a/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/nestedListString.yaml +++ b/swagger/swagger-generator/generator-jaxrs/src/test/resources/schemas/nestedListString.yaml @@ -47,4 +47,4 @@ paths: items: type: string components: - schemas: {} \ No newline at end of file + schemas: {} diff --git a/swagger/swagger-generator/generator-spring-data/pom.xml b/swagger/swagger-generator/generator-spring-data/pom.xml index 7574648ca8e..13c8923cd57 100644 --- a/swagger/swagger-generator/generator-spring-data/pom.xml +++ b/swagger/swagger-generator/generator-spring-data/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb swagger-generator - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -38,10 +38,5 @@ org.springframework.data spring-data-commons - - javax.xml.bind - jaxb-api - test - diff --git a/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module b/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module index 4b2db1ae4c3..161ee4f9e12 100644 --- a/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module +++ b/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/com.fasterxml.jackson.databind.Module @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.generator.springdata.SpringDataModule \ No newline at end of file +org.apache.servicecomb.swagger.generator.springdata.SpringDataModule diff --git a/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.ConcreteTypeRegister b/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.ConcreteTypeRegister index 784d7a0b415..42f7ef4bb1a 100644 --- a/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.ConcreteTypeRegister +++ b/swagger/swagger-generator/generator-spring-data/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.ConcreteTypeRegister @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.generator.springdata.SpringDataConcreteTypeRegister \ No newline at end of file +org.apache.servicecomb.swagger.generator.springdata.SpringDataConcreteTypeRegister diff --git a/swagger/swagger-generator/generator-spring-data/src/test/resources/pageSchema.yaml b/swagger/swagger-generator/generator-spring-data/src/test/resources/pageSchema.yaml index 8b9b4b18807..255ccceaca7 100644 --- a/swagger/swagger-generator/generator-spring-data/src/test/resources/pageSchema.yaml +++ b/swagger/swagger-generator/generator-spring-data/src/test/resources/pageSchema.yaml @@ -30,13 +30,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PageString' + $ref: "#/components/schemas/PageString" application/protobuf: schema: - $ref: '#/components/schemas/PageString' + $ref: "#/components/schemas/PageString" text/plain: schema: - $ref: '#/components/schemas/PageString' + $ref: "#/components/schemas/PageString" x-name: page responses: "200": @@ -44,13 +44,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/PageString' + $ref: "#/components/schemas/PageString" application/protobuf: schema: - $ref: '#/components/schemas/PageString' + $ref: "#/components/schemas/PageString" text/plain: schema: - $ref: '#/components/schemas/PageString' + $ref: "#/components/schemas/PageString" components: schemas: PageString: @@ -73,12 +73,12 @@ components: type: integer format: int32 pageable: - $ref: '#/components/schemas/Pageable' + $ref: "#/components/schemas/Pageable" size: type: integer format: int32 sort: - $ref: '#/components/schemas/Sort' + $ref: "#/components/schemas/Sort" totalElements: type: integer format: int64 @@ -101,7 +101,7 @@ components: paged: type: boolean sort: - $ref: '#/components/schemas/Sort' + $ref: "#/components/schemas/Sort" unpaged: type: boolean x-java-class: org.springframework.data.domain.Pageable @@ -114,4 +114,4 @@ components: type: boolean unsorted: type: boolean - x-java-class: org.springframework.data.domain.Sort \ No newline at end of file + x-java-class: org.springframework.data.domain.Sort diff --git a/swagger/swagger-generator/generator-springmvc/pom.xml b/swagger/swagger-generator/generator-springmvc/pom.xml index 1eb304a9acc..c4225f11a96 100644 --- a/swagger/swagger-generator/generator-springmvc/pom.xml +++ b/swagger/swagger-generator/generator-springmvc/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb swagger-generator - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-generator-springmvc Java Chassis::Swagger::Generator::Spring MVC @@ -45,18 +45,9 @@ log4j-core test - - com.google.inject - guice - org.apache.servicecomb foundation-test-scaffolding - - javax.xml.bind - jaxb-api - test - diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RestControllerClassAnnotationProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RestControllerClassAnnotationProcessor.java index 3671f2f2007..66ac987a442 100644 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RestControllerClassAnnotationProcessor.java +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/RestControllerClassAnnotationProcessor.java @@ -19,14 +19,11 @@ import java.lang.reflect.Type; -import org.apache.commons.lang3.StringUtils; import org.apache.servicecomb.swagger.SwaggerUtils; import org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor; import org.apache.servicecomb.swagger.generator.SwaggerGenerator; import org.springframework.web.bind.annotation.RestController; -import io.swagger.v3.oas.models.OpenAPI; - public class RestControllerClassAnnotationProcessor implements ClassAnnotationProcessor { @Override public Type getProcessType() { @@ -35,9 +32,8 @@ public Type getProcessType() { @Override public void process(SwaggerGenerator swaggerGenerator, RestController restController) { - OpenAPI swagger = swaggerGenerator.getOpenAPI(); - if (StringUtils.isEmpty(SwaggerUtils.getBasePath(swagger))) { - SwaggerUtils.setBasePath(swagger, "/"); + if (SwaggerUtils.getBasePath(swaggerGenerator.getOpenAPI()) == null) { + swaggerGenerator.setBasePath("/"); } } } diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/SpringmvcParameterAnnotationsProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/SpringmvcParameterAnnotationsProcessor.java index 6b908c32798..4774d5c6256 100644 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/SpringmvcParameterAnnotationsProcessor.java +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/annotation/SpringmvcParameterAnnotationsProcessor.java @@ -24,4 +24,3 @@ public abstract class SpringmvcParameterAnnotationsProcessor implements ParameterAnnotationProcessor { } - diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListProcessor.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListProcessor.java index 10e9f98d254..85d4648c126 100644 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListProcessor.java +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/processor/parameter/MultipartFileListProcessor.java @@ -19,16 +19,17 @@ import java.util.List; +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.apache.servicecomb.swagger.generator.core.processor.parameter.PartArrayParameterTypeProcessor; import org.springframework.web.multipart.MultipartFile; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.type.TypeFactory; -import com.google.inject.util.Types; public class MultipartFileListProcessor extends PartArrayParameterTypeProcessor { @Override public JavaType getProcessType() { - return TypeFactory.defaultInstance().constructType(Types.newParameterizedType(List.class, MultipartFile.class)); + return TypeFactory.defaultInstance().constructType( + ParameterizedTypeUtil.make(List.class, MultipartFile.class)); } } diff --git a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.java b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.java index fd010c175d8..6666f7813c5 100644 --- a/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.java +++ b/swagger/swagger-generator/generator-springmvc/src/main/java/org/apache/servicecomb/swagger/generator/springmvc/property/creator/MultipartFilePropertyCreator.java @@ -27,7 +27,7 @@ public class MultipartFilePropertyCreator implements PropertyCreator { private final Class[] classes = {MultipartFile.class}; @Override - public Schema createProperty() { + public Schema createProperty() { return new FileSchema(); } diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator index 97564b80680..11b81b52e9b 100644 --- a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.extend.property.creator.PropertyCreator @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.generator.springmvc.property.creator.MultipartFilePropertyCreator \ No newline at end of file +org.apache.servicecomb.swagger.generator.springmvc.property.creator.MultipartFilePropertyCreator diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor index c5237198ef5..e7d511d2796 100644 --- a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ClassAnnotationProcessor @@ -16,4 +16,4 @@ # org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RestControllerClassAnnotationProcessor -org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestMappingClassAnnotationProcessor \ No newline at end of file +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.RequestMappingClassAnnotationProcessor diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor index f93824e51f1..21a0a455679 100644 --- a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.MethodAnnotationProcessor @@ -20,4 +20,4 @@ org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.GetMappi org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.DeleteMappingMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PostMappingMethodAnnotationProcessor org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PutMappingMethodAnnotationProcessor -org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PatchMappingMethodAnnotationProcessor \ No newline at end of file +org.apache.servicecomb.swagger.generator.springmvc.processor.annotation.PatchMappingMethodAnnotationProcessor diff --git a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor index 78548c4b806..91110b15c86 100644 --- a/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor +++ b/swagger/swagger-generator/generator-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.generator.springmvc.processor.response.ResponseEntityProcessor \ No newline at end of file +org.apache.servicecomb.swagger.generator.springmvc.processor.response.ResponseEntityProcessor diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestSpringmvc.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestSpringmvc.java index db100a79c54..a0bb9b996c7 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestSpringmvc.java +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestSpringmvc.java @@ -19,6 +19,7 @@ import org.apache.servicecomb.swagger.generator.core.unittest.UnitTestSwaggerUtils; import org.apache.servicecomb.swagger.generator.springmvc.model.DefaultParameterSchema; +import org.apache.servicecomb.swagger.generator.springmvc.model.RestControllerWithPathSchema; import org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget; import org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget_ValueOverWritePath; import org.junit.jupiter.api.Test; @@ -62,7 +63,7 @@ public void testClassMethodNoPath() { } @Test - public void testClassMethodNoHttpMetod() { + public void testClassMethodNoHttpMethod() { UnitTestSwaggerUtils .testSwagger("schemas/requestMappingHttpMethod.yaml", ClassMethodNoHttpMethod.class, "noHttpMethod"); } @@ -136,6 +137,11 @@ public void testDefaultParameterSchema() { UnitTestSwaggerUtils.testSwagger("schemas/DefaultParameterSchema.yaml", DefaultParameterSchema.class); } + @Test + public void testRestControllerWithPathSchema() { + UnitTestSwaggerUtils.testSwagger("schemas/RestControllerWithPathSchema.yaml", RestControllerWithPathSchema.class); + } + @Test public void swaggerTestTarget() { UnitTestSwaggerUtils.testSwagger("schemas/swaggerTestTarget.yaml", SwaggerTestTarget.class); diff --git a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestTwoSameNameModels.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestTwoSameNameModels.java index fdb26fa0a7d..5bcb3b3fda6 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestTwoSameNameModels.java +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/TestTwoSameNameModels.java @@ -44,7 +44,7 @@ public class TestTwoSameNameModels { content: application/json: schema: - $ref: '#/components/schemas/GenericSameModel' + $ref: "#/components/schemas/GenericSameModel" required: true x-name: param responses: @@ -53,7 +53,7 @@ public class TestTwoSameNameModels { content: application/json: schema: - $ref: '#/components/schemas/GenericSameModel' + $ref: "#/components/schemas/GenericSameModel" /same: post: operationId: service @@ -61,7 +61,7 @@ public class TestTwoSameNameModels { content: application/json: schema: - $ref: '#/components/schemas/SameModel' + $ref: "#/components/schemas/SameModel" required: true x-name: param responses: @@ -70,14 +70,14 @@ public class TestTwoSameNameModels { content: application/json: schema: - $ref: '#/components/schemas/SameModel' + $ref: "#/components/schemas/SameModel" components: schemas: GenericSameModel: type: object properties: data: - $ref: '#/components/schemas/SameModel' + $ref: "#/components/schemas/SameModel" x-java-class: org.apache.servicecomb.swagger.generator.springmvc.model.Generic SameModel: type: object diff --git a/core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorFinishEvent.java b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/RestControllerWithPathSchema.java similarity index 64% rename from core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorFinishEvent.java rename to swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/RestControllerWithPathSchema.java index 73ee6cd6dae..c6553ba8c15 100644 --- a/core/src/main/java/org/apache/servicecomb/core/event/InvocationRunInExecutorFinishEvent.java +++ b/swagger/swagger-generator/generator-springmvc/src/test/java/org/apache/servicecomb/swagger/generator/springmvc/model/RestControllerWithPathSchema.java @@ -14,15 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.servicecomb.core.event; +package org.apache.servicecomb.swagger.generator.springmvc.model; -import org.apache.servicecomb.core.Invocation; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; -/** - * for async invocation, only indicate finished in executor, not finished invocation - */ -public class InvocationRunInExecutorFinishEvent extends InvocationBaseEvent { - public InvocationRunInExecutorFinishEvent(Invocation invocation) { - super(invocation); +@RestController +@RequestMapping("/prefix") +public class RestControllerWithPathSchema { + @GetMapping("/testSimpleParam") + public String testSimpleParam(String strParam) { + return strParam; } } diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DefaultParameterSchema.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DefaultParameterSchema.yaml index 202825a9525..7ba6ea4d9dc 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DefaultParameterSchema.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DefaultParameterSchema.yaml @@ -138,7 +138,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/TestParam' + $ref: "#/components/schemas/TestParam" - name: stringMap in: query schema: @@ -168,4 +168,4 @@ components: age: type: integer format: int32 - x-java-class: org.apache.servicecomb.swagger.generator.springmvc.model.TestParam \ No newline at end of file + x-java-class: org.apache.servicecomb.swagger.generator.springmvc.model.TestParam diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DemoRest.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DemoRest.yaml index 086a96cf713..9656cc47a5c 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DemoRest.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/DemoRest.yaml @@ -61,4 +61,4 @@ paths: responses: "200": description: response of 200 -components: {} \ No newline at end of file +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/RestControllerWithPathSchema.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/RestControllerWithPathSchema.yaml new file mode 100644 index 00000000000..20786b5d695 --- /dev/null +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/RestControllerWithPathSchema.yaml @@ -0,0 +1,47 @@ +## --------------------------------------------------------------------------- +## Licensed to the Apache Software Foundation (ASF) under one or more +## contributor license agreements. See the NOTICE file distributed with +## this work for additional information regarding copyright ownership. +## The ASF licenses this file to You under the Apache License, Version 2.0 +## (the "License"); you may not use this file except in compliance with +## the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## --------------------------------------------------------------------------- + +--- +openapi: 3.0.1 +info: + title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.RestControllerWithPathSchema + version: 1.0.0 +servers: +- url: /prefix +paths: + /testSimpleParam: + get: + operationId: testSimpleParam + parameters: + - name: strParam + in: query + schema: + type: string + responses: + "200": + description: response of 200 + content: + application/json: + schema: + type: string + application/protobuf: + schema: + type: string + text/plain: + schema: + type: string +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParameter.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParameter.yaml index 9ebc918def5..f1003fea277 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParameter.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/defaultParameter.yaml @@ -95,4 +95,4 @@ paths: responses: "200": description: response of 200 -components: {} \ No newline at end of file +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/echo.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/echo.yaml index 2553544fca8..a2f75700c1f 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/echo.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/echo.yaml @@ -275,4 +275,4 @@ paths: text/plain: schema: type: string -components: {} \ No newline at end of file +components: {} diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/mixupAnnotations.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/mixupAnnotations.yaml index bf5794890bd..25f68d289f4 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/mixupAnnotations.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/mixupAnnotations.yaml @@ -70,13 +70,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/protobuf: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" text/plain: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" required: true x-name: user responses: @@ -365,10 +365,10 @@ paths: content: text/plain: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" required: true x-name: srcUser responses: @@ -404,10 +404,10 @@ paths: content: text/plain: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" required: true x-name: srcUser responses: @@ -443,10 +443,10 @@ paths: content: text/plain: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" required: true x-name: srcUser responses: @@ -482,10 +482,10 @@ paths: content: text/plain: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" required: true x-name: srcUser responses: @@ -521,10 +521,10 @@ paths: content: text/plain: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" required: true x-name: srcUser responses: @@ -560,10 +560,10 @@ paths: content: text/plain: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" required: true x-name: srcUser responses: @@ -586,5 +586,5 @@ components: friends: type: array items: - $ref: '#/components/schemas/User' - x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User \ No newline at end of file + $ref: "#/components/schemas/User" + x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/responseEntity.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/responseEntity.yaml index 53459a889d3..c3763d4600d 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/responseEntity.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/responseEntity.yaml @@ -34,17 +34,17 @@ paths: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" text/plain: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" /usingGetMapping: get: operationId: usingGetMapping @@ -56,17 +56,17 @@ paths: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" text/plain: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" /usingPatchMapping: patch: operationId: usingPatchMapping @@ -78,17 +78,17 @@ paths: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" text/plain: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" /usingPostMapping: post: operationId: usingPostMapping @@ -100,17 +100,17 @@ paths: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" text/plain: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" /usingPutMapping: put: operationId: usingPutMapping @@ -122,17 +122,17 @@ paths: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" text/plain: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" /usingRequestMapping: put: operationId: usingRequestMapping @@ -144,17 +144,17 @@ paths: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/protobuf: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" text/plain: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" components: schemas: User: @@ -165,5 +165,5 @@ components: friends: type: array items: - $ref: '#/components/schemas/User' - x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User \ No newline at end of file + $ref: "#/components/schemas/User" + x-java-class: org.apache.servicecomb.foundation.test.scaffolding.model.User diff --git a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget_ValueOverWritePath.yaml b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget_ValueOverWritePath.yaml index d541ba60c6b..781f06649f2 100644 --- a/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget_ValueOverWritePath.yaml +++ b/swagger/swagger-generator/generator-springmvc/src/test/resources/schemas/swaggerTestTarget_ValueOverWritePath.yaml @@ -21,7 +21,6 @@ info: title: swagger definition for org.apache.servicecomb.swagger.generator.springmvc.model.SwaggerTestTarget_ValueOverWritePath version: 1.0.0 servers: -- url: /testPath - url: /testValue paths: /: diff --git a/swagger/swagger-generator/pom.xml b/swagger/swagger-generator/pom.xml index 5f2b0465ae8..e9a03a9c579 100644 --- a/swagger/swagger-generator/pom.xml +++ b/swagger/swagger-generator/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb swagger - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-generator Java Chassis::Swagger::Generator diff --git a/swagger/swagger-invocation/invocation-core/pom.xml b/swagger/swagger-invocation/invocation-core/pom.xml index 79833f80868..20897dafd3a 100644 --- a/swagger/swagger-invocation/invocation-core/pom.xml +++ b/swagger/swagger-invocation/invocation-core/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb swagger-invocation - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-invocation-core Java Chassis::Swagger::Invocation::Core @@ -46,11 +46,6 @@ swagger-generator-springmvc test - - javax.xml.bind - jaxb-api - test - org.jmockit jmockit diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java index 4aa0e848b37..655e7a2c6a1 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerEnvironment.java @@ -34,7 +34,7 @@ import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapper; import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapperCreator; import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; @@ -54,12 +54,12 @@ public class SwaggerEnvironment { public SwaggerConsumer createConsumer(Class consumerIntf, OpenAPI swagger) { swagger = checkAndGenerateSwagger(consumerIntf, swagger); - Map, ContextArgumentMapperFactory> contextFactorys = SPIServiceUtils + Map, ContextArgumentMapperFactory> contextFactories = SPIServiceUtils .getOrLoadSortedService(ConsumerContextArgumentMapperFactory.class) .stream() .collect(Collectors.toMap(ConsumerContextArgumentMapperFactory::getContextClass, Function.identity())); - ResponseMapperFactorys consumerResponseMapperFactorys = - new ResponseMapperFactorys<>(ConsumerResponseMapperFactory.class); + ResponseMapperFactories consumerResponseMapperFactories = + new ResponseMapperFactories<>(ConsumerResponseMapperFactory.class); SwaggerOperations swaggerOperations = new SwaggerOperations(swagger); @@ -79,12 +79,12 @@ public SwaggerConsumer createConsumer(Class consumerIntf, OpenAPI swagger) { ConsumerArgumentsMapperCreator creator = new ConsumerArgumentsMapperCreator( Json.mapper().getSerializationConfig(), - contextFactorys, + contextFactories, consumerIntf, consumerMethod, swaggerOperation); ArgumentsMapper argsMapper = creator.createArgumentsMapper(); - ConsumerResponseMapper responseMapper = consumerResponseMapperFactorys + ConsumerResponseMapper responseMapper = consumerResponseMapperFactories .createResponseMapper(consumerMethod.getGenericReturnType()); SwaggerConsumerOperation op = new SwaggerConsumerOperation(); @@ -123,8 +123,8 @@ public SwaggerProducer createProducer(Object producerInstance, Class schemaIn .getOrLoadSortedService(ProducerContextArgumentMapperFactory.class) .stream() .collect(Collectors.toMap(ProducerContextArgumentMapperFactory::getContextClass, Function.identity())); - ResponseMapperFactorys producerResponseMapperFactorys = - new ResponseMapperFactorys<>(ProducerResponseMapperFactory.class); + ResponseMapperFactories producerResponseMapperFactories = + new ResponseMapperFactories<>(ProducerResponseMapperFactory.class); SwaggerOperations swaggerOperations = new SwaggerOperations(swagger); @@ -153,7 +153,7 @@ public SwaggerProducer createProducer(Object producerInstance, Class schemaIn producerMethod, swaggerOperation); ProducerArgumentsMapper argsMapper = creator.createArgumentsMapper(); - ProducerResponseMapper responseMapper = producerResponseMapperFactorys.createResponseMapper( + ProducerResponseMapper responseMapper = producerResponseMapperFactories.createResponseMapper( producerMethod.getGenericReturnType()); SwaggerProducerOperation op = new SwaggerProducerOperation(); diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducerOperation.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducerOperation.java index f87c8986804..ad4728aacff 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducerOperation.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/engine/SwaggerProducerOperation.java @@ -18,13 +18,10 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; -import java.util.List; import java.util.Map; -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; import org.apache.servicecomb.swagger.generator.core.model.SwaggerOperation; import org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerArgumentsMapper; -import org.apache.servicecomb.swagger.invocation.extension.ProducerInvokeExtension; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; public class SwaggerProducerOperation { @@ -47,9 +44,6 @@ public class SwaggerProducerOperation { private ProducerResponseMapper responseMapper; - private final List producerInvokeExtenstionList = - SPIServiceUtils.getSortedService(ProducerInvokeExtension.class); - public String getOperationId() { return swaggerOperation.getOperationId(); } @@ -111,10 +105,6 @@ public void setResponseMapper(ProducerResponseMapper responseMapper) { this.responseMapper = responseMapper; } - public List getProducerInvokeExtenstionList() { - return this.producerInvokeExtenstionList; - } - public Type getSwaggerParameterType(String name) { return this.swaggerParameterTypes.get(name); } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/InvocationType.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/InvocationType.java index e519c28f007..351c876de71 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/InvocationType.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/InvocationType.java @@ -19,5 +19,6 @@ public enum InvocationType { CONSUMER, - PROVIDER + PROVIDER, + EDGE } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/SwaggerInvocation.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/SwaggerInvocation.java index a6df86d3c4c..52386716189 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/SwaggerInvocation.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/SwaggerInvocation.java @@ -45,13 +45,4 @@ public InvocationType getInvocationType() { public String getInvocationQualifiedName() { return invocationType.name(); } - - public void onBusinessMethodStart() { - } - - public void onBusinessMethodFinish() { - } - - public void onBusinessFinish() { - } } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/AbstractArgumentsMapperCreator.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/AbstractArgumentsMapperCreator.java index 2978e9d318c..28022c898ed 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/AbstractArgumentsMapperCreator.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/AbstractArgumentsMapperCreator.java @@ -57,7 +57,7 @@ * int add(int x, int y) * swagger parameters: * x, y - * 2) swagger only one POJO paramter, extract all field to method parameters (POJO dev mode) + * 2) swagger only one POJO parameter, extract all field to method parameters (POJO dev mode) * interface method: * int add(int x, int y) * swagger parameters: @@ -96,7 +96,7 @@ public abstract class AbstractArgumentsMapperCreator { protected SerializationConfig serializationConfig; // key is context class - protected Map, ContextArgumentMapperFactory> contextFactorys; + protected Map, ContextArgumentMapperFactory> contextFactories; // consumer or producer protected Method providerMethod; @@ -117,10 +117,10 @@ public abstract class AbstractArgumentsMapperCreator { protected Set processedSwaggerParameters; public AbstractArgumentsMapperCreator(SerializationConfig serializationConfig, - Map, ContextArgumentMapperFactory> contextFactorys, Class providerClass, + Map, ContextArgumentMapperFactory> contextFactories, Class providerClass, Method providerMethod, SwaggerOperation swaggerOperation) { this.serializationConfig = serializationConfig; - this.contextFactorys = contextFactorys; + this.contextFactories = contextFactories; this.providerClass = providerClass; this.providerMethod = providerMethod; this.swaggerOperation = swaggerOperation; @@ -198,7 +198,7 @@ protected void doCreateArgumentsMapper() { * @return true means processed */ protected boolean processContextParameter(java.lang.reflect.Parameter providerParameter) { - ContextArgumentMapperFactory contextFactory = contextFactorys.get(providerParameter.getType()); + ContextArgumentMapperFactory contextFactory = contextFactories.get(providerParameter.getType()); if (contextFactory == null) { return false; } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperCreator.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperCreator.java index 47ffd68f851..84c7a6fa926 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperCreator.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/ConsumerArgumentsMapperCreator.java @@ -47,9 +47,9 @@ public class ConsumerArgumentsMapperCreator extends AbstractArgumentsMapperCreat private int unknownConsumerParams = 0; public ConsumerArgumentsMapperCreator(SerializationConfig serializationConfig, - Map, ContextArgumentMapperFactory> contextFactorys, Class consumerClass, + Map, ContextArgumentMapperFactory> contextFactories, Class consumerClass, Method consumerMethod, SwaggerOperation swaggerOperation) { - super(serializationConfig, contextFactorys, consumerClass, consumerMethod, swaggerOperation); + super(serializationConfig, contextFactories, consumerClass, consumerMethod, swaggerOperation); } private boolean isAllSameMapper() { @@ -97,7 +97,8 @@ protected void processPendingBodyParameter(RequestBody parameter) { @Override protected ArgumentMapper createKnownParameterMapper(int providerParamIdx, String invocationArgumentName) { - return new ConsumerArgumentSame(invocationArgumentName, invocationArgumentName); + return new ConsumerArgumentSame(this.providerMethod.getParameters()[providerParamIdx].getName(), + invocationArgumentName); } @Override diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperCreator.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperCreator.java index a58662ca015..9aea4bec37d 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperCreator.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/ProducerArgumentsMapperCreator.java @@ -49,9 +49,9 @@ public class ProducerArgumentsMapperCreator extends AbstractArgumentsMapperCreat private final Map swaggerParameterTypes; public ProducerArgumentsMapperCreator(SerializationConfig serializationConfig, - Map, ContextArgumentMapperFactory> contextFactorys, Class producerClass, + Map, ContextArgumentMapperFactory> contextFactories, Class producerClass, Method producerMethod, SwaggerOperation swaggerOperation) { - super(serializationConfig, contextFactorys, producerClass, producerMethod, swaggerOperation); + super(serializationConfig, contextFactories, producerClass, producerMethod, swaggerOperation); swaggerParameterTypes = new HashMap<>(); } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/codec/ArgWrapperJavaType.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/codec/ArgWrapperJavaType.java deleted file mode 100644 index 26453fd47bd..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/arguments/producer/codec/ArgWrapperJavaType.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.invocation.arguments.producer.codec; - -import java.io.IOException; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JavaType; -import com.fasterxml.jackson.databind.JsonDeserializer; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.ObjectMapperUtils; -import com.fasterxml.jackson.databind.type.SimpleType; -import com.fasterxml.jackson.databind.type.TypeFactory; - -public class ArgWrapperJavaType extends SimpleType { - private static final long serialVersionUID = 964882360361204479L; - - static class ArgInfo { - JavaType javaType; - - JsonDeserializer deserializer; - - public ArgInfo(Type type) { - this.javaType = TypeFactory.defaultInstance().constructType(type); - } - } - - private final Map argInfos = new HashMap<>(); - - public ArgWrapperJavaType() { - super(Object.class); - } - - public void addProperty(String name, Type type) { - argInfos.put(name, new ArgInfo(type)); - } - - public Map readValue(ObjectMapper mapper, String json) throws IOException { - Map args = new LinkedHashMap<>(); - - JsonParser jp = mapper.getFactory().createParser(json); - DeserializationContext deserializationContext = ObjectMapperUtils.createDeserializationContext(mapper, jp); - - jp.nextToken(); - for (String fieldName = jp.nextFieldName(); fieldName != null; fieldName = jp.nextFieldName()) { - jp.nextToken(); - ArgInfo argInfo = argInfos.get(fieldName); - if (argInfo == null) { - continue; - } - - if (argInfo.deserializer == null) { - argInfo.deserializer = deserializationContext.findRootValueDeserializer(argInfo.javaType); - } - - args.put(fieldName, argInfo.deserializer.deserialize(jp, deserializationContext)); - } - - return args; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverter.java index afceb32e250..bbdb244f58e 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverter.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartArrayConverter.java @@ -19,16 +19,15 @@ import java.lang.reflect.Type; import java.util.List; -import jakarta.servlet.http.Part; - +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.apache.servicecomb.swagger.invocation.converter.Converter; -import com.google.inject.util.Types; +import jakarta.servlet.http.Part; public class PartListToPartArrayConverter implements Converter { @Override public Type getSrcType() { - return Types.newParameterizedType(List.class, Part.class); + return ParameterizedTypeUtil.make(List.class, Part.class); } @Override diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverter.java index cc2dbee4de9..5c2ee3284ac 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverter.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/converter/impl/part/PartListToPartListConverter.java @@ -19,21 +19,20 @@ import java.lang.reflect.Type; import java.util.List; -import jakarta.servlet.http.Part; - +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.apache.servicecomb.swagger.invocation.converter.Converter; -import com.google.inject.util.Types; +import jakarta.servlet.http.Part; public class PartListToPartListConverter implements Converter { @Override public Type getSrcType() { - return Types.newParameterizedType(List.class, Part.class); + return ParameterizedTypeUtil.make(List.class, Part.class); } @Override public Type getTargetType() { - return Types.newParameterizedType(List.class, Part.class); + return ParameterizedTypeUtil.make(List.class, Part.class); } @Override diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/DefaultExceptionToProducerResponseConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/DefaultExceptionToProducerResponseConverter.java deleted file mode 100644 index 3dbd0ccccf1..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/DefaultExceptionToProducerResponseConverter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.invocation.exception; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class DefaultExceptionToProducerResponseConverter implements ExceptionToProducerResponseConverter { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultExceptionToProducerResponseConverter.class); - - @Override - public Class getExceptionClass() { - // default logic, not bind to special class - return null; - } - - @Override - public Response convert(SwaggerInvocation swaggerInvocation, Throwable e) { - LOGGER.error("invoke failed, invocation={}", swaggerInvocation.getInvocationQualifiedName(), e); - //not only producer but also consumer - return Response.failResp(swaggerInvocation.getInvocationType(), e); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionFactory.java index b6c71ac5356..a4050b2f7ec 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionFactory.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionFactory.java @@ -20,12 +20,10 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; -import jakarta.ws.rs.core.Response.StatusType; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; import org.apache.servicecomb.swagger.invocation.context.HttpStatus; +import jakarta.ws.rs.core.Response.StatusType; + public final class ExceptionFactory { // cse内置的错误 // 考虑到springmvc不支持自定义http错误码,所以只能使用“标准”错误码 @@ -46,8 +44,6 @@ public final class ExceptionFactory { public static final String CONSUMER_INNER_REASON_PHRASE = "Unexpected consumer error, please check logs for details"; - private static final ExceptionToProducerResponseConverters exceptionToProducerResponseConverters = new ExceptionToProducerResponseConverters(); - public static final StatusType CONSUMER_INNER_STATUS = new HttpStatus(CONSUMER_INNER_STATUS_CODE, CONSUMER_INNER_REASON_PHRASE); @@ -56,7 +52,7 @@ private ExceptionFactory() { public static InvocationException create(StatusType status, Object exceptionOrErrorData) { - if (InvocationException.class.isInstance(exceptionOrErrorData)) { + if (exceptionOrErrorData instanceof InvocationException) { return (InvocationException) exceptionOrErrorData; } @@ -72,12 +68,12 @@ public static InvocationException createProducerException(Object errorData) { } private static InvocationException doCreate(StatusType status, - Object errorData) { + Object errorData) { return new InvocationException(status, errorData); } private static InvocationException doCreate(int statusCode, String reasonPhrase, CommonExceptionData data, - Throwable e) { + Throwable e) { return new InvocationException(statusCode, reasonPhrase, data, e); } @@ -113,7 +109,7 @@ public static InvocationException convertProducerException(Throwable e, String e // 则需要创建新的InvocationException实例,此时不允许使用e.getMessage,因为可能会涉及关键信息被传给远端 // 新创建的InvocationException,会使用errorMsg来构建CommonExceptionData private static InvocationException convertException(int statusCode, String reasonPhrase, Throwable e, - String errorMsg) { + String errorMsg) { e = unwrap(e); if (e instanceof InvocationException) { @@ -124,10 +120,6 @@ private static InvocationException convertException(int statusCode, String reaso return doCreate(statusCode, reasonPhrase, data, e); } - public static Response convertExceptionToResponse(SwaggerInvocation swaggerInvocation, Throwable e) { - return exceptionToProducerResponseConverters.convertExceptionToResponse(swaggerInvocation, e); - } - public static Throwable unwrapIncludeInvocationException(Throwable throwable) { throwable = unwrap(throwable); if (throwable instanceof InvocationException) { diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionToProducerResponseConverter.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionToProducerResponseConverter.java deleted file mode 100644 index 0c1c87aa4df..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionToProducerResponseConverter.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.invocation.exception; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; - -/** - * ExceptionToProducerResponseConverter are used to convert provider Exceptions to user-friendly messages. - * They are called when : - * 1. exception happens executing business logic - * 2. exception happens in ProducerInvokeExtension.beforeMethodInvoke(e.g. parameter validation) - */ -public interface ExceptionToProducerResponseConverter { - Class getExceptionClass(); - - default int getOrder() { - return 0; - } - - Response convert(SwaggerInvocation swaggerInvocation, T e); -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionToProducerResponseConverters.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionToProducerResponseConverters.java deleted file mode 100644 index e5b307733f2..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/exception/ExceptionToProducerResponseConverters.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.invocation.exception; - -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ExceptionToProducerResponseConverters { - private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionToProducerResponseConverters.class); - - private final Map, ExceptionToProducerResponseConverter> exceptionToProducerResponseConverters = - new HashMap<>(); - - private ExceptionToProducerResponseConverter defaultConverter; - - /** - * Load the {@link ExceptionToProducerResponseConverter} implementations. Ensure that those converters whose {@link ExceptionToProducerResponseConverter#getOrder()} - * return smaller value takes higher priority. - */ - @SuppressWarnings("unchecked") - public ExceptionToProducerResponseConverters() { - SPIServiceUtils.getSortedService(ExceptionToProducerResponseConverter.class).forEach(converter -> { - if (converter.getExceptionClass() == null) { - if (defaultConverter == null) { - defaultConverter = converter; - } - return; - } - - exceptionToProducerResponseConverters.putIfAbsent(converter.getExceptionClass(), converter); - }); - } - - public Response convertExceptionToResponse(SwaggerInvocation swaggerInvocation, Throwable e) { - ExceptionToProducerResponseConverter converter = null; - Class clazz = e.getClass(); - while (converter == null) { - converter = exceptionToProducerResponseConverters.get(clazz); - if (clazz == Throwable.class) { - break; - } - clazz = clazz.getSuperclass(); - } - if (converter == null) { - converter = defaultConverter; - } - try { - return converter.convert(swaggerInvocation, e); - } catch (Throwable throwable) { - // In case users do not implement correctly and maybe discovered at runtime to cause asycResponse callback hang. - LOGGER - .error("ExceptionToProducerResponseConverter {} cannot throw exception, please fix it.", converter.getClass(), - throwable); - return Response.failResp(swaggerInvocation.getInvocationType(), e); - } - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/extension/ProducerInvokeExtension.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/extension/ProducerInvokeExtension.java deleted file mode 100644 index 107a3a72285..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/extension/ProducerInvokeExtension.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.invocation.extension; - -import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; - -/** - * Producer method invocation extension to handle the required validations/checks before invoking the actual method. - */ -public interface ProducerInvokeExtension { - - int getOrder(); - - ///Invoked before the method execution to handle the required checks before method invocation. - void beforeMethodInvoke(SwaggerInvocation invocation, SwaggerProducerOperation producerOperation, - Object[] args) throws Exception; -} diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactorys.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactories.java similarity index 79% rename from swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactorys.java rename to swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactories.java index 0cbb2612e85..761149ae24e 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactorys.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactories.java @@ -21,16 +21,16 @@ import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -public class ResponseMapperFactorys { - private final List> factorys; +public class ResponseMapperFactories { + private final List> factories; @SuppressWarnings("unchecked") - public ResponseMapperFactorys(Class> factoryCls) { - factorys = (List>) SPIServiceUtils.getSortedService(factoryCls); + public ResponseMapperFactories(Class> factoryCls) { + factories = (List>) SPIServiceUtils.getSortedService(factoryCls); } public MAPPER createResponseMapper(Type providerType) { - for (ResponseMapperFactory factory : factorys) { + for (ResponseMapperFactory factory : factories) { if (!factory.isMatch(providerType)) { continue; } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactory.java index 73a2f1a4f9a..917a3a835b9 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/ResponseMapperFactory.java @@ -25,5 +25,5 @@ default int getOrder() { boolean isMatch(Type providerType); - MAPPER createResponseMapper(ResponseMapperFactorys factorys, Type providerType); + MAPPER createResponseMapper(ResponseMapperFactories factories, Type providerType); } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CompletableFutureConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CompletableFutureConsumerResponseMapperFactory.java index eabe5fdba9a..0a7d5a976ed 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CompletableFutureConsumerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CompletableFutureConsumerResponseMapperFactory.java @@ -20,7 +20,7 @@ import java.lang.reflect.Type; import java.util.concurrent.CompletableFuture; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class CompletableFutureConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { @Override @@ -33,9 +33,9 @@ public boolean isMatch(Type consumerType) { } @Override - public ConsumerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { Type realConsumerType = ((ParameterizedType) consumerType).getActualTypeArguments()[0]; - return factorys.createResponseMapper(realConsumerType); + return factories.createResponseMapper(realConsumerType); } } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CseResponseConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CseResponseConsumerResponseMapperFactory.java index 34fcafd5eef..76b90185dcd 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CseResponseConsumerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/CseResponseConsumerResponseMapperFactory.java @@ -19,7 +19,7 @@ import java.lang.reflect.Type; import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class CseResponseConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { private static final ConsumerResponseMapper SAME = response -> response; @@ -30,7 +30,7 @@ public boolean isMatch(Type consumerType) { } @Override - public ConsumerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { return SAME; } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapperFactory.java index 0dd56c0d636..e60f05604e0 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/DefaultConsumerResponseMapperFactory.java @@ -18,7 +18,7 @@ import java.lang.reflect.Type; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class DefaultConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { @Override @@ -32,7 +32,7 @@ public boolean isMatch(Type consumerType) { } @Override - public ConsumerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { return new DefaultConsumerResponseMapper(); } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapperFactory.java index 8c08afde5e7..eddd30e407a 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/consumer/OptionalConsumerResponseMapperFactory.java @@ -20,7 +20,7 @@ import java.lang.reflect.Type; import java.util.Optional; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class OptionalConsumerResponseMapperFactory implements ConsumerResponseMapperFactory { @Override @@ -33,10 +33,10 @@ public boolean isMatch(Type consumerType) { } @Override - public ConsumerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { Type realConsumerType = ((ParameterizedType) consumerType).getActualTypeArguments()[0]; - ConsumerResponseMapper realMapper = factorys.createResponseMapper(realConsumerType); + ConsumerResponseMapper realMapper = factories.createResponseMapper(realConsumerType); return new OptionalConsumerResponseMapper(realMapper); } } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CompletableFutureProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CompletableFutureProducerResponseMapperFactory.java index 5476719252f..fc87f5728b7 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CompletableFutureProducerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CompletableFutureProducerResponseMapperFactory.java @@ -20,7 +20,7 @@ import java.lang.reflect.Type; import java.util.concurrent.CompletableFuture; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class CompletableFutureProducerResponseMapperFactory implements ProducerResponseMapperFactory { @Override @@ -33,9 +33,9 @@ public boolean isMatch(Type producerType) { } @Override - public ProducerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { Type realProducerType = ((ParameterizedType) producerType).getActualTypeArguments()[0]; - return factorys.createResponseMapper(realProducerType); + return factories.createResponseMapper(realProducerType); } } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CseResponseProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CseResponseProducerResponseMapperFactory.java index 443487293f7..02f840cbb59 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CseResponseProducerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/CseResponseProducerResponseMapperFactory.java @@ -19,7 +19,7 @@ import java.lang.reflect.Type; import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class CseResponseProducerResponseMapperFactory implements ProducerResponseMapperFactory { private static final ProducerResponseMapper SAME = (status, response) -> (Response) response; @@ -30,7 +30,7 @@ public boolean isMatch(Type producerType) { } @Override - public ProducerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { return SAME; } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapperFactory.java index dbf1df3bb48..1b58d6cbb08 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/DefaultProducerResponseMapperFactory.java @@ -18,7 +18,7 @@ import java.lang.reflect.Type; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class DefaultProducerResponseMapperFactory implements ProducerResponseMapperFactory { @Override @@ -32,7 +32,7 @@ public boolean isMatch(Type producerType) { } @Override - public ProducerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { return new DefaultProducerResponseMapper(); } diff --git a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapperFactory.java index b7b45a1d162..39d46b8b777 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-core/src/main/java/org/apache/servicecomb/swagger/invocation/response/producer/OptionalProducerResponseMapperFactory.java @@ -20,7 +20,7 @@ import java.lang.reflect.Type; import java.util.Optional; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; public class OptionalProducerResponseMapperFactory implements ProducerResponseMapperFactory { @Override @@ -33,10 +33,10 @@ public boolean isMatch(Type producerType) { } @Override - public ProducerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { Type realProducerType = ((ParameterizedType) producerType).getActualTypeArguments()[0]; - ProducerResponseMapper realMapper = factorys.createResponseMapper(realProducerType); + ProducerResponseMapper realMapper = factories.createResponseMapper(realProducerType); return new OptionalProducerResponseMapper(realMapper); } } diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor index 1a722b7459f..f1f15cdd7a0 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.ResponseTypeProcessor @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.invocation.generator.ScbResponseProcessor \ No newline at end of file +org.apache.servicecomb.swagger.invocation.generator.ScbResponseProcessor diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister index 0811c288361..e248e16db89 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.generator.SwaggerContextRegister @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.invocation.generator.InvocationContextProcessor \ No newline at end of file +org.apache.servicecomb.swagger.invocation.generator.InvocationContextProcessor diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory index f1b9e5085e3..fa524012dc5 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerContextArgumentMapperFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerInvocationContextMapperFactory \ No newline at end of file +org.apache.servicecomb.swagger.invocation.arguments.producer.ProducerInvocationContextMapperFactory diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter index 8e08ad1ec41..c91943b26bc 100644 --- a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter +++ b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter @@ -20,4 +20,4 @@ org.apache.servicecomb.swagger.invocation.converter.impl.part.BytesToPartConvert org.apache.servicecomb.swagger.invocation.converter.impl.part.FileToPartConverter org.apache.servicecomb.swagger.invocation.converter.impl.part.PartToPartConverter org.apache.servicecomb.swagger.invocation.converter.impl.part.PartListToPartArrayConverter -org.apache.servicecomb.swagger.invocation.converter.impl.part.PartListToPartListConverter \ No newline at end of file +org.apache.servicecomb.swagger.invocation.converter.impl.part.PartListToPartListConverter diff --git a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter b/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter deleted file mode 100644 index f7315d67ce1..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.exception.ExceptionToProducerResponseConverter +++ /dev/null @@ -1,19 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -org.apache.servicecomb.swagger.invocation.exception.InvocationExceptionToProducerResponseConverter -org.apache.servicecomb.swagger.invocation.exception.DefaultExceptionToProducerResponseConverter diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V2.java index 9429f3e5fff..44f16497a27 100644 --- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V2.java +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestJaxrsV2V2.java @@ -48,7 +48,7 @@ public void add_add() { Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); - arguments.put("x-z", 3); + arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); @@ -70,7 +70,7 @@ public void add_addBeanParam() { Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); - arguments.put("x-z", 3); + arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V2.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V2.java index e31f9229d1d..0e5fcb440f6 100644 --- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V2.java +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/arguments/consumer/TestSpringmvcV2V2.java @@ -30,11 +30,11 @@ import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddV2; import org.apache.servicecomb.swagger.invocation.schemas.SpringmvcAddWrapperV2; import org.apache.servicecomb.swagger.invocation.schemas.models.AddWrapperV2; - -import io.swagger.v3.oas.models.OpenAPI; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import io.swagger.v3.oas.models.OpenAPI; + @SuppressWarnings("unchecked") public class TestSpringmvcV2V2 { @Test @@ -48,7 +48,7 @@ public void add_add() { Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); - arguments.put("x-z", 3); + arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); @@ -70,7 +70,7 @@ public void add_addWrapper() { Map arguments = new HashMap<>(); arguments.put("x", 1); arguments.put("y", 2); - arguments.put("x-z", 3); + arguments.put("z", 3); SwaggerInvocation invocation = new SwaggerInvocation(); Map result = mapper.invocationArgumentToSwaggerArguments(invocation, arguments); diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionDataTest.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionDataTest.java index 39a071f1f48..c12f149b825 100644 --- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionDataTest.java +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/CommonExceptionDataTest.java @@ -20,6 +20,8 @@ import static com.google.common.collect.ImmutableMap.of; import static org.assertj.core.api.Assertions.assertThat; +import java.util.Map; + import org.junit.jupiter.api.Test; import io.vertx.core.json.Json; @@ -36,7 +38,11 @@ void should_not_include_code_in_json_when_code_is_null() { void should_include_code_in_json_when_code_is_not_null() { CommonExceptionData data = new CommonExceptionData("code", "msg"); - assertThat(Json.encode(data)).isEqualTo("{\"code\":\"code\",\"message\":\"msg\"}"); + String json = Json.encode(data); + @SuppressWarnings("unchecked") + Map obj = Json.decodeValue(json, Map.class); + + assertThat(obj).containsEntry("code", "code").containsEntry("message", "msg").hasSize(2); } @Test diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestDefaultExceptionToProducerResponseConverter.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestDefaultExceptionToProducerResponseConverter.java deleted file mode 100644 index 38fba02c63b..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestDefaultExceptionToProducerResponseConverter.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.invocation.exception; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; - -import mockit.Mocked; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; - -public class TestDefaultExceptionToProducerResponseConverter { - DefaultExceptionToProducerResponseConverter converter = new DefaultExceptionToProducerResponseConverter(); - - @Test - public void getExceptionClass() { - Assertions.assertNull(converter.getExceptionClass()); - } - - @Test - public void convert(@Mocked SwaggerInvocation swaggerInvocation) { - IllegalStateException stateException = new IllegalStateException("test"); - Response response = converter.convert(swaggerInvocation, stateException); - Assertions.assertSame(stateException, ((InvocationException) response.getResult()).getCause()); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestExceptionFactory.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestExceptionFactory.java deleted file mode 100644 index ee933222dfd..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestExceptionFactory.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.invocation.exception; - -import jakarta.ws.rs.core.Response.Status; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; - -public class TestExceptionFactory { - @Test - public void convertExceptionToResponse() { - Error error = new Error("test"); - Response response = ExceptionFactory.convertExceptionToResponse(null, error); - - Assertions.assertSame(Status.OK, response.getStatus()); - Assertions.assertEquals("response from error: test", response.getResult()); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestExceptionToProducerResponseConverters.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestExceptionToProducerResponseConverters.java deleted file mode 100644 index e3a6846d5fd..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestExceptionToProducerResponseConverters.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.invocation.exception; - -import java.io.IOException; -import java.util.Arrays; - -import jakarta.ws.rs.core.Response.Status; - -import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.junit.Test; - -import mockit.Expectations; -import mockit.Mocked; -import org.junit.jupiter.api.Assertions; - -public class TestExceptionToProducerResponseConverters { - @Test - public void convertExceptionToResponse( - @Mocked ExceptionToProducerResponseConverter c1, - @Mocked Response r1, - @Mocked ExceptionToProducerResponseConverter c2, - @Mocked Response r2, - @Mocked ExceptionToProducerResponseConverter cDef) { - new Expectations(SPIServiceUtils.class) { - { - SPIServiceUtils.getSortedService(ExceptionToProducerResponseConverter.class); - result = Arrays.asList(c1, c2, cDef); - - c1.getExceptionClass(); - result = Throwable.class; - c1.convert((SwaggerInvocation) any, (Throwable) any); - result = r1; - - c2.getExceptionClass(); - result = Exception.class; - c2.convert((SwaggerInvocation) any, (Throwable) any); - result = r2; - - cDef.getExceptionClass(); - result = null; - } - }; - - ExceptionToProducerResponseConverters exceptionToProducerResponseConverters = new ExceptionToProducerResponseConverters(); - - Assertions.assertSame(r1, - exceptionToProducerResponseConverters.convertExceptionToResponse(null, new Throwable())); - Assertions.assertSame(r2, - exceptionToProducerResponseConverters.convertExceptionToResponse(null, new Exception())); - Assertions.assertSame(r2, - exceptionToProducerResponseConverters.convertExceptionToResponse(null, - new IllegalStateException())); - } - - @Test - public void convertExceptionToResponse_checkDefaultConverterPriority( - @Mocked ExceptionToProducerResponseConverter c1, - @Mocked Response r1, - @Mocked ExceptionToProducerResponseConverter c2, - @Mocked Response r2, - @Mocked ExceptionToProducerResponseConverter cDef, - @Mocked Response rDef, - @Mocked ExceptionToProducerResponseConverter cDef2) { - new Expectations(SPIServiceUtils.class) { - { - SPIServiceUtils.getSortedService(ExceptionToProducerResponseConverter.class); - result = Arrays.asList(c1, c2, cDef, cDef2); - - c1.getExceptionClass(); - result = RuntimeException.class; - c1.convert((SwaggerInvocation) any, (Throwable) any); - result = r1; - - c2.getExceptionClass(); - result = InvocationException.class; - c2.convert((SwaggerInvocation) any, (Throwable) any); - result = r2; - - cDef.getExceptionClass(); - result = null; - cDef.convert((SwaggerInvocation) any, (Throwable) any); - result = rDef; - - cDef2.getExceptionClass(); - result = null; - } - }; - - ExceptionToProducerResponseConverters exceptionToProducerResponseConverters = new ExceptionToProducerResponseConverters(); - - Assertions.assertSame(r2, - exceptionToProducerResponseConverters - .convertExceptionToResponse(null, new InvocationException(Status.UNAUTHORIZED, ""))); - Assertions.assertSame(r1, - exceptionToProducerResponseConverters.convertExceptionToResponse(null, new RuntimeException())); - Assertions.assertSame(rDef, - exceptionToProducerResponseConverters.convertExceptionToResponse(null, - new IOException())); - } - - @Test - public void convertExceptionToResponse_CheckCommonConvertPriority( - @Mocked ExceptionToProducerResponseConverter cR0, - @Mocked ExceptionToProducerResponseConverter cR0_LowPriority, - @Mocked ExceptionToProducerResponseConverter cR1, - @Mocked ExceptionToProducerResponseConverter cR, - @Mocked ExceptionToProducerResponseConverter cT, - @Mocked ExceptionToProducerResponseConverter cDef, - @Mocked Response rR0, - @Mocked Response rR1, - @Mocked Response rR, - @Mocked Response rT) { - new Expectations(SPIServiceUtils.class) { - { - SPIServiceUtils.getSortedService(ExceptionToProducerResponseConverter.class); - result = Arrays.asList(cR, cR0, cR0_LowPriority, cR1, cDef, cT); - - cR0.getExceptionClass(); - result = RuntimeException0.class; - cR0.convert((SwaggerInvocation) any, (RuntimeException0) any); - result = rR0; - - cR0_LowPriority.getExceptionClass(); - result = RuntimeException0.class; - - cR1.getExceptionClass(); - result = RuntimeException1.class; - cR1.convert((SwaggerInvocation) any, (RuntimeException1) any); - result = rR1; - - cR.getExceptionClass(); - result = RuntimeException.class; - cR.convert((SwaggerInvocation) any, (RuntimeException) any); - result = rR; - - cT.getExceptionClass(); - result = Throwable.class; - cT.convert((SwaggerInvocation) any, (Throwable) any); - result = rT; - - cDef.getExceptionClass(); - result = null; - } - }; - - ExceptionToProducerResponseConverters exceptionToProducerResponseConverters = new ExceptionToProducerResponseConverters(); - - Assertions.assertSame(rR0, - exceptionToProducerResponseConverters.convertExceptionToResponse(null, new RuntimeException0_0())); - Assertions.assertSame(rR0, - exceptionToProducerResponseConverters.convertExceptionToResponse(null, new RuntimeException0())); - Assertions.assertSame(rR1, - exceptionToProducerResponseConverters.convertExceptionToResponse(null, new RuntimeException1())); - Assertions.assertSame(rR, - exceptionToProducerResponseConverters.convertExceptionToResponse(null, new RuntimeException())); - // Actually, a Throwable exception converter will act like a default converter, as our implementation expects. - Assertions.assertSame(rT, - exceptionToProducerResponseConverters.convertExceptionToResponse(null, new IOException())); - } - - static class RuntimeException0 extends RuntimeException { - private static final long serialVersionUID = -5151948381107463505L; - } - - static class RuntimeException1 extends RuntimeException { - private static final long serialVersionUID = 1752513688353075486L; - } - - static class RuntimeException0_0 extends RuntimeException0 { - private static final long serialVersionUID = -6645187961518504765L; - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestInvocationExceptionToProducerResponseConverter.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestInvocationExceptionToProducerResponseConverter.java deleted file mode 100644 index f8ffed8b6bb..00000000000 --- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/exception/TestInvocationExceptionToProducerResponseConverter.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.invocation.exception; - -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; - -import mockit.Mocked; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; - -public class TestInvocationExceptionToProducerResponseConverter { - InvocationExceptionToProducerResponseConverter converter = new InvocationExceptionToProducerResponseConverter(); - - @Test - public void getExceptionClass() { - Assertions.assertEquals(InvocationException.class, converter.getExceptionClass()); - } - - @Test - public void convert(@Mocked SwaggerInvocation swaggerInvocation) { - InvocationException e = new InvocationException(jakarta.ws.rs.core.Response.Status.BAD_REQUEST, "test"); - Response response = converter.convert(swaggerInvocation, e); - Assertions.assertSame(e, response.getResult()); - } -} diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/consumer/TestConsumerResponseMapperFactorys.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/consumer/TestConsumerResponseMapperFactories.java similarity index 98% rename from swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/consumer/TestConsumerResponseMapperFactorys.java rename to swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/consumer/TestConsumerResponseMapperFactories.java index af443db9550..6a198088e4d 100644 --- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/consumer/TestConsumerResponseMapperFactorys.java +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/consumer/TestConsumerResponseMapperFactories.java @@ -35,7 +35,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.models.OpenAPI; -public class TestConsumerResponseMapperFactorys { +public class TestConsumerResponseMapperFactories { interface ConsumerResponseForTest { String normal(); diff --git a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/producer/TestProducerResponseMapperFactorys.java b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/producer/TestProducerResponseMapperFactories.java similarity index 98% rename from swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/producer/TestProducerResponseMapperFactorys.java rename to swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/producer/TestProducerResponseMapperFactories.java index c576bae5dde..fd0fec59387 100644 --- a/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/producer/TestProducerResponseMapperFactorys.java +++ b/swagger/swagger-invocation/invocation-core/src/test/java/org/apache/servicecomb/swagger/invocation/response/producer/TestProducerResponseMapperFactories.java @@ -34,7 +34,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.ws.rs.core.Response.Status; -public class TestProducerResponseMapperFactorys { +public class TestProducerResponseMapperFactories { static class ResponseForTest { public String normal() { return "normal"; diff --git a/swagger/swagger-invocation/invocation-jaxrs/pom.xml b/swagger/swagger-invocation/invocation-jaxrs/pom.xml index 3d16287f1b1..50d12dd243f 100644 --- a/swagger/swagger-invocation/invocation-jaxrs/pom.xml +++ b/swagger/swagger-invocation/invocation-jaxrs/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb swagger-invocation - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-invocation-jaxrs Java Chassis::Swagger::Invocation::JAXRS diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapperFactory.java index 241decec275..307cdb26c20 100644 --- a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsConsumerResponseMapperFactory.java @@ -20,7 +20,7 @@ import jakarta.ws.rs.core.Response; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; @@ -31,7 +31,7 @@ public boolean isMatch(Type consumerType) { } @Override - public ConsumerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { return new JaxrsConsumerResponseMapper(); } diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapperFactory.java index e36e49407d2..a8e1e064fdd 100644 --- a/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-jaxrs/src/main/java/org/apache/servicecomb/swagger/invocation/jaxrs/response/JaxrsProducerResponseMapperFactory.java @@ -20,7 +20,7 @@ import jakarta.ws.rs.core.Response; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; @@ -31,7 +31,7 @@ public boolean isMatch(Type producerType) { } @Override - public ProducerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { return new JaxrsProducerResponseMapper(); } diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory b/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory index 764b9d711d7..b15dde97c09 100644 --- a/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory +++ b/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.invocation.jaxrs.response.JaxrsConsumerResponseMapperFactory \ No newline at end of file +org.apache.servicecomb.swagger.invocation.jaxrs.response.JaxrsConsumerResponseMapperFactory diff --git a/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory b/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory index 24087486ab9..2598b38b3d6 100644 --- a/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory +++ b/swagger/swagger-invocation/invocation-jaxrs/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.invocation.jaxrs.response.JaxrsProducerResponseMapperFactory \ No newline at end of file +org.apache.servicecomb.swagger.invocation.jaxrs.response.JaxrsProducerResponseMapperFactory diff --git a/swagger/swagger-invocation/invocation-springmvc/pom.xml b/swagger/swagger-invocation/invocation-springmvc/pom.xml index c14d7f74c03..5ae28883576 100644 --- a/swagger/swagger-invocation/invocation-springmvc/pom.xml +++ b/swagger/swagger-invocation/invocation-springmvc/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb swagger-invocation - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-invocation-springmvc Java Chassis::Swagger::Invocation::Spring MVC @@ -32,10 +32,6 @@ org.apache.servicecomb swagger-invocation-core - - com.google.inject - guice - org.springframework spring-webmvc @@ -45,11 +41,6 @@ log4j-slf4j-impl test - - javax.xml.bind - jaxb-api - test - org.jmockit jmockit diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartArrayConverter.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartArrayConverter.java index cab3ec53c6c..d9b0901df6c 100644 --- a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartArrayConverter.java +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartArrayConverter.java @@ -19,16 +19,15 @@ import java.lang.reflect.Type; import java.util.List; -import jakarta.servlet.http.Part; - +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.springframework.web.multipart.MultipartFile; -import com.google.inject.util.Types; +import jakarta.servlet.http.Part; public class PartListToMultipartArrayConverter implements Converter { @Override public Type getSrcType() { - return Types.newParameterizedType(List.class, Part.class); + return ParameterizedTypeUtil.make(List.class, Part.class); } @Override diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartListConverter.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartListConverter.java index e1cba13b008..4d7d50725cd 100644 --- a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartListConverter.java +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/converter/PartListToMultipartListConverter.java @@ -20,21 +20,20 @@ import java.util.ArrayList; import java.util.List; -import jakarta.servlet.http.Part; - +import org.apache.servicecomb.foundation.common.ParameterizedTypeUtil; import org.springframework.web.multipart.MultipartFile; -import com.google.inject.util.Types; +import jakarta.servlet.http.Part; public class PartListToMultipartListConverter implements Converter { @Override public Type getSrcType() { - return Types.newParameterizedType(List.class, Part.class); + return ParameterizedTypeUtil.make(List.class, Part.class); } @Override public Type getTargetType() { - return Types.newParameterizedType(List.class, MultipartFile.class); + return ParameterizedTypeUtil.make(List.class, MultipartFile.class); } @Override diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapperFactory.java index 9fa48f7154e..0e22da39176 100644 --- a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcConsumerResponseMapperFactory.java @@ -19,7 +19,7 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory; import org.springframework.http.ResponseEntity; @@ -35,10 +35,10 @@ public boolean isMatch(Type consumerType) { } @Override - public ConsumerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ConsumerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type consumerType) { Type realConsumerType = ((ParameterizedType) consumerType).getActualTypeArguments()[0]; - ConsumerResponseMapper realMapper = factorys.createResponseMapper(realConsumerType); + ConsumerResponseMapper realMapper = factories.createResponseMapper(realConsumerType); return new SpringmvcConsumerResponseMapper(realMapper); } } diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapperFactory.java index c3bee1c344a..cfb43f5c3b2 100644 --- a/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/java/org/apache/servicecomb/swagger/invocation/springmvc/response/SpringmvcProducerResponseMapperFactory.java @@ -19,7 +19,7 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; import org.springframework.http.ResponseEntity; @@ -35,10 +35,10 @@ public boolean isMatch(Type producerType) { } @Override - public ProducerResponseMapper createResponseMapper(ResponseMapperFactorys factorys, + public ProducerResponseMapper createResponseMapper(ResponseMapperFactories factories, Type producerType) { Type realProducerType = ((ParameterizedType) producerType).getActualTypeArguments()[0]; - ProducerResponseMapper realMapper = factorys.createResponseMapper(realProducerType); + ProducerResponseMapper realMapper = factories.createResponseMapper(realProducerType); return new SpringmvcProducerResponseMapper(realMapper); } } diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter index 245329422c8..2222af91fe7 100644 --- a/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.converter.Converter @@ -18,4 +18,4 @@ org.apache.servicecomb.swagger.invocation.converter.PartListToMultipartArrayConverter org.apache.servicecomb.swagger.invocation.converter.PartToMultipartConverter org.apache.servicecomb.swagger.invocation.converter.PartListToMultipartListConverter -org.apache.servicecomb.swagger.invocation.converter.impl.part.ResourceToPartConverter \ No newline at end of file +org.apache.servicecomb.swagger.invocation.converter.impl.part.ResourceToPartConverter diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory index 80c4778dc57..26ddc8af9fa 100644 --- a/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.consumer.ConsumerResponseMapperFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.invocation.springmvc.response.SpringmvcConsumerResponseMapperFactory \ No newline at end of file +org.apache.servicecomb.swagger.invocation.springmvc.response.SpringmvcConsumerResponseMapperFactory diff --git a/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory index 7c5459b7609..5550f33368c 100644 --- a/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory +++ b/swagger/swagger-invocation/invocation-springmvc/src/main/resources/META-INF/services/org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory @@ -15,4 +15,4 @@ # limitations under the License. # -org.apache.servicecomb.swagger.invocation.springmvc.response.SpringmvcProducerResponseMapperFactory \ No newline at end of file +org.apache.servicecomb.swagger.invocation.springmvc.response.SpringmvcProducerResponseMapperFactory diff --git a/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapper.java b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapper.java index cc029356aa1..c48fc62be8c 100644 --- a/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapper.java +++ b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapper.java @@ -63,7 +63,7 @@ Response mapResponse(StatusType status, Object response) { @SuppressWarnings("unchecked") @Test - public void mapResponse_withoutHeader_sucess() { + public void mapResponse_withoutHeader_success() { ResponseEntity responseEntity = new ResponseEntity<>(arrResult, org.springframework.http.HttpStatus.OK); Response response = mapper.mapResponse(null, responseEntity); diff --git a/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapperFactory.java b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapperFactory.java index 5407786eaab..35c84db148f 100644 --- a/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapperFactory.java +++ b/swagger/swagger-invocation/invocation-springmvc/src/test/java/org/apache/servicecomb/swagger/invocation/springmvc/response/TestSpringmvcProducerResponseMapperFactory.java @@ -21,7 +21,7 @@ import org.apache.servicecomb.foundation.common.utils.ReflectUtils; import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactorys; +import org.apache.servicecomb.swagger.invocation.response.ResponseMapperFactories; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapper; import org.apache.servicecomb.swagger.invocation.response.producer.ProducerResponseMapperFactory; import org.hamcrest.MatcherAssert; @@ -34,7 +34,7 @@ public class TestSpringmvcProducerResponseMapperFactory { SpringmvcProducerResponseMapperFactory factory = new SpringmvcProducerResponseMapperFactory(); - ResponseMapperFactorys factorys = new ResponseMapperFactorys<>( + ResponseMapperFactories factories = new ResponseMapperFactories<>( ProducerResponseMapperFactory.class); public ResponseEntity responseEntity() { @@ -67,7 +67,7 @@ public void createResponseMapper() { Method responseEntityMethod = ReflectUtils.findMethod(this.getClass(), "responseEntity"); ProducerResponseMapper mapper = factory - .createResponseMapper(factorys, responseEntityMethod.getGenericReturnType()); + .createResponseMapper(factories, responseEntityMethod.getGenericReturnType()); MatcherAssert.assertThat(mapper, Matchers.instanceOf(SpringmvcProducerResponseMapper.class)); ResponseEntity responseEntity = new ResponseEntity<>(new String[] {"a", "b"}, HttpStatus.OK); diff --git a/swagger/swagger-invocation/invocation-validator/pom.xml b/swagger/swagger-invocation/invocation-validator/pom.xml index 119f90c2f30..cf0f46c7f4f 100644 --- a/swagger/swagger-invocation/invocation-validator/pom.xml +++ b/swagger/swagger-invocation/invocation-validator/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb swagger-invocation - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-invocation-validator Java Chassis::Swagger::Invocation::Validator @@ -45,4 +45,4 @@ test - \ No newline at end of file + diff --git a/swagger/swagger-invocation/invocation-validator/src/main/java/org/apache/servicecomb/swagger/invocation/validator/ParameterValidator.java b/swagger/swagger-invocation/invocation-validator/src/main/java/org/apache/servicecomb/swagger/invocation/validator/ParameterValidator.java deleted file mode 100644 index ad2cf2dcfaf..00000000000 --- a/swagger/swagger-invocation/invocation-validator/src/main/java/org/apache/servicecomb/swagger/invocation/validator/ParameterValidator.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.servicecomb.swagger.invocation.validator; - -import java.util.Set; - -import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; -import org.apache.servicecomb.swagger.engine.SwaggerProducerOperation; -import org.apache.servicecomb.swagger.invocation.SwaggerInvocation; -import org.apache.servicecomb.swagger.invocation.extension.ProducerInvokeExtension; -import org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator; -import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator; -import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import jakarta.validation.ConstraintViolation; -import jakarta.validation.ConstraintViolationException; -import jakarta.validation.Validation; -import jakarta.validation.ValidatorFactory; -import jakarta.validation.executable.ExecutableValidator; -import jakarta.validation.groups.Default; - -public class ParameterValidator implements ProducerInvokeExtension { - - private static final Logger LOGGER = LoggerFactory.getLogger(ParameterValidator.class); - - private static final String PARAM_VALIDATION_ENABLED = "servicecomb.rest.parameter.validation.enabled"; - - private static final String ENABLE_EL = "servicecomb.filters.validation.useResourceBundleMessageInterpolator"; - - private final boolean paramValidationEnabled; - - public ParameterValidator() { - paramValidationEnabled = LegacyPropertyFactory.getBooleanProperty(PARAM_VALIDATION_ENABLED, true); - } - - private static ExecutableValidator executableValidator; - - @Override - public void beforeMethodInvoke(SwaggerInvocation invocation, SwaggerProducerOperation producerOperation, - Object[] args) - throws ConstraintViolationException { - if (paramValidationEnabled) { - if (null == executableValidator) { - ValidatorFactory factory = - Validation.byDefaultProvider() - .configure() - .parameterNameProvider(new DefaultParameterNameProvider()) - .messageInterpolator(messageInterpolator()) - .buildValidatorFactory(); - executableValidator = factory.getValidator().forExecutables(); - } - Set> violations = - executableValidator.validateParameters(producerOperation.getProducerInstance(), - producerOperation.getProducerMethod(), - args, - Default.class); - if (violations.size() > 0) { - LOGGER.warn("Parameter validation failed : " + violations); - throw new ConstraintViolationException(violations); - } - } - } - - private AbstractMessageInterpolator messageInterpolator() { - if (useResourceBundleMessageInterpolator()) { - return new ResourceBundleMessageInterpolator(); - } - return new ParameterMessageInterpolator(); - } - - private boolean useResourceBundleMessageInterpolator() { - return LegacyPropertyFactory.getBooleanProperty(ENABLE_EL, false); - } - - @Override - public int getOrder() { - return 100; - } -} diff --git a/swagger/swagger-invocation/pom.xml b/swagger/swagger-invocation/pom.xml index 5cfa89e761c..00f83a03fc4 100644 --- a/swagger/swagger-invocation/pom.xml +++ b/swagger/swagger-invocation/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb swagger - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT swagger-invocation Java Chassis::Swagger::Invocation diff --git a/tracing/pom.xml b/tracing/pom.xml index b679252932a..7ffde2fbc3a 100644 --- a/tracing/pom.xml +++ b/tracing/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default 4.0.0 @@ -39,6 +39,12 @@ org.springframework.boot spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-logging + + diff --git a/tracing/tracing-common/pom.xml b/tracing/tracing-common/pom.xml index f20a10b36e4..f03ecbb2b04 100644 --- a/tracing/tracing-common/pom.xml +++ b/tracing/tracing-common/pom.xml @@ -22,7 +22,7 @@ tracing org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/tracing/tracing-zipkin/pom.xml b/tracing/tracing-zipkin/pom.xml index fd9c59ea83e..e5755729748 100644 --- a/tracing/tracing-zipkin/pom.xml +++ b/tracing/tracing-zipkin/pom.xml @@ -22,7 +22,7 @@ tracing org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 @@ -30,6 +30,14 @@ Java Chassis::Tracing::Zipkin + + org.apache.servicecomb + foundation-common + + + org.apache.servicecomb + handler-tracing-zipkin + org.apache.servicecomb tracing-common @@ -42,19 +50,17 @@ io.zipkin.brave brave - - io.zipkin.reporter2 - zipkin-sender-okhttp3 - io.zipkin.brave brave-context-slf4j - - org.springframework.boot - spring-boot-starter-test - test + io.zipkin.reporter2 + zipkin-sender-okhttp3 + + + io.zipkin.reporter2 + zipkin-reporter-brave diff --git a/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviser.java b/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviser.java index 42ec0bb3874..b2bcf1c2423 100644 --- a/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviser.java +++ b/tracing/tracing-zipkin/src/main/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviser.java @@ -17,6 +17,9 @@ package org.apache.servicecomb.tracing.zipkin; +import org.apache.servicecomb.swagger.invocation.context.ContextUtils; +import org.apache.servicecomb.swagger.invocation.context.InvocationContext; + import brave.Span; import brave.Tracer; import brave.Tracer.SpanInScope; @@ -45,7 +48,14 @@ T invoke(String spanName, String path, ThrowableSupplier supplier) throws } private Span createSpan(String spanName, String path) { - Span currentSpan = tracer.currentSpan(); + InvocationContext context = ContextUtils.getInvocationContext(); + Span currentSpan = null; + if (context != null) { + currentSpan = context.getLocalContext(ZipkinTracingFilter.CONTEXT_TRACE_SPAN); + } + if (currentSpan == null) { + currentSpan = tracer.currentSpan(); + } if (currentSpan != null) { return tracer.newChild(currentSpan.context()).name(spanName).tag(CALL_PATH, path).start(); } diff --git a/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectTest.java b/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectTest.java index acccdc3fbce..4eee935137d 100644 --- a/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectTest.java +++ b/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinSpanAspectTest.java @@ -34,14 +34,13 @@ import org.apache.servicecomb.tracing.zipkin.app.ZipkinSpanTestApplication.CustomSpanTask; import org.apache.servicecomb.tracing.zipkin.app.ZipkinSpanTestApplication.SomeSlowTask; import org.hamcrest.MatcherAssert; -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.context.annotation.Primary; import brave.Tracing; import brave.propagation.StrictScopeDecorator; @@ -49,7 +48,6 @@ import zipkin2.Span; import zipkin2.reporter.brave.ZipkinSpanHandler; -@RunWith(SpringRunner.class) @SpringBootTest(classes = {ZipkinSpanTestApplication.class, TracingConfig.class}) public class ZipkinSpanAspectTest { private Queue spans; @@ -83,7 +81,7 @@ public void setTracing(Tracing tracing) { public ZipkinSpanAspectTest() { } - @After + @AfterEach public void tearDown() throws Exception { tracing.close(); } @@ -126,7 +124,8 @@ Queue spans() { } @Bean - Tracing tracing(Queue spans) { + @Primary + Tracing zipkinTracing(Queue spans) { return Tracing.newBuilder() .currentTraceContext( ThreadLocalCurrentTraceContext.newBuilder().addScopeDecorator(StrictScopeDecorator.create()).build()) diff --git a/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviserTest.java b/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviserTest.java index 5b68f732417..60df34100ed 100644 --- a/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviserTest.java +++ b/tracing/tracing-zipkin/src/test/java/org/apache/servicecomb/tracing/zipkin/ZipkinTracingAdviserTest.java @@ -39,8 +39,6 @@ import org.apache.servicecomb.tracing.zipkin.ZipkinTracingAdviser.ThrowableSupplier; import org.hamcrest.MatcherAssert; -import org.junit.After; -import org.junit.Test; import brave.Span; import brave.Tracer.SpanInScope; @@ -48,7 +46,9 @@ import brave.propagation.StrictScopeDecorator; import brave.propagation.ThreadLocalCurrentTraceContext; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import zipkin2.reporter.brave.ZipkinSpanHandler; @@ -76,7 +76,7 @@ public class ZipkinTracingAdviserTest { private final ZipkinTracingAdviser tracingAdviser = new ZipkinTracingAdviser(tracing.tracer()); - @After + @AfterEach public void tearDown() throws Exception { tracing.close(); } diff --git a/transports/pom.xml b/transports/pom.xml index 1cbd435ad16..13439c0ffdc 100644 --- a/transports/pom.xml +++ b/transports/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb java-chassis-parent - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT ../parents/default transports diff --git a/transports/transport-common/pom.xml b/transports/transport-common/pom.xml index 174094ab255..00c1ae33c73 100644 --- a/transports/transport-common/pom.xml +++ b/transports/transport-common/pom.xml @@ -21,7 +21,7 @@ transports org.apache.servicecomb - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT 4.0.0 diff --git a/transports/transport-common/src/test/java/org/apache/servicecomb/transport/common/TestTransportConfigUtils.java b/transports/transport-common/src/test/java/org/apache/servicecomb/transport/common/TestTransportConfigUtils.java index 16c16adc89e..05351e62351 100644 --- a/transports/transport-common/src/test/java/org/apache/servicecomb/transport/common/TestTransportConfigUtils.java +++ b/transports/transport-common/src/test/java/org/apache/servicecomb/transport/common/TestTransportConfigUtils.java @@ -60,7 +60,7 @@ public void readVerticleCount_old_exist() { Assertions.assertEquals(10, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); Assertions.assertEquals("thread-count is ambiguous, and deprecated, recommended to use verticle-count.", collector.getEvent(0).getMessage().getFormattedMessage()); - collector.teardown(); + collector.tearDown(); } @Test @@ -79,7 +79,7 @@ int availableProcessors() { Assertions.assertEquals(7, TransportConfigUtils.readVerticleCount(key, deprecatedKey)); Assertions.assertEquals("verticle-count not defined, set to 7.", collector.getLastEvents().getMessage().getFormattedMessage()); - collector.teardown(); + collector.tearDown(); } @Test @@ -106,6 +106,6 @@ int availableProcessors() { Assertions.assertEquals("verticle-count not defined, set to 8.", collector.getLastEvents().getMessage().getFormattedMessage()); - collector.teardown(); + collector.tearDown(); } } diff --git a/transports/transport-highway/pom.xml b/transports/transport-highway/pom.xml index b78bb470083..d722eb7aa23 100644 --- a/transports/transport-highway/pom.xml +++ b/transports/transport-highway/pom.xml @@ -21,7 +21,7 @@ org.apache.servicecomb transports - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT transport-highway Java Chassis::Transports::Hightway @@ -56,7 +56,7 @@ vertx-codegen provided - + org.apache.logging.log4j log4j-slf4j-impl diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClient.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClient.java index 8d94333c278..681bfbd3874 100644 --- a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClient.java +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClient.java @@ -17,12 +17,11 @@ package org.apache.servicecomb.transport.highway; -import java.util.concurrent.TimeoutException; +import java.util.Map; import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; -import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager; -import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.ssl.SSLCustom; import org.apache.servicecomb.foundation.ssl.SSLOption; import org.apache.servicecomb.foundation.ssl.SSLOptionFactory; @@ -31,22 +30,13 @@ import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; import org.apache.servicecomb.foundation.vertx.client.ClientVerticle; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; -import org.apache.servicecomb.swagger.invocation.AsyncResponse; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; -import jakarta.ws.rs.core.Response.Status; public class HighwayClient { - private static final Logger LOGGER = LoggerFactory.getLogger(HighwayClient.class); - private static final String SSL_KEY = "highway.consumer"; private ClientPoolManager clientMgr; @@ -62,7 +52,10 @@ public void init(Vertx vertx) throws Exception { DeploymentOptions deployOptions = VertxUtils.createClientDeployOptions(clientMgr, HighwayConfig.getClientThreadCount()); - VertxUtils.blockDeploy(vertx, ClientVerticle.class, deployOptions); + Map result = VertxUtils.blockDeploy(vertx, ClientVerticle.class, deployOptions); + if (!(boolean) result.get("code")) { + throw new IllegalStateException((String) result.get("message")); + } } @VisibleForTesting @@ -82,69 +75,22 @@ TcpClientConfig createTcpClientConfig() { } SSLCustom sslCustom = SSLCustom.createSSLCustom(sslOption.getSslCustomClass()); VertxTLSBuilder.buildClientOptionsBase(sslOption, sslCustom, tcpClientConfig); + if (!sslOption.isCheckCNHost()) { + tcpClientConfig.setHostnameVerificationAlgorithm(""); + } else { + tcpClientConfig.setHostnameVerificationAlgorithm("HTTPS"); + } return tcpClientConfig; } - public void send(Invocation invocation, AsyncResponse asyncResp) throws Exception { - invocation.getInvocationStageTrace().startGetConnection(); - HighwayClientConnection tcpClient = findClientPool(invocation); - - invocation.getInvocationStageTrace().startClientFiltersRequest(); - OperationProtobuf operationProtobuf = ProtobufManager.getOrCreateOperation(invocation); - HighwayClientPackage clientPackage = createClientPackage(invocation, operationProtobuf); - - invocation.onStartSendRequest(); - tcpClient.send(clientPackage, ar -> { - invocation.getInvocationStageTrace().finishWriteToBuffer(clientPackage.getFinishWriteToBuffer()); - invocation.getInvocationStageTrace().finishReceiveResponse(); - // 此时是在网络线程中,转换线程 - invocation.getResponseExecutor().execute(() -> { - invocation.getInvocationStageTrace().startClientFiltersResponse(); - if (ar.failed()) { - // 只会是本地异常 - invocation.getInvocationStageTrace().finishClientFiltersResponse(); - if (ar.cause() instanceof TimeoutException) { - // give an accurate cause for timeout exception - // The timeout period of 30000ms has been exceeded while executing GET /xxx for server 1.1.1.1:8080 - // should not copy the message to invocationException to avoid leak server ip address - LOGGER.info("Request timeout, Details: {}.", ar.cause().getMessage()); - - asyncResp.consumerFail(new InvocationException(Status.REQUEST_TIMEOUT, - new CommonExceptionData("Request Timeout."))); - return; - } - asyncResp.consumerFail(ar.cause()); - return; - } - - // 处理应答 - try { - Response response = - HighwayCodec.decodeResponse(invocation, - operationProtobuf, - ar.result()); - invocation.getInvocationStageTrace().finishClientFiltersResponse(); - asyncResp.complete(response); - } catch (Throwable e) { - invocation.getInvocationStageTrace().finishClientFiltersResponse(); - asyncResp.consumerFail(e); - } - }); - }); - } - public HighwayClientPackage createClientPackage(Invocation invocation, OperationProtobuf operationProtobuf) { long msRequestTimeout = invocation.getOperationMeta().getConfig().getMsRequestTimeout(); return new HighwayClientPackage(invocation, operationProtobuf, msRequestTimeout); } public HighwayClientConnection findClientPool(Invocation invocation) { - HighwayClientConnection tcpClient = clientMgr.findClientPool(invocation.isSync()) + return clientMgr.findClientPool(invocation.isSync()) .findOrCreateClient(invocation.getEndpoint().getEndpoint()); - - invocation.getInvocationStageTrace().finishGetConnection(); - - return tcpClient; } } diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientFilter.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientFilter.java index 82d0348c440..3d657bb9d53 100644 --- a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientFilter.java +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayClientFilter.java @@ -23,18 +23,19 @@ import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.core.exception.Exceptions; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpData; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class HighwayClientFilter implements ConsumerFilter { +public class HighwayClientFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(HighwayClientFilter.class); public static final String NAME = "highway-client"; @@ -50,7 +51,7 @@ public boolean enabledForTransport(String transport) { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 2000; } @@ -63,33 +64,30 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne OperationProtobuf operationProtobuf = ProtobufManager.getOrCreateOperation(invocation); return send(invocation, operationProtobuf) .thenApply(tcpData -> convertToResponse(invocation, operationProtobuf, tcpData)) - .thenApply(this::convertFailedResponseToException) - .whenComplete((response, throwable) -> invocation.getInvocationStageTrace().finishClientFiltersResponse()); + .thenApply(this::convertFailedResponseToException); } protected CompletableFuture send(Invocation invocation, OperationProtobuf operationProtobuf) { - invocation.getInvocationStageTrace().startGetConnection(); + invocation.getInvocationStageTrace().startConsumerConnection(); HighwayClient highwayClient = ((HighwayTransport) invocation.getTransport()).getHighwayClient(); HighwayClientPackage clientPackage = highwayClient.createClientPackage(invocation, operationProtobuf); HighwayClientConnection clientConnection = highwayClient.findClientPool(invocation); - invocation.getInvocationStageTrace().startClientFiltersRequest(); + invocation.getInvocationStageTrace().finishConsumerConnection(); invocation.onStartSendRequest(); + invocation.getInvocationStageTrace().startConsumerSendRequest(); CompletableFuture sendFuture = clientConnection - .send(clientPackage) - .whenComplete((tcpData, throwable) -> afterSend(invocation, clientPackage)); + .send(clientPackage).whenComplete((r, e) -> invocation.getInvocationStageTrace().finishWaitResponse()); + invocation.getInvocationStageTrace().finishConsumerSendRequest(); + invocation.getInvocationStageTrace().startWaitResponse(); return invocation.optimizeSyncConsumerThread(sendFuture); } - protected void afterSend(Invocation invocation, HighwayClientPackage clientPackage) { - invocation.getInvocationStageTrace().finishWriteToBuffer(clientPackage.getFinishWriteToBuffer()); - invocation.getInvocationStageTrace().finishReceiveResponse(); - - invocation.getInvocationStageTrace().startClientFiltersResponse(); - } - protected Response convertToResponse(Invocation invocation, OperationProtobuf operationProtobuf, TcpData tcpData) { try { - return HighwayCodec.decodeResponse(invocation, operationProtobuf, tcpData); + invocation.getInvocationStageTrace().startConsumerDecodeResponse(); + Response result = HighwayCodec.decodeResponse(invocation, operationProtobuf, tcpData); + invocation.getInvocationStageTrace().finishConsumerDecodeResponse(); + return result; } catch (Exception e) { throw AsyncUtils.rethrow(e); } diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java index 36e0af59835..3f38fd760d3 100644 --- a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayProducerInvocationFlow.java @@ -56,8 +56,8 @@ private void logException(Throwable throwable) { } @Override - protected void sendResponse(Invocation invocation, Response response) { + protected void endResponse(Invocation invocation, Response response) { HighwayTransportContext transportContext = invocation.getTransportContext(); - connection.write(transportContext.getResponseBuffer().getByteBuf()); + connection.write(transportContext.getResponseBuffer()); } } diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java index 87d620156d6..1f165f9227c 100644 --- a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilter.java @@ -24,17 +24,18 @@ import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.filter.ProviderFilter; import org.apache.servicecomb.foundation.common.utils.AsyncUtils; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.apache.servicecomb.transport.highway.message.ResponseHeader; import io.vertx.core.buffer.Buffer; -public class HighwayServerCodecFilter implements ProviderFilter { +public class HighwayServerCodecFilter extends AbstractFilter implements ProviderFilter { public static final String NAME = "highway-server-codec"; @Override @@ -43,7 +44,7 @@ public String getName() { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { // almost time, should be the first filter. return Filter.PROVIDER_SCHEDULE_FILTER_ORDER - 2000; } @@ -65,10 +66,12 @@ public CompletableFuture onFilter(Invocation invocation, FilterNode ne protected CompletableFuture decodeRequest(Invocation invocation) { HighwayTransportContext transportContext = invocation.getTransportContext(); try { + invocation.getInvocationStageTrace().startProviderDecodeRequest(); HighwayCodec.decodeRequest(invocation, transportContext.getHeader(), transportContext.getOperationProtobuf(), transportContext.getBodyBuffer()); + invocation.getInvocationStageTrace().finishProviderDecodeRequest(); return CompletableFuture.completedFuture(invocation); } catch (Exception e) { return AsyncUtils.completeExceptionally(e); @@ -90,10 +93,18 @@ protected CompletableFuture encodeResponse(Invocation invocation, Resp ResponseRootSerializer bodySchema = operationProtobuf.findResponseRootSerializer(response.getStatusCode()); try { - Buffer respBuffer = HighwayCodec.encodeResponse( - msgId, header, bodySchema, response.getResult()); + boolean failed = response.getResult() instanceof InvocationException; + Buffer respBuffer; + if (failed) { + respBuffer = HighwayCodec.encodeResponse( + msgId, header, bodySchema, ((InvocationException) response.getResult()).getErrorData()); + } else { + respBuffer = HighwayCodec.encodeResponse( + msgId, header, bodySchema, response.getResult()); + } transportContext.setResponseBuffer(respBuffer); + invocation.onEncodeResponseFinish(); return CompletableFuture.completedFuture(response); } catch (Exception e) { // keep highway performance and simple, this encoding/decoding error not need handle by client diff --git a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransport.java b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransport.java index f449e9c47d5..2d5d4fa5683 100644 --- a/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransport.java +++ b/transports/transport-highway/src/main/java/org/apache/servicecomb/transport/highway/HighwayTransport.java @@ -18,6 +18,7 @@ package org.apache.servicecomb.transport.highway; import java.util.Collections; +import java.util.Map; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.transport.AbstractTransport; @@ -45,7 +46,12 @@ public boolean init() throws Exception { json.put(ENDPOINT_KEY, getEndpoint()); deployOptions.setConfig(json); deployOptions.setWorkerPoolName("pool-worker-transport-highway"); - return VertxUtils.blockDeploy(transportVertx, HighwayServerVerticle.class, deployOptions); + Map result = VertxUtils.blockDeploy(transportVertx, HighwayServerVerticle.class, deployOptions); + if ((boolean) result.get("code")) { + return true; + } else { + throw new IllegalStateException((String) result.get("message")); + } } public HighwayClient getHighwayClient() { diff --git a/transports/transport-highway/src/main/resources/ResponseHeader.proto b/transports/transport-highway/src/main/resources/ResponseHeader.proto index 11b89af7e72..c92bf71e94a 100644 --- a/transports/transport-highway/src/main/resources/ResponseHeader.proto +++ b/transports/transport-highway/src/main/resources/ResponseHeader.proto @@ -34,4 +34,4 @@ message Headers { //@WrapProperty message ListGoogle_protobuf_Any { repeated google.protobuf.Any value = 1; -} \ No newline at end of file +} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java index 8856cb79b76..46352690c95 100644 --- a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/HighwayServerCodecFilterTest.java @@ -32,22 +32,25 @@ import org.apache.servicecomb.core.SCBStatus; import org.apache.servicecomb.core.bootstrap.SCBBootstrap; import org.apache.servicecomb.core.definition.OperationMeta; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.FilterNode; import org.apache.servicecomb.core.invocation.InvocationFactory; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.test.scaffolding.exception.RuntimeExceptionWithoutStackTrace; import org.apache.servicecomb.swagger.invocation.Response; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.apache.servicecomb.transport.highway.message.RequestHeader; import org.apache.servicecomb.transport.highway.message.ResponseHeader; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; +import org.skyscreamer.jsonassert.JSONAssert; import org.springframework.core.env.Environment; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; -import io.vertx.core.file.impl.FileResolverImpl; +import io.vertx.core.impl.SysProps; import io.vertx.core.json.Json; import mockit.Expectations; import mockit.Mocked; @@ -69,10 +72,18 @@ public class HighwayServerCodecFilterTest { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); - FilterNode nextNode = new FilterNode((invocation, next) -> { - Response response = Response.ok("ok"); - response.setHeaders(headers); - return CompletableFuture.completedFuture(response); + FilterNode nextNode = new FilterNode(new AbstractFilter() { + @Override + public String getName() { + return "test"; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + Response response = Response.ok("ok"); + response.setHeaders(headers); + return CompletableFuture.completedFuture(response); + } }); static SCBEngine engine; @@ -85,7 +96,7 @@ public void setUp() { long.class, DEFAULT_TURN_DOWN_STATUS_WAIT_SEC)).thenReturn(DEFAULT_TURN_DOWN_STATUS_WAIT_SEC); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); @@ -136,9 +147,10 @@ public void should_convert_exception_to_response_when_decode_request_failed() Response response = codecFilter.onFilter(invocation, nextNode).get(); assertThat(response.getStatus()).isEqualTo(INTERNAL_SERVER_ERROR); - assertThat(Json.encode(response.getResult())) - .isEqualTo("{\"code\":\"SCB.50000000\",\"message\":\"Unexpected " - + "exception when processing null. encode request failed\"}"); + + JSONAssert.assertEquals(Json.encode(((InvocationException) response.getResult()).getErrorData()), + "{\"code\":\"SCB.50000000\",\"message\":\"Unexpected " + + "exception when processing null. encode request failed\"}", false); } private void success_invocation() throws InterruptedException, ExecutionException { diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayClient.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayClient.java index 24a9cfa50ab..d7a29f0f86e 100644 --- a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayClient.java +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayClient.java @@ -17,24 +17,13 @@ package org.apache.servicecomb.transport.highway; -import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; -import org.apache.servicecomb.codec.protobuf.definition.ProtobufManager; +import java.util.HashMap; +import java.util.Map; + import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; -import org.apache.servicecomb.core.Endpoint; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.definition.OperationConfig; -import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.core.executor.ReactiveExecutor; -import org.apache.servicecomb.core.invocation.InvocationStageTrace; -import org.apache.servicecomb.foundation.common.Holder; import org.apache.servicecomb.foundation.vertx.VertxUtils; import org.apache.servicecomb.foundation.vertx.client.ClientPoolManager; -import org.apache.servicecomb.foundation.vertx.client.tcp.AbstractTcpClientPackage; import org.apache.servicecomb.foundation.vertx.client.tcp.TcpClientConfig; -import org.apache.servicecomb.foundation.vertx.client.tcp.TcpData; -import org.apache.servicecomb.foundation.vertx.client.tcp.TcpResponseCallback; -import org.apache.servicecomb.swagger.invocation.Response; -import org.apache.servicecomb.swagger.invocation.exception.InvocationException; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -46,8 +35,6 @@ import io.vertx.core.AbstractVerticle; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; -import io.vertx.core.buffer.Buffer; -import jakarta.ws.rs.core.Response.Status; import mockit.Deencapsulation; import mockit.Mock; import mockit.MockUp; @@ -58,16 +45,6 @@ public class TestHighwayClient { HighwayClient client = new HighwayClient(); - Invocation invocation = Mockito.mock(Invocation.class); - - InvocationStageTrace invocationStageTrace = new InvocationStageTrace(invocation); - - OperationProtobuf operationProtobuf = Mockito.mock(OperationProtobuf.class); - - OperationMeta operationMeta = Mockito.mock(OperationMeta.class); - - Endpoint endpoint = Mockito.mock(Endpoint.class); - Environment environment = Mockito.mock(Environment.class); static long nanoTime = 123; @@ -112,10 +89,12 @@ public void testLoginTimeout(@Mocked Vertx vertx) { public void testHighwayClientSSL(@Mocked Vertx vertx) throws Exception { new MockUp() { @Mock - boolean blockDeploy(Vertx vertx, + Map blockDeploy(Vertx vertx, Class cls, DeploymentOptions options) { - return true; + Map result = new HashMap<>(); + result.put("code", true); + return result; } }; @@ -124,132 +103,4 @@ boolean blockDeploy(Vertx vertx, ClientPoolManager clientMgr = Deencapsulation.getField(client, "clientMgr"); Assertions.assertSame(vertx, Deencapsulation.getField(clientMgr, "vertx")); } - - private Object doTestSend(Vertx vertx, HighwayClientConnectionPool pool, HighwayClientConnection tcpClient, - Object decodedResponse) throws Exception { - new MockUp() { - @Mock - boolean blockDeploy(Vertx vertx, - Class cls, - DeploymentOptions options) { - return true; - } - }; - - new MockUp>() { - @Mock - public HighwayClientConnectionPool findClientPool(boolean sync) { - return pool; - } - }; - - new MockUp() { - @Mock - public OperationProtobuf getOrCreateOperation(Invocation operationMeta) { - return operationProtobuf; - } - }; - - new MockUp() { - @Mock - HighwayClientConnection findOrCreateClient(String endpoint) { - return tcpClient; - } - }; - - new MockUp() { - @Mock - public Buffer encodeRequest(Invocation invocation, OperationProtobuf operationProtobuf, - long msgId) { - return null; - } - - @Mock - Response decodeResponse(Invocation invocation, OperationProtobuf operationProtobuf, TcpData tcpData) - throws Throwable { - if (decodedResponse instanceof Response) { - return (Response) decodedResponse; - } - - throw (Throwable) decodedResponse; - } - }; - - client.init(vertx); - - Mockito.when(invocation.getOperationMeta()).thenReturn(operationMeta); - Mockito.when(invocation.getEndpoint()).thenReturn(endpoint); - Mockito.when(invocation.getEndpoint().getEndpoint()).thenReturn("endpoint"); - Mockito.when(invocation.getResponseExecutor()).thenReturn(new ReactiveExecutor()); - Mockito.when(invocation.getInvocationStageTrace()).thenReturn(invocationStageTrace); - Mockito.when(operationMeta.getConfig()).thenReturn(Mockito.mock(OperationConfig.class)); - - Holder result = new Holder<>(); - client.send(invocation, ar -> result.value = ar.getResult()); - - return result.value; - } - - @Test - public void testSend_success(@Mocked Vertx vertx, @Mocked HighwayClientConnectionPool pool, - @Mocked HighwayClientConnection tcpClient) throws Exception { - new MockUp() { - @Mock - void send(AbstractTcpClientPackage tcpClientPackage, TcpResponseCallback callback) { - callback.success(null); - } - }; - new MockUp() { - @Mock - public long getFinishWriteToBuffer() { - return nanoTime; - } - }; - Object result = doTestSend(vertx, pool, tcpClient, Response.ok("ok")); - - Assertions.assertEquals("ok", result); - Assertions.assertEquals(nanoTime, invocationStageTrace.getStartClientFiltersResponse()); - Assertions.assertEquals(nanoTime, invocationStageTrace.getFinishClientFiltersResponse()); - - Assertions.assertEquals(nanoTime, invocationStageTrace.getFinishGetConnection()); - Assertions.assertEquals(nanoTime, invocationStageTrace.getFinishWriteToBuffer()); - Assertions.assertEquals(nanoTime, invocationStageTrace.getFinishReceiveResponse()); - } - - @Test - public void testSend_success_decode_failed(@Mocked Vertx vertx, @Mocked HighwayClientConnectionPool pool, - @Mocked HighwayClientConnection tcpClient) throws Exception { - new MockUp() { - @Mock - void send(AbstractTcpClientPackage tcpClientPackage, TcpResponseCallback callback) { - callback.success(null); - } - }; - - Object result = doTestSend(vertx, pool, tcpClient, new InvocationException(Status.BAD_REQUEST, (Object) "failed")); - - Assertions.assertEquals("failed", ((InvocationException) result).getErrorData()); - Assertions.assertEquals(nanoTime, invocationStageTrace.getStartClientFiltersResponse()); - Assertions.assertEquals(nanoTime, invocationStageTrace.getFinishClientFiltersResponse()); - } - - @Test - public void testSend_failed(@Mocked Vertx vertx, @Mocked HighwayClientConnectionPool pool, - @Mocked HighwayClientConnection tcpClient) throws Exception { - new MockUp() { - @Mock - void send(AbstractTcpClientPackage tcpClientPackage, TcpResponseCallback callback) { - callback.fail(new InvocationException(Status.BAD_REQUEST, (Object) "failed")); - } - }; - - Object result = doTestSend(vertx, - pool, - tcpClient, - null); - - Assertions.assertEquals("failed", ((InvocationException) result).getErrorData()); - Assertions.assertEquals(nanoTime, invocationStageTrace.getStartClientFiltersResponse()); - Assertions.assertEquals(nanoTime, invocationStageTrace.getFinishClientFiltersResponse()); - } } diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayCodec.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayCodec.java deleted file mode 100644 index 9de05d4c49e..00000000000 --- a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayCodec.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.servicecomb.transport.highway; - -import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.codec.protobuf.definition.OperationProtobuf; -import org.apache.servicecomb.codec.protobuf.definition.RequestRootDeserializer; -import org.apache.servicecomb.codec.protobuf.definition.RequestRootSerializer; -import org.apache.servicecomb.codec.protobuf.definition.ResponseRootSerializer; -import org.apache.servicecomb.core.Endpoint; -import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.definition.MicroserviceMeta; -import org.apache.servicecomb.core.definition.OperationMeta; -import org.apache.servicecomb.core.definition.SchemaMeta; -import org.apache.servicecomb.foundation.vertx.server.TcpParser; -import org.apache.servicecomb.foundation.vertx.tcp.TcpOutputStream; -import org.apache.servicecomb.transport.highway.message.RequestHeader; -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; -import org.mockito.Mockito; - -import io.netty.buffer.ByteBuf; -import io.vertx.core.buffer.Buffer; -import mockit.Mocked; - -@SuppressWarnings({"unchecked", "rawtypes"}) -public class TestHighwayCodec { - private OperationProtobuf operationProtobuf = null; - - private Buffer bodyBuffer = null; - - private RequestRootSerializer requestSerializer = null; - - private RequestRootDeserializer requestRootDeserializer = null; - - private SchemaMeta schemaMeta = null; - - private OperationMeta operationMeta = null; - - private MicroserviceMeta microserviceMeta = null; - - private ByteBuf lByteBuf = null; - - private ByteBuffer nioBuffer = null; - - private Invocation invocation = null; - - @BeforeClass - public static void setupClass() { - } - - @Before - public void setUp() { - - operationProtobuf = Mockito.mock(OperationProtobuf.class); - - bodyBuffer = Mockito.mock(Buffer.class); - - requestSerializer = Mockito.mock(RequestRootSerializer.class); - - requestRootDeserializer = Mockito.mock(RequestRootDeserializer.class); - - schemaMeta = Mockito.mock(SchemaMeta.class); - - operationMeta = Mockito.mock(OperationMeta.class); - - microserviceMeta = Mockito.mock(MicroserviceMeta.class); - - lByteBuf = Mockito.mock(ByteBuf.class); - - nioBuffer = Mockito.mock(ByteBuffer.class); - - invocation = Mockito.mock(Invocation.class); - } - - @After - public void tearDown() { - - operationProtobuf = null; - - bodyBuffer = null; - - requestSerializer = null; - - schemaMeta = null; - - operationMeta = null; - - lByteBuf = null; - - nioBuffer = null; - - invocation = null; - } - - - @Test - public void test_decode_request_successful_and_not_copy_header(@Mocked Endpoint endpoint) throws Exception { - // test decode request not thrown exception and not copy header - // header should copied before invocation start. - commonMock(); - - Invocation invocation = new Invocation(endpoint, operationMeta, null); - - invocation.addContext("X-B3-traceId", "test1"); - Assertions.assertEquals("test1", invocation.getContext("X-B3-traceId")); - - RequestHeader headers = new RequestHeader(); - Map context = new HashMap<>(); - headers.setContext(context); - HighwayCodec.decodeRequest(invocation, headers, operationProtobuf, bodyBuffer); - Assertions.assertEquals("test1", invocation.getContext("X-B3-traceId")); - - context.put("X-B3-traceId", "test2"); - HighwayCodec.decodeRequest(invocation, headers, operationProtobuf, bodyBuffer); - Assertions.assertEquals("test1", invocation.getContext("X-B3-traceId")); - } - - @Test - public void testEncodeResponse() { - boolean status = true; - ResponseRootSerializer bodySchema = Mockito.mock(ResponseRootSerializer.class); - try { - commonMock(); - Object data = new Object(); - Mockito.when(bodySchema.serialize(data)).thenReturn(new byte[0]); - HighwayCodec.encodeResponse(23432142, null, bodySchema, data); - } catch (Exception e) { - e.printStackTrace(); - status = false; - } - Assertions.assertTrue(status); - } - - @Test - public void testEncodeRequest() { - boolean status = true; - try { - commonMock(); - Map args = new HashMap<>(0); - Mockito.when(invocation.getInvocationArguments()).thenReturn(args); - Mockito.when(requestSerializer.serialize(args)).thenReturn(new byte[0]); - TcpOutputStream os = HighwayCodec.encodeRequest(0, invocation, operationProtobuf); - Assertions.assertNotNull(os); - Assertions.assertArrayEquals(TcpParser.TCP_MAGIC, os.getBuffer().getBytes(0, 7)); - } catch (Exception e) { - e.printStackTrace(); - status = false; - } - Assertions.assertTrue(status); - } - - private void commonMock() { - Mockito.when(operationProtobuf.getRequestRootSerializer()).thenReturn(requestSerializer); - Mockito.when(operationProtobuf.getRequestRootDeserializer()).thenReturn(requestRootDeserializer); - Mockito.when(bodyBuffer.getByteBuf()).thenReturn(lByteBuf); - Mockito.when(bodyBuffer.getBytes()).thenReturn(new byte[0]); - Mockito.when(lByteBuf.nioBuffer()).thenReturn(nioBuffer); - - Mockito.when(operationMeta.getSchemaMeta()).thenReturn(schemaMeta); - Mockito.when(schemaMeta.getMicroserviceMeta()).thenReturn(microserviceMeta); - } -} diff --git a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayTransport.java b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayTransport.java index fe387de8073..18d674f00f0 100644 --- a/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayTransport.java +++ b/transports/transport-highway/src/test/java/org/apache/servicecomb/transport/highway/TestHighwayTransport.java @@ -36,7 +36,7 @@ import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; -import io.vertx.core.file.impl.FileResolverImpl; +import io.vertx.core.impl.SysProps; public class TestHighwayTransport { private static final Logger LOGGER = LoggerFactory.getLogger(TestHighwayTransport.class); @@ -70,7 +70,7 @@ public void setUp() { .thenReturn(-1); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); } diff --git a/transports/transport-rest/pom.xml b/transports/transport-rest/pom.xml index 225154a2dc8..933e7c8858c 100644 --- a/transports/transport-rest/pom.xml +++ b/transports/transport-rest/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb transports - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT transport-rest Java Chassis::Transports::Rest diff --git a/transports/transport-rest/transport-rest-client/pom.xml b/transports/transport-rest/transport-rest-client/pom.xml index 8688dd590e9..49175b2140a 100644 --- a/transports/transport-rest/transport-rest-client/pom.xml +++ b/transports/transport-rest/transport-rest-client/pom.xml @@ -22,12 +22,16 @@ org.apache.servicecomb transport-rest - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT transport-rest-client Java Chassis::Transports::Rest::Client + + io.vertx + vertx-rx-java3 + org.apache.servicecomb foundation-vertx diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpClientRequestFactory.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpClientRequestFactory.java index fe36469be89..8b2f3afc136 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpClientRequestFactory.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpClientRequestFactory.java @@ -47,4 +47,3 @@ public interface HttpClientRequestFactory { Future create(Invocation invocation, HttpClient httpClient, RequestOptions options) throws Throwable; } - diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpTransportHttpClientOptionsSPI.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpTransportHttpClientOptionsSPI.java index 3c8cc1dbbf7..1091167ceed 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpTransportHttpClientOptionsSPI.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/HttpTransportHttpClientOptionsSPI.java @@ -22,6 +22,7 @@ import io.vertx.core.VertxOptions; import io.vertx.core.http.HttpClientOptions; import io.vertx.core.http.HttpVersion; +import io.vertx.core.http.PoolOptions; public class HttpTransportHttpClientOptionsSPI implements HttpClientOptionsSPI { public static final String CLIENT_NAME = "http-transport-client"; @@ -124,6 +125,11 @@ public int getKeepAliveTimeout() { return TransportClientConfig.getConnectionKeepAliveTimeoutInSeconds(); } + @Override + public boolean enableLogActivity() { + return TransportClientConfig.enableLogActivity(); + } + @Override public int getHttp2MultiplexingLimit() { return HttpClientOptions.DEFAULT_HTTP2_MULTIPLEXING_LIMIT; @@ -131,12 +137,12 @@ public int getHttp2MultiplexingLimit() { @Override public int getHttp2MaxPoolSize() { - return HttpClientOptions.DEFAULT_HTTP2_MAX_POOL_SIZE; + return PoolOptions.DEFAULT_HTTP2_MAX_POOL_SIZE; } @Override public boolean isUseAlpn() { - return HttpClientOptions.DEFAULT_USE_ALPN; + return !HttpClientOptions.DEFAULT_ALPN_VERSIONS.isEmpty(); } @Override diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientCodecFilter.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientCodecFilter.java index e77f58e99df..72ff3f00e85 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientCodecFilter.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientCodecFilter.java @@ -23,16 +23,17 @@ import org.apache.servicecomb.common.rest.RestConst; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; import org.springframework.beans.factory.annotation.Autowired; import io.vertx.core.http.HttpClientRequest; -public class RestClientCodecFilter implements ConsumerFilter { +public class RestClientCodecFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { public static final String NAME = "rest-client-codec"; protected RestClientTransportContextFactory transportContextFactory; @@ -52,7 +53,7 @@ public boolean enabledForTransport(String transport) { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 1990; } @@ -76,23 +77,17 @@ public RestClientCodecFilter setDecoder(RestClientDecoder decoder) { @Override public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { - invocation.getInvocationStageTrace().startGetConnection(); - startClientFiltersRequest(invocation); + invocation.getInvocationStageTrace().startConsumerConnection(); CompletionStage createRequest = transportContextFactory.createHttpClientRequest(invocation).toCompletionStage() - .whenComplete((c, e) -> invocation.getInvocationStageTrace().finishGetConnection()); + .whenComplete((c, e) -> invocation.getInvocationStageTrace().finishConsumerConnection()); return CompletableFuture.completedFuture(null) .thenCompose(v -> createRequest) .thenAccept(httpClientRequest -> prepareTransportContext(invocation, httpClientRequest)) .thenAccept(v -> invocation.onStartSendRequest()) .thenAccept(v -> encoder.encode(invocation)) .thenCompose(v -> nextNode.onFilter(invocation)) - .thenApply(response -> decoder.decode(invocation, response)) - .whenComplete((response, throwable) -> finishClientFiltersResponse(invocation)); - } - - protected void startClientFiltersRequest(Invocation invocation) { - invocation.getInvocationStageTrace().startClientFiltersRequest(); + .thenApply(response -> decoder.decode(invocation, response)); } protected void prepareTransportContext(Invocation invocation, HttpClientRequest httpClientRequest) { @@ -118,8 +113,4 @@ protected void copyExtraHttpHeaders(Invocation invocation, HttpClientRequest htt } }); } - - protected void finishClientFiltersResponse(Invocation invocation) { - invocation.getInvocationStageTrace().finishClientFiltersResponse(); - } } diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java index cd3c4e89ef0..a2534d85450 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientDecoder.java @@ -30,6 +30,7 @@ import com.fasterxml.jackson.databind.JavaType; +import io.reactivex.rxjava3.core.Flowable; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; import jakarta.ws.rs.core.HttpHeaders; @@ -38,24 +39,32 @@ public class RestClientDecoder { private static final Logger LOGGER = LoggerFactory.getLogger(RestClientDecoder.class); public Response decode(Invocation invocation, Response response) { + invocation.getInvocationStageTrace().startConsumerDecodeResponse(); if (response.getResult() instanceof Buffer) { - Object result = extractBody(invocation, response); - response.entity(result); + Object result = extractBody(invocation, response, response.getResult()); + response.setResult(result); if (response.isFailed()) { throw Exceptions.create(response.getStatus(), response.getResult()); } } + if (response.getResult() instanceof Flowable) { + Flowable flowable = response.getResult(); + Flowable encoded = flowable.map(buffer -> extractBody(invocation, response, buffer)); + response.setResult(encoded); + } + + invocation.getInvocationStageTrace().finishConsumerDecodeResponse(); return response; } - protected Object extractBody(Invocation invocation, Response response) { + protected Object extractBody(Invocation invocation, Response response, Buffer buffer) { ProduceProcessor produceProcessor = safeFindProduceProcessor(invocation, response); JavaType responseType = invocation.findResponseType(response.getStatusCode()); try { - return produceProcessor.decodeResponse((Buffer) response.getResult(), responseType); + return produceProcessor.decodeResponse(buffer, responseType); } catch (Exception e) { throw createDecodeException(invocation, response, e); } diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientEncoder.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientEncoder.java index b9c1e16a123..cc16c832f08 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientEncoder.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientEncoder.java @@ -23,6 +23,7 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -40,8 +41,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; import jakarta.servlet.http.Part; @@ -58,8 +57,10 @@ public class RestClientEncoder { public void encode(Invocation invocation) { try { + invocation.getInvocationStageTrace().startConsumerEncodeRequest(); EncoderSession encoderSession = new EncoderSession(invocation); encoderSession.doEncode(); + invocation.getInvocationStageTrace().finishConsumerEncodeRequest(); } catch (Exception e) { throw new InvocationException(BAD_REQUEST, FAILED_TO_ENCODE_REST_CLIENT_REQUEST, e.getMessage(), e); } @@ -96,8 +97,8 @@ protected void doEncode() throws Exception { } protected void swaggerArgumentsToRequest() throws Exception { - RestCodec - .argsToRest(invocation.getSwaggerArguments(), transportContext.getRestOperationMeta(), requestParameters); + RestCodec.argsToRest(invocation.getSwaggerArguments(), + transportContext.getRestOperationMeta(), requestParameters); } protected void writeCookies(Map cookieMap) { @@ -148,22 +149,19 @@ protected void writeUrlEncodedForm(Map formMap) throws Exception } protected Buffer genUrlEncodedFormBuffer(Map formMap) throws Exception { - // 2x faster than UriComponentsBuilder - ByteBuf byteBuf = Unpooled.buffer(RestClientEncoder.FORM_BUFFER_SIZE); + Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); for (Entry entry : formMap.entrySet()) { - writeCharSequence(byteBuf, entry.getKey()); - byteBuf.writeByte('='); + writeCharSequence(buffer, entry.getKey()); + buffer.appendByte(((byte) '=')); String value = QueryCodec.convertToString(entry.getValue()); - String encodedValue = URLEncoder.encode(value, StandardCharsets.UTF_8.name()); - writeCharSequence(byteBuf, encodedValue); + String encodedValue = URLEncoder.encode(value, StandardCharsets.UTF_8); + writeCharSequence(buffer, encodedValue); - byteBuf.markWriterIndex(); - byteBuf.writeByte('&'); + buffer.appendByte(((byte) '&')); } - byteBuf.resetWriterIndex(); - return Buffer.buffer(byteBuf); + return buffer; } protected void writeChunkedForm(Map formMap) throws Exception { @@ -181,54 +179,65 @@ protected void writeChunkedForm(Map formMap) throws Exception { } protected Buffer genChunkedFormBuffer(Map formMap, String boundary) throws Exception { - ByteBuf byteBuf = Unpooled.buffer(RestClientEncoder.FORM_BUFFER_SIZE); + Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); for (Entry entry : formMap.entrySet()) { - writeCharSequence(byteBuf, "\r\n--"); - writeCharSequence(byteBuf, boundary); - writeCharSequence(byteBuf, "\r\nContent-Disposition: form-data; name=\""); - writeCharSequence(byteBuf, entry.getKey()); - writeCharSequence(byteBuf, "\"\r\n\r\n"); - - String value = QueryCodec.convertToString(entry.getValue()); - writeCharSequence(byteBuf, value); + Object content = entry.getValue(); + if (content instanceof List) { + for (Object item : ((List) content)) { + writeFormData(buffer, boundary, entry.getKey(), item); + } + } else { + writeFormData(buffer, boundary, entry.getKey(), entry.getValue()); + } } - return Buffer.buffer(byteBuf); + return buffer; + } + + private void writeFormData(Buffer buffer, String boundary, String key, Object data) throws Exception { + writeCharSequence(buffer, "\r\n--"); + writeCharSequence(buffer, boundary); + writeCharSequence(buffer, "\r\nContent-Disposition: form-data; name=\""); + writeCharSequence(buffer, key); + writeCharSequence(buffer, "\"\r\n\r\n"); + + String value = QueryCodec.convertToString(data); + writeCharSequence(buffer, value); } } - protected static void writeCharSequence(ByteBuf byteBuf, String value) { - byteBuf.writeCharSequence(value, StandardCharsets.UTF_8); + protected static void writeCharSequence(Buffer buffer, String value) { + buffer.appendString(value, "UTF-8"); } public static Buffer genFileBoundaryBuffer(Part part, String name, String boundary) { - ByteBuf byteBuf = Unpooled.buffer(); + Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); - writeCharSequence(byteBuf, "\r\n--"); - writeCharSequence(byteBuf, boundary); - writeCharSequence(byteBuf, "\r\nContent-Disposition: form-data; name=\""); - writeCharSequence(byteBuf, name); - writeCharSequence(byteBuf, "\"; filename=\""); - writeCharSequence(byteBuf, String.valueOf(part.getSubmittedFileName())); - writeCharSequence(byteBuf, "\"\r\n"); + writeCharSequence(buffer, "\r\n--"); + writeCharSequence(buffer, boundary); + writeCharSequence(buffer, "\r\nContent-Disposition: form-data; name=\""); + writeCharSequence(buffer, name); + writeCharSequence(buffer, "\"; filename=\""); + writeCharSequence(buffer, String.valueOf(part.getSubmittedFileName())); + writeCharSequence(buffer, "\"\r\n"); - writeCharSequence(byteBuf, "Content-Type: "); - writeCharSequence(byteBuf, part.getContentType()); - writeCharSequence(byteBuf, "\r\n"); + writeCharSequence(buffer, "Content-Type: "); + writeCharSequence(buffer, part.getContentType()); + writeCharSequence(buffer, "\r\n"); - writeCharSequence(byteBuf, "Content-Transfer-Encoding: binary\r\n"); + writeCharSequence(buffer, "Content-Transfer-Encoding: binary\r\n"); - writeCharSequence(byteBuf, "\r\n"); + writeCharSequence(buffer, "\r\n"); - return Buffer.buffer(byteBuf); + return buffer; } public static Buffer genBoundaryEndBuffer(String boundary) { - ByteBuf byteBuf = Unpooled.buffer(); + Buffer buffer = Buffer.buffer(RestClientEncoder.FORM_BUFFER_SIZE); - writeCharSequence(byteBuf, "\r\n--"); - writeCharSequence(byteBuf, boundary); - writeCharSequence(byteBuf, "--\r\n"); + writeCharSequence(buffer, "\r\n--"); + writeCharSequence(buffer, boundary); + writeCharSequence(buffer, "--\r\n"); - return Buffer.buffer(byteBuf); + return buffer; } } diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParametersImpl.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParametersImpl.java index 53d9de77284..398bde4f402 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParametersImpl.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientRequestParametersImpl.java @@ -21,18 +21,17 @@ import java.util.Map; import org.apache.servicecomb.foundation.common.utils.PartUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import io.vertx.core.MultiMap; import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpClientRequest; import jakarta.servlet.http.Part; public class RestClientRequestParametersImpl implements RestClientRequestParameters { - private static final Logger LOGGER = LoggerFactory.getLogger(RestClientRequestParametersImpl.class); + protected final HttpClientRequest httpClientRequest; protected final MultiMap headers; @@ -44,8 +43,9 @@ public class RestClientRequestParametersImpl implements RestClientRequestParamet protected Buffer bodyBuffer; - public RestClientRequestParametersImpl(MultiMap headers) { - this.headers = headers; + public RestClientRequestParametersImpl(HttpClientRequest httpClientRequest) { + this.httpClientRequest = httpClientRequest; + this.headers = this.httpClientRequest.headers(); } @Override @@ -106,15 +106,14 @@ public Multimap getUploads() { @SuppressWarnings("unchecked") @Override public void attach(String name, Object partOrList) { - if (partOrList == null) { - LOGGER.debug("null file is ignored, file name = [{}]", name); - return; - } - if (uploads == null) { uploads = ArrayListMultimap.create(); } + if (partOrList == null) { + return; + } + if (partOrList.getClass().isArray()) { for (Object part : (Object[]) partOrList) { uploads.put(name, PartUtils.getSinglePart(name, part)); @@ -131,4 +130,9 @@ public void attach(String name, Object partOrList) { uploads.put(name, PartUtils.getSinglePart(name, partOrList)); } + + @Override + public HttpClientRequest getHttpClientRequest() { + return this.httpClientRequest; + } } diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSender.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSender.java index 30ab71f7d37..b737736c243 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSender.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSender.java @@ -23,7 +23,6 @@ import java.util.concurrent.CompletableFuture; import org.apache.servicecomb.core.Invocation; -import org.apache.servicecomb.core.invocation.InvocationStageTrace; import org.apache.servicecomb.foundation.common.http.HttpStatus; import org.apache.servicecomb.foundation.vertx.executor.VertxContextExecutor; import org.apache.servicecomb.foundation.vertx.http.ReadStreamPart; @@ -36,9 +35,11 @@ import io.vertx.core.Future; import io.vertx.core.MultiMap; +import io.vertx.core.Promise; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpClientRequest; import io.vertx.core.http.HttpClientResponse; +import io.vertx.rxjava3.FlowableHelper; import jakarta.servlet.http.Part; public class RestClientSender { @@ -62,10 +63,8 @@ public RestClientSender(Invocation invocation) { } public CompletableFuture send() { - httpClientRequest.response().compose(response -> processResponse(response).compose(buffer -> { - future.complete(createResponse(response, buffer)); - return Future.succeededFuture(); - })).onFailure(future::completeExceptionally); + invocation.getInvocationStageTrace().startConsumerSendRequest(); + httpClientRequest.response().compose(this::processResponse).onFailure(future::completeExceptionally); CompletableFuture actualFuture = future.whenComplete(this::afterSend); VertxContextExecutor.create(transportContext.getVertxContext()).execute(this::runInVertxContext); @@ -78,12 +77,13 @@ protected void runInVertxContext() { if (e != null) { future.completeExceptionally(e); } - writeFinished(); + invocation.getInvocationStageTrace().finishConsumerSendRequest(); + invocation.getInvocationStageTrace().startWaitResponse(); }); } protected CompletableFuture sendInVertxContext() { - httpClientRequest.setTimeout(invocation.getOperationMeta().getConfig().getMsRequestTimeout()); + httpClientRequest.idleTimeout(invocation.getOperationMeta().getConfig().getMsRequestTimeout()); Multimap uploads = requestParameters.getUploads(); if (uploads == null) { @@ -102,10 +102,6 @@ protected CompletableFuture sendInVertxContext() { return sendFiles(); } - private void writeFinished() { - invocation.getInvocationStageTrace().finishWriteToBuffer(System.nanoTime()); - } - protected CompletableFuture sendFiles() { CompletableFuture sendFileFuture = CompletableFuture.completedFuture(null); @@ -136,14 +132,38 @@ private CompletableFuture sendFile(Part part, String name, String boundary }); } - protected Future processResponse(HttpClientResponse httpClientResponse) { + protected Future processResponse(HttpClientResponse httpClientResponse) { + Promise result = Promise.promise(); transportContext.setHttpClientResponse(httpClientResponse); - if (HttpStatus.isSuccess(httpClientResponse.statusCode()) && transportContext.isDownloadFile()) { + if (!HttpStatus.isSuccess(httpClientResponse.statusCode())) { + httpClientResponse.body().compose(buffer -> { + future.complete(createResponse(httpClientResponse, buffer)); + result.complete(); + return Future.succeededFuture(); + }); + return result.future(); + } + + if (transportContext.isDownloadFile()) { ReadStreamPart streamPart = new ReadStreamPart(transportContext.getVertxContext(), httpClientResponse); future.complete(createResponse(httpClientResponse, streamPart)); + result.complete(); + return result.future(); } - return httpClientResponse.body(); + + if (transportContext.isServerSendEvents()) { + future.complete(createResponse(httpClientResponse, FlowableHelper.toFlowable(httpClientResponse))); + result.complete(); + return result.future(); + } + + httpClientResponse.body().compose(buffer -> { + future.complete(createResponse(httpClientResponse, buffer)); + result.complete(); + return Future.succeededFuture(); + }); + return result.future(); } protected Response createResponse(HttpClientResponse httpClientResponse, Object result) { @@ -159,7 +179,7 @@ protected Response createResponse(HttpClientResponse httpClientResponse, Object } protected void afterSend(Response response, Throwable throwable) { - processMetrics(); + invocation.getInvocationStageTrace().finishWaitResponse(); if (throwable != null) { LOGGER.error("rest client send or receive failed, operation={}, method={}, endpoint={}, uri={}.", @@ -169,12 +189,4 @@ protected void afterSend(Response response, Throwable throwable) { httpClientRequest.getURI()); } } - - protected void processMetrics() { - InvocationStageTrace stageTrace = invocation.getInvocationStageTrace(); - // even failed and did not receive response, still set time for it - // that will help to know the real timeout time - stageTrace.finishReceiveResponse(); - stageTrace.startClientFiltersResponse(); - } } diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSenderFilter.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSenderFilter.java index 8ee02c36039..cf088c764eb 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSenderFilter.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientSenderFilter.java @@ -20,13 +20,14 @@ import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; import org.apache.servicecomb.core.filter.Filter; import org.apache.servicecomb.core.filter.FilterNode; -import org.apache.servicecomb.swagger.invocation.InvocationType; import org.apache.servicecomb.swagger.invocation.Response; -public class RestClientSenderFilter implements ConsumerFilter { +public class RestClientSenderFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { public static final String NAME = "rest-client-sender"; @Override @@ -40,7 +41,7 @@ public boolean enabledForTransport(String transport) { } @Override - public int getOrder(InvocationType invocationType, String application, String serviceName) { + public int getOrder() { return Filter.CONSUMER_LOAD_BALANCE_ORDER + 2000; } diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContext.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContext.java index 96ed8ae5271..5648588a714 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContext.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContext.java @@ -47,7 +47,7 @@ public RestClientTransportContext(RestOperationMeta restOperationMeta, Context v this.vertxContext = vertxContext; this.httpClientRequest = httpClientRequest; this.boundaryFactory = boundaryFactory; - this.requestParameters = new RestClientRequestParametersImpl(httpClientRequest.headers()); + this.requestParameters = new RestClientRequestParametersImpl(httpClientRequest); } public RestOperationMeta getRestOperationMeta() { @@ -58,6 +58,10 @@ public boolean isDownloadFile() { return restOperationMeta.isDownloadFile(); } + public boolean isServerSendEvents() { + return restOperationMeta.isServerSendEvents(); + } + @Override public Context getVertxContext() { return vertxContext; diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContextFactory.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContextFactory.java index cd1d90ab40b..a4b0fc12dcf 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContextFactory.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/RestClientTransportContextFactory.java @@ -65,10 +65,13 @@ protected RestClientTransportContext doCreate(Invocation invocation, HttpClientR throws Throwable { RestOperationMeta restOperationMeta = RestMetaUtils.getRestOperationMeta(invocation.getOperationMeta()); HttpClientWithContext httpClientWithContext = findHttpClientPool(invocation); - return new RestClientTransportContext(restOperationMeta, + RestClientTransportContext context = new RestClientTransportContext(restOperationMeta, httpClientWithContext.context(), httpClientRequest, boundaryFactory); + invocation.getHandlerContext().put(RestConst.INVOCATION_HANDLER_REQUESTCLIENT, + context.getRequestParameters()); + return context; } protected HttpClientWithContext findHttpClientPool(Invocation invocation) { diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportClientConfig.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportClientConfig.java index 1e07973e2fc..e1f2c6042d1 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportClientConfig.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportClientConfig.java @@ -21,6 +21,7 @@ import org.apache.servicecomb.transport.common.TransportConfigUtils; import io.vertx.core.http.HttpClientOptions; +import io.vertx.core.http.PoolOptions; public final class TransportClientConfig { private static final int DEFAULT_IDLE_TIME_OUT = 150; @@ -38,7 +39,7 @@ public static int getThreadCount() { public static int getHttp2ConnectionMaxPoolSize() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.http2.maxPoolSize", - HttpClientOptions.DEFAULT_HTTP2_MAX_POOL_SIZE); + PoolOptions.DEFAULT_HTTP2_MAX_POOL_SIZE); } public static int getHttp2MultiplexingLimit() { @@ -57,7 +58,7 @@ public static boolean isHttp2TransportClientEnabled() { public static int getConnectionMaxPoolSize() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.client.connection.maxPoolSize", - HttpClientOptions.DEFAULT_MAX_POOL_SIZE); + PoolOptions.DEFAULT_MAX_POOL_SIZE); } public static int getHttp2ConnectionIdleTimeoutInSeconds() { @@ -80,6 +81,11 @@ public static int getConnectionKeepAliveTimeoutInSeconds() { .getIntProperty("servicecomb.rest.client.connection.keepAliveTimeoutInSeconds", DEFAULT_KEEP_ALIVE_TIME_OUT); } + public static boolean enableLogActivity() { + return LegacyPropertyFactory + .getBooleanProperty("servicecomb.rest.client.enableLogActivity", false); + } + public static int getHttp2ConnectionKeepAliveTimeoutInSeconds() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.client.http2.connection.keepAliveTimeoutInSeconds", @@ -89,7 +95,7 @@ public static int getHttp2ConnectionKeepAliveTimeoutInSeconds() { public static boolean getConnectionCompression() { return LegacyPropertyFactory .getBooleanProperty("servicecomb.rest.client.connection.compression", - HttpClientOptions.DEFAULT_TRY_USE_COMPRESSION); + HttpClientOptions.DEFAULT_DECOMPRESSION_SUPPORTED); } public static int getMaxHeaderSize() { @@ -100,7 +106,7 @@ public static int getMaxHeaderSize() { public static int getMaxWaitQueueSize() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.client.maxWaitQueueSize", - HttpClientOptions.DEFAULT_MAX_WAIT_QUEUE_SIZE); + PoolOptions.DEFAULT_MAX_WAIT_QUEUE_SIZE); } public static boolean isHttpTransportClientEnabled() { @@ -111,6 +117,6 @@ public static boolean isHttpTransportClientEnabled() { public static int getConnectionTimeoutInMillis() { return LegacyPropertyFactory - .getIntProperty("servicecomb.rest.client.connection.timeoutInMillis", 1000); + .getIntProperty("servicecomb.rest.client.connection.timeoutInMillis", 60000); } } diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportRestClientConfiguration.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportRestClientConfiguration.java index f510cf2e7d2..3775744e173 100644 --- a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportRestClientConfiguration.java +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/TransportRestClientConfiguration.java @@ -16,14 +16,13 @@ */ package org.apache.servicecomb.transport.rest.client; -import org.apache.servicecomb.core.filter.ConsumerFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TransportRestClientConfiguration { @Bean - public ConsumerFilter restClientSenderFilter() { + public RestClientSenderFilter restClientSenderFilter() { return new RestClientSenderFilter(); } @@ -32,6 +31,11 @@ public RestClientCodecFilter restClientCodecFilter() { return new RestClientCodecFilter(); } + @Bean + public WebSocketClientCodecFilter webSocketClientCodecFilter() { + return new WebSocketClientCodecFilter(); + } + @Bean public RestClientDecoder restClientDecoder() { return new RestClientDecoder(); diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientCodecFilter.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientCodecFilter.java new file mode 100644 index 00000000000..73a38dfe8f2 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientCodecFilter.java @@ -0,0 +1,148 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.client; + +import java.util.concurrent.CompletableFuture; + +import org.apache.commons.lang3.StringUtils; +import org.apache.servicecomb.common.rest.RestConst; +import org.apache.servicecomb.common.rest.WebSocketTransportContext; +import org.apache.servicecomb.common.rest.definition.RestMetaUtils; +import org.apache.servicecomb.common.rest.definition.RestOperationMeta; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.filter.AbstractFilter; +import org.apache.servicecomb.core.filter.ConsumerFilter; +import org.apache.servicecomb.core.filter.EdgeFilter; +import org.apache.servicecomb.core.filter.Filter; +import org.apache.servicecomb.core.filter.FilterNode; +import org.apache.servicecomb.foundation.common.net.URIEndpointObject; +import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI; +import org.apache.servicecomb.foundation.vertx.client.http.HttpClients; +import org.apache.servicecomb.registry.definition.DefinitionConst; +import org.apache.servicecomb.swagger.invocation.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.http.ServerWebSocket; +import io.vertx.core.http.WebSocket; +import io.vertx.core.http.WebSocketClient; + +public class WebSocketClientCodecFilter extends AbstractFilter implements ConsumerFilter, EdgeFilter { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketClientCodecFilter.class); + + public static final String NAME = "websocket-client"; + + @Override + public String getName() { + return NAME; + } + + @Override + public boolean enabledForTransport(String transport) { + return CoreConst.WEBSOCKET.equals(transport); + } + + @Override + public int getOrder() { + return Filter.CONSUMER_LOAD_BALANCE_ORDER + 2000; + } + + @Override + public CompletableFuture onFilter(Invocation invocation, FilterNode nextNode) { + invocation.getInvocationStageTrace().startConsumerConnection(); + + CompletableFuture createWebSocket = new CompletableFuture<>(); + URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); + HttpClientOptionsSPI optionsSPI; + if (endpoint.isHttp2Enabled()) { + optionsSPI = SPIServiceUtils.getTargetService(HttpClientOptionsSPI.class, + HttpTransportHttpClientOptionsSPI.class); + } else { + optionsSPI = SPIServiceUtils.getTargetService(HttpClientOptionsSPI.class, + Http2TransportHttpClientOptionsSPI.class); + } + WebSocketClient webSocketClient = HttpClients.createWebSocketClient(optionsSPI, endpoint.isSslEnabled()); + + try { + webSocketClient.connect(endpoint.getPort(), endpoint.getHostOrIp(), createRequestPath(invocation, + RestMetaUtils.getRestOperationMeta(invocation.getOperationMeta()))) + .onComplete(asyncResult -> { + invocation.getInvocationStageTrace().finishConsumerConnection(); + if (asyncResult.failed()) { + createWebSocket.completeExceptionally(asyncResult.cause()); + return; + } + if (invocation.isEdge()) { + WebSocketTransportContext parentContext = invocation.getTransportContext(); + ServerWebSocket serverWebSocket = parentContext.getServerWebSocket(); + WebSocket clientWebSocket = asyncResult.result(); + serverWebSocket.closeHandler(v -> { + if (!clientWebSocket.isClosed()) { + clientWebSocket.close(); + } + }); + serverWebSocket.textMessageHandler(clientWebSocket::writeTextMessage); + serverWebSocket.binaryMessageHandler(clientWebSocket::writeBinaryMessage); + serverWebSocket.exceptionHandler(e -> { + LOGGER.warn("consumer exception.", e); + if (!serverWebSocket.isClosed()) { + serverWebSocket.close(); + } + }); + clientWebSocket.closeHandler(v -> { + if (!serverWebSocket.isClosed()) { + serverWebSocket.close(); + } + }); + clientWebSocket.textMessageHandler(serverWebSocket::writeTextMessage); + clientWebSocket.binaryMessageHandler(serverWebSocket::writeBinaryMessage); + clientWebSocket.exceptionHandler(e -> { + LOGGER.warn("producer exception.", e); + if (!clientWebSocket.isClosed()) { + clientWebSocket.close(); + } + }); + } + invocation.setTransportContext(new WebSocketClientTransportContext( + asyncResult.result())); + createWebSocket.complete(Response.createSuccess(asyncResult.result())); + }); + } catch (Exception e) { + createWebSocket.completeExceptionally(e); + } + + return createWebSocket; + } + + protected String createRequestPath(Invocation invocation, RestOperationMeta restOperationMeta) throws Exception { + String path = invocation.getLocalContext(RestConst.REST_CLIENT_REQUEST_PATH); + if (path == null) { + path = restOperationMeta.getPathBuilder().createRequestPath(invocation.getSwaggerArguments()); + } + + URIEndpointObject endpoint = (URIEndpointObject) invocation.getEndpoint().getAddress(); + String urlPrefix = endpoint.getFirst(DefinitionConst.URL_PREFIX); + if (StringUtils.isEmpty(urlPrefix) || path.startsWith(urlPrefix)) { + return path; + } + + return urlPrefix + path; + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientTransportContext.java b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientTransportContext.java new file mode 100644 index 00000000000..89d43bcde41 --- /dev/null +++ b/transports/transport-rest/transport-rest-client/src/main/java/org/apache/servicecomb/transport/rest/client/WebSocketClientTransportContext.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.client; + +import org.apache.servicecomb.swagger.invocation.context.TransportContext; + +import io.vertx.core.http.WebSocket; + +public class WebSocketClientTransportContext implements TransportContext { + + protected final WebSocket webSocketClient; + + public WebSocketClientTransportContext(WebSocket webSocketClient) { + this.webSocketClient = webSocketClient; + } + + public WebSocket getWebSocketClient() { + return webSocketClient; + } +} diff --git a/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI b/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI index b7e5b35aaed..5eb8c592516 100644 --- a/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI +++ b/transports/transport-rest/transport-rest-client/src/main/resources/META-INF/services/org.apache.servicecomb.foundation.vertx.client.http.HttpClientOptionsSPI @@ -16,4 +16,4 @@ # org.apache.servicecomb.transport.rest.client.HttpTransportHttpClientOptionsSPI -org.apache.servicecomb.transport.rest.client.Http2TransportHttpClientOptionsSPI \ No newline at end of file +org.apache.servicecomb.transport.rest.client.Http2TransportHttpClientOptionsSPI diff --git a/transports/transport-rest/transport-rest-servlet/pom.xml b/transports/transport-rest/transport-rest-servlet/pom.xml index 5e96b7480fc..0bdac6777ef 100644 --- a/transports/transport-rest/transport-rest-servlet/pom.xml +++ b/transports/transport-rest/transport-rest-servlet/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb transport-rest - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT transport-rest-servlet Java Chassis::Transports::Rest::Servlet diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServlet.java b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServlet.java index 168efb4d087..9c94e49d58c 100644 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServlet.java +++ b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestRestServlet.java @@ -30,7 +30,7 @@ import org.mockito.Mockito; import org.springframework.core.env.Environment; -import io.vertx.core.file.impl.FileResolverImpl; +import io.vertx.core.impl.SysProps; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -51,7 +51,7 @@ public void setUp() { .thenReturn(0); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); restservlet = new RestServlet(); diff --git a/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletRestTransport.java b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletRestTransport.java index d9d396fe27f..6875965a109 100644 --- a/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletRestTransport.java +++ b/transports/transport-rest/transport-rest-servlet/src/test/java/org/apache/servicecomb/transport/rest/servlet/TestServletRestTransport.java @@ -32,7 +32,7 @@ import org.mockito.Mockito; import org.springframework.core.env.Environment; -import io.vertx.core.file.impl.FileResolverImpl; +import io.vertx.core.impl.SysProps; import mockit.Expectations; public class TestServletRestTransport { @@ -48,7 +48,7 @@ public void setUp() { .thenReturn(0); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); transport = new ServletRestTransport(); diff --git a/transports/transport-rest/transport-rest-vertx/pom.xml b/transports/transport-rest/transport-rest-vertx/pom.xml index 308d099d432..7a3475d38dd 100644 --- a/transports/transport-rest/transport-rest-vertx/pom.xml +++ b/transports/transport-rest/transport-rest-vertx/pom.xml @@ -22,7 +22,7 @@ org.apache.servicecomb transport-rest - 3.0.0-SNAPSHOT + 3.4.0-SNAPSHOT transport-rest-vertx Java Chassis::Transports::Rest::Vertx diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/AbstractVertxHttpDispatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/AbstractVertxHttpDispatcher.java index 3dfcd8e9f98..8963c6f8d9b 100644 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/AbstractVertxHttpDispatcher.java +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/AbstractVertxHttpDispatcher.java @@ -43,4 +43,3 @@ protected BodyHandler createBodyHandler() { return bodyHandler; } } - diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java index f6e02fe0223..cc74e6d0a8a 100644 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestBodyHandler.java @@ -24,14 +24,12 @@ import java.io.File; import java.util.List; import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import com.google.common.annotations.VisibleForTesting; import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; import org.apache.servicecomb.swagger.invocation.exception.ExceptionFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.http.HttpHeaderValues; @@ -41,10 +39,11 @@ import io.vertx.core.file.FileSystem; import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpServerRequest; +import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.http.HttpVersion; import io.vertx.ext.web.FileUpload; import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.handler.BodyHandler; -import io.vertx.ext.web.handler.impl.BodyHandlerImpl; import io.vertx.ext.web.impl.FileUploadImpl; import io.vertx.ext.web.impl.RoutingContextInternal; @@ -56,8 +55,6 @@ */ public class RestBodyHandler implements BodyHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(BodyHandlerImpl.class); - private long bodyLimit = DEFAULT_BODY_LIMIT; private boolean handleFileUploads; @@ -98,7 +95,9 @@ boolean isDeleteUploadedFilesOnEnd() { @Override public void handle(RoutingContext context) { - HttpServerRequest request = context.request(); + final HttpServerRequest request = context.request(); + final HttpServerResponse response = context.response(); + if (request.headers().contains(HttpHeaders.UPGRADE, HttpHeaders.WEBSOCKET, true)) { context.next(); return; @@ -112,11 +111,59 @@ public void handle(RoutingContext context) { // we need to keep state since we can be called again on reroute if (!((RoutingContextInternal) context).seenHandler(RoutingContextInternal.BODY_HANDLER)) { - long contentLength = isPreallocateBodyBuffer ? parseContentLengthHeader(request) : -1; - BHandler handler = new BHandler(context, contentLength); - request.handler(handler); - request.endHandler(v -> handler.end()); ((RoutingContextInternal) context).visitHandler(RoutingContextInternal.BODY_HANDLER); + + // Check if a request has a request body. + // A request with a body __must__ either have `transfer-encoding` + // or `content-length` headers set. + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3 + final long parsedContentLength = parseContentLengthHeader(request); + // http2 never transmits a `transfer-encoding` as frames are chunks. + final boolean hasTransferEncoding = + request.version() == HttpVersion.HTTP_2 || request.headers().contains(HttpHeaders.TRANSFER_ENCODING); + + if (!hasTransferEncoding && parsedContentLength == -1) { + // there is no "body", so we can skip this handler + context.next(); + return; + } + + // before parsing the body we can already discard a bad request just by inspecting the content-length against + // the body limit, this will reduce load, on the server by totally skipping parsing the request body + if (bodyLimit != -1 && parsedContentLength != -1) { + if (parsedContentLength > bodyLimit) { + context.fail(413); + return; + } + } + + // handle expectations + // https://httpwg.org/specs/rfc7231.html#header.expect + final String expect = request.getHeader(HttpHeaders.EXPECT); + if (expect != null) { + // requirements validation + if (expect.equalsIgnoreCase("100-continue")) { + // A server that receives a 100-continue expectation in an HTTP/1.0 request MUST ignore that expectation. + if (request.version() != HttpVersion.HTTP_1_0) { + // signal the client to continue + response.writeContinue(); + } + } else { + // the server cannot meet the expectation, we only know about 100-continue + context.fail(417); + return; + } + } + + final BHandler handler = new BHandler(context, isPreallocateBodyBuffer ? parsedContentLength : -1); + boolean ended = request.isEnded(); + if (!ended) { + request + // resume the request (if paused) + .handler(handler) + .endHandler(handler::end) + .resume(); + } } else { // on reroute we need to re-merge the form params if that was desired if (mergeFormAttributes && request.isExpectMultipart()) { @@ -185,13 +232,11 @@ private class BHandler implements Handler { Buffer body; - boolean failed; - - AtomicInteger uploadCount = new AtomicInteger(); + volatile boolean failed; - AtomicBoolean cleanup = new AtomicBoolean(false); + final AtomicInteger uploadCount = new AtomicInteger(); - boolean ended; + volatile boolean ended; long uploadSize = 0L; @@ -240,7 +285,7 @@ public BHandler(RoutingContext context, long contentLength) { long size = uploadSize + upload.size(); if (size > bodyLimit) { failed = true; - cancelAndCleanupFileUploads(); + context.cancelAndCleanupFileUploads(); context.fail(413); return; } @@ -249,14 +294,14 @@ public BHandler(RoutingContext context, long contentLength) { // we actually upload to a file with a generated filename uploadCount.incrementAndGet(); String uploadedFileName = new File(uploadsDir, UUID.randomUUID().toString()).getPath(); - FileUploadImpl fileUpload = new FileUploadImpl(uploadedFileName, upload); + FileUploadImpl fileUpload = new FileUploadImpl(context.vertx().fileSystem(), uploadedFileName, upload); fileUploads.add(fileUpload); Future fut = upload.streamToFileSystem(uploadedFileName); fut.onComplete(ar -> { if (fut.succeeded()) { uploadEnded(); } else { - cancelAndCleanupFileUploads(); + context.cancelAndCleanupFileUploads(); context.fail(ar.cause()); } }); @@ -265,13 +310,16 @@ public BHandler(RoutingContext context, long contentLength) { } context.request().exceptionHandler(t -> { - cancelAndCleanupFileUploads(); + context.cancelAndCleanupFileUploads(); + int sc = 200; if (t instanceof DecoderException) { // bad request - context.fail(400, t.getCause()); - } else { - context.fail(t); + sc = 400; + if (t.getCause() != null) { + t = t.getCause(); + } } + context.fail(sc, t); }); } @@ -312,7 +360,7 @@ public void handle(Buffer buff) { uploadSize += buff.length(); if (bodyLimit != -1 && uploadSize > bodyLimit) { failed = true; - cancelAndCleanupFileUploads(); + context.cancelAndCleanupFileUploads(); context.fail(413); } else { // multipart requests will not end up in the request body @@ -335,7 +383,7 @@ void uploadEnded() { } } - void end() { + void end(Void v) { // this marks the end of body parsing, calling doEnd should // only be possible from this moment onwards ended = true; @@ -348,46 +396,24 @@ void end() { void doEnd() { - if (failed) { - cancelAndCleanupFileUploads(); + if (failed || context.failed()) { + context.cancelAndCleanupFileUploads(); return; } if (deleteUploadedFilesOnEnd) { - context.addBodyEndHandler(x -> cancelAndCleanupFileUploads()); + context.addBodyEndHandler(x -> context.cancelAndCleanupFileUploads()); } HttpServerRequest req = context.request(); if (mergeFormAttributes && req.isExpectMultipart()) { req.params().addAll(req.formAttributes()); } - if (context instanceof RoutingContextInternal) { - RoutingContextInternal contextInternal = (RoutingContextInternal) context; - contextInternal.setBody(body); - } + ((RoutingContextInternal) context).setBody(body); // release body as it may take lots of memory body = null; context.next(); } - - /** - * Cancel all unfinished file upload in progress and delete all uploaded files. - */ - private void cancelAndCleanupFileUploads() { - if (cleanup.compareAndSet(false, true) && handleFileUploads) { - for (FileUpload fileUpload : context.fileUploads()) { - FileSystem fileSystem = context.vertx().fileSystem(); - if (!fileUpload.cancel()) { - String uploadedFileName = fileUpload.uploadedFileName(); - fileSystem.delete(uploadedFileName, deleteResult -> { - if (deleteResult.failed()) { - LOGGER.warn("Delete of uploaded file failed: " + uploadedFileName, deleteResult.cause()); - } - }); - } - } - } - } } } diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java index 4583eb4725e..70f8e43c765 100644 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/RestServerVerticle.java @@ -47,6 +47,7 @@ import io.vertx.core.AbstractVerticle; import io.vertx.core.Context; +import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.Vertx; @@ -67,15 +68,16 @@ public class RestServerVerticle extends AbstractVerticle { private static final String SSL_KEY = "rest.provider"; - private Endpoint endpoint; - private URIEndpointObject endpointObject; + private WebSocketDispatcher webSocketDispatcher; + @Override public void init(Vertx vertx, Context context) { super.init(vertx, context); - this.endpoint = (Endpoint) context.config().getValue(AbstractTransport.ENDPOINT_KEY); + Endpoint endpoint = (Endpoint) context.config().getValue(AbstractTransport.ENDPOINT_KEY); this.endpointObject = (URIEndpointObject) endpoint.getAddress(); + this.webSocketDispatcher = new WebSocketDispatcher(endpoint); } @Override @@ -94,7 +96,17 @@ public void start(Promise startPromise) throws Exception { initDispatcher(mainRouter); mountGlobalRestFailureHandler(mainRouter); HttpServer httpServer = createHttpServer(); - httpServer.requestHandler(mainRouter); + httpServer.requestHandler(httpServerRequest -> { + if (this.endpointObject.isWebsocketEnabled()) { + String path = LegacyPropertyFactory.getStringProperty("servicecomb.rest.server.websocket-prefix"); + if (httpServerRequest.path().startsWith(path)) { + httpServerRequest.toWebSocket().onComplete(w -> webSocketDispatcher.onRequest(w), + e -> LOGGER.error("WebSocket error.", e)); + return; + } + } + mainRouter.handle(httpServerRequest); + }); httpServer.connectionHandler(connection -> { DefaultHttpServerMetrics serverMetrics = (DefaultHttpServerMetrics) ((ConnectionBase) connection).metrics(); DefaultServerEndpointMetric endpointMetric = serverMetrics.getEndpointMetric(); @@ -110,7 +122,7 @@ public void start(Promise startPromise) throws Exception { SPIServiceUtils.getAllService(HttpServerExceptionHandler.class); httpServer.exceptionHandler(e -> { if (e instanceof ClosedChannelException) { - // This is quite normal in between browser and ege, so do not print out. + // This is quite normal in between browser and edge, so do not print out. LOGGER.debug("Unexpected error in server.{}", ExceptionUtils.getExceptionMessageWithoutTrace(e)); } else { LOGGER.error("Unexpected error in server.{}", ExceptionUtils.getExceptionMessageWithoutTrace(e)); @@ -137,9 +149,8 @@ void mountGlobalRestFailureHandler(Router mainRouter) { return; } HttpServerResponse response = ctx.response(); - if (ctx.failure() instanceof InvocationException) { + if (ctx.failure() instanceof InvocationException exception) { // ServiceComb defined exception - InvocationException exception = (InvocationException) ctx.failure(); response.setStatusCode(exception.getStatusCode()); response.setStatusMessage(exception.getReasonPhrase()); response.end(exception.getErrorData().toString()); @@ -188,7 +199,7 @@ void mountCorsHandler(Router mainRouter) { return; } - CorsHandler corsHandler = getCorsHandler(TransportConfig.getCorsAllowedOrigin()); + CorsHandler corsHandler = getCorsHandler(); // Access-Control-Allow-Credentials corsHandler.allowCredentials(TransportConfig.isCorsAllowCredentials()); // Access-Control-Allow-Headers @@ -210,8 +221,17 @@ void mountCorsHandler(Router mainRouter) { mainRouter.route().handler(corsHandler); } - private CorsHandler getCorsHandler(String corsAllowedOrigin) { - return CorsHandler.create().addOrigin(corsAllowedOrigin); + private CorsHandler getCorsHandler() { + CorsHandler handler = CorsHandler.create(); + String[] origin = TransportConfig.getCorsAllowedOrigin(); + if (origin == null) { + handler.addOrigin("*"); + } else { + for (String item : origin) { + handler.addOrigin(item); + } + } + return handler; } private void initDispatcher(Router mainRouter) { @@ -225,11 +245,12 @@ private void initDispatcher(Router mainRouter) { } private void startListen(HttpServer server, Promise startPromise) { - server.listen(endpointObject.getPort(), endpointObject.getHostOrIp(), ar -> { - if (ar.succeeded()) { + Future result = server.listen(endpointObject.getPort(), endpointObject.getHostOrIp()); + result.onComplete((s, f) -> { + if (f == null) { LOGGER.info("rest listen success. address={}:{}", endpointObject.getHostOrIp(), - ar.result().actualPort()); + s.actualPort()); startPromise.complete(); return; } @@ -237,8 +258,8 @@ private void startListen(HttpServer server, Promise startPromise) { String msg = String.format("rest listen failed, address=%s:%d", endpointObject.getHostOrIp(), endpointObject.getPort()); - LOGGER.error(msg, ar.cause()); - startPromise.fail(ar.cause()); + LOGGER.error(msg, f); + startPromise.fail(f); }); } @@ -252,6 +273,8 @@ private HttpServerOptions createDefaultHttpServerOptions() { serverOptions.setCompressionSupported(TransportConfig.getCompressed()); serverOptions.setMaxHeaderSize(TransportConfig.getMaxHeaderSize()); serverOptions.setMaxFormAttributeSize(TransportConfig.getMaxFormAttributeSize()); + serverOptions.setMaxFormFields(TransportConfig.getMaxFormFields()); + serverOptions.setMaxFormBufferedBytes(TransportConfig.getMaxFormBufferedBytes()); serverOptions.setCompressionLevel(TransportConfig.getCompressionLevel()); serverOptions.setMaxChunkSize(TransportConfig.getMaxChunkSize()); serverOptions.setDecompressionSupported(TransportConfig.getDecompressionSupported()); @@ -261,8 +284,7 @@ private HttpServerOptions createDefaultHttpServerOptions() { serverOptions.setUseAlpn(TransportConfig.getUseAlpn()) .setHttp2ConnectionWindowSize(TransportConfig.getHttp2ConnectionWindowSize()) .setIdleTimeout(TransportConfig.getHttp2ConnectionIdleTimeoutInSeconds()) - .setReadIdleTimeout(TransportConfig.getHttp2ConnectionIdleTimeoutInSeconds()) - .setWriteIdleTimeout(TransportConfig.getHttp2ConnectionIdleTimeoutInSeconds()) + .setHttp2RstFloodMaxRstFramePerWindow(TransportConfig.getHttp2RstFloodMaxRstFramePerWindow()) .setInitialSettings(new Http2Settings().setPushEnabled(TransportConfig.getPushEnabled()) .setMaxConcurrentStreams(TransportConfig.getMaxConcurrentStreams()) .setHeaderTableSize(TransportConfig.getHttp2HeaderTableSize()) @@ -272,8 +294,6 @@ private HttpServerOptions createDefaultHttpServerOptions() { ); } else { serverOptions.setIdleTimeout(TransportConfig.getConnectionIdleTimeoutInSeconds()); - serverOptions.setReadIdleTimeout(TransportConfig.getConnectionIdleTimeoutInSeconds()); - serverOptions.setWriteIdleTimeout(TransportConfig.getConnectionIdleTimeoutInSeconds()); } if (endpointObject.isSslEnabled()) { SSLOptionFactory factory = diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java index a07cb12b06d..5c81d75646b 100644 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/TransportConfig.java @@ -61,6 +61,16 @@ public static int getMaxFormAttributeSize() { HttpServerOptions.DEFAULT_MAX_FORM_ATTRIBUTE_SIZE); } + public static int getMaxFormFields() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.maxFormFields", + HttpServerOptions.DEFAULT_MAX_FORM_FIELDS); + } + + public static int getMaxFormBufferedBytes() { + return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.maxFormBufferedBytes", + HttpServerOptions.DEFAULT_MAX_FORM_BUFFERED_SIZE); + } + public static int getCompressionLevel() { return LegacyPropertyFactory.getIntProperty("servicecomb.rest.server.compressionLevel", HttpServerOptions.DEFAULT_COMPRESSION_LEVEL); @@ -80,10 +90,16 @@ public static int getDecoderInitialBufferSize() { public static int getHttp2ConnectionWindowSize() { return LegacyPropertyFactory - .getIntProperty("servicecomb.rest.server.http2ConnectionWindowSize", + .getIntProperty("servicecomb.rest.server.http2.connectionWindowSize", HttpServerOptions.DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE); } + public static int getHttp2RstFloodMaxRstFramePerWindow() { + return LegacyPropertyFactory + .getIntProperty("servicecomb.rest.server.http2.rstFloodMaxRstFramePerWindow", + HttpServerOptions.DEFAULT_HTTP2_RST_FLOOD_MAX_RST_FRAME_PER_WINDOW); + } + public static int getThreadCount() { return TransportConfigUtils.readVerticleCount( "servicecomb.rest.server.verticle-count", @@ -93,8 +109,7 @@ public static int getThreadCount() { public static int getConnectionIdleTimeoutInSeconds() { return LegacyPropertyFactory .getIntProperty("servicecomb.rest.server.connection.idleTimeoutInSeconds", - DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND) - ; + DEFAULT_SERVER_CONNECTION_IDLE_TIMEOUT_SECOND); } public static int getHttp2ConnectionIdleTimeoutInSeconds() { @@ -165,9 +180,9 @@ public static boolean isCorsEnabled() { .getBooleanProperty(SERVICECOMB_CORS_CONFIG_BASE + ".enabled", false); } - public static String getCorsAllowedOrigin() { + public static String[] getCorsAllowedOrigin() { return LegacyPropertyFactory - .getStringProperty(SERVICECOMB_CORS_CONFIG_BASE + ".origin", "*"); + .getProperty(SERVICECOMB_CORS_CONFIG_BASE + ".origin", String[].class); } public static boolean isCorsAllowCredentials() { diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestDispatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestDispatcher.java index 38f87fba48d..20f83446e57 100644 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestDispatcher.java +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestDispatcher.java @@ -124,15 +124,13 @@ private void sendFailureRespDeterminedByStatus(RoutingContext context) { .setStatusMessage(Status.INTERNAL_SERVER_ERROR.getReasonPhrase()) .end(wrapResponseBody(Status.INTERNAL_SERVER_ERROR.getReasonPhrase())); } - context.response().close(); } /** * Use routingContext to send failure information in throwable. */ private void sendExceptionByRoutingContext(RoutingContext context, Throwable e) { - if (e instanceof InvocationException) { - InvocationException invocationException = (InvocationException) e; + if (e instanceof InvocationException invocationException) { context.response().putHeader(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD) .setStatusCode(invocationException.getStatusCode()).setStatusMessage(invocationException.getReasonPhrase()) .end(wrapResponseBody(invocationException.getReasonPhrase())); @@ -141,7 +139,6 @@ private void sendExceptionByRoutingContext(RoutingContext context, Throwable e) .setStatusCode(Status.INTERNAL_SERVER_ERROR.getStatusCode()) .end(wrapResponseBody(Status.INTERNAL_SERVER_ERROR.getReasonPhrase())); } - context.response().close(); } /** diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestTransport.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestTransport.java index 32b0c1fc1d1..8566a3c3b6b 100644 --- a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestTransport.java +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/VertxRestTransport.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.List; +import java.util.Map; import org.apache.servicecomb.core.CoreConst; import org.apache.servicecomb.core.transport.AbstractTransport; @@ -93,7 +94,13 @@ public boolean init() throws Exception { options.setWorkerPoolSize(VertxOptions.DEFAULT_WORKER_POOL_SIZE); prepareBlockResource(); - return VertxUtils.blockDeploy(transportVertx, TransportConfig.getRestServerVerticle(), options); + Map result = VertxUtils.blockDeploy(transportVertx, TransportConfig.getRestServerVerticle(), + options); + if ((boolean) result.get("code")) { + return true; + } else { + throw new IllegalStateException((String) result.get("message")); + } } private void prepareBlockResource() { diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketDispatcher.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketDispatcher.java new file mode 100644 index 00000000000..5f8e2cd329d --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketDispatcher.java @@ -0,0 +1,122 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.servicecomb.common.rest.EdgeServerWebSocketInvocationCreator; +import org.apache.servicecomb.common.rest.ProviderServerWebSocketInvocationCreator; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationItem; +import org.apache.servicecomb.common.rest.route.URLMappedConfigurationLoader; +import org.apache.servicecomb.common.rest.route.Utils; +import org.apache.servicecomb.config.ConfigurationChangedEvent; +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.Endpoint; +import org.apache.servicecomb.core.SCBEngine; +import org.apache.servicecomb.core.Transport; +import org.apache.servicecomb.core.definition.MicroserviceMeta; +import org.apache.servicecomb.core.invocation.InvocationCreator; +import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; +import org.apache.servicecomb.foundation.common.event.EventManager; +import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData; +import org.apache.servicecomb.swagger.invocation.exception.InvocationException; + +import com.google.common.eventbus.Subscribe; + +import io.vertx.core.http.ServerWebSocket; +import jakarta.ws.rs.core.Response.Status; + +public class WebSocketDispatcher { + private static final String KEY_MAPPING_PREFIX = "servicecomb.http.dispatcher.edge.websocket.mappings"; + + private final Object LOCK = new Object(); + + private volatile boolean initialized = false; + + private Endpoint endpoint; + + private MicroserviceMeta microserviceMeta; + + private boolean isEdge; + + private Map configurations = new HashMap<>(); + + public WebSocketDispatcher(Endpoint endpoint) { + this.endpoint = endpoint; + EventManager.register(this); + } + + private void loadConfigurations() { + configurations = URLMappedConfigurationLoader.loadConfigurations( + LegacyPropertyFactory.getEnvironment(), KEY_MAPPING_PREFIX); + } + + @Subscribe + public void onConfigurationChangedEvent(ConfigurationChangedEvent event) { + for (String changed : event.getChanged()) { + if (changed.startsWith(KEY_MAPPING_PREFIX)) { + loadConfigurations(); + break; + } + } + } + + protected void onRequest(ServerWebSocket webSocket) { + if (!initialized) { + synchronized (LOCK) { + if (!initialized) { + Transport transport = SCBEngine.getInstance().getTransportManager().findTransport(CoreConst.WEBSOCKET); + this.microserviceMeta = SCBEngine.getInstance().getProducerMicroserviceMeta(); + this.endpoint = new Endpoint(transport, this.endpoint.getEndpoint()); + this.isEdge = TransportConfig.getRestServerVerticle() + .getName().equals("org.apache.servicecomb.edge.core.EdgeRestServerVerticle"); + if (this.isEdge) { + loadConfigurations(); + } + } + initialized = true; + } + } + + InvocationCreator creator; + if (isEdge) { + URLMappedConfigurationItem configurationItem = findConfigurationItem(webSocket.path()); + if (configurationItem == null) { + throw new InvocationException(Status.NOT_FOUND, new CommonExceptionData( + String.format("path %s not found", webSocket.path()))); + } + String path = Utils.findActualPath(webSocket.path(), configurationItem.getPrefixSegmentCount()); + creator = new EdgeServerWebSocketInvocationCreator( + configurationItem.getMicroserviceName(), path, endpoint, webSocket); + } else { + creator = new ProviderServerWebSocketInvocationCreator(microserviceMeta, + endpoint, webSocket); + } + new WebSocketProducerInvocationFlow(creator, webSocket).run(); + } + + private URLMappedConfigurationItem findConfigurationItem(String path) { + for (URLMappedConfigurationItem item : configurations.values()) { + if (item.getPattern().matcher(path).matches()) { + return item; + } + } + return null; + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketProducerInvocationFlow.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketProducerInvocationFlow.java new file mode 100644 index 00000000000..1297da53a51 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketProducerInvocationFlow.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import org.apache.servicecomb.core.Invocation; +import org.apache.servicecomb.core.invocation.InvocationCreator; +import org.apache.servicecomb.core.invocation.ProducerInvocationFlow; +import org.apache.servicecomb.swagger.invocation.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.vertx.core.http.ServerWebSocket; + +public class WebSocketProducerInvocationFlow extends ProducerInvocationFlow { + private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketProducerInvocationFlow.class); + + private final ServerWebSocket websocket; + + public WebSocketProducerInvocationFlow(InvocationCreator invocationCreator, ServerWebSocket webSocket) { + super(invocationCreator); + this.websocket = webSocket; + } + + @Override + protected Invocation sendCreateInvocationException(Throwable throwable) { + LOGGER.error("Web socket create invocation error.", throwable); + websocket.writeTextMessage("Web socket create invocation error " + throwable.getMessage()); + websocket.close(); + return null; + } + + @Override + protected void endResponse(Invocation invocation, Response response) { + + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketTransport.java b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketTransport.java new file mode 100644 index 00000000000..9bcd614a374 --- /dev/null +++ b/transports/transport-rest/transport-rest-vertx/src/main/java/org/apache/servicecomb/transport/rest/vertx/WebSocketTransport.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.transport.rest.vertx; + +import org.apache.servicecomb.core.CoreConst; +import org.apache.servicecomb.core.transport.AbstractTransport; + +public class WebSocketTransport extends AbstractTransport { + @Override + public String getName() { + return CoreConst.WEBSOCKET; + } + + @Override + public int getOrder() { + return -500; + } + + @Override + public boolean init() throws Exception { + return true; + } +} diff --git a/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport b/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport index be04a13de92..74ae694b980 100644 --- a/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport +++ b/transports/transport-rest/transport-rest-vertx/src/main/resources/META-INF/services/org.apache.servicecomb.core.Transport @@ -16,3 +16,4 @@ # org.apache.servicecomb.transport.rest.vertx.VertxRestTransport +org.apache.servicecomb.transport.rest.vertx.WebSocketTransport diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockHttpServerResponse.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockHttpServerResponse.java index 1c42f093b9b..74a8307262b 100644 --- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockHttpServerResponse.java +++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/MockHttpServerResponse.java @@ -17,8 +17,13 @@ package org.apache.servicecomb.transport.rest.vertx; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + import io.vertx.codegen.annotations.Nullable; -import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.MultiMap; @@ -26,14 +31,9 @@ import io.vertx.core.http.Cookie; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerResponse; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import io.vertx.core.net.HostAndPort; class MockHttpServerResponse implements HttpServerResponse { - boolean responseClosed; - boolean responseEnded; Map responseHeader = new HashMap<>(1); @@ -44,11 +44,6 @@ class MockHttpServerResponse implements HttpServerResponse { String responseChunk; - @Override - public void close() { - responseClosed = true; - } - @Override public HttpServerResponse putHeader(String name, String value) { responseHeader.put(name, value); @@ -73,11 +68,6 @@ public Future end() { return Future.succeededFuture(); } - @Override - public void end(Handler> handler) { - - } - @Override public Future end(String chunk) { responseEnded = true; @@ -85,11 +75,6 @@ public Future end(String chunk) { return Future.succeededFuture(); } - @Override - public void end(String s, Handler> handler) { - - } - @Override public HttpServerResponse exceptionHandler(Handler handler) { return null; @@ -100,10 +85,6 @@ public Future write(Buffer data) { return Future.succeededFuture(); } - @Override - public void write(Buffer buffer, Handler> handler) { - } - @Override public HttpServerResponse setWriteQueueMaxSize(int maxSize) { return null; @@ -195,26 +176,23 @@ public HttpServerResponse endHandler(Handler handler) { } @Override - public Future write(String chunk, String enc) { - return Future.succeededFuture(); + public Future writeHead() { + return null; } @Override - public void write(String s, String s1, Handler> handler) { + public Future write(String chunk, String enc) { + return Future.succeededFuture(); } + @Override public Future write(String chunk) { return Future.succeededFuture(); } @Override - public void write(String s, Handler> handler) { - - } - - @Override - public HttpServerResponse writeContinue() { + public Future writeContinue() { return null; } @@ -223,29 +201,17 @@ public Future writeEarlyHints(MultiMap headers) { return Future.succeededFuture(); } - @Override - public void writeEarlyHints(MultiMap headers, Handler> handler) { - } - @Override public Future end(String chunk, String enc) { return Future.succeededFuture(); } - @Override - public void end(String s, String s1, Handler> handler) { - - } @Override public Future end(Buffer chunk) { return Future.succeededFuture(); } - @Override - public void end(Buffer buffer, Handler> handler) { - - } @Override public Future sendFile(String filename, long offset, long length) { @@ -253,8 +219,12 @@ public Future sendFile(String filename, long offset, long length) { } @Override - public HttpServerResponse sendFile(String filename, long offset, long length, - Handler> resultHandler) { + public Future sendFile(FileChannel channel, long offset, long length) { + return null; + } + + @Override + public Future sendFile(RandomAccessFile file, long offset, long length) { return null; } @@ -294,40 +264,17 @@ public int streamId() { } @Override - public HttpServerResponse push(HttpMethod method, String host, String path, - Handler> handler) { + public Future push(HttpMethod httpMethod, HostAndPort hostAndPort, String s, MultiMap multiMap) { return null; } @Override - public HttpServerResponse push(HttpMethod method, String path, MultiMap headers, - Handler> handler) { + public Future reset(long code) { return null; } @Override - public HttpServerResponse push(HttpMethod method, String path, Handler> handler) { - return null; - } - - @Override - public HttpServerResponse push(HttpMethod method, String host, String path, MultiMap headers, - Handler> handler) { - return null; - } - - @Override - public Future push(HttpMethod method, String host, String path, MultiMap headers) { - return Future.succeededFuture(); - } - - @Override - public boolean reset(long code) { - return false; - } - - @Override - public HttpServerResponse writeCustomFrame(int type, int flags, Buffer payload) { + public Future writeCustomFrame(int type, int flags, Buffer payload) { return null; } diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java index 3d3545a8ebf..4154cc635c5 100644 --- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java +++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestRestServerVerticle.java @@ -23,6 +23,8 @@ import static io.vertx.core.http.HttpServerOptions.DEFAULT_HTTP2_CONNECTION_WINDOW_SIZE; import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_CHUNK_SIZE; import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_FORM_ATTRIBUTE_SIZE; +import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_FORM_BUFFERED_SIZE; +import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_FORM_FIELDS; import static io.vertx.core.http.HttpServerOptions.DEFAULT_MAX_INITIAL_LINE_LENGTH; import static org.apache.servicecomb.common.accessLog.AccessLogConfig.CLIENT_LOG_ENABLED; import static org.apache.servicecomb.common.accessLog.AccessLogConfig.CLIENT_LOG_PATTERN; @@ -63,10 +65,10 @@ import io.vertx.core.Handler; import io.vertx.core.Promise; import io.vertx.core.Vertx; -import io.vertx.core.file.impl.FileResolverImpl; import io.vertx.core.http.Http2Settings; import io.vertx.core.http.HttpMethod; import io.vertx.core.http.HttpServerResponse; +import io.vertx.core.impl.SysProps; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Route; import io.vertx.ext.web.Router; @@ -127,6 +129,12 @@ public void setUp() { Mockito.when(environment.getProperty("servicecomb.rest.server.compressionLevel", int.class, DEFAULT_COMPRESSION_LEVEL)) .thenReturn(DEFAULT_COMPRESSION_LEVEL); + Mockito.when(environment.getProperty("servicecomb.rest.server.maxFormFields", int.class, + DEFAULT_MAX_FORM_FIELDS)) + .thenReturn(DEFAULT_MAX_FORM_FIELDS); + Mockito.when(environment.getProperty("servicecomb.rest.server.maxFormBufferedBytes", int.class, + DEFAULT_MAX_FORM_BUFFERED_SIZE)) + .thenReturn(DEFAULT_MAX_FORM_BUFFERED_SIZE); Mockito.when(environment.getProperty("servicecomb.rest.server.maxChunkSize", int.class, DEFAULT_MAX_CHUNK_SIZE)) .thenReturn(DEFAULT_MAX_CHUNK_SIZE); @@ -156,7 +164,7 @@ public void setUp() { .thenReturn(Http2Settings.DEFAULT_ENABLE_PUSH); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); Mockito.when(environment.getProperty(CLIENT_LOG_ENABLED, boolean.class, false)) .thenReturn(false); @@ -193,14 +201,14 @@ public void testRestServerVerticleWithRouter(@Mocked Transport transport, @Mocke result = endpointObject; } }; - Endpoint endpiont = new Endpoint(transport, "http://127.0.0.1:8080"); + Endpoint endpoint = new Endpoint(transport, "http://127.0.0.1:8080"); new Expectations() { { context.config(); result = jsonObject; jsonObject.getValue(AbstractTransport.ENDPOINT_KEY); - result = endpiont; + result = endpoint; } }; RestServerVerticle server = new RestServerVerticle(); @@ -220,14 +228,14 @@ public void testRestServerVerticleWithRouterSSL(@Mocked Transport transport, @Mo result = endpointObject; } }; - Endpoint endpiont = new Endpoint(transport, "http://127.0.0.1:8080?sslEnabled=true"); + Endpoint endpoint = new Endpoint(transport, "http://127.0.0.1:8080?sslEnabled=true"); new Expectations() { { context.config(); result = jsonObject; jsonObject.getValue(AbstractTransport.ENDPOINT_KEY); - result = endpiont; + result = endpoint; } }; RestServerVerticle server = new RestServerVerticle(); @@ -267,8 +275,8 @@ public void testMountCorsHandler() { false)) .thenReturn(true); Mockito.when(environment.getProperty("servicecomb.cors.origin", - "*")) - .thenReturn("*"); + String[].class)) + .thenReturn(null); Mockito.when(environment.getProperty("servicecomb.cors.allowedMethod")) .thenReturn("GET,PUT,POST"); Mockito.when(environment.getProperty("servicecomb.cors.allowedHeader")) @@ -326,8 +334,7 @@ CorsHandler maxAgeSeconds(int maxAgeSeconds) { new MockUp() { @Mock - CorsHandler getCorsHandler(String corsAllowedOrigin) { - Assertions.assertEquals("*", corsAllowedOrigin); + CorsHandler getCorsHandler() { return corsHandler; } }; diff --git a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestTransport.java b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestTransport.java index e8ca51c7599..fb14f5115de 100644 --- a/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestTransport.java +++ b/transports/transport-rest/transport-rest-vertx/src/test/java/org/apache/servicecomb/transport/rest/vertx/TestVertxRestTransport.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.net.ServerSocket; +import java.util.HashMap; +import java.util.Map; import org.apache.servicecomb.foundation.common.LegacyPropertyFactory; import org.apache.servicecomb.foundation.vertx.VertxUtils; @@ -35,7 +37,7 @@ import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; -import io.vertx.core.file.impl.FileResolverImpl; +import io.vertx.core.impl.SysProps; import mockit.Expectations; import mockit.Mock; import mockit.MockUp; @@ -67,7 +69,7 @@ public void setUp() { .thenReturn(""); Mockito.when(environment.getProperty("servicecomb.transport.eventloop.size", int.class, -1)) .thenReturn(-1); - Mockito.when(environment.getProperty(FileResolverImpl.DISABLE_CP_RESOLVING_PROP_NAME, boolean.class, true)) + Mockito.when(environment.getProperty(SysProps.DISABLE_FILE_CP_RESOLVING.name, boolean.class, true)) .thenReturn(true); LegacyPropertyFactory.setEnvironment(environment); instance = new VertxRestTransport(); @@ -95,9 +97,11 @@ public Vertx init(VertxOptions vertxOptions) { } @Mock - public boolean blockDeploy(Vertx vertx, Class cls, + public Map blockDeploy(Vertx vertx, Class cls, DeploymentOptions options) throws InterruptedException { - return true; + Map result = new HashMap<>(); + result.put("code", true); + return result; } }; instance.init();