spring cloud 2020 接入 nacos 2 脚手架
Posted anxpp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring cloud 2020 接入 nacos 2 脚手架相关的知识,希望对你有一定的参考价值。
tops
前言
这其实没啥好写的,不过最新版本有些坑,再加上国内的文档真的是一言难尽,就记录一下吧。
nacos 官方文档:https://nacos.io/zh-cn/docs/quick-start.html
spring cloud 官方文档:https://spring.io/projects/spring-cloud
该demo同时整合了 spring cloud sleuth 分布式链路追踪,文内不再细讲
Nacos
nacos 是集服务发现和配置中心一体的组件,自带后台并集成用户权限,支持动态配置。
nacos 安装比较简单,下载后直接解压就有windows和linux的脚本可用,在这之前请确保安装好 maven 和 java 。
nacos并不支持较新的 java 版本,建议使用 java 8 或者 11 。
下载好后直接解压,windows下使用cmd脚本启动,mac或者linux使用sh脚本启动,便于测试,可直接使用单机模式启动:
./startup.sh -m standalone
集群方式参考官方文档即可。
启动后可访问 http://127.0.0.1:8848/nacos 浏览控制台。
分布式服务
这里创建 2 个服务: api(消费者) 和 content (生产者),Idea 中使用 spring initializr 创建即可。
创建后添加相关的依赖,相关版本如下:
<properties>
<java.version>8</java.version>
<spring-cloud.version>2020.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
</properties>
注意: 当前最新版本的 nacos client 已不再支持 spring-cloud-starter-netflix-ribbon ,这是官方文档没提到过的,相反,文档上是写着支持 robbin 的,所以调试了很久,直到后面找个了相关的 issue ,所以我们直接使用 openfeign 。
Servive Content(生产者)
pom
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>nacosServiceContent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>nacosServiceContent</name>
<description>nacosServiceContent</description>
<properties>
<java.version>8</java.version>
<spring-cloud.version>2020.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
<nacos-client.version>1.4.1</nacos-client.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class NacosServiceContentApplication {
public static void main(String[] args) {
SpringApplication.run(NacosServiceContentApplication.class, args);
}
@EnableDiscoveryClient
开启服务发现功能
配置文件
bootstrap.yml
server:
port: 9091
spring:
application:
name: service-content
cloud:
nacos:
config:
file-extension: yaml
server-addr: 127.0.0.1:8848
extension-configs:
- data-id: content-develop.properties
refresh: true
discovery:
server-addr: 127.0.0.1:8848
service: service-content
application.yml
spring:
profiles:
active: dev
较新版的 spring cloud 不再自动读取 bootstrap.yml ,需要引入 spring-cloud-starter-bootstrap
nacos 配置文件ID结构为三段式:${prefix}-${spring.profiles.active}.${file-extension}
- prefix 默认为 spring.application.name,可使用 spring.cloud.nacos.config.prefix 自定义
- spring.profiles.active 如果为空,则配置文件ID两段式
- file-extension 默认为 properties,目前还支持yaml
其中 extension-configs 数组可配置额外的配置文件,可参考文档。
controller
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigController {
@Value("${useLocalCache:initValue}")
private String useLocalCache;
private static final Logger log = LoggerFactory.getLogger(ConfigController.class);
@RequestMapping("/get")
public String get() {
log.info("test content");
return String.format("%s", useLocalCache);
}
}
@RefreshScope
开启配置自动刷新
@Value("${useLocalCache:initValue}")
注入配置,可指定默认值
Servive Api(消费者)
pom:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>nacosServiceApi</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>nacosServiceApi</name>
<description>nacosServiceApi</description>
<properties>
<java.version>8</java.version>
<spring-cloud.version>2020.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-ribbon -->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>-->
<!-- <version>2.1.6.RELEASE</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class NacosServiceApiApplication {
// @LoadBalanced
// @Bean
// public RestTemplate restTemplate() {
// return new RestTemplate();
// }
public static void main(String[] args) {
SpringApplication.run(NacosServiceApiApplication.class, args);
}
}
@RefreshScope
开启配置自动刷新
@EnableFeignClients
开启 feign 客户端
// 注释掉的部分(pom同)为测试 robbin 的,实际不可用
配置文件
bootstrap.yml
server:
port: 9090
spring:
application:
name: service-api
cloud:
nacos:
config:
file-extension: yaml
server-addr: 127.0.0.1:8848
extension-configs:
- data-id: content-develop.properties
refresh: true
discovery:
server-addr: 127.0.0.1:8848
service: service-api
application.yml
spring:
profiles:
active: dev
配置大致同 content 服务的配置
controller
import com.example.nacosserviceapi.client.ContentClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
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;
@RestController
@RequestMapping("/echo")
@RefreshScope
public class ConfigController {
// @Value("${service-url.content}")
// private String urlContent;
// private final RestTemplate restTemplate;
@Autowired
private ContentClient contentClient;
//
// @Autowired
// public ConfigController(RestTemplate restTemplate) {
// this.restTemplate = restTemplate;
// }
private static final Logger log = LoggerFactory.getLogger(ConfigController.class);
@RequestMapping(value = "/test/{str}", method = RequestMethod.GET)
public String echo(@PathVariable String str) {
// String url = urlContent + "/config/get";
// System.out.println("urlContent: " + url);
// return restTemplate.getForObject(url, String.class) + str;
log.info("tapi est");
return contentClient.config();
}
}
client
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient("service-content")
public interface ContentClient {
@RequestMapping("/config/get")
String config();
}
@FeignClient("service-content")
指定要调用的服务
动态配置与服务发现测试
这里通过 api 调用 content 并返回 content 读取的配置文件数据完成配置服务、注册服务和服务调用的测试。
首先通过 nacos 控制台添加配置项:
dataId: service-content-dev.yaml
groupId: DEFAULT_GROUP
配置格式: yaml
配置内容L: useLocalCache: testConfig
启动服务:
api 端口为 9090
content 端口为 9091
这时候能在 nacos 控制台看到服务注册成功,发起调用测试:
http://127.0.0.1:9090/echo/test/123
如果正常,会返回刚刚添加的配置 testConfig,可以尝试修改 useLocalCache 的值,再调用该接口。
细心的朋友会发现输出的日志格式为:
2021-11-16 11:47:06.182 INFO [service-content,e968ce128347c604,baf888e4e7f7ec89]......
这里面 service-content,e968ce128347c604,baf888e4e7f7ec89
为分布式链路追踪支持,两端id分别为tranceId和spanId,具体可参考文档
Spring Cloud Gateway
pom
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>nacosServiceGateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>nacosServiceGateway</name>
<description>nacosServiceGateway</description>
<properties>
<java.version>8</java.version>
<spring-cloud.version>2020.0.4</spring-cloud.version>
<spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.github.resilience4j/resilience4j-spring-boot2 -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot2</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
启动类
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;
import org.springframework.cloud.client.circuitbreaker.Customizer;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.time.Duration;
@SpringBootApplication
@EnableDiscoveryClient
public class NacosServiceGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(NacosServiceGatewayApplication.class, args);
}
// 优先级低于配置文件
@Bean
public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer() {
return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
.timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)).build())
.circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())
.build());
}
@Bean
public GlobalFilter customFilter() {
return new CustomGlobalFilter();
}
public static class CustomGlobalFilter implements GlobalFilter, Ordered {
private static final Logger log = LoggerFactory.getLogger(CustomGlobalFilter.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("custom global filter");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}
}
defaultCustomizer
是集成 Resilience4J 轻量级容错组件
customFilter
为自定义全局过滤器,这里主要用来测试分布式服务追踪。
配置文件
bootstrap.yml
server:
port: 9080
spring:
application:
name: service-gateway
cloud:
nacos:
config:
file-extension: yaml
server-addr: 127.0.0.1:8848
extension-configs:
- data-id: gateway-develop.yaml
refresh: true
discovery:
server-addr: 127.0.0.1:8848
service: service-gateway
application.yml
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
routes:
- id: service-content
uri: lb://service-content
predicates:
- Path=/content/**
filters:
- StripPrefix=1
- id: service-api
uri: lb://service-api
predicates:
- Path=/api/**
filters:
- StripPrefix=1
#resilience4j.circuitbreaker:
# instances:
# backendA:
# registerHealthIndicator: true
# slidingWindowSize: 100
# backendB:
# registerHealthIndicator: true
# slidingWindowSize: 10
# permittedNumberOfCallsInHalfOpenState: 3
# slidingWindowType: TIME_BASED
# recordFailurePredicate: io.github.robwin.exception.RecordFailurePredicate
#
#resilience4j.timelimiter:
# instances:
# backendA:
# timeoutDuration: 2s
# cancelRunningFuture: true
# backendB:
# timeoutDuration: 1s
# cancelRunningFuture: false
调用测试
http://127.0.0.1:9080/api/echo/test/123
输出跟 http://127.0.0.1:9090/echo/test/123 是一致的。
参考文档
Nacos:https://nacos.io/zh-cn/docs/what-is-nacos.html
Resilience4J:https://github.com/resilience4j/resilience4j
Nacos Config:https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config
Nacos discovery:https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-discovery
Spring Cloud Gateway:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/
Spring Cloud OpenFeign:https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/
Spring Cloud Sleuth:https://docs.spring.io/spring-cloud-sleuth/docs/3.1.0-SNAPSHOT/reference/htmlsingle/
以上是关于spring cloud 2020 接入 nacos 2 脚手架的主要内容,如果未能解决你的问题,请参考以下文章
spring cloud 2020 接入 nacos 2 脚手架
spring cloud 2020 接入 nacos 2 脚手架
Spring Cloud Alibaba - 17 Nacos Config 配置中心 应用篇