之前的两篇系统架构的博客中都提到了高并发、高可用技术,但是却都没有详细聊过,今天就好好聊一下常见的高并发技术。
一 高并发技术核心
高并发技术的核心是分流;分别针对请求的各个环节,根据具体场景和业务特点采用不同的分流方案,逐层逐级的分担系统压力,从而达到高并发能力。
常见的高并发技术有:动静分离、缓存、异步并发、水平扩展等。分流简单来说就是:一台服务器承担不了的流量,就让多台服务器共同分担;DB承担不了流量就让缓存来帮忙分担;等等。接下来讨论的所有内容都是围绕着分流来实现的。
现实中没有一招鲜吃遍天的美事儿,每一个公司、每一种业务、每一个场景、每一个功能都有其与众不同的特点,现实中一般都会根据业务特征、根据请求的各个环节特点,做针对性优化。
二 分阶段优化策略
1. 模型
一次请求主要分为四个阶段:客户端发起请求;通过网络将请求信息发送到服务端;服务端处理请求;服务端将结果返回给客户端。为了追求并发的极致,我们可以针对每一个阶段分别使用不同的策略,来提升系统的并发性能。模型如下:
这是一个漏斗型的模型,通过逐层过滤,将流量分散,让尽可能少的请求到达底层。
此模型的核心就是,根据各环节的特点,选择相应的优化策略,从而实现高并发的目的。
2. 客户端优化技术
客户端优化技术主要指的是客户端通过缓存数据,减少访问服务端的次数,以实现降低服务端压力,达到支持更多并发量的需求。
常见的处理方式是:缓存不经常变动的内容,每隔一定时间更新一次,或者除非修改了否则较长时间不更新;如:更新微信公众账号的头像时,手机端不会立即看到这个更新。另外,根据业务场景可以限制不必要的请求;如:点击一个按钮,发送一次请求到服务端时,禁用按钮,避免因用户多次点击,而发送不必要的请求。
3. 网络优化技术
网络优化的核心目标是:将资源缓存到距离用户最近的网络节点上,这样除了可以省下大量网络带宽外,还可以达到最快的请求速度;一般针对静态资源。
通过静态化技术(将不经常变动的内容生成静态文件)或动静分离技术(例如H5+ajax),将静态的内容存储在CDN上,当用户查看一个页面时,只让少数的动态请求落到服务器端,其他内容从CDN上直接获取,这样就可以极大的减少服务器端的流量。
4. 服务端优化技术
1) 负载均衡
负载均衡的核心是让每台服务器以一个合适的负载来分担所有的请求。
常见的负载均衡方案有:随机、轮询、hash、一致性hash。当然在这些基础上有很多扩展,例如在负载均衡的机器中设置不同的权重;根据key的范围不同调用不同的服务器等等。
2) 缓存
a) 为什么高效
缓存的结构一般都是key-value结构,通过key直接寻址,时间复杂度为O(1),这比DB的遍历比较来快了非常多;另外,不少的缓存中间件通过在内存中存储数据,不用从磁盘中获取数据,性能也就会非常高。
说明一下:大部分缓存中间件都会对value做一些扩充,让value也成为一个复杂的数据结构,例如Redis的Set、SortSet,如果需要在value中寻找某一个值,时间复杂度会退化成为O(N);这些数据结构提供了强大的功能,使用的时候注意合理使用,以避免因为缓存乱用导致性能降低的问题。
b) 分布式缓存
如redis、tair、memcached等。
优点是:集群部署,不用考虑容量问题,数据支持持久化,支持高可用。
缺点是:因为服务器和缓存数据交换时存在一次网络交互,如果数据结构选择不合理或者并发量非常大或者每次交互的数据量较大时,可能会引起io阻塞等问题,需谨慎。
c) 堆缓存
通过java的堆来缓存数据。
优点是:速度非常快。
缺点是:存储数据量受限,断点数据丢失,分布式场景中如果出现不一致时,处理麻烦。
d) 磁盘文件缓存
将数据存储到服务器所在的磁盘上。
优点是:存储内容较堆内存多(少于分布式缓存),是持久化的。
缺点是:服务器迁移时也要迁移磁盘中的文件,分布式场景中如果出现不一致时,处理麻烦。
e) 注意事项
缓存数据和数据库数据不一致的问题。
缓存击穿问题。
缓存数据丢失问题(缓存一般都需要持久化的,为了保证性能缓存一般都不是同步持久化,所以是可能存在数据丢失问题的)。
3) 通信模型
高性能的IO通信模型可以给系统带来巨大的性能提升。目前就较为成熟、高效的IO通行模型是NIO(也叫IO多路复用、异步阻塞IO)。
在java服务器前加一层Nginx服务器,它既可以提供良好的负载均衡能力,也能够支持非常高的并发。当然Nginx的高性能还与他的master-worker设计有关,几乎做到了无线程切换。
使用Dubbo进行RPC调用时,默认使用的Netty也是基于NIO的。
4) 队列技术
队列技术的作用是将同步转化成异步;相比于同步,异步可以根据服务器的处理能力来消化这些请求;同步则是不顾忌服务器的承受能力,所有请求一起蜂拥而入。
常见的技术有MQ消息服务器,例如RocketMQ、RabbitMQ、kafka;其中RocketMQ和kafka都可以支持非常大的并发量和消息量,性能非常的好。
5) 线程池
线程池是一种很实用的提升系统的服务能力的方案,其思路就是一个线程处理的慢,那创建一堆线程出来处理,速度自然就快了;线程池还有另外一个优点,重复利用连接,降低创建成本。线程池大小的设置一般参考一下两点:
高并发、任务执行时间短的任务,线程池线程数可以设置为CPU核数,减少线程上下文的切换;主要针对cpu密集型任务。例如:nginx中worker线程的数目。
线程池设置可以参考《ThreadPoolExecutor》博客中的“线程池配置”部分内容
6) DB存储
DB一般是服务端性能的瓶颈,所以对DB的优化方式也比较多,整理起来主要有以下几种:
表级别性能优化,例如:单表操作,避免联表操作;使用索引;事务尽可能短;查询优化;等等。
读写分离。
分库分表;分区(注意不合适的分区策略不但不能提升性能,反而可能降低性能)。
5. 响应优化技术
返回结果优化主要是压缩返回结果,通过降低字节长度来降低网络消耗。分多次加载数据,每次只显示一屏数据。
这种技术在静态内容中使用的比较多,例如压缩javascript、css文件等,动态请求中使用的不多。
三 根据业务特征优化
现实中有非常多种根据业务特征优化的示例,这里仅仅提供两个示例,以示说明。
1. 业务特征定制流程
例如:秒杀活动其实是交易的一种变种,他的特点是瞬间下单并发量很高,但受实际秒杀商品数量限制,最终仅少量订单可以创建成功。基于这种业务特点,可以在服务端通过过滤和队列技术,先拦截掉绝大多数流量,仅仅让可以成功的请求到达底层,从而轻松实现高并发目标。
2. 调整流程
在《抽奖活动的高可用、高并发优化》博客中“流程顺序调整”内容,讨论过一种场景,当中奖概率比较低时,甚至可以通过调整流程中某些环节的顺序,从而提升性能。
四 分流技术
以写请求为例,以下是一种常见的分流模型,通过下图我们可以看到,在经历前面众多环节的分流后,仅有少量请求会需要写DB,当然也就可以轻松支持大并发流量了。