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();安排重连

 

posted on 2018-10-16 14:59  silyvin  阅读(503)  评论(0)    收藏  举报