原书地址:http://tumregels.github.io/Network-Programming-with-Go
如果不知道想要构建什么,是不可能创建一个系统的。而且如果不知道它工作的环境,也同样无法构建。
GUI程序不同于批处理程序;游戏程序不同于商业程序;分布式程序不同于单机程序。
他们都有自己的方法、一般模式和问题,都有各自的常见问题和常见解决方案。
本章讨论分布式系统高层架构层面的一些内容。有多种方式看待这样的系统,而且其中的许多问题已经被解决。
协议层
分布式系统很难!其中涉及到多台计算机,他们必须以某种方式进行连接。编写的程序必须在系统中每台计算机都能正常运行,并且他们必须通力协作才能完成分布式任务。
处理复杂性问题的常用方法是将其划分成一个个更小更简单的分块儿。这些一个个分块儿有自己的结构,但它们也定义了与其他相关分块儿通信的方法。在分布式系统中,这些分块儿称为协议层,他们具有明确定义的功能。所有的分块儿组成了一个栈,每一层只同自己的上一层和下一层进行通信。各层之间的通信由协议定义。
网络通信要求协议涵盖从高级应用程序通信到金属导线通信的所有通信,并且通过协议层封装处理其中的复杂性。
ISO OSI协议
尽管从来没有真正实现,在分布式系统设计的讨论和影响方面OSI(Open Systems Interconnect,开放式系统互联)协议一直是主要的影响因素。该协议通常如下图所示:
每一层的功能:
- 网络层提供交换和路由技术
- 传输层在终端系统之间提供透明的数据传输,负责端到端错误恢复和流控制
- 会话层建立、管理和终止应用程序之间的连接。
- 表示层提供独立于数据表示的差异(例如加密)
- 应用层支持应用程序和最终用户进程
TCP/IP协议
在OSI模型被争论、辩论、争夺时,DARPA互联网研究项目正在忙于构建TCP / IP协议,并取得了巨大的成功,且挟裹资本带来了互联网。这是一个简单得多的协议栈:
一些替代协议
虽然它看起来TCP/IP已经一统天下,但TCP / IP协议并不是唯一存在的协议,从长远来看可能甚至不是最成功的协议。 有许多协议占据重要的位置,例如:
- 火线(Firewire)
- USB(Universal Serial Bus,通用串行总线)
- 蓝牙(Bluetooth)
- WiFi(Wireless Fidelity,基于IEEE 802.11b标准的无线局域网)
许多其他协议的工作仍在积极进行,甚至是一些非常奇怪的协议,如“太空互联网”协议。
本书的重点是TCP / IP,但你应该知道是有其他协议存在的。
网络
网络是一种用于连接主机终端系统的通信系统。连接载体可以是铜线、以太网、光纤或无线电磁波,但这些与我们无关。局域网( LAN )将距离较近的计算机连接在一起,这些计算机通常属于家庭、小型组织或大型组织的一部分。广域网(WAN)将更大物理区域的计算机连接在一起,例如城市之间。还有一些其他类型网络,如城域网、个人区域网,甚至是身体区域网。
互联网是两个或更多不同网络的连接,通常是局域网或广域网。内部网是所有网络都属于一个组织的互联网。
因特网和内部网之间有很大的区别。通常内部网将处于单一的管理控制之下,这将强加一套统一的策略。另一方面,互联网不受单一机构的控制,对不同部分的控制甚至可能不兼容。
这种差异的一个例子是,内部网通常会被少数供应商限制运行特定操作系统标准化版本的计算机上。另一方面,互联网中通常会是不同计算机和操作系统的大杂烩。
本书的技术将适用于互联网。它们对内部网也是有效的,但是在内部网中你还能碰到专用的不可移植系统。
然后是所有互联网(internet)的“母亲”:因特网/互联网(译注:The Internet,注意,在英文中加定冠词The且首字母大写特指i因特网,也就是中国大陆普称的互联网。中国 台@@湾 所称的网路,而internet表示互联网这一类网络。)。这是一个非常非常大的互联网,它把我们连接到谷歌,把我的电脑连接到你的电脑,等等。
网关
网关是用于连接两个或多个网络的实体的通用术语。中继器在物理层面上工作,将信息从一个子网复制到另一个子网。网桥在数据链路层运行,并在网络之间复制帧。路由器在网络层上运行,不仅在网络之间传输信息,而且选取决定路由
数据包封装
无论OSI还是TCP/IP堆栈,其各层之间的通信是通过将数据包从一层发送到下一层,然后最终通过网络完成的。每一层都有关于它自己的层的管理信息。 在发送端,当数据包向下传递时,它通过将报头信息添加到从上面的层接收到的数据包中来实现这一点。 在接收端,随着数据包的向上移动,这些报头会被删除。(译注:发送时就像是一层层的包洋葱,接收时就像一层层剥洋葱)。
例如,TFTP(Trivial File Transfer Protocol,简单文件传输协议)将文件从一台计算机移动到另一台计算机。它使用IP协议之上的UDP协议,IP协议可以通过以太网发送。这看起来像:
通过以太网传输的数据包当然是最下层的那个。
连接模型
为了让两台计算机通信,它们必须设置一条路径,以便在一个会话中至少发送一条消息。这有两种主要模式:
- 面向连接
- 无连接
面向连接
会为会话建立单个连接。沿着连接的双向通信流(译注:双工通信),会话结束时,连接断开。这类似于电话交谈,TCP就是一个例子。
无连接
在无连接系统中,消息彼此独立地发送。普通邮件就是一个例子。无连接消息可能会无序到达。一个例子是IP协议。
面向连接的传输可以建立在无连接的传输之上——IP上的TCP。无连接传输可以建立在面向连接的传输之上——基于TCP的HTTP。
这也有一些有所不同。例如,会话可能强制消息到达,但可能不能保证它们按照发送的顺序到达。然而,这两个是最常见的。
通信模型
消息传递
一些非过程性语言是基于消息传递原则构建的。并发语言经常使用这种机制,最著名的例子可能是Unix管道。Unix管道是一个字节管道,但没有固有的限制:微软的PowerShell可以沿其管道发送对象,Parlog等并发语言可以在并发进程之间的消息中发送任意逻辑数据结构。
消息传递是分布式系统的一种基本机制。 建立连接并向其输送一些数据。 在另一端,弄清楚消息是什么并对其做出响应,可能会发回消息。 如下图所示:
低层事件驱动系统,如X窗口系统,以与此种类似的方式运行:等待来自用户的消息(鼠标单击等),对其进行解码并对其进行操作。
更高级别的事件驱动系统假设这个解码已经由底层系统完成,然后事件被分派到一个适当的对象,比如ButtonPress handler。这也可以在分布式消息传递系统中完成,由此通过网络接收到的消息被部分解码并分派给适当的handler。
远程过程调用
在任何系统中,都有信息和流量控制从系统的一部分转移到另一部分。在面向过程的语言中,这可能包括过程调用,其中信息被放置在调用堆栈中,然后控制流被转移到程序的另一部分。
即使使用过程调用,也会有一些变化。 为了控制从程序可执行代码的一部分转移到另一部分。代码有可能是静态链接的。由于越来越多地使用库例程,在控制权转移到独立的代码段动态链接库( DLL-s )中拥有这样的代码已经变得司空见惯。
DLLs与调用代码在同一台机器上运行。将控制转移到另一台机器上运行的过程在概念上是一个简单的步骤。然而这其中的机制并不简单!这种控制模型驱动了“远程过程调用”(RPC),这将在后面的章节中详细讨论。如图所示:
微软在从16位应用程序到32位应用程序的转换过程中发明了一种名为“轻量级远程过程调用”的历史怪物。16位应用程序可能需要将数据传输到同一台计算机上的32位应用程序。由于没有网络,这使得它非常轻巧!但是在数据表示和转换方面,它还有许多RPC系统的其他问题。
分布式计算模型
在最高层,我们可以考虑分布式系统组件的等价性或不等价性。最常见的情况是不对称的:客户机向服务器发送请求,服务器作出响应。这是一个C/S系统。
如果两个组件都是等效的,都能够启动和响应消息,那么我们就有了一个点对点系统(peer-to-peer,p2pm对等系统)。请注意,这是一个逻辑分类:一个对等点可能是16000核心主机,另一个可能是移动电话。但如果他们都能做出类似的行为,那么他们就是对等的。
第三种模型是所谓的过滤器。在这里,一个组件将信息传递给另一个组件,后者在将信息传递给第三个组件之前对其进行修改。这是一种相当常见的模型:例如,中间组件以SQL记录的形式从数据库获取信息,并将其转换为第三个组件(可能是浏览器)的HTML表。
C/S系统
客户端/服务器系统的另一个观点是:
C/S应用
第三种观点是:
服务器组织分布
C/S系统并不简单。基本模型是单客户机、单服务器:
但是你也可以有多个客户端,单个服务器:
在这种情况下,主服务器接收请求之后将它们传递给其他服务器来处理,而不是一次处理一个请求。当客户端是可以并发时,这是一种常见的模型。
还有单个客户机和多个服务器:
当服务器需要充当其他服务器的客户端时,这种情况经常发生,例如业务逻辑服务器从数据库服务器获取信息。
当然,也可以有多个客户机和多个服务器。
组件组织分布
分解许多应用程序的一种简单但有效的方法是将它们视为由三部分组成:
- 表现组件
- 应用逻辑
- 数据存取
表现组件负责与用户交互,包括显示数据和收集输入。它可能是一个带有按钮、列表、菜单等的现代GUI界面,也可能是一个较旧的命令行风格的界面,用于提问和获取答案。在这个概念层次上,细节并不重要。
应用程序逻辑负责解释用户的响应、应用业务规则、准备查询和管理来自组件的响应。
数据存储组件负责存储和检索数据。这通常会通过数据库进行,但也不一定。
Gartner分类
基于这种应用程序的三重分解, Gartner考虑了组件在客户端-服务器系统中的分布方式, 他们提出了五种模型:
样例:分布式数据库
Gartner 分类:1
现代手机就是一个很好的例子:由于内存有限,它们可能在本地存储数据库的一小部分,这样它们通常可以快速响应。但是,如果需要的数据不是本地保存的数据,那么可能会向远程数据库发出请求以获取这些额外的数据。
谷歌地图是另一个很好的例子。所有地图都位于Google的服务器上。当用户请求时,“附近”的地图也会被下载到浏览器中的一个小数据库中。当用户稍微移动地图时,所需的额外数据已经在本地存储中,可以快速响应。
样例:网络文件服务
Gartner 分类:2允许远程客户端访问共享文件系统
这种系统有很多例子:NFS,Microsoft共享,DCE等等。
样例:网页
Gartner 分类 3的一个例子是使用Java applet的网页。这是一个分布式超文本系统,具有许多其他机制。
样例:终端仿真
Gartner 分类 4的一个例子是终端模拟。它允许远程系统充当本地系统上的普通终端。
Telnet是最常见的例子。
样例:Expect
Expect是Gartner分类5的一个新颖示例。它就像一个经典系统(比如命令行界面)的包装器。它围绕此构建一个X Window接口,以便用户与GUI交互,然后GUI又与命令行界面交互。
样例:X窗口系统
X Window系统本身就是Gartner分类 5的一个例子。应用程序执行诸如DrawLine之类的GUI调用,但这些调用不是直接处理,而是传递给X Window服务器进行渲染。这解耦了窗口的应用程序视图和窗口的显示视图。
三层模型
当然,如果有两层,那么可以有三层、四层或更多层。图中显示了三层中的一些可能性:
现代Web就是其中最右边的一个很好的例子。后端由数据库组成,通常运行存储过程来保存一些数据库逻辑。中间层是一个HTTP服务器,例如Apache运行PHP脚本(或Ruby on Rails,或JSP页面等)。这将管理一些逻辑,并且将数据,如HTML页面存储在本地。 前端是一个浏览器,在一些Javascript的控制下显示页面。在HTML 5中,前端也可能有一个本地数据库。
重 V.S. 轻
组件的一个常见标签就是“重”和”轻“。重型组件占用大量内存并进行复杂处理。另一方面,轻型组件对这两种情况都几乎不起作用。似乎没有任何“正常”大小的成分,只有重和轻。
重和轻是一个相对的概念。浏览器通常被贴上“瘦”的标签,因为“它们所做的只是显示网页”。我Linux上的Firefox占用了将近1 / 2千兆字节的内存,我认为这一点都不小!
中间件
中间件模型
中间件是连接分布式系统组件的技术“粘合剂/胶水”。中间件模型是
中间件
中间件的组件包括:
- 包括像TCP/IP这样的网络服务
- The middleware layer is application-independent s/w using the network services.
- 中间件的例子有:DCE、RPC、Corba
- 中间件可能只执行一个功能(如RPC)或多个功能(如DCE)
中间件样例
中间件的例子包括:
- 终端模拟器,文件传输,电子邮件等基础服务
- RPC等基本服务
- 综合服务,如DCE,网络O / S
- 分布式对象服务,如CORBA,OLE / ActiveX
- 移动对象服务,如RMI、Jini
- 万维网
中间件功能
中间件的功能包括:
- 在不同计算机上启动/初始化进程
- 会话管理
- 允许客户端定位服务器的目录服务
- 远程数据访问
- 允许服务器处理多个客户端的并发控制
- 安全和完整性
- 监控
- 终止本地和远程进程
持续处理过程
Gartner模型基于将应用程序分解为表示组件、应用程序逻辑和数据处理组件。更细粒度的分解是:
故障点
分布式应用程序运行在复杂的环境中。这使得它们比单个计算机上的独立应用程序更容易出现故障。 失败的原因包括:
- 应用程序的客户端可能会崩溃
- 客户端系统可能存在硬件问题
- 客户端的网卡可能出现故障
- 网络争用可能导致超时
- 可能存在网络地址冲突
- 路由器等网络元件可能会出现故障
- 传输错误可能会丢失消息
- 客户端和服务器版本可能不兼容
- 服务器的网卡可能会出现故障
- 服务器系统可能有硬件故障
- 服务器软件可能会崩溃
- 服务器的数据库可能损坏
应用程序在设计时必须考虑到这些可能发生的故障。如果系统的其他部分发生故障,一个组件执行的任何操作都必须是可恢复的。需要使用诸如事务和连续错误检查等技术来避免错误。
验收因素
- 可靠性
- 性能
- 响应
- 可伸缩性
- 容量
- 安全
透明性
分布式系统的“圣杯”将提供以下内容:
- 访问透明
- 位置透明
- 迁移透明
- 复制透明
- 并发透明
- 伸缩透明
- 性能透明
- 鼓掌透明
分布式计算的八个谬论
SSun Microsystems是一家在分布式系统中完成大部分早期工作的公司,甚至还有一句口号“网络就是计算机”。 根据他们多年的经验,Sun公司的一些科学家提出了以下常见的谬误:
- 网络是可靠的
- 延迟为零
- 带宽是无限的
- 网络是安全的
- 拓扑是不变的
- 只有一个管理员
- 传输成本为零
- 网络是同质的(homogeneous)
其中许多直接影响到网络编程。例如,大多数远程过程调用系统的设计都基于这样一个前提:网络是可靠的,因此远程过程调用的行为将与本地调用相同。零延迟和无限带宽的谬误也导致RPC调用的持续时间与本地调用的持续时间相同的假设,而它们是数量级变慢的。
对这些谬误的认识导致Java的RMI(远程方法调用)模型要求每个RPC调用都可能抛出RemoteException。这迫使程序员至少认识到网络错误的可能性,并提醒他们不能期待与本地调用有相同的速度。
请发表评论