SpringCloud 有什么优势
使用 Spring Boot 开发分布式微服务时,我们面临以下问题
( 1 )与分布式系统相关的复杂性 - 这种开销包括网络问题,延迟开销,带宽问题,安全问题。
( 2 )服务发现 - 服务发现工具管理群集中的流程和服务如何查找和互相交谈。它涉及一个服务目
录,在该目录中注册服务,然后能够查找并连接到该目录中的服务。
( 3 )冗余 - 分布式系统中的冗余问题。
( 4 )负载平衡 -- 负载平衡改善跨多个计算资源的工作负荷,诸如计算机,计算机集群,网络链路,
中央处理单元,或磁盘驱动器的分布。
( 5 )性能 - 问题 由于各种运营开销导致的性能问题。
( 6 )部署复杂性 -Devops 技能的要求。
什么是服务熔断?什么是服务降级?
熔断机制是应对雪崩效应的一种微服务链路保护机制。当某个微服务不可用或者响应时间太长时,
会进行服务降级,进而熔断该节点微服务的调用,快速返回 “ 错误 ” 的响应信息。当检测到该节点微
服务调用响应正常后恢复调用链路。在 SpringCloud 框架里熔断机制通过 Hystrix 实现, Hystrix 会监
控微服务间调用的状况,当失败的调用到一定阈值,缺省是 5 秒内调用 20 次,如果失败,就会启动
熔断机制。
服务降级,一般是从整体负荷考虑。就是当某个服务熔断之后,服务器将不再被调用,此时客户端
可以自己准备一个本地的 fallback 回调,返回一个缺省值。这样做,虽然水平下降,但好歹可用,
比直接挂掉强。
Hystrix 相关注解 @EnableHystrix :开启熔断 @HystrixCommand(fallbackMethod=”XXX”) :声明
一个失败回滚处理函数 XXX ,当被注解的方法执行超时(默认是 1000 毫秒),就会执行 fallback 函
数,返回错误提示。
5 、 Eureka 和 zookeeper 都可以提供服务注册与发现的功能,请
说说两个的区别?
Zookeeper 保证了 CP ( C :一致性, P :分区容错性), Eureka 保证了 AP ( A :高可用) 1. 当向注
册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的信息,但不能容忍直接
down 掉不可用。也就是说,服务注册功能对高可用性要求比较高,但 zk 会出现这样一种情况,当
master 节点因为网络故障与其他节点失去联系时,剩余节点会重新选 leader 。问题在于,选取
leader 时间过长, 30 ~ 120s ,且选取期间 zk 集群都不可用,这样就会导致选取期间注册服务瘫痪。
在云部署的环境下,因网络问题使得 zk 集群失去 master 节点是较大概率会发生的事,虽然服务能够
恢复,但是漫长的选取时间导致的注册长期不可用是不能容忍的。
2.Eureka 保证了可用性, Eureka 各个节点是平等的,几个节点挂掉不会影响正常节点的工作,剩余
的节点仍然可以提供注册和查询服务。而 Eureka 的客户端向某个 Eureka 注册或发现时发生连接失
败,则会自动切换到其他节点,只要有一台 Eureka 还在,就能保证注册服务可用,只是查到的信息
可能不是最新的。除此之外, Eureka 还有自我保护机制,如果在 15 分钟内超过 85% 的节点没有正常
的心跳,那么 Eureka 就认为客户端与注册中心发生了网络故障,此时会出现以下几种情况: ①、
Eureka 不在从注册列表中移除因为长时间没有收到心跳而应该过期的服务。 ②、 Eureka 仍然能够
接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点仍然可用) ③、当
网络稳定时,当前实例新的注册信息会被同步到其他节点。
因此, Eureka 可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像 Zookeeper 那样
使整个微服务瘫痪
负载平衡的意义什么?
在计算中,负载平衡可以改善跨计算机,计算机集群,网络链接,中央处理单元或磁盘驱动器等多
种计算资源的工作负载分布。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间并避免
任何单一资源 的过载。使用多个组件进行负载平衡而不是单个组件可能会通过冗余来提高可靠性和
可用性。负载平衡通常涉及专用软件或硬件,例如多层交换机或域名系统服务器进程。
8 、什么是 Hystrix ?它如何实现容错?
Hystrix 是一个延迟和容错库,旨在隔离远程系统,服务和第三方库的访问点,当出现故障是不可避
免的故障时,停止级联故障并在复杂的分布式系统中实现弹性。
通常对于使用微服务架构开发的系统,涉及到许多微服务。这些微服务彼此协作
1 、分布式幂等性如何设计?
在高并发场景的架构里,幂等性是必须得保证的。比如说支付功能,用户发起支付,如果后台没有
做幂等校验,刚好用户手抖多点了几下,于是后台就可能多次受到同一个订单请求,不做幂等很容
易就让用户重复支付了,这样用户是肯定不能忍的。
解决方案
1 ,查询和删除不在幂等讨论范围,查询肯定没有幂等的说,删除:第一次删除成功后,后面来删
除直接返回 0 ,也是返回成功。
2 ,建唯一索引:唯一索引或唯一组合索引来防止新增数据存在脏数据 (当表存在唯一索引,并发
时新增异常时,再查询一次就可以了,数据应该已经存在了,返回结果即可)。
3 , token 机制:由于重复点击或者网络重发,或者 nginx 重发等情况会导致数据被重复提交。前端
在数据提交前要向后端服务的申请 token , token 放到 Redis 或 JVM 内存, token 有效时间。提交后
后台校验 token ,同时删除 token ,生成新的 token 返回。 redis 要用删除操作来判断 token ,删除成
功代表 token 校验通过,如果用 select+delete 来校验 token ,存在并发问题,不建议使用。
4 ,悲观锁
select id ,name from table_# where id= '##' for update ;
悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用(另外还要考
虑 id 是否为主键,如果 id 不是主键或者不是 InnoDB 存储引擎,那么就会出现锁全表)。
5 ,乐观锁,给数据库表增加一个 version 字段,可以通过这个字段来判断是否已经被修改了
update table_xxx set name=#name#,version=version+ 1 where version=#version#
6 ,分布式锁,比如 Redis 、 Zookeeper 的分布式锁。单号为 key ,然后给 Key 设置有效期(防止支 付失败后,锁一直不释放),来一个请求使用订单号生成一把锁,业务代码执行完成后再释放锁。
7 ,保底方案,先查询是否存在此单,不存在进行支付,存在就直接返回支付结果
说说你对分布式事务的了解
分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,
特别是在微服务架构中,几乎可以说是无法避免。
首先要搞清楚: ACID 、 CAP 、 BASE 理论。
ACID
指数据库事务正确执行的四个基本要素:
1. 原子性( Atomicity )
2. 一致性( Consistency )
3. 隔离性( Isolation )
4. 持久性( Durability )
CAP
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,一致性( Consistency )、可用性
( Availability )、分区容忍性( Partition tolerance )。 CAP 原则指的是,这三个要素最多只能同
时实现两点,不可能三者兼顾。
一致性:在分布式系统中的所有数据备份,在同一时刻是否同样的值。
可用性:在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。
分区容忍性:以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数
据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择。
BASE 理论
BASE 理论是对 CAP 中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到
强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
Basically Available (基本可用)
Soft state (软状态)
Eventually consistent (最终一致性)
4 、你知道哪些分布式事务解决方案?
我目前知道的有五种:
1. 两阶段提交 (2PC)
2. 三阶段提交 (3PC)
3. 补偿事务 (TCC=Try-Confifirm-Cancel)
4. 本地消息队列表 (MQ)
5. Sagas 事务模型 ( 最终一致性 )
什么是二阶段提交?
两阶段提交 2PC 是分布式事务中最强大的事务类型之一,两段提交就是分两个阶段提交:
第一阶段询问各个事务数据源是否准备好。
第二阶段才真正将数据提交给事务数据源。
为了保证该事务可以满足 ACID ,就要引入一个协调者( Cooradinator )。其他的节点被称为参与者 (Participant )。协调者负责调度参与者的行为,并最终决定这些参与者是否要把事务进行提交。
阶段一
a) 协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待答复。
b) 各参与者执行事务操作,将 undo 和 redo 信息记入事务日志中(但不提交事务)。
c) 如参与者执行成功,给协调者反馈 yes ,否则反馈 no 。
阶段二
如果协调者收到了参与者的失败消息或者超时,直接给每个参与者发送回滚 (rollback) 消息;否则,
发送提交 (commit) 消息。两种情况处理如下:
情况 1 : 当所有参与者均反馈 yes ,提交事务
a) 协调者向所有参与者发出正式提交事务的请求(即 commit 请求)。
b) 参与者执行 commit 请求,并释放整个事务期间占用的资源。
c) 各参与者向协调者反馈 ack( 应答 ) 完成的消息。
d) 协调者收到所有参与者反馈的 ack 消息后,即完成事务提交。
情况 2 : 当有一个参与者反馈 no ,回滚事务
a) 协调者向所有参与者发出回滚请求(即 rollback 请求)。
b) 参与者使用阶段 1 中的 undo 信息执行回滚操作,并释放整个事务期间占用的资源。
c) 各参与者向协调者反馈 ack 完成的消息。
d) 协调者收到所有参与者反馈的 ack 消息后,即完成事务。
问题
1) 性能问题 :所有参与者在事务提交阶段处于同步阻塞状态,占用系统资源,容易导致性能瓶颈。
2) 可靠性问题: 如果协调者存在单点故障问题,或出现故障,提供者将一直处于锁定状态。
3) 数据一致性问题: 在阶段 2 中,如果出现协调者和参与者都挂了的情况,有可能导致数据不一
致。
优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能 100% 保证
强一致)。
缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景
什么是三阶段提交?
三阶段提交是在二阶段提交上的改进版本, 3PC 最关键要解决的就是协调者和参与者同时挂掉的问
题,所以 3PC 把 2PC 的准备阶段再次一分为二,这样三阶段提交。
处理流程如下 :
阶段一
a) 协调者向所有参与者发出包含事务内容的 canCommit 请求,询问是否可以提交事务,并等待所
有参与者答复。
b) 参与者收到 canCommit 请求后,如果认为可以执行事务操作,则反馈 yes 并进入预备状态,否
则反馈 no 。
阶段二
协调者根据参与者响应情况,有以下两种可能。
情况 1 : 所有参与者均反馈 yes ,协调者预执行事务
a) 协调者向所有参与者发出 preCommit 请求,进入准备阶段。
b) 参与者收到 preCommit 请求后,执行事务操作,将 undo 和 redo 信息记入事务日志中(但不
提交事务)。
c) 各参与者向协调者反馈 ack 响应或 no 响应,并等待最终指令。
情况 2 : 只要有一个参与者反馈 no ,或者等待超时后协调者尚无法收到所有提供者的反馈,即中断
事务
a) 协调者向所有参与者发出 abort 请求。
b) 无论收到协调者发出的 abort 请求,或者在等待协调者请求过程中出现超时,参与者均会中断事
务。
阶段三
该阶段进行真正的事务提交,也可以分为以下两种情况。
情况 1 : 所有参与者均反馈 ack 响应,执行真正的事务提交
a) 如果协调者处于工作状态,则向所有参与者发出 do Commit 请求。
b) 参与者收到 do Commit 请求后,会正式执行事务提交,并释放整个事务期间占用的资源。
c) 各参与者向协调者反馈 ack 完成的消息。
d) 协调者收到所有参与者反馈的 ack 消息后,即完成事务提交。
情况 2 : 只要有一个参与者反馈 no ,或者等待超时后协调组尚无法收到所有提供者的反馈,即回滚
事务。
a) 如果协调者处于工作状态,向所有参与者发出 rollback 请求。
b) 参与者使用阶段 1 中的 undo 信息执行回滚操作,并释放整个事务期间占用的资源。
c) 各参与者向协调组反馈 ack 完成的消息。
d) 协调组收到所有参与者反馈的 ack 消息后,即完成事务回滚。
优点: 相比二阶段提交,三阶段提交降低了阻塞范围,在等待超时后协调者或参与者会中断事务。
避免了协调者单点问题。阶段 3 中协调者出现问题时,参与者会继续提交事务。
缺点: 数据不一致问题依然存在,当在参与者收到 preCommit 请求后等待 do commite 指令时,
此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造
成数据不一致。
7 、什么是补偿事务?
TCC ( Try Confifirm Cancel )是服务化的二阶段编程模型,采用的补偿机制:
TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补
偿(撤销)操作。
它分为三个步骤:
Try 阶段主要是对业务系统做检测及资源预留。
Confifirm 阶段主要是对业务系统做确认提交, Try 阶段执行成功并开始执行 Confifirm 阶段时,默
认 Confifirm 阶段是不会出错的。即:只要 Try 成功, Confifirm 一定成功。
Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。
举个例子,假入你要向 老田 转账,思路大概是: 我们有一个本地方法,里面依次调用步骤: 1 、
首先在 Try 阶段,要先调用远程接口把 你 和 老田 的钱给冻结起来。 2 、在 Confifirm 阶段,执行远
程调用的转账的操作,转账成功进行解冻。 3 、如果第 2 步执行成功,那么转账成功,如果第二步执
行失败,则调用远程冻结接口对应的解冻方法 (Cancel) 。
优点:
性能提升:具体业务来实现控制资源锁的粒度变小,不会锁定整个资源。
数据最终一致性:基于 Confifirm 和 Cancel 的幂等性,保证事务最终完成确认或者取消,保证数据
的一致性。
可靠性:解决了 XA 协议的协调者单点故障问题,由主业务方发起并控制整个业务活动,业务活动
管理器也变成多点,引入集群。
缺点: TCC 的 Try 、 Confifirm 和 Cancel 操作功能要按具体业务来实现,业务耦合度较高,提高了开
发成本。
基本思路就是:
消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提
交,也就是说他们要在一个数据库里面。然后消息会经过 MQ 发送到消息的消费方。如果消息发送
失败,会进行重试发送。
消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经
处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个
业务补偿消息,通知生产方进行回滚等操作。
生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有
靠谱的自动对账补账逻辑,这种方案还是非常实用的。
这种方案遵循 BASE 理论,采用的是最终一致性,笔者认为是这几种方案里面比较适合实际业务场景
的,即不会出现像 2PC 那样复杂的实现 ( 当调用链很长的时候, 2PC 的可用性是非常低的 ) ,也不会像
TCC 那样可能出现确认或者回滚不了的情况。
优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。在 .NET 中 有现成的解决方
案。
缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。
MQ 事务消息
有一些第三方的 MQ 是支持事务消息的,比如 RocketMQ ,他们支持事务消息的方式也是类似于采用
的二阶段提交,但是市面上一些主流的 MQ 都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不
支持。
以阿里的 RocketMQ 中间件为例,其思路大致为:
第一阶段 Prepared 消息,会拿到消息的地址。 第二阶段执行本地事务,第三阶段通过第一阶段拿到
的地址去访问消息,并修改状态。
也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息
发送失败了 RocketMQ 会定期扫描消息集群中的事务消息,这时候发现了 Prepared 消息,它会向消
息发送者确认,所以生产方需要实现一个 check 接口, RocketMQ 会根据发送端设置的策略来决定是
回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
10 ,分布式 ID 生成有几种方案?
分布式 ID 的特性
唯一性:确保生成的 ID 是全网唯一的。
有序递增性:确保生成的 ID 是对于某个用户或者业务是按一定的数字有序递增的。
高可用性:确保任何时候都能正确的生成 ID 。
带时间: ID 里面包含时间,一眼扫过去就知道哪天的交易。
13 、你知道哪些限流算法?
限流算法有四种常见算法:
计数器算法(固定窗口)
滑动窗口
漏桶算法
令牌桶算法
数据库如何处理海量数据?
对数据库进行:分库分表,主从架构,读写分离。
水平分库 / 分表,垂直分库 / 分表。
水平分库 / 表,各个库和表的结构一模一样。
垂直分库 / 表,各个库和表的结构不一样。
读写分离:主机负责写,从机负责读。
如何提高系统的并发能力?
使用分布式系统。
部署多台服务器,并做负载均衡。
使用缓存( Redis )集群。
数据库分库分表 + 读写分离。
引入消息中间件集群。