简介
Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架。Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架
NIO与BIO
NIO通信方式:
BIO通信方式:
当一个连接建立之后,他有两个步骤要做,第一步是接收完客户端发过来的全部数据,第二步是服务端处理完请求业务之后返回response给客户端。NIO和BIO的区别主要是在第一步。
-
BIO(Blocking I/O,阻塞IO) 在BIO中,等待客户端发数据这个过程是阻塞的,这样就造成了一个线程只能处理一个请求的情况,而机器能支持的最大线程数是有限的,这就是为什么BIO不能支持高并发的原因。如果连接少,他的延迟是最低的,因为一个线程只处理一个连接,适用于少连接且延迟低的场景,比如说数据库连接。
-
NIO(Nonblocking I/O,非阻塞IO) 而NIO中,当一个Socket建立好之后,Thread并不会阻塞去接受这个Socket,而是将这个请求交给Selector,Selector会不断的去遍历所有的Socket,一旦有一个Socket建立完成,他会通知Thread,然后Thread处理完数据再返回给客户端——这个过程是阻塞的,这样就能让一个Thread处理更多的请求了。适用于高并发且处理简单的场景,比如聊天软件。
快速上手
1.添加依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.24.Final</version>
<scope>compile</scope>
</dependency>
2.新建DiscardServerHandler
类
实现消息的接收,并打印到控制台。
public class DiscardServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
try {
while (byteBuf.isReadable()) {
System.out.print((char)byteBuf.readByte());
System.out.flush();
}
} finally {
ReferenceCountUtil.release(msg);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
3.新建DiscardServer
类
public class DiscardServer {
private int port;
public DiscardServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = bootstrap.bind(this.port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 6666;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}
new DiscardServer(port).run();
}
}
NioEventLoopGroup
可以理解为一个线程池,内部维护了一组线程,每个线程负责处理多个Channel上的事件,而一个Channel只对应于一个线程。ServerBootstrap
服务器端启动器,group(EventLoopGroup...)
方法用于指定一个或两个Reactor,本例中指定为两个。channel(Channel)
方法本质用来指定一个Channel工厂,本例中该工厂生产服务端用于accept客户端连接的Channel,将默认使用Channel的无参构造方法。childHandler()
用于指定subReactor中的处理器。ChannelInitializer
它是一个特殊的Handler,功能是初始化多个Handler。option(Key, Value)
用于指定TCP相关的参数以及一些Netty自定义的参数。bind(int)
方法将服务端Channel绑定到本地端口
4.测试
telnet 127.0.0.1 6666
,在telnet窗口输入,控制台会对应输出内容。
总结
Netty实现通讯的步骤:
1、创建两个NIO线程组,一个专门用来网络事件处理(接受客户端连接),另一个则进行网络通讯读写。
2、创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传入数据的缓存大小等。
3、创建一个实际处理数据的类ChannelInitializer,进行初始化的准备工作,比如设置传入数据的字符集,格式,实现实际处理数据的接口。
4、绑定端口,执行同步阻塞方法等待服务器启动即可。