shan

操作系统-分布式系统

2020-06-17

本文为操作系统导论中关于分布式部分的读书笔记。

分布式系统

在当今的构建分布式系统的过程中,我们关注的主要问题是如何在组件故障时仍能保证系统的工作?

几乎在所有的情况下,通信本身都不能被视为可靠的,位讹误、关闭或无效的链接和机器,以及缺少传入数据包的缓冲区空间,都会导致相同的结果:数据包有时无法到达目的地。为了在这种不可靠的网络上建立可靠的服务,我们必须考虑能够应对数据包丢失的技术。

分布式系统存在的重要问题:

  • 性能
  • 安全
  • 通信

通信基础

现代网络的核心原则是:通信基本是不可靠的。无论是在关于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
2
3
4
interface {
int func1(int arg1);
int func2(int arg1, int arg2);
};

存根生成器接受这样的接口,并生成一些不同的代码片段。对于客户端,生成客户端存根(client stub),其中包含接口中指定的每个函数。希望使用此RPC 服务的客户端程序将链接此客户端存根,调用它以进行RPC。

在内部,客户端存根中的每个函数都执行远程过程调用所需的所有工作。对于客户端,代码只是作为函数调用出现。在内部,func1()的客户端存根中的代码执行此操作:

  • 创建消息缓冲区
  • 将所有需要的信息打包到消息缓冲区中。
  • 将消息发送到目标RPC服务器上
  • 等待回复
  • 解包返回代码和其他参数
  • 返回调用者
  • 解包消息
  • 调用实际函数
  • 打包结果
  • 发送回复

其中有一些重要的问题需要考虑:

  1. 复杂的参数
  2. 关于并发性服务器组织方式

运行时库

构建此类运行时层主要的挑战是:

  • 如何找到远程服务,客户端必须知道运行所需RPC 服务的机器的主机名或IP 地址,以及它正在使用的端口号。然后,协议套件必须提供一种机制,将数据包从系统中的任何其他机器路由到特定地址。
  • RPC构建的协议,一般基于UDP上,这样可以实现更加高效的RPC层。
  • 远程调用耗时较长问题
  • 大参数的过程调用
  • 字节序问题,大端和小端
  • 是否向客户端暴露通信的异步性质,从而实现一些性能优化。
使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章