组通信 - 复旦大学

Report
分布式系统
Distributed Systems
第 5 讲 间接调用
LECTURE 5 INDIRECT COMMUNICATION
王晓阳、张 奇
复旦大学 计算机科学技术学院
1
目录
5.1 简介
5.2 组通信
5.4 消息队列
4.4.1 编程模型
5.2.1 编程模型
4.4.2 实现问题
5.2.2 实现问题
4.4.3 实例研究:Java 消息服务
5.2.3 实例研究:JGroups
5.3 发布—订阅系统
5.3.1 编程模型
5.3.2 RPC的实现
5.5 共享内存的方式
5.5.1 分布式共享内存
5.5.2 元组空间通信
5.6 小结
5.3.3 发布—订阅系统例子
2
5.1 简介
间接通信:在分布式系统中实体通过中介者进行通信,没有发送者
和接收者(们)之间的直接耦合
注意:接收者可以是多个,很多间接通信范型明确支持一对多通信
前两讲介绍的技术都是基于发送者和接收者之间的直接耦合
◦ 考虑简单的客户—服务器交互,因为是直接耦合,用相同功能的另一台
服务器代替原来的服务器很困难。
◦ 服务器故障后,客户必须显式的处理故障
3
5.1 简介
间接通信避免了直接耦合,使用中介有两个主要特性:
◦ 空间解耦
◦ 发送者不知道也不需要知道接收者(们)的身份,反之亦然
◦ 参与者可以被替换、更新、复制或迁移
◦ 时间解耦
◦ 发送者和接收者(们)可以有独立的生命周期
◦ 发送者和接收者(们)不需要同时存在才能通信
◦ 在易变的环境下,发送者和接收者可以随时进入和离开
间接系统常常用于预期会发生改变的分布式系统
◦ 例如:移动环境中
间接系统还常用于分布式系统的事件分发,在系统中接收者未知,
且易于改变
4
5.1 简介
Figure 6.1 Space and time coupling in distributed systems
5
5.1 简介
间接系统的主要缺点是:
◦ 增加间接层带来的性能开销
◦ 更加难以精确管理
与异步通信的关系
◦ 在异步通信中,发送者发送一个消息,然后继续工作(不阻塞),因此
不需要与接收者在同一时间通信。
◦ 时间解耦增加了额外的维度,发送者和接收者(们)可以互相独立存在。
例如:接收者在通信发起时可能不存在。
6
5.2 组通信
组通信(group communication)提供一种服务,在这种服务中,消
息首先被发送到组中,然后该消息被传送到组中的所有成员。
◦ 在这个动作中,发送者不清楚接收者们的身份
◦ 组通信是对组播通信的抽象,可以通过IP组播实现或等价的覆盖网实现
◦ 增加了一些重要特性,如管理组的成员、检测故障、提供可靠性和排序
保证
组通信的主要应用领域包括:
◦ 面向大量客户的可靠消息分发
◦ 支持协作应用
◦ 支持一系列容错策略
◦ 支持系统监控和管理,包括负载平衡策略
7
5.2.1 编程模型
在组通信中,核心概念是组和相关的组成员
◦ 进程可以加入或离开组
◦ 进程可以发送一个消息到组中,然后消息被传播到组中的所有成员,并
在可靠性和排序方面提供一定的保证
◦ 组通信实现了组播通信,即通过一个操作,消息被发送到组中所有成员
◦ 与系统中所有进程通信,而不是其中子组,被称为广播(broadcast)
◦ 而与单个进程通信被称为单播(unicast)
组通信的重要特征是一个进程事项只发起一个组播操作,它就可以
将消息发送到一组进程中的每一个,而不是发起多个发送操作到每
个进程
8
5.2.1 编程模型
只用单个组播操作对传递保证来说也很重要
◦ 如果一个进程发起多个独立的发送操作给不同的进程,那么实现上无法
保证作为一个整体影响一个进程组
◦ 如果发送者在发送过程中失败了,那么组中的一些成员收到了消息,而
另一些没有收到
◦ 另个消息传递到任意两个组成员的相对顺序是未定义的
9
5.2.1 编程模型
进程组和对象组
◦ 大多数组服务工作关注进程组(process group)概念,即通信实体是这
个组中的进程。这种服务相对低级,因为:
◦ 消息被传递到进程,没有进一步提供对分发的支持
◦ 消息通常是非结构化的字节数组,不支持对复杂数据类型的编码
◦ 对象组(object group)提供更高级的组计算方法
◦ 一个对象组是一组对象的集合(形式上是同一个类的实例),这些对象并发地处理同一组
调用,然后,各自返回其响应
◦ 客户对象不需要知道拷贝,它们调用一个本地对象上的操作,该对象充当组的代理
◦ 代理使用组通信系统向对象组的成员发送调用
◦ 对象参数和结果如在RMI中一样被编码,相关的调用自动分发到正确的目标对象/方法
10
5.2.1 编程模型
其他主要的区别
已经开发了许多组通信服务,它们因各自的假设不同而不同:
◦ 封闭和开放组:
◦ 一个组只有组成员能组播给它,这样的组被称为封闭组。
◦ 如果组外的进程可以发送消息给这个组,那么这个组被称为开放组。
Clos ed gr oup
Open gr oup
Figure 6.2 Open and closed groups
11
5.2.1 编程模型
其他主要的区别
◦ 重叠和非重叠组
◦ 在重叠组,实体(进程或对象)可能成为多个组的成员
◦ 非重叠组意味着成员不会重叠(也就是说,任一个进程属于至多一个组)
◦ 同步和异步系统
◦ 需要在两种环境中考虑组通信
12
5.2.2 实现问题
主要考虑一下几个方面:
◦ 底层组播服务在可靠性和排序方面的特性
◦ 进程可以在任何时候加入、离开或失效的动态环境中,组成员管理十分
重要
在组播中的可靠性和排序
◦ 在组通信中,所有成员必须收到发送给本组的消息的拷贝,并且一般具
有传递保证
◦ 这个保证包括组中每个进程收到的消息应达成的协定
◦ 组成员间消息传递顺序应达成的协定
◦ 组播系统非常复杂,仅提供最少的传递保证的IP组播也需要很大的工程
量
13
5.2.2 实现问题
在组播中的可靠性和排序
◦ 可靠性
◦ 2.4.2节利用完整性、有效性来定义了点对点通信中的可靠性
◦ 可靠组播的性质建立在这些语义覆盖之上
◦ 完整性保证消息至多被正确的传输一次
◦ 有效性保证消息最终会被传递
◦ 除此之外,还扩展了第三个特性,即协定(agreement)
◦ 所谓协定是指,如果消息被传递到一个进程,那么该消息会被传递到本组中所有进程
◦ 排序
◦ 组通信还要求对传递到多个目的地的消息提供消息相对排序方面的额外保障
◦ 有序是不能由底层进程间通信源语保证的
14
5.2.2 实现问题
在组播中的可靠性和排序
◦ 排序
◦ 组通信服务提供了有序组播(ordered multicast),提供如下一个或多个特性:
◦ FIFO序:先进先出(First-In-First-Out)序,保证了从发送者进程的角度所看到的顺序
如果一个消息在另一个消息之前发送,那么将以这个顺序传递到组中所有进程
◦ 因果序:因果序考虑了消息之间的因果关系,如果分布式系统中一个消息在另一个消息
之前发生,那么传递相关消息到所有进程时,这种所谓的因果关系将被保留
◦ 全序:在全序中,如果在一个进程中,一个消息在另一个之前被传递,那么相同的顺序
将在所有进程上被维持
15
5.2.2 实现问题
组成员管理
Figure 6.3 The role of group membership management
16
5.2.2 实现问题
组成员管理
◦ 组成员服务的四个主要任务:
◦ 提供组成员改变接口
◦ 组成员服务提供创建和删除进程组、在组中增加或者删除进程的操作
◦ 故障检测
◦ 检测组成员崩溃、通信故障等。检测器标记进程为可疑和非可疑的
◦ 使用故障检测器对组成员做出决策:当怀疑其已经出故障或变得不可达时,从成员中出
去该进程
◦ 组成员改变时通知成员
◦ 当增加进程,或去除进程时(故障或进程有意退出),通知成员
◦ 执行组地址扩展
◦ 当进程组播一个消息时,它提供组标识而不是组中的一系列进程。成员管理服务将该标
识扩展为要传递的当前组成员。服务通过控制地址扩展来协调成员改变时的组播传递
17
5.2.2 实现问题
组成员管理
◦ IP组播是一个较弱的组成员服务的例子
◦ 有一些组成员服务的特性,但不完全
◦ 允许进程动态的加入和离开组并执行地址扩展
◦ 对于组播消息,发送者只需提供一个IP组播地址作为目的地
◦ 但是IP组播本身不向成员提供当前成员信息,组播传递不会随成员改变而调整
◦ 需要维护组成员对基于组的方法有重要影响
◦ 尤其是,组通信在小规模、静态系统中很有效;大规模或者高度变化的
系统中运行并不完善。
18
5.2.3 实例研究:JGroups工具箱
JGroups是用Java编写的可靠组通信工具
◦ 由Connell University创建的通信工具JBoss中间件中的一部分
◦ JGroups 开源社区(www.jgroups.org)进行维护
◦ 支持进程组,在进程组中,进程可以进入、离开组,发送消息到所有组
成员或单个成员,并且从组中接收消息
◦ 支持多种可靠性和排序保证
◦ 提供组成员服务
19
5.2.3 实例研究:JGroups工具箱
JGroups的体系结构如图6-4所示
JGroups实现的主要组件如下:
◦ 通道/渠道
◦ 为应用开发者提供的最原始的接口,提供加入、离开、
发送和接收的核心功能
◦ 构造块
◦ 提供高层次的抽象,建立在由通道提供的底层服务之上
◦ 协议栈
◦ 提供底层通信协议,够了一个可以组合的协议层的栈
Figure 6.4 The architecture of JGroups
20
5.2.3 实例研究:JGroups工具箱
渠道
◦ 一个进程通过一个渠道对象和一个组进程交互,这里渠道对象作为一个
组的句柄
◦ 当渠道被创建时,连接是断开的,随后的连接(connect)操作将一个指
定名字的组绑定到这个句柄
◦ 如果这个指定名字的组不存在,那么第一次连接时隐式的创建
◦ 进程离开时,执行相应的断连(disconnect)操作
◦ 关闭(close)操作回关闭渠道的使用
◦ 注意:一个渠道一次只能连接一个组;如果一个进程希望连接多个组,
必须创建多个渠道
◦ 当进程连接渠道后,可以通过渠道发送和接收消息。消息是通过可靠组
播发送的
21
5.2.3 实例研究:JGroups工具箱
渠道
◦ 渠道上也定义了一系列其他操作,尤其是返回与渠道相关的管理信息
◦ getView返回定义为当前组成员列表的当前视图
◦ getState返回与组相关的历史应用状态
◦ 例子: 智能火警器发送“火灾!”组播消息到所有已注册的接受者
◦ 当报警产生时
(1) 创建JChannel的一个新实例
(2) 连接到AlarmChannel组
(3) 构造消息,一个消息的构造函数包括:
目的地、源、有效载荷
(4) 通过send方法传递到组中的所有成员。
import org.jgroups.JChannel;
public class FireAlarmJG {
public void raise() {
try {
JChannel channel = new JChannel();
channel.connect("AlarmChannel");
Message msg = new Message(null, null, "Fire!");
channel.send(msg);
}
catch(Exception e) {
}
}
Figure 6.5 Java class FireAlarmJG
22
5.2.3 实例研究:JGroups工具箱
渠道
◦ 例子: 智能火警器发送“火灾!”组播消息到所有已注册的接受者
◦ 接收端相应的代码具有相似的结构
import org.jgroups.JChannel;
public class FireAlarmConsumerJG {
public String await() {
try {
JChannel channel = new JChannel();
channel.connect("AlarmChannel");
Message msg = (Message) channel.receive(0);
return (String) msg.GetObject();
} catch(Exception e) {
return null;
}
}
}
Figure 6.6 Java class FireAlarmConsumerJG
23
5.2.3 实例研究:JGroups工具箱
构造块
◦ 是渠道类之上更高层的抽象
◦ 渠道在层次上类似于套接字,构造块则类似于更高级的通信范型,提供
常见通信模式的支持
◦ JGroups中,构造块的例子有:
◦ MessageDispatcher
◦ 发送者发送消息到组中,然后等待部分或所有的应答
◦ RpcDispatcher
◦ 提供一个特定的方法,然后调用组中所有对象上的该方法
◦ NotificationBus
◦ 分布式事件总线
24
5.2.3 实例研究:JGroups工具箱
协议栈
◦ 协议是由多个协议层组成的双向栈
◦ 协议处理通过在栈中向上或向下传送事件来完成
◦ 在JGroups中,事件可能是到达的消息、外发的消息或者管理事件
◦ 图6-4所示,由五层协议组成的协议栈
◦ UDP层为传输层
◦ FRAG层实现消息编码,并按照最大消息进程配置
◦ MERGE层处理非预期网络分区以及在分区后合并子组的协议
◦ GMS层实现了一个组成员协议,用以维护组成员之间的一致试图
◦ CAUSAL层实现了5.2.2介绍的因果序
◦ 还有很多其他协议层,并实现了相同接口
◦ 可以任意顺序组合
25
5.3 发布—订阅系统
发布—订阅系统(publish-subscribe systems)有时也称为基于事件
的分布式系统(distributed event-based system)
在发布—订阅系统中,发布者(publisher)发布结构化的事件到事
件服务,订阅者(subscriber)通过订阅(subscription)表达对特定
事件感兴趣,其订阅可以是结构化事件之上的任意模式。
发布订阅系统的任务是把订阅和发布事件进行匹配,保证事件通知
(event notification)的正确传递。
一个给定的事件将被传递到多个潜在的订阅者
26
5.3 发布—订阅系统
发布—订阅系统的应用
◦ 应用领域:
◦ 金融信息系统
◦ 实时数据直接输入的领域(包括RSS源)
◦ 支持协同工作
◦ 支持无处不在的计算
◦ 监控应用
◦ Google基础设施的一个重要组件
27
5.3 发布—订阅系统
发布—订阅系统的应用
例子:交易室
◦ 任务是允许交易者使用计算机查看他们交易的股票市场价格的最新信息
◦ 一个已命名的股票的市场价格由相关对象代表
◦ 到达交易室的信息来自不同的外部源,代表股票的部分或所有对象的更
新,并由信息提供者的进程收集。交易者只对自己的股票感兴趣
28
5.3 发布—订阅系统
Dealer’s computer
Dealer
Dealer’s computer
External
source
Notification
Notification
Notification
Information
provider Notification
Notification
Dealer
Notification
Notification
Dealer’s computer
Dealer’s computer
Notification
Information
provider
Notification
Notification
Dealer
Dealer
External
source
Figure 6.7 Dealing room system
29
5.3 发布—订阅系统
发布—订阅系统的特征
异构性
◦ 事件通知被用作一种通信手段,分布式系统中没有被设计实现互操作的
组件可以在一起工作。
异步性
◦ 通知是由生成事件的发布者异步地发送到所有对其感兴趣的订阅者的,
发布者和订阅者之间进行了解耦。
为通知提供不同的传递保证
◦ 具体选择依赖于应用的需求
◦ 网络游戏和交易室
◦ 时序需求应用
30
5.3.1 编程模型
Figure 6.8 The publish-subscribe paradigm
31
5.3.1 编程模型
发布—订阅系统的表达能力由订阅(过滤器)模型决定
以下是几种已定义的常见模式:
◦ 基于渠道
◦ 发布者发布事件到命名的渠道,订阅者订阅其中一个已命名的渠道,并接收所有发送到那
个渠道的事件
◦ 基于主题
◦ 假设每个通知中包含多个域,其中一个域表示主题。订阅是根据感兴趣的主题来定义的。
◦ 基于内容
◦ 基于内容的方法是基于主题方法的一般化,它允许订阅表达式具有一个事件通知上的多个
域。基于内容的过滤器是事件属性值的约束组合定义的查询。
◦ 基于类型
◦ 订阅根据事件类型来定义,匹配根据给定的过滤器的类型或者子类型来定义。
32
5.3.1 编程模型
一些商业系统中基于直接订阅感兴趣的对象
◦ 可以关注感兴趣的对象的状态改变,而不是与对象类型关联的谓词
◦ 允许一个对象对另一个对象发生的改变做出反应
◦ 事件的通知是异步的,由通知的接收者决定
◦ 交互式应用中,用户在对象上执行的行动
◦ 例如用鼠标操作一个按钮,用键盘在文本框中输入文本,都被看做事件
◦ 这些事件可以引起保存应用状态的对象的改变
◦ 每个状态改变时,负责展示当前状态视图的对象会得到通知
33
5.3.1 编程模型
一些研究者还考虑用上下文增加额外的表达能力
◦ 上下文和上下文敏感是移动和无处不在计算中的主要概念
基于概念的订阅模型
◦ 过滤器可以根据事件的语义和语法进行表述
复杂事件处理
◦ 仅能对单个事件的查询是不够的,需要更为复杂的、能够识别复杂事件
模式的系统
◦ 例如:金融系统
◦ 模式可以是逻辑的、时间上的或空间上的
34
5.3.2 实现问题
发布—订阅系统的任务是清楚的:保证所有事件被有效地传递到有
过滤器与事件匹配的所有订阅者。
除此之外,还可以有安全性、可伸缩性、故障处理、并发和服务质
量等额外需求。
本节主要考虑发布—订阅系统的实现问题和所需的系统体系结构
35
5.3.2 实现问题
集中式实现与分布式实现
◦ 已有许多实现发布—订阅系统的体系结构,最简单的方法是单节点服务
器方式的集中式实现。
◦ 服务器作为事件代理
◦ 发布者发布事件到该代理(还可以选择是否发布广告)
◦ 订阅者发送订阅到代理并接收返回的通知
◦ 与代理的交互是通过一系列点对点的消息,可以通过使用消息传递或远程调用来实现
◦ 集中式方法易于实现,但是设计缺乏弹性和可伸缩性,也意味着可能的
单点故障和性能瓶颈
36
5.3.2 实现问题
集中式实现与分布式实现
◦ 分布式发布—订阅系统
◦ 集中式代理被代理网络(network of broker)所取代,
◦ 这些方法可以从故障中生存下来
Figure 6.9 A network of brokers
37
5.3.2 实现问题
总的系统体系结构
◦ 集中式模式的实现相对简单
◦ 用中央服务器维护订阅库,并用订阅匹配形成事件通知
◦ 基于渠道或基于主题的模式的实现也相对简单
◦ 例如:可以将渠道或主题映射到相关的组,然后使用底层的组播通信设施传递事件到感兴
趣的各方
◦ 基于内容的分布式实现方法比较复杂,需要进一步考虑
38
5.3.2 实现问题
Figure 6.10 The architecture of publish-subscribe systems
39
5.3.2 实现问题
泛洪(Flooding)
◦ 最简单的方法是基于泛洪,也就是向网络中的所有节点发送事件通知,
在订阅者端执行适当的匹配
◦ 另外一种方案,用泛洪发送订阅到所有可能的发布者,在发布端执行匹
配,匹配成功的事件通过点对点通信被直接发送到相关的订阅者
◦ 泛洪可以利用底层广播或组播设施来实现
◦ 代理可以被安排在无环图中,每一个代理把到达的事件通知转发给邻居
◦ 该方法的优点是简单,但是会导致很多不必要的网络流量
40
5.3.2 实现问题
过滤
◦ 代理网络中采用过滤(filtering)是很多方法所采用的原则,这种方法被
称为基于过滤的路由
◦ 代理通过一个有路径到达有效订阅者的网络转发通知,实现机制:
◦ 通过先向潜在的发布者传播订阅信息
◦ 然后在每个代理上存储相关状态
◦ 每个结点必须维护
◦ 邻居列表(该列表包含了该节点在代理网络中所有相连接的邻居)
◦ 订阅列表(该列表包含了由该节点为之服务的所有直接连接的订阅者)
◦ 路由表(维护该路径上的邻居和有效订阅列表)
◦ 这个方法需要在代理网络中的每个结点上实现匹配
41
5.3.2 实现问题
过滤
upon receive publish(event e) from node x
matchlist := match(e, subscriptions) 2
send notify(e) to matchlist;
3
fwdlist := match(e, routing); 4
send publish(e) to fwdlist - x; 5
upon receive subscribe(subscription s) from node x 6
if x is client then 7
add x to subscriptions; 8
else add(x, s) to routing; 9
send subscribe(s) to neighbours - x; 10
1
Figure 6.11 Filtering-based routing
42
5.3.2 实现问题
广告
◦ 上述纯基于过滤的方法会由于订阅的传播而产生大量的网络流,订阅本
质上采用了泛洪的方法向所有可能的发布者推送
◦ 在广告系统中,通过与订阅传播类似的方式,向订阅者传播广告,这样
流量负担可以减少
◦ 两种方法间需要权衡,一些系统相继采用了两种方法
43
5.3.2 实现问题
汇聚(rendezvous)
◦ 控制订阅传播的方法
◦ 将所有可能的事件集合看做一个事件空间,将事件空间的责任划分到网
络中的代理集合上
◦ 汇聚结点是负责一个给定的事件空间的子集的代理节点
◦ 为了实现这种方法,一个给定的基于汇聚的路由(rendezvous-based
routing)算法必须定义两个函数
◦ SN(s)以一个给定的订阅s为参数,返回负责订阅s的一个或多个汇聚节点
◦ EN(e)以一个发布的事件e为参数,返回一个或多个汇聚节点,这些汇聚节点负责在系统中
将e和订阅进行匹配
◦ 这种方法需要保证对于一个给定的与s进行匹配的e,EN(e)和SN(s)的交集
必须非空
44
5.3.2 实现问题
汇聚(rendezvous)
upon receive publish(event e) from node x at node i
rvlist := EN(e);
if i in rvlist then begin
matchlist :=match(e, subscriptions);
send notify(e) to matchlist;
end
send publish(e) to rvlist - i;
upon receive subscribe(subscription s) from node x at node i
rvlist := SN(s);
if i in rvlist then
add s to subscriptions;
else
send subscribe(s) to rvlist;
Figure 6.12 Rendezvous-based routing
45
5.3.2 实现问题
汇聚(rendezvous)
◦ 对基于汇聚的路由的一种解释是将事件空间映射到分布式散列表
(Distributed Hash Table)
◦ 散列函数可以用于将事件和订阅映射到相应的管理这些订阅的汇聚节结
点上
◦ 也可以采用其他对象中间件方法来支持发布—订阅系统中的事件路由
◦ 采用闲聊协议作为支持事件路由的一种手段
◦ 基于闲聊的方法通过网络中的结点周期性地、以一定概率地与邻居结点交换事件(或数
据)
46
5.3.3 发布—订阅系统的例子
Figure 6.13 Example publish-subscribe system
47
5.4 消息队列
分布式消息队列是间接通信系统的一个重要的类别
消息队列使用队列概念作为一种间接机制提供点对点的服务
消息队列可以实现时间和空间解耦
消息队列也称消息中间件
◦ IBM的WebSphere MQ
◦ Microsoft的MSMQ
◦ Oracle的Streams Advanced Queuing(AQ)
48
5.4.1 编程模型
消息队列提供的编程模型很简单。
它提供了在分布式系统中通过队列进行通信的一种方法
生产者进程发送消息到特定队列,其他(消费者)进程从该队列中
接受消息
通常支持三种接收方式:
◦ 阻塞接收
◦ 非阻塞接收(轮询操作)
◦ 通知操作
49
5.4.1 编程模型
Figure 6.14 The message queue paradigm
50
5.4.1 编程模型
一些进程能将消息发送到同一个队列,同样也有一些接收者能从队
列中取出消息。
排队的策略通常是先进先出(FIFO),但是大多数队列的实现也支
持优先级概念,即高优先级的消息先被传递。
消费者进程也能基于消息的优先级从队列中选择消息。
一条消息通常由以下内容组成
◦ 目的地(即一个指定目的队列的唯一标识符)
◦ 消息的相关元数据(包括:消息优先级、传递模式等)
◦ 消息体
51
5.4.1 编程模型
消息体通常是不透明的,且未被消息队列系统改变过
◦ 消息体内容可使用3.3节所述的任意标准方法进行序列化
◦ 消息大小可以配置,可以很大(可达100MB的数量级)
◦ 通常通过定义在元数据上的谓词表示选择消息的规则
Oracle的AQ在基本想法上引入一个有趣的改动,以更好地实现与
(关系型)数据库的集成;在Oracle AQ中,消息是数据库表中的行,
队列是数据库表,可以使用数据库查询语言的所有功能对队列进行
查询。
52
5.4.1 编程模型
消息队列系统的一个重要特性是消息是持久的---也就是说,消息队
列会无限期存储消息(直到它们被消费为止),并将消息提交到磁
盘,以实现可靠传递。
消息传递系统还能支持额外的功能:
◦ 大部分商用系统支持消息的发送或接收可以包含在一个事务内。
◦ 目的是保证在事务中的步骤都能完成,或者事务不产生任何作用
◦ 一些系统也支持消息转换
◦ 可能很简单,也可能很复杂;有些系统还允许程序员开发自己的特定于应用的转换,以响
应来自底层消息队列系统的触发器
◦ 一些消息队列实现也提供安全性支持
◦ 例如:WebSphere MQ使用安全套接字层(Secure Sockets Layer, SSL),提供对保密数据传
输的支持,也支持认证和访问控制
53
5.4.2 实现问题
实现问题中最重要的选择:集中式实现还是分布式实现
集中式实现,由位于指定节点上的消息管理器管理一个或多个消息
队列。这种实现的优点是简单,但是这些管理器可能变成重量级组
件,可能成为瓶颈或单点故障。
实例研究:WebSphere MQ
◦ WebSphere MQ是IBM开发的基于消息队列概念的中间件,它在消息发送
者和接收者之间提供了一个间接机制。
◦ WebSphere MQ中队列由队列管理器(queue manager)管理,它存储并
管理队列,允许应用通过消息队列接口(Message Queue Interface,MQI)
访问队列。
◦ 多个队列管理器可以驻留在一个物理服务器上。
54
5.4.2 实现问题
访问一个队列管理器的客户应用可以驻留在同一个物理服务器上
如果它们在不同的机器上,就必须通过所谓的客户渠道(client
channel)和队列管理器通信。客户渠道采用代理的概念,即在代理
上发出MQI命令,这些命令通过RPC透明地传送到队列管理器去执
行。
Figure 6.15 A simple networked topology in WebSphere MQ
55
5.4.2 实现问题
队列管理器常被连接成一个联邦结构,类似(具有代理网络的)发
布—订阅系统经常采用的方法。
WebSphere MQ引入消息渠道(message channel)概念,作为两个队列
管理器之间的单向连接,该消息渠道用于从一个队列异步地转发消息
到另一个队列。
◦ 注意:消息渠道和客户渠道的区别
消息渠道通过两端的消息渠道代理(Message Channel Agent,MCA)进
行管理。
两个代理负责建立和维护渠道,包括最初的对渠道特性的协商(包括
安全特性)
每个队列管理器包含路由表以及渠道,这样允许创建任意的拓扑结构
56
5.4.2 实现问题
WebSphere MQ可以创建广泛的拓扑结构,包括树形、网格或基于
总线的配置。
WebSphere MQ为管理员提供工具用于创建合适的拓扑结构,并隐
藏建立消息渠道和路由策略的复杂性
集线器和辐条拓扑方法(hub-and-spoke):
◦ 指定一个队列管理器作为集线器
◦ 客户通过指定作为辐条的队列管理器来连接
◦ 辐条转发消息到集线器的消息队列以便由不同的服务进行处理
◦ 辐条按照某种策略放置到网络中支持不同的客户
◦ 集线器放置在网络总合适的位置,即有足够资源处理网络流量的结点上
◦ 大多数应用和服务位于集线器上,虽然辐条上也可能有很多本地服务
57
5.4.2 实现问题
集线器和辐条拓扑在WebSphere MQ中大量使用,尤其是用于覆盖
重要地理位置的大规模部署。
客户应用和队列管理器之间是使用RPC通信,而队列管理器内容的
通信是异步的(非阻塞)。这意味着客户应用被阻塞,直到消息被
存储在本地消息管理器(本地辐条)中;随后的传递可能在广域网
上,并且是异步的,由WebSphere MQ中间件保证传递的可靠性。
这一方法的关键是要能够通过高带宽连接到一个本地辐条
这个体系结构的缺点是集线器将成为潜在的瓶颈和单点故障
WebSphere MQ还支持队列管理器集群,该方式允许相同服务的多
个实例由多个队列管理器支持,并能在不同的实例间进行隐式的负
载均衡
58
5.4.3 实例研究:Java消息服务
Java 消息服务(JMS)是分布式Java程序间接通信的一个标准化规约。
该规约通过支持主题和队列作为消息的另一种目的地,至少在表面上
整合了发布—订阅和消息队列泛型
通用规约的实现包括:OW2的Joram、Jboss的Jave Messaging、Sun的
Open MQ、Apache的ActiveMQ以及Open JMS;WebSphree MQ也提供一
个JMS接口
JMS区分以下角色
◦ JMS客户产生或消费信息,JMS生产者创建和产生消息,JMS消费者接收和消
费消息
◦ JMS提供者是任一个实现了JMS规约的系统
◦ JMS消息是用于在JMS客户(从生产者到消费者)之间交流信息的对象
◦ JMS的目的地是JMS中支持间接通信的对象,可以是JMS主题或JMS队列
59
5.4.3 实例研究:Java消息服务
用JMS编程
◦ JMS API提供的编程模型如下图所示
60
5.4.3 实例研究:Java消息服务
用JMS编程
◦ 会话对象是JMS操作的核心,它具有创建消息、创建消息生产者、创建
消息消费者的方法:
◦ 在JMS中,消息由三个部分组成:头部、特性集和消息体
◦ 头部包含识别和路由所需的全部信息,包括目的地、消息优先级、过期日等
◦ 特性全部是由用户定义的,可用于将其他特定于应用程序的元数据元素和消息关联
◦ 消息体可以是一个文本消息、一个字节流、一个序列化的Java对象等
◦ 消息生产者是一个对象
◦ 它发布特定主题的消息或者将消息发送到一个队列中
◦ 消息消费者是一个对象
◦ 用来定于关于给定主题的消息或接收来自一个队列的消息
◦ 消息选择器(message selector)是一个谓词,可以定义在消息头的值和消息的特性部分
◦ 提供接收消息的两种模式:阻塞和消息监听者(message listener)
61
5.4.3 实例研究:Java消息服务
例子:火警服务
import javax.jms.*;
import javax.naming.*;
public class FireAlarmJMS {
public void raise() {
try {
1
Context ctx = new InitialContext();
2
TopicConnectionFactory topicFactory =
3
(TopicConnectionFactory)ctx.lookup ("TopicConnectionFactory"); 4
Topic topic = (Topic)ctx.lookup("Alarms");
5
TopicConnection topicConn =
6
topicConnectionFactory.createTopicConnection();
7
TopicSession topicSess = topicConn.createTopicSession(false,
8
Session.AUTO_ACKNOWLEDGE);
9
TopicPublisher topicPub = topicSess.createPublisher(topic);
10;
TextMessage msg = topicSess.createTextMessage();
11
msg.setText("Fire!");
12
topicPub.publish(message);
13
} catch (Exception e) {
14
}
15
}
Figure 6.17 Java class FireAlarmJMS
62
5.4.3 实例研究:Java消息服务
import javax.jms.*; import javax.naming.*;
public class FireAlarmConsumerJMS
public String await() {
try {
1
Context ctx = new InitialContext();
2
TopicConnectionFactory topicFactory =
3
(TopicConnectionFactory)ctx.lookup("TopicConnectionFactory"); 4
Topic topic = (Topic)ctx.lookup("Alarms");
5
TopicConnection topicConn =
6
topicConnectionFactory.createTopicConnection();
7
TopicSession topicSess = topicConn.createTopicSession(false,
8
Session.AUTO_ACKNOWLEDGE);
9
TopicSubscriber topicSub = topicSess.createSubscriber(topic);
10
topicSub.start();
11
TextMessage msg = (TextMessage) topicSub.receive();
12
return msg.getText();
13
} catch (Exception e) {
14
return null;
15
}
16
}
Figure 6.18 Java class FireAlarmConsumerJMS
63
5.5 分布式共享内存
共享内存的抽象是另外一种间接通信范型
为并行计算开发,分布式共享内存在读和写字节级别操作
分布式共享内存可以通过地址进行访问
64
5.5.1 分布式共享内存
分布式共享内存(DSM)是一种抽象,用于给不共享物理内存的计
算机共享数据
进程通过读和更新看上去是其他地址空间中普通的内存来访问DSM
底层的运行时系统透明地保证运行在不同的计算机上的进程可以观
察到其他进程的更新
Figure 6.19 The distributed shared memory abstraction
65
5.5.1 分布式共享内存
DSM的要点是DSM节省了程序员在写应用程序时对消息传递的考虑
DSM是一个重要的工具,用于并行应用、分布式应用
总体来讲,DSM不太适合客户—服务器系统,客户通常视服务器所
持有的资源为抽象数据,通过请求对它们进行访问(出于模块化和
安全保护的原因)
分布式系统中消息传递不能全部避免:没有物理共享内存时,DSM
运行时支持通过计算机之间的消息传送更新
DMS系统管理复制数据:为了加快访问速度,每台计算机都有存储
在DSM中最近访问过的数据项的本地拷贝
66
5.5.1 分布式共享内存
DSM实现中,一个著名的例子是Apollo Domain文件系统
◦ 该系统中,不同工作站拥有的进程通过将文件同时映射到它们的地址空
间来共享文件。
◦ 这个例子表明分布式共享内存可以是持久的。
◦ 它可以比任何访问它的进程或者进程组的运行更持久并且它可以被不同进程组共享。
DSM的意义伴随着共享内存多处理器的发展而增加
◦ 许多研究工作已经研究了适合在这些多处理器上进行并行计算的算法
◦ 在硬件体系结构层次,开发工作包括缓存策略以及快速的处理器—内存
互联。目标是在实现快速内存访问的低延迟和高吞吐量时,最大化可支
持的处理器数目。
67
5.5.1 分布式共享内存
DSM的意义伴随着共享内存多处理器的发展而增加
◦ 在进程通过一个公共总线连接到内存模块的地方,总线竞争导致性能急
速下降,实际的极限是10个处理器量级
◦ 共享内存的处理器通常以4个为一组进行构造,通过单个电路板上的总
线共享一个内存模块
◦ 可以用这样的电路板按非均匀内存访问(Non-Uniform Memory Access,
NUMA)体系结构构造多处理器(至多64个处理器)
◦ 这是一个层次式体系结构,其中四处理器的电路板通过使用高性能的交换器或者高层次总
线相连
◦ 在NUMA体系结构中,处理器看到一个单一的地址空间,其中包含所有电路板上的所有内
存,但是,板上的内存的访问延迟要小于不同板上的内存模块的访问延迟
68
5.5.1 分布式共享内存
分布式内存多处理器(distributed-memory multiprocessor)和现成
的计算组件集群中,处理器没有共享内存但是通过一个非常高速的
网络来连接。
◦ 可以扩展到比共享内存多处理器的64个左右的处理器更多数量的处理器
DSM以及多处理器研究所关注的核心问题是,共享内存算法以及相
关软件上的知识投资是否可以直接被转化为一个可伸缩性强的分布
式内存体系结构
69
5.5.1 分布式共享内存
消息传递与DSM
◦ 作为一种通信机制,DSM与消息传递更具有可比性,而不是基于请求—
应答的通信,因为DSM在并行处理方面的应用必须使用异步通信。
◦ 在编程方面,DSM和消息传递方法对比如下:
◦ 提供的服务
◦ 在消息传递模式下,变量必须在一个进程中编码,被传递后在接收进程中解码成与其他
的变量。采用共享内存,相关进程直接共享变量,不需要编码,不需要单独的通信操作。
大多数实现允许存储在DSM中的变量可以像普通的非共享变量一样被命名和访问。
◦ 支持消息传递是因为它允许进程通信,同时通过拥有各自私有地址空间而保护彼此,然
而,共享DSM的进程可能因为诸如错误地变更数据而引起其他进程失效。
◦ 在消息模型中,通过消息传递原语本身,可以实现进程之间的同步。而在DSM中,同步
通过共享内存编程的常规组成成分,例如锁和信号量实现的。
◦ DSM可以持久化,通过DSM通信的进程可能在非重叠的生命周期上执行。进程可以在一
个协定的内存位置放置数据,以便其他进程运行时查看数据。通过消息传递通信的进程
必须在同时
70
5.5.1 分布式共享内存
消息传递与DSM
◦ DSM和消息传递方法对比如下:
◦ 效率
◦ DSM开发的某个并行程序能表现得和在相同硬件上用消息传递平台编写的具有等价功
能的程序一样出色(至少是在相对少量的计算机上(10台)如此)
◦ 然而,这个结论不能推广。基于DSM的程序性能依赖于很多因素
◦ 两种编程方式相关的开销的可见性是不同的
◦ 在消息传递中,所有的远程数据访问是显式的。程序员总是知道一个特定操作是在
进行还是涉及了通信开销
◦ 使用DSM,任何特定的读或者更新有可能涉及、也可能不涉及底层运行时支持提供
的通信
◦ DSM是否比消息传递更适合于一个特定应用,没有绝对答案
71
5.5.2 元组空间通信
元组空间是耶鲁大学的David Gelernter 作为分布式计算的一种新形
式引入的,它基于David Gelernter提出的“生成通信”
进程通过在元组空间放置元组间接地进行通信,其他进程可以从该
元组读或删除元组
元组没有地址,但是可以通过内容上的模式匹配进行访问(内容可
寻址的内存)
所形成的Linda编程模型有很广泛的影响力,并在分布式编程方面带
来了重大的发展,包括:Agora系统,Sun的JavaSpaces和IBM的
Tspaces
元组空间通信在无处不在的计算领域也很有影响
72
5.5.2 元组空间通信
编程模型
◦ 在元组编程模型中,进程通过元组空间(一个共享的元组集合)进行通信。
元组由一个或多个带类型的数据域组成
◦ 如序列<“fred”, 1958>, <“sid”, 1964>, <4, 9.8, “yes”>
◦ 元组类型的任何组合都可能存在于相同的元组空间中。
◦ 进程通过访问同一个元组空间实现共享数据:
◦ 通过使用write操作将元组放置在元组空间中
◦ 使用read或者take操作从元组空间读或提取元组
◦ 从元组空间中读或者删除元组时,一个进程提供了一个元组规约,元组空间
返回符合该规约的任何元组
◦ 为了使得进程能够同步其活动,read和take操作都会阻塞,直到在元组空间
中找到一个相匹配的元组
◦ 一个元组规约包括域的数量和所需的域值或者域类型
◦ 例如:take(<String, integer>)可以提取<“fred”, 1958>或者<“sid”, 1964>;
◦ take(<String, 1958>)只能提取到两者中的<“fred”, 1958>
73
5.5.2 元组空间通信
编程模型
◦ 在元组空间范型中,不允许直接访问元组空间中的元组,进程必须替换
元组空间中的元组而不是修改它。元组是保持不变的。
◦ 假设一组进程在元组空间中维护一个共享的计数器。
◦ 当前计数在元组<“counter”, 64>中是64
◦ 一个进程为了增加myTS元组空间的计数器的值,必须执行以下形式的代
码:
<s, count>:= myTS.take(<“counter”,integer>)
myTS.writer (<“counter”,count+1>);
74
5.5.2 元组空间通信
编程模型
Figure 6.20 The tuple space abstraction
75
5.5.2 元组空间通信
与元组空间相关的特性
◦ 空间解耦
◦ 放置在元组空间中的元组可能源自任何数量的发送者进程,也可能被传递到任何一个潜在
的接收者
◦ 时间解耦
◦ 放置在元组空间中的元组会保留在元组空间中直到被删除(可能是无限期的),因此,发
送者和接收者不需要在时间上重叠
◦ 这些特性提供了一种在空间和时间上完全分布的方法,还通过元组空间
提供了一种共享变量的分布式共享形式
76
5.5.2 元组空间通信
相关变种:Linda引入以来,已经提出了许多对原有模型的改进
◦ 原有的Linda模型提了一个单一、全局的元组空间
◦ 在大系统中并不是最佳的,有可能导致意想不到的元组混乱:随着元组空间中元组数量的
增加,read或者take操作匹配到元组的机会会无意中增加,尤其在对元组类型进行匹配时
◦ 因此,很多系统提出了多元组空间(multiple tuple spaces),包括了动态创建元组空间的
能力,并在系统中引入作用域
◦ Linda按预期被实现成一个集中式的实体,后续的系统试验了元组空间的
分布式实现
◦ 修改或扩展元组空间提供的操作,并改变底层语义。
◦ 例如:将所有元素都建模为集合,来统一元组和元组空间的概念。元组空间是元组的集合,
元组是值的集合,这里的值也可能包括元组
◦ 从数据项转移到数据对象,将元组空间转变为对象空间
77
5.5.2 元组空间通信
实现问题
◦ 许多元组空间的实现采用了集中式的方案,方案中元组空间资源由一个
服务器管理。该方案虽然简单,但是不能容错和伸缩。
◦ 几个系统提出利用复制来克服上述问题。
◦ Bakken和Schlichting[1995]、Bessani等[2008]提出的方案采用与复制类似
的状态机方法。
◦ 该方法假设元组空间的行为像状态机,维护状态或改变状态以响应来自其他副本或环境的
事件。为了保证一致性,副本:
◦ 1)必须从相同的状态开始;
◦ 2)必须以相同的顺序执行事件;
◦ 3)每个事件必须做出确定的反应。
◦ 第二个特性可以通过采用全序组播算法保证。
78
5.5.2 元组空间通信
其他方法:
◦ 纽约大学开发的Linda内核采用了将元组划分到可用的元组空间服务器
(Tuple Space Server,TSS)上的方法。
Figure 6.22 Partitioning in the York Linda Kernel
83
5.5.2 元组空间通信
实例研究:JavaSpaces
JavaSpaces是Sun开发的空间通信工具
◦ Sun提供JavaSpaces服务的规约,第三方开发者免费提供JavaSpaces的实
现(包括GigaSpaces和Blitz等)
◦ 依赖于Jini工具,Jini工具也包括了一个JavaSpaces的实现,称为Outrigger
◦ 技术目标是:
◦ 提供一个简化分布式应用和服务设计平台
◦ 要使关联类的数量和大小简单化和最小化
◦ 运行占用的空间小,使得代码可以在资源有限的设备上运行
◦ 规约能够以复制方式实现(实践中大多实现是集中式的)
84
5.5.2 元组空间通信
实例研究:JavaSpaces
JavaSpaces编程:JavaSpaces允许程序员创建任意数量的空间实例。
空间是一个共享的、持久的对象仓库。
一个JavaSpaces项被称为一个条目(entry)
◦ 包含在实现了net.jinni.core.entry.Entry的类中的一组对象
◦ 注意:条目包含对象(而不是元组),所以这有可能将任意行为与条目
关联,这大大增加了这种方法的表达能力
85
5.5.2 元组空间通信
Figure 6.23 The JavaSpaces API
86
5.5.2 元组空间通信
例子:智能火警
首先定义一个类型为AlarmTupleJS的条目对象
import net.jini.core.entry.*;
public class AlarmTupleJS implements Entry {
public String alarmType;
public AlarmTupleJS() { }
}
public AlarmTupleJS(String alarmType) {
this.alarmType = alarmType;}
}
}
Figure 6.24 Java class AlarmTupleJS
87
5.5.2 元组空间通信
import net.jini.space.JavaSpace;
public class FireAlarmJS {
public void raise() {
try {
JavaSpace space = SpaceAccessor.findSpace("AlarmSpace");
AlarmTupleJS tuple = new AlarmTupleJS("Fire!");
space.write(tuple, null, 60*60*1000);
catch (Exception e) {
}
}
}
Figure 6.25 Java class FireAlarmJS
88
5.5.2 元组空间通信
import net.jini.space.JavaSpace;
public class FireAlarmConsumerJS {
public String await() {
try {
JavaSpace space = SpaceAccessor.findSpace();
AlarmTupleJS template = new AlarmTupleJS("Fire!");
AlarmTupleJS recvd = (AlarmTupleJS) space.read(template, null,
Long.MAX_VALUE);
return recvd.alarmType;
}
catch (Exception e) {
return null;
}
}
}
Figure 16.26 Java class FireAlarmReceiverJS
89
5.6 小结
Figure 6.27 Summary of indirect communication styles
90
Question?
91

similar documents