- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
Source Code: https://github.com/joexu01/rsocket-demo 。
让我们来简单复习一下 RSocket 的四种通信模式:
即发即忘 - FireAndForget:立即发送一个请求,无需为这个请求发送响应报文。适用于监控埋点,日志上报等,这种场景下无需回执,丢失几个请求无伤大雅 。
请求响应 - RequestResponse:请求方发送一条请求消息,响应方收到请求后并返回一条响应消息。传统的HTTP是典型的Request-Response 。
流式响应 - RequestStream:请求方发送一个请求报文,响应方发回N个响应报文。传统的MQ是典型的RequestStream 。
双向通道 - Channel:创建一个通道上下文,双方可以互相发送消息。IM是个典型的RequestChannel通讯场景 。
*本篇文章的客户端示例文件在 rsocket-client-raw/src/main/java/org/example/FourCommunicationScheme.java 。
我们使用 decodeRoute 和 encodeRoute 函数来解码和编码路由信息.
static String decodeRoute(ByteBuf metadata) {
final RoutingMetadata routingMetadata = new RoutingMetadata(metadata);
return routingMetadata.iterator().next();
}
static ByteBuf encodeRoute(String route) {
return TaggingMetadataCodec.createTaggingContent(
ByteBufAllocator.DEFAULT,
Collections.singletonList(route));
}
服务端处理函数 。
在这里我们编写一个简单的 Handler,它的 Route 是 test.echo ,它接收一个请求并返回请求 Payload 的 data 中的字符串.
@MessageMapping("test.echo")
public Mono<String> simplyEcho(String data) throws InterruptedException {
Thread.sleep(1500);
logger.info("[test.echo]Received echo string from client: {}", data);
return Mono.just(String.format("[test.echo]I received your string: %s. Thank you.", data));
}
注意,这里的参数也可以是 Mono<String> ,然后对 Mono 进行操作并返回。事实上,如果严格按照响应式编程的策略,这里应该直接对 Mono 进行操作.
客户端发送请求 。
ByteBuf routeMetadata = encodeRoute("test.echo");
Payload echoPayload = ByteBufPayload.create(
ByteBufUtil.writeUtf8(ByteBufAllocator.DEFAULT, "This is a message from client using rsocket-java library."),
routeMetadata);
Mono<Payload>
。然后我们对这个 Mono 设定一些操作(具体操作请看代码注释):
Mono<Payload> requestResponse = socket.requestResponse(echoPayload);
requestResponse
// 当 subscribe() 操作开始执行时打印一下日志
.doOnSubscribe(subscription -> logger.info("Test1 subscribed to {}", subscription.toString()))
// 当携带的请求成功后要做的事情
.doOnSuccess(payload -> {
logger.info("Test1 - Successfully returned: {}", payload.getDataUtf8());
payload.release();
})
.doOnError(throwable -> logger.info("Test1 doOnError: {}", throwable.toString()))
// 可以使用 timeout 丢弃等待超时的 Mono
//.timeout(Duration.ofSeconds(1))
// 可以使用 doOnTerminate 在请求结束后做一些工作
// .doOnTerminate(() -> {})
// 但是一定要设置 doOnError
//.doOnError(TimeoutException.class, e -> logger.info("Test1 doOnError: {}", e.toString()))
// .onErrorReturn(TimeoutException.class, DefaultPayload.create("Payload: Test1 - timeout"))
// 可以使用 log() 来观察数据的状态
//.log()
// 客户端在执行 subscribe() 操作时才会开始从服务端接收数据流
// 在响应式编程中使用 subscribe 操作符是订阅一个数据流并处理发布的数据、错误和完成信号的核心方式之一
.subscribe();
请求发出后主线程不会阻塞,所以我们需要使用 socket.onClose().block(); 保持连接.
然后我们尝试运行服务端和客户端,看看一看客户端的输出:
[main] INFO org.example.RSocketClientRaw - My UUID is 0718ef3b-9ee0-42f1-9003-700a8aa9a98d
[main] INFO org.example.RSocketClientRaw - Test1 subscribed to RequestResponseRequesterMono
[reactor-tcp-epoll-2] INFO org.example.RSocketClientRaw - Test1 - Successfully returned: [test.echo]I received your string: This is a message from client using rsocket-java library.. Thank you.
服务端日志:
2023-03-12 21:47:29.291 INFO 32099 --- [or-http-epoll-2] o.example.controller.RSocketController : [connect.setup]Client connection: 0718ef3b-9ee0-42f1-9003-700a8aa9a98d
2023-03-12 21:47:32.304 INFO 32099 --- [or-http-epoll-2] o.example.controller.RSocketController : [test.echo]Received echo string from client: This is a message from client using rsocket-java library.
客户端成功地发出请求并收到来自服务端的回复.
服务端 。
@MessageMapping("upload.log")
public void fireAndForgetHandler(@Headers Map<String, Object> header, RSocketRequester requester, String data) {
header.forEach((k, v) -> System.out.printf("[upload.log]header key: %s, val: %s\n", k, v));
System.out.printf("[upload.log]UploadEventLogs: Received log string from client: %s\n", data);
}
服务端接受一个请求,不返回任何结果(Fire'n'Forget),只在服务端打印 Header 的内容.
客户端 。
// 测试 FnF
routeMetadata = TaggingMetadataCodec.createTaggingContent(ByteBufAllocator.DEFAULT, Collections.singletonList("upload.log"));
socket.fireAndForget(
ByteBufPayload.create(
ByteBufUtil.writeUtf8(ByteBufAllocator.DEFAULT, "This is a log from client using rsocket-java library."),
routeMetadata))
.doOnSubscribe(subscription -> logger.info("Test2 - Fire And Forget onSubscribe: {}", subscription.toString()))
.subscribe();
客户端输出:
[main] INFO org.example.RSocketClientRaw - Test2 - Fire And Forget onSubscribe: FireAndForgetRequesterMono
服务端输出:
2023-03-10 15:10:25.675 INFO 5318 --- [or-http-epoll-4] o.example.controller.RSocketController : [test.echo]Received echo string from client: This is a message from client using rsocket-java library.
[upload.log]header key: dataBufferFactory, val: NettyDataBufferFactory (PooledByteBufAllocator(directByDefault: true))
[upload.log]header key: rsocketRequester, val: org.springframework.messaging.rsocket.DefaultRSocketRequester@607cc59
[upload.log]header key: lookupDestination, val: upload.log
[upload.log]header key: contentType, val: application/binary
[upload.log]header key: rsocketFrameType, val: REQUEST_FNF
[upload.log]UploadEventLogs: Received log string from client: This is a log from client using rsocket-java library.
服务端 。
服务端接收一个 Mono<String> 然后返回给客户端包含 10 个 String 的 Flux .
事实上,严格按照响应式编程的策略,这里应该直接对 Mono 进行操作,可以使用 flatMapMany() 把生成的数据流通过异步方式处理,扩展出新的数据流。下面是扩展新数据流的简单示例:
Mono.just(3)
.flatMapMany(i -> Flux.range(0, i))
.subscribe(System.out::println);
在这里为了演示方便就先打印 Mono 然后新生成一个 Flux .
@MessageMapping("handler.request.stream")
public Flux<String> responseStreaming(Mono<String> request) {
request
.doOnNext(s -> logger.info("[handler.request.stream]: {}", s))
// 可以使用 then() 结束操作链
.then()
.subscribe();
return Flux
.range(1, 10)
.map(idx -> String.format("Resp from Server: %s, Thank you!", idx));
}
客户端 。
请看代码注释来理解对数据流 Flux 的各种操作:
// 测试 RequestStream
routeMetadata = encodeRoute("handler.request.stream");
Flux<Payload> requestStream = socket.requestStream(
ByteBufPayload.create(
ByteBufUtil.writeUtf8(ByteBufAllocator.DEFAULT, "TEST3 - Request&Stream"),
routeMetadata));
requestStream
// 当然可以使用 map 对每个 Payload 进行操作,这会改变数据
// .map(payload -> System.out.printf("%s\n", payload.getDataUtf8()))
.doOnSubscribe(subscription -> logger.info("Test3 subscribed to {}", subscription.toString()))
// 使用 doOnNext 不会对流的数据进行改变
// doOnNext()是一个 Reactor 式流操作符,它允许编写者注册一个在每次出现新元素时执行的回调函数
.doOnNext(nextPayload -> System.out.println("Test3 Received payload: " + nextPayload.getDataUtf8()))
// 当需要从流中选择一些特定的元素时,可以使用 Flux.take(long n) 操作符
// 该操作符将创建一个新的 Flux,该 Flux 包含原始 Flux 的前 n 个元素
// take 操作符发出了指定数量的元素之后,就不再接收任何元素,并且将取消其上游发布者的订阅
// 在这里服务端使用 Flux.range 来限定 Flux 流中的元素个数
// 如果服务端使用 Flux.interval 生成一个无限长度的流,客户端使用 take 接收限定个数的元素
// 便会取消发布者的订阅
.take(5)
.subscribe();
客户端输出结果:
[main] INFO org.example.RSocketClientRaw - My UUID is 28afc749-75e1-4289-8607-14810103de6c
[main] INFO org.example.RSocketClientRaw - Test3 subscribed to RequestStreamRequesterFlux
Test3 Received payload: Resp from Server: 1, Thank you!
Test3 Received payload: Resp from Server: 2, Thank you!
Test3 Received payload: Resp from Server: 3, Thank you!
Test3 Received payload: Resp from Server: 4, Thank you!
Test3 Received payload: Resp from Server: 5, Thank you!
服务端接收到了请求:
2023-03-12 22:01:33.520 INFO 32099 --- [or-http-epoll-3] o.example.controller.RSocketController : [handler.request.stream]: TEST3 - Request&Stream
服务端 。
服务端接收来自客户端的整数字符串,将它们乘以2以后发送回去。我们不妨把处理客户端请求流的函数封装为一个 Spring Service:
@Service
public class MathService {
public Flux<String> doubleInteger(Flux<String> request) {
return request
.map(s -> {
System.out.println("received " + s);
int i = Integer.parseInt(s);
return String.valueOf(i * 2);
});
}
}
编写处理函数:
@Autowired
private MathService mathService;
@MessageMapping("handler.request.channel")
public Flux<String> responseChannel(Flux<String> payloads) {
return this.mathService.doubleInteger(payloads);
}
客户端 。
Flux<Payload> payloadFlux = Flux.range(-5, 10)
.delayElements(Duration.ofMillis(500))
.map(obj ->
{
ByteBuf metadata = encodeRoute("handler.request.channel");
return ByteBufPayload.create(
ByteBufUtil.writeUtf8(ByteBufAllocator.DEFAULT, obj.toString()), metadata);
});
Flux<Payload> channelResp = socket.requestChannel(payloadFlux);
channelResp
.doOnSubscribe(subscription -> logger.info("Test4 subscribed to {}", subscription.toString()))
.doOnError(throwable -> logger.info(throwable.toString()))
.doOnNext(nextPayload -> System.out.println("Test4 Received payload: " + nextPayload.getDataUtf8()))
.subscribe();
客户端输出:
[main] INFO org.example.RSocketClientRaw - My UUID is 96ff8fe7-416c-4607-9518-463114725a7a
[main] INFO org.example.RSocketClientRaw - Test4 subscribed to RequestChannelRequesterFlux
Test4 Received payload: -10
Test4 Received payload: -8
Test4 Received payload: -6
Test4 Received payload: -4
Test4 Received payload: -2
Test4 Received payload: 0
Test4 Received payload: 2
Test4 Received payload: 4
Test4 Received payload: 6
Test4 Received payload: 8
服务端输出:
2023-03-12 22:07:05.542 INFO 33083 --- [or-http-epoll-2] o.example.controller.RSocketController : [connect.setup]Client connection: 96ff8fe7-416c-4607-9518-463114725a7a
received -5
received -4
received -3
received -2
received -1
received 0
received 1
received 2
received 3
received 4
下一篇文章会展示服务端如何主动调用客户端的函数。如有错误欢迎在评论区批评指正! 。
最后此篇关于【RSocket】使用RSocket(二)——四种通信模式实践的文章就讲到这里了,如果你想了解更多关于【RSocket】使用RSocket(二)——四种通信模式实践的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
如何设置路由元数据(在服务器使用 Spring Boot Rsocket 时仅使用 RSocket-Java 的有效负载。Flux s = connection.flatMapMany(request
好的,我是 RSocket 的新手。我正在尝试创建一个简单的 RSocket 客户端和简单的 RSocket 服务器。从我所做的研究来看,RSocket 支持恢复: 它特别有用,因为当发送包含有关最后
我对 RSocket 完全陌生。 我阅读了常见问题解答和动机页面(并浏览了协议(protocol)页面)并了解 RSocket 可以在 TCP、WebSocket 和 Aeron 之上使用。但是我不明
我正在尝试在 Vue 中创建一个站点并在 Spring 上创建后端。我想使用 rsocket 来传输数据,但是只要我在 spring 中添加 rsocket seurity,我就会得到: 'metad
我在通过 TCP 连接到 Spring Boot RSocket 应用程序时遇到问题。使用 RSocketRequester 时的客户端工作正常,但是当我尝试使用 RSocketFactory 客户端
Is it somehow possible to use Spring's RSocket integration in a "traditional" servlet-b
目录 0. RSocket 简介 1. 服务端 1.1 SETUP阶段 - 处理客户端发起的连接请求
Source Code: https://github.com/joexu01/rsocket-demo 0. 四种通信模式 让我们来简单复习一下 RSocket 的四种通信模式:
1. 编写客户端接收请求的逻辑 我们可以在初始化 Rsocket 实例的时候指定客户端可以被调用的方法,使用 acceptor() 指定可被调用的方法和方法使用的通信模型类型:
rsocket 似乎是个很酷的主意。我有这个问题,但找不到更好的答案。 让我们考虑这个初始设置。客户端依次向server-1发送多个请求。 client --> server-1 server-1 正
我已经使用rsocket编写了简单的客户端和服务器程序-使用以下版本的librdmacm-dev和librdmacm1软件包(使用Ubuntu 14.04)的RDMA套接字API: librdmacm
在我的项目中,我希望有多个客户端连接到一个服务。我正在使用 java Rsocket 实现。 服务应该为每个客户端维护一个状态。现在在这一点上,我可以通过一些标识符来管理客户端。这个选项我已经实现了。
这是我的代码,它仅适用于我所在的选项卡。我收到了回复,似乎一切正常,但我仍然不完全了解该技术的操作,这就是我去找你的原因。 它以“responseHanlder”结尾 connect() {
有人可以告诉我或使用 提供现成的 CRUD 示例吗? WebFlux、RScoket 和 Spring(或 SpringBoot) ? 我研究了 RSocket 文档, WebFlux ,也写了我的简
我必须再次回到 Symbian 中的套接字。建立与远程服务器的连接的代码如下所示: TInetAddr serverAddr; TUint iPort=111; TRequestStatus iS
我似乎无法在 RSocket 上找到任何资源/教程,除了阅读他们在 GitHub 上的代码,我不明白。 我的服务器上有一个文件路径:String serverFilePath; 我希望能够从我的客户端
假设我有这个用于聊天消息的简单 Websocket 处理程序: @Override public Mono handle(WebSocketSession webSocketSession) {
我正在尝试为我的服务器编写一个客户端(在 Kotlin 和使用 Spring Reactive Web 中)。我在尝试使用 RSocket 时遇到了这个问题。如何使用 RSocket 获得 Flux?
我正在使用 Spring 对 RSocket 的支持,特别是请求流模型。 IE。: @MessageMapping("stream") Flux stream(final SubscriptionMe
解决摘要: 在目前的大多数 RSocket 示例中,即使在 SpringBoot 相关教程中,服务器端接受器也被简单地构造为一个新对象(如下面的 new MqttMessageService() )。
我是一名优秀的程序员,十分优秀!