Merge remote-tracking branch 'upstream/master'

pull/364/head
fangjian0423
commit 29cfe3537f

@ -0,0 +1,34 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
我们鼓励使用英文,如果不能直接使用,可以使用翻译软件,您仍旧可以保留中文原文。
We recommend using English. If you are non-native English speaker, you can use the translation software.
**Which Component**
eg. Nacos Discovery, Sentinel
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
e.g. MacOS 、Java8 、 Version 0.2.1.RELEASE

@ -0,0 +1,27 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
我们鼓励使用英文,如果不能直接使用,可以使用翻译软件,您仍旧可以保留中文原文。
We recommend using English. If you are non-native English speaker, you can use the translation software.
**Which Component**
eg. Nacos Discovery, Sentinel
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

@ -0,0 +1,21 @@
---
name: Question
about: how to ask an valid question
title: ''
labels: ''
assignees: ''
---
我们鼓励使用英文,如果不能直接使用,可以使用翻译软件,您仍旧可以保留中文原文。
We recommend using English. If you are non-native English speaker, you can use the translation software.
**Which Component**
eg. Nacos Discovery, Sentinel
**Describe what problem you have encountered**
A clear and concise description of what you want to do.
**Describe what information you have read**
eg. I have read the reference doc of Sentinel

@ -0,0 +1,45 @@
= Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open
and welcoming community, we pledge to respect all people who contribute through reporting
issues, posting feature requests, updating documentation, submitting pull requests or
patches, and other activities.
We are committed to making participation in this project a harassment-free experience for
everyone, regardless of level of experience, gender, gender identity and expression,
sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
religion, or nationality.
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses,
without explicit permission
* Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or reject comments,
commits, code, wiki edits, issues, and other contributions that are not aligned to this
Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors
that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and
consistently applying these principles to every aspect of managing this project. Project
maintainers who do not follow or enforce the Code of Conduct may be permanently removed
from the project team.
This Code of Conduct applies both within project spaces and in public spaces when an
individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will
be reviewed and investigated and will result in a response that is deemed necessary and
appropriate to the circumstances. Maintainers are obligated to maintain confidentiality
with regard to the reporter of an incident.
This Code of Conduct is adapted from the
http://contributor-covenant.org[Contributor Covenant], version 1.3.0, available at
http://contributor-covenant.org/version/1/3/0/[contributor-covenant.org/version/1/3/0/]

@ -0,0 +1,29 @@
## Contributor Code of Conduct
As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery
- Personal attacks
- Trolling or insulting/derogatory comments
- Public or private harassment
- Publishing others private information, such as physical or electronic addresses, without explicit permission
- Other unethical or unprofessional conduct
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting a project maintainer at spring-code-of-conduct@pivotal.io . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident.
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org/), version 1.3.0, available at [contributor-covenant.org/version/1/3/0/](http://contributor-covenant.org/version/1/3/0/)

@ -0,0 +1,15 @@
### Describe what this PR does / why we need it
### Does this pull request fix one issue?
<!--If that, add "Fixes #xxxx" below in the next line. For example, Fixes #15. Otherwise, add "NONE" -->
### Describe how you did it
### Describe how to verify it
### Special notes for reviews

@ -19,8 +19,10 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
* **服务注册与发现**:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
* **分布式配置管理**:支持分布式系统中的外部化配置,配置更改时自动刷新。
* **消息驱动能力**:基于 Spring Cloud Stream 为微服务应用构建消息驱动能力。
* **分布式事务**:使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。
* **阿里云对象存储**:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
* **分布式任务调度**:提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Workerschedulerx-client上执行。
* **阿里云短信服务**:覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
更多功能请参考 [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap-zh.md)。
@ -33,11 +35,17 @@ Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。
**[RocketMQ](https://rocketmq.apache.org/)**:一款开源的分布式消息系统,基于高可用分布式集群技术,提供低延时的、高可靠的消息发布与订阅服务。
**[Dubbo](https://github.com/apache/incubator-dubbo)**Apache Dubbo™ (incubating) 是一款高性能 Java RPC 框架。
**[Fescar](https://github.com/alibaba/fescar)**:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
**[Alibaba Cloud ACM](https://www.aliyun.com/product/acm)**:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
**[Alibaba Cloud OSS](https://www.aliyun.com/product/oss)**: 阿里云对象存储服务Object Storage Service简称 OSS是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
**[Alibaba Cloud SchedulerX](https://help.aliyun.com/document_detail/43136.html?spm=a2c4g.11186623.6.709.baef7da9QVICiD)**: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
**[Alibaba Cloud SchedulerX](https://help.aliyun.com/document_detail/43136.html)**: 阿里中间件团队开发的一款分布式任务调度产品,提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。
**[Alibaba Cloud SMS](https://www.aliyun.com/product/sms)**: 覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道。
更多组件请参考 [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap-zh.md)。
@ -100,6 +108,8 @@ Example 列表:
[RocketMQ Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme-zh.md)
[Fescar Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/fescar-example/readme-zh.md)
[Alibaba Cloud OSS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme-zh.md)
[Alibaba Cloud ANS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/ans-example/ans-provider-example/readme-zh.md)

@ -20,21 +20,33 @@ With Spring Cloud Alibaba, you only need to add some annotations and a small amo
* **Service registration and discovery**Service can be registered and clients can discover the instances using Spring-managed beans, auto integration Ribbon.
* **Distributed configuration**support for externalized configuration in a distributed system, auto refresh when configuration changes.
* **Event-driven**support for building highly scalable event-driven microservices connected with shared messaging systems.
* **Distributed Transaction**support for distributed transaction solution with high performance and ease of use.
* **Alibaba Cloud Object Storage**massive, secure, low-cost, and highly reliable cloud storage services. Support for storing and accessing any type of data in any application, anytime, anywhere.
* **Alibaba Cloud SchedulerX**accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds.
* **Alibaba Cloud SMS** A messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers.
For more features, please refer to [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap.md).
## Components
**[Sentinel](https://github.com/alibaba/Sentinel)**: Sentinel takes "traffic flow" as the breakthrough point, and provides solutions in areas such as flow control, concurrency, circuit breaking, and load protection to protect service stability.
**[Nacos](https://github.com/alibaba/Nacos)**: an easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.
**[Nacos](https://github.com/alibaba/Nacos)**: An easy-to-use dynamic service discovery, configuration and service management platform for building cloud native applications.
**[RocketMQ](https://rocketmq.apache.org/)**A distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.
**[RocketMQ](https://rocketmq.apache.org/)**a distributed messaging and streaming platform with low latency, high performance and reliability, trillion-level capacity and flexible scalability.
**[Dubbo](https://github.com/apache/incubator-dubbo)**A high-performance, Java based open source RPC framework.
**[Alibaba Cloud ACM](https://www.aliyun.com/product/acm)**an application configuration center that enables you to centralize the management of application configurations, and accomplish real-time configuration push in a distributed environment.
**[Fescar](https://github.com/alibaba/fescar)**A distributed transaction solution with high performance and ease of use for microservices architecture.
**[Alibaba Cloud ACM](https://www.aliyun.com/product/acm)**An application configuration center that enables you to centralize the management of application configurations, and accomplish real-time configuration push in a distributed environment.
**[Alibaba Cloud OSS](https://www.aliyun.com/product/oss)**: An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world.
**[Alibaba Cloud SMS](https://www.aliyun.com/product/sms)**: A messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers.
**[Alibaba Cloud SchedulerX](https://www.aliyun.com/product/SchedulerX)**:accurate, highly reliable, and highly available scheduled job scheduling services with response time within seconds..
For more features please refer to [Roadmap](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/Roadmap.md).
## How to build
@ -120,6 +132,28 @@ During the incubation period, the version management of the project will follow
* Functional updates will be reflected in the 3rd number of the version, for example, the next version of 0.1.0 will be 0.1.1.
## Code of Conduct
This project is a sub-project of Spring Cloud, it adheres to the Contributor Covenant [code of conduct](https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
## Code Conventions and Housekeeping
None of these is essential for a pull request, but they will all help. They can also be added after the original pull request but before a merge.
Use the Spring Framework code format conventions. If you use Eclipse you can import formatter settings using the eclipse-code-formatter.xml file from the Spring Cloud Build project. If using IntelliJ, you can use the Eclipse Code Formatter Plugin to import the same file.
Make sure all new .java files to have a simple Javadoc class comment with at least an @author tag identifying you, and preferably at least a paragraph on what the class is for.
Add the ASF license header comment to all new .java files (copy from existing files in the project)
Add yourself as an @author to the .java files that you modify substantially (more than cosmetic changes).
Add some Javadocs and, if you change the namespace, some XSD doc elements.
A few unit tests would help a lot as wellsomeone has to do it.
If no-one else is using your branch, please rebase it against the current master (or other target branch in the main project).
When writing a commit message please follow these conventions, if you are fixing an existing issue please add Fixes gh-XXXX at the end of the commit message (where XXXX is the issue number).
## Contact Us
Mailing list is recommended for discussing almost anything related to spring-cloud-alibaba.

@ -17,21 +17,10 @@ Spring Cloud Alibaba 致力于提供分布式应用服务开发的一站式解
阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
**Alibaba Cloud OSS**
阿里云对象存储服务Object Storage Service简称 OSS是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
**RocketMQ**
Apache RocketMQ™ 基于 Java 的高性能、高吞吐量的分布式消息和流计算平台。
**Alibaba Cloud SchedulerX**
阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。
## 即将加入的组件
**Dubbo**
Apache Dubbo™ (incubating) 是一款高性能 Java RPC 框架。
@ -40,6 +29,14 @@ Apache Dubbo™ (incubating) 是一款高性能 Java RPC 框架。
阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决方案。
**Alibaba Cloud SLS**
**Alibaba Cloud OSS**
阿里云对象存储服务Object Storage Service简称 OSS是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
**Alibaba Cloud SchedulerX**
阿里中间件团队开发的一款分布式任务调度产品,支持周期性的任务与固定时间点触发任务。
**Alibaba Cloud SMS**
针对日志类数据的一站式服务,在阿里巴巴集团经历大量大数据场景锤炼而成。您无需开发就能快捷完成日志数据采集、消费、投递以及查询分析等功能,提升运维、运营效率,建立 DT 时代海量日志处理能力。
覆盖全球的短信服务,友好、高效、智能的互联化通讯能力,帮助企业迅速搭建客户触达通道

@ -24,24 +24,23 @@ An opensource project of Alibaba, an easy-to-use dynamic service discovery, conf
Apache RocketMQ™ is an open source distributed messaging and streaming data platform.
**Alibaba Cloud OSS**
**Dubbo**
Alibaba Cloud Object Storage Service, An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world.
Apache Dubbo™ (incubating) is a high-performance, Java based open source RPC framework.
**Alibaba Cloud Schedulerx**
**Fescar**
A distributed task scheduling product developed by Alibaba Middleware team. It supports both periodical tasks and tasks to be triggered at specified time points.
A distributed transaction solution with high performance and ease of use for microservices architecture.
More components will be supported by Spring Cloud Alibaba in the future, which may include but are not limited to the following:
**Alibaba Cloud OSS**
**Dubbo**
Alibaba Cloud Object Storage Service, An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world.
Apache Dubbo™ (incubating) is a high-performance, Java based open source RPC framework.
**Alibaba Cloud Schedulerx**
**Fescar**
A distributed task scheduling product developed by Alibaba Middleware team. It supports both periodical tasks and tasks to be triggered at specified time points.
A distributed transaction solution with high performance and ease of use for microservices architecture.
**Alibaba Cloud SMS**
**Alibaba Cloud SLS**
A messaging service that covers the globe, Alibaba SMS provides convenient, efficient, and intelligent communication capabilities that help businesses quickly contact their customers.
Aliyun Log Service is an all-in-one service for log-type data. It helps increase Operations & Management and operational efficiency, as well as build the processing capability to deal with massive logs.

@ -91,8 +91,10 @@
<module>spring-cloud-alibaba-sentinel-datasource</module>
<module>spring-cloud-alibaba-nacos-config</module>
<module>spring-cloud-alibaba-nacos-discovery</module>
<module>spring-cloud-alibaba-fescar</module>
<module>spring-cloud-stream-binder-rocketmq</module>
<module>spring-cloud-alibaba-nacos-config-server</module>
<module>spring-cloud-alibaba-dubbo</module>
<module>spring-cloud-alicloud-context</module>
<module>spring-cloud-alibaba-examples</module>
<module>spring-cloud-alibaba-test</module>
@ -109,6 +111,15 @@
<dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>

@ -19,8 +19,9 @@
<properties>
<sentinel.version>1.4.1</sentinel.version>
<oss.version>3.1.0</oss.version>
<nacos.client.version>0.6.2</nacos.client.version>
<nacos.config.version>0.6.1</nacos.config.version>
<fescar.version>0.1.3</fescar.version>
<nacos.client.version>0.8.0</nacos.client.version>
<nacos.config.version>0.8.0</nacos.config.version>
<acm.version>1.0.8</acm.version>
<ans.version>1.0.1</ans.version>
<aliyun.sdk.version>4.0.1</aliyun.sdk.version>
@ -28,6 +29,9 @@
<aliyun.sdk.edas.version>2.16.0</aliyun.sdk.edas.version>
<rocketmq.version>4.3.1</rocketmq.version>
<schedulerX.client.version>2.1.6</schedulerX.client.version>
<dubbo.version>2.6.5</dubbo.version>
<dubbo-spring-boot.version>0.2.1.RELEASE</dubbo-spring-boot.version>
<dubbo-registry-nacos.version>0.0.2</dubbo-registry-nacos.version>
<aliyun.java.sdk.dysmsapi>1.1.0</aliyun.java.sdk.dysmsapi>
<aliyun.sdk.mns>1.1.8</aliyun.sdk.mns>
<aliyun.java.sdk.dysmsapi>1.1.0</aliyun.java.sdk.dysmsapi>
@ -176,6 +180,29 @@
</dependency>
<!--Alibaba Fescar-->
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-core</artifactId>
<version>${fescar.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-common</artifactId>
<version>${fescar.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-spring</artifactId>
<version>${fescar.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-rm-datasource</artifactId>
<version>${fescar.version}</version>
</dependency>
<!-- Aliyun OSS dependencies -->
<dependency>
<groupId>com.aliyun.oss</groupId>
@ -183,6 +210,49 @@
<version>${oss.version}</version>
</dependency>
<!-- Apache Dubbo dependencies-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-dependencies-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo.version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-spring-boot.version}</version>
</dependency>
<!-- Dubbo Nacos registry dependency -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo-registry-nacos</artifactId>
<version>${dubbo-registry-nacos.version}</version>
</dependency>
<!-- Own dependencies -->
<dependency>
@ -215,6 +285,11 @@
<artifactId>spring-cloud-alibaba-nacos-config-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-fescar</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-acm</artifactId>
@ -258,6 +333,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-fescar</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>

@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-alibaba-dubbo</artifactId>
<name>Spring Cloud Alibaba Dubbo</name>
<dependencies>
<!-- Spring Boot dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Cloud dependencies -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<optional>true</optional>
</dependency>
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<!-- Netty -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<!-- REST support dependencies -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-netty4</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxb-provider</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- Feign for JAX-RS 2-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jaxrs2</artifactId>
<version>9.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Nacos Service Discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<scope>test</scope>
</dependency>
<!-- Eureka Service Discovery -->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>-->
<!--<scope>test</scope>-->
<!--</dependency>-->
</dependencies>
</project>

@ -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.springframework.cloud.alibaba.dubbo.autoconfigure;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import org.springframework.cloud.alibaba.dubbo.metadata.service.MetadataConfigService;
import org.springframework.cloud.alibaba.dubbo.metadata.service.NacosMetadataConfigService;
import org.springframework.cloud.alibaba.nacos.NacosConfigProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Spring Boot Auto-Configuration class for Dubbo Metadata
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Configuration
@Import(DubboServiceMetadataRepository.class)
public class DubboMetadataAutoConfiguration {
@Bean
@ConditionalOnBean(NacosConfigProperties.class)
public MetadataConfigService metadataConfigService() {
return new NacosMetadataConfigService();
}
}

@ -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.springframework.cloud.alibaba.dubbo.autoconfigure;
import feign.Contract;
import feign.Feign;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.FeignMetadataResolver;
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolver;
import org.springframework.cloud.alibaba.dubbo.openfeign.DubboFeignClientsConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Dubbo Feign Auto-{@link Configuration Configuration}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@ConditionalOnClass(value = Feign.class)
@AutoConfigureAfter(FeignAutoConfiguration.class)
@EnableFeignClients(defaultConfiguration = DubboFeignClientsConfiguration.class)
@Configuration
public class DubboOpenFeignAutoConfiguration {
@Value("${spring.application.name}")
private String currentApplicationName;
@Bean
@ConditionalOnMissingBean
public MetadataResolver metadataJsonResolver(ObjectProvider<Contract> contract) {
return new FeignMetadataResolver(currentApplicationName, contract);
}
}

@ -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.springframework.cloud.alibaba.dubbo.autoconfigure;
import com.alibaba.dubbo.config.spring.ServiceBean;
import com.alibaba.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolver;
import org.springframework.cloud.alibaba.dubbo.metadata.service.MetadataConfigService;
import org.springframework.cloud.client.discovery.event.InstancePreRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* The Auto-Configuration class for Dubbo REST metadata registration,
* REST metadata that is a part of {@link Registration#getMetadata() Spring Cloud service instances' metadata}
* will be registered Spring Cloud registry.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@ConditionalOnMissingBean(value = {
MetadataResolver.class,
MetadataConfigService.class
})
@AutoConfigureAfter(value = {DubboMetadataAutoConfiguration.class})
@Configuration
public class DubboRestMetadataRegistrationAutoConfiguration {
/**
* A Map to store REST metadata temporary, its' key is the special service name for a Dubbo service,
* the value is a JSON content of JAX-RS or Spring MVC REST metadata from the annotated methods.
*/
private final Set<ServiceRestMetadata> serviceRestMetadata = new LinkedHashSet<>();
@Autowired
private MetadataResolver metadataResolver;
@Autowired
private MetadataConfigService metadataConfigService;
@EventListener(ServiceBeanExportedEvent.class)
public void recordRestMetadata(ServiceBeanExportedEvent event) throws JsonProcessingException {
ServiceBean serviceBean = event.getServiceBean();
serviceRestMetadata.addAll(metadataResolver.resolveServiceRestMetadata(serviceBean));
}
/**
* Pre-handle Spring Cloud application service registered:
* <p>
* Put <code>restMetadata</code> with the JSON format into
* {@link Registration#getMetadata() service instances' metadata}
* <p>
*
* @param event {@link InstancePreRegisteredEvent} instance
*/
@EventListener(InstancePreRegisteredEvent.class)
public void registerRestMetadata(InstancePreRegisteredEvent event) throws Exception {
Registration registration = event.getRegistration();
metadataConfigService.publishServiceRestMetadata(registration.getServiceId(), serviceRestMetadata);
}
}

@ -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.springframework.cloud.alibaba.dubbo.context;
import org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistryFactory;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
/**
* The Dubbo services will be registered as the specified Spring cloud applications that will not be considered
* normal ones, but only are used to Dubbo's service discovery even if it is based on Spring Cloud Commons abstraction.
* However, current application will be registered by other DiscoveryClientAutoConfiguration.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboServiceRegistrationApplicationContextInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
// Set ApplicationContext into SpringCloudRegistryFactory before Dubbo Service Register
SpringCloudRegistryFactory.setApplicationContext(applicationContext);
}
}

@ -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.springframework.cloud.alibaba.dubbo.metadata;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.apache.commons.lang3.ClassUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
/**
* {@link Method} Metadata
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class MethodMetadata {
private String name;
private String returnType;
private List<MethodParameterMetadata> params;
@JsonIgnore
private Method method;
public MethodMetadata() {
this.params = new LinkedList<>();
}
public MethodMetadata(Method method) {
this.name = method.getName();
this.returnType = ClassUtils.getName(method.getReturnType());
this.params = initParameters(method);
this.method = method;
}
private List<MethodParameterMetadata> initParameters(Method method) {
int parameterCount = method.getParameterCount();
if (parameterCount < 1) {
return Collections.emptyList();
}
List<MethodParameterMetadata> params = new ArrayList<>(parameterCount);
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameterCount; i++) {
Parameter parameter = parameters[i];
MethodParameterMetadata param = toMethodParameterMetadata(i, parameter);
params.add(param);
}
return params;
}
private MethodParameterMetadata toMethodParameterMetadata(int index, Parameter parameter) {
MethodParameterMetadata metadata = new MethodParameterMetadata();
metadata.setIndex(index);
metadata.setName(parameter.getName());
metadata.setType(parameter.getType().getTypeName());
return metadata;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getReturnType() {
return returnType;
}
public void setReturnType(String returnType) {
this.returnType = returnType;
}
public List<MethodParameterMetadata> getParams() {
return params;
}
public void setParams(List<MethodParameterMetadata> params) {
this.params = params;
}
public Method getMethod() {
return method;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodMetadata that = (MethodMetadata) o;
return Objects.equals(name, that.name) &&
Objects.equals(returnType, that.returnType) &&
Objects.equals(params, that.params);
}
@Override
public int hashCode() {
return Objects.hash(name, returnType, params);
}
}

@ -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.springframework.cloud.alibaba.dubbo.metadata;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* {@link Method} Parameter Metadata
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class MethodParameterMetadata {
private int index;
private String name;
private String type;
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MethodParameterMetadata that = (MethodParameterMetadata) o;
return index == that.index &&
Objects.equals(name, that.name) &&
Objects.equals(type, that.type);
}
@Override
public int hashCode() {
return Objects.hash(index, name, type);
}
}

@ -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.springframework.cloud.alibaba.dubbo.metadata;
import feign.RequestTemplate;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
/**
* Request Metadata
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class RequestMetadata {
private String method;
private String url;
private Map<String, Collection<String>> queries;
private Map<String, Collection<String>> headers;
public RequestMetadata() {
}
public RequestMetadata(RequestTemplate requestTemplate) {
this.method = requestTemplate.method();
this.url = requestTemplate.url();
this.queries = requestTemplate.queries();
this.headers = requestTemplate.headers();
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Map<String, Collection<String>> getHeaders() {
return headers;
}
public void setHeaders(Map<String, Collection<String>> headers) {
this.headers = headers;
}
public Map<String, Collection<String>> getQueries() {
return queries;
}
public void setQueries(Map<String, Collection<String>> queries) {
this.queries = queries;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RequestMetadata that = (RequestMetadata) o;
return Objects.equals(method, that.method) &&
Objects.equals(url, that.url) &&
Objects.equals(queries, that.queries) &&
Objects.equals(headers, that.headers);
}
@Override
public int hashCode() {
return Objects.hash(method, url, queries, headers);
}
}

@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.dubbo.metadata;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
/**
* Method Request Metadata
*/
public class RestMethodMetadata {
private MethodMetadata method;
private RequestMetadata request;
private Map<Integer, Collection<String>> indexToName;
public MethodMetadata getMethod() {
return method;
}
public void setMethod(MethodMetadata method) {
this.method = method;
}
public RequestMetadata getRequest() {
return request;
}
public void setRequest(RequestMetadata request) {
this.request = request;
}
public Map<Integer, Collection<String>> getIndexToName() {
return indexToName;
}
public void setIndexToName(Map<Integer, Collection<String>> indexToName) {
this.indexToName = indexToName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RestMethodMetadata that = (RestMethodMetadata) o;
return Objects.equals(method, that.method) &&
Objects.equals(request, that.request) &&
Objects.equals(indexToName, that.indexToName);
}
@Override
public int hashCode() {
return Objects.hash(method, request, indexToName);
}
}

@ -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.springframework.cloud.alibaba.dubbo.metadata;
import java.util.Set;
/**
* Service Rest Metadata
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @see RestMethodMetadata
*/
public class ServiceRestMetadata {
private String name;
private Set<RestMethodMetadata> meta;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<RestMethodMetadata> getMeta() {
return meta;
}
public void setMeta(Set<RestMethodMetadata> meta) {
this.meta = meta;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ServiceRestMetadata that = (ServiceRestMetadata) o;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
return meta != null ? meta.equals(that.meta) : that.meta == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (meta != null ? meta.hashCode() : 0);
return result;
}
}

@ -0,0 +1,118 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.dubbo.metadata.repository;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.alibaba.dubbo.rpc.service.GenericService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.service.MetadataConfigService;
import org.springframework.stereotype.Repository;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceGroup;
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceInterface;
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceSegments;
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceVersion;
/**
* Dubbo Service Metadata {@link Repository}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Repository
public class DubboServiceMetadataRepository {
/**
* Key is application name
* Value is Map<RequestMetadata, GenericService>
*/
private Map<String, Map<RequestMetadata, GenericService>> genericServicesRepository = new HashMap<>();
private Map<String, Map<RequestMetadata, MethodMetadata>> methodMetadataRepository = new HashMap<>();
@Autowired
private MetadataConfigService metadataConfigService;
@Value("${dubbo.target.protocol:dubbo}")
private String targetProtocol;
@Value("${dubbo.target.cluster:failover}")
private String targetCluster;
public void updateMetadata(String serviceName) {
Map<RequestMetadata, GenericService> genericServicesMap = genericServicesRepository.computeIfAbsent(serviceName, k -> new HashMap<>());
Map<RequestMetadata, MethodMetadata> methodMetadataMap = methodMetadataRepository.computeIfAbsent(serviceName, k -> new HashMap<>());
Set<ServiceRestMetadata> serviceRestMetadataSet = metadataConfigService.getServiceRestMetadata(serviceName);
for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) {
ReferenceBean<GenericService> referenceBean = adaptReferenceBean(serviceRestMetadata);
serviceRestMetadata.getMeta().forEach(restMethodMetadata -> {
RequestMetadata requestMetadata = restMethodMetadata.getRequest();
genericServicesMap.put(requestMetadata, referenceBean.get());
methodMetadataMap.put(requestMetadata, restMethodMetadata.getMethod());
});
}
}
public GenericService getGenericService(String serviceName, RequestMetadata requestMetadata) {
return getGenericServicesMap(serviceName).get(requestMetadata);
}
public MethodMetadata getMethodMetadata(String serviceName, RequestMetadata requestMetadata) {
return getMethodMetadataMap(serviceName).get(requestMetadata);
}
private ReferenceBean<GenericService> adaptReferenceBean(ServiceRestMetadata serviceRestMetadata) {
String dubboServiceName = serviceRestMetadata.getName();
String[] segments = getServiceSegments(dubboServiceName);
String interfaceName = getServiceInterface(segments);
String version = getServiceVersion(segments);
String group = getServiceGroup(segments);
ReferenceBean<GenericService> referenceBean = new ReferenceBean<GenericService>();
referenceBean.setGeneric(true);
referenceBean.setInterface(interfaceName);
referenceBean.setVersion(version);
referenceBean.setGroup(group);
referenceBean.setProtocol(targetProtocol);
referenceBean.setCluster(targetCluster);
return referenceBean;
}
private Map<RequestMetadata, GenericService> getGenericServicesMap(String serviceName) {
return genericServicesRepository.getOrDefault(serviceName, Collections.emptyMap());
}
private Map<RequestMetadata, MethodMetadata> getMethodMetadataMap(String serviceName) {
return methodMetadataRepository.getOrDefault(serviceName, Collections.emptyMap());
}
}

@ -0,0 +1,190 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.dubbo.metadata.resolver;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.config.spring.ServiceBean;
import feign.Contract;
import feign.Feign;
import feign.MethodMetadata;
import feign.Util;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* The metadata resolver for {@link Feign}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class FeignMetadataResolver implements BeanClassLoaderAware, SmartInitializingSingleton, MetadataResolver {
private static final String[] CONTRACT_CLASS_NAMES = {
"feign.jaxrs2.JAXRS2Contract",
"org.springframework.cloud.openfeign.support.SpringMvcContract",
};
private final String currentApplicationName;
private final ObjectProvider<Contract> contract;
private ClassLoader classLoader;
/**
* Feign Contracts
*/
private Collection<Contract> contracts;
public FeignMetadataResolver(String currentApplicationName, ObjectProvider<Contract> contract) {
this.currentApplicationName = currentApplicationName;
this.contract = contract;
}
@Override
public void afterSingletonsInstantiated() {
LinkedList<Contract> contracts = new LinkedList<>();
// Add injected Contract if available, for example SpringMvcContract Bean under Spring Cloud Open Feign
contract.ifAvailable(contracts::add);
Stream.of(CONTRACT_CLASS_NAMES)
.filter(this::isClassPresent) // filter the existed classes
.map(this::loadContractClass) // load Contract Class
.map(this::createContract) // create instance by the specified class
.forEach(contracts::add); // add the Contract instance into contracts
this.contracts = Collections.unmodifiableCollection(contracts);
}
private Contract createContract(Class<?> contractClassName) {
return (Contract) BeanUtils.instantiateClass(contractClassName);
}
private Class<?> loadContractClass(String contractClassName) {
return ClassUtils.resolveClassName(contractClassName, classLoader);
}
private boolean isClassPresent(String className) {
return ClassUtils.isPresent(className, classLoader);
}
@Override
public Set<ServiceRestMetadata> resolveServiceRestMetadata(ServiceBean serviceBean) {
Object bean = serviceBean.getRef();
Class<?> beanType = bean.getClass();
Set<ServiceRestMetadata> serviceRestMetadata = new LinkedHashSet<>();
Set<RestMethodMetadata> methodRestMetadata = resolveMethodRestMetadata(beanType);
List<URL> urls = serviceBean.getExportedUrls();
urls.stream()
.map(SpringCloudRegistry::getServiceName)
.forEach(serviceName -> {
ServiceRestMetadata metadata = new ServiceRestMetadata();
metadata.setName(serviceName);
metadata.setMeta(methodRestMetadata);
serviceRestMetadata.add(metadata);
});
return serviceRestMetadata;
}
@Override
public Set<RestMethodMetadata> resolveMethodRestMetadata(Class<?> targetType) {
List<Method> feignContractMethods = selectFeignContractMethods(targetType);
return contracts.stream()
.map(contract -> contract.parseAndValidatateMetadata(targetType))
.flatMap(v -> v.stream())
.map(methodMetadata -> resolveMethodRestMetadata(methodMetadata, targetType, feignContractMethods))
.collect(Collectors.toSet());
}
/**
* Select feign contract methods
* <p>
* extract some code from {@link Contract.BaseContract#parseAndValidatateMetadata(java.lang.Class)}
*
* @param targetType
* @return non-null
*/
private List<Method> selectFeignContractMethods(Class<?> targetType) {
List<Method> methods = new LinkedList<>();
for (Method method : targetType.getMethods()) {
if (method.getDeclaringClass() == Object.class ||
(method.getModifiers() & Modifier.STATIC) != 0 ||
Util.isDefault(method)) {
continue;
}
methods.add(method);
}
return methods;
}
protected RestMethodMetadata resolveMethodRestMetadata(MethodMetadata methodMetadata, Class<?> targetType,
List<Method> feignContractMethods) {
String configKey = methodMetadata.configKey();
Method feignContractMethod = getMatchedFeignContractMethod(targetType, feignContractMethods, configKey);
RestMethodMetadata metadata = new RestMethodMetadata();
metadata.setRequest(new RequestMetadata(methodMetadata.template()));
metadata.setMethod(new org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata(feignContractMethod));
metadata.setIndexToName(methodMetadata.indexToName());
return metadata;
}
private Method getMatchedFeignContractMethod(Class<?> targetType, List<Method> methods, String expectedConfigKey) {
Method matchedMethod = null;
for (Method method : methods) {
String configKey = Feign.configKey(targetType, method);
if (expectedConfigKey.equals(configKey)) {
matchedMethod = method;
break;
}
}
return matchedMethod;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
}

@ -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.springframework.cloud.alibaba.dubbo.metadata.resolver;
import com.alibaba.dubbo.config.spring.ServiceBean;
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import java.util.Set;
/**
* The REST metadata resolver
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface MetadataResolver {
/**
* Resolve the {@link ServiceRestMetadata} {@link Set set} from {@link ServiceBean}
*
* @param serviceBean {@link ServiceBean}
* @return non-null {@link Set}
*/
Set<ServiceRestMetadata> resolveServiceRestMetadata(ServiceBean serviceBean);
/**
* Resolve {@link RestMethodMetadata} {@link Set set} from {@link Class target type}
*
* @param targetType {@link Class target type}
* @return non-null {@link Set}
*/
Set<RestMethodMetadata> resolveMethodRestMetadata(Class<?> targetType);
}

@ -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.springframework.cloud.alibaba.dubbo.metadata.service;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import java.util.Set;
/**
* Config Service for Metadata
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface MetadataConfigService {
void publishServiceRestMetadata(String serviceName, Set<ServiceRestMetadata> serviceRestMetadata);
Set<ServiceRestMetadata> getServiceRestMetadata(String serviceName);
}

@ -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.springframework.cloud.alibaba.dubbo.metadata.service;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.nacos.NacosConfigProperties;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP;
/**
* Nacos {@link MetadataConfigService}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class NacosMetadataConfigService implements MetadataConfigService {
private final ObjectMapper objectMapper = new ObjectMapper();
@Autowired
private NacosConfigProperties nacosConfigProperties;
private ConfigService configService;
@PostConstruct
public void init() {
this.configService = nacosConfigProperties.configServiceInstance();
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
/**
* Get the data Id of service rest metadata
*/
private static String getServiceRestMetadataDataId(String serviceName) {
return "metadata:rest:" + serviceName + ".json";
}
@Override
public void publishServiceRestMetadata(String serviceName, Set<ServiceRestMetadata> serviceRestMetadata) {
String dataId = getServiceRestMetadataDataId(serviceName);
String json = writeValueAsString(serviceRestMetadata);
try {
configService.publishConfig(dataId, DEFAULT_GROUP, json);
} catch (NacosException e) {
throw new RuntimeException(e);
}
}
@Override
public Set<ServiceRestMetadata> getServiceRestMetadata(String serviceName) {
Set<ServiceRestMetadata> metadata = Collections.emptySet();
String dataId = getServiceRestMetadataDataId(serviceName);
try {
String json = configService.getConfig(dataId, DEFAULT_GROUP, 1000 * 3);
metadata = objectMapper.readValue(json,
TypeFactory.defaultInstance().constructCollectionType(LinkedHashSet.class, ServiceRestMetadata.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
return metadata;
}
private String writeValueAsString(Object object) {
String content = null;
try {
content = objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException(e);
}
return content;
}
}

@ -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.springframework.cloud.alibaba.dubbo.openfeign;
import feign.Contract;
import feign.Feign;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
/**
* Dubbo {@link Configuration} for {@link FeignClient FeignClients}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @see DubboOpenFeignAutoConfiguration
* @see org.springframework.cloud.openfeign.FeignContext#setConfigurations(List)
* @see FeignClientsConfiguration
*/
@Configuration
public class DubboFeignClientsConfiguration {
@Autowired
private Contract contract;
@Autowired
private DubboServiceMetadataRepository dubboServiceRepository;
@Bean
public BeanPostProcessor beanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Feign.Builder) {
Feign.Builder builder = (Feign.Builder) bean;
builder.invocationHandlerFactory(new DubboInvocationHandlerFactory(contract, dubboServiceRepository));
}
return bean;
}
};
}
}

@ -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.springframework.cloud.alibaba.dubbo.openfeign;
import com.alibaba.dubbo.rpc.service.GenericService;
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.MethodParameterMetadata;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
/**
* Dubbo {@link GenericService} for {@link InvocationHandler}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboInvocationHandler implements InvocationHandler {
private final Map<Method, GenericService> genericServicesMap;
private final Map<Method, MethodMetadata> methodMetadata;
private final InvocationHandler defaultInvocationHandler;
public DubboInvocationHandler(Map<Method, GenericService> genericServicesMap,
Map<Method, MethodMetadata> methodMetadata,
InvocationHandler defaultInvocationHandler) {
this.genericServicesMap = genericServicesMap;
this.methodMetadata = methodMetadata;
this.defaultInvocationHandler = defaultInvocationHandler;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
GenericService genericService = genericServicesMap.get(method);
MethodMetadata methodMetadata = this.methodMetadata.get(method);
if (genericService == null || methodMetadata == null) {
return defaultInvocationHandler.invoke(proxy, method, args);
}
String methodName = methodMetadata.getName();
String[] parameterTypes = methodMetadata
.getParams()
.stream()
.map(MethodParameterMetadata::getType)
.toArray(String[]::new);
return genericService.$invoke(methodName, parameterTypes, args);
}
}

@ -0,0 +1,101 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.dubbo.openfeign;
import com.alibaba.dubbo.rpc.service.GenericService;
import feign.Contract;
import feign.InvocationHandlerFactory;
import feign.MethodMetadata;
import feign.Target;
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static feign.Feign.configKey;
/**
* Dubbo {@link InvocationHandlerFactory}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboInvocationHandlerFactory implements InvocationHandlerFactory {
private final static InvocationHandlerFactory DEFAULT_INVOCATION_HANDLER_FACTORY =
new InvocationHandlerFactory.Default();
private final Contract contract;
private final DubboServiceMetadataRepository dubboServiceRepository;
public DubboInvocationHandlerFactory(Contract contract, DubboServiceMetadataRepository dubboServiceRepository) {
this.contract = contract;
this.dubboServiceRepository = dubboServiceRepository;
}
@Override
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
// The target class annotated @FeignClient
Class<?> targetType = target.type();
// Resolve metadata from current @FeignClient type
Map<Method, RequestMetadata> methodRequestMetadataMap = resolveMethodRequestMetadataMap(targetType, dispatch.keySet());
// @FeignClient specifies the service name
String serviceName = target.name();
// Update specified metadata
dubboServiceRepository.updateMetadata(serviceName);
Map<Method, GenericService> genericServicesMap = new HashMap<>();
Map<Method, org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata> methodMetadataMap = new HashMap<>();
methodRequestMetadataMap.forEach((method, requestMetadata) -> {
GenericService genericService = dubboServiceRepository.getGenericService(serviceName, requestMetadata);
org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata methodMetadata =
dubboServiceRepository.getMethodMetadata(serviceName, requestMetadata);
genericServicesMap.put(method, genericService);
methodMetadataMap.put(method, methodMetadata);
});
InvocationHandler defaultInvocationHandler = DEFAULT_INVOCATION_HANDLER_FACTORY.create(target, dispatch);
DubboInvocationHandler invocationHandler = new DubboInvocationHandler(genericServicesMap, methodMetadataMap,
defaultInvocationHandler);
return invocationHandler;
}
private Map<Method, RequestMetadata> resolveMethodRequestMetadataMap(Class<?> targetType, Set<Method> methods) {
Map<String, RequestMetadata> requestMetadataMap = resolveRequestMetadataMap(targetType);
return methods.stream().collect(Collectors.toMap(method -> method, method ->
requestMetadataMap.get(configKey(targetType, method))
));
}
private Map<String, RequestMetadata> resolveRequestMetadataMap(Class<?> targetType) {
return contract.parseAndValidatateMetadata(targetType)
.stream().collect(Collectors.toMap(MethodMetadata::configKey, this::requestMetadata));
}
private RequestMetadata requestMetadata(MethodMetadata methodMetadata) {
return new RequestMetadata(methodMetadata.template());
}
}

@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.dubbo.registry;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import java.net.URI;
import java.util.Map;
/**
* The {@link Registration} of Dubbo uses an external of {@link ServiceInstance} instance as the delegate.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
class DubboRegistration implements Registration {
private final ServiceInstance delegate;
public DubboRegistration(ServiceInstance delegate) {
this.delegate = delegate;
}
@Override
public String getServiceId() {
return delegate.getServiceId();
}
@Override
public String getHost() {
return delegate.getHost();
}
@Override
public int getPort() {
return delegate.getPort();
}
@Override
public boolean isSecure() {
return delegate.isSecure();
}
@Override
public URI getUri() {
return delegate.getUri();
}
@Override
public Map<String, String> getMetadata() {
return delegate.getMetadata();
}
@Override
public String getScheme() {
return delegate.getScheme();
}
}

@ -0,0 +1,443 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.dubbo.registry;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.common.utils.UrlUtils;
import com.alibaba.dubbo.registry.NotifyListener;
import com.alibaba.dubbo.registry.RegistryFactory;
import com.alibaba.dubbo.registry.support.FailbackRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static com.alibaba.dubbo.common.Constants.CONFIGURATORS_CATEGORY;
import static com.alibaba.dubbo.common.Constants.CONSUMERS_CATEGORY;
import static com.alibaba.dubbo.common.Constants.PROTOCOL_KEY;
import static com.alibaba.dubbo.common.Constants.PROVIDERS_CATEGORY;
import static com.alibaba.dubbo.common.Constants.ROUTERS_CATEGORY;
/**
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class SpringCloudRegistry extends FailbackRegistry {
/**
* All supported categories
*/
private static final String[] ALL_SUPPORTED_CATEGORIES = of(
PROVIDERS_CATEGORY,
CONSUMERS_CATEGORY,
ROUTERS_CATEGORY,
CONFIGURATORS_CATEGORY
);
private static final int CATEGORY_INDEX = 0;
private static final int PROTOCOL_INDEX = CATEGORY_INDEX + 1;
private static final int SERVICE_INTERFACE_INDEX = PROTOCOL_INDEX + 1;
private static final int SERVICE_VERSION_INDEX = SERVICE_INTERFACE_INDEX + 1;
private static final int SERVICE_GROUP_INDEX = SERVICE_VERSION_INDEX + 1;
private static final String WILDCARD = "*";
/**
* The separator for service name
*/
private static final String SERVICE_NAME_SEPARATOR = ":";
private final ServiceRegistry<Registration> serviceRegistry;
private final DiscoveryClient discoveryClient;
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* {@link ScheduledExecutorService} lookup service names(only for Dubbo-OPS)
*/
private volatile ScheduledExecutorService scheduledExecutorService;
/**
* The interval in second of lookup service names(only for Dubbo-OPS)
*/
private static final long LOOKUP_INTERVAL = Long.getLong("dubbo.service.names.lookup.interval", 30);
public SpringCloudRegistry(URL url, ServiceRegistry<Registration> serviceRegistry,
DiscoveryClient discoveryClient) {
super(url);
this.serviceRegistry = serviceRegistry;
this.discoveryClient = discoveryClient;
}
@Override
protected void doRegister(URL url) {
final String serviceName = getServiceName(url);
final Registration registration = createRegistration(serviceName, url);
serviceRegistry.register(registration);
}
@Override
protected void doUnregister(URL url) {
final String serviceName = getServiceName(url);
final Registration registration = createRegistration(serviceName, url);
this.serviceRegistry.deregister(registration);
}
@Override
protected void doSubscribe(URL url, NotifyListener listener) {
List<String> serviceNames = getServiceNames(url, listener);
doSubscribe(url, listener, serviceNames);
}
@Override
protected void doUnsubscribe(URL url, NotifyListener listener) {
if (isAdminProtocol(url)) {
shutdownServiceNamesLookup();
}
}
@Override
public boolean isAvailable() {
return false;
}
private void shutdownServiceNamesLookup() {
if (scheduledExecutorService != null) {
scheduledExecutorService.shutdown();
}
}
private Registration createRegistration(String serviceName, URL url) {
return new DubboRegistration(createServiceInstance(serviceName, url));
}
private ServiceInstance createServiceInstance(String serviceName, URL url) {
// Append default category if absent
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
URL newURL = url.addParameter(Constants.CATEGORY_KEY, category);
newURL = newURL.addParameter(Constants.PROTOCOL_KEY, url.getProtocol());
String ip = NetUtils.getLocalHost();
int port = newURL.getParameter(Constants.BIND_PORT_KEY, url.getPort());
DefaultServiceInstance serviceInstance = new DefaultServiceInstance(serviceName, ip, port, false);
serviceInstance.getMetadata().putAll(new LinkedHashMap<>(newURL.getParameters()));
return serviceInstance;
}
public static String getServiceName(URL url) {
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
return getServiceName(url, category);
}
private static String getServiceName(URL url, String category) {
StringBuilder serviceNameBuilder = new StringBuilder(category);
appendIfPresent(serviceNameBuilder, url.getParameter(PROTOCOL_KEY, url.getProtocol()));
appendIfPresent(serviceNameBuilder, url, Constants.INTERFACE_KEY);
appendIfPresent(serviceNameBuilder, url, Constants.VERSION_KEY);
appendIfPresent(serviceNameBuilder, url, Constants.GROUP_KEY);
return serviceNameBuilder.toString();
}
private static void appendIfPresent(StringBuilder target, URL url, String parameterName) {
String parameterValue = url.getParameter(parameterName);
appendIfPresent(target, parameterValue);
}
private static void appendIfPresent(StringBuilder target, String parameterValue) {
if (StringUtils.hasText(parameterValue)) {
target.append(SERVICE_NAME_SEPARATOR).append(parameterValue);
}
}
private void filterServiceNames(List<String> serviceNames, URL url) {
final String[] categories = getCategories(url);
final String targetServiceInterface = url.getServiceInterface();
final String targetVersion = url.getParameter(Constants.VERSION_KEY);
final String targetGroup = url.getParameter(Constants.GROUP_KEY);
filter(serviceNames, new Filter<String>() {
@Override
public boolean accept(String serviceName) {
// split service name to segments
// (required) segments[0] = category
// (required) segments[1] = serviceInterface
// (required) segments[2] = protocol
// (required) segments[3] = version
// (optional) segments[4] = group
String[] segments = getServiceSegments(serviceName);
int length = segments.length;
if (length < 4) { // must present 4 segments or more
return false;
}
String category = getCategory(segments);
if (Arrays.binarySearch(categories, category) > -1) { // no match category
return false;
}
String protocol = getProtocol(segments);
if (StringUtils.hasText(protocol)) {
return false;
}
String serviceInterface = getServiceInterface(segments);
if (!WILDCARD.equals(targetServiceInterface) &&
!Objects.equals(targetServiceInterface, serviceInterface)) { // no match service interface
return false;
}
String version = getServiceVersion(segments);
if (!WILDCARD.equals(targetVersion) &&
!Objects.equals(targetVersion, version)) { // no match service version
return false;
}
String group = getServiceGroup(segments);
if (group != null && !WILDCARD.equals(targetGroup)
&& !Objects.equals(targetGroup, group)) { // no match service group
return false;
}
return true;
}
});
}
public static String[] getServiceSegments(String serviceName) {
return StringUtils.delimitedListToStringArray(serviceName, SERVICE_NAME_SEPARATOR);
}
public static String getCategory(String[] segments) {
return segments[CATEGORY_INDEX];
}
public static String getProtocol(String[] segments) {
return segments[PROTOCOL_INDEX];
}
public static String getServiceInterface(String[] segments) {
return segments[SERVICE_INTERFACE_INDEX];
}
public static String getServiceVersion(String[] segments) {
return segments[SERVICE_VERSION_INDEX];
}
public static String getServiceGroup(String[] segments) {
return segments.length > 4 ? segments[SERVICE_GROUP_INDEX] : null;
}
/**
* Get the categories from {@link URL}
*
* @param url {@link URL}
* @return non-null array
*/
private String[] getCategories(URL url) {
return Constants.ANY_VALUE.equals(url.getServiceInterface()) ?
ALL_SUPPORTED_CATEGORIES : of(Constants.DEFAULT_CATEGORY);
}
private List<String> getAllServiceNames() {
return discoveryClient.getServices();
}
/**
* Get the service names from the specified {@link URL url}
*
* @param url {@link URL}
* @param listener {@link NotifyListener}
* @return non-null
*/
private List<String> getServiceNames(URL url, NotifyListener listener) {
if (isAdminProtocol(url)) {
scheduleServiceNamesLookup(url, listener);
return getServiceNamesForOps(url);
} else {
return doGetServiceNames(url);
}
}
private boolean isAdminProtocol(URL url) {
return Constants.ADMIN_PROTOCOL.equals(url.getProtocol());
}
private void scheduleServiceNamesLookup(final URL url, final NotifyListener listener) {
if (scheduledExecutorService == null) {
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
List<String> serviceNames = getAllServiceNames();
filter(serviceNames, new Filter<String>() {
@Override
public boolean accept(String serviceName) {
boolean accepted = false;
for (String category : ALL_SUPPORTED_CATEGORIES) {
String prefix = category + SERVICE_NAME_SEPARATOR;
if (StringUtils.startsWithIgnoreCase(serviceName, prefix)) {
accepted = true;
break;
}
}
return accepted;
}
});
doSubscribe(url, listener, serviceNames);
}
}, LOOKUP_INTERVAL, LOOKUP_INTERVAL, TimeUnit.SECONDS);
}
}
private void doSubscribe(final URL url, final NotifyListener listener, final List<String> serviceNames) {
for (String serviceName : serviceNames) {
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
notifySubscriber(url, listener, serviceInstances);
// TODO Support Update notification event
}
}
private List<String> doGetServiceNames(URL url) {
String[] categories = getCategories(url);
List<String> serviceNames = new ArrayList<String>(categories.length);
for (String category : categories) {
final String serviceName = getServiceName(url, category);
serviceNames.add(serviceName);
}
return serviceNames;
}
/**
* Notify the Healthy {@link ServiceInstance service instance} to subscriber.
*
* @param url {@link URL}
* @param listener {@link NotifyListener}
* @param serviceInstances all {@link ServiceInstance instances}
*/
private void notifySubscriber(URL url, NotifyListener listener, List<ServiceInstance> serviceInstances) {
List<ServiceInstance> healthyInstances = new LinkedList<ServiceInstance>(serviceInstances);
// Healthy Instances
filterHealthyInstances(healthyInstances);
List<URL> urls = buildURLs(url, healthyInstances);
this.notify(url, listener, urls);
}
private void filterHealthyInstances(Collection<ServiceInstance> instances) {
filter(instances, new Filter<ServiceInstance>() {
@Override
public boolean accept(ServiceInstance data) {
// TODO check the details of status
// return serviceRegistry.getStatus(new DubboRegistration(data)) != null;
return true;
}
});
}
private List<URL> buildURLs(URL consumerURL, Collection<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
return Collections.emptyList();
}
List<URL> urls = new LinkedList<URL>();
for (ServiceInstance serviceInstance : serviceInstances) {
URL url = buildURL(serviceInstance);
if (UrlUtils.isMatch(consumerURL, url)) {
urls.add(url);
}
}
return urls;
}
private URL buildURL(ServiceInstance serviceInstance) {
URL url = new URL(serviceInstance.getMetadata().get(Constants.PROTOCOL_KEY),
serviceInstance.getHost(),
serviceInstance.getPort(),
serviceInstance.getMetadata());
return url;
}
/**
* Get the service names for Dubbo OPS
*
* @param url {@link URL}
* @return non-null
*/
private List<String> getServiceNamesForOps(URL url) {
List<String> serviceNames = getAllServiceNames();
filterServiceNames(serviceNames, url);
return serviceNames;
}
private <T> void filter(Collection<T> collection, Filter<T> filter) {
Iterator<T> iterator = collection.iterator();
while (iterator.hasNext()) {
T data = iterator.next();
if (!filter.accept(data)) { // remove if not accept
iterator.remove();
}
}
}
private static <T> T[] of(T... values) {
return values;
}
/**
* A filter
*/
private interface Filter<T> {
/**
* Tests whether or not the specified data should be accepted.
*
* @param data The data to be tested
* @return <code>true</code> if and only if <code>data</code>
* should be accepted
*/
boolean accept(T data);
}
}

@ -0,0 +1,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.springframework.cloud.alibaba.dubbo.registry;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.registry.Registry;
import com.alibaba.dubbo.registry.RegistryFactory;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationContext;
/**
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @see RegistryFactory
* @see SpringCloudRegistry
*/
public class SpringCloudRegistryFactory implements RegistryFactory {
private static ApplicationContext applicationContext;
@Override
public Registry getRegistry(URL url) {
ServiceRegistry<Registration> serviceRegistry = applicationContext.getBean(ServiceRegistry.class);
DiscoveryClient discoveryClient = applicationContext.getBean(DiscoveryClient.class);
return new SpringCloudRegistry(url, serviceRegistry, discoveryClient);
}
public static void setApplicationContext(ApplicationContext applicationContext) {
SpringCloudRegistryFactory.applicationContext = applicationContext;
}
}

@ -0,0 +1 @@
spring-cloud=org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistryFactory

@ -0,0 +1,7 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboRestMetadataRegistrationAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer

@ -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.springframework.cloud.alibaba.dubbo.autoconfigure;
import org.springframework.boot.test.context.SpringBootTest;
/**
* {@link DubboServiceRegistrationAutoConfiguration} Test
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@SpringBootTest
public class DubboServiceRegistrationAutoConfigurationTest {
}

@ -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.springframework.cloud.alibaba.dubbo.bootstrap;
import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.cloud.alibaba.dubbo.service.EchoService;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* Dubbo Spring Cloud Bootstrap
*/
@EnableDiscoveryClient
@EnableAutoConfiguration
@EnableFeignClients
@RestController
public class DubboSpringCloudBootstrap {
@Reference(version = "1.0.0")
private EchoService echoService;
@Autowired
@Lazy
private FeignEchoService feignEchoService;
@GetMapping(value = "/call/echo")
public String echo(@RequestParam("message") String message) {
return feignEchoService.echo(message);
}
@FeignClient("spring-cloud-alibaba-dubbo")
public interface FeignEchoService {
@GetMapping(value = "/echo")
String echo(@RequestParam("message") String message);
}
@Bean
public ApplicationRunner applicationRunner() {
return arguments -> {
// Dubbo Service call
System.out.println(echoService.echo("mercyblitz"));
// Spring Cloud Open Feign REST Call
System.out.println(feignEchoService.echo("mercyblitz"));
};
}
public static void main(String[] args) {
new SpringApplicationBuilder(DubboSpringCloudBootstrap.class)
.run(args);
}
}

@ -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.springframework.cloud.alibaba.dubbo.service;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.dubbo.rpc.RpcContext;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
/**
* Default {@link EchoService}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Service(version = "1.0.0", protocol = {"dubbo", "rest"})
@RestController
@Path("/")
public class DefaultEchoService implements EchoService {
@Override
@GetMapping(value = "/echo"
// consumes = MediaType.APPLICATION_JSON_VALUE,
// produces = MediaType.APPLICATION_JSON_UTF8_VALUE
)
@Path("/echo")
@GET
// @Consumes("application/json")
// @Produces("application/json;charset=UTF-8")
public String echo(@RequestParam @QueryParam("message") String message) {
return RpcContext.getContext().getUrl() + " [echo] : " + message;
}
@Override
@PostMapping("/plus")
@Path("/plus")
@POST
public String plus(@RequestParam @QueryParam("a") int a, @RequestParam @QueryParam("b") int b) {
return null;
}
}

@ -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.springframework.cloud.alibaba.dubbo.service;
/**
* Echo Service
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface EchoService {
String echo(String message);
String plus(int a, int b);
}

@ -0,0 +1,16 @@
dubbo:
scan:
base-packages: org.springframework.cloud.alibaba.dubbo.service
protocols:
dubbo:
name: dubbo
port: 12345
rest:
name: rest
port: 9090
server: netty
registry:
address: spring-cloud://nacos
server:
port: 8080

@ -0,0 +1,28 @@
spring:
application:
name: spring-cloud-alibaba-dubbo
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
eureka:
client:
enabled: false
---
spring:
profiles: eureka
cloud:
nacos:
discovery:
enabled: false
register-enabled: false
eureka:
client:
enabled: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka/

@ -1,7 +1,11 @@
package org.springframework.alicloud.env.extension;
import java.lang.annotation.*;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>account-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-fescar</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<inherited>false</inherited>
<configuration>
<skip>true</skip>
<check/>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,32 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author xiaojing
*/
@SpringBootApplication
public class AccountApplication {
public static void main(String[] args) {
SpringApplication.run(AccountApplication.class, args);
}
}

@ -0,0 +1,66 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import java.util.Random;
import com.alibaba.fescar.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xiaojing
*/
@RestController
public class AccountController {
private static final Logger LOGGER = LoggerFactory.getLogger(AccountController.class);
private static final String SUCCESS = "SUCCESS";
private static final String FAIL = "FAIL";
private final JdbcTemplate jdbcTemplate;
private Random random;
public AccountController(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.random = new Random();
}
@RequestMapping(value = "/account", method = RequestMethod.POST, produces = "application/json")
public String account(String userId, int money) {
LOGGER.info("Account Service ... xid: " + RootContext.getXID());
if (random.nextBoolean()) {
throw new RuntimeException("this is a mock Exception");
}
int result = jdbcTemplate.update(
"update account_tbl set money = money - ? where user_id = ?",
new Object[] { money, userId });
LOGGER.info("Account Service End ... ");
if (result == 1) {
return SUCCESS;
}
return FAIL;
}
}

@ -0,0 +1,92 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import java.sql.SQLException;
import java.util.Random;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fescar.rm.datasource.DataSourceProxy;
/**
* @author xiaojing
*/
@Configuration
public class DatabaseConfiguration {
private final ApplicationContext applicationContext;
public DatabaseConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean(initMethod = "init", destroyMethod = "close")
public DruidDataSource storageDataSource() throws SQLException {
Environment environment = applicationContext.getEnvironment();
String ip = environment.getProperty("mysql.server.ip");
String port = environment.getProperty("mysql.server.port");
String dbName = environment.getProperty("mysql.db.name");
String userName = environment.getProperty("mysql.user.name");
String password = environment.getProperty("mysql.user.password");
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName);
druidDataSource.setUsername(userName);
druidDataSource.setPassword(password);
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setInitialSize(0);
druidDataSource.setMaxActive(180);
druidDataSource.setMaxWait(60000);
druidDataSource.setMinIdle(0);
druidDataSource.setValidationQuery("Select 'x' from DUAL");
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
druidDataSource.setMinEvictableIdleTimeMillis(25200000);
druidDataSource.setRemoveAbandoned(true);
druidDataSource.setRemoveAbandonedTimeout(1800);
druidDataSource.setLogAbandoned(true);
druidDataSource.setFilters("mergeStat");
return druidDataSource;
}
@Bean
public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
@Bean
public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy);
jdbcTemplate.update("delete from account_tbl where user_id = 'U100001'");
jdbcTemplate.update(
"insert into account_tbl(user_id, money) values ('U100001', 10000)");
return jdbcTemplate;
}
}

@ -0,0 +1,30 @@
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
#thread factory for netty
thread-factory {
boss-thread-prefix = "NettyBoss"
worker-thread-prefix = "NettyServerNIOWorker"
server-executor-thread-prefix = "NettyServerBizHandler"
share-boss-worker = false
client-selector-thread-prefix = "NettyClientSelector"
client-selector-thread-size = 1
client-worker-thread-prefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
boss-thread-size = 1
#auto default pin or 8
worker-thread-size = 8
}
}
service {
#vgroup->rgroup
vgroup_mapping.account-service-fescar-service-group = "localRgroup"
#only support single node
localRgroup.grouplist = "127.0.0.1:8091"
#degrade current not support
enableDegrade = false
#disable
disable = false
}

@ -0,0 +1,9 @@
spring.application.name=account-service
server.port=18084
mysql.server.ip=127.0.0.1
mysql.server.port=3306
mysql.db.name=demo
mysql.user.name=xxxxx
mysql.user.password=xxxxx

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>business-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-fescar</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<inherited>false</inherited>
<configuration>
<skip>true</skip>
<check/>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,63 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
/**
* @author xiaojing
*/
@SpringBootApplication
@EnableFeignClients
public class BusinessApplication {
public static void main(String[] args) {
SpringApplication.run(BusinessApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
@FeignClient(value = "storage", url = "http://127.0.0.1:18082")
public interface StorageService {
@RequestMapping(path = "/storage/{commodityCode}/{count}")
String storage(@RequestParam("commodityCode") String commodityCode,
@RequestParam("count") int count);
}
@FeignClient(value = "order", url = "http://127.0.0.1:18083")
public interface OrderService {
@RequestMapping(path = "/order", method = RequestMethod.POST)
String order(@RequestParam("userId") String userId,
@RequestParam("commodityCode") String commodityCode,
@RequestParam("orderCount") int orderCount);
}
}

@ -0,0 +1,117 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import com.alibaba.fescar.spring.annotation.GlobalTransactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.alibaba.cloud.examples.BusinessApplication.OrderService;
import org.springframework.cloud.alibaba.cloud.examples.BusinessApplication.StorageService;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @author xiaojing
*/
@RestController
public class HomeController {
private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class);
private static final String SUCCESS = "SUCCESS";
private static final String FAIL = "FAIL";
private static final String USER_ID = "U100001";
private static final String COMMODITY_CODE = "C00321";
private static final int ORDER_COUNT = 2;
private final RestTemplate restTemplate;
private final OrderService orderService;
private final StorageService storageService;
public HomeController(RestTemplate restTemplate, OrderService orderService,
StorageService storageService) {
this.restTemplate = restTemplate;
this.orderService = orderService;
this.storageService = storageService;
}
@GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-demo-tx")
@RequestMapping(value = "/fescar/rest", method = RequestMethod.GET, produces = "application/json")
public String rest() {
String result = restTemplate.getForObject(
"http://127.0.0.1:18082/storage/" + COMMODITY_CODE + "/" + ORDER_COUNT,
String.class);
if (!SUCCESS.equals(result)) {
throw new RuntimeException();
}
String url = "http://127.0.0.1:18083/order";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("userId", USER_ID);
map.add("commodityCode", COMMODITY_CODE);
map.add("orderCount", ORDER_COUNT + "");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(
map, headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, request,
String.class);
result = response.getBody();
if (!SUCCESS.equals(result)) {
throw new RuntimeException();
}
return SUCCESS;
}
@GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-demo-tx")
@RequestMapping(value = "/fescar/feign", method = RequestMethod.GET, produces = "application/json")
public String feign() {
String result = storageService.storage(COMMODITY_CODE, ORDER_COUNT);
if (!SUCCESS.equals(result)) {
throw new RuntimeException();
}
result = orderService.order(USER_ID, COMMODITY_CODE, ORDER_COUNT);
if (!SUCCESS.equals(result)) {
throw new RuntimeException();
}
return SUCCESS;
}
}

@ -0,0 +1,33 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import java.io.Serializable;
public class Order implements Serializable {
public long id;
public String userId;
public String commodityCode;
public int count;
public int money;
@Override
public String toString() {
return "Order{" + "id=" + id + ", userId='" + userId + '\'' + ", commodityCode='"
+ commodityCode + '\'' + ", count=" + count + ", money=" + money + '}';
}
}

@ -0,0 +1,30 @@
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
#thread factory for netty
thread-factory {
boss-thread-prefix = "NettyBoss"
worker-thread-prefix = "NettyServerNIOWorker"
server-executor-thread-prefix = "NettyServerBizHandler"
share-boss-worker = false
client-selector-thread-prefix = "NettyClientSelector"
client-selector-thread-size = 1
client-worker-thread-prefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
boss-thread-size = 1
#auto default pin or 8
worker-thread-size = 8
}
}
service {
#vgroup->rgroup
vgroup_mapping.business-service-fescar-service-group = "localRgroup"
#only support single node
localRgroup.grouplist = "127.0.0.1:8091"
#degrade current not support
enableDegrade = false
#disable
disable = false
}

@ -0,0 +1,8 @@
server.port=18081
spring.application.name=business-service
# The following configuration can be omitted.
#feign.hystrix.enabled=true
#feign.sentinel.enabled=true
logging.level.com.alibaba.fescar=debug

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-fescar</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<inherited>false</inherited>
<configuration>
<skip>true</skip>
<check/>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,89 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import java.sql.SQLException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fescar.rm.datasource.DataSourceProxy;
/**
* @author xiaojing
*/
@Configuration
public class DatabaseConfiguration {
private final ApplicationContext applicationContext;
public DatabaseConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean(initMethod = "init", destroyMethod = "close")
public DruidDataSource storageDataSource() throws SQLException {
Environment env = applicationContext.getEnvironment();
String ip = env.getProperty("mysql.server.ip");
String port = env.getProperty("mysql.server.port");
String dbName = env.getProperty("mysql.db.name");
String userName = env.getProperty("mysql.user.name");
String password = env.getProperty("mysql.user.password");
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName);
druidDataSource.setUsername(userName);
druidDataSource.setPassword(password);
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setInitialSize(0);
druidDataSource.setMaxActive(180);
druidDataSource.setMaxWait(60000);
druidDataSource.setMinIdle(0);
druidDataSource.setValidationQuery("Select 'x' from DUAL");
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
druidDataSource.setMinEvictableIdleTimeMillis(25200000);
druidDataSource.setRemoveAbandoned(true);
druidDataSource.setRemoveAbandonedTimeout(1800);
druidDataSource.setLogAbandoned(true);
druidDataSource.setFilters("mergeStat");
return druidDataSource;
}
@Bean
public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
@Bean
public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy);
jdbcTemplate.execute("TRUNCATE TABLE order_tbl");
return jdbcTemplate;
}
}

@ -0,0 +1,39 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author xiaojing
*/
@SpringBootApplication
public class OderApplication {
public static void main(String[] args) {
SpringApplication.run(OderApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

@ -0,0 +1,33 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import java.io.Serializable;
public class Order implements Serializable {
public long id;
public String userId;
public String commodityCode;
public int count;
public int money;
@Override
public String toString() {
return "Order{" + "id=" + id + ", userId='" + userId + '\'' + ", commodityCode='"
+ commodityCode + '\'' + ", count=" + count + ", money=" + money + '}';
}
}

@ -0,0 +1,131 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Random;
import com.alibaba.fescar.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
/**
* @author xiaojing
*/
@RestController
public class OrderController {
private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);
private static final String SUCCESS = "SUCCESS";
private static final String FAIL = "FAIL";
private static final String USER_ID = "U100001";
private static final String COMMODITY_CODE = "C00321";
private final JdbcTemplate jdbcTemplate;
private final RestTemplate restTemplate;
private Random random;
public OrderController(JdbcTemplate jdbcTemplate, RestTemplate restTemplate) {
this.jdbcTemplate = jdbcTemplate;
this.restTemplate = restTemplate;
this.random = new Random();
}
@RequestMapping(value = "/order", method = RequestMethod.POST, produces = "application/json")
public String order(String userId, String commodityCode, int orderCount) {
LOGGER.info("Order Service Begin ... xid: " + RootContext.getXID());
int orderMoney = calculate(commodityCode, orderCount);
invokerAccountService(orderMoney);
final Order order = new Order();
order.userId = userId;
order.commodityCode = commodityCode;
order.count = orderCount;
order.money = orderMoney;
KeyHolder keyHolder = new GeneratedKeyHolder();
int result = jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection con)
throws SQLException {
PreparedStatement pst = con.prepareStatement(
"insert into order_tbl (user_id, commodity_code, count, money) values (?, ?, ?, ?)",
PreparedStatement.RETURN_GENERATED_KEYS);
pst.setObject(1, order.userId);
pst.setObject(2, order.commodityCode);
pst.setObject(3, order.count);
pst.setObject(4, order.money);
return pst;
}
}, keyHolder);
order.id = (long) keyHolder.getKey();
if (random.nextBoolean()) {
throw new RuntimeException("this is a mock Exception");
}
LOGGER.info("Order Service End ... Created " + order);
if (result == 1) {
return SUCCESS;
}
return FAIL;
}
private int calculate(String commodityId, int orderCount) {
return 2 * orderCount;
}
private void invokerAccountService(int orderMoney) {
String url = "http://127.0.0.1:18084/account";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
map.add("userId", USER_ID);
map.add("money", orderMoney + "");
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(
map, headers);
ResponseEntity<String> response = restTemplate.postForEntity(url, request,
String.class);
}
}

@ -0,0 +1,30 @@
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
#thread factory for netty
thread-factory {
boss-thread-prefix = "NettyBoss"
worker-thread-prefix = "NettyServerNIOWorker"
server-executor-thread-prefix = "NettyServerBizHandler"
share-boss-worker = false
client-selector-thread-prefix = "NettyClientSelector"
client-selector-thread-size = 1
client-worker-thread-prefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
boss-thread-size = 1
#auto default pin or 8
worker-thread-size = 8
}
}
service {
#vgroup->rgroup
vgroup_mapping.order-service-fescar-service-group = "localRgroup"
#only support single node
localRgroup.grouplist = "127.0.0.1:8091"
#degrade current not support
enableDegrade = false
#disable
disable = false
}

@ -0,0 +1,9 @@
spring.application.name=order-service
server.port=18083
mysql.server.ip=127.0.0.1
mysql.server.port=3306
mysql.db.name=demo
mysql.user.name=xxxxx
mysql.user.password=xxxxx

@ -0,0 +1,156 @@
# Fescar Example
## 项目说明
本项目演示如何使用 Fescar Starter 完成 Spring Cloud 应用的分布式事务接入。
[Fescar](https://github.com/alibaba/fescar) 是 阿里巴巴 开源的 分布式事务中间件,以 高效 并且对业务 0 侵入 的方式,解决 微服务 场景下面临的分布式事务问题。
## 准备工作
在运行此示例之前,你需要先完成如下几步准备工作:
1. 配置数据库
1. 创建 UNDO_LOG 表
1. 创建 示例中 业务所需要的数据库表
1. 启动 Fescar Server
### 配置数据库
首先,你需要有一个支持 InnoDB 引擎的 MySQL 数据库。
**注意** 实际上Fescar 支持不同的应用使用完全不相干的数据库,但是这里为了简单地演示一个原理,所以我们选择了只使用一个数据库。
`account-server`、`order-service`、`storage-service` 这三个应用中的 resources 目录下的 `application.properties` 文件中的如下配置修改成你运行环境中的实际配置。
```
mysql.server.ip=your mysql server ip address
mysql.server.port=your mysql server listening port
mysql.db.name=your database name for test
mysql.user.name=your mysql server username
mysql.user.password=your mysql server password
```
### 创建 UNDO_LOG 表
[Fescar AT 模式]() 需要使用到 UNDO_LOG 表。
``` $sql
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
`ext` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_unionkey` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=159 DEFAULT CHARSET=utf8
```
### 创建 示例中 业务所需要的数据库表
```$sql
DROP TABLE IF EXISTS `storage_tbl`;
CREATE TABLE `storage_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY (`commodity_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `order_tbl`;
CREATE TABLE `order_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`commodity_code` varchar(255) DEFAULT NULL,
`count` int(11) DEFAULT 0,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `account_tbl`;
CREATE TABLE `account_tbl` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`money` int(11) DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
```
### 启动 Fescar Server
点击这个页面 [https://github.com/alibaba/fescar/releases](https://github.com/alibaba/fescar/releases),下载最新版本的 Fescar Server 端.
进入解压之后的 bin 目录,执行如下命令来启动
```$shell
sh fescar-server.sh $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA
```
在这个示例中,采用如下命令来启动 Fescar Server
```$shell
sh fescar-server.sh 8091 ~/fescar/data/
```
**注意** 如果你修改了端口号,那么记得需要在各个示例工程中的 `application.conf` 文件中,修改 grouplist 的值。
## 运行示例
分别运行 `account-server`、`order-service`、`storage-service` 和 `business-service` 这三个应用的 Main 函数,启动示例。
启动示例后,通过 HTTP 的 GET 方法访问如下两个 URL可以分别验证在 `business-service` 中 通过 RestTemplate 和 FeignClient 调用其他服务的场景。
```$xslt
http://127.0.0.1:18081/fescar/feign
http://127.0.0.1:18081/fescar/rest
```
## 如何验证分布式事务成功?
### Xid 信息是否成功传递
`account-server`、`order-service` 和 `storage-service` 三个 服务的 Controller 中,第一个执行的逻辑都是输出 RootContext 中的 Xid 信息,如果看到都输出了正确的 Xid 信息,即每次都发生变化,且同一次调用中所有服务的 Xid 都一致。则表明 Fescar 的 Xid 的传递和还原是正常的。
### 数据库中数据是否一致
在本示例中我们模拟了一个用户购买货物的场景StorageService 负责扣减库存数量OrderService 负责保存订单AccountService 负责扣减用户账户余额。
为了演示样例,我们在 OrderService 和 AccountService 中 使用 Random.nextBoolean() 的方式来随机抛出异常,模拟了在服务调用时随机发生异常的场景。
如果分布式事务生效的话, 那么以下等式应该成立
- 用户原始金额(1000) = 用户现存的金额 + 货物单价 (2) * 订单数量 * 每单的货物数量(2)
- 货物的初始数量(100) = 货物的现存数量 + 订单数量 * 每单的货物数量(2)
## 对 Spring Cloud 支持点
- 通过 Spring MVC 提供服务的服务提供者,在收到 header 中含有 Fescar 信息的 HTTP 请求时,可以自动还原 Fescar 上下文。
- 支持服务调用者通过 RestTemplate 调用时,自动传递 Fescar 上下文。
- 支持服务调用者通过 FeignClient 调用时,自动传递 Fescar 上下文。
- 支持 FeignClient 和 Hystrix 同时使用的场景。
- 支持 FeignClient 和 Sentinel 同时使用的场景。

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>0.2.2.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>storage-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-fescar</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<inherited>false</inherited>
<configuration>
<skip>true</skip>
<check/>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,94 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import java.sql.SQLException;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fescar.rm.datasource.DataSourceProxy;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* @author xiaojing
*/
@Configuration
public class DatabaseConfiguration {
private final ApplicationContext applicationContext;
public DatabaseConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Bean(initMethod = "init", destroyMethod = "close")
public DruidDataSource storageDataSource() throws SQLException {
Environment environment = applicationContext.getEnvironment();
String ip = environment.getProperty("mysql.server.ip");
String port = environment.getProperty("mysql.server.port");
String dbName = environment.getProperty("mysql.db.name");
String userName = environment.getProperty("mysql.user.name");
String password = environment.getProperty("mysql.user.password");
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName);
druidDataSource.setUsername(userName);
druidDataSource.setPassword(password);
druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
druidDataSource.setInitialSize(0);
druidDataSource.setMaxActive(180);
druidDataSource.setMaxWait(60000);
druidDataSource.setMinIdle(0);
druidDataSource.setValidationQuery("Select 'x' from DUAL");
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
druidDataSource.setMinEvictableIdleTimeMillis(25200000);
druidDataSource.setRemoveAbandoned(true);
druidDataSource.setRemoveAbandonedTimeout(1800);
druidDataSource.setLogAbandoned(true);
druidDataSource.setFilters("mergeStat");
return druidDataSource;
}
@Bean
public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
@Bean
public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy);
jdbcTemplate.update("delete from storage_tbl where commodity_code = 'C00321'");
jdbcTemplate.update(
"insert into storage_tbl(commodity_code, count) values ('C00321', 100)");
return jdbcTemplate;
}
}

@ -0,0 +1,32 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author xiaojing
*/
@SpringBootApplication
public class StorageApplication {
public static void main(String[] args) {
SpringApplication.run(StorageApplication.class, args);
}
}

@ -0,0 +1,58 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import com.alibaba.fescar.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xiaojing
*/
@RestController
public class StorageController {
private static final Logger LOGGER = LoggerFactory.getLogger(StorageController.class);
private static final String SUCCESS = "SUCCESS";
private static final String FAIL = "FAIL";
private final JdbcTemplate jdbcTemplate;
public StorageController(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@RequestMapping(value = "/storage/{commodityCode}/{count}", method = RequestMethod.GET, produces = "application/json")
public String echo(@PathVariable String commodityCode, @PathVariable int count) {
LOGGER.info("Storage Service Begin ... xid: " + RootContext.getXID());
int result = jdbcTemplate.update(
"update storage_tbl set count = count - ? where commodity_code = ?",
new Object[] { count, commodityCode });
LOGGER.info("Storage Service End ... ");
if (result == 1) {
return SUCCESS;
}
return FAIL;
}
}

@ -0,0 +1,30 @@
transport {
# tcp udt unix-domain-socket
type = "TCP"
#NIO NATIVE
server = "NIO"
#thread factory for netty
thread-factory {
boss-thread-prefix = "NettyBoss"
worker-thread-prefix = "NettyServerNIOWorker"
server-executor-thread-prefix = "NettyServerBizHandler"
share-boss-worker = false
client-selector-thread-prefix = "NettyClientSelector"
client-selector-thread-size = 1
client-worker-thread-prefix = "NettyClientWorkerThread"
# netty boss thread size,will not be used for UDT
boss-thread-size = 1
#auto default pin or 8
worker-thread-size = 8
}
}
service {
#vgroup->rgroup
vgroup_mapping.storage-service-fescar-service-group = "localRgroup"
#only support single node
localRgroup.grouplist = "127.0.0.1:8091"
#degrade current not support
enableDegrade = false
#disable
disable = false
}

@ -0,0 +1,9 @@
spring.application.name=storage-service
server.port=18082
mysql.server.ip=127.0.0.1
mysql.server.port=3306
mysql.db.name=demo
mysql.user.name=xxxxx
mysql.user.password=xxxxx

@ -113,11 +113,11 @@ Nacos Client 从 Nacos Server 端获取数据时,调用的是此接口 `Config
在 Nacos Config Starter 中dataId 的拼接格式如下
${prefix} - ${spring.active.profile} . ${file-extension}
${prefix} - ${spring.profiles.active} . ${file-extension}
* `prefix` 默认为 `spring.application.name` 的值,也可以通过配置项 `spring.cloud.nacos.config.prefix`来配置。
* `spring.active.profile` 即为当前环境对应的 profile详情可以参考 [Spring Boot文档](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)
* `spring.profiles.active` 即为当前环境对应的 profile详情可以参考 [Spring Boot文档](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)
**注意,当 activeprofile 为空时,对应的连接符 `-` 也将不存在dataId 的拼接格式变成 `${prefix}`.`${file-extension}`**

@ -114,11 +114,11 @@ Nacos Client gets data from Nacos Server through this method. `ConfigService.get
In Nacos Config Starter, the splicing format of dataId is as follows
${prefix} - ${spring.active.profile} . ${file-extension}
${prefix} - ${spring.profiles.active} . ${file-extension}
* `prefix` default value is `spring.application.name` value, which can also be configured via the configuration item `spring.cloud.nacos.config.prefix`.
* `spring.active.profile` is the profile corresponding to the current environment. For details, please refer to [Spring Boot Doc](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)
* `spring.profiles.active` is the profile corresponding to the current environment. For details, please refer to [Spring Boot Doc](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)
**Note: when the activeprofile is empty, the corresponding connector `-` will also not exist, and the splicing format of the dataId becomes `${prefix}`.`${file-extension}`**

@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.RestController;
* @author xiaojing
*/
@SpringBootApplication
@EnableDiscoveryClient
public class ProviderApplication {
public static void main(String[] args) {

@ -29,6 +29,10 @@
<module>ans-example/ans-consumer-feign-example</module>
<module>ans-example/ans-consumer-ribbon-example</module>
<module>ans-example/ans-provider-example</module>
<module>fescar-example/business-service</module>
<module>fescar-example/order-service</module>
<module>fescar-example/storage-service</module>
<module>fescar-example/account-service</module>
<module>acm-example/acm-local-example</module>
<module>rocketmq-example</module>
<module>spring-cloud-bus-rocketmq-example</module>

@ -30,7 +30,11 @@
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
</dependency>
</dependencies>

@ -29,8 +29,13 @@
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
</dependency>
</dependencies>
<build>

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>0.2.2.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-fescar</artifactId>
<name>Spring Cloud Alibaba Fescar</name>
<dependencies>
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-core</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-common</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-spring</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fescar</groupId>
<artifactId>fescar-rm-datasource</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!--spring boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,42 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author xiaojing
*/
@ConfigurationProperties("spring.cloud.alibaba.fescar")
public class FescarProperties {
// todo support config Fescar server information
/**
* Fescar tx service group.default is ${spring.application.name}-fescar-service-group.
*/
private String txServiceGroup;
public String getTxServiceGroup() {
return txServiceGroup;
}
public void setTxServiceGroup(String txServiceGroup) {
this.txServiceGroup = txServiceGroup;
}
}

@ -0,0 +1,60 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar;
import com.alibaba.fescar.spring.annotation.GlobalTransactionScanner;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
/**
* @author xiaojing
*/
@Configuration
@EnableConfigurationProperties(FescarProperties.class)
public class GlobalTransactionAutoConfiguration {
private final ApplicationContext applicationContext;
private final FescarProperties fescarProperties;
public GlobalTransactionAutoConfiguration(ApplicationContext applicationContext,
FescarProperties fescarProperties) {
this.applicationContext = applicationContext;
this.fescarProperties = fescarProperties;
}
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
String applicationName = applicationContext.getEnvironment()
.getProperty("spring.application.name");
String txServiceGroup = fescarProperties.getTxServiceGroup();
if (StringUtils.isEmpty(txServiceGroup)) {
txServiceGroup = applicationName + "-fescar-service-group";
fescarProperties.setTxServiceGroup(txServiceGroup);
}
return new GlobalTransactionScanner(applicationName, txServiceGroup);
}
}

@ -0,0 +1,44 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* @author xiaojing
*/
final class FescarBeanPostProcessor implements BeanPostProcessor {
private final FescarFeignObjectWrapper fescarFeignObjectWrapper;
FescarBeanPostProcessor(FescarFeignObjectWrapper fescarFeignObjectWrapper) {
this.fescarFeignObjectWrapper = fescarFeignObjectWrapper;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return this.fescarFeignObjectWrapper.wrap(bean);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
}

@ -0,0 +1,59 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.openfeign.FeignContext;
/**
* @author xiaojing
*/
public class FescarContextBeanPostProcessor implements BeanPostProcessor {
private final BeanFactory beanFactory;
private FescarFeignObjectWrapper fescarFeignObjectWrapper;
FescarContextBeanPostProcessor(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof FeignContext && !(bean instanceof FescarFeignContext)) {
return new FescarFeignContext(getFescarFeignObjectWrapper(),
(FeignContext) bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
private FescarFeignObjectWrapper getFescarFeignObjectWrapper() {
if (this.fescarFeignObjectWrapper == null) {
this.fescarFeignObjectWrapper = this.beanFactory
.getBean(FescarFeignObjectWrapper.class);
}
return this.fescarFeignObjectWrapper;
}
}

@ -0,0 +1,34 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign;
import org.springframework.beans.factory.BeanFactory;
import feign.Feign;
/**
* @author xiaojing
*/
final class FescarFeignBuilder {
private FescarFeignBuilder() {
}
static Feign.Builder builder(BeanFactory beanFactory) {
return Feign.builder().client(new FescarFeignClient(beanFactory));
}
}

@ -0,0 +1,85 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.fescar.core.context.RootContext;
import org.springframework.beans.factory.BeanFactory;
import feign.Client;
import feign.Request;
import feign.Response;
import org.springframework.util.StringUtils;
/**
* @author xiaojing
*/
public class FescarFeignClient implements Client {
private final Client delegate;
private final BeanFactory beanFactory;
FescarFeignClient(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.delegate = new Client.Default(null, null);
}
FescarFeignClient(BeanFactory beanFactory, Client delegate) {
this.delegate = delegate;
this.beanFactory = beanFactory;
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
Request modifiedRequest = getModifyRequest(request);
try {
return this.delegate.execute(modifiedRequest, options);
}
finally {
}
}
private Request getModifyRequest(Request request) {
String xid = RootContext.getXID();
if (StringUtils.isEmpty(xid)) {
return request;
}
Map<String, Collection<String>> headers = new HashMap<>();
headers.putAll(request.headers());
List<String> fescarXid = new ArrayList<>();
fescarXid.add(xid);
headers.put(RootContext.KEY_XID, fescarXid);
return Request.create(request.method(), request.url(), headers, request.body(),
request.charset());
}
}

@ -0,0 +1,85 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import feign.Client;
import feign.Feign;
/**
* @author xiaojing
*/
@Configuration
@ConditionalOnClass(Client.class)
@AutoConfigureBefore(FeignAutoConfiguration.class)
public class FescarFeignClientAutoConfiguration {
@Bean
@Scope("prototype")
@ConditionalOnClass(name = "com.netflix.hystrix.HystrixCommand")
@ConditionalOnProperty(name = "feign.hystrix.enabled", havingValue = "true")
Feign.Builder feignHystrixBuilder(BeanFactory beanFactory) {
return FescarHystrixFeignBuilder.builder(beanFactory);
}
@Bean
@Scope("prototype")
@ConditionalOnClass(name = "com.alibaba.csp.sentinel.SphU")
@ConditionalOnProperty(name = "feign.sentinel.enabled", havingValue = "true")
Feign.Builder feignSentinelBuilder(BeanFactory beanFactory) {
return FescarSentinelFeignBuilder.builder(beanFactory);
}
@Bean
@ConditionalOnMissingBean
@Scope("prototype")
Feign.Builder feignBuilder(BeanFactory beanFactory) {
return FescarFeignBuilder.builder(beanFactory);
}
@Configuration
protected static class FeignBeanPostProcessorConfiguration {
@Bean
FescarBeanPostProcessor fescarBeanPostProcessor(
FescarFeignObjectWrapper fescarFeignObjectWrapper) {
return new FescarBeanPostProcessor(fescarFeignObjectWrapper);
}
@Bean
FescarContextBeanPostProcessor fescarContextBeanPostProcessor(
BeanFactory beanFactory) {
return new FescarContextBeanPostProcessor(beanFactory);
}
@Bean
FescarFeignObjectWrapper fescarFeignObjectWrapper(BeanFactory beanFactory) {
return new FescarFeignObjectWrapper(beanFactory);
}
}
}

@ -0,0 +1,67 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign;
import java.util.HashMap;
import java.util.Map;
import feign.Client;
import org.springframework.cloud.openfeign.FeignContext;
/**
*
* @author xiaojing
*/
public class FescarFeignContext extends FeignContext {
private final FescarFeignObjectWrapper fescarFeignObjectWrapper;
private final FeignContext delegate;
FescarFeignContext(FescarFeignObjectWrapper fescarFeignObjectWrapper,
FeignContext delegate) {
this.fescarFeignObjectWrapper = fescarFeignObjectWrapper;
this.delegate = delegate;
}
@Override
public <T> T getInstance(String name, Class<T> type) {
T object = this.delegate.getInstance(name, type);
if (object instanceof Client) {
return object;
}
return (T) this.fescarFeignObjectWrapper.wrap(object);
}
@Override
public <T> Map<String, T> getInstances(String name, Class<T> type) {
Map<String, T> instances = this.delegate.getInstances(name, type);
if (instances == null) {
return null;
}
Map<String, T> convertedInstances = new HashMap<>();
for (Map.Entry<String, T> entry : instances.entrySet()) {
if (entry.getValue() instanceof Client) {
convertedInstances.put(entry.getKey(), entry.getValue());
}
else {
convertedInstances.put(entry.getKey(),
(T) this.fescarFeignObjectWrapper.wrap(entry.getValue()));
}
}
return convertedInstances;
}
}

@ -0,0 +1,67 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import feign.Client;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
/**
* @author xiaojing
*/
public class FescarFeignObjectWrapper {
private final BeanFactory beanFactory;
private CachingSpringLoadBalancerFactory cachingSpringLoadBalancerFactory;
private SpringClientFactory springClientFactory;
FescarFeignObjectWrapper(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
Object wrap(Object bean) {
if (bean instanceof Client && !(bean instanceof FescarFeignClient)) {
if (bean instanceof LoadBalancerFeignClient) {
LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean);
return new FescarLoadBalancerFeignClient(client.getDelegate(), factory(),
clientFactory(), this.beanFactory);
}
return new FescarFeignClient(this.beanFactory, (Client) bean);
}
return bean;
}
CachingSpringLoadBalancerFactory factory() {
if (this.cachingSpringLoadBalancerFactory == null) {
this.cachingSpringLoadBalancerFactory = this.beanFactory
.getBean(CachingSpringLoadBalancerFactory.class);
}
return this.cachingSpringLoadBalancerFactory;
}
SpringClientFactory clientFactory() {
if (this.springClientFactory == null) {
this.springClientFactory = this.beanFactory
.getBean(SpringClientFactory.class);
}
return this.springClientFactory;
}
}

@ -0,0 +1,37 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign;
import feign.Retryer;
import feign.hystrix.HystrixFeign;
import org.springframework.beans.factory.BeanFactory;
import feign.Feign;
/**
* @author xiaojing
*/
final class FescarHystrixFeignBuilder {
private FescarHystrixFeignBuilder() {
}
static Feign.Builder builder(BeanFactory beanFactory) {
return HystrixFeign.builder().retryer(Retryer.NEVER_RETRY)
.client(new FescarFeignClient(beanFactory));
}
}

@ -0,0 +1,53 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign;
import java.io.IOException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import feign.Client;
import feign.Request;
import feign.Response;
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
/**
* @author xiaojing
*/
public class FescarLoadBalancerFeignClient extends LoadBalancerFeignClient {
private final BeanFactory beanFactory;
FescarLoadBalancerFeignClient(Client delegate,
CachingSpringLoadBalancerFactory lbClientFactory,
SpringClientFactory clientFactory, BeanFactory beanFactory) {
super(wrap(delegate, beanFactory), lbClientFactory, clientFactory);
this.beanFactory = beanFactory;
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
return super.execute(request, options);
}
private static Client wrap(Client delegate, BeanFactory beanFactory) {
return (Client) new FescarFeignObjectWrapper(beanFactory).wrap(delegate);
}
}

@ -0,0 +1,39 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign;
import org.springframework.beans.factory.BeanFactory;
import feign.Feign;
import feign.Retryer;
import feign.hystrix.HystrixFeign;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel;
import org.springframework.cloud.alibaba.sentinel.feign.SentinelFeign;
/**
* @author xiaojing
*/
final class FescarSentinelFeignBuilder {
private FescarSentinelFeignBuilder() {
}
static Feign.Builder builder(BeanFactory beanFactory) {
return SentinelFeign.builder().retryer(Retryer.NEVER_RETRY)
.client(new FescarFeignClient(beanFactory));
}
}

@ -0,0 +1,37 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign.hystrix;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.hystrix.HystrixCommand;
/**
* @author xiaojing
*/
@Configuration
@ConditionalOnClass(HystrixCommand.class)
public class FescarHystrixAutoConfiguration {
@Bean
FescarHystrixConcurrencyStrategy fescarHystrixConcurrencyStrategy() {
return new FescarHystrixConcurrencyStrategy();
}
}

@ -0,0 +1,80 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.feign.hystrix;
import java.util.concurrent.Callable;
import com.alibaba.fescar.core.context.RootContext;
import com.netflix.hystrix.strategy.HystrixPlugins;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
/**
* @author xiaojing
*/
public class FescarHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private HystrixConcurrencyStrategy delegate;
public FescarHystrixConcurrencyStrategy() {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
HystrixPlugins.reset();
HystrixPlugins.getInstance().registerConcurrencyStrategy(this);
}
@Override
public <K> Callable<K> wrapCallable(Callable<K> c) {
if (c instanceof FescarContextCallable) {
return c;
}
Callable<K> wrappedCallable;
if (this.delegate != null) {
wrappedCallable = this.delegate.wrapCallable(c);
}
else {
wrappedCallable = c;
}
if (wrappedCallable instanceof FescarContextCallable) {
return wrappedCallable;
}
return new FescarContextCallable<>(wrappedCallable);
}
private static class FescarContextCallable<K> implements Callable<K> {
private final Callable<K> actual;
private final String xid;
FescarContextCallable(Callable<K> actual) {
this.actual = actual;
this.xid = RootContext.getXID();
}
@Override
public K call() throws Exception {
try {
RootContext.bind(xid);
return actual.call();
}
finally {
RootContext.unbind();
}
}
}
}

@ -0,0 +1,76 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.rest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
/**
* @author xiaojing
*/
@Configuration
public class FescarRestTemplateAutoConfiguration {
@Bean
public FescarRestTemplateInterceptor fescarRestTemplateInterceptor() {
return new FescarRestTemplateInterceptor();
}
@Autowired(required = false)
private Collection<RestTemplate> restTemplates;
@Autowired
private FescarRestTemplateInterceptor fescarRestTemplateInterceptor;
@PostConstruct
public void init() {
if (this.restTemplates != null) {
for (RestTemplate restTemplate : restTemplates) {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>(
restTemplate.getInterceptors());
interceptors.add(this.fescarRestTemplateInterceptor);
restTemplate.setInterceptors(interceptors);
}
}
}
}

@ -0,0 +1,46 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.rest;
import java.io.IOException;
import com.alibaba.fescar.core.context.RootContext;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.support.HttpRequestWrapper;
import org.springframework.util.StringUtils;
/**
* @author xiaojing
*/
public class FescarRestTemplateInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes,
ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
String xid = RootContext.getXID();
if (!StringUtils.isEmpty(xid)) {
requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);
}
return clientHttpRequestExecution.execute(requestWrapper, bytes);
}
}

@ -0,0 +1,85 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.alibaba.fescar.core.context.RootContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* @author xiaojing
*
* Fescar HandlerInterceptor, Convert Fescar information into
* @see com.alibaba.fescar.core.context.RootContext from http request's header in
* {@link org.springframework.web.servlet.HandlerInterceptor#preHandle(HttpServletRequest , HttpServletResponse , Object )},
* And clean up Fescar information after servlet method invocation in
* {@link org.springframework.web.servlet.HandlerInterceptor#afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)}
*/
public class FescarHandlerInterceptor implements HandlerInterceptor {
private static final Logger log = LoggerFactory
.getLogger(FescarHandlerInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String xid = RootContext.getXID();
String rpcXid = request.getHeader(RootContext.KEY_XID);
if (log.isDebugEnabled()) {
log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid);
}
if (xid == null && rpcXid != null) {
RootContext.bind(rpcXid);
if (log.isDebugEnabled()) {
log.debug("bind {} to RootContext", rpcXid);
}
}
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception e) throws Exception {
String rpcXid = request.getHeader(RootContext.KEY_XID);
if (StringUtils.isEmpty(rpcXid)) {
return;
}
String unbindXid = RootContext.unbind();
if (log.isDebugEnabled()) {
log.debug("unbind {} from RootContext", unbindXid);
}
if (!rpcXid.equalsIgnoreCase(unbindXid)) {
log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid);
if (unbindXid != null) {
RootContext.bind(unbindXid);
log.warn("bind {} back to RootContext", unbindXid);
}
}
}
}

@ -0,0 +1,31 @@
/*
* Copyright (C) 2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.fescar.web;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author xiaojing
*/
public class FescarHandlerInterceptorConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new FescarHandlerInterceptor()).addPathPatterns("/**");
}
}

@ -0,0 +1,7 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.fescar.rest.FescarRestTemplateAutoConfiguration,\
org.springframework.cloud.alibaba.fescar.web.FescarHandlerInterceptorConfiguration,\
org.springframework.cloud.alibaba.fescar.GlobalTransactionAutoConfiguration,\
org.springframework.cloud.alibaba.fescar.feign.FescarFeignClientAutoConfiguration,\
org.springframework.cloud.alibaba.fescar.feign.hystrix.FescarHystrixAutoConfiguration

@ -30,7 +30,14 @@ import java.util.List;
import java.util.Objects;
import java.util.Properties;
import static com.alibaba.nacos.api.PropertyKeyConst.*;
import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY;
import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME;
import static com.alibaba.nacos.api.PropertyKeyConst.CONTEXT_PATH;
import static com.alibaba.nacos.api.PropertyKeyConst.ENCODE;
import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT;
import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE;
import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY;
import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR;
/**
* nacos properties

@ -26,7 +26,11 @@ import org.springframework.core.io.ByteArrayResource;
import org.springframework.util.StringUtils;
import java.io.StringReader;
import java.util.*;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* @author xiaojing

@ -16,17 +16,18 @@
package org.springframework.cloud.alibaba.nacos;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alibaba.nacos.registry.NacosAutoServiceRegistration;
import org.springframework.cloud.alibaba.nacos.registry.NacosRegistration;
import org.springframework.cloud.alibaba.nacos.registry.NacosServiceRegistry;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@ -34,21 +35,26 @@ import org.springframework.context.annotation.Configuration;
/**
* @author xiaojing
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Configuration
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnClass(name = "org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent")
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureBefore({ AutoServiceRegistrationAutoConfiguration.class,
NacosDiscoveryClientAutoConfiguration.class })
@AutoConfigureAfter(AutoServiceRegistrationConfiguration.class)
@AutoConfigureBefore(NacosDiscoveryClientAutoConfiguration.class)
@AutoConfigureAfter(AutoServiceRegistrationAutoConfiguration.class)
public class NacosDiscoveryAutoConfiguration {
@Bean
public NacosServiceRegistry nacosServiceRegistry() {
return new NacosServiceRegistry();
@ConditionalOnMissingBean
public NacosDiscoveryProperties nacosProperties() {
return new NacosDiscoveryProperties();
}
@Bean
public NacosServiceRegistry nacosServiceRegistry(
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosServiceRegistry(nacosDiscoveryProperties);
}
@Bean
@ -68,4 +74,26 @@ public class NacosDiscoveryAutoConfiguration {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
@Bean
@ConditionalOnBean(NacosAutoServiceRegistration.class) // NacosAutoServiceRegistration
// should be present
@ConditionalOnNotWebApplication // Not Web Application
public ApplicationRunner applicationRunner(
NacosAutoServiceRegistration nacosAutoServiceRegistration) {
return args -> {
// WebServerInitializedEvent should not be multicast in Non-Web environment.
// Whatever, NacosAutoServiceRegistration must be checked it's running or not.
if (!nacosAutoServiceRegistration.isRunning()) { // If it's not running, let
// it start.
// FIXME: Please make sure "spring.cloud.nacos.discovery.port" must be
// configured on an available port,
// or the startup or Nacos health check will be failed.
nacosAutoServiceRegistration.start();
// NacosAutoServiceRegistration will be stopped after its destroy() method
// is invoked.
// @PreDestroy destroy() -> stop()
}
};
}
}

@ -23,7 +23,11 @@ import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author xiaojing

@ -37,10 +37,4 @@ public class NacosDiscoveryClientAutoConfiguration {
return new NacosDiscoveryClient(discoveryProperties);
}
@Bean
@ConditionalOnMissingBean
public NacosDiscoveryProperties nacosProperties() {
return new NacosDiscoveryProperties();
}
}

@ -39,6 +39,7 @@ import static com.alibaba.nacos.api.PropertyKeyConst.*;
/**
* @author dungu.zpf
* @author xiaojing
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@ConfigurationProperties("spring.cloud.nacos.discovery")
@ -148,6 +149,9 @@ public class NacosDiscoveryProperties {
}
serverAddr = Objects.toString(serverAddr, "");
if(serverAddr.lastIndexOf("/") != -1) {
serverAddr.substring(0,serverAddr.length()-1);
}
endpoint = Objects.toString(endpoint, "");
namespace = Objects.toString(namespace, "");
logName = Objects.toString(logName, "");
@ -160,7 +164,7 @@ public class NacosDiscoveryProperties {
else {
NetworkInterface netInterface = NetworkInterface
.getByName(networkInterface);
if (null == networkInterface) {
if (null == netInterface) {
throw new IllegalArgumentException(
"no such interface " + networkInterface);
}
@ -379,14 +383,13 @@ public class NacosDiscoveryProperties {
properties.put(CLUSTER_NAME, clusterName);
properties.put(NAMING_LOAD_CACHE_AT_START, namingLoadCacheAtStart);
try {
namingService = NacosFactory.createNamingService(properties);
return namingService;
}
catch (Exception e) {
try {
namingService = NacosFactory.createNamingService(properties);
} catch (Exception e) {
LOGGER.error("create naming service error!properties={},e=,", this, e);
return null;
}
return namingService;
}
}

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

Loading…
Cancel
Save