多并发是网站的基本要求,大型网站的并发量甚至会达到数万,单台服务器的并发用户也会达到数百,例如淘宝的双十一、商务网站的促销活动。

 

一、多少线程合适

 

多并发又可以分为CPU密集型和IO密集型。

1CPU密集型即需要非常多的CPU计算资源,如有多颗CPU核心,可以让每一颗CPU都参与计算,从而不浪费服务器资源,CPU密集型的典型例子,例如文件排序、图形搜索、动态规划等需要复杂的科学计算的情况,CPU密集型需要减少线程数,减少线程的上下文切换导致的资源损耗。

2IO密集型即需要大量的数据读写,例如网络传输、数据库读写等,大部分的网站、企业应用系统都属于IO密集型,当发生IO读写的时候,由于IO操作时间很长(受限于硬盘的读写速度和网络传输速度),线程会处于等待状态,此时CPU空闲,这时CPU可以调度其他线程进行处理,IO密集型需要增加线程数,这样在IO处理的时候,可以去做其他事情,以提高并发量。

 

对于多并发,目前主要采用多进程和多线程两种模式,例如一台服务器上有多个应用服务器,然后多个应用服务器接收多个线程并发执行,因为线程之间的切换也有成本,所以也有观点认为应该采用多进程的模式。总之,进程数和线程数应该要根据实际情况进行综合选择。

 

那么,到底启动多少线程合适呢?根据以上分析,线程数应该和CPU核心数量成正比,而和IO阻塞时间成反比,因此有两个公式可供参考:

 

1线程数=[任务执行时间/(任务执行时间-IO等待时间)] * CPU内核数

 

  

 

2线程数 = CPU核心数/(1-阻塞系数)

   这个阻塞系数一般为0.8~0.9之间,也可以取0.8或者0.9

 

如果是CPU密集型,则线程数不超过CPU内核,如果是IO密集型,则应该增加线程数,提高并发量。

 

当发现系统运行慢的时候,不是盲目去加CPU加内存或者加硬盘,应该充分了解系统资源的使用情况,从而决定如何升级系统配置,例如在Linux环境下用TOP指令观察CPU、硬盘的使用情况:

   Tasks: 29 total, 1 running, 28 sleeping,0 stopped, 0 zombie
    Cpu(s): 0.3% us, 1.0% sy, 0.0% ni, 98.7%id, 0.0% wa, 0.0% hi, 0.0% si


0.3% us反应CPU使用情况,0.0% wa 则大致体现出当前的磁盘io请求是否频繁。如果 wa的数量比较大,说明等待输入输出的的io比较多,另外还可以通过交互命令H了解各个线程对CPU的使用情况。

 

Mysql中还可以通过show processlist命令查看高导致磁盘频繁读写的查询语句。

 

二、多层次连接池

 

从以上分析,线程也不应***,而且线程之间的切换需要成本,而且线程数过多,对资源抢占却不释放,会出现死锁的情况,在数据库操作中经常会碰到这样情况。所以应该通过连接池,控制线程数量,并减少线程之间的切换,对于一个多层次的网站结构,有Web服务器、应用服务器、缓存服务器、数据库服务器。

 

可以在不同服务器之间通过多个连接池进行互联,从而控制并发数和线程资源。

 

wKioL1ax1y-R4EUeAADwUEpT9FE467.png

 


三、异步消息实现高并发

 

     在不使用消息队列的情况,所以请求都是并发操作,会对数据库造成很大的压力,也导致系统响应能力下降。

 

使用消息队列,则用户可以马上得到响应,数据写入消息队列,由消费者从队列获取数据,异步写入数据,从而提高用户响应速度。如果消息队列满,则可以即使反馈给用户等待,而不是导致系统崩溃,该做法体现在淘宝双十一活动的前10分钟,当时用户并发操作量激增,如果没有使用消息队列,则系统崩溃,使用消息队列从而实现并发高峰的削平,消息队列也可以应用于公交车到站提醒、促销等多用户高并发的场景。

  

    当前主要的消息队列有:IBM MQRabbitMQActiveMQ等。

 

四、使用集群提高并发处理能力

 

  使用负载均衡可以将并发访问分散到多台服务器上处理,避免单台服务器压力过大,提高系统响应能力。

   可以采用重定向负载均衡、DNS负载均衡、反向代理负载均衡、IP负载均衡等模式

 

 

五、多线程模式下的线程安全

 

并发线程,容易产生线程冲突,因此需要解决线程安全问题

 

1、讲对象设计为无状态,这样不会出现状态不一致情况,例如Servlet就是无状态,因此是线程安全

 

2、使用ThreadLocal的局部对象,这样可以减少线程之间并发操作对象

 

3、并发使用锁,通过锁实现顺序操作,避免并发修改,但是锁会带来系统性能的下降,javaconcurrent包下的ConcurrentHashMap、ConcurrentLinkedQueue和CopyOnWriteArrayList等等,都是线程安全的。




除此之外,还可以考虑采用更为复杂的分布式结构:分布式服务,例如SOA,分布式缓存,例如memcache、redis等,网络上的加速,例如CDN等,硬件上的升级,例如固态硬盘等。