关于Quasar 协程在实际生产中的使用

一.背景

公司python 项目需要转换成java,而该项目中存在大量IO操作,属于IO密集型系统,在高并发的场景下会有大量线程处于阻塞状态,CPU利用率低,性能低下,所以整个项目中凡是涉及到IO操作的地方都使用了协程,了解Python的都知道Python天然支持协程语法,而java目前不支持,官方说Java17会支持,而我司目前使用的JDK版本是Java8.所以经过调研究找到了Quasar。本身Qusar在我司的另外一个系统也有使用,经过高并发场景的验证,所以不用担心性能问题。本文主要描述一下什么是协程,以及搭建项目中遇到的一些问题.

二.什么是协程

协程是一种由进程自身调度任务的模式,本质上的区别就是协程是由进程调度,而线程是由内核调度,协程有专门的调度器,由调度器来调度当前线程执行哪一个协程,而没有被调度的的协程则被保存到内存中。当协程被挂起时,会记录原先方法执行的位置,等异步结果回调时再将原先的执行信息还原到栈中,由于是在用户态进行,不需要进行内核调用,避免了线程间的上下文切换,从一定意义上提升了性能,并且当协程阻塞时会主动让出线程来执行其他任务,以此来提到CPU的利用率,从某种意义上说,协程指的就是子程序通过协作来完后任务,而不是线程的抢占式完成任务.

三.Quasar

3.1 什么是Quasar

Quasar是一个框架(https://github.com/puniverse/quasar),提供了高性能轻量级的线程,提供了类似Go的channel,Erlang风格的actor,以及其它的异步编程的工具,可以用在Java和Kotlin编程语言中。

3.2 Quasar原理

利用Java instrument 技术对字节码进行修改,使方法被挂起前可以保存栈帧,而方法执行时可以恢复栈帧,方法内部执行的位置也通过增加一个flag的方式记录,到恢复执行的时候可以跳转到指定位置。

四.如何使用Quasar

4.1 使用java Agent

Quasar java agent可以在运行时动态修改字节码,虚拟机配置中配置以下参数,路径是你Quasar核心jar包的位置

-javaagent:path-to-quasar-jar.jar

4.2 AOT

AOT是在编译时的时候完成instrumentation,目前我们选的是这种,第一种方式使用起来不利于管理

4.3 引入依赖,在项目中引入以下依赖

<build>
        <pluginManagement>
            <plugins>
                <!--quasar-->
                <plugin>
                    <groupId>com.vlkan</groupId>
                    <artifactId>quasar-maven-plugin</artifactId>
                    <version>0.7.9</version>
                    <configuration>
                        <check>true</check>
                        <debug>true</debug>
                        <verbose>true</verbose>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>instrument</goal>
                            </goals>
                        </execution>
                    </executions>
                    <dependencies>
                        <dependency>
                            <groupId>co.paralleluniverse</groupId>
                            <artifactId>quasar-core</artifactId>
                            <version>0.7.10</version>
                        </dependency>
                    </dependencies>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <configuration>
                        <skipTests>true</skipTests>
                        <argLine>-Dco.paralleluniverse.fibers.verifyInstrumentation=true</argLine>
                        <!-- Quasar Agent -->
                        <argLine>-javaagent:${co.paralleluniverse:quasar-core:jar}</argLine>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

由于本身项目是模块话的,当时照着官方文档引入上述依赖一直没有生效,加上对maven的依赖管理没有深入了解导致此插件没有生效,切记子模块下一定要引入以下依赖才会生效,mave npluginManagement只是让你拥有这个插件,想要他生效还要具体的引入以下依赖才会生效

<build>
        <plugins>
            <plugin>
                <groupId>com.vlkan</groupId>
                <artifactId>quasar-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

五.代码中如何使用

5.1方式一

声明SuspendExecution异常,这个异常切记不可捕获,Quasar通过识别这个异常来对这个方法的字节码进行修改.

public void searchDB () throws SuspendExecution {
   }

5.1方式二 添加@Suspendable注解

@Suspendable
public void searchDB () {
}

5.2方式三

对于第三的库,你不可能更改它们的代码,如果想指定这些库的某些方法是suspendable方法,比如java.net.URL.openStream()Ljava/io/InputStream;, 就需要另外一种解决办法,也就是在META-INF/suspendablesMETA-INF/suspendable-supers定义

六.总结

本文主要介绍Quasar和协程是什么,以及如何使用,具体使用可以参照官方文档,只做记录,由于最近忙于改造,抽空写下这篇博客,略显粗糙。TODO:后续闲下来仔细写一下,去打球了。