本文为操作系统导论中关于分布式部分的读书笔记。
分布式系统
在当今的构建分布式系统的过程中,我们关注的主要问题是如何在组件故障时仍能保证系统的工作?
几乎在所有的情况下,通信本身都不能被视为可靠的,位讹误、关闭或无效的链接和机器,以及缺少传入数据包的缓冲区空间,都会导致相同的结果:数据包有时无法到达目的地。为了在这种不可靠的网络上建立可靠的服务,我们必须考虑能够应对数据包丢失的技术。
分布式系统存在的重要问题:
- 性能
- 安全
- 通信
通信基础
现代网络的核心原则是:通信基本是不可靠的。无论是在关于Internet,还是在局域高速网络中,数据报都会经常丢失、损坏或者无法到达目的地。
其丢包的原因右很多:
- 在传输过程中,由于电气或者其他类似的原因,其某些位会被翻转。
- 系统中某个元素会以某种方式损坏,或其他方式无法工作(如数据包路由器,或远程主机)。
- 网络电缆被意外切断。
- 由于网络交换机、路由器或者中端节点内缺少缓冲,从而导致包丢失。
即使我们保证所有链路都能正常工作,并且系统中的所有组件都按预期启动并运行,仍可能出现丢包现象,如路由器缓冲不足,导致路由器选择丢弃数据包,那么该怎么处理丢包现象。
不可靠的通信层
简单的方法是不对丢包进行处理。由于某些应用程序知道如何处理丢包,因此让它们用基本的不可靠消息传递层进行通信有时是很有用的,这是端到端的论点。如UDP,它是一个不可靠通信层的例子,如果遇到丢包,就会遇到数据布包无法到达的情况,但UDP中包含校验和,可以检测某些数据包的损坏。
可靠的通信层
为了构建可靠的通信层,我们采取了一些新的措施来处理数据包丢失。
如何让发送方知道接收方实际接收到了消息?使用的技术是确认(acknowledge),简称ack。发送方向接收方发送消息,接收方然后发回短消息确认收到。当发送发收到该消息的确认时,可以放心接收方收到了消息,但是,如果没有收到确认时,通过超时(timeout),发送方断定该消息已经丢失,然后重新发送(retry)。这种称为超时/重试机制(timeout/retry)。
同时我们为了保证不会收到相同的消息,还希望接收方每个消息只接收一次(exactly once)。为了让接收方能够检测重复消息传输,发送方必须以某种独特的方式标识每个消息,并且接收方需要以某种方式来追踪它是否已经看到过每个消息。当接收方看到重复传输时,他只是简单的响应消息,但不会将消息传递给接收数据的应用程序。如为每条顺序生成唯一ID,但成本非常高。或者利用一种叫顺序计数器(sequence counter)的机制。如 TCP/IP 协议。
通信抽象
构建分布式系统时,应该使用什么抽象通信?
分布式共享内存(Distribution Shared Memory,DSM)系统使不同的机器上的进程能够共享一个大的虚拟地址空间。这种抽象将分布式计算变成貌似多线程应用程序,唯一的区别是这些线程运行在不同的机器上。其最大的问题是,如何处理故障和访问内存的性能。
远程过程调用(Remote Procedure Call,RPC)是编程语言方面的抽象,其目标是:使在远程机器上执行代码的过程像调用本地函数一样简单直接。因此对于客户端来说,进行一个过程调用,并在一段时间后返回结果。服务器只是定义了一些它希望导出的例程。其与由RPC系统处理,RPC系统通常有两部分组成:存根生成器(stub generator,或称为协议编译器,protocol compiler)和运行时库(run-time library)。
存根生成器
其工作十分简单:通过自动化,消除将函数参数和结果打包成消息的痛苦,其好处是通过设计避免了手工编写此类代码时出现的简单错误。其也可以优化此类代码,从而提高性能。
这种编译器的输入就是服务器希望导出到客户端的一组调用。从概念上讲,它可能就像这样简单:
1 | interface { |
存根生成器接受这样的接口,并生成一些不同的代码片段。对于客户端,生成客户端存根(client stub),其中包含接口中指定的每个函数。希望使用此RPC 服务的客户端程序将链接此客户端存根,调用它以进行RPC。
在内部,客户端存根中的每个函数都执行远程过程调用所需的所有工作。对于客户端,代码只是作为函数调用出现。在内部,func1()的客户端存根中的代码执行此操作:
- 创建消息缓冲区
- 将所有需要的信息打包到消息缓冲区中。
- 将消息发送到目标RPC服务器上
- 等待回复
- 解包返回代码和其他参数
- 返回调用者
- 解包消息
- 调用实际函数
- 打包结果
- 发送回复
其中有一些重要的问题需要考虑:
- 复杂的参数
- 关于并发性服务器组织方式
运行时库
构建此类运行时层主要的挑战是:
- 如何找到远程服务,客户端必须知道运行所需RPC 服务的机器的主机名或IP 地址,以及它正在使用的端口号。然后,协议套件必须提供一种机制,将数据包从系统中的任何其他机器路由到特定地址。
- RPC构建的协议,一般基于UDP上,这样可以实现更加高效的RPC层。
- 远程调用耗时较长问题
- 大参数的过程调用
- 字节序问题,大端和小端
- 是否向客户端暴露通信的异步性质,从而实现一些性能优化。
若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏
扫描二维码,分享此文章