1 版本说明
- netty的版本
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.6.Final</version>
</dependency>
Netty版本在不断升级,但是4.0以上的版本使用比较广泛。Netty曾经升级到5.0,不过出现了一些问题,版本又回退了。另外,很多的大数据开源框架使用的还是3.0版本的Netty。
- java的版本
Netty官方建议使用JDK 1.6以上,本次使用的是JDK 1.8;
2 入门程序: 创建一个服务端类NettyDiscardServer
DiscardServer功能很简单:读取客户端的输入数据,直接丢弃,不给客户端任何回复。
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
/**
* @author wyaoyao
* @date 2021/7/6 9:09
* 入门程序: DiscardServer
*/
@Slf4j
public class DiscardServer {
private final int serverPort;
private ServerBootstrap bootstrap = new ServerBootstrap();
public DiscardServer(int serverPort) {
this.serverPort = serverPort;
}
public void start() {
// 创建反应器轮询组
EventLoopGroup bossLoopGroup = new NioEventLoopGroup(1);
EventLoopGroup workLoopGroup = new NioEventLoopGroup();
try {
// 设置反应器轮询组
this.bootstrap.group(bossLoopGroup, workLoopGroup);
// 设置nio通道类型
this.bootstrap.channel(NioServerSocketChannel.class);
// 设置监听端口
this.bootstrap.localAddress(this.serverPort);
// 设置通道参数
this.bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
this.bootstrap.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
this.bootstrap.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
// 装备子通道流水线
this.bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
// 有连接到达时会创建一个通道
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// 流水线的职责:负责管理通道中的处理器
// 向“子通道”(传输通道)流水线添加一个处理器
ch.pipeline().addLast(new DiscardHandler());
}
});
// 开始绑定服务器
// 通过调用sync同步方法阻塞直到绑定成功
// 返回值是个future
ChannelFuture channelFuture = this.bootstrap.bind().sync();
log.info("netty discard server start success on port : [{}]", this.serverPort);
// 等待通道关闭的异步任务结束
// 服务监听通道会一直等待通道关闭的异步任务结束
ChannelFuture closeFuture = channelFuture.channel().closeFuture();
closeFuture.sync();
} catch (Exception e) {
e.printStackTrace();
}finally {
// 关闭EventLoopGroup(Gracefully: 优雅的)
bossLoopGroup.shutdownGracefully();
workLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) {
new DiscardServer(10010).start();
}
}
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
/**
* @author wyaoyao
* @date 2021/7/6 10:31
* Handler的作用是对应到IO事件,完成IO事件的业务处理。
* Handler需要为业务做专门开发
*/
@Slf4j
public class DiscardHandler extends ChannelInboundHandlerAdapter {
/**
* 重写channelRead方法,实现Discard(丢弃)逻辑
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
try {
log.info("收到消息,丢弃如下:");
while (byteBuf.isReadable()) {
System.out.println((char)byteBuf.readByte());
}
System.out.println("----------------------------------");
}finally {
ReferenceCountUtil.release(msg);
}
}
}
反应器组件的作用是进行IO事件的查询和分发。Netty中对应的反应器组件有多种,不同应用通信场景用到的反应器组件各不相同。一般来说,对应于多线程的Java NIO通信的应用场景,Netty对应的反应器组件为NioEventLoopGroup。
在上面的例子中,使用了两个NioEventLoopGroup反应器组件实例:
- 第一个负责服务器通道新连接的IO事件的监听;
- 第二个主要负责传输通道的IO事件的处理和数据传输
Reactor模式中的Handler(处理器)角色组件。Handler的作用是对应到IO事件,完成IO事件的业务处理。Handler需要为业务做专门开发
上面的例子中还用到了Netty的服务引导类ServerBootstrap。服务引导类是一个组装和集成器,职责是将不同的Netty组件组装在一起。此外,ServerBootstrap能够按照应用场景的需要为组件设置好基础性的参数,最后帮助快速实现Netty服务器的监听和启动。
Netty的Handler需要处理多种IO事件(如读就绪、写就绪),对应于不同的IO事件,Netty提供了一些基础方法。这些方法都已经提前封装好,应用程序直接继承或者实现即可。比如说,对于处理入站的IO事件,其对应的接口为ChannelInboundHandler,并且Netty提供了ChannelInboundHandlerAdapter适配器作为入站处理器的默认实现。
入站和出站
简单理解,入站指的是输入,出站指的是输出。
Netty中的出/入站与Java NIO中的出/入站有些微妙的不同,Netty的出站可以理解为从Handler传递到Channel的操作,比如说write写通道、read读通道数据;
Netty的入站可以理解为从Channel传递到Handler的操作,比如说Channel数据过来之后,会触发Handler的channelRead()入站处理方法。
如果要实现自己的入站处理器,可以简单地继承ChannelInboundHandlerAdapter入站处理器适配器,再写入自己的入站处理的业务逻辑。也就是说,重写通道读取方法channelRead()即可。
在上面例子中的channelRead()方法将Netty缓冲区ByteBuf的输入数据打印到服务端控制台后,直接丢弃不管了,而且不给客户端任何回复。