Rust正在成为各种领域的一流语言。 在Discord,我们已经看到Rust在客户端和服务器端都取得了成功。 例如,我们在客户端将其用于Go Live的视频编码管道,在服务器端将其用于Elixir NIF。 最近,我们通过将服务的实现从Go切换到Rust来极大地提高了服务的性能。 这篇文章解释了为什么重新实现服务对我们有意义,它是如何完成的,以及由此带来的性能改进。
读取状态服务
Discord是一家专注于产品的公司,因此我们将从一些产品上下文入手。 我们从Go切换到Rust的服务是"读取状态"服务。 其唯一目的是跟踪您已阅读的频道和消息。 每次您连接到Discord,每次发送消息和每次阅读消息时,都会访问"读取状态"。 简而言之,"读取状态"正处于热销中。 我们要确保Discord始终都感觉超级敏捷,因此我们需要确保Read State快速。
通过Go实施,Read States服务不支持其产品要求。 在大多数情况下,速度很快,但是每隔几分钟,我们就会看到大量的延迟峰值,这不利于用户体验。 经过调查,我们确定峰值是由于Go的核心功能:其内存模型和垃圾收集器(GC)引起的。
为什么Go无法达到我们的绩效目标
为了说明Go无法达到我们的性能目标的原因,我们首先需要讨论服务的数据结构,规模,访问模式和体系结构。
我们用来存储读取状态信息的数据结构通常称为"读取状态"。 Discord拥有数十亿个读取状态。 每个用户每个通道有一个读取状态。 每个读取状态都有几个需要自动更新的计数器,通常需要将其重置为0。例如,计数器之一是一个通道中有多少个提及。
为了获得快速的原子计数器更新,每个读取状态服务器都具有读取状态的最近最少使用(LRU)缓存。 每个缓存中有数百万个用户。 每个缓存中有数千万个读取状态。 每秒有数十万个缓存更新。
为了保持持久性,我们使用Cassandra数据库集群支持缓存。 在驱逐缓存键时,我们将您的读取状态提交到数据库。 每当读取状态被更新时,