高并发推荐系统架构设计
共 7733字,需浏览 16分钟
· 2022-09-17
本文由 InfoQ 整理自小年糕算法中台后端架构师封幼林在 QCon+ 大厂案例(2021 冬季北京站)的分享《高并发推荐系统架构设计》。
你好!我是封幼林,在小年糕负责推荐系统,主要从事服务架构相关工作。今天我要和你分享的话题是《高并发推荐系统架构设计》。
这次分享主要分为以下这几个部分:
推荐系统的基本架构;
旧版线上架构的并发瓶颈问题;
新版线上架构编程语言的选择;
印象深刻的 Go 踩坑经历。
我们进入第一部分,推荐系统的基本架构。第一个问题是,什么是推荐系统?以及为什么要有推荐系统?
推荐系统
经过近几年互联网应用的飞速发展,我们迎来了数据爆炸时代。以时下流行的短视频应用为例,平台上有海量的视频资源。当一个用户到来时,平台需要展示他感兴趣的内容,用户的体验直接关系到平台的盈利能力。所以推荐系统本质上是一个信息过滤装置,用来在数以万计的内容中筛选出最符合用户兴趣的那一少部分。通过推荐系统,用户可以发现自己感兴趣的内容,从而提升体验;平台也可以自动发现有价值的内容,从而提高效率并带来更多收益。
一个通用推荐系统的架构
一个通用的推荐系统,就是以物料数据和用户行为日志为输入,经过预处理和特征工程之后得到相应的特征数据,再用一些算法模型学习特征数据中的隐含知识,最后基于学到的知识实现精准的个性化推荐。整个架构中,包含离线部分和线上部分。
推荐系统的线上架构,才是真正处理用户请求的部分。
推荐系统 Online 架构
当一个请求到来时,首先经过 AB 分流模块,在这里,基于用户的某些特征应用哈希算法来进行分流,对分流后的用户应用不同的动态配置,主要用来支持不同策略的对比试验。AB 模块之后是多路召回,就是根据用户的特征在多个召回服务和 Redis 中并发获取数据。召回的数据经过过滤去重之后,就得到了本次推荐的粗略列表。再对这个列表应用模型排序,就得到了符合用户兴趣的个性化推荐结果。最后把结果返回给用户之前,还要根据具体业务规则,对结果进行一次重排序。关于推荐系统基本架构的介绍就到这里,后续内容都是基于推荐系统的线上架构展开的。
接下来进入第二部分,在这里,我要和你分享一个真实的并发瓶颈问题。
旧版推荐系统
我们的旧版推荐系统是使用 C++ 开发的,主要包含以下几个核心服务。
接口服务 API 实现了业务侧的接口,推荐引擎 engine 实现了推荐的主要流程,recall 和 predict 分别是召回和模型预测服务。API 服务主要实现了两个接口,feed 流推荐接口和详情接口。当一个 http 请求到来时,先经过 SLB 到达一台 ECS,再经过本机部署的 Nginx,转换为 FastCGI 协议到达我们的服务程序。服务程序采用的是多进程单线程模式,类似于 php-fpm。推荐接口依赖于后续的几个服务,而详情接口比较简单,就是读取 Redis 并返回而已。要说的这个问题就出现在 API 服务上。
有一天晚高峰,接口的最大延迟突然从 300 毫秒左右飙升至 30 秒,触发了一系列报警。监控平台显示,API 两个接口的延迟曲线基本一致。API 和 engine 的 CPU 使用率都没超过 50%,但是 predict 服务却几乎跑满了 CPU。于是我们紧急将 predict 服务翻倍扩容,扩容后负载和延迟都下降至正常水平。
事后复盘发现有一个问题无法解释,那就是不依赖于后续服务的详情接口为何也会延迟飙升。带着这个问题去分析程序底层框架的源码,发现 FastCGI 模块是基于 epoll 实现的,但是用来发起 http 请求以及用来读写 Redis 的模块,都是工作在阻塞式 IO 之下的。所以在并发量很高时,进程都因等待后续服务的响应而阻塞,无法处理新的请求,这才造成详情接口延迟飙升。
沿着这个问题,我们来对比一下阻塞式 IO 与 IO 多路复用的不同。
阻塞式 IO
首先来看阻塞式 IO,线程调用 recv 来接收数据,内核这边发现 socket 接收缓冲区里没有数据,就把当前线程挂起。等到网络对端的数据发送过来,线程被唤醒向用户空间拷贝数据。
最后送你一句话,知其所以然,磨刀不误砍柴工。
推荐阅读: