Netty 是一个异步的、基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务
异步:这里的异步是指Netty 使用了多线程来完成方法调用 和 处理结果相分离 ,指的是调用时的异步,并不是值异步IO,Netty的IO模型还是基于多路复用的事件驱动:Netty底层实现采用的是多路复用技术(即selector),在IO事件发生时才进行处理(如可连接、可读、可写事件等)Netty 在 Java 网络应用框架中的地位就好比:Spring 框架在 JavaEE 开发中的地位
以下的框架都使用了 Netty,因为它们都涉及到网络通信需求!
netty底层基于NIO,
自己基于NIO去开发的话,工作量大,bug 多,需要处理下面的内容:
netty完善了上面的内容
要求:开发一个简单的服务器端和客户端
<dependency>
<groupId>io.nettygroupId>
<artifactId>netty-allartifactId>
<version>4.1.39.Finalversion>
dependency>
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.kqueue.KQueueSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.socket.oio.OioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LoggingHandler;
public class HelloServer {
public static void main(String[] args) {
// 1. 服务器端启动器,负责组装 netty 组件(协调它们的工作),启动服务器
new ServerBootstrap()
// 2. BossEventLoop, WorkerEventLoop(selector,thread), group 组
/*
BossEventLoop:处理可连接事件
WorkerEventLoop:处理可读事件
selector:通过selector去监听事件
thread:线程
一个selector + thread 就是一个EventLoop,循环处理event事件
一个BossEventLoop + WorkerEventLoop 就是一个组
NioEventLoopGroup里面包含了一个线程和选择器
*/
.group(new NioEventLoopGroup())
// 3. 选择 服务器的 ServerSocketChannel 实现
/*
有NIO和BIO
NioServerSocketChannel:基于NIO
EpollServerSocketChannel:基于Linux的Epoll的channel
KQueueSocketChannel:
OioSocketChannel:基于阻塞IO事件
*/
.channel(NioServerSocketChannel.class) // OIO BIO
// 4. boss 负责处理连接 worker(child) 负责处理读写,决定了 worker(child) 能执行哪些操作(handler)
/*
告诉将来作为worker的EventLoop,应该做什么事情
确定worker将来要做那些业务逻辑
*/
.childHandler(//将来连接建立后才会去执行这里面的内容,只有在连接建立后才开始执行
// 5. channel 代表和客户端进行数据读写的通道, Initializer 初始化,负责添加别的 handler
new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {//初始化,责添加别的 handler
// 6. 添加具体 handler
ch.pipeline().addLast(new LoggingHandler());
ch.pipeline().addLast(new StringDecoder()); // 将 ByteBuf 转换为字符串
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { // 自定义 handler
@Override // 处理读事件
public void channelRead(ChannelHandlerContext ctx,Object msg) throws Exception {
System.out.println(msg); // 打印上一步转换好的字符串
}
});
}
})
// 7. 访问启动后,绑定的监听端口
.bind(8080);
}
}
new ServerBootstrap()
.group(new NioEventLoopGroup()) // 1
.channel(NioServerSocketChannel.class) // 2
.childHandler(new ChannelInitializer<NioSocketChannel>() { // 3
protected void initChannel(NioSocketChannel ch) {
ch.pipeline().addLast(new StringDecoder()); // 5
ch.pipeline().addLast(new SimpleChannelInboundHandler<String>() { // 6
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println(msg);
}
});
}
})
.bind(8080); // 4
代码说明:
1 处,创建 NioEventLoopGroup,可以简单理解为 线程池 + Selector 后面会详细展开
2 处,选择服务 Scoket 实现类,其中 NioServerSocketChannel 表示基于 NIO 的服务器端实现,其它实现还有

3 处,方法叫 childHandler,是接下来添加的处理器都是给 SocketChannel 用的,而不是给 ServerSocketChannel。ChannelInitializer 处理器(仅执行一次),它的作用是待客户端 SocketChannel 建立连接后,执行 initChannel 以便添加更多的处理器
4 处,ServerSocketChannel 绑定的监听端口
5 处,SocketChannel 的处理器,解码 ByteBuf => String
6 处,SocketChannel 的业务处理器,使用上一个处理器的处理结果
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import java.net.InetSocketAddress;
public class HelloClient {
public static void main(String[] args) throws InterruptedException {
// 1. 创建启动类,启动客户端
new Bootstrap()
// 2. 添加 EventLoop:比如服务器端发送数据过来,客户端的eventLoop就可以从选择器里触发读事件,去进行进一步的处理
.group(new NioEventLoopGroup())
// 3. 选择客户端 channel 实现,NioSocketChannel(封装了jdk的NioSocketChannel)
.channel(NioSocketChannel.class)
// 4. 添加处理器,ChannelInitializer(连接建立后会被调用,调用后便会执行initChannel方法)
.handler(new ChannelInitializer<NioSocketChannel>() {
@Override // 在连接建立后被调用
protected void initChannel(NioSocketChannel ch) throws Exception {
//客户端将字符串编码成为ByteBuf,服务器端将ByteBuf解码为字符串
ch.pipeline().addLast(new StringEncoder());
}
})
// 5. 连接到服务器
.connect(new InetSocketAddress("localhost", 8080))
.sync()//
.channel()
// 6. 向服务器发送数据,即写数据
.writeAndFlush("hello, world");
}
}

new Bootstrap()
.group(new NioEventLoopGroup()) // 1
.channel(NioSocketChannel.class) // 2
.handler(new ChannelInitializer<Channel>() { // 3
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(new StringEncoder()); // 8
}
})
.connect("127.0.0.1", 8080) // 4
.sync() // 5
.channel() // 6
.writeAndFlush(new Date() + ": hello world!"); // 7
代码说明


执行流程:
sync()方法 (这是一个同步方法或阻塞方法,直到连接建立才开始运行),由此可知,凡是收发数据,都要走handler观念
channel 理解为数据的通道msg 理解为流动的数据,最开始输入是 ByteBuf,但经过 pipeline 的加工,会变成其它类型对象,最后输出又变成 ByteBufhandler 理解为数据的处理工序
工序有多道,合在一起就是 pipeline,pipeline 负责发布事件(读、读取完成...)传播给每个 handler, handler 对自己感兴趣的事件进行处理(重写了相应事件处理方法)Inbound(入栈,数据输入时) 和 Outbound(出栈,数据向客户端写出时) 两类eventLoop 理解为处理数据的工人管理多个 channel 的 io 操作,并且一旦工人负责了某个 channel,就要负责到底(一开始建立了关系,二者就绑定上了,这个过程是为了线程安全)既可以执行 io 操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个 channel 的待处理任务,任务分为普通任务、定时任务工人按照 pipeline 顺序,依次按照 handler 的规划(代码)处理数据,可以为每道工序指定不同的工人