欢迎光临
我们一直在努力

脑洞大开!Rust 借用检查和流计算也能扯上关系?

没有瞎掰,而且本文打算跟你分享的还是相当深刻的领悟。由于我曾在大数据领域工作过十余年,对流计算十分了解,在学习 Rust 借用检查的权限模型运作机制时,我发现两个领域有一些相通的“思想”,并且这种思想能很好地帮助新人理解 Rust 借用检查,这篇文章要聊的就是这个看似离奇实则深刻的话题。不过,这两个领域确实差得有点远,可能很多学习 Rust 的朋友没有大数据背景,但这也不是个大问题,因为我们讨论的是背后的“思想”而非“技术细节”,所以,大家可以放心阅读,但如果你了解流计算的话,那确实能更容易的 get 到本文想要表达的那个“点”。

从哪里说起呢?还是得从 Rust 借用检查的权限模型说起。我们知道:使用 Aquascope 生成的标注反映的是 Rust 编译器对一段代码的真实分析过程,是对 Rust 借用检查工作方式最真实的呈现。下图就是使用 Aquascope 生成的一份代码标注示例:

完全看懂这些标注并不是一件容易的事情,因为这些符号背后隐藏了很多 Rust 借用检查的机制和规则,这也是为什么专门介绍这些符号的《你真得懂 Rust 借用检查吗?一文聊透 Borrow Checker 底层逻辑:权限模型》一文有 1.2 万字和 7 张配图的原因。对于这些符号的解读,我们不能在本文复述,需要大家自己去研读这篇文章,本文我们着力要解读的是:为什么说 Rust 借用检查的“全部秘密”都藏在了这些符号的前后变化中?

我们假设各位已经理解了 Aquascope 标注系统中的值、引用以及解引用的 R、W、O 权限的含义,此时,单纯地去解读上面示例中的符号并不足以让我们理解整个借用检查的工作方式,真正能读懂它们的“窍门”是要动态地去看待这些符号的“变化”,而这就引出了“流计算”这个话题。

在大数据领域,流计算是专门用来对数据进行实时处理的一种计算模式,一些知名的流计算引擎有 Flink 和 Spark Structured Streaming。什么是流计算呢?简单地说就是原始数据产生后,会在极短的时间内被数据采集工具收集并推送到流计算引擎中,流计算引擎会以独特的“流处理模式”进行处理,数据从产生到处理完毕只有秒级甚至亚秒级的延迟。比如:一个实时的监控系统会通过 agent 从成千上万台服务器中收集各种 metrics (性能指标)数据,然后它把这些数据发送给流处理引擎,后者会对 metrics 进行实时分析,判断目标服务器是否正面临 CPU 利用率过高、内存不足等问题。

在流计算中,有一个重要的概念叫“状态”(State),每次流入的数据会催生流计算引擎创建并更新与这种数据有关的“状态信息”,后续流入的数据也会根据这个“状态信息”进行相应的处理。有点抽象是吧?举个例子就明白了,还是以实时监控系统为例,当一台服务器的 CPU 利用率持续 5 分钟超过 90% 时,agent 会发出一条 alert 消息,这个消息也会被发送到流处理引擎中,流处理引擎发现这条消息后,必须在流上建立并维持一个对应这台服务器的“状态”数据,一种标识这台服务器当前正处于告警中的数据结构,在这个数据结构中除了有告警信息和时间戳之外,还有一个告警状态字段,值是 OPEN。流处理引擎需要持续维持这个状态信息,因为当服务器处于告警状态时,针对很多 metrics 的处理方式和规则都会发生变化,当服务器的 CPU 利用率将至 90% 以下并维持一定时间后,agent 会次发送一个 alert 解除的消息,同样的,它也会被发送到流处理引擎中,流处理引擎接收到这条消息后,会将这台服务器对应的“状态”信息中的告警状态字段改为 CLOSED,相关 metrics 的处理方式和规则会恢复到常规模式。

那这和 Rust 的借用检查有什么关系呢?我们以《你真得懂 Rust 借用检查吗?一文聊透 Borrow Checker 底层逻辑:权限模型》一文使用的一个示例来解释它们之间的关系:

每当我们添加一个引用都可能会改变值的权限状态,而值现在的权限状态又决定着下一个引用能否创建成功,所以 R、W、O 的本质是什么呢?它们其实是一个值在可读、可写、可拥有三个属性上的“标志位”,每一个属性无外乎“有”或“没有”两种状态,而决定这些标志位状态的是借用规则,每当创建一个新的引用时编译器会检查这个标志位,以判断能否创建它(是否违反了借用规则),创建之后,还要根据借用规则相应地修改状态位,以便能如实地反映值当前最新的权限状况,在下一次创建引用时需要再次使用最新的权限状态结合引用的类型(可变还是不可变)作出判断,在引用失效时也会同步更新相关的状态位。

这个过程像极了流计算中的“状态”(State):在一个作用域中,一个值的 R、W、O 权限就像是在流计算中维持的“状态”,编译器一行一行地解析代码时就如同流处理引擎在流上处理流过的一条一条数据,当一个可变值被创建时,流处理引擎会为它创建一个“状态”,这个“状态”包含三个“标志位” ,分别对应着它的 R、W、O 权限,初值都是 true,此后在流过的一行一行代码中,所有从这个值派生出来的引用都会被 group by 到同一个分组,然后,每一行创建引用的代码都会影响到这三个 “标志位”,流计算引擎要不断地过滤出这些语句,根据创建引用的类型(可变还是不可变)和状态更新规则(也就是借用规则)来实时更新 R、W、O 的值,比如:假设当前流过了一个可变引用,流计算引擎(编译器)会首先检查 W 标志位是不是 true,确定是 true 之后才接受这个操作,同时立即将 R、W 两个标志位改为 false,因为“状态”的计算规则要求:如果已经出现了一个可变引用,就不能再有第二个可变引用和任何的不可变引用,如果之后又流过来一个不可变引用,流计算引擎(编译器)也是先检查“状态”,发现 R 标志位现在是 false,这就意味着当前这条数据(这行代码)是一个异常数据(一行错误代码)。

如果你并不了解流计算,我们也可以用“朴素”的语言把 R、W、O 权限的实质给描述出来:R、W、O 其实是编译器在逐行解析代码时,针对一个值建立起来的一个“状态”,这个状态从值被创建到离开作用域之前一直存在并会持续变化,它有三个 bool 类型的“标志位”,分别代表这个值当前是否拥有 R、W、O 三个权限。这三个标志位会随着引用的创建和失效而持续更新,更新的逻辑就是借用规则。比如:一个新值创建是、它的 R、W、O 三个标志位全部都是 true,当编译器遇到这个值的一个可变引用时,它会先检查值现在的 W 标志位是不是 true,如果是,那就意味着可以接受这个可变引用的创建,同时,会立即将这个值的 R 和 W 标志位改为 false,因为如果已经出现了一个可变引用,就不能再有第二个可变引用和任何的不可变引用。假设后面又遇到了一行代码要创建这个值的一个不可变引用,编译器一样会先检查值的 R 标志位,发现它现在是 false 时,就直接报错了,因为这一行代码违反了借用检查规则:当一个值已经有了可变引用后(值已不可读且不可写),还试图创建一个不可变引用。好了,相信到这里,你应该能非常准确而透彻地理解 R、W、O 权限的实质了!

以上就是 Rust 借用检查和流计算之间的“秘密”,如果你对部分内容感到困惑,建议完整地阅读一下《你真得懂 Rust 借用检查吗?一文聊透 Borrow Checker 底层逻辑:权限模型》一文。

赞(0)
未经允许不得转载:171主机测评 » 脑洞大开!Rust 借用检查和流计算也能扯上关系?
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址