高并发应该是面试的时候很多面试官很喜欢问的问题,虽然很多公司的业务根本就没有高并发的场景。如果要聊一下高并发的问题,首先要明确我们最终的目的是保证系统能稳定运行、快速响应,然后我们需要采取什么方案来实现。

一个系统不管并发高不高,最终我们的目的是要保证系统能稳定运行、快速响应,在解决高并发问题的时候要找出系统的瓶颈在哪,然后再对症下药。

基础概念

首先要说一下一些基本概念,并发是系统在一段时间内可以处理多个任务,在短时间内cpu交替执行,以达到接近可以同时处理多任务的效果。与之区别的是并行,指系统可以同时处理多个任务。任务同时进行,不抢占cpu,不需要线程切换。

QPS每秒查询率(Query Per Second)是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准。QPS和并发数的关系可以简单用公式表示:

1
2
3
QPS(TPS)= 并发数/平均响应时间 
或者
并发数 = QPS*平均响应时间
  • 系统压力小的时候,并发数和 QPS 呈正相关,比如此时响应时间都小于1秒;
  • 当系统压力大的时候,比如响应时间远大于1秒,QPS 可能会趋向最大值或反而会下降。

内网测试一般接口100ms以内,简单的接口可以做到50ms以内。响应时间当然是越快越好,但是实际到公网,由于网络波动和物理距离,就会差了很多,波动很大,这里只讨论内网稳定的网络环境。

项目在做系统设计的时候都会定一个预期目标,比如接口三千QPS,响应时间在一秒以内,以支撑较高的并发,要达到这个目标,就需要系统的各层做好设计。以一个页面请求和页面包括的接口从前端到后端应用,再到数据库的调用流程作为例子,分析各个环节可以使用哪些技术来支撑高并发。

优化方式

在系统的不同部分,有不同的应对高并发的策略,可以用一个典型的前后端分离web应用为例子,一个页面通常包含页面本身的请求和后端的接口,从浏览器发起的请求到应用服务器,再到底层的数据库,来看看各部分使用的方法。

前端应用

前端总体来讲可以分为静态资源和来自后端接口的数据。

  • 与后端应用,做前后端分离,各自单独部署,互不影响;
  • CSS样式文件和JavaScript脚本,特别是图片音视频媒体可以做CDN缓存,减小源服务器的压力;
  • 如果用到Nginx,可以开启gzip压缩,提高加载速度;
  • localStorage缓存数据(业务范畴),减少对后端接口的请求。

网关

来自客户端(浏览器)的请求到服务器之后,为了实现应用的高可用,通常是使用分布式多节点部署,所以这时就需要一个网关来做负载均衡,比如F5或Nginx。

对于微服务系统中各服务的相互调用,如果是用Spring Cloud架构,则有Spring Gateway这样的组件做内部服务的网关。

后端应用

后端应用的优化方案更多更复杂,跟技术选型也有关。

  • 线程池/协程,提供线程利用率,减小创建线程的压力;
  • 锁机制,尽量减小锁粒度,以减小资源竞争;
  • 使用缓存,如Redis、Ecache,MemCache等中间件;
  • 削峰和限流,消息队列MQ;
  • 数据库缓存、优化索引、分表分库等;
  • 批量操作,减少嵌套循环和冗余操作。

有些优化在前后端都是相同的思想,比如缓存,前端的CDN,后端的本地/分布式缓存,数据库缓存,构成了多级缓存。

带来的问题

为了提高并发量我们使用了分布式系统和各种组件,难免也会带来各种问题。

一个是可用性问题,用了中间件就得保证更多组件的可用性,比如:

  • 数据库、Redis和MQ需要多节点保证可用,数据库主从切换、Redis缓存策略和MQ消息消费保证都是要关注的点;
  • 微服务节点众多,节点压力大或宕机需要做限流或熔断,以免影响链路调用;
  • 微服务要做服务发现治理,方便服务调用,监控节点健康

另一个微服务中的分布式锁事务也是一个难点,二段提交、TCC、MQ+本地消息表或最大努力通知,在不同业务中有选择不同的处理方法。

归根到底大多是CAP的问题,做分布式系统都绕不过的问题。

可以看出,这篇文章也是对系统架构设计的思考,由于本人水平经验有限,也只能说是一个粗浅的理解。