源文档
[很有趣的一个库,一并翻译了。]
本文依照 知识共享许可协议(署名-非商业性使用-禁止演绎) 发布。
Vertx-sync 是一组工具集,其特点是在不阻塞内核线程的同时,允许用户以同步的方式接收事件、执行异步操作。
简介
比起很多历史遗留的程序库,Vert.x的一个关键优点是完全非阻塞(于内核线程而言)--这使它用少量的内核线程就可以处理大量的并发(例如,很多的连接、消息之类),具有良好的可扩展性。
Vert.x非阻塞性的结果是异步的API 。异步API 可以有多种风格,包括像回调、promise、Rx(Java的风格)。Vert.x在绝大多数地方使用回调(尽管它也支持Rx)。
某些情况下,使用异步API 编程比起直接使用同步API 要更具挑战,特别是当你有好几个操作要按顺序完成时。同时,使用异步API 时,错误的传递也会变得更复杂。
Vertx-sync 可以让你在熟悉的同步风格下继续使用异步API 。
在此,通往自由之路的功臣乃fiber
(这个词国内有译作纤程,类似协程-coroutine)。Fiber 是超轻量级的线程,并不是对应于底层的那种内核线程,它们被阻塞时不会导致内核线程也被阻塞。
Vert.x 借助Quasar 库来实现fiber 。
注意:Vertx-sync 当前只适用于Java 。
SyncVerticle
要使用Vertx-sync 库,你的代码需要继承io.vertx.ext.sync.SyncVerticle
类,并重载start()
方法和stop()
方法(stop 非必需)。
这些方法还必须加上@Suspendable
的标注。
写好的sync verticle,其部署方法和其他verticle完全一样。
Instrumentation
Vert.x用到了Quasar 库,这个库借助字节码增强(bytecode instrumentation)的技术实现了fiber 。(字节码增强的)工作是在运行时(run-time)由java agent 完成的。
要使这个特性正常工作,需要在启动JVM 时指定quasar-core jar包为java agent jar包:
-javaagent:/path/to/quasar/core/quasar-core.jar
如果你用的是vertx
命令行工具,可以在执行vertx
前设置环境变量ENABLE_VERTX_SYNC_AGENT
为ture
,这样可以启用agent 的配置。
你也可以使用quasar-maven-plugin 达成离线增强(a offline instrumentation, 指非运行时织入字节码)的效果。更多细节请参考 Quasar documentation 。
获得一次性的异步操作结果
Vert.x 的领域里,很多操作都会接受一个Handler
作为最后的参数。例如用Vert.x 的 Mongo 客户端执行一次查询或者发送一个event bus 消息然后拿到回应。
Vertx-sync 可以让你用同步的方式拿到这种一次性的异步操作的结果。
这是通过调用Sync.awaitResult 方法达成的。
运行这个方法时,需将想要执行的异步操作以Consumer的形式指定为其参数;handler 参数会在运行时传给此consumer 。
看下面的例子:
EventBus eb = vertx.eventBus();
// Send a message and get the reply synchronously
Message reply = awaitResult(h -> eb.send("someaddress", "ping", h));
System.out.println("Received reply " + reply.body());
上面的例子中,在回应返回前,fiber 会一直被阻塞住;而内核线程不会。
获得一次性的事件
Vertx-sync 也能以同步的方式获得一次性的事件,例如定时器的触发,或者end handler(关于end handler 的例子可以参见Vert.x 核心包文档中 HTTP 服务器与客户端 一节) 的执行。这是通过Sync.awaitEvent 方法达成的。
看下面的例子:
long tid = awaitEvent(h -> vertx.setTimer(1000, h));
System.out.println("Timer has now fired");
事件流
很多时候,Vert.x 的handler 接收到的是事件流,例如event bus 消息的消费者(consumer)、HTTP 服务器里的HTTP 服务端请求(server request)。
Vertx-sync 使你能以同步的方式从这种流中接收事件。
你需要一个同时实现了Handler 和Receiver接口的HandlerReceiverAdaptor 类实例。Sync.streamAdaptor 方法可以创建这样一个实例。
你可以把它当成一个普通的handler ,然后可以用实现自Receiver 接口的方法来同步地接收事件。
下面是一个 event bus 消息消费者的例子:
EventBus eb = vertx.eventBus();
HandlerReceiverAdaptor> adaptor = streamAdaptor();
eb.consumer("some-address").handler(adaptor);
// Receive 10 messages from the consumer:
for (int i = 0; i < 10; i++) {
Message received1 = adaptor.receive();
System.out.println("got message: " + received1.body());
}
使用FiberHandler
如果你想在一般的handler 中使用fiber --例如Http 服务器的请求handler ,那得首先把这个一般的handler 转换为fiber handler 。
Fiber handler 会在fiber 里运行那个一般的handler 。
看例子:
EventBus eb = vertx.eventBus();
vertx.createHttpServer().requestHandler(fiberHandler(req -> {
// Send a message to address and wait for a reply
Message reply = awaitResult(h -> eb.send("some-address", "blah", h));
System.out.println("Got reply: " + reply.body());
// Now end the response
req.response().end("blah");
})).listen(8080, "localhost");
更多示例
在examples repository 这里有一打示例,展示了vertx-sync 的用法(官方示例简单明了,不妨一览)。