I wanted to retry in case of any exception from the service. But when using retryWhen am getting exception java.lang.IllegalStateException: UnicastProcessor allows only a single Subscriber.
Without retry, its working fine
Flux.window(10)
.flatMap(
windowedFlux ->
webclient.post().uri(url)
.body(BodyInserters.fromPublisher(windowedFlux, Request.class))
.exchange()
.doOnNext(ordRlsResponse -> {
if( ordRlsResponse.statusCode().is2xxSuccessful()) {
Mono<ResponseEntity<Response>> response = ordRlsResponse.toEntity(Response.class);
//doing some processing here
}
else {
throw new CustomeException(errmsg);
}
}).retryWhen(retryStrategy)).subscribe();
And my retryStrategy is defined like:
Retry retryStrategy = Retry.fixedDelay((long)5, Duration.ofSeconds((long)5))
.filter(exception -> exception instanceof CustomeException)
.doAfterRetry( exception -> log.info("Retry attempted"))
Related
Ley's say we have a service:
interface ExceptionService : Service {
fun doSmth(flag: Boolean)
}
implementation:
class ExceptionServiceImpl : ExceptionService {
override fun doSmth(flag: Boolean) {
if (flag) {
throw IOException("my IO exception")
} else {
throw IllegalArgumentException("my IllegalArgument exception")
}
}
}
We deploy it onto Ignite cluster:
Ignition.start(
IgniteConfiguration()
.setServiceConfiguration(
ServiceConfiguration()
.setService(ExceptionServiceImpl())
.setName("service")
)
)
And now we call it from client:
val client = Ignition.startClient(ClientConfiguration().setAddresses("127.0.0.1"))
val service = client.services().serviceProxy("service", ExceptionService::class.java)
try {
service.doSmth(true)
} catch (e: Exception) {
println(e.mesaage)
}
try {
service.doSmth(false)
} catch (e: Exception) {
println(e.mesaage)
}
Console output will be:
Ignite failed to process request [1] my IO exception (server status code [1])
Ignite failed to process request [2] my IllegalArgument exception (server status code [1])
The problem is that the type of the caught exception is always org.apache.ignite.client.ClientException. The only thing that left from the original exceptions thrown on server is message, and even it is wrapped in other words. The cause is of type org.apache.ignite.internal.client.thin.ClientServerError. Types are lost.
I want to handle on client side different types of exceptions thrown by service. Is there a way to do it? Maybe some Ignite configuration that I missed?
Try enabling ThinClientConfiguration#sendServerExceptionStackTraceToClient
i'm trying to understand with no luck why this throwable is not catched in my catch block:
CoroutineScope(IO).launch {
try { FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
if (task.isSuccessful) {
token = task.result
}
throw Exception("Hi There!")
}).await()
getUsers().await()
}catch (e: Exception){
binding.txtTitle.text = "Error: ${e.message}"
}
}
The exception is called but the app crash and not handle by the catch block. But if i throw an exception outside the addOnCompleteListener the exception is handled normally. My objective is to stop the execution of the getUsers function if no token is available.
The exception which is thrown in OnCompleteListener will not propagate to the outer scope, it is scoped to OnCompleteListener block. To achieve your objective I would recommend to rewrite the code to something like the following:
coroutineScope.launch {
try {
val token: String = FirebaseMessaging.getInstance().token.await()
if (token.isNotEmpty) {
getUsers().await()
}
} catch (e: Exception){
// ...
}
}
await function waits for task to complete.
I have a spring webclient making http calls to an external service and backed by reactive circuit breaker factory (resilience4J impl). WebClient and circuit breaker behave as expected when the client establishes connection and fails on response (Any internal server or 4XX errors). However, if the client fails to establish connection, either Connection Refused or UnknownHost, it starts to break down.
I cannot seem to catch the error message within the webclient and trigger circuit breaker.
Circuit breaker never opens and throws TimeoutException.
java.util.concurrent.TimeoutException: Did not observe any item or terminal signal within 1000ms in 'circuitBreaker' (and no fallback has been configured) .
Error from Web client.
io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/127.0.0.1:9000 .
Here's my code. I have pasted the error origins as well. I have tried to map ConnectException to my custom exception for circuit breaker to pick up but, it did not work. Can someone help me on handling errors without the response from remote server?
public Mono<String> toSink(
Envelope envelope, ConsumerConfiguration webClientConfiguration) {
return getWebClient()
.post()
.uri(
uriBuilder -> {
if (webClientConfiguration.getPort() != null) {
uriBuilder.port(webClientConfiguration.getPort());
}
return uriBuilder.path(webClientConfiguration.getServiceURL()).build();
})
.headers(
httpHeaders ->
webClientConfiguration.getHttpHeaders().forEach((k, v) -> httpHeaders.add(k, v)))
.bodyValue(envelope.toString())
.retrieve()
.bodyToMono(Map.class)
// Convert 5XX internal server error and throw CB exception
.onErrorResume(
throwable -> {
log.error("Inside the error resume callback of webclient {}", throwable.toString());
if (throwable instanceof WebClientResponseException) {
WebClientResponseException r = (WebClientResponseException) throwable;
if (r.getStatusCode().is5xxServerError()) {
return Mono.error(new CircuitBreakerOpenException());
}
}
return Mono.error(new CircuitBreakerOpenException());
})
.map(
map -> {
log.info("Response map:{}", Any.wrap(map).toString());
return Status.SUCCESS.name();
})
.transform(
it -> {
ReactiveCircuitBreaker rcb =
reactiveCircuitBreakerFactory.create(
webClientConfiguration.getCircuitBreakerId());
return rcb.run(
it,
throwable -> {
/// "Did not observe any item or terminal signal within 1000ms.. " <--- Error here
log.info("throwable in CB {}", throwable.toString());
if (throwable instanceof CygnusBusinessException) {
return Mono.error(throwable);
}
return Mono.error(
new CircuitBreakerOpenException(
throwable, new CygnusContext(), null, null, null));
});
})
///io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: localhost/127.0.0.1:9000 <-- Error prints here
.onErrorContinue((throwable, o) -> log.error(throwable.toString()))
.doOnError(throwable -> log.error("error from webclient:{}", throwable.toString()));
}
I fixed it by adding an onErrorContinue block and re-throwing the exception as a custom that gets handled in my circuit breaker code.
.onErrorContinue(
(throwable, o) -> {
log.info("throwable => {}", throwable.toString());
if (throwable instanceof ReadTimeoutException || throwable instanceof ConnectException) {
throw new CircuitBreakerOpenException();
}
})
I would make the following suggestions on your solution:
1- There is an alternate variation of onErrorContinue which accepts a predicate so you can define which exceptions this operator will be applied to - Docs
2- Return a Mono.error instead of throwing RuntimeExceptions from Mono/Flux operators. This other stackoverflow answer covers this quite well - Stackoverflow
3- Perform logging with side effect operators (doOn*)
.doOnError(throwable -> log.info("throwable => {}", throwable.toString()))
.onErrorResume(throwable -> throwable instanceof ReadTimeoutException || throwable instanceof ConnectException,
t -> Mono.error(new CircuitBreakerOpenException()))
Hope this is helpful.
def expectError() {
StepVerifier.create(readDB())
.expectError(RuntimeException.class)
.verify();
}
private Mono<String> readDB() {
// try {
return Mono.just(externalService.get())
.onErrorResume(throwable -> Mono.error(throwable));
// } catch (Exception e) {
// return Mono.error(e);
// }
}
unable to make it work if externalService.get throws Exception instead of return Mono.error. Is is always recommended to transform to Mono/Flow using try catch or is there any better way to verify such thrown exception?
Most of the time, if the user-provided code that throws an exception is provided as a lambda, exceptions can be translated to onError. But here you're directly throwing in the main thread, so that cannot happen
I am making synchronous api calls using RequestFuture provided by Volley library.
I need to handle error response when in case the status code is 4xx/500.
try {
JSONObject response = future.get();
} catch (InterruptedException e) {
// exception handling
} catch (ExecutionException e) {
// exception handling
}
Now the error is caught by ExecutionException catch clause. How do I get NetworkResponse from this error.
How to override onErrorListener in the catch clause.
Try this for grabbing the error from volley. Also just a note when preforming future requests you should use get with a timeout so your not waiting forever.
try
{
JSONObject response = future.get(30,TimeUnit.SECONDS);
}
catch(InterruptedException | ExecutionException ex)
{
//check to see if the throwable in an instance of the volley error
if(ex.getCause() instanceof VolleyError)
{
//grab the volley error from the throwable and cast it back
VolleyError volleyError = (VolleyError)ex.getCause();
//now just grab the network response like normal
NetworkResponse networkResponse = volleyError.networkResponse;
}
}
catch(TimeoutException te)
{
Log.e(TAG,"Timeout occurred when waiting for response");
}