Skip to content

谈谈 tokio::select! 的公平性

Published: at 23:47

ToC

前言

Pingora 的源码阅读中,我不止一次看到有这样的用法:

pingora-proxy/src/lib.rs
let res = tokio::select! {
biased; // biased select is cheaper and we don't want to drop already buffered requests
res = downstream_session.read_request() => { res }
_ = self.shutdown.notified() => {
// service shutting down, dropping the connection to stop more req from coming in
return None;
}
};

于是正好借着这个机会,来看看 tokio::select! 的一些知识(常识)。

公平性(Fairness

我们知道,Rust 的异步运行时是需要通过 poll 的方式获取每一个 Future 的实际运行状况的。而对于 select! 这种需要「同时」执行多个 FutureBuilder 并同时 poll 的实现,就需要考虑每个分支的执行顺序问题了。

我们以 JavaScript 举个不严谨的例子。假设有以下两个 async function

async function first() {
return 1;
}
function second() {
return new Promise(resolve => setTimeout(resolve, 1e3));
}

第一个 first 是立即返回的实质同步函数,对应了 Rustpoll 直接返回了 Poll::Ready 的情况;而第二个 second 则是等待一秒才会返回。如果我们这样实现 select

function select(...futures) {
while (true) {
for (const future of futures) {
const result = future();
if (result.is_resolved()) {
return result.value;
}
}
}
}
select(first, second);

由于 first 的实现每次都是立即返回,所以这个 select 的实现永远都不会返回 second 的结果。

例子中我们给出的 first 实现是立即返回,但对于实际的实现而言,这个条件可能更宽松。只要排在 select 前面的函数较快返回的概率比排在后面的函数更大,后面的函数返回结果的几率就会显著小于排在前面的函数,造成一定程度的不公平性。而如果是像我们例子里这种立即返回,那么排在后面的函数就永远没有出头之日,造成某种程度的「饿死」。

因此,tokio::select! 的实现对于位于不同「顺位」的 Future 采取了随机选择的方式,以尽量减少这类情况的出现。

biased 是什么?

随机固然保证了一定程度的公平,但其本身就存在一定的开销。此外,对于某些场景,可能也会希望「顺序执行」的稳定性。biased 就是为了这种情景诞生的。还是回到 Pingora 的这个例子:

pingora-proxy/src/lib.rs
let res = tokio::select! {
biased; // biased select is cheaper and we don't want to drop already buffered requests
res = downstream_session.read_request() => { res }
_ = self.shutdown.notified() => {
// service shutting down, dropping the connection to stop more req from coming in
return None;
}
};

如果我们希望在处理 shutdown 之前,把已经解析完的所有 request 都通过 read_request 读取出来,此时再使用随机的策略就不大合适了。在这种情况下,我们可以在 select! 的最开头加上 biased;,这样 tokio::select! 就会保证选择的顺序执行,也就能实现我们想要的效果了。

这个写法怎么那么像 “use strict”;

参考


Previous Post
Learning Pingora 05 - Connect with TLS
Next Post
How To Blog 04: The Astro v5 Era