ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【架构师从入门到进阶】第六章:服务集群优化——第七节:服务内并发

【架构师从入门到进阶】第六章:服务集群优化——第七节:服务内并发

【架构师从入门到进阶】第六章:服务集群优化——第七节:服务内并发

  • 多进程
  • 多线程
    • 使用多线程提高程序的运行效率
    • 使用多线程实现异步
  • 多协程

本篇文章我们来学习服务内的并发。

请求到了某个节点,在节点的内部,我们也可以通过一些优化的手段让它提高性能,比如说在节点内部进行并发,就是我们经常说的一种手段。

这里所说的并发是指广义上的并发,包括并行,就是并行和并发,我们都称为广义上的并发。

那么这里所说的节点指的是服务器的节点。多个请求来了之后,通过某一个组件把它路由分配到不同的服务节点,那么这个节点呢,我们把它称作是一个服务器,上面可以部署我们的应用程序。那么本篇文章,我们就来讲节点上的优化。

我们分为三个角度:多进程,多线程,多协程。

多进程

多进程的好处是在每个进程之间资源独立,进程之间存在很强的隔离性。当一个进程因为资源耗尽的原因崩溃时,不会影响到其他的进程。

比如说在我这台电脑上,我开了一个Chrome,然后开了一个记事本,开了一个企业微信,比如说企业微信崩溃了,它并不会影响到我的Chrome。

那么,我们如何用多进程的方式运行程序呢?我们平时大家可能都做过,可能不太注意。举个例子。

比如说一台linux服务器上,一个spring boot的web项目,它的端口是8080。那么,如果我们把这个端口换成8081,我们再用“java -jar”去启动这个程序,那么它是不是就变成了两个JAVA进程?其实换个端口去运行spring boot项目,这个就是一个多进程的运行。

这种方式就是利用进程之间的这个隔离性,一个服务坏了并不会影响另外一个服务。比如在物理机上,我们通过不同的端口运行了两个spring boot项目,一个oom了,另外一个服务不影响。

有一些小的公司,或者说创业公司,它在服务器比较赘据的情况下一般都会这么做,就是一个服务器节点上可能会部署多个应用,或者说多个应用会交叉部署。

举一个例子。比如说这是一台物理机,在一台物理机上分别部署了ABC三个web程序,另外一台物理机上也布了ABC。哪怕这个服务器上的A坏了,那么另一个服务器上A还有,如果说这个服务器上ABC都坏了,那么另一个服务器上还有。

如果说资源的利用可以互补的服务呢,那么这种方式更好,什么意思呢?比如说A服务着重消耗的是CPU资源,B着重消耗的是内存,C可能着重消耗的是网络,那么这样就会更好。因为一台机器上,比如说有一些创业公司,它的机器都是买回来一个特定的机器,那么这个机器CPU多少,内存多少,网络多少,都是固定的。如果说一个进程他用CPU,你在这台机器上布多一个用CPU的服务,是不是CPU的资源就会多用一些,甚至把它挤爆?那么我们把这些A用CPU、B用内存、C用网络,把它们综合在一起,部署在一台机器上,这样的话就能在充分保证性能的情况下,节省机器的硬件资源。

多线程

下面来看多线程。

使用多线程提高程序的运行效率

多线程能提高程序的运行效率。比如说我们有一个程序,功能里面分成两个步骤:第一步是计算;第二步输出,就是说把计算后的结果输出到文件上,写到一个磁盘的文件。

有一个线程叫做a,a执行完计算得等,计算之后执行输出。输出设备的效率一般都比较低。CPU的效率一般都比较快,那么后来的线程如果说都等待a,比如后面又来了bcd,如果说后来的这些线程都等待a做完之后它才紧接着去做的话。

那么是不是就对我们的CPU是一个浪费?因为,比如说a在输出的时候,b得等到a输出完才开始做计算,那没必要啊,就是说a在等输出的时候b其实可以利用CPU去运算了,然后b做完必须等输出,然后c也可以用CPU去计算,都不用等。

在打车项目的场景当中,它有这么一种情况,就是按照时长和公里计费。这是什么意思呢?也就是说我打车,比如说打了十分钟的车,走了七公里,每一分钟两毛钱,一公里两块二,你算价钱的时候就是“10 * 0.2 + 7 * 2.2”。

那么,在计算这两部分价格的时候,没必要等待把时长计算完了再去计算里程,可以计算时长的同时去计算里程,最后把它综合在一起就ok了。

使用多线程实现异步

多线程的第一个目就是提升效率,第二个目是实现异步。这个实现异步是什么意思呢?比如说有一个请求中需要处理一个耗时特别长的任务,在不使用多线程的情况下,后端只能在任务结束后再响应给前端。那么这会导致什么呢?会导致我们的响应时间增加,这个指标如果很长的话,第一对用户的体体验不好,第二对服务器也会造成一定的压力。为什么呢?因为一个请求打到了服务器上,服务器一直没有处理,那么它和请求之间保持连接,也会消耗服务器一定的资源。

那么使用多线程就可以来实现这种长任务的优化。比如说可以调起另外一个线程来处理耗时较长的任务,让主线程快速响应,马上将请求关闭。

比如说这是一个客户端client,client请求到server之后,需要执行一个很长时间的任务,再给客户端进行返回,比如说用了十秒。然后呢,我把它进行一个优化,主线程这边执行很短的一个任务,给client一个响应,那么原来执行长时间任务呢,就开另外一个线程去执行。长任务执行完之后,再给客户端一个推送。

以网约车为例,乘客发起一个派单的请求,给了服务端,服务端马上给乘客一个响应说:“派单服务器正在派单中”,然后用户该干嘛干嘛去。因为server派单是一个计算量很大的过程,他要找附近的司机,还得看司机是不是符合条件,还得看是否是顺路单等等,还会有各种麻烦的条件,计算量比较大,那么这个耗时较长的任务呢,就给他开另外一个线程去处理,处理完之后,比如派到一个车了,再给他推送到客户端说:“您的订单被某个司机接单了。

对用户来说,主线程的耗时其实才是用户关心的,客户只需要知道服务器收没收到我的派单请求,不是说马上就让你派个单,可能客户也能容忍服务端在计算在等。所以说这个时候我们的主线程执行耗时就是成了主要指标,它就被降下来了,因为它只需要执行很短时间任务,把指令相应的参数给到服务端,就可以撤回去告诉用户。而原理的方案需要等待服务器把这个任务执行完才给用户一个响应。这样一比较的话,这边的响应时间极短,就这边的响应时间极快,客户体验起来也是非常好的。

而剩下的这个线程我们把它叫做副线程,这个副线程执行的用时就不那么重要了,因为客户可以容忍。这样做还有好处,就是说主线程完成核心业务,主任务之外的一些无关紧要的,对时效性要求不高的业务呢,可以用副线程去做,让主线程专心专注于核心操作。副线程的任务有哪些呢?

比如说我们一些中间件的日志都是通过副线程去完成的;还有一些与第三方的交互;还有一些信息的上报与统计。

多协程

多协程在JAVA中,目前来说用的还是比较少的,因为它在jdk19中,协程也就是虚拟的线程。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进