小红书技术REDtech

小红书自研keyless支持HTTPs卸载

Image

本文系统介绍了小红书基础技术团队自研的 keyless 架构实现,主要涵盖几个方面:Intel QAT 硬件选型与性能调优、Rustls 异步化支持,高性能 keyserver 实现等,该方案已经承接了小红书自建 IDC 公网接入层流量,在大幅提升 HTTPs 处理能力的同时,降低了服务器资源成本。方案的整体思路和技术实现,对业界有类似 HTTPs 卸载需求的场景,也具备很大的参考意义。

Image

随着小红书自建 IDC 持续建设,自建公网接入层逐渐承接了线上流量,面临着端上数百万级别 QPS 请求压力。端上流量主要包含两大类:HTTPs(基于 TLS 1.3)和 HTTP/3(基于 QUIC),都涉及私钥签名操作,该操作是 TLS/SSL 握手中比较慢的一个环节,需要占用较多服务端 CPU 资源。

在这样的背景下,接入层团队通过自研 keyless 方案,将大量消耗 CPU 的私钥 sign 操作,远程卸载到配置 QAT 硬件的加解密集群,该方案提供了 TLS 加速能力的同时,大幅度降低资源成本,解决了堆砌大量机器应对非对称加解密的困境,同时保持接入层的弹性部署能力。

Image

2.1 QAT 介绍

Intel CPU 内置很多加速器,其中,QAT 可以用来大幅度加速 CPU 网络处理性能,包括:编解码、压缩与解压缩、对称与非对称加解密等,不同的 CPU 架构、QAT 加速器性能存在一定差异:

Image
Image

2.2 硬件选型

这里也分享一个硬件选型的经验:并不是 QAT 设备越多、性价比越高,这里针对 4516Y+ 和 6554S 两款 CPU 做一个对比:

Image

Intel 4516Y 在设计上是 MCC(单 Die 架构),QAT 加速器性能在加解密性能上超过 XCC(多 Die 架构),Intel 6554S 虽然内置了 8 * QAT ,并不一定比 4516Y 的 2 * QAT 性价比更高。

QAT Engine(Quick Assist Technology Engine) 加速分为「硬件加速」和「软件加速」两部分,内置的加速机制会优先使用硬件加速,QAT 硬件加速满载后自动切换到软件加速。

在实际硬件选型中,需要综合考虑 QAT 资源配比、硬件采购价格、整机吞吐能力等因素,从而得到性价比最好的机型选型。

2.3 性能调优

这里介绍几个与 QAT 相关的调优选项:

  1. QAT Engine 开启 HW 和 SW 加速,内置加速机制实现自动切换,提升节点资源利用率:

    https://github.com/intel/QAT_Engine/blob/master/docs/config_options.md#qat_sw-options

  2. QAT Engine 内部默认使用全局内存锁,导致多核扩展性比较差:

    https://github.com/intel/QAT_Engine/issues/138

  3. QAT 驱动调整 ServicesEnabled 配置,去掉不需要的模式,节省硬件资源和 CPU 占用:

    https://intel.github.io/quickassist/PG/configuration_files_generalsection.html?highlight=servicesenabled

  4. QAT Engine 支持开启 debug 模式,可以输出完整的日志信息,便于调试和问题定位:

    https://github.com/intel/QAT_Engine/blob/master/docs/config_options.md#qat_sw-options

Image
Image

keyless 架构整体分为两个部分,keyclient 和 keyserver:

  1. keyclient 实现非对称加解密操作的异步化:

    a. keyless 协议:通过 keyless protocol 消息与 keyserver 交互;

    b. TLS 异步化:借助 Rust 语言优势,实现异步化私钥 sign 操作。

  2. keyserver 实现用户态高性能网络服务端:

    a. keyless 协议:处理 keyclient 侧请求的 keyless protocol 消息;

    b. 异步任务调度:充分利用 CPU 并行能力,高性能处理卸载请求;

    c. 加解密计算卸载:基于 Intel QAT 卸载加解密、降低 CPU 成本。

Image

4.1 keyless 协议

keyless protocol 用于 keyclient 和 keyserver 之间通信,沿用 cloudflare 所定义的格式能够满足需求:

Image

4.2 keyclient 侧

keyclient 基于 Rust 语言开发,通过修改 rustls 标准库,使其提供 TLS 异步模式,从而支持 QUIC-TLS 、TCP-TLS 协议卸载能力,具备 keyless 远程卸载、本地卸载兜底等能力,充分保证功能高可用:

Image

4.3 keyserver 侧

4.3.1 模块分层

考虑到 openssl async 模块与 QAT Engine 高效交互、高性能网络处理、异步任务调度等因素,内部在对比调研后,采用自研 keyserver 实现,最大程度做到高性能处理:

Image

1. keyserver 层:多线程 epoll,高性能接收 RPC 消息、处理 QAT 回调事件;

2. openssl 层:借助 fiber 机制将加解密操作异步化、调用 libcrypto 计算;

3. QAT 驱动层:将加解密操作放入硬件队列,完成后回调通知上层应用;

4. QAT 设备层:硬件加解密计算,中断通知 QAT 驱动,进而传递到应用层。

4.3.2 异步框架

4.3.2.1 任务抽象

openssl 通过 ASYNC Job 提供了异步处理机制,一个 job 代表一个协程,在 SSL 卸载场景下,一个 job 代表一次调用 QAT 硬件加速卡操作,此协程可以执行开始、挂起、释放等操作,相关接口如下:

Image

ASYNC_start_job 和 ASYNC_pause_job 是最常使用的两个接口:前者用于开始一个 job,或者恢复一个 job,后者用于暂停 job(该协程会被 yield,切换到其他协程)。

4.3.2.2 通知机制

某个 job 被暂停、如何触发调度 job 重新运行,以及 job 完成,如何通知上层事件完成:

1. eventfd

常用接口如下:

Image
  1. 用户态代码创建一个 fd,调用 ASYNC_WAIT_CTX_set_wait_fd() 将 fd 加入到 ASYNC_WAIT_CTX 的 fds 中,一般在 ASYNC_pause_start() 之后、ASYNC_pause_job() 之前调用。

  2. 用户态代码通过对应的 get 函数获取到这个 fd,并将其加入 epoll 中,感知到 fd 上的 EPOLLIN/EPOLLOUT 事件,从而恢复被暂停的 job 。

2. callback

当 QAT 设备完成异步操作之后调用回调函数去通知用户代码,回调函数需要是小的且非阻塞,常用接口如下:

Image

3. 总结

无论是通过写event fd、还是调用 callback,都是借助 QAT 驱动进行的,卸载操作的完成通知都是从底层 QAT 设备回传到用户态:

  • QAT设备: 中断 -> QAT Driver/Library: Callback (write fd/callbackFn) -> keyserver (epoll轮询事件)。

4.3.3 QAT加速

涉及到跟 openssl、QAT engine 的交互处理,libcrypto 里实现了相对完整的异步框架,对上层屏蔽了较多的 QAT 交互细节,keyserver 也基于 libcrypto async 框架实现:

Image

单次 sign 卸载操作对应的异步处理过程,如下图所示:

Image
  1. keyserver 接收 RPC 消息,需要异步操作:

    a. 启动 ASYNC_start_job 开启一个 job、切换到 job 执行;

    b. 针对当前 job/ctx 设置 async fd,用于事件完成通知操作。

  2. 进入 event lop 循环,监听 fd 上的 EPOLLIN/EPOLLOUT 事件或者 QAT 通过 callback 回调,通知应用层事件完成。

  3. keyserver 回复 RPC 消息,回收 async fd 资源、释放 job。

Image

单 keyserver 节点,支持 30w+/s 的 sign 卸载操作,这里包含「硬件加速」和「软件加速」两部分,此时两个 QAT 设备满负载、CPU 加速跑满 32 个物理核:

Image

内部经过对比测试,与 rustls 纯软件处理 TLS 相比,转发成本降低5倍以上。

Image

keyless 架构最初由 cloudflare 提出,在业界已经有过不少实践,但在实际场景落地过程中,仍面临着一些技术挑战:传统的 TLS 库难以实现异步化、QAT 加速缺少成熟的开源项目作为参考等,导致 keyless 卸载落地有一定的门槛。

当前方案是基于「标准 keyless 协议」 + 「Rust 支持 TLS 异步化」 + 「QAT 卸载高性能 keyserver」,能够给业界提供一些比较有价值的参考,后续还会对当前方案做进一步的完善和迭代:

  1. keyless 实现优化:优化为基于 UDP 实现,减少 TCP 链接带来的通信开销;

  2. keyserver 性能优化:内存以及文件描述符等资源池化、减少系统调用开销;

  3. 支持 QUIC 场景:底层 QUIC-TLS 异步化、sign 操作远程卸载到 keyserver。

未来也计划将内部代码开源,包括:支持 TLS 异步版本的 rustls 库、支持 QAT 卸载的 keyserver 等。

Image

Core Contributors

雾行

网络系统方向

克林

网络系统方向

小宗

网络系统方向

轩宇

接入网关方向

露卡

接入网关方向

Image

QAT Engine:

https://github.com/intel/QAT_Engine

QAT manual:

https://intel.github.io/quickassist/index.html

QAT resources:

https://www.intel.com/content/www/us/en/developer/topic-technology/open/quick-assist-technology/resources.html

图片