微服务与分布式面试大纲持续更新
2021-12-29 17:40:37 3 举报
AI智能生成
Redis与缓存 面试大纲
作者其他创作
大纲/内容
服务发现,注册与负载均衡
Spring Cloud Eureka(<b><font color="#f15a23">工作在传输层</font></b>)
Eureka是 SpringCloud 微服务架构中的注册中心,专门负责服务的注册与发现。
什么是 Eureka?工作原理?
简单来说,Eureka 就是一个 REST 服务。<br>Eureka Server :提供服务注册和服务发现的能力。<br>Eureka Client: 提供负载均衡(LB)的能力
Eureka 架构图
Eureka Server:提供服务注册和发现,多个Eureka Server之间会同步数据,做到状态一致(最终一致性)<br>Service Provider:服务提供方,将自身服务注册到Eureka,从而使服务消费方能够找到<br>Service Consumer:服务消费方,从Eureka获取注册服务列表,从而能够消费服务<br>
服务发现原理?
Eureka server 可以集群部署,多个节点之间会进行异步的数据同步,Eureka server 端通过 appName 和 istanceInfoId 来唯一区分一个 服务实例,服务实例的信息保存在本地注册表中,其实就是保存在一个 Map 中:<br>// 第一层的key是appName,第二层的key是instanceInfoId<br>private final <b>ConcurrentHashMap</b><<b><font color="#f15a23">String, Map<String, Lease<InstanceInfo>></font></b>> registry ;<br>
服务注册原理?
每个 Service 在启动的时候,都会通过 HTTP 调用把自己的服务信息注册到 Eureka Server 端, Eureka Service 接收到注册请求后,会把服务信息写入到 本地注册表中,然后同步给其它Eureka Server,每隔一段时间会进行一次服务的心跳检测。
Eureka 有哪些不足?
Eureka Client 本身有缓存,最常见的情况就是服务下线了,但是服务消费者未及时感知,此时就会调用失败。
<b><font color="#f15a23">对业务有侵入性</font></b>,需要每个服务都集成 Eureka Client
阿里开源的 nacos
不了解
K8S (<b><font color="#f15a23">工作在网络层</font></b>)
K8s服务发现流程
1. Pod 实例发布时,(通过 kind:Delooyment),kubelet 会负责启动 Pod 实例,<br> 启动完成后,kubelet 把 Pod IP 列表汇报给 Master 节点
2. Service 发布时,(通过kind:Service),K8s 会为 Service 分配 ClusterIP
3. 进行服务阶段时,Kube-Proxy 会监听Master 拿到ClusterIP 和 PodIP 列表的映射关系,<br>修改 iptables 转发规则,指示 iptables 在接收到 ClusterIP请求时,进行负载均衡并转发到对应的 Pod 上。
4. 进行服务调用时,一般通过 serviceName 先去 Kube-DNS 解析到 ClusterIP,<br>这个ClusetrIP 会被本地的 iptables 截获,通过负载均衡,转发到目标 Pod 上。
对比 K8s 服务发现机制和目前微服务主流的服务发现机制?
K8s 的服务发现机制明显抽象更好,<br>它通过 ClusterIP 统一屏蔽服务发现和负载均衡,一个服务一个ClusterIP。<br>并且对应用无侵入性。
OSI 七层网络模型
应用层
为应用程序提供服务
表示层
数据格式转化,数据加密
会话层
建立,连接,管理 维护 会话
传输层
建立管理维护端到端的连接
<b><font color="#f15a23">网络层</font></b>
负责IP 选址和路由选择
数据链路层
提供介质访问和链路管理
物理层
配置中心
Spring Cloud Config
配置更改刷新微服务
重启
刷新微服务的 /refresh 端点
Spring Cloud Bus 推送机制
存储方式
文件系统
数据库
Git
Apollo
K8S配置
configMap
微服务调用与负载均衡
OpenFeign
总的来说,Feign的源码实现的过程如下:<br>1. 首先通过@EnableFeignCleints注解开启FeignCleint<br>2. 根据Feign的规则实现接口,并加@FeignCleint注解<br> 程序启动后,会进行包扫描,扫描所有的@ FeignCleint的注解的类,并将这些信息注入到ioc容器中。<br>3. 当接口的方法被调用,通过jdk的代理,来生成具体的RequesTemplate<br> RequesTemplate在生成Request<br>4. Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp<br>5 . 最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。<br><ul><li>--------------------- </li></ul>
我们现在使用的是 openFeign
兼容 SpringMVC 注解,便于使用契约包的形式发布 SDK
Ribbon
它的作用是负载均衡,会帮你在每次请求时选择一台机器,均匀的把请求分发到各个机器上
默认超时时间是 5 s
限流和熔断
Hystrix
发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题
ThreadLocal和Hystrix: HystrixConcurrencyStrategy
通过自定义并发策略,可以将父线程的上下文注入由Hystrix管理的线程中
ThreadLocal 与Hytrix 一起使用需要作额外的操作
客户端负载均衡模式(@HystrixCommand)
断路器模式(熔断)
10s -> 50% -> 5s
后备模式(降级)- fallbackMethod 回调方法
舱壁模式(限流)
不同服务不同线程池,资源隔离
Hystrix 进阶 - 微调Hystrix
熔断,降级,限流的过程<b><font color="#f15a23">(图)</font></b>
1.Hystrix 收到请求时,会开启一个 10s 的计时器窗口,当10s 内请求失败的调用超过默认最小失败次数(20)时,就会统计失败比,超过50%的失败就会触发熔断。<br>2.熔断被出发后,默认 5s 内,后续请求都会进行降级,走后备模式,即 fallback。<br>3. 当熔断触发5s 后,会再让一个请求通过,如果还是失败,就继续开启一个5s的计时器,快速失败后,再让一个请求通过,一旦成功,就会关闭熔断,重置断路器。
上面所有的 参数都是可以配置的,并且 Hystrix 支持不同级别的配置(@HystrixCommand)
1. 整个应用程序级别的默认配置
2.类级别的默认配置
3. 在类中定义 的线程池级别
网关
Zuul
可以做统一的降级、限流、认证授权、安全,中央策略执行点(PEP)等等。
静态路由
动态路由
验证和授权
度量数据收集和日志记录
在zuul配置路由
通过服务发现自动映射路由 - 不用配置 配置文件,自动根据服务名称调用
使用服务发现手动映射路由
使用静态URL手动映射路由 - 处理非eureka服务
动态重新加载路由配置
实现方式: 和 Spring Cloud Config 配置中心结合,Zuul 暴露 /refresh 端点
配置zuul超时
1. 配置所有的服务超时
2. 针对特定服务设置超时
Zuul 的真正威力-- 过滤器 (继承ZuulFilter 来实现)
前置过滤器(TrackingFilter)
路由过滤器(SpecialRoutersFilter)
构建动态路由过滤器
后置过滤器(ReponseFilter)
TreadLocal变量可以在用户请求的那个线程的任何地方调用
getaway
安全与认证
Oauth2
OAuth2 和 Spring Security
使用OAuth2保护组织服务
如何创建受保护的资源?
传播OAuth2 令牌
令牌是在具体的微服务中进行验证的,不是在zuul。
JWT
为OAuth2 令牌提供了一个标准结构,jwt令牌特点
小巧
jwt令牌为 base64 编码,可以通过url,http首部,post 参数等轻松传递
密码签名
jwt令牌由颁发它的验证服务器签名,可以保证令牌没有被篡改
自包含
由于jwt是密码签名的,接受该令牌的微服务可以保证该令牌是有效的,不需要通过OAuth2验证服务来确定令牌的内容<br>因为令牌的签名可以被接受的微服务确认,并且内容(用户信息,过期时间)可以被接受微服务检查
可扩展
当验证服务生成一个令牌时,可以可以在令牌中放置额外的信息.<br>接受服务可以解密令牌净荷,并从它里面检索出额外自定义信息.
OAuth+JWT
如何在微服务上下文中传播JWT?
从jwt令牌中解析自定义字段.
异步与解耦
Spring Cloud Stream<br>事件驱动架构
Spring Cloud 项目通过 Spring Cloud Stream 子项目使构建基于消息传递的解决方案变得轻而易举。<br>Spring Cloud Stream 允许开发人员轻松实现消息发布和消费,同事屏蔽与底层消息传递平台相关的实现细节。
Spring Cloud Stream 是一个注解驱动的框架,可以使用多个消息平台,比如 Kafaka,RabbitMQ。<br>在应用中实现消息发布和消费是通过平台无关的 Spring 接口实现的。
组件和架构图
使用消息驱动架构的优缺点<br>也就是 使用 MQ 的优缺点
优点
松耦合
可伸缩
灵活性
持久性
缺点
消息处理语义
比如来自同一个客户的订单要按照消息顺序消费执行,如何保证?
消息可见性
一个完整的调用链如果使用了MQ,上下文全局ID如何保证,事务怎么做?
消息编排
代码不再以 请求-响应的线性处理模式,如何保证消息消费顺序性,调试时如何保证是我想要的消息消费方?
Spring Cloud Stream 中消费者组的概念
保证一个服务好几个实例消费同一个消息,只被处理一次。
Spring Data Redis
CAP理论
“可用性”和“分区容错性”,“一致性”
可用性,就是我们所说的高可用,保证高可用那就是集群多实例部署呗,比如,异地多活<br>既然多实例部署了,肯定分布在不同的网络节点中,也就是我们所说的分区容错性。<br><br>可以看出,可用性和分区容错性是紧密相关,不可缺少的,但是一致性相对而言,我们可以做出牺牲,采用最终一致性。
<b><font color="#f15a23">DDD</font></b>
你了解 DDD 吗?简单说下
DDD 在我们公司的最新骨架项目中也有落地,我理解的 DDD 也不是很深刻,DDD 没有一套严格的规范和标准,<br>实施起来往往也是 一千个人眼中有一千个哈姆雷特。就像 REST 规范一样,只是一套设计思想,具体实施起来往往各不相同。
说一下我对 DDD 的理解吧:<br><br>理解 DDD 的关键在于理解 Repository ,<br><br>理解 Repository 的关键在于区分 数据模型(Data Model) 和 领域模型(Domain Model)<br><br>
<b><font color="#f15a23">Repository</font></b>
<b><font color="#f15a23">Data Model</font></b>
贫血模型:指业务数据该如何持久化,以及数据之间的关系,也就是传统的ER模型;
<b><font color="#f15a23">Domain Model</font></b>
充血模型:指业务逻辑中,相关联的数据该如何联动。
Repository 就是连接 这两层的关键对象
Repository 与 DAO 的关系
传统的基于数据库驱动开发,DAO 封装了 数据库的操作逻辑。<br>在《架构整洁之道》一书中,Bob 大叔把 DAO 层归类为操作数据库的一个固件。
三种模型:
DO(Data Object):持久化对象
Entity:实体对象,对应业务模型,
DTO:
数据传输对象,对应 Application 的入参和出参。<br><br>CQRS里的Command、Query、Event,以及Request、Response等都属于DTO的范畴<br>
好处: 模型细分虽然带来了代码的膨胀,但是在复杂业务场景下,会让业务功能具有单一性和可测性,最终使业务逻辑的复杂性降低。
由于 DAO 是一个固件,所以 Repository 应该是一个屏蔽 数据库固件的一个软件。<br><br><b><font color="#f15a23">Repository的接口是在Domain层,但是实现类是在Infrastructure(基础设施)层。</font></b><br><br>套路:所有的Entity/Aggregate会被转化为DO,然后根据业务场景,调用相应的DAO方法进行操作,事后如果需要则把DO转换回Entity。<br>
子主题
DDD 设计
战略设计
在战略设计中,我们讲求的是子域和限界上下文(Bounded Context,BC)的划分,以及各个限界上下文之间的上下游关系
战术设计
战术设计便更偏向于编码实现。DDD战术设计的目的是使得业务能够从技术中分离并突显出来,让代码直接表达业务的本身,其中包含了聚合根、应用服务、资源库、工厂等概念。
我们业务中的 DDD 实现?
领域驱动设计
<b><font color="#f15a23">1. 拆分领域</font></b>: 诊次
病历
检验,检查
处方
诊断
账单
我们也是按照子域的划分来拆分微服务的
<b><font color="#f15a23">2. 划分: 限界上下文</font></b>
<b><font color="#f15a23">3. 分层架构:</font></b>
经典 DDD 分为四层
User Interface
用户界面层,向用户展示信息和传入用户命令。这里指的用户不单单只使用用户界面的人,也可能是外部系统,诸如用例中的参与者
Application
应用层,用来协调应用的活动,不包含业务逻辑,通过编排领域模型,包括领域对象及领域服务,使它们互相协作。不保留业务对象的状态,但它保有应用任务的进度状态。
Domain
领域层,负责表达业务概念,业务状态信息以及业务规则。尽管保存业务状态的技术细节是由基础设施层实现的,但是反映业务情况的状态是由本层控制并且使用的。领域层是业务软件的核心,领域模型位于这一层
Infrastructure
为基础实施层,提供公共的基础设施组件,如持久化机制、消息管道的读取写入、文件服务的读取写入、调用邮件服务、对外部系统的调用等等。
六边形架构+端口适配器
分布式系统接口幂等性如何设计?<br>乐观锁<br>唯一索引
何为幂等性?
一个接口被重复请求多次
为什么产生幂等性问题?
重复的请求
超时重试
用户连续点击两次
MQ 消息重复消费
如何解决幂等性问题?<br>其实还是需要考虑业务逻辑等的
查询,
天然支持幂等性,不用考虑
更新
1. 不涉及中间计算,可以不用管
2. 如果涉及了中间计算,还是要保证幂等性的
乐观锁,版本号机制,数据库表多维护一个 version 字段
新增插入
1. 从业务端保证,比如新增用户,那就手机号作为<b><font color="#f15a23">唯一索引</font></b>,先查询后插入
注意,查询插入 要保证原子性哦
2. 去重表机制
使用一张额外的表记录插入的数据
删除
非计算式:天然具备幂等性
计算式:结合实际业务逻辑考虑
分布式系统中接口顺序性如何保证?
1.分布式锁
2.一致性 hash + 内存队列
把到来的三个请求 A B C 经过一致性 hash 算法都转发到 Service1 服务,在 Service1 服务使用内存队列来进行消费
分布式一致性协议?
3. MQ
把顺序到来的请求都放到 MQ 的队列中,只需要保证这个队列同一时刻只有一个消费者就行了。
分布式锁?
基于数据库
首先:表结构设计
表结构
第一步:先查询
查询是否有锁
第二步 : 加锁
加锁语句
-------------缺点--------------
没有过期时间
不可重入
要想获取锁就要不听查询,增大数据库压力
基于 Redis
SETNX
SETNX key value
<b>"<font color="#f15a23">SET </font>if <font color="#f15a23">N</font>ot e<font color="#f15a23">X</font>ists"</b>
设置成功返回 1<br>设置失败返回 0
Redis 分布式锁框架 Redission 锁过期可以续期
---------缺点----------
主从同步时,master 宕机,分布式锁失效
基于 Zookeeper
Znode 分为四种
1.持久节点 (PERSISTENT)
2.持久节点顺序节点(PERSISTENT_SEQUENTIAL)
3.临时节点(EPHEMERAL)
4.临时顺序节点(EPHEMERAL_SEQUENTIAL)
Zookeeper分布式锁的原理
使用 临时顺序节点完成分布式锁
实现
可以直接使用zookeeper第三方库Curator客户端,这个客户端中封装了一个可重入的锁服务。
------------缺点---------------
性能不高
实现较复杂,好在轮子比较多
----------------------------------对比------------------------------------------
Redis 和 Zookeeper 实现分布式锁对比
优缺点
三种方案比较
难易程度
从理解的难易程度角度(从低到高)<br><br>数据库 > 缓存 > Zookeeper
实现复杂度
从实现的复杂性角度(从低到高)<br><br>Zookeeper >= 缓存 > 数据库
性能
从性能角度(从高到低)<br><br>缓存 > Zookeeper >= 数据库
可靠性
从可靠性角度(从高到低)<br><br>Zookeeper > 缓存 > 数据库
分布式锁如何优化?
参考 ConCurrentHashMap 的分段锁机制
把共享资源进行分段
zookeeper
分布式协调
一个 Znode 可以对 另一个 Znode 进行监听
分布式锁
元数据/配置信息管理
HA高可用
分布式事物?
文章推荐:(先看文章,再看总结)
对比 5 种分布式事务方案,还是宠幸了阿里的 Seata(原理 + 实战)
分布式事物有哪些解决方案?
<font color="#f15a23">MySQL XA(两阶段提交)方案</font>
Mysql 5.7 加入了分布式事物的支持,Mysql XA 有两种角色
RM
RM(Resource Manager):用于直接执行本地事务的提交和回滚。在分布式集群中,一台MySQL服务器就是一个RM。<br>
TM
TM(Transaction Manager):TM是分布式事务的核心管理者。事务管理器与每个RM进行通信,协调并完成分布式事务的处理。发起一个分布式事务的MySQL客户端就是一个TM。<br>
两阶段提交分为 Prepare 和 Commit 阶段:
Prepare
阶段一: 为准备(prepare)阶段。即所有的RM锁住需要的资源,在本地执行这个事务(执行sql,写redo/undo log等),但不提交,然后向Transaction Manager报告已准备就绪。<br>
Commit
阶段二: 为提交阶段(commit)。当Transaction Manager确认所有参与者都ready后,向所有参与者发送commit命令。<br>
缺点
严重依赖于数据库层面来搞定复杂的事务,效率很低,绝对不适合高并发的场景
<font color="#f15a23">MySQL XA 方案 和 阿里 Seata 对比:</font>
<b>阿里 Seata 是一个业务层的 XA(两阶段提交)。为了解决 MySQL XA 在整个分布式事物期间锁住资源过多的问题,阿里 Seata 采用了先提交,然后 记录 undo log 日志,一旦某个事物发生回滚,由事物协调器,通知其它已经提交的事物 根据 redo log 进行回滚。</b>
阿里 Seata 介绍:<br>(隔离级别:读未提交)
概念:
TC: 事物协调器
事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
TM: 事物管理器
控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议。
RM: 资源管理器
控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
一个分布式事务在Seata中的执行流程:
<ol><li>TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。</li><li>XID 在微服务调用链路的上下文中传播。</li><li>RM 向 TC 注册分支事务,接着执行这个分支事务并提交(重点:RM在第一阶段就已经执行了本地事务的提交/回滚),最后将执行结果汇报给TC。</li><li>TM 根据 TC 中所有的分支事务的执行情况,发起全局提交或回滚决议。</li><li>TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。</li></ol><br>
为什么Seata在第一阶段就直接提交了分支事务?
Seata能够在第一阶段直接提交事务,是因为Seata框架为每一个RM维护了一张UNDO_LOG表(这张表需要客户端自行创建),其中保存了每一次本地事务的回滚数据。因此,二阶段的回滚并不依赖于本地数据库事务的回滚,而是RM直接读取这张UNDO_LOG表,并将数据库中的数据更新为UNDO_LOG中存储的历史数据。<br><br>如果第二阶段是提交命令,那么RM事实上并不会对数据进行提交(因为一阶段已经提交了),而实发起一个异步请求删除UNDO_LOG中关于本事务的记录。<br>
可靠消息最终一致性方案- RocketMQ<br>
这个的意思,就是干脆不要用本地的消息表了,直接基于MQ来实现事务。比如阿里的RocketMQ就支持消息事务。<br>大概的意思就是:<br>1)A系统先发送一个prepared消息到mq,如果这个prepared消息发送失败那么就直接取消操作别执行了<br>2)如果这个prepared消息发送成功过了,那么接着执行本地事务,如果成功就告诉mq发送确认消息,如果失败就告诉mq回滚消息<br>3)如果发送了确认消息,那么此时B系统会接收到确认消息,然后执行本地的事务<br>4)mq会自动定时轮询所有prepared消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确认消息?那是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,别确认消息发送失败了。<br>5)这个方案里,要是系统B的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如B系统本地回滚后,想办法通知系统A也回滚;或者是发送报警由人工来手工回滚和补偿<br>这个还是比较合适的,目前国内互联网公司大都是这么玩儿的,要不你举用RocketMQ支持的,要不你就自己基于类似ActiveMQ?RabbitMQ?自己封装一套类似的逻辑出来,总之思路就是这样子的<br>
你们的分布式事物如何解决的?
TCC
1)Try阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行锁定或者预留<br>2)Confirm阶段:这个阶段说的是在各个服务中执行实际的操作<br>3)Cancel阶段:如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,就是执行已经执行成功的业务逻辑的回滚操作<br>
v-pipeline(企鹅杏仁):
采用了 TCC 的方案
分布式session
将session存储到一个分布式的nosql数据库中,比如 Redis.
spring session 架构与设计
45_说说一般如何设计一个高并发的系统架构?
1.系统拆分 - 根据业务模块,不同模块不同数据库.
2.缓存 - 大并发读场景
2. MQ - 大并发写场景
3.分库分表 - 大表拆小表
4.读写分离 - 读多写少
5.Elasticsearch - 复杂查询,随时扩容
高并发分布式系统下唯一ID(订单号)如何生成?
ID生成规则部分硬性要求:
全局唯一
单调递增
更好的索引性能
信息安全
不能被猜到
含时间戳
一般需要通过订单号看出这个分布式ID如何生成的
可用性要求:
高可用
低延迟
高QPS
解决方案
UUID
UUID的标准型包含32个16进制数字,以连字号分为五段,形式为 8-4-4-4-12的36个字符,性能非常高,本地生成,没有网络消耗。
优点
性能高,本地生成,无网络消耗
缺点
入数据库性能差,因为UUID是无序的
索引性能差,容易造成 B+ 树索引的页分裂
高并发下不安全
数据库自增主键
高可用情况下,单独引入一个数据库集群有点大材小用
高并发情况下,性能较差,每次都要进行数据库查询更新操作
基于Redis生成全局ID策略<br>
高可用情况下,单独引入一个Redis高可用集群有点大材小用
雪花算法
Twitter的分布式自增ID算法,Snowflake
第一部分:符号位
第二部分:时间戳
第三部分:机器相关ID
第四部分:序列号位
优点
缺点
0 条评论
下一页