架构师养成记--22.客户端与服务器端保持连接的解决方案,netty的ReadTimeoutHandler
概述
保持客户端与服务器端连接的方案常用的有3种
1.长连接,也就是客户端与服务器端一直保持连接,适用于客户端比较少的情况。
2.定时段连接,比如在某一天的凌晨建立连接,适用于对实时性要求不高的情况。
3.设置连接超时,比如超过1分钟没有传输数据就断开连接,等下次需要的时候再建立连接,这种方案比较常用。
netty的ReadTimeOut实现方案3
服务端
大部分代码都保持不变,有变化的代码在第30行,设置服务端的超时时间
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;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler; public class Server { public static void main(String[] args) throws Exception{ EventLoopGroup pGroup = new NioEventLoopGroup();
EventLoopGroup cGroup = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap();
b.group(pGroup, cGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
//设置日志
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(new ServerHandler());
}
}); ChannelFuture cf = b.bind(8765).sync(); cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully(); }
}
ServerHandler代码也没有什么变化
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; public class ServerHandler extends ChannelHandlerAdapter{ @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { } @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Request request = (Request)msg;
System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " + request.getRequestMessage());
Response response = new Response();
response.setId(request.getId());
response.setName("response" + request.getId());
response.setResponseMessage("响应内容" + request.getId());
ctx.writeAndFlush(response);//.addListener(ChannelFutureListener.CLOSE);
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
} }
客户端
客户端的代码也设置了超时时间(实际上只要服务器端设置也就可以了,有人说客户端不设置会出问题,现在还没有发现什么问题)。主要看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会重新建立连接。
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.ReadTimeoutHandler; import java.util.concurrent.TimeUnit; /**
* Best Do It
*/
public class Client { private static class SingletonHolder {
static final Client instance = new Client();
} public static Client getInstance(){
return SingletonHolder.instance;
} private EventLoopGroup group;
private Bootstrap b;
private ChannelFuture cf ; private Client(){
group = new NioEventLoopGroup();
b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel sc) throws Exception {
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
//超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
sc.pipeline().addLast(new ReadTimeoutHandler(5));
sc.pipeline().addLast(new ClientHandler());
}
});
} public void connect(){
try {
this.cf = b.connect("127.0.0.1", 8765).sync();
System.out.println("远程服务器已经连接, 可以进行数据交换..");
} catch (Exception e) {
e.printStackTrace();
}
} public ChannelFuture getChannelFuture(){ if(this.cf == null){
this.connect();
}
if(!this.cf.channel().isActive()){
this.connect();
} return this.cf;
} public static void main(String[] args) throws Exception{
final Client c = Client.getInstance();
//c.connect(); ChannelFuture cf = c.getChannelFuture();
for(int i = 1; i <= 3; i++ ){
Request request = new Request();
request.setId("" + i);
request.setName("pro" + i);
request.setRequestMessage("数据信息" + i);
cf.channel().writeAndFlush(request);
TimeUnit.SECONDS.sleep(4);
} cf.channel().closeFuture().sync(); new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("进入子线程...");
ChannelFuture cf = c.getChannelFuture();
System.out.println(cf.channel().isActive());
System.out.println(cf.channel().isOpen()); //再次发送数据
Request request = new Request();
request.setId("" + 4);
request.setName("pro" + 4);
request.setRequestMessage("数据信息" + 4);
cf.channel().writeAndFlush(request);
cf.channel().closeFuture().sync();
System.out.println("子线程结束.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start(); System.out.println("断开连接,主线程结束.."); } }
clientHandler没有什么变化
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil; public class ClientHandler extends ChannelHandlerAdapter{ @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception { } @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
Response resp = (Response)msg;
System.out.println("Client : " + resp.getId() + ", " + resp.getName() + ", " + resp.getResponseMessage());
} finally {
ReferenceCountUtil.release(msg);
}
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
} }
工厂类不变
import io.netty.handler.codec.marshalling.DefaultMarshallerProvider;
import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallerProvider;
import io.netty.handler.codec.marshalling.MarshallingDecoder;
import io.netty.handler.codec.marshalling.MarshallingEncoder;
import io.netty.handler.codec.marshalling.UnmarshallerProvider; import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration; /**
* Marshalling工厂
* @author(alienware)
* @since 2014-12-16
*/
public final class MarshallingCodeCFactory { /**
* 创建Jboss Marshalling解码器MarshallingDecoder
* @return MarshallingDecoder
*/
public static MarshallingDecoder buildMarshallingDecoder() {
//首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
//创建了MarshallingConfiguration对象,配置了版本号为5
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
//根据marshallerFactory和configuration创建provider
UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
//构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024);
return decoder;
} /**
* 创建Jboss Marshalling编码器MarshallingEncoder
* @return MarshallingEncoder
*/
public static MarshallingEncoder buildMarshallingEncoder() {
final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
//构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
MarshallingEncoder encoder = new MarshallingEncoder(provider);
return encoder;
}
}
自定义的Request和Response对象没有什么变化,这里不再赘述
架构师养成记--22.客户端与服务器端保持连接的解决方案,netty的ReadTimeoutHandler的更多相关文章
- 架构师养成记--35.redis集群搭建
前记:redis哨兵经验之谈.哨兵做主从切换可能要花费一两秒,这一两秒可能会丢失很多数据.解决方法之一是在java代码中做控制,try catch 到 链接断开的异常就sleep 一两秒钟再conti ...
- 架构师养成记--21.netty编码解码
背景 作为网络传输框架,免不了哟啊传输对象,对象在传输之前就要序列化,这个序列化的过程就是编码过程.接收到编码后的数据就需要解码,还原传输的数据. 代码 工厂类 import io.netty.han ...
- 架构师养成记--19.netty
一.Netty初步 为什么选择Netty? 和NIO比较,要实现一个通信要简单得很多,性能很好.分布式消息中间件.storm.Dubble都是使用Netty作为底层通信. Netty5.0要求jdk1 ...
- 架构师养成记--9.future模式讲解
什么是future模式呢?解释这个概念之前我们先来了解一个场景吧,财务系统的结账功能,这个功能可能是每个月用一次,在这一个月中相关的数据量已经积累得非常大,这一个功能需要调用好几个存储过程来完成.假如 ...
- 架构师养成记--30.Redis环境搭建
Redis的安装 下载地址http://redis.io/download 安装步骤: 首先需要安装gcc,把下载好的redis-3.0.0-rc2.tar.gz 放到 /usr/local 文件夹下 ...
- 架构师养成记--20.netty的tcp拆包粘包问题
问题描述 比如要发ABC DEFG HIJK 这一串数据,其中ABC是一个包,DEFG是一个包,HIJK是一个包.由于TCP是基于流发送的,所以有可能出现ABCD EFGH 这种情况,那么ABC和D就 ...
- 架构师养成记--18.NIO
有人叫new IO 我这里就叫Non-block IO 经典概念: Buffer(缓冲区):之前直接通过流,现在提供一个buffer存放数据. Channel:管道,包括ServerSocketCha ...
- 架构师养成记--15.Disruptor并发框架
一.概述 disruptor对于处理并发任务很擅长,曾有人测过,一个线程里1s内可以处理六百万个订单,性能相当感人. 这个框架的结构大概是:数据生产端 --> 缓存 --> 消费端 缓存中 ...
- 架构师养成记--14.重入锁ReentrantLock 和 读写锁 ReentrantReadWriteLock
ReentrantLock 有嗅探锁定和多路分支等功能,其实就是synchronized,wait,notify的升级. this锁定当前对象不方便,于是就有了用new Object()来作为锁的解决 ...
随机推荐
- mysql新建用户并授权
参考:https://www.cnblogs.com/zhangdiIT/p/8116776.html
- iOS界面设计,12个优秀案例激发你的灵感
总所周知,iOS和Android是当今两大移动平台,前者采用Human Interface Design,后者采用Material Design.作为设计师,尤其是App设计师,总是会在这两者进行设计 ...
- centos7 redis伪集群安装
安装gcc: yum install gcc -y 上传redis软件包到 /home下 解压: tar xf redis-3.2.11.tar.gz 进入 redis 目录 : cd r ...
- SLAM拾萃(2):doxygen
今天给大家介绍一下doxygen.这个工具由来已久了,至少08年左右就已经在用了,但是目前还没见到好的介绍.我个人觉得这是个很简单易用的工具,但是为什么看了别人介绍反而觉得复杂了……所以趁着今天比较闲 ...
- apicloud 和 微信小程序,你会用哪 个?
微信 小程序开始火了,app跨平台的革命再次高涨,不得不说,不用再担心android和ios双版本开发成本,及h5的开发 和apicloud一样,不需要关注平台问题,只需要关注前端js.css就能大a ...
- Access denied for user 'root'@'MiWiFi-Ryyy-srv' (using password: YES)
虽然是跟很多人一样的问题但是原因不同,其他很多文章说是授权问题,也确实是授权问题,但是,配置文件写的是连接localhost,而这里不知道什么原因切换了使用的用户,变成了默认访问MiWiFi-Ryyy ...
- Linux 非阻塞connect,错误码:EINPROGRESS
当我们以非阻塞的方式来进行连接的时候,返回的结果如果是 -1,这并不代表这次连接发生了错误,如果它的返回结果是 EINPROGRESS,那么就代表连接还在进行中. 后面可以通过poll或者select ...
- 自我介绍及如何注册GITHUB
自我介绍 我是来自南通大学网络工程141班的周楠,我的学号是1413042014,我的兴趣是喜欢玩游戏(如果这算是一个兴趣爱好的话),喜欢尝试各种游戏. 如何注册一个GitHub账号? 1.首先我们需 ...
- Python学习-6.Python的分支语句
Python的分支语句比较简单,只有if.else.elif三个关键字,也就是说,Python没有switch语句,而且,Python中并没有?:这个三目运算符. 例子: age = 18 if ag ...
- 使用Python登录腾讯MTA数据分析平台,然后获取相关数据
思路: 第一步:使用pypeteer.launcher打开浏览器, 第二步:找到mta的登录页面,默认是使用QQ登录的,需要再触发一下切换使用帐号密码登录的按钮(通过使用iframe嵌入的腾讯单点登录 ...