Publish and Receive Messages with Nats Java Client
1.概述
在本教程中,我们将使用Java Client for NAT连接到NATS Server并发布和接收消息。
NATS提供了三种主要的消息交换模式。 发布/订阅语义将消息传递给主题的所有订阅者。 请求/回复消息传递通过主题发送请求,并将响应路由回请求者。
订阅者还可以在订阅主题时加入消息队列组。 发送到关联主题的消息仅传递给队列组中的一个订户。
2.设定
2.1。 Maven依赖
首先,我们需要将NATS库添加到我们的pom.xml中:
1 2 3 4 5 | <dependency> <groupId>io.nats</groupId> <artifactId>jnats</artifactId> <version>1.0</version> </dependency> |
该库的最新版本可以在这里找到,Github项目在这里。
2.2。 NATS服务器
<
其次,我们需要一个NATS服务器来交换消息。 这里有所有主要平台的说明。
我们假设有一个在localhost:4222上运行的服务器。
3.连接和交换消息
3.1。 连接到NATS
静态NATS类中的connect()方法创建Connections。
如果要使用具有默认选项的连接并在端口4222上的localhost侦听,则可以使用默认方法:
1 |
但是Connections有许多可配置的选项,我们要覆盖其中的一些。
我们将创建一个Options对象,并将其传递给Nats:
1 2 3 4 5 6 7 8 9 | private Connection initConnection() { Options options = new Options.Builder() .errorCb(ex -> log.error("Connection Exception:", ex)) .disconnectedCb(event -> log.error("Channel disconnected: {}", event.getConnection())) .reconnectedCb(event -> log.error("Reconnected to server: {}", event.getConnection())) .build(); return Nats.connect(uri, options); } |
NATS连接是耐用的。 API将尝试重新连接丢失的连接。
我们已经安装了回调函数,以通知我们何时发生断开连接以及何时恢复连接。 在此示例中,我们使用的是lambda,但是对于需要做更多事情而不是简单地记录事件的应用程序,我们可以安装实现所需接口的对象。
我们可以进行快速测试。 创建一个连接并添加60秒的睡眠以保持进程运行:
1 2 |
运行这个。 然后停止并启动您的NATS服务器:
1 2 3 4 5 6 | [jnats-callbacks] ERROR com.baeldung.nats.NatsClient - Channel disconnected: <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="c7aea8e9a9a6b3b4e9a4abaea2a9b3e984a8a9a9a2a4b3aea8a98eaab7ab87f0fef3f5ffa3a4f6">[emailprotected]</a> [reconnect] WARN io.nats.client.ConnectionImpl - couldn't connect to nats://localhost:4222 (nats: connection read error) [jnats-callbacks] ERROR com.baeldung.nats.NatsClient - Reconnected to server: <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="026b6d2c6c6376712c616e6b676c762c416d6c6c6761766b6d6c4b6f726e42353b36303a666133">[emailprotected]</a> |
我们可以看到回调记录了断开连接并重新连接。
3.2。 订阅消息
现在我们有了连接,我们可以进行消息处理了。
NATS消息是字节数组[]的容器。 除了预期的setData(byte [])和byte [] getData()方法外,还有设置和获取消息目标并回复主题的方法。
我们订阅主题,即字符串。
NATS支持同步和异步订阅。
让我们看一下异步订阅:
1 2 | AsyncSubscription subscription = natsConnection .subscribe( topic, msg -> log.info("Received message on {}", msg.getSubject())); |
API在其线程中将消息传递到我们的MessageHandler()。
某些应用程序可能希望控制处理消息的线程:
1 2 | SyncSubscription subscription = natsConnection.subscribeSync("foo.bar"); Message message = subscription.nextMessage(1000); |
SyncSubscription具有阻塞的nextMessage()方法,该方法将阻塞指定的毫秒数。 我们将对测试使用同步订阅,以使测试用例保持简单。
AsyncSubscription和SyncSubscription都有一个unsubscribe()方法,我们可以使用该方法来关闭订阅。
1 | subscription.unsubscribe(); |
3.3。 发布消息
发布消息可以通过多种方式完成。
最简单的方法只需要一个主题字符串和消息字节:
1 | natsConnection.publish("foo.bar","Hi there!".getBytes()); |
如果发布者希望回复或提供有关消息来源的特定信息,它还可以发送带有回复主题的消息:
1 | natsConnection.publish("foo.bar","bar.foo","Hi there!".getBytes()); |
其他一些组合也有重载,例如传递消息而不是字节。
3.4。 简单的消息交换
给定一个有效的连接,我们可以编写一个测试来验证消息交换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | SyncSubscription fooSubscription = natsConnection.subscribe("foo.bar"); SyncSubscription barSubscription = natsConnection.subscribe("bar.foo"); natsConnection.publish("foo.bar","bar.foo","hello there".getBytes()); Message message = fooSubscription.nextMessage(); assertNotNull("No message!", message); assertEquals("hello there", new String(message.getData())); natsConnection .publish(message.getReplyTo(), message.getSubject(),"hello back".getBytes()); message = barSubscription.nextMessage(); assertNotNull("No message!", message); assertEquals("hello back", new String(message.getData())); |
我们首先订阅带有同步订阅的两个主题,因为它们在JUnit测试中工作得更好。 然后,我们向其中一个发送一条消息,将另一个指定为replyTo地址。
阅读来自第一个目的地的消息后,我们"翻转"主题以发送响应。
3.5。 通配符订阅
NATS服务器支持主题通配符。
通配符对以"。"分隔的主题令牌进行操作。 字符。 星号字符" *"与单个标记匹配。 大于符号">"是主题其余部分的通配符匹配,可能超过一个标记。
例如:
foo。*匹配foo.bar,foo.requests,但不匹配foo.bar.requests
foo。>匹配foo.bar,foo.requests,foo.bar.requests,foo.bar.baeldung等。
让我们尝试一些测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | SyncSubscription fooSubscription = client.subscribeSync("foo.*"); client.publishMessage("foo.bar","bar.foo","hello there"); Message message = fooSubscription.nextMessage(200); assertNotNull("No message!", message); assertEquals("hello there", new String(message.getData())); client.publishMessage("foo.bar.plop","bar.foo","hello there"); message = fooSubscription.nextMessage(200); assertNull("Got message!", message); SyncSubscription barSubscription = client.subscribeSync("foo.>"); client.publishMessage("foo.bar.plop","bar.foo","hello there"); message = barSubscription.nextMessage(200); assertNotNull("No message!", message); assertEquals("hello there", new String(message.getData())); |
4.请求/回复消息
我们的消息交换测试类似于pub / sub消息传递系统上的常见习语; 请求/回复。 NATS对此请求/答复消息传递有明确的支持。
发布者可以使用我们上面使用的异步订阅方法为请求安装处理程序:
1 2 3 4 5 6 7 | AsyncSubscription subscription = natsConnection .subscribe("foo.bar.requests", new MessageHandler() { @Override public void onMessage(Message msg) { natsConnection.publish(message.getReplyTo(), reply.getBytes()); } }); |
或者,他们可以在到达时响应请求。
API提供了request()方法:
1 | Message reply = natsConnection.request("foo.bar.requests", request.getBytes(), 100); |
此方法为响应创建一个临时邮箱,并为我们编写回复地址。
Request()返回响应,如果请求超时,则返回null。 最后一个参数是要等待的毫秒数。
我们可以修改测试以进行请求/回复:
1 2 3 4 5 6 | natsConnection.subscribe(salary.requests", message -> { natsConnection.publish(message.getReplyTo(),"denied!".getBytes()); }); Message reply = natsConnection.request("salary.requests","I need a raise.", 100); assertNotNull("No message!", reply); assertEquals("denied!", new String(reply.getData())); |
5.消息队列
订阅者可以在订阅时指定队列组。 当消息发布到该组时,NATS会将其传递给一个唯一的订户。
队列组不保留消息。 如果没有可用的侦听器,则该消息将被丢弃。
5.1。 订阅队列
订阅服务器将队列组名称指定为字符串:
1 | SyncSubscription subscription = natsConnection.subscribe("topic","queue name"); |
当然,还有一个异步版本:
1 2 3 4 5 6 7 | SyncSubscription subscription = natsConnection .subscribe("topic","queue name", new MessageHandler() { @Override public void onMessage(Message msg) { log.info("Received message on {}", msg.getSubject()); } }); |
订阅将在NATS服务器上创建队列。
5.2。 发布到队列
将消息发布到队列组仅需要发布到关联的主题:
1 | natsConnection.publish("foo", "queue message".getBytes()); |
NATS服务器会将消息路由到队列并选择消息接收者。
我们可以通过测试验证这一点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | SyncSubscription queue1 = natsConnection.subscribe("foo","queue name"); SyncSubscription queue2 = natsConnection.subscribe("foo","queue name"); natsConnection.publish("foo","foobar".getBytes()); List<Message> messages = new ArrayList<>(); Message message = queue1.nextMessage(200); if (message != null) messages.add(message); message = queue2.nextMessage(200); if (message != null) messages.add(message); assertEquals(1, messages.size()); |
我们只收到一条消息。
如果我们将前两行更改为普通订阅:
1 2 | SyncSubscription queue1 = natsConnection.subscribe("foo"); SyncSubscription queue2 = natsConnection.subscribe("foo"); |
测试失败,因为该消息被传递给两个订户。
六,结论
在此简要介绍中,我们连接到NATS服务器,并发送了发布/订阅消息和负载平衡的队列消息。 我们研究了NATS对通配符订阅的支持。 我们还使用了请求/回复消息。
与往常一样,可以在GitHub上找到代码示例。