day11-微服务面试篇

YangeIT大约 44 分钟

day11-微服务面试篇

常见的面试题

微服务在面试时被问到的内容相对较少,常见的面试题如下:

  • SpringCloud 有哪些常用组件?分别是什么作用?
  • 服务注册发现的基本流程是怎样的?
  • Eureka 和 Nacos 有哪些区别?
  • Nacos 的分级存储模型是什么意思?
  • Ribbon 和 SpringCloudLoadBalancer 有什么差异
  • 什么是服务雪崩,常见的解决方案有哪些?
  • Hystix 和 Sentinel 有什么区别和联系?
  • 限流的常见算法有哪些?
  • 什么是 CAP 理论和 BASE 思想?
  • 项目中碰到过分布式事务问题吗?怎么解决的?
  • AT 模式如何解决脏读和脏写问题的?
  • TCC 模式与 AT 模式对比,有哪些优缺点

可以发现,这些问题都是围绕着 SpringCloud 的相关组件的,其中有些问题我们在课堂上已经介绍过,这里不再赘述。我们重点讲解一些之前没有讲过的,与底层实现有关的部分。

讲解的思路还是基于 SpringCloud 的组件分类来讲的,主要包括:

  • 分布式事务
  • 注册中心
  • 远程调用
  • 服务保护

等几个方面

0.分布式事务问题 🍐

0.1.本地事务Vs分布式事务

本地事务Vs分布式事务

本地事务,也就是传统的单机事务。在传统数据库事务中,必须要满足四个原则:

image-20210724165045186
image-20210724165045186
  • 原子性(Atomicity):本地事务是原子的,要么全部成功,要么全部失败。如果在事务执行过程中发生错误,会回滚事务,以确保不会留下不一致的状态。

  • 一致性(Consistency):本地事务保证了数据库或资源的一致性。在事务开始和结束时,数据应该遵循事务的完整性规则,确保数据的状态在事务之前和之后没有不一致。

  • 隔离性(Isolation):事务之间应该是隔离的,即一个事务的执行不应影响其他事务。本地事务通常使用锁和事务隔离级别来确保不同事务的数据互相隔离。

  • 读未提交(Read Uncommitted):最低的隔离级别。允许一个事务读取另一个事务尚未提交的数据。可能导致脏读、不可重复读和幻影读。
  • 读已提交(Read Committed):允许一个事务只能读取已经提交的数据,而不能读取其他事务尚未提交的数据。防止脏读,但仍可能发生不可重复读和幻影读。
  • 可重复读(Repeatable Read):保证一个事务在读取数据时不会被其他事务所修改。防止脏读和不可重复读,但仍可能发生幻影读。
  • 串行化(Serializable):最高的隔离级别。保证事务之间的完全隔离,不会发生脏读、不可重复读和幻影读。通常会带来最高的性能开销,因为它需要强制顺序执行事务
  • 持久性(Durability):一旦事务被提交,其结果应该在系统故障或崩溃后仍然保持持久。这通常涉及将事务更改写入持久性存储(如磁盘)。

  • 单一资源:本地事务仅涉及单个资源或数据库,没有涉及分布式事务或多个不同的资源。这意味着所有操作都在一个数据库上执行,而不涉及多个数据库或外部系统。

  • 较低的复杂性:与分布式事务相比,本地事务通常更简单,因为它们不需要协调多个参与者或解决分布式一致性问题。

  • 较高的性能:由于本地事务的较低复杂性和较少的协调开销,它们通常具有更高的性能,因为不需要处理网络通信和分布式系统中的复杂协议。

1.理论基础 ❤️ 🍐

解决分布式事务问题,需要一些分布式系统的基础知识作为理论指导。🎯 ❤️

1.1.CAP定理 ❤️ 🍐

CAP定理

1998年,加州大学的计算机科学家 Eric Brewer【埃里克·布鲁尔】 提出,分布式系统有三个指标。

  • Consistency(一致性) [kənˈsɪstənsi]
  • Availability(可用性) [əˌveɪləˈbɪləti]
  • Partition tolerance (分区容错性)[pɑrˈtɪʃ(ə)n] [ˈtɑlərəns]
image
image

它们的第一个字母分别是 C、A、P。

Eric Brewer【埃里克·布鲁尔】 说,这三个指标不可能同时做到。这个结论就叫做 CAP 定理。

CAP 定理解释:👇

Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致。

比如现在包含两个节点,其中的初始数据是一致的:

image-20210724170704694
image-20210724170704694

当我们修改其中一个节点的数据时,两者的数据产生了差异:

image-20210724170735847
image-20210724170735847

要想保住一致性,就必须实现node01 到 node02的数据 同步:

image-20210724170834855
image-20210724170834855

总结

  • 解释CAP定理是什么?

答案: CAP定理是由计算机科学家Eric Brewer提出的,它指出在分布式系统中,无法同时满足三个属性:一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)。根据CAP定理,分布式系统只能满足其中两个属性中的任意两个,但无法同时满足所有三个。

  • 分布式系统节点通过网络连接,一定会出现分区问题(P)
  • 当分区出现时,系统的一致性(C)和可用性(A)就无法同时满足

1.2.BASE理论 ❤️ 🍐

BASE理论

image
image

BASE理论是对CAP的一种解决思路,包含三个思想:

  • Basically Available (基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。
  • Soft State(软状态) :在一定时间内,允许出现中间状态,比如临时的不一致状态
  • Eventually Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。

解决分布式事务的思路:🍐

image
image

分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理BASE理论,有两种解决思路:

  • AP模式:各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。AT模式

  • CP模式:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态。XA模式

思考:为什么P都要存在?

  • 不管是哪一种模式,都需要在子系统事务之间互相通讯,协调事务状态,也就是需要一个事务协调者(TC)image-20210724172123567 这里的子系统事务,称为分支事务;有关联的各个分支事务在一起称为全局事务

总结

  1. 简述BASE理论三个思想:
  • 基本可用
  • 软状态
  • 最终一致
  1. 解决分布式事务的思想和模型:
  • 全局事务:整个分布式事务
  • 分支事务:分布式事务中包含的每个子系统的事务
  • 最终一致思想:各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据
  • 强一致思想:各分支事务执行完业务不要提交,等待彼此结果。而后统一提交或回滚

1.3.AT模式

AT模式

AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。

1.Seata的AT模型

基本流程图:

image-20210724175327511
image-20210724175327511

阶段一RM的工作:

  • 注册分支事务
  • 记录undo-log(数据快照)
  • 执行业务sql并提交
  • 报告事务状态

阶段二提交时RM的工作:

  • 删除undo-log即可

阶段二回滚时RM的工作:

  • 根据undo-log恢复数据到更新前

总结

课堂作业

  1. AT模式优缺点?🎤
  2. AT 模式如何解决脏读和脏写问题的?🎤

1.4.TCC模式 🍐

TCC模式

TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个方法:

  • Try:资源的检测和预留;

  • Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。

  • Cancel:预留资源释放,可以理解为try的

TCC流程分析 👇

举例,一个扣减用户余额的业务。假设账户A原来余额是100,需要余额扣减30元。

  • 阶段一( Try ):检查余额是否充足,如果充足则冻结金额增加30元,可用余额扣除30

初识余额:

image-20210724182424907
image-20210724182424907

余额充足,可以冻结:

image-20210724182457951
image-20210724182457951

此时,总金额 = 冻结金额 + 可用金额,数量依然是100不变。事务直接提交无需等待其它事务。

  • 阶段二(Confirm):假如要提交(Confirm),则冻结金额扣减30

确认可以提交,不过之前可用金额已经扣减过了,这里只要清除冻结金额就好了:

image-20210724182706011
image-20210724182706011

此时,总金额 = 冻结金额 + 可用金额 = 0 + 70 = 70元

  • 阶段二(Canncel):如果要回滚(Cancel),则冻结金额扣减30,可用余额增加30

需要回滚,那么就要释放冻结金额,恢复可用金额:

image-20210724182810734
image-20210724182810734

TCC模式-代码操作 如果想看代码,点击这里

总结

课堂作业

面试题:TCC 模式的每个阶段是做什么的?🎤

  • Try:资源检查和预留
  • Confirm:业务执行和提交
  • Cancel:预留资源的释放

TCC 的优点是什么?🎤

  • 一阶段完成直接提交事务,释放数据库资源,性能好
  • 相比 AT 模型,无需生成快照,无需使用全局锁,性能最强
  • 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库

TCC 的缺点是什么?🎤

  • 有代码侵入,需要人为编写 try、Confirm 和 Cancel 接口,太麻烦
  • 软状态,事务是最终一致
  • 需要考虑 Confirm 和 Cancel 的失败情况,做好幂等处理、事务悬挂和空回滚处理

TCC 模式与 AT 模式对比,有哪些优缺点

image
image

2.注册中心

本章主要学习 Nacos 中的一些特性和原理,以及与 Eureka 的功能对比。

1.1 Eureka

Eureka

Eureka 是 Netflix 公司开源的一个服务注册中心组件,早期版本的 SpringCloud 都是使用 Eureka 作为注册中心。由于 Eureka 和 Nacos 的 starter 中提供的功能都是基于 SpringCloudCommon 规范,因此两者使用起来差别不大。

课前资料中提供了一个 Eureka 的 demo:

也可以点击这里下载cloud-demo.zip初始工程open in new window 👈

我们可以用 idea 打开查看一下:

结构说明:

  • eureka-server:Eureka 的服务端,也就是注册中心。没错,Eureka 服务端要自己创建项目
  • order-service:订单服务,是一个服务调用者,查询订单的时候要查询用户
  • user-service:用户服务,是一个服务提供者,对外暴露查询用户的接口

启动以后,访问 localhost:10086 即可查看到 Eureka 的控制台,相对于 Nacos 来说简陋了很多:

image
image
image
image

微服务引入 Eureka 的方式也极其简单,分两步:

  • 引入 eureka-client 依赖
  • 配置 eureka 地址

接下来就是编写 OpenFeign 的客户端了,怎么样?是不是跟 Nacos 用起来基本一致。

image
image

总结

课堂作业

  1. SpringCloud 有哪些常用组件?分别是什么作用? 🎤
  2. 服务注册发现的基本流程是怎样的?🎤
  3. Eureka 和 Nacos 有哪些区别?🎤
  4. Nacos的服务临时实例和非临时实例有什么区别?🎤

1.2.服务分级存储模型

服务分级存储模型

一个服务可以有多个实例,例如我们的user-service,可以有:

  • 127.0.0.1:8081
  • 127.0.0.1:8082
  • 127.0.0.1:8083

假如这些实例分布于全国各地的不同机房,例如:

  • 127.0.0.1:8081,在上海机房
  • 127.0.0.1:8082,在上海机房
  • 127.0.0.1:8083,在杭州机房

Nacos就将同一机房内的实例 划分为一个集群

容灾系统是指在相隔较远的异地,建立两套或多套功能相同的IT系统,互相之间可以进行健康状态监视和功能切换,当一处系统因意外(如火灾、地震等)停止工作时,整个应用系统可以切换到另一处,使得该系统功能可以继续正常工作。容灾技术是系统的高可用性技术

也就是说,user-service是服务,一个服务可以包含多个集群,如杭州、上海,每个集群下可以有多个实例,形成分级模型,如图:

image-20210713232522531
image-20210713232522531

微服务互相访问时,应该尽可能访问同集群实例,因为本地访问速度更快。当本集群内不可用时,才访问其它集群。例如:

image-20210713232658928
image-20210713232658928

杭州机房内的order-service应该优先访问同机房的user-service。 👈

修改user-service的application.yml文件,添加集群配置:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: CS # 集群名称

重启两个user-service实例后,我们可以在nacos控制台看到下面结果:

image
image

我们再次复制一个user-service启动配置,添加属性:

-Dserver.port=8083 -Dspring.cloud.nacos.discovery.cluster-name=SH

配置如图所示:

image-20210713233528982
image-20210713233528982

启动UserApplication3后再次查看nacos控制台:

image
image

如果停掉一个就会出现不可用现象:

image
image

课堂作业

  1. Nacos 的分级存储模型是什么意思?🎤
  2. NacosRule有什么特点?

1.2.权重配置和环境隔离

权重配置和环境隔离

权重配置

实际部署中会出现这样的场景:

服务器设备性能有差异,部分实例所在机器性能较好,另一些较差,我们希望性能好的机器承担更多的用户请求。

但默认情况下NacosRule是同集群内随机挑选,不会考虑机器的性能问题。

因此,Nacos提供了权重配置来控制访问频率,权重越大则访问频率越高。

在nacos控制台,找到user-service的实例列表,点击编辑,即可修改权重:

image
image

在弹出的编辑窗口,修改权重:

imageimage

可以发现,改成0.1的实例被访问的次数剧减!

实际开发中,如果要升级,但是往常要停机,但是现在可以设置权重为0 ,然后升级

注意:如果权重修改为0,则该实例永远不会被访问 ⚠️

1.3.环境隔离

环境隔离

Nacos提供了namespace来实现环境隔离功能。

  • nacos中可以有多个namespace
  • namespace下可以有group、service等
  • 不同namespace之间相互隔离,例如不同namespace的服务互相不可见
image-20210714000101516
image-20210714000101516

5.5.1.创建namespace

默认情况下,所有service、data、group都在同一个namespace,名为public:

image-20210714000414781
image-20210714000414781

我们可以点击页面新增按钮,添加一个namespace:

image-20210714000440143
image-20210714000440143

然后,填写表单:

image-20210714000505928
image-20210714000505928

就能在页面看到一个新的namespace:

image-20210714000522913
image-20210714000522913

3.远程调用

我们知道微服务间远程调用都是有 OpenFeign 帮我们完成的,甚至帮我们实现了服务列表之间的负载均衡。但具体负载均衡的规则是什么呢?何时做的负载均衡呢?

接下来我们一起来分析一下。

3.1.负载均衡原理

负载均衡原理

在 SpringCloud 的早期版本中,负载均衡都是有 Netflix 公司开源的 Ribbon 组件来实现的,甚至 Ribbon 被直接集成到了 Eureka-client 和 Nacos-Discovery 中。

但是自 SpringCloud2020 版本开始,已经弃用 Ribbon,改用 Spring 自己开源的 Spring Cloud LoadBalancer 了,我们使用的 OpenFeign 的也已经与其整合。

接下来我们就通过源码分析,来看看 OpenFeign 底层是如何实现负载均衡功能的。

总结

课堂作业

  1. Ribbon 和 SpringCloudLoadBalancer 有什么差异?🎤

4.服务保护

服务保护

在 SpringCloud 的早期版本中采用的服务保护技术叫做 Hystix,不过后来被淘汰,替换为 Spring Cloud Circuit Breaker,其底层实现可以是 Spring RetryResilience4J

不过在国内使用较多还是 SpringCloudAlibaba 中的 Sentinel 组件。

接下来,我们就分析一下 Sentinel 组件的一些基本实现原理以及它与 Hystix 的差异。

4.1.线程隔离

首先我们来看下线程隔离功能,无论是 Hystix 还是 Sentinel 都支持线程隔离。不过其实现方式不同。

线程隔离有两种方式实现:

  • 线程池隔离:给每个服务调用业务分配一个线程池,利用线程池本身实现隔离效果
  • 信号量隔离:不创建线程池,而是计数器模式,记录业务使用的线程数量,达到信号量上限时,禁止新的请求

如图:

两者的优缺点如下:

Sentinel 的线程隔离就是基于信号量隔离实现的,而 Hystix 两种都支持,但默认是基于线程池隔离。

5.作业

尝试用自己的语言回答下列面试题:

  • SpringCloud 有哪些常用组件?分别是什么作用?
  • 服务注册发现的基本流程是怎样的?
  • Eureka 和 Nacos 有哪些区别?
  • Nacos 的分级存储模型是什么意思?
  • OpenFeign 是如何实现负载均衡的?
  • 什么是服务雪崩,常见的解决方案有哪些?
  • Hystix 和 Sentinel 有什么区别和联系?
  • 限流的常见算法有哪些?
  • 什么是 CAP 理论和 BASE 思想?
  • 项目中碰到过分布式事务问题吗?怎么解决的?
  • AT 模式如何解决脏读和脏写问题的?
  • TCC 模式与 AT 模式对比,有哪些优缺点
  • RabbitMQ 是如何确保消息的可靠性的?
  • RabbitMQ 是如何解决消息堆积问题的?

实战

经过一段时间的学习,是不是感觉很充实?但是心理感觉有点不确定,到底有没有掌握微服务开发技能,不用怀疑和恐慌,接下来用心用力借力,将实战任务完成就证明自己了 加油!!!!!! image