Does anyone know why channelReadComplete gets called before channelRead in some situations? I have a server that is pretty dumb right now that another netty server connects to. The first initializer does this:
final EventExecutorGroup taskGroup = new DefaultEventExecutorGroup(50);
ChannelPipeline pipeline = ctx.pipeline();
pipeline.addLast(taskGroup, "ServerHandler", new ServerHandler());
And then in ServerHandler
public class ServerHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
System.out.println("channelRead");
// ctx.write(msg);
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
System.out.println("channelReadComplete");
// ctx.flush();
}
I consistently get channelReadComplete before channelRead when the first netty server connects to the second netty server. Is there a reason for this?
Related
This is a simple question. Netty seems to be a great tool for sending information between servers and clients. I want to send informations, and also events..or rather called instructions what do to.
public class PojoServerHandler extends ChannelInboundHandlerAdapter {
private Logger logger = LoggerFactory.getLogger(getClass());
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Message body = (Message) msg;
logger.info("server read msg id:{}, body:{}", body.getId(), body.getBody());
Message response = new Message();
response.setId(1024);
response.setFrom("server");
response.setBody("hello from server");
ctx.writeAndFlush(response);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.error("server caught exception", cause);
ctx.close();
}
}
I have this code here and it describes a handler for a server. When the client send its message to the server. The method channelRead going to be called directly.
Is it smart to implements a switch case inside channelRead if I want to use other functions around my application, or can Netty do that in another way?
It is possible to have multiple handlers each doing a different task instead of having a switch case. You can simply call
ctx.fireChannelRead(msg);
if you plan not to handle the message in a particular handler, then it will trigger the next handler in the pipeline.
I am writing a client using netty for handling custom protocol. I have defined a handler which extends SimpleChannelInboundHandler which handles both sending and receiving message.
public class ClientHandler extends SimpleChannelInboundHandler {
#Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
log.info("Client received: " + ((ByteBuf)o).toString(CharsetUtil.UTF_8));
System.out.println("Client received: " + ((ByteBuf)o).toString(CharsetUtil.UTF_8));
}
#Override
public void channelActive(ChannelHandlerContext channelHandlerContext){
log.info("Client sent: $"+ new MessageRequest().toString() +"$");
channelHandlerContext.writeAndFlush(Unpooled.copiedBuffer((new MessageRequest().toString()), CharsetUtil.UTF_8));
}
#Override
public void exceptionCaught(ChannelHandlerContext channelHandlerContext, Throwable cause){
cause.printStackTrace();
channelHandlerContext.close();
}
}
This handler is able to print the response to the console. But since I am writing an client which will be used by another service, I need to send the response to the service which is calling my client.
Kindly help me in sending the response received to the calling service.
You can store the reference of your listening service in your ClientHandler class and call a setMessage method of the service class to give it the message from the channelRead0 method of your handler.
A better approach would be to use the Observer pattern
My I/O flow is following:
client sends data#1 to channel
server(handler) receives data from database according the client data and sends it to the client
client sends data#2 to channel
server(handler) receives data again from database according the client data and sends it back to the client and closes channel
If first read in channel takes too long, ReadTimeoutHandler fires exception as expected. But if first read is ok (= fast enough) and second read in channels takes too long, no TimeoutException is thrown and handler waits 5 minutes until it closes channel. It seems that ReadTimeoutHandler only works for the first read in channel. Is it even possible to get ReadTimeoutHandler work with multiple reads in the channel?
Used Netty version: 4.0.12
public class MyServer {
private static final class MyInitializer extends ChannelInitializer<SocketChannel> {
...
#Override
public void initChannel(SocketChannel channel) throws Exception {
channel.pipeline().addLast(
new ReadTimeoutHandler(5, TimeUnit.SECONDS),
new MyHandler(server, serverConnection));
}
...
}
}
public class MyHandler extends SimpleChannelInboundHandler {
private static final Logger LOG = LoggerFactory.getLogger(MyHandler.class);
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
Message message = database.getMessage(msg);
ChannelFuture operation = ctx.writeAndFlush(message)
if (message.isEnd()) operation.addListener(new CloseConverstationListener(ctx));
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (cause instanceof ReadTimeoutException) {
LOG.error("ReadTimeoutException");
}
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
}
private class CloseConverstationListener implements GenericFutureListener<ChannelFuture> {
private final ChannelHandlerContext ctx;
private CloseConverstationListener(ChannelHandlerContext ctx) {
this.ctx = ctx;
}
#Override
public void operationComplete(ChannelFuture future) throws Exception {
future.channel().close().sync();
}
}
}
The ReadTimeoutHandler behavior is - If no read happens in a channel for the specified duration it will fire exception and close the channel. It is not for the delay in responding or processing a read.
At the beginning of channel read read flag is set to true and that is set to false when when read is complete. A scheduler runs which checks if the channel is open and has not read for the specified duration then it fires the exception and closes the connection.
If first read in channel takes too long, ReadTimeoutHandler fires exception as expected.
The above does not sound correct to me.
If you want to timeout based on delay in writing a response, the you might consider WriteTimeoutHandler.
I have a very basic question. I have just started with vertx Framework. There is one thing I am not able to understand like
vertx.createNetServer().connectHandler(new Handler<NetSocket>() {
#Override
public void handle(final NetSocket socket) {
socket.dataHandler(new Handler<Buffer>() {
public void handle(Buffer buffer) {
/* I can access the socket object inside this handler */
socket.write("Hello");
}
});
}
}).listen(1234);
but if I write the handler function separately like this
Handler<Message> socketHandler = new Handler<Buffer>() {
public void handle(Buffer buffer) {
/* How can i access the socket object now */
socket.write("Hello"); //this will give error as it does not recognize the socket object
}
};
vertx.createNetServer().connectHandler(new Handler<NetSocket>() {
#Override
public void handle(final NetSocket socket) {
socket.dataHandler(socketHandler);//now I write handler separately
}
}).listen(1234);
Handler can be anywhere in the same java class or other java class. Please help me me how I can write handler separately and still access the socket object.
In your second example, you're not passing socket to socketHandler, so you cannot expect socketHandler to know about it.
You could solve this by creating a method like createSocketHandler(final Socket socket) that returns a Handler<Message> and that replaces your socketHandler variable.
To learn more, and to find out why you need socket to be final, look up "anonymous inner classes"
I want to send more than one response to client based on back end process. But in Netty examples I saw echo server is sending back the response at the same time.
My requirement is, I need to validate the client and send him OK response, then send him the DB updates when available.
How can I send more responses to client? Pls direct me to an example or any guide?
at every point in your pipeline you can get the pipeline Channel object from the MessageEvent object (or ChannelEvent) which is passed from handler to handler. you can use this information to send multiple responses at different points in the pipeline.
if we take the echo server example as a base, we can add a handler which send the echo again (that can be done also in the same handler, but the example is to show that multiple handlers can respond).
public class EchoServerHandler extends ChannelHandlerAdapter {
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
Channel ch = e.getChannel();
// first message
ch.write(e.getMessage());
}
// ...
}
public class EchoServerHandler2 extends ChannelHandlerAdapter {
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
Channel ch = e.getChannel();
// send second message
ch.write(e.getMessage());
}
// ...
}
You can do that as long as you have the reference to the relevant Channel (or ChannelHandlerContext). For example, you can do this:
public class MyHandler extends ChannelHandlerAdapter {
...
public void channelRead(ctx, msg) {
MyRequest req = (MyRequest) msg;
ctx.write(new MyFirstResponse(..));
executor.execute(new Runnable() {
public void run() {
// Perform database operation
..
ctx.write(new MySecondResponse(...));
}
}
}
...
}
You can do this as long as Netty doesn't close the Channel. Its better you call close() yourself when you're done.
Here's a sample: https://stackoverflow.com/a/48128514/2557517