netty 基础介绍、IO模型、线程模型(01)

介绍:

  1. 异步事件驱动NIO框架
  2. Netty是由JBOSS提供的一个开源框架,是业界最流行的NIO框架,整合了多种协议(FTP、SMTP、HTTP等各种二进制文本文件)
  3. 优点
   1)API使用简单
   2)成熟、稳定
   3)社区活跃  有很多种NIO框架 如mina
   4)经过大规模的验证(互联网、大数据、网络游戏、电信通信行业)

     4.那些主流框架产品在用?

1)搜索引擎框架 ElasticSerach
2) Hadopp子项目Avro项目,使用Netty作为底层通信框架
3)阿里巴巴开源的RPC框架 Dubbo

BIO(同步阻塞io,一客户端一线程)

 流的关闭在finally中关闭

优点:模型简单、编码简单

缺点:请求数和线程数 N:N关系,高并发情况下,CPU切换线程上下文损耗大。

 

同步:执行一个操作之后,进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看IO操作(也就是我们说的非阻塞)是否完成,等待结果,然后才继续执行后续的操作。

异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。

阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作。

非阻塞:进程给CPU传达任我后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询。

我认为

简单理解:

同步和异步:请求发出后,是否需要等待请求结果,才能继续执行其他操作(针对返回消息)
同步:一直等待结果的返回(轮询等待结果也是)
异步:去执行其他事情,结果返回时,唤醒执行之前的消息

阻塞非阻塞(针对线程)
阻塞:在调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会被唤醒执行后续的操作。
非阻塞:在结果没有返回之前,该调用不会阻塞住当前线程。

 

public class BioServer {
    private static final int PORT = 8080;

    public static void main(String[] args)throws IOException {
        ServerSocket server = null;
        try {
            server = new ServerSocket(PORT);
            System.out.println("the time server is start in port :"+PORT);
            Socket socket = null;
            while (true){
                socket  =  server.accept();
                new Thread(new TimeServerHandler(socket)).start();
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(server != null){
                System.out.println("the time server close");
                server.close();
            }
        }
    }
}
public class TimeServerHandler implements  Runnable{
    private Socket socket;
    public  TimeServerHandler(Socket socket){
        this.socket = socket;
    }
    
    @Override
    public void run() {
        BufferedReader in = null;
        PrintWriter  out = null;
        try {
            in = new BufferedReader( new InputStreamReader(this.socket.getInputStream()));
            out = new PrintWriter(this.socket.getOutputStream(),true);
            String body = null;
            while (( body = in.readLine())!= null && body.length()!=0){
                System.out.println("the time server receive msg :"+body);
                out.println(new Date().toString());
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            if(in != null){
                try {
                    in.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(out != null){
                try {
                    out.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if(this.socket != null){
                try{
                    this.socket.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

启动后在cmd上输入:curl localhost:8080   测试连接

五种IO模型,理解

1.阻塞I/O模型
老李去火车站买票,排队三天买到一张退票。
耗费:在车站吃喝拉撒睡 3天,其他事一件没干。

2.非阻塞I/O模型
老李去火车站买票,隔12小时去火车站问有没有退票,三天后买到一张票。耗费:往返车站6次,路上6小时,其他时间做了好多事。

3.I/O复用模型
1.select/poll
老李去火车站买票,委托黄牛,然后每隔6小时电话黄牛询问,黄牛三天内买到票,然后老李去火车站交钱领票。
耗费:往返车站2次,路上2小时,黄牛手续费100元,打电话17次
2.epoll
老李去火车站买票,委托黄牛,黄牛买到后即通知老李去领,然后老李去火车站交钱领票。
耗费:往返车站2次,路上2小时,黄牛手续费100元,无需打电话

4.信号驱动I/O模型
老李去火车站买票,给售票员留下电话,有票后,售票员电话通知老李,然后老李去火车站交钱领票。
耗费:往返车站2次,路上2小时,免黄牛费100元,无需打电话

5.异步I/O模型
老李去火车站买票,给售票员留下电话,有票后,售票员电话通知老李并快递送票上门。
耗费:往返车站1次,路上1小时,免黄牛费100元,无需打电话

多路复用(高并发编程必备知识IO多路复用技术select、poll、epoll)

I/O多路复用,I/O是指网络I/O, 多路指多个TCP连接(即socket或者channel),复用指复用一个或几个线程。
简单来说:就是使用一个或者几个线程处理多个TCP连接
最大优势是减少系统开销小,不必创建过多的进程/线程,也不必维护这些进程/线程

select:
基本原理:
监视文件3类描述符: writefds、readfds、和exceptfds
调用后select函数会阻塞住,等有数据 可读、可写、出异常 或者 超时 就会返回
select函数正常返回后,通过遍历fdset整个数组才能发现哪些句柄发生了事件,来找到就绪的描述符fd,然后进行对应的IO操作
几乎在所有的平台上支持,跨平台支持性好

缺点:
    1)select采用轮询的方式扫描文件描述符,全部扫描,随着文件描述符FD数量增多而性能下降
    2)每次调用 select(),需要把 fd 集合从用户态拷贝到内核态,并进行遍历(消息传递都是从内核到用户空间)
    2)最大的缺陷就是单个进程打开的FD有限制,默认是1024   (可修改宏定义,但是效率仍然慢)
    static final  int MAX_FD = 1024
            
poll:
基本流程:
    select() 和 poll() 系统调用的大体一样,处理多个描述符也是使用轮询的方式,根据描述符的状态进行处理
    一样需要把 fd 集合从用户态拷贝到内核态,并进行遍历。
    
    最大区别是: poll没有最大文件描述符限制(使用链表的方式存储fd)
epoll:
基本原理:
在2.6内核中提出的,对比select和poll,epoll更加灵活,没有描述符限制,用户态拷贝到内核态只需要一次
使用事件通知,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用callback的回调机制来激活对应的fd

优点:
    1)没fd这个限制,所支持的FD上限是操作系统的最大文件句柄数,1G内存大概支持10万个句柄
    2)效率提高,使用回调通知而不是轮询的方式,不会随着FD数目的增加效率下降
    2)通过callback机制通知,内核和用户空间mmap同一块内存实现

    Linux内核核心函数
        1)epoll_create()  在Linux内核里面申请一个文件系统 B+树,返回epoll对象,也是一个fd
        2)epoll_ctl()   操作epoll对象,在这个对象里面修改添加删除对应的链接fd, 绑定一个callback函数
        3)epoll_wait()  判断并完成对应的IO操作

缺点:
编程模型比select/poll 复杂
例子:100万个连接,里面有1万个连接是活跃,在 select、poll、epoll分别是怎样的表现

select:不修改宏定义,则需要 1000个进程才可以支持 100万连接
poll:100万个链接,遍历都响应不过来了,还有空间的拷贝消耗大量的资源
epoll: 不用遍历fd信息,不用内核空间和用户空间相互拷贝(比另两个好)

Java的I/O演进历史

简介:讲解java的IO演进历史
    1、jdk1.4之前是采用同步阻塞模型,也就是BIO
        大型服务一般采用C或者C++, 因为可以直接操作系统提供的异步IO,AIO
    2、jdk1.4推出NIO,支持非阻塞IO.
    3、jdk1.7升级,推出NIO2.0,提供AIO的功能,支持文件和网络套接字的异步IO

Netty线程模型(Reactor模式)

    Reactor模式基于事件驱动,适合处理海量的I/O事件,属于同步非阻塞IO(NIO)

    设计模式——Reactor模式(反应器设计模式),是一种基于事件驱动的设计模式,在事件驱动的应用中,将一个或多个客户的服务请求分离(demultiplex)和调度(dispatch)给应用程序。在事件驱动的应用中,同步地、有序地处理同时接收的多个服务请求
    一般出现在高并发系统中,比如Netty,Redis等

    优点

    1)响应快,不会因为单个同步而阻塞,虽然Reactor本身依然是同步的; 
    2)编程相对简单,最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销; 
    3)可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源; 

    缺点
    1)相比传统的简单模型,Reactor增加了一定的复杂性,因而有一定的门槛,并且不易于调试。 
    2)Reactor模式需要系统底层的的支持,比如Java中的Selector支持,操作系统的select系统调用支持

 

Reactor单线程模型(比较少用)

所有的I/O操作都在同一个NIO线程上面完成,(建立连接,读取或写入消息)

Reactor线程是个多面手,负责多路分离套接字,Accept新连接,并分派请求到处理器链中。该模型 适用于处理器链中业务处理组件能快速完成的场景。不过,这种单线程模型不能充分利用多核资源,所以实际使用的不多。

使用场景:
            对应小业务则适合,编码简单;对于高负载、大并发的应用场景不适合,一个NIO线程处理太多请求,则负载过高,并且可能响应变慢,导致大量请求超时,而且万一线程挂了,则不可用了

Reactor多线程模型

  1. 有一个专门的NIO线程--acceptor新城用于监听服务端,接收客户端的TCP连接请求;
  2. 网络I/O操作--读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;
  3. 1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。

使用场景:
            可满足大多数场景,但是当Acceptor需要做复杂操作的时候,比如认证等耗时操作,再高并发情况下则也会有性能问题

Reactor主从线程模型

服务端用于接收客户端连接的不再是1个单独的NIO线程,而是一个独立的NIO线程池(MainReactor)。Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到I/O线程池(sub reactor线程池)(SubReactor)的某个I/O线程上,由它负责SocketChannel的读写和编解码工作。

使用场景:
        满足目前的大部分场景,也是Netty推荐使用的线程模型(BossGroup、WorkGroup)

为什么Netty使用NIO而不是AIO,是同步非阻塞还是异步非阻塞?

            在Linux系统上,AIO的底层实现仍使用EPOLL,与NIO相同,因此在性能上没有明显的优势
            Netty整体架构是reactor模型,采用epoll机制,所以往深的说,还是IO多路复用模式,所以也可说netty是同步非阻塞模型(看的层次不一样)

            很多人说这是netty是基于Java NIO 类库实现的异步通讯框架
            特点:异步非阻塞、基于事件驱动,性能高,高可靠性和高可定制性。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值