How to correctly simulate latency with Spring WebClient - spring-boot

I want to add code that would simulate latency in my WebClient calls so I could ensure my timeouts/retries/etc are working correctly.
Since WebClient is reactive and uses a thread pool, it seems like Thread.sleep would block the thread in a way that WebClient wouldn't typically be blocked in real usage.
Is there a better way to simulate that latency?
(Inspired by https://github.com/fletchgqc/chaos-monkey-spring-boot/pull/2/files#diff-7f7c533cc2b344aa04848a17d0eff0cda404a5ab3cc55a47bba9ed019fba82e3R9
public class LatencyInducingRequestInterceptor implements ClientHttpRequestInterceptor {
public ClientHttpResponse intercept(
HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// do nothing
}
return response;
}
}

The answer is to use delayElement (The code I had posted above was for RestTemplate, that explains why Thread.sleep was used.
ExchangeFilterFunction latencyAddingFilterFunction =
(clientRequest, nextFilter) -> {
return nextFilter.exchange(clientRequest).delayElement(Duration.ofSeconds(2));
};

Related

Call Spring MicroService from gwt

I have a GWT client which needs to call a Spring Boot MicroService. I think it can be similar to calling a rest web service, but is there any better way to do this ?
You can probably use RequestBuilder to call your API from the client side of your GWT app:
import com.google.gwt.http.client.RequestBuilder;
// ....
try {
new RequestBuilder(
RequestBuilder.GET, // GET, POST, etc.
url // url of your microservice endpoint
).sendRequest(null, new RequestCallback() { // replace null with your req body if needed
#Override
public void onResponseReceived(Request req, Response resp) {
// Parse resp.getText() which is hopefully a JSON string
}
#Override
public void onError(Request res, Throwable throwable) {
// handle errors
}
});
} catch (RequestException e) {
// log, rethrow... the usual
}

Jersey AsyncResponse and Response

I'd like to return a temporaryRedirect, using AsyncResponse.
The following "works" (in that there is no error), but doesn't seem to be asynchronous (it processes one request at a time).
#GET
public Response doLoad(#Suspended final AsyncResponse asyncResponse,
#QueryParam("path") String path) {
Response response = veryExpensiveOperation(path);
asyncResponse.resume(response);
return response;
}
private Response veryExpensiveOperation(String path) {
// veryExpensiveOperation
}
Should this work? If I explicitly need to start a new Thread like at https://jersey.github.io/documentation/latest/async.html#d0e9895, what does returning the Response look like?
I do not think you are using async context properly. From what I see, you are blocking the request processing thread. You are not supposed to block this thread in vertx. How about doing as follows :
#GET
public void doLoad(#Suspended final AsyncResponse asyncResponse,
#QueryParam("path") String path) {
CompletableFuture<Response> future = veryExpensiveOperation(path);
future.thenAccept(resp -> asyncResponse.resume(resp));
}
private CompletableFuture<Response> veryExpensiveOperation(String path){
CompletableFuture<Response> completableFuture = new CompletableFuture<>();
new Thread(() -> {
//do expensive stuff here
completableFuture.complete(Response.ok().entity("Completed").build());
}).start();
return completableFuture;
}

Why is my RestTemplate ClientHttpRequestInterceptor not called?

I want to use interceptor to add authorization header to every request made via rest template. I am doing it like this:
public FirebaseCloudMessagingRestTemplate(#Autowired RestTemplateBuilder builder, #Value("fcm.server-key") String serverKey) {
builder.additionalInterceptors(new ClientHttpRequestInterceptor() {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().add("Authorization", "key=" + serverKey);
System.out.println(request.getHeaders());
return execution.execute(request, body);
}
});
this.restTemplate = builder.build();
}
However when I do this
DownstreamHttpMessageResponse response = restTemplate.postForObject(SEND_ENDPOINT, request, DownstreamHttpMessageResponse.class);
Interceptor is not called (Iv put breakpoint in it and it did not fire). Request is made and obvious missing auth key response is returned. Why is my interceptor not called?
Ok I know whats happening. After checking build() implementation I discovered that RestTemplateBuilder is not changing self state when calling additionalInterceptors but returns a new builder with given interceptors. Chaining calls solves the issue.
public FirebaseCloudMessagingRestTemplate(final #Autowired RestTemplateBuilder builder, final #Value("${fcm.server-key}") String serverKey) {
this.restTemplate = builder.additionalInterceptors((request, body, execution) -> {
request.getHeaders().add("Authorization", "key=" + serverKey);
log.debug("Adding authorization header");
return execution.execute(request, body);
}).build();
}

How to implement receiving a server push in OkHttp?

Following the question Does OkHttp support HTTP/2 server push?, are there any examples available on how receiving pushed content on the client side could be implemented?
How will the interaction of OkHttpClient, Request, Response and Http2Connection be? I understand that the Http2Connection has a PushObserver, but how will it play together with OkHttpClient and Request/Response?
Consider the snippet below. There is a client and a request. How would they come together with the PushObserver?
OkHttpClient client = getOkHttpClient();
Request request = new Request.Builder()
.url("https://nghttp2.org:443") // The Http2Server should be running here.
.build();
try {
Socket socket = client.socketFactory().createSocket();
Http2Connection con = new Http2Connection.Builder(true)
.socket(socket)
.pushObserver(new PushObserver(){
#Override
public boolean onRequest(int streamId, List<Header> requestHeaders) {
// do something here
return true;
}
#Override
public boolean onHeaders(int streamId,
List<Header> responseHeaders, boolean last) {
// do something here
return true;
}
#Override
public boolean onData(int streamId, BufferedSource source,
int byteCount, boolean last) throws IOException {
// do something here
return true;
}
#Override
public void onReset(int streamId, ErrorCode errorCode) {
// do something
}
}).build();
} catch (IOException e) {
LOG.error("IOException", e);
}
OkHttp has no public APIs for server push and it is unlikely to gain them. We’re building mechanisms to persist pushed responses into the cache, but it’s unlikely this will be visible to application code. You just get a faster response sometimes because the server pushed it into the cache.
If you need this kind of behavior please look at web sockets.

Closing connection in GET request using Jersey Client 2.22.1

I am using Jersey client for REST calls from Java code:
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.22.1</version>
</dependency>
In my GET request,
javax.ws.rs.client.Invocation.Builder builder = ClientBuilder.newClient().target(url).request();
builder.get().readEntity(String.class);
the client will be closed automatically after calling readEntity(String.class).
If I use,
builder.get(String.class);
I get the same output.
Is the connection closed automatically or do I need to close it manually in this case?
Short answer
Consider the following code:
Client client = ClientBuilder.newClient();
String result = client.target(url).request().get(String.class);
Under the hood, Jersey invokes Response#readEntity(Class<T>) if the request has succeeded and the connection will be closed for you. So the connection doesn't need to be closed manually in this situation.
Now consider the following code:
Client client = ClientBuilder.newClient();
Response response = client.target(url).request().get();
For this situation, you need to invoke Response#close() to close the connection. Or invoke Response#readEntity(Class<T>) to make Jersey close the connection for you.
Long answer
As stated in the documentation, if you don't read the entity, then you need to close the response manually by invoking Response#close().
For more details, have a look at Jersey's documentation about how to close connections:
5.7. Closing connections
The underlying connections are opened for each request and closed
after the response is received and entity is processed (entity is
read). See the following example:
final WebTarget target = ... some web target
Response response = target.path("resource").request().get();
System.out.println("Connection is still open.");
System.out.println("string response: " + response.readEntity(String.class));
System.out.println("Now the connection is closed.");
If you don't read the entity, then you need to close the response
manually by response.close().
Also if the entity is read into an
InputStream (by response.readEntity(InputStream.class)), the
connection stays open until you finish reading from the InputStream.
In that case, the InputStream or the Response should be closed
manually at the end of reading from InputStream.
Additionally, have a look at JerseyInvocation source. The most important parts are quoted below.
In the translate(ClientResponse, RequestScope, Class<T>) method you'll see that response.readEntity(Class<T>) is invoked.
JerseyInvocation.Builder#get(Class<T>)
Invoke HTTP GET method for the current request synchronously.
#Override
public <T> T get(final Class<T> responseType)
throws ProcessingException, WebApplicationException {
return method("GET", responseType);
}
JerseyInvocation.Builder#method(String, Class<T>)
Invoke an arbitrary method for the current request synchronously.
#Override
public <T> T method(final String name, final Class<T> responseType)
throws ProcessingException, WebApplicationException {
// responseType null check omitted for brevity
requestContext.setMethod(name);
return new JerseyInvocation(this).invoke(responseType);
}
JerseyInvocation#invoke(Class<T>)
Synchronously invoke the request and receive a response of the specified type back.
#Override
public <T> T invoke(final Class<T> responseType)
throws ProcessingException, WebApplicationException {
// responseType null check omitted for brevity
final ClientRuntime runtime = request().getClientRuntime();
final RequestScope requestScope = runtime.getRequestScope();
return requestScope.runInScope(new Producer<T>() {
#Override
public T call() throws ProcessingException {
try {
return translate(runtime.invoke(requestForCall(requestContext)),
requestScope, responseType);
} catch (final ProcessingException ex) {
// Exception handling omitted for brevity
}
}
});
}
JerseyInvocation#translate(ClientResponse, RequestScope, Class<T>)
If the request suceeded, the response entity is read as an instance of specified Java type using Response#readEntity(Class<T>):
private <T> T translate(final ClientResponse response, final RequestScope scope,
final Class<T> responseType) throws ProcessingException {
if (responseType == Response.class) {
return responseType.cast(new InboundJaxrsResponse(response, scope));
}
if (response.getStatusInfo().getFamily() == Response.Status.Family.SUCCESSFUL) {
try {
return response.readEntity(responseType);
} catch (final ProcessingException ex) {
// Exception handling omitted for brevity
}
} else {
throw convertToException(new InboundJaxrsResponse(response, scope));
}
}

Resources