from: http://www.dozer.cc/2015/05/netty-auto-reconnect.html
自動重連
用 Netty 寫 Client 和 Server 的時候必須要去處理自動重連。
Server 端啟動時的錯誤,要去不斷重試。
Client 端不僅要處理啟動時的錯誤,還要處理中途斷開連接。
Server 端的處理
和常規的代碼相比,Server 端只要處理一個地方即可:
public final class TcpServer { private volatile EventLoopGroup bossGroup; private volatile EventLoopGroup workerGroup; private volatile ServerBootstrap bootstrap; private volatile boolean closed = false; private final int localPort; public TcpServer(int localPort) { this.localPort = localPort; } public void close() { closed = true; bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); System.out.println("Stopped Tcp Server: " + localPort); } public void init() { closed = false; bossGroup = new NioEventLoopGroup(); workerGroup = new NioEventLoopGroup(); bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workerGroup); bootstrap.channel(NioServerSocketChannel.class); bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { //todo: add more handler } }); doBind(); } protected void doBind() { if (closed) { return; } bootstrap.bind(localPort).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture f) throws Exception { if (f.isSuccess()) { System.out.println("Started Tcp Server: " + localPort); } else { System.out.println("Started Tcp Server Failed: " + localPort); f.channel().eventLoop().schedule(() -> doBind(), 1, TimeUnit.SECONDS); } } }); } }
我們把整個初始化分成了兩個部分,第一部分是初始化相關 class,第二部分做真正的監聽端口。
這里最特殊的地方就是在調用bind
方法后,添加一個listener
檢查是否成功,如果失敗的話,需要調用.channel().eventLoop().schedule()
方法,創建一個任務,我這代碼設置的是1秒后嘗試重新連接。
另外考慮到 server 可以被人為關閉,所以還需要檢查當前時候已經關閉。如果不檢查的話,你的 server 可能就永遠也關不掉了。
Client 端的處理
client 端啟動流程差不多,但是需要加一個 handler 來處理連接斷開。
public class TcpClient { private volatile EventLoopGroup workerGroup; private volatile Bootstrap bootstrap; private volatile boolean closed = false; private final String remoteHost; private final int remotePort; public TcpClient(String remoteHost, int remotePort) { this.remoteHost = remoteHost; this.remotePort = remotePort; } public void close() { closed = true; workerGroup.shutdownGracefully(); System.out.println("Stopped Tcp Client: " + getServerInfo()); } public void init() { closed = false; workerGroup = new NioEventLoopGroup(); bootstrap = new Bootstrap(); bootstrap.group(workerGroup); bootstrap.channel(NioSocketChannel.class); bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addFirst(new ChannelInboundHandlerAdapter() { @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { super.channelInactive(ctx); ctx.channel().eventLoop().schedule(() -> doConnect(), 1, TimeUnit.SECONDS); } }); //todo: add more handler } }); doConnect(); } private void doConnect() { if (closed) { return; } ChannelFuture future = bootstrap.connect(new InetSocketAddress(remoteHost, remotePort)); future.addListener(new ChannelFutureListener() { public void operationComplete(ChannelFuture f) throws Exception { if (f.isSuccess()) { System.out.println("Started Tcp Client: " + getServerInfo()); } else { System.out.println("Started Tcp Client Failed: " + getServerInfo()); f.channel().eventLoop().schedule(() -> doConnect(), 1, TimeUnit.SECONDS); } } }); } private String getServerInfo() { return String.format("RemoteHost=%s RemotePort=%d", remotePort, remotePort); } }
可以看到,我們在channelInactive
事件中,也創建了一個任務,在1秒后重新連接。
示例代碼
大家可以自己跑跑看:
https://github.com/dozer47528/AutoReconnectNettyExample
本作品由
Dozer 創作,采用
知識共享署名-非商業性使用 4.0 國際許可協議 進行許可。