Netty——基本使用介绍

https://blog.csdn.net/haoyuyang/article/details/53243785

1.为什么选择Netty

上一篇文章我们已经了解了Socket通信(IO/NIO/AIO)编程,对于通信模型已经有了一个基本的认识。其实上一篇文章中,我们学习的仅仅是一个模型,如果想把这些真正的用于实际工作中,那么还需要不断的完善、扩展和优化。比如经典的TCP读包写包问题,或者是数据接收的大小,实际的通信处理与应答的处理逻辑等等一些细节问题需要认真的去思考,而这些都需要大量的时间和经历,以及丰富的经验。所以想学好Socket通信不是件容易事,那么接下来就来学习一下新的技术Netty,为什么会选择Netty?因为它简单!使用Netty不必编写复杂的逻辑代码去实现通信,再也不需要去考虑性能问题,不需要考虑编码问题,半包读写等问题。强大的Netty已经帮我们实现好了,我们只需要使用即可。

Netty是最流行的NIO框架,它的健壮性、功能、性能、可定制性和可扩展性在同类框架都是首屈一指的。它已经得到成百上千的商业/商用项目验证,如Hadoop的RPC框架Avro、RocketMQ以及主流的分布式通信框架Dubbox等等。

2.Netty简介

Netty是基于Java NIO client-server的网络应用框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供了一种新的方式来开发网络应用程序,这种新的方式使它很容易使用和具有很强的扩展性。Netty的内部实现是很复杂的,但是Netty提供了简单易用的API从网络处理代码中解耦业务逻辑。Netty是完全基于NIO实现的,所以整个Netty都是异步的。

网络应用程序通常需要有较高的可扩展性,无论是Netty还是其他的基于Java Nio的框架,都会提供可扩展性的解决方案。Netty中一个关键组成部分是它的异步特性,本片文章将讨论同步(阻塞)和异步(非阻塞)的IO来说明为什么使用异步代码解决扩展性问题以及如何使用异步。

3.Netty架构组成(借用一下网上的图片)

Netty实现原理浅析,写的很不错,感兴趣的可以看一下。

4.Helloworld入门

在学习Netty之前,先来回顾一下NIO的通信步骤:

①创建ServerSocketChannel,为其配置非阻塞模式。

②绑定监听,配置TCP参数,录入backlog大小等。

③创建一个独立的IO线程,用于轮询多路复用器Selector。

④创建Selector,将之前创建的ServerSocketChannel注册到Selector上,并设置监听标识位SelectionKey.OP_ACCEPT。

⑤启动IO线程,在循环体中执行Selector.select()方法,轮询就绪的通道。

⑥当轮询到处于就绪状态的通道时,需要进行操作位判断,如果是ACCEPT状态,说明是新的客户端接入,则调用accept方法接收新的客户端。

⑦设置新接入客户端的一些参数,如非阻塞,并将其继续注册到Selector上,设置监听标识位等。

⑧如果轮询的通道标识位是READ,则进行读取,构造Buffer对象等。

⑨更细节的问题还有数据没发送完成继续发送的问题......

好啦,开始学习Netty了。先去http://netty.io/上下载所有的Netty包。

Netty通信的步骤:

①创建两个NIO线程组,一个专门用于网络事件处理(接受客户端的连接),另一个则进行网络通信的读写。

②创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传出数据的缓存大小等。

③创建一个用于实际处理数据的类ChannelInitializer,进行初始化的准备工作,比如设置接受传出数据的字符集、格式以及实际处理数据的接口。

④绑定端口,执行同步阻塞方法等待服务器端启动即可。

强烈推荐读一读Netty官方翻译文档。

好了,说了那么多,下面就来HelloWorld入门吧!

服务器端:

public class Server {

private int port;

public Server(int port) {
this.port = port;
}

public void run() {
EventLoopGroup bossGroup = new NioEventLoopGroup(); //用于处理服务器端接收客户端连接
EventLoopGroup workerGroup = new NioEventLoopGroup(); //进行网络通信(读写)
try {
ServerBootstrap bootstrap = new ServerBootstrap(); //辅助工具类,用于服务器通道的一系列配置
bootstrap.group(bossGroup, workerGroup) //绑定两个线程组
.channel(NioServerSocketChannel.class) //指定NIO的模式
.childHandler(new ChannelInitializer<SocketChannel>() { //配置具体的数据处理方式
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ServerHandler());
}
})
/**
* 对于ChannelOption.SO_BACKLOG的解释:
* 服务器端TCP内核维护有两个队列,我们称之为A、B队列。客户端向服务器端connect时,会发送带有SYN标志的包(第一次握手),服务器端
* 接收到客户端发送的SYN时,向客户端发送SYN ACK确认(第二次握手),此时TCP内核模块把客户端连接加入到A队列中,然后服务器接收到
* 客户端发送的ACK时(第三次握手),TCP内核模块把客户端连接从A队列移动到B队列,连接完成,应用程序的accept会返回。也就是说accept
* 从B队列中取出完成了三次握手的连接。
* A队列和B队列的长度之和就是backlog。当A、B队列的长度之和大于ChannelOption.SO_BACKLOG时,新的连接将会被TCP内核拒绝。
* 所以,如果backlog过小,可能会出现accept速度跟不上,A、B队列满了,导致新的客户端无法连接。要注意的是,backlog对程序支持的
* 连接数并无影响,backlog影响的只是还没有被accept取出的连接
*/
.option(ChannelOption.SO_BACKLOG, 128) //设置TCP缓冲区
.option(ChannelOption.SO_SNDBUF, 32 * 1024) //设置发送数据缓冲大小
.option(ChannelOption.SO_RCVBUF, 32 * 1024) //设置接受数据缓冲大小
.childOption(ChannelOption.SO_KEEPALIVE, true); //保持连接
ChannelFuture future = bootstrap.bind(port).sync();
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}

public static void main(String[] args) {
new Server(8379).run();
}
}

ServerHandler类:

public class ServerHandler extends ChannelHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

//do something msg
ByteBuf buf = (ByteBuf)msg;
byte[] data = new byte[buf.readableBytes()];
buf.readBytes(data);
String request = new String(data, "utf-8");
System.out.println("Server: " + request);
//写给客户端
String response = "我是反馈的信息";
ctx.writeAndFlush(Unpooled.copiedBuffer("888".getBytes()));
//.addListener(ChannelFutureListener.CLOSE);

}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}

}

客户端:

public class Client {

public static void main(String[] args) throws InterruptedException {
EventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new ClientHandler());
}
});
ChannelFuture future = bootstrap.connect("127.0.0.1", 8379).sync();
future.channel().writeAndFlush(Unpooled.copiedBuffer("777".getBytes()));
future.channel().closeFuture().sync();
workerGroup.shutdownGracefully();
}

}

ClientHandler类:
public class ClientHandler extends ChannelHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
ByteBuf buf = (ByteBuf) msg;
byte[] data = new byte[buf.readableBytes()];
buf.readBytes(data);
System.out.println("Client:" + new String(data).trim());
} finally {
ReferenceCountUtil.release(msg);
}
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}

}

运行结果:

5.TCP粘包、拆包问题

熟悉TCP编程的可能都知道,无论是服务器端还是客户端,当我们读取或者发送数据的时候,都需要考虑TCP底层的粘包/拆包机制。

TCP是一个“流”协议,所谓流就是没有界限的遗传数据。大家可以想象一下,如果河水就好比数据,他们是连成一片的,没有分界线,TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的具体情况进行包的划分,也就是说,在业务上一个完整的包可能会被TCP分成多个包进行发送,也可能把多个小包封装成一个大的数据包发送出去,这就是所谓的粘包/拆包问题。

解决方案:

①消息定长,例如每个报文的大小固定为200个字节,如果不够,空位补空格。

②在包尾部增加特殊字符进行分割,例如加回车等。

③将消息分为消息头和消息体,在消息头中包含表示消息总长度的字段,然后进行业务逻辑的处理。

Netty中解决TCP粘包/拆包的方法:

①分隔符类:DelimiterBasedFrameDecoder(自定义分隔符)

②定长:FixedLengthFrameDecoder

6.Netty编解码技术

通常我们也习惯将编码(Encode)成为序列化,它将数据序列化为字节数组,用于网络传输、数据持久化或者其他用途。反之,解码(Decode)/反序列化(deserialization)

把从网络、磁盘等读取的字节数组还原成原始对象(通常是原始对象的拷贝),以方便后续的业务逻辑操作。进行远程跨进程服务调用时(例如RPC调用),需要使用特定的编解码技术,对需要进行网络传输的对象做编码或者解码,以便完成远程调用。

主流的编解码框架:

①JBoss的Marshalling包

②google的Protobuf

③基于Protobuf的Kyro

④MessagePack框架

上代码,一读就懂,注意红色字体部分。

服务器端:

public class Server {

public Server(int port) {

EventLoopGroup bossGroup = newNioEventLoopGroup();

EventLoopGroup workerGroup = newNioEventLoopGroup();

try {

ServerBootstrap bootstrap = newServerBootstrap();

bootstrap.group(bossGroup, workerGroup)

.channel(NioServerSocketChannel.class)

.handler(newLoggingHandler(LogLevel.INFO))

.childHandler(newChannelInitializer<SocketChannel>() {

@Override

protected voidinitChannel(SocketChannel socketChannel) throws Exception {

socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());

socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());

socketChannel.pipeline().addLast(new ServerHandler());

}

})

.option(ChannelOption.SO_BACKLOG,1024)

.option(ChannelOption.SO_RCVBUF, 32 * 1024)

.option(ChannelOption.SO_SNDBUF, 32 * 1024)

.option(ChannelOption.SO_KEEPALIVE, true);

ChannelFuture future = bootstrap.bind(port).sync();

future.channel().closeFuture().sync();

} catch (Exception e) {

e.printStackTrace();

} finally {

bossGroup.shutdownGracefully();

workerGroup.shutdownGracefully();

}

}

public static void main(String[] args) {

new Server(8765);

}

}

ServerHandler类:

public classServerHandler extends ChannelHandlerAdapter {

@Override

public voidexceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

cause.printStackTrace();

ctx.close();

}

@Override

public voidchannelActive(ChannelHandlerContext ctx) throws Exception {

super.channelActive(ctx);

}

@Override

public void channelRead(ChannelHandlerContextctx, Object msg) throws Exception {

Request request = (Request) msg;

System.out.println("Server:"+ request.getId() + "," + request.getName() + "," +request.getReqeustMessag());

Response response = new Response();

response.setId(request.getId());

response.setName("response "+ request.getId());

response.setResponseMessage("响应内容:" +request.getReqeustMessag());

byte[] unGizpData =GzipUtils.unGzip(request.getAttachment());

char separator = File.separatorChar;

FileOutputStream outputStream = newFileOutputStream(System.getProperty("user.dir") + separator +"recieve" + separator + "1.png");

outputStream.write(unGizpData);

outputStream.flush();

outputStream.close();

ctx.writeAndFlush(response);

}

}

客户端:

public class Client {

public static void main(String[] args) {

EventLoopGroup workerGroup = newNioEventLoopGroup();

try {

Bootstrap bootstrap = new Bootstrap();

bootstrap.group(workerGroup)

.handler(newLoggingHandler(LogLevel.INFO))

.channel(NioSocketChannel.class)

.handler(newChannelInitializer<SocketChannel>() {

@Override

protected voidinitChannel(SocketChannel socketChannel) throws Exception {

socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());

socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());

socketChannel.pipeline().addLast(new ClientHandler());

}

});

ChannelFuture future =bootstrap.connect(new InetSocketAddress("127.0.01", 8765)).sync();

for(int i=1; i<=5; i++) {

Request request = newRequest();

request.setId(i);

request.setName("pro"+ i);

request.setReqeustMessag("数据信息" + i);

//传输图片

char separator =File.separatorChar;

File file = newFile(System.getProperty("user.dir") + separator + "source"+ separator + "2.jpg");

FileInputStream inputStream = newFileInputStream(file);

byte[] data = newbyte[inputStream.available()];

inputStream.read(data);

inputStream.close();

byte[] gzipData =GzipUtils.gzip(data);

request.setAttachment(gzipData);

future.channel().writeAndFlush(request);

}

future.channel().closeFuture().sync();

} catch (Exception e) {

e.printStackTrace();

} finally {

workerGroup.shutdownGracefully();

}

}

}

ClientHandler类:

public classClientHandler extends ChannelHandlerAdapter {

@Override

public voidexceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

super.exceptionCaught(ctx, cause);

}

@Override

public voidchannelActive(ChannelHandlerContext ctx) throws Exception {

super.channelActive(ctx);

}

@Override

public voidchannelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

Response response = (Response) msg;

System.out.println("Client:"+ response.getId() + "," + response.getName() + "," +response.getResponseMessage());

}

}

Marshalling工具类:

public final classMarshallingCodeCFactory {

/**

* 创建Jboss Marshalling解码器MarshallingDecoder

* @return MarshallingDecoder

*/

public static MarshallingDecoderbuildMarshallingDecoder() {

//首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。

final MarshallerFactorymarshallerFactory =Marshalling.getProvidedMarshallerFactory("serial");

//创建了MarshallingConfiguration对象,配置了版本号为5

final MarshallingConfigurationconfiguration = new MarshallingConfiguration();

configuration.setVersion(5);

//根据marshallerFactory和configuration创建provider

UnmarshallerProvider provider= new DefaultUnmarshallerProvider(marshallerFactory, configuration);

//构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度

MarshallingDecoder decoder =new MarshallingDecoder(provider, 1024 * 1024);

return decoder;

}

/**

* 创建Jboss Marshalling编码器MarshallingEncoder

* @return MarshallingEncoder

*/

public static MarshallingEncoderbuildMarshallingEncoder() {

final MarshallerFactorymarshallerFactory =Marshalling.getProvidedMarshallerFactory("serial");

final MarshallingConfigurationconfiguration = new MarshallingConfiguration();

configuration.setVersion(5);

MarshallerProvider provider =new DefaultMarshallerProvider(marshallerFactory, configuration);

//构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组

MarshallingEncoder encoder =new MarshallingEncoder(provider);

return encoder;

}

}

Gizp压缩与解压缩工具类:

public classGzipUtils {

public static byte[] gzip(byte[] val)throws IOException {

ByteArrayOutputStream bos = newByteArrayOutputStream(val.length);

GZIPOutputStream gos = null;

try {

gos = new GZIPOutputStream(bos);

gos.write(val, 0, val.length);

gos.finish();

gos.flush();

bos.flush();

val = bos.toByteArray();

} finally {

if (gos != null)

gos.close();

if (bos != null)

bos.close();

}

return val;

}

public static byte[] unGzip(byte[] buf)throws IOException {

GZIPInputStream gzi = null;

ByteArrayOutputStream bos = null;

try {

gzi = new GZIPInputStream(newByteArrayInputStream(buf));

bos = newByteArrayOutputStream(buf.length);

int count = 0;

byte[] tmp = new byte[2048];

while ((count = gzi.read(tmp)) !=-1) {

bos.write(tmp, 0, count);

}

buf = bos.toByteArray();

} finally {

if (bos != null) {

bos.flush();

bos.close();

}

if (gzi != null)

gzi.close();

}

return buf;

}

}

7.最佳实践

(1)数据通信

我们需要了解在真正项目中如何使用Netty,大体上对于一些参数设置都是根据服务器性能决定的。我们需要考虑的问题是两台机器(甚至多台)使用Netty怎样进行通信。

大体上分为三种:
     ①使用长连接通道不断开的形式进行通信,也就是服务器和客户端的通道一直处于开启状态,如果服务器性能足够好,并且客户端数量也比较上的情况下,推荐这种方式。
     ②一次性批量提交数据,采用短连接方式。也就是说先把数据保存到本地临时缓存区或者临时表,当达到界值时进行一次性批量提交,又或者根据定时任务轮询提交,

这种情况的弊端是做不到实时性传输,对实时性要求不高的应用程序中推荐使用。
     ③使用一种特殊的长连接,在某一指定时间段内,服务器与某台客户端没有任何通信,则断开连接。下次连接则是客户端向服务器发送请求的时候,再次建立连接。
     在这里将介绍使用Netty实现第三种方式的连接,但是我们需要考虑两个因素:
     ①如何在超时(即服务器和客户端没有任何通信)后关闭通道?关闭通道后又如何再次建立连接?
     ②客户端宕机时,我们无需考虑,下次重启客户端之后就可以与服务器建立连接,但服务器宕机时,客户端如何与服务器端通信?

服务器端:增加了红色框部分

客户端(注意红色字体部分):

public class Client {

private static class SingleHodler {

static final Client client = newClient();

}

public static Client getInstance() {

return SingleHodler.client;

}

private EventLoopGroup workerGroup;

private Bootstrap bootstrap;

private ChannelFuture future;

private Client() {

workerGroup = new NioEventLoopGroup();

bootstrap = new Bootstrap();

bootstrap.group(workerGroup)

.channel(NioSocketChannel.class)

.handler(newChannelInitializer<SocketChannel>() {

@Override

protected voidinitChannel(SocketChannel socketChannel) throws Exception {

socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());

socketChannel.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());

socketChannel.pipeline().addLast(newReadTimeoutHandler(5)); //5秒后未与服务器通信,则断开连接。

socketChannel.pipeline().addLast(new ClientHandler());

}

});

}

public void connect() {

try {

future =bootstrap.connect("127.0.0.1", 8765).sync();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

public ChannelFuture getFuture() {

if(future == null ||!future.channel().isActive()) {

this.connect();

}

return future;

}

public static void main(String[] args)throws InterruptedException {

Client client = getInstance();

ChannelFuture future = client.getFuture();

for(int i=1; i<=3; i++) {

Message message = new Message(i,"pro" + i, "数据信息" + i);

future.channel().writeAndFlush(message);

Thread.sleep(4000);  //休眠4秒后再发送数据

}

future.channel().closeFuture().sync();

new Thread(() -> {

try {

System.out.println("子线程开始....");

ChannelFuture f =client.getFuture();

Message message = newMessage(4, "pro" + 4, "数据信息" + 4);

f.channel().writeAndFlush(message);

f.channel().closeFuture().sync();

} catch (Exception e) {

e.printStackTrace();

}

}).start();

System.out.println("主线程退出......");

}

}

其他的类与之前的一样,没有变化。

运行结果:

(2)心跳检测

我们使用Socket通信一般经常会处理多个服务器之间的心跳检测,一般来讲我们去维护服务器集群,肯定要有一台或多台服务器主机(Master),然后还应该有N台(Slave),那么我们的主机肯定要时时刻刻知道自己下面的从服务器的各方面情况,然后进行实时监控的功能。这个在分布式架构里交做心跳检测或者心跳监控。最佳处理方案是使用一些通信框架进行实现,Netty就可以做这样的事。

这个例子需要使用Sigar,不熟悉的可以看这篇文章。

Server

public class Server {

public Server(int port) {

EventLoopGroup bossGroup = newNioEventLoopGroup();

EventLoopGroup workerGroup = newNioEventLoopGroup();

try {

ServerBootstrap bootstrap = newServerBootstrap();

bootstrap.group(bossGroup,workerGroup)

.channel(NioServerSocketChannel.class)

.childHandler(newChannelInitializer<SocketChannel>() {

@Override

protected voidinitChannel(SocketChannel sc) throws Exception {

sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());

sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());

sc.pipeline().addLast(new ServerHeartBeatHandler());

}

})

.handler(newLoggingHandler(LogLevel.INFO))

.option(ChannelOption.SO_BACKLOG, 1024);

ChannelFuture future =bootstrap.bind(new InetSocketAddress("127.0.0.1", port)).sync();

future.channel().closeFuture().sync();

} catch (Exception e) {

e.printStackTrace();

} finally {

bossGroup.shutdownGracefully();

workerGroup.shutdownGracefully();

}

}

public static void main(String[] args) {

new Server(8765);

}

}

ServerHeartBeatHandler类:

public classServerHeartBeatHandler extends ChannelHandlerAdapter {

private static Map<String, String>AUTH_IP_MAP = new HashMap<>();

private static final String SUCCESS_KEY ="auth_success_key";

static {

AUTH_IP_MAP.put("192.168.3.176","1234");

}

private boolean auth(ChannelHandlerContextctx, Object msg) {

String[] rets = ((String)msg).split(",");

String auth = AUTH_IP_MAP.get(rets[0]);

if(auth != null &&auth.equals(rets[1])) {

ctx.writeAndFlush(SUCCESS_KEY);

return true;

} else {

ctx.writeAndFlush("authfailure!").addListener(ChannelFutureListener.CLOSE);

return false;

}

}

@Override

public void channelRead(ChannelHandlerContextctx, Object msg) throws Exception {

if(msg instanceof String) {

auth(ctx, msg);

} else if(msg instanceof RequestInfo) {

RequestInfo info = (RequestInfo)msg;

System.out.println("----------------------------------------------");

System.out.println("当前主机ip:" +info.getIp());

System.out.println("当前主机cpu:情况");

Map<String, Object> cpuMap =info.getCpuPercMap();

System.out.println("总使用率:" +  cpuMap.get("combined"));

System.out.println("用户使用率:" +cpuMap.get("user"));

System.out.println("系统使用率:" +cpuMap.get("sys"));

System.out.println("等待率:" +cpuMap.get("wait"));

System.out.println("空闲率:" +cpuMap.get("idle"));

System.out.println("当前主机memory情况:");

Map<String, Object> memMap =info.getMemoryMap();

System.out.println("内存总量:" +memMap.get("total"));

System.out.println("当前内存使用量:" +memMap.get("used"));

System.out.println("当前内存剩余量:" +memMap.get("free"));

System.out.println("-----------------------------------------------");

ctx.writeAndFlush("inforeceived!");

} else {

ctx.writeAndFlush("connectfailure").addListener(ChannelFutureListener.CLOSE);

}

}

}

Client类:

public class Client {

public static void main(String[] args) {

EventLoopGroup workerGroup = newNioEventLoopGroup();

try {

Bootstrap bootstrap = newBootstrap();

bootstrap.group(workerGroup)

.channel(NioSocketChannel.class)

.handler(newChannelInitializer<SocketChannel>() {

@Override

protected void initChannel(SocketChannelsc) throws Exception {

sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());

sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());

sc.pipeline().addLast(new ClientHeartBeatHandler());

}

});

ChannelFuture future =bootstrap.connect(new InetSocketAddress("127.0.0.1", 8765)).sync();

future.channel().closeFuture().sync();

} catch (Exception e) {

e.printStackTrace();

} finally {

workerGroup.shutdownGracefully();

}

}

}

ClientHeartBeatHandler类:

public classClientHeartBeatHandler extends ChannelHandlerAdapter {

private ScheduledExecutorService scheduled= Executors.newScheduledThreadPool(1);

private ScheduledFuture<?> heartBeat;

private InetAddress address;

private static final String SUCCESS_KEY ="auth_success_key";

@Override

public voidchannelActive(ChannelHandlerContext ctx) throws Exception {

address = InetAddress.getLocalHost();

String ip = address.getHostAddress();

String key = "1234";

String auth = ip + "," + key;

ctx.writeAndFlush(auth);

}

@Override

public voidexceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

cause.printStackTrace();

if(heartBeat != null) {

heartBeat.cancel(true);

heartBeat = null;

}

ctx.fireExceptionCaught(cause);

}

@Override

public voidchannelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

try {

if(msg instanceof String) {

String data = (String) msg;

if(SUCCESS_KEY.equals(data)) {

heartBeat =scheduled.scheduleWithFixedDelay(new HeartBeatTask(ctx), 0, 5,TimeUnit.SECONDS);

System.out.println(msg);

} else {

System.out.println(msg);

}

}

} finally {

ReferenceCountUtil.release(msg);

}

}

private class HeartBeatTask implements Runnable{

private final ChannelHandlerContextctx;

publicHeartBeatTask(ChannelHandlerContext ctx) {

this.ctx = ctx;

}

@Override

public void run() {

try {

RequestInfo requestInfo = newRequestInfo();

requestInfo.setIp(address.getHostAddress());

Sigar sigar = new Sigar();

CpuPerc cpuPerc =sigar.getCpuPerc();

Map<String, Object>cpuPercMap = new HashMap<>();

cpuPercMap.put("combined",cpuPerc.getCombined());

cpuPercMap.put("user", cpuPerc.getUser());

cpuPercMap.put("sys",cpuPerc.getSys());

cpuPercMap.put("wait", cpuPerc.getWait());

cpuPercMap.put("idle",cpuPerc.getIdle());

Mem mem = sigar.getMem();

Map<String, Object>memoryMap = new HashMap<>();

memoryMap.put("total", mem.getTotal() / (1024 * 1024));

memoryMap.put("used",mem.getUsed() / (1024 * 1024));

memoryMap.put("free",mem.getFree() / (1024 * 1024));

requestInfo.setCpuPercMap(cpuPercMap);

requestInfo.setMemoryMap(memoryMap);

ctx.writeAndFlush(requestInfo);

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

RequestInfo类:

public classRequestInfo implements Serializable {

private String ip ;

private Map<String, Object>cpuPercMap ;

private Map<String, Object>memoryMap;

//.. other field

public String getIp() {

return ip;

}

public void setIp(String ip) {

this.ip = ip;

}

public Map<String, Object>getCpuPercMap() {

return cpuPercMap;

}

public voidsetCpuPercMap(Map<String, Object> cpuPercMap) {

this.cpuPercMap = cpuPercMap;

}

public Map<String, Object>getMemoryMap() {

return memoryMap;

}

public void setMemoryMap(Map<String,Object> memoryMap) {

this.memoryMap = memoryMap;

}

}

MarshallingCodeCFactory类就不贴出来了,跟之前的一样。

运行结果:

每5秒发送一次数据到服务器端,这样主服务器就可以知道每台从服务器的状态了。当然,这只是一个简单的小例子,真实环境中肯定需要更严格的校验。

---------------------
作者:隔壁老王来也
来源:CSDN
原文:https://blog.csdn.net/haoyuyang/article/details/53243785
版权声明:本文为博主原创文章,转载请附上博文链接!

原文地址:https://www.cnblogs.com/lukelook/p/11217237.html

时间: 2024-10-13 14:39:11

Netty——基本使用介绍的相关文章

精通并发与Netty入门一:Netty理论知识介绍

Netty是目前无论是国内还是国外各大互联网公司必备的一个网络应用框架.Netty本身既然是网络框架,处理的基本都是与网络相关的这样的一些作用.由于Netty本身在设计上的一些非常巧妙的方式,是对于NIO的一个很好的实现.Netty在各种应用场景下都会得到很广泛的应用.无论是传统的基于http的这种短连接方式还是基于底层Socket的这样的访问方式.另外还支持H5中规范中新增加的一个特别重要的标准,就是关于长连接的websocket这样一种新的规范.Netty对于其提供了非常好的支撑.那么Net

Java NIO框架Mina、Netty、Grizzly介绍与对比

Mina Mina(Multipurpose Infrastructure for Network Applications) 是 Apache组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架.当前发行的 Mina 版本2.04支持基于 JavaNIO 技术的 TCP/UDP 应用程序开发.串口通讯程序,Mina 所支持的功能也在进一步的扩展中.目前,正在使用Mina的应用包括:Apache Directory Project.AsyncWeb.AMQP(Advan

Java NIO框架Mina、Netty、Grizzly介绍与对比(zz)

Mina: Mina(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架.当前发行的 Mina 版本2.04支持基于 Java NIO 技术的 TCP/UDP 应用程序开发.串口通讯程序,Mina 所支持的功能也在进一步的扩展中.目前,正在使用 Mina的应用包括:Apache Directory Project.AsyncWeb.AMQP(A

netty基本组件介绍

Netty做为一款用于搭建高性能网络应用程序的高级框架,由以下几个主要构件组成: 一.Channel Channel 是java NIO的一个基本构造,可以把channel看作是传入或者传出的数据载体,可以被打开或者关闭,连接或者断开连接.简单来说其实就是我们平常网络编程中经常使用的socket套接字对象. 二.EventLoop EventLoop定义了Netty的核心对象,用于处理IO事件,多线程模型.并发.EventLoop及其相关的设计实现,我们这里不做深入了解.只需要暂时了解以下几点:

netty05(netty的一些介绍)

netty的一些理论 netty是一个异步事件驱动的网络应用框架(NIO框架),所有IO操作都是异步非阻塞的,NIO是对IO的一个补充 用于开发客户端和服务器的通信(TCP/UDP)长短连接 netty启动服务是堵塞的,可以开一个线程用于启动netty netty中每一个channel 就是一个连接,id作为唯一标示 采用 ByteBuf 存储信息数据 有新连接.新信息.异常都会有对应的方法进行监控 DefaultChannelGroup 用于管理所有的连接 零拷贝  ,“零拷贝”是指计算机操作

Netty快速入门(08)ByteBuf组件介绍

前面的内容对netty进行了介绍,写了一个入门例子.作为一个netty的使用者,我们关注更多的还是业务代码.也就是netty中这两种组件: ChannelHandler和ChannelPipeline---对应于NIO中的客户逻辑实现handleRead/handleWrite(interceptor pattern) ByteBuf---- 对应于NIO 中的ByteBuffer 我们的业务逻辑要放在handler里面,读写数据用的是ByteBuf.其余的Transport.ServerBoo

基于nio的java网络通信框架介绍和选择

Java NIO框架Mina.Netty.Grizzly介绍与对比 Mina Mina(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架.当前发行的 Mina 版本2.04支持基于 Java NIO 技术的 TCP/UDP 应用程序开发.串口通讯程序,Mina 所支持的功能也在进一步的扩展中.目前,正在使用 Mina的应用包括:Apache

Netty 中文用户手册(一)

序言 本指南对Netty 进行了介绍并指出其意义所在. 1. 问题 现在,我们使用适合一般用途的应用或组件来和彼此通信.例如,我们常常使用一个HTTP客户端从远程服务器获取信息或者通过web services进行远程方法的调用. 然而,一个适合普通目的的协议或其实现并不具备其规模上的扩展性.例如,我们无法使用一个普通的HTTP服务器进行大型文件,电邮信息的交互,或者处理金融信息和多人游戏数据那种要求准实时消息传递的应用场景.因此,这些都要求使用一个适用于特殊目的并经过高度优化的协议实现.例如,你

Netty里面的Boss和Worker【Server篇】

Netty里面的Boss和Worker[Server篇] 最近在总结Dubbo关于Netty通信方面的实现,于是也就借此机会深入体会了一下Netty.一般启动Netty的Server端时都会设置两个ExecutorService对象,我们都习惯用boss,worker两个变量来引用这两个对象,于是从我一开始接触Netty就有了boss和worker的概念.这篇博客将对boss和worker进行介绍,但并不是涉及Netty其他部分介绍. 在Netty的里面有一个Boss,他开了一家公司(开启一个服