-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nightcore: Efficient and Scalable Serverless Computing for Latency-Sensitive, Interactive Microservices #8
Comments
IntroductionServerless 提出的模型使得 FaaS 系统具有较高的调用延迟开销,如下表展示了在排去冷启动影响下调用 nop 函数的延迟: 本文提出了现有 FaaS 系统目前还没达成的两个性能目标:
早前的一些研究,比如 Faasm, 通过利用了 software-based fault isolation(SFI),做到了 FaaS 的 runtime 开销达到毫秒规模,但是这个降低了函数间的隔离保证。本文提出的 Nightcore 在做到了类似 OpenFaaS 和 OpenWhisk 一样的容器隔离级别的同时达到上述性能目标。 以目前开源的 FaaS 系统 OpenFaaS 和 OpenWhisk 来举例,尽管数据中心的网络性能在提升,同一个 AWS region 下的两台 VM 的 RTT(round-trip time) 耗时大约在 101 毫秒到 237 毫秒【1】。因此,Nightcore 提出了使用内部函数调用的办法,Nightcore 将会有链式调用的函数放置在同一台机器上,消除了和 gateway 的通信开销,降低延迟。 使用这种策略意味着大多数通信都是本地的,意味着 IPC 通信必须高效。类似 gRPC 这样的库同样适用 IPC 通信(Unix sockets),但是 gRPC 协议大约带来了额外 10 毫秒的开销【2】。Nightcore 设计了一种自己的 message channels 用来 IPC 通信,它建立在 OS pipes 之上,并且传输固定 1KB 大小的消息,实验证明,Nightcore 的 message channel 发送 1KB 数据需要 3.4 毫秒,而 gRPC 的发送 1Kb 的 payload 需要 13 毫秒。 为了能够支持 100K 每秒的调用,Nightcore engine 使用 Event-based Concurrency,通过较少的系统线程 handle 大量并发 I/O 事件,实验证明大约 4 个 OS 线程就可以 handle 这样的需求。 总的来说,Nightcore,是一个具有微秒级别开销的 serverless function runtime,同时提供了基于容器的函数间的隔离。Nightcore 的设计仔细考虑了具有微秒级开销的各种因素,包括 function 请求的调度、通信原语、I/O 线程模型和并发函数执行。当运行对延迟敏感的交互式微服务时,Nightcore 实现了 1.36 倍到 2.93 倍的高吞吐量和减少了约 69% 的尾延迟。 |
DesignArchitecture整体的架构可以用下图非常清晰的表述: 强调两点:
Processing Function Requests上图描述了函数调用的例子,假设 FnX 调用 FnY,整体流程如下:
Managing Concurrency for Function ExecutionsNightcore 维护了一个 worker 线程池进行并发执行函数,但是线程池的大小是一个问题。一种明显的解决办法是每当需要就创建一个线程,从而最大化并发度。但是这种方法对于微服务来说是有问题的,一个函数可以被执行多次,这种方法很容易导致服务器超负载。 Nightcore 使用一个自适应的手段管理函数执行数量,保证高并发并防止过载。根据 Little's Law,理想并发度可以估算为平均请求率和平均处理时间的乘积。对于一个注册的函数 Fn,用 𝜆 表述调用率,用 𝑡 表示处理时间,那么并发度就是 𝜆𝑡,用 𝜏 表示。当收到 Fn 的请求时,只有当前 Fn 的并发执行少于 𝜏 时,engine 才会派发该请求,否则,请求排队,等待函数执行完成。 注意这里的“自适应”, 𝜏 是由 𝜆 和 𝑡 计算出来,而这两个值会随着时间发生变化,为了达到预期的并发水平,Nightcore 必须维护一个至少有 𝜏 的线程池,但是 𝜏 的动态性质使其值通常变化迅速,频繁的创建和终止线程是不可行的,为了调节 𝜏 的动态值,Nightcore 允许线程池中超过 𝜏 的线程,但只使用其中的 𝜏,当线程超过 2𝜏 时,终止额外的线程。下图展示了使用了这种方法带来的好处: |
Engine Implementation下图展示了 Engine 的事件驱动设计: 每个 I/O 线程保持固定数量到网关的持久性 TCP 连接,用于收发请求,message channel 以轮询的方式分配到 I/O 线程,共享数据结构(包括队列和 tracing) 通过 mutex 保护。 Event-Driven IO Threads:Nightcore 使用了 libuv,它是建立在 Message Channels:Message Channels 由两个 Linux pipe 实现,它们方向相反,从而形成一个全双工连接,同时,当内联有效载荷缓冲区不足以满足函数输入或输出时,共享内存缓冲区被使用。尽管共享内存允许 IPC,但它缺乏一种有效的机制来通知消费者线程何时有数据可用。 Nightcore 对 pipe 和共享内存的使用得到了两方面的好处。它允许消费者通过管道上的阻塞读取得到通知,同时,在传输大型消息有效载荷时,它提供了共享内存的低延迟和高吞吐量。 Nightcore在容器之间挂载一个共享的 tmpfs 目录,以帮助设置 pipe 和共享内存缓冲区。Nightcore 在共享的 tmpfs 中创建了 named pipe,允许 worker 进行连接。共享内存缓冲区是通过在 tmpfs 中创建文件来实现的,engine 和 worker 都会用 Computing Concurrency Hints:为了有效地调节并发函数的执行量,Nightcore Engine 为每个函数维护 𝜆 和 𝑡 。调用率的样本计算 |
这篇 paper 同样放到了 repo 中的相关部分。 |
你写的整篇文章的总结笔记我已经看完了 但有一些点我不是很理解,求指教 @linxuyalun 在 Event-Driven IO Threads 的章节中,每个 worker thread 是对 fd 事件进行轮询的,这不是很卡吗 在效果描述的文字中,它说道:同时使用共享内存和 pipe,让开销能够进一步降低。我理解这不是更卡了吗,怎么会 1+1 小于 1 呢,至少得等于 2 吧 |
提议一份 typo: nameed pipe -> named pipe |
我说一下我的大概理解吧,欢迎进一步讨论
|
Got it |
ASPLOS 2021
https://www.cs.utexas.edu/~zjia/asplos21main-p89-final.pdf
The text was updated successfully, but these errors were encountered: