最近周末也没啥事就学学Netty,同时打算写一些博客记录一下(写的过程理解更加深刻了)
本文主要从三个方法来呈现:Netty核心组件简介、Netty服务端创建、Netty启动过程源码分析
如果你对Netty有一定的了解, 那阅读起来应该会比较愉快
Netty核心组件简介 ByteBuf缓冲区ByteBuf是对JDK NIO类库中ByteBuffer的增强
缓冲区直接连接通道两端( 通过通道发送数据时需要先转换为ByteBuf对象, 从通道直接获取的也是ByteBuf对象)
Channel和UnsafeChannel聚合一组网络I/O操作 --- 读、写、客户端发起连接、关闭连接、链路关闭等
UnSafe接口辅助Channel实现I/O操作(不应有用户代码直接调用)
ChannelPipeline和ChannelHandlerChannelHandler:负责处理I/O事件,每个ChannelHanlder对需要关注的I/O事件实现自己的处理逻辑,一般职责较单一,如解码Handler只做解码操作。
ChannelPipeline:一个ChannelPipeline由多个按一定顺序排列的ChannelHandler组成, I/O事件在pipeline中流动(入站事件从头到尾、出站事件从尾到头),每个handler会对事件进行处理。
NioEventLoop和NioEventLoopGroupNioEventLoop: 事件循环(Reactor线程),负责监听多个通道的就绪状态,当通道就绪时产生相应的入站事件
NioEventLoopGroup:事件循环池(Reactor线程池),当新的通道被创建时,NioEventLoopGroup会为其分配一个事件循环,后续该通道的所有I/O操作都在该事件循环进行。
Future和Promise这两个类是Netty对异步的支持,Promise用于设置异步操作结果(写),Future用于获取异步操作结果(读)。
Netty服务端创建我们从搭建一个简单的服务端程序开始
下面是一个获取当前日期和时间的服务端程序:当客户端输入行为"today"时返回当天日期 "2020-12-11",输入行为"time"时返回当前时间 "03:11:11"。
public static void main(String[] args) { //1.线程池配置 ServerBootstrap bootstrap = new ServerBootstrap(); NioEventLoopGroup parentGroup = new NioEventLoopGroup(1); NioEventLoopGroup childGroup = new NioEventLoopGroup(4); bootstrap.group(parentGroup, childGroup); //2.服务端Channel配置 bootstrap.channel(NioServerSocketChannel.class); bootstrap.option(ChannelOption.SO_BACKLOG, 1024); //3.子Channel配置 bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel channel) throws Exception { //解码器 channel.pipeline().addLast(new LineBasedFrameDecoder(1024)); channel.pipeline().addLast(new StringDecoder(StandardCharsets.UTF_8)); //业务handler channel.pipeline().addLast(new BusinessHandler()); //编码器 channel.pipeline().addLast(new StringEncoder(StandardCharsets.UTF_8)); } }); try { ChannelFuture future = bootstrap.bind(7001).syncUninterruptibly(); future.channel().closeFuture().sync(); } catch (Exception e) { //todo }finally { parentGroup.shutdownGracefully(); childGroup.shutdownGracefully(); } } static class BusinessHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { String s = (String) msg; String ret = ""; if ("today".equals(s)) { ret = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); } else if ("time".equals(s)) { ret = new SimpleDateFormat("hh:mm:ss").format(new Date()); } ret += "\r\n"; ctx.channel().writeAndFlush(ret); } }整个应用搭建的过程很简单,归纳起来有四步
1.Reactor线程池配置
2.服务端Channel配置
3.子Channel配置(通过服务端通道创建的子通道)
4.绑定本地端口并启动服务
Reactor线程池配置我们新建两个Reactor线程池parentGroup和childGroup
parentGroup是服务端通道使用,用于接受新的客户端连接(accept)
childGroup用于处理所有服务端通道创建子通道的网络I/O请求
ServerBootstrap bootstrap = new ServerBootstrap(); NioEventLoopGroup parentGroup = new NioEventLoopGroup(1); NioEventLoopGroup childGroup = new NioEventLoopGroup(4); bootstrap.group(parentGroup, childGroup); 服务端Channel配置服务端Channel配置主要涉及:Channel类型、ChanelOption、AttributeKey(handler一般不用配置)
Channel类型配置
Channel的类型我们选用 NioServerSocketChannel -- 底层使用的是JDK NIO的 ServerSocketChannel.
bootstrap.channel(NioServerSocketChannel.class);设置ChannelOption 和 AttributeKey
ChildOption:TCP选项, 如接受缓冲区大小(SO_RCVBUF)、发送缓冲区大小(SO_SNDBUF)、内核TCP连接队列大小(SO_BACKLOG)等
AttributeKey:附在Channel上的对象, 可以在多个ChannelHandler之间进行数据共享
bootstrap.option(ChannelOption.SO_BACKLOG, 1024); bootstrap.attr(AttributeKey.newInstance("TEST"), new Object());备注:ChannelHandler 用于处理I/O事件,是通道所必须的。因为Netty提供了初始化客户端连接的handler(ServerBootstrapAcceptor),所以对于服务端Channel我们可以不用设置
Channel配置Channel配置主要涉及:ChanelOption和AttributeKey、ChannelHandler
设置ChannelOption 和 AttributeKey
针对每个Channel可以配置ChannelOption和AttributeKey,同服务端通道配置一样。
ChannelHandler配置