Spring Cloud
Spring Cloud 五大组件
- 注册中心(
Nacos
) - 远程调用(
Dubbo
) - 负载均衡(
Loadbalancer
) - 服务保护(
Sentinel
) - 网关(
Gateway
)
服务注册和发现
服务注册
服务注册是指当一个服务启动时,它会向一个服务注册中心注册自己,通常会提供一些元数据,如服务名称、地址(IP和端口)、健康检查URL等。这样,其他服务就可以通过服务注册中心找到这个服务。
服务发现
服务发现是指服务在运行时如何动态地找到它所依赖的其他服务的过程。当服务A需要调用服务B时,它会查询服务注册中心,获取服务B的地址信息,然后进行调用。
服务监控
服务实例定期向 Nacos
发送心跳,以证明自己是健康的。如果 Nacos
在一定时间内没有收到心跳,它可能会认为该服务实例不健康,并将其从服务列表中移除。
nacos
与eureka
的区别
- 共同点
- 都支持服务注册和服务拉取
- 都支持服务提供者心跳方式做健康检测
- 不同点
Nacos
支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用住的那个检测模式- 临时实例心跳不正常会被剔除,非临时实例则不会被剔除
Nacos
支持服务列表变更的消息推送,服务列表跟新更及时Nacos
集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka
采用AP方式
Nacos
还支持配置中心,Eureka
只有注册中心,这也是选择Nacos
的一个重要原因
负载均衡
loadbalancer
本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
负载均衡算法
-
轮询(Random)
将请求按顺序轮流地分配到每个节点上,不关心每个节点实际的连接数和当前的系统负载。- 优点:简单高效,易于水平扩展,每个节点满足字面意义上的均衡;
- 缺点:没有考虑机器的性能问题,集群性能瓶颈更多的会受性能差的服务器影响。
-
随机
将请求随机分配到各个节点。由概率统计理论得知,随着客户端调用服务端的次数增多,其实际效果越来越接近于平均分配,也就是轮询的结果。 -
动态均衡算法
-
最小连接数法
根据每个节点当前的连接情况,动态地选取其中当前积压连接数最少的一个节点处理当前请求,尽可能地提高后端服务的利用效率,将请求合理地分流到每一台服务器。
- 优点:动态,根据节点状况实时变化;
- 缺点:提高了复杂度,每次连接断开需要进行计数;
- 实现:将连接数的倒数当权重值。
-
最快响应速度法
根据请求的响应时间,来动态调整每个节点的权重,将响应速度快的服务节点分配更多的请求,响应速度慢的服务节点分配更少的请求,俗称能者多劳,扶贫救弱。
- 优点:动态,实时变化,控制的粒度更细,跟灵敏;
- 缺点:复杂度更高,每次需要计算请求的响应速度;
- 实现:可以根据响应时间进行打分,计算权重。
-
观察模式法
观察者模式是综合了最小连接数和最快响应度,同时考量这两个指标数,进行一个权重的分配
-
-
源地址哈希
根据客户端的IP地址,通过哈希计算得到一个数值,用该数值对服务器节点数进行取模,得到的结果便是要访问节点序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会落到到同一台服务器进行访问。- 优点:相同的IP每次落在同一个节点,可以人为干预客户端请求方向;
- 缺点:如果某个节点出现故障,会导致这个节点上的客户端无法使用,无法保证高可用。当某一用户成为热点用户,那么会有巨大的流量涌向这个节点,导致冷热分布不均衡,无法有效利用起集群的性能。所以当热点事件出现时,一般会将源地址哈希法切换成轮询法。
-
一致性哈希
主要的特点就是Hash环,我们的请求可以构建成一个Hash环,按照顺时针记录hash和请求。当我们的服务挂了A时,我们只需要将A的请求交给A后面的B处理;当我们需要增加服务器C时,我们只需要在Hash环上划一块范围,然后交给C;这样就可以实现动态的扩容和缩容。一致性哈希用于解决分布式缓存系统中的节点选择和在增删服务器后,节点减少带来的数据缓存的消失与重新分配问题。
Dubbo
特点
- 面向接口代理的高性能RPC调用。
- 智能负载均衡。
- 服务自动注册与发现。
- 高度可扩展。
- 运行期流量调度。
- 可视化的服务治理与运维。
工作原理
- 服务提供者启动时,向注册中心注册自己提供的服务。
- 服务消费者启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者。
- 消费者从提供者地址列表中,基于负载均衡算法选一台提供者进行调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
序列化方式
Dubbo
序列化Hessian2
序列化Java
序列化Kryo
序列化FST
序列化FastJson
序列化Avro
序列化Protobuf
序列化
负载均衡
Random LoadBalance
: 随机负载均衡。RoundRobin LoadBalance
: 轮询负载均衡。LeastActive LoadBalance
: 最少活跃调用数负载均衡。ConsistentHash LoadBalance
: 一致性哈希负载均衡。
服务调用流程
- 服务消费者通过代理对象发起RPC调用。
- 服务消费者端的代理将调用信息序列化后,通过网络发送给服务提供者。
- 服务提供者端的代理收到请求后进行反序列化。
- 服务提供者端根据调用信息找到本地对应的服务实现并进行调用。
- 服务提供者将调用结果序列化后返回给消费者。
- 服务消费者端代理对象接收到响应后进行反序列化,返回给调用者。
服务雪崩
Dubbo服务雪崩是指在分布式系统中,由于某个服务提供者出现问题,导致整个系统的服务调用链路受到影响,从而引发连锁故障的现象。以下是Dubbo服务雪崩的原因及解决方式:
原因
- 服务提供者不可用:当服务提供者出现故障或性能下降时,会导致调用该服务的消费者无法获得响应或响应超时。
- 服务调用链路过长:在复杂的微服务架构中,服务之间的调用链路可能很长,一旦某个节点出现问题,整个链路都会受到影响。
- 同步调用:Dubbo默认采用同步调用方式,如果一个服务调用耗时较长,会导致调用线程长时间等待,从而影响整个系统的吞吐量。
- 重试机制:消费者在调用服务失败时会进行重试,如果重试次数过多,会进一步加大服务提供者的压力。
- 流量突增:例如促销活动等场景下,系统流量突增,可能导致服务提供者处理不过来,出现雪崩效应。
解决方式
- 限流:
- 在服务提供者端设置合理的阈值,限制请求的并发数,超出阈值的请求可以直接返回,防止系统过载。
- 使用Dubbo的令牌桶或漏桶算法进行限流。
- 熔断:
- 引入熔断机制,当服务调用失败率达到一定阈值时,自动触发熔断,后续请求不再调用故障服务,而是返回降级响应。
- 使用Hystrix等熔断组件与Dubbo结合。
- 服务降级:
- 当服务调用非核心功能失败时,可以提供降级服务,保证核心功能的可用性。
- Dubbo提供了服务降级功能,可以通过配置实现。
- 超时设置:
- 合理设置服务调用的超时时间,避免长时间占用线程资源。
- 异步调用:
- 使用Dubbo的异步调用方式,可以减少线程阻塞,提高系统吞吐量。
- 重试策略优化:
- 优化重试策略,例如设置合理的重试次数,避免大量重试给服务提供者带来压力。
- 负载均衡:
- 使用Dubbo提供的负载均衡策略,如随机、轮询、最少活跃调用数等,合理分配请求,避免单点过载。
- 监控与报警:
- 对服务调用进行实时监控,一旦发现异常立即报警,并采取措施。
- 预备容量:
- 预留一定的服务容量,以应对突发流量。
通过上述措施,可以在一定程度上预防Dubbo服务雪崩的发生,或者在雪崩发生时,快速恢复服务,保证系统的稳定性。
- 预留一定的服务容量,以应对突发流量。
微服务监控
- SpringBoot-admin
- prometheus+Grafana
- zipkin
- skywalking
SkyWalking
Skywalking是一款分布式的系统 性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。SkyWalking是一款 观察性的分析平台和应用性能管理系统,提供了 分布式追踪、性能指标分析、应用服务依赖分析、可视化一体化等解决方案。
- skywalking主要可以监控接口、服务、物理实例的一些状态。特别是在压测的时候可以看到众多服务中那些服务和接口比较慢,外卖可以针对性的分析和优化
- skywalkiing还可以自定义告警规则,特别是项目上线后,如果报错,可以设置给项目负责人发短信和发邮件,第一时间通知和修复
限流
为什么要限流
-
并发的确大(突发流量)
-
防止用户恶意刷接口
实现方法
- Tomcat:可以设置最大连接数
- Nginx:漏桶算法
- 网关,令牌桶算法
- 自定义拦截
Nginx限流
控制速率(突发流量)
“流量限制”配置两个主要的指令,limit_req_zone
和limit_req
,如下所示:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location /login/ {
limit_req zone=mylimit;
proxy_pass http://my_upstream;
}
}
limit_req_zone指令定义了流量限制相关的参数,而limit_req指令在出现的上下文中启用流量限制(示例中,对于”/login/”的所有请求)。limit_req_zone指令通常在 HTTP 块中定义,使其可在多个上下文中使用,它需要以下三个参数:
- Key - 定义应用限制的请求特性。示例中的 Nginx 变量
remote_addr
,占用更少的空间) - Zone - 定义用于存储每个 IP 地址状态以及被限制请求 URL 访问频率的共享内存区域。保存在内存共享区域的信息,意味着可以在 Nginx 的 worker 进程之间共享。定义分为两个部分:通过
zone=keyword
标识区域的名字,以及冒号后面跟区域大小。16000 个 IP 地址的状态信息,大约需要 1MB,所以示例中区域可以存储 160000 个 IP 地址。 - Rate - 定义最大请求速率。在示例中,速率不能超过每秒 10 个请求。Nginx 实际上以毫秒的粒度来跟踪请求,所以速率限制相当于每 100 毫秒 1 个请求。因为不允许”突发情况”,这意味着在前一个请求 100 毫秒内到达的请求将被拒绝。
控制并发连接数
ngx_http_limit_conn_module
提供了限制连接数的能力。主要是利用limit_conn_zone
和limit_conn
两个指令。
利用连接数限制 某一个用户的ip连接的数量来控制流量。
网关限流
yml配置文件中,微服务路由设置添加局部过滤器RequestRateLimiter
- id: gateway-consumer
uri: lb://GATEWAY-CONSUMER
predicates:
- Path=/order/**
filters:
- name: RequestRateLimiter
args:
# 使用SpEL从容器中获取对象
key-resolver: '#{@pathKeyResolver}'
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 令牌桶的上限
redis-rate-limiter.burstCapacity: 3
id
: 过滤器的唯一标识符,这里是 “gateway-consumer”。uri
: 指定服务的URI地址,这里使用了负载均衡(lb)协议指向 “GATEWAY-CONSUMER” 服务。predicates
: 定义了匹配路径的条件,这里指定的是/order/**
路径下的所有请求。filters
: 应用到该路由上的过滤器和参数列表。name
: 过滤器名称为 “RequestRateLimiter”,表示这是一个请求频率限制器。args
: 包含了与请求频率限制相关的参数。key-resolver
: 用于解析键值以确定如何对不同的请求进行分类,这里使用了Spring表达式语言(SpEL),通过引用一个bean (@pathKeyResolver
) 来实现。redis-rate-limiter.replenishRate
: 表示每秒可以填充的平均令牌数,这里是1个。redis-rate-limiter.burstCapacity
: 表示令牌桶的最大容量,这里是3个。
这段配置定义了一个特定的路由规则,并对这个路由应用了请求频率限制功能,确保每秒最多只能处理1次请求,且允许瞬间处理的请求最大数量不超过3次。
分布式事务
CAP定理
- Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致
- Availability(可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝
- Partition Tolerance(分区容错)
- Partition (分区):因为网络故障或其他原因导致分布式系统中的部分节点与其他节点失去链接,形成独立分区
- Tolerance(容错):在集群发现分区时,整个系统也要持续对外提供服务
结论
- 分布式系统节点之间肯定需要网络连接的,分区(P)是必然存在的。
- 如果保证访问的高可用性(A),可以持续对外提供服务,但不能保证数据的强一致性—> AP。
- 如果保证访问的数据强一致性©,就要放弃高可用性 —> CP。
BASE理论
BASE理论是对CAP的一种解决思路,包含三个思想:
- Basically Available(基本可用):分布式系统在出现故障时,允许损失部分可用性,及保障核心功能可用
- Soft State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态
- Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致
解决分布式事务的思想和模型
-
最终一致思想:各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据(AP)
-
强一致思想:各分支事务执行完业务不提交,等待彼此结果,而后统一提交或回滚(CP)
解决方案
Seata
- TC(Transaction Coordinator)-事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚
- TM(Transaction Manager)-事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务
- RM(Resource Manager)-资源管理器:管理分支事务处理的资源,与TC交谈已注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚
XA模式
- RM一阶段
- 注册分支事务到TC
- 执行分支业务slq但不提交
- 报告执行状态到TC
- TC二阶段
- 如果都成功,通知多有RM提交事务
- 如果有失败,通知所有RM回滚事务
- RM二阶段
- 接受TC指令,提交或回滚事务
AT模式(官方推荐)
- RM一阶段
- 注册分支事务
- 记录undo-log(数据日志)
- 执行sql并提及
- 报告事务状态
- RM二阶段
- 成功——删除undo-log
- 回滚——根据undo-log恢复数据到更新前
TCC模式
- Try:资源的检测和预留
- Confirm:完成资源操作业务;要求Try成功Confirm一定要成功
- Cancel:预留资源释放,可以理解为try的反向操作
MQ分布式事务
实现分布式事务,在A服务写数据的时候,需要在用一个事务内发送消息到另一个事务,异步,性能最好
接口幂等性
- 幂等:多次调用方法或者接口不会改变业务状态,可以保证重复调用的结构和单词调用的结果一致
- 如果是新增数据,可以使用数据库的唯一索引
- 如果是新增或修改数据
- 分布式锁,性能较低
- 使用tokne+redis来实现,性能较好
- 第一次请求,生成一个唯一token存入reids,返回给前端
- 第二次请求,业务处理,写道之前的token,到redis进行验证,如果存在,可以执行业务,删除token;如果不存在则直接返回,不处理业务
需要幂等的场景
- 用户重复点击(网络波动)
- MQ消息重复
- 应用使用失败或超时重试机制
接口幂等
基于RESTful API的角度对部分常见类型请求的幂等性特点进行分析
- GET:查询操作,天然幂等。
- POST:新增操作,请求一次与请求多次造成的结果不同,不是幂等的。
- PUT:更新操作,如果是绝对值更新,则是幂等的;如果是通过增量的方式更新,则不是幂等的。
- DELETE:删除操作,根据唯一值删除,是幂等的。
分布式任务调度(xxl-job)
解决的问题
- 解决集群任务的重复执行问题
- cron表达式定义灵活
- 定时任务失败了,重试和统计
- 任务量大,分片执行
xxl-job路由策略
- FIRST(第一个):固定选择第一个机器;
- LAST(最后一个):固定选择最后一个机器;
- ROUND(轮询)
- RANDOM(随机):随机选择在线的机器;
- CONSISTENT_HASH(一致性HASH):每个任务按照Hash算法固定选择某一台机器,且所有任务均匀散列在不同机器上;
- LEAST_FREQUENTLY_USED(最不经常使用):使用频率最低的机器优先被选举;
- LEAST_RECENTLY_USED(最近最少未使用):最久未使用的机器优先被选举;
- FAILOVER(故障转移):按照顺序依次进行心跳检测,第一个心跳检测成功的机器选定为目标执行器并发起调度;
- BUSYOVER(忙碌转移):按照顺序依次进行空闲检测,第一个空闲检测成功的机器选定为目标执行器并发起调度;
- SHARDING_BROADCAST(分片广播):广播触发对应集群中所有机器执行一次任务,同时系统自动传递分片参数;可根据分片参数开发分片任务。
xxl-job任务失败怎么解决
- 路由策略选择故障转移,使用健康的实例来执行任务
- 设置重试次数
- 查看日志+邮件告警来通知相关负责人解决
大数据量任务同时执行
- 然后多个实例一块去执行(集群部署),路由策略分片广播
- 在任务执行的代码中可以获取分片总数和当前分片,按照取模的方式分摊到各个实例执行