怎么写netty,netty入门教程文档

首页 > 实用技巧 > 作者:YD1662024-02-21 13:34:19

可以看出都是继承于AbstractBootStrap抽象类,所以大致上的配置方法都相同。

一般来说,使用Bootstrap创建启动器的步骤可分为以下几步:

怎么写netty,netty入门教程文档(9)

5.4.1 group()

在上一篇文章《Reactor模式》中,我们就讲过服务端要使用两个线程组:

一般创建线程组直接使用以下new就完事了:

EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup();

有点好奇的是,既然是线程组,那线程数默认是多少呢?深入源码:

//使用一个常量保存 private static final int DEFAULT_EVENT_LOOP_THREADS; static { //NettyRuntime.availableProcessors() * 2,cpu核数的两倍赋值给常量 DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt( "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); if (logger.isDebugEnabled()) { logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS); } } protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) { //如果不传入,则使用常量的值,也就是cpu核数的两倍 super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args); }

通过源码可以看到,默认的线程数是cpu核数的两倍。假设想自定义线程数,可以使用有参构造器:

//设置bossGroup线程数为1 EventLoopGroup bossGroup = new NioEventLoopGroup(1); //设置workerGroup线程数为16 EventLoopGroup workerGroup = new NioEventLoopGroup(16);

5.4.2 channel()

这个方法用于设置通道类型,当建立连接后,会根据这个设置创建对应的Channel实例。

怎么写netty,netty入门教程文档(10)

使用debug模式可以看到

怎么写netty,netty入门教程文档(11)

通道类型有以下:

NioSocketChannel: 异步非阻塞的客户端 TCP Socket 连接。

NioServerSocketChannel: 异步非阻塞的服务器端 TCP Socket 连接。

常用的就是这两个通道类型,因为是异步非阻塞的。所以是首选。

OioSocketChannel: 同步阻塞的客户端 TCP Socket 连接。

OioServerSocketChannel: 同步阻塞的服务器端 TCP Socket 连接。

稍微在本地调试过,用起来和Nio有一些不同,是阻塞的,所以API调用也不一样。因为是阻塞的IO,几乎没什么人会选择使用Oio,所以也很难找到例子。我稍微琢磨了一下,经过几次报错之后,总算调通了。代码如下:

//server端代码,跟上面几乎一样,只需改三个地方 //这个地方使用的是OioEventLoopGroup EventLoopGroup bossGroup = new OioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup)//只需要设置一个线程组boosGroup .channel(OioServerSocketChannel.class)//设置服务端通道实现类型 //client端代码,只需改两个地方 //使用的是OioEventLoopGroup EventLoopGroup eventExecutors = new OioEventLoopGroup(); //通道类型设置为OioSocketChannel bootstrap.group(eventExecutors)//设置线程组 .channel(OioSocketChannel.class)//设置客户端的通道实现类型

NioSctpChannel: 异步的客户端 Sctp(Stream Control Transmission Protocol,流控制传输协议)连接。

NioSctpServerChannel: 异步的 Sctp 服务器端连接。

本地没启动成功,网上看了一些网友的评论,说是只能在linux环境下才可以启动。从报错信息看:SCTP not supported on this platform,不支持这个平台。因为我电脑是window系统,所以网友说的有点道理。

5.4.3 option()与childOption()

首先说一下这两个的区别。

option()设置的是服务端用于接收进来的连接,也就是boosGroup线程。

childOption()是提供给父管道接收到的连接,也就是workerGroup线程。

搞清楚了之后,我们看一下常用的一些设置有哪些:

SocketChannel参数,也就是childOption()常用的参数:

SO_RCVBUF Socket参数,TCP数据接收缓冲区大小。TCP_NODELAY TCP参数,立即发送数据,默认值为Ture。SO_KEEPALIVE Socket参数,连接保活,默认值为False。启用该功能时,TCP会主动探测空闲连接的有效性。

ServerSocketChannel参数,也就是option()常用参数:

SO_BACKLOG Socket参数,服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝。默认值,Windows为200,其他为128。

由于篇幅限制,其他就不列举了,大家可以去网上找资料看看,了解一下。

5.4.4 设置流水线(重点)

ChannelPipeline是Netty处理请求的责任链,ChannelHandler则是具体处理请求的处理器。实际上每一个channel都有一个处理器的流水线。

在Bootstrap中childHandler()方法需要初始化通道,实例化一个ChannelInitializer,这时候需要重写initChannel()初始化通道的方法,装配流水线就是在这个地方进行。代码演示如下:

//使用匿名内部类的形式初始化通道对象 bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //给pipeline管道设置自定义的处理器 socketChannel.pipeline().addLast(new MyServerHandler()); } });

处理器Handler主要分为两种:

ChannelInboundHandlerAdapter(入站处理器)、ChannelOutboundHandler(出站处理器)

入站指的是数据从底层java NIO Channel到Netty的Channel。

出站指的是通过Netty的Channel来操作底层的java NIO Channel。

ChannelInboundHandlerAdapter处理器常用的事件有

  1. 注册事件 fireChannelRegistered。
  2. 连接建立事件 fireChannelActive。
  3. 读事件和读完成事件 fireChannelRead、fireChannelReadComplete。
  4. 异常通知事件 fireExceptionCaught。
  5. 用户自定义事件 fireUserEventTriggered。
  6. Channel 可写状态变化事件 fireChannelWritabilityChanged。
  7. 连接关闭事件 fireChannelInactive。

ChannelOutboundHandler处理器常用的事件有

  1. 端口绑定 bind。
  2. 连接服务端 connect。
  3. 写事件 write。
  4. 刷新时间 flush。
  5. 读事件 read。
  6. 主动断开连接 disconnect。
  7. 关闭 channel 事件 close。

还有一个类似的handler(),主要用于装配parent通道,也就是bossGroup线程。一般情况下,都用不上这个方法。

5.4.5 bind()

提供用于服务端或者客户端绑定服务器地址和端口号,默认是异步启动。如果加上sync()方法则是同步。

有五个同名的重载方法,作用都是用于绑定地址端口号。不一一介绍了。

5.4.6 优雅地关闭EventLoopGroup

//释放掉所有的资源,包括创建的线程 bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully();

会关闭所有的child Channel。关闭之后,释放掉底层的资源。

5.5 Channel

Channel是什么?不妨看一下官方文档的说明:

A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind

翻译大意:一种连接到网络套接字或能进行读、写、连接和绑定等I/O操作的组件。

如果上面这段说明比较抽象,下面还有一段说明:

A channel provides a user:

the current state of the channel (e.g. is it open? is it connected?),the configuration parameters of the channel (e.g. receive buffer size),the I/O operations that the channel supports (e.g. read, write, connect, and bind), andthe ChannelPipeline which handles all I/O events and requests associated with the channel.

翻译大意:

channel为用户提供:

  1. 通道当前的状态(例如它是打开?还是已连接?)
  2. channel的配置参数(例如接收缓冲区的大小)
  3. channel支持的IO操作(例如读、写、连接和绑定),以及处理与channel相关联的所有IO事件和请求的ChannelPipeline。

5.5.1 获取channel的状态

boolean isOpen(); //如果通道打开,则返回true boolean isRegistered();//如果通道注册到EventLoop,则返回true boolean isActive();//如果通道处于活动状态并且已连接,则返回true boolean isWritable();//当且仅当I/O线程将立即执行请求的写入操作时,返回true。

以上就是获取channel的四种状态的方法。

5.5.2 获取channel的配置参数

获取单条配置信息,使用getOption(),代码演示:

ChannelConfig config = channel.config();//获取配置参数 //获取ChannelOption.SO_BACKLOG参数, Integer soBackLogConfig = config.getOption(ChannelOption.SO_BACKLOG); //因为我启动器配置的是128,所以我这里获取的soBackLogConfig=128

获取多条配置信息,使用getOptions(),代码演示:

ChannelConfig config = channel.config(); Map<ChannelOption<?>, Object> options = config.getOptions(); for (Map.Entry<ChannelOption<?>, Object> entry : options.entrySet()) { System.out.println(entry.getKey() " : " entry.getValue()); } /** SO_REUSEADDR : false WRITE_BUFFER_LOW_WATER_MARK : 32768 WRITE_BUFFER_WATER_MARK : WriteBufferWaterMark(low: 32768, high: 65536) SO_BACKLOG : 128 以下省略... */

5.5.3 channel支持的IO操作

写操作,这里演示从服务端写消息发送到客户端:

@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ctx.channel().writeAndFlush(Unpooled.copiedBuffer("这波啊,这波是肉蛋葱鸡~", CharsetUtil.UTF_8)); }

客户端控制台:

//收到服务端/127.0.0.1:6666的消息:这波啊,这波是肉蛋葱鸡~

连接操作,代码演示:

ChannelFuture connect = channelFuture.channel().connect(new InetSocketAddress("127.0.0.1", 6666));//一般使用启动器,这种方式不常用

通过channel获取ChannelPipeline,并做相关的处理:

//获取ChannelPipeline对象 ChannelPipeline pipeline = ctx.channel().pipeline(); //往pipeline中添加ChannelHandler处理器,装配流水线 pipeline.addLast(new MyServerHandler());

5.6 Selector

在NioEventLoop中,有一个成员变量selector,这是nio包的Selector,在之前《NIO入门》中,我已经讲过Selector了。

Netty中的Selector也和NIO的Selector是一样的,就是用于监听事件,管理注册到Selector中的channel,实现多路复用器。

怎么写netty,netty入门教程文档(12)

上一页12345下一页

栏目热文

文档排行

本站推荐

Copyright © 2018 - 2021 www.yd166.com., All Rights Reserved.