实践生产环境中netty如何使用,项目应用如何去考虑Netty的使用,大体上对于一些参数设置都是根据服务器性能决定,这个不是最主要;
数据通信
需要考虑的问题是生产环境中两台(多台)服务器之间使用Netty是怎样进行通信,我个人大体上把他分为三种:
第一种,使用长连接通道不断开的形式进行通信,也就是服务器和客户端的通道一直处于开启状态,如果服务器性能足够好,并且我们的客户端数量也比较少(万以下的长连接基本不会有影响)的情况下,还是推荐这种方式;
第二种,一次性批量提交数据,采用短连接方式。也就是会把数据保存在本地临时缓冲区或者临时表里,当达到临界值时进行一次性批量提交,又或者根据定时任务轮询提交,这种情况弊端是做不到实时性传输,在对实时性不高的应用程序中可以推荐使用。
第三种(主流推荐),可以使用一种特殊的长连接,在指定某一时间之内,服务器与某台客户端没有任何通信,则断开连接;下次连接则是客户端向服务器发送请求的时候,再次建立连接;但是这种模式需要考虑2个因素:
-1 如何在超时(即服务器和客户端没有任何通信)后关闭通道?关闭通道后又如何再次建立连接?
设置60秒极限阈值,60秒服务器与客户端午通信就将通道断开(类似连接池机制),下一次客户端有新数据在发起一次新的链接;
-2 客户端宕机时,无需考虑,下次客户端重启之后我们就可以与服务器建立连接,但是服务器宕机时,客户端如何与服务器进行连接呢?
客户端宕机下次重启后会再与服务器建立连接;客户端底层轮询机制,在周期内客户端主动查看服务器状态,并尝试建立连接;
package com.cc.netty.best.runtime;
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();
//AbstractNioSelector 关于reactor模型
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));//超时handler,阀值5秒
sc.pipeline().addLast(new ServerHandler());
}
});
ChannelFuture cf = b.bind(8765).sync();
cf.channel().closeFuture().sync();
pGroup.shutdownGracefully();
cGroup.shutdownGracefully();
}
}
package com.cc.netty.best.runtime;
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 --- 可以尝试enum创建单例模式
*/
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));//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();
}
}
/** 获取ChannelFuture方法
* 1 当前cf为空就调用connect()重新连接
* 2 当前cf不可用就connect()重新连接
* 3 存活可用直接返回
*/
public ChannelFuture getChannelFuture(){
if(this.cf == null){
this.connect();
}
if(!this.cf.channel().isActive()){
this.connect();
}
return this.cf;
}
/**
* 关闭客户端应用
* @throws InterruptedException
*/
public void close() throws InterruptedException{
this.cf.channel().closeFuture().sync();
this.group.shutdownGracefully();
}
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);//每写出一条数据等待4秒钟
}
/** 发送3次消息循环结束,5秒后触发超时Handler_ReadTimeoutHandler(5),
* -通信断开,通道被关闭,但客户端应用未关闭;
*/
//c.close();//调用close()关闭客户端应用
/** 启动一个子进程,在客户端应用未关闭下重新发起通信
* -发送第4条数据
*/
new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
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("子线程结束.");
//c.close();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}