SpringCloud 微服务解决方案
- 版本
- 0. Alibaba 解决方案
- 概述
- 核心组件分析
- 解决方案架构设计
- 1.服务注册与调用
- 代码实现
- 2. 服务负载均衡设计及实现
- 代码实现
- 3. 基于Feign的远程服务调用
- 3.1 Feign应用过程分析:
- 代码实现
- 3.2 小节面试分析
- 4. Nacos服务配置中心
- 4.1 nacos配置管理模型
- 4.1.1 Nacos 配置管理模型由三部分构成
- 4.1.3 共享配置设计及读取
- 4.2 服务配置和读取步骤
- 代码实现
- 5.Sentinel限流熔断
- 5.1 简介
- 5.2 Sentinel流控规则分析
- 5.2.1 阈值类型分析
- 5.2.2 限流模式
- 5.2.3 限流效果
- 5.3 Sentinel降级
- 5.3.1 降级规则
- 5.3.2 Sentinel异常处理
- 5.3.3 Sentinel降级策略分析
- 5.3.3.1 慢调用比例
- 5.3.3.2 异常比例
- 5.3.3.3 异常数量
- 5.3.4 限流熔断实现步骤(注意点)
- 5.4 Sentinel热点规则分析
- 5.5 Sentinel系统规则
- 5.6 Sentlnel授权规则
- 6. 网关 Spring Cloud Gateway
- 6.1 断言(Predicate)
- 6.1.1 Predicate 内置工厂
- 6.2 网关配置文件说明:
- 6.3 网关执行过程分析:
- 6.4 网关负载均衡调用流程分析
- 6.5 过滤器(Filter)增强分析
- 6.5.1 局部过滤器设计与实现
- 6.5.2 全局过滤器设计与实现
- 6.6 网关限流
- 6.6.1 网关限流实现及注意事项:
- 6.6.2 定制流控网关返回值
- 总结
- 1. Nacos
- 1. 1 服务注册分析
- 1.2 nacos分析
- 1.3 nacos服务配置中心分析
- 2 . Sentinel
- 2.1 Sentinel限流分析
- 2.2 Sentinel熔断降级分析
- 2.3 Sentinel热点规则(数据)分析
- 2.4 sentinel系统规则分析
- 2.5 sentlnel授权规则分析
- 2.6 sentinel总结分析
- 3. Gateway网关分析
- 3.1 网关基本分析
- 3.2 断言(Predicate)增强分析
- 3.3 网关过滤器分析
- 网关限流分析
- 图解
版本
spring-boot:2.3.2.RELEASE
spring-cloud:Hoxton.SR9
spring-cloud-alibaba:2.2.6.RELEASE
参考网址:
https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
0. Alibaba 解决方案
概述
Spring Cloud Alibaba 是Spring Cloud的一个子项目,致力于提供微服务开发的一站式解决方案。此项目包含开发分布式应用微服务的必需组件,方便开发者通过 Spring Cloud 编程模型轻松使用这些组件来开发分布式应用服务。依托 Spring Cloud Alibaba,您只需要添加一些注解和少量配置,就可以将 Spring Cloud 应用接入阿里微服务解决方案,通过阿里中间件来迅速搭建分布式应用系统。
核心组件分析
Spring Cloud Alibaba 默认提供了如下核心功能(先了解):
- 服务限流降级:
默认支持 WebServlet、OpenFeign、RestTemplate、Spring Cloud Gateway, RocketMQ 限流降级功能的接入,可以在运行时通过控制台实时修改限流降级规则,还支持查看限流降级 Metrics 监控。 - 服务注册与发现:
基于Spring Cloud 服务注册与发现标准,借助Nacos进行实现,默认还集成了 Ribbon 的支持。 - 分布式配置管理:
基于Nacos支持分布式系统中的外部化配置,配置更改时自动刷新。 - 消息驱动能力:
基于Spring Cloud Stream 为微服务应用构建消息驱动能力。 - 分布式事务:
使用 @GlobalTransactional 注解, 高效并且对业务零侵入地解决分布式事务问题。。 - 分布式任务调度:
提供秒级、精准、高可靠、高可用的定时(基于 Cron 表达式)任务调度服务。同时提供分布式的任务执行模型,如网格任务。网格任务支持海量子任务均匀分配到所有 Worker上执行。
解决方案架构设计
基于Spring Cloud Alibaba实现的微服务,解决方案设计架构如图所示:
1.服务注册与调用
- 为什么要将服务注册到nacos?(为了更好的查找这些服务)
- 在Nacos中服务提供者是如何向Nacos注册中心(Registry)续约的?(5秒心跳)
- 对于Nacos服务来讲它是如何判定服务实例的状态?(检测心跳包,15,30)
- 服务启动时如何找到服务启动注册配置类?(NacosNamingService)
- 服务消费方是如何调用服务提供方的服务的?(RestTemplate)
代码实现
2. 服务负载均衡设计及实现
- @Bean注解的作用?(一般用于配置类内部,描述相关方法,用于告诉spring此方法的返回值要交给spring管理,bean的名字默认为方法名,假如需要指定名字可以@Bean(“bean的名字”),最多的应用场景是整合第三方的资源-对象)
- @Autowired注解的作用?(此注解用于描述属性,构造方法,set方法等,用于告诉spring框架,按找一定的规则为属性进行DI操作,默认按属性,方法参数类型查找对应的对象,假如只找到一个,则直接注入,类型多个时还会按照属性名或方法参数名进行值的注入,假如名字也不同,就出报错.)
- Nacos中的负责均衡底层是如何实现的?(通过Ribbon实现,Ribbon中定义了一些负载均衡算法,然后基于这些算法从服务实例中获取一个实例为消费方法提供服务)
- Ribbon 是什么?(Netflix公司提供的负载均衡客户端,一般应用于服务的消费方法)
- Ribbon 可以解决什么问题? (基于负载均衡策略进行服务调用, 所有策略都会实现IRule接口)
- Ribbon 内置的负载策略都有哪些?(8种,可以通过查看IRule接口的实现类进行分析)
- @LoadBalanced的作用是什么?(描述RestTemplate对象,用于告诉Spring框架,在使用RestTempalte进行服务调用时,这个调用过程会被一个拦截器进行拦截,然后在拦截器内部,启动负载均衡策略。)
- 我们可以自己定义负载均衡策略吗?(可以,基于IRule接口进行策略定义,也可以参考NacosRule进行实现)
代码实现
3. 基于Feign的远程服务调用
3.1 Feign应用过程分析:
- 1 . 通过 @EnableFeignCleints 注解告诉springcloud,启动 Feign Starter 组件。
- 2 . Feign Starter 在项目启动过程中注册全局配置,扫描包下所由@FeignClient注解描述的接口,然后由系统底层创建接口实现类(JDK代理类),并构建类的对象,然后交给spring管理(注册 IOC 容器)。
- 3 . 接口被调用时被动态代理类逻辑拦截,将 @FeignClient 请求信息通过编码器生成 Request对象,基于此对象进行远程过程调用。
- 4 . 请求对象经Ribbon进行负载均衡,挑选出一个健康的 Server 实例(instance)。
- 5 . 通过 Client 携带 Request 调用远端服务返回请求响应。
- 6 . 通过解码器生成 Response 返回客户端,将信息流解析成为接口返回数据。
代码实现
3.2 小节面试分析
- 为什么使用feign?(基于Feign可以更加友好的实现服务调用,简化服务消费方对服务提供方方法的调用)。
- @FeignClient注解的作用是什么?(告诉Feign Starter,在项目启动时,为此注解描述的接口创建实现类-代理类)
- Feign方式的调用,底层负载均衡是如何实现的?(Ribbon)
- @EnableFeignCleints 注解的作用是什么?(描述配置类,例如启动类)
4. Nacos服务配置中心
以前,我们把软件配置写在一个配置文件中,就像 Windows 下的 ini 文件,或是 Linux 下的 conf 文件。然而,在分布式系统下,这样的方式就变得非常不好管理,并容易出错。假如生产环境下,项目现在正在运行,此时修改了配置文件,我们需要让这些配置生效,通常的做法是不是要重启服务。但重启是不是会带来系统服务短时间的暂停,从而影响用户体验呢,还有可能会带来经济上的很大损失(例如双11重启下服务)。基于这样的背景,配置中心诞生了。
市场上主流配置中心有Apollo(携程开源),nacos(阿里开源),Spring Cloud Config(Spring Cloud 全家桶成员)。我们在对这些配置中心进行选型时重点要从产品功能、使用体验、实施过程和性能等方面进行综合考量。
4.1 nacos配置管理模型
4.1.1 Nacos 配置管理模型由三部分构成
namespace:命名空间,对不同的环境进行隔离,比如隔离开发环境和生产环境,可以有多个
group:分组,将若干个服务或者若干个配置归结为一组
service/dataid: 某一个服务或者配置文件
一个命名空间可以包含多个组,每个组可以包含多个服务或配置文件,一个组内的服务或者配置文件名字不可以重复,不同组之间服务或者配置文件名字可以一样.
4.1.3 共享配置设计及读取
当同一个namespace的多个配置文件中都有相同配置时,可以对这些配置进行提取,然后存储到nacos配置中心的一个或多个指定配置文件,那个微服务需要,就在哪个服务的配置中设置读取即可
4.2 服务配置和读取步骤
1.添加依赖
2.修改配置文件名并添加配置信息
3.打开nacos配置中心,新建配置
4. 在Controller中添加方法
代码实现
5.Sentinel限流熔断
5.1 简介
Sentinel (分布式系统的流量防卫兵) 是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点, 从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。
使用webmvc方式,即使用SentinelWebInterceptor拦截器
5.2 Sentinel流控规则分析
5.2.1 阈值类型分析
QPS(Queries Per Second):当调用相关url对应的资源时,QPS达到单机阈值时,就会限流。(每秒请求数量)
线程数:当调用相关url对应的资源时,线程数达到单机阈值时,就会限流。
5.2.2 限流模式
Sentinel的流控模式代表的流控的方式,默认【直接】,还有关联,链路。
1 . 直接模式
Sentinel默认的流控处理就是【直接->快速失败】。
2. 关联模式
当关联的资源达到阈值,就限流自己,例如url1设置了关联资源为url2,假如关联资源url2的访问超过了设定的阈值,就限流url1.
3.链路模式
链路模式只记录指定链路入口的流量,也就是当多个服务对指定资源调用时,假如流量超出了指定的阈值,则进行限流.
被调用的方法用 @SentinelResource 注解进行描述,然后分别用不同业务方法对此业务进行调用,假如A业务设置了链路模式的限流,在B业务中是不受影响的。
说明:流控模式为链路模式时,Sentinel Web过滤器默认会聚合所有URL的入口为sentinel_spring_web_context,因此单独对指定链路限流会不生效,需要在application.yml添加如下语句来关闭URL PATH聚合,例如:
sentinel:
web-context-unify: false #关闭URL PATH聚合
5.2.3 限流效果
1 . 快速失败
流量达到指定阀值,直接返回报异常。(类似路前方坍塌,后面设定路标,让后面的车辆返回)
2 . WarmUp (预热)
WarmUp也叫预热,根据codeFactor(默认3)的值,(阀值/codeFactor)为初始阈值,经过预热时长,才到达设置的QPS的阈值,假如单机阈值为100,系统初始化的阈为 100/3 ,即阈值为33,然后过了10秒,阈值才恢复到100。
- 排队等待
匀速排队,让每个请求以匀速的速度通过,阈值必须设置为QPS,否则无效.
5.3 Sentinel降级
除了流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。
当资源被降级后,在接下来的降级时间窗口之内,对该资源的调用都自动熔断(默认行为是抛出 DegradeException)。
5.3.1 降级规则
这里表示熔断策略为慢调用比例,表示链路请求数超过3时,假如平均响应时间假如超过200毫秒的有50%,则对请求进行熔断,熔断时长为10秒钟,10秒以后恢复正常。
5.3.2 Sentinel异常处理
系统提供了默认的异常处理机制,假如默认处理机制不满足我们需求,我们可以自己进行定义。定义方式上可以直接或间接实现BlockExceptionHandler接口,并将对象交给spring管理(在类上添加@Component注解)。
5.3.3 Sentinel降级策略分析
Sentinel熔断降级支持慢调用比例、异常比例、异常数三种策略。
5.3.3.1 慢调用比例
慢调用值耗时大于阈值RT(Reponse Time)的请求称为慢调用,阈值RT由用户设置
慢调用逻辑中的状态分析如下:
- 熔断(OPEN):请求数大于最小请求数并且慢调用的比率大于比例阈值则发生熔断,熔断时长为用户自定义设置。
- 探测(HALFOPEN):当熔断过了定义的熔断时长,状态由熔断(OPEN)变为探测(HALFOPEN)。
- 关闭(CLOSED):如果接下来的一个请求小于最大RT,说明慢调用已经恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED),如果接下来的一个请求大于最大RT,说明慢调用未恢复,继续熔断,熔断时长保持一致
注意:Sentinel默认统计的RT上限是4900ms,超出此阈值的都会算作4900ms,若需要变更此上限可以通过启动配置项-Dcsp.sentinel.statistic.max.rt=xxx来配置
5.3.3.2 异常比例
当资源的每秒请求数大于等于最小请求数,并且异常总数占通过量的比例超过比例阈值时,资源进入降级状态
异常比例中的状态分析如下:
- 熔断(OPEN):当请求数大于最小请求并且异常比例大于设置的阈值时触发熔断,熔断时长由用户设置。
- 探测(HALFOPEN):当超过熔断时长时,由熔断(OPEN)转为探测(HALFOPEN)
- 关闭(CLOSED):如果接下来的一个请求未发生错误,说明应用恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED)。如果接下来的一个请求继续发生错误,说明应用未恢复,继续熔断,熔断时长保持一致。
5.3.3.3 异常数量
当资源近1分钟的异常数目超过阈值(异常数)之后会进行服务降级.注意,由于统计时间窗口是分钟级别的,若熔断时长小于60s,则结束熔断状态后可能再次进入熔断状态.
基于异常数的状态分析如下:
- 熔断(OPEN):当请求数大于最小请求并且异常数量大于设置的阈值时触发熔断,熔断时长由用户设置。
- 探测(HALFOPEN):当超过熔断时长时,由熔断(OPEN)转为探测(HALFOPEN)
- 关闭(CLOSED):如果接下来的一个请求未发生错误,说明应用恢复,结束熔断,状态由探测(HALF_OPEN)变更为关闭(CLOSED)如果接下来的一个请求继续发生错误,说明应用未恢复,继续熔断,熔断时长保持一致。
5.3.4 限流熔断实现步骤(注意点)
1 . 添加依赖(pom文件)
<!--sentinel依赖->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--springboot监控->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- 添加配置文件(application.yml)
spring:
cloud:
sentinel:
transport:
port: 8719 #可以不写,默认值8719,跟sentinel控制台交流的端口,随意指定一个未使用的端口即可
dashboard: localhost:8180 # 指定sentinel控制台地址。
web-context-unify: false #取消链路聚合,否则sentinel 1.7.2以后,会把所有链路聚合到一个入口上
#feign方式的服务熔断处理
feign:
hystrix: # hystrix 含义是熔断(相当于服务停止了)或降级
enabled: true
- 添加具体方法实现
其中,链路限流时,须在共同访问的资源方法上添加**@SentinelResource** 注解,并在配置文件中关闭URL PATH聚合.
再对异常处理机制自定义的时候,需要实现BlockExceptionHandler接口,并将对象交给spring管理(在类上添加**@Component**注解)。 - Sentinel中心设置对应限流和熔断规则
5.4 Sentinel热点规则分析
何为热点?热点即经常访问的数据。
Sentinel会利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。
热点规则的限流模式只有QPS模式,配置策略中的参数索引为@SentinelResource注解的方法参数下标.
也可以设置特定参数外例,设置参数类型,参数值,限流阈值
特别注意:
1 . 热点业务方法上需要添加 @SentinelResource(“res”)注解,并赋唯一值(配置热点规则中的资源名)
2. 方法中的参数获取需要使用@RequestParam注解,例: (@RequestParam(“id”) Integer id)
5.5 Sentinel系统规则
Sentinel的系统保护规则是从应用级别的入口流量进行控制,从单台机器的总体 Load、RT、入口 QPS 、线程数和CPU使用率五个维度监控应用数据,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
- Load(仅对 Linux/Unix-like 机器生效):当系统 load1 超过阈值,且系统当前的并发线程数超过系统容量时才会触发系统保护。系统容量由系统的 maxQps * minRt 计算得出。设定参考值一般是 CPU cores * 2.5。
- CPU使用率:当系统 CPU 使用率超过阈值即触发系统保护(取值范围 0.0-1.0)。
- RT:当单台机器上所有入口流量的平均 RT 达到阈值即触发系统保护,单位是毫秒。
- 线程数:当单台机器上所有入口流量的并发线程数达到阈值即触发系统保护。
- 入口 QPS:当单台机器上所有入口流量的 QPS 达到阈值即触发系统保护。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务。
5.6 Sentlnel授权规则
sentinel可以基于黑白名单方式进行授权规则设计
黑白名单规则(AuthorityRule)非常简单,主要有以下配置项:
- 资源名:即限流规则的作用对象
- 流控应用:对应的黑名单/白名单中设置的规则值,多个值用逗号隔开.
- 授权类型:白名单,黑名单(不允许访问).
注意点:
1 . 添加RequestOriginParser接口的实现类,并交给spring管理(类上添加 @Component注解),在方法中自定义一个参数名(随意指定)
2. 添加授权规则,流控应用的值为传递的参数值
3. 当请求路径中的参数和方法中定义的一样且传递的参数值和授权规则一致,就不允许访问(允许访问).
6. 网关 Spring Cloud Gateway
网关本质上要提供一个各种服务访问的入口,并提供服务接收并转发所有内外部的客户端调用,还有就是权限认证,限流控制等等。
SpringCloud GetWay 是基于webFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
Gateway 由三部分组成:
(1)Filter(过滤器):
使用它拦截和修改请求,并且对上游的响应,进行二次处理。
(2)Route(路由):
一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。
(3)Predicate(断言):
这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的输入类型是一个 ServerWebExchange。
6.1 断言(Predicate)
Predicate(断言)又称谓词,用于条件判断,只有断言结果都为真,才会真正的执行路由。断言其本质就是定义路由转发的条件。
6.1.1 Predicate 内置工厂
1 .基于Datetime类型的断言工厂
此类型的断言根据时间做判断,主要有三个:
1) AfterRoutePredicateFactory:判断请求日期是否晚于指定日期
2) BeforeRoutePredicateFactory:判断请求日期是否早于指定日期
3) BetweenRoutePredicateFactory:判断请求日期是否在指定时间段内
例: ( - After=2021-08-23T23:59:59.789+08:00[Asia/Shanghai] )
2 . 基于header的断言工厂HeaderRoutePredicateFactory
判断请求Header是否具有给定名称且值与正则表达式匹配。
例如:( - Header=X-Request-Id, \d+ )
3 . 基于Method请求方法的断言工厂,
MethodRoutePredicateFactory接收一个参数,判断请求类型是否跟指定的类型匹配。
例如:( - Method=GET )
4 . 基于Query请求参数的断言工厂,QueryRoutePredicateFactory :
接收两个参数,请求param和正则表达式, 判断请求参数是否具 有给定名称且值与正则表达式匹配。
例如:( - Query=pageSize,\d+ )
6.2 网关配置文件说明:
spring:
application:
name: sca-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
discovery:
locator:
enabled: true #开启基于服务名获取服务实例的功能(基于服务名创建路由)
routes: #网关路由配置
- id: route01 #路由id,可以配置不同的路由id以实现多个服务
uri: lb://sca-provider #lb代表负载均衡 底层默认使用ribbon实现
predicates: #断言(谓词):用于条件判断,只有断言结果都为真,才会真正的执行路由。断言其本质就是定义路由转发的条件。
- After=2021-08-23T15:59:59.789+08:00[Asia/Shanghai] # 基于Datetime类型的断言工厂(After,Before,Between),时间值可通过ZonedDateTime.now()获取。
- Header=X-Request-Id, \d+ #判断请求Header是否具有给定名称且值与正则表达式匹配,数字递增
- Method=GET # 基于Method请求方法的断言工厂
- Query=pageSize,\d+ # 基于Query请求参数的断言工厂
- Path=/nacos/provider/echo/** #请求路径设计
filters: #网关过滤器,用于对断言(谓词)中的内容进行判断分析以及处理
- StripPrefix=1 #转发之前去掉path中第一层路径,例如nacos
6.3 网关执行过程分析:
1 . 客户端向Spring Cloud Gateway发出请求。
2. 如果Gateway Handler Mapping 通过断言(predicates)的集合,确定请求与路由(Routers)匹配,则将其发送到Gateway Web Handler。
3. Gateway Web Handler 通过确定的路由中所配置的过滤器集合链式调用过滤器(也就是所谓的责任链模式)。
4. Filter由虚线分隔的原因是, Filter可以在发送代理请求之前和之后运行逻辑。处理的逻辑是 在处理请求时 排在前面的过滤器先执行,而处理返回相应的时候,排在后面的过滤器先执行。
6.4 网关负载均衡调用流程分析
6.5 过滤器(Filter)增强分析
过滤器(Filter)就是在请求传递过程中,对请求和响应做一个处理。Gateway 的Filter从作用范围可分为两种:GatewayFilter与GlobalFilter。其中:
1 . GatewayFilter:应用到单个路由或者一个分组的路由上。
2 . GlobalFilter:应用到所有的路由上。全局过滤器
6.5.1 局部过滤器设计与实现
filters: #网关过滤器,用于对断言(谓词)中的内容进行判断分析以及处理
- StripPrefix=1 #转发之前去掉path中第一层路径,例如nacos
- AddRequestHeader=X-Request-Foo, Bar #为原始请求添加名为 X-Request-Foo ,值为 Bar 的请求头
- AddRequestParameter=foo, bar # 为原始请求添加名为foo,值为bar的参数
- PrefixPath=/mypath #为原始的请求路径添加一个前缀路径
- name: RequestSize #设置允许接收最大请求包的大小
args:
# 单位为字节
maxSize: 5000000
6.5.2 全局过滤器设计与实现
全局过滤器(GlobalFilter)作用于所有路由, 无需配置。在系统初始化时加载,并作用在每个路由上。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。
实现步骤及注意点:
1 . 实现 GlobalFilter , Ordered这两个接口,并实现方法
2. 对象和json字符串的转换使用Gson(谷歌的一个API,使用之前需要添加依赖)。使用方法:gson.tojson(对象)
3. 返回响应对象时,注意返回的是Mono类型的dataBuffer对象,new dataBuffer对象时需要传递一个字节数组.
4. 重写的Ordered接口中的方法是设置该过滤器的级别,数字越小,级别越高
package com.jt.config;
import com.google.gson.Gson;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
/**
* @author 小新
* @date 2021/08/24 14:39
*/
/**
* 自定义全局过滤器,作用于所有路由
*/
@Component
public class AuthGatewayFilter implements GlobalFilter , Ordered {
@Value("${white.prefix}")
private String whitePrefix;
/**
* 业务写到这个filter方法内部,需要在配置文件添加白名单
* @param exchange 基于此对象可以获取请求和响应对象
* @param chain 这个对象指向了一个过滤链(这个链中有多个过滤器)
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取请求对象
ServerHttpRequest request = exchange.getRequest();
//2.获取请求数据
String path = request.getURI().getPath();
System.out.println("path = "+path);
// String username = request.getQueryParams().getFirst("username");
//3.对请求数据进行处理
System.out.println("whitePrefix = :"+ whitePrefix);
//例如判定请求的path是否在我们的允许范围之内
if(!path.startsWith(whitePrefix)) { //判断路径的前缀
//获取响应对象
ServerHttpResponse response = exchange.getResponse();
//设置响应状态码,这里的值为401
//response.setStatusCode(HttpStatus.UNAUTHORIZED);
Map<String,Object> map = new HashMap<>();
map.put("message", "request failer");
map.put("status", 502);
//将map转换成json
Gson gson = new Gson(); //谷歌的一个 对象与json字符串之间转换 的工具,需要添加依赖
String jsonStr = gson.toJson(map);
//把json串转字节数组
byte[] bytes = jsonStr.getBytes();
DataBuffer dataBuffer = response.bufferFactory().wrap(bytes);
//返回响应对象,结束请求
return response.writeWith(Mono.just(dataBuffer));
}
//4.返回响应数据
return chain.filter(exchange); //将请求交给下一个过滤器处理
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE; //设置该过滤器的级别,数字越小,级别越高
}
}
6.6 网关限流
网关是所有请求的公共入口,所以可以在网关进行限流.
限流类型: Route ID 和 API分组.
可以针对 IP Host Header URL参数 Cookie 参数类型进行限流
其中API分组中的匹配串是访问时的url路径
6.6.1 网关限流实现及注意事项:
1 . 添加依赖(两个)
2. 启动服务之前添加sentinel的jvm参数,
-Dcsp.sentinel.app.type=1
3 . 在sentinel面板设置限流策略 Route ID,自定义API 两种
3.1 Route ID类型的限流:
新增流控规则为Route ID
API名称为配置文中的路由id
可以指定基于属性的限流策略(选项中的针对请求属性)
3.2 自定义API维度限流
新增API分组,自定义API名称,匹配串为访问时的url路径
新增流控规则为API分组
选择刚定义好的API名称
6.6.2 定制流控网关返回值
当发生限流时,页面提示信息不友好,这时需要自定义返回信息
定义配置类.设计流控返回值
package com.jt.config;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.google.gson.Gson;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.HashMap;
import java.util.Map;
/**
* @author 小新
* @date 2021/08/25 10:51
*/
@Configuration //配置类,交给spring管理
public class GatewayConfig {
//无参构造,对象创建时就会调用
public GatewayConfig(){
GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
//map存放返回的数据
Map<String,Object> map = new HashMap<>();
map.put("state", 429);
map.put("message", "two many request");
Gson gson = new Gson();
//把map转换成json串
String jsonStr = gson.toJson(map);
//响应
return ServerResponse.ok().body(Mono.just(jsonStr),String.class);//String.class表示响应数据类型
}
});
}
}
总结
1. Nacos
1. 1 服务注册分析
- 何为注册中心?(用于记录服务信息的一个web服务,例如淘宝平台,滴滴平台,美团外卖平台,……)
- 注册中心的核心对象?(服务提供方,服务消费方,注册中心-Registry)
- 市面上常用注册中心?(Google-Consul,Alibaba-Nacos,…)
- 微服务架构下项目的构建过程?(聚合工程)
- Nacos安装、启动、服务的注册、发现机制以及实现过程?
- Feign的基本应用以及底层底层调用原理?
1.2 nacos分析
- Nacos是什么,提供了什么特性(服务的注册、发现、配置)?
- 你为什么会选择Nacos?(活跃度、稳定、性能、学习成本)
- Nacos的官网?(nacos.io)
- Nacos在github的源码?(github.com/alibaba/nacos)
- Nacos在windows环境下安装?(解压即可使用)
- Nacos在windows中的的初步配置?(application.properties访问数据库的数据源)
- Nacos服务注册的基本过程?(服务启动时发送web请求)
- Nacos服务消费的基本过程?(服务启动时获取服务实例,然后调用服务)
- Nacos服务负载均衡逻辑及设计实现?(Ribbon)
- 注册中心的核心数据是什么?(服务的名字和它对应的网络地址)
- 注册中心中心核心数据的存取为什么会采用读写锁?(底层安全和性能)
- Nacos健康检查的方式?(基于心跳包机制进行实现)
- Nacos是如何保证高可用的?(重试,本地缓存、集群)
- Feign是什么,它的应用是怎样的,feign应用过程中的代理对象是如何创建的(JDK)?
- Feign方式的调用过程,其负载均衡是如何实现?(Ribbon)
1.3 nacos服务配置中心分析
- 什么是配置中心?(存储项目配置信息的一个服务)
- 为什么要使用配置中心?(集中管理配置信息,动态发布配置信息,无需重启服务,更好保证服务的可用)
- 市场上有哪些主流的配置中心?(Apollo,nacos,……)
- 配置中心的选型。(市场活跃度、稳定性、性能、易用)
- Nacos配置管理模型是怎样的?(命名空间-namespace,分组-group,服务实例-dataId)
- 配置中心一般都会配置什么内容?(可能会经常变化的配置信息,例如连接池,日志、线程池、限流熔断规则)
- 什么信息一般不会写到配置中心?(服务端口,服务名,服务的注册地址,配置中心)
- 项目中为什么要定义bootstrap.yml文件?(此文件被读取的优先级比较高,可以在服务启动时读取配置中心的数据)
- Nacos配置中心宕机了,我们的服务还可以读取到配置信息吗?(可以从内存中获取,客户端获取了配置中心的配置信息以后,会将配置信息在本地内存中存储一份.)
- 微服务应用中我们的客户端如何获取配置中心的信息?(我们的服务一般首先会从内存读取配置信息,同时我们的微服务还可以定时(30s)向nacos配置中心发请求拉取(pull)更新的配置信息,如果此次没有获取到新数据,会等待29.5s后无功而返,)
- Nacos客户端(微服务业务)如何动态感知配置中心数据变化的?(nacos2.0之前nacos客户端采用长轮询机制拉取nacos服务的配置信息,pull(每隔30秒)+“push(是在服务端的配置信息变更以后,通知服务端中处于等待状态的客户端队列中的客户端对象,并更新客户端对应的响应数据,然后返回响应)”)
- 服务启动后没有从配置中心获取我们的配置数据是什么原因?(依赖,配置文件名字bootstrap.yml,配置中心的dataId名字是否正确,分组是否正确,配置的名字是否正确,缩进关系是否正确,假如是动态发布,类上是否有@RefreshScope注解)
- 你项目中使用的日志规范是什么?(SLF4J),创建java中的日志对象(slf4j是java中的日志规范,是日志对外的窗口,是门面设计模式),注意导包名---->org.slf4j.Logger,目前市场上对SLF4J规范的实现主要有种:log4j,logback
- 你了解项目中的日志级别吗?(trace,debug,info,warm,error,…,可以基于日志级别控制日志的输出)
2 . Sentinel
2.1 Sentinel限流分析
- Sentinel是什么?(阿里推出一个流量控制平台,防卫兵)
- 类似Sentinel的产品你知道有什么?(hystrix-一代微服务产品)
-你了解哪些限流算法?(计数器、令牌桶、漏斗算法,滑动窗口算法,…) - Sentinel 默认的限流算法是什么?(滑动窗口算法)
- 你了解sentinel中的阈值应用类型吗?(两种-QPS,线程数)
- Sentinel 限流规则中默认有哪些限流模式?(直接,关联,链路)
- Sentinel的限流效果有哪些?(快速失败,预热,排队)
- Sentinel 为什么可以对我们的业务进行限流,原理是什么?
我们在访问web应用时,在web应用内部会有一个拦截器,这个拦截器会对请求的url进行拦截,拦截到请求以后,读取sentinel 控制台推送到web应用的流控规则,基于流控规则对流量进行限流操作。
2.2 Sentinel熔断降级分析
- 何为降级熔断?(让外部应用停止对服务的访问,生活中跳闸,路障设置-此路不通)
- 为什么要进行熔断呢?(平均响应速度越来越慢或经常出现异常,这样可能会导致调用链堆积,最终系统崩溃)
- Sentinel中限流,降级的异常父类是谁?(BlockException)
- Sentinel 出现降级熔断时,系统底层抛出的异常是谁?(DegradeException)
- Sentinel中异常处理接口是谁?(BlockExceptionHandler)
- Sentinel中异常处理接口下默认的实现类为? (DefaultBlockExceptionHandler)
- 假如Sentinel中默认的异常处理规则不满足我们的需求怎么办?(自己定义)
- 我们如何自己定义Sentinel中异常处理呢?(直接或间接实现BlockExceptionHandler ,并将对象交给spring管理)
2.3 Sentinel热点规则(数据)分析
- 如何理解热点数据?(访问频度比较高的数据,某些商品、谋篇文章、某个视频)
- 热点数据的限流规则是怎样的?(主要是针对参数进行限流设计)
- 热点数据中的特殊参数如何理解?(热点限流中的某个参数值的阈值设计)
- 对于热点数据的访问出现限流以后底层异常是什么?(ParamFlowException)
2.4 sentinel系统规则分析
- 如何理解sentinel中的系统规则?(是对所有链路的控制规则,是一种系统保护策略)
- Sentinel的常用系统规则有哪些?(RT,QPS,CPU,线程,Load-linux,unix)
- Sentinel系统保护规则被触发以后底层会抛出什么异常?(SystemBlockException)
2.5 sentlnel授权规则分析
- 如何理解Sentinel中的授权规则?(对指定资源的访问给出的一种简易的授权策略)
- Sentinel的授权规则是如何设计的?(白名单和黑名单)
- 如何理解Sentinel中的白名单?(允许访问的资源名单)
- 如何理解Sentinel中的黑名单?(不允许访问的资源名单)、
Sentinel如何识别白名单和黑名单?(在拦截器中通过调用RequestOriginParser对象的方法检测具体的规则) - 授权规则中RequestOriginParser类的做用是什么?(对流控应用值进行解析,检查服务访问时传入的值是否与RequestOriginParser的parseOrigin方法返回值是否相同。)
2.6 sentinel总结分析
- Sentinel诞生的背景?(计算机的数量是否有限,处理能力是否有限,并发比较大或突发流量比较大)
- 服务中Sentinel环境的集成,初始化?(添加依赖-两个,sentinel配置)
- Sentinel 的限流规则?(阈值类型-QPS&线程数,限流模式-直接,关联,链路)
- Sentinel 的降级(熔断)策略?(慢调用,异常比例,异常数)
- Sentinel 的热点规则设计(掌握)?
- Sentinel 系统规则设计?(了解,全局规则定义,针对所有请求有效)
- Sentinel 授权规则设计?(掌握,黑白名单)
- 为什么要限流?
- 你了解的那些限流框架?(sentinel)
- 常用的限流算法有那些?(计数,令牌桶-电影票,漏桶-漏斗,滑动窗口)
- Sentinel有哪些限流规则?(QPS,线程数)
- Sentinel有哪些限流模式?(直接,关联-创建订单和查询订单,链路限流-北京六环外不限号,但是五环就限号)
- Sentinel 的降级(熔断)策略有哪些?(慢调用-响应时长,异常比例-异常占比,异常数)
- Sentinel 的热点规则中的热点数据?(热卖商品,微博大咖,新上映的电影)
- 如何理解Sentinel 授权规则中的黑白名单?
3. Gateway网关分析
3.1 网关基本分析
- 网关层面是如何实现负载均衡的?(Ribbon)
- 网关层面是如何通过服务名查找服务实例的?(通过服务名去查找具体的服务实例)
- 你了解Ribbon中的哪些负载均衡算法?(轮询,权重,hash,……可通过IRule接口进行查看分析)
- 网关进行请求转发的流程是怎样,有哪些关键对象?(XxxHandlerMapping,Handler,。。。)
- 网关层面服务的映射方式怎样的?(谓词-path,…,服务名/服务实例)
- 网关层如何记录服务的映射?(通过map,并要考虑读写锁的应用)
- 网关(Gateway)诞生的背景?(第一:统一微服务访问的入口,第二:对系统服务进行保护,第三进行统一的认证,授权,限流)
- 网关的选型?(Netifix Zuul(不维护),Spring Cloud Gateway,…)
- Spring Cloud Gateway的入门实现(添加依赖,路由配置,启动类)
- Spring Cloud Gateway中的负载均衡?(网关服务注册,服务的发现,基于uri:lb://服务id方式访问具体服务实例)
- Spring Cloud Gateway中的断言配置?(掌握常用几个就可,用时可以通过搜索引擎去查)
- Spring Cloud Gateway中的过滤器配置?(掌握过滤器中的两大类型-局部和全局)
- Spring Cloud Gateway中的限流设计?(Sentinel)
- Gateway在互联网架构中的位置?(nginx->gateway–>微服务–>微服务(服务之间的调用))
- Gateway底层负载均衡的实现?(Ribbon)
- Gateway应用过程中设计的主要概念?(路由id,路由uri,断言,过滤器)
- Gateway中你做过哪些断言配置?(after(时间断言工厂),header,path,cookie,…)
- Gateway中你用的过滤器有哪些?(添加前缀,去掉前缀,添加请求头,…,负载均衡,…)
3.2 断言(Predicate)增强分析
- 何为谓词?(网关中封装了判断逻辑的一个对象)
- 谓词逻辑的设计是怎样的?(谓词判断逻辑返回值为true则进行请求转发)
- 你了解哪些谓词逻辑?(path,请求参数,ip,请求方式,cookie,请求头,….)
- 我们可以自己定义谓词工厂对象吗?(可以的)
3.3 网关过滤器分析
- 网关过滤器的作用是什么?(对请求和响应数据做一个预处理)
- 网关过滤器的类型有哪些?(局部过滤器,全局过滤器)
- 如何理解局部过滤器?(针对具体链路的应用的过滤器,需要进行配置)
- 你了解那些局部过滤器?(请求头,请求参数,前缀路径,接收请求包大小)
- 如何理解全局过滤器?(作用域所有请求链路)
- 如何自定义全局过滤器>(直接或间接实现GlobalFilter, Ordered两个接口)
网关限流分析
- 网关层面结合sentinel实现限流,其限流的类型有几种?(两种-route id,api)
- 网关层面可以自定义限流后的异常处理结果吗?(可以)
- 你知道Sentinel底层限流的算法有哪些?(滑动窗口,令牌桶,漏斗,。。。)
图解