1. Netty中Handler
从应用程序开发人员的角度来看,Netty的主要组件是ChannelHandler,而ChannelHandler包括入站和出站两种类型。
- 数据入站,指的是数据从底层的Java NIO channel到Netty的Channel。
- 数据出站,指的是通过Netty的Channel来操作底层的 Java NIO chanel。
1.1 inbound
入站(inbound)处理通常由底层Java NIO channel触发,主要事件如下:
1. 注册事件,当前channel注册到EventLoop(channelRegistered) 。当前channel从EventLoop取消注册(channelUnregistered)。
2. 连接建立事件 channelActive 和 连接关闭事件 channelInactive。
3. 读事件和读完成事件 channelRead、channelReadComplete。
4. 异常通知事件 exceptionCaught。
5. 用户自定义事件userEventTriggered。
6. Channel 可写状态变化事件 channelWritabilityChanged。
注意:每个方法都带了ChannelHandlerContext作为参数,具体作用是,在每个回调事件里面,处理完成之后,使用ChannelHandlerContext的fireChannelXXX方法来传递给下个ChannelHandler。
1.2 outbound
出站(inbound) Handler通常是Netty channel操作底层Java NIO channel,主要操作如下:
1. 端口绑定 bind。
2. 连接服务端 connect。
3. 写事件 write。
4. 刷新时间 flush。
5. 读事件 read。
6. 主动断开连接 disconnect。
7. 关闭 channel 事件 close。
注意:一些回调方法有ChannelPromise这个参数,我们可以调用它的addListener注册监听,当回调方法所对应的操作完成后,会触发这个监听。
1.3 handler、pipeline、handlerContext、channel的关系
在一个channel被创建时,会创建一个与之对应的DefaultChannelPipeline,然后可以通过pipeline的addLast将handler添加到pipeline的链表中,在添加的过程中会自动生成一个DefaultChannelHandlerContext对象来封装对应的Handler和channel。
因此,channel和pipeline是一一对应的,而一个pipeline中会有一个ChannelHandlerContext链表,每个ChannelHandlerContext是channel和handler的封装。
2. 自定义业务逻辑handler
public class MyServerHandler extends ChannelInboundHandlerAdapter {
static ChannelGroup group=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("MyServerHandler:"+ctx.channel().remoteAddress()+":"+msg);
// ctx.writeAndFlush("123");
// ctx.fireChannelRead(msg);//如果没有这个,则后面的入站handler将不会被执行
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
group.add(ctx.channel());
System.out.println("接收到第"+group.size()+"个连接");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("接收到第"+group.size()+"个连接");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
public class MyServerHandler1 extends SimpleChannelInboundHandler<String> {
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("MyServerHandler1:"+ctx.channel().remoteAddress()+":"+msg);
ctx.fireChannelRead(msg);//如果没有这个,则后面的入站handler将不会被执行
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
自定义channelHandler,一般是继承SimpleChannelInboundHandler或者ChannelInboundHandlerAdapter,并重写相关方法。建议都用ChannelInboundHandlerAdapter。
SimpleChannelInboundHandler类的channelRead方法会在执行完后将buffer里的数据自动清空,而ChannelInboundHandlerAdapter类的channelRead方法只是调用了fire操作。
实现SimpleChannelInboundHandler并重写channelRead0方法注意事项:
不能在实现类中将客户端请求的参数回写到客户端,如下代码所示:
public class MyServerHandler1 extends SimpleChannelInboundHandler<String> {
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println("MyServerHandler1:"+ctx.channel().remoteAddress()+":"+msg);
ctx.writeAndFlush(msg);//这行代码有风险,因为ctx的writeAndFlush是异步操作,有可能msg对应的buffer已经在这个方法执行完后被清空了
ctx.fireChannelRead(msg);//如果没有这个,则后面的入站handler将不会被执行
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
注意:如果实现的是SimpleChannelInboundHandler,则泛型类型如果和消息类型不一致时,不会进入channelRead0方法,而是进入下一个handler。