打造千万级流量秒杀第十七课 技术选型:如何选择满足“三高”要求的技术?

发布于:2023-01-16 ⋅ 阅读:(596) ⋅ 点赞:(0)

在开始之前,请你先思考一个问题:在一个项目进入开发阶段前,我们该如何选择满足“三高”要求的技术呢?

这是个非常关键的问题。为什么?来看一个场景:

小明在做一个分布式任务系统。他在开始写代码前,通过网上查资料了解到一款开源的分布式任务系统。他一看评价挺高,就直接拿来用了。结果,上线后在压力大的时候,容易出现任务超时的问题。后来通过连续好几天的仔细排查,最终发现该系统使用了一个全局互斥锁,压力大的时候部分任务持锁时间太长,从而导致任务超时。

小明的例子想必你不会陌生,同样的功能,所使用的基础组件和技术可能有好几种,如果选对了,项目开发时会事半功倍,选错了则会事倍功半,项目上线后事故频发。具体到秒杀系统里也一样,比如仅秒杀系统使用的 MQ ,就有 Kafka、RabbitMQ、NSQ、ActiveMQ、RocketMQ,如何从中选出适合自己业务的技术,保证“三高”就显得很重要。

那么,具体需要怎么做呢?

技术选型原则

以我的经验,做技术选型,最好先以一些标准或原则来判断,这样会省力许多。下面是我实践中总结的 14 个字原则——简单易用可扩展,稳定高效成本低,希望对你有多帮助。

“简单” 并不是指所用技术实现简单,而是指该技术逻辑和架构清晰明了。它可能是个很庞大的系统,但内部功能逻辑划分清晰,耦合度低,每一部分都可以独立使用。比如 Go Web 框架中的 Beego 框架,虽然功能很多,但每个功能都比较独立,且文档比较全,能让你很快明白。

“易用” 主要是指上手容易,它是基于简单这个原则上的。除了功能逻辑耦合度低外,易用还要求所选技术的文档齐全,看文档或者命令提示就能学会如何用。另外,它还要求该技术对环境没有太多的依赖,社区也比较活跃,即使遇到问题也能随时找到人沟通问题。比如 Go 语言,就具备这些优点,有编程基础的程序员,看文档自学不到一周便能上手。

“可扩展” 同样是基于简单这一原则之上。当你发现所选组件有些功能无法满足你的需求时,该组件容许你快速扩展功能。比如,你可以通过修改组件的源码,或者实现中间件注入它的框架中来实现扩展。像 Gin 框架和 gRPC 框架,都支持编写中间件注入框架中,以便扩展自定义的功能。

“稳定” 是指什么呢?它意味着该技术已有功能变动小,Bug 少,生产环境运行可用性高,有固定的开发者持续维护等。比如 Go 语言从第一版到现在,基本做到了版本前向兼容,10 年前的代码用现在版本的 Go 编译器来编译,无须改动或者极少改动就能编译成功。在运行方面,Go 语言编写的程序也非常稳定,这得益于 Go 工具支持在开发阶段做各种代码检查,确保代码质量。

“高效” 是从哪些方面来看的呢?主要有两方面:开发效率和运行效率。开发效率关系着项目的时间成本,一个需要快速上线的 API 项目,用 C++ 开发可能需要两周,用 Go 语言开发一周搞定,你会选哪个?

运行效率也就是性能,关系着项目的复杂度,如果一个项目用 Python 做可能需要 10 台机器,要配置负载均衡,需要用分布式锁,而用 Go 只需要一台机器,连负载均衡都可以不要,分布式锁也省了,你选哪个?相信在这两个场景中你都会选择 Go。

“成本低” 主要是考虑长期维护成本,比如机器成本、运维成本、功能扩展成本等。其中,机器成本受单机性能影响,运维成本受架构复杂度影响,功能扩展成本受可扩展性影响。

假如用 1 台机器能搞定的事情,绝对不要用 10 台同等配置机器来做,否则机器成本和架构复杂度都会增加。如果一个框架可以通过注入插件的方式来扩展,另一个框架需要修改自身代码来扩展,那你应该优先选择前者,因为通常加代码要比改代码容易。

以上便是我从多年工作经历中总结出来的技术选型原则。但在实际当中,有些原则不能兼得,比如开发效率和性能之间就比较难兼顾,很多脚本语言开发效率高,但性能差。此时,就需要你活学活用,根据具体场景来分析,不能生搬硬套。

秒杀系统技术选型

前面是技术选型原则,那到具体场景当中该怎么做呢?接下来我们看秒杀系统如何做技术选型。总的来说,秒杀系统技术选型分三部分:语言环境、基础组件、设计模式。

语言环境选型

首先,开发一个系统必定需要用到某种计算机语言。语言的选型要考虑到很多方面,比如,该语言是否匹配研发团队的技术栈和能力,能否被团队新人快速掌握,也就是说能否满足“简单易用”的原则;该语言编写的程序能否在当前系统架构中稳定运行,能否扛住业务流量,能否快速定位问题,也就是能否满足“稳定高效”的原则。

经过从易用性、性能、稳定性等方面,我对比分析了 Java、C++、Go,最后决定用综合能力比较强的 Go 语言来实现秒杀系统。主要原因就是,它满足“简单易用,稳定高效”的原则。

Go 语言上手很快,有一两年经验的工程师不到一周就能上手写项目。在谷歌和云原生的推动下,Go 语言一直在稳定发展,社区也非常活跃,云原生技术也证明了 Go 语言的稳定和高性能。况且,由于秒杀对并发、性能、可用性的要求都非常高,Go 在这三个方面都非常优秀,这也是我们选择 Go 来实现秒杀系统的主要原因。

语言在各个阶段需要特定的环境,如开发环境、编译打包环境、运行环境等。当我们选定好语言后,需要为它的这几个环境制定相应规范,避免因环境差异而带来代码格式错乱、软件版本不兼容等麻烦。

在秒杀项目中,鉴于服务器运行环境大多是使用 Linux,我建议开发环境使用 Linux 或者 Mac,与线上运行环境保持一致。至于 IDE ,我推荐使用 Goland,搭配 goimports、golint 等工具自动检查代码,这样可以不用把精力浪费在折腾 Vim、VSCode 的插件上。

至于编译打包环境,推荐 GitLab CI 的方式,可以用它的 Pipeline 做单元测试、代码检查等工作,提升工作效率。至于运行环境,我建议直接用云主机部署,系统使用 Linux。因为我们需要为秒杀服务做系统参数优化,以便提供最好的性能。

基础组件选型

秒杀用到的基础组件,主要有框架、KV 存储、关系型数据库、MQ

框架主要有 Web 框架和 RPC 框架。

其中,Web 框架主要用于提供 HTTP 接口给浏览器访问,所以 Web 框架的选型在秒杀服务中非常重要。在这里,我推荐Gin,它的性能和易用性都不错,在 GitHub 上的 Star 达到了 44k。有关各个 Web 框架的性能对比,你还可以参考 GitHub 上的 go-web-framework-benchmark 项目。

你可能要问了,我为什么没有选择性能最好的 fasthttp,而是选择 Gin?这是因为,虽然 fasthttp 在请求延迟低于 10ms 时性能优势明显,但其底层使用的对象池容易让人踩坑,导致其易用性较差。如果没用好,服务在高并发下容易出现奇怪的问题,从而引发故障。

从 go-web-framework-benchmark 项目中的性能测试结果里你也能发现,当请求延迟在 10ms 以上时,Gin 与 fasthttp 的性能差距也越来越小,所以没必要过于追求性能而忽略了稳定性。对于研发人员来说,在技术选型时找好性能与稳定性之间的平衡点是很重要的事情。

至于 RPC 框架,我推荐选用 gRPC,因为它的扩展性和性能都非常不错。在秒杀系统中,Redis 中的数据主要是给秒杀接口服务使用。为了保证缓存功能的高内聚低耦合,我们将用 gRPC 在秒杀接口服务中实现配置同步接口,供秒杀管理后台调用,以便将配置从管理后台同步到 Redis 缓存中。

KV 存储方面,秒杀系统中主要是用 Redis 缓存活动配置,用 etcd 存储集群信息。具体原因我在第 9 讲的时候也介绍过,这里就不多解释了。

关系型数据库中,MySQL 技术成熟且稳定可靠,秒杀系统用它存储活动配置数据很合适。主要原因还是秒杀活动信息和库存数据都缓存在 Redis 中,活动过程中秒杀服务不操作数据库, 使用 MySQL 完全能够满足需求。

MQ 有很多种,其中 Kafka 在业界认可度最高,技术也非常成熟,性能很不错,非常适合用在秒杀系统中。而且,Kafka 支持自动创建队列,秒杀服务各个节点可以用它自动创建属于自己的队列。

介绍完基础组件的选型后,接下来就需要考虑如何写代码了,而这就涉及设计模式的选型了。

设计模式选型

设计模式总共有 23 种,我们该如何选取呢?

在秒杀系统中,我们将使用拦截过滤器模式、组合模式、抽象工厂模式、观察者模式等几种设计模式。 接下来我给你介绍下它们都分别用在什么地方。

拦截过滤器模式的作用,你光看名字就能看出来。没错,我们将在秒杀系统中用它来实现漏斗模型中的流量拦截器。我在上一讲中提到,除了网关和 WAF 的拦截器外,秒杀服务自身有两层拦截器。因此我们需要用拦截过滤器模式将它们组织起来,自上而下形成一条链式处理流程。当请求被链式处理流程中的某个拦截器拦截掉,就表示该请求失败了,不进行后续处理。

组合模式主要用于活动场次信息的逻辑中,因为活动场次信息在 DDD 中是聚合根,聚合了活动专题信息、商品信息、库存信息等数据。

抽象工厂模式在秒杀中有两处作用,一个是用于对多种流量拦截器的抽象设计,另一个是对读写内存队列和读写 MQ 的抽象设计。我们将在秒杀服务中设计出 Interceptor 和 Queue 这两种抽象接口类,并提供对应的构造函数给工厂方法调用。

观察者模式主要用于管理秒杀集群信息的同步逻辑,比如当管理后台修改了限流速度后,需要将限流速度更新到各个 Queue 对象中。每种队列关心的限流配置不一样,因此需要从 etcd 中订阅不同的配置。

以上便是秒杀系统中设计模式的选型。关于设计模式本身的具体内容,网上资料很多,这里不详细介绍。秒杀中各设计模式的代码,我将会在后面的代码实战环节给你详细介绍。

技术选型的最终结果是形成开发视图,这是我们得到的开发视图:

image (1).png

Lark20210106-183219.png

小结

这一讲我分享了技术选型的基本原则,以及秒杀系统是如何做技术选型的,希望对你有所帮助。

在这里,我再强调一下,技术选型时,一定要找好性能和可用性的平衡点,可用性是基础,在保证可用性的基础之上再去选择性能高的技术。此外,一个重要的项目往往涉及多个团队的成员,我们不光要站在研发人员的角度考虑所选择的技术,还要站在测试、运维等成员的角度考虑,这样更有利于项目进行。

思考题:技术选型时,需要从测试、运维方面思考什么呢?

欢迎在留言区写下你的答案。

这一讲就介绍到这里了,下一讲我将给你介绍“Go 项目初始化都有哪些规范”。到时见!


精选评论

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到