Recently i am reading the source code in OKHTTP and found that in RetryAndFollowUpInterceptor.java has a while(true) recycle. But, the program dose not stacked in that place or occur 'Application not responding'.Then I attached the debugger and put a breakpoint in that place and found it is running in main thread.I dont know why the program runs normal at all.Who can help me?
Thanks to stephenCs answer, but you might not get my point.In my question I mean why does the program not stuck in while(true) recycle in main thread.
For example, if there is a while(true) recycle in activity`s onCreate() lifecycle, the app might not run correctly ,just stuck in that place and can not respond touch event, which means the application not responding(ANR).
How will the recycle exit?
The following is the source code:
#Override
public Response intercept(Chain chain) throws IOException {
...
//This is the start of the recycle!Just recycle forever
while (true) {
if (canceled) {
streamAllocation.release();
throw new IOException("Canceled");
}
Response response;
boolean releaseConnection = true;
try {
response = realChain.proceed(request, streamAllocation, null, null);
releaseConnection = false;
} catch (RouteException e) {
// The attempt to connect via a route failed. The request will not have been sent.
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getFirstConnectException();
}
releaseConnection = false;
continue;
} catch (IOException e) {
// An attempt to communicate with a server failed. The request may have been sent.
boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
if (!recover(e, streamAllocation, requestSendStarted, request)) throw e;
releaseConnection = false;
continue;
} finally {
// We're throwing an unchecked exception. Release any resources.
if (releaseConnection) {
streamAllocation.streamFailed(null);
streamAllocation.release();
}
...
}
}
}
If you are talking about this:
if (!recover(e.getLastConnectException(), streamAllocation, false, request)) {
throw e.getFirstConnectException();
}
releaseConnection = false;
continue;
or the similar code for IOException, the recover(...) call tests to see if failed request is recoverable. (See the method for what that actually means. But one of the criteria is that there is an alternative route that has not been tried yet.) If the call returns true, then the intercept method will retry. If it returns false then the relevant exception is rethrown.
In general, the logic in class is complicated, but it needs to be. And it clearly does work. So maybe you just need to read / study more of the context to understand what is happening.
Note that using a debugger to trace this could be difficult because the behavior is liable to change due to timeouts happening differently and altering the normal flow of control. Consider that possibility ...
UPDATE
How will the recycle exit?
It is called a loop, not a recycle.
As I have explained above, the particular loop paths that you highlighted will terminate (by rethrowing an exception) if the recover(...) call returns false. Let us take a look at recover.
private boolean recover(IOException e, boolean routeException, Request userRequest) {
streamAllocation.streamFailed(e);
// The application layer has forbidden retries.
if (!client.retryOnConnectionFailure()) return false;
// We can't send the request body again.
if (!routeException && userRequest.body() instanceof UnrepeatableRequestBody) return false;
// This exception is fatal.
if (!isRecoverable(e, routeException)) return false;
// No more routes to attempt.
if (!streamAllocation.hasMoreRoutes()) return false;
// For failure recovery, use the same route selector with a new connection.
return true;
}
The first statement is doing some cleanup on the StreamAllocation object. It isn't relevant to this.
The rest of the method is testing various things:
The application layers forbids retries, it says don't retry.
If a request was sent and it is not a resendable request, it says don't retry.
If the exception indicates an error that won't be fixed by retrying, it says don't retry.
If the route selector has no more routes to try, it says don't retry.
Otherwise it says retry. Next time that proceed is called, it will try the next route from the route selector.
Note that eventually it will run out of alternative routes to try.
Related
I'm trying to get into CompletableFuture class for a project I'm running, and I got to some question here:
There is the following method: it tries to find a conversation by its ID or hash; and, if not found, it throws an exception. So far, so good.
public ConversationOutput getConversationByIdOrHash(String conversationIdOrHash)
throws ConversationNotFoundException {
Conversation conversation = this.conversationRepository.getByIdOrHash(conversationIdOrHash);
if (conversation == null) {
throw new ConversationNotFoundException(conversationIdOrHash);
}
return this.modelMapper.map(conversation, ConversationOutput.class);
}
Note that I am throwing ConversationNotFoundException from my method signature. My SpringBoot controller is reacting to this exception and it's all working fine since the beginning.
What I'm trying to do is to make this to a CompletableFuture return and actually throwing an exception, something similar to:
public CompletableFuture<ConversationOutput> getConversationByIdOrHashAsync(String conversationIdOrHash)
throws ConversationNotFoundException {
return CompletableFuture.supplyAsync(() -> this.getConversationByIdOrHash(conversationIdOrHash));
}
I've seen posts where people use exceptionally to handle exceptions, but what I really want to do is to throw it to my controller and let it handle it. Any suggestions of how can I make it?
Thank you all!
The question is do you care about the result of CompletableFuture.
CompletableFuture is like a special task and it is processed on other thread. If you don't invoke .join() you won't receive the results of CompletableFuture. This method also will propagate the exception if any occured. However it waits for CompletableFuture to finish and blocks the request.
However, there is no way to get exceptions from the inside of the CompletableFuture without waiting, you have to treat it like other task.
You can pass the completed future in case of a success, and failed future along with your custom exception.
public CompletableFuture<ConversationOutput> getConversationByIdOrHashAsync(String conversationIdOrHash) {
try {
return CompletableFuture.completedFuture(this.getConversationByIdOrHash(conversationIdOrHash));
} catch (ConversationNotFoundException e) {
return CompletableFuture.failedFuture(e);
}
}
and then at your controller level you can handle the exception.
final CompletableFuture<ConversationOutput> future = getConversationByIdOrHashAsync("idOrHash");
future.whenComplete((r, e) -> {
if (e != null) {
if (e instanceof ConversationNotFoundException) {
//handling
}
}
});
Whenever our code throws an exception that says com.netflix.hystrix.exception.HystrixRuntimeException: MyClass timed-out and fallback failed., we always have to second guess ourselves as to whether or not the problem is that Hystrix is configured correctly. An easy way we could answer that question would be if the log said how long it took for the Hystrix thread to run before it errored. That way, if it says 1000ms, we know that Hystrix is not configured correctly (because that's the default) and if it says 5000ms, we know that it is configured the way we want.
you can do something like this:
command.getExecutionTimeInMilliseconds()
this method tells you how many seconds your run method took, to capture in a timeout you can wrap this in a try catch inside your command class
#Override
protected HttpResponse run() throws Exception {
try {
return executeSomeCall();
} catch (Exception e) {
System.out.println(getExecutionTimeInMilliseconds());
throw e;
}
}
or outside it on the invocation
try {
HttpResponse response = command.execute();
return response;
}
catch (HystrixRuntimeException e) {
command.getExecutionTimeInMilliseconds() // do something with the info
}
just one point, if you use Threads strategy its ok, but if you use Semaphor the timeout will only happen after you get a response. even that your timeout is 1000 ms and the request took 5000 ms. hystryx will wait 5000 ms to give you a timeout error and the getExecutionTimeInMilliseconds will return 1000 ms to you.
I have all the logic in place to check if a user is logged in and therefore is allowed to execute a RPC call. I can place this check in all my RPC methods and throw a throw new MyLoginException() if the user is not logged in. In the client side I just check for this exception and handle it accordingly.
The problem now is: I don't want to put this in every RPC method. I want a generic solution for this.
I tried to override methods like com.google.gwt.user.server.rpc.RemoteServiceServlet.processCall() which is called before every RPC call. Problem here: I can only throw unchecked exception because the method signature does not allow me to throw my own exception. The unchecked exception result then in generic exception in the client code and I can not handle them correctly because I do not know anymore which exception was thrown.
Where can I put my login check? Is there any clean solution for this problem?
I found the solution in the link posted by Baz: https://groups.google.com/forum/#!topic/google-web-toolkit/D2MPaD2EpOQ
Instead of throwing the exception like this (Which is not possible since the signature only allows SerializationException :
#Override
public String processCall(String payload) throws SerializationException {
if(!userIsAuthorized()){
throw new MyAuthorizationException();
}
return super.processCall(payload);
}
The exception has to be returned encoded in the response like this:
#Override
public String processCall(String payload) throws SerializationException {
if(!userIsAuthorized()){
return RPC.encodeResponseForFailure(null, new MyAuthorizationException());
}
return super.processCall(payload);
}
This will be called every time a RPC call is invoked and the method will not be executed if the user is not authorized and the checked exception will be transported to the front end where it can be handled accordingly.
Edit #Baz: This is how the client handling looks like for me. But I think if you get a IncompatibleRemoteServiceException you have a problem that some old stuff is cached in your browser und you need to clean your cache.
#Override
public void onFailure(Throwable caught) {
try {
throw caught;
}
catch (SessionExpiredException ex){
onSessionExpired();
}catch(Throwable e){
MyCallback.this.onFailure(e);
}
}
I have a Java web app A that sends Pojo's to anothet web app B. I want to create a mechanism that retries the sending of the Pojos from A to B if B is down. The way B's api is configured in A is via Spring with B's jars in A's classpath. I have made a stab at it but the retry is not working. Can anyone tell me if I'm going in the right direction?
What is wrong here is that my code first executes, then rightly throws an exception. The exception is caught in the catch block. The catch block re-calls callB() which rightly throws an exception again but then my retry code goes into non source-file exception handling code and stops.
EDIT: I cannot use jcabi-aspects because it is not supported in Gradle.
public Object getB(Object request){
if (bClient != null) {
int retryCount = 0;
try {
return callB(request);
} catch (Exception e) {
if (++retryCount > 3) {
return null;
}
return callB(request);
}
}
return null;
}
private Object callB(Object request){
return bClient.callServiceB(request);
}
As you want code to repeatedly do something until it is successful, consider writing the control logic as a loop. You will find this makes it easy for you to change the number of retries before the code should give up.
I am using Netty 3.6.6.Final and trying to implement write timeout for my handler such that on timeout I need to write specific response. In addition I need to cancel another write response which is currently executing in the pipeline (or will be executing).
Here is my current pipeline:
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(LOGGER,
new HttpServerCodec(),
new MyHttpContentDecoder(),
new IdleStateHandler(timer, 0, 1000, 0, TimeUnit.MILLISECONDS),
handler.get());
}
});
Handler extends IdleStateAwareChannelHandler and implementing channelIdle method where I check for write timeout:
if (e.getState() == IdleState.WRITER_IDLE) {
e.getChannel().write(SOME_RESPONSE).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
future.getChannel().close();
}});
}
The question is how do I cancel write which I have planned in messageReceived method in case no timeout occurs. Is there something customary in Netty to deal with such a problem?
EDIT
Cancelling via ChannelFuture does not work. As far as I understand most of the time write will not be cancelled. During my tests it was all the time, i.e. cancel() always returned false. So I guess it is really hard to achieve it this way.
In the end I have updated the code to the latest release - 4.0.9.Final (much nicer API).
And all of the sudden, I received responses as a result of the write timeout. That didn't work this way in 3.6.6.Final.
In 4.0.9.Final the code for handling write timeout is a bit different but I always get a second write on timeout (if I comment ctx.writeAndFlush below, then I am getting write from channelRead0):
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent e = (IdleStateEvent) evt;
if (e.state() == IdleState.WRITER_IDLE) {
//the following condition was always false
//(channelFuture is a state variable of my handler for previous write)
if (channelFuture != null && channelFuture.isCancellable()) {
System.out.println("Cancel "+channelFuture.cancel(true));
}
ctx.writeAndFlush(SOME_RESPONSE);
}
}
}
Don't know if it is the right way to "overwrite" first write attempt when timeout occurs, and would be glad if someone can explain why it works and what was changed in the latest release regarding this scenario.
when message is flushed, the promise is set to be uncancellable. Once a message is write out one byte, it must be write completely so that decoder can deal with a Stream-based Transport
You can try to Channel the ChannelFuture returned by the write operation.