SpringBoot和SpringCloud配置
一、springBoot配置
1.1、yml
端口
server:
port: 8089
应用名称
spring:
application:
name: eureka-server
项目访问路径
server:
servlet:
context-path: /java521
数据库
spring:
datasource:
url: jdbc:mysql://localhost:3306/user?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
#url: jdbc:mysql://localhost:3306/user?characterEncoding=utf8&autoReconnect=true&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
mybatis
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml #xml路径
type-aliases-package: com.java521.pojo #扫描包
redis
spring:
redis:
host: localhost
port: 6379
password:
database: 0
1.2、pom
打包插件
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
1.3、RestTemplate
在Application启动类中添加
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
然后使用的时候
@Autowired
private RestTemplate restTemplate;
二、注册中心 Spring Cloud Eureka
官方推荐端口 8761
2.1、yml配置
应用名称
spring:
application:
name: eureka-server # 应用名称,会在Eureka中作为服务的id标识(serviceId)
eureka配置
eureka:
client:
service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要写其它Server的地址。
defaultZone: http://127.0.0.1:8761/eureka
关闭自己注册自己 不设置会报错 但不影响使用 如果是集群 需要开启
eureka:
client:
fetch-registry: false #是否抓取注册列表
register-with-eureka: false #是否注册服务中心Eureka
在生产者和消费者pom文件中添加eureka中的
<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>
</dependencies>
</dependencyManagement>
并且要添加版本到properties中
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
除此之外,生产者和消费者还需要添加eureka客户端到dependencies中
<!--eureka客户端starter-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
生产者和消费者修改配置文件:spring.application.name指定应用名称,作为服务ID使用,并配置eureka注册中心地址
spring:
application:
name: provider-service
#配置eurekaserver
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka
Eureka Application启动类
添加@EnableEurekaServer
声明是一个eureka服务端
将服务注册到注册中心, 在生产者和消费者application中添加注解,二选一
@EnableEurekaClient //将服务注册到eureka
@EnableDiscoveryClient //将服务注册到注册中心
消费者使用
添加依赖注入
@Autowired
private DiscoveryClient discoveryClient;
消费者通过注册中心调用生产者
//获取服务列表
List<ServiceInstance> list = discoveryClient.getInstances("provider-service");
//获取uri
URI uri = list.get(0).getUri();
//url连接
String url = uri + "/user/findUserById/" + id; //请求服务者地址
User user = restTemplate.getForObject(url, User.class); //类的class
return user;
2.2、注意
如果打开eureka使用的是主机名
想用ip进行注册添加如下配置, 在生产者中和消费者中添加
eureka:
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port} #管控台展示服务ip+port
2.3、基础架构
Eureka架构中的三个核心角色
- 服务注册中心:Eureka服务端应用,提供服务注册发现功能,eureka-server
- 服务提供者:提供服务的应用
- 要求统一对外提供Rest风格服务即可
- 本例子:provider-service
- 服务消费者:从注册中心获取服务列表,知道去哪调用服务方,consumer-service
2.4、Eureka客户端
服务提供者要向EurekaServer注册服务,并完成服务续约等工作
服务注册过程:
- 当我们导入了eureka-client依赖坐标,配置Eureka服务注册中心地址
- 服务在启动时,会检测是否有@DiscoveryClient注解和配置信息
- 如果有,则会向注册中心发起注册请求,携带服务元数据信息
- Eureka注册中心会把服务的信息保存在Map中。
服务续约过程:
服务每隔30秒会向注册中心续约(心跳)一次,如果没有续约,租约在90秒后到期,然后服务会被失效。每隔30秒的续约操作我们称之为:心跳
检测。
#向Eureka服务中心集群注册服务
eureka:
instance:
# 租约续约间隔时间,默认30秒
lease-renewal-interval-in-seconds: 30
# 租约到期,服务时效时间,默认值90秒
lease-expiration-duration-in-seconds: 90
- 两个参数可以修改服务续约行为
- lease-renewal-interval-seconds:90,租约到期时效时间,默认90秒
- lease-expiration-duration-in-seconds:30,租约续约间隔时间,默认30秒
- 服务超过90秒没有发生心跳,EurekaServer会将服务从列表移除
获取服务列表:
每隔30秒服务会从注册中心中拉取一份服务列表
这个时间可以通过配置修改
#向Eureka服务中心集群注册服务
eureka:
client:
# 每隔多久获取服务中心列表,(只读备份)
registry-fetch-interval-seconds: 30
- 服务消费者启动时,会检测是否获取服务注册信息配置
- 如果是,则会从 EurekaServer服务列表获取只读备份,缓存到本地
- 每隔30秒,会重新获取并更新数据
- 每隔30秒的时间可以通过配置
registry-fetch-interval-seconds
修改
2.5、Eureka服务端
服务下线:
- 当服务正常关闭操作时,会发送服务下线的REST请求给EurekaServer。
- 服务中心接受到请求后,将该服务置为下线状态
失效剔除:
Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认值为60)进行检查,如果发现实例在在一定时间(此值由客户端设置的eureka.instance.lease-expiration-duration-in-seconds定义,默认值为90s)内没有收到心跳,则会注销此实例。
自我保护:
Eureka会统计服务实例最近15分钟心跳续约的比例是否低于85%,如果低于则会触发自我保护机制。
服务中心页面会显示如下提示信息
含义:紧急情况!Eureka可能错误地声称实例已经启动,而事实并非如此。续约低于阈值,因此实例不会为了安全而过期。
- 自我保护模式下,不会剔除任何服务实例
- 自我保护模式保证了大多数服务依然可用
- 通过
enable-self-preservation
配置可用关停自我保护,默认值是打开
#向Eureka服务中心集群注册服务
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
三、负载均衡 Spring Cloud Ribbon [ˈrɪbən]
3.1、实现负载均衡访问用户服务。
如果想要做负载均衡,我们的服务至少2个以上,所以我们先来在idea中启动两个生产者。
实现步骤:
第一步:启动多个provider-service服务
第二步:开启消费者负载均衡
1、在RestTemplate的注入方法上加入@LoadBalanced注解
@Bean
@LoadBalanced//开启负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
2、修改调用请求的Url地址,改为服务名称调用
String url = "http://provider-service/user/findUserById/" + id; //通过服务名称调用
3.2、负载策略
Ribbon内置了多种负载均衡策略,内部负责复杂均衡的顶级接口为:com.netflix.loadbalancer.IRule ,实现方式如下 :
- com.netflix.loadbalancer.RandomRule :随机选择一个server
- com.netflix.loadbalancer.RetryRule :对选定的负载均衡策略机上重试机制。
- com.netflix.loadbalancer.RoundRobinRule :以轮询的方式进行负载均衡
- com.netflix.loadbalancer.WeightedResponseTimeRule :根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。
- com.netflix.loadbalancer.AvailabilityFilteringRule :可用过滤策略,过滤掉故障和请
求数超过阈值的服务实例,再从剩下的实例中轮询调用(默认)
在服务消费者的application.yml配置文件中修改负载均衡策略,格式:
{服务提供者名称}.ribbon.NFLoadBalancerRuleClassName
# 修改服务地址轮询策略,默认是轮询,配置之后变随机,RandomRule
provider-service:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
3.3、Ribbon关键组件
- ServerList:可以响应客户端的特定服务的服务器列表。
- ServerListFilter:可以动态获得的具有所需特征的候选服务器列表的过滤器。
- ServerListUpdater:用于执行动态服务器列表更新。
- Rule:负载均衡策略,用于确定从服务器列表返回哪个服务器。
- Ping:客户端用于快速检查服务器当时是否处于活动状态。
- LoadBalancer:负载均衡器,负责负载均衡调度的管理。
四、熔断器 Spring Cloud Hystrix
4.1、Hystrix 简介
Hystrix,英文意思是豪猪,全身是刺,刺是一种保护机制。Hystrix也是Netflix公司的一款组件。
Hystrix的作用是什么?
Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库、防止出现级联失败也就是雪崩效应。
4.2、雪崩效应
- 微服务中,一个请求可能需要多个微服务接口才能实现,会形成复杂的调用链路。
- 如果某服务出现异常,请求阻塞,用户得不到响应,容器中线程不会释放,于是越来越多用户请求堆积,越来越多线程阻塞。
- 单服务器支持线程和并发数有限,请求如果一直阻塞,会导致服务器资源耗尽,从而导致所有其他服务都不可用,从而形成雪崩效应;
Hystrix解决雪崩问题的手段,主要是服务降级**(兜底)**,线程隔离;
4.3、熔断案例
目标:服务提供者的服务出现了故障,服务消费者快速失败给用户友好提示。体验服务降级
实现步骤:
1、引入熔断的starter依赖坐标
2、消费者服务开启熔断的注解
3、编写服务降级处理的方法
4、配置熔断的策略
5、模拟异常代码
6、测试熔断服务效果
实现过程:
1、引入熔断的依赖坐标:
consumer-service中加入依赖
<!--熔断Hystrix starter-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2、开启熔断的注解
@SpringCloudApplication =等同于@SpringBootApplication+@EnableDiscoveryClient+@EnableCircuitBreaker
//注解简化写法:微服务中,注解往往引入多个,简化注解可以使用组合注解。@SpringCloudApplication =等同于@SpringBootApplication+@EnableDiscoveryClient+@EnableCircuitBreaker
/*
@SpringBootApplication
@EnableDiscoveryClient//开启服务发现
@EnableCircuitBreaker//开启熔断
*/
@SpringCloudApplication
public class ConsumerApplication {
@Bean
@LoadBalanced//开启负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}
3、编写服务降级处理方法:使用@HystrixCommand定义fallback方法。
注意:熔断服务降级方法必须保证与被降级方法相同的参数列表和返回值。
@GetMapping("/findUserById/{id}")
@HystrixCommand(fallbackMethod ="fallBackMethod")
public User findUserById(@PathVariable Integer id){
String url = "http://provider-service/user/findUserById/" + id;
return restTemplate.getForObject(url,User.class);
}
//熔断方法
//要求: 1、返回数据类型必须与原方法一致
// 2、入参必须与原方法一致
public User fallBackMethod(Integer id){
User user = new User();
user.setId(id);
user.setName("熔断方法");
return user;
}
4、配置熔断策略
- 常见熔断策略配置
- 熔断后休眠时间:sleepWindowInMilliseconds
- 熔断触发最小请求次数:requestVolumeThreshold
- 熔断触发错误比例阈值:errorThresholdPercentage
- 熔断超时时间:timeoutInMilliseconds
# 配置熔断策略:
hystrix:
command:
default:
circuitBreaker:
forceOpen: false # 强制打开熔断器 默认false关闭的
errorThresholdPercentage: 50 # 触发熔断错误比例阈值,默认值50%
sleepWindowInMilliseconds: 5000 # 熔断后休眠时长,默认值5秒
requestVolumeThreshold: 20 # 熔断触发最小请求次数,默认值是20
execution:
isolation:
thread:
timeoutInMilliseconds: 1000 # 熔断超时设置,默认为1秒
5、停止生产者服务,模拟服务异常,然后访问消费者会发现走了熔断方法。
6、测试访问超时:服务提供者线程休眠超过5秒,访问消费者触发fallback方法。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
/**
* @Author: guodong
* @Date: 15:04 2019/10/8
* @Description: 根据id查找用户
*/
@Override
public User findUserById(Integer id) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return userMapper.findUserById(id);
}
}
7、测试
4.4、总结
五、远程调用 Spring Cloud Feign
Feign 的英文表意为“假装,伪装,变形”, 是一个http请求调用的轻量级框架,是以Java接口注解的方式调用Http请求,而不用像Java中通过封装HTTP请求报文的方式直接调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。
Feign将请求的发送伪装到接口中
**封装了Http调用流程,更符合面向接口化的编程习惯。**类似Dubbo服务调用。
5.1、入门案例
1、导入依赖
在consumer-service中添加spring-cloud-starter-openfeign
依赖
<!--配置feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2、Feign的客户端
在consumer-service中编写Feign客户端接口类ConsumerService
@FeignClient(value = "provider-service")
public interface ConsumerService {
@RequestMapping("/user/findUserById/{id}")
User findUserById(@PathVariable("id") Integer id);
}
- Feign会通过动态代理,帮我们生成实现类。
- 注解@FeignClient声明Feign的客户端,指明服务名称
- 接口定义的方法,采用SpringMVC的注解。Feign会根据注解帮我们生成URL地址
3、编写ConsumerFeignController,使用ConsumerService访问
@Autowired注入ConsumerService
@FeignClient(value = "provider-service")
public interface UserService {
@GetMapping("/user/findUserById/{id}")
User findUserById(@PathVariable("id") Integer id);
}
4、开启Feign功能
在ConsumerApplication启动类上,添加@EnableFeignClients
注解,开启Feign功能
@SpringCloudApplication
@EnableFeignClients //开启Feign自动配置支持
public class ConsumerServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerServerApplication.class, args);
}
}
5、启动测试:访问接口
http://localhost:8081/user/findUserById/2,正常获取结果
5.2、注意
/1 Resuful方式使用@PathVariable
?id=1 问号传参方式使用@RequestParam
5.3、熔断器支持
Feign本身也集成Hystrix熔断器,starter内查看。
服务降级方法实现步骤:
1、在配置文件application.yml中开启feign熔断器支持
2、编写FallBack处理类,实现FeignClient客户端
3、在@FeignClient注解中,指定FallBack处理类。
4、测试服务降级效果
实现过程:
1、在配置文件application.yml中开启feign熔断器支持:默认关闭
feign:
hystrix:
enabled: true # 开启Feign的熔断功能
2、 定义一个类ConsumerServiceImpl,实现刚才编写的ConsumerService,作为FallBack的处理类
@Component
public class ConsumerServiceImpl implements ConsumerService {
//熔断方法
@Override
public User findUserById(Integer id) {
User user = new User();
user.setId(id);
user.setNote("网络异常,请稍后再试...");
return user;
}
}
3、在@FeignClient注解中,指定FallBack处理类。。
@FeignClient(value = "provider-service",fallback = ConsumerServiceImpl.class)
public interface ConsumerService {
// String url = "http://provider-service/user/findUserById/" + id;
@RequestMapping("/user/findUserById/{id}")
User findUserById(@PathVariable("id") Integer id);
}
4、重启测试:关闭provider-service服务,然后在页面访问;http://localhost:8081/consumer/findUserById/1
5.4、请求压缩和响应压缩
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 # 设置触发压缩的大小下限
#以上数据类型,压缩大小下限均为默认值
5.5、配置日志级别
在发送和接收请求的时候,Feign定义了日志的输出定义了四个等级:这里我们配置测试一下。
级别 | 说明 |
---|---|
NONE | 不做任何记录 |
BASIC | 只记录输出Http 方法名称、请求URL、返回状态码和执行时间 |
HEADERS | 记录输出Http 方法名称、请求URL、返回状态码和执行时间 和 Header 信息 |
FULL | 记录Request 和Response的Header,Body和一些请求元数据 |
实现步骤:
- 在application.yml配置文件中开启日志级别配置
- 编写配置类,定义日志级别bean。
- 在接口的@FeignClient中指定配置类
- 重启项目,测试访问
实现过程:
1、在consumer-service的配置文件中设置com.itheima包下的日志级别都为debug
# com.itheima 包下的日志级别都为Debug
logging:
level:
com.itheima: debug
2、在consumer-service编写配置类,定义日志级别
@Configuration
public class FeignLogLevleConfig {
//采用full打印日志
@Bean
public Logger.Level configLog(){
return Logger.Level.FULL;
}
}
3、在consumer-service的ConsumerService中指定配置类
@FeignClient(value = "provider-service",
fallback = ConsumerServiceImpl.class,
configuration = FeignLogLevleConfig.class)
public interface ConsumerService {
@RequestMapping("/user/findUserById/{id}")
User findUserById(@PathVariable("id") Integer id);
}
4、重启项目,即可看到每次访问的日志
六、网关 Spring Cloud Gateway
6.1、简介
Spring Cloud Gateway 是Spring Cloud团队的一个全新项目,基于Spring 5.0、SpringBoot2.0、Project Reactor 等技术开发的网关。旨在为微服务架构提供一种简单有效统一的REST 请求路由管理方式。
Spring Cloud Gateway 作为SpringCloud生态系统中的网关,目标是替代Netflix Zuul。Gateway不仅提供统一路由方式,并且基于Filter链的方式提供网关的基本功能。例如:安全,监控/指标,和限流。
本身也是一个微服务,需要注册到Eureka
**网关的核心功能:**过滤(权限)、路由
核心概念:
- 路由(route):
- **断言Predicate函数:**路由转发规则
- 过滤器(Filter):
- 不管是来自客户端的请求,还是服务内部调用。一切对服务的请求都可经过网关。
- 网关实现鉴权、动态路由等等操作。
- Gateway是我们服务的统一入口
6.2、快速入门
搭建网关微服务,实现服务路由分发。
实现步骤:
- 创建SpringBoot工程gateway-server
- 勾选starter:网关、Eureka客户端
- 编写基础配置
- 编写路由规则
- 启动网关服务进行测试
实现过程:
1、创建SpringBoot工程gateway-server[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hzs6QCZd-1615724762033)(springcloud-02/1571299918242.png)]
2、勾选Starter:网关、Eureka客户端[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EKIOQyXy-1615724762034)(springcloud-02/1564920887287.png)]
3、启动引导类开启注册中心Eureka客户端发现
@SpringBootApplication
@EnableDiscoveryClient// 开启Eureka客户端发现功能
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class,args);
}
}
4、编写基础配置
- 在gateway_server中创建application.yml文件,配置
server:
port: 10010
spring:
application:
name: gateway-service # 应用名
# Eureka服务中心配置
eureka:
client:
service-url:
# 注册中心Eureka服务地址
defaultZone: http://127.0.0.1:8761/eureka
5、编写路由规则
- 需要用网关来路由provider-service服务,查看服务ip和端口[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6hbctgup-1615724762036)(springcloud-02/1571299740919.png)]
- 修改gateway-server的配置文件application.yml,配置网关内容
spring:
cloud:
gateway:
# 路由si(集合)
routes:
# id唯一标识
- id: consumer-service-route
# 路由服务地址
uri: http://127.0.0.1:8081
# 断言
predicates:
- Path=/**
- 将符合
path
规则的请求,路由到uri
参数指定地址。 - 举例:http://localhost:10010/user/findUserById/1 路由转发到http://localhost:8081/user/findUserById/1
6、启动GatewayApplication进行测试
6.3、动态路由
刚才路由规则中,我们把路径对应服务地址写死了!如果服务提供者集群的话,这样做不合理。应该是根据服务名称,去Eureka注册中心查找服务对应的所有实例列表,然后进行动态路由!
- 修改映射配置:通过服务名称获取
- 因为已经配置了Eureka客户端,可以从Eureka获取服务的地址信息,修改application.yml文件如下
# 注解版
server:
port: 10010
spring:
application:
name: gateway-service # 应用名
# Eureka服务中心配置
cloud:
gateway:
# 路由si(集合)
routes:
# id唯一标识
- id: consumer-service-route
# 路由服务地址
#uri: http://127.0.0.1:8081
uri: lb://provider-service
# 断言
predicates:
- Path=/user/**
eureka:
client:
service-url:
# 注册中心Eureka服务地址
defaultZone: http://127.0.0.1:8761/eureka
- 路由配置中uri所用的协议为lb时,gateway将把consumer-service解析为实际的主机和端口,并通过Ribbon进行负载均衡。
- 启动GatewayApplication测试
6.4、路由前缀
第一:添加前缀:
在gateway中可以通过配置路由的过滤器PrefixPath 实现映射路径中的前缀添加。可以起到隐藏接口地址的作用,避免接口地址暴露。
1、配置请求地址添加路径前缀过滤器
spring:
cloud:
gateway:
routes:
- id: consumer-service-route # 路由id,可以随意写
# 代理服务地址;lb表示从Eureka中获取具体服务
uri: lb://consumer-service
# 路由断言,配置映射路径
predicates:
- Path=/**
# 请求地址添加路径前缀过滤器
filters:
- PrefixPath=/consumer
2、重启GatewayApplication
3、配置完成的效果:
配置 | 访问地址 | 路由地址 |
---|---|---|
PrefixPath=/consumer | localhost:10010/findUserById/1 | localhost:8081/consumer/findUserById/1 |
PrefixPath=/consumer/abc | localhost:10010/findUserById/1 | localhost:8081/consumer/abc/findUserById/1 |
第二:去除前缀:
在gateway中通过配置路由过滤器StripPrefix,实现映射路径中地址的去除。通过StripPrefix=1来指定路由要去掉的前缀个数。如:路径/api/consumer/findUserById/1将会被路由到/consumer/findUserById/1
1、配置去除路径前缀过滤器
server:
port: 10010
spring:
application:
name: gateway-service # 应用名
# Eureka服务中心配置
cloud:
gateway:
# 路由si(集合)
routes:
# id唯一标识
- id: consumer-service-route
# 路由服务地址
#uri: http://127.0.0.1:8081
uri: lb://provider-service
# 断言
predicates:
- Path=/** # 请求地址添加路径前缀过滤器
filters:
#- PrefixPath=/user #添加前缀
- StripPrefix=1 #去除前缀
eureka:
client:
service-url:
# 注册中心Eureka服务地址
defaultZone: http://127.0.0.1:8761/eureka
2、重启GatewayApplication
3、访问查看效果
配置 | 访问地址 | 路由地址 |
---|---|---|
StripPrefix=1 | localhost:10010/api/consumer/findUserById/1 | localhost:8081/consumer/findUserById/1 |
StripPrefix=2 | localhost:10010/aa/api/consumer/findUserById/1 | localhost:8081/consumer/findUserById/1 |
6.5、过滤器
6.5.1、简介
过滤器作为网关的其中一个重要功能,就是实现请求的鉴权。前面的路由前缀
章节中的功能也是使用过滤器实现的。
Gateway自带过滤器有几十个,常见自带过滤器有:
过滤器名称 | 说明 |
---|---|
StripPrefix | 对匹配上的请求路径去除前缀 |
PrefixPath | 对匹配上的请求路径添加前缀 |
AddRequestHeader | 对匹配上的请求加上Header |
AddRequestParameter | 对匹配上的请求添加参数 |
AddResponseHeader | 对从网关返回的响应添加Header |
详细说明官方链接
使用场景:
- 请求鉴权:如果没有访问权限,直接进行拦截
- 异常处理:记录异常日志
- 服务调用时长统计
6.5.2、过滤器配置
**过滤器类型:**Gateway有两种过滤器
- 局部过滤器:只作用在当前配置的路由上。
- 全局过滤器:作用在所有路由上。
配置全局过滤器:
对输出的响应设置其头部属性名称为i-love,值为itheima
1、修改配置文件
server:
port: 10010
spring:
application:
name: gateway-service # 应用名
cloud:
gateway:
default-filters:
- AddResponseHeader=i-love,java
# 路由si(集合)
routes:
# id唯一标识
- id: consumer-service-route
# 路由服务地址
#uri: http://127.0.0.1:8081
uri: lb://provider-service
# 断言
predicates:
- Path=/** # 请求地址添加路径前缀过滤器
filters:
#- PrefixPath=/user #添加前缀
- StripPrefix=1 #去除前缀
# Eureka服务中心配置
eureka:
client:
service-url:
# 注册中心Eureka服务地址
defaultZone: http://127.0.0.1:8761/eureka
2、查看浏览器响应头信息
6.5.3、执行顺序
Spring Cloud Gateway 的 Filter 的执行顺序有两个:“pre” 和 “post”。“pre”和 “post” 分别会在请求被执行前调用和被执行后调用。
这里的 pre
和post
可以通过过滤器的 GatewayFilterChain 执行filter方法前后来实现。
6.6、自定义全局过滤器
需求:模拟一个登录的校验。基本逻辑:如果请求中有token参数,则认为请求有效,放行;如果没有拦截返回http 401。
实现步骤:
1、在gateway-server工程编写全局过滤器类MyGlobalFilter
2、编写业务逻辑代码:判断如果包含token值,则放行请求,如果不包含则拦截
3.、访问接口测试,加token和不加token。
实现过程:
- 在gateway_server工程编写全局过滤器类MyGlobalFilter
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("-----------------全局过滤器MyGlobalFilter---------------------");
//1、获取参数中的token,以及token的值
String token = exchange.getRequest().getQueryParams().getFirst("token");
//2、如果token的值为空,则拦截
if (StringUtils.isEmpty(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
/**
* 定义过滤器执行顺序
* 返回值越小,越靠前执行
* @return
*/
@Override
public int getOrder() {
return 0;//
}
}
2、访问:http://localhost:10010/aaa/user/findUserById/1
3.、访问:http://localhost:10010/aaa/user/findUserById/1?token=1
七、配置中心 Spring Cloud Config
7.1、Config简介
分布式系统中,由于服务数量非常多,配置文件分散在不同微服务项目中,管理极其不方便。为了方便配置文件集中管理,需要分布式配置中心组件。在Spring Cloud中,提供了Spring Cloud Config,它支持配置文件放在配置服务的本地,也支持配置文件放在远程仓库Git(GitHub、码云)。配置中心本质上是一个微服务,同样需要注册到Eureka服务中心!
配置中心,也是一个微服务,注册到注册中心
【配置中心的架构图】
7.2、配置中心整合步骤:
- 配置文件集中放在码云
- 配置中心获取码云配置文件
- 用户服务获取配置中心文件
7.3、搭建配置中心微服务
实现步骤:
1、创建配置中心SpringBoot项目config-server
2、勾选Starter坐标依赖:配置中心starter,Eureka客户端starter
3、启动类添加开启配置中心服务注解
4、配置服务中心application.yml文件
5、启动测试
实现过程:
1、启动类:创建配置中心工程config_server的启动类ConfigServerApplication
@SpringBootApplication
@EnableDiscoveryClient//开启Eureka客户端发现功能
@EnableConfigServer //开启配置服务支持
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class,args);
}
}
2、 配置文件:创建配置中心工程config_server的配置文件application.yml
# 应用基本信息
server:
port: 1200 # 端口号
spring:
application:
name: config-server # 应用名
cloud:
config:
server:
git:
# 配置gitee的仓库地址
uri: https://gitee.com/guodongzz/itheima-361.git
# Eureka服务中心配置
eureka:
client:
service-url:
# 注册Eureka Server
注意:上述spring.cloud.config.server.git.uri是在码云创建的仓库地址,如果仓库创建时设置为私有的,需要在配置文件中增加用户名、密码配置信息。
3、启动测试:启动eureka注册中心和配置中心;
- 访问http://localhost:1200/user-dev.yml查看能否输出码云存储管理的user-dev.yml文件
- 并且可以在gitee上修改user-dev.yml,然后刷新上述测试地址也能及时更新数据
7.4、服务去获取配置中心配置
关于application.yml和bootstrap.yml文件的说明:
- bootstrap.yml文件是SpringBoot的默认配置文件,而且其加载时间相比于application.yml更早。
- bootstrap.yml和application.yml都是默认配置文件,但定位不同
- bootstrap.yml相当于项目启动的引导文件
- application.yml文件是微服务的常规配置参数,变化比较频繁
- 搭配spring-cloud-config使application.yml的配置可以动态替换。
目标:改造provider-service工程,配置文件不再由微服务项目提供,而是从配置中心获取。
实现步骤:
1、在provider-service中添加starter-config依赖
2、修改服务提供者的配置文件
3、启动服务,测试效果
实现过程:
1、添加依赖(注意这里的依赖与配置中心服务的依赖有区别)
<!--spring cloud 配置中心-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
2、修改配置
- 创建provider-service工程bootstrap.yml配置文件,配置内容如下
server:
port: 9091
# 基本应用信息
spring:
cloud:
config:
name: user # 与远程仓库中的配置文件的application保持一致,{application}-{profile}.yml
profile: dev # 远程仓库中的配置文件的profile保持一致
label: master # 远程仓库中的版本保持一致
discovery:
enabled: true # 使用配置中心
service-id: config-server # 配置中心服务id
3、启动测试:
- 依次启动:注册中心、配置中心、用户中心provider-service
- 如果启动没报错,其实已经使用上配置中心内容了
7.5、配置中心存在的问题
复现问题步骤:
1、修改远程Git配置
- 修改在码云上的user-dev.yml文件,添加一个属性personName
2、修改UserController
@RestController
@RequestMapping("/user")
public class UserController {
@Value("${server.port}")
private String port;
@Value("${personName}")
private String perName;
@Autowired
UserService userService;
//根据id查询
@GetMapping("/findUserById/{id}")
public User findUserById(@PathVariable Integer id){
/*try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
User user = userService.findUserById(id);
user.setNote("生产者端口号: " + port);
return user;
}
/**
* 获取配置文件中的personName
**/
@RequestMapping("/getPerName")
public String getPerName(){
return perName;
}
}
3、测试:
- 依次启动Eureka,配置中心,用户微服务;
- 不要开启消费者服务,因为消费者服务是8081,端口冲突
- 然后我们修改的user-dev.yml中personName=zhangsan-test01 ,访问用户微服务,查看输出内容并没有发生变化。
结论:通过浏览器输出结果发现,我们对于Git仓库中的配置文件的修改,并没有及时更新到provider-service微服务,只有重启用户微服务才能生效。
7.6、服务手动刷新更新配置
步骤:
1、添加依赖
2、修改配置文件暴露
3、Controller添加 @RefreshScope注解
4、启动测试,修改git仓库中配置文件,并发送post请求 http://localhost:9091/actuator/refresh
过程:
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2、修改配置文件
#暴露刷新地址
management:
endpoints:
web:
exposure:
include: refresh
3、Controller添加 @RefreshScope注解
4、启动测试,修改git仓库中配置文件,并发送post请求 http://localhost:9091/actuator/refresh
问题:服务器少的情况系可以分别手动刷新,如果有100台服务器呢?
SpringCloud Bus,解决上述问题,实现配置自动更新。