快速理解Netty

快速理解Netty

netty的简单介绍

Netty是由Trustin Lee(韩国人 Line公司)开发

  • 本质:网络应用程序框架
  • 实现:异步、事件驱动
  • 特性:高性能、可维护、快速开发
  • 应用:客户端可服务器

要想了解netty最好先是去了解BIO、NIO,因为NIO的多线程模式就是很接近netty了。而且现在很多项目都是用netty来做的,因为netty真的是个很好的东西,相同的功能NIO可能需要20行,但是netty确能少很多,而且在github上可以看到netty的bug和JDK里NIO的bug数量比较,就能明白为什么这么多人用netty了。

Netty其实也是支持BIO、NIO、AIO这三种模式的,但是如果用BIO、AIO的话,会有横线就是过时了已经,主要是支持NIO的。
但是为什么只支持NIO了呢?
因为如果用BIO的话,在连接数很多的情况下,是非常消耗资源的。
那为什么又不支持AIO呢?
因为一般项目都是部署在linux上的,但是呢,linux上AIO的实现不是那么完善,虽然说windows上的AIO实现还可,但是很少用windows来做服务器的,而且,在linux下NIO的性能提升比AIO明显,所以netty就支持NIO了。

而且netty对NIO是有多种实现的在不同的系统下。要问为什么,就是netty对自己的实现更加自信。
注意一下,并不是说有了NIO,BIO就不行了,在特殊的情况下 ,比如说你连接数就一点点,那就不用去用NIO,用NIO代码还多,而且用NIO的话需要多多线程和网络知识有一个很扎实的基础,才能写出高质量的程序,而且NIO定位具体的错误比较困难。

Reactor的三种模式:

Reactor 单线程模式;

简单的说就是一个线程既处理连接也处理读写编解码等事件。这样的话会出现很多问题,假如这个线程挂掉的话,那基本也就全都挂了。这个时候就出现了多线程的解决方法。
在这里插入图片描述

Reactor 多线程模式;

简单的说就是,一个线程负责连接和简单的读写,然后把耗时的操作放到线程池里去做。
在这里插入图片描述

Reactor 主从模式;

简单的说,就是mainReactor只负责连接请求,把IO的读取放到subReactor这个线程里去做,具体的业务处理放到worker线程池里去处理,这个subReactor是可以有好几个的。

工作过程:
1)Reactor对象通过select监控客户端请求事件,收到事件后,通过dispatch进行分发
2)如果建立连接请 求,则右Acceptor通过accept处理连接请求,然后创建一-个Handler对象处理完成连接后的各种事件
3)如果不是连接请求,则由reactor分发调用连接对应的handler来处理
4)handler只负责响应事件,不做具体的业务处理,通过read读取数据后,会分发给后面的worker线程池的某个线程处理业务
5) worker 线程池会分配独立线程完成真正的业务,并将结果返回给handler
6)handler收到响应后,通过send将结果返回给client

在这里插入图片描述

Netty的线程模型

netty其实就是基于Reactor主从模型做的一个改善,Reactor变多了。

BossGroup 的执行步骤
1.BossGroup里的每个线程都有一个selector,这个selector会去轮询accept()事件。
2.如果有客户端建立连接,就会生成一个NIOSocketChannel,并将这个通道注册到WorkerGroup的某一个线程的selector上,具体是注册到哪一个线程上的,这个会根据一个算法,在源码里有两种实现方法。
3.处理任务队列的任务
WorkerGroup 的执行步骤
1.轮询read、write事件
2.执行I/O事件,具体的业务逻辑会在pipleline里处理,这个里面会有很多的处理器。
3.处理任务队列里的任务。

具体流程如下图所示:
在这里插入图片描述
具体创建服务端案列代码如下:

补充说明一下:
这个EventLoopGroup里的线程数默认是你的CPU的核数的2倍。为什么是两倍呢? 源码 里就是这么定义的。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
但是这里还有一个问题,这种情况下有多少个线程在运行呢?
答案不是6个而是4个 why?
EventLoopGroup bossGroup = new NioEventLoopGroup(3);
EventLoopGroup workerGroup = new NioEventLoopGroup(3);
因为bossgroup里一般只会用到一个线程,如果用多了会有惊群这个问题产生,那问题又来了什么是惊群?
惊群效应(thundering herd)是指多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群效应。

public class HelloNetty {
    public static void main(String[] args) {
        new NettyServer(8888).serverStart();
    }
}

class NettyServer {
    int port = 8888;
    public NettyServer(int port) {
        this.port = port;
    }
    public void serverStart() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new Handler());
                    }
                });
        try {
            ChannelFuture f = b.bind(port).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}

class Handler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //super.channelRead(ctx, msg);
        System.out.println("server: channel read");
        ByteBuf buf = (ByteBuf)msg;
        System.out.println(buf.toString(CharsetUtil.UTF_8));
        ctx.writeAndFlush(msg);
        ctx.close();
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

客户端案例代码如下:

public class Client {
    public static void main(String[] args) {
        new Client().clientStart();
    }
    private void clientStart() {
        EventLoopGroup workers = new NioEventLoopGroup();
        Bootstrap b = new Bootstrap();
        b.group(workers)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        System.out.println("channel initialized!");
                        ch.pipeline().addLast(new ClientHandler());
                    }
                });
        try {
            System.out.println("start to connect...");
            ChannelFuture f = b.connect("127.0.0.1", 8888).sync();
            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workers.shutdownGracefully();
        }
    }
}

class ClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channel is activated.");

        final ChannelFuture f = ctx.writeAndFlush(Unpooled.copiedBuffer("HelloNetty".getBytes()));
        f.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                System.out.println("msg send!");
                //ctx.close();
            }
        });
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            ByteBuf buf = (ByteBuf)msg;
            System.out.println(buf.toString());
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }
}

本人学艺不精,主要是写了用来方便自己记忆的,如果能帮助到其他小伙伴最好,哈哈,不喜勿喷。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值