相关文章:
shutdownGracefully初识(一)
【Netty】shutdownGracefully初识(二)
如何优雅地停止Java进程 netty需要结合java语法,才能实现最合理的关闭netty
1. 前言
写本篇文章的出发点来自之前的一篇文章《future.channel().closeFuture().sync()作用》
那篇文章初识了shutdownGracefully()
方法,但是没有深入的解释该方法作用,文章中的例子也没有实际执行到该方法,只能从字面理解到优雅的关闭,本篇主要讲解该方法的入门,通过一个测试例子,简单介绍该方法的作用和学习该方法一些技巧。
shutdownGracefully()如何优雅关闭
我的思路是启动一个netty server
,然后通过外部事件触发关闭事件,在关闭事件中调用shutdownGracefully()
方法。
DiscardServer.java
负责启动一个netty server
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* 官方原始例子改造
*/
public class DiscardServer {
private int port;
EventLoopGroup bossGroup;
EventLoopGroup workerGroup;
DiscardServer discardServer;
public DiscardServer(int port) {
this.port = port;
}
public void run() throws Exception {
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
discardServer = this;
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("", new DiscardServerHandler(discardServer));
};
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
}
public void close() {
this.workerGroup.shutdownGracefully();
this.bossGroup.shutdownGracefully();
}
public static void main(String[] args) {
try {
new DiscardServer(8088).run();
} catch (Exception e) {
e.printStackTrace();
}
}
}
DiscardServerHandler.java
负责处理接收到的消息,如果收到的字符是'c'
,则调用
netty server的shutdownGracefully()
方法
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
public class DiscardServerHandler extends ChannelInboundHandlerAdapter {
DiscardServer discardServer;
public DiscardServerHandler(DiscardServer discardServer) {
this.discardServer = discardServer;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
try {
while (in.isReadable()) {
char c = (char) in.readByte();
System.out.println(c);
if (c == 'c') {
this.discardServer.close();
}
}
} finally {
ReferenceCountUtil.release(msg);
}
}
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// channel失效处理,客户端下线或者强制退出等任何情况都触发这个方法
System.out.println("关闭一个链接");
super.channelInactive(ctx);
}
测试:命令行下运行
telnet localhost 8088
输入字符a,我们看下控制台输出
log4j:ERROR Could not find value for key log4j.appender.A
log4j:ERROR Could not instantiate appender named "A".
开启一个链接
a
表明一个正常的连接,我们测试下关闭事件。
在命令行输入字符c
,在这里插入代码片我们看下控制台输出:
log4j:ERROR Could not find value for key log4j.appender.A
log4j:ERROR Could not instantiate appender named "A".
开启一个链接
a
c
关闭一个链接 'channelInactive()被触发了'
我们发现触发了shutdownGracefully()方法后,间接的触发了DiscardServerHandler中的channelInactive()
方法,至此,我们有一个初步了解,shutdownGracefully()方法作用是优雅的关闭netty server,什么是优雅的关闭呢,我们先来说说与优雅关闭相对应的暴力关闭,我们知道暴力关闭等价于猝死,猝死在瞬间完成,你来不及做任何想做的事情,比如让自己先化个妆死的 “好看一点” 或吃顿饱饭再走?与之类似的,netty server在关闭之前也有很多想做的事情,比如我们例子中hanler的channelInactive()方法,一般这个方法监听客户端的主动断链行为,服务端记录用户退出时的时间或其他的一些特定信息,如果是服务器主动关闭,我们其实也是希望继续触发链路断路事件的。
3. 深入学习的技巧
好了,我们现在对shutdownGracefully()方法有了一点了解,它所做的事情远比你想象的要多的多,而且也很复杂,我们后续文章会接着探讨。我们前文讲到学习的一些技巧,就是如何深入的学习这个方法,这里有个快捷方式,比如,调用shutdownGracefully()方法后,netty server是如果何触发channelInactive()方法的?
如果我们在eclipse
中直接F3
进入shutdownGracefully()
方法源码查看的话,会是一头雾水:
public interface EventExecutorGroup extends ScheduledExecutorService, Iterable<EventExecutor> {
Future<?> shutdownGracefully();
shutdownGracefully()只是一个接口的abstract
方法,我们需要找到实际的实现类,MultithreadEventExecutorGroup是我们要找到的实现类,循环自身的children
集合,再调用子元素的shutdownGracefully()
方法·
public abstract class MultithreadEventExecutorGroup extends AbstractEventExecutorGroup {
@Override
public Future<?> shutdownGracefully(long quietPeriod, long timeout, TimeUnit unit) {
for (EventExecutor l: children) {
l.shutdownGracefully(quietPeriod, timeout, unit);
}
return terminationFuture();
}
找到MultithreadEventExecutorGroup
类还是蛮费劲的,我们看下继承图
图一 类之间的关系
如何快速的找到方法的实现类呢? 这里有个技巧就是开启debug模式,打断点
,通过f5进入方法体,这样会自动进入实现类:
1.在close()方法打断点
2.f5进入shutdownGracefully()方法
3.f5进入重载的shutdownGracefully()方法
有了上面的技巧,我们后续在看源码的时候就方便多了。小伙伴们是不是收获很大?