How to make the ChannelOutboundHandler.write() method to be asynchronous? - java

My project uses the Java Netty framework to transfer messages. The application is both a client and a server. When we send a message to the remote server, we want to do some processing of this message. I use ChannelOutboundHandler.write() in my project to achieve this purpose:
public class MyOutBoundHandler extends ChannelOutboundHandlerAdapter{
#Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
process((ByteBuf) msg); // do some processing of this message
ctx.write(msg, promise);
}
}
I found a problem that when the process((ByteBuf) msg) method throws an exception, it will cause blocking, and the next method ctx.write(msg, promise) will not be executed. So how to make them asynchronous cause I hope the process((ByteBuf) msg) will not affect the writing of messages to the remote server.

If 'ctx.write(msg, promise)' does not rely on the result of the 'process((ByteBuf) msg)', you can just wrap the 'process((ByteBuf) msg)' in a runnable task and submit the task to the ThreadPool.

found a problem that when the process((ByteBuf) msg) method throws an exception, it will cause blocking, and the next method ctx.write(msg, promise) will not be executed
Unless you executing blocking code, netty will not block.
Behavior you are explaining is not blocking, it is just how control flow in java works. If an exception is thrown, none of the subsequent code will be executed unless you explicitly catch and resume.
Ideally, in your case, you want to add a try-catch block around the call to process() and if it fails, fail the promise using promise.tryFailure()

you can just add a listener for the ChannelPromise before process method was called。
refer to the following code here :
promise.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
Throwable cause = future.cause();
if (cause != null) {
//process happed exception will be here and you can call ctx.write(msg)
//to keep spreading forward the write event in the pipeline
ctx.write(msg);
} else {
// when msg has been write to socket successfully, netty will notify here!!
}
}
});
process(msg);

Related

How to throw an exception on another Thread in Java?

So I'm using ListenableFuture as a return type for certain operations. I expect the users to add callback to the future and then handle the success and exception cases. Now if the user cannot handle the exception, I want to have the ability to throw that exception onto the main Thread. Here's some code example:
public class SomeProcessor {
ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
public ListenableFuture<String> doStringProcessing() {
return executor.submit(() -> doWork());
}
private String doWork() {
return "stuff";
}
}
Then in a client class:
public class SomeConsumer {
public SomeConsumer (SomeProcessor processor) {
Futures.addCallback(processor.doStringProcessing(), new FutureCallback<String>() {
#Override
public void onSuccess(String result) {
// do something with result
}
#Override
public void onFailure(Throwable t) {
if (t instanceof ExceptionICanHandle) {
// great, deal with it
} else {
// HERE I want to throw on the Main thread, not on the executor's thread
// Assume somehow I can get a hold of the main thread object
mainThread.getUncaughtExceptionHandler().uncaughtException(mainThread, t);
// This above code seems wrong???
throw new RuntimeException("Won't work as this is not on the mainthread");
}
}
}, MoreExecutors.directionExecutor());
}
}
There is no direct way to do this.1
Hence, this question boils down to a combination of 2 simple things:
How do I communicate some data from a submitted task back to the code that is managing the pool itself? Which boils down to: How do I send data from one thread to another, and...
How do I throw an exception - which is trivial - throw x;.
In other words, you make the exception in your task, and do not throw it, instead, you store the object in a place the main thread can see it, and notify the main thread they need to go fetch it and throw it. Your main thread waits for this notification and upon receiving it, fetches it, and throws it.
A submitted task cannot simply 'ask' for its pool or the thread that manages it. However, that is easy enough to solve: Simply pass either the 'main thread' itself, or more likely some third object that serves as common communication line between them, to the task itself, so that task knows where to go.
Here is one simplistic approach based on the raw synchronization primitives baked into java itself:
public static void main(String[] args) {
// I am the main thread
// Fire up the executorservice here and submit tasks to it.
// then ordinarily you would let this thread end or sleep.
// instead...
ExecutorService service = ...;
AtomicReference<Throwable> err = new AtomicReference<>();
Runnable task = () -> doWork(err);
service.submit(task);
while (true) {
synchronized (err) {
Throwable t = err.get();
if (t != null) throw t;
err.wait();
}
}
}
public void doWork(AtomicReference<Throwable> envelope) {
try {
doActualWork();
catch (Throwable t) {
synchronized (envelope) {
envelope.set(t);
envelope.notifyAll();
}
}
}
There are many, many ways to send messages from one thread to another and the above is a rather finicky, primitive form. It'll do fine if you don't currently have any comms channels already available to you. But, if you already have e.g. a message queue service or the like you should probably use that instead here.
[1] Thread.stop(someThrowable) literally does this as per its own documentation. However, it doesn't work - it's not just deprecated, it has been axed entirely; calling it throws an UnsupportedOperationException on modern VMs (I think at this point 10 years worth of releases at least), and is marked deprecated with the rather ominous warning of This method is inherently unsafe. and a lot more to boot, it's not the right answer.

How to send multiple asynchronous requests in parallel using Unirest

While using Unirest, the program doesn't exit until we manually shutdown every thread by invoking Unirest.shutdown(). If I had to make just one request, it's easy:
private static void asyncRequest (String link) {
try {
Future <HttpResponse <JsonNode>> request = Unirest.head(link).asJsonAsync(
new Callback<JsonNode>() {
#Override
public void completed(HttpResponse<JsonNode> httpResponse) {
print(httpResponse.getHeaders());
try {
Unirest.shutdown();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void failed(UnirestException e) {
print(e.getMessage());
}
#Override
public void cancelled() {
print("Request cancelled");
}
}
);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
asyncRequest("https://entrepreneur.com");
}
But I have to make multiple HTTP request in parallel (subsequent requests are meant not to wait for previous requests to complete). In the code above, I have to execute the code inside asyncRequest more than once with different links. The problem is I can't decide when to invoke Unirest.shutdown() so that the program exits as soon as the last request receives response. If I call Unirest.shutdown() after all the calls to asyncRequest in main, some or all the requests might get interrupted. If I call it inside completed (and other overridden methods), only the first request is made and others are interrupted. How can I solve this?
In theory, you could make the current thread wait for the execution of the method and after they are all done you can call the shutdown. But this would make the whole process synchronous, which is not what we want. So what I would do is, run different thread (other than the main one) which will wait for the execution of all your http requests. To do so you can use the class CountDownLatch, initializing with the countdown before it releases the control to the parent thread. You pass the instance of the CountDownLatch to the async method and you decrease by one each time you complete an http request. When it reaches 0 it returns the control to the parent thread, where you know you can call shutdown method as all your requests are done.

Akka DeathWatch - Find reason for termination

Question: How can I find out if an actor was stopped gracefully (e.g. through its parent stopping) or through an exception?
Context: With the following deathwatch setup I only get the Terminated.class message in the good test, where I explicitly call stop. I expected a Terminated.class message only in the bad case. Using a supervisorStrategy that stops the child that threw an exception would make no difference, as this leads to the behaviour of the good test. And there I can't find a way to decide if it was caused by an exception or not.
My test setup is the following:
DeathWatch
public class DeathWatch extends AbstractActor {
#Override
public Receive createReceive() {
return receiveBuilder()
.matchAny(this::logTerminated)
.build();
}
private <P> void logTerminated(final P p) {
log.info("terminated: {}", p);
}
}
Actor
public class MyActor extends AbstractActor {
#Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("good", s -> { getContext().stop(self()); })
.matchEquals("bad", s -> { throw new Exception("baaaad"); })
.build();
}
}
Test
public class Test {
private TestActorRef<Actor> actor;
#Before
public void setUp() throws Exception {
actor = TestActorRef.create(ActorSystem.create(), Props.create(MyActor.class), "actor");
TestActorRef.create(ActorSystem.create(), Props.create(DeathWatch.class),"deathwatch").watch(actor);
}
#Test
public void good() throws Exception {
actor.tell("good", ActorRef.noSender());
}
#Test
public void bad() throws Exception {
actor.tell("bad", ActorRef.noSender());
}
}
Update: Adding the following supervisor, leads to a second logging of "terminated", but yields no further context information.
public class Supervisor extends AbstractActor {
private final ActorRef child;
#Override
public Receive createReceive() {
return receiveBuilder()
.match(String.class, s -> child.tell(s, getSelf()))
.build();
}
#Override
public SupervisorStrategy supervisorStrategy() {
return new OneForOneStrategy(DeciderBuilder.match(Exception.class, e -> stop()).build());
}
}
The Terminated message is behaving as expected. From the documentation:
In order to be notified when another actor terminates (i.e. stops permanently, not temporary failure and restart), an actor may register itself for reception of the Terminated message dispatched by the other actor upon termination.
And here:
Termination of an actor proceeds in two steps: first the actor suspends its mailbox processing and sends a stop command to all its children, then it keeps processing the internal termination notifications from its children until the last one is gone, finally terminating itself (invoking postStop, dumping mailbox, publishing Terminated on the DeathWatch, telling its supervisor)....
The postStop() hook is invoked after an actor is fully stopped.
The Terminated message isn't reserved for the scenario in which an actor is stopped due to an exception or error; it comes into play whenever an actor is stopped, including scenarios in which the actor is stopped "normally." Let's go through each scenario in your test case:
"Good" case without an explicit supervisor: MyActor stops itself, calls postStop (which isn't overridden, so nothing happens in postStop), and sends a Terminated message to the actor that's watching it (your DeathWatch actor).
"Good" case with an explict supervisor: same as 1.
"Bad" case without an explicit supervisor: The default supervision strategy is used, which is to restart the actor. A restart does not trigger the sending of a Terminated message.
"Bad" case with an explicit supervisor: the supervisor handles the Exception, then stops MyActor, again launching the termination chain described above, resulting in a Termination message sent to the watching actor.
So how does one distinguish between the "good" and "bad" cases when an actor is stopped? Look at the logs. The SupervisorStrategy, by default, logs Stop failures at the ERROR level.
When an exception is thrown, if you want to do more than log the exception, consider restarting the actor instead of stopping it. A restart, unlike a stop, always indicates that something went wrong (as mentioned earlier, a restart is the default strategy when an exception is thrown). You could place post-exception logic inside the preRestart or postRestart hook.
Note that when an exception is thrown while an actor is processing a message, that message is lost, as described here. If you want to do something with that message, you have to catch the exception.
If you have an actor that you want to inform whenever an exception is thrown, you can send a message to this monitor actor from within the parent's supervisor strategy (the parent of the actor that can throw an exception). This assumes that the parent actor has a reference to this monitor actor. If the strategy is declared inside the parent and not in the parent's companion object, then the body of the strategy has access to the actor in which the exception was thrown (via sender). ErrorMessage below is a made-up class:
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case t: Throwable =>
val problemActor = sender
monitorActor ! ErrorMessage(t, problemActor)
Stop
}

Netty - An exceptionCaught() event was fired, and it reached at the tail of the pipeline

I got this warning in my log:
Nov 02, 2016 12:07:20 AM io.netty.channel.DefaultChannelPipeline onUnhandledInboundException
WARNUNG: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
java.util.concurrent.TimeoutException
That are my ChannelHandlers:
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
ch.pipeline().addLast(new TimeoutHandler(TIME_OUT_SECONDS));
ch.pipeline().addLast(new ServerCommunicationHandler(messageHandler));
}
My TimeoutHandler throws a TimeoutException if there was no read for the last 15 seconds.
And in my last handler, the ServerCommunicationHandler, i have overridden the exeptionCaught function:
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
}
So if i understand right, i do not rethrow the exception to the end of the pipeline, because my last handler processes the exception correctly, doesn't it?
Why do i get this warning?
I use Netty 4.1.6.Final
This can be caused by your TimeoutHandler throwing the TimeoutException after the channel has been closed. It can happen because once a channel is closed all the ChannelHandlers will be removed (unregistered), but the ChannelHandlerContext still has a reference to the pipeline, so if you close a channel and then fire an event on the ctx there won't be any handlers to intercept the event.
I was able to recreate the error you see by writing a simple/broken TimeoutHandler:
#RequiredArgsConstructor
private static class TimeoutHandler extends ChannelInboundHandlerAdapter {
private final int timeoutSeconds;
#Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ctx.executor().schedule(
// This exception can still be fired once the channel is closed and all handlers removed
() -> ctx.fireExceptionCaught(new TimeoutException()),
timeoutSeconds, TimeUnit.SECONDS);
super.channelRegistered(ctx);
}
}
Have you considered using the Netty ReadTimeoutHandler instead of writing your own?
If you really want to write your own, make sure you are canceling your timer when the channel goes inactive. You can see how IdleStateHandler does this.
Maybe it's an exception thrown due to incomplete data received?
My project also encountered this problem recently, because the received data code is GBK and I use utf8, so when intercepting the data in the corresponding location, I threw a subscript out of range error and caused this error.
screenshot of error message which is neither English nor printable with ISO-8859 charset

How to catch all exception in netty

As far as I know netty handle exceptions by override method exceptionCaught(). But what I want is a Handler that can handler all exceptions in or out. So, the pipeline should be like :
InboundExceptionHandler - inboundHandler1 - inboundHandler2 - outboundHandler1 - outboundHandler2 - OutboundExceptionHandler
That means I should place 2 exception Handler in my pipeline separated in head and tail. But I think it looks ugly. any better idea?
You could just have a single inbound and outbound exception handler at the top/tail of your pipeline. If you want to catch all exceptions you could do something like this (I'm assuming this is Netty 4.0):
import io.netty.channel.*;
import java.net.SocketAddress;
public class ExceptionHandler extends ChannelDuplexHandler {
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Uncaught exceptions from inbound handlers will propagate up to this handler
}
#Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
ctx.connect(remoteAddress, localAddress, promise.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
if (!future.isSuccess()) {
// Handle connect exception here...
Throwable failureCause = future.cause();
}
}
}));
}
#Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
ctx.write(msg, promise.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
if (!future.isSuccess()) {
// Handle write exception here...
Throwable failureCause = future.cause();
}
}
}));
}
// ... override more outbound methods to handle their exceptions as well
}
Any exceptions that are thrown by inbound handlers will propagate "up" the pipeline and invoke this handler's exceptionCaught() method, assuming a handler below does not consume them.
For outbound operations like write() and connect(), you need to add a ChannelFutureListener to catch their exceptions. The exceptionCaught() method is only invoked for exceptions from inbound events like channelRead(), channelActive() etc.
With this handler at the "top" of the pipeline we can catch exceptions from all outbound handlers below. Say one of your outbound handlers is doing some encoding and this fails with an exception, this will be handled by our channel future listener that we added to the write() operation's promise.
If this exception handler was installed at the "bottom"/head of the pipeline like you initially suggested, then it would not see exceptions from handlers above it, because its write() method would never be invoked if the write failed in a previous handler. That's why this handler must be at the top.
To hopefully avoid confusion about top/bottom of the pipeline, here is how I would configure your example pipeline:
pipeline.addLast(outboundHandler2) // bottom
.addLast(outboundHandler1)
.addLast(inboundHandler2)
.addLast(inboundHandler1)
.addLast(new ExceptionHandler()); // top
The final solution is to customize the ChannelInitializer You can even add more logic

Categories

Resources