分布式系统概念和设计
分布式对象和远程调用
能够接收远程方法调用的对象称为远程对象,远程对象实现一个远程接口。
调用者和被调用对象分别存在不同的失败可能性,RMI和本地调用有不同的语义。
-
中间件
在进程和消息传递等基本构造模块之上提供编程模型的软件为中间件。中间层使用基于进程通信消息的协议提供更高级的抽象,如远程调用和事件。重要作用是提供位置透明性等无关性。
-
位置透明性
在RPC中,调用某一个过程的客户不能判断该过程是运行在同一个进程还是运行在不同的进程中或者运行在其他的机器上。客户端不需要知道位置。
-
通信协议
支持中间抽象的协议与下层传输协议无关,请求-应答协议可以在多种底层通信协议上完成.
-
计算机硬件
用于外部数据表示的两种公认标准。消息解码和编码,隐藏了由于硬件体系结构差异的不同。
-
操作系统
中间件提供的更高级的抽象和基本的操作系统无关。
接口
编程模型中可以把一个程序组成一系列能彼此通信的模块。
模块之间的通信可以采用模块间的过程调用或者直接访问模块中的一个变量。
为了控制模块之间可能的交互,必须为每一个模块定义显示的接口,模块接口指定了可供其他模块访问过程和变量。
模块实现后就隐藏了除通过其接口可获得的信息之外的所有信息。
模块接口保持相同,实现的改变就不会影响模块的使用者。
分布式系统接口
在分布式系统中,模块可能运行在彼此独立的进程中或者机器。
进程间的变量共享不可实现,因此定义RPC & RMI的模块的接口不能指定对变量的直接访问。
当过程及其调用者分布在不同的进程中,用于本地调用的值调用和引用调用等参数传递机制就不在适用。
在分布式程序中,模块接口中的过程或方法的规约把参数描述为输入和输出。
输入参数被传递给远程模块,先通过请求消息发送参数的值,然后将这些值提供给服务器作为操作执行的参数。
输出参数在应答消息中返回,可以作为调用的结果或者替换调用环境中的值。
当一个消息即输入又输出,则必须包含在请求和应答中。
分布式接口不适合传递指针,也不能返回。
- 服务接口:客户-服务模式下,每个服务提供一系列客户使用的过程(例如,文件服务器会提供读写文件的过程),术语服务接口是指由服务器提供的过程规约,定义过程的输入和输出参数类型。
- 远程接口:在分布式对象模型中,远程接口指定了可供其他进程中对象进行调用的对象的方法,定义了每个方法的输入输出参数的类型。远程的接口可以把对象作为参数传递或者作为方法的结果传递,也可以传递对象的引用。
- 引用的概念不能和指针混淆,指针是指特定的内存地址。
接口定义语言 IDL
RMI机制可以集成到某个编程语言中,如果语言包含了适当的定义接口的表示方法,就允许将输入和输出参数映射成该语言中正常使用的参数。
分布式对象间通信
对象模型
面向对象编程方式是将数据和方法组织到对象中,对象之间通信的方法就是调用方法,传递参数和接收结果。
对象可以封装数据和方法的代码。面向对象语言允许直接定义共享变量对象。
在分布式系统中数据只能通过其方法提供。
对象引用
访问一个对象可以通过对象的引用,调用一个对象需要提供引用和方法名和需要传递的参数。
一个变量也可能只是拥有一个对象的引用。
对象引用可以作为参数,赋值或返回结果进行。
接口
接口在无需规定其实现的情况下提供了一系列标记方法的定义(参数类型,返回值,异常等)
如果对象包含了实现接口方法的代码,对象必须提供特定接口进行调用。
类可以实现多个接口或者一组接口,
接口提供参数类型,异常类型,变量类型和返回值类型,但不提供构造函数。
在 Java 中,接口是一种特殊的抽象类,它只包含常量和抽象方法。接口没有构造函数的原因是因为接口中的方法都是抽象的,没有具体的实现,因此不需要构造函数来初始化实例变量。
接口的主要作用是定义一组公共的方法,让不同的类实现这些方法来达到相同的目的。接口中定义的方法都是抽象的,没有实现,因此接口不能直接被实例化。只有实现了接口的类才能被实例化,而且必须实现接口中的所有方法。
因为接口没有实例变量,所以没有必要在接口中定义构造函数。接口的实现类必须提供一个公共的默认构造函数,用于实例化对象。当然,实现类也可以定义其他的构造函数,但是这些构造函数不能被接口调用。
总之,接口是一种特殊的抽象类,它只包含常量和抽象方法,没有实例变量,因此不需要构造函数来初始化实例变量。
在JDK17
新增sealed
和
permits的作用是为了限制继承层次结构,防止继承层次结构变得过于复杂和不可控。通过使用
sealed和
permits`,可以明确地定义哪些类可以继承一个受限制的类,使得继承层次结构更加清晰和可维护。
动作
在OOP中,动作是由对象启动的,一个对象发出调用到另一个对象。
发出调用所需要的信息比如参数的传递。
接受者采用适当的执行方法,然后将控制返回给调用对象,如果有返回值则会返回一个结果:
- 执行动作可能改变接受者的状态
- 可能会触发其他对象的链接调用,对象中调用链
一连串的调用最终返回,其中并没有解释异常的发生。
怀疑自己是不是合格的程序设计者就出于此处!
异常
异常是一种在不增加代码复杂度的情况下处理程序因未预测到的异常情况下处理问题的手段。
设计方法的时候提供了一系列的方法异常标题,调用者方法需要自己处理这个问题。
如果出现异常意味着程序的控制将转移到异常对象中,控制不会再返回抛出异常的地方。
无用单元回收
GC算法实现处理已经不在引用对象的占用空间。
分布式对象
对象的状态由实例变量值组成。
在基于对象的模型中,程序的模型被划分为几个独立的部分,每个部分都与一个对象关联。
基于对象的程序在逻辑上是分区的,在分布式系统中对象可以通过分布在不同的物理机机器进程中。
分布式对象模型
每个进程包含若干的对象,有些能接收远程和本地调用。
不同进程的对象调用是远程调用,同一进程中的调用时本地调用。
分布式对象模型的设计核心:
- 远程对象引用:如果其他对象能够访问远程对象的某个对象的引用,那么就可以调用这个远程对象上的方法。
- 远程接口:每个远程对象都有一个远程对象的接口,由该接口描述哪些方法提供给远程调用的。
分布式对象系统中的动作
在分布式系统中涉及到方法调用链的对象可能处于不同的进程中或者不同的计算机中。
当调用跨越了计算机和进程的边界的时候,就需要使用RMI,使用RMI的前提是,对象的远程引用必须是可用的。
分布式系统的无用单元回收
分布式系统中的无用单元回收是指在系统运行过程中,由于系统中的各个节点会产生大量的无用单元(例如无用的对象、文件等),这些无用单元会占用大量的系统资源,严重影响系统性能和稳定性。为了解决这个问题,需要对无用单元进行回收和清理。
分布式系统的无用单元回收一般分为两种方式:
- 基于引用计数的垃圾回收
这种方式是在系统中维护一个引用计数器,记录每个对象被引用的次数。当对象的引用计数为0时,就可以将其回收。这种方式在分布式系统中的应用比较广泛,但是由于需要维护引用计数,可能会影响系统性能。
- 基于标记-清除的垃圾回收
这种方式是在系统中进行标记和清除操作,首先标记所有可以访问到的对象,然后清除所有未被标记的对象。这种方式对于分布式系统来说更加适用,因为它可以跨越多个节点进行回收,同时也可以避免引用计数带来的性能问题。
分布式系统异常
分布式系统的异常处理机制通常包括以下几个方面:
- 异常检测:分布式系统需要实时检测异常,包括节点故障、网络故障等。可以通过心跳检测、超时机制、重试机制等方法来实现。
- 异常处理:发生异常时,需要快速响应并进行相应的处理,比如节点故障时需要重新分配任务或启动备份节点等。异常处理的具体方式根据系统的不同而有所不同。
- 异常恢复:当分布式系统发生异常时,需要能够快速恢复正常状态,比如自动切换到备份节点、重新分配任务等。异常恢复需要考虑到数据的一致性和可靠性。
- 日志记录:分布式系统需要记录异常日志,以便于后续的分析和调试。日志记录可以帮助开发人员快速定位问题并进行解决。
设计问题
RMI调用语义
- 引用透明性:RMI调用应该像本地方法调用一样透明,调用者不需要关心方法调用是在本地还是在远程执行。
- 远程对象的生命周期管理:RMI提供了一种机制来管理远程对象的生命周期,确保远程对象的创建和销毁都能正确处理。
- 参数传递和返回值:RMI支持在远程调用中传递和返回各种数据类型的参数,包括Java对象。
- 异常处理:RMI支持在远程调用期间捕获和处理异常,可以抛出本地异常和远程异常。
- 安全性:RMI提供了一些安全机制来保护远程调用的安全性,包括身份验证和授权等。
-
或许调用语义:调用者不能判断一个远程方法是已经执行过一次还是从来没有执行过。
- 可以偶然接收调用失败的应用可用——OPENAI
-
至少一次调用语义:调用者或许可以接收到一次执行结果,调用者知道至少执行了一次,或者接收到一个异常通知执行结果。通过重发消息实现至少一次语义。
-
最多一次调用语义:调用者接收到一个返回结果,调用者知道方法执行了一次或者接收一个异常告知方法执行异常。
- 要么执行一次,要么没有执行。
实现
通信模块
两个相互协作的通信模块实现客户和服务器之间的请求-应答的信息传递。
通信模块:消息类型,请求id,请求对象的远程引用。
远程引用模块
负责翻译本地和远程对象引用以及创建远程对象引用。
每个进程的都保存远程对象引用表,记录着该进程中的对象应用和远程系统的对象引用。
该进程拥有的所有的远程对象的引用表项。
每个本地代理的表项。
当远程对象第一次作为结果或者参数传递,要求远程引用模块创建一个远程对象引用,并将其保存在表中。
当远程对象引用随请求或应答消息到达,远程引用模块提供对应的本地对象的应用。
- 可以指向一个代理或者一个其他的对象
- 如果不在表中,RMI就会创建一个新的远程对象引用,并保存。
RMI软件
应用层对象和通信模块,远程引用模块之间的软件组成。
-
代理:表现的和远程对象一样的的调用,远程客户调用透明,隐藏其中和远程对象交互的细节。
- 代理中的每个方法会把一个目标对象的引用,自身的id和请求的参数编码进请求消息,一起发送到目标。等待应答,然后解码将结果返回给调用者。
-
调度程序:服务器对表示远程对象的每个类都有一个调度程序和骨架;
- 调度程序接收通信模块传递的消息,通过ID找到骨架中对象的方法执行。
- 调度程序和代理程序中的ID相同。
-
骨架:远程对象中用于实现接口调用的方法。
- 骨架方法解码请求中的参数,找到远程对象中执行的方法。
- 等待调用完成,将结果或任何异常编码进入应答消息,发送给代理方法。
事件和通知
基本原因是希望描述一个对象能够对另一个对象发生的改变做出反应。
本质上是异步的,并且取决于接受者的对于该时间的响应方式。
发布-订阅模型是在分布式系统中对本地事件的一种扩展模型。
生成事件的对象发布事件的类型,其他订阅了该事件类型的观察者接收该类型的事件。
- 异构性:当事件通知作为一种分布式系统对象间的通信方式,分布式组件原本不是设计用于交互的,依然可以简单的集成在一起工作。主要是生成事件增加新的事件类型,订阅者处理该类型的时间通知。
- 异步性:生成事件异步将消息传输给所有的订阅者,避免和每个订阅者同步通信,也太复杂处理了。
- 订阅者和经历事件是去耦合。
分布式事件通知的参与者
- 兴趣对象:经历操作被改变状态的对象。该状态改变可能引起其他对象感兴趣。
- 事件:发生在兴趣对象上,作为方法执行完成的结果。
- 通知:包含事件类型和属性(兴趣对象标识符,调用的方法,发生时间或者一个序列号)
- 订阅者:一个对象,订阅另一个对象的某些类型的事件,并接收关于这些事件的通知。
- 观察者对象:将兴趣对象和其订阅者之间的关系去耦合。
- 一个兴趣对象可能具有不同兴趣的不同订阅者。
- 一个或多个观察者对象可以介入到兴趣对象和订阅对象之间。
- 发布者:声明将要生产某种类型的事件。发布者可能是兴趣对象或者一个观察者。
传递语义
为通知提供不同的传输抱保障,基于不用的网络通信协议可以完成。
观察者的作用
可以从兴趣对象传输通知到接受者,期间可能经历多个进程,经历多个不同观察者的进程中。
- 转发:观察者可以作为一个或多个兴趣对象完成通知转发工作。
- 过滤:based-conditon handle the message
- 事件模式:当对象订阅一个兴趣对象上的事件,可以说明感兴趣事件的模式。
- 通知邮箱:扮演订阅者的角色暂时接收消息。