引领先锋|携程基于Quasar协程的NIO实践( 二 )


引领先锋|携程基于Quasar协程的NIO实践协程中调用的方法是可以挂起的 。 不同于线程的阻塞会使线程休眠 , 协程在等待异步任务的结果时 , 会通知调度器将自己放入挂起队列 , 释放占用的线程以处理其他的协程 。 异步任务完毕后 , 通过回调将异步结果告知协程 , 并通知调度器将协程重新加入就绪队列执行 。
1.3 Quasar任务调度原理
Quasar()是一个开源的Java协程框架 , 通过利用Java instrument技术对字节码进行修改 , 使方法挂起前后可以保存和恢复JVM栈帧 , 方法内部已执行到的字节码位置也通过增加状态机的方式记录 , 在下次恢复执行可直接跳转至最新位置 。 以如下方法为例 , 该方法分为两步 , 第一步为initial初始化 , 第二部为通过NIO获取网络响应 。
public String instrumentDemo(){initial();String ans = getFromNIO();return ans;}
引领先锋|携程基于Quasar协程的NIO实践Quasar会在initial前增加一个flag字段 , 表明当前方法执行的位置 。 第一次执行方法时 , 检查到flag为0 , 修改flag为1并继续往下执行initial方法 。 执行getFromNIO方法前插入字节码指令将栈帧中的数据全部保存在一个Quasar自定义的栈结构中 , 在执行getFromNIO后 , 挂起协程 , 让出线程资源 。 直至NIO异步完成后 , 协程调度器将第二次执行该方法 , 检测到flag为1 , 将会调用jump指令跳转到returnans语句前 , 并将保存的栈结构还原到当前栈中 , 最后调用人return ans语句 , 方法执行完毕 。
二、系统异步IO改造在项目中添加Quasar依赖后 , 可以使用Fiber类新建协程 。 建立的方法与线程类似 。
new Fiber(()->{//方法体}).start();2.1 整合Netty与Quasar
系统使用的Http框架是基于Netty的async-http-client() , 该框架提供了异步回调和CompletableFuture两种对响应的异步处理方式 。
CompletableFuture自JDK8推出 , 与之前的Future类最大的不同在于 , 提供了异步任务跨线程的通知和控制机制 。 即 , 任务的等待者可以在CompletableFuture注册任务完成或异常时的回调 , 而执行者也可以通过它通知等待者 。 Quaasr框架对它也做了支持 , 提供了API用于在协程中等待CompletableFuture的结果 。 调用后 , 协程将挂起 , 直至future状态为已完成 。
AsyncCompletionStage.get(future)通过CompletableFuture作为通知中介 , 我们可以将AsyncHttpClient与Quasar做整合 , 挂起协程等待IO结果 。
//创建HttpClientAsyncHttpClient httpClient = Dsl.asyncHttpClient();//创建请求Request request = createRequest();//将网络请求交给HttpClient执行CompletableFuture future = httpClient.executeRequest(request).toCompletableFuture();//通过Quasar挂起协程Response response = AsyncCompletionStage.get(future);//获取网络结果后 , 通过future传递response并唤醒协程重新执行deal(response);过程可由下图表示 。
引领先锋|携程基于Quasar协程的NIO实践Quasar框架AsyncCompletionStage.get内部完成的工作相当于 , 在HttpClient返回的future上注册回调 , 回调的内容是“IO操作完成后通知调度器唤醒协程” , 这样将NIO异步回调全部操作封装在协程调度器中 , 用户代码看起来是同步等待的形式 , 避免了自行实现回调处理带来的繁琐 , 解决了前文所述的回调地狱 。


推荐阅读