第2章 SpringCloud
学习目标
-
能够使用Feign(通信服务)
1.使用Feign进行Web Service 远程调用 2.支持负载均衡配置 3.支持熔断配置 4.请求压缩/响应数据压缩 5.日志配置
-
能够搭建Spring Cloud Gateway
1.微服务网关 2.路由 3.过滤配置 4.自定义全局过滤器/自定义局部过滤器
-
能够配置Spring Cloud Gateway过滤器
-
能够使用Spring Cloud Gateway默认过滤器:全局过滤、局部过滤
-
能够搭建Spring Cloud Config配置中心服务
1.集中管理配置文件
-
能够使用Spring Cloud Bus 消息总线实时更新配置文件
1.每个微服务的通知消息管理服务
1 Spring Cloud Feign
1.1 目标
- 了解Feign的作用
- 掌握Feign的使用过程
- 掌握Feign的负载均衡配置
- 掌握Feign的熔断配置
- 掌握Feign的压缩配置
- 掌握Feign的日志配置
1.2 讲解
1.2.1 Feign简介
Feign [feɪn] 译文 伪装. Feign是一个声明式WebService客户端,使用Feign能让编写WebService客户端更加简单,它的使用方法是定义一个接口,然后在上面添加注解.,不再需要自行拼接URL,参数等操作。项目主页:https://github.com/OpenFeign/feign 。
Feign的由来是基于retrofit(OkHttp的加强版) , jax-rs(应用程序接口) , JSR 356 [Java API for WebSocket(实时通信规范)];其内部对RestTemplate进行了封装,在使用过程中简化了HTTP远程调用过程,使开发人员使用更加简便.
Feign特色如下:
- 集成了Ribbon的负载均衡功能
- 集成了Hystrix的熔断器功能
- 支持请求及响应数据压缩
- 类SLF4J 日志支持
- 响应缓存支持,有多种类型选择
- 支持扩展的缓存,如EhCache, Google, Spring
- 完整的URI 模板表达式语言支持
- Feign以更加优雅的方式编写远程调用代码,并简化重复代码
Feign 工作流程:
1.Feign用来实现远程调用,主要用于微服务之间的调用
@EnableFeignClients 表示开启Feign功能,然后扫描 注解@FeignClient(value = "user-provider",fallback = UserClientFallback.class,configuration = FeignConfig.class) ;user-provider为微服务名/UserClientFallback.class自定义的熔断类/FeignConfig.class自定义的日志类
程序启动后,会将这些类扫描进IOC容器 ; @Component / @Configuration+ @Bean
Feign会 对 RestTemplate 进行 封装,简化HTTP远程过程调用 ;
RestTemplate使用Request 模板生成新的Requst 发送请求,其底层通常是基于URLConnection ;
Feign图解
1.2.2 快速入门
使用Feign替代RestTemplate发送Rest请求,通过user-consumer
调用user-provider
里的查询数据库的服务
实现步骤:
1. 导入Feign依赖
2. 在user-consumer模块编写Feign客户端接口-将请求地址写到该接口上 value的值
3. 消费者启动引导类开启Feign功能注解
4. 访问接口测试
实现过程:
(1)导入依赖
在user-consumer
中添加spring-cloud-starter-openfeign
依赖
<!--配置feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
(2)创建Feign客户端
在user-consumer
中创建com.zl.feign.UserClient
接口,代码如下:
@FeignClient(value = "user-provider")
public interface UserClient {
@RequestMapping(value = "user/find/{id}")
User findById(@PathVariable(value = "id") Integer id);
}
解释说明:
Feign会通过动态代理,帮我们生成实现类。(springcloud 基于 springboot ,springboot中整合了JpaRepository)
注解@FeignClient声明Feign的客户端,注解value指明服务名称
接口定义的方法,采用SpringMVC的注解。Feign会根据注解帮我们生成URL地址
注解@RequestMapping中对应user-provider里的/user...,不要忘记。因为Feign需要拼接可访问地址
(3)编写控制层
在user-consumer
中创建com.zl.controller.ConsumerFeignController
,在Controller中使用@Autowired注入FeignClient,代码如下:
@RestController
@RequestMapping(value = "/feign")
public class ConsumerFeignController {
@Autowired
private UserClient userClient;
/****
* 使用Feign调用user-provider的方法
*/
@RequestMapping(value = "/{id}")
public User queryById(@PathVariable(value = "id")Integer id){
return userClient.findById(id);
}
}
(4)开启Feign
修改user-consumer
的启动类,在启动类上添加@EnableFeignClients
注解,开启Feign,代码如下:
@SpringBootApplication
@EnableEurekaClient //开启Eureka客户端发现功能,注册中心只能是Eureka
@EnableFeignClients //开启Feign, Client有个S
public class UserConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(UserConsumerApplication.class, args);
}
}
(5)测试
请求<http://localhost:18082/feign/2>
1.2.3 负载均衡
Feign自身已经集成了Ribbon,开启Feign后默认开启负载均衡,默认服务地址是轮询策略
无需用注解@LoadBalanced(开启Eureka中集成的Ribbon) 开启负载均衡
Feign内置的ribbon默认设置了请求超时时长,默认是1000,可以修改
ribbon内部有重试机制,一旦超时,会自动重新发起请求。如果不希望重试可以关闭配置
部分配置介绍如下:
# 修改服务地址轮询策略,默认是轮询
user-provider:
ribbon:
#轮询
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
#随机算法
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#重试算法,该算法先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重试,获取可用的服务↓
#可以在idea软件中按ctr shift n或者t搜对应的类进行其中一种算法涉及时间的说明↑
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule
#加权法,会根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越大。刚启动时如果同统计信息不足,则使用轮询的策略,等统计信息足够会切换到自身规则。
#NFLoadBalancerRuleClassName: com.netflix.loadbalancer.ZoneAvoidanceRule
ConnectTimeout: 10000 # 连接超时时间,比如修改服务提供者的ip让你连不上那你消费者肯定连接超时报错
ReadTimeout: 2000 # 数据读取超时时间,这个是连上服务提供者,然后服务器提供者程序执行超过2秒报错
MaxAutoRetries: 1 # 最大重试次数(第一个服务),比如连上了,但读取超时再重去尝试读取1次,控制器被访问↓
MaxAutoRetriesNextServer: 0 # 最大重试下一个服务次数(集群的情况才会用到,这个以后用到再说,类似↑)
OkToRetryOnAllOperations: false # 无论是请求超时或者socket read timeout都进行重试
1.2.4 熔断器
实现步骤:
1. 在配置文件application.yml中开启feign熔断器支持
2. 编写FallBack处理类,实现FeignClient客户端
3. 在接口的@FeignClient注解中,指定FallBack处理类。
4. 测试
(1)开启Hystrix
在配置文件application.yml中开启feign熔断器支持:默认关闭
feign:
hystrix:
enabled: true # 开启Feign的熔断功能
(2)熔断降级类创建
修改user-consumer
,创建一个类com.zl.feign.fallback.UserClientFallback
,实现刚才编写的UserClient接口,作为FallBack的处理类,代码如下:
@Component//注意!!!,这个不能少这里,把对象交给交给spring容器才能调用下面的重写方法,很多人容易犯错!!!
public class UserClientFallback implements UserClient{
/***
* 服务降级处理方法
* @param id
* @return
*/
@Override
public User findById(Integer id) {
User user = new User();
user.setUsername("Feign,服务降级。。。");
return user;
}
}
(3)指定Fallback处理类
在@FeignClient注解中,指定FallBack处理类
@FeignClient(value = "user-provider",fallback = UserClientFallback.class)
public interface UserClient {
@RequestMapping(value = "user/find/{id}")
User findById(@PathVariable(value = "id") Integer id);
}
(4)测试(模拟服务出错)
1.关闭服务提供者,请求<http://localhost:18082/feign/1>
2.请求<http://localhost:18082/feign/8>
id=8在数据库中不存在
1.2.5 请求压缩
SpringCloudFeign支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。
通过配置开启请求与响应的压缩功能:
feign:
compression:
request:
enabled: true # 开启请求压缩
response:
enabled: true # 开启响应压缩
也可以对请求的数据类型,以及触发压缩的大小下限进行设置
# Feign配置
feign:
compression:
request:
enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型
min-request-size: 2048 # 设置触发压缩的大小下限
#以上数据类型,压缩大小下限均为默认值
1.2.6 Feign的日志级别配置
springcloud可通过loggin.level.xx=debug来设置日志级别。然而这个对Feign客户端不会产生效果。因为@FeignClient注解修饰的客户端在被代理时,都会创建一个新的Feign.Logger实例。所以我们需要额外通过编写指定配置类(覆盖之前的配置)的方式指定这个日志的级别才可以。
实现步骤:
1. 在application.yml配置文件中开启日志级别配置
2. 编写配置类,定义日志级别bean。
3. 在接口的@FeignClient中指定配置类
4. 重启项目,测试访问
也可以直接在消费者的启动类写一个日志方法,就不用编写配置类和在接口的@FeignClient中指定配置类了
// 配置日志时,可不写日志配置类,直接在启动加载类用@Bean 把方法写到spring容器中
// Feign支持中的四种日志级别 NONE/BASIC/HEADERS/FULL
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
实现过程:
(1)普通日志等级配置
在user-consumer
的配置文件中设置com.zl包下的日志级别都为debug
# com.zl 包下的日志级别都为Debug
logging:
level:
com.zl: debug
(2)Feign日志等级配置
在user-consumer
中创建com.zl.feign.util.FeignConfig
,定义日志级别
@Configuration
public class FeignConfig {
/***
* 日志级别
* @return
*/
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
日志级别说明:
Feign支持4中级别:
NONE:不记录任何日志,默认值
BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
HEADERS:在BASIC基础上,额外记录了请求和响应的头信息
FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据
(3)指定配置类
修改user-consumer
的com.zl.feign.UserClient
指定上面的配置类,代码如下:
@FeignClient(value = "user-provider",fallback = UserClientFallback.class,configuration = FeignConfig.class)
public interface UserClient {
@RequestMapping(value = "user/find/{id}")
User findById(@PathVariable(value = "id") Integer id);
}
重启项目,即可看到每次访问的日志(DEBUG 记录了所有请求和响应的明细,包括头信息、请求体、元数据)
记录所有请求和响应的明细,包括头信息、请求体、元数据
1.3 小结
-
Feign的作用:不再使用拼接URL的方式实现远程调用,以接口调用的方式实现远程调用,简化了远程调用的实现方式,还增强了远程调用的功能,例如:增加了负载均衡、熔断、压缩、日志启用。
-
掌握Feign的使用过程
1.引入Feign依赖包 2.创建Feign接口,feign接口中需要指定调用的服务名字 3.使用@EnabledFeignClients启用Feign功能
-
掌握Feign的负载均衡配置
在配置文件中配置 {spring.application.name}:ribbon:负载均衡属性配置
-
掌握Feign的熔断配置
1.在application.yml中开启Hystrix 2.给Feign接口创建一个实现类 3.给Feign指定fallback类
-
掌握Feign的压缩配置
在application.yml中指定压缩属性即可
-
掌握Feign的日志配置
1.在application.yml中开启普通日志等级 2.创建一个类,定义Feign日志等级 3.在Feign接口中指定定义日志的配置 23可换成在启动类直接定义一个日志等级的方法用@bean注入到ioc容器
1.4 补充
@FeignClient注解部分属性说明如下图:
2 网关 Spring Cloud Gateway
2.1 目标
- 了解网关的作用
- 掌握配置动态路由
- 掌握配置过滤器
- 掌握自定义全局过滤器
网关作用:为微服务提供统一的路由管理,可以在路由管理基础上进行一系列的过滤,可以做一系列的监控操作,限流。
2.2 讲解
2.2.1 Gateway 简介
Spring Cloud Gateway 是Spring Cloud团队的一个全新项目,基于Spring 5.0、SpringBoot2.0、Project Reactor 等技术开发的网关。 旨在为微服务架构提供一种简单有效统一的API路由管理方式。
Spring Cloud Gateway 作为SpringCloud生态系统中的网关,目标是替代Netflix Zuul。Gateway不仅提供统一路由方式,并且基于Filter链的方式提供网关的基本功能。例如:安全,监控/指标,和限流。
本身也是一个微服务,需要注册到Eureka
网关的核心功能: 过滤、路由
核心概念: API网关是介于客户端和服务器端之间的中间层,所有的外部请求都会先经过API网关这一层。也就是说,网关可以完成安全、性能、监控等功能,而服务提供者可以专门的完成具体的业务逻辑。
- 路由(route): 是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言函数为真,则路由匹配。
- 断言Predicate函数: 路由转发规则,看做一把钥匙
- 过滤器(Filter): org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。
图解:
2.2.2 快速入门
实现步骤:
1. 创建gateway-service模块
2. 编写基础配置
3. 编写路由规则,配置静态路由策略
4. 启动网关服务进行测试
实现过程:
(1)修改gateway-service模块的pom.xml依赖,导入网关依赖和Eureka客户端
<?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>springcloud-parent</artifactId>
<groupId>com.itheima</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gateway-service</artifactId>
<dependencies>
<!--网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
(2)启动类
创建启动类com.zl.GatewayApplication
,代码如下:
@SpringBootApplication
@EnableDiscoveryClient// 开启Eureka客户端发现功能
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
(3)application.yml配置
# 注释版本
server:
port: 18084
spring:
application:
name: api-gateway # 应用名
# Eureka服务中心配置
eureka:
client:
service-url:
# 注册Eureka Server集群
defaultZone: http://127.0.0.1:7001/eureka
2.2.3 路由配置
通过网关配置一个路由功能,用户访问网关的时候,如果用户请求的路径是以/user
开始,则路由到user-provider
服务去,修改application.yml配置即可实现,配置如下:
spring:
application:
# 应用名
name: api-gateway
cloud:
gateway:
routes:
#id唯一标识,可自定义
- id: user-service-route
#路由的服务地址,user-provider的端口
uri: http://localhost:18081
# 路由拦截的地址配置(断言)
predicates:
- Path=/user/**
启动GatewayApplication测试
访问http://localhost:18084/user/find/2
会访问user-provider
服务
2.2.4 动态路由
刚才路由规则中,我们把路径对应服务地址(uri)写死了!如果服务提供者集群的话,这样做不合理。应该是根据服务名称,去Eureka注册中心查找服务对应的所有实例列表,然后进行动态路由!
修改映射配置:通过服务名称获取:
因为已经配置了Eureka客户端,可以从Eureka获取服务的地址信息,修改application.yml文件如下:
spring:
application:
# 应用名
name: api-gateway
cloud:
gateway:
routes:
#id唯一标识,可自定义
- id: user-service-route
#路由的服务地址
#uri: http://localhost:18081
#lb协议表示从Eureka注册中心获取服务请求地址,巧记low b才使用ip访问,应该用服务名集群访问,lb↓
#user-provider访问的服务名称。
#路由地址如果通过lb协议加服务名称时,会使用负载均衡访问对应服务,负载均衡LoaderBalance,lb↑
uri: lb://user-provider
# 路由拦截的地址配置(断言)
predicates:
- Path=/user/**
路由配置中uri所用的协议为lb时,gateway将把user-provider解析为实际的主机和端口,并通过Ribbon进行负载均衡。
2.2.5 过滤器
过滤器作为Gateway的重要功能。常用于请求鉴权、服务调用时长统计、修改请求或响应header、限流、去除路径等等…
2.2.5.1 过滤器的分类
默认过滤器:出厂自带,实现好了拿来就用,不需要实现
全局默认过滤器
局部默认过滤器
自定义过滤器:根据需求自己实现,实现后需配置,然后才能用哦。
全局过滤器:作用在所有路由上。
局部过滤器:配置在具体路由下,只作用在当前路由上。根据id作用
默认过滤器几十个,常见如下:
过滤器名称 | 说明 |
---|---|
AddRequestHeader | 对匹配上的请求加上Header |
AddRequestParameters | 对匹配上的请求路由 |
AddResponseHeader | 对从网关返回的响应添加Header |
StripPrefix | 对匹配上的请求路径去除前缀,巧记S截取然后tri去除p,path路径前缀 |
详细说明官方链接
2.2.5.2 默认过滤器配置
全局过滤器:对输出响应头设置属性
对输出的响应设置其头部属性名称为X-Response-Default-MyName,值为lyt
修改application.yml文件
spring:
cloud:
gateway:
# 配置全局默认过滤器
default-filters:
# 往响应过滤器中加入信息
- AddResponseHeader=X-Response-Default-MyName,lyt
可查看浏览器响应头里信息!
局部过滤器: 通过局部默认过滤器,修改请求路径。局部过滤器在这里介绍两种:添加路径前缀、去除路径前缀。
第一:添加路径前缀:
在gateway中可以通过配置路由的过滤器PrefixPath 实现映射路径中的前缀
修改application.yml文件
spring:
application:
# 应用名
name: api-gateway
cloud:
gateway:
routes:
#id唯一标识,可自定义
- id: user-service-route
#路由的服务地址
#uri: http://localhost:18081
#lb协议表示从Eureka注册中心获取服务请求地址
#user-provider访问的服务名称。
#路由地址如果通过lb协议加服务名称时,会自动使用负载均衡访问对应服务
uri: lb://user-provider
# 路由拦截的地址配置(断言),拦截所有请求,给请求地址加上前缀/user
predicates:
- Path=/**
# 局部过滤器,写在哪个id下就作用于哪个id的uri
filters:
# 请求地址添加路径前缀过滤器,只针对当前id对应的微服务地址有效
- PrefixPath=/user
拦截所有请求,给请求地址加上/**匹配的地址钱,加上前缀/user,路由地址信息:
配置 | 访问地址 | 路由地址 (实际地址) |
---|---|---|
PrefixPath=/user | http://localhost:18084/find/2 | http://localhost:18081/user/find/2 |
第二:去除路径前缀:
在gateway中通过配置路由过滤器StripPrefix,实现映射路径中地址的去除。通过StripPrefix=1来指定路由要去掉的前缀个数。如:路径/**/user/1将会被路由到/user/1。
修改application.yml文件
spring:
application:
# 应用名
name: api-gateway
cloud:
gateway:
routes:
#id唯一标识,可自定义
- id: user-service-route
#路由的服务地址
#uri: http://localhost:18081
#lb协议表示从Eureka注册中心获取服务请求地址
#user-provider访问的服务名称。
#路由地址如果通过lb协议加服务名称时,会自动使用负载均衡访问对应服务
uri: lb://user-provider
# 路由拦截的地址配置(断言)
predicates:
- Path=/**
filters:
# 请求地址添加路径前缀过滤器
#- PrefixPath=/user
# 去除路径前缀过滤器
- StripPrefix=1
default-filters:
- AddResponseHeader=X-Response-Default-MyName,lyt
路由地址信息:
配置 | 访问地址 | 路由地址 |
---|---|---|
StripPrefix=1 | http://localhost:18084/api/user/find/2 | http://localhost:18081/user/find/2 |
StripPrefix=2 | http://localhost:18084/api/api/user/find/2 | http://localhost:18081/user/find/2 |
2.2.5.3 自定义过滤器案例
自定义全局过滤器的案例: 模拟登陆校验。
基本逻辑:如果请求中有token(口令)参数,则认为请求有效放行,如果没有则拦截提示授权无效
2.2.5.3.1 全局过滤器自定义:
实现步骤:
1.在gateway-service模块编写全局过滤器类GlobalFilter接口
2.编写业务逻辑代码
3.访问接口测试,加token和不加token。
全局过滤器作用范围:所有请求都会被拦截。
实现过程:
在gateway-service
中创建com.zl.filter.LoginGlobalFilter
全局过滤器类,代码如下:
@Component//这个不能少,创建对象才能调用下面的非静态方法↓
public class LoginGlobalFilter implements GlobalFilter, Ordered {
//以后可能有多个自定义过滤器类要排序所以实现Ordered接口
/***
* 过滤拦截
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//获取请求参数
String token = exchange.getRequest().getQueryParams().getFirst("token");
//如果token为空,则表示没有登录
if(StringUtils.isEmpty(token)){
//没登录,状态设置403,点HttpStatus查看源码可了解各种报错状态
exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
//结束请求
return exchange.getResponse().setComplete();
}
//放行
return chain.filter(exchange);
}
/***
* 定义过滤器执行顺序
* 返回值越小,越靠前执行,可以理解为默认从小到大执行
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
测试:不携带token 访问 <http://localhost:18084/api/user/find/2>
时报错 403
测试:携带token <http://localhost:18084/api/user/find/2?token=abc>
此时可以正常访问。
2.2.5.3.2 局部过滤器定义
自定义局部过滤器,该过滤器在控制台输出配置文件中指定名称的请求参数及参数的值
实现步骤:
1. 在gateway-service中编写MyParamGatewayFilterFactory类去继承AbstractGatewayFilterFactory类
2. 实现业务代码:执行局部过滤器
3. 修改application.yml文件
4. 访问请求进行测试
局部过滤器作用范围:该局部过滤器在哪个id下配置,则该局部过滤器只针对该id的路由规则有效。
实现过程:
在gateway-service中编写MyParamGatewayFilterFactory类
@Component
public class MyParamGatewayFilterFactory extends AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> {
/****
* 处理过程
* @param config
* @return
*/
@Override
public GatewayFilter apply(MyParamGatewayFilterFactory.Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("局部过滤器执行啦!");
return chain.filter(exchange); // 返回数据
}
};
}
/***
* 构造函数
*/
public MyParamGatewayFilterFactory() {
super(MyParamGatewayFilterFactory.Config.class);
}
/****
* 在该类执行初始化后,可以在这里执行相关初始化操作
*/
public static class Config {
}
}
修改application.yml配置文件
spring:
application:
# 应用名
name: api-gateway
cloud:
gateway:
routes:
#id唯一标识,可自定义
- id: user-service-route
#路由的服务地址
#uri: http://localhost:18081
#lb协议表示从Eureka注册中心获取服务请求地址
#user-provider访问的服务名称。
#路由地址如果通过lb协议加服务名称时,会自动使用负载均衡访问对应服务
uri: lb://user-provider
# 路由拦截的地址配置(断言)
predicates:
- Path=/**
filters:
# 请求地址添加路径前缀过滤器
#- PrefixPath=/user
# 去除路径前缀过滤器
- StripPrefix=1
# 自定义过滤器
- MyParam
default-filters:
- AddResponseHeader=X-Response-Default-MyName,lyt
测试 访问 <http://localhost:18084/api/user/find/2>
后台输出局部过滤器执行啦!
2.2.6 微服务架构加入Gateway后
- 不管是来自客户端的请求,还是服务内部调用。一切对服务的请求都可经过网关。
- 网关实现鉴权、动态路由等等操作。
- Gateway就是我们服务的统一入口
2.3 小结
-
网关的作用
1.为微服务架构提供一种简单有效统一的API路由管理方式 2.可以在网关中实现微服务鉴权、安全控制、请求监控、限流
-
会配置动态路由
使用lb配置,能根据服务名字动态请求。
-
会配置过滤器
默认全局过滤器 : cloud.gateway.default-filters: 默认局部过滤器 : cloud.gateway.routes.filters:
-
能自定义全局过滤器
编写过滤器类,实现GlobalFilter和Ordered,在filter方法中实现过滤。
3 配置中心 Spring Cloud Config
3.1 目标
- 了解配置中心的作用
- 能配置Git仓库
- 能搭建配置中心
- 修改微服务,从配置中心获取修改的配置
3.2 讲解
3.2.1 Config简介
分布式系统中,由于服务数量非常多,配置文件分散在不同微服务项目中,管理极其不方便。为了方便配置文件集中管理,需要分布式配置中心组件。在Spring Cloud中,提供了Spring Cloud Config,它支持配置文件放在配置服务的本地,也支持配置文件放在远程仓库Git(GitHub、码云)。配置中心本质上是一个微服务,同样需要注册到Eureka服务中心!
【配置中心的架构图】
3.2.2 Git配置管理
3.2.2.1 远程Git仓库
- 知名的Git远程仓库有国外的GitHub和国内的码云(gitee);
- GitHub主服务在外网,访问经常不稳定,如果希望服务稳定,可以使用码云;
- 码云访问地址:http://gitee.com
测试地址:
https://gitee.com/skllll/config.git
3.2.2.2 创建远程仓库
首先使用码云上的git仓库需要先注册账户
账户注册完成,然后使用账户登录码云控制台并创建公开仓库
再配置仓库 名称和路径
3.2.2.3 创建配置文件
在新建的仓库中创建需要被统一配置管理的配置文件
文件命名有规则:
配置文件的命名方式:{application}-{profile}.yml或{application}-{profile}.properties
application为应用名称
profile用于区分开发环境dev,测试环境test,生产环境pro等
开发环境 user-dev.yml
测试环境 user-test.yml
生产环境 user-pro.yml
创建一个user-provider-dev.yml文件
将user-provider工程里的配置文件application.yml内容复制进去。
3.2.3 搭建配置中心微服务
实现步骤:
1. 新建模块config-server
2. 配置坐标依赖
3. 启动类添加开启配置中心服务注解
4. 配置服务中心application.yml文件
5. 启动测试
实现过程:
(1)pom.xml中导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud-parent</artifactId>
<groupId>com.zl</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>config-server</artifactId>
<dependencies>
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--配置中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
</project>
(2)创建启动类
在config-server
模块中创建启动类com.zl.ConfigServerApplication
,代码如下:
@SpringBootApplication
@EnableDiscoveryClient//开启Eureka客户端发现功能
@EnableConfigServer//开启配置服务支持
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class,args);
}
}
(3)application.yml配置文件
# 注释版本
server:
port: 18085 # 端口号
spring:
application:
name: config-server # 应用名
cloud:
config:
server:
git:
# 配置gitee的仓库地址
uri: https://gitee.com/leiyating/config.git
# Eureka服务中心配置
eureka:
client:
service-url:
# 注册Eureka Server集群
defaultZone: http://127.0.0.1:7001/eureka
# com.itheima 包下的日志级别都为Debug
logging:
level:
com: debug
注意:上述spring.cloud.config.server.git.uri是在码云创建的仓库地址
(5)测试
启动config-server
,访问<http://localhost:18085/user-provider-dev.yml>
,得到user-provider的yml文件
这样可以查看到码云上的文件数据,并且可以在gitee上修改user-dev.yml(开发环境),然后刷新上述测试地址也能及时更新数据
3.2.4 服务去获取配置中心配置
目标:改造user-provider工程,配置文件不再由微服务项目提供,而是从配置中心获取。
实现步骤:
1. 添加配置中心客户端启动依赖
2. 修改服务提供者的配置文件
3. 启动服务
实现过程:
(1)添加依赖
<!--spring cloud 配置中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
(2)修改配置
删除user-provider工程的application.yml文件
创建user-provider工程bootstrap.yml配置文件,配置内容如下
# 注释版本
spring:
cloud:
config:
name: user-provider # 与远程仓库中的配置文件的application保持一致,{application}-{profile}.yml
profile: dev # 远程仓库中的配置文件的profile保持一致
label: master # 远程仓库中的版本保持一致
discovery:
enabled: true # 使用配置中心
service-id: config-server # 配置中心服务id
#向Eureka服务中心集群注册服务
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
关于application.yml和bootstrap.yml文件的说明:
- bootstrap.yml文件是SpringBoot的默认配置文件,而且其加载时间相比于application.yml更早。
- bootstrap.yml和application.yml都是默认配置文件,但定位不同
- bootstrap.yml可以理解成系统级别的一些参数配置,一般不会变动
- application.yml用来定义应用级别的参数
- 搭配spring-cloud-config使用application.yml的配置可以动态替换。
- bootstrap.yml相当于项目启动的引导文件,内容相对固定
- application.yml文件是微服务的常规配置参数,变化比较频繁
3.2.5 配置中心存在的问题↓黄色高亮说明↓
(1)修改码云配置文件
修改在码云上的user-provider-dev.yml文件,添加一个属性test.message
(2)读取配置文件数据
在user-provider
工程中创建一个com.itheima.controller.LoadConfigController
读取配置文件信息,代码如下:
@RestController
@RequestMapping(value = "/config")
public class LoadConfigController {
@Value("${test.message}")
private String msg;
/***
* 响应配置文件中的数据
* @return
*/
@RequestMapping(value = "/load")
public String load(){
return msg;
}
}
启动运行user-provider
,访问<http://localhost:18081/config/load>
修改码云上的配置后,发现项目中的数据仍然没有变化,只有项目重启后才会变化↓那怎么解决项目不重启就更新码云上的配置文件到项目呢?下面会介绍Bus
3.3 小结
-
配置中心的作用:将各个微服务的配置文件集中到一起进行统一管理。
-
能搭建配置中心
需要在application.yml配置文件中指定需要远程更新的仓库地址。
-
修改微服务,从配置中心获取修改的配置
创建bootstrap.yml,并在bootstrap.yml中配置 # 注释版本 spring: cloud: config: name: user-provider # 与远程仓库中的配置文件的application保持一致,{application}-{profile}.yml profile: dev # 远程仓库中的配置文件的profile保持一致 label: master # 远程仓库中的版本保持一致 discovery: enabled: true # 使用配置中心 service-id: config-server # 配置中心服务id #向Eureka服务中心集群注册服务 eureka: client: service-url: defaultZone: http://127.0.0.1:7001/eureka
4 消息总线 Spring Cloud Bus
SpringCloud Bus,解决上述问题,实现配置自动更新↑。
注意:SpringCloudBus基于RabbitMQ实现,默认使用本地的消息队列服务,所以需要提前安装并启动RabbitMQ
4.1 Bus简介
Bus是用轻量的消息代理将分布式的节点连接起来,可以用于广播配置文件的更改或者服务的监控管理。
Bus可以为微服务做监控,也可以实现应用程序之间互相通信。Bus可选的消息代理RabbitMQ和Kafka。
广播出去的配置文件服务会进行本地缓存。
4.2 整合案例
目标:消息总线整合入微服务系统,实现配置中心的配置自动更新。不需要重启微服务。
4.2.1 改造配置中心
改造步骤:
- 在config-server项目中加入Bus相关依赖
- 修改application.yml,加入RabbitMQ的配置信息,和暴露触发消息总线地址
实现过程:
(1)引入依赖
修改config-server
的pom.xml引入依赖:
<!--消息总线依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<!--RabbitMQ依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
(2)修改application.yml配置文件
修改config-server
的application.yml,如下配置的rabbit都是默认值,其实可以完全不配置
# 注释版本
server:
port: 18085 # 端口号
spring:
application:
name: config-server # 应用名
cloud:
config:
server:
git:
# 配置gitee的仓库地址
uri: https://gitee.com/leiyating/config.git
# rabbitmq的配置信息;如下配置的rabbit都是默认值,其实可以完全不配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
# 暴露触发消息总线的地址
management:
endpoints:
web:
exposure:
# 暴露触发消息总线的地址,这个主意↓↓↓,下面黄色高亮处有说明
include: bus-refresh
# Eureka服务中心配置
eureka:
client:
service-url:
# 注册Eureka Server集群
defaultZone: http://127.0.0.1:7001/eureka
# com.itheima 包下的日志级别都为Debug
logging:
level:
com: debug
4.2.2 改造用户服务
改造步骤:
1. 在用户微服务user-provider项目中加入Bus相关依赖
2. 修改user-provider项目的bootstrap.yml,加入RabbitMQ的配置信息
3. UserController类上加入@RefreshScope刷新配置注解
4. 测试
实现过程:
(1)引入依赖,多了一个健康监控依赖↓
修改user-provider
引入如下依赖:
<!--消息总线依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<!--RabbitMQ依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<!--健康监控依赖,↑-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
(2)添加bootstrap.yml文件
在user-provider
的resources目录下添加bootstrap.yml,添加rabbitmq配置,代码如下:
# 注释版本
spring:
cloud:
config:
name: user-provider # 与远程仓库中的配置文件的application保持一致,{application}-{profile}.yml
profile: dev # 远程仓库中的配置文件的profile保持一致
label: master # 远程仓库中的版本保持一致
discovery:
enabled: true # 使用配置中心
service-id: config-server # 配置中心服务id
# rabbitmq的配置信息;如下配置的rabbit都是默认值,其实可以完全不配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
#向Eureka服务中心集群注册服务
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
(3)添加刷新配置
修改user-provider
的com.itheima.controller.LoadConfigController
,添加一个@RefreshScope
注解刷新配置信息,代码如下:
@RestController
@RefreshScope
@RequestMapping(value = "/config")
public class LoadConfigController {
@Value("${test.message}")
private String msg;
/***
* 响应配置文件中的数据
* @return
*/
@RequestMapping(value = "/load")
public String load(){
return msg;
}
}
@RefreshScope:用于启用刷新配置文件的信息。
4.3 测试
目标:当我们修改Git仓库的配置文件,用户微服务是否能够在不重启的情况下自动更新配置文件信息。
测试步骤:
(1)启动eureka-server
(2)启动config-server
(3)启动user-provider
(4)访问测试
访问<http://localhost:18081/config/load>
(5)修改码云配置
修改码云的配置文件,修改后并提交
(6)刷新配置
使用Postman以POST方式请求http://localhost:18085/actuator/bus-refresh
请求地址中actuator是固定的,bus-refresh对应的是配置中心的config-server中的application.yml文件的配置项include的内容,上面说注意的写法原因在此↑↑↑
(7)刷新测试
再次访问<http://localhost:18081/config/load>
消息总线实现消息分发过程:
- 请求地址访问配置中心的消息总线
- 消息总线接收到请求
- 消息总线向消息队列发送消息
- user-service微服务会监听消息队列
- user-service微服务接到消息队列中消息后
- user-service微服务会重新从配置中心获取最新配置信息