概述
官方地址https://netty.io/
Github:https://github.com/netty/netty
什么是netty
Netty is an asynchronous event-driven network application framework
for rapid development of maintainable high performance protocol servers & clients.
本质: 网络应用框架
实现: 异步,事件驱动
特性: 快速开发,可维护的,高性能
用途: 服务器和客户端开发
echo example
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class NettyServer {
public static void main(String[] args) {
//配置服务端NIO线程组
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel) throws Exception {
System.out.println("有客户端链接到本服务端");
System.out.println("IP:" + channel.localAddress().getHostString());
System.out.println("Port:" + channel.localAddress().getPort());
}
});
ChannelFuture f = b.bind(8080).sync();
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
官方案例https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/echo
1.声明线程池
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
声明两个 Group,一个是 bossGroup,用于处理 Accept 事件,一个是 workerGroup,用于处理消息的读写事件。
2.创建服务端引导类
引导类,是用来集成所有配置,引导程序加载的,分成两种,一种是客户端引导类 Bootstrap(个人觉得叫 ClientBootstrap 可能更贴切),另一种是服务端引导类 ServerBootstrap,我们这里编写的是服务端程序,创建的当然是服务端引导类。
注意,Bootstrap 和 ServerBootstrap 之间并不是继承关系,他们是平等的,都继承了 AbstractBootstrap 抽象类。
ServerBootstrap serverBootstrap = new ServerBootstrap();
3.设置线程池
把第一步声明的线程池设置到 ServerBootstrap 中,它说明了 Netty 应用程序以什么样的线程模型运行,正如前面所说 bossGroup 负责接受(Accept)连接,workerGroup 负责读写数据。
serverBootstrap.group(bossGroup, workerGroup);
4.设置 ServerSocketChannel 类型
如果您需要使用阻塞型 IO 模型,直接把 Nio 改成 Oio 就可以了,即 OioServerSocketChannel,不过它已经废弃了,所以不建议。
另外,如果您的程序运行在 Linux 系统上,还可以使用一种更高效的方式,即 EpollServerSocketChannel,它使用的是 Linux 系统上的 epoll 模型,比 select 模型更高效,可见 Netty 把性能优化做到了极致。
serverBootstrap.channel(NioServerSocketChannel.class);
5.设置参数(可选)
设置 Netty 中可以使用的任何参数,这些参数都在 ChannelOption 及其子类中,后面我们会详细介绍各个参数的含义,不过,很遗憾地告诉你,大多数情况下并不需要修改 Netty 的默认参数,这就是 Netty 比较牛的地方。
serverBootstrap.option(ChannelOption.SO_BACKLOG, 100);
serverBootstrap.option(ChannelOption.SO_BACKLOG, 100);
我们这里设置了一个 SO_BACKLOG 系统参数,它表示的是最大等待连接数量。
6. 设置 Handler(可选)
设置 ServerSocketChannel 对应的 Handler,注意只能设置一个,它会在 SocketChannel 建立起来之前执行,等我们看源码的时候会详细介绍它的执行时机。
serverBootstrap.handler(new LoggingHandler(LogLevel.INFO))
7. 编写并设置子 Handler(必须)
Netty 中的 Handler 分成两种,一种叫做 Inbound,一种叫做 Outbound。我们这里简单地写一个 Inbound 类型的 Handler,它接收到数据后立即写回客户端。
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 读取数据后写回客户端
ctx.write(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
设置子 Handler 设置的是 SocketChannel 对应的 Handler,注意也是只能设置一个,它用于处理 SocketChannel 的事件。
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
// 可以添加多个子Handler
p.addLast(new LoggingHandler(LogLevel.INFO));
p.addLast(new EchoServerHandler());
}
});
虽然只能设置一个,但是 Netty 的提供了一种可以设置多个 Handler 的途径,即使用 ChannelInitializer 方式,当然,第六步的设置 Handler 也可以使用这种方式设置多个 Handler。
这里,我们设置了一个打印的 Handler 和一个自定义的 EchoServerHandler。
8. 绑定端口(必须)
绑定端口,并启动服务端程序,sync () 会阻塞直到启动完成才执行后面的代码。
ChannelFuture f = serverBootstrap.bind(PORT).sync();
9. 等待服务端端口关闭(必须)
等待服务端监听端口关闭,sync () 会阻塞主线程,内部调用的是 Object 的 wait () 方法。
f.channel().closeFuture().sync();
10. 优雅地关闭线程池(建议)
最后,是在 finally 中调用 shutdownGracefully () 方法优雅地关闭线程池,优雅停机。
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();