本文总结于黑马项目,其中部分截图取自视频
总结的有问题大佬勿喷
微服务
常用的微服务组件:
服务注册发现:
nacos,Eureka,Consoul
服务远程调用:
OpenFegin,Dubbo
服务链路监控:
Zipkin,Sleuth
统一配置管理:
SpringCloudConfig,nacos
统一网关路由:
gateWay,zuul
流控,降级,保护:
Hystix,Sentinel
SpringCLoudAlibaba:nacos,OpenFegin,gateway,sentinel
版本:一般目前使用springCloud2021版本对应 springboot2.+
例如:
Spring Cloud 2020.x 通常是兼容 Spring Boot 2.3.x 或 2.4.x 的。
Spring Cloud 2021.x 通常是兼容 Spring Boot 2.5.x 或 2.6.x 的。
Spring Cloud 2022.x 通常是兼容 Spring Boot 2.7.x 或 3.0.x 的。
对于SpringCloud项目的理解:
Spring Cloud 的底层基于 Spring Boot 的。在构建 Spring Cloud 项目时,通常是通过多个 Spring Boot 项目来组成一个完整的微服务架构,并借助 Spring Cloud 提供的各种组件(如 Nacos、Eureka、Config、Ribbon、Zuul 等)来管理这些服务的注册、发现、配置、负载均衡、路由等。
所以说在选用SpringCloud组件的时候,需要对齐Springboot的版本
nacos:
作用:
1.服务注册与发现
2.配置:共享配置/动态配置
共享配置
1.不同服务的一些配置是相同的例如数据库,swagger等配置,每次都要在不同的服务中进行添加,过于麻烦,因此nacos提供了共享配置,在nacos中配置列表中进行添加共有配置,然后在springCloud中bootstrap.yaml文件中进行添加共享配置文件
动态配置:
将配置文件写到nacos中,在调用接口的时候获取naocs中的配置文件实现热更新
实现:
1.在nacos中配置动态参数
2.在代码层设置配置文件,以特定方式读取需要热更新的的配置属性,一旦配置文件发生变更,这里的配置属性就会进行变更
3.代码中调用
例子
nacos中的配置
java中的配置
@Data
@Component
@RefreshScope
@ConfigurationProperties(prefix = "thirdpay.allinpay")
public class AllinPayProperties {
private String url;
private String appId;
private String secretKey;
private String privateKeyPath;
private String pwd;
private String tlPublicKey;
private String b3Payer;
/**
* 商户统一账户集编号
*/
private String uniAccountSetNo;
/**
* 回调地址
*/
private String callbackUrl;
/**
* 终端公网IP,用于风控
*/
private String consumerIp;
}
动态配置路由:
由于springCloud 中bootstrap.yaml文件读取nacos配置文件,在nacos配置文件中更新了路由信息,但是getaway并没提供动态更新到路由表,所有需要自己实现监听nacos更新的信息到路由表中
相当于正常我们动态更新的数据都是在调用时候会去通过配置路径等信息来从nacos里面进行拉取,而getaway就不行,因为他的路由等信息是在yaml文件中,我们没有主动调用nacos中配置问题,无法更新路由信息,这时候就需要设置监听器,每次nacos更新则会触发回调函数,如下:
实现:
1.获取nacosConfigManager对象得到网关配置文件以及监听器,每次更新的时候就会触发里面的监听回调函数,
2.获取最新配置之后,利用routeDefinitionWriter Api接口中的save更新路由信息,以及delete删除不需要的路由
Eureka和Nacos的区别
相似点:
1.都支持服务拉取和服务注册
2.都支持服务心跳检测
区别:
1.Eureka不支持共享配置
2.Eureka不像Nacos是一个jar,直接部署运行就行,Eureka需要手动引入依赖,搭建项目部署运行。
3.Eureka采用AP模式,Nacos默认AP,支持CP
4.Nacos更加全面,支持主动检测,推送服务变更,心态检测周期比Eureka短,因此更加精确
openFegin/Dubbo
1.简介:简化远程调用,并实现负载均衡,底层对http请求做出封装,实际就是调用的把ip,端口拼接起来实现了http请求
实现:
1.引入依赖
2.在启动类配置@EnableFeginClients注解
3.编写FeginClient类
4.调用接口
连接池
OpenFegin每次远程调用都要创建连接,销毁连接,效率低,因此引入连接池
默认HttpURLConnection框架,不带连接池
okHttp,带连接池
默认情况下,OkHttp 的连接池会保持最多 5 个空闲连接,空闲时间为 5 分钟。这些参数可以通过 ConnectionPool 的构造函数进行自定义。
实现:整合okHtttp连接池
1.映入依赖
2.开启连接池功能
日志
不同的日志基本显示不同内容
实现:
负载均衡
默认轮询算法
openFegin支持3种负载均衡算法
1.randomLoadBalancer:随机
2.NacosLoadBalancer:先看本集群,然后加权随机
3.RoundRobinLoadBalancer:轮询
实现
生成一个配置类,声明一个负载均衡器,这个类不加@Configuration注解
而是在启动类上加@LoadBalancerClients注解
为什么:因为这里的负载均衡器只对fegin客户端生效,通常在启动类上加
openFegin和dubbo的区别
Dubbo 和 OpenFeign 都是流行的微服务框架,它们都用于在分布式系统中进行服务间通信
特性 | Dubbo | OpenFeign |
---|---|---|
协议 | RPC(支持多种协议:Dubbo、HTTP、Hessian 等) | HTTP(通常用于 RESTful API) |
底层实现 | 高性能 RPC 框架,基于 Netty 和 NIO | 基于 HTTP 的声明式调用,底层使用 HTTP 客户端 |
服务治理 | 提供完整的服务治理功能:注册、发现、负载均衡、容错 | 基于 Spring Cloud 提供负载均衡和服务发现 |
负载均衡 | 内置多种负载均衡策略,支持动态路由和策略配置 | 依赖 Spring Cloud LoadBalancer 或 Ribbon |
性能 | 高性能,适用于高并发、低延迟场景 | 性能较为普通,适用于中小型系统 |
容错机制 | 提供内建的容错机制(熔断、重试、超时控制等) | 可通过 Spring Cloud 集成 Hystrix 或 Resilience |
使用场景 | 大规模、高性能、高可用的分布式系统 | 微服务架构中,简化 HTTP 调用的场景 |
dubbo在springboot中的集成
dubbo和openFegin之前能很好的切换,因为dubbo来远程调用也很简单
dubbo 实现
配置项(共享配置)
在nacos中统一进行comsumer,privider,和全局配置
shared.dubbo.properties
全局通用配置(对 Provider 和 Consumer 均生效)
通常包含:
注册中心地址(dubbo.registry.address)
元数据中心地址(dubbo.metadata-report.address)
全局超时/重试策略
公共序列化协议
shared.dubbo.provider.properties
仅服务提供者(Provider)生效
例如:
服务暴露协议(dubbo.protocol.name)
线程池策略(dubbo.provider.threadpool)
权重/负载均衡规则
shared.dubbo.consumer-only.properties
仅服务消费者(Consumer)生效
例如:
调用超时时间(dubbo.consumer.timeout)
集群容错模式(dubbo.consumer.cluster)
启动检查(dubbo.consumer.check=false)
代码示例,例如
dubbo.scan.base-packages=${p.dubbo.scan.base-packages}
dubbo.cloud.subscribed-services=${p.dubbo.cloud.subscribed-services}
dubbo.protocols.dubbo.status=server
dubbo.consumer.timeout=30000
dubbo.consumer.reties=0
dubbo.consumer.check=false
dubbo.comsumer.threads=200
dubbo.provider.filter=-exception
dubbo.provider.prefer-serialization=fastjson2
#dubbo.provider.filter=dubboExceptionFilter,-exception
dubbo.provider.payload=524288000
配置完全全局之后,针对每个微服务进行配置,
配置(每个服务私有配置)
通过 p.dubbo 前缀覆盖或补充共享配置
# 从 Spring 应用名获取值(如 order-service)
p.dubbo.application.id=${spring.application.name}
p.dubbo.application.name=${spring.application.name}
# 服务暴露的 Dubbo 协议端口 (默认20880)
p.dubbo.protocols.dubbo.port=20112
# Dubbo 运维端口 (QOS,默认22222)
p.dubbo.application.qos.port=22112
# Dubbo 注解扫描包路径(关键!)
p.dubbo.scan.base-packages=com.htyc.biz.gather.dubbo
# 订阅的服务列表(空表示订阅所有)
p.dubbo.cloud.subscribed-services=
# 注册中心分组隔离 (Nacos Group)
p.dubbo.registry.parameters.group=
# 元数据中心分组 (接口/配置元数据存储隔离)
p.dubbo.metadata-report.parameters.group=
# 注释掉的配置中心分组(未启用)
# p.dubbo.config-center.app-name=${spring.application.name}
# p.dubbo.config-center.parameters.group=
代码实现
1.Dubbo 采用接口和实现类的方式来定义服务,首先定义服务接口。
服务接口:
package com.example.dubbo.api;
public interface HelloService {
String sayHello(String name);
}
2.实现 Dubbo 服务接口
然后,你需要实现这个服务接口,作为服务提供者。
服务提供者实现:
package com.example.dubbo.provider;
import com.example.dubbo.api.HelloService;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.stereotype.Component;
@DubboService(interfaceClass = OcrTaskRpcServiceI.class) // 标记为 Dubbo 服务
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "Hello, " + name;
}
}
在这个实现中:
@DubboService: 这是 Dubbo 提供的注解,标记为服务提供者。
3.创建 Dubbo 服务消费者
接着,你可以创建 Dubbo 服务的消费者,通过 @Reference 注解来引用服务接口。
服务消费者:
package com.example.dubbo.consumer;
import com.example.dubbo.api.HelloService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class DubboConsumer implements CommandLineRunner {
@Lazy
@DubboReference(timeout = 5000, retries = 0)
private HelloService helloService; // 引用 Dubbo 服务
@Override
public void run(String... args) throws Exception {
// 调用服务
String result = helloService.sayHello("Dubbo");
System.out.println(result);
}
}
在消费者中:
@Reference: 这是 Dubbo 提供的注解,表示要引用某个服务。version 可选,若没有指定版本,默认引用最新版本。
4.启动类
最后,你的 Spring Boot 启动类需要加上 @EnableDubbo 注解来启用 Dubbo。@SpringBootApplication 注解的类就是你的 Spring Boot 启动类。
package com.example.dubbo;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo // 启用 Dubbo
public class DubboApplication {
public static void main(String[] args) {
SpringApplication.run(DubboApplication.class, args);
}
}
或者实现 Dubbo 的自动激活 会自动获取nacos配置文件扫描dubbo包下
@ComponentScan(value = {"com.hh"}) // 关键扫描路径
@SpringBootApplication(
scanBasePackages = {"com.hhh","com.alibaba.cola"}, // 扩大扫描范围
exclude = { DataSourceAutoConfiguration.class, ... }
)
@EnableDiscoveryClient // 服务注册与发现核心注解
问题
openFegin跨服务以及同一服务器都能实现
如果你部署了多个 Spring Boot 项目在同一台服务器上,完全可以使用 OpenFeign 进行项目之间的接口调用。这种方式不仅能够保持接口调用的一致性,还能让代码更加简洁和易于维护。唯一需要注意的是,调用同一台服务器上的服务时,性能相对于直接方法调用会稍逊,但在大多数情况下是可以接受的。
总结
OpenFeign
优势:开发简单、HTTP 协议通用性强
劣势:同机调用仍有网络栈开销,性能天花板明显
Dubbo
优势:同机调用可达到近本地方法调用性能,资源消耗极低
劣势:需要维护独立 API 模块,学习曲线稍陡
gateway
简介:每个微服务有自己的ip端口号,甚至会变换,导致前端无法确定每个微服务的ip端口,所以出现网关,统一所有微服务入口,前端访问网关,网关判断进入哪个微服务
实现:
1.配置网关模块
2.引入依赖
3.编写启动类
4.配置路由规则
也可用用网关校验登录信息
通用配置
//基础设置 (HTTP 编码 & 文件编码) 目的: 统一处理中文等非ASCII字符,避免乱码问题
spring.http.encoding.force=true // 强制请求和响应使用配置的字符集(UTF-8)
spring.http.encoding.charset=UTF-8 // 设置HTTP请求和响应的字符集为UTF-8
spring.http.encoding.enabled=true // 启用HTTP编码支持
file.encoding=utf-8 // 设置JVM默认文件编码为UTF-8 (确保系统层面编码一致)
//服务器端口 访问入口: 所有外部请求首先到达这个端口。
server.port=31999 // 主网关应用监听的端口。客户端通过此端口访问网关。
//日志配置 (Log4j2) 目的: 使用异步日志记录器上下文选择器。这能显著提升日志记录性能,尤其是在高并发场景下,减少日志I/O对业务线程的阻塞。
log4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelectorswagger.enable=true
management.endpoints.web.exposure.include=*
management.server.port=38999
//Swagger 配置
swagger.enable=true // 启用Swagger API文档功能
swagger.allowed.ips=0.0.0.0 //允许访问Swagger UI的IP地址 (0.0.0.0 表示允许所有IP访问,生产环境应限制)
p.springdoc.packages-to-scan=com.htyc.biz //指定SpringDoc扫描的包路径 (用于生成API文档)
springdoc.webjars.prefix= //置Webjars资源的前缀为空 (通常用于自定义Swagger UI资源路径)
//防 CC 攻击 (自定义配置)
anticc.enable=true //启用防CC攻击功能 (通常是自定义或集成的安全模块)
anticc.count=300 //防CC攻击的阈值 (例如,单位时间内允许的请求次数)
//集成配置 (Sentinel & SkyWalking) 目的:Sentinel: 配置流量控制、熔断降级、系统保护等功能所需的端口。SkyWalking: 配置应用性能监控 (APM) 工具 SkyWalking 的服务标识。
p.spring.cloud.sentinel.transport.port=33999 //Sentinel Dashboard 与控制台通信的端口
p.skywalking.agent.service_name=${spring.application.name} //设置SkyWalking APM的service名 (使用应用名)
//HTTP 客户端连接池 目的: 优化网关向下游服务发起 HTTP 请求的性能。这里设置了非常长的空闲时间,意味着连接池中的空闲连接会保持很长时间才被回收,减少频繁创建新连接的开销(适用于内部网络稳定、长连接场景)。
spring.cloud.gateway.httpclient.pool.maxIdleTime=9999999 //设置连接池中空闲连接的最大存活时间 (毫秒)
//服务发现集成
spring.cloud.gateway.discovery.locator.enabled=true //启用通过服务发现自动创建路由
spring.cloud.gateway.discovery.locator.lower-case-service-id=true //将服务ID转换为小写 (兼容不同注册中心)
#spring.cloud.gateway.discovery.locator.filters[0]=StripPrefix=1
//全局默认过滤器 (Default Filters)
spring.cloud.gateway.default-filters[0]=AddResponseHeader=X-Response-Default-Foo, Default-Bar //向所有下游响应添加头
spring.cloud.gateway.default-filters[1]=AddRequestHeader=X-Request-Foo, Bar
spring.cloud.gateway.default-filters[2]=AddRequestParameter=foo, bar
spring.cloud.gateway.default-filters[3]=DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Request-Headers Access-Control-Request-Method Access-Control-Allow-Origin, RETAIN_FIRST
//全局跨域配置 (Global CORS) 目的: 配置网关级别的全局 CORS (跨域资源共享) 策略。
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allow-credentials=true //允许发送Cookie等凭证
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowed-headers=* //允许所有请求头
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowed-methods=* //允许所有HTTP方法 (GET, POST...)
spring.cloud.gateway.globalcors.corsConfigurations.[/**].allowed-origin-patterns=* //允许所有来源 (使用模式匹配,更灵活)
#http请求为lb://** ws请求为lb:ws://** 其中**为注册的服务名
#配置filter将Path=/portal/**的前缀portal去掉,有几个前缀就去掉几个,否则调用路径会变成lb://portal/portal
#若无StripPrefix过滤器时,gateway 发送请求到后台服务portal的url就是http://portal/portal/**
#若有StripPrefix过滤器时,gateway会根据StripPrefix=1所配的值(这里是1)去掉URL路径中的部分前缀(这里去掉一个前缀,即去掉portal)
#发送请求到后台服务portal的url变成http://portal/**
#id随便取
//路由定义 (Route Definitions) - 核心配置
spring.cloud.gateway.routes[0].id=biz.portal //路由的唯一标识符
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1 //路径过滤器:移除请求路径的第一部分
spring.cloud.gateway.routes[0].predicates[0]=Path=/biz.portal/** //断言:匹配任何以 `/biz.portal/` 开头的请求路径
spring.cloud.gateway.routes[0].uri=lb://biz.portal //目标URI:使用负载均衡指向服务名为`biz.portal`的服务
spring.cloud.gateway.routes[0].order=0 //路由的优先级 (数值越小优先级越高)
spring.cloud.gateway.routes[1].id=biz.user
spring.cloud.gateway.routes[1].filters[0]=StripPrefix=1
spring.cloud.gateway.routes[1].predicates[0]=Path=/biz.user/**
spring.cloud.gateway.routes[1].uri=lb://biz.user
spring.cloud.gateway.routes[1].order=0
这份配置展示了一个功能比较完备的 Spring Cloud Gateway 实例,涵盖了路由、过滤、服务发现、监控、安全、性能优化等关键方面。理解 Path 断言、StripPrefix 过滤器和 lb:// URI 的协作是掌握网关路由机制的核心。
总结
Gateway 的核心价值在于将原本分散在多个微服务中的“入口”和“公共配置/功能”,集中整合到了一个统一的、强大的网关层。
它通过server.port成为系统对外的唯一大门,并通过其丰富的配置在这个大门上集成了路由导航、安全警卫(anticc, cors, security)、请求处理流水线(filters, default-filters)、监控探头(actuator, sentinel, skywalking)等关键设施。
白话来说,没有gateway,那么多个微服务就需要单独配置各种各样的文件,以及入口有n多个,但是有了gateway之后就整合了多个微服务的配置,整合到了一个入口进行配置。
sentinel
1.简介:sentinel是alibaba开源的一套微服务流量控制组件
他会监控接口
安装部署:
1.下载sentinel jar,部署jar
2.微服务整合 ,链接sentinel-dashboard控制台
- 添加依赖
- 配置application.yaml文件
springCloud面临的问题:
1.雪崩问题:微服务调用链中的某个服务故障,导致整个链路中的微服务都不可用
解决方案:
请求限流
特点:限流主要是控制请求速率,防止过载
实习:在sentinel控制台,对簇点链路设置流控,设置QPS
线程隔离
当B服务出现卡顿或者故障导致访问他A服务被拖慢,甚至资源耗尽,这时候就需要限制访问B服务中的线程数,实现线程隔离
例如:B服务很来只需要10ms返回,但是由于故障,数据库等原理,导致返回时间10min,占用线程,A服务访问B服务还在进行,占用大量线程,导致tomcat可用线程数量减少,导致服务卡顿,甚至不可用
流控模式
直接模式:
在 直接流控模式下,流量控制是基于 单一资源(通常是某个接口或方法)的访问量进行控制。也就是说,当某个资源的流量达到设定的阈值时,直接触发限流。
场景和使用时机:
简单的接口限流:适用于那些不依赖其他服务或上下游链路的简单接口。即,对于某个服务的单一接口,不考虑其它接口的影响,直接进行流量限制。
资源限流:当我们希望针对某个独立的资源进行流量限制时,比如数据库查询、文件上传等。
典型场景:
登录接口:假设你的应用有一个登录接口,想要限制一定时间内的登录请求数量。可以使用 直接流控模式 来限制登录接口的访问速率。
单一服务接口:在一些简单的微服务系统中,某个接口需要单独进行流量控制,而不依赖于其他服务或接口的调用情况。
关联模式:
关联流控模式是基于 多个资源之间的关系进行流量控制的模式。Sentinel 会根据 多个资源之间的关联性来决定是否进行流控。具体来说,当一个资源的流量超过设定阈值时,可能会影响到其他相关资源的流控。
场景和使用时机:
关联服务的流量控制:适用于一些微服务场景,多个接口或服务之间存在一定的 调用关系。当一个接口被频繁调用时,可能会影响到其他依赖它的接口,此时需要采用关联流控模式来对相关接口进行限制。
系统中的依赖关系:当一个系统中的接口或服务之间存在 依赖关系时,某个高频访问的接口可能会影响到其他接口的负载。此时可以通过 关联流控模式 来控制相关接口的流量。
典型场景:
服务依赖控制:在微服务架构中,某个服务的接口(比如用户认证服务)可能被其他服务调用。为了保证某个高频调用接口不会对其他依赖接口的流量造成影响,可以使用关联流控模式。
数据库查询:多个接口可能会对同一个数据库进行查询操作,当某个接口的查询流量过高时,可能影响到其他接口的查询负载。可以通过关联流控控制整个系统的流量。
链路模式
链路流控模式是根据 调用链路来进行流量控制的。也就是说,流量控制不仅基于单个接口,还可以基于一整个调用链路来进行控制。当链路中的某个节点(接口)达到流量限制时,整个链路的流量会受到控制。链路流控模式可以帮助更精细地控制跨服务调用的流量。
场景和使用时机:
跨服务流量控制:适用于微服务架构中,多个服务通过调用链路形成的依赖关系。链路流控模式可以根据整个链路的负载进行流量控制,防止某个节点的高负载影响到整个系统。
复杂系统中的流量控制:当服务之间有复杂的调用链时,链路流控模式可以帮助你对整个链路的流量进行协调管理。
典型场景:
微服务调用链流控:假设一个微服务架构中,服务A 调用 服务B,而 服务B 又调用 服务C。在这种情况下,如果 服务A 受到流量控制,可以直接影响整个调用链,进而影响 服务B 和 服务C 的流量。链路流控模式允许你根据整个调用链的情况进行流量控制。
调用链延迟控制:对于跨服务的调用链,某些服务可能由于处理能力有限而容易成为瓶颈。此时,可以通过链路流控模式对整个链路进行流控,从而防止瓶颈点的过载。
注意:
如果链路中的接口是在service层
1.需要加在方法上面加上@SentinelResource()注解
2.sentinel配置的时候web-context-unify: false #关闭context整个
流控效果
快速失败
快速失败(Fast Fail) 是指在流量超过限制时,系统会直接拒绝超出限制的请求,并立即返回错误(如 HTTP 429 - Too Many Requests)。这样,超出限制的请求会迅速失败,而不需要进入排队等待或其他处理。
场景和使用时机:
高可用性要求:当你希望系统能够快速响应,并避免请求堆积,减少资源浪费时,快速失败是一个合适的策略。例如,如果系统本身无法承受过多的请求,直接拒绝超出的请求能够避免系统因过载而崩溃。
高并发系统:对于 高并发、高负载的接口,快速失败可以让系统在流量过高时迅速丢弃无用请求,从而避免产生大量无效的资源消耗。
实时性要求高的场景:如果系统对请求的响应时间有严格要求,不希望请求被阻塞,可以选择快速失败,让系统保持高吞吐量而不会因为排队等候导致性能下降。
典型场景:
API 限流:例如,一个 RESTful API 在高并发时,可以使用快速失败让请求在流量过载时直接被拒绝,避免接口响应时间过长。
高并发访问的支付系统:在支付系统等高并发应用中,当并发请求量超过限额时,采用快速失败可以有效防止系统被过多请求淹没。
warm up(预热)
Warm Up(预热) 是一种 流控效果,其目的是通过在初期限制请求的速率,逐步增加流量的方式,避免系统在突然的流量暴增时被压垮。在 Sentinel 中,Warm Up 通过设置 冷启动阈值,在系统初始阶段引入更平滑的流控。
这种机制通过控制在短时间内的请求速率逐渐增加来帮助系统更好地适应流量波动,尤其在系统刚启动时。
场景和使用时机:
适用于系统刚启动时,流量从零到高的过渡阶段,帮助系统更平稳地增加负载,避免系统在负载突然增加时过载。
适合 有突发流量且系统资源有限的场景,可以平滑的增加流量,减少因为突发流量导致的拒绝请求。
防止系统过载启动:当系统刚启动时,通常不会立即承载大量流量。通过预热,可以让系统逐步适应流量,避免瞬时请求过载。
典型场景:
微服务启动时的流控:系统在初始化阶段流量较小,随着流量逐渐增加,系统也可以逐步放开流控限制,避免一次性接受大量请求。
应对高峰流量的场景:例如,电商大促销时,某些接口在流量突然增加时采用 Warm Up 策略来逐步提高承载能力,避免崩溃。
排队等待
排队等待是指当请求超过限流阈值时,超出限制的请求不会立刻被拒绝,而是进入一个等待队列。请求会在队列中等待一段时间,如果在这段时间内有空闲资源处理它们,它们将继续被执行,否则会被拒绝。
场景和使用时机:
适用于系统负载相对较轻、能够容忍短时间请求积压的场景。
如果系统可以 缓解瞬时流量峰值,而不想让请求直接失败,可以使用排队等待,避免拒绝请求。
当系统需要 平滑流量控制,不希望请求瞬间拒绝,同时还要避免过载的情况。
典型场景:
高并发请求的流量控制:例如,电商平台的商品查询接口,当流量过大时,超出流量限制的请求会排队等待,避免服务崩溃。
后台任务:例如,某些操作可能由于业务逻辑的限制或者后端处理能力不足,允许用户请求排队处理,以便后续逐步处理。
热点参数
使用场景
热门商品或用户请求:在电商平台中,某些商品或某些用户的请求可能会因为热点促销、秒杀活动等因素,产生大量流量。例如,某个商品的 ID 在促销期间被大量访问,这时可以对该商品的 ID 进行流量控制,避免过度访问影响到系统的稳定性。
API 接口的特殊参数:某些 API 接口可能会根据特定的查询参数(如用户 ID、商品类别、地区)进行数据查询。如果这些参数中某些值请求量过大,可以采用热点参数限流。
资源消耗较大的操作:某些操作(如查询数据库、缓存数据等)对于特定参数值的查询开销较大,可以对这些参数进行限流,避免单个请求带来的高并发压力。
线程隔离中hystrix和sentinel的区别
其他:
hystrix:默认线程池隔离
优点:远程调用服务新建一个线程池,减少资源销毁,并起到隔离作用,控制能力也更好
缺点:每一个远程调用都要创建一个线程池,如果一个服务依赖很多服务,则要创建很多线程池,额外的CPU开销,这种也是资源浪费
sentinel:默认信号量隔离
优点:信号量相当于就是一个计数器,性能较好
缺点:隔离性较低
降级、Fallback
当由于设置了请求限流和线程隔离之后,后续超出的请求,需要进行反馈,所以需要降级并用到Fallback反馈降级后返回的逻辑
实现:
1.自定义类,实现FallbackFactory,编写对于某个FeginClientd的Fallback返回逻辑
2.将这个类注册成bean对象,使之生效
3.远程调用接口的时候在FeginClient注解中配置
服务熔断
当B服务因为数据库或者网络等原因导致响应非常慢,虽然做了请求限流和线程隔离,但是在满足这两个设置的情况下,还有请求进来访问B服务,访问依旧很慢,这时候可用使用服务熔点,直接拦截慢查询,当服务恢复之后放行该查询
配置:
最大RT:超过该时间算慢
比例阈值:0.5:超过百分之50就熔断
熔断时长:20s,过了20s进行放行一次
最小请求数:5 最小5次进行一次统计
统计时长:1000ms 1000ms内是否达到5次 且比例超过50%
底层实现算法
滑动窗口算法
在sentinel中不论是限流还是熔断都是用滑动窗口算法实现
漏桶算法
当多余的请求,需要排队等待的时候,这时候就用到漏桶算法
桶的容量取决于限流的QPS阈值,以及允许等待的最大超时时间
令牌桶算法
用于热点参数限流
sentinel限流和getaway限流的区别
1.getaway限流采用了基于redis中的令牌桶算法
2.sentinel内部默认滑动窗口限流算法,而排队等待使用的漏桶算法,热点参数限流使用的令牌桶算法
授权规则
这里有了getaway为啥还授权规程,因为getaway就相当于一个把门的,如果经getaway访问服务,则会根据getaway方法来拦截过滤请求,如果这时候有个人知道了服务本身的ip地址端口,直接通过浏览器访问,这时候就需要nacos的授权规则来拦截这种请求了
实现
1.sentinel配置信息
nacos里面配置信息
资源名:接口路径
流控应用:请求头值(取出请求头key为origin中的值)
2.在gateway配置里面配置请求头 key=origin value=gateway
3.实现RequestOriginParser中的parseOrigin方法
自定义异常结果
在需要的服务里面自定义类实现BlockExceptionHandler
当请求触发 Sentinel 限流、熔断或降级规则时,BlockException 异常会被抛出。
这个异常会被你自定义的 BlockExceptionHandler 捕获。
在 handleBlockException 方法中,你可以通过 HttpServletResponse 对象自定义响应的状态码、内容或头信息等。
响应会返回给客户端,客户端接收到的就是你定义的自定义错误信息,而不是默认的错误返回。
规则持久化
sentinel有3种管理模式
原始模式:保持在内存
服务一旦重启,则sentinel关于此服务的配置就消失了
Pull模式:保存在数据库
由sentinel推到数据库,服务定时拉取数据库的规则
Push模式:保存在nacos,监听变更实时更新
sentinel规则由nacos管理,服务实时监听,一般使用这种形式
seata
简介:
一个业务需要多个服务配合完成,其中每个服务必须同时成功或者失败,其中每个服务称为分支事物,整个服务称为完整事物
整个业务中每个服务不知道其他服务是否成功,所有引入seata,使各个服务能感知其他服务是否成功
seata三个重要组成角色
TC:事务协调者,维护全局和分支事物状态,协调全局事物提交或者回滚
TM:事物管理者,定时事物范围,开启全局事物,提交或者回滚全局事物
RM:资源管理者,管理分支事物,通知TC已注册的分支事物,以及报告分支事物的状态
部署:
1.部署TC服务
1.准备数据库(seata支持多种存储方式,为了持久化需要,一般使用mysql等数据库),表中记录分支事物,全局事物,锁等一些信息
2.编写配置文件application.yaml
3.部署seata
完成之后nacos配置列表就有seata配置了
seata集成微服务
1.在其他需要分布式事物的微服务中引入seata依赖
2.编写共享配置 填写命名空间,组信息
XA模式
简介:
优缺点:
事物强一致性:因为需要等带全部事物完成之后再进行提交,其中等待过程占用数据库资源
基本数据库都支持
实现:
1.修改application.yaml文件(nacos中的共享配置)开启XA模式
2.在方法入口添加@GlobalTransactional注解
AT模式
简介:AT模式依然是分段式提交,不过弥补了XA模式中资源锁定时间过长的问题
相比于XA模式,AT模式在执行业务完会直接提交,并生成一个undolog回滚日志,如果事物不是全部执行成功,则根据undolog日志进行回滚
seata默认AT模式
XA模式和AT模式区别:
实现:
1.在数据库创建undolog表:存储分支事物id,全局事物id,以及回滚信息等
脏数据问题
因为AT模式是在第一阶段执行完业务代码直接提交,所拥有数据库的锁释放掉了,当另一个事物对该数据执行,后续再回滚导致数据不一致,这样就会造成数据安全问题
seata为了解决这样的问题引入了全局锁的概念
A事物获取db锁之后,在执行业务sql的时候会添加一个全局锁,后续提交事物A释放db锁,B事物在在获取db锁,准备执行sql需要获取全局锁,但是A事物还没释放全局锁,所有B事物无法执行sql,这时候B事物等待超时会释放db锁,A事物就可以根据快照回滚了
这里的全局锁和XA模式的数据库锁还是有差距,锁定粒度会小很多,因为这里的全局锁是TC管理的,相当于不由seata管理的事物,可以对这个数据进行操作不会有影响
但是这种情况其他非seata管理的事物对该数据进行了修改,也会出问题,但是这种机率很小,以为业务过程中肯定会尽量避免事物失败的情况,但是seata也对这种情况进行处理
seata在AT模式执行sql之后生成的快照除了更改前的快照,还生成了更改后数据的快照,当其他非seata管理的事物更改了数据,这时候当前事物回滚的时候会比较更改后的快照和当前值是否一样,不一样这会通知,这时候一般人工进行处理
TCC模式
TCC是三个单词的缩写(try-confirm-cancel) TCC分布式事务最核⼼的思想就是在应⽤层(可以理解为我们编码)将⼀个完整的事务分为3个阶段,Try阶段、Confirm确认阶段、Cancel取消阶段
TCC模式需要自己编写TCC3个阶段
主要就是Try阶段,需要预留资源,保持数据隔离
TCC模式的优缺点:
最大努力通知
最大努力通知是最终一致性的分布式事物解决方案
A服务访问B服务,需要手动确认B是否是否执行成功,失败再重试。A->B的时候可以使用消息中间件来传递,利用ACK,重试机制,例如Kafka中的消息生产,消费。
CAP
分布式系统总是满足CAP定律
C:consistency:一致性:用户访问分布式中的任意节点,得到的数据必须一致
A(Availability可用性):用户访问分布式系统中的任意节点,读或者写都能成功
P(partition分区、tolerance容错):由于网络等原因,导致一个或者多个服务和其他服务不在一个网络(集群)导致分区,系统要能都容忍此分区的现象,出现分区的时候,整个系统也要持续的对外提供服务
AP:只能读不能写,满足一致性
CP:其他服务不在一个集群不满足一致性,可以读写
BASE理论:解决CAP思路
CP:XA模式
AP:AT模式