Netty入门一:服务端应用搭建 & 启动过程源码分析

最近周末也没啥事就学学Netty,同时打算写一些博客记录一下(写的过程理解更加深刻了)

本文主要从三个方法来呈现:Netty核心组件简介、Netty服务端创建、Netty启动过程源码分析

如果你对Netty有一定的了解, 那阅读起来应该会比较愉快

Netty核心组件简介 ByteBuf

缓冲区ByteBuf是对JDK NIO类库中ByteBuffer的增强

缓冲区直接连接通道两端( 通过通道发送数据时需要先转换为ByteBuf对象, 从通道直接获取的也是ByteBuf对象)

Channel和Unsafe

Channel聚合一组网络I/O操作 --- 读、写、客户端发起连接、关闭连接、链路关闭等

UnSafe接口辅助Channel实现I/O操作(不应有用户代码直接调用)

ChannelPipeline和ChannelHandler

ChannelHandler:负责处理I/O事件,每个ChannelHanlder对需要关注的I/O事件实现自己的处理逻辑,一般职责较单一,如解码Handler只做解码操作。

ChannelPipeline:一个ChannelPipeline由多个按一定顺序排列的ChannelHandler组成, I/O事件在pipeline中流动(入站事件从头到尾、出站事件从尾到头),每个handler会对事件进行处理。

NioEventLoop和NioEventLoopGroup

NioEventLoop: 事件循环(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配置

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywfyf.html