目录
1.概述
2.如何处理高并发
1.概述
先看两个业务场景:
数据采集模块是指采集终端采集上来后负责归集的模块。这个模块是必须的吗?不一定,要是采集终端能协调好的话可以直接往存储中走(mq或者各类分布式文件系统或者各类数据库),数据处理模块直接去处理就是了,这样能省掉采集模块。我们这里是假设需要这样一个归集的采集模块的。
这个模块是什么?这个模块是个能接收、处理http请求的模块,比较多的是会用JAVA来开发,也就是个web应用。
采集模块无非就是要面对很多采集终端的连接,然后对采集的数据进行归集。所以它要关注的核心其实就是如何抗住高并发。
2.如何处理高并发
要抓药,首先要知道得了什么病?什么症状
先来定位病的症状是什么,即高并发会带来什么问题:
- 连接量很大
采集模块需要接收采集终端发送来的数据,采集终端的量可能很大,所以要在采集模块处处理大量tcp连接的问题。这些tcp连接指的是连接到web server上的tcp连接。web server主要指的springboot默认的web server——tomcat。
大量连接带来的是大量请求,而众所周知tomcat处理请求是一条线程去处理一个请求,用来处理请求的线程数是有上限的,不可能无限扩展。所以高效的调度线程去处理请求是抗住高并发带来的大量请求的核心。
以tomcat为切入点分析如何进行线程的调度来抗住大量连接,也就是如何拉高tomcat的吞吐量,拉高它的qps。拉高web server的qps无非有以下几个方面:
- 采用非阻塞IO
- 加快任务处理过程
采用非阻塞式IO:
采用非阻塞IO,也就是采用NIO作为IO模型。NIO因为是选择的已经准备好的请求来执行,所以会减少因为等数据包等造成的不必要线程阻塞,所以吞吐量要明显高于BIN。阻塞式IO,BIO;非阻(塞式IO,NIO,博主前面有文章想聊过:
JAVA BIO_java的bio有哪些-CSDN博客
全网最清晰JAVA NIO,看一遍就会-CSDN博客)
加快任务处理过程:
数据采集模块可能会对数据做一些处理,然后将结果写入下游MQ中。也就是分为两步:
- 数据处理
- 发给下游MQ
数据处理是一个计算密集型任务,也就是个纯CPU任务,将结果放到下游MQ中,这是一个纯IO任务,是个IO密集型任务。当连接数上来了,意味着任务数也上来了,将计算密集型任务和IO密集型任务拆开是很有必要的。
先说怎么拆,再说为什么要拆。
怎么拆?拆,就是要将一个任务拆成多个子任务,进行多任务的编排,任务编排该用什么?用JDK的任务编排工具类Completablefuture。
(博主之前有详细聊过Completablefuture,有需要,可移步:
【JAVA多线程】Future,专为异步编程而生_future异步编程-CSDN博客
【JAVA多线程】CompletableFuture原理剖析_completablefuture底层原理-CSDN博客)
接下来我们举一个伪代码的列子:
整个任务分为解码、数据处理、发送给下游三步。
这样拆开后和在单线程里面执行是一样的,任务都是被串行执行,这样分阶段主要是可以动态调整每一个阶段的线程数、如果扛不住了也可以分阶段的来阻塞或者拒绝任务,实现分阶段的背压。
背压:
上游采集数据速度大于下游数据采集模块又或者采集模块之间各自的速率不匹配时都可能造成消息积压,容易造成两个后果:
- 1.任务队列由于是不限容量的,会一直new线程,直接导致jvm oom
- 2.数据失去实时性,由于老数据的积压,且不停增多,系统中数据会逐渐失去实时性。
所以最好的办法就是当消费能力满时,直接拒绝掉再来的线程,也就是给线程池装上阻塞队列,将队列的拒绝策略设置为直接丢弃。用上面的Completablefuture举例就是给每个任务一个自定义线程池,线程池的拒绝策略定义为直接丢弃即可。