架构师养成记--22.客户端与服务器端保持连接的解决方案,netty的ReadTimeoutHandler

概述

保持客户端与服务器端连接的方案常用的有3种

1.长连接,也就是客户端与服务器端一直保持连接,适用于客户端比较少的情况。

2.定时段连接,比如在某一天的凌晨建立连接,适用于对实时性要求不高的情况。

3.设置连接超时,比如超过1分钟没有传输数据就断开连接,等下次需要的时候再建立连接,这种方案比较常用。

netty的ReadTimeOut实现方案3

服务端 

 

大部分代码都保持不变,有变化的代码在第30行,设置服务端的超时时间

 1 import io.netty.bootstrap.ServerBootstrap;
 2 import io.netty.channel.ChannelFuture;
 3 import io.netty.channel.ChannelInitializer;
 4 import io.netty.channel.ChannelOption;
 5 import io.netty.channel.EventLoopGroup;
 6 import io.netty.channel.nio.NioEventLoopGroup;
 7 import io.netty.channel.socket.SocketChannel;
 8 import io.netty.channel.socket.nio.NioServerSocketChannel;
 9 import io.netty.handler.logging.LogLevel;
10 import io.netty.handler.logging.LoggingHandler;
11 import io.netty.handler.timeout.ReadTimeoutHandler;
12 
13 public class Server {
14 
15     public static void main(String[] args) throws Exception{
16         
17         EventLoopGroup pGroup = new NioEventLoopGroup();
18         EventLoopGroup cGroup = new NioEventLoopGroup();
19         
20         ServerBootstrap b = new ServerBootstrap();
21         b.group(pGroup, cGroup)
22          .channel(NioServerSocketChannel.class)
23          .option(ChannelOption.SO_BACKLOG, 1024)
24          //设置日志
25          .handler(new LoggingHandler(LogLevel.INFO))
26          .childHandler(new ChannelInitializer<SocketChannel>() {
27             protected void initChannel(SocketChannel sc) throws Exception {
28                 sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
29                 sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
30                 sc.pipeline().addLast(new ReadTimeoutHandler(5)); 
31                 sc.pipeline().addLast(new ServerHandler());
32             }
33         });
34         
35         ChannelFuture cf = b.bind(8765).sync();
36         
37         cf.channel().closeFuture().sync();
38         pGroup.shutdownGracefully();
39         cGroup.shutdownGracefully();
40         
41     }
42 }

 

ServerHandler代码也没有什么变化

 1 import io.netty.channel.ChannelHandlerAdapter;
 2 import io.netty.channel.ChannelHandlerContext;
 3 
 4 public class ServerHandler extends ChannelHandlerAdapter{
 5 
 6     @Override
 7     public void channelActive(ChannelHandlerContext ctx) throws Exception {
 8 
 9     }
10 
11     @Override
12     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
13         Request request = (Request)msg;
14         System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " + request.getRequestMessage());
15         Response response = new Response();
16         response.setId(request.getId());
17         response.setName("response" + request.getId());
18         response.setResponseMessage("响应内容" + request.getId());
19         ctx.writeAndFlush(response);//.addListener(ChannelFutureListener.CLOSE);
20     }
21 
22     @Override
23     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
24         
25     }
26 
27     @Override
28     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
29         ctx.close();
30     }
31 
32     
33     
34 }

 

 

客户端

客户端的代码也设置了超时时间(实际上只要服务器端设置也就可以了,有人说客户端不设置会出问题,现在还没有发现什么问题)。主要看getChannelFuture这个方法,this.cf == null是第一次连接的时候用到的,!this.cf.channel().isActive() 是连接超时后重新发起连接用到的。再看main方法,可以发现for(int i = 1; i <= 3; i++ ) 这个循环中,每个循环停顿4秒,也就是每隔4秒发送一次请求,而服务器端的超时时间设置为5秒,那么在这个for循环期间连接是不会断开的,等for循环结束 cf.channel().closeFuture().sync(); 断开连接this.cf.channel().isActive()  变为否,在new Thread()中再次发送请求,getChannelFuture会重新建立连接。

  1 import io.netty.bootstrap.Bootstrap;
  2 import io.netty.channel.ChannelFuture;
  3 import io.netty.channel.ChannelInitializer;
  4 import io.netty.channel.EventLoopGroup;
  5 import io.netty.channel.nio.NioEventLoopGroup;
  6 import io.netty.channel.socket.SocketChannel;
  7 import io.netty.channel.socket.nio.NioSocketChannel;
  8 import io.netty.handler.logging.LogLevel;
  9 import io.netty.handler.logging.LoggingHandler;
 10 import io.netty.handler.timeout.ReadTimeoutHandler;
 11 
 12 import java.util.concurrent.TimeUnit;
 13 
 14 
 15 /**
 16  * Best Do It
 17  */
 18 public class Client {
 19     
 20     private static class SingletonHolder {
 21         static final Client instance = new Client();
 22     }
 23     
 24     public static Client getInstance(){
 25         return SingletonHolder.instance;
 26     }
 27     
 28     private EventLoopGroup group;
 29     private Bootstrap b;
 30     private ChannelFuture cf ;
 31     
 32     private Client(){
 33             group = new NioEventLoopGroup();
 34             b = new Bootstrap();
 35             b.group(group)
 36              .channel(NioSocketChannel.class)
 37              .handler(new LoggingHandler(LogLevel.INFO))
 38              .handler(new ChannelInitializer<SocketChannel>() {
 39                     @Override
 40                     protected void initChannel(SocketChannel sc) throws Exception {
 41                         sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
 42                         sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
 43                         //超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
 44                         sc.pipeline().addLast(new ReadTimeoutHandler(5)); 
 45                         sc.pipeline().addLast(new ClientHandler());
 46                     }
 47             });
 48     }
 49     
 50     public void connect(){
 51         try {
 52             this.cf = b.connect("127.0.0.1", 8765).sync();
 53             System.out.println("远程服务器已经连接, 可以进行数据交换..");                
 54         } catch (Exception e) {
 55             e.printStackTrace();
 56         }
 57     }
 58     
 59     public ChannelFuture getChannelFuture(){
 60         
 61         if(this.cf == null){
 62             this.connect();
 63         }
 64         if(!this.cf.channel().isActive()){
 65             this.connect();
 66         }
 67         
 68         return this.cf;
 69     }
 70     
 71     public static void main(String[] args) throws Exception{
 72         final Client c = Client.getInstance();
 73         //c.connect();
 74         
 75         ChannelFuture cf = c.getChannelFuture();
 76         for(int i = 1; i <= 3; i++ ){
 77             Request request = new Request();
 78             request.setId("" + i);
 79             request.setName("pro" + i);
 80             request.setRequestMessage("数据信息" + i);
 81             cf.channel().writeAndFlush(request);
 82             TimeUnit.SECONDS.sleep(4);
 83         }
 84 
 85         cf.channel().closeFuture().sync();
 86         
 87         
 88         new Thread(new Runnable() {
 89             @Override
 90             public void run() {
 91                 try {
 92                     System.out.println("进入子线程...");
 93                     ChannelFuture cf = c.getChannelFuture();
 94                     System.out.println(cf.channel().isActive());
 95                     System.out.println(cf.channel().isOpen());
 96                     
 97                     //再次发送数据
 98                     Request request = new Request();
 99                     request.setId("" + 4);
100                     request.setName("pro" + 4);
101                     request.setRequestMessage("数据信息" + 4);
102                     cf.channel().writeAndFlush(request);                    
103                     cf.channel().closeFuture().sync();
104                     System.out.println("子线程结束.");
105                 } catch (InterruptedException e) {
106                     e.printStackTrace();
107                 }
108             }
109         }).start();
110         
111         System.out.println("断开连接,主线程结束..");
112         
113     }
114     
115     
116     
117 }

 

 

clientHandler没有什么变化

 1 import io.netty.channel.ChannelHandlerAdapter;
 2 import io.netty.channel.ChannelHandlerContext;
 3 import io.netty.util.ReferenceCountUtil;
 4 
 5 public class ClientHandler extends ChannelHandlerAdapter{
 6     
 7     @Override
 8     public void channelActive(ChannelHandlerContext ctx) throws Exception {
 9 
10     }
11 
12     @Override
13     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
14         try {
15             Response resp = (Response)msg;
16             System.out.println("Client : " + resp.getId() + ", " + resp.getName() + ", " + resp.getResponseMessage());            
17         } finally {
18             ReferenceCountUtil.release(msg);
19         }
20     }
21 
22     @Override
23     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
24         
25     }
26 
27     @Override
28     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
29         ctx.close();
30     }
31     
32 }

 

 

 

工厂类不变

 1 import io.netty.handler.codec.marshalling.DefaultMarshallerProvider;
 2 import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider;
 3 import io.netty.handler.codec.marshalling.MarshallerProvider;
 4 import io.netty.handler.codec.marshalling.MarshallingDecoder;
 5 import io.netty.handler.codec.marshalling.MarshallingEncoder;
 6 import io.netty.handler.codec.marshalling.UnmarshallerProvider;
 7 
 8 import org.jboss.marshalling.MarshallerFactory;
 9 import org.jboss.marshalling.Marshalling;
10 import org.jboss.marshalling.MarshallingConfiguration;
11 
12 /**
13  * Marshalling工厂
14  * @author(alienware)
15  * @since 2014-12-16
16  */
17 public final class MarshallingCodeCFactory {
18 
19     /**
20      * 创建Jboss Marshalling解码器MarshallingDecoder
21      * @return MarshallingDecoder
22      */
23     public static MarshallingDecoder buildMarshallingDecoder() {
24         //首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
25         final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
26         //创建了MarshallingConfiguration对象,配置了版本号为5 
27         final MarshallingConfiguration configuration = new MarshallingConfiguration();
28         configuration.setVersion(5);
29         //根据marshallerFactory和configuration创建provider
30         UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
31         //构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
32         MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024);
33         return decoder;
34     }
35 
36     /**
37      * 创建Jboss Marshalling编码器MarshallingEncoder
38      * @return MarshallingEncoder
39      */
40     public static MarshallingEncoder buildMarshallingEncoder() {
41         final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
42         final MarshallingConfiguration configuration = new MarshallingConfiguration();
43         configuration.setVersion(5);
44         MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
45         //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
46         MarshallingEncoder encoder = new MarshallingEncoder(provider);
47         return encoder;
48     }
49 }

 

 

自定义的Request和Response对象没有什么变化,这里不再赘述

 

posted on 2017-02-07 09:16  司广孟  阅读(13550)  评论(4编辑  收藏  举报

导航