分布式反应系统软件架构的SPLICE技术解析
应用在线替换与版本管理
在许多系统中,直接用新应用替换当前应用是不可行的,因为这需要系统离线。不过,通过对代理进行优化,可以实现应用的在线替换。当应用执行写操作时,其代理会在数据元素上附加一个代表应用版本号的关键字段。当有读取请求时,代理首先会检查本地数据库中是否存在所请求实例的多个版本。若存在,会将版本号最高的实例提供给应用,因为通常较高的版本号对应着较新的版本。从此时起,所有来自同一代理且版本号较低的数据元素都会被丢弃。这样,只需启动应用的新版本,它就能自动集成并替换当前版本,实现应用的动态升级。
SPLICE的形式化建模
为了给SPLICE和基于SPLICE的系统开发理论框架,我们进行了一系列工作。进程代数是一种用于推理分布式系统的形式化方法,而SPLICE中的进程是顺序进程,它们通过对共享数据空间的读写操作进行交互。因此,在通信顺序进程(CSP)的背景下研究SPLICE是很自然的。不过,SPLICE中的通信操作是非同步的,所以我们采用数据流网络(DFN)的进程代数作为SPLICE进程代数(SPA)的基础,DFN是CSP的异步变体,其输出不会被阻塞,环境总是准备好接收数据。
在SPA中,我们在一定抽象层次上对SPLICE进程进行建模,重点关注进程的通信行为。进程通常用P、Q和R表示,不同类型用a、b和c表示。每个进程都有输入和输出字母表,分别用i(P)和o(P)表示,它们定义了根据SPLICE的订阅范式,进程P可以读写的类型。我们将代理的通信管理角色集成到SPA运算符的语义中,用“after”运算符对本地数据空间进行建模,不同进程之间的通信通过并行组合来表示。
SPA中SPLICE的协调原语如下:
-
a!v
:写入类型为a、值为v的元素。要求若P是发布a.v的进程,则a属于o(P),且写入操作不会被环境阻塞。
-
a?x
:读取类型为a的元素x(非破坏性读取)。要求若P是读取类型为a元素的进程,则a属于i(P),当没有可用的x时,读取操作会阻塞。
-
a?
:获取(读取并移除)类型为a的元素x(破坏性读取)。要求执行此操作的进程P满足a属于i(P),当没有类型为a的元素可用时,获取操作会阻塞。
-
P/a.v
:P在接收到数据a.v之后的状态,意味着a.v已存储在P的本地数据空间中。
-
P || Q
:P和Q的并行组合。
同时,SPA对进程有一些语法限制,要求i(P)和o(P)是有限的,输出字母表非空,且输入和输出字母表不相交。我们用基于受保护命令的抽象编程语言替换了宿主语言,该语言具有非确定性选择、前缀、受保护选择和递归等运算符。通过SPA,我们可以表示SPLICE的协调原语,如异步写入、(非)破坏性读取、动态进程创建和进程复制等。形式化的目标是支持在进程代数中设计基于SPLICE的系统,后续还需要定义设计规则和相关指南,扩展读写操作以包含查询机制,并对其使用进行限制。此外,还需表达SPLICE的其他重要特性,如实时和容错行为。
下面用表格总结SPA的协调原语:
| 原语 | 描述 | 要求 | 阻塞情况 |
| ---- | ---- | ---- | ---- |
| a!v | 写入类型为a、值为v的元素 | a属于o(P)(P为发布进程) | 不阻塞 |
| a?x | 非破坏性读取类型为a的元素x | a属于i(P)(P为读取进程) | 无可用x时阻塞 |
| a? | 破坏性读取类型为a的元素x | a属于i(P)(P为执行进程) | 无可用元素时阻塞 |
| P/a.v | P接收数据a.v后的状态 | - | - |
| P || Q | P和Q的并行组合 | - | - |
铁路调度示例
为了展示SPLICE在设计复杂系统中的优势,我们以铁路调度问题为例。假设有一个矩形铁路轨道网格,东西向和南北向轨道定期相交,且每根轨道上有一辆列车。列车只能从西向东或从北向南行驶,到达轨道终点后会回到起点重新开始行程,行程时间是在预设时间基础上的随机扰动。列车需要满足安全行驶、尽可能按时到达以及为乘客提供舒适旅程这三个要求,且重要性依次降低。
由于列车的行驶时间存在随机变化,无法为每辆列车计算出全局最优的行驶计划,因此需要动态控制列车。解决方案有集中控制和分布式控制两种,这里采用的是分布式控制,基于以下原则:每辆列车定期发布自己的状态信息,同时获取可能与之碰撞的列车的状态信息,然后所有列车独立地根据相同的交通规则决定自己的行动方案。
列车的状态信息包括:
-
time
:状态生成的时间
-
position
:列车头部在“time”时刻的位置
-
nextX
:下一个交叉点
-
onX
:不在交叉点时为0,否则为占用交叉点的编号
-
length
:列车长度
-
speed
:“time”时刻的速度
-
acc
:“time”时刻的加速度
-
delay
:“time”时刻相对于计划的延迟
-
action taken
:与避免碰撞相关的行动
-
maximum speed
:最大速度
-
maximum acceleration
:最大加速度
-
maximum deceleration
:最大减速度
交通规则如下:
| 条件 | 动作 |
| ---- | ---- |
|
冲突决策 | 东西向列车优先 |
|
列车已在交叉点 | 继续行驶 |
|
其他列车在交叉点 | 等待交叉点清空 |
|
其他列车已采取行动 | 采取互补行动 |
| 不同延迟 | 延迟最大的列车优先 |
| 不同速度 | 速度快的列车优先 |
| 不同长度 | 最短的列车优先 |
| *其他情况 | 东西向列车优先 |
这个解决方案的核心是每辆列车都能获取其他相关列车的信息,SPLICE提供了理想的信息交换机制。每辆列车可以看作一个独立的程序,程序会不断从SPLICE请求数据,判断是否需要采取行动,并将当前状态写入SPLICE。
为了提高可扩展性和效率,需要尽量减少通信的数据量。一方面,将列车的状态数据分为描述静态属性和动态属性的两部分,利用SPLICE的多类型订阅机制确保这两部分数据能一起处理。另一方面,通过SPLICE的过滤机制,列车程序可以只请求与自己轨道方向不同的列车的数据,减少不必要的通信。
以下是相关代码示例:
char dir_filter[64];
char *df = dir_filter;
struct sp_consumer_part train_state[]={
{sp_use_sort(train_descr),"@id","",0,NULL,1,&df},
{sp_use_sort(state_descr),"@id","",0,NULL,1,&df}};
sprintf(dir_filter, ".id %c 0",
myself.track_dir == dir_hor ? '<' : '>');
ts_cons=sp_start_consuming_multi(app,2,train_state,
1,&w,NULL,
"splice","","",NULL,SP_SYNC_DEFAULT);
q=sp_define_query(ts_cons,"@track==%p matching @tail<=%f",
&p_track,Xings[myself.track]);
通过这个示例,我们可以看到SPLICE在分布式系统中的强大功能,它能有效管理数据通信和处理,为复杂系统的设计提供了有力支持。同时,通过形式化建模和合理的设计规则,可以进一步优化系统性能,确保系统的可靠性和稳定性。
分布式反应系统软件架构的SPLICE技术解析
解决方案的讨论
该铁路调度问题的解决方案有两个方面值得深入探讨,分别是方案的有效性和具体实现。
方案有效性分析
要论证列车能满足相关要求,需分几步进行。首先,要证明在列车能及时检测到即将发生的碰撞并刹车的假设下,所设定的交通规则足以避免事故。其次,要明确保证该假设成立的条件。最后,需表明在没有碰撞危险时,列车会专注于按时到达目的地。
具体实现分析
这里着重讨论如何使用SPLICE实现该解决方案。
- 解决方案结构 :该方案的核心在于每辆列车都能获取其他相关列车的信息,而SPLICE恰好提供了理想的信息交换机制。具体来说,将每辆列车建模为一个独立的程序,这些程序仅在参数设置上有所不同,用于指示轨道和列车的静态属性。每个程序都包含一个无限循环,在循环中,它会不断从SPLICE请求数据,判断是否需要采取行动,并将当前状态写入SPLICE。其流程如下:
graph LR
A[开始] --> B[从SPLICE请求数据]
B --> C{是否需要行动}
C -- 是 --> D[确定行动方案]
C -- 否 --> E[不采取行动]
D --> F[将当前状态写入SPLICE]
E --> F
F --> B
-
可扩展性问题 :为了提高可扩展性和效率,需要将通信数据量降至最低。在一个规模为n的轨道网格中,所有列车获取其他不同方向列车的信息需要大量的通信。因此,减少信息交换量对降低网络负载至关重要。此外,由于列车不会与已经经过的轨道上的列车发生碰撞,所以列车无需获取这些轨道上列车的状态数据。随着列车从轨道起点行驶到终点,潜在危险列车的数量会逐渐减少,列车程序的处理需求也应相应降低。在实际应用中,列车的预期处理负载可能非常低,主要的优化方向是减少通信需求。
-
具体实现方法
- 数据拆分与多类型订阅 :为了实现最小的通信负载,将列车的状态数据拆分为两部分:一部分描述列车的(伪)静态属性,这部分数据很少或几乎不发生变化;另一部分描述列车的实际位置、速度等动态属性。虽然这种方法看似简单,但要确保特定列车的属性数据和动态数据始终能一起处理却存在一定难度。SPLICE支持对所谓的多类型进行订阅,即一组必须一起处理且通过公共键关联的单个类型。应用程序可以根据自身需求以不同方式从SPLICE检索数据,SPLICE会确保组成多类型的不同类型的数据形成一个连贯的集合。
- 过滤机制 :防止并行轨道上列车的状态数据被不必要地传输相对复杂一些。列车程序可以使用SPLICE的内置过滤机制,仅请求与自己轨道方向不同的列车的数据。具体操作是,在状态数据中编码列车的轨道方向,然后指定只需要方向与自己不同的数据。SPLICE会对所有传入的相关类型数据元素评估过滤表达式,并丢弃不符合条件的元素。根据应用程序的结构,SPLICE可能会在生产者端评估过滤表达式,以更好地利用可用的通信带宽。
- 查询机制 :为了避免处理已经经过的轨道上的数据,使用SPLICE的查询功能将这部分数据排除在返回给应用程序的范围之外。这样,数据仍然会存储在共享数据空间的本地实例中,在列车后续的轨道循环中仍可用于处理。
过滤和查询机制的选择在一定程度上取决于个人喜好。以下是一个从列车程序中直接提取的示例代码,进一步说明了上述机制的使用:
char dir_filter[64];
char *df = dir_filter;
struct sp_consumer_part train_state[]={
{sp_use_sort(train_descr),"@id","",0,NULL,1,&df},
{sp_use_sort(state_descr),"@id","",0,NULL,1,&df}};
sprintf(dir_filter, ".id %c 0",
myself.track_dir == dir_hor ? '<' : '>');
ts_cons=sp_start_consuming_multi(app,2,train_state,
1,&w,NULL,
"splice","","",NULL,SP_SYNC_DEFAULT);
q=sp_define_query(ts_cons,"@track==%p matching @tail<=%f",
&p_track,Xings[myself.track]);
总结
通过上述铁路调度问题的示例,我们可以清晰地看到SPLICE在分布式系统中的强大功能。它不仅能有效管理数据通信和处理,为复杂系统的设计提供有力支持,还能通过形式化建模和合理的设计规则,进一步优化系统性能,确保系统的可靠性和稳定性。未来,还可以进一步扩展SPLICE的功能,如引入实时和容错行为的表达,以及完善设计规则和相关指南,以更好地满足各种复杂系统的需求。
221

被折叠的 条评论
为什么被折叠?



