1.微服务
集群: cluster 同一种软件服务的多个服务节点共同为系统提供服务过程称之为该软件服务集群
分布式: distribute 不同软件集群共同为一个系统提供服务 这个系统称之为分布式系统
1.1 定义
官方定义:微服务是一种架构
- 基于原来单个应用开发出一系列微小服务
- 每个服务运行在自己计算机进程里面
- 每个服务基于项目中业务进行拆分
- 拆分出来的每个服务独立部署
- 这些服务都是基于分布式管理
通俗定义:微服务是一种架构,这种架构是将单个的整体应用程序分割成更小的项目关联的独立的服务。一个服务通常实现一组独立的特性或功能,包含自己的业务逻辑和适配器。各个微服务之间的关联通过暴露api来实现。这些独立的徽服务不需要都署在同一个虚拟机,同一个系统和同一个应用服务器中。
2. 为什么是微服务
单体应用
优点:
- 单一架构模式在项目初期很小的时候开发方便,测试方便,部署方便,运行良好。
缺点:
- 应用随着时间的推进,加入的功能越来越多,最终会变得巨大,一个项目中很有可能数百万行的代码,互相之间繁琐的jar包。
- 久而久之,开发效率低,代码雄护困难
- 还有一个如果想整体应用采用新的技术,新的框架或者语言,那是不可能的。
- 任意模块的漏洞或者错误都会影响这个应用,降低系统的可靠性
微服务架构应用
优点:
- 将服务拆分成多个单一职责的小的服务,进行单独部署,服务之间通过网络进行通信
- 每个服务应该有自己单独的管理团队,高度自治
- 服务各自有自己单独的职责,服务之间松耦合,避免因一个模块的问题导致服务崩溃
缺点:
- 开发人员要处理分布式系统的复杂性
- 多服务运维难度,随着服务的增加,运维的压力也在增大
- 服务治理 和 服务监控 关键
架构的演变
# 1.架构的演变过程
- [单一应用架构] ===> [垂直应用架构] ===> [分布式服务架构] ===> [流动计算架构] | [微服务架构] ===> [未知]
3.微服务架构的解决方案
国内阿里系:
- springboot + dubbo(spring springboot(兼容)) + zookeeper 最早期
spring cloud技术栈:
- spr ing cloud netflix(公司) 最早期 16-17 维护 组件
- spring cloud spring自己封装微服务解决方家 自己组件 继续使用
- spring cloud alibaba阿里巴巴解决方案 自己组件 继续使用
4.什么是spring cloud
4.1 定义
**官方定义:**springcloud为开发人员提供了在分布式系统中快速构建一些通用模式的工具(例如配置管理、服务发现、断路器、智能路由、微代理、控制总线)
**通俗定义:**springc loud含有众多子项目的工具集tools collection 微服务工具集合
- springcloud是一个含概多个子项目的开发工具集,集合了众多的开源框架,他利用了Spring Boot开发的便利性实现了很多功能,如服务注册,服务注册发现,负载均衡等.SpringcCloud在整合过程中主要是针对Netflix(耐非)开源组件的封装. springCloud的出现真正的简化了分布式架构的开发。
- NetFlix是美国的一个在线视频网站 ,微服务业的翘楚,他是公认的大规模生产级微服务的杰出实践者, NetFlix的开源组件已经在他大规模分布式微服务环境中经过多年的生产实战验证,因此Spring cloud中很多组件都是基于NetFlix
4.2 微服务
基于单体应用围绕业务进行服务拆分,拆分出来每一个服务独立应用、独立运行、独立都署、运行在自己计算机进程中。基于分布式服务管理
5.环境搭建
5.1 版本命名
springcloud是一个由众多独立子项目组成的大型综合项目,原则每个子项目上有不同的发布节奏,都维护自己发布版本号。为了更好的管理springcloud的版本,通过一个资源清单BOw(Gi1l of Materials),为避免与子项目的发布号混猾,所以没有采用版本号的方式,而是通过命名的方式。这些名字是按字母顺序排列的。如伦敦地铁站的名称“天使"是第一个版本,“布里斯顿"是第二个版本, “卡姆登"是第三个版本)。当单个项目的点发布累积到一个临界量,或者其中一个项目中有一个关键缺陷需要每个人都可以使用时,发布序列将推出名称以” .SRX"“结尾的“服务发布”",其中"×"是一个数字。
5.2 版本选择
5.3 环境搭建
-
在父项目中继承spring boot父项目
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.12.RELEASE</version> </parent>
-
维护springcloud的依赖
<properties> <spring.cloud-version>Hoxton.SR12</spring.cloud-version> </properties> <!--版本维护 springcloud版本--> <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>
6.服务注册中心
6.1 定义
服务注册中心就是在整个微服务架构单独抽取一个服务,这个服务不完成项目中任何业务功能,仅仅用来在微服务中记录微服务以及对整个系统微服务进行健康状态检查,以及服务元数据信息存储
服务注册中心作用:
- 可以对所有的微服务的信息进行存储,如微服务的名称、IP、端口等
- 可以在进行服务调用时通过服务发现查询可用的微服务列表及网络地址进行服务调用
- 可以对所有的微服务进行心跳检测,如发现某实例长时间无法访间,就会从服务注册表移除该实例。
6.2 常用的注册中心组件
常用注册中心组件: eureka(netflix)、 zookeeper (java) 、consul (Go) 、nacos (java阿里巴巴)
1. Eureka
- Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现Springcloud的服务注册和发现功能。
- Eureka包含两个组件:Eureka Server和Eureka Client。
开发 Eureka Server
-
创建项目并引入eureka server依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>
-
编写配置文件,指定eureka server端口、服务地址
#eurrka server 默认端口号 server.port=8761 #eurrka server服务中心注册地址,暴露服务地址 eureka.client.service-url.defaultZone=http://localhost:8761/eureka
-
在入口类中加入注解
@EnableEurekaServer
@SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
Eureka Server 细节
- 在项目启动成功之后默认在eureka server管理界面中出现UNKHOWN 一个未知应用
注意:在微服务架构中服务名称代表服务唯一标识,至关重要,服务名称必须唯一,使用时必须通过如下配置指定服务名称
# 指定服务名称 建议大写 且不运行名称中带有下划线_
spring.application.name=EUREKASERVER
- 在eureka server启动过程中报错:
出错解释: eureka含有两个组件 eureka server , eureka client组件,当项目中引入enrekaserver组件时,这个组件同时将eureka client引入到项目中,因此启动时即会将自己作为一个服务中心启动,同时也会将自己作为服务客户端进行注册,默认启动时立即注册,注册时服务还没有准备完成因此会出现当前错误
-
如何关闭erueka server 自己注册自己
# 关闭eureka的立即注册功能 默认true,启动就注册,关闭后,启动不会报错 eureka.client.fetch-registry=false # 让当前应用仅为注册中心,启动后不再把自己注册进去(关闭自己注册自己) eureka.client.register-with-eureka=false
Eureka client 开发
-
开发 eureka client 就是日后基于业务拆分出来的一个个微服务
-
创建新的springboot应用,引入相关依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
-
编写application.properties
# 指定服务端口 server.port=8989 # 服务名称 spring.application.name=EUREKACLIENT # 指定服务注册中心地址 eureka.client.service-url.defaulZone=http://localhost:8761/eureka
-
在入口类中加入注解
@EnableEurekaClient
@SpringBootApplication @EnableEurekaClient //让当前微服务作为一个eureka serve客户端进行服务注册 public class EurekaClientApplication { public static void main(String[] args) { SpringApplication.run(EurekaClientApplication.class,args); } }
Eureka 自我保护机制
- 自我保护机制触发现象
-
自我保护机制(Self Perservation Mode)
默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,,Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。EurekaServer在运行期间会去统计心跳失败比例在15分钟之内是否低于85%,如果低于85%,Eureka Server会将这些实例保护起来,让这些实例不会过期。这种设计的哲学原理就是""可信其有不可信其无!"。自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。
- 注意:自我保护机制默认是开启的
- 现象:在自我保护模式下,eureka服务器将停止逐出所有实例
- 机制:这样做是为了确保灾难性的网络事件不会清除enreka注册表数据,并将其传播到下游的所有客户端触发自我保护机制
什么时候讲客户端在服务注册中中清除:
1.心跳的次数高于预期阈值
2.自我保护被禁用
-
eureka server关闭自我保护机制
# 关闭自我保护机制 默认true开启 eureka.server.enable-self-preservation=false #超过3000毫秒自动清除 默认60*1000 1分钟后清除 eureka.server.eviction-interval-timer-in-ms=3000
-
客户端
# 默认接受心跳最大时间,默认90s eureka.instance.lease-expiration-duration-in-seconds=10 #指定客户端多久想eureka注册中心发送心跳检测频率,默认30s eureka.instance.lease-renewal-interval-in-seconds=5
-
关闭自我保护机制 官方不推荐 关闭时同样发出警告
Eureka 集群
-
集群搭建
-
完全集群
-
创建3个springboot项目
-
引入eureka server依赖
-
配置文件application.properties
node1: server.port=8761 http://localhost:8762/eureka,http://localhost:8763/eureka node2: server.port=8761 http://localhost:8761/eureka,http://localhost:8763/eureka node3: server.port=8763 http://localhost:8761/eureka,http://localhost:8762/eureka
-
在每个项目入口类加入
@EnableEurekaClient
注解
-
2.consul
consul是一个可以提供服务发现,健康检查,多数据中心,Key/Value存储等功能的分布式服务框架,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,使用起来也较为简单。consul用Golang实现,因此具有天然可移植性(支持Linux、windows和Mac os x);安装包仅包含个可执行文件,方便部署。
- 简介:consul基于go语言进行开发服务注册中心轻量级服务注册中心
- 作用:管理微服务中所有服务注册、发现 管理服务元数据信息存储(服务名 地址列表)︰心跳健康检查
consul 服务注册中心
-
启动服务注册中心
consul agent -dev
-
访问consul管理界面
http:端口默认8500
浏览器::localhost: 8500
-
管理界面基本介绍
dc1:数据中心名称 datacenter 默认为: dc1 指定数据中心启动 consul agent -dev -datacenter=aa
services:当前consul服务中注册服务列表 默认:client server同时启动自己注册自己会出现一个consul服务
nodes:用来查看consul的集群节点
consul 客户端开发
-
创建独立的springboot应用
-
引入依赖
<!--consul依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency>
-
编写application.properties
server.port=8082 spring.application.name=CONSULCLIENT # consul server 服务注册地址 spring.cloud.consul.host=localhost spring.cloud.consul.port=8500 # 指定当前服务注册名 默认${spring.application.name} spring.cloud.consul.discovery.service-name=${spring.application.name} # 关闭健康检查 不建议关闭 #spring.cloud.consul.discovery.register-health-check=false
-
在入口类中加入注解
@EnableDiscoveryClient
@SpringBootApplication @EnableDiscoveryClient //作用:通用服务注册客户端注解代表consul client, zk client, nacos client public class ConsulClientApplication { public static void main(String[] args) { SpringApplication.run(ConsulClientApplication.class, args); } }
-
直接启动consul elient出现如下问题
原因:consul server检测所有客户端心跳,但是发送心跳时client必须给予响应才能该服务才能正常使用,在现有客户端中我们并没有引入健康检查依赖,所以导致健康检查始终不同通过,导致服务不能使用
<!--开启consul健康检查依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
7.微服务间通信
基于RestTemplate的服务调用
1 解决微服务的服务间通信问题
HTTP Rest方式 | 使用http协议进行数据传输 | JSON | spring cloud 使用http协议传输数据 |
---|---|---|---|
RPC方式 | 远程过程调用 | 二进制 |
OSI:物理层 数据链路层 网络层 传输层(RPC) 会话层 表示层 应用层(Http)
2 如何在java代码中发起http方式请求
- spring框架提供HttpClient对象 RestTemplate 发起一个http请求
3 实现服务间通信案例
-
开发两个测试服务 用户服务users 订单服务orders
-
用户服务 订单服务 都是两个独立springboot应用
-
两个服务都引入consul client依赖和健康检查依赖
<!--开启consul健康检查依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--consul client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency>
-
配置两个服务application.properties
# 各自端口号与项目名 # consul server spring.cloud.consul.host=localhost spring.cloud.consul.port=8500
-
在入口类中加入注解
@EnableDiscoveryClient
案例
@RestController
public class OrderController {
private static final Logger log = LoggerFactory.getLogger(OrderController.class);
@GetMapping("order")
public String demo(){
log.info("order demo ...");
return "order demo ok!";
}
}
@RestController
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
@GetMapping("user")
public String invoke(){
log.info("demo user...");
// 调用订单服务
RestTemplate template = new RestTemplate();
String orderResult = template.getForObject("http://localhost:9999/order", String.class);
log.info("调用订单服务成功+",orderResult);
return "调用order服务成功,结果为:" + orderResult;
}
}
4.使用RestTemplate对象实现服务间通信存在的问题
-
现有RestTemplate在进行服务间通信时
-
调用服务的路径主机和服务端口直接写死在url中无法实现服务集群时请求负载均衡
-
调用服务的请求路径写死在代码中,日后提供服务服务路径发生变化时不利于后续维护工作
-
-
解决RestTemplate负载均衡问题:
-
自定义负载均衡策略
问题:无法实现服务健康检查,负载均衡策略过于单一(随机)
-
-
使用springcloud提供的组件 ribbon 解决负载均衡调用
基于 Ribbon 的服务调用
spring cloud - netflix - ribbon 作用:负载均衡客户端组件,就是用来实现请求调用时负载均衡
1.Ribbon负载均衡原理
2.使用Ribbon+RestTemplate实现请求负载均衡
-
使用用户调用订单服务用户服务中引入ribbon依赖
注意:consul client依赖中已经存在ribbon相关依赖无需项目中
-
直接使用Ribbon组件根据服务id实现请求负裁均衡
-
Discoveryclient 服务发现客户端对象、根据服务id去服务注册中心获取对应服务服务列表到本地中
缺点:没有负载均衡需要自己实现负载均衡
@RestController public class UserController { @Autowired //服务与注册发现客户端对象 private DiscoveryClient discoveryClient; private static final Logger log = LoggerFactory.getLogger(UserController.class); @GetMapping("user") public String invoke(){ log.info("demo user..."); // 使用ribbon组件+RestTemplate实现负载均衡调用 1.DiscoveryClient 2.LoadBalanceClient 3.@LoadBalanceClient List<ServiceInstance> serviceInstances = discoveryClient.getInstances("ORDERS"); serviceInstances.forEach(serviceInstance -> { log.info("服务主机:{} 服务端口:{} 服务地址:{}",serviceInstance.getHost(),serviceInstance.getPort(),serviceInstance.getUri()); }); String ordersResult = new RestTemplate().getForObject(serviceInstances.get(0).getUri()+"/order", String.class); return "ok" + ordersResult; } }
-
LoadBalanceClient 负载均衡客户端对象 根据服务id去服务注册中心获取对应服务列表,根据默认负载均衡策略选择列表中一台机器进行返回
缺点:使用时需要每次先根据服务id获取一个负载均衡机器之后再通过restTemplate调用服务
@RestController public class UserController { @Autowired private LoadBalancerClient loadBalancerClient; private static final Logger log = LoggerFactory.getLogger(UserController.class); @GetMapping("user") public String invoke(){ log.info("demo user..."); ServiceInstance serviceInstance = loadBalancerClient.choose("ORDERS"); //默认轮询 log.info("服务主机:{} 服务端口:{} 服务地址:{}",serviceInstance.getHost(),serviceInstance.getPort(),serviceInstance.getUri()); String ordersResult = new RestTemplate().getForObject(serviceInstance.getUri()+"/order", String.class); return "ok" + ordersResult; } }
-
@LoadBalance + RestTemplate 负裁均衡客户端注解
修饰范围:用在方法上 作用:让当前方法、当前对象具有ribbon负载均衡特性
@Configuration public class BeanConfig { @Bean @LoadBalanced public RestTemplate restTemplate(){ return new RestTemplate(); } }
@RestController public class UserController { @Autowired private RestTemplate restTemplate; private static final Logger log = LoggerFactory.getLogger(UserController.class); @GetMapping("user") public String invoke(){ log.info("demo user..."); String ordersResult = restTemplate.getForObject("http://ORDERS/order", String.class); return "ok" + ordersResult; } }
-
-
使用RestTemplate + Ribbon这种完成服务间通信
路径写死在代码中不利于维护
restTemplate.getForObject("http://ORDERS/order", String.class);
3.ribbon组件细节
-
Ribbon组件实现负载均衡原理
原理:根据调用服务的服务id去服务注册中心获取对应服务id的服务列表,并将服务列表拉取本地进行缓存,然后在本地通过默认的轮询的负载均衡策略在现有列表中选择一个可用节点提供服务
注意:客户端负裁均衡
-
Ribbon组件支持那些负载均衡策略
源码分析:
-
查看
loadBalancerClient.choose("ORDERS")
源码 -
得知
ServiceInstanceChooser
是LoadBalancerClient
的父接口 -
ServiceInstanceChooser
的choose方法默认实现RibbonLoadBalancerClient
-
在
RibbonLoadBalancerClient
有一个choose方法带有两个参数 这里面进行负载均衡实现 -
查看getServer方法实现
-
总结通过源码得知IRule是底层负载均衡父接口
-
-
Ribbon负载均衡策略支持哪些?
# 1.Ribbon负载均衡算法 - RoundRobinRule 轮询策略 按顺序循环选择server - RandomRule 随机策略 随机选择 server - AvailabilityFi1teringRule 可用过滤策略 `会先过滤由于多次访间故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问 - weightedResponseTimeRule 响应时间加权策略 `根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够会切换到 - RetryRule 重试策略 `先按照RoundRobinRule的策略获取服务,如果获取失败则在制定时间内进行重试,获取可用的服务。 - BestAviableRule 最低并发策略 `会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
-
设置服务间调用负载均衡策略
服务
id.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
-
Ribbon组件现在状态(停止维护)
ribbon-core、ribbon-loadbalance依然在大规模生产实践中部署、意味着日后如果实现服务间通信负载均衡依然使用ribbon组件
总结:
8.OpenFeign组件的使用
OpenFeign组件
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性(可以使用springmvc的注解),可使用Feign注解和AX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,默认实现了负载均衡的效果并且springcloud为feign添加了springmvc注解的支持。
- 简介:Rest client: 0penFeign与RestTemplate作用一致,都是一个http客户端
- RestTemplate:spring框架封装Httpclient对象
- openFeign:伪Httpclient客户端对象 他可以使服务间通信变得更加简单 Feign默认集成了Ribbon 实现请求负载均衡
简单:1.使用写一个接口加一个注解
2.调用服务代码更加简单自动完成数据传递过程中对象转换
- 为什么使用openFeign
a.RestTemplate 使用问题:
1.路径写死
2.不能自动转换响应结果为对应对象
3.必须集成ribbon实现负载均衡
b. openFeign组件可以解决RestTemplate实现服务间通信所有问题
使用openfeign
-
建立两个独立的springboot应用,并注册到服务中心
-
引入服务注册中心依赖
<!--开启consul健康检查依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--consul client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency>
-
修改配置文件
server.port=8787 spring.application.name=CATEGORY #server.port=8788 #spring.application.name=PRODUCT # 注册到consul server spring.cloud.consul.host=localhost spring.cloud.consul.port=8500
-
加入入口注解
@SpringBootApplication @EnableDiscoveryClient
-
使用openfeign进行调用
-
在调用服务方引入openFeign依赖
<!--引入OpenFeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
在服务调用方入口类加注解,开启Feign调用支持
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients // 开启openFeign客户端调用 public class CategoryApplication { }
-
在服务调用方建立接口类,其中方法与被调用方controller方法返回值、路径一致
@FeignClient("PRODUCT") // value:用来书写调用服务的服务id public interface ProductClient { @GetMapping("/product") public String product(); } // 被调用方controller方法 @RestController public class ProductController { private static final Logger log = LoggerFactory.getLogger(ProductController.class); @Value("${server.port}") private int port; @GetMapping("/product") public String product(){ log.info("product demo..."); return "product ok! 服务端口号是:" + port; } }
-
在服务调用方使用该接口,完成调用
@RestController public class CategoryController { private static final Logger log = LoggerFactory.getLogger(CategoryController.class); @Autowired private ProductClient productClient; @GetMapping("/category") public String category(){ log.info("category service..."); String product = productClient.product(); log.info("结果 {}", product); return "category ok!" + product; } }
-
服务通信之参数传递和响应处理
-
微服务架构中服务间通信手段
http协议: springcloud两种方式: 1.RestTeamplate + Ribbon 2. openFeign推荐
-
服务间通信,参数传递和响应处理
参数传递: 1.传递零散类型参数 ⒉.传递对象类型 3.数组或集合类型参数
参数传递
-
零散类型参数传递
-
queryString方式传递参数,?name=xiaochen
注意:在openfiegn接口中声明必须给参数加注解
@RequestParam
-
路径传递参数 url/xiaochen/23
注意:在openfiegn接口中声明必须给参数加注解
@PathValue
-
-
对象类型参数传递
application/json 方式:推荐
注意:使用json方式在openfeign接口声明中必须给参数加入注解
@RequestBody
注解 -
数组参数传递
注意:在openFeign传递数组类型参数时必须在声明时使用
@RequestParam
注解标识 -
集合类型的参数接收
-
服务提供者 商品服务定义
-
openfeign实现服务间通信响应处理
-
使用openFeign调用服务,并返回对象
// 声明调用根据id查询商品信息接口 @GetMapping("/product/{id}") Product product(@PathVariable("id") Integer id);
-
使用openFeign调用服务,返回集合List Map集合
// 声明调用商品服务根据类别id查询一组商品信息 @GetMapping("/products") List<Product> findByCategoryId(@RequestParam("categoryId") Integer categoryId);
openFegin细节
-
openFeign默认超时处理
默认的调用超时:使用openFeign组件在进行服务间通信时要求被调用服务必须在1s内给予响应,一旦服务执行业务逻辑时间超过1s , openFeigm组件将直接报错: Read timed out execut ing GET http:/ /PRODUCT/product
-
修改openFeign超时时间
# 指定服务修改基个服务调用超时时间 feign.client.config.PRODUCTS.connectTimeout=5000 #配置指定服务连接超时 feign.client.config.PRODUCTS.readTimeout=5000 #配置指定服务等待超时 #PRODUCTS为调用服务的服务id #修改openfeign默认调用所有服务超时间 feign.client.config.default.connectTimeout=5000 #配置所有服务连接超时 feign.client.config.default.readTimeout=5000 #配置所有服务等待超时
-
openFeign日志调用
openFeign 伪HttpClient客户端对象,用来帮助我们完成服务间通信 底层用http协议 完成服务间调用
**日志:**OpenFeign为了更好方便在开发过程中调试openFeigm数据传递和响应处理,,openFeign在设计时添加了日志功能,默认openFeign白志功能需鉴手动开启的
日志使用:
# 1.展示openfeign日志 - logging.level.com.baizhi.feignclient=debug # 2.feign每一个客户端提供一个日志对象 ` HOHE 不记录任何日志 `BASIC 仅仅记录请求方法,ur1,响应状态代码及执行时间 `HEADERS 记录Basic级别的基础上,记录请求和响应的header `FULL记录请求和响应的header,body和元数据,展示全部http协议状态 - feign.client.config.PRODUCT.loggerLevel = FULL
9.Hystrix组件的使用
Hystrix组件
在分布式环境中,许多服务依赖项不可避免地会失败。Hystrix是一个库,它通过添加延迟容忍和容错逻辑来帮助您控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止它们之间的级联故障以及提供后备选项来实现这一点,所有这些都可以提高系统的整体弹性。
通俗定义:Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖不可避免的会调用失败,超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障(服务雪崩现象),提高分布式系统的弹性。
Hystrix 豪猪 起源: netFlix springcloud netflix hystrix
作用:用来防止微服务系统中服务雪崩现象﹑实现服务熔断 熔断是防止雪崩手段
1. 服务雪崩
- 在一个时刻微服务系统中所有微服务均不可用的这种现象称之为服务雪崩现象
# 1.服务雪崩
- **在微服务之间进行服务调用是由于某一个服务故障,导致级联服务故障的现象,称为雪崩效应。雪崩效应描述的是提供方不可用,导致消费方不可用并将不可用逐渐放大的过程**
- 根本原因:在调用链路中链路某一服务因为执行业务时间过长,或者是大规模出现异常导致自身服务不可用,并把这种不可用放大情况
# 2.图解雪崩效应
- 如存在如下链路调用:
- 而此时,Service A的流量波动很大,流量经常会突然性增加!那么在这种情况下,就算service A能扛得往请求,Sservice B和Service c未必能扛得住这突发的请求。此时,如果Service c因为抗不住请求,变得不可用。那么Service B的请求也会阻塞,慢慢耗尽service B的线程资源,Service B就会变得不可用。紧接着,service A也会不可用,这一过程如下图所示
2.服务熔断
作用:用来在为服务系统中防止服务雪崩现象的出现
熔断机制:所有的微服务中必须引入Hystrix组件,一旦引入hystrix这个组件就具有了服务熔断功能
# 服务熔断
- "熔断器"本身是一种开关装置,当某个服务单元发生故障之后,通过断踏器(hystrix)的故障监控,某个异常条件被触发,直接熔断整个服务。向调用方法返回一个符合预期的、可处理的备选响应(Fa11Back) ,而不是长时间的等待或者抛出调用方法无法处理的异常,就保证了服务调用方的线程不会破长时间占用,避免故障在分布式系统中蔓延,乃至雪崩。如果目标服务情况好转则恢复调用。服务熔断是解袂服务雪崩的重要手段
# 服务熔断示意图
3.服务降级
# 服务降级
- 定义:服务压力剧增的时侯根据当前的业务情祝及流量对一些服务和页面有策略的降级,以此缓解服务器的压力,以保证核心任务的进行。同时保证部分甚至大部分任务客户能得到正确的响应。也就是当前的请求处理不了了或者出错了,给一个默认的返回。
- 通俗定义:当网站|服务流量突然增加时,为了保证系统核心服务正常运行,有策略关闭系统中边缘服务,以保证核心服务正常运行
- 服务降级:关闭微服务系统中某些边缘服务保证系统核心服务正常运行
- 12 淘宝 京东
- 删除订单 --- 关闭订单 确认收货 ----> 服务繁忙,!!
# 服务降级图示
4.降级和熔断总结
# 1.共同点
- 目的很一致,都是从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段;
- 最终表现类似,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用;
- 粒度一般都是服务级别,当然,业界也有不少更细粒度的做法,比如做到数据持久层(允许查询,不允许增珊改)﹔
- 自治性要求很高,熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段: sentinel
# 2.异同点
- 触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
- 管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务边缘服务开始)
# 3.总结
- 熔断必会触发降级,所以熔断也是降级一种,区别在于熔断是对调用链路的保护,而降级是对系统过载的一种保护处理
5.服务熔断的实现
# 服务熔断的实现思路
- 引入hystrix依赖,并开启熔断器(断路器)
- 模拟降级方法
- 进行调用测试
-
项目中引入hystrix依赖
<!--引入Hystrix--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
-
开启断路器(开启熔断功能在入口类加入注解
@EnableCircuitBreaker
)@SpringBootApplication @EnableDiscoveryClient @EnableCircuitBreaker // 开启hystrix服务熔断 public class HystrixApplication { public static void main(String[] args) { SpringApplication.run(HystrixApplication.class,args); } }
-
在控制器方法中加入备选处理
@HystrixCommand(fallbackMethod = "demoFallBack")
@GetMapping("/demo/{id}") @HystrixCommand(fallbackMethod = "demoFallBack") //熔断之后处理 fallbackMethod 书写快速失败方法名 public String demo(@PathVariable("id") Integer id){ System.out.println("demo ok!!!"); if (id < 0) { throw new RuntimeException("无效id!!!!"); } return "demo ok !"; } public String demoFallBack(Integer id){ return "当前活动过于火爆,服务已经被熔断了!!"; } @HystrixCommand(fallbackMethod = "demoFallBack") //自定义备选处理 @HystrixCommand(defaultFallback = "默认处理方法名") //默认处理
-
断路器打开条件
# 打开关闭条件 - 1.当满足一定的阀值的时候(默认10秒内超过20个请求次数) - 2.当失败率达到一定的时候(默认10秒内超过50%的请求失败) - 3.到达以上阀值,断路器将会开启 - 4.当开启的时候,所有请求都不会进行转发 - 5.一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个请求进行转发。如果成功,断路器会关闭,若失败,继续开启。重复4和 # 面试重点问题:断路器流程 - **整个流程** - 当Hystrix监控到对该服务接口调用触发1 2两个阈值时,会在系统中自动触发熔断器,在熔断器打开期间内,任何到该接口请求均不可用,同时在断路器打开ss后断路器会处于半开状态,此时断路器允许放行一个请求到该服务接口,如果该请求执行成功,断路器彻底关闭,如果该请求执行失败断路器重新打开。
6.openfeign调用服务过程集成Hystrix实现默认备选处理
-
引入Hystrix依赖
注意:openfeign组件底层自动依赖Hystrix依赖项目中无须显示引入
-
开启openfeign对Hystrix支持
#开启openfeign在调用服务过程中开启hystrix支持默认:就是没有开启 feign.hystrix.enabled=true
-
开发openfeign服务调用失败默认处理的实现类
-
在openFeign客户端接口中的
@Feignclients(value="服务id" ,fallBack=默认处理.class)
-
当调用服务不可用时,直接回执行自定义默认处理
7.Hystrix Dashboard
仪表盘:用来显示状态信息
作用:监控每一个@HystrixCommond
注解创建一组度量,构建一组信息,然后通过图形化方式展示当前方法@RHystrixComnond
的状态信息
-
构建Hystrix DashBoard 仅仅是一个仪表盘应用
-
创建springboot应用
-
引入hystrix dashboard依赖
<!--hystrix dashboard--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>
-
在入口类加入注解,开启仪表盘应用
@SpringBootApplication //@EnableDiscoveryClient //注意:默认只要引入discovery client依赖该注解无须显示声明自动注册 @EnableHystrixDashboard //注意:这个注解作用用来开启当前应用为仪表盘应用 public class HystrixDashBoardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashBoardApplication.class, args); } }
-
访问仪表盘web页面
http://localhost:当前应用的端口号(9909)/hystrix
# 1.解决第一个问题,直接引入如下代码(加入要监控的微服务里面) @Bean public ServletRegistrationBean getServlet() { HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet(); ServletRegistrationBean regBean = new ServletRegistrationBean(streamServlet); regBean.setLoadOnStartup(1); List mappingList = new ArrayList(); mappingList.add("/hystrix.stream"); regBean.setUrlMappings(mappingList); regBean.setName("HystrixMetricsStreamServlet"); return regBean; }
# Unable to connect to Command Metric Stream 错误 hystrix.dashboard.proxy-stream-allow-list=localhost management.endpoints.web.exposure.include=hystrix.stream
-
-
Hystrix & Hystrix DashBoard
-
目前状态
a . Hystrix不再处于积极开发中,目前处于维护模式.
b. Hystrix DashBoard已经被废弃
The hystrix-dashboard component of this project has been deprecated and moved to
替换产品: Hetflix- Skunkworks/hystrix-dashboard .
-
日后如何解决服务雪崩
a . Hystrix(版本1.5.18)足够稳定,可以满足Netflix对我们现有应用程序的需求
b. resilience4j 替换Hystrix 实时自适应系统熔断 Springcloud resilience4j
c .sentinel 被称为流量卫兵、流量控制、降低策略(根据异常)推荐 sentinel & sentinel dashboard
-
-
总结
10.Gateway网关组件
什么是服务网关
# 1.说明
- 网关统一服务入口,可方便实现对平台众多服务接口进行管控,对访问服务的身份认证、防报文重放与防数据篡改、功能调用的业务鉴权、响应数据的脱敏、流量与并发控制,甚至基于API调用的计量或者计费等等。
- 网关 = 路由转发 + 过滤器
`路由转发:接收一切外界请求,转发到后端的微服务上去;
`在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等,这些都可以通过过滤器完成
# 2.为什么需要网关
- 1.网关可以实现服务的统一管理
- 2.网关可以解决微服务中通用代码的冗余问题(如权限控制,流量监控,限流等)
# 3.网管组件在微服务中架构
网关作用:
- 网关统—所有微服务入口
- 网关可以实现请求路由转发(router dispatcher)以及请求过程负载均衡
- 访问服务的身份认证、防报文重放与防数据篡改、功能调用的业务鉴权、响应数据的脱敏、流量与并发控制,甚至基于API调用的计量或者计费等等
服务网关组件(主讲Gateway)
netflix zuul 1. x(效率) zuul 2.x(近两年)
spring cloud gateway组件(Flux异步非阻塞Io模型) 推荐
# gateway(spring)
- 这个项目提供了一个在springmvc之上构建API网关的库。springcloudgateway旨在提供一种简单而有效的方法来路由到api,并为api提供横切关注点,比如:安全性、监控/度量和弹性。
# 特性
- 动态路由
- 请求过滤
1.网关的使用
-
创建一个springboot应用
-
引入相关依赖 注意:去掉项目中的
spring-boot-starter-web
因为存在冲突<!--引入gateway网管组件--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <exclusions> <exclusion> <groupId>*</groupId> <artifactId>*</artifactId> </exclusion> </exclusions> </dependency>
-
编写配置
配置文件方式
server: port: 7979 spring: application: name: GATEWAY cloud: consul: host: localhost port: 8500 gateway: routes: - id: category_router #路由对象唯一标识 uri: http://localhost:8787 #用来类别服务地址 http//localhost:8787/category predicates: #断言 用来配置路由规则 - Path=/category - id: product_router #路由对象唯一标识 #uri: http://localhost:8788 #用来类别服务地址 http//localhost:8788/list uri: lb://PRODUCT #实现负载均衡 predicates: #断言 用来配置路由规则 - Path=/product
java代码配置(了解)
@Bean public RouteLocator routeLocator(RouteLocatorBuilder routeLocatorBuilder) { return routeLocatorBuilder.routes() // 类别路由 .route("category_router", r -> r.path("/category").uri("http://localhost:8787")) // 商品路由 .route("product_router", r -> r.path("/product").uri("http://localhost:8788")) .build(); }
2.网关路由解析规则
-
访问网关路径
-
path规则可以写一个或者多个,支持通配符
3.网关在路由转发时如何实现在均衡
-
现有网关配置存在的问题
现有网关配置方式在uri的属性中路径直接写死为服务的某一个节点,这样没有办法实现请求的负载均衡
-
如何配置网关转发实现负载均衡 Ribbon组件(负裁均衡客户端组件)(gateway中集成了Ribbon)
4.网关的断言和过滤
网关gateway = 断言predicate + 过滤(后置filter)
断言:当请求到达网关时,网关前置处理,满足断言放心请求,不满足断言立即返回
过滤:当请求满足断言的所有条件之后,会向后端服务转发,在向后端服务转发之前会经过一些过滤
断言(predicate)使用
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories
-
- Path=/product
路径断言 -
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
代表该路由规则必须在指定时间之后才能生效 -
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
代表该路由规则必须在指定时间之前有效过了时间失效 -
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
代表路由规则在某个时间段有效 -
- Cookie=name, xiaosiao
- Cookie=chocolate, ch.p
此路由匹配具有名称chocolate
与ch.p
正则表达式匹配的cookie 的请求 -
- Header=X-Request-Id, \d+
请求必须含有指定请求头才行 -
- Method=GET,POST
限定指定请求方式
过滤(gateway filter)
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
-
内置filter
-
AddRequestHeader Filter
- AddRequestHeader=X-Request-red, blue 用来给路由对象的所有转发请求加入指定请求头信息
-
AddRequestParameter Filter
- AddRequestParameter=red, blue 用来给路由对象的所有转发请求加入指定请求参数
-
AddResponseHeader Filter
- AddResponseHeader=X-Response-Red, Blue 用来给路由对象的所有转发请求的响应加入指定头信息
-
PrefixPath Filter
- PrefixPath=/mypath 用来给路由对象的所有转发请求的ur1加入指定前缀信息 如:浏览器访问网关地址/list 前缀路径/mypath 转发到后端服务地址为: uri+前缀路径+地址栏路径===> uri+/mypath/list
-
StripPrefix Filter
- StripPrefix=2 用来给路由对象的所有转发请求的ur1去掉指定2个前缀 如:浏览器访问网关地址:/produet/list stripPefix=1====>/list 后端接口:list
-
-
自定义全局filter 所有请求都整经过全局filter之后再转发到后端服务
@Configuration public class filterCustomerGlobalFilter implements GlobalFilter, Ordered { //类似javaweb doFilter // exchange :交换request response 封装了request response @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //httpRequest对象 ServerHttpRequest request = exchange.getRequest(); // HttpResponse对象 ServerHttpResponse response = exchange.getResponse(); System.out.println("经过全局filter处理。。。。。"); Mono<Void> filter = chain.filter(exchange); System.out.println("响应回来的Filter处理....."); return filter; } // order 排序 int数字:用来指定filter执行顺序﹒默认顺序按照自然数字进行排序 -1表示在所有filter执行之前执行 @Override public int getOrder() { return -1; } }
-
通过网关提供web路径查看路由详细规则
http: / / localhost : 7979/ actuator /gateway/routes
查看网关路由规则详细路径必须在网关配置文件中暴露当前路径
management: endpoints: web: exposure: include: "*"
11.config组件的使用
config定义
config(配置)又称为统一配置中心顾名思义,就是将配置统一管理,配置统一管理的好处是在日后大规模集群部署服务应用时相同的服务配置一致,日后再修改配置只需要统一修改全部同步,不需要一个一个服务手动维护。
统一配置中心组件流程图
作用:用来实现微服务系统中服务配置统一管理组件
组件:统一配置中心服务端(集中管理配置文件)、统一配置中心客户端client
config组件使用
1.config serve
-
选择—个远端git仓库
- 使用GitHub或者gitee
- 创建新仓库
- 获取仓库地址https://gitee.com/zi_long006/configs.git
-
搭建config server统一配置中心服务
-
创建独立spring boot应用
-
引入config server依赖
<--引入config server依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
-
配置application. properties 远程仓库地址
# 远程仓库地址 spring.cloud.config.server.git.uri=https://gitee.com/zi_long006/configs.git spring.cloud.config.server.git.default-label=master # spring.cloud.config.server.git.username= 私有仓库访问用户名与密码 # spring.cloud.config.server.git.password=
-
开启统—配置中心,在入口类加入注解
@EnableConfigServer
@SpringBootApplication @EnableDiscoveryClient @EnableConfigServer // 表示为统一配置中心服务 public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }
-
2.config client
-
创建springboot应用
-
将自身配置交给远端git仓库管理
-
引入config client依赖
<--引入config client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
-
配置文件编写 告诉config server地址
#告诉当前configclient统一配置中心在注册中心服务id spring.cloud.config.discovery.service-id=CONFIGSERVE #开启当前configclient根据服务id去注册中心获取 spring.cloud.config.discovery.enable=true # 配置注册中心 spring.cloud.consul.host=localhost spring.cloud.consul.port=8500 # 获取那个配置文件 1.确定分支 2.确定文件名 3.确定环境 spring.cloud.config.lable=master spring.cloud.config.name=configclient spring.cloud.config.profile=prod
-
启动configclient
3.手动配置刷新
当远端qit仓库中配置发生变化时,不需要重启微服务就可以直接读取远端修改之后配置信息,这种就叫手动配置刷新
-
在每一个需要获取配置文件参数的controller中添加注解
@RefreshScope
-
修改完远端git仓库配置文件之后,向每一个微服务发送一个post方式请求
必须是 post方式 cmd curl -X POST http://localhost:8990/actuator/refresh
-
必须在微服务配置文件中暴露远端配置刷新端点(endpoint )
management.endpoints.web.exposure.include=*
12.BUS组件
简介
springcloudbus使用轻量级消息代理将分布式系统的节点连接起来。然后,可以使用它来广播状态更改(例如配置更改)或其他管理指令。AMQP和Kafka broker(中间件)实现包含在项目中。或者,在类路径上找到的任何springcloudstream绑定器都可以作为传输使用。
通俗定义:bus称之为springcloud中消息总线,主要用来在微服务系统中实现远端配置更新时通过广播形式通知所有客户端刷新配置信息,避免手动重启服务的工作
作用:利用bus 广播特性当某一个状态(配置文件)发生改变时通知到bus中所有服务节点更新当前状态(更新自身配置)
Bus实现原理
RubbitMQ的搭建
https://www.bilibili.com/video/BV1S5411c7hM?p=50&spm_id_from=pageDriver
实现自动刷新配置
-
准备启动MQ服务
MQ服务主机: 192.168.80.128
MQ: 15672端口(Web) 5672端口(JAVA)
虚拟主机:/默认用户: guest 密码:guest
-
配置统—配置中心通过Bus连接到MQ服务
-
统─配置中心服务中引入Bus依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>
-
配置config server文件
spring.rabbitmq.host=192.168.80.128 spring.rabbitmq.port=5679 spring.rabbitmq.username=guest spring.rabbitmq.password=guest spring.rabbitmq.virtual-host=/
-
重启config server
-
-
配置微服务(config client)通过bus连接MQ服务
-
在所有微服务项目中引入Bus依赖
-
在所有微服务项目中配置MQ连接配置,主要这段配置要放入远端仓库管理
spring.rabbitmq.host=192.168.80.128 spring.rabbitmq.port=5679 spring.rabbitmq.username=guest spring.rabbitmq.password=guest spring.rabbitmq.virtual-host=/
-
重启所有微服务时出现,在引入bus依赖之后启动出现如下错误:
错误原因:引入bus依赖启动立即根据配置文件bus配置连接mq服务器,但是此时mq配置信息都在远端,因此bus连接不到mq直接报错,阻止了应用启动
解决方案:允许项目启动时bus组件立即连接mq这个失败,因为获取远端配置之后可以再以远端配置初始化bus组件
spring.cloud.config.fail-fast=true #代表在启动时还没有拉取远端配置完成是的失败都是允许的
-
-
通过向config server统—配置发送POST方式请求实现自动配置刷新
注意:
/actuator/bus-refresh
必须在config server中暴露management.endpoints.web.exposure.include=*
刷新所有服务:
POST http://localhost:8848(configserver地址)/actuator/bus-refresh
刷新指定服务:
POST http://localhost:8848(configserver地址)/actuator/bus-refresh/服务id
利用gitee、github webhooks自动刷新
-
钩子 hooks
根据仓库触发事件执行相应操作
-
webhooks
根据远程仓库触发对应事件发送一个web请求这个请求默认就是POST方式请求
-
在远端仓库中配置webhooks
-
添加webhooks
-
添加webhooks,借助内网穿透(将内网地址转换为公网地址) natapp
下载客户端,cmd启动 natapp --authtoken=XXXXXXXXX 穿透地址:http://hgw3uy.natappfree.cc/actuator/bus-refresh
-
配置webhooks
-
首次配置完成之后config server 发送post方式出现400错误,需要在configServer中加入filter配置
package com.wg.config.component; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.io.ByteArrayInputStream; import java.io.IOException; @Component public class WebHooksFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String url = new String(httpServletRequest.getRequestURI()); if(!url.endsWith("/bus-refresh")){ filterChain.doFilter(servletRequest,servletResponse); return; } RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest); filterChain.doFilter(requestWrapper, servletResponse); } private class RequestWrapper extends HttpServletRequestWrapper { public RequestWrapper(HttpServletRequest request) { super(request); } @Override public ServletInputStream getInputStream() throws IOException { byte[] bytes = new byte[0]; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); ServletInputStream servletInputStream = new ServletInputStream() { @Override public int read() throws IOException { return byteArrayInputStream.read(); } @Override public boolean isFinished() { return byteArrayInputStream.read() == -1 ? true : false; } @Override public boolean isReady() { return false; } @Override public void setReadListener(ReadListener listener) { } }; return servletInputStream; } } }
在入口类中加注解
@ServletComponentScan (basePackages = "com.zzl.filters")
-
重新添加webhooks
-