高并发与高可用青竹玉简

高并发(HighConcurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。

QPS:每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。

并发用户数:同时承载正常使用系统功能的用户数量。例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。

二、如何提升系统的并发能力

互联网分布式架构设计,提高系统并发能力的方式,方法论上主要有两种:垂直扩展(ScaleUp)与水平扩展(ScaleOut)。

垂直扩展:提升单机处理能力。垂直扩展的方式又有两种:

(1)增强单机硬件性能,例如:增加CPU核数如32核,升级更好的网卡如万兆,升级更好的硬盘如SSD,扩充硬盘容量如2T,扩充系统内存如128G;

不管是提升单机硬件性能,还是提升单机架构性能,都有一个致命的不足:单机性能总是有极限的。所以互联网分布式架构设计高并发终极解决方案还是水平扩展。

水平扩展:只要增加服务器数量,就能线性扩充系统性能。水平扩展对系统架构设计是有要求的,如何在架构各层进行可水平扩展的设计,以及互联网公司架构各层常见的水平扩展实践,是本文重点讨论的内容。

三、常见的互联网分层架构

(1)客户端层:典型调用方是浏览器browser或者手机应用APP

(2)反向代理层:系统入口,反向代理

(3)站点应用层:实现核心应用逻辑,返回html或者json

(4)服务层:如果实现了服务化,就有这一层

(5)数据-缓存层:缓存加速访问存储

(6)数据-数据库层:数据库固化数据存储

整个系统各层次的水平扩展,又分别是如何实施的呢?

四、分层水平扩展架构实践

反向代理层的水平扩展

当nginx成为瓶颈的时候,只要增加服务器数量,新增nginx服务的部署,增加一个外网ip,就能扩展反向代理层的性能,做到理论上的无限高并发。

站点层的水平扩展

当web后端成为瓶颈的时候,只要增加服务器数量,新增web服务的部署,在nginx配置中配置上新的web后端,就能扩展站点层的性能,做到理论上的无限高并发。

服务层的水平扩展

站点层通过RPC-client调用下游的服务层RPC-server时,RPC-client中的连接池会建立与下游服务多个连接,当服务成为瓶颈的时候,只要增加服务器数量,新增服务部署,在RPC-client处建立新的下游服务连接,就能扩展服务层性能,做到理论上的无限高并发。如果需要优雅的进行服务层自动扩容,这里可能需要配置中心里服务自动发现功能的支持。

数据层的水平扩展

在数据量很大的情况下,数据层(缓存,数据库)涉及数据的水平扩展,将原本存储在一台服务器上的数据(缓存,数据库)水平拆分到不同服务器上去,以达到扩充系统性能的目的。

互联网数据层常见的水平拆分方式有这么几种,以数据库为例:

按照范围水平拆分

user0库,存储uid范围1-1kw

user1库,存储uid范围1kw-2kw

这个方案的好处是:

(1)规则简单,service只需判断一下uid范围就能路由到对应的存储服务;

(2)数据均衡性较好;

(3)比较容易扩展,可以随时加一个uid[2kw,3kw]的数据服务;

不足是:

(1)请求的负载不一定均衡,一般来说,新注册的用户会比老用户更活跃,大range的服务请求压力会更大;

按照哈希水平拆分

user0库,存储偶数uid数据

user1库,存储奇数uid数据

(1)规则简单,service只需对uid进行hash能路由到对应的存储服务;

(3)请求均匀性较好;

(1)不容易扩展,扩展一个数据服务,hash方法改变时候,可能需要进行数据迁移;

这里需要注意的是,通过水平拆分来扩充系统性能,与主从同步读写分离来扩充数据库性能的方式有本质的不同。

通过水平拆分扩展数据库性能:

(1)每个服务器上存储的数据量是总量的1/n,所以单机的性能也会有提升;

(2)n个服务器上的数据没有交集,那个服务器上数据的并集是数据的全集;

(3)数据水平拆分到了n个服务器上,理论上读性能扩充了n倍,写性能也扩充了n倍(其实远不止n倍,因为单机的数据量变为了原来的1/n);

通过主从同步读写分离扩展数据库性能:

(1)每个服务器上存储的数据量是和总量相同;

(2)n个服务器上的数据都一样,都是全集;

(3)理论上读性能扩充了n倍,写仍然是单点,写性能不变;

缓存层的水平拆分和数据库层的水平拆分类似,也是以范围拆分和哈希拆分的方式居多,就不再展开。

五、总结

提高系统并发能力的方式,方法论上主要有两种:垂直扩展(ScaleUp)与水平扩展(ScaleOut)。前者垂直扩展可以通过提升单机硬件性能,或者提升单机架构性能,来提高并发性,但单机性能总是有极限的,互联网分布式架构设计高并发终极解决方案还是后者:水平扩展。

互联网分层架构中,各层次水平扩展的实践又有所不同:

(1)反向代理层可以通过“DNS轮询”的方式来进行水平扩展;

(2)站点层可以通过nginx来进行水平扩展;

(3)服务层可以通过服务连接池来进行水平扩展;

(4)数据库可以按照数据范围,或者数据哈希的方式来进行水平扩展;

各层实施水平扩展后,能够通过增加服务器数量的方式来提升系统的性能,做到理论上的性能无限。

一、关于并发我们说的高并发是什么?

高并发,通常关心的系统指标与业务指标?

二、关于三种应对大并发的常见优化方案

【数据库缓存】

为什么是要使用缓存?

缓存数据是为了让客户端很少甚至不访问数据库,减少磁盘IO,提高并发量,提高应用数据的响应速度。

【CDN加速】

什么是CDN?

CDN的全称是ContentDeliveryNetwork,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离等综合信息将用户的请求重新导向离用户最近的服务节点上。

使用CDN的优势?

CDN的本质是内存缓存,就近访问,它提高了企业站点(尤其含有大量图片和静态页面站点)的访问速度,跨运营商的网络加速,保证不同网络的用户都得到良好的访问质量。

同时,减少远程访问的带宽,分担网络流量,减轻原站点WEB服务器负载。

【服务器的集群化,以及负载均衡】

什么是七层负载均衡?

内置策略:IPHash、加权轮询

扩展策略:fair策略、通用hash、一致性hash

什么是加权轮询策略?

首先将请求都分给高权重的机器,直到该机器的权值降到了比其他机器低,才开始将请求分给下一个高权重的机器,即体现了加权权重,又体现了轮询。

一、什么是高可用

假设系统一直能够提供服务,我们说系统的可用性是100%。

百度的搜索首页,是业内公认高可用保障非常出色的系统,甚至人们会通过www.baidu.com能不能访问来判断“网络的连通性”,百度高可用的服务让人留下啦“网络通畅,百度就能访问”,“百度打不开,应该是网络连不上”的印象,这其实是对百度HA最高的褒奖。

二、如何保障系统的高可用

我们都知道,单点是系统高可用的大敌,单点往往是系统高可用最大的风险和敌人,应该尽量在系统设计的过程中避免单点。方法论上,高可用保证的原则是“集群化”,或者叫“冗余”:只有一个单点,挂了服务会受影响;如果有冗余备份,挂了还有其他backup能够顶上。

保证系统高可用,架构设计的核心准则是:冗余。

有了冗余之后,还不够,每次出现故障需要人工介入恢复势必会增加系统的不可服务实践。所以,又往往是通过“自动故障转移”来实现系统的高可用。

接下来我们看下典型互联网架构中,如何通过冗余+自动故障转移来保证系统的高可用特性。

整个系统的高可用,又是通过每一层的冗余+自动故障转移来综合实现的。

四、分层高可用架构实践

【客户端层->反向代理层】的高可用

【反向代理层->站点层】的高可用

【站点层->服务层】的高可用

【服务层>缓存层】的高可用

缓存层的数据冗余又有几种方式:第一种是利用客户端的封装,service对cache进行双读或者双写。

以redis为例,redis天然支持主从同步,redis官方也有sentinel哨兵机制,来做redis的存活性检测。

说完缓存的高可用,这里要多说一句,业务对缓存并不一定有“高可用”要求,更多的对缓存的使用场景,是用来“加速数据访问”:把一部分数据放到缓存里,如果缓存挂了或者缓存没有命中,是可以去后端的数据库中再取数据的。

这类允许“cachemiss”的业务场景,缓存架构的建议是:

【服务层>数据库层】的高可用

大部分互联网技术,数据库层都用了“主从同步,读写分离”架构,所以数据库层的高可用,又分为“读库高可用”与“写库高可用”两类。

【服务层>数据库层“读”】的高可用

既然冗余了读库,一般来说就至少有2个从库,“数据库连接池”会建立与读库多个连接,每次请求会路由到这些读库。

【服务层>数据库层“写”】的高可用

以mysql为例,可以设置两个mysql双主同步,一台对线上提供服务,另一台冗余以保证高可用,常见的实践是keepalived存活探测,相同virtualIP提供服务。

方法论上,高可用是通过冗余+自动故障转移来实现的。

整个互联网分层系统架构的高可用,又是通过每一层的冗余+自动故障转移来综合实现的,具体的:

(1)【客户端层】到【反向代理层】的高可用,是通过反向代理层的冗余实现的,常见实践是keepalived+virtualIP自动故障转移

(2)【反向代理层】到【站点层】的高可用,是通过站点层的冗余实现的,常见实践是nginx与web-server之间的存活性探测与自动故障转移

(3)【站点层】到【服务层】的高可用,是通过服务层的冗余实现的,常见实践是通过service-connection-pool来保证自动故障转移

(4)【服务层】到【缓存层】的高可用,是通过缓存数据的冗余实现的,常见实践是缓存客户端双读双写,或者利用缓存集群的主从数据同步与sentinel保活与自动故障转移;更多的业务场景,对缓存没有高可用要求,可以使用缓存服务化来对调用方屏蔽底层复杂性

(5)【服务层】到【数据库“读”】的高可用,是通过读库的冗余实现的,常见实践是通过db-connection-pool来保证自动故障转移

(6)【服务层】到【数据库“写”】的高可用,是通过写库的冗余实现的,常见实践是keepalived+virtualIP自动故障转移

互联网公司,这样的场景是否似曾相识:

场景一:pm要做一个很大的运营活动,技术老大杀过来,问了两个问题:

(1)机器能抗住么?

(2)如果扛不住,需要加多少台机器?

场景二:系统设计阶段,技术老大杀过来,又问了两个问题:

(1)数据库需要分库么?

(2)如果需要分库,需要分几个库?

【步骤一:评估总访问量】

如何知道总访问量?对于一个运营活动的访问量评估,或者一个系统上线后PV的评估,有什么好的方法?

答案是:询问业务方,询问运营同学,询问产品同学,看对运营活动或者产品上线后的预期是什么。

举例:58要做一个APP-push的运营活动,计划在30分钟内完成5000w用户的push推送,预计push消息点击率10%,求push落地页系统的总访问量?

回答:5000w*10%=500w

【步骤二:评估平均访问量QPS】

如何知道平均访问量QPS?

举例1:push落地页系统30分钟的总访问量是500w,求平均访问量QPS

回答:500w/(30*60)=2778,大概3000QPS

举例2:主站首页估计日均pv8000w,求平均访问QPS

回答:一天按照4w秒算,8000w/4w=2000,大概2000QPS

提问:为什么一天按照4w秒计算?

回答:一天共24小时*60分钟*60秒=8w秒,一般假设所有请求都发生在白天,所以一般来说一天只按照4w秒评估

【步骤三:评估高峰QPS】

系统容量规划时,不能只考虑平均QPS,而是要抗住高峰的QPS,如何知道高峰QPS呢?

答案是:根据业务特性,通过业务访问曲线评估

举例:日均QPS为2000,业务访问趋势图如下图,求峰值QPS预估?

说明:有一些业务例如“秒杀业务”比较难画出业务访问趋势图,这类业务的容量评估不在此列。

【步骤四:评估系统、单机极限QPS】

如何评估一个业务,一个服务单机能的极限QPS呢?

答案是:压力测试

在一个服务上线前,一般来说是需要进行压力测试的(很多创业型公司,业务迭代很快的系统可能没有这一步,那就悲剧了),以APP-push运营活动落地页为例(日均QPS2000,峰值QPS5000),这个系统的架构可能是这样的:

2)运营活动H5落地页是一个web站点

3)H5落地页由缓存cache、数据库db中的数据拼装而成

通过压力测试发现,web层是瓶颈,tomcat压测单机只能抗住1200的QPS(一般来说,1%的流量到数据库,数据库500QPS还是能轻松抗住的,cache的话QPS能抗住,需要评估cache的带宽,假设不是瓶颈),我们就得到了web单机极限的QPS是1200。一般来说,线上系统是不会跑满到极限的,打个8折,单机线上允许跑到QPS1000。

【步骤五:根据线上冗余度回答两个问题】

好了,上述步骤1-4已经得到了峰值QPS是5000,单机QPS是1000,假设线上部署了2台服务,就能自信自如的回答技术老大提出的问题了:

(1)机器能抗住么?->峰值5000,单机1000,线上2台,扛不住

(2)如果扛不住,需要加多少台机器?->需要额外3台,提前预留1台更好,给4台更稳

除了并发量的容量预估,数据量、带宽、CPU/MEM/DISK等评估亦可遵循类似的步骤。

互联网架构设计如何进行容量评估:

【步骤一:评估总访问量】->询问业务、产品、运营

【步骤三:评估高峰QPS】->根据业务曲线图来

【步骤四:评估系统、单机极限QPS】->压测很重要

【步骤五:根据线上冗余度回答两个问题】->估计冗余度与线上冗余度差值

【业务场景】

有一类写多读少的业务场景:大部分请求是对数据进行修改,少部分请求对数据进行读取。

例子1:滴滴打车,某个司机地理位置信息的变化(可能每几秒钟有一个修改),以及司机地理位置的读取(用户打车的时候查看某个司机的地理位置)。

voidSetDriverInfo(longdriver_id,DriverInfoi);//大量请求调用修改司机信息,可能主要是GPS位置的修改

DriverInfoGetDriverInfo(longdriver_id);//少量请求查询司机信息

例子2:统计计数的变化,某个url的访问次数,用户某个行为的反作弊计数(计数值在不停的变)以及读取(只有少数时刻会读取这类数据)。

voidAddCountByType(longtype);//大量增加某个类型的计数,修改比较频繁

longGetCountByType(longtype);//少量返回某个类型的计数

【底层实现】

具体到底层的实现,往往是一个Map(本质是一个定长key,定长value的缓存结构)来存储司机的信息,或者某个类型的计数。

Map

Map

【临界资源】

这个Map存储了所有信息,当并发读写访问时,它作为临界资源,在读写之前,一般要进行加锁操作,以司机信息存储为例:

voidSetDriverInfo(longdriver_id,DriverInfoinfo){

WriteLock(m_lock);

Map=info;

UnWriteLock(m_lock);

}

DriverInfoGetDriverInfo(longdriver_id){

DriverInfot;

ReadLock(m_lock);

t=Map;

UnReadLock(m_lock);

returnt;

【并发锁瓶颈】

假设滴滴有100w司机同时在线,每个司机没5秒更新一次经纬度状态,那么每秒就有20w次写并发操作。假设滴滴日订单1000w个,平均每秒大概也有300个下单,对应到查询并发量,可能是1000级别的并发读操作。

上述实现方案没有任何问题,但在并发量很大的时候(每秒20w写,1k读),锁m_lock会成为潜在瓶颈,在这类高并发环境下写多读少的业务仓井,如何来进行优化,是本文将要讨论的问题。

上文中之所以锁冲突严重,是因为所有司机都公用一把锁,锁的粒度太粗(可以认为是一个数据库的“库级别锁”),是否可能进行水平拆分(类似于数据库里的分库),把一个库锁变成多个库锁,来提高并发,降低锁冲突呢?显然是可以的,把1个Map水平切分成多个Map即可:

i=driver_id%N;//水平拆分成N份,N个Map,N个锁

WriteLock(m_lock[i]);//锁第i把锁

Map[i]=info;//操作第i个Map

UnWriteLock(m_lock[i]);//解锁第i把锁

每个Map的并发量(变成了1/N)和数据量都降低(变成了1/N)了,所以理论上,锁冲突会成平方指数降低。

分库之后,仍然是库锁,有没有办法变成数据库层面所谓的“行级锁”呢,难道要把x条记录变成x个Map吗,这显然是不现实的。

假设driver_id是递增生成的,并且缓存的内存比较大,是可以把Map优化成Array,而不是拆分成N个Map,是有可能把锁的粒度细化到最细的(每个记录一个锁)。

index=driver_id;

WriteLock(m_lock[index]);//超级大内存,一条记录一个锁,锁行锁

Array[index]=info;//driver_id就是Array下标

UnWriteLock(m_lock[index]);//解锁行锁

上文中提到的另一个例子,用户操作类型计数,操作类型是有限的,即使一个type一个锁,锁的冲突也可能是很高的,还没有方法进一步提高并发呢?

【无锁的结果】

voidAddCountByType(longtype/*,intcount*/){

//不加锁

Array[type]++;//计数++

//Array[type]+=count;//计数增加count

【脏数据是如何产生的】

这个并发写的脏数据是如何产生的呢,详见下图:

2)线程2对缓存进行操作,对key想要写入value2

3)如果不加锁,线程1和线程2对同一个定长区域进行一个并发的写操作,可能每个线程写成功一半,导致出现脏数据产生,最终的结果即不是value1也不是value2,而是一个乱七八糟的不符合预期的值value-unexpected。

【数据完整性问题】

并发写入的数据分别是value1和value2,读出的数据是value-unexpected,数据的篡改,这本质上是一个数据完整性的问题。通常如何保证数据的完整性呢?

例子1:运维如何保证,从中控机分发到上线机上的二进制没有被篡改?

回答:md5

例子2:即时通讯系统中,如何保证接受方收到的消息,就是发送方发送的消息?

回答:发送方除了发送消息本身,还要发送消息的签名,接收方收到消息后要校验签名,以确保消息是完整的,未被篡改。

当当当当=>“签名”是一种常见的保证数据完整性的常见方案。

【加上签名之后的流程】

加上签名之后,不但缓存要写入定长value本身,还要写入定长签名(例如16bitCRC校验):

1)线程1对缓存进行操作,对key想要写入value1,写入签名v1-sign

2)线程2对缓存进行操作,对key想要写入value2,写入签名v2-sign

3)如果不加锁,线程1和线程2对同一个定长区域进行一个并发的写操作,可能每个线程写成功一半,导致出现脏数据产生,最终的结果即不是value1也不是value2,而是一个乱七八糟的不符合预期的值value-unexpected,但签名,一定是v1-sign或者v2-sign中的任意一个

4)数据读取的时候,不但要取出value,还要像消息接收方收到消息一样,校验一下签名,如果发现签名不一致,缓存则返回NULL,即cachemiss。

当然,对应到司机地理位置,与URL访问计数的case,除了内存缓存之前,肯定需要timer对缓存中的数据定期落盘,写入数据库,如果cachemiss,可以从数据库中读取数据。

在【超高并发】,【写多读少】,【定长value】的【业务缓存】场景下:

1)可以通过水平拆分来降低锁冲突

2)可以通过Map转Array的方式来最小化锁冲突,一条记录一个锁

3)可以把锁去掉,最大化并发,但带来的数据完整性的破坏

4)可以通过签名的方式保证数据的完整性,实现无锁缓存

我们经常使用事务来保证数据库层面数据的ACID特性。

举个栗子,用户下了一个订单,需要修改余额表,订单表,流水表,于是会有类似的伪代码:

starttransaction;

CURDtablet_account;anyExceptionrollback;

CURDtablet_order;anyExceptionrollback;

CURDtablet_flow;anyExceptionrollback;

commit;

如果对余额表,订单表,流水表的SQL操作全部成功,则全部提交,如果任何一个出现问题,则全部回滚,以保证数据的一致性。

互联网的业务特点,数据量较大,并发量较大,经常使用拆库的方式提升系统的性能。如果进行了拆库,余额、订单、流水可能分布在不同的数据库上,甚至不同的数据库实例上,此时就不能用事务来保证数据的一致性了。这种情况下如何保证数据的一致性,是今天要讨论的话题。

补偿事务是一种在业务端实施业务逆向操作事务,来保证业务数据一致性的方式。

举个栗子,修改余额表事务为

intDo_AccountT(uid,money){

//余额改变money这么多

CURDtablet_accountwithmoney;anyExceptionrollbackreturnNO;

returnYES;

那么补偿事务可以是:

intCompensate_AccountT(uid,money){

//做一个money的反向操作

returnDo_AccountT(uid,-1*money){

同理,订单表操作为

Do_OrderT,新增一个订单

Compensate_OrderT,删除一个订单

要保重余额与订单的一致性,可能要写这样的代码:

//执行第一个事务

intflag=Do_AccountT();

if(flag=YES){

//第一个事务成功,则执行第二个事务

flag=Do_OrderT();

//第二个事务成功,则成功

else{

//第二个事务失败,执行第一个事务的补偿事务

Compensate_AccountT();

该方案的不足是:

(1)不同的业务要写不同的补偿事务,不具备通用性

(2)没有考虑补偿事务的失败

(3)如果业务流程很复杂,if/else会嵌套非常多层

例如,如果上面的例子加上流水表的修改,加上Do_FlowT和Compensate_FlowT,可能会变成一个这样的if/else:

//第二个事务成功,则执行第三个事务

flag=Do_FlowT();

//第三个事务成功,则成功

//第三个事务失败,则执行第二、第一个事务的补偿事务

flag=Compensate_OrderT();

if…else…//补偿事务执行失败?

flag=Compensate_AccountT();

单库是用这样一个大事务保证一致性:

拆分成了多个库,大事务会变成三个小事务:

starttransaction1;

//第一个库事务执行

//第一个库事务提交

commit1;

starttransaction2;

//第二个库事务执行

//第二个库事务提交

commit2;

starttransaction3;

//第三个库事务执行

//第三个库事务提交

commit3;

第二个事务执行120ms,提交1ms;

第三个事务执行80ms,提交1ms;

那在什么时候系统出现问题,会出现不一致呢?

回答:第一个事务成功提交之后,最后一个事务成功提交之前,如果出现问题(例如服务器重启,数据库异常等),都可能导致数据不一致。

如果改变事务执行与提交的时序,变成事务先执行,最后一起提交,情况会变成什么样呢:

第二个事务执行120ms;

第三个事务执行80ms;

第一个事务执行1ms;

第二个事务执行1ms;

第三个事务执行1ms;

问题的答案与之前相同:第一个事务成功提交之后,最后一个事务成功提交之前,如果出现问题(例如服务器重启,数据库异常等),都可能导致数据不一致。

这个变化的意义是什么呢?

虽然没有彻底解决数据的一致性问题,但不一致出现的概率大大降低了!

事务提交后置降低了数据不一致的出现概率,会带来什么副作用呢?

trx1.exec();

trx1.commit();

trx2.exec();

trx2.commit();

trx3.exec();

trx3.commit();

优化为:

一、需求缘起

Web-Server通常有个配置,最大工作线程数,后端服务一般也有个配置,工作线程池的线程数量,这个线程数的配置不同的业务架构师有不同的经验值,有些业务设置为CPU核数的2倍,有些业务设置为CPU核数的8倍,有些业务设置为CPU核数的32倍。

“工作线程数”的设置依据是什么,到底设置为多少能够最大化CPU性能,是本文要讨论的问题。

在进行进一步深入讨论之前,先以提问的方式就一些共性认知达成一致。

提问:工作线程数是不是设置的越大越好?

回答:肯定不是的

1)一来服务器CPU核数有限,同时并发的线程数是有限的,1核CPU设置10000个工作线程没有意义

2)线程切换是有开销的,如果线程切换过于频繁,反而会使性能降低

提问:调用sleep()函数的时候,线程是否一直占用CPU?

回答:不占用,等待时会把CPU让出来,给其他需要CPU资源的线程使用

不止调用sleep()函数,在进行一些阻塞调用,例如网络编程中的阻塞accept()【等待客户端连接】和阻塞recv()【等待下游回包】也不占用CPU资源

提问:如果CPU是单核,设置多线程有意义么,能提高并发性能么?

回答:即使是单核,使用多线程也是有意义的

1)多线程编码可以让我们的服务/代码更加清晰,有些IO线程收发包,有些Worker线程进行任务处理,有些Timeout线程进行超时检测

2)如果有一个任务一直占用CPU资源在进行计算,那么此时增加线程并不能增加并发,例如这样的一个代码

while(1){i++;}

该代码一直不停的占用CPU资源进行计算,会使CPU占用率达到100%

3)通常来说,Worker线程一般不会一直占用CPU进行计算,此时即使CPU是单核,增加Worker线程也能够提高并发,因为这个线程在休息的时候,其他的线程可以继续工作

了解常见的服务线程模型,有助于理解服务并发的原理,一般来说互联网常见的服务线程模型有如下两种

IO线程与工作线程通过队列解耦类模型

1)有少数几个IO线程监听上游发过来的请求,并进行收发包(生产者)

2)有一个或者多个任务队列,作为IO线程与Worker线程异步解耦的数据传输通道(临界资源)

3)有多个工作线程执行正真的任务(消费者)

这个线程模型应用很广,符合大部分场景,这个线程模型的特点是,工作线程内部是同步阻塞执行任务的(回想一下tomcat线程中是怎么执行Java程序的,dubbo工作线程中是怎么执行任务的),因此可以通过增加Worker线程数来增加并发能力,今天要讨论的重点是“该模型Worker线程数设置为多少能达到最大的并发”。

纯异步线程模型

1)如果使用单线程模式,难以利用多CPU多核的优势

2)程序员更习惯写同步代码,callback的方式对代码的可读性有冲击,对程序员的要求也更高

3)框架更复杂,往往需要server端收发组件,server端队列,client端收发组件,client端队列,上下文管理组件,有限状态机组件,超时管理组件的支持

however,这个模型不是今天讨论的重点。

了解工作线程的工作模式,对量化分析线程数的设置非常有帮助:

2)访问cache拿一些数据

6)访问DB进行一些数据操作

2.1)请求在网络上传输到下游的cache、service、DB

2.2)下游cache、service、DB进行任务处理

2.3)cache、service、DB将报文在网络上传回工作线程

最后一起来回答工作线程数设置为多少合理的问题。

1)假设此时是单核,则设置为2个工作线程就可以把CPU充分利用起来,让CPU跑到100%

2)假设此时是N核,则设置为2N个工作现场就可以把CPU充分利用起来,让CPU跑到N*100%

THE END
1.1毫秒屏幕响应时间有什么用?华硕商城 在得物App发布了一条热门动态!快来围观,就等你啦!https://m.dewu.com/note/trend/details?id=253438965
2.内存时序解析:提升电脑性能的关键因素与选购指南首先是CAS延迟,也就是“Column Address Strobe”延迟。简单说,就是内存控制器发出读取命令后,内存芯片需要多长时间才能将数据返回给控制器。这个时间越短,内存的响应速度就越快。通常,CAS延迟的数值越小,性能越好。 接下来是RAS到CAS延迟,简称为tRCD。这项指标代表了内存芯片在行地址和列地址之间切换所需的时间。行https://news.huochengrm.cn/cygs/6344.html
3.系统性能优化性能优化的目标:是提高系统或应用程序的响应时间、吞吐量、效率和可伸缩性等方面的性能指标。 性能优化需要有一些技巧:对于一个整个产品或项目而言,比如可以从前端优化、后端优化、架构优化、高并发优化、Linux内核优化、常用中间件优化等方便去优化,当然每个方面侧重点不同,用的技术点也不同。 https://blog.csdn.net/javajy/article/details/144394515
4.计算机性能入门:新手必读教程内存容量越大,同时运行的程序越多,性能越好。 对于专业应用,例如视频编辑或大型数据库操作,建议选择更大的内存容量。 一般桌面机建议选择8GB以上的内存,对于高性能工作站建议选择16GB或更高。 硬盘选择: 选择SSD固态硬盘可以提高读写速度,减少系统启动和程序加载时间。 如果需要大容量存储,可以选择混合硬盘或机械硬盘https://www.imooc.com/article/372069
5.如何评估中间件的性能和质量?评估中间件的标准有哪些?股票频道评估中间件的性能,首先要考虑其响应时间。响应时间是指从发送请求到收到响应所经过的时间。较短的响应时间意味着中间件能够快速处理请求,提供更流畅的服务。可以通过模拟大量并发请求来测试中间件在不同负载下的响应时间。 吞吐量也是一个关键指标。它表示单位时间内中间件能够处理的请求数量。高吞吐量意味着中间件能够https://stock.hexun.com/2024-12-06/216014901.html
6.存储性能基准测试方法与工具天翼云开发者社区IOPS(Input/Output Operations Per Second):每秒输入/输出操作数,是衡量存储系统处理读写请求能力的重要指标。IOPS越高,表示存储系统在处理大量随机读写请求时性能越好。 传输速度:也称为吞吐量,表示存储系统在单位时间内能够传输的数据量。高传输速度意味着存储系统能够更快地处理大量数据。 https://www.ctyun.cn/developer/article/619805361881157
7.液晶显示屏的那个响应时间是高好还是越低越好?4条回答:响应时间指的是LCD显示器对于输入信号的反应速度,也就是液晶由暗转亮或者是由亮转暗的反应时间。所以是越短越好。https://wap.zol.com.cn/ask/x_8783566.html
8.响应时间越小越好还是越大越好当然是越小越好.响应时间,顾名思义,就是LCD显示器对于输入信号的反应速度,也就是液晶由暗转亮或者是由亮转暗的反应时间。如果这个时间越大,则显示器的拖尾现象就越严重.https://zhidao.baidu.com/question/29495870.html
9.负荷率越大越好还是越小越好?理想状态是什么?从事高致病性病原微生物相关实验活动的实验室的设立单位,应当建立健全安全保卫制度,采取安全保卫措施,严防高致病性病原微生物( ),保障实验室及其病原微生物的安全。实验室发生上述情况的,实验室的设立单位应当依照《病原微生物实验室生物安全管理条例》第十七条的规定进行报告。https://www.shuashuati.com/ti/0648c49fea6c41ef89ef9a1939198c3e.html?fm=bd1e4100177d50917632c7c958e88b1699
10.传感器灵敏度是什么?灵敏度是越大越好吗?灵敏度是越大越好吗? 传感器是一种检测装置,能感受到被测量的信息,并能将感受到的信息,按一定规律变换成为电信号或其他所需形式的信息输出,传感器的灵敏度是指传感器在稳态工作情况下,输出量变化对输入量变化的比值。它是输出一输入特性曲线的斜率,如果传感器的输出和输入之间显线性关系,则灵敏度S是一个常数,否则,https://m.mrchip.cn/newsDetail/2696
11.轮毂大小不是越大越好?如何选择适合的轮毂尺寸?大轮毂可以让车轮转动的角速度更慢,减少了因为响应时间慢造成的加速性能不足的问题。 但是,大尺寸的轮毂也会增加轮胎的重量,降低油耗效率。 小尺寸的轮毂则相反,虽然加速性能更好,但是也会增加引擎负担,使油耗增加。 其次,轮毂尺寸还影响车辆的稳定性。 https://www.yoojia.com/article/9891217392363344382.html
12.2024液晶电视机选购指南,千元4.色域越大越好? 大家在买电视的时候,往往会看它的色域大小。 一定程度上,色域越大的电视,的确能带来更丰富的画面色彩,但也得看它的标准是什么。 一般色域标准分为BT.709、DCI-P3和BT.2020,其中BT.2020涵盖的色域范围最大,BT.709涵盖的色域范围越小。大多数情况下,主流电视会以DCI-P3为首选标准。 https://zhuanlan.zhihu.com/p/482008649
13.网络吞吐量android网络吞吐量越大越好吗网络吞吐量 android 网络吞吐量越大越好吗 1、吞吐量 单位时间内,系统能够处理多少请求,吞吐量代表网络的流量,TPS越高,吞吐量越大,还包含了数据的吞吐量。一般单位为秒,每秒处理的请求量。 注意:我们看到的JMeter聚合报告一般如下图,下表中的吞吐量实际是我们文中说的TPS或者QPS。如果要计算吞吐量的话应该是接收https://blog.51cto.com/u_16213711/8695233
14.响应时长并非越久越佳(响应时间越短越好吗)服务器技术响应时长并非越久越佳(响应时间越短越好吗) 60Hz刷新率8ms响应时间的电视,会有拖影吗 1、从理论上讲,一般的文字处理用户,只要响应时间不超过40ms的LCD就可以接受。我们平常看到的电影,每秒钟只能显示24帧画面(1/24帧=40ms),而这时我们已经不会感觉到画面的延迟了,当画面显示速度超过每秒25帧时,人眼就会将快速https://www.kangle.im/post/107479.html
15.如何设计老龄化产品(二):12个必须关注的设计点优设网44*44 point 是 Apple HIG 建议的一个最小点触面积,这里的 44point 可以换算成物理尺寸为0.27英寸(6.86mm)。有文章建议老龄化产品的点触最小目标为10.5mm,同时减少相邻互动元素之间的距离,至少相距2mm。 再说「Fitts` Law」中表明任意一点移动到目标中心点位置所需的时间与该点到目标的距离和大小有关,距离越大https://www.uisdc.com/designing-elderly-products-2
16.显示器响应时间越高越好?指不定也是智商税习惯了普遍高素质的手机屏幕,越来越多人不再满足于显示器的「能亮就行」。同时好不好、值不值的评判标准也在发生着变化。比如几年前还挺昂贵的高分辨率、高刷新率,现在是真不值钱了,但同样很重要的 1ms 显示器响应时间,就不是都一个样了。 显示器响应时间中的学问 http://www.dianshouit.com/thread-1010.htm
17.无线传输速率是不是越大越好随着科技的不断发展,我们对无线传输速率的需求也越来越高。无线传输速率指的是无线网络传输数据的速度,通常以Mbps(兆位每秒)为单位。在选择无线网络设备或者订购网络服务时,我们会关注其传输速率的大小,常常认为速率越大越好。然而,无线传输速率是否真的越大越好呢?本文将从不同的角度来探讨这个问题。 https://www.chugeyun.com/news/12655.html
18.淘宝客服平均响应时间影响客服响应的因素客服平均响应时间是指客服在回复客户的过程中,从客户的咨询到客服的回复的每一次时间差的均值,当然,这个数值要越小越好,这项数据可以帮助店铺分析客服的响应速度够不够及时,一般小型的店铺接待量200-300的话,客服的平就响应时间是16秒左右。 影响这个标准的因素有很多,主要涉及到以下几点:https://www.mmker.cn/article/19857.html