ForkJoin & Quasar

用ForkJoin&Quasar对IO密集型服务进行优化

背景介绍

做业务系统开发面对的服务大都是IO密集型服务,这里指的IO可大致分为如下几种:

  • 数据库 IO
  • 缓存 IO
  • 网络 IO

这里暂时不谈 缓存IO,因为缓存IO都发生在内存上,速度很快,可以忽略这部分IO

也有人可能会疑惑为什么会存在网路IO?

现在稍微大一点的公司都在做微服务,根据业务领域进行服务划分,将服务与服务尽可能的进行解耦,方便管理,方便维护,将开发效率最大化。

但也因此,很多单服务不存在的问题也会随即而来,比如增加网络IO:所有特定业务领域内的服务将可提供服务注册到治理中心,由治理中心进行服务调度分发,服务与服务之间不再简单的通过内存接口进行交流,而是将请求信息通过网络打到所依赖的服务上,网络延迟可是不可避免的。如果网络较好的情况下大概几毫秒到几十毫秒,如果网络不好就可能出现几百毫秒甚至秒级。

微服务调用

这里我们给出个概念图,当请求A服务时,A服务要调用BCD三个服务,并且要查询一次数据库 E, 并且将四个结果组装成一个实体返回出去。 如果A内部代码是顺序处理的,那么这次请求的整体响应时间则为 B的响应时间+C的响应时间+D的响应时间+E的响应时间+A内部逻辑处理时间。 (B的响应时间包括A请求B的网络延迟时间)。也就是说A的响应时间不单单与自己的代码逻辑有关,还要与其他服务的代码逻辑有关,还要与网络延迟有关, 如果其中有一个环节延迟增加都会增加整个请求的响应时间。

一个请求大概是这样的(OtherService包括DB读写):

顺序时序图1

优化

服务层面的优化的方向大致有下面这几种,优化的地方都是在 MyService -> OtherService 这一步上:

  • 合并多次请求
  • 请求异步代理
  • 请求并发处理

合并多次请求

将多次请求合并成一次,将单量调用改成批量调用。 这种是变动最小,风险最小的办法,却也是比较难推动的办法。

如果我们调用的服务仅仅提供了单量调用接口,就需要被调用方进行批量接口提供。需要各服务之间协作支持(也许我们提出的是一个比较特别的需求,也许提供接口的性价比不高,很容易遭受到依赖方的拒绝~),这种方法无关技术,属于交流解决问题。

请求异步代理

这种方法需要在 MyServiceOtherService 之间再介入一层 Agent ,大致结构如下:

请求异步代理1

A要调用OtherService的时候,会让Agent异步去请求OtherService,当AgentOtherService请求有结果的时候将结果异步返回给A,这样整体的响应时间也就变成 内部逻辑时间 + max(OtherService Response Times)

但这种架构的修改带来系统稳定性的降低,服务A和服务B、C、D之间的通讯增加了复杂性。同时,因为是异步方式,服务A的业务也要实现异步方式,否则还是一个阻塞的架构。 而且对Agent的实现也有着比较高的并发要求。

请求并发处理

请求并发处理应该是相对来说比较简单且实用的实现办法, 而且有现成的框架支持并发处理,比如Executor, ForkJoin, Quasar …

Executor

提供现有的线程池,代码简单

ExecutorService executorPool = Executors.newFixedThreadPool(10);
Future<Object> future = executorService.submit(new Callable<Object>() {
    public Object call() throws Exception {
        OtherService otherService = new OtherService();
        return otherService.dispose();
    }
})
// future.get() 拿到返回值 OR 异常
ForkJoin

与 Executor 类似, ForkJoin 提供的线程池(感觉不应该叫做线程池了)最大为 cpu核心数,虽然Excutor线程池大小可以自定义,但其实真正能并发运行的不过CPU的核心数那么多,cpu核心数 的线程池感觉足够了。

ForkJoin 还增加了work-stealing(工作窃取算法),为了提高对任务的处理效率。

工作窃取1,图片来源网络,侵删

两个线程队列同时处理任务,如果线程队列1的任务已经处理完毕且没有新任务进来时,如果其他线程队列有任务没处理完,线程1回去其他队列里”偷取”任务用来加快处理。

这里一定是从队列尾偷任务

其实代码写起来也是蛮简单的, 与Executor类似

ForkJoinPool forkJoinPool = new ForkJoinPool();
Future<Object> future = forkJoinPool.submit(new Callable<Object>() {
    public Object call() throws Exception {
        OtherService otherService = new OtherService();
        return otherService.dispose();
    }
})

算法和线程池的配置都是封装好的,没什么可以说的。

Quasar

这个是Java的一个实现协程的框架,大致的原理如下:

  • Quasar 中存在默认的work线程
  • work线程轮询执行协程任务
  • 当该任务遇到阻塞的时候(比如IO任务),协程自动挂起
  • 当阻塞完成的时候,恢复协程,继续执行

是不是很像“请求异步代理”, work线程就像Agent服务,协程就像抛给Agent的任务,不同是Agent服务(前提是Agent服务是用异步线程实现的,如果用协程实现,没啥区别)将会用异步线程去等待IO并且返回结果,而Quasar的work线程是轮询这些协程任务,有IO完成的便让这个任务返回。

协程不是线程,更不是进程,所以协程并非异步,但却可以充分利用CPU,不要将CPU的宝贵资源浪费在等待IO阻塞上,因为不是线程,所以不用担心线程的维护。更不用担心大量线程阻塞导致系统可用资源下降。

使用代码随下章节给出。

性能比较

我们模拟一下遇到的服务场景,分别用 Executor, ForkJoin, Quasar 进行顺序请求代码的修改,看下效率如何

代码结构如下:

  • Main 充当请求方,可以并发对调用MyService中的方法
  • MyService 充当我们自己的服务,响应Main进来的请求
  • OtherService 充当别人的服务,MyService依赖OtherService中的方法
    • OtherService 以Sleep来模拟响应延迟

Main.java


public class Main {
   
   
    private 
### Quasar 框架概述 Quasar 是一套完整的解决方案,整合了 Angular、React 和 Vue.js 三个主流框架的优点[^2]。作为一个企业级的跨平台 Vue.js 框架,Quasar 致力于帮助开发者快速构建高性能的应用程序,涵盖了 Web 应用、移动应用、桌面应用以及浏览器扩展等多种应用场景[^4]。 #### 核心优势 - **高效开发**:通过提供超过 70 个高性能、可自定义的 Material Design 组件,Quasar 让开发者能够专注于业务逻辑而非底层技术细节,显著提高了开发效率和应用质量[^3]。 - **多平台支持**:不仅限于单一平台,Quasar 支持多种集成方式,使得同一个代码库可以在不同平台上运行,极大地方便了跨平台应用的开发。 - **详尽文档与社区支持**:Quasar 配备有详细的官方文档,并拥有一个活跃的开发者社区,这为学习和解决问题提供了极大的便利。 ### 使用教程 为了开始使用 Quasar 进行项目开发,首先需要安装 Quasar CLI 工具: ```bash npm install -g @quasar/cli ``` 接着可以通过以下命令创建一个新的 Quasar 项目并启动开发服务器: ```bash quasar create my-project cd my-project quasar dev ``` 新创建的 Quasar 项目会有一个清晰的目录结构,便于管理和维护[^5]。具体来说,`src` 文件夹包含了所有的源码文件,而 `quasar.conf.js` 则是用于配置项目的全局设置文件[^1]。 ### 示例代码片段 下面是一个简单的 Quasar 组件示例,展示了如何利用内置组件来构建用户界面: ```html &lt;template&gt; &lt;q-page class=&quot;flex flex-center&quot;&gt; &lt;div&gt;Hello, Quasar!&lt;/div&gt; &lt;/q-page&gt; &lt;/template&gt; &lt;script&gt; export default { name: &#39;HomePage&#39; } &lt;/script&gt; ``` 此段代码定义了一个名为 HomePage 的页面组件,在其中使用了 `&lt;q-page&gt;` 来包裹主要内容区域,并显示了一条欢迎消息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值