SpringCloudAlibaba包含了多种开发分布式微服务系统的必需组件:
SpringCloud中的技术组件是集众家之长,如注册中心Eureka、Zuul等都是依赖于Netflix,受制于第三方厂商。如Zuul宣布停止维护,Spring机构便不得不寻找替代品或自研;Eureka2.x闭源不允许使用。SpringCloud作为国外产品引入到国内后出现了水土不服,如SpringCloudConfig默认将文件存在Github上,且没有维护界面,国内软件公司很难使用。
SpringCloudAlibaba与原有SpringCloud兼容的同时对微服务生态进行扩展,通过添加少量的配置注解,便可实现更符合国情的微服务架构。SpringCloudAlibaba对服务注册、配置中心与负载均衡功能都整合进Nacos,有图形化界面,简化了微服务架构的复杂度,出问题的概率也会降低。原有的服务保护组件也调整为Sentinel,相较Hystrix功能更强大,使用也更加友好。同时还支持了对Dubbo的调用,而且还有Seata用于支持分布式事务。
sentinel在微服务中叫做流量防卫兵,以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。Sentinel具有以下特征:
令牌桶算法:令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。
seata是开源分布式事务框架,提供了AT、TCC、SAGA和XA几种事务模式。分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于分布式系统的不同节点之上。简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。
场景举例:一个顾客访问电商平台,电商平台架构按照不同的功能进行了拆分,有订单、会员、库存子系统,下单的过程中分布式事务就产生了。顾客在创建一个订单的时候,要在订单库增加一个订单数据,同时会同步的会员库增加积分和库存库减少库存。这种操作在单个数据库中完成是没有任何问题的,一旦涉及到了分布式的情况,比如下方三个独立数据库的情况,如何来保障数据全局提交、全局回滚,这就是分布式事务要做的事情了。
Seata架构中有三个角色:
假设有一个下单业务,用户发起下单(Create_Order)后需要进行支付扣款、扣库存。Create_Order需要调用其他两个服务进行数据的更新。微服务OrderService就是TM,它发起了全局事务,库存和支付这两个微服务都是事务的参与者,而全局事务的进行需要TC协调两个RM完成。
在一阶段中,Seata会拦截“业务SQL“,首先解析SQL语义,找到要更新的业务数据,在数据被更新前,保存下来"undo",然后执行”业务SQL“更新数据,更新之后再次保存数据”redo“,最后生成行锁,这些操作都在本地数据库事务内完成,这样保证了一阶段的原子性。
二阶段负责整体的回滚和提交,如果之前的一阶段中有本地事务没有通过,那么就执行全局回滚,否在执行全局提交,回滚用到的就是一阶段记录的"undoLog",通过回滚记录生成反向更新SQL并执行,以完成分支的回滚。当然事务完成后会释放所有资源和删除所有日志。
脏写:假设两个事务A和B同时在更新一条数据,事务A先把它更新为A值,事务B紧接着把它更新为B值。当事务B更新完后,事务A突然回滚了,导致事务B修改的值也没了。
脏读:假设事务A更新了一行数据的值为A值,事务B去查询了一下这行数据的值,看到的值是A值。事务A突然回滚了,导致刚才更新的A值没了,当事务B再去再次查询那行数据的值,也不是A了。
无论是脏写还是脏读,都是因为一个事务去更新或者查询了另外一个还没提交的事务更新过的数据。因为另外一个事务还没提交,所以它随时可能会回滚,那么必然导致更新的数据就没了,或者之前查询到的数据就没了,这就是脏写和脏读两种场景。