今天和大家聊聊作为一个后端开发,在实际工作中,我们如何打造一个高并发的系统?
如下图所示,大概有六个层面,我们结合具体的场景直播间签到去一一细说。
一、前端
1、打散请求:即把用户的接口分散一点去请求后端,尽量不要集中在某一时刻。
场景:比如直播间讲师发起了一个签到,用户去点击签到,调用签到请求接口。假设这个直播间的在线人数上百万,那么这个签到接口的请求可能会有近百万的QPS。毫无疑问,这会对后端服务造成瞬时、巨大的流量冲击。
解决措施:这种瞬时洪峰流量,我们就可以在前端对用户签到接口做打散处理。比如针对直播间的在线人数,当超过某个阈值时,做3秒内任一毫秒的分散请求。避免这些请求在大流量直播的直播间里,瞬间同时请求过来,冲垮后端服务。
2、防抖防重:即在前端加上防止重复点击、重复请求后端的逻辑。比如点击签到按钮后,前端做一些类似3秒的防重复请求处理,避免用户多次点击请求到后端,这样可以减少大量无意义无效的请求。
3、服务降级:在高并发、系统压力较大或部分服务出现故障时,通过暂时减少或关闭某些功能,以保证系统的核心功能仍然能够正常运行,避免整个系统崩溃。
场景:比如在签到服务中,用户签到是核心接口,当签到服务受到流量冲击导致压力过大时,可以将除该接口外的其它功能在前端暂时屏蔽掉(可配置),这样用户就不会去请求对应的接口了,优先保证核心接口正常运行。
二、Nginx/网关
1、负载均衡:这个应该大家都比较熟悉,就是尽可能将请求均匀地打在各个服务器上,常见的有轮询策略、hash策略等,就不一一赘述了。
2、网关限流:一般我们C端的服务都会有一个统一的网关系统,实现比如用户信息获取、路由的分发、接口限流等功能。我们对于C端的接口,在上线前会经过接口压测,然后根据压测结果设置接口限流值。在高并发场景下,比如签到接口设置的是10WQPS,我们就会把在同一秒内超过10W的请求直接在网关层丢弃掉,避免将下游的签到服务打挂。
三、业务层
1、同步返回:比如前端请求签到接口,我们后端会返回签到成功给前端,但实际是否成功还不确定,因为我们肯定不会实时同步去查询或者写入MySQL。毕竟我们的MySQL还是比较脆弱的,经不住上万QPS同时操作。
2、异步处理:跟上面其实意思差不多,同步返回结果,但我们会将实际的数据使用中间件如kafka、RocketMQ、RabbitMQ、Redis等进行削峰,然后推送到下游,最后使用脚本去消费推送的数据进行实际的逻辑处理。
四、数据层
1、本地缓存:感兴趣的可以翻翻 我之前写的文章 。可以说本地缓存是服务器上读写性能都最高的介质了,因为本地缓存是直接操作该服务器本身的内存呀。只要内存够大,几十万、几百万的QPS都轻轻松松不在话下。
2、Redis缓存:Redis缓存的性能仅此于本地缓存,一般抗10万以下的QPS用Redis就够了。Redis缓存实际上也是操作的Redis服务器的内存,只是Redis服务器和本地服务器还有网络连接。所以Redis性能会比本地缓存稍微差一点,一般同等配置可能都会差一到两个量级。
3、MySQL:这个大家应该都很熟系了,常用的分库分表、读写分离等等这时都排上用场了。
五、底层架构
1、K8s弹性伸缩:在高峰期提前扩容、高峰期之后缩容。在流量突刺时快速扩容,在流量下落时缩容。k8s提供了多种灵活的弹性伸缩机制,可以根据不同场景和需求自动调整集群的资源使用,确保系统能够在负载波动时稳定运行。根据实际的应用场景,合理配置这些伸缩机制,能够有效提升系统的弹性和可用性。最重要的是,比之前使用CVM物理机,能极大地降低服务器成本。
2、微服务架构:微服务架构通过将单一应用拆分成多个独立的服务,使得每个服务可以独立扩展和维护,从而提高系统的可扩展性和维护性。例如直播间红包是一个服务、直播间签到是一个服务、直播间抽奖又是另一个服务...。这些服务通常是在单独的服务去维护,尽量做到互相隔离,互不影响。
六、日志监控和告警
假如现网服务出现了问题,我们如何快速地感知呢?那当然是在各个关键服务都增加日志记录,然后增加监控告警,触发某个阈值时就告警。尽量在用户感知到问题前,我们去发现到并解决。
以上,就是我们在设计高并发系统时,需要考虑的一些问题和一些解决方案。当然,这里只是讲了大的宏观架构层面,落地到具体的业务场景中,还需要结合实际情况去思考一些细节和方案,去解决实际的性能瓶颈点,这是一件非常有意思的事,希望大家都能从解决问题中收获快乐。