1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00

Merge branch '1.x' of github.com:spring-cloud-incubator/spring-cloud-alibaba into 1.x

This commit is contained in:
xiaolongzuo 2018-10-30 14:51:03 +08:00
commit e2bacc4528
74 changed files with 2996 additions and 218 deletions

1
.gitignore vendored
View File

@ -28,4 +28,5 @@ hs_err_pid*
.project .project
.settings .settings
target target
.DS_Store

View File

@ -1,5 +1,7 @@
# Spring Cloud Alibaba # 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. 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. 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.

View File

@ -15,7 +15,7 @@ Spring Cloud Alibaba 致力于提供分布式应用服务开发的一站式解
**Nacos** **Nacos**
阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
**Aliyun OSS** **AliCloud OSS**
阿里云对象存储服务Object Storage Service简称 OSS是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。 阿里云对象存储服务Object Storage Service简称 OSS是阿里云提供的海量、安全、低成本、高可靠的云存储服务。您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
## 即将加入的组件 ## 即将加入的组件

81
pom.xml
View File

@ -8,7 +8,7 @@
<parent> <parent>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId> <artifactId>spring-cloud-build</artifactId>
<version>1.3.10.RELEASE</version> <version>1.3.11.RELEASE</version>
<relativePath/> <relativePath/>
</parent> </parent>
@ -28,12 +28,12 @@
</licenses> </licenses>
<scm> <scm>
<url>https://github.com/spring-cloud-incubator/spring-cloud-alibabacloud</url> <url>https://github.com/spring-cloud-incubator/spring-cloud-alibaba</url>
<connection> <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> </connection>
<developerConnection> <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> </developerConnection>
<tag>HEAD</tag> <tag>HEAD</tag>
</scm> </scm>
@ -47,6 +47,10 @@
<name>fangjian</name> <name>fangjian</name>
<email>fangjian0423@gmail.com</email> <email>fangjian0423@gmail.com</email>
</developer> </developer>
<developer>
<name>xiaolongzuo</name>
<email>150349407@qq.com</email>
</developer>
<developer> <developer>
<name>hengyunabc</name> <name>hengyunabc</name>
<email>hengyunabc@gmail.com</email> <email>hengyunabc@gmail.com</email>
@ -55,7 +59,7 @@
<properties> <properties>
<!-- Dependency Versions --> <!-- Dependency Versions -->
<spring-cloud-commons.version>1.3.3.RELEASE</spring-cloud-commons.version> <spring-cloud-commons.version>1.3.5.RELEASE</spring-cloud-commons.version>
<spring-cloud-netflix.version>1.4.4.RELEASE</spring-cloud-netflix.version> <spring-cloud-netflix.version>1.4.4.RELEASE</spring-cloud-netflix.version>
<junit.version>4.12</junit.version> <junit.version>4.12</junit.version>
@ -82,6 +86,8 @@
<module>spring-cloud-starter-alicloud</module> <module>spring-cloud-starter-alicloud</module>
<module>spring-cloud-alicloud-oss</module> <module>spring-cloud-alicloud-oss</module>
<module>spring-cloud-alicloud-context</module> <module>spring-cloud-alicloud-context</module>
<module>spring-cloud-alicloud-acm</module>
<module>spring-cloud-alicloud-ans</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>
@ -138,4 +144,69 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </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> </project>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>spring-cloud-dependencies-parent</artifactId> <artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<version>1.3.10.RELEASE</version> <version>1.3.11.RELEASE</version>
<relativePath/> <relativePath/>
</parent> </parent>
<artifactId>spring-cloud-alibaba-dependencies</artifactId> <artifactId>spring-cloud-alibaba-dependencies</artifactId>
@ -117,7 +117,7 @@
</dependency> </dependency>
<!-- Aliyun storage dependencies --> <!-- Aliyun OSS dependencies -->
<dependency> <dependency>
<groupId>com.aliyun.oss</groupId> <groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId> <artifactId>aliyun-sdk-oss</artifactId>
@ -125,7 +125,7 @@
</dependency> </dependency>
<!-- Own dependencies autoconfigure --> <!-- Own dependencies -->
<dependency> <dependency>
<groupId>org.springframework.cloud</groupId> <groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel</artifactId> <artifactId>spring-cloud-alibaba-sentinel</artifactId>
@ -151,6 +151,21 @@
<artifactId>spring-cloud-alibaba-nacos-config</artifactId> <artifactId>spring-cloud-alibaba-nacos-config</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </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 --> <!-- Own dependencies - Starters -->
<dependency> <dependency>
@ -176,7 +191,17 @@
<version>${project.version}</version> <version>${project.version}</version>
</dependency> </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 --> <!-- Testing Dependencies -->

View File

@ -0,0 +1,27 @@
<?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.1.0.BUILD-SNAPSHOT</version>
</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>

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,5 @@
spring.application.group=com.alibaba.acm
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

View File

@ -0,0 +1,31 @@
<?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.1.0.BUILD-SNAPSHOT</version>
</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-feign</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>

View File

@ -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.netflix.feign.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);
}
}

View File

@ -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.netflix.feign.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);
}

View File

@ -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;
/**
* @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;
}
}

View File

@ -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

View File

@ -0,0 +1,27 @@
<?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.1.0.BUILD-SNAPSHOT</version>
</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>

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -0,0 +1,27 @@
<?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.1.0.BUILD-SNAPSHOT</version>
</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>

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -19,8 +19,9 @@
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency> </dependency>
2. 在应用的 /src/main/resources/bootstrap.properties 配置文件中配置 Nacos Config 地址 2. 在应用的 /src/main/resources/bootstrap.properties 配置文件中配置 Nacos Config 元数据
spring.application.name=nacos-config-example
spring.cloud.nacos.config.server-addr=127.0.0.1:8848 spring.cloud.nacos.config.server-addr=127.0.0.1:8848
3. 完成上述两步后,应用会从 Nacos Config 中获取相应的配置,并添加在 Spring Environment 的 PropertySources 中。这里我们使用 @Value 注解来将对应的配置注入到 SampleController 的 userName 和 age 字段,并添加 @RefreshScope 打开动态刷新功能 3. 完成上述两步后,应用会从 Nacos Config 中获取相应的配置,并添加在 Spring Environment 的 PropertySources 中。这里我们使用 @Value 注解来将对应的配置注入到 SampleController 的 userName 和 age 字段,并添加 @RefreshScope 打开动态刷新功能
@ -70,8 +71,8 @@
1. 增加配置,在应用的 /src/main/resources/application.properties 中添加基本配置信息 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 直接启动和编译打包后启动。 2. 启动应用,支持 IDE 直接启动和编译打包后启动。

View File

@ -19,8 +19,9 @@ 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> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency> </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.application.name=nacos-config-example
spring.cloud.nacos.config.server-addr=127.0.0.1:8848 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 . 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 .
@ -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 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. 2. Start the application in IDE or by building a fatjar.

View File

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

View File

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

View File

@ -23,6 +23,10 @@
<module>nacos-example/nacos-discovery-example</module> <module>nacos-example/nacos-discovery-example</module>
<module>nacos-example/nacos-config-example</module> <module>nacos-example/nacos-config-example</module>
<module>oss-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> </modules>
<build> <build>

View File

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

View File

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

View File

@ -16,9 +16,30 @@
package org.springframework.cloud.alibaba.nacos; 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.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment; 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 * nacos properties
@ -29,6 +50,9 @@ import org.springframework.util.StringUtils;
@ConfigurationProperties("spring.cloud.nacos.config") @ConfigurationProperties("spring.cloud.nacos.config")
public class NacosConfigProperties { public class NacosConfigProperties {
private static final Logger LOGGER = LoggerFactory
.getLogger(NacosConfigProperties.class);
/** /**
* nacos config server address * nacos config server address
*/ */
@ -89,6 +113,21 @@ public class NacosConfigProperties {
*/ */
private String clusterName; 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 // todo sts support
public String getServerAddr() { public String getServerAddr() {
@ -187,6 +226,14 @@ public class NacosConfigProperties {
this.clusterName = clusterName; this.clusterName = clusterName;
} }
public String getName() {
return name;
}
public String[] getActiveProfiles() {
return activeProfiles;
}
@Override @Override
public String toString() { public String toString() {
return "NacosConfigProperties{" + "serverAddr='" + serverAddr + '\'' return "NacosConfigProperties{" + "serverAddr='" + serverAddr + '\''
@ -195,46 +242,32 @@ public class NacosConfigProperties {
+ ", timeout=" + timeout + ", endpoint='" + endpoint + '\'' + ", timeout=" + timeout + ", endpoint='" + endpoint + '\''
+ ", namespace='" + namespace + '\'' + ", accessKey='" + accessKey + '\'' + ", namespace='" + namespace + '\'' + ", accessKey='" + accessKey + '\''
+ ", secretKey='" + secretKey + '\'' + ", contextPath='" + contextPath + ", 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())) { if (null != configService) {
this.setServerAddr( return configService;
env.resolvePlaceholders("${spring.cloud.nacos.config.server-addr:}"));
} }
if (StringUtils.isEmpty(this.getEncode())) {
this.setEncode( Properties properties = new Properties();
env.resolvePlaceholders("${spring.cloud.nacos.config.encode:}")); 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.getNamespace())) { catch (Exception e) {
this.setNamespace( LOGGER.error("create config service error!properties={},e=,", this, e);
env.resolvePlaceholders("${spring.cloud.nacos.config.namespace:}")); return null;
}
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:}"));
}
if (StringUtils.isEmpty(this.getPrefix())) {
this.setPrefix(
env.resolvePlaceholders("${spring.cloud.nacos.config.prefix:}"));
} }
} }
} }

View File

@ -83,7 +83,6 @@ public class NacosPropertySourceBuilder {
String data = null; String data = null;
try { try {
data = configService.getConfig(dataId, group, timeout); data = configService.getConfig(dataId, group, timeout);
// todo add file extension yaml support
if (!StringUtils.isEmpty(data)) { if (!StringUtils.isEmpty(data)) {
logger.info(String.format("Loading nacos data, dataId: '%s', group: '%s'", logger.info(String.format("Loading nacos data, dataId: '%s', group: '%s'",
dataId, group)); dataId, group));

View File

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

View File

@ -44,15 +44,6 @@ public class NacosConfigEndpointAutoConfiguration {
@Autowired @Autowired
private NacosPropertySourceRepository nacosPropertySourceRepository; private NacosPropertySourceRepository nacosPropertySourceRepository;
@Autowired
private ConfigService configService;
@Bean
@ConditionalOnBean
public NacosConfigProperties nacosConfigProperties() {
return new NacosConfigProperties();
}
@ConditionalOnMissingBean @ConditionalOnMissingBean
@Bean @Bean
public NacosConfigEndpoint nacosConfigEndpoint() { public NacosConfigEndpoint nacosConfigEndpoint() {
@ -64,6 +55,7 @@ public class NacosConfigEndpointAutoConfiguration {
public NacosConfigHealthIndicator nacosConfigHealthIndicator( public NacosConfigHealthIndicator nacosConfigHealthIndicator(
NacosPropertySourceRepository nacosPropertySourceRepository) { NacosPropertySourceRepository nacosPropertySourceRepository) {
return new NacosConfigHealthIndicator(nacosConfigProperties, return new NacosConfigHealthIndicator(nacosConfigProperties,
nacosPropertySourceRepository, configService); nacosPropertySourceRepository,
nacosConfigProperties.configServiceInstance());
} }
} }

View File

@ -44,12 +44,11 @@ public class NacosConfigAutoConfigurationTests {
public void setUp() throws Exception { public void setUp() throws Exception {
this.context = new SpringApplicationBuilder( this.context = new SpringApplicationBuilder(
NacosConfigBootstrapConfiguration.class, NacosConfigBootstrapConfiguration.class,
NacosConfigAutoConfiguration.class, NacosConfigAutoConfiguration.class, TestConfiguration.class).web(false)
TestConfiguration.class) .run("--spring.application.name=myapp",
.web(false).run(
"--spring.cloud.config.enabled=true", "--spring.cloud.config.enabled=true",
"--spring.cloud.nacos.config.server-addr=127.0.0.1:8080", "--spring.cloud.nacos.config.server-addr=127.0.0.1:8080",
"--spring.cloud.nacos.config.prefix=myapp"); "--spring.cloud.nacos.config.prefix=test");
} }
@After @After
@ -61,40 +60,32 @@ public class NacosConfigAutoConfigurationTests {
@Test @Test
public void testNacosConfigProperties() { public void testNacosConfigProperties() {
NacosConfigProperties nacosConfigProperties = this.context.getParent()
NacosPropertySourceLocator nacosPropertySourceLocator = this.context.getBean(NacosPropertySourceLocator.class); .getBean(NacosConfigProperties.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.getFileExtension()).isEqualTo("properties");
assertThat(nacosConfigProperties.getPrefix()).isEqualTo("myapp"); assertThat(nacosConfigProperties.getPrefix()).isEqualTo("test");
assertThat(nacosConfigProperties.getName()).isEqualTo("myapp");
} }
@Test @Test
public void testNacosRefreshProperties() { public void testNacosRefreshProperties() {
NacosRefreshProperties nacosRefreshProperties = this.context.getBean(NacosRefreshProperties.class); NacosRefreshProperties nacosRefreshProperties = this.context
.getBean(NacosRefreshProperties.class);
assertThat(nacosRefreshProperties.isEnabled()).isEqualTo(true); assertThat(nacosRefreshProperties.isEnabled()).isEqualTo(true);
} }
@Configuration @Configuration
@AutoConfigureBefore(NacosConfigAutoConfiguration.class) @AutoConfigureBefore(NacosConfigAutoConfiguration.class)
static class TestConfiguration{ static class TestConfiguration {
@Autowired @Autowired
ConfigurableApplicationContext context; ConfigurableApplicationContext context;
@Bean @Bean
ContextRefresher contextRefresher(){ ContextRefresher contextRefresher() {
return new ContextRefresher(context, new RefreshScope()); return new ContextRefresher(context, new RefreshScope());
} }

View File

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

View File

@ -195,30 +195,44 @@ public class SentinelDataSourcePostProcessor
@EventListener(classes = ApplicationReadyEvent.class) @EventListener(classes = ApplicationReadyEvent.class)
public void appStartedListener(ApplicationReadyEvent event) throws Exception { public void appStartedListener(ApplicationReadyEvent event) throws Exception {
logger.info("[Sentinel Starter] Start to find ReadableDataSource");
Map<String, ReadableDataSource> dataSourceMap = event.getApplicationContext().getBeansOfType( Map<String, ReadableDataSource> dataSourceMap = event.getApplicationContext().getBeansOfType(
ReadableDataSource.class); ReadableDataSource.class);
if (dataSourceMap.size() == 1) { 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(); ReadableDataSource dataSource = dataSourceMap.values().iterator().next();
Object ruleConfig = dataSource.loadConfig(); Object ruleConfig = dataSource.loadConfig();
SentinelProperty sentinelProperty = dataSource.getProperty(); SentinelProperty sentinelProperty = dataSource.getProperty();
if (checkRuleType(ruleConfig, FlowRule.class)) { Integer rulesNum;
if ((rulesNum = checkRuleType(ruleConfig, FlowRule.class)) > 0) {
FlowRuleManager.register2Property(sentinelProperty); FlowRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} flow rules", rulesNum);
} }
if (checkRuleType(ruleConfig, DegradeRule.class)) { if ((rulesNum = checkRuleType(ruleConfig, DegradeRule.class)) > 0) {
DegradeRuleManager.register2Property(sentinelProperty); DegradeRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} degrade rules", rulesNum);
} }
if (checkRuleType(ruleConfig, SystemRule.class)) { if ((rulesNum = checkRuleType(ruleConfig, SystemRule.class)) > 0) {
SystemRuleManager.register2Property(sentinelProperty); SystemRuleManager.register2Property(sentinelProperty);
logger.info("[Sentinel Starter] load {} system rules", rulesNum);
} }
if (checkRuleType(ruleConfig, AuthorityRule.class)) { if ((rulesNum = checkRuleType(ruleConfig, AuthorityRule.class)) > 0) {
AuthorityRuleManager.register2Property(sentinelProperty); 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 boolean checkRuleType(Object ruleConfig, Class type) { private Integer checkRuleType(Object ruleConfig, Class type) {
if (ruleConfig.getClass() == type) { if (ruleConfig.getClass() == type) {
return true; return 1;
} else if (ruleConfig instanceof List) { } else if (ruleConfig instanceof List) {
List ruleList = (List)ruleConfig; List ruleList = (List)ruleConfig;
List checkList = new ArrayList(); List checkList = new ArrayList();
@ -228,10 +242,10 @@ public class SentinelDataSourcePostProcessor
} }
} }
if (ruleList.size() == checkList.size()) { if (ruleList.size() == checkList.size()) {
return true; return ruleList.size();
} }
} }
return false; return -1;
} }
class SentinelDataSourceField { class SentinelDataSourceField {

View File

@ -0,0 +1,71 @@
<?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>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alibaba</artifactId>
<version>0.1.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-alicloud-acm</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-context</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-edas</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.edas.acm</groupId>
<artifactId>acm-sdk</artifactId>
</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-actuator</artifactId>
<optional>true</optional>
</dependency>
<!-- Spring Cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,73 @@
/*
* 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.alicloud.acm;
import com.taobao.diamond.client.Diamond;
import org.springframework.beans.BeansException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alicloud.acm.endpoint.AcmHealthIndicator;
import org.springframework.cloud.alicloud.acm.refresh.AcmContextRefresher;
import org.springframework.cloud.alicloud.acm.refresh.AcmRefreshHistory;
import org.springframework.cloud.alicloud.context.acm.AcmProperties;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created on 01/10/2017.
*
* @author juven.xuxb
*/
@Configuration
@ConditionalOnClass({Diamond.class})
@EnableConfigurationProperties(AcmProperties.class)
public class AcmAutoConfiguration implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Bean
public AcmPropertySourceRepository acmPropertySourceRepository() {
return new AcmPropertySourceRepository(applicationContext);
}
@Bean
public AcmHealthIndicator acmHealthIndicator(AcmProperties acmProperties,
AcmPropertySourceRepository acmPropertySourceRepository) {
return new AcmHealthIndicator(acmProperties, acmPropertySourceRepository);
}
@Bean
public AcmRefreshHistory acmRefreshHistory() {
return new AcmRefreshHistory();
}
@Bean
public AcmContextRefresher acmContextRefresher(AcmProperties acmProperties, ContextRefresher contextRefresher,
AcmRefreshHistory refreshHistory,
AcmPropertySourceRepository propertySourceRepository) {
return new AcmContextRefresher(contextRefresher, acmProperties, refreshHistory, propertySourceRepository);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.alicloud.acm;
import org.springframework.cloud.alicloud.acm.bootstrap.AcmPropertySource;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.PropertySource;
import java.util.ArrayList;
import java.util.List;
/**
* @author juven.xuxb, 5/17/16.
*/
public class AcmPropertySourceRepository {
private final ApplicationContext applicationContext;
public AcmPropertySourceRepository(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* get all acm properties from application context
* @return
*/
public List<AcmPropertySource> getAll() {
List<AcmPropertySource> result = new ArrayList<>();
ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) applicationContext;
for (PropertySource p : ctx.getEnvironment().getPropertySources()) {
if (p instanceof AcmPropertySource) {
result.add((AcmPropertySource) p);
}
else if (p instanceof CompositePropertySource) {
collectAcmPropertySources((CompositePropertySource) p, result);
}
}
return result;
}
private void collectAcmPropertySources(CompositePropertySource composite,
List<AcmPropertySource> result) {
for (PropertySource p : composite.getPropertySources()) {
if (p instanceof AcmPropertySource) {
result.add((AcmPropertySource) p);
}
else if (p instanceof CompositePropertySource) {
collectAcmPropertySources((CompositePropertySource) p, result);
}
}
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.alicloud.acm.bootstrap;
import org.springframework.core.env.MapPropertySource;
import java.util.Date;
import java.util.Map;
/**
* @author juven.xuxb
* @author xiaolongzuo
*/
public class AcmPropertySource extends MapPropertySource {
private final String dataId;
private final Date timestamp;
private final boolean groupLevel;
AcmPropertySource(String dataId, Map<String, Object> source, Date timestamp,
boolean groupLevel) {
super(dataId, source);
this.dataId = dataId;
this.timestamp = timestamp;
this.groupLevel = groupLevel;
}
public String getDataId() {
return dataId;
}
public Date getTimestamp() {
return timestamp;
}
public boolean isGroupLevel() {
return groupLevel;
}
}

View File

@ -0,0 +1,96 @@
/*
* 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.alicloud.acm.bootstrap;
import com.alibaba.edas.acm.ConfigService;
import com.alibaba.edas.acm.exception.ConfigException;
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;
import java.io.StringReader;
import java.util.*;
/**
* @author juven.xuxb
* @author xiaolongzuo
*/
class AcmPropertySourceBuilder {
private Logger logger = LoggerFactory.getLogger(AcmPropertySourceBuilder.class);
/**
* 传入 ACM DataId groupID获取到解析后的 AcmProperty 对象
*
* @param dataId
* @param diamondGroup
* @param groupLevel
* @return
*/
AcmPropertySource build(String dataId, String diamondGroup, boolean groupLevel) {
Properties properties = loadDiamondData(dataId, diamondGroup);
if (properties == null) {
return null;
}
return new AcmPropertySource(dataId, toMap(properties), new Date(), groupLevel);
}
private Properties loadDiamondData(String dataId, String diamondGroup) {
try {
String data = ConfigService.getConfig(dataId, diamondGroup, 3000L);
if (StringUtils.isEmpty(data)) {
return null;
}
if (dataId.endsWith(".properties")) {
Properties properties = new Properties();
logger.info(String.format("Loading acm data, dataId: '%s', group: '%s'",
dataId, diamondGroup));
properties.load(new StringReader(data));
return properties;
} else if (dataId.endsWith(".yaml") || dataId.endsWith(".yml")) {
YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
yamlFactory.setResources(new ByteArrayResource(data.getBytes()));
return yamlFactory.getObject();
}
} catch (Exception e) {
if (e instanceof ConfigException) {
logger.error("DIAMOND-100500:" + dataId + ", " + e.toString(), e);
} else {
logger.error("DIAMOND-100500:" + dataId, e);
}
}
return null;
}
@SuppressWarnings("unchecked")
private Map<String, Object> toMap(Properties properties) {
Map<String, Object> result = new HashMap<>();
Enumeration<String> keys = (Enumeration<String>)properties.propertyNames();
while (keys.hasMoreElements()) {
String key = keys.nextElement();
Object value = properties.getProperty(key);
if (value != null) {
result.put(key, ((String)value).trim());
} else {
result.put(key, null);
}
}
return result;
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.alicloud.acm.bootstrap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alicloud.context.acm.AcmIntegrationProperties;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
/**
* @author juven.xuxb
* @author xiaolongzuo
*/
public class AcmPropertySourceLocator implements PropertySourceLocator {
private static final String DIAMOND_PROPERTY_SOURCE_NAME = "diamond";
private static String defaultDiamondGroup = "DEFAULT_GROUP";
private AcmPropertySourceBuilder acmPropertySourceBuilder = new AcmPropertySourceBuilder();
@Autowired
private AcmIntegrationProperties acmIntegrationProperties;
@Override
public PropertySource<?> locate(Environment environment) {
CompositePropertySource compositePropertySource = new CompositePropertySource(
DIAMOND_PROPERTY_SOURCE_NAME);
for (String dataId : acmIntegrationProperties.getGroupConfigurationDataIds()) {
loadDiamondDataIfPresent(compositePropertySource, dataId, defaultDiamondGroup,
true);
}
for (String dataId : acmIntegrationProperties
.getApplicationConfigurationDataIds()) {
loadDiamondDataIfPresent(compositePropertySource, dataId, defaultDiamondGroup,
false);
}
return compositePropertySource;
}
private void loadDiamondDataIfPresent(final CompositePropertySource composite,
final String dataId, final String diamondGroup, final boolean groupLevel) {
AcmPropertySource ps = acmPropertySourceBuilder.build(dataId, diamondGroup,
groupLevel);
if (ps != null) {
composite.addFirstPropertySource(ps);
}
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.alicloud.acm.diagnostics.analyzer;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
/**
* A {@code FailureAnalyzer} that performs analysis of failures caused by a
* {@code DiamondConnectionFailureException}.
*
* @author juven.xuxb, 07/11/2016.
*/
public class DiamondConnectionFailureAnalyzer
extends AbstractFailureAnalyzer<DiamondConnectionFailureException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure,
DiamondConnectionFailureException cause) {
return new FailureAnalysis(
"Application failed to connect to Diamond, unable to access http://"
+ cause.getDomain() + ":" + cause.getPort()
+ "/diamond-server/diamond",
"config the right endpoint", cause);
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.alicloud.acm.diagnostics.analyzer;
/**
* A {@code DiamondConnectionFailureException} is thrown when the application fails to
* connect to Diamond Server.
*
* @author juven.xuxb, 07/11/2016.
*/
public class DiamondConnectionFailureException extends RuntimeException {
private final String domain;
private final String port;
public DiamondConnectionFailureException(String domain, String port, String message) {
super(message);
this.domain = domain;
this.port = port;
}
public DiamondConnectionFailureException(String domain, String port, String message,
Throwable cause) {
super(message, cause);
this.domain = domain;
this.port = port;
}
String getDomain() {
return domain;
}
String getPort() {
return port;
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.alicloud.acm.endpoint;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.cloud.alicloud.acm.AcmPropertySourceRepository;
import org.springframework.cloud.alicloud.acm.bootstrap.AcmPropertySource;
import org.springframework.cloud.alicloud.acm.refresh.AcmRefreshHistory;
import org.springframework.cloud.alicloud.context.acm.AcmProperties;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Created on 01/10/2017.
*
* @author juven.xuxb
*/
public class AcmEndpoint extends AbstractEndpoint<Map<String, Object>> {
private final AcmProperties properties;
private final AcmRefreshHistory refreshHistory;
private final AcmPropertySourceRepository propertySourceRepository;
private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public AcmEndpoint(AcmProperties properties, AcmRefreshHistory refreshHistory,
AcmPropertySourceRepository propertySourceRepository) {
super("acm", false);
this.properties = properties;
this.refreshHistory = refreshHistory;
this.propertySourceRepository = propertySourceRepository;
}
@Override
public Map<String, Object> invoke() {
Map<String, Object> result = new HashMap<>();
result.put("config", properties);
Map<String, Object> runtime = new HashMap<>();
List<AcmPropertySource> all = propertySourceRepository.getAll();
List<Map<String, Object>> sources = new ArrayList<>();
for (AcmPropertySource ps : all) {
Map<String, Object> source = new HashMap<>();
source.put("dataId", ps.getDataId());
source.put("lastSynced", dateFormat.format(ps.getTimestamp()));
sources.add(source);
}
runtime.put("sources", sources);
runtime.put("refreshHistory", refreshHistory.getRecords());
result.put("runtime", runtime);
return result;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.alicloud.acm.endpoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.cloud.alicloud.acm.AcmPropertySourceRepository;
import org.springframework.cloud.alicloud.acm.refresh.AcmRefreshHistory;
import org.springframework.cloud.alicloud.context.acm.AcmProperties;
import org.springframework.context.annotation.Bean;
/**
* @author xiaojing
*/
@ConditionalOnWebApplication
@ConditionalOnClass(name = "org.springframework.boot.actuate.endpoint.AbstractEndpoint")
public class AcmEndpointAutoConfiguration {
@Autowired
private AcmProperties acmProperties;
@Autowired
private AcmRefreshHistory acmRefreshHistory;
@Autowired
private AcmPropertySourceRepository acmPropertySourceRepository;
@ConditionalOnMissingBean
@Bean
public AcmEndpoint acmEndpoint() {
return new AcmEndpoint(acmProperties, acmRefreshHistory,
acmPropertySourceRepository);
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.alicloud.acm.endpoint;
import com.alibaba.edas.acm.ConfigService;
import org.springframework.boot.actuate.health.AbstractHealthIndicator;
import org.springframework.boot.actuate.health.Health;
import org.springframework.cloud.alicloud.acm.AcmPropertySourceRepository;
import org.springframework.cloud.alicloud.acm.bootstrap.AcmPropertySource;
import org.springframework.cloud.alicloud.context.acm.AcmProperties;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
/**
* @author leijuan
* @author juven
*/
public class AcmHealthIndicator extends AbstractHealthIndicator {
private final AcmProperties acmProperties;
private final AcmPropertySourceRepository acmPropertySourceRepository;
private final List<String> dataIds;
public AcmHealthIndicator(AcmProperties acmProperties,
AcmPropertySourceRepository acmPropertySourceRepository) {
this.acmProperties = acmProperties;
this.acmPropertySourceRepository = acmPropertySourceRepository;
this.dataIds = new ArrayList<>();
for (AcmPropertySource acmPropertySource : this.acmPropertySourceRepository
.getAll()) {
this.dataIds.add(acmPropertySource.getDataId());
}
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
for (String dataId : dataIds) {
try {
String config = ConfigService.getConfig(dataId, acmProperties.getGroup(),
acmProperties.getTimeout());
if (StringUtils.isEmpty(config)) {
builder.down().withDetail(String.format("dataId: '%s', group: '%s'",
dataId, acmProperties.getGroup()), "config is empty");
}
} catch (Exception e) {
builder.down().withDetail(String.format("dataId: '%s', group: '%s'",
dataId, acmProperties.getGroup()), e.getMessage());
}
}
builder.up().withDetail("dataIds", dataIds);
}
}

View File

@ -0,0 +1,127 @@
/*
* 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.alicloud.acm.refresh;
import com.alibaba.edas.acm.ConfigService;
import com.alibaba.edas.acm.listener.ConfigChangeListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.alicloud.acm.AcmPropertySourceRepository;
import org.springframework.cloud.alicloud.acm.bootstrap.AcmPropertySource;
import org.springframework.cloud.alicloud.context.acm.AcmProperties;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.context.ApplicationListener;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* On application start up, AcmContextRefresher add diamond listeners to all application
* level dataIds, when there is a change in the data, listeners will refresh
* configurations.
*
* @author juven.xuxb, 5/13/16.
*/
public class AcmContextRefresher implements ApplicationListener<ApplicationReadyEvent> {
private Logger logger = LoggerFactory.getLogger(AcmContextRefresher.class);
private final ContextRefresher contextRefresher;
private final AcmProperties properties;
private final AcmRefreshHistory refreshHistory;
private final AcmPropertySourceRepository acmPropertySourceRepository;
private Map<String, ConfigChangeListener> listenerMap = new ConcurrentHashMap<>(16);
@Autowired
private Environment environment;
public AcmContextRefresher(ContextRefresher contextRefresher,
AcmProperties properties, AcmRefreshHistory refreshHistory,
AcmPropertySourceRepository acmPropertySourceRepository) {
this.contextRefresher = contextRefresher;
this.properties = properties;
this.refreshHistory = refreshHistory;
this.acmPropertySourceRepository = acmPropertySourceRepository;
}
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
this.registerDiamondListenersForApplications();
}
private void registerDiamondListenersForApplications() {
if (properties.isRefreshEnabled()) {
for (AcmPropertySource acmPropertySource : acmPropertySourceRepository
.getAll()) {
if (acmPropertySource.isGroupLevel()) {
continue;
}
String dataId = acmPropertySource.getDataId();
registerDiamondListener(dataId);
}
if (acmPropertySourceRepository.getAll().isEmpty()) {
String applicationName = environment
.getProperty("spring.application.name");
String dataId = applicationName + "." + properties.getFileExtension();
registerDiamondListener(dataId);
}
}
}
private void registerDiamondListener(final String dataId) {
ConfigChangeListener listener = listenerMap.get(dataId);
if (listener == null) {
listener = new ConfigChangeListener() {
@Override
public void receiveConfigInfo(String configInfo) {
String md5 = "";
if (!StringUtils.isEmpty(configInfo)) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md5 = new BigInteger(1,
md.digest(configInfo.getBytes("UTF-8"))).toString(16);
}
catch (NoSuchAlgorithmException
| UnsupportedEncodingException e) {
logger.warn("unable to get md5 for dataId: " + dataId, e);
}
}
refreshHistory.add(dataId, md5);
contextRefresher.refresh();
}
};
listenerMap.put(dataId, listener);
}
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.alicloud.acm.refresh;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
/**
* @author juven.xuxb, 5/16/16.
*/
public class AcmRefreshHistory {
private static final int MAX_SIZE = 20;
private LinkedList<Record> records = new LinkedList<>();
private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void add(String dataId, String md5) {
records.addFirst(new Record(dateFormat.format(new Date()), dataId, md5));
if (records.size() > MAX_SIZE) {
records.removeLast();
}
}
public LinkedList<Record> getRecords() {
return records;
}
}
class Record {
private final String timestamp;
private final String dataId;
private final String md5;
public Record(String timestamp, String dataId, String md5) {
this.timestamp = timestamp;
this.dataId = dataId;
this.md5 = md5;
}
public String getTimestamp() {
return timestamp;
}
public String getDataId() {
return dataId;
}
public String getMd5() {
return md5;
}
}

View File

@ -0,0 +1,9 @@
{
"properties": [
{
"name": "spring.application.group",
"type": "java.lang.String",
"description": "spring application group."
}
]
}

View File

@ -0,0 +1,9 @@
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.alicloud.acm.bootstrap.AcmPropertySourceLocator
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alicloud.acm.AcmAutoConfiguration,\
org.springframework.cloud.alicloud.acm.endpoint.AcmEndpointAutoConfiguration
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.cloud.alicloud.acm.diagnostics.analyzer.DiamondConnectionFailureAnalyzer

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-cloud-alibaba</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>0.1.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-ans</artifactId>
<name>Spring Cloud Alibaba Cloud ANS</name>
<dependencies>
<dependency>
<groupId>com.alibaba.ans</groupId>
<artifactId>ans-sdk</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-edas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,66 @@
/*
* 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.alicloud.ans;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alicloud.ans.registry.AnsAutoServiceRegistration;
import org.springframework.cloud.alicloud.ans.registry.AnsRegistration;
import org.springframework.cloud.alicloud.ans.registry.AnsServiceRegistry;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author xiaolongzuo
*/
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(name = "org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent")
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@ConditionalOnAnsEnabled
@AutoConfigureBefore({ AutoServiceRegistrationAutoConfiguration.class,
AnsDiscoveryClientAutoConfiguration.class })
public class AnsAutoConfiguration {
@Bean
public AnsServiceRegistry ansServiceRegistry() {
return new AnsServiceRegistry();
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public AnsRegistration ansRegistration() {
return new AnsRegistration();
}
@Bean
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
public AnsAutoServiceRegistration ansAutoServiceRegistration(
AnsServiceRegistry registry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
AnsRegistration registration) {
return new AnsAutoServiceRegistration(registry, autoServiceRegistrationProperties,
registration);
}
}

View File

@ -0,0 +1,103 @@
/*
* 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.alicloud.ans;
import java.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alicloud.context.ans.AnsProperties;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
/**
* @author xiaolongzuo
*/
public class AnsDiscoveryClient implements DiscoveryClient {
public static final String DESCRIPTION = "Spring Cloud ANS Discovery Client";
@Autowired
private AnsProperties ansProperties;
@Override
public String description() {
return DESCRIPTION;
}
@Override
public ServiceInstance getLocalServiceInstance() {
String serviceId = ansProperties.getClientDomains();
String host = ansProperties.getClientIp();
int port = ansProperties.getClientPort();
boolean secure = ansProperties.isSecure();
Map<String, String> metadata = ansProperties.getClientMetadata();
return new DefaultServiceInstance(serviceId, host, port, secure, metadata);
}
@Override
public List<ServiceInstance> getInstances(String serviceId) {
try {
List<Host> hosts = NamingService.getHosts(serviceId);
return hostToServiceInstanceList(hosts, serviceId);
}
catch (Exception e) {
throw new RuntimeException(
"Can not get hosts from ans server. serviceId: " + serviceId, e);
}
}
private static ServiceInstance hostToServiceInstance(Host host, String serviceId) {
AnsServiceInstance ansServiceInstance = new AnsServiceInstance();
ansServiceInstance.setHost(host.getIp());
ansServiceInstance.setPort(host.getPort());
ansServiceInstance.setServiceId(serviceId);
Map<String, String> metadata = new HashMap<String, String>(5);
metadata.put("appUseType", host.getAppUseType());
metadata.put("site", host.getSite());
metadata.put("unit", host.getUnit());
metadata.put("doubleWeight", "" + host.getDoubleWeight());
metadata.put("weight", "" + host.getWeight());
ansServiceInstance.setMetadata(metadata);
return ansServiceInstance;
}
private static List<ServiceInstance> hostToServiceInstanceList(List<Host> hosts,
String serviceId) {
List<ServiceInstance> result = new ArrayList<ServiceInstance>(hosts.size());
for (Host host : hosts) {
result.add(hostToServiceInstance(host, serviceId));
}
return result;
}
@Override
public List<String> getServices() {
Set<String> doms = NamingService.getDomsSubscribed();
List<String> result = new LinkedList<>();
for (String service : doms) {
result.add(service);
}
return result;
}
}

View File

@ -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.alicloud.ans;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author xiaolongzuo
*/
@Configuration
@ConditionalOnMissingBean(DiscoveryClient.class)
@EnableConfigurationProperties
@AutoConfigureBefore(SimpleDiscoveryClientAutoConfiguration.class)
public class AnsDiscoveryClientAutoConfiguration {
@Bean
public DiscoveryClient ansDiscoveryClient() {
return new AnsDiscoveryClient();
}
}

View File

@ -0,0 +1,90 @@
/*
* 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.alicloud.ans;
import java.net.URI;
import java.util.Map;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
/**
* @author xiaolongzuo
*/
public class AnsServiceInstance implements ServiceInstance {
private String serviceId;
private String host;
private int port;
private boolean secure;
private Map<String, String> metadata;
@Override
public String getServiceId() {
return serviceId;
}
@Override
public String getHost() {
return host;
}
@Override
public int getPort() {
return port;
}
@Override
public boolean isSecure() {
return secure;
}
@Override
public URI getUri() {
return DefaultServiceInstance.getUri(this);
}
@Override
public Map<String, String> getMetadata() {
return metadata;
}
public void setServiceId(String serviceId) {
this.serviceId = serviceId;
}
public void setHost(String host) {
this.host = host;
}
public void setPort(int port) {
this.port = port;
}
public void setSecure(boolean secure) {
this.secure = secure;
}
public void setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
}
}

View File

@ -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.alicloud.ans;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* @author xiaolongzuo
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@ConditionalOnProperty(value = "spring.cloud.ans.enabled", matchIfMissing = true)
public @interface ConditionalOnAnsEnabled {
}

View File

@ -0,0 +1,70 @@
/*
* 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.alicloud.ans.endpoint;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.cloud.alicloud.context.ans.AnsProperties;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
/**
* @author xiaolongzuo
*/
public class AnsEndpoint extends AbstractEndpoint<Map<String, Object>> {
private static final Logger LOGGER = LoggerFactory.getLogger(AnsEndpoint.class);
private AnsProperties ansProperties;
public AnsEndpoint(AnsProperties ansProperties) {
super("ans", false);
}
/**
* @return ans endpoint
*/
@Override
public Map<String, Object> invoke() {
Map<String, Object> ansEndpoint = new HashMap<>();
LOGGER.info("ANS endpoint invoke, ansProperties is {}", ansProperties);
ansEndpoint.put("ansProperties", ansProperties);
Map<String, Object> subscribes = new HashMap<>();
Set<String> subscribeServices = NamingService.getDomsSubscribed();
for (String service : subscribeServices) {
try {
List<Host> hosts = NamingService.getHosts(service);
subscribes.put(service, hosts);
}
catch (Exception ignoreException) {
}
}
ansEndpoint.put("subscribes", subscribes);
LOGGER.info("ANS endpoint invoke, subscribes is {}", subscribes);
return ansEndpoint;
}
}

View File

@ -0,0 +1,35 @@
/*
* 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.alicloud.ans.endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.cloud.alicloud.context.ans.AnsProperties;
import org.springframework.context.annotation.Bean;
/**
* @author xiaolongzuo
*/
@ConditionalOnWebApplication
@ConditionalOnClass(name = "org.springframework.boot.actuate.endpoint.AbstractEndpoint")
public class AnsEndpointAutoConfiguration {
@Bean
public AnsEndpoint ansEndpoint(AnsProperties ansProperties) {
return new AnsEndpoint(ansProperties);
}
}

View File

@ -0,0 +1,113 @@
/*
* 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.alicloud.ans.registry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.serviceregistry.AbstractAutoServiceRegistration;
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* @author xiaolongzuo
*/
public class AnsAutoServiceRegistration
extends AbstractAutoServiceRegistration<AnsRegistration> {
private static final Logger LOGGER = LoggerFactory
.getLogger(AnsAutoServiceRegistration.class);
@Autowired
private AnsRegistration registration;
public AnsAutoServiceRegistration(ServiceRegistry<AnsRegistration> serviceRegistry,
AutoServiceRegistrationProperties autoServiceRegistrationProperties,
AnsRegistration registration) {
super(serviceRegistry, autoServiceRegistrationProperties);
this.registration = registration;
}
@Deprecated
public void setPort(int port) {
getPort().set(port);
}
@Override
protected AnsRegistration getRegistration() {
if (this.registration.getPort() < 0 && this.getPort().get() > 0) {
this.registration.setPort(this.getPort().get());
}
Assert.isTrue(this.registration.getPort() > 0, "service.port has not been set");
return this.registration;
}
@Override
protected AnsRegistration getManagementRegistration() {
return null;
}
@Override
protected void register() {
if (!this.registration.getAnsProperties().isRegisterEnabled()) {
LOGGER.debug("Registration disabled.");
return;
}
if (this.registration.getPort() < 0) {
this.registration.setPort(getPort().get());
}
super.register();
}
@Override
protected void registerManagement() {
if (!this.registration.getAnsProperties().isRegisterEnabled()) {
return;
}
super.registerManagement();
}
@Override
protected int getConfiguredPort() {
return this.getPort().get();
}
@Override
protected void setConfiguredPort(int port) {
this.getPort().set(port);
}
@Override
protected Object getConfiguration() {
return this.registration.getAnsProperties();
}
@Override
protected boolean isEnabled() {
return this.registration.getAnsProperties().isRegisterEnabled();
}
@Override
@SuppressWarnings("deprecation")
protected String getAppName() {
String appName = registration.getServiceId();
return StringUtils.isEmpty(appName) ? super.getAppName() : appName;
}
}

View File

@ -0,0 +1,130 @@
/*
* 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.alicloud.ans.registry;
import java.net.URI;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alicloud.context.ans.AnsProperties;
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;
/**
* @author xiaolongzuo
*/
public class AnsRegistration 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 AnsProperties ansProperties;
@Autowired
private ApplicationContext context;
@PostConstruct
public void init() {
Environment env = context.getEnvironment();
Integer managementPort = ManagementServerPortUtils.getPort(context);
if (null != managementPort) {
Map<String, String> metadata = ansProperties.getClientMetadata();
metadata.put(MANAGEMENT_PORT, managementPort.toString());
String contextPath = env.getProperty("management.context-path");
String address = env.getProperty("management.address");
if (!StringUtils.isEmpty(contextPath)) {
metadata.put(MANAGEMENT_CONTEXT_PATH, contextPath);
}
if (!StringUtils.isEmpty(address)) {
metadata.put(MANAGEMENT_ADDRESS, address);
}
}
}
@Override
public String getServiceId() {
return ansProperties.getClientDomains();
}
@Override
public String getHost() {
return ansProperties.getClientIp();
}
@Override
public int getPort() {
return ansProperties.getClientPort();
}
public void setPort(int port) {
// if spring.cloud.ans.port is not set,use the port detected from context
if (ansProperties.getClientPort() < 0) {
this.ansProperties.setClientPort(port);
}
}
@Override
public boolean isSecure() {
return ansProperties.isSecure();
}
@Override
public URI getUri() {
return DefaultServiceInstance.getUri(this);
}
@Override
public Map<String, String> getMetadata() {
return ansProperties.getClientMetadata();
}
public boolean isRegisterEnabled() {
return ansProperties.isRegisterEnabled();
}
public String getCluster() {
return ansProperties.getClientCluster();
}
public float getRegisterWeight(String dom) {
if (null != ansProperties.getClientWeights().get(dom)
&& ansProperties.getClientWeights().get(dom) > 0) {
return ansProperties.getClientWeights().get(dom);
}
return ansProperties.getClientWeight();
}
public AnsProperties getAnsProperties() {
return ansProperties;
}
@Override
public String toString() {
return "AnsRegistration{" + "ansProperties=" + ansProperties + '}';
}
}

View File

@ -0,0 +1,114 @@
/*
* 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.alicloud.ans.registry;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.ipms.NodeReactor;
/**
* @author xiaolongzuo
*/
public class AnsServiceRegistry implements ServiceRegistry<AnsRegistration> {
private static Logger logger = LoggerFactory.getLogger(AnsServiceRegistry.class);
private static final String SEPARATOR = ",";
@Override
public void register(AnsRegistration registration) {
if (!registration.isRegisterEnabled()) {
logger.info("Registration is disabled...");
return;
}
if (StringUtils.isEmpty(registration.getServiceId())) {
logger.info("No service to register for client...");
return;
}
List<NodeReactor.Tag> tags = new ArrayList<>();
for (Map.Entry<String, String> entry : registration.getAnsProperties().getTags()
.entrySet()) {
NodeReactor.Tag tag = new NodeReactor.Tag();
tag.setName(entry.getKey());
tag.setValue(entry.getValue());
tags.add(tag);
}
for (String dom : registration.getServiceId().split(SEPARATOR)) {
try {
NamingService.regDom(dom, registration.getHost(), registration.getPort(),
registration.getRegisterWeight(dom), registration.getCluster(),
tags);
logger.info("INFO_ANS_REGISTER, {} {}:{} register finished", dom,
registration.getAnsProperties().getClientIp(),
registration.getAnsProperties().getClientPort());
}
catch (Exception e) {
logger.error("ERR_ANS_REGISTER, {} register failed...{},", dom,
registration.toString(), e);
}
}
}
@Override
public void deregister(AnsRegistration registration) {
logger.info("De-registering from ANSServer now...");
if (StringUtils.isEmpty(registration.getServiceId())) {
logger.info("No dom to de-register for client...");
return;
}
try {
NamingService.deRegDom(registration.getServiceId(), registration.getHost(),
registration.getPort(), registration.getCluster());
}
catch (Exception e) {
logger.error("ERR_ANS_DEREGISTER, de-register failed...{},",
registration.toString(), e);
}
logger.info("De-registration finished.");
}
@Override
public void close() {
}
@Override
public void setStatus(AnsRegistration registration, String status) {
}
@Override
public <T> T getStatus(AnsRegistration registration) {
return null;
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.alicloud.ans.ribbon;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ServerList;
/**
* @author xiaolongzuo
*/
@Configuration
public class AnsRibbonClientConfiguration {
@Bean
@ConditionalOnMissingBean
public ServerList<?> ribbonServerList(IClientConfig config) {
AnsServerList serverList = new AnsServerList(config.getClientName());
return serverList;
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.alicloud.ans.ribbon;
import java.util.Collections;
import java.util.Map;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
import com.netflix.loadbalancer.Server;
/**
* @author xiaolongzuo
*/
public class AnsServer extends Server {
private final MetaInfo metaInfo;
private final Host host;
private final Map<String, String> metadata;
public AnsServer(final Host host, final String dom) {
super(host.getIp(), host.getPort());
this.host = host;
this.metadata = Collections.emptyMap();
metaInfo = new MetaInfo() {
@Override
public String getAppName() {
return dom;
}
@Override
public String getServerGroup() {
return getMetadata().get("group");
}
@Override
public String getServiceIdForDiscovery() {
return null;
}
@Override
public String getInstanceId() {
return null;
}
};
}
@Override
public MetaInfo getMetaInfo() {
return metaInfo;
}
public Host getHealthService() {
return this.host;
}
public Map<String, String> getMetadata() {
return metadata;
}
}

View File

@ -0,0 +1,78 @@
/*
* 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.alicloud.ans.ribbon;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.ans.core.NamingService;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractServerList;
/**
* @author xiaolongzuo
*/
public class AnsServerList extends AbstractServerList<AnsServer> {
private String dom;
public AnsServerList(String dom) {
this.dom = dom;
}
@Override
public List<AnsServer> getInitialListOfServers() {
try {
List<Host> hosts = NamingService.getHosts(getDom());
return hostsToServerList(hosts);
}
catch (Exception e) {
throw new IllegalStateException("Can not get ans hosts, dom=" + getDom(), e);
}
}
@Override
public List<AnsServer> getUpdatedListOfServers() {
return getInitialListOfServers();
}
private AnsServer hostToServer(Host host) {
AnsServer server = new AnsServer(host, getDom());
return server;
}
private List<AnsServer> hostsToServerList(List<Host> hosts) {
List<AnsServer> result = new ArrayList<AnsServer>(hosts.size());
for (Host host : hosts) {
if (host.isValid()) {
result.add(hostToServer(host));
}
}
return result;
}
public String getDom() {
return dom;
}
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
this.dom = iClientConfig.getClientName();
}
}

View File

@ -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.alicloud.ans.ribbon;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* @author xiaolongzuo
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@ConditionalOnProperty(value = "ribbon.ans.enabled", matchIfMissing = true)
public @interface ConditionalOnRibbonAns {
}

View File

@ -0,0 +1,39 @@
/*
* 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.alicloud.ans.ribbon;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alicloud.ans.ConditionalOnAnsEnabled;
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.context.annotation.Configuration;
/**
* @author xiaolongzuo
*/
@Configuration
@EnableConfigurationProperties
@ConditionalOnAnsEnabled
@ConditionalOnBean(SpringClientFactory.class)
@ConditionalOnRibbonAns
@AutoConfigureAfter(RibbonAutoConfiguration.class)
@RibbonClients(defaultConfiguration = AnsRibbonClientConfiguration.class)
public class RibbonAnsAutoConfiguration {
}

View File

@ -0,0 +1,6 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alicloud.ans.endpoint.AnsEndpointAutoConfiguration,\
org.springframework.cloud.alicloud.ans.ribbon.RibbonAnsAutoConfiguration,\
org.springframework.cloud.alicloud.ans.AnsAutoConfiguration
org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
org.springframework.cloud.alicloud.ans.AnsDiscoveryClientAutoConfiguration

View File

@ -0,0 +1,78 @@
/*
* 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.ans.ribbon;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
import org.springframework.cloud.alicloud.ans.ribbon.AnsServer;
import org.springframework.cloud.alicloud.ans.ribbon.AnsServerList;
import com.alibaba.ans.shaded.com.taobao.vipserver.client.core.Host;
import com.netflix.loadbalancer.Server;
/**
* @author xiaolongzuo
*/
public class AnsServiceListTests {
static final String IP_ADDR = "10.0.0.2";
static final int PORT = 8080;
@Test
public void testAnsServer() {
AnsServerList serverList = getAnsServerList();
List<AnsServer> servers = serverList.getInitialListOfServers();
assertNotNull("servers was null", servers);
assertEquals("servers was not size 1", 1, servers.size());
Server des = assertAnsServer(servers);
assertEquals("hostPort was wrong", IP_ADDR + ":" + PORT, des.getHostPort());
}
protected Server assertAnsServer(List<AnsServer> servers) {
Server actualServer = servers.get(0);
assertTrue("server was not a DomainExtractingServer",
actualServer instanceof AnsServer);
AnsServer des = AnsServer.class.cast(actualServer);
assertNotNull("host is null", des.getHealthService());
assertEquals("unit was wrong", "DEFAULT", des.getHealthService().getUnit());
return des;
}
protected AnsServerList getAnsServerList() {
Host host = mock(Host.class);
given(host.getIp()).willReturn(IP_ADDR);
given(host.getDoubleWeight()).willReturn(1.0);
given(host.getPort()).willReturn(PORT);
given(host.getWeight()).willReturn(1);
given(host.getUnit()).willReturn("DEFAULT");
AnsServer server = new AnsServer(host, "testDom");
@SuppressWarnings("unchecked")
AnsServerList originalServerList = mock(AnsServerList.class);
given(originalServerList.getInitialListOfServers())
.willReturn(Arrays.asList(server));
return originalServerList;
}
}

View File

@ -61,13 +61,6 @@
<artifactId>spring-cloud-commons</artifactId> <artifactId>spring-cloud-commons</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId> <artifactId>spring-boot-starter-logging</artifactId>
@ -89,6 +82,13 @@
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>

View File

@ -14,6 +14,8 @@
<description>Spring Cloud Alibaba Cloud Starters</description> <description>Spring Cloud Alibaba Cloud Starters</description>
<modules> <modules>
<module>spring-cloud-starter-alicloud-oss</module> <module>spring-cloud-starter-alicloud-oss</module>
<module>spring-cloud-starter-alicloud-acm</module>
<module>spring-cloud-starter-alicloud-ans</module>
</modules> </modules>

View File

@ -0,0 +1,20 @@
<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>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud</artifactId>
<version>0.1.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-cloud-starter-alicloud-acm</artifactId>
<name>Spring Cloud Starter Alibaba Cloud ACM</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-acm</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,21 @@
<?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-starter-alicloud</artifactId>
<version>0.1.0.BUILD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-cloud-starter-alicloud-ans</artifactId>
<name>Spring Cloud Starter Alibaba Cloud ANS</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-alicloud-ans</artifactId>
</dependency>
</dependencies>
</project>