SpringCloud前置一 :
官网地址:Spring Cloud。
互联网的服务架构分为:
- 单体架构
- 将业务的所有功能集中在一个项目中开发,部署为一个节点叫单体架构
- 优点: 架构简单,部署成本低
- 缺点: 耦合度高
- 分布式架构
- 根据业务功能对系统进行拆分,每个业务模块称为一个服务
- 优点: 降低服务耦合度,有利于服务升级拓展
- 缺点: 维护成本增加,服务间调度复杂度增加
- SOA架构
- 微服务架构
- 微服务是系统架构的一种设计风格,将原本独立的服务拆分成多个小型服务,每个服务独立运行在各自的进程中,服务之间通过HTTP RESTFul API进行通信,每个小型的服务都围绕着系统中的某个耦合度较高的业务进行构建
- 架构特征:
- 单一职责: 微服务拆分粒度更小,每个服务都对应唯一的业务能力
- 自治: 团队独立,技术独立,数据独立,独立部署和交付
- 面向服务: 服务提供统一标准的接口,与语言无关,与技术无关
- 隔离性强: 服务调用做好隔离,容错,降级,避免出现级联问题
- 优点: 拆分粒度小,服务更独立,耦合度更低
- 缺点: 架构复杂,运维,监控,部署难度提高
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现这些组件的自动装配
每个功能都对应单独的数据库
远程调用: RestTemplate
是spring家族中一款基于HTTP协议的组件(HttpURLConnection),他的作用就是: 用于实现基于HTTP的协议方式的服务之间的通信(也就是远程服务调用)
采用同步方式执行HTTP请求,底层使用JDK原生HttpURLConnection API
总结: RestTemplate是spring提供的一个用来模拟浏览器发送和接收响应的一个类,底层封装了HTTP请求,能基于HTTP实现远程调用
使用方式:
- 在消费者启动类中注册RestTemplate
- 在实现类中注入调用
服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
服务消费者:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)
注册中心: ---Eureka(不常用)(新建一个工程进行配置启动类和配置文件)
注册中心启动后,启动服务提供者,服务提供者在注册中心注册服务信息,注册中心就可以拿到服务提供者的ip地址,消费者启动后从注册中心去拉取地址,通过远程调用服务提供者
服务提供者会默认每30秒向注册中心发送心跳告知注册中心服务提供者还存在
注册中心会给服务提供者三次机会,也就是90秒没有收到心跳,则会注销这个服务提供者
有多个服务提供者时,默认采用的是轮询的方式进行访问,为了达到负载均衡我们可以在实现类中远程调用的Bean上添加@LoadBalanced注解开启负载均衡算法,从服务列表中挑选一个
消费者每隔90秒会去注册中心拉取地址,所以当我们服务提供者停掉后,注册中心就没有了这个服务提供者的地址,但是在消费者没有去 拉取地址之前这个地址还会存储消费者中,这是去访问会报错,重新拉取地址后就不会有这个问题了
地址: http://itheima-user/user/
itheima-user 表示服务提供者的名称,后面是访问的路径
服务提供者的名称在配置文件spring中配置,这个名称是作为Eureka中服务的唯一标识
负载均衡Ribbon:
Ribbon是基于Http协议请求的客户端负载均衡器,能实现很丰富的负载均衡算法。
是Netflie发布的负载均衡器,有助于控制HTTP客户端行为.未Ribbon配置服务提供者地址列表后,Ribbon就可基于均衡算法,自动帮助消费者请求
1:用户发起请求,会先到达itheima-order服务
2:itheima-order服务通过Ribbon负载均衡器从eurekaserver中获取服务列表
3:获取了服务列表后,轮询(负载均衡算法)调用
负载均衡算法: ---面试
Ribbon负载均衡内部:
发起请求 ---> 进到RibbonLoadBalancerClient(一个客户端) ---> 去获取服务提供者的应用名称, ---> 根据这个名称去加载服务列表得到服务地址 ---> 拿到地址后通过负载均衡策略(这个策略的接口IRule,有很多实现负载均衡的规则类)以默认轮询的方式通过客户端去调用服务
饥饿加载的步骤 :
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。 而饥饿加载则会在项目启动时创建,降低第一次访问的耗时.
- 在消费者服务的核心配置文件中配置饥饿加载即可(指定饥饿加载的微服务名称)
- 开启饥饿加载
HTTP客户端Feign: (优化从注册中心获取的路径)
Feign的其他功能:
Feign运行自定义配置可以覆盖默认配置:
类型 | 作用 | 说明 |
feign.Logger.Level | 修改日志级别 | 包含四种不同的级别:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encoder | 请求参数编码 | 将请求参数编码,便于通过http请求发送 |
feign. Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign. Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试 |
NONE:默认的,不显示任何日志
BASIC:仅记录请求方法、URL、响应状态码以及执行时间
HEADERS:除了BASIC中定义的信息以外,还有请求和响应的头信息
FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据
1:引入依赖包 spring-cloud-starter-openfeign
2:添加注解@EnableFeignClients开启Feign功能
3:定义远程调用接口,在接口中指定远程调用的【服务名字】、【方法签名】
4:注入接口,执行远程调用(接口)
SpringCloud前置二 :
微服务网关:
SpringCloud 中的网关的实现有两种:
- Gateway 基于spring5提供的webFlux,属于响应式编程的实现,具备更好的性能,为微服务架构提供一种简单有效的统一的API路由管理方式
- Zuul 基于Servlet的实现,功能不强,性能较低,是阻塞式的
SpringCloud gateway是SpringCloud微服务中常用的网关,是所有微服务的统一入口(类似于小区的保安)
网关的核心功能:
请求路由
权限控制
限流
权限控制: 网关作为微服务入口,需要校验用户是否有请求资格,如果没有则进行拦截
路由和负载均衡: 一切请求都必须先经过gateway, 但网关不需要处理业务,而是根据某种规则,把请求转发到某个微服务,这个过程叫做路由.路由的目标服务存在多个时需要做负载均衡
限流: 请求流量过高时,在网关中按照下流的微服务能够接受的速度放行请求,避免服务压力过大
路由配置: 创建一个路由工程,添加依赖,创建启动类,配置文件:注册中心和路由端口,网关,路由地址
断言工厂:
配置文件中写的断言规则只是字符串,这些字符串会被Predicate Factory读取并处理,转变为路由判断的条件
Path | 请求路径必须符合指定规则 | - Path=/red/{segment},/blue/** |
过滤器工厂:
GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理
路由过滤器的种类有很多,spring提供了31种不同的路由过滤器工厂
名称 | 说明 |
AddRequestHeader | 给当前请求添加一个请求头 |
RemoveRequestHeader | 移除请求中的一个请求头 |
AddResponseHeader | 给响应结果中添加一个响应头 |
RemoveResponseHeader | 从响应结果中移除有一个响应头 |
RequestRateLimiter | 限制请求的流量 |
过滤器配置的方式:
过滤器对路由的请求或响应做加工处理,比如添加请求头
- 配置在路由下的过滤器只对当前路由的请求生效
- default-filters 默认过滤器,对所有的路由都生效 ------ 是gatewayFilter默认的过滤器,需要在配置文件中定义
- globalFilter全局过滤器----定义方式,实现globalFilter接口并交给spring管理,使用自定义的逻辑可以
- 对登录状态的判断
- 权限校验
- 请求限流等
过滤器的执行顺序:
请求进入网关会经过三类过滤器: 当前路由过滤器,DefaultFilter,GlobalFilter
请求路由后会将当前路由过滤器,DefaultFilter,GlobalFilter三个合并到一个过滤器链(集合)中,排序后一次执行每个过滤器
排序的规则:
- 每个过滤器都必须指定一个int类型的Order值,order值越小,优先级越高,执行顺序越靠前
- GlobalFilter通过实现Ordered接口,或添加@Order注解指定order的值,由我们指定
- 路由过滤器和DefaultFilter的order有spring指定,默认是按声明顺序从1递增
- 当过滤器order的值一样时,会按照DefaultFilter>路由过滤器>GlobalFilter的顺序执行
跨域问题:
跨域: 域名不一致就是跨域
域名不同: www.taobao.com 和 www.taobao.org 和 www.jd.com 和 miaosha.jd.com
域名相同,端口不同:localhost:8080和localhost8081
跨域问题:
浏览器禁止请求的发起者与服务端发送跨域ajax其请求,请求被浏览器拦截的问题
在配置文件中配置即可解决
Nacos服务注册: 及时一个注册中心也是一个配置管理
添加依赖,配置地址即可使用
服务分级存储模型:
一个服务包含多个集群,一个集群包含多个实例
微服务互相访问时, 会访问同集群的实例,因为本地访问的速度是很快的,当本地集群不可用时,才会去访问其他的集群
同集群优先的负载均衡:
默认的ZoneAvoidanceRule并不能实现根据同集群优先来实现负载均衡。
因此Nacos中提供了一个NacosRule的实现,可以优先从同集群中挑选实例。
权重配置:
但默认情况下NacosRule是同集群内随机挑选,不会考虑机器的性能问题。
因此,Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。
在nacos控制台,找到user-service的实例列表,点击编辑,即可修改权重:
权重配置在(建议这个值在0-1之间,为0则不会访问这个实例)
环境隔离:
Nacos提供了namespace来实现环境隔离功能。
nacos中可以有多个namespace(环境隔离:test dev pro)
namespace下有group(项目隔离 探花项目 头条项目)、service(实例隔离tanhua-server tanhua-service)等不同namespace之间相互隔离
创建namespace:
默认情况下,所有service、data、group都在同一个namespace,名为public:点击命名空间新建进行创建,在服务的配置文件中添加namespace命名的名称
Nacos与Eureka的区别:--- 面试
Nacos的服务实例分为两种类型:
临时实例: 如果实例宕机超过一定时间,会从服务列表剔除,默认的类型
非临时实例: 如果实例宕机,不会从服务列表剔除,也可以叫做永久实例
Nacos注册中心:
服务提供者每个5秒向注册中心发送心跳,某一个服务提供者停掉了,注册中心会立刻知道并将变更的服务列表推送给消费者服务,在通过远程调用去调用服务提供者
Nacos与eureka的共同点:
都支持服务注册和服务拉取
都支持服务提供者心跳方式做健康检测
Nacos与Eureka的区别:
Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
临时实例心跳不正常会被剔除,非临时实例则不会被剔除
Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式(CAP理论:C一致性,A高可用,P分区容错性)
Nacos使用的netty和服务进行连接,属于长连接。eureka使用定时发送和服务进行连接,属于短连接
Nacos不仅作为注册中心,还是配置中心(统一配置管理):
当微服务部署的实例越来越多,统一的配置管理可以有效的对配置文件进行更改,当配置文件变更时,微服务都会向配置中心去拉取最新的配置
Nacos一方面可以将配置集中管理,另一方可以在配置变更时,及时通知微服务,实现配置的热更新。
注意:项目的核心配置,需要热更新的配置才有放到nacos管理的必要。基本不会变更的一些配置还是保存在微服务本地比较好。
微服务从配置中心拉取配置:
微服务拉取nacos管理的配置并且与本地的application.yml配置合并才能完成项目的启动
spring引入了一种新的配置文件:bootstrap.yaml文件,会在application.yml之前被读取,流程如下:
配置热更新:
修改了nacos中的配置,微服务中无需重启即可让配置生效
方式1: 控制层添加注解@RefreshScope刷新配置
方式2: 使用@ConfigurationProperties注解和@Setter
配置共享文件:
配置共享的优先级
当nacos、服务本地同时出现相同属性时,优先级有高低之分:
itheima-user-dev.yaml(当前环境配置) > itheima-user.yaml(共享配置) > application.yml(本地配置)>bootstrap.yml(本地配置)
SpringCloud前置三 :
微服务雪崩问题:
在微服务中,服务器调用关系错综复杂,一个微服务往往依赖于多个其他微服务,有一个服务提供者出现故障,那么当前应用的业务会因为依赖于其他服务而导致其他服务产生阻塞,没有依赖关系的就不会被阻塞.依赖的服务请求被阻塞了,用户得不到响应,tomcat不会释放该线程,最终tomcat的资源被耗尽导致更多的微服务被阻塞.也就是因为一个点而导致很多个服务产生阻塞叫做雪崩
QPS(阈值) : 每秒的处理请求数
四种解决方式:
- 超时处理----属于补救措施
设定超时时间,请求超过一定时间没有响应就返回错误信息,不会无休止的等待
- 仓壁模式----(线程池隔离)属于补救措施
(来源于船舱的设计)也叫线程池隔离,每个业务指定一定数量的线程数,避免tomcat的资源被耗尽,指定的线程数就叫做线程池,某个业务发生故障了不会影响其他的业务
- 断路器模式----(降级熔断)属于补救措施
由断路器统计业务执行的异常比例,如果超出阈值则会熔断该业务,拦截访问该业务的一切请求(异常比例大于阈值).断路器默认关闭,需要配置,当异常比例大于阈值是会开启熔断进入半开启的状态(半开启状态会试着放行一个请求看看是否通过,通过了那么断路器会关闭,业务进入正常状态).整个过程是循环的
- 限流----属于预防措施
流量控制: 限制业务访问的QPS(阈值),避免因流量的突增而产生故障
大量请求都会经过Sentinel保证业务每秒接收的请求数量,多的请求则处于等待状态
总结:
雪崩: 因为微服务之间的相互调用,如果某个微服务发生了故障,则会引起整个链路都无法访问的情况
解决方式:
预防措施: 可以采用限流的方式,避免因为瞬间高并发而导致整个服务故障,进而避免雪崩情况
补救措施: 超时处理,线程池隔离,降级熔断在部分服务故障时,将故障控制在一定范围内,避免雪崩
服务保护技术:
sentinel框架是阿里巴巴开源的微服务流量控制组件
特点:
- 丰富的应用场景:
- 承接了阿里巴巴近十年的双十一大促流量的核心场景
- 例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
- 完备的实时监控:
- 提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
- 广泛的开源生态:
- 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。
- 完善的SPI扩展点:
- Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
Sentinel 的使用: 安装,运行,访问http://localhost:8080,添加依赖,配置控制台,随机访问一个端点控制台就会显示了
限流:流量控制
- 簇点链路
当请求进入微服务时,首先会访问前端控制器(DispatcherServlet),然后进入Controller,Service,Mapper这样的一个调用链叫做簇点链路.簇点链路被监控的每一个接口就是一个资源
默认情况下Sentinel会监控springMVC的每一个端点(也就是Controller中的方法),因此springMVC的每一个端点(Endpoint)就是调用链路中的资源
控制台中流控、熔断等都是针对簇点链路中的资源来设置的,因此我们可以点击对应资源后面的按钮来设置规则:
流控:流量控制
降级:降级熔断
热点:热点参数限流,是限流的一种
授权:请求的权限控制
1.1流控模式:
在添加限流规则时,点击高级选项,可以选择三种流控模式:
直接:统计当前资源的请求,触发阈值时对当前资源直接限流,也是默认的模式
关联:统计与当前资源相关的另一个资源,触发阈值时,对当前资源限流(也就是当达到另一个资源的阈值时,对当前资源限流)
满足以下条件可以使用关联:
两个有竞争关系的资源
一个优先级较高,一个优先级较低
链路:统计从指定链路访问到本资源的请求,触发阈值时,对指定链路限流
只针对从指定链路访问到本资源的请求做统计,判断是否超过阈值
配置注意:
默认情况下,接口实现类中的方法不会被sentinel监控的,我们需要在方法上添加注解进行监控@SentinelResource("goods")(里面随便写)
链路模式中,是对不同来源的两个链路做监控,但是sentinel默认会给进入SpringMVC的所有请求设置同一个root资源,会导致链路模式失效。我们需要在配置文件中关闭springMVC的资源聚合
总结:
直接:对当前资源限流
关联:高优先级资源触发阈值,对低优先级资源限流。
链路:阈值统计时,只统计从指定资源进入当前资源的请求,是对请求来源的限流
流控效果: 指请求达到流控阈值时应该采取的措施,包括三种:
快速失败:达到阈值后,新的请求会被立即拒绝并抛出FlowException异常。是默认的处理方式。
warm up:预热模式,对超出阈值的请求同样是拒绝并抛出异常。但这种模式阈值会动态变化,从一个较小值逐渐增加到最大阈值。
排队等待:让所有的请求按照先后次序排队执行,两个请求的间隔不能小于指定时长
Warm up 预热模式:
阈值一般是一个微服务能承担的最大QPS,但是一个服务刚刚启动时,一切资源尚未初始化(冷启动),如果直接将QPS跑到最大值,可能导致服务瞬间宕机。
warm up也叫预热模式,是应对服务冷启动的一种方案。请求阈值初始值是 maxThreshold(最大阈值) / coldFactor(冷因子),持续指定时长后,逐渐提高到maxThreshold值。而coldFactor的默认值是3.
排队等待:
当请求超过QPS阈值时,快速失败和warm up 会拒绝新的请求并抛出异常。
而排队等待则是让所有请求进入一个队列中,然后按照阈值允许的时间间隔依次执行。后来的请求必须等待前面执行完成,如果请求预期的等待时间超出最大时长,则会被拒绝。
例如:QPS = 5,意味着每200ms处理一个队列中的请求;timeout = 2000,意味着预期等待时长超过2000ms的请求会被拒绝并抛出异常。
如果使用队列模式做流控,所有进入的请求都要排队,以固定的200ms的间隔执行,QPS会变的很平滑,平滑的QPS曲线,对于服务器来说是更友好的。
总结:
快速失败:QPS超过阈值时,拒绝新的请求
warm up: QPS超过阈值时,拒绝新的请求;QPS阈值是逐渐提升的,可以避免冷启动时高并发导致服务宕机。
排队等待:请求会进入队列,按照阈值允许的时间间隔依次执行请求;如果请求预期等待时长大于超时时间,直接拒绝
1.2热点参数限流:
指分别统计参数值相同的请求,判断是否超过QPS阈值
1.2.1全局参数限流:
1.2.2热点参数限流:
热点参数限流对默认的SpringMVC资源无效,需要利用@SentinelResource(“hot”)注解标记资源,括号里面随便写
1.3熔断降级和线程池隔离
限流是一种预防措施,虽然限流可以尽量避免因高并发而引起的服务故障,但服务还会因为其它原因而故障。
而要将这些故障控制在一定范围,避免雪崩,就要靠线程隔离(舱壁模式)和熔断降级手段了。
线程隔离:调用者在调用服务提供者时,给每个调用的请求分配独立线程池,出现故障时,最多消耗这个线程池内资源,避免把调用者的所有资源耗尽。
熔断降级:是在调用方这边加入断路器,统计对服务提供者的调用,如果调用的失败比例过高,则熔断该业务,不允许访问该服务的提供者了。
熔断降级使用feign整合sentinel
配置文件中feign.sentienl.enable=true开启熔断降级
创建工厂类实现FallbackFactory
将FallbackFactory配置到我们共享的接口工程中
1.3.1线程隔离的两种实现方式:线程池隔离: 给每个服务调用业务分配一个线程池,利用线程池本身实现隔离效果
- 信号量隔离(sentinel默认的): 计数器的模式,记录使用的线程数量,达到信号量上限时,禁止新的请求进来
两者的优缺点:
扇出: 请求到A微服务,A微服务依赖了N个微服务,请求到A就会扇出N个微服务
线程隔离的用法说明:
1.3.2熔断降级:
熔断降级是解决雪崩问题的重要手段。其思路是由断路器统计服务调用的异常比例、慢请求比例,如果超出阈值则会熔断该服务。即拦截访问该服务的一切请求;而当服务恢复时,断路器会放行访问该服务的请求。
断路器控制熔断和放行是通过状态机来完成的:
状态机包括三个状态:
closed:关闭状态,断路器放行所有请求,并开始统计异常比例、慢请求比例。超过阈值则切换到open状态
open:打开状态,服务调用被熔断,访问被熔断服务的请求会被拒绝,快速失败,直接走降级逻辑。Open状态5秒后会进入half-open状态
half-open:半开状态,放行一次请求,根据执行结果来判断接下来的操作。
请求成功:则切换到closed状态
请求失败:则切换到open状态
断路器熔断策略有三种:慢调用、异常比例、异常数
慢调用:
业务的响应时长(RT)大于指定时长的请求认定为慢调用请求。在指定时间内,如果请求数量超过设定的最小数量,慢调用比例大于设定的阈值,则触发熔断。
解读:RT超过500ms的调用是慢调用,统计最近10000ms内的请求,如果请求量超过10次,并且慢调用比例不低于0.5,则触发熔断,熔断时长为5秒。然后进入half-open状态,放行一次请求做测试。
异常比例或异常数:
统计指定时间内的调用,如果调用次数超过指定请求数,并且出现异常的比例达到设定的比例阈值(或超过指定异常数),则触发熔断。
1.4授权规则:
可以对请求方的来源做判断和控制的(请求是否是通过网关还是别的浏览器进来的),分为黑名单和白名单两种方式
白名单: 请求方的来源在白名单内的可以访问
黑名单: 请求方的来源在黑名单内的不可以访问
资源名:就是受保护的资源,例如/order/{id}
流控应用:是来源者的名单,
如果是勾选白名单,则名单中的来源被许可访问。
如果是勾选黑名单,则名单中的来源被禁止访问。
允许请求从网关(gateway)访问业务,不允许浏览器访问业务,那么白名单中就要填写网关的来源名称(origin)。
给网关添加请求头
既然获取请求origin的方式是从reques-header中获取origin值,我们必须让所有从gateway路由到微服务的请求都带上origin头。
这个需要利用之前学习的一个GatewayFilter来实现,AddRequestHeaderGatewayFilter。
在控制台配置授权规则
自定义异常结果:
默认情况下,发生限流、降级、授权拦截时,都会抛出异常到调用方。异常结果都是flow limmiting(限流)
异常 | 说明 |
FlowException | 限流异常 |
ParamFlowException | 热点参数限流的异常 |
DegradeException | 降级异常 |
AuthorityException | 授权规则异常 |
SystemBlockException | 系统规则异常 |
规则持久化:
sentinel的所有规则都是内存存储,重启后所有规则都会丢失。在生产环境下,我们必须确保这些规则的持久化,避免丢失。
规则管理模式:
规则是否能持久化,取决于规则管理模式,sentinel支持三种规则管理模式:
原始模式:Sentinel的默认模式,将规则保存在内存,重启服务会丢失。
pull模式
push模式