Archive for June, 2020

Service Mesh 服务网格

June 21st, 2020

Sidecar 设计模式
在了解服务网格之前,先来看一个微服务的设计模式——Sidecar,也就是边车模式。边车模式是一种分布式服务架构的设计模式,特别是在各大云服务厂商中应用较多。

边车模式因为类似于生活中的边三轮摩托车而得名,也就是侉子摩托车。边三轮摩托车是给摩托车加装一个挎斗,可以装载更多的货物,变得更加多用途,得益于这样的特性,边三轮摩托曾经得到了广泛应用。

在系统设计时,边车模式通过给应用程序添加边车的方式来拓展应用程序现有的功能,分离通用的业务逻辑,比如日志记录、流量控制、服务注册和发现、限流熔断等功能。通过添加边车实现,微服务只需要专注实现业务逻辑即可,实现了控制和逻辑的分离与解耦。

边车模式中的边车,实际上就是一个 Agent,微服务的通信可以通过 Agent 代理完成。在部署时,需要同时启动 Agent,Agent 会处理服务注册、服务发现、日志和服务监控等逻辑。这样在开发时,就可以忽略这些和对外业务逻辑本身没有关联的功能,实现更好的内聚和解耦。

应用边车模式解耦了服务治理和对外的业务逻辑,这一点和 API 网关比较像,但是边车模式控制的粒度更细,可以直接接管服务实例,合理扩展边车的功能,能够实现服务的横向管理,提升开发效率。

Service Mesh 服务网格
在边车模式中,可以实现服务注册和发现、限流熔断等功能。如果边车的功能可以进一步标准化,那么会变得更加通用,就可以抽象出一个通用的服务治理组件,通过边车与其他系统交互,在各个微服务中进行推广。

随着分布式服务的发展,类似的需求越来越多,就出现了服务网格的概念。

什么是 Service Mesh
微服务领域有 CNCF 组织(Cloud Native Computing Foundation),也就是云原生基金会,CNCF 致力于微服务开源技术的推广。Service Mesh 是 CNCF 推广的新一代微服务架构,致力于解决服务间通讯。

Service Mesh 基于边车模式演进,通过在系统中添加边车代理,也就是 Sidecar Proxy 实现。

Service Mesh 可以认为是边车模式的进一步扩展,提供了以下功能:

  • 管理服务注册和发现
  • 提供限流和降级功能
  • 前置的负载均衡
  • 服务熔断功能
  • 日志和服务运行状态监控
  • 管理微服务和上层容器的通信

Service Mesh 有哪些特点
使用 Sidecar 或者 Service Mesh,都可以认为是在原有的系统之上抽象了一层新的设计来实现。计算机领域有这么一句话:没有什么系统问题不是抽象一层解决不了的,如果有,那就再抽象一层。

Service Mesh 服务网格就是使用了这样的思想,抽象出专门的一层,提供服务治理领域所需的服务注册发现、负载均衡、熔断降级、监控等功能。现在的微服务有很多部署在各大云服务厂商的主机上,不同厂商的实现标准不同,如何更好地基于各类云服务部署业务系统,这也是云原生要解决的问题。

Service Mesh 可以统一管理微服务与上层通信的部分,接管各种网络通信、访问控制等,我们的业务代码只需要关心业务逻辑就可以,简化开发工作。

Service Mesh 和 API 网关的区别
服务网格实现的功能和 API 网关类似,都可以以一个切面的形式,进行一些横向功能的实现,比如流量控制、访问控制、日志和监控等功能。

服务网格和 API 网关主要的区别是部署方式不同,在整体系统架构中的位置不一样。

API 网关通常是独立部署,通过单独的系统提供服务,为了实现高可用,还会通过网关集群等来管理;而服务网格通常是集成在应用容器内的,服务网格离应用本身更近,相比 API 网关,和应用交互的链路更短,所以可以实现更细粒度的应用管理,也体现了 Sidecar 边车的设计思想。

Service Mesh 解决方案
目前两款流行的 Service Mesh 开源软件分别是 Istio 和 Linkerd,下面简单介绍。

Istio
Istio 是 Google、IBM 等几大公司联合开源的一个服务网格组件,Istio 提供了负载均衡、服务间的身份验证、监控等方法。

Istio 的实现是通过 Sidecar ,通过添加一个 Sidecar 代理,在环境中为服务添加 Istio 的支持。Istio 代理会拦截不同服务之间的通信,然后进行统一的配置和管理。

官方文档中,对 Istio 支持的特性描述如下:

  • 为 HTTP、gRPC、WebSocket 和 TCP 流量自动负载均衡;
  • 对流量行为进行细粒度控制,包括丰富的路由规则、重试、故障转移和故障注入;
  • 可插拔的策略层和配置 API,支持访问控制、速率限制和配额;
  • 管理集群内所有流量的自动化度量、日志记录和追踪;
  • 实现安全的服务间通信,支持基于身份验证和授权的集群。
  • Istio 官网开放了中文用户指南,可以点击链接查看 https://istio.io/zh/docs/,翻译质量一般,感兴趣的同学建议直接查看英文手册。

Linkerd
Linkerd 最早由 Twitter 贡献,支持的功能和 Istio 类似,Linkerd 是一款开源网络代理,可以作为服务网格进行部署,在应用程序内管理和控制服务与服务之间的通信。

Linkerd 出现来自 Linkerd 团队为 Twitter、Yahoo、Google 和 Microsoft 等公司运营大型生产系统时发现:最复杂和令人惊讶的问题来源通常不是服务本身,而是服务之间的通讯。Linkerd 目标是解决服务之间的通信问题,通过添加 Linkerd 代理,实现一个专用的基础设施层,为应用提供服务发现、路由、错误处理及服务可见性等功能,而无须侵入应用内部实现。

Istio 和 Linkerd 都处于快速发展阶段,可以到 Istio 和 Linkerd 的官网了解更多的信息。

 

 

理解 Paxos 算法

June 14th, 2020

Paxos 的节点角色

在 Paxos 协议中,有三类节点角色,分别是 Proposer、Acceptor 和 Learner,另外还有一个 Client,作为产生议题者。

上述三类角色只是逻辑上的划分,在工作实践中,一个节点可以同时充当这三类角色。

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。

Client 产生议题者

Client 角色,作为产生议题者,实际不参与选举过程,比如发起修改请求的来源等。

Proposer 与 Acceptor 之间的交互

Paxos 中, Proposer 和 Acceptor 是算法核心角色,Paxos 描述的就是在一个由多个 Proposer 和多个 Acceptor 构成的系统中,如何让多个 Acceptor 针对 Proposer 提出的多种提案达成一致的过程,而 Learner 只是“学习”最终被批准的提案。

Proposer 与 Acceptor 之间的交互主要有 4 类消息通信,如下图:

这 4 类消息对应于 Paxos 算法的两个阶段 4 个过程,下面在分析选举过程时会讲到。

Paxos 选举过程

选举过程可以分为两个部分,准备阶段和选举阶段,可以查看下面的时序图:

Phase 1 准备阶段

Proposer 生成全局唯一且递增的 ProposalID,向 Paxos 集群的所有机器发送 Prepare 请求,这里不携带 value,只携带 N 即 ProposalID。

Acceptor 收到 Prepare 请求后,判断收到的 ProposalID 是否比之前已响应的所有提案的 N 大,如果是,则:

在本地持久化 N,可记为 Max_N;

回复请求,并带上已经 Accept 的提案中 N 最大的 value,如果此时还没有已经 Accept 的提案,则返回 value 为空;

做出承诺,不会 Accept 任何小于 Max_N 的提案。

如果否,则不回复或者回复 Error。

Phase 2 选举阶段

为了方便描述,我们把 Phase 2 选举阶段继续拆分为 P2a、P2b 和 P2c。

P2a:Proposer 发送 Accept

经过一段时间后,Proposer 收集到一些 Prepare 回复,有下列几种情况:

若回复数量 > 一半的 Acceptor 数量,且所有回复的 value 都为空时,则 Porposer 发出 accept 请求,并带上自己指定的 value。

若回复数量 > 一半的 Acceptor 数量,且有的回复 value 不为空时,则 Porposer 发出 accept 请求,并带上回复中 ProposalID 最大的 value,作为自己的提案内容。

若回复数量 <= 一半的 Acceptor 数量时,则尝试更新生成更大的 ProposalID,再转到准备阶段执行。

P2b:Acceptor 应答 Accept

Accpetor 收到 Accpet 请求 后,判断:

若收到的 N >= Max_N(一般情况下是等于),则回复提交成功,并持久化 N 和 value;

若收到的 N < Max_N,则不回复或者回复提交失败。

P2c: Proposer 统计投票

经过一段时间后,Proposer 会收集到一些 Accept 回复提交成功的情况,比如:

当回复数量 > 一半的 Acceptor 数量时,则表示提交 value 成功,此时可以发一个广播给所有的 Proposer、Learner,通知它们已 commit 的 value;

当回复数量 <= 一半的 Acceptor 数量时,则尝试更新生成更大的 ProposalID,转到准备阶段执行。

当收到一条提交失败的回复时,则尝试更新生成更大的 ProposalID,也会转到准备阶段执行。

Paxos 常见的问题

关于Paxos协议,有几个常见的问题,简单介绍下。

1.如果半数以内的 Acceptor 失效,如何正常运行?

在Paxos流程中,如果出现半数以内的 Acceptor 失效,可以分为两种情况:

第一种,如果半数以内的 Acceptor 失效时还没确定最终的 value,此时所有的 Proposer 会重新竞争提案,最终有一个提案会成功提交。

第二种,如果半数以内的 Acceptor 失效时已确定最终的 value,此时所有的 Proposer 提交前必须以最终的 value 提交,也就是Value实际已经生效,此值可以被获取,并不再修改。

2. Acceptor需要接受更大的N,也就是ProposalID有什么意义?

这种机制可以防止其中一个Proposer崩溃宕机产生阻塞问题,允许其他Proposer用更大ProposalID来抢占临时的访问权。

3. 如何产生唯一的编号,也就是 ProposalID?

在《Paxos made simple》论文中提到,唯一编号是让所有的 Proposer 都从不相交的数据集合中进行选择,需要保证在不同Proposer之间不重复,比如系统有 5 个 Proposer,则可为每一个 Proposer 分配一个标识 j(0~4),那么每一个 Proposer 每次提出决议的编号可以为 5*i + j,i 可以用来表示提出议案的次数。

 

The PartTime Parliament

Paxos Made Simple

fast-paxos