Netty简介

来源:互联网 发布:巴蒂斯图塔巅峰数据 编辑:程序博客网 时间:2024/05/29 14:50

Netty

Netty是基于Java NIO的网络应用框架。
Netty是一个NIO client-server(客户端-服务器)框架,使用Netty可以快速开发网络应用,例如服务器和客户端协议。Netty提供一种新的方式来开发网络应用程序,这种新的方式是的它很容易使用和有很强的扩展性。
Netty的内部实现是复杂的,但是Netty提供了简单一用的api从网络处理代码中解耦业务逻辑。
Netty是完全基于NIO实现的,所以整个Netty都是非阻塞的。
网络应用程序通常需要较高的可扩展性,无论是Netty还是其他基于JAVA NIO的框架,都会提供可扩展性的解决方案。Netty中一个关键组成部分是它的异步特性,本章将讨论同步(阻塞)和异步(非阻塞)的IO来说明为什么使用异步代码来解决扩展性问题以及如何使用异步。

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

  1. 创就绪建ServerSocketChannel,为它配置非阻塞模式。
  2. 绑定监听,配置TCP参数,录入backlog大小
  3. 创建一个独立的IO线程,用于轮询多路复用器Selector
  4. 创建Selector,将之前创建的ServerSocketChannel注册到Selector上,并设置监听标志位SelectionKEY.ACCEPT
  5. 启动IO线程,在循环体中执行Selector.select()方法,轮询就绪的通道。
  6. 当轮询到了处于就绪的通道时,需要进行判断操作位,如果是ACCEPT状态,说明是新的客户端接入,则调用accept方法接受新的客户端。
  7. 设置新接入客户端的一些参数,如非阻塞、并将其通道继续注册到Selector之中,设置监听标志位等。
  8. 如果轮询的通道操作位是READ,则进行读取,构造Buffer对象等。
  9. 更细节的还有数据没法送完成继续发送的问题。

Netty实现通信的步骤:

  1. 创建两个NIO线程组,一个专门用于网络事件处理(接受客户端的连接),另一个则进行网络通信读写
  2. 创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传出数据的缓存大小等等。
  3. 创建一个实际处理数据的类ChannelInitalizer,进行初始化的准备工作,比如设置接受传出数据的字符集、格式、已经实际处理数据的接口。
  4. 绑定端口,执行同步阻塞方法等待服务器启动即可。

Client.java

public class Client {    public static void main(String[] args) throws InterruptedException {        EventLoopGroup workgroup = new NioEventLoopGroup();        Bootstrap b = new Bootstrap();        b.group(workgroup)        .channel(NioSocketChannel.class)        .handler(new ChannelInitializer<SocketChannel>() {            @Override            protected void initChannel(SocketChannel sc) throws Exception {                sc.pipeline().addLast(new ClientHandler());            }        });        ChannelFuture cf1 = b.connect("127.0.0.1",8765).sync();//      ChannelFuture cf2 = b.connect("127.0.0.1",8764).sync();        //buf        cf1.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty".getBytes()));        Thread.sleep(1000);        cf1.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty".getBytes()));        Thread.sleep(1000);        cf1.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty".getBytes()));//      cf1.channel().flush();        cf1.channel().closeFuture().sync();        workgroup.shutdownGracefully();    }}

Server.java

public class Server {    public static void main(String[] args) throws InterruptedException {        //1 第一个线程组用于接受client端连接的        NioEventLoopGroup bossGroup = new NioEventLoopGroup();        //2 第二个线程组用于实际的业务处理操作的        NioEventLoopGroup workerGroup = new NioEventLoopGroup();        //3 创建一个辅助类Bootstrap,就是对我们的Server进行一系列的配置        ServerBootstrap b = new ServerBootstrap();        b.group(bossGroup,workerGroup)//把两个工作线程组加入进来        .channel(NioServerSocketChannel.class)//使用NIOServerSocketChannel这种类型的通道        .childHandler(new ChannelInitializer<SocketChannel>() {//使用childHandler绑定具体的事件处理器            @Override            protected void initChannel(SocketChannel sc) throws Exception {                sc.pipeline().addLast(new ServerHandler());            }        })        /**         * 服务器端TCP内核模块维护两个队列,我们称之为A和B         * 客户端向服务器端connect的时候 会发送带有SYN标志的包(第一次握手)         * 服务器收到客户端发来的SYN时,向客户端发送SYN ACK确认(第二次握手)         * 此时TCP内核模块将客户端连接加入A队列中,然后服务器收到客户端发来的ACK时(第三次握手)         * TCP内核模块将客户端从A队列移动到B队列,连接完成,应用程序的accept会返回。         * 也就是说accept从B队列中取出完成三次握手的连接         * A队列和B队列的长度之和是backlog。当A、B队列的长度之和大于backlog时,新连接将会被TCP内核拒绝         * 所以,如果backlog过小,可能会出现accept速度跟不上,A、B队列满了,导致新的客户无法连接。         * 要注意的是:backlog对程序的连接数并无影响,backlog影响的知识还没有被accept取出的连接。         */        //设置TCP缓冲区        .option(ChannelOption.SO_BACKLOG, 128)        //保持连接        .childOption(ChannelOption.SO_KEEPALIVE, true);        //绑定指定的端口进行监听        ChannelFuture f = b.bind(8765).sync();//      ChannelFuture f2 = b.bind(8764).sync();        //将服务器阻塞        f.channel().closeFuture().sync();//      f2.channel().closeFuture().sync();        bossGroup.shutdownGracefully();        workerGroup.shutdownGracefully();    }}

ServerHandler.java

public class ServerHandler extends ChannelHandlerAdapter{    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg)            throws Exception {//      ((ByteBuf)msg).release();        try{            //do sth 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 = "我是反馈的消息";            ChannelFuture channelFuture = ctx.channel().writeAndFlush(Unpooled.copiedBuffer("Hi Client!".getBytes()));            //写完后断开连接//          channelFuture.addListener(ChannelFutureListener.CLOSE);//          ctx.channel().flush();        }finally{            ReferenceCountUtil.release(msg);        }    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)            throws Exception {        cause.printStackTrace();        ctx.close();    }}

ClientHandler.java

public class ClientHandler extends ChannelHandlerAdapter{    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg)            throws Exception {//      ((ByteBuf)msg).release();        try{            //do sth msg            ByteBuf buf = (ByteBuf)msg;            byte[] data = new byte[buf.readableBytes()];            buf.readBytes(data);            String request = new String(data,"utf-8");            System.out.println("client:"+request);            //写给服务器端            /*String response = "我是反馈给服务器的消息";            ctx.channel().writeAndFlush(Unpooled.copiedBuffer("999".getBytes()));*/        }finally{            //读数据必须释放            ReferenceCountUtil.release(msg);        }    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)            throws Exception {        cause.printStackTrace();        ctx.close();    }}
0 0