弹性系统的算法多样性
1. 消息处理的控制流
在处理接收到的消息时,有异步和同步两种方式。
1.1 异步处理
使用
receive
定义,形式为
receive m from p: stmt
。它处理来自
p
且与模式
m
匹配的未处理消息。若匹配成功,
m
和
p
中的未绑定变量会分别绑定到消息的相应组件和消息发送者,然后执行
stmt
。为了使消息处理与本地计算同步,
receive
处理程序仅在
yield
点执行。程序中任何语句前后的点都可声明为
yield
点,并且每个
await
语句前有一个隐式的
yield
点,用于在等待时处理消息。默认情况下,
yield
点可处理任意数量的待处理消息。
1.2 同步处理
使用
await
语句,形式为
await cond1: stmt1 or ... or condk: stmtk timeout t: stmt
。它会等待,直到
cond1, ..., condk
中有一个为真,或者时间
t
已过,然后从条件为真的
stmt1, ..., stmtk, stmt
中随机选择一个执行。每个分支都是可选的。
2. 同步条件的高级查询
DistAlgo 提供了在
await
语句中表达同步条件的构造,可将其作为对消息历史(或其他集合、序列)的高级查询。查询可以是存在量化、全称量化、推导式或聚合操作。
-
存在量化
:形式为
some v1 in s1 , ..., vk in sk | cond
。当且仅当对于满足所有
vi in si
子句的变量值的某些组合,
cond
成立时,返回
true
。
-
全称量化
:与存在量化类似,只是将关键字
some
替换为
each
。
-
推导式
:形式为
{e: v1 in s1 , ..., vk in sk, cond}
。它返回对于满足所有
vi in si
子句和条件
cond
的变量值的所有组合,
e
的值的集合。
DistAlgo 会自动维护每个进程发送和接收的消息历史,分别存储在变量
sent
和
received
中,若未使用则会自动消除。
3. 配置
可以使用 DistAlgo 的
configure
语句指定诸如使用逻辑时钟、使用可靠和 FIFO 通道等要求的配置。例如,
configure clock = Lamport
指定使用 Lamport 的逻辑时钟,它会配置消息的发送和接收以更新时钟值,并定义一个返回时钟值的函数
logical_time()
。
4. 使用增量化创建变体
算法变体因它们维护的不同高级不变量以及维护这些不变量的不同方式而有所不同。通过将昂贵的查询转换为高级不变量,并使用系统的增量化方法,可以生成高效的算法,以增量方式维护查询结果。每种维护不变量的方式组合形成一个算法变体。
4.1 示例:Lamport 分布式互斥算法
该算法用于多个进程互斥访问共享资源(即临界区),同一时间最多只有一个进程可以处于临界区。每个进程在 DistAlgo 中的实现如下:
# 进程设置
s = ... # 进程集合
q = ... # 请求队列
# 运行互斥任务
# 发送请求并添加到 q
send request to all in s
q.add((t, self))
# 等待条件
await (t, self) before each (t2, p2) in q and
received an ack with t2 > t from each p2 in s
# 进入临界区执行任务
do task in critical section
# 移除请求并发送释放消息
q.remove((t, self))
send release to all in s
# 接收请求或释放消息的处理
receive request:
send ack
q.add(request)
receive release:
send ack
q.remove(release)
await
中的两个条件是该算法确保互斥的关键,其余部分主要是基本的消息发送和接收以及
q
的记录。
4.2 增量化
增量化将查询和更新转换为以增量方式维护高级不变量,包括中间和辅助值的不变量,从而产生多样化的算法。对于上述示例,
await
中的两个条件是查询,包括三个量化操作(其中两个是嵌套的);对
s
和
q
的赋值和记录以及在
receive
处理程序中隐式添加到
received
是更新。
-
直接算法
:使用
for
循环进行迭代计算查询。
-
增量算法
:在更新时维护查询结果,并在需要时查找结果。增量化算法不仅维护查询结果的高级不变量,还维护所需的中间和辅助值的不变量。可以使用替代不变量,从而产生更大的多样性。
例如,上述示例中第 10 行的条件可以转换为:
count {p2: p2 in s, ('ack', t2, p2) in received, t2 > t} = count s
然后,使用变量
responded
、
number
和
total
分别保存集合值和两个计数值,形成三个不变量,进一步转换为:
number = total
变量
total
在
s
设置时计算,
responded
和
number
在请求时计算,并添加以下
receive
处理程序:
receive ('ack', t2, p2):
# 新消息处理程序
if t2 > t:
# 条件中的比较
if p2 in s:
# 条件中的成员资格检查
if p2 not in responded:
# 添加前的测试
responded.add(p2)
# 添加到 responded
number +:= 1
# 增加 number
这个结果算法与直接迭代处理嵌套量化操作有很大不同。第 10 行的条件也可以转换为两个嵌套的计数查询,第 9 行的条件可以转换为计数查询或使用
min
的聚合查询,从而产生不同的增量维护算法。
5. DistAlgo 的同步执行
多样化进程是具有变体的进程,系统可能包含多样化和非多样化进程的混合。为每个多样化进程创建一个网关进程,它将多样化进程的变体呈现给系统的其余部分,使它们看起来像一个单一进程。网关拦截并转发多样化进程所有变体的所有入站和出站消息。同步执行框架由两部分组成:
5.1 自动化程序转换
-
确保消息通过网关路由
:将所有对 DistAlgo 的
send方法的调用替换为对send_sync的调用,并在每个进程类中插入该方法的定义。send_sync将原始消息及其原始目的地发送到网关,并将消息中变体的进程 ID 替换为网关的进程 ID。 -
在
yield点插入与网关的同步 :在每个yield点插入对yield_sync(block, timeout)的调用,并在每个进程类中插入该方法的定义。yield_sync会增加num_yields,将包含block、timeout和num_yields的yield消息发送到网关,并等待来自网关的yield-reply消息后返回。
5.2 网关运行的算法
该算法确定何时转发消息以及何时报告分歧(即行为差异)。当报告分歧时,系统可能会启动特定于应用程序的防御措施。
5.2.1 处理出站消息
转换将所有对
send
方法的调用替换为对
send_sync
的调用,
send_sync
方法会将原始消息和目的地发送给网关,并将消息中的变体进程 ID 替换为网关进程 ID。网关为每个变体的未转发出站消息分别维护一个 FIFO 队列。当所有队列都非空时,比较队列头部的消息(包括目的地)。如果它们相同,则从所有队列中出队并转发一个副本;否则,报告分歧。为确保活性,如果某个分歧变体未能发送消息,一旦一个队列非空,网关会等待有限时间让所有队列非空,若超时则报告分歧。
5.2.2
yield
点和
await
语句的同步
转换在每个
yield
点插入对
yield_sync(block, timeout)
的调用。
block
表示
yield
点是否与
await
语句相关,
timeout
是
await
语句中的超时时间(若存在)。对于有超时的
await
语句,转换如下:
1 start_time = time.time()
2 while not (c1 or ... or ck):
3 elapsed = time.time() - start_time
4 remaining = t - elapsed
5 if remaining ≤0:
6 break
7 yield_sync(True, remaining)
8 if c1: s1
9 elif c2: s2
10 ...
11 elif ck: sk
12 else s
若
await
语句没有超时,则省略第 1、3 - 6 和 12 行,
yield_sync
的第二个参数为
None
。
5.2.3 处理入站消息
当网关接收到入站消息
m
时,将其存储在未转发入站消息队列中,等待直到从所有变体接收到具有相同
num_yields
的
yield
消息,然后将所有未转发入站消息转发给所有变体并出队,最后向所有变体发送
yield-reply
消息。在转发给变体
p
的消息副本中,网关将自身的进程 ID 替换为
p
的进程 ID。若网关从所有变体接收到
yield
消息,但没有入站消息要转发,则根据
yield
消息中的
block
和
timeout
值决定行为:
- 若
block = False
,则向所有变体发送
yield-reply
消息,允许它们继续执行。
- 若
block = True
且
timeout = None
,则等待直到接收到并转发入站消息后再发送
yield-reply
消息。
- 若
block = True
且
timeout
是一个数字,则在接收到并转发入站消息或时间
timeout
过去后发送
yield-reply
消息。
5.2.4 进程创建
程序转换读取配置文件,指定哪些进程类型是多样化的以及它们的变体类型。对于每个多样化进程类型
P
,生成一个网关类型
GatewayP
,并将类型为
P
的进程创建语句转换为创建
GatewayP
的实例。
GatewayP
的
setup
方法会创建每个指定变体类型的实例,并将网关的进程 ID 作为额外参数传递给变体的
setup
方法。
5.3 宽松同步
上述方法在每个同步点为多样化进程的变体引入了屏障同步,确保最及时地检测到分歧。另一种方法是允许一个变体(“领导者”)领先,尝试使其他进程(“追随者”)的行动与领导者的行动一致(例如,在相应的
yield
事件中传递相同数量的消息),并在无法做到时报告分歧。这种方法可能会提高速度,但允许分歧的领导者在其分歧被检测到之前执行分歧行动;当这种情况不可接受时,在追随者赶上并同意这些行动之前,不应允许这些行动产生外部可见的影响。
5.4 允许消息模式的差异
为了允许更大的多样性,可以放宽要求,即进程的所有变体发送的相应消息必须相同。
-
相同通信模式但不同消息内容
:修改网关,当目的地是多样化进程时,省略对出站消息的相等性检查。此时,网关向另一个网关发送一个包含每个变体消息的数组,该网关将数组中的每个消息转发给其相应的变体。
-
不同通信模式
:配置文件可以指定某些类型的消息是不同步的。当网关从其第
i
个变体接收到不同步类型的消息时,立即将该消息转发到目的地的网关,该网关将消息转发给其第
i
个变体。
6. 多样性指标和运行时监控工具
6.1 代码多样性
由于多样性是相似性的补集,使用成熟的文档相似性指标(即带筛除的 n - 元语法相似性)来衡量代码多样性。该指标应用于 Python 字节码,即编译程序中的字节码指令序列。字节码相似性比源代码级相似性更相关,因为 Python 级别的多样性旨在提高对运行时系统缺陷的弹性,而字节码是运行时系统使用的程序表示。
-
n - 元语法
:是,从任何位置开始的
n
个连续指令的序列。算法计算编译程序中每个
n
- 元语法的哈希值,然后选择这些哈希值的一个子集并存储在一个称为程序指纹的集合中。选择的哈希值数量由算法参数
w
(窗口大小)间接控制。大小为
w
的窗口由程序中
w
个连续
n
- 元语法的哈希值组成。筛除算法保证从每个大小为
w
的窗口中至少选择一个哈希值。
-
归一化
:为确保鲁棒性,对每个
n
- 元语法内的变量索引进行归一化,将绝对行号替换为占位符。
-
量化代码多样性
:将两个程序的代码多样性(和相似性)量化为 1 减去其指纹的 Jaccard 相似性。Jaccard 相似性为
|S ∩ T|/|S ∪ T|
,使用 1 减去 Jaccard 相似性,更大的值表示更大的多样性。
另一种替代方法是使用 Levenshtein 距离(即编辑距离,将一个字符串转换为另一个字符串所需的单元素插入、删除和替换的最小数量),但它不太适合,因为它对编译程序中可能在运行时不重要的字节码顺序敏感。
6.2 代码多样性计算流程
graph TD;
A[编译程序] --> B[计算 n - 元语法哈希值];
B --> C[使用筛除算法选择哈希子集];
C --> D[形成程序指纹];
D --> E[计算 Jaccard 相似性];
E --> F[计算代码多样性(1 - Jaccard 相似性)];
6.3 同步执行框架总结
| 功能 | 操作步骤 |
|---|---|
| 处理出站消息 |
1. 替换
send
为
send_sync
;2.
send_sync
发送消息和目的地到网关,替换进程 ID;3. 网关比较队列头部消息,相同则转发,不同则报告分歧
|
yield
点和
await
同步
|
1. 插入
yield_sync
调用;2.
yield_sync
增加
num_yields
,发送消息到网关并等待回复;3. 转换
await
语句以保留总等待时间
|
| 处理入站消息 |
1. 网关存储入站消息;2. 等待相同
num_yields
的
yield
消息;3. 转发消息并发送
yield-reply
消息
|
| 进程创建 | 1. 读取配置文件;2. 生成网关类型;3. 创建网关实例并传递进程 ID 给变体 |
6.4 不同同步方法对比
| 同步方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 严格同步(屏障同步) | 最及时检测分歧 | 可能降低系统速度 | 对分歧检测及时性要求高的场景 |
| 宽松同步(领导者 - 追随者模式) | 可能提高系统速度 | 允许分歧领导者提前行动 | 对速度要求较高,且分歧行动影响可接受的场景 |
6.5 消息模式处理方式对比
| 消息模式情况 | 处理方式 | 说明 |
|---|---|---|
| 相同通信模式但不同消息内容 | 网关省略相等性检查,发送消息数组 | 适用于算法变体有相同通信模式但消息内容不同的情况 |
| 不同通信模式 | 配置文件指定不同步消息类型,直接转发 | 适用于算法变体通信模式不同的情况 |
7. 总结与展望
7.1 核心要点回顾
本文围绕算法多样性在弹性系统中的应用展开,主要涵盖以下核心内容:
1.
消息处理控制流
:介绍了异步和同步处理消息的方式,包括
receive
定义和
await
语句的使用。
2.
同步条件查询
:DistAlgo 提供了多种高级查询构造,如存在量化、全称量化和推导式,用于在
await
语句中表达同步条件。
3.
配置管理
:通过
configure
语句可以指定系统的各种配置,如逻辑时钟的使用。
4.
算法变体创建
:使用增量化方法将昂贵查询转换为高级不变量,从而创建不同的算法变体。以 Lamport 分布式互斥算法为例,展示了增量化的应用。
5.
同步执行框架
:包括自动化程序转换和网关算法,实现多样化进程的同步执行,并介绍了宽松同步和处理不同消息模式的方法。
6.
多样性指标
:使用带筛除的 n - 元语法相似性衡量代码多样性,避免了 Levenshtein 距离的不足。
7.2 未来发展方向
- 算法变体的进一步优化 :虽然增量化方法已经能产生多样化的算法,但仍有进一步优化的空间。例如,可以探索更高效的不变量维护方式,减少计算开销。
- 同步执行框架的改进 :当前的同步执行框架在处理复杂场景时可能存在性能瓶颈。未来可以研究更灵活的同步策略,提高系统的整体性能和可扩展性。
- 多样性指标的完善 :目前的代码多样性指标主要基于字节码,可能无法完全反映算法的语义差异。可以考虑结合更多的信息,如程序的执行路径、数据依赖等,来完善多样性指标。
- 实际应用的拓展 :将算法多样性的理论和技术应用到更多的实际场景中,如分布式系统、云计算等,验证其在不同环境下的有效性和可靠性。
7.3 实践建议
如果您希望在实际项目中应用算法多样性的相关技术,可以按照以下步骤进行:
1.
理解需求
:明确项目对弹性和多样性的需求,确定是否需要使用算法变体和同步执行框架。
2.
选择合适的算法
:根据项目的特点和需求,选择合适的基础算法,并使用增量化方法创建算法变体。
3.
配置系统
:使用
configure
语句配置系统的相关参数,如逻辑时钟、消息通道等。
4.
实现同步执行
:按照同步执行框架的要求,进行自动化程序转换和网关算法的实现。
5.
监控和评估
:使用多样性指标对代码进行监控和评估,及时发现和处理分歧情况。
通过以上步骤,您可以在项目中有效地应用算法多样性技术,提高系统的弹性和可靠性。
7.4 整体架构流程图
graph LR;
A[消息处理] --> B[同步条件查询];
B --> C[配置管理];
C --> D[算法变体创建];
D --> E[同步执行框架];
E --> F[多样性指标];
F --> G[实际应用];
G --> A;
这个流程图展示了整个算法多样性系统的架构,各个部分相互关联,形成一个闭环。从消息处理开始,经过同步条件查询、配置管理等步骤,创建算法变体并实现同步执行,通过多样性指标进行监控和评估,最终应用到实际场景中,而实际应用的反馈又可以促进消息处理等环节的改进。
总之,算法多样性为构建弹性系统提供了一种有效的方法。通过合理应用本文介绍的技术和方法,可以提高系统的可靠性和适应性,应对各种复杂的情况。希望本文能为您在相关领域的研究和实践提供有价值的参考。
超级会员免费看
959

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



