How can i handle connection and reconnect if connection got closed? - spring-boot

I need this client stay connected for long, How can i make sure about connection? because the issue was in connection, so i am updating my question. what should i do if server close connection? or if client close connection? how can i handle it and reconnect client to the server?
public void consumeServerSentEvent() {
WebClient client = WebClient.create("http://localhost:8080/sse-server");
ParameterizedTypeReference<ServerSentEvent<String>> type
= new ParameterizedTypeReference<ServerSentEvent<String>>() {};
Flux<ServerSentEvent<String>> eventStream = client.get()
.uri("/stream-sse")
.retrieve()
.bodyToFlux(type);
eventStream.subscribe(
content -> logger.info("Time: {} - event: name[{}], id [{}], content[{}] ",
LocalTime.now(), content.event(), content.id(), content.data()),
error -> logger.error("Error receiving SSE: {}", error),
() -> logger.info("Completed!!!"));
}

According to documentation retrieve() returns Mono of ClientResponse, but for your case you need to consume Flux of the body.
Try some thing like this:
Flux<ServerSentEvent<String>> eventStream = client.get()
.uri("/stream-sse")
.retrieve()
.flatMapMany(response -> response.bodyToFlux(type));

Related

Why is the log statement in Spring WebClient subscribe() block not invoked?

I am playing around with the spring webclient to send emails via an api. The mails get send, however I am trying to do this without blocking and I want to log the successful response (or error). As I understood it so far, I could do this in the subscribe block. However, I do not manage to get the logging (or anything else) within the subscribe block to work.
MailClient.kt
fun sendMail(mail: Mail): Mono<String> {
return getWebClient()
.post()
.uri("uri")
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(..)
.retrieve()
.bodyToMono(String::class.java)
//.timeout(Duration.ofSeconds(10))
}
Service function which uses MailClient:
logger.info("Sending mail due to failure with id = ${failure.id}, error: $errorMessage")
mailClient.sendMail(
Mail(..)
)
.subscribe(
{ response -> logger.info("success: $response") },
{ error -> logger.error("error: $error") })

how to fulfill functionality of httpclient use tcpclient in Reactor-netty

i'm trying to make a tcp version of gateway. and this is the gateway code . it's use httpclient
NettyInbound serverInbound;
NettyOutbound serverOutbound;
Map map;
Flux<HttpClientResponse> flux = HttpClient.create()
.port(8080)
.request(HttpMethod.POST)
.send((req, outbound) -> {
// send infomation
return outbound.send(serverInbound.receive().retain());
}).responseConnection((res, connection) -> {
// save connection
map.put("connection",connection);
return Mono.just(res);
});
flux.then(Mono.defer(()->{
// this is after the gateway server receiving from backend and send to the front
Connection connection = map.get("connectoin");
return serverOutbound.send(connection.inbound().receive().retain());
}));
and i'm trying to do the same thing with tcpclient
TcpClient client = TcpClient.create()
.port(8900);
TcpServer.create()
.port(8080)
.handle((inbound,outbound)->{
// must be something wrong with it
return client.connect().flatMap(con->{
NettyInbound cin = con.inbound();
NettyOutbound cout = con.outbound();
return cout.send(inbound.receive().retain()).then().map(ss->{
return cin;
});
}).map(cin->{
return outbound.send(cin.receive());
}).then();
}).bindNow()
.onDispose()
.block();
but it won't work . is anything i can do to fulfill the functionality like responseConnection ?

Why can't subscribe the request in reactor-netty?

I just want access the Http content in reactor-netty project. But the result is null.
Code is below.
DisposableServer server =
HttpServer.create()
.host("localhost")
.port(8000)
.route(routes ->
.post("/echo",
(request, response) ->
{ request.receive()
.retain()
.aggregate()
.asString()
.subscribe(System.out::println);
return response.sendString(Mono.just("hello"));})
.bindNow();
I can't get the rerult in the console.
Could I access the request as what I do in the code?
Anyone can help? Thanks.
You return the response before the request data is received, so Reactor Netty will drop any incoming data that is received AFTER the response is sent.
I don't know your use case but changing the example to this below, you will be able to see the incoming data:
DisposableServer server =
HttpServer.create()
.host("localhost")
.port(8000)
.route(routes ->
routes.post("/echo",
(request, response) ->
response.sendString(request.receive()
.retain()
.aggregate()
.asString()
.flatMap(s -> {
System.out.println(s);
return Mono.just("hello");
})))
)
.bindNow();

with spring boot rsocket capture the cancel frame type

I have a spring boot rsocket implementation where if a client cancels or closes their rsocket request then I want to cancel other subscription registrations on the server.
In the logs on the spring boot server I can see that a cancel message is sent or received:
WARN i.r.t.n.s.WebsocketServerTransport$1 [reactor-http-nio-3] received WebSocket Close Frame - connection is closing
INFO r.u.Loggers$Slf4JLogger [reactor-http-nio-3] cancel()
How do I capture and handle this cancel signal?
I tried cancel endpoints but these don't capture the signal:
#MessageMapping("cancel")
Flux<Object> onCancel() {
log.info("Captured cancel signal");
}
or
#ConnectMapping("cancel")
Flux<Object> onCancel2() {
log.info("Captured cancel2 signal");
}
This question on cancel subscriptions is possibly related, and this question on detecting websocket disconnection
To capture the cancel signal you can use subscribe to onClose() event.
In your controller
#Controller
class RSocketConnectionController {
#ConnectMapping("client-id")
fun onConnect(rSocketRequester: RSocketRequester, clientId: String) {
// rSocketRequester.rsocket().dispose() //to reject connection
rSocketRequester
.rsocket()
.onClose()
.subscribe(null, null, {
log.info("{} just disconnected", clientId)
//TODO here whatever you want
})
}
}
Your client needs to send the SETUP frame properly to invoke this #ConnectMapping. If you use rsocket-js you need to add a payload like this:
const client = new RSocketClient({
// send/receive JSON objects instead of strings/buffers
serializers: {
data: JsonSerializer,
metadata: IdentitySerializer
},
setup: {
//for connection mapping on server
payload: {
data: 'unique-client-id', //TODO you can receive this data on server side
metadata: String.fromCharCode("client-id".length) + "client-id"
},
// ms btw sending keepalive to server
keepAlive: 60000,
.....
}
});
It was not a well set out question. The answer is that
INFO r.u.Loggers$Slf4JLogger [reactor-http-nio-3] cancel()
is seen by a FluxSink that was setup from the original #MessageMapping endpoint.
For example:
#MessageMapping("hello")
Flux<Object> hello(#Payload String message) {
return myService.generateWorld(message);
}
In myService class
public Flux<Object> generateWorld(String hello) {
EmitterProcessor<Object> emitter = EmitterProcessor.create();
FluxSink<Object> sink = emitter.sink(FluxSink.OverflowStrategy.LATEST);
// doing stuff with sink here
sink.next(stuff());
// This part will handle a cancel from the client
sink.onCancel(() -> {log.info("********** SINK.onCancel ***********");});
return Flux.from(emitter));
}
The sink.onCancel() will handle a cancel of the flux to the hello endpoint, from the client.

How to use routes with reactive websocketstream in vertx

Vertx vertx = Vertx.vertx();
var router = Router.router(vertx).route("/entity/:id")
vertx.createHttpServer()
.websocketStream()
.toObservable()
.subscribe(sock -> sock.)
.map(ServerWebSocket::toObservable)
I'm new to vert.x I've managed to create an observable socket, but I don't understand how to use routes and url route parameters with reactive version of API.
Vert.x Web router deals with HTTP requests. You can use it to handle the upgrade to websocket request:
router.route("/entity/:id").handler(rc -> {
String entityId = rc.pathParam("id");
HttpServerRequest request = rc.request();
ServerWebSocket webSocket = request.upgrade();
webSocket.frameHandler(frame -> {
webSocket.writeFrame(WebSocketFrame.textFrame("Pong " + entityId, true));
});
webSocket.accept();
});
Inside the router handler, you may retrieve path parameters with the RoutingContext#pathParam method.

Resources