第10课:论技术体系的相通性

第10课:论技术体系的相通性

从几道架构师面试题说起

以下是我在阿里、网易、滴滴、蘑菇街、贝贝网等国内大型互联网公司面试架构师岗位时碰到的几道面试题,供大家参考:

  • CAP 理论、BASE 思想及在分布式系统中应用?
  • Reactor 模式、微内核架构及应用场景?
  • RPC 架构核心组件、Dubbo 框架原理?
  • 高可用架构以及在大数据技术体系中的表现形式?
  • 如何构建微服务架构、微服务架构的本质?

关于这些面试题的具体回答思路以及所涉及的知识体系我们将在本课程的最后一篇中具体展开,在这里列举这些面试题的目的在于引出一个话题,即面对这些看上去比较高大上的问题时,我们应该如何应对?

很多读者可能对以上问题有一定的了解,有些则可能连题目都无法看懂。但对于那些能够回答这些问题的人而言,知道的可能也仅仅是针对这些问题的答案,也就是对于已经接触过或使用过的工具框架而言肯定多少能明白其中的原理,但是如果是那些没有用过的工具和框架呢?工具和框架发展日新月异,例如在第8篇《微服务——最热门的架构》中提到的 Dubbo 框架在2013年开始已经几乎不再更新,在不久之前才重新启动维护工作。不难想象在不久的将来,我们还会出现第二个、第三个类似 Dubbo 这样的框架。我们知道 Dubbo 是阿里巴巴开源的一个分布式服务框架,其背后的核心原理就是实现了 RPC 和服务治理。对于任何一个分布式服务框架而言,RPC 和服务治理都是不可缺少的组件,我们只要能够对 RPC 和服务治理有深刻的理解,对于 Dubbo 同类型的任何新框架,如果出现类似“RPC 架构核心组件、Dubbo 框架原理”这样的面试题,我们都应该有明确的回答思路和方法。为了达到这样的境界,这就要引出本篇的核心主题,即我们认为技术知识体系之间存在一定的相通性。

技术知识体系的相通性

技术知识体系的建设存在如下三个层次:

  • 知道有哪些技术

作为一名软件开发人员,首先需要明确目前市面上存在的主流技术,包括各种工具、框架、第三方工具库等,这是技术知识体系建设的初级阶段,绝大多数开发人员都要经历这一阶段,架构师自然也不例外。

  • 知道技术背后的原理

知道各项技术背后的原理是技术知识体系建设的第二个阶段,架构师不仅仅需要知道各种技术体系的具体表现形式,更应该掌握其背后的设计思想、原理和实现机制。这是架构师与普通开发人员之间的本质区别,也是向架构师转型所需要的关键阶段。

  • 知道技术在具体场景的应用

在知道技术背后的原理之后,我们就能够明白各项技术的应用场景。这一阶段实际上已经是一个非常高级的阶段,涉及到了技术选型和技术创新等架构师的核心工作内容。

在以上三个阶段中,第三阶段是架构师的努力目标,但在本课程中第二阶段才是最为重要的阶段,因为具体应用场景需要因地制宜,而只有明白原理才能真正明确技术的应用场景。技术知识体系的相通性也体现在这一阶段,通过了解各个工具和框架背后的原理,我们就能做到触类旁通,面对各种面试题时能够从题目中提炼出背后的技术相通性,并通过这种相通性解决你平时并不一定接触过的问题。

本篇的后续内容将通过一系列示例来阐述技术知识体系的相通性,而这些示例同样也是架构师面试过程中非常重要的知识点,供读者参考。

技术知识体系相通性示例之一:RPC 架构

我们在第6篇《RPC——一切架构的基础》中详细讨论了 RPC 架构,网络通信、序列化/反序列化、传输协议和服务调用四个组件构成了 RPC 架构的基础组件,而我们也知道 RPC 架构是构成分布式服务架构和微服务架构的基本结构。对于常见的分布式系统而言,RPC 架构的应用非常常见,但我们这里要讲的是它在大数据体系中的应用。以 Hadoop 为代表的大数据生态系统中的大多数工具本质上也表现为一种分布式系统,也需要 RPC 架构这样的远程通信工具实现各个节点组件之间的协作和管理。

我们来分析一下 Hadoop 中的 RPC 实现机制,Hadoop 采用了 RpcEngine 接口封装了 RPC 的整个调用过程,RpcEngine 接口的一个实现类是 WritableRpcEngine(见下图)。WritableRpcEngine 使用 Hadoop 自带的 Writable 序列化机制实现远程调用过程中的序列化,包括 Invoker 类、Invocation 类和 Server 类等三个核心类。Invoker 是 InvocationHandler 接口(Java 动态代理的核心接口)实现类,用于序列化并生成 Invocation 对象并将 Invocation 对象发送到远程服务器,同时获取响应并反序列化。而 Invocation 类代表 RPC 请求对象,包括方法、参数等调用信息。最后的 Server 类启动 Socket 监听 RPC 请求,调用 WritableRpcInvoker 响应请求,这里 WritableRpcInvoker 负责响应远程客户端请求,发序列化请求,通过反射调用服务端程序并包装结果对象。

如果采用 Protobuf 序列化方式,Hadoop 也提供了 ProtobufRpcEngine(见下图)。在 ProtobufRpcEngine 中,Invoker 的序列化/反序列化工具使用 Protobuf,并使用 RPCRequest/ResponseWrapper 包装 RPC 请求。Server 类与 WritableRPCEngine 完全一致。而 ProtobufRpcInvoker 主要体现就是序列化/反序列化方式的不同。

Hadoop RPC 客户端请求流程如下图所示,可以看到它是一个比较标准的动态代理实现方式。

Hadoop RPC 服务器端的请求响应方式如下图所示,可以看到也是 Reactor 模式的一种变体。其中 Listener 监听来自客户端的 Socket 连接请求,初始化连接并轮询从 readers 线程中挑选一个线程处理请求;Reader 读取 RPC 请求,封装 Call 对象并放到 CallQueue 中;Hander 处理 RPC 请求并返回响应,并从 CallQueue 中取出 RPC 请求,执行 RPC 对应的本地函数并返回结果;而 Responder 针对网络不佳等情况,向客户端发送 RPC 响应。

在这个案例中,我们看到了 Java 动态代理、Java NIO 和 Protobuf 等 RPC 架构中通用的技术体系,如果你已经掌握了这些知识点,那么就算没有具体分析过 Hadoop 中实现远程通信的过程,你也应该能够通过这些通用技术体系构建对 Hadoop RPC 的理解模型并在具体的面试构成中表现出对这块内容的理解程度。

技术知识体系相通性示例之二:分布式协调

对于分布式协调,我们通常会有一些常见需求,如提供集群的集中化配置管理功能,不重启系统而让新的配置即时生效;提供简单可靠的集群节点动态发现机制,确保节点上线和宕机能立即通知,实现复杂的故障恢复功能;提供简单可靠的节点 Leader 选举机制,从而解决中心化架构集群中 Leader 选举问题;提供分布式锁,确保集群共享数据不被破坏等。

分布式协调实现的主流工具是 Zookeeper,其核心是一个精简的文件系统,提供基于目录节点树方式的数据存储,以及一些额外的抽象操作,如排序、通知和监控。Zookeeper 应用非常广泛,在 Yarn、Storm、Hbase 和 Kafka 等框架中都能看到它的不同应用方式。

Zookeeper 的基本组成结构是 ZNode,表示路径的同时也能存储数据(见下图),所有的 ZNode 通过路径被引用。一方面 ZNode 能够做到原子性访问,即所有请求的处理结果在整个 Zookeeper 集群中的所有机器是一致的。同时也能确保顺序(Sequential)性访问,从同一客户端发起的事务请求,会按照其发起顺序严格的应用到 Zookeeper 中去。

Zookeeper 有两个特性与分布式协调直接相关:

  • 会话(Session)机制

会话是客户端和服务器端的 TCP 连接,能够发送请求并接收 Watcher 事件。从类型而言,会话又可以分为短暂(Ephemeral)性会话和持久(Persistent)会话两种,前者在会话断开的同时会自动删除会话对应的 ZNode,而后者则不会。

  • Watcher 机制

Watcher 机制本质上就是分布式的回调,ZNode 的客户端关注 ZNode 发生的变化,一旦发生变化则回传消息到客户端,然后客户端的消息处理函数得到调用。在 Zookeeper 中,任何读操作都能够设置 Watcher。

有了会话和 Watcher 机制,我们就可以实现如下图所示的分布式环境下配置信息共享管理。

基于 Zookeeper 的分布式协调机制,我们也可以实现与 Dubbo 中注册中心类似的共享存储方案。在注册中心中,所有服务在指定路径 /services 目录下创建临时节点,所有访问这些服务的客户端 Watcher 该目录。当新节点加入集群时,Zookeeper 实时通知到所有客户端,客户端做相应路由信息维护;而当某个节点宕机时,客户端通过 Watcher 同样会收到通知。整个流程的示意图见下图。

最后,我们再来看一下分布式锁的实现。在 Zookeeper 中,整个分布式锁的实现流程参考下图,可以看到核心的原理还是依赖于临时节点和 Watcher 机制。

通过以上讨论可以得出的结论是:不同场景表现出不同的需求,但这些需求的背后却可以通过同一种技术体系进行分析并解决。以分布式协调为例,Zookeeper 的临时节点和 Watcher 机制提供了一套可用于多种不同场景的统一解决方案。通过掌握其中的基本原理,我们面对自己并不熟悉但又类似的场景时就能得出正确的解决方案。

技术知识体系相通性示例之三:Gossip 协议

几年前有一次去网易面试,面试官问了这样一个问题:在 Redis 集群构建过程中使用到了什么协议?我当时完全没有意识到其中的知识点,回来查了一下资料才知道原来有一个 Gossip 协议。

Gossip(中文就是流言蜚语的意思)协议指的就是在一个有界网络中,每个节点都随机地与其他节点通信,经过一番杂乱无章的通信,最终所有节点的状态都会达成一致,本质上也是最终一致性的一种具体体现。通过 Gossip 协议所构建的集群从种类上讲属于对等集群(Peer To Peer Cluster),节点之间完全对等,不需要任何的中心节点,是去中心化思想的一种具体实现。

以 Redis 为例,通过 Gossip 协议构建集群的示意图如下所示。各个节点通过握手和响应握手的方式加入到集群中。更为具体一点的说法,这里面存在几种操作确保 Gossip 节点的通信方式和收敛性。如 push 操作,A 节点将数据(key,value,version)及对应的版本号推送给 B 节点,B 节点更新 A 中比自己新的数据;pull 操作,A 仅将数据 key 和 version 推送给 B,B 将本地比 A 新的数据(key,value,version)推送给 A,A 更新本地。而 push/pull 操作与 pull 类似,只是多了一步,A 再将本地比 B 新的数据推送给 B,B 更新本地。

Gossip 协议虽然比较冷僻,但应用并不少。最早使用 Gossip 协议的是 Cassandra,这样是一个 NoSQL 数据库。而在搜索引擎 Elastic Search 中,我们也发现了 Gossip 协议的身影。Elastic Search 的对等网络自动切换机制也是基于 Gossip 协议实现,下图就是一个有5个分片和3个节点的 Elastic Search 集群,展示了一个节点在发生宕机之后又恢复正常的工作流程。

通过这个示例,我们得到了两点启示:首先对于冷僻的知识体系同样需要有一定的了解,也就是要有知识体系的广度;另一方面,在我们掌握一个核心原理之后,同样需要了解该原理在其他工具和框架中的应用,做到举一反三。正如这个示例中提到的 Gossip 协议,我们看到在 Redis、Cassandra 和 Elastic Search 中都是采用同一个协议来完成集群的构建,那么只要知道这一协议,我们就应该在面对与这三个具体工具集群构建相关的面试题时做到游刃有余。

技术知识体系相通性示例之四:Master 可用性

在分布式架构中,Master/Slave 架构是主流架构之一,通常表现为一主多从的服务器部署结构,其中 Master 是整个架构的核心节点。Master/Slave 架构中关于 Master 存在一个普遍性的问题,即如何确保 Master 的高可用性。

通常,我们可以采用热备方案实现 Master 的高可用。在热备方案中,存在一个 Active Master 和若干个 Standby Master,当 Active Master 失效时,从若干个 Standby Master 中挑选一个作为 Active Master。为了能够实现 Active Master 和 Standby Master 之间的无缝切换,一般都会采用共享存储的解决方案,即 Active Master 不断写入信息,Standby Master 不断同步信息,当主备切换时,选中的 Standby Master 保证信息完全同步之后再切换到 Active Master。这里的共享存储实现媒介可以包括 Zookeeper、NFS、HDFS、BookKeeper 和 QJM(Qurom Journal Manager)等,简单起见我们还是基于 Zookeeper 来讨论如何实现 Active Master 和 Standby Master 的切换过程,也将依赖前面 Zookeeper 示例中提到的临时节点和 Watcher 机制。

Master/Slave 架构应用非常广泛,几乎所有的非对等性分布式系统中都采用了这一架构。在本节中,我们将使用大数据技术体系中的YARN架构作为示例来讲解Master的可用性。YARN架构的结构图如下所示,可以看到通过ResourceManager(RM)、NodeManager(NM)、ApplicationMaster(AM)和 Container 之间的分布式调用构成了整体的工作流程。这里 ResourceManager 就是 Master,负责集群中所有资源的统一管理和分配,接收来自各个节点(NodeManager)的资源汇报信息,并把这些信息按照一定的策略分配给各个应用程序。

Yarn 的 ResourceManager 高可用方案采用多个 ResourceManager 并存的方式,其中一个处于 Active 状态,当 Active ResourceManager 失效时,处于 Standby 的 ResourceManager 就会通过竞争选举产生新的 Active 节点。Yarn 就是使用 Zookeeper 来实现这一过程,包括以下三个主要步骤:

http://images.gitbook.cn/87139d60-fcd6-11e7-a304-9f095f94e6f0

1.创建锁节点

所有的 ResourceManager 在启动的时候都会去竞争写一个 Lock 子节点,该节点为临时节点,Zookeeper 能够为我们保证最终只有一个 ResourceManager 能够创建成功。创建成功的那个 ResourceManager 就切换为 Active 状态,没有成功的那些 ResourceManager 则切换为 Standby 状态。

2.注册 Watcher 监听

所有处于 Standby 状态的 ResourceManager 都会对处于 Active 状态的 ResourceManager 节点注册一个节点变更的 Watcher 监听。利用 Watcher 机制,能够快速感知到处于 Active 状态的 ResourceManager 节点的运行情况。

3.主备切换

当 Active 状态的 ResourceManager 出现诸如宕机或重启的异常情况时,其在 ZooKeeper 上连接的客户端会话就会失效,因此该临时节点就会被删除。此时其余各个 Standby 状态的 ResourceManager 就都会接收到来自 ZooKeeper 服务端的 Watcher 事件通知,然后会重复进行步骤1的操作。

上述三个步骤的示意图参考下图。该示意图中展示的工作流程在很多确保 Master 可用性的分布式架构中都有所体现,可以理解为一种通用的知识体系。当我们面对不同的工具或框架时,回想这张示意图能够帮助我们快速掌握背后的原理。

下篇预告

技术体系之间存在相通性,本篇讨论了这种相通性,一方面是对前面各个技术主题的总结,另一方面也是探索新技术领域的起点。从下一篇开始,我们会探讨向架构师转型所需要的一些非技术性因素,首先要讨论的是架构设计的系统工程。

上一篇
下一篇
目录