-1

I'm using Feign Client in Reactive Java. The Feign client has an interceptor that sends a blocking request to get auth token and adds it as a header to the feign request.

the feign request is wrapped in Mono.FromCallable with Schedulers.boundedElastic().

my question is: does the inner call to get the auth token considered as a blocking call?

I get that both calls will be on a different thread from Schedulers.boundedElastic() but not sure is ok to execute them on the same thread or I should change it so they'll run on different threads.

Feign client:

@FeignClient(name = "remoteRestClient", url = "${remote.url}",
        configuration = AuthConfiguration.class, decode404 = true)
@Profile({ "!test" })
public interface RemoteRestClient {
    @GetMapping(value = "/getSomeData" )
    Data getData();
}

interceptor:

public class ClientRequestInterceptor implements RequestInterceptor {

    private IAPRequestBuilder iapRequestBuilder;
    private String clientName;

    public ClientRequestInterceptor(String clientName, String serviceAccount, String jwtClientId) {
        this.iapRequestBuilder = new IAPRequestBuilder(serviceAccount, jwtClientId);
        this.clientName = clientName;
    }

    @Override
    public void apply(RequestTemplate template) {
        try {
            HttpRequest httpRequest = iapRequestBuilder.buildIapRequest();   <---- blocking call
            template.header(HttpHeaders.AUTHORIZATION, httpRequest.getHeaders().getAuthorization());
        } catch (IOException e) {
            log.error("Building an IAP request has failed: {}", e.getMessage(), e);
            throw new InterceptorException(String.format("failed to build IAP request for %s", clientName), e);
        }
    }
}

feign configuration:

public class AuthConfiguration {
    @Value("${serviceAccount}")
    private String serviceAccount;

    @Value("${jwtClientId}")
    private String jwtClientId;

    @Bean
    public ClientRequestInterceptor getClientRequestInterceptor() {
        return new ClientRequestInterceptor("Entitlement", serviceAccount, jwtClientId);
    }
}

and feign client call:

  private Mono<Data> getData() {
return Mono.fromCallable(() -> RemoteRestClient.getData()
                                .publishOn(Schedulers.boundedElastic());
    }
Lior Derei
  • 154
  • 12

1 Answers1

0

You can sort of tell that it is a blocking call since it returns a concrete class and not a Future (Mono or Flux). To be able to return a concrete class, the thread needs to wait until we have the response to return it.

So yes it is most likely a blocking call.

Reactor recommends that you use the subscribeOn operator when doing blocking calls, this will place that entire chain of operators on its own thread pool.

You have chosen to use the publishOn and it is worth pointing out the following from the docs:

affects where the subsequent operators execute

This in practice means that up until the publishOn operator all actions will be executed using any available anonymous thread.

But all calls after will be executed on the defined thread pool.

private Mono<Data> getData() {
    return Mono.fromCallable(() -> RemoteRestClient.getData()
                                .publishOn(Schedulers.boundedElastic());
}

You have chosen to place it after so the thread pool switch will be done after the call to getData.

publishOns placing in the chain matters while subscribeOn affects the entire chain of operator which means it's placing does not matter.

So to answer your question again, yes it is most likely a blocking call (i can't confirm by 100% since i have not looked into the source code) and how you wish to solve it with either publishOn on subscribeOn is up to you.

Or look into if there is an reactive alternative library to use.

Toerktumlare
  • 12,548
  • 3
  • 35
  • 54
  • Thank you for the detailed answer, very helpful! so I change the `publishOn` to `subscribeOn` the whole chain we'll run on a thread from a different thread pool, is that correct? even if I'll use `subscribeOn` wouldn't it still be a blocking call? not sure how would it solve this – Lior Derei Mar 21 '22 at 09:14
  • Yes that is correct, i suggest you read the docs i linked to. A blocking call is always a blocking call. You cant change that. By placing it on its own threadpool you dont ”disturb” or block the webflux nio-threadpool so that the rest of your application that is reactive wont slow down because of the blocking call slowing down the reactive thread pool. – Toerktumlare Mar 21 '22 at 15:00
  • I think I didn't explain myself well :) - even if I do use subscribeOn the inner call to get the auth token is still blocking and is not being wrapped by fromCallabale, or run on a different thread. The question is if it's an issue since a different thread will run both requsts (the feign and the auth). I guess if it is, the only way to solve it is to use a different lib like WebClient or ReactiveFeign no? – Lior Derei Mar 21 '22 at 15:12
  • no you are not clear, you assume i know how feign works, and i dont `even if I do use subscribeOn the inner call to get the auth token is still blocking and is not being wrapped by fromCallabale` this is not true if the `apply` function is called when getData is beeing called. The code snippet you have provided does not contain any part of the code that calls the function `apply` that contains the function you have marked as "blocking". When you do `RemoteRestClient.getData()` i have no idea what gets called in what order since you have not provided a stack trace. – Toerktumlare Mar 21 '22 at 16:18
  • but as i told you, in my answer, when calling `onSubscribe` the entire call, from the request until the response for that endpoint, will place that entire thing on its own thread. This is a fallback to have the reactive server to behave like a normal standard webserver. If you dont understand the answer im giving you, i suggest you read up on how reactor works since you seem to be lacking in the basic knowledge. – Toerktumlare Mar 21 '22 at 16:21
  • also, stack overflow is not a discussion forum its a Q&A, you asked i answered. If you dont understand the answer, then its very hard for me to help you. – Toerktumlare Mar 21 '22 at 16:22
  • i provided an answer, you have a choice to accept it or not. I pointed out that stack overflow is not a forum, lengthy discussions as above are to be avoided. This what is expected from you https://meta.stackoverflow.com/questions/261592/how-much-research-effort-is-expected-of-stack-overflow-users I took time out of my day to try to help you, and you keep asking more questions. Telling you to read the documentation is not rude or toxic. But the comment `you shouldn't answer questions you don't understand` is toxic and i have reported the above comment. I am leaving now have a nice day. – Toerktumlare Mar 22 '22 at 08:17