# Study_SpringCloud
**Repository Path**: vmvm/study_-spring-cloud
## Basic Information
- **Project Name**: Study_SpringCloud
- **Description**: 记录学习SpringCloud的一些笔记
- **Primary Language**: Java
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2025-12-05
- **Last Updated**: 2025-12-05
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# SpringCloud 学习
## 服务消费方直接调用服务提供方(通过 RestTemplate 直接进行 HTTP 调用)

## Eureka 组件
### Eureka 概述
Eureka 是基于 C-S 架构设计的,Eureka Client 其实就是对应的微服务,可以获取到 Eureka Server 上注册的用户(服务发现),Eureka Server 是服务的注册中心,可用于注册服务以及实现服务的负载均衡和故障转移。
Eureka 的整体运作图如下:

### 启动 Eureka Server
1、新建一个 SpringBoot 项目,pom.xml 中添加下面的依赖(**加依赖**)。
```xml
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
```
2、配置 application.yml(**改配置**)。
```yml
server:
port: 8761
eureka:
instance:
# 设置该服务注册中心的主机号
hostname: localhost
client:
# 当前的EurekaServer也是一个应用(默认也会将该应用注册到注册中心), 设置为false表示不需要将当前应用注册到注册中心
register-with-eureka: false
# 指定服务注册中心的URL
service-url.defaultZone: http://localhost:8761/eureka
```
3、在启动类上加上`EnableEurekaServer`注解(**写注解**)。

4、访问`http://localhost:8761`,如下图:

### 微服务注册与调用
1、pom.xml 中添加下面的依赖(**加依赖**)。
```xml
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
```
2、配置 application.yml(**改配置**)。
```yml
server:
port: 8080
spring:
application:
# 不写这个会在eureka监控页出现UNKNOW
name: springcloud-service-portal
eureka:
client:
# Eureka注册中心的连接地址
service-url.defaultZone: http://localhost:8761/eureka
instance:
# 每隔2秒向Eureka服务端发送一次心跳, 证明自己还存活着
lease-renewal-interval-in-seconds: 2
# 告诉Eureka服务端如果10秒内没有给你发心跳, 代表当前应用发生故障, 将我踢掉
lease-expiration-duration-in-seconds: 10
# 注册到Eureka服务端的服务实例的名字
instance-id: springcloud-service-portal
```
3、在启动类上加上`EnableEurekaClient`注解(**写注解**)。

4、在注入 RestTemplate 的方法上加上注解 LoadBalanced。
.png)
5、修改调用的 URL。

6、访问`http://127.0.0.1:8080/cloud/goods`,结果如下图:
.png)
### Eureka 服务注册中心自我保护机制
在没有 Eureka 自我保护的情况下,如果 Eureka Server 在一定时间内没有接收到某个微服务实例的心跳,Eureka Server 将会注销该实例,但是当发生网络故障时,那么微服务与 Eureka Server 之间将无法正常通信,此时该微服务会被注册中心错误地剔除(微服务本身其实是正常的)。 此处联想到 Eureka Client 端下面的配置:
```yml
eureka:
instance:
# 每隔2秒向Eureka服务端发送一次心跳, 证明自己还存活着
lease-renewal-interval-in-seconds: 2
# 告诉Eureka服务端如果10秒内没有给你发心跳, 代表当前应用发生故障, 将我踢掉
lease-expiration-duration-in-seconds: 10
```
Eureka 通过**自我保护模式**来解决这个问题——当 Eureka Server 节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么就会把这个微服务节点进行保护,不删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该 Eureka Server 节点会再自动退出自我保护模式。
**Eureka Server 端可以使用配置项禁用自我保护模式**。`eureka.server.enable-self-preservation = false `
**生产上还是最好启用自我保护模式**,虽然该模式给我们带来一些困扰,比如:如果在保护期内某个服务提供者刚好非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,但对于这个问题需要**服务消费者端具有一些容错机制,如重试、断路器等**。
### Eureka 高可用集群搭建
Eureka 服务注册中心它本身也是一个服务(可以看做是一个提供者,又可以看做是一个消费者),我们之前通过配置:`eureka.client.register-with-eureka=false`让注册中心不注册自己,但是我们可以向其他注册中心注册自己。
Eureka Server 的高可用实际上就是**将自己作为服务向其他服务注册中心进行注册**,这样就会形成一组互相注册的服务注册中心,进而实现服务清单的互相同步。**往注册中心 A 上注册的服务,可以被复制同步到注册中心 B 上**,所以从任何一台注册中心上都能查询到已经注册的服务,从而达到高可用的效果。Eureka 高可用集群架构图如下:
.png)
1、复制三份配置文件,做出如下修改:
```yml
# application-eureka8762.yml
server:
port: 8762
eureka:
instance:
# 设置该服务注册中心的主机号
hostname: localhost
client:
# 当前的EurekaServer也是一个应用(默认也会将该应用注册到注册中心), 设置为false表示不需要将当前应用注册到注册中心
register-with-eureka: false
# 指定服务注册中心的URL
service-url.defaultZone: http://eureka8763:8763/eureka,http://eureka8764:8764/eureka
# application-eureka8763.yml
server:
port: 8763
eureka:
instance:
# 设置该服务注册中心的主机号
hostname: localhost
client:
# 当前的EurekaServer也是一个应用(默认也会将该应用注册到注册中心), 设置为false表示不需要将当前应用注册到注册中心
register-with-eureka: false
# 指定服务注册中心的URL
service-url.defaultZone: http://eureka8762:8762/eureka,http://eureka8764:8764/eureka
# application-eureka8764.yml
server:
port: 8764
eureka:
instance:
# 设置该服务注册中心的主机号
hostname: localhost
client:
# 当前的EurekaServer也是一个应用(默认也会将该应用注册到注册中心), 设置为false表示不需要将当前应用注册到注册中心
register-with-eureka: false
# 表示不会从服务端主动检索其他服务信息(自己就是服务端)
fetch-registry: false
# 指定服务注册中心的URL
service-url.defaultZone: http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
```
2、在`C:\Windows\System32\drivers\etc\hosts`文件中添加下图配置:

3、在 Program Arguments 中配置`--spring.profiles.active=eureka...`

4、访问其中一个 Eureka8762 后台页面,启动 goods、protal 服务并将其注册到 8762,可以看到 Eureka8763 上有该服务信息(从Eureka8762 上复制过来的)。
.png)

### Eureka 宕机时分析
**当注册中心 Eureka 宕机时,服务消费者也可以调用服务提供者**,该机制和 Dubbo 一样,服务消费者那里会维护一份本地缓存的。
## Ribbon 组件
### 启用 Ribbon
只需要在 RestTemplate 上面加上 LoadBalanced 注解,即可实现**服务调用的负载均衡**。
### 负载均衡实现的原理
客户端负载均衡时会调用`ILoadBalancer 实现类上的 chooseServer 方法`,然后会进入 BaseLoadBalancer 的 chooseServer 方法,真正负载均衡的逻辑在`IRule 实现类的 choose 方法`。



| IRule 实现类的类名 | 策略 |
| RandomRule | 随机 |
| RoundRobinRule | 轮询 |
| AvailabilityFilteringRule | 先过滤掉由于多次访问故障的服务,以及并发连接数超过阈值的服务,然后对剩下的服务按照轮询策略进行访问 |
| ResponseTimeWeightedRule | ... |
| WeightedResponseTimeRule | 根据平均响应时间计算所有服务的权重,响应时间越快服务权重就越大被选中的概率即越高,如果服务刚启动时统计信息不足,则使用RoundRobinRule策略,待统计信息足够会切换到该WeightedResponseTimeRule策略 |
| RetryRule | 先按照RoundRobinRule策略分发,如果分发到的服务不能访问,则在指定时间内进行重试,然后分发其他可用的服务 |
| BestAvailableRule | 先过滤掉由于多次访问故障的服务,然后选择一个并发量最小的服务 |
| ZoneAvoidanceRule(默认) | 综合判断服务节点所在区域的性能和服务节点的可用性,来决定选择哪个服务 |
### 自定义负载均衡算法
1、`继承 AbstractLoadBalancerRule 类并实现 choose 方法 `。
```java
@Slf4j
public class MyRule extends AbstractLoadBalancerRule {
private AtomicInteger integer = new AtomicInteger(0);
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object key) {
log.info("执行了自定义的负载均衡算法...");
ILoadBalancer lb = getLoadBalancer();
if (lb == null) {
return null;
}
List allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
return null;
}
while(true) {
int currentIndex = integer.get();
int nextIndex = (1 + currentIndex) % serverCount;
if(integer.compareAndSet(currentIndex, nextIndex)) {
return allList.get(nextIndex);
}
}
}
}
```
2、将该规则注入到服务消费者的容器中。
```java
@Bean
public IRule iRule() {
return new MyRule();
}
```
3、测试。
.png)
.png)
## Feign 组件
### Feign 概述
在之前进行微服务调用时都是直接需要写一大堆冗余代码(比如:一长串的 URL 和 RestTemplate 的相关调用),在实际开发中,由于服务提供者提供的接口很多,直接引用 RestTemplate 会有很多冗余代码,因此可以使用 Feign 组件。
Feign 在 Ribbon + RestTemplate 的基础上做了进一步封装,在 Feign封装之后,我们只需**创建一个接口并使用注解的方式来配置**,即可完成对服务提供方的接口绑定,简化了使用Ribbon + RestTemplate 的调用,自动封装服务调用客户端,减少了代码量。
### Feign 使用
1、引依赖
在 commons 模块中引入下面的依赖(接口一般放在 commons 模块,和 Dubbo 开发类似):
```xml
org.springframework.cloud
spring-cloud-starter-openfeign
```
2、写接口(配置服务名+URI)

3、加注解
消费者启动类上加上`@EnableFeignClients`。
## Hystrix 组件(TODO:如何通过 yml 细粒度配置 hystrix 的超时时间、整合了 feign 的 hystrix 如何直接将异常抛给用户---不走降级方法)
### Hystrix 概述
Hystrix 被称为熔断器,可以用于处理分布式系统中的延迟和容错。比如:在分布式系统中,许多服务都需要通过远程调用其他服务来实现自己的功能,调用时不可避免会出现超时、出现异常等导致调用失败,Hystrix 组件可以保证在某个服务出现故障的情况下,不会导致整体服务失败,**避免级联故障**,以提高分布式系统的弹性。

### 启动 Hystrix
1、引依赖
在服务消费者模块中引入下面的依赖:
```xml
org.springframework.cloud
spring-cloud-starter-netflix-hystrix
```
2、加注解

服务消费者的启动类上加上`@EnableHystrix`。
3、Feign 整合 Hystrix(**此时降级都在 Feign 接口上完成,如果还是在 Controller 上做的话其实和之前普通的降级一样了**)


**必须要将该实现类注入到容器中!**
```java
//需要注入到容器
@Component
public class GoodsRemoteClientFallback implements GoodsRemoteClient {
@Override
public ResultObject goods() {
return new ResultObject(Constant.ERROR, "feign服务调用降级...", null);
}
}
```
```yml
feign:
hystrix:
# 开启feign的hystrix支持
enabled: true
```

### 降级测试
① 服务提供者未启动,此时调用服务消费者的接口,执行了 fallback 备份方法。


② 在 fallback 备份方法中加入 Throwable 参数,即可捕获到服务调用时抛出的异常。


③ 普通的 hystrix 超时引起降级。
当前执行指令的超时时间为 5 秒,而方法内线程休眠了 8 秒,势必会导致降级。
```java
@HystrixCommand(fallbackMethod = "fallback",
commandProperties = {
//命令执行知否开启超时, 默认为true
@HystrixProperty(name = "execution.timeout.enabled", value = "true"),
//指定命令执行的超时时间, 默认为1秒
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
}
)
@GetMapping("/cloud/goodsHystrixTimeout")
public ResultObject goodsHystrixTimeout() throws InterruptedException {
ResultObject result = restTemplate.getForEntity(GOODS_SERVICE_URL_2, ResultObject.class).getBody();
TimeUnit.SECONDS.sleep(8);
return result;
}
```

④ 整合了 feign 的 hystrix 超时引起降级(**feign 调用服务存在默认时长上限制,当超过 1 秒还未连接上或者超过 1 秒还未返回结果都会报错**)。



可以通过下面的配置修改 Ribbon 的连接时长和处理时长以及 Hystrix 指令的执行时长。
```yml
ribbon:
ReadTimeout: 5000
ConnectTimeout: 5000
hystrix:
command:
# default表示全局, 非全局的待研究
default:
execution:
timeout:
enable: true
isolation:
thread:
timeoutInMilliseconds: 4000
```
⑤ 捕获到整合了 feign 的 hystrix 抛出的异常。


```java
@Slf4j
//需要注入到容器, 泛型为Feign的接口类
@Component
public class GoodsRemoteClientFallbackFactory implements FallbackFactory {
@Override
public GoodsRemoteClient create(Throwable throwable) {
return new GoodsRemoteClient() {
@Override
public ResultObject goods() {
log.info(throwable.getMessage());
return new ResultObject(Constant.ERROR, "feign服务调用降级...GoodsRemoteClientFallbackFactory", null);
}
};
}
}
```


⑥ 将服务提供方的异常忽略**即不执行备份方法**,直接抛给用户(默认情况下方法抛了异常会自动进行服务降级)。
在 @HystrixCommand 注解中指定 ignoreExceptions 属性为要忽略的异常类即可。
```java
@HystrixCommand(fallbackMethod = "fallback", ignoreExceptions = {RuntimeException.class})
@GetMapping("/cloud/goodsHystrixIgnoreExceptions")
public ResultObject goodsHystrixIgnoreExceptions() throws InterruptedException {
ResultObject result = restTemplate.getForEntity(GOODS_SERVICE_URL_2, ResultObject.class).getBody();
if (1 == 1) {
throw new RuntimeException("has error...");
}
return result;
}
```

**经测试整合了 feign 的 hystrix 貌似无法通过上述方式忽略异常**。
## Zuul 网关组件
### Zuul 网关组件概述
微服务架构中有很多个独立服务都要对外提供服务,为了方便地管理这些微服务接口,同时要确保每个微服务接口的安全,每个微服务需要做权限管理,为了减少工作量,微服务架构中提出了 API 网关的概念,外部所有的请求都要经过该网关的调度和过滤,由 API 网关实现请求路由、负载均衡、权限验证等功能。
当前微服务的架构图如下:

### 初级路由功能
1、新建 Zuul 的 SpringBoot 项目,pom 文件中引入下面的依赖。
```xml
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
true
org.springframework.cloud
spring-cloud-starter-netflix-zuul
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
```
2、启动类上加入下面注解(`@EnableEurekaClient` 和 `@EnableZuulProxy`)。PS:Zuul 网关也需要作为微服务注册到 Eureka 中。
```java
//激活Eureka的客户端
@EnableEurekaClient
//开启Zuul网关的支持
@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
```
3、修改 application.yml 文件的配置。
```yml
server:
port: 80
spring:
application:
# 不写这个会在eureka监控页出现UNKNOW
name: springcloud-service-zuul
eureka:
client:
# Eureka注册中心的连接地址
service-url.defaultZone: http://localhost:8761/eureka
instance:
# 每隔2秒向Eureka服务端发送一次心跳, 证明自己还存活着
lease-renewal-interval-in-seconds: 2
# 告诉Eureka服务端如果10秒内没有给你发心跳, 代表当前应用发生故障, 将我踢掉
lease-expiration-duration-in-seconds: 10
# 注册到Eureka服务端的服务实例的名字
instance-id: springcloud-service-zuul
```
4、测试。
启动 Eureka 的管理界面,可以看到 goods、portal、zuul服务都成功注册了。

`浏览器中只需要访问 80 端口(Zuul 应用的端口) + 服务名 + URI 即可完成服务的调用。`
.png)
.png)
可通过下面的配置禁止直接通过微服务名(之间用逗号分隔)调用接口。
```yml
zuul:
ignored-services: springcloud-service-portal,springcloud-service-goods
```
从下图中可以看到成功禁用了微服务名的直接调用。


可通过下面的配置将请求的 URL 前面加上固定的前缀。
```yml
# 经测试写成 /api/ 也可以生效
zuul:
prefix: /api
```
### 高级路由(自定义映射规则)
如果只是用初级路由功能,则访问时需要输入`服务名+URI`,此时对外暴露了服务名,增加了一定的隐患,而且实际开发当中肯定不会通过微服务名去调用,此时就需要配置路由规则(高级路由)。在 Zuul 应用的 application.yml 添加下面配置:
```yml
zuul:
routes:
portal:
service-id: springcloud-service-portal
path: /portal/**
goods:
service-id: springcloud-service-goods
path: /goods/**
```
访问下面的 URL,可以看到接口调用的结果。


通配符规则:
|
通配符
|
含义
|
举例
|
说明
|
|
?
|
匹配任意单个字符
|
/springcloud-service-goods/?
|
匹配 /springcloud-service-goods/a, /springcloud-service-goods/b, /springcloud-service-goods/c 等
|
|
*
|
匹配任意数量的字符
|
/springcloud-service-goods/*
|
匹配 /springcloud-service-goods/aa, /springcloud-service-goods/bb, /springcloud-service-goods/cc 等,但无法匹配 /springcloud-service-goods/a/b
|
|
**
|
匹配任意数量的字符
|
/springcloud-service-goods/*
|
匹配 /springcloud-service-goods/aa, /springcloud-service-goods/bb, /springcloud-service-goods/cc 等,也可匹配 /springcloud-service-goods/a/b
|
### 过滤器
1、概述:
Zuul 中定义了四种标准过滤器类型(Pre、Route、Post、Error),分别对应请求的不同生命周期。Zuul 中过滤器的工作生命周期图如下:
.png)
|
过滤器类型
|
过滤器触发时机
|
过滤器作用
|
|
Pre
|
在请求被路由之前调用
|
利用这种过滤器可实现身份验证、在集群中选择请求的微服务、记录调试信息等
|
|
Route
|
将请求路由到微服务
|
该过滤器可用于构建发送给微服务的请求,并使用 Apache HttpClient 或 Netfilx Ribbon 请求微服务
|
|
Post
|
在路由到微服务以后执行
|
该过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等
|
|
Error
|
某阶段发生错误时执行
|
统一处理错误
|
2、自定义过滤器的使用:
```java
@Slf4j
@Component
public class LoginFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
log.info("进入了LoginFilter... request: {}", request.toString());
return null;
}
}
```

3、可配置要禁用过滤器:
在 application.yml 文件中添加下面的配置:
```yml
#第一个点后面表示过滤器的类名,第二个点后面表示过滤器的类型(pre等)
zuul.LoginFilter.prev.disable=true
```
从下图可以看到禁用 LoginFilter 成功了。

### 熔断降级
如果使用 Zuul 网关路由到的微服务宕机了,则会执行 Zuul 的熔断策略。
```java
@Component
public class ZuulFallback implements FallbackProvider {
@Override
public String getRoute() {
//所有经过网关路由的服务出异常都会执行该熔断
return "*";
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.BAD_REQUEST;
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.BAD_REQUEST.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.BAD_REQUEST.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
String responseJson = "{\"data:\" null, \"msg:\" 服务正在维护,请稍后再试...}";
return new ByteArrayInputStream(responseJson.getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json; charset=UTF-8");
return headers;
}
};
}
}
```
将 goods 服务下线后,通过网关访问该服务,则会执行 Zuul 的熔断,如下如图所示:

## Config 分布式配置中心组件
### 分布式配置中心概述
在微服务架构中,服务数量及其配置信息会不断增多,这对配置管理提出了如下挑战:
安全性:配置文件和源代码一起保存在代码库,容易造成配置泄露;
时效性:修改配置文件后,需要重启服务才能生效;
局限性:无法支持动态调整,例如:日志开关、功能开关。
Spring Cloud Config 是一个解决分布式系统的配置管理方案,其包含 Client 和 Server 两部分,Server 提供配置文件的存储(可用 Git 管理,**Git 天生就是为版本管理而生的,因此 Config 组件也自带了版本管理的功能**)、以接口的形式将配置文件的内容提供出去,Client 通过接口获取数据、并依据此数据初始化自己的应用。
### Config 工作过程图

### 基础配置
1、在 GitEE 或 GitHub 上创建中央仓库。

2、配置 Config 的 Server 端。
新建一个 SpringBoot 的应用,在 pom.xml 文件中加入下述依赖:
```xml
org.springframework.cloud
spring-cloud-config-server
```
并在启动类上加上`@EnableConfigServer`。
新增 bootstrap.yml 文件,添加下面的配置:
```yml
server:
port: 8888
spring:
application:
name: springcloud-service-config
cloud:
config:
server:
git:
# git仓库的URL
uri: https://gitee.com/chenjie1561435010/spring-cloud-config.git
# 查询配置文件的路径, 多个路径间用逗号分隔
search-paths: config-server/goods,config-server/portal
# git的用户名
username: ***
# git的密码
password: ***
```
3、可通过下面的方式访问到对应远程仓库上的配置文件(**{application} 表示配置文件的名字,{profile} 表示环境,{label} 表示分支,默认放在 master 分支上**)。
① /{application}/{profile}[/{label}]
http://127.0.0.1:8888/application/goods-dev/master 或 http://127.0.0.1:8888/application/goods-dev



② /{application}-{profile}.properties 或 /{application}-{profile}.yml
http://127.0.0.1:8888/application-goods-dev.yml

③ /{label}/{application}-{profile}.properties 或 /{label}/{application}-{profile}.yml
http://127.0.0.1:8888/master/application-goods-dev.yml

4、配置 Config 的 Client 端。
此处新建了一个应用测试,其实可以复用原来的 goods 和 portal 微服务,在 pom.xml 文件中添加下述依赖:
```xml
org.springframework.cloud
spring-cloud-starter-config
```
新增 bootstrap.yml 文件,分别在两服务中添加下面的配置:
```yml
spring:
cloud:
config:
# Config Server应用的IP+端口
uri: http://127.0.0.1:8888
# 远程仓库的分支
label: master
# 配置文件的环境
profile: goods-dev
spring:
cloud:
config:
# Config Server应用的IP+端口
uri: http://127.0.0.1:8888
# 远程仓库的分支
label: master
# 配置文件的环境
profile: portal-dev
```
5、测试。


### 高级配置
1、加密
Config Server 应用的配置文件上加上密钥配置,如下所示:
```yml
encrypt:
# 用来加密的密钥
key: springcloud-service-config
```
将配置文件中要加密的用户名、密码等敏感数据依次通过 `http://127.0.0.1:8888/encrypt` 进行加密,也可以通过 `http://127.0.0.1:8888/decrypt` 将加密后的数据还原为原始数据。
.png)
之后用响应的数据替换掉原始数据(PS:**前面加上{cipher},yml 文件中需要加上单引号才能正确解密**)。
.png)
访问 `http://127.0.0.1:8888/application-goods-dev.yml`,可以看到配置文件被自动解密了。

2、配置文件局部刷新
Config Client 端(goods 服务和 portal 服务)的 pom.xml 文件中添加 `actuator` 依赖。
```xml
org.springframework.boot
spring-boot-starter-actuator
```
Config Client 端的 Controller 类上加上 `@RefreshScope` 注解。

Config Client 端的配置文件中加上下面的配置:
```yml
management:
endpoints:
web:
exposure:
include: '*'
```
每次修改远程仓库的配置文件后,手动 POST 调用 `http://127.0.0.1:port/actuator/refresh`,之后 Config Client 就能从远程仓库获取到最新的配置数据了(**感觉就类似一个缓存问题,手动调用 refresh 接口后会手动删除本地缓存,从远程仓库读取最新的数据再放入本地缓存**)。


3、配置文件全局刷新
Config Server 端和 Client 端的 pom.xml 文件中都添加 `actuator` 和 `amqp` 依赖。
```xml
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-bus-amqp
```
Config Server 端的配置文件中加上 RabbitMQ 的连接配置:
```yml
spring:
rabbitmq:
host: 192.168.255.204
port: 5672
username: guest
password: guest
cloud:
bus:
# 开启bus总线
enabled: true
```
Config Client 端(goods 服务和 portal 服务)的配置文件中加上 RabbitMQ 的连接配置:
```yml
spring:
rabbitmq:
host: 192.168.255.204
port: 5672
username: guest
password: guest
```
每次修改远程仓库的配置文件后,手动 POST 调用 `http://127.0.0.1:port/actuator/refresh`(**该端口是 Config Server 端的端口**),之后 Config Client 就能从远程仓库获取到最新的配置数据了。

### Config 高可用
要使得 Config 服务达到高可用,可以将 Config 服务注册到 Eureka 中,利用注册中心来实现 Config 高可用的目的。
1、新建三个 Config 的应用,端口号分别是 8887、8888、8889,将其注册到 Eureka 中。

2、客户端只需要将之前在 bootstrap.yml 中配置的 URL 注释掉,从注册中心你中获取服务即可,如下图所示:

可以看到 Config 的高可用已经生效,而且客户端也可以成功访问到。


### Config 的安全认证
该安全认证说穿了其实就是限制用户或微服务直接访问 Config 配置中心上的配置,Config 服务中引入 SpringSecurity 依赖并配置用户名和密码,客户端微服务要想读取配置中心上的配置,则需要在配置文件中增加下述配置:
```yml
spring:
cloud:
config:
username: root
password: 123456
```
.png)

.png)

## Sleuth + Zipkin 分布式链路追踪
### 概述
对于一个大型的几十个、几百个微服务构成的微服务架构系统,通常会遇到下面一些问题,比如:
如何串联整个调用链路,快速定位问题?
如何理清各个微服务之间的依赖关系?
如何进行各个微服务接口的性能分折?
如何跟踪整个业务流程的调用处理顺序?
Sleuth + Zipkin 其实可以帮我做上述事情,其中 **Sleuth 负责给数据打点,Zipkin 负责将跟踪的数据展示给用户**。
原理图如下所示:

### 简易搭建(默认将追踪的数据保存在 ZipkinServer 的内存中,重启会丢失)
1、搭建 ZipkinServer 应用。application.yml 配置如下:
```yml
server:
port: 9410
management:
metrics:
web:
server:
auto-time-requests: false
```
2、引入下述依赖:
```xml
io.zipkin.java
zipkin-autoconfigure-ui
2.12.3
io.zipkin.java
zipkin-server
2.12.3
```
3、启动类上添加 `@EnableZipkinServer` 注解。
4、微服务应用的 pom.xml 上加入下述依赖:
```xml
org.springframework.cloud
spring-cloud-starter-sleuth
org.springframework.cloud
spring-cloud-starter-zipkin
```
5、微服务应用的 application.yml 上加入下面配置:

6、访问 `http://127.0.0.1:9410/zipkin`,并请求 goods 微服务,从下图可以看到分布式链路追踪搭建成功了。


但是一旦重启 ZipkinServer 应用后,从下图看到数据丢失了。

### 使用 Elastic Search 对 ZipkinServer 进行持久化
1、Zipkin Server 的 pom.xml 中添加依赖。
```xml
io.zipkin.java
zipkin-autoconfigure-storage-elasticsearch-http
2.8.4
```
2、Zipkin Server 的 application.yml 中添加下面的配置。
```yml
zipkin:
storage:
type: elasticsearch
elasticsearch:
cluster: elasticsearch
# elasticsearch启动的端口号
hosts: http://localhost:9200
# 随便去个名字
index: zipkin
```
3、此时访问打点后,即使 Zipkin Server 重新启动也不会丢失数据。

## Spring Cloud Stream(TODO Add)
## 微服务添加安全认证
### RestTemplate 添加安全认证
如果不添加认证信息,会报错,如下图:

1、在 ConfigBean 中增加下面代码:
```java
@Bean
public HttpHeaders httpHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
byte[] encodedAuth = Base64.getEncoder().encode(String.format("%s:%s", username, password).getBytes(Charset.forName("US-ASCII")));
String authHeader = "Basic " + new String(encodedAuth);
httpHeaders.set("Authorization", authHeader);
return httpHeaders;
}
```
2、并修改 RestTemplate 的调用:

3、修改后再次访问,可以成功访问,如下图:

### Feign 添加安全认证
如果不添加认证信息,会报错,如下图:

1、在 Feign 接口的工程中增加下面配置类:
```java
@Configuration
public class FeignConfiguration {
@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("admin", "123456");
}
}
```
2、并在注解上增加下面的属性:

3、可以看到可以成功通过 Feign 访问需要权限的微服务了。

### 微服务统一安全认证的做法
新建一个模块专门用于认证(里面放个配置类,其他要保证安全的模块依赖这个模块即可)。
1、新建模块,并在 pom 文件中增加如下配置:
```xml
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-web
```
2、新增如下的配置类:
```java
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin")
.password(new BCryptPasswordEncoder().encode("123456"))
.roles("ADMIN");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.httpBasic().and().authorizeRequests().anyRequest().fullyAuthenticated();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
```
3、其他模块依赖当前 auth 模块,访问时如下图所示就说明认证模块搭建成功。
