![](https://img-blog.csdnimg.cn/direct/cb122a94c2f04c1fa9e2ed3a581ebe32.jpeg?x-oss-process=image/resize,m_fixed,h_224,w_224)
构建工业级QPS百万级服务
文章平均质量分 85
分享作者实现QPS百万,甚至千万的服务的经验
Bovinitwo
曾百度高级开发工程师,现阿里技术专家。有海量数据离线处理、流式处理、在线服务经验。参与开发、设计的系统,大部分人都使用过。
展开
-
《构建工业级QPS百万级服务》系列简介
本文不会花大篇幅讲,使用频率极低的、接口成熟但原理晦涩难懂的、几乎只会出现在面试中的内容,因为这类文章曾经让我在成为一位靠谱工程师的道路上浪费了许多时间,并且学习一周后完全忘掉,对于软件工程师,我们要把更多的精力放在如何站在巨人的肩膀上,去实现目标。而不是花大量时间研究,linux在部署Alpha 架构时,如何管理应用死锁的,这些经验,只有在你实际遇到这类问题的时候,再去研究吧(最好永远不要遇到),毕竟人的精力是有限的,让我们把精力花在更有趣的事上面。“如果我看得更远,那是因为我站在巨人的肩膀上。原创 2024-01-31 12:42:06 · 1029 阅读 · 0 评论 -
- 工程实践 - 《工业级QPS百万级服务从0到1概述》
我赞成《数据密集型应用系统设计》中对服务类型的划分,即服务类型分为“在线查询服务”、“流式计算服务”、“离线计算服务”,我会以在线查询服务为例,因为在大部分IT公司,它都是必不可少的。在架构根据需求生长的过程中,特别是涉及到取舍时,我会花一些篇幅讲解我抉择背后的逻辑,这可能会涉及一些中间件,比如MQ、Redis、Mysql、ZooKeeper等,我会尽可能简单的讲出它们原理以及架构的核心特点,以及我的取舍原因。在上述需求完成的同时,我也会把平时看到的一些我觉得值得借鉴的业务架构分享出来。原创 2024-02-05 10:18:09 · 526 阅读 · 1 评论 -
- 工程实践 - 《QPS百万级的无状态服务实践》01 - 从单机到集群
这个时候通常的做法,是做一个服务A,管理所有负载均衡服务的ip列表,用户在发送请求时,先根据业务名称,去A查询下面有哪些负载均衡服务ip,然后服务A根据用户的信息,提供部分ip,这样,不同用户拿到不同的ip,保证负载均衡服务的请求量级是大体一致的。而业务名称,就是所谓的域名,服务A,就是DNS(域名系统)。对于小流量,负载均衡使用一台机器,安装nginx足够,但是流量增大之后,可扩展性、高可用性、安全性等会是一个问题,使用亚马逊的ELB,或者阿里云的SLB等云厂商的负载均衡解决方案,会是更省心的选择。原创 2024-02-07 13:57:30 · 2601 阅读 · 1 评论 -
- 工程实践 - 《QPS百万级的无状态服务实践》02 - 单人开发到多人开发
内核的本质,是也是一个进程,之前我们的进程直接和内核交互,这样的问题是,内核的状态、磁盘的状态,对每个进程都是开放的,版本无法管理。第二,服务器上的代码管理,核心就是同一时间,只能有一个版本正在发布,并且发布人知道发布前的程序版本,对于小公司,简单的办法,可以通过群沟通,或者分发开始和结束时,使用redis等数据库建一个锁,保证不会并发发布,如果人员再多一些,对发布的监控、稳定性等,要求再高一点,就需要建一个发布平台。如上所述,我们的架构为了解决环境一致的问题,又升级了,其中业务服务,变成了业务容器。原创 2024-02-10 20:08:11 · 914 阅读 · 1 评论 -
- 工程实践 - 《QPS百万级的无状态服务实践》03 - 服务出问题了怎么办
首先我们讲下如何快速事后发现,目前通用的方法就是监控+告警,监控靠的就是日志,比如对于QPS1000的机器,在一分钟内,我们会打印60行日志,每行日志都写着“当前秒处理用户1000”,这样我们可以统计到每分钟处理60000个用户。对于更加重要的服务,一个机房的稳定性就不够了,比如某个机房断电时,我们也希望用户也几乎没有感觉(这里做不到完全无感,未来我在分享分布式系统设计理念时,会细讲如何取舍),那我们的系统需要部署在多个机房。所以,为了出问题后,能够快速发现,我们的架构又升级为图2了。原创 2024-02-12 10:26:42 · 1096 阅读 · 1 评论 -
- 工程实践 - 《QPS百万级的无状态服务实践》04 - 如何降低成本
另外如果我们使用足够多的物理机,物理机之间可能有性能差异,甚至芯片版本不一样,比如有的机器能接受1500请求/秒,有的只能500请求/秒,这个时候我们要以性能最差的机器来算,那需要的核数是2000核。这个是运维层面的责任,作为研发,我们也要确认每台机器性能差异不大,确认的办法很简单,压测,也就是与线上请求隔离的情况下,验证出性能最差的那台机器达到性能瓶颈时,集群整体处理能力。降本是一个复杂而系统的事,这里,我们先基于图1的架构,看看目前的架构可能会遇到哪些成本问题,一般是如何解决的。原创 2024-02-13 15:15:52 · 1038 阅读 · 1 评论 -
- 工程实践 - 《QPS百万级的有状态服务实践》01 - 存储选型实践
但是目前的业务形态,我不会去做这样的事,因为这个方案,需要“数据生产方通知+服务容器轮询”,它增加了服务的复杂度,而成本大概也只是从2分钱变成了1.5分钱。技术选型,本质上做的事情是,找到满足业务需求的最便宜的方案(这里的便宜不止是机器资源,还是开发、维护成本)。从上面的业务特征,可以大概刻画出我们想要的存储中间件特征为,数据量不大(按数据量大小收费比较划算),写少读多(数据读取便宜,写可以贵点),数据生产成本不高,对业务小时级别影响(有备份,但也不要成本太高)如图3,我的选择是同城冗余存储标准型。原创 2024-02-16 13:24:52 · 2360 阅读 · 1 评论 -
- 工程实践 - 《QPS百万级的有状态服务实践》02 - 冷启动和热更新
同时程序有另外一个专门用于数据更新的线程,当发现有数据时,会在步骤4新开辟一片内存,存储新版数据B,然后在步骤5上写锁,防止数据更新的过程中数据被读取,然后在步骤6,替换数据,在步骤7释放写锁,这样之后的请求读取的都是数据版本B了。从代码逻辑层面,冷启动不会增加复杂度,如图2,只要关闭程序,更新节假日数据就行了。但是我们的服务器有很多台,并且有的机器可能正在重启或者置换,ip也会变化,这个时候nginx则不够用了,不仅需要支持更复杂规则的负载均衡服务器,也会降低负载均衡的效率,所以这不是一个好的办法。原创 2024-02-18 13:09:28 · 1047 阅读 · 1 评论 -
- 工程实践 - 《QPS百万级的有状态服务实践》03 - 消息队列
第一,数据发送是I/O密集型,占用CPU有限,为了提高发送速度,配置多线程发送是很有必要的,最佳线程数量,需要业务测试。但是不同机器收到消息的时间是有差别的,那机器更新数据版本的时间就有差别,比如一个机器已经是版本B了,另一个机器还是版本A,那一个请求方的短时间大量请求结果,可能在两个版本反复横跳,用户体验会很不好。我会在后续的文章中说下我的经验。简单的说,如图3,数据生产者把消息发送给Broker,Broker把数据存在Commit Log中,并根据标签,在每个Consume Queue中添加索引。原创 2024-02-19 14:13:15 · 1068 阅读 · 1 评论 -
- 工程实践 - 《QPS百万级的有状态服务实践》04 - 服务一致性
客观的不一致是不可避免的,但是从观察者的角度来看,不同请求的一致性,还有优化的空间的。但是客户端如果也在我们的控制范围内,事情似乎也变得简单,我们在客户端记录上次获取的结果使用的数据版本,如果此次请求返回的版本比上次还小,就重试,直到得到获取到的结果使用的数据版本大于等于上次,这个方法并不是没有代价,我是用了部分请求的延迟,换了一致性。这意味着,用户上次请求的信息,在某个地方持久化了,并且一个用户的多个请求,在不同机器不能同时执行,必须一个机器执行完了,才能执行下一个,这大大降低了请求处理的吞吐量。原创 2024-02-20 23:17:54 · 1023 阅读 · 0 评论 -
- 工程实践 - 《QPS百万级的有状态服务实践》05 - 持久化存储
本地数据库一个关键问题暴露了,就是磁盘是会坏的,一般情况下硬盘寿命在3-5年,如果数据只存了一份,磁盘坏了,数据就丢了,就像如果古人刻有信息的龟壳坏了,那信息将永久丢失。但是对于处理用户量不大,对几十毫秒的延迟不敏感的场景,通过双机房容灾,但是使用一个集群的MySQL才是最佳的方案,维护简单,成本也几乎不会增长。所以我们的架构升级了,如图2。图中,我们选择每个机房有自己的MySQL集群的原因是因为,跨机房的延迟是远大于同机房,而我们的业务容器是同步查询MySQL,如果查询延迟大,将极大影响吞吐量。原创 2024-02-24 18:49:48 · 1007 阅读 · 0 评论 -
- 工程实践 -《分布式系统可用性保证方法和实践》
可用性是指一个系统在用户需要时能够正常提供服务的能力。需要注意,从用户的角度来看,可用性针对的功能,而不是某个系统。另外用户群体不一样,则可用性则一样。如GPT的聊天功能,“可用”意味着能秒级响应人类的输入,而对银行的大额转账功能,“可用”意味者秒级接受用户请求,小时级甚至天级完成实际转账。总的来说,前置手段,无论哪种方式,一定是两个目的之一提前暴露可能的问题、通过抽象标准流程减少操作问题。而实践中,我们更多的精力是,结合业务,在可用性概率和成本之间做取舍。总的来说,原创 2024-04-13 16:19:46 · 1429 阅读 · 0 评论 -
- 工程实践 -《构建可观测软件系统的落地方法》
可观测性最早是1948年由电气工程领域的控制理论家Claude Shannon在《通信的数学理论》提出的。这也是信息论的奠基性文献。可观测性是指能够通过检查系统或应用的指标(Metrics)、日志(Logs)、痕迹(Traces)来监控、测量和理解系统或应用的状态。同理,对应到软件系统,这单个指标基本适用,但不够直接到让我们基于它们直接设计落地方案。我的实践经验是,软件系统中,“输出”、“日志”、“性能”,是系统状态最重要的指标。它们分别对应了输出(下游观测):当前系统生产给I/O设备的字节。原创 2024-05-03 18:03:10 · 837 阅读 · 0 评论 -
- 工程实践 - 《高并发系统正确性保障 - 锁的范式》
硬件支持锁,本质就是,在CPU系统中,如何保证一个表示权限的变量,一定时间界限内,只被一个CPU核写入,且下一个CPU在写时,保证已经获取到最新的数值。这里还有一个可以断言的,,注意这里的性能问题不是加锁和释放锁本身耗费资源太多,而是加锁之后业务代码持有时间太长,对其他线程阻塞导致的,以c++互斥锁为例,这个用户态的锁,在现代计算机,平均一次获取仅需要5-15ns。同理,如果信息传递是不需要时间的,且我们认为时间是离散的,那就不再需要锁,比如量子计算机,就是一个不需要锁的计算机,不过这不在我们讨论范围中。原创 2024-03-30 18:45:52 · 2021 阅读 · 0 评论 -
- 项目落地 - 《如何给项目排期》
谋定而后动,知止而有得”-《孙子兵法》软件的生命周期包括【设计-开发-测试-部署-运维-下线】,给软件项目排期是设计阶段所做的事情。而如何给项目排期,我在刚工作两年时疑惑过,也在之后作为有经验的人给别人解惑过。接下来,我以研发的角度,分享一下我在排期这个事情上的经验。作为研发,给软件项目排期。有两类角色:一线开发人员和项目一号位。原创 2024-02-01 13:08:50 · 1038 阅读 · 0 评论 -
- 项目落地 - 《选择项目工具的方法论》
在2020年的时候,我负责设计搭建一个新的服务,其中json解析的工具,我选择了RapidJson,而实际上性能显著提升的simdjson、yyjson在2019年、2020年相继出现了,从今天的角度回看,似乎我的选型是错误的。不一定是每个业务的最优解,在深入解决业务关键问题的时候,认识到问题的本质,才能找到最合适的办法。但是一个人的知识是有局限的,而环境是不断变化的,做到每个环节最优,是需要大量精力,且需要承担风险的,另外不同的工具最擅长解决不同的问题,在工程初期,我们很难预估所有的关键问题在哪里。原创 2024-02-17 14:13:43 · 1381 阅读 · 1 评论 -
- 项目落地 - 《项目成员高效沟通的方法》
本文属于专栏。原创 2024-02-14 17:23:20 · 1166 阅读 · 1 评论 -
- 语言经验 -《海量数据服务的c++ core dump问题调查难点和方法》
本文属于专栏c++的问题调查效率低,是开发、维护效率相对低的核心原因之一。无论是core dump还是内存泄漏,到目前为止,业界也没有一个标准的解法。本文主要介绍个人在调查多线程、异步的程序的core dump问题的通用经验。我会先说抽象出的思想,再说具体措施。原创 2024-02-03 11:57:04 · 939 阅读 · 0 评论 -
- 语言经验 - 《c++的高性能内存管理库tcmalloc和jemalloc》
本文属于专栏。原创 2024-02-15 11:33:53 · 1307 阅读 · 1 评论 -
- 语言经验 - 《c++11获取系统时间最快的方式》
代码运行在32核linux机器,g++版本4.85,系统版本centos7。在我的测试环境中,单核clock_gettime、gettimeofday为3000万次/秒,c。c++11中获取unix时间最快的方式是clock_gettime和gettimeofday函数。编译命令 g++ test.cpp -std=c++11 -pthread。原创 2024-02-08 11:14:46 · 499 阅读 · 0 评论 -
- 语言经验 - 《c++最快的日志库spdlog》
使用spdlog。其是截止2024年2月,c++日志库中,性能最好的。原创 2024-02-06 16:53:21 · 715 阅读 · 1 评论 -
- 语言经验 - 《使用google profiler 对c++应用进行性能热点分析》
其统计原理就是等间隔时间采样,确认当前时间正在执行的函数栈,然后对每个函数栈统计运行次数。如下图中最大的方块syscall,是chrono调用的,这就是chrono慢的原因,因为它会在内核态和用户态之间切换。整个应用的执行过程37.7%的时间,都在执行该函数。因为当我们的重要目的之一是盈利时,那么成本就十分重要。而性能优化的前提是,我们知道哪一部分功能,是最耗费资源的,才能用20%的精力去解决80%的问题。在我使用过热点分析的工具中,最喜欢的是google profiler,因为它的信息足够详细。原创 2024-02-11 12:29:41 · 1014 阅读 · 1 评论