# Netty
# Netty启动流程
启动流程大概可以分为三个步骤:初始化,注册,绑定。
1).初始化主要的过程:创建Channel对象,为Channel配置属性及选项,添加对应的handler。
2).注册的主要过程:将channel与eventLoop绑定;调用内部封装的ServerSocketChannel注册interestOps;注册完成后,调用pipeline产生对应的事件(fireChannelRegister,以及可能的Actived事件)
3).绑定的主要过程:调用内部封装的ServerSocketChannel绑定端口,绑定成功后(fireChannelActived)
# Netty的退出
常规的demo级别的netty服务端的代码写法是这样的:
try {
//创建并初始化 Netty 服务端辅助启动对象 ServerBootstrap
ServerBootstrap serverBootstrap = RpcServer.this.initServerBootstrap(bossGroup, workerGroup);
//绑定对应ip和端口,同步等待成功
ChannelFuture future = serverBootstrap.bind(port).sync();
LOGGER.info("rpc server 已启动,端口:{}", port);
//等待服务端监听端口关闭
future.channel().closeFuture().sync();
} catch (InterruptedException i) {
LOGGER.error("rpc server 出现异常,端口:{}, cause:", port, i.getMessage());
} finally {
//优雅退出,释放 NIO 线程组
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在这里面future.channel().closeFuture().sync();这个语句的主要目的是,方便测试,方便写一个非springboot的demo,比如一个简单地junit test方法,closeFuture().sync()可以阻止junit test将server关闭,同时停止test应用的时候也不需要手动再调用关闭服务器的方法workerGroup.shutdownGracefully()...。这样设计在测试时省心。
但是,当将nettyserver联系到springboot应用的启动时,例如nettyserver设置为@Component,当springboot扫描到nettyserver时,springboot主线程执行到nettyserver的postconstruct注解的方法,然后发生了
future.channel().closeFuture().sync(); 这样导致springboot主线程阻塞,无法继续加载剩下的bean, 更糟糕的是,如果springboot还添加了springboot-web的依赖(自带tomcat容器),那么被阻塞后将无法启动tomcat servlet engine和webapplicationcontext.
所以不能简单地在nettyserver中的构造方法/init方法中写future.channel().closeFuture().sync();和workerGroup.shutdownGracefully().
只需在构造方法/init方法中bootstrap.bind(port),这是异步的,不会阻塞springboot主线程。
而将stop方法单独抽取出来。
需要注意的是,即使直接关闭springboot应用,不手动调用上面的stop方法,nettyserver也会将之前绑定的端口解除,为了保险起见,可以将stop方法添加@predestroy注解
# 服务端接收消息后关闭客户端
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
当出现 INACTIVE 的时候意味着该通道已经关闭
.addListener(ChannelFutureListener.CLOSE) 读完信息之后,就会关闭该通道
Channel 是用于服务端和客户端通信的,所以无论哪一方进行了关闭操作,该 Channel 都会关闭的
ctx.write(in);//写入channel 然后将这些信息返回给发送者
//将所有的待审的消息发送,然后冲刷 并且关闭通道 后面那个监听 只是监听通道是否关闭 ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);