这里说一下真实项目应用中使用Netty的场景,大体上一些参数的设置都是根据服务器的性能决定的,这不是我要说的事。
我们要考虑得是2台机器(或者多台)使用Netty进行的通信,大体上分为三种:
第一种:使用长连接的通道不断开的方式进行通信,也就是客户端和服务端的通道一直处于开启状态,如果服务器性能足够好,并且我们的客户端数量也比较少的情况下,这种方式可以考虑使用。
第二种:一次性批量提交数据,采用短连接方式,也就是我们会把数据保存在本地临时缓冲区或者临时表当中,当达到临界值的时候进行一次性批量提交,又或者根据定时任务轮询提交,这种情况弊端是做不到实时性传输,在对实施性不高的应用中建议使用。
第三种:我们可以使用一种特殊的长连接方式,在指定的某一段时间内,服务端与客户端没有任何通信,则断开连接,下次连接则是客户端向服务端发起请求的时候,再次建立连接,但是这种情况我们必须考虑2个因素:
一:如何在超时(即服务端与客户端没有任何通信)后关闭通道?关闭通道后又如何再次建立连接;
二:客户端宕机时,我们无需考虑,下次客户端重启之后即可再次与服务端建立连接,但如果是服务器宕机,我们的客户端如何与服务端建立连接呢?
因为前面所有的基本都是第一种方式,这里就不再赘述,第二种不好模拟,这里做一下第三种:
在这里我开启了一个子线程模拟客户端断开后进行重连操作:这里的request和response以及对应的handler类和MyMarshallingFactory类都和上一章一样,这里不再重复粘贴,看下代码:
client:
package cn.lh.netty2;
import java.util.concurrent.TimeUnit;
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.timeout.ReadTimeoutHandler;
public class NettyClient {
private static class SingalInstance{
static final NettyClient instance = new NettyClient();
}
public static NettyClient getInstance(){
return SingalInstance.instance;
}
private EventLoopGroup group;
private Bootstrap bootstrap;
private ChannelFuture cf;
private NettyClient(){
group = new NioEventLoopGroup();
bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(MyMarshallingFactory.buildMarshallingDecoder());
ch.pipeline().addLast(MyMarshallingFactory.buildMarshallingEncoder());
ch.pipeline().addLast(new ReadTimeoutHandler(5));
ch.pipeline().addLast(new NettyClientHandler());
}
});
}
public void connect(){
try {
this.cf = bootstrap.connect("127.0.0.1", 8888).sync();
System.out.println("远程服务器已经连接,可以开始通信");
} catch (Exception e) {
e.printStackTrace();
}
}
public ChannelFuture getFuture(){
if(this.cf == null){
this.connect();
}
if(!this.cf.channel().isActive()){
this.connect();
}
return this.cf;
}
public static void main(String[] args) throws InterruptedException {
final NettyClient client = NettyClient.getInstance();
ChannelFuture future = client.getFuture();
for(int i=0;i<=3;i++){
Request request = new Request();
request.setId(i);
request.setMsg("请求消息"+i);
future.channel().writeAndFlush(request);
TimeUnit.SECONDS.sleep(4);
}
future.channel().closeFuture().sync();
new Thread(new Runnable() {
public void run() {
try {
System.out.println("进入了子线程");
ChannelFuture f = client.getFuture();
System.out.println(f.channel().isOpen());
System.out.println(f.channel().isActive());
Request request = new Request();
request.setId(50);
request.setMsg("子线程请求消息");
f.channel().writeAndFlush(request);
f.channel().closeFuture().sync();
System.out.println("子线程模拟结束");
} catch (Exception e) {
// TODO: handle exception
}
}
}).start();
System.out.println("连接结束,关闭客户端");
}
}
server类:
package cn.lh.netty2;
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.timeout.ReadTimeoutHandler;
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup boss = new NioEventLoopGroup();
EventLoopGroup worker = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boss, worker)
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.SO_SNDBUF, 32*1024)
.option(ChannelOption.SO_RCVBUF, 32*1024)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(MyMarshallingFactory.buildMarshallingDecoder());
ch.pipeline().addLast(MyMarshallingFactory.buildMarshallingEncoder());
ch.pipeline().addLast(new ReadTimeoutHandler(5));
ch.pipeline().addLast(new NettyServerHandler());
}
});
ChannelFuture f = bootstrap.bind(8888).sync();
f.channel().closeFuture().sync();
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
看下结果:
client:
远程服务器已经连接,可以开始通信
server response:0--回应消息0
server response:1--回应消息1
server response:2--回应消息2
server response:3--回应消息3
连接结束,关闭客户端
进入了子线程
远程服务器已经连接,可以开始通信
true
true
server response:50--回应消息50
子线程模拟结束
server:
client request:0请求消息0
client request:1请求消息1
client request:2请求消息2
client request:3请求消息3
io.netty.handler.timeout.ReadTimeoutException
client request:50子线程请求消息
io.netty.handler.timeout.ReadTimeoutException
关于其中的逻辑其实很简单,大家注意ChannelFuture的获取方式,我认为没有什么值得好说的,有一些java基础的应该都可以看懂。