1. 关于Netty的基本认知:
在JDK 1.4推出Java NIO之前,基于Java的所有Socket通信都采用的BIO(同步阻塞式IO),同步阻塞式IO存在巨大的性能和可靠性瓶颈,无法适用于高性能服务器的开发。虽然后来出现了伪异步I/O通信框架,但它仅仅是对之前I/O线程模型的一个简单优化。
在JDK 1.4之后,Java诞生了NIO,也就是引入了java.nio类库,提供了很多进行异步I/O开发的API。随着java NIO的发展,对于高性能的服务器开发来说,java原生NIO仍然存在许多问题,甚至出现了非常严重的epoll bug。另外Java原生NIO的API类库繁杂,上手难度大,并非开发者心中理想的框架。
Netty是业界最流行的NIO框架之一,它经历的大规模的商业应用考验,在互联网、大数据、网络游戏等众多行业已经得到了成功商用,因此Netty逐渐成为了Java NIO编程的首选框架。
2. Netty开发环境搭建
可以使用maven自动构建java项目,本例中只需一个netty jar包,手动导入即可。
下载Netty jar包并导入Java项目中。Netty jar包到http://netty.io/ (netty官网)的download页面,本例中就使用最新的netty-4.1.16.Final,在解压之后的文件夹中找到netty-all-4.1.16.Final.jar,路径netty-4.1.16.Final > jar > all-in-one 。
在java项目中创建lib文件夹,导入netty jar包(jar包要作为库文件导入)。
本例中需要创建两个项目,分别是时间服务器和时间客户端。
3. 基于Netty的DayTime时间服务器与客户端架构
DayTime服务器只需一个Server启动类和一个业务逻辑处理类(处理客户端请求)即可,客户端也只需一个Client启动类和一个业务逻辑处理类(向服务器发送请求并处理服务器响应),参考下图。
①TimeServer.java(服务器启动类)源码如下:
--------------------------------------------------------------------------------------------------------------------
public class TimeServer {
public void bind(int port) throws Exception {
//配置服务端NIO线程组。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChildChannelHandler());
//绑定端口,同步等待成功。
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
//等待服务端监听端口关闭。
channelFuture.channel().closeFuture().sync();
} finally {
//优雅退出,释放线程池资源。
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
--------------------------------------------------------------------------------------------------------------------
②TimeServerHandler.java(处理客户端请求)源码如下:
--------------------------------------------------------------------------------------------------------------------
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext channelHandlerContext, Object msg)
throws Exception {
ByteBuf buf = (ByteBuf) msg;
byte[] req = new byte[buf.readableBytes()];
buf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println("时间服务器收到请求:" + body);
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(
System.currentTimeMillis()).toString() : "BAD ORDER";
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
channelHandlerContext.write(resp);
}
@Override
public void channelReadComplete(ChannelHandlerContext channelHandlerContext)
throws Exception {
channelHandlerContext.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
--------------------------------------------------------------------------------------------------------------------
③TimeClient.java(客户端启动类)源码如下(流程与服务器启动类类似):
--------------------------------------------------------------------------------------------------------------------
public class TimeClient {
public void connect(int port, String host) throws Exception {
//配置客户端NIO线程组。
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new TimeClientHandler());
}
});
//发起异步连接操作。
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
//等待客户端链路关闭。
channelFuture.channel().closeFuture().sync();
} finally {
//优雅推出,释放NIO线程组。
eventLoopGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port = 8080;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
//采用默认值。
}
}
new TimeClient().connect(port, "127.0.0.1");
}
}
--------------------------------------------------------------------------------------------------------------------
④TimeClientHandler.java(客户端业务逻辑处理类)源码如下:
--------------------------------------------------------------------------------------------------------------------
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = Logger
.getLogger(TimeClientHandler.class.getName());
private final ByteBuf firstMessage;
public TimeClientHandler() {
byte[] request = "QUERY TIME ORDER".getBytes();
firstMessage = Unpooled.buffer(request.length);
firstMessage.writeBytes(request);
}
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(firstMessage);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
byte[] req = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(req);
String body = new String(req, "UTF-8");
System.out.println("Now is:" + body);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// 释放资源
logger.warning("Unexpected exception from downstream : " + cause.getMessage());
ctx.close();
}
}
--------------------------------------------------------------------------------------------------------------------
4. 启动服务器,运行客户端
先启动服务器,然后运行客户端,此时服务收到客户端的获取时间的请求“QUERY TIME ORDER”,会在控制台输出。客户端此时会接收到服务器响应消息,也就是当前服务器时间。
本文出自 “一赫。” 博客,谢绝转载!
初识Netty -- 基于Netty的DayTime时间服务器
原文地址:http://consolas.blog.51cto.com/9388583/1975711