Netty学习(一)——Netty为何出现

BIO编程

一般网络编程模型是Client/Server模型,也就是服务端(Server)提供IP地址和监听的端口Port,客户端(Client)则通过连接操作向服务器监听的地址和端口,通过三次握手来建立连接,如果连接建立成功,双方就会通过网络套字节(Socket)进行通信。 – 以上这段话摘自《Netty权威指南》

那么这里我要介绍下BIO模型:
在这里插入图片描述
我们可以看到Client的连接请求是先发送到Acceptor线程上,然后由Acceptor来为每个连接请求都创建一个线程来进行链路处理,处理完后把消息返回给客户端。

在这里要特别说一下Acceptor线程的作用,它的主要作用是监听客户端的连接,一有连接就会创建线程进行处理请求。

通过以上图解,不难看出BIO模型的瓶颈在那里,就是线程数量与客户端访问数成1:1的关系,如果我现在有100万个请求,那我就要创建100万个线程来进行处理,那服务器可能要挂了。

循例上一下实现代码:

public class TimeServer {

    public static void main(String[] args) {
        int port = 8080;
        ServerSocket server = null;
        try {
            server = new ServerSocket(port);
            System.out.println("The time server is start in port:" + port);
            Socket socket = null;
            // 通过无限循环来监听客户端连接,若有客户端连接则创建线程处理,没有则阻塞在accept()
            while (true){
                socket = server.accept();
                new Thread(/* 这里传自己的类,前提要继承Runnable */).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(server != null){
                System.out.println("The time server close");
                try {
                    server.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                server = null;
            }
        }
    }
}

少量的客户端连接可能还可以接收,但是如果在并发量高的情况下,想想还是有点瑟瑟发抖!

NIO编程

虽然之前为了解决BIO的问题也出现过伪异步I/O(各位有兴趣可以去了解一下),但只是对BIO进行优化,并没有实质性的改变问题,这时候NIO(非阻塞I/O)就出现了,能够高速处理大量连接请求,下面我们简单点介绍下NIO的主要概念和功能。
在这里插入图片描述

  1. 缓冲区Buffer:NIO的所有数据处理都是用缓冲区处理,无论读写,读是从缓冲区读,写入数据是写入到缓冲区中。
  2. 通道Channel:通道就像一个水管,网络的数据都是通过Channel读取和写入。
  3. 多路复用器Selector:Selector提供选择已经就绪的任务的能力。当Channel已经就绪,就会被Selector轮询出来,然后获取就绪的Channel集合,再进行后续的I/O操作。

下面是NIO执行的流程:
在这里插入图片描述
由于NIO的代码实在太复杂,繁琐,这也是Netty产生的原因。我这里就不贴了。各位有兴趣可以去看看。

Netty的诞生

正是因为NIO的API复杂繁琐,同时使用也比较麻烦,需要投入的人力成本,因此就出现Netty,Netty是NIO的框架之一,有了它,程序员就能很容易的实现NIO的的功能。下面看看Netty官方实现的TimeServer:
TimeServer.java

public class TimeServer {
    // 端口号
    private int port;

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

    public void run() {
        // NioEventLoopGroup是一个处理I/O操作的多线程循环事件
        // 老板, 接收传入连接
        EventLoopGroup boss = new NioEventLoopGroup();
        // 工作者, 一旦老板接受了连接并向工作者注册了接受的连接,就处理接受连接的流量
        EventLoopGroup worker = new NioEventLoopGroup();
        try {
            // ServerBootstrap是一个建立服务的辅助类
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker)
                    // NioServerSocketChannel是用于实例化一个新的Channel来接收进来的连接
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new TimeServerHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128);
            // 绑定端口
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }

    }

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

TimeServerHandler.java

public class TimeServerHandler extends ChannelHandlerAdapter {

    private int counter;

    @Override
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
        final ByteBuf time = ctx.alloc().buffer(4);
        time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));

        final ChannelFuture f = ctx.writeAndFlush(time);
        f.addListener(new ChannelFutureListener() {

            public void operationComplete(ChannelFuture future) {
                assert f == future;
                ctx.close();
            }
        });
    }

    public void exceptionCaught(ChannelHandlerContext chx, Throwable cause){
        cause.printStackTrace();
        chx.close();
    }

}

TimeClient.java:

public class TimeClient {

    public static void main(String[] args) {
        String host = "localhost";
        int port = Integer.parseInt("8080");
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.SO_KEEPALIVE, true);
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new TimeClientHandler());
                }
            });

            ChannelFuture f = b.connect(host, port).sync();

            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }

}

TimeClientHandler.java

public class TimeClientHandler extends ChannelHandlerAdapter {

    private int counter;
    private byte[] req;

    public TimeClientHandler(){
        req = ("QUERY TIME ORDER").getBytes();
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf m = (ByteBuf) msg;
        try {
            long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
            System.out.println(new Date(currentTimeMillis));
            ctx.close();
        } finally {
            m.release();
        }
    }

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

Client显示结果:
在这里插入图片描述

总结

netty的诞生是因为nio的API操作繁琐,投入成本高,而nio的出现则是因为bio的低效率处理,其实,一个新的技术出现主要是为了弥补旧技术的不足,如果你认为新的不足,你也可以创建一个全新的框架。

下一篇会介绍netty的基础用法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值