How to catch all exception in netty - java

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

Related

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

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);

what is the different between channelInactive and channe.closeFuture().addListener() in Netty

public class ChannelActiveHandler extends SimpleChannelInboundHandler {
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel open");
// add closeListener
ctx.channel().closeFuture().addListener(future->{
// do somthing when channel is close!
System.out.println("channel close! state:"+ctx.channel().isActive());
});
super.channelActive(ctx);
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// do somthing when channel is close!
System.out.println("channel inactive!");
super.channelInactive(ctx);
}
}
As above, what is the different of channelInactive() and channel.closeFuture().addListener() in Netty. Two method would be called when then channel is close.
Can both methods achieve the same effect?
In your use-case these are no different. That said in channelInactive(...) you can also delay firing the event to the next handler in the pipeline. Usually if you using channelActive in a handler you should also just make use of the other methods if needed.

Is it possible to catch netty exception in Camel?

It seems to me that netty has its own exception handlers and they don't propagate exceptions (ie. IOException) back to camel route. Is there any way to know that client has disconnected?
Answering my own question.
My problem was releasing clients that would just wait forever to get some kind of response from netty mostly in case of connections closed by remote hosts during processing the pipeline.
What needs to be done is to add a custom handler to the pipeline that should extend ChannelDuplexHandler and override connect and write methods or SimpleChannelInboundHandler and override channelInactive. I used ChannelDuplexHandler.
public class ExceptionHandler extends ChannelDuplexHandler {
private final NettyProducer producer;
#Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise)
throws Exception {
ctx.connect(remoteAddress, localAddress, promise)
.addListener((future -> {
if (!future.isSuccess()) {
// no need to do anything here, camel will manage it on its own
}
}));
}
#Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
ctx.write(msg, promise).addListener(future -> {
if (!future.isSuccess()) {
reportStatusBackToCamel(ctx);
}
});
}
private void reportStatusBackToCamel(ChannelHandlerContext ctx) {
NettyCamelState nettyCamelState = producer.getCorrelationManager().getState(ctx, ctx.channel(),
new IOException());
Exchange exchange = nettyCamelState.getExchange();
AsyncCallback callback = nettyCamelState.getCallback();
exchange.setException(new RuntimeException("Client disconnected"));
callback.done(false);
}
}
In case of SimpleChannelInboundHandler just put exchange handling into channelInactive method.
In your ClientInitializerFactory in initChannel you add this handler to the pipeline:
pipeline.addLast(new ExceptionHandler(producer));
producer is given to you on application startup. If you need additional spring injected beans as I did, you simply end up having a couple of constructors in your factory class, one #Autowired (with your injected fields) calling the other setting additional producer field.

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

Multiple ChannelInboundHandlerAdapter's in Netty pipleline

I'm quite new with netty, I want to create a TCP server which does a custom application layer handshaking when a connection is to be instantiated. After the handshaking I want to pass the messages (ByteBuf) to a queue so that they could be processed by some other threads.
My question is, can I have multiple ChannelInboundHandlerAdapter's in the channel pipeline? one for the application layer handshaking protocol and the other one for passing the message to the queue. Furthermore I want to know how the messages flow through the pipeline. If a message is received at one handler (or decoder/encoder) how is it passed to another handler.
Specifically, if I change the EchoServer from here and add another ChannelInboundHandlerAdapter, the echo server handler would stop receiving any messages.
ServerBootstrap b = new ServerBootstrap();
b.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
#Override
public void channelRead(ChannelHandlerContext ctx,
Object msg) {
}
});
ch.pipeline().addLast(
new EchoServerHandler());
}
});
My logic is: have 2 ChannelInboundHandlerAdapter's then do the handshaking with the first handler and discard packets if they do not match the handshaking criteria, and then pass the messages to a queue through the second ChannelInboundHandlerAdapter. Is my logic correct? If not how should it be?
Thank you very much.
ChannelInboundHandlerAdapter is an adapter class to the ChannelInBoundHandler interface. For beginning you can use SimpleChannelInboundHandler (or more complicated you can extend the adapter class writing your own handler that extends ChannelInboundHandlerAdapter ).
The SimpleCHannelInboundHandler releases the message automatically after channelRead() (and thereby passes it to the next handler in the ChannelPipeline).
For using the easier SimpleChannelInboundHandler see this thread Netty hello world example not working
So instead of this ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {}
you have to write a new class that extends SimpleChannelInboundHandler like
public class MyHandler extends SimpleChannelInboundHandler{
#Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
System.out.println(in.toString(io.netty.util.CharsetUtil.US_ASCII));
} finally {
in.release();
}
}
}
and invoke it like
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new MyHandler());
}
As said above the SimpleCHannelInboundHandler releases the message automatically after channelRead() (and thereby passes it to the next handler in the ChannelPipeline).
If you use ChannelInboundHandlerAdapter you have to implement the passing of the message/event to the next handler yourself
A handler has to invoke the event propagation methods in ChannelHandlerContext ctx to forward an event to its next handler. (in the SimpleChannelInboundHandler class this is implemented yet)
public class MyInboundHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("Connected!");
ctx.fireChannelActive();
}
}
See this http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html
I must remind that:
Only One SimpleChannelInboundHandler extention can be add to the pipeline chain.
Because SimpleChannelInboundHandler have a finally code block will release all the msg.
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (acceptInboundMessage(msg)) {
#SuppressWarnings("unchecked")
I imsg = (I) msg;
channelRead0(ctx, imsg);
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (autoRelease && release) {
//release all handled messages,so the next handler won't be executed
ReferenceCountUtil.release(msg);**
}
}
}
Use ChannelInboundHandlerAdapter instead:
public class CustomizeChannelInboundHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("do something you like!")
super.channelRead(ctx, msg);
}
}

Categories

Resources