Netty入门应用

来源:互联网 发布:淘宝详情编辑不用神笔 编辑:程序博客网 时间:2024/05/16 08:12

Netty入门应用

  Netty是业界最流行的NIO框架之一,它的健壮性,功能,性能,可定制性和可扩展性在同类框架中都是首屈一指的,它已经得到成百上千的商用项目验证,例如hadoopRPC框架的avro使用netty作为底层通信框架,很多其他业界主流的RPC框架,也使用netty来构建高性能的异步通信能力。

   开发Netty入门应用之前我们先了解一下NIO服务端开发的步骤,如下图所示

 

  

   一个简单的NIO服务端程序,如果我们直接使用JDK的NIO类库记性开发,要经过十几步操作才能完成最基本的消息读取和发送,这也是我们要使用netty的原因。

一.Netty服务端开发

1.1 TimeServer实现:

   importio.netty.bootstrap.ServerBootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

importio.netty.channel.socket.nio.NioServerSocketChannel;

 

public class TimeServer {

        

         publicvoid bind(int port) throws Exception{

                   //配置服务端的NIO线程组,专门用于网络事件的处理,实际就是Reactor线程组

                   //一个用于服务端接收客户端的连接,另一个用于进行SocketChannel的网络读写

                   EventLoopGroupbossGroup =new NioEventLoopGroup();

                   EventLoopGroupworkerGroup =new NioEventLoopGroup();

         try{

                   //用于启动NIO服务器的辅助启动类,降低服务端的开发复杂度

                   ServerBootstrapb=new ServerBootstrap();

      // group方法将2个NIO线程组当做入参传递到ServerBootstrap

                   b.group(bossGroup,workerGroup)

      // 对应于JDK NIO类库中的ServerSocketChannel

                    .channel(NioServerSocketChannel.class)

     // 配置TCP,此处将它的backlog设置为1024

                    .option(ChannelOption.SO_BACKLOG,1024)  

     // 绑定IO事件的处理类ChildChannelHandler,作用类似于Reactor模式中的handler类,主要用于处理网络I/O事件,例如记录日志,对消息进行编解码等。

                    .childHandler(new ChildChannelHandler()); 

                   //绑定端口,同步阻塞方法sync,同步等待成功,功能类似于JDK的java.uti.concurrent.Future,主要用于异步操作的通知回调

                   ChannelFuturef=b.bind(port).sync();

                   //等待服务端监听端口关闭

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

        

                  

         }catch (Exception e) {

                   //TODO: handle exception

         }finally{

                   //优雅退出,释放线程池资源

                   bossGroup.shutdownGracefully();

                   workerGroup.shutdownGracefully();

         }

        

         }

        

         privateclass ChildChannelHandler extends ChannelInitializer <SocketChannel> {

                   @Override

                   protectedvoid initChannel(SocketChannel arg0) throws Exception{

                            arg0.pipeline().addLast(newTimeServerHandler());

                   }

         }

        

         publicstatic void main(String[] args) throws Exception{

                   intport=8080;

                   if(args!=null&&args.length>0){

                            try{

                                     port=Integer.valueOf(args[0]);

                            }catch (Exception e) {

                                     //TODO: handle exception

                            }

                   }

                   newTimeServer().bind(port);

         }

 

}

2.TimeServerHandler实现:

TimeServerHandler继承自ChannelHandlerAdapter,用于对网络时间进行读写操作

import io.netty.buffer.ByteBuf;

import io.netty.buffer.Unpooled;

importio.netty.channel.ChannelHandlerAdapter;

importio.netty.channel.ChannelHandlerContext;

/*

 * 对网络事件进行读写操作

 */

public class TimeServerHandler extendsChannelHandlerAdapter{

     

         @Override

         publicvoid channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{

                   //ByteBuf类似于JDK中的java.nio.ByteBuffer对象

                   ByteBufbuf=(ByteBuf) msg;

                   //readableBytes方法可以获取缓冲区可读的字节数

                   byte[]req=new byte[buf.readableBytes()];

                   buf.readBytes(req);

                   Stringbody=new String(req,"UTF-8");

                   System.out.println("Thetime server receive order:"+body);

                   StringcurrentTime="QUERY TIME ORDER".equalsIgnoreCase(body)?

                                     newjava.util.Date(System.currentTimeMillis()).toString():"BAD ORDER";

                            ByteBufresp=Unpooled.copiedBuffer(currentTime.getBytes());  

                            //异步发送应答消息给客户端,把待发送的消息放到发送缓冲数组里

                            ctx.write(resp);

         }

         /*

          * 从性能角度考虑,为了防止频繁地唤醒Selector进行消息发送,

          * Netty的write方法并不直接将消息写入SocketChannel中,

          * 调用write方法只是把待发送的消息放到发送缓冲数组中,

          * 再调用flush方法,将发送缓冲区中的消息全部写到SocketChannel中

          * (non-Javadoc)

          * @seeio.netty.channel.ChannelHandlerAdapter#channelReadComplete(io.netty.channel.ChannelHandlerContext)

          */

         @Override

         publicvoid channelReadComplete(ChannelHandlerContext ctx) throws Exception{

                   //将发送队列中的消息写入到SocketChannel中发送给对方

                   ctx.flush();

                  

         }

        

         /*当发生异常时,关闭ChannelHandlerContext,释放和ChannelHandlerContext相关联的句柄等资源

          * (non-Javadoc)

          * @seeio.netty.channel.ChannelHandlerAdapter#exceptionCaught(io.netty.channel.ChannelHandlerContext,java.lang.Throwable)

          */

         @Override

         publicvoid exceptionCaught(ChannelHandlerContext ctx,Throwable cause){

                   ctx.close();

         }

}

2. Netty客户端开发

2.1 TimeClient实现:

import io.netty.bootstrap.Bootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelOption;

import io.netty.channel.EventLoopGroup;

importio.netty.channel.nio.NioEventLoopGroup;

importio.netty.channel.socket.SocketChannel;

import io.netty.channel.socket.nio.NioSocketChannel;

 

public class TimeClient {

        

         publicvoid connect(int port,String host) throws Exception{

                   //配置客户端NIO线程组,处理IO读写

                   EventLoopGroupgroup=new NioEventLoopGroup();

                   try{

                            //创建客户端辅助启动类

                            Bootstrapb=new Bootstrap();

                            b.group(group)

                            //客户端的Channel设置为NioSocketChannel,

                            .channel(NioSocketChannel.class) 

                            .option(ChannelOption.TCP_NODELAY,true)   // 对其进行配置

                            //此处为了简单,直接创建匿名内部类,实现initChannel方法

                            //其作用是当创建NioSocketChannel成功之后,

                            //在初始化它时将它的ChannelHandler设置到ChannelPipeline中,用于处理网络I/O事件

                            .handler(newChannelInitializer<SocketChannel>() {

                                     @Override

                                     publicvoid initChannel(SocketChannel ch) throws Exception{

                                              

                                              

                                               ch.pipeline().addLast(newTimeClientHandler());

                                     }

                            });

                            //发起异步连接操作

                            ChannelFuturef=b.connect(host,port).sync();

                            //等待客户端链路关闭

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

                   }catch (Exception e) {

                            //TODO: handle exception

                   }finally{

                            //优雅退出,释放NIO线程组

                            group.shutdownGracefully();

                   }

         }

         publicstatic void main(String[] args) throws Exception{

                   intport=8080;

                   if(args!=null&&args.length>0){

                            try{

                                     port=Integer.valueOf(args[0]);

                                    

                            }catch (NumberFormatException e) {

                                     //TODO: handle exception

                                    

                            }

                            newTimeClient().connect(port,"127.0.0.1");

                  }

         }

 

}

2.2 TimeClientHandler实现

import java.util.logging.Logger;

 

import io.netty.buffer.ByteBuf;

import io.netty.buffer.Unpooled;

importio.netty.channel.ChannelHandlerAdapter;

importio.netty.channel.ChannelHandlerContext;

 

 

 

public class TimeClientHandler extendsChannelHandlerAdapter{

 

        

         privatestatic final Logger logger=Logger.getLogger(TimeClientHandler.class.getName());

         privatefinal ByteBuf firstMessage;

        

         publicTimeClientHandler(){

                   byte[]req="QUERY TIME ORDER".getBytes();

                   firstMessage=Unpooled.buffer(req.length);

                   firstMessage.writeBytes(req);

         }

        

         /*

          * 当客户端和服务端TCP链路建立成功之后,

          * Netty的NIO线程会调用channelActive方法,发送查询时间的指令给服务端

          * (non-Javadoc)

          * @seeio.netty.lActive(channel.ChannelHandlerAdapter#channeio.netty.channel.ChannelHandlerContext)

          */

         @Override

         publicvoid channelActive(ChannelHandlerContext ctx){

                   //将请求消息发送给服务端

                   ctx.writeAndFlush(firstMessage);

         }

        

         /*

          * 当服务端返回应答消息时,channelRead方法被调用

          * (non-Javadoc)

          * @seeio.netty.channel.ChannelHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext,java.lang.Object)

          */

         @Override

         publicvoid channelRead(ChannelHandlerContext ctx,Object msg) throws Exception{

                   //从Netty的ByteBuf中读取并打印应答消息

                   ByteBufbuf=(ByteBuf) msg;

                   byte[]req=new byte[buf.readableBytes()];

                   buf.readBytes(req);

                   Stringbody=new String(req,"UTF-8");

                   System.out.println("Nowis:"+body);

         }

        

         @Override

         publicvoid exceptionCaught(ChannelHandlerContext ctx,Throwable cause){

                   //释放资源

                   logger.warning("Unexpectedexception from downstream:"+cause.getMessage());

                   ctx.close();

         }

}

   可以发现,通过netty开发的nio服务端和客户端非常简单。基于Netty应用开发不单API使用简单,开发模式固定,而且扩展性和定制型非常好。

原创粉丝点击