netty 入门

近期公司通过TCP连接的的方式接了一个硬件设备,用了最基础的ServerSocket类,参考的oracle的文档 。 实现的比较简单,放在github 上,不过这里应该用Netty才是正解。所以,过一下Netty的入门文档。

本文demo

序言 问题

我们一般会用Http客户端库来调用web服务,获取数据。如果一个东西是出于一般性目的设计出来的,那么他在某些方面可能就不是最合适的。比如获取大文件,收发邮件,展示实时的金融数据,游戏数据传输等。为了实现这些需求,需要一个为其高度优化的特定协议。还有一个无法避免的问题是你可能需要调用老系统的数据,但是他的协议又是特定。重点来了,如何在不牺牲可靠性和性能的前提下快速实现这么一个系统。

解决方案

用Netty。用Netty。用Netty。重要的事情说3遍。

Netty是一个异步 事件驱动 网络框架 ,可以用来快速开发易维护,高性能,可扩展的服务端/客户端。换句话说他简化了TCP和UDP 等服务的网络开发。

容易开发或者快速开发并不意味着他会牺牲可维护性或者是面临性能问题。Netty吸取了大量用于实现FTP,SMTP,HTTP协议的经验,并且仔细小心谨慎的设计。所以,他在易于开发,追求性能,确保稳定性和灵活性上并没有对任何一点有所妥协。

有人可能会说别的框架他们也这么说自己,那Netty到底或者为什么和他们不一样。答案是他的设计理念。Netty提供的API用起来就非常舒服。现在可能不是那么直观,但是当你使用的时候就会体会到。

开始使用

这节会围绕Netty的核心构建过程,用几个例子来让你快速上手。学完这节你会可以在Netty框架的基础上学会写client和server。

如果你想学的深入一点,了解一下他的底层实现,是个不错的起点。

开始之前

这节需要两个东西,新版的Netty和jdk1.6+。Netty下载地址。

<dependencies> <!-- https://mvnrepository.com/artifact/io.netty/netty-all --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.49.Final</version> </dependency> </dependencies>

随着你不断往下看,你能会对这节引入的类有疑惑,你可以随时通过API文档来了解更多。类名都是带链接的,可以直接点过去。

编写一个Discard Server 前半部分

世界上最简单的协议并不是输出Hello world,而是Discard,就是过来什么都直接丢弃,并且不给任何回复。下面让我们直接从Netty提供的handler实现来处理IO事件。

package io.netty.example.discard; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class DiscardServerHandler extends ChannelInboundHandlerAdapter {//1 @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//2 // super.channelRead(ctx, msg); ((ByteBuf) msg).release();//3 } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {//4 // super.exceptionCaught(ctx, cause); cause.printStackTrace(); ctx.close(); } }

有以下几点:

我们写一个DiscardServerHandler类继承自ChannelInboundHandlerAdapter,这个ChannelInboundHandlerAdapter继承自抽象类ChannelHandlerAdapter并且实现了接口ChannelInboundHandler。ChannelInboundHandler提供了各种各样的可重写的事件handler方法。这里只要使用ChannelInboundHandlerAdapter对ChannelInboundHandler的默认实现就好,不需要自己去实现所有的ChannelInboundHandler方法。

channelRead方法我们重写掉了,这个方法会在收到客户端消息的时候调用。例子中,消息msg的类型为ByteBuf。ByteBuf是对byte[]的一种抽象,可以让我们访问数组内容。

我们这里需要实现的是Discard协议,就是丢弃协议,所以需要忽略收到的所有消息。ByteBuf是一种reference-counted的对象(可以简单理解指针之类的东西),必须通过显式调用其release方法来释放。通常,我们的channelRead方法是下面这样的

@Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { try{ //对msg做一些处理 }finally { ReferenceCountUtil.release(msg); } }

Netty在处理IO的遇到exception就会进入exceptionCaught方法。通常,需要做一下日志记录,然后把相关的channel(通道)关闭。这里做法也不是固定的,你可以先发一个带code的Response然后再关闭。

后半部分

到这一步,我们已经实现了Discard服务的前半部分,剩下的就是写一个main方法然后来启动这个DiscardServerHandler服务。

package io.netty.example.discard; 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; public class DiscardServer { private int port; public DiscardServer(int port) { this.port = port; } public void run() throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup();//1 EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap();//2 b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class)//3 .childHandler(new ChannelInitializer<SocketChannel>() {//4 protected void initChannel(SocketChannel socketChannel) throws Exception { socketChannel.pipeline().addLast(new DiscardServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128)//5 最大连接数128 .childOption(ChannelOption.SO_KEEPALIVE, true);//6 //绑定端口启动服务 ChannelFuture f = b.bind(port).sync();//7 //server关闭的时候调用。因为这里是Discard 服务,所以永远不会调用。 f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { int port = 8080; if (args.length > 0) port = Integer.parseInt(args[0]); new DiscardServer(port).run(); } }

有以下几点:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxffs.html