Merge branch 'master' into master

pull/898/head
小马哥 6 years ago committed by GitHub
commit 5b9758ddd8

@ -0,0 +1,40 @@
version: 2
jobs:
build:
docker:
- image: springcloud/pipeline-base
user: appuser
environment:
_JAVA_OPTIONS: "-Xms1024m -Xmx2048m"
TERM: dumb
branches:
ignore:
- gh-pages # list of branches to ignore
resource_class: large
steps:
- checkout
- restore_cache:
key: sc-alibaba-{{ .Branch }}
- run:
name: "Download dependencies"
command: ./mvnw -Pspring -U --fail-never dependency:go-offline || true
- save_cache:
key: sc-alibaba-{{ .Branch }}
paths:
- ~/.m2
- run:
name: "Running build"
command: ./mvnw -Pspring clean install -U -nsu --batch-mode -Dmaven.test.redirectTestOutputToFile=true -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
- run:
name: "Aggregate test results"
when: always
command: |
mkdir -p ~/junit/
find . -type f -regex ".*/target/.*-reports/.*" -exec cp {} ~/junit/ \;
bash <(curl -s https://codecov.io/bash)
- store_artifacts:
path: ~/junit/
destination: artifacts
- store_test_results:
path: ~/junit/
destination: testartifacts

3
.gitignore vendored

@ -1,5 +1,7 @@
# Compiled class file
*.class
*.classpath
*.factorypath
# Log file
*.log
@ -28,4 +30,5 @@ hs_err_pid*
.project
.settings
target
.DS_Store

@ -1,14 +1,14 @@
# Spring Cloud Alibaba
Spring Cloud Alibaba 致力于提供分布式应用服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
Spring Cloud Alibaba 致力于提供服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba您只需要添加一些注解和少量配置就可以将 Spring Cloud 应用接入阿里分布式应用解决方案,通过阿里中间件来迅速搭建分布式应用系统。
依托 Spring Cloud Alibaba您只需要添加一些注解和少量配置就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
## 主要功能
* **服务限流降级**:默认支持为 HTTP 服务的提供限流保护,也支持添加注解实现方法的自定义限流降级,且支持动态修改限流降级规则
* **服务注册与发现**:适配 sprig cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
* **服务限流降级**:默认支持 Servlet、RestTemplate、Dubbo 和 RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控
* **服务注册与发现**:适配 Spring Cloud 服务注册与发现标准,默认集成了 Ribbon 的支持。
* **分布式配置管理**:支持分布式系统中的外部化配置,配置更改时自动刷新。
* **阿里云对象存储**:阿里云提供的海量、安全、低成本、高可靠的云存储服务。支持在任何应用、任何时间、任何地点存储和访问任意类型的数据。
@ -38,6 +38,50 @@ Spring Cloud 使用 Maven 来构建最快的使用方式是将本项目clone
## 如何使用
### 如何引入依赖
项目已经发布了第一个版本,版本 0.2.0.RELEASE 对应的是 Spring Boot 2.x 版本,版本 0.1.0.RELEASE 对应的是 Spring Boot 1.x 版本。
如果需要使用已发布的版本,在 `dependencyManagement` 中添加如下配置。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
然后再 `dependencies` 中添加自己所需使用的依赖即可使用。
如果您想体验最新的 BUILD-SNAPSHOT 的新功能,则可以将版本换成最新的版本,但是需要在 pom.xml 中配置 Spring BUILDSNAPSHOT 仓库,**注意: SNAPSHOT 版本随时可能更新**
<repositories>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
### Reference Doc
[目录](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc)
[Nacos Config](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc)
[ACM](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc)
## 演示 Demo
为了演示如何使用Spring Cloud Alibaba 项目包含了一个子模块`spring-cloud-alibaba-examples`。此模块中提供了演示用的 example ,您可以阅读对应的 example 工程下的 readme 文档,根据里面的步骤来体验。
Example 列表:
@ -50,6 +94,10 @@ Example 列表:
[AliCloud OSS Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/oss-example/readme-zh.md)
[AliCloud 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)
[AliCloud ACM Example](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/acm-example/acm-local-example/readme-zh.md)
## 版本管理规范
项目的版本号格式为 x.x.x 的形式,其中 x 的数值类型为数字从0开始取值且不限于 0~9 这个范围。项目处于孵化器阶段时第一位版本号固定使用0即版本号为 0.x.x 的格式。

@ -1,5 +1,7 @@
# Spring Cloud Alibaba
A project maintained by Alibaba.
See the [中文文档](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/README-zh.md) for Chinese readme.
Spring Cloud Alibaba provides a one-stop solution for distributed application development. It contains all the components required to develop distributed applications, making it easy for you to develop your applications using Spring Cloud.
@ -35,7 +37,51 @@ Spring Cloud uses Maven for most build-related activities, and you should be abl
./mvnw install
## How to use
## How to Use
### Add maven dependency
Version 0.2.0.RELEASE is compatible with the Spring Boot 2.0.x line. Version 0.1.0.RELEASE is compatible with the Spring Boot 1.x line.
These artifacts are available from Maven Central and Spring Release repository via BOM:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
add the module in `dependencies`.
If you want to use the latest BUILD-SNAPSHOT version, add `Spring Snapshot Repository` in pom.xml , **Attention: BUILD-SNAPSHOT may be updated in any time**
<repositories>
<repository>
<id>spring-snapshot</id>
<name>Spring Snapshot Repository</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
### Reference Doc
[Contents](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/spring-cloud-alibaba.adoc)
[Nacos Config](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/nacos-config.adoc)
[ACM](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc-zh/acm.adoc)
## Examples
A `spring-cloud-alibaba-examples` module is included in our project for you to get started with Spring Cloud Alibaba quickly. It contains an example, and you can refer to the readme file in the example project for a quick walkthrough.

@ -8,13 +8,13 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>2.0.0.RELEASE</version>
<version>2.0.4.RELEASE</version>
<relativePath/>
</parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Alibaba</name>
@ -28,12 +28,12 @@
</licenses>
<scm>
<url>https://github.com/spring-cloud-incubator/spring-cloud-alibabacloud</url>
<url>https://github.com/spring-cloud-incubator/spring-cloud-alibaba</url>
<connection>
scm:git:git://github.com/spring-cloud-incubator/spring-cloud-alibabacloud.git
scm:git:git://github.com/spring-cloud-incubator/spring-cloud-alibaba.git
</connection>
<developerConnection>
scm:git:ssh://git@github.com/spring-cloud-incubator/spring-cloud-alibabacloud.git
scm:git:ssh://git@github.com/spring-cloud-incubator/spring-cloud-alibaba.git
</developerConnection>
<tag>HEAD</tag>
</scm>
@ -47,6 +47,10 @@
<name>fangjian</name>
<email>fangjian0423@gmail.com</email>
</developer>
<developer>
<name>xiaolongzuo</name>
<email>150349407@qq.com</email>
</developer>
<developer>
<name>hengyunabc</name>
<email>hengyunabc@gmail.com</email>
@ -55,9 +59,9 @@
<properties>
<!-- Dependency Versions -->
<spring-cloud-commons.version>2.0.0.RELEASE</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.0.0.RELEASE</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>2.0.0.RELEASE</spring-cloud-openfeign.version>
<spring-cloud-commons.version>2.0.2.RELEASE</spring-cloud-commons.version>
<spring-cloud-netflix.version>2.0.2.RELEASE</spring-cloud-netflix.version>
<spring-cloud-openfeign.version>2.0.2.RELEASE</spring-cloud-openfeign.version>
<junit.version>4.12</junit.version>
<javax-servlet-api>3.0</javax-servlet-api>
@ -74,15 +78,19 @@
<modules>
<module>spring-cloud-alibaba-dependencies</module>
<module>spring-cloud-alibaba-sentinel</module>
<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-examples</module>
<module>spring-cloud-alibaba-test</module>
<module>spring-cloud-alibaba-docs</module>
<module>spring-cloud-starter-alibaba</module>
<module>spring-cloud-starter-alicloud</module>
<module>spring-cloud-alicloud-oss</module>
<module>spring-cloud-alibaba-nacos-config-server</module>
<module>spring-cloud-alicloud-context</module>
<module>spring-cloud-alicloud-acm</module>
<module>spring-cloud-alicloud-ans</module>
</modules>
<dependencyManagement>
@ -155,4 +163,69 @@
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>spring</id>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/libs-snapshot-local</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/libs-release-local</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</profile>
</profiles>
</project>

@ -6,25 +6,62 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>2.0.0.RELEASE</version>
<version>2.0.4.RELEASE</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud Alibaba Dependencies</name>
<description>Spring Cloud Alibaba Dependencies</description>
<properties>
<sentinel.version>0.1.1</sentinel.version>
<sentinel.version>1.3.0-GA</sentinel.version>
<oss.version>3.1.0</oss.version>
<nacos.version>0.2.2-SNAPSHOT</nacos.version>
<nacos.version>0.3.0</nacos.version>
<acm.version>1.0.8</acm.version>
<ans.version>0.1.1</ans.version>
<aliyun.sdk.version>4.0.1</aliyun.sdk.version>
<alicloud.context.version>1.0.0</alicloud.context.version>
<aliyun.sdk.edas.version>2.16.0</aliyun.sdk.edas.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- Nacos -->
<!-- Alibaba -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>alicloud-context</artifactId>
<version>${alicloud.context.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-edas</artifactId>
<version>${aliyun.sdk.edas.version}</version>
<exclusions>
<exclusion>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>${aliyun.sdk.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.ans</groupId>
<artifactId>ans-sdk</artifactId>
<version>${ans.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.edas.acm</groupId>
<artifactId>acm-sdk</artifactId>
<version>${acm.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
@ -104,6 +141,11 @@
<artifactId>spring-cloud-alibaba-sentinel</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-oss</artifactId>
@ -119,6 +161,21 @@
<artifactId>spring-cloud-alibaba-nacos-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-acm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-ans</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-context</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Own dependencies - Starters -->
<dependency>
@ -144,7 +201,17 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-ans</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-acm</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Testing Dependencies -->

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>0.2.1.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-docs</artifactId>
<name>Spring Cloud Alibaba Documentation</name>
<packaging>pom</packaging>
<properties>
<docs.main>spring-cloud-alibaba</docs.main>
<main.basedir>${basedir}/..</main.basedir>
</properties>
<profiles>
<profile>
<id>docs</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<inherited>false</inherited>
<configuration>
<sourceDirectory>${basedir}/src/main/asciidoc-zh</sourceDirectory>
</configuration>
</plugin>
<plugin>
<groupId>com.agilejava.docbkx</groupId>
<artifactId>docbkx-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<inherited>false</inherited>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

@ -0,0 +1,281 @@
= Spring Cloud AliCloud ACM
Spring Cloud AliCloud ACM 提供了和阿里云上的ACM的集成。使得在项目中如果需要上云的用户可以非常方便无缝的对接阿里云来享受云端提供的稳定服务。
=== 快速开始
为了能够在本地开发时可以使用和云上具有相同功能的ACM服务我们提供了用于本地开发测试的轻量版配置中心。轻量版配置中心你可以理解为用于方便的本地开发测试的Saas平台。
==== 基于properties文件扩展名的配置
===== 轻量版配置中心服务端初始化
1、轻量版配置中心的下载和启动方式可参考 https://help.aliyun.com/document_detail/44163.html?spm=a2c4g.11186623.6.677.5f206b82Z2mTCF[这里]
2、启动好轻量版配置中心之后在控制台中添加如下的配置。注意data id是以 properties为扩展名(默认的文件扩展名方式)。
[source,subs="normal"]
----
Group: DEFAULT_GROOUP
DataId: acm-config.properties
Content: user.name=alibaba
user.age=56
----
===== 客户端使用方式
为了能够在应用程序中使用ACM作为Sping Cloud Config的后端存储服务在您构建Spring Boot 应用的同时添加一个Spring Boot Starter org.springframework.cloud:spring-cloud-starter-alicloud-acm。以下是一个基础的maven 依赖配置:
[source,xml]
----
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-acm</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
----
现在你就可以创建一个标准的Spring Boot的应用。
[source,java]
----
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args);
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :"+userName+"; age: "+userAge);
}
}
----
spring-cloud-starter-alicloud-acm 对于ACM服务端的基础配置有默认值(默认配置绑定到的ip和端口为127.0.0.1和8080)因此在运行此Example 之前, 你只需在 bootstrap.properties 配置文件配置spring.application.name即可例如
[source,properties]
----
spring.application.name=acm-config #注意spring.application.name 必须要放在bootstrap.properties中
----
启动这个Example可以在控制台看到打印出的值正是我们在轻量版配置中心上预先配置的值。
[source,subs="normal"]
----
2018-11-05 11:41:50.661 INFO 1541 --- [main] com.alibaba.demo.normal.ProviderApp : Started ProviderApp in 15.809 seconds (JVM running for 16.513)
user name :alibaba; age: 56
2018-11-05 11:41:50.710 INFO 1541 --- [gPullingdefault] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1f854e9e: startup date [Mon Nov 05 11:41:50 CST 2018]; root of context hierarchy
----
==== 基于yaml文件扩展名的配置
spring-cloud-starter-alicloud-acm 默认对文件扩展名为properties的支持如果你习惯使用yaml格式来作为您的基础配置也是可以支持的。这个时候你只需要在bootstrap.properties配置文件中显示的来声明使用的文件扩展名如下所示
[source,properties]
----
spring.application.name=acm-config
spring.cloud.nacos.config.file-extension=yaml #显示的声明使用的文件扩展名
----
同时还需在轻量版配置中心的控制台新增一个dataid为yaml为扩展名的配置如下所示
[source,subs="normal"]
----
Group: DEFAULT_GROUP
DataId: acm-config.yaml
Content: user.name: yaml-alibaba
user.age: 102
----
完成之后,重启测试程序,可以在控制台看到输出的结果的值。
[source,subs="normal"]
----
2018-11-05 11:50:13.829 INFO 1674 --- [main] com.alibaba.demo.normal.ProviderApp : Started ProviderApp in 15.091 seconds (JVM running for 15.626)
user name :yaml-alibaba; age: 102
2018-11-05 11:50:13.868 INFO 1674 --- [gPullingdefault] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@7f64acf5: startup date [Mon Nov 05 11:50:13 CST 2018]; root of context hierarchy
----
==== 支持配置的动态更新
spring-cloud-starter-alicloud-acm 也支持配置的动态更新如下所示当变更user.name时应用程序中能够获取到最新的值
[source,subs="normal"]
----
user name :yaml-alibaba; age: 102
user name :yaml-alibaba; age: 102
2018-11-05 11:52:15.192 INFO 1707 --- [gPullingdefault] o.s.boot.SpringApplication : Started application in 0.162 seconds (JVM running for 26.869)
2018-11-05 11:52:15.193 INFO 1707 --- [gPullingdefault] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@36b222ff: startup date [Mon Nov 05 11:52:15 CST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@5cfd2308
2018-11-05 11:52:15.193 INFO 1707 --- [gPullingdefault] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@5cfd2308: startup date [Mon Nov 05 11:52:15 CST 2018]; root of context hierarchy
//感知到配置发生变更
user name :yaml-alibaba-update; age: 1020
----
==== 可支持profile粒度的配置
spring-cloud-starter-alicloud 在加载配置的时候不仅仅加载了以dataid为${spring.application.name}.${file-extension:properties}为前缀的基础配置还加载了dataid为${spring.application.name}-${profile}.${file-extension:properties}的基础配置。在我们日常开发中如果遇到多套环境下的不同配置我们可以打开Spring自带的配置功能放置在application.properties 配置文件中。如下所示:
[sources,properties]
----
spring.profiles.active=${deploy.env}
----
其中 ${deploy.env}变量的值可以在启动应用时通过-Ddeploy.env=*****来动态指定。比如现在在轻量版配置中心上新增了一个dataid为acm-config-develop.yaml的基础配置如下所示
[source,subs="normal"]
----
Group: DEFAULT_GROUP
DataId: acm-config-develop.yaml
Content: deploy.env: develop
----
同时启动应用的时候通过-Ddeploy.env=develop 来指定当前spring.profiles.active的值。
启动 Spring Boot 应用测试的代码如下:
[source,java]
----
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApp.class, args);
while (true) {
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
//获取当前的部署环境
String deployEnv = applicationContext.getEnvironment().getProperty("deploy.env");
System.err.println("Deploy Env:"+deployEnv+"\n \t user name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}
----
启动后,可见控制台的输出结果:
[source,subs="normal"]
----
2018-11-05 14:06:25.898 INFO 1852 --- [main] com.alibaba.demo.normal.ProviderApp : Started ProviderApp in 14.947 seconds (JVM running for 15.471)
Deploy Env:develop
user name :yaml-alibaba-update; age: 1020
2018-11-05 14:06:25.939 INFO 1852 --- [gPullingdefault] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@75d62d18: startup date [Mon Nov 05 14:06:25 CST 2018]; root of context hierarchy
2018-11-05 14:06:25.967 INFO 1852 --- [gPullingdefault] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
----
如果我们要切换到生产环境,那么只需要更改启动的-Ddeploy.env=product 参数即可。前提是生产环境 模式下轻量版配置中心已经添加了该环境的基础配置。例如dataid为acm-config-product.yaml的配置
[source,subs="normal"]
----
Group: DEFAULT_GROUP
DataId: acm-config-product.yaml
Content: deploy.env: Product
----
以-Ddeploy.env=product 启动测试程序,输出结果如下:
[source,subs="normal"]
----
2018-11-05 14:10:05.481 INFO 1857 --- [gPullingdefault] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@455d34c2: startup date [Mon Nov 05 14:10:05 CST 2018]; root of context hierarchy
Deploy Env:Product
user name :yaml-alibaba-update; age: 1020
2018-11-05 14:10:06.798 INFO 1857 --- [ Thread-20] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3899782c: startup date [Mon Nov 05 14:10:01 CST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@245b4bdc
----
==== 基于组级别配置的高级功能
当你习惯使用application.properties 或者 application.yaml 来作为你的配置时,为了能够区分其他应用的配置,这个时候可以开启 spring-cloud-starter-alicloud-acm 提供的基于组级别的配置。即只需在bootstrap.properties配置文件中添加spring.application.group配置来标识应用所属的分组即可。如下所示
[source,properties]
----
spring.application.group=com.infrastructure.alibaba
----
这个时候注意在轻量版配置中心填写的dataid的格式为
${spring.application.group}:application.${file-extension}
如下所示:
[source,subs="normal"]
----
Group: DEFAULT_GROUP
DataId: com.infrastructure.alibaba:application.yaml
Content: owner.group: infrastructure
----
启动 Spring Boot 应用测试的代码如下:
[source,java]
----
@SpringBootApplication
public class ProviderApp {
public static void main(String[] args) throws Exception{
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApp.class, args);
while (true) {
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
String deployEnv = applicationContext.getEnvironment().getProperty("deploy.env");
//获取当前应用所属的组
String ownerGroup = applicationContext.getEnvironment().getProperty("owner.group");
System.err.println("Deploy Env:"+deployEnv+"; in group :"+ownerGroup+"\n \tuser name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}
----
测试输出的结果如下:
[source,subs="normal"]
----
2018-11-05 14:34:28.059 INFO 1902 --- [main] com.alibaba.demo.normal.ProviderApp : Started ProviderApp in 15.431 seconds (JVM running for 16.008)
Deploy Env:product; in group :Infrastructure
user name :yaml-alibaba-update; age: 1020
2018-11-05 14:34:28.109 INFO 1902 --- [gPullingdefault] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4846b0aa: startup date [Mon Nov 05 14:34:28 CST 2018]; root of context hierarchy
----

@ -0,0 +1,111 @@
== Spring Cloud AliCloud ANS
ANSApplication Naming Service 是隶属于阿里云EDAS产品的组件 Spring Cloud AliCloud ANS 提供了Spring Cloud规范下商业版的服务注册与发现可以让用户方便的在本地开发同时也可以运行在云环境里。
=== 如何引入 Spring Cloud AliCloud ANS
Spring Cloud Alibaba 已经发布了0.2.0版本需要首先导入依赖管理POM。
[source,xml]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
接下来引入 Spring Cloud AliCloud ANS Starter 即可。
[source,xml]
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-ans</artifactId>
</dependency>
----
=== 使用ANS进行服务注册
当客户端引入了 Spring Cloud AliCloud ANS Starter 以后服务的元数据会被自动注册到注册中心比如IP、端口、权重等信息。客户端会与服务端保持心跳来证明自己可以正常提供服务。
以下是一个简单的应用示例。
[source,java]
----
@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class ProviderApplication {
@RequestMapping("/")
public String home() {
return "Hello world";
}
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
----
既然服务会被注册到注册中心那么肯定需要配置注册中心的地址在application.properties中还需要配置上以下地址。
[source,properties]
----
# 应用名会被作为服务名称使用,因此会必选
spring.application.name=ans-provider
server.port=18081
# 以下就是注册中心的IP和端口配置因为默认就是127.0.0.1和8080因此以下两行配置也可以省略
spring.cloud.alicloud.ans.server.list=127.0.0.1
spring.cloud.alicloud.ans.server.port=8080
----
NOTE: 此时没有启动注册中心,启动应用会报错,因此在应用启动之前,应当首先启动注册中心。
=== 启动注册中心
ANS使用的注册中心有两种一种是完全免费的轻量版配置中心主要用于开发和本地调试一种是云上注册中心ANS依托于阿里云EDAS产品提供服务注册的功能。通常情况下可以使用轻量版配置中心作为开发和测试环境使用云上的EDAS作为灰度和生产环境。
==== 启动轻量版配置中心
轻量版配置中心的下载和启动方式可参考 https://help.aliyun.com/document_detail/44163.html?spm=a2c4g.11186623.6.677.5f206b82Z2mTCF[这里]
NOTE: 只需要进行第1步下载轻量配置中心和第2步启动轻量配置中心即可第3步配置hosts在与 ANS 结合使用时,不需要操作。
启动完轻量版配置中心以后,直接启动 ProviderApplication 即可将服务注册到轻量版配置中心由于轻量版配置中心的默认端口是8080因此你可以打开 http://127.0.0.1:8080 ,点击左侧"服务列表",查看注册上来的服务。
==== 使用云上注册中心
使用云上注册中心,可以省去服务端的维护工作,同时稳定性也会更有保障。当使用云上注册中心时,代码部分和使用轻量配置中心并没有区别,但是配置上会有一些区别。
以下是一个简单的使用云上配置中心的配置示例。
[source,properties]
----
# 应用名会被作为服务名称使用,因此是必选
spring.application.name=ans-provider
# 端口配置自由配置即可
server.port=18081
# 以下就是注册中心的IP和端口配置因为默认就是127.0.0.1和8080因此以下两行配置也可以省略
spring.cloud.alicloud.ans.server-mode=EDAS
spring.cloud.alicloud.access-key=你的阿里云AK
spring.cloud.alicloud.secret-key=你的阿里云SK
spring.cloud.alicloud.edas.namespace=cn-xxxxx
----
server-mode 的默认值为 LOCAL ,如果要使用云上注册中心,则需要更改为 EDAS 。
access-key 和 secret-key 则是阿里云账号的AK/SK需要首先注册阿里云账号然后登陆 https://usercenter.console.aliyun.com/#/manage/ak[阿里云AK/SK管理页面] ,即可看到 AccessKey ID 和 Access Key Secret ,如果没有的话,需要点击"创建AccessKey"按钮创建。
namespace 是阿里云EDAS产品的概念用于隔离不同的环境比如测试环境和生产环境。要获取 namespace 需要 https://common-buy.aliyun.com/?spm=5176.11451019.0.0.6f5965c0Uq5tue&commodityCode=edaspostpay#/buy[开通EDAS服务],按量计费模式下开通是免费的,开通以后进入 https://edas.console.aliyun.com/#/namespaces?regionNo=cn-hangzhou[EDAS控制台]即可看到对应的namespace比如cn-hangzhou。
NOTE: EDAS提供应用托管服务如果你将应用托管到EDAS那么EDAS将会自动为你填充所有配置。

@ -0,0 +1,4 @@
== 依赖管理
Spring Cloud Alibaba BOM 包含了它所使用的所有依赖的版本。

@ -0,0 +1,235 @@
= Spring Cloud Alibaba Nacos Config
== 快速开始
=== 基于properties的文件扩展名的配置方式
==== Nacos 服务端初始化
1、启动Nacos Server。启动方式可见 https://nacos.io/zh-cn/docs/quick-start.html[Nacos 官网]
2、启动好Nacos之后在Nacos添加如下的配置。注意data id是以 properties为扩展名默认的文件扩展名方式。如下所示
[source,subs="normal"]
----
Data ID: nacos-config.properties
Group : DEFAULT_GROUP
配置格式: TEXT
配置内容: user.name: nacos-config-properties
user.age: 90
----
==== 客户端使用方式
为了能够在应用程序中使用Nacos作为Sping Cloud Config的后端存储服务在您构建Spring Boot 应用的同时添加一个Spring Boot Starter org.springframework.cloud:spring-cloud-starter-alibaba-nacos-config。以下是一个基础的maven 依赖配置:
[source,xml]
----
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
----
现在就可以创建一个标准的Spring Boot的应用。
[source,java]
----
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args);
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
System.err.println("user name :"+userName+"; age: "+userAge);
}
}
----
spring-cloud-starter-alibaba-nacos-config 对于Nacos服务端的基础配置没有默认值因此在运行此Example 之前, 必须使用 bootstrap.properties 配置文件来配置Nacos Server地址例如
[source,properties]
----
spring.application.name=nacos-config #注意spring.application.name 必须要放在bootstrap.properties中
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
----
启动这个Example可以在控制台看到打印出的值正是在Nacos上预先配置好的值。
[source,subs="normal"]
----
2018-11-02 14:24:51.638 INFO 32700 --- [main] c.a.demo.provider.ProviderApplication : Started ProviderApplication in 14.645 seconds (JVM running for 15.139)
user name :nacos-config-properties; age: 90
2018-11-02 14:24:51.688 INFO 32700 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@a8c5e74: startup date [Fri Nov 02 14:24:51 CST 2018]; root of context hierarchy
2018-11
----
=== 基于yaml的文件扩展名的配置方式
spring-cloud-starter-alibaba-nacos-config 默认对文件扩展名为properties的支持如果习惯使用yaml格式来作为应用中的基础配置也是可以支持的。这个时候只需要完成以下两步
1、在bootstrap.properties配置文件中显示的来声明使用的文件扩展名。如下所示
[source,properties]
----
spring.application.name=nacos-config
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.file-extension=yaml #显示的声明使用的文件扩展名
----
2、在Nacos的控制台新增一个dataid为yaml为扩展名的配置如下所示
[source,subs="normal"]
----
Data ID: nacos-config.yaml
Group : DEFAULT_GROUP
配置格式: YAML
配置内容: user.name: nacos-config-yaml
user.age: 68
----
这两步完成后重启测试程序可以在控制台看到输出是以dataid为opensource-service-provider3.yaml配置的值。
[source,subs="normal"]
----
2018-11-02 14:59:00.484 INFO 32928 --- [main] c.a.demo.provider.ProviderApplication:Started ProviderApplication in 14.183 seconds (JVM running for 14.671)
user name :nacos-config-yaml; age: 68
2018-11-02 14:59:00.529 INFO 32928 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@265a478e: startup date [Fri Nov 02 14:59:00 CST 2018]; root of context hierarchy
----
=== 支持配置的动态更新
spring-cloud-starter-alibaba-nacos-config 也支持配置的动态更新如下所示当变更user.name时应用程序中能够获取到最新的值
[source,subs="normal"]
----
user name :nacos-config-yaml; age: 68
user name :nacos-config-yaml; age: 68
user name :nacos-config-yaml; age: 68
2018-11-02 15:04:25.069 INFO 32957 --- [-127.0.0.1:8848] o.s.boot.SpringApplication : Started application in 0.144 seconds (JVM running for 71.752)
2018-11-02 15:04:25.070 INFO 32957 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@10c89124: startup date [Fri Nov 02 15:04:25 CST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@6520af7
2018-11-02 15:04:25.071 INFO 32957 --- [-127.0.0.1:8848] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@6520af7: startup date [Fri Nov 02 15:04:24 CST 2018]; root of context hierarchy
user name :nacos-config-yaml-update; age: 68
user name :nacos-config-yaml-update; age: 68
----
=== 可支持profile粒度的配置
spring-cloud-starter-alibaba-nacos-config 在加载配置的时候不仅仅加载了以dataid为${spring.application.name}.${file-extension:properties}为前缀的基础配置还加载了dataid为${spring.application.name}-${profile}.${file-extension:properties}的基础配置。在日常开发中如果遇到多套环境下的不同配置可以打开Spring自带的配置功能。
[source,properties]
----
spring.profiles.active=${deploy.env}
----
其中 ${deploy.env}变量的值可以在启动应用时通过-Ddeploy.env=*****来动态指定。比如现在在Nacos上新增了一个dataid为nacos-config-develop.yaml的基础配置如下所示
[source,subs="normal"]
----
Data ID: nacos-config-develop.yaml
Group : DEFAULT_GROUP
配置格式: YAML
配置内容: current.env: develop-env
----
同时启动应用的时候通过-Ddeploy.env=develop 来指定当前spring.profiles.active的值。
启动 Spring Boot 应用测试的代码如下:
[source,java]
----
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(ProviderApplication.class, args);
while(true) {
String userName = applicationContext.getEnvironment().getProperty("user.name");
String userAge = applicationContext.getEnvironment().getProperty("user.age");
//获取当前部署的环境
String currentEnv = applicationContext.getEnvironment().getProperty("current.env");
System.err.println("in "+currentEnv+" enviroment; "+"user name :" + userName + "; age: " + userAge);
TimeUnit.SECONDS.sleep(1);
}
}
}
----
启动后,可见控制台的输出结果:
[source,subs="normal"]
----
in develop-evn enviroment; user name :nacos-config-yaml-update; age: 68
2018-11-02 15:34:25.013 INFO 33014 --- [ Thread-11] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6f1c29b7: startup date [Fri Nov 02 15:33:57 CST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@63355449
----
如果需要切换到生产环境,那么只需要更改启动的-Ddeploy.env=product 参数即可。前提是生产环境上Nacos已经添加了该环境的基础配置。例如现在在生成环境下的Naocs添加了dataid为nacos-config-product.yaml的配置
[source,subs="normal"]
----
Data ID: nacos-config-product.yaml
Group : DEFAULT_GROUP
配置格式: YAML
配置内容: current.env: product-env
----
以-Ddeploy.env=product 启动测试程序,输出结果如下:
[source,subs="normal"]
----
in product-env enviroment; user name :nacos-config-yaml-update; age: 68
2018-11-02 15:42:14.628 INFO 33024 --- [Thread-11] ConfigServletWebServerApplicationContext : Closing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6aa8e115: startup date [Fri Nov 02 15:42:03 CST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@19bb07ed
----

@ -0,0 +1,317 @@
= Spring Cloud Alibaba Nacos Discovery
该项目通过自动配置以及其他 Spring 编程模型的习惯用法为 Spring Boot 应用程序在服务注册与发现方面提供和 Nacos 的无缝集成。
通过一些简单的注解,您可以快速来注册一个服务,并使用经过双十一考验的 Nacos 组件来作为大规模分布式系统的服务注册中心。
== 1. 服务注册发现: Nacos Discovery Starter
服务发现是微服务架构体系中最关键的组件之一。如果尝试着用手动的方式来给每一个客户端来配置所有服务提供者的服务列表是一件非常困难的事,而且也不利于
服务的动态扩缩容。Nacos Discovery Starter 可以帮助您将服务自动注册到 Nacos 服务端并且能够动态感知和刷新某个服务实例的服务列表。除此之外Nacos
Discovery Starter 也将服务实例自身的一些元数据信息-例如 hostport,健康检查URL主页等-注册到 Nacos 。Nacos 的获取和启动方式可以参考 https://nacos.io/zh-cn/docs/quick-start.html[Nacos 官网]。
=== 如何引入 Nacos Discovery Starter
为了能够在你的工程下引入 Nacos Discovey Starter使用group ID 为 `org.springframework.cloud` 和 artifact ID 为 `spring-cloud-starter-alibaba-nacos-discovery`。
pom.xml 示例如下所示:
[source,xml,indent=0]
----
<!-- dependency management-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- dependencies -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
----
=== 启动一个 Provider 应用
如果您使用的 Spring Cloud 版本是 Finchley.SR1 版本,那么这个时候您的Spring Boot版本的选择可需要额外的小心了因为版本的不匹对可能会导致许多意外的效果。
Spring Cloud 的 Finchley.SR1 版本最佳实践的 Spring Boot 版本是 2.0.6.RELEASE。在启动您的一个 Provider 应用时请检查依赖的 Spring Boot 版本是否是
1.X.Y.RELEASE 或者 2.1.0.RELEASE 的版本。如果不是,请更正到 2.0.6.RELEASE 版本。
以下步骤向您展示了如何将一个服务注册到 Nacos。
1. pom.xml的配置。一个完整的 pom.xml 配置如下所示:
[source, xml]
----
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>open.source.test</groupId>
<artifactId>nacos-discovery-test</artifactId>
<version>1.0-SNAPSHOT</version>
<name>nacos-discovery-test</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<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-nacos-discovery</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
----
2. application.properties 配置。一些关于 Nacos 基本的配置也必须在 application.properties(也可以是application.yaml)配置,如下所示:
application.properties
[source,properties]
----
server.port=8081
spring.application.name=nacos-producer
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
management.endpoints.web.exposure.include=*
----
NOTE: 如果不想使用 Nacos 作为您的服务注册与发现,可以将 `spring.cloud.nacos.discovery` 设置为 `false`。
3. 启动 Provider 示例。如下所示:
[source,java,indent=0]
----
@SpringBootApplication
@EnableDiscoveryClient
public class NacosProviderDemoApplication {
public static void main(String[] args) {
SpringApplication.run(NacosProducerDemoApplication.class, args);
}
@RestController
public class EchoController {
@GetMapping(value = "/echo/{string}")
public String echo(@PathVariable String string) {
return "Hello Nacos Discovery " + string;
}
}
}
----
这个时候你就可以在 Nacos的控制台上看到注册上来的服务信息了。
NOTE: 再启动 Provider 应用之前 请先将 Nacos 服务启动。具体启动方式可参考 https://nacos.io/zh-cn/docs/quick-start.html[Naco 官网]。
=== 服务的 EndPoint
spring-cloud-starter-alibaba-nacos-discovery 在实现的时候提供了一个EndPoint,EndPoint的访问地址为 `http://ip:port/actuator/nacos-discovery`。
EndPoint 的信息主要提供了两类:
1、subscribe: 显示了当前有哪些服务订阅者
2、NacosDiscoveryProperties: 显示了当前服务实例关于 Nacos 的基础配置
一个服务实例访问 EndPoint 的信息如下所示:
[source, json]
----
{
"subscribe": [
{
"jsonFromServer": "",
"name": "nacos-provider",
"clusters": "",
"cacheMillis": 10000,
"hosts": [
{
"instanceId": "30.5.124.156#8081#DEFAULT#nacos-provider",
"ip": "30.5.124.156",
"port": 8081,
"weight": 1.0,
"healthy": true,
"enabled": true,
"cluster": {
"serviceName": null,
"name": null,
"healthChecker": {
"type": "TCP"
},
"defaultPort": 80,
"defaultCheckPort": 80,
"useIPPort4Check": true,
"metadata": {
}
},
"service": null,
"metadata": {
}
}
],
"lastRefTime": 1541755293119,
"checksum": "e5a699c9201f5328241c178e804657e11541755293119",
"allIPs": false,
"key": "nacos-producer",
"valid": true
}
],
"NacosDiscoveryProperties": {
"serverAddr": "127.0.0.1:8848",
"endpoint": "",
"namespace": "",
"logName": "",
"service": "nacos-provider",
"weight": 1.0,
"clusterName": "DEFAULT",
"metadata": {
},
"registerEnabled": true,
"ip": "30.5.124.201",
"networkInterface": "",
"port": 8082,
"secure": false,
"accessKey": "",
"secretKey": ""
}
}
----
=== 启动一个 Consumer 应用
Consumer 的应用可能还没像启动一个 Provider 应用那么简单。因为在 Consumer 端需要去调用 Provider 端提供的REST 服务。例子中我们使用最原始的一种方式,
即显示的使用 LoadBalanceClient 和 RestTemolate 结合的方式来访问。
pom.xml 和 application.properties 的配置可以参考 1.2 小结。启动一个 Consumer应用的示例代码如下所示
NOTE: 通过带有负载均衡的RestTemplate 和 FeignClient 也是可以访问的。
[source, java]
----
@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApp {
@RestController
public class NacosController{
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
@Value("${spring.application.name}")
private String appName;
@GetMapping("/echo/app-name")
public String echoAppName(){
//使用 LoadBalanceClient 和 RestTemolate 结合的方式来访问
ServiceInstance serviceInstance = loadBalancerClient.choose("nacos-provider");
String url = String.format("http://%s:%s/echo/%s",serviceInstance.getHost(),serviceInstance.getPort(),appName);
System.out.println("request url:"+url);
return restTemplate.getForObject(url,String.class);
}
}
//实例化 RestTemplate 实例
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(NacosConsumerApp.class,args);
}
}
----
这个例子中我们注入了一个 LoadBalancerClient 的实例,并且手动的实例化一个 RestTemplate同时将 `spring.application.name` 的配置值 注入到应用中来,
目的是调用 Provider 提供的服务时,希望将当前配置的应用名给显示出来。
NOTE: 在启动 Consumer 应用之前请先将 Nacos 服务启动好。具体启动方式可参考 https://nacos.io/zh-cn/docs/quick-start.html[Nacos 官网]。
启动后,访问 Consumer 提供出来的 `http://ip:port/echo/app-name` 接口。我这里测试启动的 port是 8082。访问结果如下所示
访问地址http://127.0.0.1:8082/echo/app-name
访问结果Hello Nacos Discovery nacos-consumer
== 2. 关于 Nacos Starter 更多的配置项信息
更多关于 spring-cloud-starter-alibaba-nacos-discovery 的 starter 配置项如下所示:
:frame: topbot
[width="60%",options="header"]
|====
^|配置项 ^|Key ^|默认值 ^|说明
|`服务端地址`|`spring.cloud.nacos.discovery.server-addr`|`无`| `Nacos Server 启动监听的ip地址和端口`
|`服务名`|`spring.cloud.nacos.discovery.service`|`${spring.application.name}`|`给当前的服务命名`
|`权重`|`spring.cloud.nacos.discovery.weight`|`1`|`取值范围 1 到 100数值越大权重越大`
|`网卡名`|`spring.cloud.nacos.discovery.network-interface`|`无`|`当IP未配置时注册的IP为此网卡所对应的IP地址如果此项也未配置则默认取第一块网卡的地址`
|`注册的IP地址`|`spring.cloud.nacos.discovery.ip`|`无`|`优先级最高`
|`注册的端口`|`spring.cloud.nacos.discovery.port`|`-1`|`默认情况下不用配置,会自动探测`
|`命名空间`|`spring.cloud.nacos.discovery.namespace`|`无`|`常用场景之一是不同环境的注册的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。`
|`AccessKey`|`spring.cloud.nacos.discovery.access-key`|`无`|`当要上阿里云时,阿里云上面的一个云账号名`
|`SecretKey`|`spring.cloud.nacos.discovery.secret-key`|`无`|`当要上阿里云时,阿里云上面的一个云账号密码`
|`Metadata`|`spring.cloud.nacos.discovery.metadata`|`无`|`使用Map格式配置用户可以根据自己的需要自定义一些和服务相关的元数据信息`
|`日志文件名`|`spring.cloud.nacos.discovery.log-name`|`无`|
|`接入点`|`spring.cloud.nacos.discovery.enpoint`|`UTF-8`|`地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址`
|`是否集成Ribbon`|`ribbon.nacos.enabled`|`true`|`一般都设置成true即可`
|====

@ -0,0 +1,157 @@
== Spring Cloud AliCloud OSS
OSSObject Storage Service是阿里云的一款对象存储服务产品 Spring Cloud AliCloud OSS 提供了Spring Cloud规范下商业版的对象存储服务提供简单易用的API并且支持与 Spring 框架中 Resource 的整合。
=== 如何引入 Spring Cloud AliCloud OSS
Spring Cloud Alibaba 已经发布了0.2.0版本需要首先导入依赖管理POM。
[source,xml]
----
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>0.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
----
接下来引入 Spring Cloud AliCloud OSS Starter 即可。
[source,xml]
----
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
----
=== 如何使用 OSS API
==== 配置 OSS
使用 Spring Cloud AliCloud OSS 之前,需要在 application.properties 中加入以下配置。
[source,properties]
----
spring.cloud.alicloud.access-key=你的阿里云AK
spring.cloud.alicloud.secret-key=你的阿里云SK
spring.cloud.alicloud.oss.endpoint=***.aliyuncs.com
----
access-key 和 secret-key 是阿里云账号的AK/SK需要首先注册阿里云账号然后登陆 https://usercenter.console.aliyun.com/#/manage/ak[阿里云AK/SK管理页面] ,即可看到 AccessKey ID 和 Access Key Secret ,如果没有的话,需要点击"创建AccessKey"按钮创建。
endpoint可以到 OSS 的 https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.9.7dc72841Z2hGqa#concept-zt4-cvy-5db[官方文档]中查看,根据所在的 region ,填写对应的 endpoint 即可。
==== 引入 OSS API
Spring Cloud Alicloud OSS 中的 OSS API 基于阿里云官方OSS SDK提供具备上传、下载、查看等所有对象存储类操作API。
一个简单的使用 OSS API 的应用如下。
[source,java]
----
@SpringBootApplication
public class OssApplication {
@Autowired
private OSS ossClient;
@RequestMapping("/")
public String home() {
ossClient.putObject("bucketName", "fileName", new FileInputStream("/your/local/file/path"));
return "upload success";
}
public static void main(String[] args) throws URISyntaxException {
SpringApplication.run(OssApplication.class, args);
}
}
----
在上传文件之前,首先需要 https://account.aliyun.com/register/register.htm?spm=5176.8142029.388261.26.e9396d3eaYK2sG&oauth_callback=https%3A%2F%2Fwww.aliyun.com%2F[注册阿里云账号] ,如果已经有的话,请 https://common-buy.aliyun.com/?spm=5176.8465980.unusable.dopen.4cdf1450rg8Ujb&commodityCode=oss#/open[开通OSS服务]。
进入 https://oss.console.aliyun.com/overview[OSS控制台],点击左侧"新建Bucket"按照提示创建一个Bucket然后将bucket名称替换掉上面代码中的"bucketName",而"fileName"取任意文件名,"/your/local/file/path"取任意本地文件路径,然后 curl http://127.0.0.1:端口/ 即可上传文件,可以到 https://oss.console.aliyun.com/overview[OSS控制台]查看效果。
更多关于 OSS API 的操作,可以参考 https://help.aliyun.com/document_detail/32008.html[OSS官方SDK文档]。
=== 与 Spring 框架的 Resource 结合
Spring Cloud AliCloud OSS 整合了 Spring 框架的 Resource 规范,可以让用户很方便的引用 OSS 的资源。
一个简单的使用 Resource 的例子如下。
[source,java]
----
@SpringBootApplication
public class OssApplication {
@Value("oss://bucketName/fileName")
private Resource file;
@GetMapping("/file")
public String fileResource() {
try {
return "get file resource success. content: " + StreamUtils.copyToString(
file.getInputStream(), Charset.forName(CharEncoding.UTF_8));
} catch (Exception e) {
return "get resource fail: " + e.getMessage();
}
}
public static void main(String[] args) throws URISyntaxException {
SpringApplication.run(OssApplication.class, args);
}
}
----
NOTE: 以上示例运行的前提是,在 OSS 上需要有名为"bucketName"的Bucket同时在该Bucket下存在名为"fileName"的文件。
=== 采用 STS 授权
Spring Cloud AliCloud OSS 除了 AccessKey/SecretKey 的授权方式以外,还支持 STS 授权方式。 STS 是临时访问令牌的方式,一般用于授权第三方,临时访问自己的资源。
作为第三方,也就是被授权者,只需要配置以下内容,就可以访问临时被授权的资源。
[source,properties]
----
spring.cloud.alicloud.oss.authorization-mode=STS
spring.cloud.alicloud.oss.endpoint=***.aliyuncs.com
spring.cloud.alicloud.oss.sts.access-key=你被授权的AK
spring.cloud.alicloud.oss.sts.secret-key=你被授权的SK
spring.cloud.alicloud.oss.sts.security-token=你被授权的ST
----
其中 spring.cloud.alicloud.oss.authorization-mode 是枚举类型,此时填写 STS ,代表采用 STS 的方式授权。 endpoint可以到 OSS 的 https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.9.7dc72841Z2hGqa#concept-zt4-cvy-5db[官方文档]中查看,根据所在的 region ,填写对应的 endpoint 即可。
access-key、secret-key和security-token需要由授权方颁发如果对 STS 不了解的话,可以参考 https://help.aliyun.com/document_detail/31867.html[STS官方文档]。
=== 更多客户端配置
除了基本的配置项以外, Spring Cloud AliCloud OSS 还支持很多额外的配置,也是在 application.properties 文件中。
以下是一些简单的示例。
[source,properties]
----
spring.cloud.alicloud.oss.authorization-mode=STS
spring.cloud.alicloud.oss.endpoint=***.aliyuncs.com
spring.cloud.alicloud.oss.sts.access-key=你被授权的AK
spring.cloud.alicloud.oss.sts.secret-key=你被授权的SK
spring.cloud.alicloud.oss.sts.security-token=你被授权的ST
spring.cloud.alicloud.oss.config.connection-timeout=3000
spring.cloud.alicloud.oss.config.max-connections=1000
----
如果想了解更多的配置项,可以参考 https://help.aliyun.com/document_detail/32010.html?spm=a2c4g.11186623.6.703.50b25413nGsYHc[OSSClient配置项] 的末尾表格。
NOTE: 通常情况下,都需要将 https://help.aliyun.com/document_detail/32010.html?spm=a2c4g.11186623.6.703.50b25413nGsYHc[OSSClient配置项] 末尾表格中的参数名更换成"-"连接,且所有字母小写。例如 ConnectionTimeout对应 connection-timeout。

@ -0,0 +1 @@
== Spring Cloud Alibaba Rocket Binder

@ -0,0 +1,31 @@
[[spring-cloud-alibaba-reference]]
= Spring Cloud Alibaba 参考文档
xiaojing; xiaolongzuo; jim fang; bingting peng
:doctype: book
:toc:
:toclevels: 4
:source-highlighter: prettify
:numbered:
== 介绍
Spring Cloud Alibaba 致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。
依托 Spring Cloud Alibaba您只需要添加一些注解和少量配置就可以将 Spring Cloud 应用接入阿里分布式应用解决方案,通过阿里中间件来迅速搭建分布式应用系统。
include::dependency-management.adoc[]
include::nacos-discovery.adoc[]
include::nacos-config.adoc[]
include::sentinel.adoc[]
include::rocketmq.adoc[]
include::ans.adoc[]
include::acm.adoc[]
include::oss.adoc[]

@ -0,0 +1,4 @@
== Dependency Management
The Spring Cloud Alibaba Bill of Materials (BOM) contains the versions of all the dependencies it uses.

@ -0,0 +1 @@
== Spring Cloud Alibaba Nacos Discovery

@ -0,0 +1 @@
== Spring Cloud Alibaba Rocket Binder

@ -0,0 +1,31 @@
[[spring-cloud-alibaba-reference]]
= Spring Cloud Alibaba Reference Documentation
xiaojing; xiaolongzuo; jim fang; bingting peng
:doctype: book
:toc:
:toclevels: 4
:source-highlighter: prettify
:numbered:
== Introduction
Spring Cloud Alibaba provides a one-stop solution for distributed application development. It contains all the components required to develop distributed applications, making it easy for you to develop your applications using Spring Cloud.
With Spring Cloud Alibaba, you only need to add some annotations and a small amount of configurations to connect Spring Cloud applications to the distributed solutions of Alibaba, and build a distributed application system with Alibaba middleware.
include::dependency-management.adoc[]
include::nacos-discovery.adoc[]
include::nacos-config.adoc[]
include::sentinel.adoc[]
include::rocketmq.adoc[]
include::ans.adoc[]
include::acm.adoc[]
include::oss.adoc[]

@ -0,0 +1,28 @@
<?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.1.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>acm-local-example</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-acm</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,46 @@
# ACM Local Example
## 项目说明
本项目展示了在Spring Cloud规范下如何以最简单且免费的方式使用ACM产品将配置统一管理。
应用配置管理Application Configuration Management简称 ACM其前身为淘宝内部配置中心 Diamond是一款应用配置中心产品。基于该应用配置中心产品您可以在微服务、DevOps、大数据等场景下极大地减轻配置管理的工作量的同时保证配置的安全合规。更多 ACM 相关的信息,请参考 [ACM官网](https://www.aliyun.com/product/acm)。
## 示例
### 准备工作
ACM支持直接使用免费的轻量版配置中心进行开发和调试工作。本示例也是基于轻量版配置中心的因此我们需要首先安装和启动轻量版配置中心。
1. [下载轻量版配置中心](https://edas-public.oss-cn-hangzhou.aliyuncs.com/install_package/LCC/2018-11-01/edas-lite-configcenter.tar.gz?file=edas-lite-configcenter.tar.gz)
2. 解压 edas-lite-configcenter.tar.gz ,然后执行以下命令。
cd edas-config-center && sh startup.sh
出现以下内容说明轻量版配置中心启动成功。
Edas-config-center has been started successfully.
You can see more details in logs/config-center.log.
3. 进入页面 http://127.0.0.1:8080在左侧"配置列表"页面中,点击"添加"按钮,添加如下配置。
GroupDEFAULT_GROUP
DataIdacm-local.properties
Contentuser.id=xiaolongzuo
### 启动应用
直接运行main class即`AcmApplication`。
### 查看效果
1. 使用`curl`可以看到在配置中心配置的user.id。
curl http://127.0.0.1:18089/
2. 进入页面 http://127.0.0.1:8080在左侧"配置列表"页面中更改user.id的值以后再次使用`curl`命令,可以看到配置变化。
如果您对 Spring Cloud ACM Starter 有任何建议或想法,欢迎提交 issue 中或者通过其他社区渠道向我们反馈。

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

@ -0,0 +1,41 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xiaolongzuo
*/
@RestController
public class EchoController {
private static final Logger LOGGER = LoggerFactory.getLogger(EchoController.class);
@Value("${user.id}")
private String userId;
@RequestMapping(value = "/")
public String echo() {
LOGGER.info("User id is " + userId);
return userId;
}
}

@ -0,0 +1,4 @@
spring.application.name=acm-local
server.port=18089
spring.cloud.alicloud.acm.server-list=127.0.0.1
spring.cloud.alicloud.acm.server-port=8080

@ -0,0 +1,32 @@
<?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.1.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ans-consumer-feign-example</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-ans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,25 @@
# ANS Consumer Feign Example
## 项目说明
本项目展示了在Spring Cloud规范下如何以最简单且免费的方式使用Feign客户端调用一个服务。
## 示例
### 准备工作
ans-provider-example已经成功启动并发布服务成功。
### 启动应用
直接运行main class即`ConsumerApplication`。
### 查看效果
使用`curl`命令可以看到应用打印出相应的调用日志,命令如下。
curl http://127.0.0.1:18082/
如果您对 Spring Cloud ANS Starter 有任何建议或想法,欢迎提交 issue 中或者通过其他社区渠道向我们反馈。

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

@ -0,0 +1,32 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @author xiaolongzuo
*/
@FeignClient(value = "ans-provider")
public interface EchoService {
@RequestMapping(path = "echo/{str}")
String echo(@RequestParam("str") String param);
}

@ -0,0 +1,46 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xiaolongzuo
*/
@RestController
public class HomeController {
private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class);
@Autowired
private EchoService echoService;
@RequestMapping(value = "/", method = RequestMethod.GET, produces = "application/json")
public String home() {
LOGGER.info("-----------------consumer调用开始-----------------");
String param = "Nice to meet you.";
LOGGER.info("消费者传递参数:" + param);
String result = echoService.echo(param);
LOGGER.info("收到提供者响应:" + result);
return param + "<br>" + result;
}
}

@ -0,0 +1,4 @@
server.port=18083
# The following configuration can be omitted.
spring.cloud.alicloud.ans.server.list=127.0.0.1
spring.cloud.alicloud.ans.server.port=8080

@ -0,0 +1,28 @@
<?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.1.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ans-consumer-ribbon-example</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-ans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,25 @@
# ANS Consumer Ribbon Example
## 项目说明
本项目展示了在Spring Cloud规范下如何以最简单且免费的方式使用Ribbon客户端调用一个服务。
## 示例
### 准备工作
ans-provider-example已经成功启动并发布服务成功。
### 启动应用
直接运行main class即`ConsumerApplication`。
### 查看效果
使用`curl`命令可以看到应用打印出相应的调用日志,命令如下。
curl http://127.0.0.1:18082/
如果您对 Spring Cloud ANS Starter 有任何建议或想法,欢迎提交 issue 中或者通过其他社区渠道向我们反馈。

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

@ -0,0 +1,47 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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 xiaolongzuo
*/
@RestController
public class HomeController {
private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class);
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/", method = RequestMethod.GET, produces = "application/json")
public String home() {
LOGGER.info("-----------------consumer调用开始-----------------");
String param = "Nice to meet you.";
LOGGER.info("消费者传递参数:" + param);
String result = restTemplate.getForObject("http://ans-provider/echo/" + param, String.class);
LOGGER.info("收到提供者响应:" + result);
return param + "<br>" + result;
}
}

@ -0,0 +1,4 @@
server.port=18082
# The following configuration can be omitted.
spring.cloud.alicloud.ans.server.list=127.0.0.1
spring.cloud.alicloud.ans.server.port=8080

@ -0,0 +1,28 @@
<?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.1.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ans-provider-example</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-ans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>

@ -0,0 +1,35 @@
# ANS Provider Example
## 项目说明
本项目展示了在Spring Cloud规范下如何以最简单且免费的方式发布一个服务。
## 示例
### 准备工作
ANS支持直接使用免费的轻量版配置中心进行开发和调试工作。本示例也是基于轻量版配置中心的因此我们需要首先安装和启动轻量版配置中心。
1. [下载轻量版配置中心](https://edas-public.oss-cn-hangzhou.aliyuncs.com/install_package/LCC/2018-11-01/edas-lite-configcenter.tar.gz?file=edas-lite-configcenter.tar.gz)
2. 解压 edas-lite-configcenter.tar.gz ,然后执行以下命令。
cd edas-config-center && sh startup.sh
出现以下内容说明轻量版配置中心启动成功。
Edas-config-center has been started successfully.
You can see more details in logs/config-center.log.
### 启动应用
直接运行main class即`ProviderApplication`。
### 查看效果
进入页面 http://127.0.0.1:8080在左侧"服务列表"页面中,可以看到一条名为`ans-provider`的服务。
如果您对 Spring Cloud ANS Starter 有任何建议或想法,欢迎提交 issue 中或者通过其他社区渠道向我们反馈。

@ -0,0 +1,42 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.cloud.examples;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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 xiaolongzuo
*/
@RestController
public class EchoController {
private static final Logger LOGGER = LoggerFactory.getLogger(EchoController.class);
@RequestMapping(value = "/echo/{str}", method = RequestMethod.GET, produces = "application/json")
public String echo(@PathVariable String str) {
LOGGER.info("-----------收到消费者请求-----------");
LOGGER.info("收到消费者传递的参数:" + str);
String result = "Nice to meet you, too.";
LOGGER.info("提供者返回结果:" + result);
return result;
}
}

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

@ -0,0 +1,4 @@
spring.application.name=ans-provider
server.port=18081
spring.cloud.alicloud.ans.server.list=127.0.0.1
spring.cloud.alicloud.ans.server.port=8080

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -19,9 +19,10 @@
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2. 在应用的 /src/main/resources/bootstrap.properties 配置文件中配置 Nacos Config 地址
2. 在应用的 /src/main/resources/bootstrap.properties 配置文件中配置 Nacos Config 元数据
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.application.name=nacos-config-example
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
3. 完成上述两步后,应用会从 Nacos Config 中获取相应的配置,并添加在 Spring Environment 的 PropertySources 中。这里我们使用 @Value 注解来将对应的配置注入到 SampleController 的 userName 和 age 字段,并添加 @RefreshScope 打开动态刷新功能
@ -70,8 +71,8 @@
1. 增加配置,在应用的 /src/main/resources/application.properties 中添加基本配置信息
spring.application.name=nacos-config-example
server.port=18084
server.port=18084
management.endpoints.web.exposure.include=*
2. 启动应用,支持 IDE 直接启动和编译打包后启动。
@ -118,9 +119,9 @@ Nacos Client 从 Nacos Server 端获取数据时,调用的是此接口 `Config
* `spring.active.profile` 即为当前环境对应的 profile详情可以参考 [Spring Boot文档](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html#boot-features-profiles)
**注意,当 activeprofile 为空时,对应的连接符 `-` 也将不存在dataId 的拼接格式变成 `${prefix}`.`${context.type}`**
**注意,当 activeprofile 为空时,对应的连接符 `-` 也将不存在dataId 的拼接格式变成 `${prefix}`.`${file-extension}`**
* `file-extension` 为配置内容的数据格式,可以通过配置项 `spring.cloud.nacos.config.content-type`来配置。
* `file-extension` 为配置内容的数据格式,可以通过配置项 `spring.cloud.nacos.config.file-extension`来配置。
目前只支持 `properties` 类型。
#### group
@ -168,7 +169,7 @@ Spring Boot 2.x 可以通过访问 http://127.0.0.1:18084/actuator/nacos-config
服务端地址|spring.cloud.nacos.config.server-addr||
DataId前缀|spring.cloud.nacos.config.prefix||spring.application.name
Group|spring.cloud.nacos.config.group|DEFAULT_GROUP|
dataID后缀及数据格式|spring.cloud.nacos.config.file-extension|properties|目前只支持 properties
dataID后缀及内容文件格式|spring.cloud.nacos.config.file-extension|properties|dataId的后缀同时也是配置内容的文件格式目前只支持 properties
配置内容的编码方式|spring.cloud.nacos.config.encode|UTF-8|配置的编码
获取配置的超时时间|spring.cloud.nacos.config.timeout|3000|单位为 ms
配置的命名空间|spring.cloud.nacos.config.namespace||常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源隔离等。

@ -19,9 +19,10 @@ Before we start the demo, let's learn how to connect Nacos Config to a Spring Cl
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
2. Add Nacos server address configurations to file /src/main/resources/bootstrap.properties
2. Add Nacos config metadata configurations to file /src/main/resources/bootstrap.properties
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.application.name=nacos-config-example
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
3. After completing the above two steps, the application will get the externalized configuration from Nacos Server and put it in the Spring Environment's PropertySources.We use the @Value annotation to inject the corresponding configuration into the userName and age fields of the SampleController, and add @RefreshScope to turn on dynamic refresh .
@RefreshScope
@ -70,8 +71,8 @@ Before we start the demo, let's learn how to connect Nacos Config to a Spring Cl
1. Add necessary configurations to file /src/main/resources/application.properties
spring.application.name=nacos-config-example
server.port=18084
server.port=18084
management.endpoints.web.exposure.include=*
2. Start the application in IDE or by building a fatjar.
@ -119,9 +120,9 @@ In Nacos Config Starter, the splicing format of dataId is as follows
* `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)
**Note: when the activeprofile is empty, the corresponding connector `-` will also not exist, and the splicing format of the dataId becomes `${prefix}`.`${context.type}`**
**Note: when the activeprofile is empty, the corresponding connector `-` will also not exist, and the splicing format of the dataId becomes `${prefix}`.`${file-extension}`**
* `file-extension` is the data format of the configuration content, which can be configured by the configuration item `spring.cloud.nacos.config.content-type`.
* `file-extension` is the data format of the configuration content, which can be configured by the configuration item `spring.cloud.nacos.config.file-extension`.
Currently only the `properties` type is supported.
#### group

@ -1,3 +1,2 @@
spring.application.name=nacos-config-example
server.port=18084
management.endpoints.web.exposure.include=*

@ -1 +1,2 @@
spring.application.name=nacos-config-example
spring.cloud.nacos.config.server-addr=127.0.0.1:8848

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>nacos-discovery-example</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>nacos-discovery-example</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -197,7 +197,7 @@ AccessKey|spring.cloud.nacos.discovery.access-key||
SecretKey|spring.cloud.nacos.discovery.secret-key||
Metadata|spring.cloud.nacos.discovery.metadata||使用Map格式配置
日志文件名|spring.cloud.nacos.discovery.log-name||
接入点|spring.cloud.nacos.discovery.enpoint|UTF-8|地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
接入点|spring.cloud.nacos.discovery.endpoint|UTF-8|地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址
是否集成Ribbon|ribbon.nacos.enabled|true|

@ -205,7 +205,7 @@ AccessKey|spring.cloud.nacos.discovery.access-key||
SecretKey|spring.cloud.nacos.discovery.secret-key||
Metadata|spring.cloud.nacos.discovery.metadata||Extended data, Configure using Map format
log name|spring.cloud.nacos.discovery.log-name||
endpoint|spring.cloud.nacos.discovery.enpoint||The domain name of a service, through which the server address can be dynamically obtained.
endpoint|spring.cloud.nacos.discovery.endpoint||The domain name of a service, through which the server address can be dynamically obtained.
Integration Ribbon|ribbon.nacos.enabled|true|

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -11,7 +11,7 @@
### 接入 OSS
在启动示例进行演示之前,我们先了解一下如何接入 OSS。
**注意:本节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您只需修改 accessKeyId、secretAccessKey、region 即可。**
**注意:本节只是为了便于您理解接入方式,本示例代码中已经完成接入工作,您只需修改 accessKey、secretKey、endpoint 即可。**
1. 修改 pom.xml 文件,引入 alicloud-oss starter。
@ -20,24 +20,24 @@
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
2. 在配置文件中配置 OSS 服务对应的 accessKeyId、secretAccessKey 和 region
2. 在配置文件中配置 OSS 服务对应的 accessKey、secretKey 和 endpoint
// application.properties
spring.cloud.alibaba.oss.accessKeyId=your-ak
spring.cloud.alibaba.oss.secretAccessKey=your-sk
spring.cloud.alibaba.oss.region=cn-beijing
spring.cloud.alicloud.access-key=your-ak
spring.cloud.alicloud.secret-key=your-sk
spring.cloud.alicloud.oss.endpoint=***
以阿里云 accessKeyId、secretAccessKey 为例,获取方式如下。
以阿里云 accessKey、secretKey 为例,获取方式如下。
i. 在阿里云控制台界面,单击右上角头像,选择 accesskeys或者直接登录[用户信息管理界面](https://usercenter.console.aliyun.com/)
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535371973274-3ebec90a-ebde-4eb7-96ed-5372f6b32fe0.png)
ii. 获取 accessKeyId、secretAccessKey
ii. 获取 accessKey、secretKey
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535372168883-b94a3d77-3f81-4938-b409-611945a9e21c.png)
**注意:**如果您使用了阿里云 [STS服务](https://help.aliyun.com/document_detail/28756.html) 进行短期访问权限管理,则除了 accessKeyId、secretAccessKey、region 以外,还需配置 securityToken。
**注意:**如果您使用了阿里云 [STS服务](https://help.aliyun.com/document_detail/28756.html) 进行短期访问权限管理,则除了 accessKey、secretKey、endpoint 以外,还需配置 securityToken。
3. 注入 OSSClient 并进行文件上传下载等操作。
@ -61,9 +61,9 @@
spring.application.name=oss-example
server.port=18084
spring.cloud.alibaba.oss.accessKeyId=your-ak
spring.cloud.alibaba.oss.secretAccessKey=your-sk
spring.cloud.alibaba.oss.region=cn-beijing
spring.cloud.alicloud.access-key=your-ak
spring.cloud.alicloud.secret-key=your-sk
spring.cloud.alicloud.oss.endpoint=***
2. 通过 IDE 直接启动或者编译打包后启动应用。
@ -72,7 +72,7 @@
1. 执行 `mvn clean package` 将工程编译打包;
2. 执行 `java -jar oss-example.jar`启动应用。
应用启动后会自动在 OSS 上创建一个名为 `spring-cloud-alibaba` 的 Bucket。
应用启动后会自动在 OSS 上创建一个名为 `spring-cloud-alibaba-test` 的 Bucket。
### 上传或下载文件
@ -96,7 +96,7 @@
显示结果:
// 如果配置正确,则输出
download success, content: { "name": "spring-cloud-alibaba", "github": "https://github.com/spring-cloud-incubator/spring-cloud-alibaba", "authors": ["Jim", "flystar32"], "emails": ["fangjian0423@gmail.com", "flystar32@163.com"] }
download success, content: { "name": "oss-test" }
// 下载的过程中如果发生异常则会输出download fail: fail reason。比如accessKeyId配置错误则fail reason内容如下
download fail: The OSS Access Key Id you provided does not exist in our records. [ErrorCode]: InvalidAccessKeyId [RequestId]: RequestId [HostId]: xxx.oss-cn-beijing.aliyuncs.com [ResponseError]: InvalidAccessKeyId The OSS Access Key Id you provided does not exist in our records. RequestId sxxx.oss-cn-beijing.aliyuncs.com xxx-accessKeyId
@ -106,11 +106,11 @@
完成文件上传或者下载操作后,可以登录 OSS 控制台进行验证。
1. 登陆[OSS控制台](https://oss.console.aliyun.com/),可以看到左侧 Bucket 列表新增一个名字为`spring-cloud-alibaba`的 Bucket。
1. 登陆[OSS控制台](https://oss.console.aliyun.com/),可以看到左侧 Bucket 列表新增一个名字为`spring-cloud-alibaba-test`的 Bucket。
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535369224513-387afdf9-6078-4a42-9f18-d9fe9926a9cd.png)
2. 单击`spring-cloud-alibaba` Bucket选择 `文件管理` 页签,发现上传的 oss-test 文件在 custom-dir 目录中。上传的 objectName 为`custom-dir/oss-test`。目录和文件以'/'符号分割。
2. 单击`spring-cloud-alibaba-test` Bucket选择 `文件管理` 页签,发现上传的 oss-test 文件。上传的 objectName 为`oss-test.json`。目录和文件以'/'符号分割。
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535615378605-df1381e9-c5ff-4da1-b3b3-ce9acfef313f.png)
@ -134,24 +134,6 @@ Endpoint 内部会显示所有的 OSSClient 配置信息,以及该 OSSClient
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535373658171-20674565-6fe1-4e1e-a596-1dd6f4159ec3.png)
## 多个 OSSClient 场景
如果您需要配置多个 OSSClient类似多数据源的配置则可以先构造 `OSSProperties`,再构造 `OSSClient`,并分别为每个 OSSClient 配置相应的 accessKeyId、secretAccessKey 等信息。
@Bean
@ConfigurationProperties(prefix = "spring.cloud.alibaba.oss1")
public OSSProperties ossProperties1() {
return new OSSProperties();
}
@Bean
public OSS ossClient1(@Qualifier("ossProperties1") OSSProperties ossProperties) {
return new OSSClientBuilder().build(ossProperties.getEndpoint(),
ossProperties.getAccessKeyId(), ossProperties.getSecretAccessKey(),
ossProperties.getSecurityToken(), ossProperties.getConfiguration());
}
<h2 id="1"> 以 Resource 的形式读取文件 </h2>
OSS Starter 支持以 Resource 的形式得到文件对象。如果只需读取少量文件,您可以使用这种方式。

@ -11,7 +11,7 @@ If your applications are Spring Cloud applications and you need to use Alibaba C
### Connect to OSS
Before we start the demo, let's learn how to connect OSS to a Spring Cloud application.
**Note: This section is to show you how to connect to oss. The actual configurations have been completed in the following example, and you only need to specify your accessKeyId, secretAccessKey and region.**
**Note: This section is to show you how to connect to oss. The actual configurations have been completed in the following example, and you only need to specify your accessKey, secretKey and endpoint.**
1. Add dependency spring-cloud-starter-alicloud-oss in the pom.xml file in your Spring Cloud project.
@ -23,21 +23,21 @@ Before we start the demo, let's learn how to connect OSS to a Spring Cloud appli
2. Configure accessKeyId, secretAccessKey and region in application.properties.
// application.properties
spring.cloud.alibaba.oss.accessKeyId=your-ak
spring.cloud.alibaba.oss.secretAccessKey=your-sk
spring.cloud.alibaba.oss.region=cn-beijing
spring.cloud.alicloud.access-key=your-ak
spring.cloud.alicloud.secret-key=your-sk
spring.cloud.alicloud.oss.endpoint=***
To get accessKeyId, secretAccessKey, follow these steps:
To get accessKey, secretKey, follow these steps:
1. On the Alibaba Cloud console, click your avatar on the upper-right corner and click accesskeys. Or visit [User Management](https://usercenter.console.aliyun.com/) page directly
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535464041257-5c7ae997-daff-45b3-89d4-02d578da4ac7.png)
2. Get your accessKeyId、secretAccessKey
2. Get your accessKey、secretKey
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535464098793-517491f6-156b-4a98-a5a4-6113cb3c01a4.png)
**Note:** If you are using [STS](https://www.alibabacloud.com/help/doc-detail/28756.html), you should configure securityToken in addition to accessKeyId, secretAccessKey, and region.
**Note:** If you are using [STS](https://www.alibabacloud.com/help/doc-detail/28756.html), you should configure securityToken in addition to accessKey, secretKey, and endpoint.
3. Inject OSSClient and use it to upload files to the OSS server and download a file from OSS server.
@ -60,9 +60,9 @@ Before we start the demo, let's learn how to connect OSS to a Spring Cloud appli
spring.application.name=oss-example
server.port=18084
spring.cloud.alibaba.oss.accessKeyId=your-ak
spring.cloud.alibaba.oss.secretAccessKey=your-sk
spring.cloud.alibaba.oss.region=cn-beijing
spring.cloud.alicloud.access-key=your-ak
spring.cloud.alicloud.secret-key=your-sk
spring.cloud.alicloud.oss.endpoint=***
2. Start the application in IDE or by building a fatjar.
@ -71,7 +71,7 @@ Before we start the demo, let's learn how to connect OSS to a Spring Cloud appli
1. Execute command `mvn clean package` to build a fatjar.
2. Run command `java -jar oss-example.jar` to start the application.
After startup, a bucket called 'spring-cloud-alibaba' is automatically created in OSS.
After startup, a bucket called 'spring-cloud-alibaba-test' is automatically created in OSS.
### Upload or download files
@ -88,14 +88,14 @@ Results
upload fail: The OSS Access Key Id you provided does not exist in our records. [ErrorCode]: InvalidAccessKeyId [RequestId]: RequestId [HostId]: xxx.oss-cn-beijing.aliyuncs.com [ResponseError]: InvalidAccessKeyId The OSS Access Key Id you provided does not exist in our records. RequestId xxx.oss-cn-beijing.aliyuncs.com xxx-accessKeyId
#### Download files
Use `curl` command to download files. It will download the oss-test.json file that you uploaded just now and print in result):
Use `curl` command to download files. It will download the oss-test.json file that you uploaded just now and print in result:
curl http://localhost:18084/download
Results
// If configurations are correct, the output will be as follows
download success, content: { "name": "spring-cloud-alibaba", "github": "https://github.com/spring-cloud-incubator/spring-cloud-alibaba", "authors": ["Jim", "flystar32"], "emails": ["fangjian0423@gmail.com", "flystar32@163.com"] }
download success, content: { "name": "oss-tes" }
// If an error occurs during downloading, the output will be 'download fail: fail reason'. For example, if accessKeyId is wrongfail reason will be as follows
download fail: The OSS Access Key Id you provided does not exist in our records. [ErrorCode]: InvalidAccessKeyId [RequestId]: RequestId [HostId]: xxx.oss-cn-beijing.aliyuncs.com [ResponseError]: InvalidAccessKeyId The OSS Access Key Id you provided does not exist in our records. RequestId sxxx.oss-cn-beijing.aliyuncs.com xxx-accessKeyId
@ -103,11 +103,11 @@ Results
### Verify results on OSS
You can verify results on the OSS console when you finish uploading or downloading files.
1. Log on to the [OSS console](https://oss.console.aliyun.com/)and you will find a bucket named `spring-cloud-alibaba`.
1. Log on to the [OSS console](https://oss.console.aliyun.com/)and you will find a bucket named `spring-cloud-alibaba-test`.
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535464204462-ccebb9e0-7233-499c-8dec-8b8348231b2b.png)
2. Click the `spring-cloud-alibaba` bucket, select the Files tab, and you will find the oss-test file. The file 'oss-test' is located in directory 'custom-dir'. The objectName of the file is 'custom-dir/oss-test'. File directory and file is separated by '/'.
2. Click the `spring-cloud-alibaba-test` bucket, select the Files tab, and you will find the oss-test.json file. The objectName of the file is 'oss-test.json'. File directory and file is separated by '/'.
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535618026281-613a338c-f89c-4c7b-8b04-d404d1320699.png)
@ -133,26 +133,6 @@ Endpoint will show the configurations and the list of buckets of all OSSClients.
![undefined](https://cdn.nlark.com/lark/0/2018/png/64647/1535373658171-20674565-6fe1-4e1e-a596-1dd6f4159ec3.png)
## Multiple OSSClients
If you need multiple OSSClientslike Multi DataSources, build `OSSProperties` firstand then build `OSSClient`. Specify information such as assessKeyId and secrectAccessKey for each OSSClient.
@Bean
@ConfigurationProperties(prefix = "spring.cloud.alibaba.oss1")
public OSSProperties ossProperties1() {
return new OSSProperties();
}
@Bean
public OSS ossClient1(@Qualifier("ossProperties1") OSSProperties ossProperties) {
return new OSSClientBuilder().build(ossProperties.getEndpoint(),
ossProperties.getAccessKeyId(), ossProperties.getSecretAccessKey(),
ossProperties.getSecurityToken(), ossProperties.getConfiguration());
}
* OSSClient shutdownYou do not need to shutdown OSSClient. It will be done in `OSSApplicationListener`.
<h2 id="1">Read file using resource mode</h2>
OSS Starter supports getting file objects by `Spring Resource`. Simply configure OSS protocol of the resource

@ -1,51 +0,0 @@
package org.springframework.cloud.alibaba.cloud.examples;
import java.net.URISyntaxException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.aliyun.oss.OSS;
/**
* OSS Application
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@SpringBootApplication
public class OSSApplication {
public static final String BUCKET_NAME = "spring-cloud-alibaba";
public static void main(String[] args) throws URISyntaxException {
SpringApplication.run(OSSApplication.class, args);
}
@Bean
public AppRunner appRunner() {
return new AppRunner();
}
class AppRunner implements ApplicationRunner {
@Autowired
private OSS ossClient;
@Override
public void run(ApplicationArguments args) throws Exception {
try {
if (!ossClient.doesBucketExist(BUCKET_NAME)) {
ossClient.createBucket(BUCKET_NAME);
}
}
catch (Exception e) {
System.err.println("oss handle bucket error: " + e.getMessage());
System.exit(-1);
}
}
}
}

@ -1,72 +0,0 @@
package org.springframework.cloud.alibaba.cloud.examples;
import java.nio.charset.Charset;
import org.apache.commons.codec.CharEncoding;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.IOUtils;
import com.aliyun.oss.model.OSSObject;
/**
* OSS Controller
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@RestController
public class OSSController {
@Autowired
private OSS ossClient;
@Value("oss://" + OSSApplication.BUCKET_NAME + "/oss-test")
private Resource file;
private String dir = "custom-dir/";
@GetMapping("/upload")
public String upload() {
try {
ossClient.putObject(OSSApplication.BUCKET_NAME, dir + "oss-test", this
.getClass().getClassLoader().getResourceAsStream("oss-test.json"));
}
catch (Exception e) {
e.printStackTrace();
return "upload fail: " + e.getMessage();
}
return "upload success";
}
@GetMapping("/file-resource")
public String fileResource() {
try {
return "get file resource success. content: " + StreamUtils.copyToString(
file.getInputStream(), Charset.forName(CharEncoding.UTF_8));
}
catch (Exception e) {
e.printStackTrace();
return "get resource fail: " + e.getMessage();
}
}
@GetMapping("/download")
public String download() {
try {
OSSObject ossObject = ossClient.getObject(OSSApplication.BUCKET_NAME,
dir + "oss-test");
return "download success, content: " + IOUtils
.readStreamAsString(ossObject.getObjectContent(), CharEncoding.UTF_8);
}
catch (Exception e) {
e.printStackTrace();
return "download fail: " + e.getMessage();
}
}
}

@ -0,0 +1,49 @@
package org.springframework.cloud.alibaba.cloud.examples;
import com.aliyun.oss.OSS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import java.net.URISyntaxException;
/**
* OSS Application
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@SpringBootApplication
public class OssApplication {
public static final String BUCKET_NAME = "spring-cloud-alibaba-test";
public static void main(String[] args) throws URISyntaxException {
SpringApplication.run(OssApplication.class, args);
}
@Bean
public AppRunner appRunner() {
return new AppRunner();
}
class AppRunner implements ApplicationRunner {
@Autowired
private OSS ossClient;
@Override
public void run(ApplicationArguments args) throws Exception {
try {
if (!ossClient.doesBucketExist(BUCKET_NAME)) {
ossClient.createBucket(BUCKET_NAME);
}
} catch (Exception e) {
System.err.println("oss handle bucket error: " + e.getMessage());
System.exit(-1);
}
}
}
}

@ -0,0 +1,65 @@
package org.springframework.cloud.alibaba.cloud.examples;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.utils.IOUtils;
import com.aliyun.oss.model.OSSObject;
import org.apache.commons.codec.CharEncoding;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.nio.charset.Charset;
/**
* OSS Controller
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
@RestController
public class OssController {
@Autowired
private OSS ossClient;
@Value("oss://" + OssApplication.BUCKET_NAME + "/oss-test.json")
private Resource file;
@GetMapping("/upload")
public String upload() {
try {
ossClient.putObject(OssApplication.BUCKET_NAME, "oss-test.json", this
.getClass().getClassLoader().getResourceAsStream("oss-test.json"));
} catch (Exception e) {
e.printStackTrace();
return "upload fail: " + e.getMessage();
}
return "upload success";
}
@GetMapping("/file-resource")
public String fileResource() {
try {
return "get file resource success. content: " + StreamUtils.copyToString(
file.getInputStream(), Charset.forName(CharEncoding.UTF_8));
} catch (Exception e) {
e.printStackTrace();
return "get resource fail: " + e.getMessage();
}
}
@GetMapping("/download")
public String download() {
try {
OSSObject ossObject = ossClient.getObject(OssApplication.BUCKET_NAME, "oss-test.json");
return "download success, content: " + IOUtils
.readStreamAsString(ossObject.getObjectContent(), CharEncoding.UTF_8);
} catch (Exception e) {
e.printStackTrace();
return "download fail: " + e.getMessage();
}
}
}

@ -1,8 +1,6 @@
spring.application.name=oss-example
server.port=18084
spring.cloud.alibaba.oss.accessKeyId=[your-ak]
spring.cloud.alibaba.oss.secretAccessKey=[your-sk]
spring.cloud.alibaba.oss.region=[your-region]
management.endpoints.web.exposure.include=*
spring.cloud.alicloud.access-key=AK
spring.cloud.alicloud.secret-key=SK
spring.cloud.alicloud.oss.endpoint=***.aliyuncs.com
management.endpoints.web.exposure.include=*

@ -1,6 +1,3 @@
{
"name": "spring-cloud-alibaba",
"github": "https://github.com/spring-cloud-incubator/spring-cloud-alibaba",
"authors": ["Jim", "flystar32"],
"emails": ["fangjian0423@gmail.com", "flystar32@163.com"]
"name": "oss-test"
}

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -23,6 +23,10 @@
<module>nacos-example/nacos-discovery-example</module>
<module>nacos-example/nacos-config-example</module>
<module>oss-example</module>
<module>ans-example/ans-consumer-feign-example</module>
<module>ans-example/ans-consumer-ribbon-example</module>
<module>ans-example/ans-provider-example</module>
<module>acm-example/acm-local-example</module>
</modules>
<build>

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -192,9 +192,9 @@ Sentinel 控制台支持实时监控查看,您可以通过 Sentinel 控制台
<p align="center"><img src="https://cdn.nlark.com/lark/0/2018/png/54319/1532313595369-8428cd7d-9eb7-4786-a149-acf0da4a2daf.png" width="480" heigh='180' ></p>
## DataSource 支持
## ReadableDataSource 支持
Sentinel 内部提供了[动态规则的扩展实现 DataSource](https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95#datasource-%E6%89%A9%E5%B1%95)。
Sentinel 内部提供了[动态规则的扩展实现 ReadableDataSource](https://github.com/alibaba/Sentinel/wiki/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%99%E6%89%A9%E5%B1%95#datasource-%E6%89%A9%E5%B1%95)。
Sentinel starter 整合了目前存在的几类 DataSource。只需要在配置文件中进行相关配置即可在 Spring 容器中自动注册 DataSource。
@ -204,13 +204,13 @@ Sentinel starter 整合了目前存在的几类 DataSource。只需要在配置
spring.cloud.sentinel.datasource.recommendRefreshMs=2000
spring.cloud.sentinel.datasource.bufSize=2048
spring.cloud.sentinel.datasource.charset=utf-8
spring.cloud.sentinel.datasource.configParser=myParser
spring.cloud.sentinel.datasource.converter=myParser
spring.cloud.sentinel.datasource.file=/Users/you/rule.json
然后使用`@SentinelDataSource` 注解修饰 DataSource 即可注入:
@SentinelDataSource("spring.cloud.sentinel.datasource")
private DataSource dataSource;
private ReadableDataSource dataSource;
`@SentinelDataSource` 注解的 value 属性可以不填。默认值就是 `spring.cloud.sentinel.datasource`
@ -220,9 +220,9 @@ Sentinel starter 整合了目前存在的几类 DataSource。只需要在配置
`spring.cloud.sentinel.datasource.recommendRefreshMs` 里的 `recommendRefreshMs` 对应相关 DataSource 的属性。
`spring.cloud.sentinel.datasource.configParser`代表 `ConfigParser` 在 Spring 容器里的 name。如果没找到会抛出异常。
`spring.cloud.sentinel.datasource.converter`代表 `Converter` 在 Spring 容器里的 name。如果没找到会抛出异常。
type目前支持file, nacos, zk, apollo。
type目前支持file, nacos, zk, apollo。其中nacoszkapollo的使用需要加上对应的依赖`sentinel-datasource-nacos`, `sentinel-datasource-zookeeper`, `sentinel-datasource-apollo`
### 自定义DataSource
@ -230,7 +230,7 @@ type目前支持file, nacos, zk, apollo。
1. 定义DataSource
public class CustomDataSource implements DataSource {
public class CustomDataSource implements ReadableDataSource {
private String fieldA;
private String fieldB;
...
@ -260,7 +260,7 @@ type目前支持file, nacos, zk, apollo。
spring.cloud.sentinel.datasource.fieldA = valueA
spring.cloud.sentinel.datasource.fieldB = valueB
注意由于目前Sentinel的AbstractDataSource需要有个ConfigParser作为构造函数中的参数并且它的子类的构造都是通过多个参数的构造函数构造的。
注意由于目前Sentinel的AbstractDataSource需要有个Converter作为构造函数中的参数并且它的子类的构造都是通过多个参数的构造函数构造的。
所以目前所有的Sentinel starter中的DataSource都是基于FactoryBean并且通过设置属性构造的。如果有这方面的需求需要再多加一个registerFactoryBean过程。
SentinelDataSourceRegistry.registerFactoryBean("custeom", CustomDataSourceFactoryBean.class);

@ -165,9 +165,9 @@ To see the metrics, click **实时监控(Real-time Monitoring)** in the left-sid
<p align="center"><img src="https://cdn.nlark.com/lark/0/2018/png/54319/1532313595369-8428cd7d-9eb7-4786-a149-acf0da4a2daf.png" width="480" heigh='180'></p>
## DataSource
## ReadableDataSource
Sentinel provide [DataSource](https://github.com/alibaba/Sentinel/blob/master/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/DataSource.java) to manage dynamic rules.
Sentinel provide [ReadableDataSource](https://github.com/alibaba/Sentinel/blob/master/sentinel-extension/sentinel-datasource-extension/src/main/java/com/alibaba/csp/sentinel/datasource/ReadableDataSource.java) to manage dynamic rules.
Sentinel starter integrated 4 DataSources provided by Sentinel. It will be register into Spring Context if you write some configs in `application.properties`.
@ -177,13 +177,13 @@ If you want to define FileRefreshableDataSource:
spring.cloud.sentinel.datasource.recommendRefreshMs=2000
spring.cloud.sentinel.datasource.bufSize=2048
spring.cloud.sentinel.datasource.charset=utf-8
spring.cloud.sentinel.datasource.configParser=myParser
spring.cloud.sentinel.datasource.converter=myParser
spring.cloud.sentinel.datasource.file=/Users/you/rule.json
then use `@SentinelDataSource` to annotate DataSource:
@SentinelDataSource("spring.cloud.sentinel.datasource")
private DataSource dataSource;
private ReadableDataSource dataSource;
The value() of `@SentinelDataSource` is not required, it means the prefix of configuration. Default value is `spring.cloud.sentinel.datasource`.
@ -191,9 +191,10 @@ spring.cloud.sentinel.datasource.type means the type of DataSource.
spring.cloud.sentinel.datasource.recommendRefreshMs means the recommendRefreshMs property of specified DataSource.
spring.cloud.sentinel.datasource.configParser means the name of spring bean that type is ConfigParser. If the bean is not exists, will throw exception.
spring.cloud.sentinel.datasource.converter means the name of spring bean that type is Converter. If the bean is not exists, will throw exception.
Now datasource type support 4 categories: file, nacos, zk, apollo.
Now datasource type support 4 categories: file, nacos, zk, apollo. If you want to using nacos, zk or apollo, you should add `sentinel-datasource-nacos`, `sentinel-datasource-zookeeper` or `sentinel-datasource-apollo` dependency.
### User-defined DataSource
@ -201,7 +202,7 @@ User-defined DataSource need 2 steps.
1. Define DataSource
public class CustomDataSource implements DataSource {
public class CustomDataSource implements ReadableDataSource {
private String fieldA;
private String fieldB;
...
@ -230,7 +231,7 @@ User-defined DataSource need 2 steps.
spring.cloud.sentinel.datasource.fieldA = valueA
spring.cloud.sentinel.datasource.fieldB = valueB
Note: The AbstractDataSource of Sentinel need a ConfigParser as a constructor param and the subclass of AbstractDataSource was construct by multi-param constructor.
Note: The AbstractDataSource of Sentinel need a Converter as a constructor param and the subclass of AbstractDataSource was construct by multi-param constructor.
Now All DataSources in starter was construct by FactoryBean. If you want to do it in this way, you should register FactoryBean by SentinelDataSourceRegistry.
SentinelDataSourceRegistry.registerFactoryBean("custeom", CustomDataSourceFactoryBean.class);

@ -2,7 +2,7 @@ package org.springframework.cloud.alibaba.cloud.examples;
import java.util.List;
import com.alibaba.csp.sentinel.datasource.ConfigParser;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
@ -10,9 +10,10 @@ import com.alibaba.fastjson.TypeReference;
/**
* @author fangjian
*/
public class JsonFlowRuleListParser implements ConfigParser<String, List<FlowRule>> {
public class JsonFlowRuleListParser implements Converter<String, List<FlowRule>> {
@Override
public List<FlowRule> parse(String source) {
return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {});
public List<FlowRule> convert(String source) {
return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
});
}
}

@ -1,6 +1,6 @@
package org.springframework.cloud.alibaba.cloud.examples;
import com.alibaba.csp.sentinel.datasource.ConfigParser;
import com.alibaba.csp.sentinel.datasource.Converter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.alibaba.sentinel.annotation.SentinelProtect;
@ -25,7 +25,7 @@ public class ServiceApplication {
}
@Bean
public ConfigParser myParser() {
public Converter myParser() {
return new JsonFlowRuleListParser();
}

@ -10,5 +10,5 @@ spring.cloud.sentinel.datasource.type=file
spring.cloud.sentinel.datasource.recommendRefreshMs=3000
spring.cloud.sentinel.datasource.bufSize=4056196
spring.cloud.sentinel.datasource.charset=utf-8
spring.cloud.sentinel.datasource.configParser=myParser
spring.cloud.sentinel.datasource.converter=myParser
spring.cloud.sentinel.datasource.file=/Users/you/rule.json

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-examples</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -16,8 +16,6 @@
package org.springframework.cloud.alibaba.nacos;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.refresh.NacosContextRefresher;
@ -43,14 +41,6 @@ public class NacosConfigAutoConfiguration implements ApplicationContextAware {
@Autowired
private NacosRefreshProperties nacosRefreshProperties;
@Autowired
private ConfigService configService;
@Bean
public NacosConfigProperties nacosConfigProperties() {
return new NacosConfigProperties();
}
@Bean
public NacosPropertySourceRepository nacosPropertySourceRepository() {
return new NacosPropertySourceRepository(applicationContext);
@ -69,10 +59,10 @@ public class NacosConfigAutoConfiguration implements ApplicationContextAware {
@Bean
public NacosContextRefresher nacosContextRefresher(ContextRefresher contextRefresher,
NacosRefreshHistory refreshHistory,
NacosPropertySourceRepository propertySourceRepository,
ConfigService configService) {
NacosPropertySourceRepository propertySourceRepository) {
return new NacosContextRefresher(contextRefresher, nacosConfigProperties,
nacosRefreshProperties, refreshHistory, propertySourceRepository,configService);
nacosRefreshProperties, refreshHistory, propertySourceRepository,
nacosConfigProperties.configServiceInstance());
}
@Override

@ -16,6 +16,7 @@
package org.springframework.cloud.alibaba.nacos;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.alibaba.nacos.client.NacosPropertySourceLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -32,6 +33,7 @@ public class NacosConfigBootstrapConfiguration {
}
@Bean
@ConditionalOnMissingBean
public NacosConfigProperties nacosConfigProperties() {
return new NacosConfigProperties();
}

@ -16,9 +16,30 @@
package org.springframework.cloud.alibaba.nacos;
import java.util.Arrays;
import java.util.Objects;
import java.util.Properties;
import javax.annotation.PostConstruct;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
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
@ -29,6 +50,9 @@ import org.springframework.util.StringUtils;
@ConfigurationProperties("spring.cloud.nacos.config")
public class NacosConfigProperties {
private static final Logger LOGGER = LoggerFactory
.getLogger(NacosConfigProperties.class);
/**
* nacos config server address
*/
@ -89,6 +113,21 @@ public class NacosConfigProperties {
*/
private String clusterName;
@Value("${spring.application.name}")
private String name;
private String[] activeProfiles;
private ConfigService configService;
@Autowired
private Environment environment;
@PostConstruct
public void init() {
this.activeProfiles = environment.getActiveProfiles();
}
// todo sts support
public String getServerAddr() {
@ -187,6 +226,14 @@ public class NacosConfigProperties {
this.clusterName = clusterName;
}
public String getName() {
return name;
}
public String[] getActiveProfiles() {
return activeProfiles;
}
@Override
public String toString() {
return "NacosConfigProperties{" + "serverAddr='" + serverAddr + '\''
@ -195,46 +242,32 @@ public class NacosConfigProperties {
+ ", timeout=" + timeout + ", endpoint='" + endpoint + '\''
+ ", namespace='" + namespace + '\'' + ", accessKey='" + accessKey + '\''
+ ", secretKey='" + secretKey + '\'' + ", contextPath='" + contextPath
+ '\'' + ", clusterName='" + clusterName + '\'' + '}';
+ '\'' + ", clusterName='" + clusterName + '\'' + ", name='" + name + '\''
+ ", activeProfiles=" + Arrays.toString(activeProfiles) + '}';
}
public void overrideFromEnv(Environment env) {
public ConfigService configServiceInstance() {
if (StringUtils.isEmpty(this.getServerAddr())) {
this.setServerAddr(
env.resolvePlaceholders("${spring.cloud.nacos.config.server-addr:}"));
}
if (StringUtils.isEmpty(this.getEncode())) {
this.setEncode(
env.resolvePlaceholders("${spring.cloud.nacos.config.encode:}"));
}
if (StringUtils.isEmpty(this.getNamespace())) {
this.setNamespace(
env.resolvePlaceholders("${spring.cloud.nacos.config.namespace:}"));
if (null != configService) {
return configService;
}
if (StringUtils.isEmpty(this.getAccessKey())) {
this.setAccessKey(
env.resolvePlaceholders("${spring.cloud.nacos.config.access-key:}"));
}
if (StringUtils.isEmpty(this.getSecretKey())) {
this.setSecretKey(
env.resolvePlaceholders("${spring.cloud.nacos.config.secret-key:}"));
}
if (StringUtils.isEmpty(this.getContextPath())) {
this.setContextPath(env
.resolvePlaceholders("${spring.cloud.nacos.config.context-path:}"));
}
if (StringUtils.isEmpty(this.getClusterName())) {
this.setClusterName(env
.resolvePlaceholders("${spring.cloud.nacos.config.cluster-name:}"));
}
if (StringUtils.isEmpty(this.getEndpoint())) {
this.setEndpoint(
env.resolvePlaceholders("${spring.cloud.nacos.config.endpoint:}"));
Properties properties = new Properties();
properties.put(SERVER_ADDR, Objects.toString(this.serverAddr, ""));
properties.put(ENCODE, Objects.toString(this.encode, ""));
properties.put(NAMESPACE, Objects.toString(this.namespace, ""));
properties.put(ACCESS_KEY, Objects.toString(this.accessKey, ""));
properties.put(SECRET_KEY, Objects.toString(this.secretKey, ""));
properties.put(CONTEXT_PATH, Objects.toString(this.contextPath, ""));
properties.put(CLUSTER_NAME, Objects.toString(this.clusterName, ""));
properties.put(ENDPOINT, Objects.toString(this.endpoint, ""));
try {
configService = NacosFactory.createConfigService(properties);
return configService;
}
if (StringUtils.isEmpty(this.getPrefix())) {
this.setPrefix(
env.resolvePlaceholders("${spring.cloud.nacos.config.prefix:}"));
catch (Exception e) {
LOGGER.error("create config service error!properties={},e=,", this, e);
return null;
}
}
}

@ -28,6 +28,8 @@ import com.alibaba.nacos.api.exception.NacosException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.util.StringUtils;
/**
@ -81,13 +83,23 @@ public class NacosPropertySourceBuilder {
String data = null;
try {
data = configService.getConfig(dataId, group, timeout);
// todo add content type yaml support
if (!StringUtils.isEmpty(data)) {
Properties properties = new Properties();
logger.info(String.format("Loading nacos data, dataId: '%s', group: '%s'",
dataId, group));
properties.load(new StringReader(data));
return properties;
if (fileExtension.equalsIgnoreCase("properties")) {
Properties properties = new Properties();
properties.load(new StringReader(data));
return properties;
}
else if (fileExtension.equalsIgnoreCase("yaml")
|| fileExtension.equalsIgnoreCase("yml")) {
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
yamlFactory.setResources(new ByteArrayResource(data.getBytes()));
return yamlFactory.getObject();
}
}
}
catch (NacosException e) {

@ -16,12 +16,9 @@
package org.springframework.cloud.alibaba.nacos.client;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.cloud.alibaba.nacos.NacosConfigProperties;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.annotation.Order;
@ -30,11 +27,7 @@ import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
import org.springframework.util.StringUtils;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import static com.alibaba.nacos.api.PropertyKeyConst.*;
/**
* @author xiaojing
@ -48,47 +41,15 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
private static final String SEP1 = "-";
private static final String DOT = ".";
@Autowired
private ConfigurableListableBeanFactory beanFactory;
@Autowired
private NacosConfigProperties nacosConfigProperties;
private ConfigService configService;
private NacosPropertySourceBuilder nacosPropertySourceBuilder;
private Properties getPropertiesFromEnv(Environment env) {
nacosConfigProperties.overrideFromEnv(env);
Properties properties = new Properties();
properties.put(SERVER_ADDR, nacosConfigProperties.getServerAddr());
properties.put(ENCODE, nacosConfigProperties.getEncode());
properties.put(NAMESPACE, nacosConfigProperties.getNamespace());
properties.put(ACCESS_KEY, nacosConfigProperties.getAccessKey());
properties.put(SECRET_KEY, nacosConfigProperties.getSecretKey());
properties.put(CONTEXT_PATH, nacosConfigProperties.getContextPath());
properties.put(CLUSTER_NAME, nacosConfigProperties.getClusterName());
properties.put(ENDPOINT, nacosConfigProperties.getEndpoint());
return properties;
}
@Override
public PropertySource<?> locate(Environment env) {
Properties properties = getPropertiesFromEnv(env);
try {
configService = NacosFactory.createConfigService(properties);
}
catch (NacosException e) {
logger.error("create config service error, nacosConfigProperties:{}, ",
properties, e);
return null;
}
beanFactory.registerSingleton("configService", configService);
ConfigService configService = nacosConfigProperties.configServiceInstance();
if (null == configService) {
logger.warn(
@ -99,13 +60,12 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
nacosPropertySourceBuilder = new NacosPropertySourceBuilder(configService,
timeout);
String applicationName = env.getProperty("spring.application.name");
logger.info("Initialize spring.application.name '" + applicationName + "'.");
String name = nacosConfigProperties.getName();
String nacosGroup = nacosConfigProperties.getGroup();
String dataIdPrefix = nacosConfigProperties.getPrefix();
if (StringUtils.isEmpty(dataIdPrefix)) {
dataIdPrefix = applicationName;
dataIdPrefix = name;
}
String fileExtension = nacosConfigProperties.getFileExtension();
@ -113,23 +73,21 @@ public class NacosPropertySourceLocator implements PropertySourceLocator {
CompositePropertySource composite = new CompositePropertySource(
NACOS_PROPERTY_SOURCE_NAME);
loadApplicationConfiguration(composite, env, nacosGroup, dataIdPrefix,
fileExtension);
loadApplicationConfiguration(composite, nacosGroup, dataIdPrefix, fileExtension);
return composite;
}
private void loadApplicationConfiguration(
CompositePropertySource compositePropertySource, Environment environment,
String nacosGroup, String dataIdPrefix, String fileExtension) {
CompositePropertySource compositePropertySource, String nacosGroup,
String dataIdPrefix, String fileExtension) {
loadNacosDataIfPresent(compositePropertySource,
dataIdPrefix + DOT + fileExtension, nacosGroup, fileExtension);
for (String profile : environment.getActiveProfiles()) {
for (String profile : nacosConfigProperties.getActiveProfiles()) {
String dataId = dataIdPrefix + SEP1 + profile + DOT + fileExtension;
loadNacosDataIfPresent(compositePropertySource, dataId, nacosGroup,
fileExtension);
}
// todo multi profile active order and priority
}
private void loadNacosDataIfPresent(final CompositePropertySource composite,

@ -16,8 +16,6 @@
package org.springframework.cloud.alibaba.nacos.endpoint;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
@ -46,15 +44,6 @@ public class NacosConfigEndpointAutoConfiguration {
@Autowired
private NacosPropertySourceRepository nacosPropertySourceRepository;
@Autowired
private ConfigService configService;
@Bean
@ConditionalOnBean
public NacosConfigProperties nacosConfigProperties() {
return new NacosConfigProperties();
}
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
@Bean
@ -67,6 +56,7 @@ public class NacosConfigEndpointAutoConfiguration {
public NacosConfigHealthIndicator nacosConfigHealthIndicator(
NacosPropertySourceRepository nacosPropertySourceRepository) {
return new NacosConfigHealthIndicator(nacosConfigProperties,
nacosPropertySourceRepository, configService);
nacosPropertySourceRepository,
nacosConfigProperties.configServiceInstance());
}
}

@ -39,66 +39,59 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class NacosConfigAutoConfigurationTests {
private ConfigurableApplicationContext context;
@Before
public void setUp() throws Exception {
this.context = new SpringApplicationBuilder(
NacosConfigBootstrapConfiguration.class,
NacosConfigAutoConfiguration.class,
TestConfiguration.class)
.web(WebApplicationType.NONE).run(
"--spring.cloud.config.enabled=true",
"--spring.cloud.nacos.config.server-addr=127.0.0.1:8080",
"--spring.cloud.nacos.config.prefix=myapp");
}
@After
public void tearDown() throws Exception {
if (this.context != null) {
this.context.close();
}
}
@Test
public void testNacosConfigProperties() {
NacosPropertySourceLocator nacosPropertySourceLocator = this.context.getBean(NacosPropertySourceLocator.class);
Environment environment = this.context.getEnvironment();
try{
nacosPropertySourceLocator.locate(environment);
}catch (Exception e){
}
NacosConfigProperties nacosConfigProperties = this.context.getBean(NacosConfigProperties.class);
assertThat(nacosConfigProperties.getFileExtension()).isEqualTo("properties");
assertThat(nacosConfigProperties.getPrefix()).isEqualTo("myapp");
}
@Test
public void testNacosRefreshProperties() {
NacosRefreshProperties nacosRefreshProperties = this.context.getBean(NacosRefreshProperties.class);
assertThat(nacosRefreshProperties.isEnabled()).isEqualTo(true);
}
@Configuration
@AutoConfigureBefore(NacosConfigAutoConfiguration.class)
static class TestConfiguration{
@Autowired
ConfigurableApplicationContext context;
@Bean
ContextRefresher contextRefresher(){
return new ContextRefresher(context, new RefreshScope());
}
}
private ConfigurableApplicationContext context;
@Before
public void setUp() throws Exception {
this.context = new SpringApplicationBuilder(
NacosConfigBootstrapConfiguration.class,
NacosConfigAutoConfiguration.class, TestConfiguration.class)
.web(WebApplicationType.NONE)
.run("--spring.application.name=myapp",
"--spring.cloud.config.enabled=true",
"--spring.cloud.nacos.config.server-addr=127.0.0.1:8080",
"--spring.cloud.nacos.config.prefix=test");
}
@After
public void tearDown() throws Exception {
if (this.context != null) {
this.context.close();
}
}
@Test
public void testNacosConfigProperties() {
NacosConfigProperties nacosConfigProperties = this.context.getParent()
.getBean(NacosConfigProperties.class);
assertThat(nacosConfigProperties.getFileExtension()).isEqualTo("properties");
assertThat(nacosConfigProperties.getPrefix()).isEqualTo("test");
assertThat(nacosConfigProperties.getName()).isEqualTo("myapp");
}
@Test
public void testNacosRefreshProperties() {
NacosRefreshProperties nacosRefreshProperties = this.context
.getBean(NacosRefreshProperties.class);
assertThat(nacosRefreshProperties.isEnabled()).isEqualTo(true);
}
@Configuration
@AutoConfigureBefore(NacosConfigAutoConfiguration.class)
static class TestConfiguration {
@Autowired
ConfigurableApplicationContext context;
@Bean
ContextRefresher contextRefresher() {
return new ContextRefresher(context, new RefreshScope());
}
}
}

@ -43,9 +43,10 @@ public class NacosConfigBootstrapConfigurationTests {
public void setUp() throws Exception {
this.context = new SpringApplicationBuilder(
NacosConfigBootstrapConfiguration.class).web(WebApplicationType.NONE).run(
"--spring.application.name=myapp",
"--spring.cloud.config.enabled=true",
"--spring.cloud.nacos.config.server-addr=127.0.0.1:8080",
"--spring.cloud.nacos.config.prefix=myapp");
"--spring.cloud.nacos.config.prefix=test");
}
@After
@ -68,15 +69,14 @@ public class NacosConfigBootstrapConfigurationTests {
}
Field configServiceField = ReflectionUtils
.findField(NacosPropertySourceLocator.class, "configService");
configServiceField.setAccessible(true);
Field nacosConfigPropertiesField = ReflectionUtils
.findField(NacosPropertySourceLocator.class, "nacosConfigProperties");
nacosConfigPropertiesField.setAccessible(true);
ConfigService configService = (ConfigService) ReflectionUtils
.getField(configServiceField, locator);
NacosConfigProperties configService = (NacosConfigProperties) ReflectionUtils
.getField(nacosConfigPropertiesField, locator);
assertThat(configService).isNotNull();
}
}

@ -5,7 +5,7 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>0.2.0.BUILD-SNAPSHOT</version>
<version>0.2.1.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -48,12 +48,6 @@ public class NacosDiscoveryAutoConfiguration {
return new NacosServiceRegistry();
}
@Bean
@ConditionalOnMissingBean
public NacosDiscoveryProperties nacosProperties() {
return new NacosDiscoveryProperties();
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
public NacosRegistration nacosRegistration() {

@ -19,18 +19,17 @@ package org.springframework.cloud.alibaba.nacos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.registry.NacosRegistration;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import java.util.*;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
/**
* @author xiaojing
* @author renhaojun
*/
public class NacosDiscoveryClient implements DiscoveryClient {
@ -39,7 +38,7 @@ public class NacosDiscoveryClient implements DiscoveryClient {
public static final String DESCRIPTION = "Spring Cloud Nacos Discovery Client";
@Autowired
private NacosRegistration nacosRegistration;
private NacosDiscoveryProperties discoveryProperties;
@Override
public String description() {
@ -49,8 +48,8 @@ public class NacosDiscoveryClient implements DiscoveryClient {
@Override
public List<ServiceInstance> getInstances(String serviceId) {
try {
NamingService namingService = nacosRegistration.getNacosNamingService();
List<Instance> instances = namingService.getAllInstances(serviceId);
List<Instance> instances = discoveryProperties.namingServiceInstance()
.getAllInstances(serviceId);
return hostToServiceInstanceList(instances, serviceId);
}
catch (Exception e) {
@ -79,7 +78,7 @@ public class NacosDiscoveryClient implements DiscoveryClient {
List<Instance> instances, String serviceId) {
List<ServiceInstance> result = new ArrayList<ServiceInstance>(instances.size());
for (Instance instance : instances) {
if(instance.isHealthy()) {
if (instance.isHealthy()) {
result.add(hostToServiceInstance(instance, serviceId));
}
}
@ -90,9 +89,8 @@ public class NacosDiscoveryClient implements DiscoveryClient {
public List<String> getServices() {
try {
NamingService namingService = nacosRegistration.getNacosNamingService();
ListView<String> services = namingService.getServicesOfServer(1,
Integer.MAX_VALUE);
ListView<String> services = discoveryProperties.namingServiceInstance()
.getServicesOfServer(1, Integer.MAX_VALUE);
return services.getData();
}
catch (Exception e) {
@ -100,5 +98,4 @@ public class NacosDiscoveryClient implements DiscoveryClient {
return Collections.emptyList();
}
}
}

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

@ -16,6 +16,8 @@
package org.springframework.cloud.alibaba.nacos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ -33,6 +35,18 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
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.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;
/**
* @author dungu.zpf
@ -42,6 +56,9 @@ import java.util.Objects;
@ConfigurationProperties("spring.cloud.nacos.discovery")
public class NacosDiscoveryProperties {
private static final Logger LOGGER = LoggerFactory
.getLogger(NacosDiscoveryProperties.class);
/**
* nacos discovery server address
*/
@ -125,6 +142,11 @@ public class NacosDiscoveryProperties {
@Autowired
private InetUtils inetUtils;
@Autowired
private Environment environment;
private NamingService namingService;
@PostConstruct
public void init() throws SocketException {
@ -163,6 +185,8 @@ public class NacosDiscoveryProperties {
}
}
this.overrideFromEnv(environment);
}
public String getEndpoint() {
@ -333,4 +357,28 @@ public class NacosDiscoveryProperties {
}
}
public NamingService namingServiceInstance() {
if (null != namingService) {
return namingService;
}
Properties properties = new Properties();
properties.put(SERVER_ADDR, serverAddr);
properties.put(NAMESPACE, namespace);
properties.put(UtilAndComs.NACOS_NAMING_LOG_NAME, logName);
properties.put(ENDPOINT, endpoint);
properties.put(ACCESS_KEY, accessKey);
properties.put(SECRET_KEY, secretKey);
properties.put(CLUSTER_NAME, clusterName);
try {
namingService = NacosFactory.createNamingService(properties);
return namingService;
}
catch (Exception e) {
LOGGER.error("create naming service error!properties={},e=,", this, e);
return null;
}
}
}

@ -30,7 +30,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.registry.NacosRegistration;
/**
* Endpoint for nacos discovery, get nacos properties and subscribed services
@ -45,9 +44,6 @@ public class NacosDiscoveryEndpoint {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosRegistration nacosRegistration;
/**
* @return nacos discovery endpoint
*/
@ -56,7 +52,7 @@ public class NacosDiscoveryEndpoint {
Map<String, Object> result = new HashMap<>();
result.put("NacosDiscoveryProperties", nacosDiscoveryProperties);
NamingService namingService = nacosRegistration.getNacosNamingService();
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
List<ServiceInfo> subscribe = Collections.emptyList();
try {

@ -28,7 +28,6 @@ import org.springframework.context.annotation.Configuration;
* @author xiaojing
*/
@Configuration
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@ConditionalOnClass(Endpoint.class)
public class NacosDiscoveryEndpointAutoConfiguration {

@ -20,52 +20,51 @@ import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.ManagementServerPortUtils;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import java.net.URI;
import java.util.Map;
import java.util.Properties;
import javax.annotation.PostConstruct;
import com.alibaba.nacos.api.NacosFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.client.naming.utils.UtilAndComs;
import static com.alibaba.nacos.api.PropertyKeyConst.*;
/**
* @author xiaojing
*/
public class NacosRegistration implements Registration, ServiceInstance {
private static final String MANAGEMENT_PORT = "management.port";
private static final String MANAGEMENT_CONTEXT_PATH = "management.context-path";
private static final String MANAGEMENT_ADDRESS = "management.address";
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
private NamingService nacosNamingService;
@Autowired
private Environment environment;
private ApplicationContext context;
@PostConstruct
public void init() {
nacosDiscoveryProperties.overrideFromEnv(environment);
Properties properties = new Properties();
properties.put(SERVER_ADDR, nacosDiscoveryProperties.getServerAddr());
properties.put(NAMESPACE, nacosDiscoveryProperties.getNamespace());
properties.put(UtilAndComs.NACOS_NAMING_LOG_NAME,
nacosDiscoveryProperties.getLogName());
properties.put(ENDPOINT, nacosDiscoveryProperties.getEndpoint());
properties.put(ACCESS_KEY, nacosDiscoveryProperties.getAccessKey());
properties.put(SECRET_KEY, nacosDiscoveryProperties.getSecretKey());
properties.put(CLUSTER_NAME, nacosDiscoveryProperties.getClusterName());
try {
nacosNamingService = NacosFactory.createNamingService(properties);
}
catch (Exception e) {
Environment env = context.getEnvironment();
Integer managementPort = ManagementServerPortUtils.getPort(context);
if (null != managementPort) {
Map<String, String> metadata = nacosDiscoveryProperties.getMetadata();
metadata.put(MANAGEMENT_PORT, managementPort.toString());
String contextPath = env
.getProperty("management.server.servlet.context-path");
String address = env.getProperty("management.server.address");
if (!StringUtils.isEmpty(contextPath)) {
metadata.put(MANAGEMENT_CONTEXT_PATH, contextPath);
}
if (!StringUtils.isEmpty(address)) {
metadata.put(MANAGEMENT_ADDRESS, address);
}
}
}
@ -85,9 +84,7 @@ public class NacosRegistration implements Registration, ServiceInstance {
}
public void setPort(int port) {
if (nacosDiscoveryProperties.getPort() < 0) {
this.nacosDiscoveryProperties.setPort(port);
}
this.nacosDiscoveryProperties.setPort(port);
}
@Override
@ -122,11 +119,7 @@ public class NacosRegistration implements Registration, ServiceInstance {
}
public NamingService getNacosNamingService() {
return nacosNamingService;
}
public void setNacosNamingService(NamingService nacosNamingService) {
this.nacosNamingService = nacosNamingService;
return nacosDiscoveryProperties.namingServiceInstance();
}
public void setNacosDiscoveryProperties(
@ -137,7 +130,6 @@ public class NacosRegistration implements Registration, ServiceInstance {
@Override
public String toString() {
return "NacosRegistration{" + "nacosDiscoveryProperties="
+ nacosDiscoveryProperties + ", nacosNamingService=" + nacosNamingService
+ '}';
+ nacosDiscoveryProperties + '}';
}
}

@ -19,7 +19,7 @@ package org.springframework.cloud.alibaba.nacos.ribbon;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractServerList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.registry.NacosRegistration;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import java.util.ArrayList;
import java.util.List;
@ -28,11 +28,12 @@ import com.alibaba.nacos.api.naming.pojo.Instance;
/**
* @author xiaojing
* @author renhaojun
*/
public class NacosServerList extends AbstractServerList<NacosServer> {
@Autowired
private NacosRegistration registration;
private NacosDiscoveryProperties discoveryProperties;
private String serviceId;
@ -55,7 +56,7 @@ public class NacosServerList extends AbstractServerList<NacosServer> {
private List<NacosServer> getServers() {
try {
List<Instance> instances = registration.getNacosNamingService()
List<Instance> instances = discoveryProperties.namingServiceInstance()
.getAllInstances(serviceId);
return instancesToServerList(instances);
}

@ -37,7 +37,8 @@ public class NacosDiscoveryAutoConfigurationTests {
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(NacosDiscoveryTestConfiguration.class,
NacosDiscoveryAutoConfiguration.class))
NacosDiscoveryAutoConfiguration.class,
NacosDiscoveryClientAutoConfiguration.class))
.withPropertyValues("spring.cloud.nacos.discovery.server-addr=127.0.0.1:8080")
.withPropertyValues("spring.cloud.nacos.discovery.port=18080")
.withPropertyValues("spring.cloud.nacos.discovery.service=myapp");

@ -6,6 +6,7 @@ import org.junit.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryClientAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
@ -19,49 +20,43 @@ import static org.assertj.core.api.Assertions.assertThat;
*/
public class NacosRibbonClientConfigurationTests {
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(
AutoConfigurations.of(NacosRibbonTestConfiguration.class,
NacosRibbonClientConfiguration.class,
RibbonNacosAutoConfiguration.class))
.withPropertyValues("spring.cloud.nacos.discovery.server-addr=127.0.0.1:8080")
.withPropertyValues("spring.cloud.nacos.discovery.port=18080")
.withPropertyValues("spring.cloud.nacos.discovery.service=myapp");
@Test
public void testProperties() {
this.contextRunner.run(context -> {
NacosServerList serverList = context.getBean(NacosServerList.class);
assertThat(serverList.getServiceId()).isEqualTo("myapp");
});
}
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
static class NacosRibbonTestConfiguration {
@Bean
IClientConfig iClientConfig(){
//return new IClientConfig.Builder().s.build();
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.setClientName("myapp");
return config;
}
@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}
private WebApplicationContextRunner contextRunner = new WebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(NacosRibbonTestConfiguration.class,
NacosRibbonClientConfiguration.class,
NacosDiscoveryClientAutoConfiguration.class,
RibbonNacosAutoConfiguration.class))
.withPropertyValues("spring.cloud.nacos.discovery.server-addr=127.0.0.1:8080")
.withPropertyValues("spring.cloud.nacos.discovery.port=18080")
.withPropertyValues("spring.cloud.nacos.discovery.service=myapp");
@Test
public void testProperties() {
this.contextRunner.run(context -> {
NacosServerList serverList = context.getBean(NacosServerList.class);
assertThat(serverList.getServiceId()).isEqualTo("myapp");
});
}
@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
static class NacosRibbonTestConfiguration {
@Bean
IClientConfig iClientConfig() {
// return new IClientConfig.Builder().s.build();
DefaultClientConfigImpl config = new DefaultClientConfigImpl();
config.setClientName("myapp");
return config;
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
}

@ -0,0 +1,69 @@
<?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.1.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-datasource</artifactId>
<name>Spring Cloud Alibaba Sentinel DataSource</name>
<dependencies>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-zookeeper</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-apollo</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!--spring boot-->
<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-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -0,0 +1,152 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.sentinel.datasource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import static org.springframework.core.io.support.ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX;
/**
* {@link ReadableDataSource} Loader
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public class DataSourceLoader {
private static final Logger logger = LoggerFactory.getLogger(DataSourceLoader.class);
private final static String PROPERTIES_RESOURCE_LOCATION = "META-INF/sentinel-datasource.properties";
private final static String ALL_PROPERTIES_RESOURCES_LOCATION = CLASSPATH_ALL_URL_PREFIX
+ PROPERTIES_RESOURCE_LOCATION;
private final static ConcurrentMap<String, Class<? extends ReadableDataSource>> dataSourceClassesCache
= new ConcurrentHashMap<String, Class<? extends ReadableDataSource>>(
4);
static void loadAllDataSourceClassesCache() {
Map<String, Class<? extends ReadableDataSource>> dataSourceClassesMap = loadAllDataSourceClassesCache(
ALL_PROPERTIES_RESOURCES_LOCATION);
dataSourceClassesCache.putAll(dataSourceClassesMap);
}
static Map<String, Class<? extends ReadableDataSource>> loadAllDataSourceClassesCache(
String resourcesLocation) {
Map<String, Class<? extends ReadableDataSource>> dataSourcesMap
= new HashMap<String, Class<? extends ReadableDataSource>>(
4);
ClassLoader classLoader = DataSourceLoader.class.getClassLoader();
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
Resource[] resources = resolver.getResources(resourcesLocation);
for (Resource resource : resources) {
if (resource.exists()) {
Properties properties = PropertiesLoaderUtils
.loadProperties(resource);
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
String type = (String)entry.getKey();
String className = (String)entry.getValue();
if (!ClassUtils.isPresent(className, classLoader)) {
if (logger.isDebugEnabled()) {
logger.debug(
"Sentinel DataSource implementation [ type : "
+ type + ": , class : " + className
+ " , url : " + resource.getURL()
+ "] was not present in current classpath , "
+ "thus loading will be ignored , please add dependency if required !");
}
continue;
}
Assert.isTrue(!dataSourcesMap.containsKey(type),
"The duplicated type[" + type
+ "] of SentinelDataSource were found in "
+ "resource [" + resource.getURL() + "]");
Class<?> dataSourceClass = ClassUtils.resolveClassName(className,
classLoader);
Assert.isAssignable(ReadableDataSource.class, dataSourceClass);
dataSourcesMap.put(type,
(Class<? extends ReadableDataSource>)dataSourceClass);
if (logger.isDebugEnabled()) {
logger.debug("Sentinel DataSource implementation [ type : "
+ type + ": , class : " + className
+ "] was loaded.");
}
}
}
}
} catch (IOException e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
}
return dataSourcesMap;
}
public static Class<? extends ReadableDataSource> loadClass(String type)
throws IllegalArgumentException {
Class<? extends ReadableDataSource> dataSourceClass = dataSourceClassesCache.get(type);
if (dataSourceClass == null) {
if (dataSourceClassesCache.isEmpty()) {
loadAllDataSourceClassesCache();
dataSourceClass = dataSourceClassesCache.get(type);
}
}
if (dataSourceClass == null) {
throw new IllegalArgumentException(
"Sentinel DataSource implementation [ type : " + type
+ " ] can't be found!");
}
return dataSourceClass;
}
}

@ -0,0 +1,33 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.sentinel.datasource;
/**
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
*/
public interface SentinelDataSourceConstants {
String PROPERTY_PREFIX = "spring.cloud.sentinel";
String PROPERTY_ITEM_SEPARATOR = ".";
String PROPERTY_DATASOURCE_NAME = "datasource";
String PROPERTY_DATASOURCE_PREFIX = PROPERTY_PREFIX + PROPERTY_ITEM_SEPARATOR
+ PROPERTY_DATASOURCE_NAME;
}

@ -0,0 +1,272 @@
/*
* Copyright (C) 2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.sentinel.datasource;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.alibaba.sentinel.datasource.annotation.SentinelDataSource;
import org.springframework.cloud.alibaba.sentinel.datasource.util.PropertySourcesUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import static org.springframework.core.annotation.AnnotationUtils.getAnnotation;
/**
* {@link SentinelDataSource @SentinelDataSource} Post Processor
*
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see ReadableDataSource
* @see SentinelDataSource
*/
public class SentinelDataSourcePostProcessor
extends InstantiationAwareBeanPostProcessorAdapter
implements MergedBeanDefinitionPostProcessor {
private static final Logger logger = LoggerFactory
.getLogger(SentinelDataSourcePostProcessor.class);
@Autowired
private ApplicationContext applicationContext;
@Autowired
private ConfigurableEnvironment environment;
private final Map<String, List<SentinelDataSourceField>> dataSourceFieldCache = new ConcurrentHashMap<>(
64);
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition,
Class<?> beanType, String beanName) {
// find all fields using by @SentinelDataSource annotation
ReflectionUtils.doWithFields(beanType, new ReflectionUtils.FieldCallback() {
@Override
public void doWith(Field field)
throws IllegalArgumentException, IllegalAccessException {
SentinelDataSource annotation = getAnnotation(field,
SentinelDataSource.class);
if (annotation != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn(
"@SentinelDataSource annotation is not supported on static fields: "
+ field);
}
return;
}
if (dataSourceFieldCache.containsKey(beanName)) {
dataSourceFieldCache.get(beanName)
.add(new SentinelDataSourceField(annotation, field));
} else {
List<SentinelDataSourceField> list = new ArrayList<>();
list.add(new SentinelDataSourceField(annotation, field));
dataSourceFieldCache.put(beanName, list);
}
}
}
});
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs,
PropertyDescriptor[] pds, Object bean, String beanName)
throws BeanCreationException {
if (dataSourceFieldCache.containsKey(beanName)) {
List<SentinelDataSourceField> sentinelDataSourceFields = dataSourceFieldCache
.get(beanName);
sentinelDataSourceFields.forEach(sentinelDataSourceField -> {
try {
// construct DataSource field annotated by @SentinelDataSource
Field field = sentinelDataSourceField.getField();
ReflectionUtils.makeAccessible(field);
String dataSourceBeanName = constructDataSource(
sentinelDataSourceField.getSentinelDataSource());
field.set(bean, applicationContext.getBean(dataSourceBeanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
});
}
return pvs;
}
private String constructDataSource(SentinelDataSource annotation) {
String prefix = annotation.value();
if (StringUtils.isEmpty(prefix)) {
prefix = SentinelDataSourceConstants.PROPERTY_DATASOURCE_PREFIX;
}
Map<String, Object> propertyMap = PropertySourcesUtils
.getSubProperties(environment.getPropertySources(), prefix);
String alias = propertyMap.get("type").toString();
Class dataSourceClass = DataSourceLoader.loadClass(alias);
String beanName = StringUtils.isEmpty(annotation.name())
? StringUtils.uncapitalize(dataSourceClass.getSimpleName()) + "_" + prefix
: annotation.name();
if (applicationContext.containsBean(beanName)) {
return beanName;
}
Class targetClass = null;
// if alias exists in SentinelDataSourceRegistry, wired properties into
// FactoryBean
if (SentinelDataSourceRegistry.checkFactoryBean(alias)) {
targetClass = SentinelDataSourceRegistry.getFactoryBean(alias);
} else {
// if alias not exists in SentinelDataSourceRegistry, wired properties into
// raw class
targetClass = dataSourceClass;
}
registerDataSource(beanName, targetClass, propertyMap);
return beanName;
}
private void registerDataSource(String beanName, Class targetClass,
Map<String, Object> propertyMap) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(targetClass);
for (String propertyName : propertyMap.keySet()) {
Field field = ReflectionUtils.findField(targetClass, propertyName);
if (field != null) {
if (field.getType().isAssignableFrom(Converter.class)) {
// Converter get from ApplicationContext
builder.addPropertyReference(propertyName,
propertyMap.get(propertyName).toString());
} else {
// wired properties
builder.addPropertyValue(propertyName, propertyMap.get(propertyName));
}
}
}
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)applicationContext
.getAutowireCapableBeanFactory();
beanFactory.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
@EventListener(classes = ApplicationStartedEvent.class)
public void appStartedListener(ApplicationStartedEvent event) throws Exception {
logger.info("[Sentinel Starter] Start to find ReadableDataSource");
Map<String, ReadableDataSource> dataSourceMap = event.getApplicationContext().getBeansOfType(
ReadableDataSource.class);
if (dataSourceMap.size() == 1) {
logger.info("[Sentinel Starter] There exists only one ReadableDataSource named {}, start to load rules",
dataSourceMap.keySet().iterator().next());
ReadableDataSource dataSource = dataSourceMap.values().iterator().next();
Object ruleConfig = dataSource.loadConfig();
SentinelProperty sentinelProperty = dataSource.getProperty();
Integer rulesNum;
if ((rulesNum = checkRuleType(ruleConfig, FlowRule.class)) > 0) {
FlowRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} flow rules", rulesNum);
}
if ((rulesNum = checkRuleType(ruleConfig, DegradeRule.class)) > 0) {
DegradeRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} degrade rules", rulesNum);
}
if ((rulesNum = checkRuleType(ruleConfig, SystemRule.class)) > 0) {
SystemRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} system rules", rulesNum);
}
if ((rulesNum = checkRuleType(ruleConfig, AuthorityRule.class)) > 0) {
AuthorityRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} authority rules", rulesNum);
}
} else if (dataSourceMap.size() > 1) {
logger.warn(
"[Sentinel Starter] There exists more than one ReadableDataSource, can not choose which one to load");
} else {
logger.warn(
"[Sentinel Starter] No ReadableDataSource exists");
}
}
private Integer checkRuleType(Object ruleConfig, Class type) {
if (ruleConfig.getClass() == type) {
return 1;
} else if (ruleConfig instanceof List) {
List ruleList = (List)ruleConfig;
if (ruleList.stream().filter(rule -> rule.getClass() == type).toArray().length == ruleList.size()) {
return ruleList.size();
}
}
return -1;
}
class SentinelDataSourceField {
private SentinelDataSource sentinelDataSource;
private Field field;
public SentinelDataSourceField(SentinelDataSource sentinelDataSource,
Field field) {
this.sentinelDataSource = sentinelDataSource;
this.field = field;
}
public SentinelDataSource getSentinelDataSource() {
return sentinelDataSource;
}
public void setSentinelDataSource(SentinelDataSource sentinelDataSource) {
this.sentinelDataSource = sentinelDataSource;
}
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
}
}

@ -16,7 +16,9 @@
package org.springframework.cloud.alibaba.sentinel.datasource;
import java.util.concurrent.ConcurrentHashMap;
import java.util.HashMap;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.ApolloDataSourceFactoryBean;
@ -27,8 +29,8 @@ import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.Zookeep
/**
* Registry to save DataSource FactoryBean
*
* @author fangjian
* @see com.alibaba.csp.sentinel.datasource.DataSource
* @author <a href="mailto:fangjian0423@gmail.com">Jim</a>
* @see ReadableDataSource
* @see FileRefreshableDataSourceFactoryBean
* @see ZookeeperDataSourceFactoryBean
* @see NacosDataSourceFactoryBean
@ -36,32 +38,31 @@ import org.springframework.cloud.alibaba.sentinel.datasource.factorybean.Zookeep
*/
public class SentinelDataSourceRegistry {
private static ConcurrentHashMap<String, Class<? extends FactoryBean>> cache = new ConcurrentHashMap<>(
32);
private static HashMap<String, Class<? extends FactoryBean>> cache = new HashMap<>(
32);
static {
SentinelDataSourceRegistry.registerFactoryBean("file",
FileRefreshableDataSourceFactoryBean.class);
SentinelDataSourceRegistry.registerFactoryBean("zk",
ZookeeperDataSourceFactoryBean.class);
SentinelDataSourceRegistry.registerFactoryBean("nacos",
NacosDataSourceFactoryBean.class);
SentinelDataSourceRegistry.registerFactoryBean("apollo",
ApolloDataSourceFactoryBean.class);
}
static {
SentinelDataSourceRegistry.registerFactoryBean("file",
FileRefreshableDataSourceFactoryBean.class);
SentinelDataSourceRegistry.registerFactoryBean("zk",
ZookeeperDataSourceFactoryBean.class);
SentinelDataSourceRegistry.registerFactoryBean("nacos",
NacosDataSourceFactoryBean.class);
SentinelDataSourceRegistry.registerFactoryBean("apollo",
ApolloDataSourceFactoryBean.class);
}
public static synchronized void registerFactoryBean(String alias,
Class<? extends FactoryBean> clazz) {
cache.putIfAbsent(alias, clazz);
cache.put(alias, clazz);
}
public static synchronized void registerFactoryBean(String alias,
Class<? extends FactoryBean> clazz) {
cache.put(alias, clazz);
}
public static Class<? extends FactoryBean> getFactoryBean(String alias) {
return cache.get(alias);
}
public static Class<? extends FactoryBean> getFactoryBean(String alias) {
return cache.get(alias);
}
public static boolean checkFactoryBean(String alias) {
return cache.containsKey(alias);
}
public static boolean checkFactoryBean(String alias) {
return cache.containsKey(alias);
}
}

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

Loading…
Cancel
Save