搭建高并发、高可用的系统

前言:

以下全文分享的内容不涉及到开发高并发、高可用系统具体的实现方法,主要分享的是在编写高并发、高可用系统的思想,具体的实现方法会随着时间而过时,但思想思路是常青的。

文章会先从背景知识开始介绍,让大家了解一些基本的概念,只有明白每样东西的优势和劣势,才能跟据自己现有的需求跟现实的资源去选择合适的工具。再从高并发、高可用系统的瓶颈和棘手的问题去反推需要我们去解决的问题。

以下全文需要用客观辩证的眼光来阅读,其实日常应该用客观辩证的眼光来看待日常的问题,正反两面系统地、完整地看待事物本身。

背景知识:

资源利用率

这里的资源指的是硬件资源,如cpu、内存、硬盘、网络带宽、网格延迟等。现实情况下资源都是有限的,不可能无限地增加硬件来满足软件服务。因此我们在使用软件服务中,要跟据软件的硬件使用率合理地安排机器,在现有的条件和成本下实现最大的资源利用。

Latency numbers every programmer should know

搭建高并发、高可用的系统

引用:gist.github.com/hellerbarde/284337...

当我们看到上面的表格对比 ,CPU 周期的延迟、CPU 缓存访问延迟、网络延迟、磁盘存储延迟时,我们可以看到 CPU 通过网络或磁盘访问与 I/O 相比是非常快的。 SSD 与老式的旋转硬盘相比,也可以快很多。很明显,跨机房的 TCP 延迟 与发生在一个实例或机器内部的延迟相比,要大得多。我们在构建高可靠系统的时候,也要牢记这些数据来权衡和平衡问题。

单体&微服务&中台

在软件工程中,核心思想之一是尽可能减少代码耦合,如果发现代码耦合,就要解耦,但模块间有依赖关系必然存在耦合,理论上的绝对零耦合是做不到的,但可以通过一些现有的方法将耦合度降至最低。核心思想之二是在不同的业务上选用最合适的工具,比如搜索功能,你如果对Mysql一张超过50W的表做模糊搜索,那慢查询将会搞垮该机器的cpu,对于搜索功能,只能选用搜索引擎来实现,其他的功能也如此。

单体、微服务就是在项目的不同阶段中同的解耦方案,单体项目就不能降低耦合度跟提高代码重用吗?就不能搭建高并发系统吗?答案是可以的,只不过微服务做得更彻底。

中台又跟单体和微服务不同,是两个维度的东西,中台一般指的是业务中台,可共享可复用的一组业务,这个是从业务视角出发的。中台并不跟微服务绑定,完全可以基于单体服务去打造一个业务中台。只是微服务技术的体系更合适,符合业务中台长期不断优化、不断沉淀的思路,所以一般业务中台也是基于微服务技术体系来构建的。

微服务跟中台的优势很明显,但需要付出的代价也是相应的,如用户量1000万左右的项目单体应用技术团队会在15人左右,当单体应用分拆为多个微服务后,单个微服务开发跟维护人员就要4人左右,加之运维跟排错成本也会相应提高,开发周期也一延再延。中台更是百人级的技术团队才能玩得起的。
辩证的看待架构方案,回看10多年前SOA架构,到当下微服务、中台、云原生等分布式概念流行,架构的思想渐进会不断更新,并不是说用了某某架构就等同于商业上的成功,技术是推动商业而不是绑架商业,行业内微服务化、中台化失败的案例太多了,工作中有了解到一个服饰公司花了200万一年时间用spring cloud中台化后,效果并不理想。根据业内的独家消息,2020年末,自2015年开始大力推广中台的开山鼻祖阿里巴巴也彻底开始拆中台了!
下面会有三个架构图,其中单体跟微服务是我画的,中台的图是网上找的,可以看出,单体应用一样能做得很大型,但微服务的优势也很明显,这也是未来的方向。回头辩证地看两个架构的缺点,单体应用的解耦度相对高,微服务会浪费较多的资源到周边服务,运维排错的成本也相对高。

单体架构图

搭建高并发、高可用的系统

微服务架构图
搭建高并发、高可用的系统

中台架构图

搭建高并发、高可用的系统

编程语言

编程语言由机器语言、汇编语言到现在常用的高级语言,其本质还是人与机器之间的交流和沟通,程序员的调用难度机器语言 > 汇编语言 > 高级语言,运行效率机器语言 < 汇编语言 < 高级语言。
高级语言中大家常用的如java,go,py,php,c,c++,nodejs等,跟据上一段落的说明,本质都是基于系统 api的封装。如高级语言中对文件的操作调用了Linux 文件 API、对网络请求的操作调用了Linux Socket api等。如我们编写代码时不难会遇到文件操作的权限不足,理解上文后很容易知道原因是运行高级语言的Linux用户组权限不足。如各高级语言都拥有相类似的功能,理解上文后很容易知道各高级语言都是跟据各自的特性跟设计思想而对Linux API不同的封装。

选取原则

  • 分析编程程序要求和硬件支持性
  • 分析编程程序生态、参考文档
  • 分析编程语言编写成本
  • 挑选熟悉的编程语言

分享链接:

blog.chinaunix.net/uid-21411227-id-...

segmentfault.com/a/119000003878211...

进程&线程&协程

要理解程序的运行方式恐怕不是三言两语能解释清楚,很多文章中所讲的内容都过于片面和学术化,不易于理解。个人只有实际使用进程和协程的工作经历,对于线程只局限于网上文章,所以在这里放几篇写得不错的文章给大家阅读,方便大家有个基础的认识,在这建议大家能掌握多门不同的编程语言后,用对比的方式去看待编程语言的差异,比如php的进程、swoole/go的协程、java的线程等等,了解各编程语言的适用场景、运行方式、缺点等等之后,在实际的工作中跟据公司的技术团队跟资源选择相对适用的工具。

分享链接:

www.imooc.com/article/31751te

www.zhihu.com/question/63520385/an...

www.360doc.com/content/20/0417/14/3...

任务类型

应用程序中的任务可能是 I/O 密集的,也可能是 CPU 密集的。通过解耦 IO和 CPU 计算任务,我们可以最大限度地提高 IO 硬件和 CPU 及内存硬件的性能。
I/O 密集操作是在程序系统中,大部分时间都在等网络请求、磁盘I/O完成的逻辑操作就是I/O密集操作
CPU 密集操作是在程序系统中,大部分时间用来计算、逻辑判断等CPU运算的逻辑操作就是CPU密集操作

实现方向

现实中很多项目都会存在前期资源不足、赶工期、开发人员水平低、架构不满足现状等诸多问题,性能问题是互联网项目中最难解决的问题。解决的思路是先要找出整个系统的瓶颈,避免局部优化,再站在未来回看现在,避免每隔数月重复修改。

导步任务队列

将同步任务分拆成多个子任务派发到队列中并立即返回结果,而不是等待任务结果的通知。
优点

  • 将顺序阻塞执行流程解耦多路复用模式,提高资源利用率。
  • 每一个组件都不只是空闲等待,而是在一个执行流没有准备好的情况下开始执行其他任务。
  • 每一个组件功能相对单一,代码块相对简结

缺点

  • 异步不一定总是更快,每个执行单元之间有额外的通信成本和管理成本
  • 顺序逻辑被分割成多个小部分,管理起来更加复杂。
  • 我们必须做一些额外的工作来管理每个执行单元的使用容量和异常情况。

非阻塞IO

阻塞或非阻塞是指你的用户区代码如何通过系统调用与操作系统内核进行通信的模式,go、swoole里的协程,java里的线程都能实现非阻塞模式,在非阻塞模式下,系统会将控制权传递给调用者,并返回状态,在 I/O 操作时不会被阻塞。阻塞IO只会等当前IO完成时才将控制权传递给调用者。如果当IO处于等待状态,则一直等待。

如果一个 CPU 核以 3GHz 的速度运行,它每秒可以执行 30 亿次 CPU 循环。一次非阻塞系统调用可能只需要几个 CPU 周期,也就是几纳秒,相比之下,阻塞系统调用可能需要几百毫秒。差距是1000 万倍。

优点

  • 并行处理能力大大提高,解决了IO等待时间里无法发送更多请求问题

缺点

  • 非阻塞IO的调度器存在一定的cpu损耗
  • 代码编写能力要求相对阻塞IO更高

系统可靠性-处理异常

应用程序可能会崩溃、退出、占用太多内存、响应缓慢。

需要实现但不限于:

  • 完善且分类明细的日志系统
  • 捕捉处理全局异常和错误
  • 完善的监控报警机制
  • 完善代码审核机制
  • 完善测试流程

网络可靠性-超时处理

如果应用程序正在使用上游服务,例如数据库,缓存服务,远程 HTTP 服务,网络连接超时异常是十分正常的情况,因为我们可以控制如数据库、缓存服务等内部服务,但无法控制第三方服务,所以合理的超时处理机制十分重要。

需要实现但不限于:

  • 对第三方服务抱有悲剧感
  • 构建网络超时机制
  • 构建重试调用次数机制,重试一定次数后暂时停止该失连的第三方

系统安全

系统安全是个庞大的课题,内容多偏运维为主,其中包括前端+后端+数据库等软件服务漏洞、网络安全漏洞、服务器漏洞等等,这些漏洞的存在,使得系统很容易被攻击者利用,进而破坏系统的安全性。在实际的生产环境中,除了ddos这种大流量攻击需要用到专业的硬件跟流量硬抗之外,其他问题都有一定的解决办法。
系统安全的内容足够写几本书了,由于篇幅有限,这里就不按点列明,在此分享几点书跟文章,个人觉得站在攻击者的角度来学习会有意思过防守者。系统安全要跟系统体量挂钩,不要因此影响业务流程与服务器性能,也流费公司资源。
分享链接:

《黑客攻防—web安全实战详解》

《安全之路:Web渗透技术及实战案例解析(第2版)》

服务隔离

服务隔离是生产环境中很重要的思想,任何在软件系统中,故障是不可避免的,并且大多数还是不可预测的,越是用户量大的系统,发生故障的影响越是严重。因此,我们只能在系统的设计之初就充分的考虑好应对措施,如何在故障发生时,去尽最大可能的止损和减少故障范围,尽可能的去提高系统的整体可用率。

  • 按服务/功能做隔离
    按服务能来隔离可以说是微服务主打的卖点了,相信有了解过微服务的同学也都了解,但其实单体应用也是要部置多个服务,前端跟据业务请求对应的服务器即可。除了应用系统的服务,如数据库等周边服务也应该按业务功能来隔离,常见的读写分离、微服务里的分布式数据库,还有搜索模块转移到ES,数据统计转移到BI工具。最大可能的止损和减少故障范围。

  • 按用户分类隔离
    saas系统会遇到多租户数据库解决方案的问题,在saas里用户 = 租户,隔离是为了在服务稳定性上跟数据上不受其他租户影响。普通的系统也是可以按用户来分类隔离的,如可以按会员分组。又或者按地区分组。按照自己的业务场景来分组。最大可能的止损和减少故障范围。

监控、发布、数据上报系统

当系统涉及的到多种服务集群时,应用服务器的代码发布、全部服务器的状态监控/上报/分析的重要性不言而喻。如何管理好这么多服务器?如何弹性增加某个集群的机器?如何能在某个指标不正常时提前吿知运维、发开人员?这些问题都是实际生产中经常遇到的,大厂有大厂的解决方案,小厂有小厂的解决方案,如何按照当前资源来完成这些工作,这也是运维岗位体现价值的地方。

软件项目管理

高可用的系统都离不用严格项目管理,软件项目管理过程从项目计划活动开始,而第一项计划活动就是估算:需要多长时间、需要多少工作量、以及需要多少人员。此外,我们跟据我们一开始计划的架构来估算所需要的资源(硬件及软件)和可能涉及到的风险,把文章中的每一点都落实到具体的负责人,再由项目管理来监控完成质量。
没有项目管理,项目也有可能成功。但没有管理的项目,很难保证项目的利润空间,对公司来说,亏损的风险就大。所以我们要有项目管理,以保证公司在总体上是盈利的,注意不是每一个项目都要盈利。
另外,有了项目管理,就有了管理改进的基础,无论刚开始的项目管理多么糟糕,只要有管理,就有了改进的可能性,至于能不能得到改进,以及改进的快慢,则取决于两个因素:一个是人,特别是各级管理者;另一个是利益。
对于项目管理的内容在80年代出的《人月神话》就有很详细的说明,时间过了40年,但本质依然没变。

总结

至此,主体内容分享完了,还有很多点其实是没分享到的,以上的每一项的介绍都是在本人水平有限的情况下总结出来的浅显之见,深入到每一项学问都很深,深到可以随便在网上搜一个关键词都能搜到收费的项目咨询,深到值得大家再深入了解学习,大家辩证地按需吸收。在学习途中要学会区分那些垃圾文章,如上来就是快速入门xxx、中级必学、高级必学这些。每一个点、每一个服务,都要重视基础,如果只了解表面的使用,却不明白服务作者设定各个参数的意义,那么出了问题你也不会处理。只求速成,不愿深钻,最终只会练成绵绵无力的猫猫拳。
在系统立项的第一天起,通过不断地投入资源,不断地发现问题解决问题,到站在更高纬度回来当前问题来投入资源重构,每一天每一步都伴随着企业、项目组的壮大,如果提前预判到系统的并发量不会增加,也就不需要再投入了。
高并发、高可靠的系统是个庞大的工程,世界上的系统业务又大相径庭,想要找一个完全一样的方案来复制是不可能的,只能在清楚思路后跟据自身情况按需吸收。企图通过一个单点的工具/策略/方法去解决一个系统问题是没有成功可能的,因而不存在解决任何问题的银弹。

参考文献:

《异步和⾼并发系统开发指南 - ⽤ Swoole PHP 协程构建异步和⾼并发系统》 - Bruce Dou

《人月神话》 - Fred Brooks

其他参考文章

本作品采用《CC 协议》,转载必须注明作者和本文链接
未经允许禁止转载 -- 苦力小林,
本帖由系统于 1个月前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 5

好硬核,收藏了,慢慢看。

1个月前 评论
Double-Jin (楼主) 1个月前

感谢分享

1个月前 评论

问下老哥,图是用什么工具画的?

1个月前 评论
Double-Jin (楼主) 1个月前

感谢,CY,之后慢慢看

1个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!