(15)SpringCloudAlibaba
1 Spring Cloud Alibaba
1.1 Spring Cloud Alibaba主要包含哪些组件?
Spring Cloud Alibaba 包含了多种开发分布式微服务系统的必需组件:
Nacos:阿里巴巴开源产品,一个更易于构建云原生应用的动态服务发现,配置管理和服务管理平台。
Sentinel:阿里巴巴开源产品,把流量作为切入点,从流量控制,熔断降级,系统负载保护等多个维度保护服务的稳定性。
RocketMQ:Apache RocketMQ 是一款基于Java 的高性能、高吞吐量的分布式消息和流计算平台。
Dubbo:Apache Dubbo 是一款高性能的 Java RPC 框架。
Seata:阿里巴巴开源产品,一个易于使用的高性能微服务分布式事务解决、案。
Alibaba Cloud ACM:一款在分布式架构环境中对应用配置进行集中管理和推送的应用配置中心产品。
Alibaba Cloud OSS:阿里云对象存储服务器(Object Storage Service,简称OSS),是阿里云提供的海量、安全、低成本、高可靠的云存储服务。
Alibaba Cloud SchedulerX:阿里中间件团队开发的一款分布式调度产品,支持周期性的任务与固定时间点触发任务。
1.2 相比原生的Spring Cloud,Spring Cloud Alibaba有哪些优势?
SpringCloud中的技术组件是集众家之长,如注册中心 Eureka、Zuul等都是依赖于Netflix,受制于第三方厂商。如Zuul宣布停止维护,Spring机构便不得不寻找替代品或自研;Eureka2.x 闭源不允许使用。Spring Cloud作为国外产品引入到国内后出现了水土不服,如SpringCloud Config默认将文件存在Github上,且没有维护界面,国内软件公司很难使用。
Spring Cloud Alibaba与原有 Spring Cloud 兼容的同时对微服务生态进行扩展,通过添加少量的配置注解,便可实现更符合国情的微服务架构。Spring Cloud Alibaba 对服务注册、配置中心与负载均衡功能都整合进 Nacos,有图形化界面,简化了微服务架构的复杂度,出问题的概率也会降低。原有的服务保护组件也调整为 Sentinel,相较Hystrix功能更强大,使用也更加友好。同时还支持了对Dubbo的调用,而且还有Seata用于支持分布式事务。
2 Nacos
2.1 Nacos的核心功能有哪些?
(1)服务注册:Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务,提供自身的元数据,比如ip地址、端口等信息。Nacos Server接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中。
(2)服务心跳:在服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server,说明服务一直处于可用状态,防止被剔除。默认5s发送一次心跳。
(3)服务同步:Nacos Server集群之间会互相同步服务实例,用来保证服务信息的一致性。 leader raft
(4)服务发现:服务消费者(Nacos Client)在调用服务提供者的服务时,会发送一个REST请求给Nacos Server,获取上面注册的服务清单,并且缓存在Nacos Client本地,同时会在Nacos Client本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存。
(5)服务健康检查:Nacos Server会开启一个定时任务用来检查注册服务实例的健康情况,对于超过15s没有收到客户端心跳的实例会将它的healthy属性置为false(客户端服务发现时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复发送心跳则会重新注册)
2.2 说说Nacos的工作流程?
(1)客户端启动时会将当前服务的信息包含ip)端口号)服务名)分组名)集群名等信息封装为一个Instance对象,准备向Nacos服务器注册服务,在注册服务之前,会根据Instance中的信息创建一个BeatInfo对象,然后创建一个定时任务,每隔一段时间向Nacos服务器发送PUT请求并携带相关信息,作为定时心跳连接,服务器端在接收到心跳请求后,会去检查当前服务列表中有没有该实例,如果没有的话将当前服务实例重新注册,注册完成后立即开启一个异步任务,更新客户端实例的最后心跳时间,如果当前实例是非健康状态则将其改为健康状态;
(2)心跳定时任务创建完成后,通过POST请求将当前服务实例信息注册进Nacos服务器,服务器端在接收到注册实例请求后,会将请求携带的数据封装为一个Instance对象,然后为这个服务实例创建一个服务Service,一个Service下可能有多个服务实例,服务在Nacos保存到一个ConcurrentHashMap中,格式为命名空间为key,value为map,分组名和服务名为内层map的key,value为服务数据,如Map(namespace,Map(group::serviceName, Service));
(3)服务创建完成之后,开启一个定时任务(5s),检查当前服务中的各个实例是否在线,如果实例上次心跳时间大于15s就将其状态设置为不健康,如果超出30s,则直接将该实例删除;
(4)然后将当前实例添加到对应服务列表中,这里会通过synchronized锁住当前服务,然后分两种情况向集群中添加实例,如果是持久化数据,则使用基于CP模式的简单Raft协议,通过leader节点将实例数据更新到内存和磁盘文件中,并且通过CountDownLatch实现了一个简单的raft写入数据的逻辑,必须集群半数以上节点写入成功才会给客户端返回成功;
(5)如果是非持久话实例数据,使用的是基于AP模式的Distro协议,首先向任务阻塞队列添加一个本地服务实例改变任务,去更新本地服务列表,然后在遍历集群中所有节点,分别创建数据同步任务放进阻塞队列异步进行集群数据同步,不保证集群节点数据同步完成即可返回;
(6)在将服务实例更新到服务注册表中时,为了防止并发读写冲突,采用的是写时复制的思想,将原注册表数据拷贝一份,添加完成之后再替换回真正的注册表,更新完成之后,通过发布服务变化事件,将服务变动通知给客户端,采用的是UDP通信,客户端接收到UDP消息后会返回一个ACK信号,如果一定时间内服务端没有收到ACK信号,还会尝试重发,当超出重发时间后就不在重发,虽然通过UDP通信不能保证消息的可靠抵达,但是由于Nacos客户端会开启定时任务,每隔一段时间更新客户端缓存的服务列表,通过定时轮询更新服务列表做兜底,所以不用担心数据不会更新的情况,这样既保证了实时性,又保证了数据更新的可靠性;
(7)服务发现:客户端通过定时任务定时从服务端拉取服务数据保存在本地缓存,服务端在发生心跳检测)服务列表变更或者健康状态改变时会触发推送事件,在推送事件中会基于UDP通信将服务列表推送到客户端,同时开启定时任务,每隔10s定时推送数据到客户端。
3 Sentinel
3.1 说说Sentinel的使用场景?
sentinel在微服务中叫做流量防卫兵,以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。Sentinel 具有以下特征:
(1)丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
(2)完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。
(3)广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。
(4)完善的 SPI 扩展机制:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。
3.2 Sentinel和Hystrix有何区别?
3.3 说说限流算法有哪几种?
- 计数器:计数器限流算法是最简单的一种限流实现方式。其本质是通过维护一个单位时间内的计数器,每次请求计数器加1,当单位时间内计数器累加到大于设定的阈值,则之后的请求都被拒绝,直到单位时间已经过去,再将计数器重置为零。
- 漏桶算法:漏桶算法可以很好地限制容量池的大小,从而防止流量暴增。漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。 在网络中,漏桶算法可以控制端口的流量输出速率,平滑网络上的突发流量,实现流量整形,从而为网络提供一个稳定的流量。为了更好的控制流量,漏桶算法需要通过两个变量进行控制:一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate)。
- 令牌桶算法:令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。
3.4 Sentinel熔断降级的原则是什么?
当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。
3.5 Sentinel如何限制资源的调用?
- 通过并发线程数进行限制:Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
- 通过响应时间对资源进行降级:除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
4 Seata
4.1 说说Seata的使用场景?
seata是开源分布式事务框架,提供了AT、TCC、SAGA 和 XA 几种事务模式。分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。
4.2 为什么会产生分布式事务?
场景举例:一个顾客访问电商平台,电商平台架构按照不同的功能进行了拆分,有订单、会员、库存子系统,下单的过程中分布式事务就产生了。顾客在创建一个订单的时候,要在订单库增加一个订单数据,同时会同步的会员库增加积分和库存库减少库存。这种操作在单个数据库中完成是没有任何问题的,一旦涉及到了分布式的情况,比如下方三个独立数据库的情况,如何来保障数据全局提交、全局回滚,这就是分布式事务要做的事情了。
4.3 Seata架构有哪几个角色,作用是什么?
Seata架构中有三个角色:
(1)TC (Transaction Coordinator): 事务协调者维护全局和分支事务的状态,驱动全局事务提交或回滚。
(2)TM (Transaction Manager) : 事务管理器定义全局事务的范围:开始全局事务、提交或回滚全局事务。
(3)RM (Resource Manager) : 资源管理器管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
假设有一个下单业务,用户发起下单(Create_Order)后需要进行支付扣款、扣库存。Create_Order需要调用其他两个服务进行数据的更新。微服务OrderService就是TM,它发起了全局事务, 库存和支付这两个微服务都是事务的参与者,而全局事务的进行需要TC协调两个RM完成。
4.4 Seata主推的事务模式是什么,有什么优势?
AT是Seata主推的模式,是基于改进后的二阶段协议实现的。AT模式是一种无侵入的分布式事务解决方案,技术核心是在每个服务的业务数据库中创建一个undolog表。用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。
在一阶段中,Seata会拦截“业务SQL“,首先解析SQL语义,找到要更新的业务数据,在数据被更新前,保存下来"undo",然后执行”业务SQL“更新数据,更新之后再次保存数据”redo“,最后生成行锁,这些操作都在本地数据库事务内完成,这样保证了一阶段的原子性。
二阶段负责整体的回滚和提交,如果之前的一阶段中有本地事务没有通过,那么就执行全局回滚,否在执行全局提交,回滚用到的就是一阶段记录的"undo Log",通过回滚记录生成反向更新SQL并执行,以完成分支的回滚。当然事务完成后会释放所有资源和删除所有日志。
4.5 Seata如何避免并发场景的脏读与脏写?
脏写:假设两个事务A 和 B 同时在更新一条数据,事务 A 先把它更新为 A 值,事务 B 紧接着把它更新为 B 值。当事务 B 更新完后,事务 A 突然回滚了,导致事务 B 修改的值也没了。
脏读:假设事务 A 更新了一行数据的值为 A 值,事务 B 去查询了一下这行数据的值,看到的值是 A 值。事务 A 突然回滚了,导致刚才更新的 A 值没了,当事务 B 再去再次查询那行数据的值,也不是A了。
无论是脏写还是脏读,都是因为一个事务去更新或者查询了另外一个还没提交的事务更新过的数据。因为另外一个事务还没提交,所以它随时可能会回滚,那么必然导致更新的数据就没了,或者之前查询到的数据就没了,这就是脏写和脏读两种场景。
Seata利用TC所自带的分布式锁来避免脏读与脏写。
脏写解决方案:如下图所示,作为TC它会为第一个事务增加一个全局的分布式锁,当对ID进行操作的时候,只有持有锁的事务才可以进行操作,要是其他进程要对这个数据进行操作,就会一直处于等待的状态,直到获取到这个锁的时候在去对同一条数据进行后续处理。通过分布式锁在对资源进行锁定,来完成并发写的操作。
脏读解决方案:在原有select 语句后面加 for update,加上以后Seata就会对这个select语句进行解析,为查找到的数据增加分布式锁,这样即使有其他线程要对这个数据进行写操作的时候,因为要获取分布式锁,所以他会一直处于阻塞状态,只有select执行完毕,分布式锁被释放以后,后续的写操作才会进行执行。
参考(摘抄的文字版权属于原作者)
https://www.cnblogs.com/qdhxhz/p/14563991.html
https://baijiahao.baidu.com/s?id=1715685686403118096&wfr=spider&for=pc
https://blog.51cto.com/u_15257216/5459196
https://blog.csdn.net/pastxu/article/details/124531980
https://www.cnblogs.com/dalianpai/p/14389421.html
https://www.sohu.com/a/436741013_465221
https://zhuanlan.zhihu.com/p/391421374
http://www.fblinux.com/?p=2429#为什么会产生分布式事务
https://www.cnblogs.com/leung-Gabriel/p/16291835.html