spring boot rsocket with "message was successfully sent" - spring-boot

I have a client that needs to receive an Exception from the server
client:
rSocketRequester
.route("v1.data")
.data(data)
.sendAndAwait()
server:
return Mono.error(RuntimeException("test excep"))
In docs say: https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#rsocket-requester-requests "For Fire-and-Forget use the send() method that returns Mono. Note that the Mono indicates only that the message was successfully sent, and not that it was handled."
How do i get message was complete or the exception data?

Switch to a Request-Response operation instead of Fire-and-Forget, you may want to assume the result is a lightweight result object, or empty byte.
https://github.com/spring-projects/spring-framework/blob/3ed8813bbfc1678de73529a882ce535e2636519c/src/docs/asciidoc/rsocket.adoc
Java
Mono<AirportLocation> location = requester.route("find.radar.EWR"))
.retrieveMono(AirportLocation.class);
Kotlin
import org.springframework.messaging.rsocket.retrieveAndAwait
val location = requester.route("find.radar.EWR")
.retrieveAndAwait<AirportLocation>()

Related

Detect server disconnect in gRPC Go client

I have a gRPC service simmilar to below
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
I need the client to maintain a long living gRPC connection to the server so that if the server goes down, the client can reconnect and issue SayHello() again.
Based on my understanding there are a few options:
Pass in a statsHandler to grpc.Dial and add retry logic in HandleConn()
Add a new ClientStreaming API that maybe sends a message every few seconds. Check for server side stream close errors and implement retry logic.
Not sure if there is a recommended way for my use case and would appreciate any help.

Spring AMQP AsyncRabbitTemplate Doesn't Send Message In Delay Time

I'm trying to send delayed messages on RabbitMQ with Spring AMQP.
I'm defining MessageProperties like this:
MessageProperties delayedMessageProperties = new MessageProperties();
delayedMessageProperties.setDelay(45000);
I'm defining the message which should be send in delay time like this:
org.springframework.amqp.core.Message amqpDelayedMessage = org.springframework.amqp.core.MessageBuilder.withBody(objectMapper.writeValueAsString(reversalMessage).getBytes())
.andProperties(reversalMessageProperties).build();
And then, If I send this message with RabbitTemplate, there is no problem. Message is being sent in defined delay time.
rabbitTemplate.convertSendAndReceiveAsType("delay-exchange",delayQueue, amqpDelayedMessage, new ParameterizedTypeReference<org.springframework.amqp.core.Message>() {
});
But I need to send this message asynchronously because I need not to block any other message in the system and to get more performance and if I use asyncRabbitTemplate, message is being delivered immediately. There is no delay.
asyncRabbitTemplate.convertSendAndReceiveAsType("delay-exchange",delayQueue, amqpDelayedMessage, new ParameterizedTypeReference<org.springframework.amqp.core.Message>() {
});
How can I obtain the delay with asnycRabbitTemplate?
This is probably a bug; please open an issue on GitHub.
The convertSendAndReceive() methods are not intended to send and receive raw Message objects.
In the case of the RabbitTemplate the conversion is skipped if the object is already a Message; there are some cases where this skip is not performed with the async template; please edit the question to show your template configuration.
However, since you are dealing with Message directly, don't use the convert... methods at all, simply use
public RabbitMessageFuture sendAndReceive(String exchange, String routingKey, Message message) {

customization in protobuf java generated code

We have a use case where, we have many RPC defined in different-different .proto files , and we generate a java based grpc stub code by using google's protobuf-java & protoc-gen-grpc-java as gradle plugin.
The requirement is we want to generate a new Service which flips the request, response and add stream to new flipped rpc.
So for example :
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
to be converted to like
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
rpc SayHelloStreaming (stream HelloReply) returns (stream HelloRequest) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
In java generated code I should be having 2 services for each original service. We just want the final java generated code to be having 2 services, the parser may/may not update original .proto files.
Is this customization possible with current protoc ? Can we extend the plugin and write ours -> Can someone please give some pointers.
Your question is unclear to me.
Revising proto files is a fundamental requirement of gRPC.
The Java tutorial on https://grpc.io includes an example of adding a method to a service. In part, this is because adding|removing|updating methods|messages|fields is a common behavior.
NOTE To clarify nomenclature, in your example, you're proposing adding a method to an existing service (definition). If you consider the proto as defining an API, this represents a non-breaking change. See Versioning gRPC services for a good overview. Existing clients will continue to work (they are only aware of SayHello) while new clients will be aware of SayHelloStreaming too.

Webflux - How to prevent an IllegalReferenceCountException when executing 2 WebClient request in parallel

I am using the spring WebClient to make two calls in parallel.
One of the call results is passed back as a ResponseEntity, and the other result is inspected and then disregarded. Although the transactions are both successful, I see an IllegalReferenceCountException that occurs before any of the WebClient calls actually get executed.
What I see in my logging is that the container logs the exception, then my two HTTP requests get executed successfully, and one of these responses gets returned to the client.
If the shouldBackfill() function returns false, then I execute one HTTP request and return that response (and the IllegalReferenceCountException does not occur).
I was initially thinking that I should release the reference in the second response that I disregard.
If I attempt to call releaseBody() directly on the WebClient response. (See https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/function/client/ClientResponse.html), this does not help. I assume now that the container is detecting that the WebClient request that I disregarded is in an illegal state, hence the error detection. But what I don't understand is that the actual request occurs AFTER the IllegalReferenceCountException gets logged.
Any ideas here on how to get around this? I am wondering if the exception is actually NOT any kind of leak.
The code looks like this:
fun execute(routeHttpRequest: RouteHttpRequest): Mono<ResponseEntity<String>> =
propertyRepository.getProperty(routeHttpRequest.propertyId.orDefault())
.flatMap {
val status = it.getOrElse { unknownStatus(routeHttpRequest.propertyId.orDefault()) }
val response1 = execute(routeHttpRequest, routingRepository.webClientFor(routeHttpRequest))
if (shouldBackfill(routeHttpRequest, status.type())) {
val response2 =
execute(routeHttpRequest, routingRepository.shadowOrBackfillWebClientFor(routeHttpRequest))
zip(response1, response2).map { response ->
compare(routeHttpRequest, response.t1, response.t2, status.type())
response.t1 // response.t2 is NOT returned here..
}
} else response1
}
// This function returns a wrapper on a spring Webclient that makes an HTTP post.
//
private fun execute(routeHttpRequest: RouteHttpRequest, client: Mono<MyWebClient>) =
client
.flatMap { dataService.execute(routeHttpRequest, it) }
.subscribeOn(Schedulers.elastic()) // TODO: consider a dedicated executor here?
private fun shouldBackfill(routeHttpRequest: RouteHttpRequest, migrationStatus: MigrationStatusType): Boolean {
... this logic returns true when we should execute 2 requests in parallel
}
Here's the exception and partial trace:
io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
at io.netty.util.internal.ReferenceCountUpdater.toLiveRealRefCnt(ReferenceCountUpdater.java:74)
at io.netty.util.internal.ReferenceCountUpdater.release(ReferenceCountUpdater.java:138)
at io.netty.buffer.AbstractReferenceCountedByteBuf.release(AbstractReferenceCountedByteBuf.java:100)
at io.netty.util.ReferenceCountUtil.release(ReferenceCountUtil.java:88)
Sorry for not posting the exact code. Fix- I was passing the incoming http request org.springframework.core.io.buffer.DataBuffer directly to the WebClient request body. This was intentional because my application is acting as a proxy service. The problem came up when I attempted to make two outbound WebClient calls in parallel - the container was trying to release the underlying buffer twice, and the IllegalReferenceCountException occurs. My fix was to just copy the DataBuffer byte array into a new buffer before sending the request along to it's destination.

Request-response pattern using Spring amqp library

everyone. I have an HTTP API for posting messages in a RabbitMQ broker and I need to implement the request-response pattern in order to receive the responses from the server. So I am something like a bridge between the clients and the server. I push the messages to the broker with specific routing-key and there is a Consumer for that messages, which is publishing back massages as response and my API must consume the response for every request. So the diagram is something like this:
So what I do is the following- For every HTTP session I create a temporary responseQueue(which is bound to the default exchange, with routing key the name of that queue), after that I set the replyTo header of the message to be the name of the response queue(where I will wait for the response) and also set the template replyQueue to that queue. Here is my code:
public void sendMessage(AbstractEvent objectToSend, final String routingKey) {
final Queue responseQueue = rabbitAdmin.declareQueue();
byte[] messageAsBytes = null;
try {
messageAsBytes = new ObjectMapper().writeValueAsBytes(objectToSend);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
MessageProperties properties = new MessageProperties();
properties.setHeader("ContentType", MessageBodyFormat.JSON);
properties.setReplyTo(responseQueue.getName());
requestTemplate.setReplyQueue(responseQueue);
Message message = new Message(messageAsBytes, properties);
Message receivedMessage = (Message)requestTemplate.convertSendAndReceive(routingKey, message);
}
So what is the problem: The message is sent, after that it is consumed by the Consumer and its response is correctly sent to the right queue, but for some reason it is not taken back in the convertSendAndReceived method and after the set timeout my receivedMessage is null. So I tried to do several things- I started to inspect the spring code(by the way it's a real nightmare to do that) and saw that is I don't declare the response queue it creates a temporal for me, and the replyTo header is set to the name of the queue(the same what I do). The result was the same- the receivedMessage is still null. After that I decided to use another template which uses the default exchange, because the responseQueue is bound to that exchange:
requestTemplate.send(routingKey, message);
Message receivedMessage = receivingTemplate.receive(responseQueue.getName());
The result was the same- the responseMessage is still null.
The versions of the amqp and rabbit are respectively 1.2.1 and 1.2.0. So I am sure that I miss something, but I don't know what is it, so if someone can help me I would be extremely grateful.
1> It's strange that RabbitTemplate uses doSendAndReceiveWithFixed if you provide the requestTemplate.setReplyQueue(responseQueue). Looks like it is false in your explanation.
2> To make it worked with fixed ReplyQueue you should configure a reply ListenerContainer:
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(rabbitConnectionFactory);
container.setQueues(responseQueue);
container.setMessageListener(requestTemplate);
3> But the most important part here is around correlation. The RabbitTemplate.sendAndReceive populates correlationId message property, but the consumer side has to get deal with it, too: it's not enough just to send reply to the responseQueue, the reply message should has the same correlationId property. See here: how to send response from consumer to producer to the particular request using Spring AMQP?
BTW there is no reason to populate the Message manually: You can just simply support Jackson2JsonMessageConverter to the RabbitTemplate and it will convert your objectToSend to the JSON bytes automatically with appropriate headers.

Resources