Calling Webclient API call(asynchronous) from mono core environment - webclient

can we use webclient API from in mono core environment?
For Example
Func A()
{
while(1)
{
....
// Call webclint API
Client.getWebClient().post().uri(test\_uri)
.header(HttpHeaders.CONTENT\_TYPE, MediaType.APPLICATION\_JSON\_VALUE)
.body(Mono.just(record.value()), String.class)
.accept(MediaType.APPLICATION\_JSON).exchangeToMono(response -> {
if (response.statusCode().equals(HttpStatus.OK)) {
logger.info("received 200 OK");
return response.bodyToMono(String.class);
} else if (response.statusCode().is4xxClientError()) {
return response.createException().flatMap(Mono::error).onErrorReturn(
WebClientRequestException.class,
"4xx");
} else if (response.statusCode().is5xxServerError()) {
return response.createException().flatMap(Mono::error).onErrorReturn(
WebClientRequestException.class,
"5xx");
}
return null;
}).onErrorMap(WebClientRequestException.class, throwable -> {
return throwable;
}).onErrorReturn(WebClientRequestException.class,
"Unavailable")
.subscribe();
....
}
}
I am observing memory of applicatiion is increasing with every webclient API call.
As per MAT, netty event poll handler is getting most the memory.
Could you please suggest anything?

Related

The configured execution strategy 'RetryTransactionExecutionStrategy' does not support user initiated transactions

We wrote our own simple execution strategy to retry saving any data using our DbContext when it runs into a table lock timeout.
public class RetryTransactionExecutionStrategy : DbExecutionStrategy
{
public RetryTransactionExecutionStrategy() : base()
{
}
protected override bool ShouldRetryOn(Exception exception)
{
while (exception != null)
{
if (exception is MySqlException ex
&& ex.Number == 1205) // Deadlock error code
{
return true;
}
exception = exception.InnerException;
}
return false;
}
}
We register it by using the DbConfig class, in the same folder as the context class.
public class DbConfig : DbConfiguration
{
public DbConfig()
{
SetExecutionStrategy(MySqlProviderInvariantName.ProviderName, () => new RetryTransactionExecutionStrategy());
}
}
Now most regular usage of the context will use the retry execution strategy. However, transactions are a more special case. Microsoft mentions usage of them in their documentation, and tells the user to manually call the execution strategy, like this:
var executionStrategy = new RetryTransactionExecutionStrategy();
executionStrategy.Execute(() =>
{
using (PigDbAccountEntities pigDbAccountEntities = new PigDbAccountEntities())
{
using (var dbtransaction = pigDbAccountEntities.Database.BeginTransaction())
{
try
{
//work on some data
pigDbAccountEntities.SaveChanges();
//work on some more data
pigDbAccountEntities.SaveChanges();
//work on even more data
pigDbAccountEntities.SaveChanges();
dbtransaction.Commit();
isSaved = true;
}
catch (Exception ex)
{
dbtransaction.Rollback();
Logger.Instance.Log(LogLevel.ERROR, LogSource.DB, "error in AccountEntityManager.SaveApplicationUser", ex);
}
}
}
});
And yet we still get this error message:
The configured execution strategy 'RetryTransactionExecutionStrategy' does not support user initiated transactions. See http://go.microsoft.com/fwlink/?LinkId=309381 for additional information.
Any idea on what to do/check?

Can't handle bad request using doOnError WebFlux

I wanna send some DTO object to server. Server have "Valid" annotation, and when server getting not valid DTO, he should send validation errors and something like "HttpStatus.BAD_REQUEST", but when I'm trying to send HttpStatus.BAD_REQUEST doOnError just ignore it.
POST-request from client
BookDTO bookDTO = BookDTO
.builder()
.author(authorTf.getText())
.title(titleTf.getText())
.publishDate(LocalDate.parse(publishDateDp.getValue().toString()))
.owner(userAuthRepository.getUser().getLogin())
.fileData(file.readAllBytes())
.build();
webClient.post()
.uri(bookAdd)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(bookDTO)
.retrieve()
.bodyToMono(Void.class)
.doOnError(exception -> log.error("Error on server - [{}]", exception.getMessage()))
.onErrorResume(WebClientResponseException.class, throwable -> {
if (throwable.getStatusCode() == HttpStatus.BAD_REQUEST) {
log.error("BAD_REQUEST!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); --My log doesn't contain this error, but server still has errors from bindingResult
return Mono.empty();
}
return Mono.error(throwable);
})
.block();
Server-part
#PostMapping(value = "/add", consumes = {MediaType.APPLICATION_JSON_VALUE})
public HttpStatus savingBook(#RequestBody #Valid BookDTO bookDTO, BindingResult bindingResult) {
List<FieldError> errors = bindingResult.getFieldErrors();
if (bindingResult.hasErrors()) {
for (FieldError error : errors ) {
log.info("Client post uncorrected data [{}]", error.getDefaultMessage());
}
return HttpStatus.BAD_REQUEST;
}else{libraryService.addingBookToDB(bookDTO);}
return null;
}
doOnError is a so-called side effect operation that could be used for instrumentation before onError signal is propagated downstream. (e.g. to log error).
To handle errors you could use onErrorResume. The example, the following code handles the WebClientResponseException and returns Mono.empty instead.
...
.retrieve()
.doOnError(ex -> log.error("Error on server: {}", ex.getMessage()))
.onErrorResume(WebClientResponseException.class, ex -> {
if (ex.getStatusCode() == HttpStatus.BAD_REQUEST) {
return Mono.empty();
}
return Mono.error(ex);
})
...
As an alternative as #Toerktumlare mentioned in his comment, in case you want to handle http status, you could use onStatus method of the WebClient
...
.retrieve()
.onStatus(HttpStatus.BAD_REQUEST::equals, res -> Mono.empty())
...
Update
While working with block it's important to understand how reactive signals will be transformed.
onNext(T) -> T in case of Mono and List<T> for Flux
onError -> exception
onComplete -> null, in case flow completes without onNext
Here is a full example using WireMock for tests
class WebClientErrorHandlingTest {
private WireMockServer wireMockServer;
#BeforeEach
void init() {
wireMockServer = new WireMockServer(wireMockConfig().dynamicPort());
wireMockServer.start();
WireMock.configureFor(wireMockServer.port());
}
#Test
void test() {
stubFor(post("/test")
.willReturn(aResponse()
.withHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.withStatus(400)
)
);
WebClient webClient = WebClient.create("http://localhost:" + wireMockServer.port());
Mono<Void> request = webClient.post()
.uri("/test")
.retrieve()
.bodyToMono(Void.class)
.doOnError(e -> log.error("Error on server - [{}]", e.getMessage()))
.onErrorResume(WebClientResponseException.class, e -> {
if (e.getStatusCode() == HttpStatus.BAD_REQUEST) {
log.info("Ignoring error: {}", e.getMessage());
return Mono.empty();
}
return Mono.error(e);
});
Void response = request.block();
assertNull(response);
}
}
The response is null because we had just complete signal Mono.empty() that was transformed to null by applying block

How to return either Uni<Object> or Uni<Void>?

#Route(...)
public Uni<?> call() {
return Uni.createFrom().voidItem();
}
throws a NullPointerException: Invalid value returned by Uni: null
However
#Route(...)
public Uni<Void> call() {
return Uni.createFrom().voidItem();
}
works perfectly fine and responds with HTTP 204
How do I manage to get either Uni<Void> or Uni<AnyObject> from the same method?
I need to return http 204 only in specific scenarios
You can't do that directly as the types are different.
I would recommend using RESTEasy Reactive and do:
#GET
public Uni<Response> call() {
Uni<AnyObject> uni = .... ;
return uni
.onItem().transform(res -> {
if (res == null) return Response.noContent().build();
return Response.ok(res).build();
});
}
By emitting a Response object, you can customize the response status.
Another solution if you want to keep using Reactive Routes is to not return a Uni, but get the RoutingContext as a parameter:
#Route(...)
public void call(RoutingContext rc) {
HttpServerResponse response = rc.response();
Uni<AnyObject> uni = .... ;
return uni
.subscribe().with(res -> {
if (res == null) response.setStatus(204).end();
else response.setStatus(200).end(res);
}, failure -> rc.fail(failure)); // will produce a 500.
}

vertx.executeBlocking failing

I am new to vertx and am trying to execute a function1 using vertx.executeBlocking from ABCHandler.java
public class ABCHandler implements Handler<RoutingContext> {
public ABCHandler( Vertx vertx)
{this.vertx =vertx;}
#Override
public void handle(RoutingContext routingContext) {
vertx.executeBlocking(future -> {
function1(routingContext, as ->
{
if (as.failed()) {
future.fail(as.cause());
} else {
future.complete(as.result());
}
});
}, rs -> {
if (rs.failed()) {
routingContext.response().putHeader(CONTENT_TYPE,
"application/json").setStatusCode(Integer.valueOf(401)).end("error");
} else {
routingContext.put("key_1", rs.result());
routingContext.next();
}
});
}
}
ABCHandler is meant to validate some data before request is routed to actual URI. But after routingContext.next(); I am getting 500 (Internal server error).
WebClientCreator.getWebClient(vertx);
Router router = Router.router(vertx);
router.route().handler(new ABCHandler(vertx));
router.post(AgentBindingConstants.AGENT_ENROLLMENT_URI).handler(
BodyHandler.create().setBodyLimit(10000));
router.post("/abc").handler(routingContext -> {
//some code
});
Also, when I run same code as non blocking it works.
Any help here is much appreciated.

No handlers for address while using eventBus in communicating between verticles of a springboot project

I developed a project with Springboot and used Vertx as an asynchronous reactive toolkit. My ServerVerticle, create a httpServer which receives http requests from an Angular app and sends messages to it via eventBus. By the way, the time that received message arrives, ServerVerticle sends it to another verticle which has service instance in it (for connecting to repository). i tested it with postman and get "No handlers for address" error as a bad request.
here is my ServerVerticle:
HttpServerResponse res = routingContext.response();
res.setChunked(true);
EventBus eventBus = vertx.eventBus();
eventBus.request(InstrumentsServiceVerticle.FETCH_INSTRUMENTS_ADDRESS, "", result -> {
if (result.succeeded()) {
res.setStatusCode(200).write((Buffer) result.result().body()).end();
} else {
res.setStatusCode(400).write(result.cause().toString()).end();
}
});
My instrumentVerticle is as follows:
static final String FETCH_INSTRUMENTS_ADDRESS = "fetch.instruments.service";
// Reuse the Vert.x Mapper :)
private final ObjectMapper mapper = Json.mapper;
private final InstrumentService instrumentService;
public InstrumentsServiceVerticle(InstrumentService instrumentService) {
this.instrumentService = instrumentService;
}
private Handler<Message<String>> fetchInstrumentsHandler() {
return msg -> vertx.<String>executeBlocking(future -> {
try {
future.complete(mapper.writeValueAsString(instrumentService.getInstruments()));
} catch (JsonProcessingException e) {
logger.error("Failed to serialize result "+ InstrumentsServiceVerticle.class.getName());
future.fail(e);
}
},
result -> {
if (result.succeeded()) {
msg.reply(result.result());
} else {
msg.reply(result.cause().toString());
}
});
}
#Override
public void start() throws Exception {
super.start();
vertx.eventBus().<String>consumer(FETCH_INSTRUMENTS_ADDRESS).handler(fetchInstrumentsHandler());
}
and i deployed both verticles in the springbootApp starter.