[总结]并发,流量,带宽,端口计算
带宽是有上限的
上限是根据你的网络带宽大小决定的。但是,为什么你还是有可能会掉入这个陷阱里呢?这往往由于你对所传输的数据的大小和频率没有充分的认识,导致了你觉得达到上限是一个很久远的事情,不用考虑它。
举个例子,你的网络都是基于 100M 带宽标准搭建的。那么此时如果每一秒会产生 10240 次数据传输,平均每个数据包大小是 10KB,我们来看下是否会遇到瓶颈。
100M 带宽的理论传输速率是 12.5MB/ 秒。那么现在每一秒需要传输:10240 次 / 秒 * 10KB = 100MB/ 秒。很明显,带宽远远不够了。
关于带宽上限,你需要明白这几点:
- 实际环境中的传输速率大小,是由服务商所提供的带宽大小,以及网卡、网卡、交换机、路由器所支持的传输速率中的最小值决定的。
- 内网与外网传输速率是不同的,一般都是内网大于外网。因为服务商所提供的带宽成本更高。
- 同一个局域网内的节点是公用外网出入口的,所以尽可能的缩小在外网传输的数据,以降低占用“独木桥”外网的空间。
QPS计算
现在咱们假设手头有一套大型的分布式系统,一共100个服务,每个服务部署在20台机器上,机器是4核8G的标准配置。
也就是说,相当于你一共部署了100 * 20 = 2000个服务实例,有2000台机器。
每台机器上的服务实例内部都有一个Eureka Client组件,它会每隔30秒请求一次Eureka Server,拉取变化的注册表。
此外,每个服务实例上的Eureka Client都会每隔30秒发送一次心跳请求给Eureka Server。
那么大家算算,Eureka Server作为一个微服务注册中心,每秒钟要被请求多少次?一天要被请求多少次?
- 按标准的算法,每个服务实例每分钟请求2次拉取注册表,每分钟请求2次发送心跳
- 这样一个服务实例每分钟会请求4次,2000个服务实例每分钟请求8000次
- 换算到每秒,则是8000 / 60 = 133次左右,我们就大概估算为Eureka Server每秒会被请求150次
- 那一天的话,就是8000 * 60 * 24 = 1152万,也就是每天千万级访问量
根据之前的测试,单台4核8G的机器,处理纯内存操作,哪怕加上一些网络的开销,每秒处理几百请求也是轻松加愉快的。
单台服务器最大并发
单台服务器最大并发问题,一般是指一台服务器能够支持多少 TCP 并发连接。
一种理论说法是受到端口号范围限制。操作系统上端口号 1024 以下是系统保留的,从 1024-65535 是用户使用的。由于每个 TCP 连接都要占一个端口号,所以我们最多可以有 60000 多个并发连接。
但实际上单机并发连接数肯定要受硬件资源(内存、网卡)、网络资源(带宽)的限制。特别是网卡处理数据的能力,它是最大并发的瓶颈。
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 就会翻倍,这三者之间的关系可以简单地描述成:
1 2 |
吞吐量(QPS/TPS)= 处理能力(CPU)/ 响应时间 吞吐量(QPS/TPS)= 并发数/ 响应时间 |
其实 CPU 的数量就是并发最基本的概念,即有多少个 CPU 在工作。当然在实际的服务器端环境中,我们在 CPU 的基础上建立起了进程、线程、协程这样复杂的抽象、通过异步的 IO 提高 CPU 的利用率 —— 当需要从硬盘或网络读取数据时,CPU 会去做其他工作,所以并发和 CPU 的比值会比 1 高一些,IO 越多,这个比值会越高。
这时我们可以观测到的并发数就是服务器在同时处理多少个请求,也即「并发连接数」。对于 Web 后端的场景来说(而不考虑推送等长链接的场景),我们希望尽快地给客户端响应,所以请求在服务器端花费的几十毫秒中每一毫秒都是必不可少的:可能是在进行计算、也可能是在向磁盘或网络读写数据,都在占用着服务器的资源,因此并发依然是衡量服务器负荷和处理能力的关键指标。
除了并发本身,我们还经常提到「最大并发」的概念,最大并发就是在单位时间(通常是一天)里并发最高的那一刻有多少个 CPU 在为你工作。大部分应用的请求量并不是均匀地分布在一天中的,因为用户们往往会集中在傍晚的几个小时中使用手机,这些时段中的请求量要远远高于凌晨。所以人人都希望在傍晚得到更多的计算能力,但遗憾的是这些计算能力需要原子世界中的 CPU 去支持,你不可能在傍晚购买一批服务器然后在凌晨卖掉(当然,这其实是云计算要解决的问题),所以为了支撑傍晚的高并发,我们必须去准备那么多的服务器、必须在凌晨让很多服务器闲置,因此其实我们只关心一天中最高的并发数 —— 这代表了我们需要采购多少硬件资源。
当然,LeanCloud 的存在就是为了帮助开发者减轻维护后端的负担,应用开发者往往更关注的是「我有 100 万用户对应多少并发」。但这个问题往往得不到一个答案,因为有太多的因素在影响着最后的结果,例如你的 100 万用户中可能并不是每个人每天都会打开你的应用(每日活跃用户比例);而且用户对于不同类型的应用使用的频率也并不相同(平均打开次数);不同类型的应用在工作期间发起的请求数量也不相同(平均请求数量);对于不同类型的请求,需要占用服务器的计算能力同样不同(平均响应时间);最后还要考虑到你的大部分用户会集中在傍晚的几个小时使用你的应用,对于游戏抽奖、电商秒杀之类的场景,用户会更加集中在几分钟内使用你的应用。前些天我根据这些指标编写了一个简单的计算器(https://budget.leanapp.cn),将最大并发数的计算抽象为了前面提到的几个指标,如果你能给每个指标一个相对准确的估算,那么就可以计算出一个可供参考的并发数。
如何设置线程数
线程的执行,是由CPU进行调度的,一个CPU在同一时刻只会执行一个线程,我们看上去的线程A 和 线程B并发执行。
为了让用户感觉这些任务正在同时进行,操作系统利用了时间片轮转的方式,CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载,这段过程就叫做上下文切换。
1 2 |
吞吐量(QPS/TPS)= 处理能力(CPU)/ 响应时间 吞吐量(QPS/TPS)= 并发数/ 响应时间 |
上面公式代表并发数越大,QPS就越大;所以很多人就会以为调大线程池,并发数就会大,也会提升QPS,所以才会出现一开始前言所说的,大多数人的误区。
其实QPS还跟响应时间成反比,响应时间越大,QPS就会越小。
虽然并发数调大了,就会提升QPS,但线程数也会影响响应时间,因为上面我们也提到了上下文切换的问题,那怎么设置线程数的呢?
我们提供一个公式:
1 |
最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目 |
备注这个公式也是前辈们分享的,当然之前看了淘宝前台系统优化实践的文章,和上面的公式很类似,不过在CPU数目那边,他们更细化了,上面的公式只是参考。不过不管什么公式,最终还是在生产环境中运行后,再优化调整。
我们继续上面的任务,我们的服务器CPU核数为4核,一个任务线程cpu耗时为20ms,线程等待(网络IO、磁盘IO)耗时80ms,那最佳线程数目:
1 |
( 80 + 20 )/20 * 4 = 20 |
也就是设置20个线程数最佳。
从这个公式上面我们就得出,线程的等待时间越大,线程数就要设置越大,这个正好符合我们上面的分析,可提升CPU利用率。那从另一个角度上面说,线程数设置多大,是根据我们自身的业务的,需要自己去压力测试,设置一个合理的数值。
当很多业务集中到一个线程池中,不像上面的案例比较简单,事实上业务太多,怎么设置呢?这个就是要去压力测试去调整。不过我们的前辈已经帮我们总结了一个基础的值(最终还是要看运行情况自行调整)
1、CPU密集型:操作内存处理的业务,一般线程数设置为:
CPU核数 + 1 或者 CPU核数*2。核数为4的话,一般设置 5 或 82、IO密集型:文件操作,网络操作,数据库操作,一般线程设置为:cpu核数 / (1-0.9),核数为4的话,一般设置 40
JVM线程
以32位Windows系统为例,每一个进程的用户地址空间是2G,假如每个线程栈的大小是128K,最多会有16384(=2*1024*1024 / 128)个线程。实际在XP系统上,大约能启动13000个线程。
[source]