精子又要搬家了,来  看一看有什么正在甩卖  吧。

QPS 和并发:如何衡量服务器端性能

在 LeanCloud 的控制台和文档中大家会接触到「并发连接数(Concurrent Connections)」这个衡量服务器负荷或处理能力的概念,但很多人并不了解什么是并发 —— 甚至在我们团队内部,很多没有服务器端开发经验的工程师对这个词的理解也不是很准确。我们还在继续优化文案来减少用户的困惑,但与此同时不如听我仔细介绍一下并发这个概念。

和并发相关不得不提的一个概念就是 QPS(Query Per Second),QPS 其实是衡量吞吐量(Throughput)的一个常用指标,就是说服务器在一秒的时间内处理了多少个请求 —— 我们通常是指 HTTP 请求,显然数字越大代表服务器的负荷越高、处理能力越强。作为参考,一个有着简单业务逻辑(包括数据库访问)的程序在单核心运行时可以提供 50 - 100 左右的 QPS,即每秒可以处理 50 - 100 个请求,LeanCloud 目前也是按照请求数量进行收费的。

但 QPS 只能粗略地衡量请求的数量,完全不关心服务器处理每个请求的开销。例如一个命中缓存的请求和一个需要进行多次数据库查询的请求的开销可能会有一个数量级的差距,所以 QPS 并不能十分精确地衡量服务器的负载或处理能力,因此我们引入了一个非常抽象的概念 —— 并发。

大部分请求的响应时间在 15 - 30 毫秒左右,这里的响应时间是指服务器处理这个请求所花费的时间,从客户端测量到的时间可能会稍长一些。想象如果服务器上只有一个 CPU 核心在逐个地在处理请求,如果每个请求花费 15 毫秒的话,那么每秒可以处理 66 个请求,也就是我们前面提到的 66 QPS;而如果都是复杂的请求,每个需要 30 毫秒的话,那么服务器就只有 33 QPS 了。可以看到在处理能力不变的情况下(只有一个核心),响应时间越高,QPS 就越低。又如果在响应时间不变的情况下,如果我们增加一个 CPU,QPS 就会翻倍,这三者之间的关系可以简单地描述成:吞吐量(QPS)= 处理能力(CPU)/ 响应时间。

其实 CPU 的数量就是并发最基本的概念,即有多少个 CPU 在工作。当然在实际的服务器端环境中,我们在 CPU 的基础上建立起了进程、线程、协程这样复杂的抽象、通过异步的 IO 提高 CPU 的利用率 —— 当需要从硬盘或网络读取数据时,CPU 会去做其他工作,所以并发和 CPU 的比值会比 1 高一些,IO 越多,这个比值会越高。

这时我们可以观测到的并发数就是服务器在同时处理多少个请求,也即「并发连接数」。对于 Web 后端的场景来说(而不考虑推送等长链接的场景),我们希望尽快地给客户端响应,所以请求在服务器端花费的几十毫秒中每一毫秒都是必不可少的:可能是在进行计算、也可能是在向磁盘或网络读写数据,都在占用着服务器的资源,因此并发依然是衡量服务器负荷和处理能力的关键指标。

除了并发本身,我们还经常提到「最大并发」的概念,最大并发就是在单位时间(通常是一天)里并发最高的那一刻有多少个 CPU 在为你工作。大部分应用的请求量并不是均匀地分布在一天中的,因为用户们往往会集中在傍晚的几个小时中使用手机,这些时段中的请求量要远远高于凌晨。所以人人都希望在傍晚得到更多的计算能力,但遗憾的是这些计算能力需要原子世界中的 CPU 去支持,你不可能在傍晚购买一批服务器然后在凌晨卖掉(当然,这其实是云计算要解决的问题),所以为了支撑傍晚的高并发,我们必须去准备那么多的服务器、必须在凌晨让很多服务器闲置,因此其实我们只关心一天中最高的并发数 —— 这代表了我们需要采购多少硬件资源。

当然,LeanCloud 的存在就是为了帮助开发者减轻维护后端的负担,应用开发者往往更关注的是「我有 100 万用户对应多少并发」。但这个问题往往得不到一个答案,因为有太多的因素在影响着最后的结果,例如你的 100 万用户中可能并不是每个人每天都会打开你的应用(每日活跃用户比例);而且用户对于不同类型的应用使用的频率也并不相同(平均打开次数);不同类型的应用在工作期间发起的请求数量也不相同(平均请求数量);对于不同类型的请求,需要占用服务器的计算能力同样不同(平均响应时间);最后还要考虑到你的大部分用户会集中在傍晚的几个小时使用你的应用,对于游戏抽奖、电商秒杀之类的场景,用户会更加集中在几分钟内使用你的应用。前些天我根据这些指标编写了一个简单的计算器(https://budget.leanapp.cn),将最大并发数的计算抽象为了前面提到的几个指标,如果你能给每个指标一个相对准确的估算,那么就可以计算出一个可供参考的并发数。

强弱类型、动静类型、GC 和 VM

强类型 Vs. 弱类型

  • 强和弱是一个相对的概念,强是指倾向于将未定义的行为视作错误(Java、Python),弱是指倾向于进行隐式的转换、忽略类型相关的错误(JavaScript)。
  • 很多设计得不够严谨的语言,虽然大多数情况下(或者我们通常鼓励大家这么做)是强类型的,但也有弱类型的部分(PHP)
  • 还有的语言因为提供的抽象能力很弱,我们不得不去用弱类型的部分(C)
  • 鸭子类型(duck typing)是强弱类型的一个折中(常见于动态类型中,例如 Python),兼顾了灵活性和严谨性。
  • 我们认为弱类型是为了方便,而强类型是为了尽早发现错误。

动态类型 Vs. 静态类型

  • 静态类型的变量的类型是在编译时确定的(C++、Java);动态类型的类型是在运行时确定的(JavaScript、Python),例如你可以在一个 if 的两个分支里给一个变量赋值不同的类型。
  • 有的动态类型语言也会添加编译期的类型检查(TypeScript、Python),但因为语言本身的动态性,这些检查仅能覆盖一部分情况。
  • 在动态类型的语言中因为类型不那么重要,所以很多时候甚至没有提供指定类型的语法(隐含了运行时的自动推导);而在静态类型语言里通常需要为变量指定类型,所以才有了编译期自动类型推导来提供便利,而动态类型语言则做不到这一点(因为不能在编译期确定类型,更无从推导)。
  • 我们认为静态类型有助于在编译时发现有关类型的错误,确定的类型也给了编译期更多的优化空间;而动态类型给了开发者更高的灵活度。

垃圾回收 Vs. 无垃圾回收

  • 无 GC 是指代码必须自行管理申请到的内存并在恰当的时机释放(Rust、C/C++);而有 GC 的语言会通过引用计数(PHP<5.3)、标记复制(V8)等方式定期查找无用的内存进行释放。
  • 标记复制的 GC 的过程通常会引起线程的暂停,也会花费额外的 CPU;但 GC 对于建立高层次的抽象又是必不可少的:异常、闭包等(虽然 C++ 在无 GC 的情况下也实现了这两个特性,但也引入了非常高的复杂度)。
  • 我们认为有 GC 可以简化对内存的管理,建立复杂的抽象;而无 GC 可以得到更底层的对内存的控制,带来更好的性能,避免因为 GC 造成的卡顿。

虚拟机 VS. 本地代码

  • 虚拟机是指在语言和 CPU 之间还有一个用于进行翻译的层次(JavaScript、Java);无虚拟机是指编译器直接生成本地代码给 CPU 执行(C/C++、Golang)。
  • 虚拟机也提供了更为复杂的运行时的动态特性,但这些特性有的时候也可以在没有虚拟机的情况下实现(例如 C++ 的运行时类型识别、Go 的 GC)。
  • 虚拟机可以以解释的方式执行(Python,将代码视作一种数据指令来执行),也可以即时编译(JIT)的方式来执行(V8,先将代码编译到本地代码然后执行),有时也会混合这两种方式(为了更快的启动速度)。
  • 我们认为无虚拟机的语言可以在更低的层次和其他程序交互,同时也天然地有着更好的性能;而有虚拟机的语言则可以轻松地跨平台,针对特定的架构在运行时即时编译出更高性能的代码。

小结

语言 强类型 动态类型 垃圾回收 虚拟机
C
C++ Y
Java Y Y Y
Python Y Y Y Y
JavaScript Y Y Y
PHP Y Y Y
Golang Y Y
Rust Y

参考来源:

20 岁的我在想些什么

从年初开始我就希望今后每年写这样一篇文章,如果说「年度小结」和「年度小结(技术方面)」代表了我这一年做了什么事情的话,那么这个系列的文章会聊一聊我这一年在想些什么。

每当我生病或者身体不舒服的时候才会去注意一下自己的身体和健康,重新审视自己的生活状态,改变一些不健康的习惯。虽说不如早点这样做,不过现在也不晚吧,也许大家年轻的时候都是这样挥霍过来的吧。有一点可能是我之前没想过的,虽然公司不加班,但其实我做业余项目、写文章、准备演讲,和加班对身体的影响可能是差不多的。感觉自己赚那么多钱也没什么意义,也许应该放慢一点节奏,多出去走走,时不时请几天假来做自己喜欢的事情?

今年最令我担忧的是眼睛,虽然视力变化不明显,但总有一种感觉不如以前看得清楚了,眼睛也比以前更容易疲劳了。去医院也检查了两次,医生都说只是用眼过度,没什么大问题,要尽量减少看屏幕的时间。确实今年年末我也是这样做的 —— 碎片时间和晚上睡觉前不再看手机了,睡眠时间也增加了一个小时左右,但一想到将来的工作和娱乐都会非常依赖视力,还是感觉有一种非常大的压力。

收入高了之后也就适应了这样的生活,虽然我并没有在追求这些,但钱的确给了我很多自由:可以周末毫无目的地跑到另外一个城市闲逛,可以买一些即使自己用不上的东西而不必精打细算,可以用最好的猫粮来把皮蛋豆腐喂得油亮油亮的。

但赚钱的过程本身又是在限制自由,有时候也觉得自己的钱够多了,没必要为了工作那么努力,想休息一下。也觉得可能写代码并不能给我带来像以前那么多的成就感了,刚学习编程的时候,可能只需要花很短的时间学习一些东西就可以比很多人都「厉害」,也可以得到很大的成就感;但现在虽然我很清楚自己去提高哪方面会有比较好的效果,但要付出很多的时间和精力,才能和别人比起来进步一点点。也许是时候开拓一个新的领域,学习一些新的技能?但一旦停下来,又不好说今后能不能再重新捡起来。

有的时候一个人会有些空虚,所以一个人住的这一年我听了很多播客,路上在听、吃饭在听、洗澡在听、收拾房间的时候在听、睡不着的时候在听,后来也自己录了一档播客。一个人一旦闲下来就总想做一些「有意义的事情」,学学新技术、看看书、写写文章,没人打扰就会很长时间也不动一下身体,这就又回到了前面健康的话题。

想出去走走但想到没人陪,一个人就很无趣,结果除了做些「有意义的事情」、经营一下个人品牌之外感觉生活没有什么目标。但我真的准备好了接纳另外一个人么?其实我不确定,很多时候我还是希望能有一个私人的空间,就连皮蛋豆腐我还是有很多时候不希望它们靠近,把它们关在阳台,也从不允许它们进我的卧室。

现在想起来在番茄土豆的两年也许对我来说就好像是大学生活吧,和一群有趣的同龄人在一起,经历了很多的第一次,这一年我也时常想起那些经历。也许从职业规划的角度离开番茄土豆没有什么问题,当时也是觉得自己的生活状态需要调整,想换一个环境,但换来换去还是不知道自己想要什么,也许我明年还会去一个新的城市。

精子生于 1995.11.25, 21 岁,英文 ID jysperm.

订阅推送

通过邮件订阅精子的博客日志、产品和项目的最新动态,精子承诺每一封邮件都会认真撰写(历史邮件),有想和精子说的话也可以直接回复邮件。

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-SA 3.0  授权发布。最后生成于 2017-03-19.