Ada与共享对象层的绑定:技术解析与实践
1. 分层方法
在分布式和异构环境中,需要一种技术来屏蔽分布式对象的属性,如位置、复制、表示和持久性,使程序员无需关注这些细节。常见的方法有消息传递和虚拟共享内存(VSM)两种范式。
VSM并非要取代或排除像CORBA或DCOM这样的消息传递架构,而是作为一个额外的层,为程序员提供增强的机制。对于Ada语言而言,与VSM绑定能增加其功能,便于开发分布式应用,具体体现在以下方面:
-
通信、数据共享和持久性
:无需使用标准Ada来实现,可交由VSM系统进行协调,该系统能将多种语言和系统架构整合在一起。
-
对称性
:VSM的对称属性很好地满足了应用的实际需求。特定子任务可以使用最适合的语言来实现,例如安全关键部分的核心开发使用Ada,而GUI开发使用Java。
2. 共享对象
分布式系统的通信和同步存在两种范式:消息传递和虚拟共享内存。
2.1 消息传递
消息传递是经典且广泛应用的方法。进程通过显式的(异步)同步消息发送和接收进行通信,远程过程调用(RPC)就是一种双向通信。分布式对象系统如CORBA和DCOM是消息传递范式的高级抽象,允许在远程站点调用对象方法,同样是双向通信模式。Ada的分布式系统附件也倾向于消息传递。
然而,消息传递存在一些缺点:
-
容错性
:应用程序需要显式实现容错机制。
-
网络站点管理
:添加或移除分布式应用网络中的站点需要明确考虑。
-
数据访问成本
:系统不缓存数据字段,每次访问都需要进行昂贵的远程过程调用,即使是之前使用过的数据字段也需重新获取。
-
性能瓶颈
:由于缺乏复制和缓存支持,通常构建分层的客户端/服务器结构,这会导致性能和可用性方面的瓶颈。
以下是消息传递范式的简单示意:
graph LR
A[客户端对象] -->|服务调用| B[服务对象]
B -->|结果返回| A
2.2 虚拟共享内存
虚拟共享内存提供了比消息传递更高层次的抽象。它为所有参与的分布式和并行执行的进程提供了一个共同的共享对象空间,使它们具有一致的视图。共享数据对象用于并行和分布式进程之间的通信和同步,将本地内存扩展到所有进程运行的站点的内存。
VSM的优点如下:
-
隐藏网络异构性
:自然地隐藏了网络中的异构部分,使程序员无需关注。
-
减轻开发负担
:共享数据对象使应用程序开发人员无需处理缓存和复制问题。
-
对称架构设计
:允许设计对称的应用架构,避免了客户端/服务器瓶颈。
虚拟共享内存范式的示意如下:
graph LR
subgraph Site A
P1[进程1]
O1[共享数据对象1]
end
subgraph Site B
P2[进程2]
O2[共享数据对象2]
end
subgraph Site C
P3[进程3]
O3[共享数据对象3]
end
subgraph Site D
P4[进程4]
O4[共享数据对象4]
end
P1 -->|使用| O1
P2 -->|使用| O2
P3 -->|使用| O3
P4 -->|使用| O4
3. CORSO概念
CORSO是一个用于开发健壮和并行应用的分层软件组件,支持虚拟共享内存范式。它由维也纳技术大学计算机语言研究所开发,目前由Tecco Coordination Systems商业化提供。
CORSO的特点如下:
-
数据对象共享
:共享粒度是数据对象,而非整个内存页面,适用于各种分布式应用场景。
-
复杂协调模式
:通过先进的事务和进程模型支持对共享对象的复杂协调模式。事务用于协调对共享对象的访问并确保其持久性,进程用于处理并发和并行性,并定义恢复检查点。
-
满足异构需求
:满足分布式应用的异构性要求,屏蔽位置、迁移、复制、访问、事务、故障、表示和持久性等方面的要求。
-
支持多语言
:通过与C、C++、Java、Visual Basic、Oracle的Developer 2000和Ada的语言绑定,支持面向对象的组件编程,充分利用组件重用的概念。
-
替代与增强技术
:是实现分布式系统的替代技术,也可用于增强现有的客户端/服务器技术。
4. CORSO的技术方面
4.1 CORSO架构
分布式CORSO应用由本地软件系统(LSYS)组成,应用进程在其上运行。这些应用进程可使用CORSO提供的功能,被称为CORSO进程,它们使用CORSO支持绑定的编程语言编写。一种编程语言“L”扩展了CORSO功能后被称为“L&Co”,目前支持C&Co、C++&Co、Java&Co、VisualBasic&Co和Developer2000&Co,我们正在添加Ada&Co。
具体编程语言支持情况如下表所示:
| 编程语言 | 扩展后的名称 |
| ---- | ---- |
| C | C&Co |
| C++ | C++&Co |
| Java | Java&Co |
| Visual Basic | VisualBasic&Co |
| Oracle’s Developer 2000 | Developer2000&Co |
| Ada | Ada&Co(待添加) |
4.2 接口定义语言(IDL)
CORSO对象可以在不同语言范式和异构平台之间共享,为确保数据在每个站点都能被正确理解,内部使用了接口定义语言(IDL)。协调内核会自动解释和转换从其他协调内核接收到的所有信息。如果LSYS连接到使用不同数据类型格式的远程协调内核,它也会自动转换这些数据,使应用程序在所有平台上都具有可移植性。
IDL支持以下基本类型的数据项:
-
Integer
:32位整数值。
-
Character
:8位字节值。
-
String
:以0结尾的字符串。
-
Raw
:可能包含0字符的字符串。
-
Object identification (OID)
:虚拟共享内存中对象的唯一引用,将本地内存中的指针泛化为OIDs。
-
Structure
:数据结构,可选择命名,由n个成员组成,每个成员都是一个IDL项。
-
Stream
:特别适用于通信目的的数据类型。内部是一个由两个组件组成的结构,第一个组件称为头,包含用户数据;第二个组件称为尾,其中的单个OID作为链接到流中的下一个元素。流可以看作是无限的协调结构,类似于“UNIX管道”,用于从生产者向消费者传递对象。
4.3 对象共享
IDL用于访问和操作CORSO对象。由于这些对象通常被缓存,与简单的双向通信相比,大大提高了性能。事务概念确保了与全局虚拟共享内存的一致性。可以通过选择由共享和嵌套对象组成的适当协调数据结构来定义任意通信模式。
CORSO提供了不同的缓存策略,可根据对象的实际访问频率进行选择。例如,如果一个很少写入的对象被(部分)更改,它会自动传播到所有当前读者的缓存中。
CORSO事务的一种变体是子事务,允许事务嵌套。每个分布式任务都可以根据ACID属性(原子性、一致性、隔离性、持久性)进行事务控制。可以将一个大事务细分为多个子事务,事务场景可以分布在多个站点,由所谓的事务依赖进程负责处理远程子事务。成功的子事务可以独立于所有包含它的事务提供结果,因此包含事务的正常回滚不再可行。为保证事务的原子性,必须至少在语义上补偿子事务的影响,可以为此定义特殊的补偿操作。
事务的另一个方面是同步CORSO进程。CORSO风格的进程间通信基于对共享对象的对称事务。CORSO流对象可用于实现类似于队列或管道的通信。在事务控制下的写操作与同步读操作是实现这种通信的主要构造。CORSO进程还可以用于模拟远程过程调用:一个共享对象包含输入数据、远程站点的方法标识以及方法调用的结果。
5. Ada与CORSO的绑定
实现与CORSO的绑定的主要任务是用Ada构建一个LSYS,该任务可细分为以下几个步骤:
1.
声明符合Ada的IDL数据类型
:定义一个抽象的带标签的CORSO基类型,从中派生IDL类型。
2.
实现这些类型的编组和解组操作
:为所有IDL类型实现读和写属性。目前不支持输入和输出属性,因为CORSO IDL类型是基本类型,无法从Ada的输入和输出属性的高级表示问题中获益。
3.
提供Ada与CORSO进程和事务的接口
:通过基于C&Co的“薄绑定”实现,借助Intermetrics开发的c2ada工具半自动化生成,但不直接呈现给程序员。
5.1 IDL类型和编组
下面通过一个示例(CoKeInt类型)来说明CORSO IDL类型在Ada绑定中的实现。
-- Program 1 Package Coke
package CoKe is
type Comm_type is (const, var, stream);
end Coke;
上述代码给出了Ada CoKe库树的根,并包含了CORSO对象的通信类型声明。
const
表示对象只写一次,但可以任意多次读取;
var
表示对象可以任意多次读写;
stream
允许以类似于“UNIX管道”的方式从生产者向消费者传递对象。
-- Program 2 Package Coke.Base.Attribute
with Ada.Streams, CoKe.Base.Streams;
package CoKe.Base.Attribute is
type CoKeBaseAttribute is abstract new CokeBase with private;
private
type CoKeBaseAttribute is abstract new CokeBase with
record
Strm: CoKe.Base.Streams.CoKe_Stream_AD;
end record;
procedure Read(
Stream: access Ada.Streams.Root_Stream_Type'CLASS;
Item: out CoKeBaseAttribute);
for CoKeBaseAttribute'Read use Read;
procedure Write(
Stream: access Ada.Streams.Root_Stream_Type'CLASS;
Item: in CoKeBaseAttribute);
for CoKeBaseAttribute'Write use Write;
end CoKe.Base.Attribute;
CoKeBaseAttribute
是所有IDL类型的基类型。注意私有部分的
Read
和
Write
过程,这些过程以及为其他IDL类型定义的类似过程执行编组和解组操作,使Ada与CORSO绑定的用户可以通过Ada的读和写属性以相当透明的方式对共享内存池进行读写操作。
-- Program 3 Package Coke.Base.Attribute.CoKeInt
generic
type Int is range <>;
package CoKe.Base.Attribute.CoKeInt is
type CoKeInt is new CoKeBaseAttribute with private;
procedure Int_to_CoKeInt(from: Int; to: out CokeInt);
function CoKeInt_to_Int(from: CokeInt) return Int;
private
type CoKeInt is new CoKeBaseAttribute with
record
I: Int;
end record;
procedure Read(
Stream: access Ada.Streams.Root_Stream_Type'CLASS;
Item: out CoKeInt);
for CoKeInt'Read use Read;
procedure Write(
Stream: access Ada.Streams.Root_Stream_Type'CLASS;
Item: in CoKeInt);
for CoKeInt'Write use Write;
end CoKe.Base.Attribute.CoKeInt;
上述代码是用于创建
CoKeInt
类型对象的泛型包,当前绑定不支持IDL类型的操作,因此它们仅用于通信目的,后续版本将为所有IDL类型提供合适的操作。
-- Program 4 Package Shared
with Ada.Streams, CoKe.Base.Attribute, CoKe.Base.Attribute.CoKeOid;
generic
type Base is new CoKe.Base.Attribute.CoKeBaseAttribute with private;
Comm_type: CoKe.Comm_type;
package Shared is
package Oid renames CoKe.Base.Attribute.CoKeOid;
type Shared is new Base with private;
procedure Set_Oid(
Obj: in out Shared;
the_Oid: Oid.CoKeOid := Oid.Create_Oid);
function Get_Oid(Obj: Shared) return Oid.CoKeOid;
private
type Shared is new Base with
record
the_Oid: Oid.CoKeOid;
end record;
procedure Read(
Stream: access Ada.Streams.Root_Stream_Type'CLASS;
Item: out Shared);
for Shared'Read use Read;
procedure Write(
Stream: access Ada.Streams.Root_Stream_Type'CLASS;
Item: in Shared);
for Shared'Write use Write;
end Shared;
Shared
包用于构建共享内存池中的对象实例。共享类型的对象可以配备一个OID。与程序2中的
Read
和
Write
操作不同,此包中的相应过程不仅执行(解)编组操作,还从/向共享内存池读取/写入相应的对象,通过其唯一的OID进行标识。基本IDL类型
Oid
在
CoKe.Base.Attribute.CoKeOid
包中定义,但未在此处明确显示。
5.2 事务
共享对象可以与事务结合使用,这通过Ada的标准面向对象特性(如泛型和带标签的类型)来实现。
-- Program 5 Package Tx
with Ada.Streams, Shared, Transaction_Mgt;
generic
with package Some_Shared is new Shared(<>);
package Tx is
type Txed is new Some_Shared.Shared with private;
procedure Set_Tx(
Obj: in out Txed;
the_Tx: Transaction_Mgt.Tx);
function Get_Tx(Obj: Txed) return Transaction_Mgt.Tx;
private
type Txed is new Some_Shared.Shared with
record
the_Tx: Transaction_Mgt.Tx;
end record;
procedure Read(
Stream: access Ada.Streams.Root_Stream_Type'CLASS;
Item: out Txed);
for Txed'Read use Read;
procedure Write(
Stream: access Ada.Streams.Root_Stream_Type'CLASS;
Item: in Txed);
for Txed'Write use Write;
end Tx;
一般来说,所有事务都由
Transaction_Mgt
包中的事务管理器控制。该管理器提供控制事务的基本类型和功能。通过泛型包
Tx
,可以将事务语义附加到任何共享对象类型。访问这种新类型的对象(使用
Read
和
Write
操作)与之前相同,只是增加了事务属性。
要建立一个事务,首先需要使用
Create_TX
函数从事务管理器获取一个事务对象。
Set_Tx
过程将这个新打开的事务绑定到共享对象上。
Get_Tx
仅作为
Set_Tx
过程的对应项提供,以保证完整性。
综上所述,Ada与CORSO的绑定为Ada语言在分布式应用开发中提供了强大的支持,通过虚拟共享内存和事务机制,提高了开发效率和系统的健壮性。在实际应用中,可以根据具体需求选择合适的编程语言和技术,充分发挥各技术的优势。
5.3 事务操作流程
建立和使用事务的具体操作流程如下:
1.
获取事务对象
:使用
Transaction_Mgt
包中的
Create_TX
函数从事务管理器获取一个事务对象。
2.
绑定事务到共享对象
:使用
Set_Tx
过程将新获取的事务对象绑定到共享对象上。
3.
访问共享对象
:使用
Read
和
Write
操作对绑定了事务的共享对象进行读写操作,操作过程中会遵循事务的语义。
4.
可选:获取事务信息
:如果需要,可以使用
Get_Tx
函数获取共享对象绑定的事务信息。
下面是一个简单的mermaid流程图,展示了事务操作的流程:
graph TD
A[开始] --> B[获取事务对象: Create_TX]
B --> C[绑定事务到共享对象: Set_Tx]
C --> D[访问共享对象: Read/Write]
D --> E{是否需要事务信息}
E -- 是 --> F[获取事务信息: Get_Tx]
E -- 否 --> G[结束]
F --> G
6. 绑定的优缺点分析
6.1 优点
- 功能增强 :与CORSO绑定后,Ada语言能够利用虚拟共享内存的优势,增加了通信、数据共享和持久化的功能,无需开发者手动实现这些复杂的机制。
- 多语言支持 :CORSO支持多种编程语言的绑定,使得不同语言编写的组件可以在同一个分布式系统中协同工作,充分发挥各语言的优势。例如,安全关键部分使用Ada,GUI部分使用Java。
- 事务控制 :通过事务机制,能够保证数据的一致性和完整性,提高系统的健壮性和容错能力。子事务的支持使得事务管理更加灵活。
- 性能提升 :对象缓存机制减少了远程过程调用的次数,提高了数据访问的效率,从而提升了整个系统的性能。
6.2 缺点
- 学习成本 :引入了新的概念和机制,如虚拟共享内存、事务、IDL等,开发者需要花费一定的时间来学习和理解这些内容。
- 复杂性增加 :与CORSO绑定后,系统的架构和实现变得更加复杂,调试和维护的难度也相应增加。
- 依赖外部组件 :依赖于CORSO软件组件和相关的协调内核,增加了系统的依赖性和部署的复杂性。
为了更直观地对比,以下是一个优缺点的表格:
| 方面 | 优点 | 缺点 |
| ---- | ---- | ---- |
| 功能 | 增加通信、数据共享和持久化功能,多语言协同 | 引入新概念,学习成本高 |
| 性能 | 对象缓存提高数据访问效率 | 系统架构复杂,调试维护难 |
| 可靠性 | 事务机制保证数据一致性和完整性 | 依赖外部组件,部署复杂 |
7. 与其他语言绑定的比较
将Ada与CORSO的绑定与其他语言(如C、C++、Java)的绑定进行比较,有助于更好地理解其特点和适用场景。
语言 | 绑定特点 | 适用场景 |
---|---|---|
Ada | 基于Ada语言的特性,提供了类型安全和可靠性,适合安全关键的分布式应用 | 航空航天、工业控制等对安全性要求高的领域 |
C | 性能高,与底层系统交互能力强,适合对性能要求极高的场景 | 嵌入式系统、高性能计算等 |
C++ | 支持面向对象编程,具有丰富的类库和模板,适合开发复杂的分布式系统 | 大型软件项目、游戏开发等 |
Java | 跨平台性好,具有强大的生态系统,适合开发分布式Web应用 | 互联网应用、企业级应用等 |
从表格中可以看出,Ada与CORSO的绑定在安全性和可靠性方面具有优势,适合对安全要求较高的分布式应用场景。
8. 总结
通过对Ada与共享对象层(CORSO)绑定的详细分析,我们可以看到这种绑定为Ada语言在分布式应用开发中带来了诸多优势。虚拟共享内存范式提供了更高层次的抽象,隐藏了网络的异构性,使得开发者可以更专注于业务逻辑的实现。事务机制保证了数据的一致性和完整性,提高了系统的健壮性和容错能力。
同时,我们也认识到这种绑定存在一些缺点,如学习成本和系统复杂性的增加。在实际应用中,开发者需要根据具体的需求和场景,权衡利弊,选择合适的技术方案。如果应用对安全性和可靠性要求较高,且开发者对Ada语言有一定的了解,那么Ada与CORSO的绑定是一个不错的选择。
未来,随着分布式系统的不断发展,虚拟共享内存和事务处理技术可能会进一步完善和优化,Ada与CORSO的绑定也有望在更多领域得到应用。开发者可以持续关注这些技术的发展动态,不断提升自己的开发能力和水平。
以下是一个简单的总结流程图,展示了Ada与CORSO绑定的整体优势和应用思路:
graph TD
A[Ada与CORSO绑定] --> B[虚拟共享内存]
A --> C[事务机制]
B --> D[隐藏异构性]
B --> E[提高开发效率]
C --> F[保证数据一致性]
C --> G[增强系统健壮性]
D --> H[专注业务逻辑]
E --> H
F --> I[安全可靠应用]
G --> I
通过以上的分析和总结,希望能为开发者在分布式应用开发中提供一些有益的参考和指导。