坏时代Java编程(一):续 vertx-sync实践

之前介绍过quasar,如果您愿意以vert.x项目里下coroutine的讲话,建议采用vertx-sync。本篇将介绍vertx-sync。

本来打算另于一篇,写其他方面的东西,但是近来较繁忙,就先勾勒一篇实践方面的篇章。

vertx-sync是什么

直达一样篇我们都摆了 Fiber
相关的学问,想必大家对Java实现类似Golang的coroutine已经发记忆了,既然Java世界里发生第三着提供了这样好之库,
这就是说咱们就是看怎么跟 vert.x 结合起来用。

vert.x官方为了解决异步代码编写的紧,使的更同步化对开发人员更温馨,便冲quasar包装了一个底同库,vertx-sync,该库底撰稿人同样也是vert.x的原作者,所以就度还是那个高之。
vertx-sync
对外只是暴露了几乎独简单的静态API,来形成对vert.x体系内一律多重之操作包装,其实主要为尽管是三静态API而已。

  • Sync.fiberHandler
    如果你指望您的handler里生一些逻辑需要在Fiber里运行,则你的handler必须用这艺术包一下。
  • Sync.awaitEvent 从Handler里返回一个波结果(同步的),且
    非见面阻塞EventLoop
  • Sync.awaitResult 从Handler里返回一个异步事件结果(同步的),且
    未会见阻塞EventLoop

直接扣介绍可能有硌未直观,下面跑几只例。

使用vertx-sync

前介绍过quasar,如果你希望在项目里应用coroutine的言语,需要在JVM里安一个参数,用于采取启动前修改字节码(注入有抛锚方法),从而可以达成协程的目的。
具体方法也充分简单。

-javaagent:/path/to/the/quasar-core-0.7.5-jdk8.jar

要是是冲Maven跑单元测试,那不过需要引用quasar instrument的插件就是足以里

<plugin>
    <groupId>com.vlkan</groupId>
    <artifactId>quasar-maven-plugin</artifactId>
    <version>0.7.3</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.5</version>
        </dependency>
    </dependencies>
</plugin>

方是有的不胜必要之备选干活,否则你无法运用quasar以及vertx-sync。

vertx定时器例子

事先经过vert.x调用定时器,需要传一个回调handler,然后所有的代码逻辑都管在里。

vertx.setTimer(1000L, h -> {
    System.out.println("time's up");
});

今我们来重新塑造一下三观。

awaitEvent(h -> vertx.setTimer(1000L, h));
System.out.println("time's up");

这里定时器会卡住在awaitEvent这同样履行,直到一秒后才会实施下的一条龙。有点类似实践
Thread.sleep(1000L),但是并无会见卡住 EventLoop
因为quasar会在EventLoop基础之上再打开一个fiber。
脚我看个稍复杂点的事例。

HTTP Client请求例子

我们先行用传统的回调方式下vert.x的HttpClient API。

HttpClientRequest httpClientRequest = vertx.createHttpClient().get("leapcloud.cn");
httpClientRequest.handler(response -> {
    response.handler(responseBody -> {
        System.out.println(responseBody.toString());
    });
}).end();

此地有少重叠回调嵌套,一重叠是得Http的Response,另一样层是起Response里抱详细的body。因为来lambda表达式才让Java现在羁押起并无是那么恶心。但是如果我们得根据body的始末愈发做判断失继续呼吁其他页面,则嵌套会变的怪之杀。下面尝试改造成sync方式省。

HttpClientRequest httpClientRequest = vertx.createHttpClient().get("leapcloud.cn");
HttpClientResponse response = awaitEvent(Sync::fiberHandler);
Buffer body = awaitEvent(response::handler);
System.out.println(body.toString());

额头,是免是觉得看在特别舒畅,无嵌套,直接顺序下来,非常的直观,加上Java8特有的方式引用,会被代码更简短。

通过vertx-sync使用Vert.x JDBC

描绘了vert.x同学肯定懂得那个vertx-jdbc-client为了要其配合异步开发模型,将JDBC的底色线程池用异步方式包装了瞬间,也就是说JDBC层还是经过线程池去拜访数据库的,但是是透过vert.x的context做了层封装,使该得以用结果放到对应的
EventLoop
里,这样于吻合vert.x的开支风格。但是带来的弊病就是嵌套太老。

final JDBCClient client = JDBCClient.createShared(vertx, new JsonObject()
.put("url", "jdbc:hsqldb:mem:test?shutdown=true")
.put("driver_class", "org.hsqldb.jdbcDriver")
.put("max_pool_size", 30));

client.getConnection(conn -> {
    if (conn.failed()) {
        System.err.println(conn.cause().getMessage());
        return;
    }

    final SQLConnection connection = conn.result();
    connection.execute("create table test(id int primary key, name varchar(255))", res -> {
        if (res.failed()) {
            throw new RuntimeException(res.cause());
        }
        // insert some test data
        connection.execute("insert into test values(1, 'Hello')", insert -> {
            // query some data
            connection.query("select * from test", rs -> {
                for (JsonArray line : rs.result().getResults()) {
                    System.out.println(line.encode());
                }

                // and close the connection
                connection.close(done -> {
                    if (done.failed()) {
                        throw new RuntimeException(done.cause());
                    }
                });
            });
        });
    });
});

点代码可以是勿是略恶心啊?尝试改造一下咔嚓。

final JDBCClient client = JDBCClient.createShared(vertx, new JsonObject()
.put("url", "jdbc:hsqldb:mem:test?shutdown=true")
.put("driver_class", "org.hsqldb.jdbcDriver")
.put("max_pool_size", 30));

try (SQLConnection conn = awaitResult(jdbc::getConnection)) {
    awaitResult(h -> conn.execute("create table test(id int primary key, name varchar(255))", h));
    awaitResult(h -> conn.execute("insert into test values(1, 'Hello')", h));
    ResultSet query = awaitResult(h -> conn.query("select * from test", h));
    for (JsonArray line : query.result.getResults()) {
        System.out.println(line.encode());
    }
    AsyncResult done = awaitResult(h -> conn.close(h));
    if (done.failed()) {
        throw new RuntimeException(done.cause())
    }
} catch (Exception e) {
    e.printStackTrace();
}

除去一个try
catch,其他还没嵌套,整体逻辑的可读性非常高,完全是线性的。

争以逻辑放倒Fiber里

卿或许会发觉我们像一直还不曾用到 fiberHandler
这个静态方法,上面虽然写了概念,可能大家要未可知明白,这里做场景恐会更好理解。
俺们品尝实现一个操作十分耗时的逻辑然后保到fiber里,避免 EventLoop
被打断。这里而或会异常诧异,既然 Fiber
这么廉价开启10万8万之掉以轻心啊,恩,这里再次领一下quasar的要害片:
fiber可以好廉价的给创造出来,但是他本质上或者走在一个线程上面,如果内部一个fiber执行了老耗时的操作,则后面的fiber会一直守候,从而致使通线程阻塞。
也就是说一个fiber不克尽好耗时的操作,比如计算100万之内的素数的同,对于这种操作,我们好透过一直用逻辑放到vert.x的worker线程里独自去走,然后经fiber包装一下就是足以了。

AsyncResult<Long> result = awaitResult(fiberHandler(h -> vertx.executeBlocking((Handler<Future<Long>>) event -> {
    //求百万以内素数之和,这里的逻辑会在vert.x的worker线程里跑。随便耗时多久,都不会阻塞EventLoop
    long sum = sumOfPrime(1, 000, 000);
    event.complete(sum);
}, h)));
//打印结果
System.out.println(result.result());

此处您晤面小心到 awaitReslt 里之所以了 fiberHandler
,因为executeBlocking里的 handler
逻辑本身并没走在fiber体系下,所以会见招致无效,而fiberHandler的作用就是拿平段落vert.x的handler包到
fiber
里。使下续的await可以以那个结果回到,这里用awaitResult返回结果。
咱们重新深入一些探访 fiberHandler 方法里究竟干了什么。

@Suspendable
public static <T> Handler<T> fiberHandler(Handler<T> handler) {
  FiberScheduler scheduler = getContextScheduler();
  return p -> new Fiber<Void>(scheduler, () -> handler.handle(p)).start();
}

此获得Fiber的调度器,然后直接new了一个 Fiber
,避免了俺们团结对逻辑做Fiber包装。是无是老简单也。

总结

相互之间较传统的回调Handler,vertx-sync的包好淡雅,基本得以还原至共同方法调用级别,很好的减轻了异步回调带来的心智负担。
然而这个总不是JVM级别之落实,所以要多或者遗失还是发出硌门槛的,比如部署之时节,需要经过安装JVM参数来改部分字节码,同时还要小心有
消挂于底艺术方面加注或者强行让那个抛弃来而暂停异常。个人建议在有的无重大的工具级项目里应用,非常关键之门类不推荐用,当然矣使您道您的政工仅需要借助vert.x那么强烈而推荐而采取,只要记得打开
BlockingChecker 就吓,可以即时的发现地下的围堵逻辑。

责编:另外7.24哀号,我们力谱宿云携手Vert.x中国用户组在太库·上海的辅助下开了平等摆有关Vert.x的艺,之后还会时有发生一系列活动,有趣味之同桌等得以关心我们的微信公众号:MaxLeap_yidongyanfa,了解再多新闻。


笔者往期大手笔
差时代Java编程(一)
Java里的协程


笔者信息
正文系力谱宿云 LeapCloud旗下MaxLeap团队_UX成员:刘小溪 【原创】
首发地址:https://blog.maxleap.cn/archives/1013

刘小溪,Maxleap的高等开发工程师,喜欢倒腾一些有趣的技能框架,对新的技巧与语言非常有趣味,以前当shopex担任架构师,目前以Maxleap负责基础架构以及劳动框架这块技术,同时为会见针对Vert.x的社区供有起源上的支持。

相关文章