netty(十四)netty client 重连模型
本次作实践,序列号承接 netty(十三)netty 心跳 with protobuf
public class Client4Reconnect {
/**
* 可用的
*/
public static ConcurrentSet<String> setAvailableServers = new ConcurrentSet<>();
/**
* 记录Proxy传递的信息服务清单,及相应的线程,线程可能是正常连接,也可能在重连
*/
private static ConcurrentHashMap<String, Thread> mapThread = new ConcurrentHashMap();
private static final String SPLITTER = ":";
/**
* 外部开启信任某后端服务
* @param index
*/
public static void create(String index) {
Thread thread = new Thread() {
@Override
public void run() {
new Client4Reconnect(index).boot();
}
};
mapThread.put(index, thread);
System.out.println("***************开启信任线程-" + index + SPLITTER + thread.getName());
thread.start();
}
/**
* 外部不再信任某后端服务
* @param index */
public static void interrupt(String index) {
Thread thread = mapThread.get(index);
thread.interrupt();
mapThread.remove(index);
System.out.println("***************退出信任线程-" + index + SPLITTER + thread.getName());
}
/**
* 内部将自身服务置为可用,对外暴露
*/
private void addMe() {
setAvailableServers.add(index);
System.out.println("--------------新增可用服务" + index);
}
/**
* 内部将自身服务置为不可用,对外不暴露
*/
private void removeMe() {
setAvailableServers.remove(index);
System.out.println("--------------去除可用服务" + index);
}
private String ipAddress;
private Integer port;
private String index;
private Client4Reconnect(String index) {
String [] items = index.split(SPLITTER);
String ipAddress = items[0];
int port = Integer.valueOf(items[1]);
this.ipAddress = ipAddress;
this.port = port;
this.index = index;
}
private void reConnect() {
connect();
}
private void connect() {
try {
ChannelFuture futrue = bootstrap.connect(new InetSocketAddress(ipAddress, port)).sync();
futrue.addListener(new ConnectionListener(this));
System.out.println("连接成功-" + index);
addMe();
futrue.channel().closeFuture().sync();
System.out.println("连接异常中断,重新连接-" + index);
removeMe();
reConnect();
} catch (InterruptedException e) {
System.out.println("终止信任,断开连接,服务不再启用-" + index);
removeMe();
} catch (Exception e) {
System.out.println("连接失败-" + index);
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
System.out.println("终止信任,不再重连-" + index);
return;
}
System.out.println("重新连接-" + index);
reConnect();
} finally {
worker.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
String ipAddress = "127.0.0.1";
int port = 8866;
String index = ipAddress + SPLITTER + port;
create(index);
Thread.sleep(10000);
interrupt(index);
}
private Bootstrap bootstrap;
private EventLoopGroup worker;
private void boot() {
worker = new NioEventLoopGroup();
bootstrap = new Bootstrap();
bootstrap.group(worker);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new LengthFieldBasedFrameDecoder(10000, 0, 4, 0, 4));
pipeline.addLast(new ProtobufDecoder(MyBaseProto.BaseProto.getDefaultInstance()));
pipeline.addLast(new LengthFieldPrepender(4));
pipeline.addLast(new ProtobufEncoder());
pipeline.addLast(new IdleStateHandler(61, 30, 0, TimeUnit.SECONDS));
pipeline.addLast(new ClientHeartbeatHandler());
pipeline.addLast(new ClientHandler4Heart());
}
});
connect();
}
private static class ConnectionListener implements ChannelFutureListener {
Client4Reconnect client4Reconnect;
public ConnectionListener(Client4Reconnect client4Reconnect) {
this.client4Reconnect = client4Reconnect;
}
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
if (!channelFuture.isSuccess()) {
System.out.println("Reconnect");
final EventLoop loop = channelFuture.channel().eventLoop();
loop.schedule(new Runnable() {
@Override
public void run() {
client4Reconnect.connect();
}
}, 3L, TimeUnit.SECONDS);
}
}
}
}
一个信任服务集合mapThread,表示人认为的后端服务列表,一个可用服务集合setAvailableServers,表示后端实际可用也在用的服务集合
mapThread >= setAvailableServers ,相差的信任服务中不可用当机的服务
2个地方可以响应外部打断,表示外部不再信任相应的后台服务器,所以不再重连
1 正常连接状态时,netty 响应,
2 重连时sleep 3s 间响应
而连接失败和中断(比如断网后无心跳close)的2种情况,仍认为外部信任相应的后台服务器,故重连
针对这两种状态,我们做测试:
首先,关掉后端服务:主线程等待10s后打断netty线程,模拟信任服务下线
***************开启信任线程-127.0.0.1:8866:Thread-1
连接失败-127.0.0.1:8866
重新连接-127.0.0.1:8866
连接失败-127.0.0.1:8866
重新连接-127.0.0.1:8866
连接失败-127.0.0.1:8866
重新连接-127.0.0.1:8866
连接失败-127.0.0.1:8866
***************退出信任线程-127.0.0.1:8866:Thread-1
终止信任,不再重连-127.0.0.1:8866
然后,开启后端服务:
***************开启信任线程-127.0.0.1:8866:Thread-1
连接成功-127.0.0.1:8866
--------------新增可用服务127.0.0.1:8866:ALL - [127.0.0.1:8866]
SERVER'S general msg RESPONSE : code: 2
msg: "SERVER:hello"
***************退出信任线程-127.0.0.1:8866:Thread-1
终止信任,断开连接,服务不再启用-127.0.0.1:8866
--------------去除可用服务127.0.0.1:8866:ALL - []
channelInactive
再针对2种情况,做测试
(1)后端先开后关,模拟服务当机或者关闭重启
***************开启信任线程-127.0.0.1:8866:Thread-1
连接成功-127.0.0.1:8866
--------------新增可用服务127.0.0.1:8866:ALL - [127.0.0.1:8866]
SERVER'S general msg RESPONSE : code: 2
msg: "SERVER:hello"
连接异常中断,重新连接-127.0.0.1:8866
--------------去除可用服务127.0.0.1:8866:ALL - []
channelInactive
连接失败-127.0.0.1:8866
重新连接-127.0.0.1:8866
连接失败-127.0.0.1:8866
重新连接-127.0.0.1:8866
连接失败-127.0.0.1:8866
***************退出信任线程-127.0.0.1:8866:Thread-1
终止信任,不再重连-127.0.0.1:8866
(2)后端先关后开,模拟服务重启
***************开启信任线程-127.0.0.1:8866:Thread-1
连接失败-127.0.0.1:8866
重新连接-127.0.0.1:8866
连接失败-127.0.0.1:8866
重新连接-127.0.0.1:8866
连接成功-127.0.0.1:8866
--------------新增可用服务127.0.0.1:8866:ALL - [127.0.0.1:8866]
SERVER'S general msg RESPONSE : code: 2
msg: "SERVER:hello"
***************退出信任线程-127.0.0.1:8866:Thread-1
终止信任,断开连接,服务不再启用-127.0.0.1:8866
--------------去除可用服务127.0.0.1:8866:ALL - []
channelInactive
1.9补充,需有以下地方安排重连:
1 connect直接抛异常
2 connect回调返回fail情况
3 若被服务端切断连接,则 handler inactive 中定时重连
本例子显式保证了1,2
对于3 并未显式在channel inactive中重连,而是在futrue.channel().closeFuture().sync();安排重连
浙公网安备 33010602011771号