代码
调试技术
数据库
- 《Getting started with impala》
- 《mysql 必知必会》
- 《mysql 性能调优与架构实践》
- 《Mysql 技术内幕 InnoDB 存储引擎》
- 《Redis 实战》
- 《Redis 深度历险核心原理和应用实践》
- 《redis设计与实现》
- 《七周七数据库》
- 《深入浅出mysql》
- 《高性能mysql第三版》
- 《MySQL是怎样运行的》
前端
GOLANG
- 《1 The Go Programming Language》
- 《2 The Go Programming Language》
- 《3 The Go Programming Language》
- 《Build Web Application With Golang》
- 《Go101》
- 《Network Programming with go》
- 《Building Microservices With Go》
- 《Building Restful Web Services with Go》
- 《Concurrency In Go》
- 《Go In Action(Go 实战)》
- 《Go学习笔记语言详解》
- 《Go学习笔记源码剖析》
- 《Go语言编程》
JAVA
网络
心理学
PYTHON
创业
UNIX/LINUX
分布式
系统设计
搜索引擎
开发工具
- 《Practical Vim》
- 《Vim8文本处理实战》
- 《Learn vim scrpt the hard way》
- 《Pro Git》
- 《Mastering Vim》
- 《Mastering Vim Quickly》
思维
源码
网站架构微服务
- 《微服务架构设计模式》
- 《从0开始学架构》
- 《web scalavility for startup engineers》
- 《designdatainstensive_application》
- 《designdatainstensive_application2》
- 《clean_architecture》
- 《微服务实战》
- 《微服务设计》
软件工程/项目管理
运维
金融理财
写作
互联网
区块链
技术演讲网课
- 《哔哩哔哩的go微服务实战》
- 《go业务基础库之Error&Context》
- 《Go同步和并发设计模式》
- 《300分钟吃透分布式缓存》
- 《DDD实战课》
- 《分布式技术原理与实战45讲》
- 《架构设计面试精讲》
- 《高并发系统设计40问》
- 《java并发编程78讲》
- 《中间件核心技术与实战讲》
职场
《分布式技术原理与实战45讲》
01 如何证明分布式系统的 CAP 理论?
CAP 理论可以表述为,一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)这三项中的两项。 CAP 理论的证明有多种方式,通过反证的方式是最直观的。反证法来证明 CAP 定理,最早是由 Lynch 提出的,通过一个实际场景,如果 CAP 三者可同时满足, 由于允许 P 的存在,则一定存在 Server 之间的丢包,如此则不能保证 C。
避免单点故障 + 无状态服务
- CP 架构:对于 CP 来说,放弃可用性,追求一致性和分区容错性。
- 我们熟悉的 ZooKeeper,就是采用了 CP 一致性,ZooKeeper 是一个分布式的服务框架,主要用来解决分布式集群中应用系统的协调和一致性问题。其核心算法是 Zab,所有设计都是为了一致性。在 CAP 模型中,ZooKeeper 是 CP,这意味着面对网络分区时,为了保持一致性,它是不可用的。
- AP 架构:对于 AP 来说,放弃强一致性,追求分区容错性和可用性,这是很多分布式系统设计时的选择,后面的 Base 也是根据 AP 来扩展的。
- Eureka,不保证一致性
02 不同数据一致性模型有哪些应用?
BASE: 基本可用(Basically Available),软状态(Soft-state),最终一致性(Eventually consistent)
不同数据一致性模型
- 强一致性: 更新之后,后续访问都是最新值
- 弱一致性: 不承诺可以立即读取到最新写入的值,也不承诺多久可以读取到。(不一致性窗口
- 最终一致性
- 因果一致性: 有因果关系的操作顺序得到保证,非因果关系的操作顺序无所谓。比如你对一条评论的回复,你的回复必须在评论之后。
- 会话一致性: 同一个会话中“读已之所写”。同一个会话始终可以访问到最新值
- 单调读一致性
- 单调写一致性
03 如何透彻理解 Paxos 算法
- Proposer 提案者: Proposer 可以有多个,在流程开始时,Proposer 提出议案,也就是value,所谓 value,在工程中可以是任何操作, 比如“修改某个变量的值为某个新值”,Paxos 协议中统一将这些操作抽象为 value。 不同的 Proposer 可以提出不同的甚至矛盾的 value,比如某个 Proposer 提议“将变量 X 设置为 1”,另一个 Proposer 提议“将变量 X 设置为 2”,但对同一轮 Paxos 过程,最多只有一个 value 被批准。
- Acceptor 批准者: 在集群中,Acceptor 有 N 个,Acceptor 之间完全对等独立,Proposer 提出的 value 必须获得超过半数(N/2+1)的 Acceptor 批准后才能通过。
- Learner 学习者: Learner 不参与选举,而是学习被批准的 value,在Paxos中,Learner主要参与相关的状态机同步流程。
这里Leaner的流程就参考了Quorum 议会机制,某个 value 需要获得 W=N/2 + 1 的 Acceptor 批准,Learner 需要至少读取 N/2+1 个 Accpetor,最多读取 N 个 Acceptor 的结果后,才能学习到一个通过的 value。
04 ZooKeeper 如何保证数据一致性
数据发布和订阅、命名服务、配置中心、注册中心、分布式锁等。
Zab 协议的具体实现可以分为以下两部分:
Leader 节点接受事务提交,并且将新的 Proposal 请求广播给 Follower 节点,收集各个节点的反馈,决定是否进行 Commit,在这个过程中,也会使用上一课时提到的 Quorum 选举机制。 如果在同步过程中出现 Leader 节点宕机,会进入崩溃恢复阶段,重新进行 Leader 选举,崩溃恢复阶段还包含数据同步操作,同步集群中最新的数据,保持集群的数据一致性。 整个 ZooKeeper 集群的一致性保证就是在上面两个状态之前切换,当 Leader 服务正常时,就是正常的消息广播模式;当 Leader 不可用时,则进入崩溃恢复模式,崩溃恢复阶段会进行数据同步,完成以后,重新进入消息广播阶段。
07 分布式事务有哪些解决方案
- 2PC/3PC (two-phase commit protocl): 两阶段提交
- TCC分段提交: 将事务拆分成 try/confirm/cancel 三个步骤
- 基于消息补偿的最终一致性
- 本地消息表: 生产方创建一个事务消息表,并记录消息发送状态。消费方需要处理这个消息并完成业务逻辑。另有一个异步机制定期扫描为未完成消息,保证最终一致性
- 统收到下单请求,将订单业务数据存入到订单库中,并且同时存储该订单对应的消息数据,比如购买商品的 ID 和数量,消息数据与订单库为同一库,更新订单和存储消息为一个本地事务,要么都成功,要么都失败。
- 库存服务通过消息中间件收到库存更新消息,调用库存服务进行业务操作,同时返回业务处理结果。
- 消息生产方,也就是订单服务收到处理结果后,将本地消息表的数据删除或者设置为已完成。
- 设置异步任务,定时去扫描本地消息表,发现有未完成的任务则重试,保证最终一致性。
- 第三方可靠消息队列
- 本地消息表: 生产方创建一个事务消息表,并记录消息发送状态。消费方需要处理这个消息并完成业务逻辑。另有一个异步机制定期扫描为未完成消息,保证最终一致性
08 对比两阶段提交,三阶段协议哪些改进
两阶段提交
- 提交请求阶段
在提交请求阶段,协调者将通知事务参与者准备提交事务,然后进入表决过程。在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地事务执行成功)或取消(本地事务执行故障),在第一阶段,参与节点并没有进行Commit操作。
- 提交阶段
在提交阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消这个事务。这个结果的处理和前面基于半数以上投票的一致性算法不同,必须当且仅当所有的参与者同意提交,协调者才会通知各个参与者提交事务,否则协调者将通知各个参与者取消事务。
参与者在接收到协调者发来的消息后将执行对应的操作,也就是本地 Commit 或者 Rollback。
mysql cluster 内部数据的同步就是用的2pc协议。
三阶段提交
- CanCommit 阶段
3PC 的 CanCommit 阶段其实和 2PC 的准备阶段很像。协调者向参与者发送 Can-Commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应。
- PreCommit 阶段
协调者根据参与者的反应情况来决定是否可以继续事务的 PreCommit 操作。根据响应情况,有以下两种可能。
A. 假如协调者从所有的参与者获得的反馈都是 Yes 响应,那么就会进行事务的预执行:
- 发送预提交请求,协调者向参与者发送 PreCommit 请求,并进入 Prepared 阶段;
- 事务预提交,参与者接收到 PreCommit 请求后,会执行事务操作;
- 响应反馈,如果参与者成功执行了事务操作,则返回 ACK 响应,同时开始等待最终指令。
B. 假如有任何一个参与者向协调者发送了 No 响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就中断事务:
- 发送中断请求,协调者向所有参与者发送 abort 请求;
- 中断事务,参与者收到来自协调者的 abort 请求之后,执行事务的中断。
- DoCommit 阶段
该阶段进行真正的事务提交,也可以分为以下两种情况。
A. 执行提交
- 发送提交请求。协调者接收到参与者发送的 ACK 响应后,那么它将从预提交状态进入到提交状态,并向所有参与者发送 doCommit 请求。
- 事务提交。参与者接收到 doCommit 请求之后,执行正式的事务提交,并在完成事务提交之后释放所有事务资源。
- 响应反馈。事务提交完之后,向协调者发送 ACK 响应。
- 完成事务。协调者接收到所有参与者的 ACK 响应之后,完成事务。
B. 中断事务 协调者没有接收到参与者发送的 ACK 响应,可能是因为接受者发送的不是 ACK 响应,也有可能响应超时了,那么就会执行中断事务。
C.超时提交 参与者如果没有收到协调者的通知,超时之后会执行 Commit 操作。
09 MySQL 数据库如何实现 XA 规范?
XA 规范是如何定义的: XA 是由 X/Open 组织提出的分布式事务规范,XA 规范主要定义了事务协调者(Transaction Manager)和资源管理器(Resource Manager)之间的接口。
mysql 当有事务提交时:
第一步,InnoDB 进入 Prepare 阶段,并且 write/sync redo log,写 redo log,将事务的 XID 写入到 redo 日志中,binlog 不作任何操作; 第二步,进行 write/sync Binlog,写 binlog 日志,也会把 XID 写入到 Binlog; 第三步,调用 InnoDB 引擎的 Commit 完成事务的提交,将 Commit 信息写入到 redo 日志中。
10 如何在业务中体现 TCC 事务模型?
TCC 提出了一种新的事务模型,基于业务层面的事务定义,锁粒度完全由业务自己控制,目的是解决复杂业务中,跨表跨库等大颗粒度资源锁定的问题。 TCC 把事务运行过程分成 Try、Confirm / Cancel 两个阶段,每个阶段的逻辑由业务代码控制,避免了长事务,可以获取更高的性能。
- Try 阶段:调用 Try 接口,尝试执行业务,完成所有业务检查,预留业务资源。
- Confirm 或 Cancel 阶段:两者是互斥的,只能进入其中一个,并且都满足幂等性,允许失败重试。
- Confirm 操作:对业务系统做确认提交,确认执行业务操作,不做其他业务检查,只使用 Try 阶段预留的业务资源。
- Cancel 操作:在业务执行错误,需要回滚的状态下执行业务取消,释放预留资源。
Try 阶段失败可以 Cancel,如果 Confirm 和 Cancel 阶段失败了怎么办?
TCC 中会添加事务日志,如果 Confirm 或者 Cancel 阶段出错,则会进行重试,所以这两个阶段需要支持幂等;如果重试失败,则需要人工介入进行恢复和处理等。
真实业务场景改造
我们把订单业务拆解为以下几个步骤:
- 订单更新为支付完成状态
- 扣减用户账户余额
- 增加用户账户积分
- 扣减当前商品的库存
下面应用 TCC 事务,需要对业务代码改造,抽象 Try、Confirm 和 Cancel 阶段。
Try 操作一般都是锁定某个资源,设置一个预备的状态,冻结部分数据。比如,订单服务添加一个预备状态,修改为 UPDATING,也就是更新中的意思,冻结当前订单的操作,而不是直接修改为支付成功。
库存服务设置冻结库存,可以扩展字段,也可以额外添加新的库存冻结表。积分服务和库存一样,添加一个预增加积分,比如本次订单积分是 100,添加一个额外的存储表示等待增加的积分,账户余额服务等也是一样的操作。
Confirm 操作就是把前边的 Try 操作锁定的资源提交,类比数据库事务中的 Commit 操作。在支付的场景中,包括订单状态从准备中更新为支付成功;库存数据扣减冻结库存,积分数据增加预增加积分。
Cancel 操作执行的是业务上的回滚处理,类比数据库事务中的 Rollback 操作。首先订单服务,撤销预备状态,还原为待支付状态或者已取消状态,库存服务删除冻结库存,添加到可销售库存中,积分服务也是一样,将预增加积分扣减掉。
执行业务操作
下面来分析业务的实际执行操作,首先业务请求过来,开始执行 Try 操作,如果 TCC 分布式事务框架感知到各个服务的 Try 阶段都成功了以后,就会执行各个服务的 Confirm 逻辑。
如果 Try 阶段有操作不能正确执行,比如订单失效、库存不足等,就会执行 Cancel 的逻辑,取消事务提交。
开源框架:Tcc-transaction, ByteTCC, Spring-cloud-rest-tcc, Seata(支持TCC,AT,Saga)
11 分布式锁有哪些场景和实现
- 基于数据库: 依赖数据库唯一性,主键或者唯一索引。存在单点故障、无法失效、不可重入、无法实现阻塞
- redis : setnx + expire (lua或者新版set保证原子性)
- ZooKeeper: 临时顺序节点 (Apache Curator)
12 redis实现分布式锁
一般来说,生产环境可用的分布式锁需要满足以下几点:
- 互斥性,互斥是锁的基本特征,同一时刻只能有一个线程持有锁,执行临界操作;
- 超时释放,超时释放是锁的另一个必备特性,可以对比 MySQL InnoDB 引擎中的 innodb_lock_wait_timeout 配置,通过超时释放,防止不必要的线程等待和资源浪费;
- 可重入性,在分布式环境下,同一个节点上的同一个线程如果获取了锁之后,再次请求还是可以成功;
- 高性能和高可用,加锁和解锁的开销要尽可能的小,同时也需要保证高可用,防止分布式锁失效;
- 支持阻塞和非阻塞性,对比 Java 语言中的 wait() 和 notify() 等操作,这个一般是在业务代码中实现,比如在获取锁时通过 while(true) 或者轮询来实现阻塞操作。
redis实现细节:
- 设置一个 uuid 保证只能被加锁的线程释放
- redis + lua 保证原子性
- 记录日志和上下游数据链路
- 集群下使用 redlock 算法。大于 3 个奇数个独立的 redis 节点
- 一般使用单点的 redis 实现分布式锁就可以,出现数据不一致及时监控报警人工修补
14 如何理解 RPC 远程服务调用?
- 如何通信。Netty 等网络框架
- 如何网络传输。协议类型。Protobuf, Dubbo协议等
- 服务注册和发现。注册中心存储存储了服务的 ip、端口、调用方式(协议)等
15 为什么微服务需要API网关
在微服务设计中,需要隔离内外部调用,统一进行系统鉴权、业务监控等,API 服务网关是一个非常合适的切入口。
通过引入 API 网关这一角色,可以高效地实现微服务集群的输出,节约后端服务开发成本,减少上线风险,并为服务熔断、灰度发布、线上测试等提供解决方案。
除了封装内部系统之外,API 网关作为一个系统访问的切面,还可以添加身份验证、监控、负载均衡、限流、降级与应用检测等功能。
Spring Cloud Zuul, Spring Cloud Gateway
16 如何实现服务注册和发现
首先,在服务启动时,服务提供者会向注册中心注册服务,暴露自己的地址和端口等,注册中心会更新服务列表。 服务消费者启动时会向注册中心请求可用的服务地址,并且在本地缓存一份提供者列表,这样在注册中心宕机时仍然可以正常调用服务。
如果提供者集群发生变更,注册中心会将变更推送给服务消费者,更新可用的服务地址列表。
ZooKeeper(CP), Eureka(AP), Nacos(AP/CP切换), Consul
17 如何实现分布式调用跟踪?
Google Dapper 论文
Dapper 用 Span 来表示一个服务调用开始和结束的时间,也就是时间区间,并记录了 Span 的名称以及每个 Span 的 ID 和父 ID,如果一个 Span 没有父 ID 则被称之为 Root Span。
一个请求到达应用后所调用的所有服务,以及所有服务组成的调用链就像是一个树结构,追踪这个调用链路得到的树结构称之为 Trace,所有的 Span 都挂在一个特定的 Trace 上,共用一个 TraceId。
在一次 Trace 中,每个服务的每一次调用,就是一个 Span,每一个 Span 都有一个 ID 作为唯一标识。同样,每一次 Trace 都会生成一个 TraceId 在 Span 中作为追踪标识,另外再通过一个 parentSpanId,标明本次调用的发起者。
当 Span 有了上面三个标识后,就可以很清晰地将多个 Span 进行梳理串联,最终归纳出一条完整的跟踪链路。
Google Dapper, Twitter Zipkin, Jager, 阿里EagleEye
18 分布式下如何实现配置管理
实现配置管理中心,一般需要下面几个步骤:
- 提取配置信息,放到一个公共的地方存储,比如文件系统、数据库、Redis;
- 使用发布/订阅模式,让子系统订阅这些配置信息; (或者客户端拉取的模式)
- 对外开放可视化的配置管理中心,对配置信息进行操作维护。
一个合格的分布式配置管理系统,除了配置发布和推送,还需要满足以下的特性:
- 高可用性,服务器集群应该无单点故障,只要集群中还有存活的节点,就能提供服务;
- 容错性,保证在配置平台不可用时,也不影响客户端的正常运行;
- 高性能,对于配置平台,应该是尽可能低的性能开销,不能因为获取配置给应用带来不可接受的性能损耗;
- 可靠存储,包括数据的备份容灾,一致性等,尽可能保证不丢失配置数据;
- 实时生效,对于配置的变更,客户端应用能够及时感知。
开源系统:淘宝 Diamond, 百度Disconf(ZooKeeper), 携程 Apollo
19 容器化升级对服务有哪些影响
容器基于 linux 的Namespace 和 CGroups 技术
Namespace
Namespace 的目的是通过抽象方法使得 Namespace 中的进程看起来拥有它们自己的隔离的全局系统资源实例。 Linux 内核实现了六种 Namespace:Mount namespaces、UTS namespaces、IPC namespaces、PID namespaces、Network namespaces、User namespaces,功能分别为:隔离文件系统、定义 hostname 和 domainame、特定的进程间通信资源、独立进程 ID 结构、独立网络设备、用户和组 ID 空间。
Docker 在创建一个容器的时候,会创建以上六种 Namespace 实例,然后将隔离的系统资源放入到相应的 Namespace 中,使得每个容器只能看到自己独立的系统资源。
Cgroups
Docker 利用 CGroups 进行资源隔离。CGroups(Control Groups)也是 Linux 内核中提供的一种机制,它的功能主要是限制、记录、隔离进程所使用的物理资源,比如 CPU、Mermory、IO、Network 等。
简单来说,CGroups 在接收到调用时,会给指定的进程挂上钩子,这个钩子会在资源被使用的时候触发,触发时会根据资源的类别,比如 CPU、Mermory、IO 等,然后使用对应的方法进行限制。
CGroups 中有一个术语叫作 Subsystem 子系统,也就是一个资源调度控制器,CPU Subsystem 负责 CPU 的时间分配,Mermory Subsystem 负责 Mermory 的使用量等。Docker 启动一个容器后,会在 /sys/fs/cgroup 目录下生成带有此容器 ID 的文件夹,里面就是调用 CGroups 的配置文件,从而实现通过 CGroups 限制容器的资源使用率。
注意服务如何获取正确的核心数,比如 go 有一个 automaxprocs 库。
20 ServiceMesh: 服务网格有哪些应用
Sidercar边车模式
Service Mesh 可以认为是边车模式的进一步扩展,提供了以下功能:
- 管理服务注册和发现
- 提供限流和降级功能
- 前置的负载均衡
- 服务熔断功能
- 日志和服务运行状态监控
- 管理微服务和上层容器的通信
开源解决方案:Istio, Linkerd
23 读写分离如何在业务中落地
mysql 主从复制过程:
- 主库将变更写入 binlog 日志,从库连接到主库之后,主库会创建一个log dump 线程,用于发送 bin log 的内容。
- 从库开启同步以后,会创建一个 IO 线程用来连接主库,请求主库中更新的 bin log,I/O 线程接收到主库 binlog dump 进程发来的更新之后,保存在本地 relay 日志中。
- 接着从库中有一个 SQL 线程负责读取 relay log 中的内容,同步到数据库存储中,也就是在自己本地进行回放,最终保证主从数据的一致性。
问题:
- 主从延时。高并发场景下,可能要等待几十毫秒甚至上百毫秒以后从库才能访问到
- 强制读取主库
- 一致性要求高的业务比如金融支付,不进行读写分离
- 丢数据。极端场景主库宕机,数据还没有同步到从库
- 异步复制(不关心从库是否同步成功)、半同步(至少一个从库同步完成,才完成写操作)、全同步复制(性能最差)
24 分库分表如何实现
引入问题:
- 分布式事务
- 跨库查询。字段冗余
中间件:Apache ShardingSphere, 淘宝TDDL
25 唯一主键问题
全局唯一、有序性、高并发
- uuid。不适合,长度太长又是非自增(数据库页分裂)
- snowflake 算法。 符号位(1) + 41位时间戳 + 10位机器 id + 12位序列号 。可能有时钟回拨问题
- 数据库维护区间分配
- redis incr (随机增加,防止被遍历)
26 分库分表后,如何实现扩容?
路由规则与扩容方案
- 哈希取模
- 停机扩容
- 不停机扩容:双写
如果重新部署新的数据库存储,可以粗略地分为以下的步骤:
- 创建一套新的订单数据库;
- 在某个时间点上,将历史数据按照新的路由规则分配到新的数据库中;
- 在旧数据库的操作中开启双写,同时写入到两个数据库;
- 用新的读写服务逐步替代旧服务,同步进行数据不一致校验,最后完成全面切流。
- 数据范围拆分
扩容可以直接增加新的存储,新数据区间映射到新的节点中,不用在节点时间调整,也不用迁移历史数据。缺点是数据可能不均匀
- 结合数据范围和哈希取模
首先根据订单 ID 哈希取模,然后对取模后的数据再次进行范围分区。结合两者优点
27 NoSQL 数据库的典型应用
- Key-Value 存储: 基于内存的 Redis, Memcached。基于 SSD 的 RocksDB, LevelDB
- 文档型。MongoDB
- 列式存储:海量数据, Cassandra, Hbase。高并发写入性能更好
- 图形数据库。
- 索引型:Elasticsearch
28 Elasticsearch 是如何建立索引的?
Elasticsearch 基于 Lucene 的分布式全文检索框架。倒排索引
ELK: Elasticsearch 数据分析和检索, Logstash 用于日志收集, Kibana 用于界面展示
30 消息队列有哪些应用场景
- 系统解耦
- 异步处理。流量削峰
- 请求缓冲
- 数据分发
常见队列:
- Apache Kafka
- Apache RocketMQ(阿里 java 开源)。尽可能保证消息投递中的顺序一致性和可靠性。特别适合电商等相对复杂业务场景
- Apache RabbitMQ (Erlang)。一旦出现堆积性能下降比较快,适合企业应用
如果在一个电商系统的构建中,这三款消息队列可以怎样组合使用呢?
- Kafka 可以在各类数据埋点中使用,比如电商营销的转化率日志收集和计算,另外,Kafka 的高性能使得特别它适合应用在各类监控、大数据分析等场景。
- RocketMQ 对一致性的良好保证,可以应用在电商各级业务调用的拆分中,比如在订单完成后通知用户,物流信息更新以后对订单状态的更新等。
- RabbitMQ 则可以在数据迁移、系统内部的业务调用中应用,比如一些后台数据的同步、各种客服和 CRM 系统。
31 集群消费和广播消费有什么区别
消息队列的消费模型
- 点对点模型
在点对点模型下,生产者向一个特定的队列发布消息,消费者从该队列中读取消息,每条消息只会被一个消费者处理。
- 发布订阅模型
大部分人在浏览资讯网站时会订阅喜欢的频道,比如人文社科,或者娱乐新闻,消息队列的发布订阅也是这种机制。在发布订阅模型中, 消费者通过一个 Topic 来订阅消息,生产者将消息发布到指定的队列中。如果存在多个消费者,那么一条消息就会被多个消费者都消费一次。
32 业务需要顺序消费,如何保证时序性?
保证时序性的难点:
- 分布式时钟问题。不同机器时钟不同,所以不能用时间作为判断标准
- 消息发送端和消费端的集群
- 消息重传
- 网络和内部并发
kafka 顺序消息:
- 发到单个partition; 同一类别消息发到同一个 partition
- 一个比较特殊的情况是消息失败重发的场景,比如同一个订单下的消息 1 和 2,如果 1 发送失败了,重发的时候可能会出现在 2 的后边, 种情况可以通过设置“max.in.flight.requests.per.connection”参数来解决,该参数可以限制客户端能够发送的未响应请求的个数,还可以在一定程度上避免这种消息乱序。
RocketMQ 顺序消息:
- 和 kafka 类似,同一个 queue 中的顺序
业务角度保证顺序消费: 合理的设计方案
- 秒杀提交时服务端的时间戳(不用绝对有序)
- 消息添加单调自增的序列 id
33. 消息幂等:如何保证消息不被重复消息
以 Apache Dubbo 为例,它支持多种集群容错的方式,并且可以针对业务特性,配置不同的失败重试机制,包括 Failover 失败自动切换、Failsafe 失败安全、Failfast 快速失败等。 比如在 Failover 下,失败会重试两次;在 Failfast 下,失败则不会重试,直接抛出异常。
- 天然幂等无需处理。允许重试的,可以配置消息队列通过合理重试,提高请求成功率
- 数据库去重。去重表;唯一索引
- 全局唯一消息 id
34. 高可用:如何实现消息队列的 HA?
kafka 多副本机制
35. 消息队列选型:kafka如何实现高性能?
- 磁盘顺序读写。kafka 可以配置异步刷盘,提高吞吐量
- 批量操作优化。批量发送和拉取;消息压缩协议
- sendfile 零拷贝: Kafka 依赖 Linux 内核提供的 Sendfile 系统调用。 在 Sendfile 方法中,数据在内核缓冲区完成输入和输出,不需要拷贝到用户空间处理,这也就避免了重复的数据拷贝
- Kafka 把所有的消息都存放在单独的文件里,在消息投递时直接通过 Sendfile 方法发送文件,减少了上下文切换,因此大大提高了性能。
- MMAP 技术。内存映射。 MMAP 可以将文件直接映射到用户态的内存空间,省去了用户空间到内核空间复制的开销,所以说 MMAP 也是一种零拷贝技术。
36. 消息队列选型:RocketMQ 适用场景
- 实现 Binlog 分发。结合阿里 Canal
- 实现分布式一致性。支持事务消息
38. 分布式系统还有哪些缓存?
- 前端缓存。页面和浏览器缓存
- 网络传输缓存。CDN
- 服务端缓存。本地缓存和外部缓存
- 数据库缓存
39. 如何避免缓存穿透、击穿、雪崩?
- 穿透:请求查询不存在数据或者大量缓存同时失效落到数据库
- 缓存空值
- 布隆过滤器
- 击穿:热点 key 失效导致请求全部落到数据库(可以看成穿透的一种特殊场景)
- 雪崩:大量缓存同时失效导致数据库压力过大宕机;缓存 redis 宕机
- 明确缓存集群容量峰值,通过合理限流和降级,防止大量请求拖垮缓存
- 集群高可用
缓存稳定性:
- 缓存数据。一般要命中率 90%以上,大促销等场景会要求 99% 以上命中率
- 缓存集群稳定性。缓存服务压测,明确最大水位,超过就限流等处理
40: 先更新数据库,还是先更新缓存?
更新缓存的方式
- 先更新数据库,再更新缓存。
- 先删缓存,再更新数据库
- 先更新数据库,再删除缓存 (不一致概率最小)
为什么删除缓存而不是更新缓存:
- 1.删除更轻量级
- 2.长时间不用的缓存可以清理节省空间
多级缓存如何更新
- 消息队列通知。也就是在数据库更新后,通过事务性消息队列加监听的方式,失效对应的缓存。
- 多级缓存难保证一致性, 通常是用在数据一致性不敏感业务。(或者保证会话一致性 or 单调度一致性)
- 版本号、时间戳+主键,实现最终一致性
41. 失效策略:缓存过期都有哪些策略?
页面置换算法:FIFO, LRU, LFU
redis 内存淘汰策略:
- noeviction,这是默认的策略,对于写请求会拒绝服务,直接返回错误,这种策略下可以保证数据不丢失;
- allkeys-lru,这种策略操作的范围是所有 key,使用 LRU 算法进行缓存淘汰;
- volatile-lru,这种策略操作的范围是设置了过期时间的 key,使用 LRU 算法进行淘汰;
- allkeys-random,这种策略下操作的范围是所有 key,会进行随机淘汰数据;
- volatile-random,这种策略操作的范围是设置了过期时间的 key,会进行随机淘汰;
- volatile-ttl,这种策略操作的范围是设置了过期时间的 key,根据 key 的过期时间进行淘汰,越早过期的越优先被淘汰。
42. 负载均衡:一致性哈希解决了哪些问题?
- 哈希取模
- 一致性哈希
45. 从双十一看高可用保障方式
系统限流、降级熔断、负载均衡、稳定性指标、系统监控、日志系统
提高可用性手段:缓存、池化、异步化、负载均衡、队列、限流降级熔断
- 海量用户请求,万倍日常流量
- 流量突增。独立热点集群部署、消息队列削峰、商品缓存预热
- 高并发,海量用户请求。订单丢失、扣减库存异常、超卖等问题、锁冲突死锁等
- 高可用常见手段:缓存、消息队列
- 避免雪崩、链路问题、故障传导。限流、降级、熔断、隔离
自动降级策略:
- 超时降级
- 失败次数降级
- 故障降级
46. 高并发场景下如何实现系统限流?
限流一般需要结合容量规划和压测进行。超出之后降级策略:延迟处理、拒绝服务、随机拒绝等
- AbortPolicy, 丢弃任务并抛出异常
- DiscardPolicy, 丢弃任务不抛出异常
- DiscardOldestPolicy 等
常见限流算法:
- 计数器算法。
- 可以从单点扩展到集群,适合分布式环境。单点使用内存,分布式使用 redis 等存储
- 对临界流量不友好,限流不够平滑。可以用滑动窗口优化
- 漏桶
- 令牌桶。允许突发流量。 acquire(阻塞), tryAcquire(非阻塞)
47. 降级和熔断:如何增强服务稳定性?
降级
降级:解决资源不足和海量请求之间的矛盾。
手段:流量暴增时,对非核心流程业务、非关键业务,进行有策略的放弃,以此释放系统资源,保证核心业务正常运行。
比如大促时候的评论、退款功能。
- 梳理核心流程。哪些可以降级
- 系统在一定水位开启。一般通过配置中心开关
熔断
保护系统不被外部大流量或者下游系统的异常拖垮,防止雪崩。
Alibaba Sentinel 或者 Netflix Hystrix
熔断器的恢复时间,也就是平均故障恢复时间,称为 MTTR,在稳定性设计中是一个常见的指标,在 Hystrix 的断路器设计中,有以下几个状态。
- Closed:熔断器关闭状态,比如系统检测到下游失败到了 50% 的阈值,会开启熔断。
- Open:熔断器打开状态,此时对下游的调用在内部直接返回错误,不发出请求,但是在一定的时间周期以后,会进入下一个半熔断状态。
- Half-Open:半熔断状态,允许少量的服务请求,如果调用都成功(或一定比例)则认为恢复了,关闭熔断器,否则认为还没好,又回到熔断器打开状态。
48 如何选择适合业务的负载均衡策略?
- 硬件负载均衡,就是通过专门的硬件来实现负载均衡,比如常见的 F5 设备。
- 软件负载均衡则是通过负载均衡软件实现,常见的就是 Nginx。
常见负载均衡策略:
- 轮询策略。顺序从服务器列表中选择一个节点
- 加权轮询
- 随机策略
- 最小响应时间
- 最小并发数
- 哈希策略
如何实现:
- 在服务器端负载均衡中,请求先发送到负载均衡服务器,然后通过负载均衡算法,在众多可用的服务器之中选择一个来处理请求。
- 在客户端负载均衡中,不需要额外的负载均衡软件,客户端自己维护服务器地址列表,自己选择请求的地址,通过负载均衡算法将请求发送至该服务器。
Spring Cloud Eureka, Ribbon
49 线上服务有哪些稳定性指标?
监控组件、键控指标、监控处理
稳定性指标:稳定性指标,这里我按照自己的习惯,把它分为服务器指标、系统运行指标、基础组件指标和业务运行时指标。
每个分类下面我选择了部分比较有代表性的监控项,如果你还希望了解更多的监控指标,可以参考 Open-Falcon 的监控采集,地址为 Linux 运维基础采集项。
50 分布式下有哪些监控组件?
- OpenFalcon
- Zabbix
- Nagios
- CAT
监控处理制度:
- 发现故障,第一时间同步到相关业务负责人,上下游链路;
- 第一时间快速恢复业务,快速进行故障止血;
- 及时协调资源,避免故障升级;
- 事后进行故障复盘和总结,避免再次出现类似问题。
51 分布式下如何实现统一日志系统?
ELK 统一日志系统: Elasticsearch, Logstash, Kibana
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论