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