Event Bus介绍
事件总线是什么:
vert.x 的事件总线是一种以异步方式发送和接收消息的方法。消息被发送到目的地并从目的地检索,目的地只是一个自由格式的字符串。
消息有一个正文,用于存储元数据的可选标头,以及一个过期时间戳,如果尚未处理,消息将被丢弃。
消息体通常使用 Vert.x JSON 表示形式进行编码。使用 JSON 的优点是它是一种序列化格式,可以轻松地通过网络传输,并且所有编程语言都可以理解它。
使用事件总线的优点:
- 事件总线允许 verticle 之间的解耦。一个 Verticle 不需要访问另一个 Verticle 类——所需要做的只是就目标名称和数据表示达成一致。
- 由于 Vert.x 是多语言的,因此事件总线允许用不同语言编写的 verticle 相互通信,而无需任何复杂的语言互操作层,无论是在同一 JVM 进程内还是通过网络进行通信。
Event Bus三种通信模型
- 点对点消息传递
- 请求-回复消息传递
- 发布/订阅消息
事件总线是另外一种mq吗?
Vert.x 事件总线不是 Apache ActiveMQ、RabbitMQ、ZeroMQ 或 Apache Kafka 的替代品。它是应用程序内部用于 verticle 到 verticle 通信的事件总线,而不是用于应用程序到应用程序通信的消息总线。
具体来说,事件总线不会执行以下操作:
- 支持消息确认
- 支持消息优先级
- 支持消息持久性以从崩溃中恢复
- 提供路由规则
- 提供转换规则(模式适应、分散/聚集等)
Event Bus API
如何获取eventbus的实例
得到一个 event bus:
EventBus eb = vertx.eventBus();
注册和注销handler
注册handler处理消息:
EventBus eb = vertx.eventBus();
eb.consumer("news.uk.sport", message -> {
System.out.println("I have received a message: " + message.body());
});
这是简单的方式,用一个consumer直接处理消息。
也可以返回一个consumer,然后再绑定一个handler:
EventBus eb = vertx.eventBus();
MessageConsumer<String> consumer = eb.consumer("news.uk.sport");
consumer.handler(message -> {
System.out.println("I have received a message: " + message.body());
});
注销handler:
consumer
.unregister()
.onComplete(res -> {
if (res.succeeded()) {
System.out.println("The handler un-registration has reached all nodes");
} else {
System.out.println("Un-registration failed!");
}
});
三种发送消息的模式
使用publish推送消息:
eventBus.publish("news.uk.sport", "Yay! Someone kicked a ball");
这个消息会被推送给所有注册了这个地址的handler。
使用send发送消息:
eventBus.send("news.uk.sport", "Yay! Someone kicked a ball");
使用send方法时,只会有一个handler接收到消息,这就是点对点模式。
使用request发送消息:
eventBus
.request("news.uk.sport", "Yay! Someone kicked a ball across a patch of grass")
.onComplete(ar -> {
if (ar.succeeded()) {
System.out.println("Received reply: " + ar.result().body());
}
});
使用request发送消息时,那么接收者在接收到消息的时候就可以给sender发送一个确认的消息。
MessageConsumer<String> consumer = eventBus.consumer("news.uk.sport");
consumer.handler(message -> {
System.out.println("I have received a message: " + message.body());
message.reply("how interesting!");
});
Event Bus使用示例
模拟温度传感器,并将温度传感器的数据实时推送到前端(示例中使用postman替代)。
Main.java
public class Main {
public static void main(String[] args) {
Vertx vertx = Vertx.vertx();
vertx.deployVerticle("chapter3.HeatSensor", new DeploymentOptions().setInstances(4));
vertx.deployVerticle("chapter3.Listener");
vertx.deployVerticle("chapter3.SensorData");
vertx.deployVerticle("chapter3.HttpServer");
}
}
模拟热传感器生成数据:
public class HeatSensor extends AbstractVerticle {
private final Random random = new Random();
private final String sensorId = UUID.randomUUID().toString();
private double temperature = 21.0;
@Override
public void start() {
scheduleNextUpdate();
}
private void scheduleNextUpdate() {
vertx.setTimer(random.nextInt(5000) + 1000, this::update);
}
private void update(long timerId) {
temperature = temperature + (delta() / 10);
JsonObject payload = new JsonObject()
.put("id", sensorId)
.put("temp", temperature);
vertx.eventBus().publish("sensor.updates", payload);
scheduleNextUpdate();
}
private double delta() {
if (random.nextInt() > 0) {
return random.nextGaussian();
} else {
return -random.nextGaussian();
}
}
}
更新和计算数据:
public class SensorData extends AbstractVerticle {
private final HashMap<String, Double> lastValues = new HashMap<>();
@Override
public void start() {
EventBus bus = vertx.eventBus();
bus.consumer("sensor.updates", this::update);
bus.consumer("sensor.average", this::average);
}
private void update(Message<JsonObject> message) {
JsonObject json = message.body();
lastValues.put(json.getString("id"), json.getDouble("temp"));
}
private void average(Message<JsonObject> message) {
double avg = lastValues.values().stream()
.collect(Collectors.averagingDouble(Double::doubleValue));
JsonObject json = new JsonObject().put("average", avg);
message.reply(json);
}
}
一个普通的监听器:
public class Listener extends AbstractVerticle {
private final Logger logger = LoggerFactory.getLogger(Listener.class);
private final DecimalFormat format = new DecimalFormat("#.##");
@Override
public void start() {
EventBus bus = vertx.eventBus();
bus.<JsonObject>consumer("sensor.updates", msg -> {
JsonObject body = msg.body();
String id = body.getString("id");
String temperature = format.format(body.getDouble("temp"));
logger.info("{} reports a temperature ~{}C", id, temperature);
});
}
}
http服务:
public class HttpServer extends AbstractVerticle {
private static final Logger log = LoggerFactory.getLogger(HttpServer.class);
@Override
public void start() {
vertx.createHttpServer()
.requestHandler(this::handler)
.listen(config().getInteger("port", 8080));
}
private void handler(HttpServerRequest request) {
if ("/".equals(request.path())) {
request.response().sendFile("index.html");
} else if ("/sse".equals(request.path())) {
sse(request);
} else {
request.response().setStatusCode(404);
}
}
private void sse(HttpServerRequest request) {
HttpServerResponse response = request.response();
response
.putHeader("Content-Type", "text/event-stream")
.putHeader("Cache-Control", "no-cache")
.setChunked(true);
MessageConsumer<JsonObject> consumer = vertx.eventBus().consumer("sensor.updates");
consumer.handler(msg -> handleData(msg, response)).completionHandler(res -> {
if (res.succeeded()) {
log.info("成功处理消息");
} else {
log.info("处理消息失败");
}
});
TimeoutStream ticks = vertx.periodicStream(1000);
ticks.handler(id -> {
vertx.eventBus().<JsonObject>request("sensor.average", "", reply -> {
if (reply.succeeded()) {
response.write("event: average\n");
response.write("data: " + reply.result().body().encode() + "\n\n");
}
});
});
response.endHandler(v -> {
consumer.unregister();
ticks.cancel();
});
}
private void handleData(Message<JsonObject> msg, HttpServerResponse response) {
response.write("event: update\n");
response.write("data: " + msg.body().encode() + "\n\n");
}
}
服务端日志数据:
使用postman直接访问:localhost:8080/sse就可以得到如下的数据: