Netty入门


Netty入门

Netty是JBOSS提供的开源项目,github上的独立项目

Netty是一个异步的、基于事件驱动的网络应用框架,用以快速开发高性能、高可靠的网络IO程序。Netty主要是针对TCP协议下,面向客户端高并发应用或Peer to Peer场景下的大量数据持续传输的应用,本质是NIO框架。

互联网行业在分布式系统中,各个节点之间需要远程服务调用,高性能的RPC框架必不可少,Netty作为异步高性能通信框架。

java支持3中网络编程模型I/O模式:BIO、NIO、AIO

java BIO同步并阻塞,服务器实现模式为一个连接一个线程,客户端有一个连接就有一个服务器线程进行处理,如果连接不做任何事产生不必要的开销(服务器监听与等待客户端发送数据产生的阻塞),应用场景:适用于连接数目较小且固定的架构,服务器资源要求较高

java NIO同步非阻塞,服务器实现模式为一个线程处理多个请求,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求就进行处理。连接数目较多,连接比较短,聊天服务器,弹幕系统,服务器通讯

java AIO异步非阻塞:AIO引入通道的概念,采用Proactor模式,有效的请求才启动线程,特点是先有操作系统完成后才通知服务器端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。适用于连接数目多且连接比较长的架构,例如相册服务器。

BIO连接流程:

1.服务器端启动ServerSocket

2.客户端启动Socket对服务器进行通信,默认情况下服务器需要对每个客户建立一个线程与之通信。

3.客户端发出请求后,先咨询是否有线程响应,没有则等待或被拒绝

4.服务器响应后,客户端等待请求结束后,再继续执行

Bio服务器端程序(使用线程池)


 * @author xing
 * @create 2020/4/26-Multithreading-study-master
 */
public class BIOServer {
    public static void main(String[] args) throws IOException {
        //线程池机制

        //1.创建一个线程池
        //2.如果有客户端连接就创建一个线程,与之通信
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        ServerSocket serverSocket = new ServerSocket(6666);
        System.out.println("服务器启动");
        while(true){
            //监听,等待客户端的连接
            final Socket socket = serverSocket.accept();
            System.out.println("连接到一个客户端");
            //线程池监听到一个连接就与之通信
            newCachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    handler(socket);
                }
            });

        }
    }


    public static void handler(Socket socket){
        try {
            System.out.println("当前线程Id"+Thread.currentThread().getId()+"当前线程名"+Thread.currentThread().getName());

            byte[] bytes = new byte[1024];
            //通过socket,获取输入流
            InputStream inputStream = socket.getInputStream();
            while(true){
                int read = inputStream.read(bytes);
                if(read!=-1){
                    System.out.println(new String(bytes,0,read));
                }else{
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            System.out.println("关闭socket的连接");
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

Bio的程序中会后两个阻塞的位置,第一个位置在于serverSocket.accept();,服务器端箭筒时阻塞,第二个位置在inputStream.read(bytes);,服务器端接受数据的阶段。

windows7安装telnet服务

1.点击控制面板——》程序——》打开或关闭windows功能,选择telnet服务器与telnet客户端进行勾选,点击确定。

2.设置,win+R,services.msc回车,将telnet设置为手动启动

3.输入命令telnet ip 端口号 例如: telnet 127.0.0.1 6666

注:连接后如果不显示字符只显示光标,使用ctrl+]解决

java NIO,三大核心部分:Channel(通道)、Buffer(缓冲区)、Selector(选择器)

NIO面向通道,面向块的的编程,数据读取到稍后处理的缓冲区,需要时在缓冲区中前后移动。Bio使用流的方式处理数据,Nio使用快的方式处理数据,I/O的效率比流I/O高很多

nio三大核心的关系:

每个channel对应一个Buffer,Selector对应一个线程,一个线程对应多个Channel连接,selector进行切换,轮询channel由事件(Event)决定,Selector根据不同的事件,在各个通道上切换。Buffer是一个内存块,是一个数组,数据的读取写入是通过Buffer,BIO要么是输入流要么是输出流。NIO的buffer可以读也可以写,需要一个flip方法切换,channel是双向的,可以返回底层操作系统的情况。

缓冲区(Buffer):缓冲区本质上是一个可以读写数据的内存块,可以理解成一个容器对象该对象提供了一组方法,可以使用内存块,缓冲区对象内置一些机制,能够跟踪和记录缓冲区的状态变化情况。常用的缓冲区:ByteBuffer、shortBuffer、CharBuffer、IntBuffer、LongBuffer、DoubleBuffer、FloatBuffer

  //buffer抽象类中的变量
    private int mark = -1;//编辑
    private int position = 0;//位置,下一个要被读或者写的索引,每次读写缓冲区都会改变
    private int limit;//缓冲区的当前终点,不能对缓冲区超过极限的位置进行读写操作
    private int capacity;//容量,缓冲区可容纳最大数据量
/*
常用方法:
    public static ByteBuffer allocateDirect(int capacity);创建直接缓冲区
    public static ByteBuffer allocate(int capacity);创建直接缓冲区
    public abstract byte get();从当前位置position上get,get之后,position会自动+1
    public abstract byte get(int index);从绝对位置get
    public abstract ByteBuffer put(byte b);从当前位置上put,put后,position+1
    public abstract ByteBuffer put(int index,byte b);从绝对位置上put
*/

通道(Channel)

通道类似于流,但其能够同时进行读写,而流只能读或者写;通道可以实现异步读写数据;通道可以从缓冲区读数据,也可以在缓冲区写数据

Channel是一个接口,常用的实现类有:FileChannel(文件的数据读写)、DatagramChannel()、ServletSocketChannel()和SocketChannel()

FileChannel类:

public int read(Buffer dst)从通道读取数据并放到缓冲区

public int write(ByteBuffer src) 把缓冲区的数据写到通道

public long transferFrom(ReadableByteChannel src,long position,long count);从目标通道复制数据到当前通道

public long transferTo(long position,long count,WriteableByteChannel target);把数据从当前通道复制给目标通道

记录linux的一个命令

strace -ff -o out /usr/java/jdk1.8.0_181-amd64/bin/java TestOldSocket

Selector(多路复用器):Selector能够检测多个注册的通道上是否有事件发生,可以同时并发处理多个客户端连接

Selector类:其子类SelectorImpl,selector与线程相关。

客户端连接后ServerSocketChannel会有SocketChannel,一个Selector可以注册多个SocketChannel,注册后返回SelectorKey,监听select()有事件发生时,通过SelectorKey能够通过方法channel()反向获取事件发生的channel,

SelectionKey API:

public static final int OP_READ = 1;
public static final int OP_WRITE = 4;
public static final int OP_CONNECT = 8;
public static final int OP_ACCEPT = 16;

ServletSocketChannel API:

服务器端监听客户端socket连接

open();得到一个ServlerSocketChannel通道

bind(SocketAddress local);设置服务器端口号

configBlocking(boolean block);设置阻塞或非阻塞

accept();接受一个连接,返回代表这个连接的通道对象

register();注册一个选择器并设置监听事件。

SocketChannel API

网络IO通道,具体负责进行读写操作,NIO把缓冲区的数据写入通道,或者把通道里的数据读到缓冲区。

群聊系统:

需求:实现一个服务器和客户端之间的数据简单通讯,实现多人群聊。

服务器端可以监测用户上线,离线,并实现消息转发功能

零拷贝(没有Cpu参与的拷贝):

java中常用的零拷贝有mmap(内存映射)和sendFile。

java AIO(异步不阻塞的IO)常用的两种模式Readctor和Proactor

java中的NIO是Reactor模式,有效的请求才能够启动线程,适用于连接数较多且连接时间较长的应用。

BIO NIO AIO
io模型 同步阻塞 同步非阻塞(多路复用) 异步非阻塞
编程难度 简单 复杂 复杂
可靠性
吞吐量

Netty:异步的基于时间驱动的网络应用框架,可以快速开发高性能、高可靠的网络IO程序

原生的NIO存在的问题:API较为复杂,需要熟悉Selector、ServerSocketChannel、SocketChannel、ByteBuffer等,需要熟悉多线程编程以及网络编程。

优点:高性能、高吞吐量,延迟更低,减少资源消耗,最小化不必要的内存复制

Reactor解决传统IO的问题:Reactor称之为反应器模式、分发者模式、通知者模式

1.基于I/O复用模型,多了连接公用一个阻塞对象,应用程序只需要在一个阻塞对象等待,无需阻塞等待所有连接。当某个链接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始处理业务

2.基于线程池复用线程资源,不必为每个连接创建线程,将连接完成后的业务处理任务分配给其他线程进行处理,一个线程可以处理多个连接的业务。

Reactor是将客户端的多个请求交给服务处理器模式(ServiceHandler)又称事件驱动,服务器端程序处理传入的请求并将这些请求同步分派到响应的处理线程。

核心组成:Rector和Handlers

Rector在一个单独的线程,用于监听和分发时间

Handlers是一个事件处理器,用来在线程池中获取线程来处理事件

模式一:单Reactor单线程(一个线程能够处理多个客户端的消息)

Reactor里有:Selector和dispatch

模式二:单Reactor多线程

Reactor对象通过selector监控客户端请求事件,通过dispatcher进行分发,如果建立连接请求,则Acceptor通过accept处理连接请求,然后创建一个Handler对象,处理完连接后的各种事件。如果不是连接请求,reactor将分发调用连接对象的handler来处理,handler负责响应时间不进行处理,通过read读取数据后,将其发送给后面的worker线程池的某个线程处理,worker线程会分配独立线程完成业务并将结果返回给handler,handler收到响应通过send方法将结果进行返回。

模式三:主从Reactor多线程

Reactor主线程MainReactor对象通过select监听连接事件,通过Acceptor处理客户端的连接,将其他的数据请求分给SubReactor进行处理,SubReactor将连接的客户端加入一个队列,并创建handler进行各种事件处理,handler通过read读取数据,分发给后面的worker线程处理。

Netty模型(基于主从Reator模型)

BossGroup线程维护Selector,只关注Accept;当接收到Accept事件时,获取对应的Socketchannel,封装成NIOScoketChannel并注册到Worker的selector中,并维护

Boss Group和WorkerGroup类型都是NioEventGroup,这是一个事件循环组,每一个事件循环都是一个NIOEventLoop,每个NioEventLoop都有一个Selector,用于监听绑定在其上的socket的网络通讯。Boss专门负责接收客户端的连接,Worker专门负责网络读写操作。

Boss NioEventLoop循环的步骤有三步:

1.轮询accept事件

2.处理accept事件,与客户端建立连接,生成NioSocketChannel,并将其注册到某个worker NioEventLoop上的selector

3.处理任务队列的任务,即runAllTasks

Worker NioEventLoop循环执行的步骤:

1.轮询read,write事件

2.处理IO事件,即read,write事件,在对应NioSocketChannel处理

3.处理任务队列的任务,即runAllTasks

任务队列的三种使用场景:用户程序自定义普通任务、用户自定义定时任务、非当前Reactor线程调用Channel的各种方法。

NioEventLoopGroup下包含多个NioEventLoop

每个NioEventLoop中包含有一个Selector,一个taskQueue

每个NioEventLoop的Selector上可以注册监听多个NioChannel

每个NioChannel只会绑定在唯一的NioEventLoop上

每个NioChannel都绑定有一个自己的ChannelPipline

Future表示异步的执行结果,可以通过它提供的方法来检测是否完成。ChannelFuture是一个接口,可以对其添加监听器,当事件发生时监听器会得到通知。

Bootstrap、ServerBootStrap,一个netty应用通常是由bootstrap开始主要作用是配置整个Netty程序。

Future、ChannelFuture通过该类或接口实现监听事件

Channel:Netty的网络组建,能够用于执行网络I/O操作,能够获取网络连接通道状态、网络连接配置等信息。

NioSocketChannel,异步的客户端TCP Socket连接

NioServerSocketChannel,异步服务器端TCP Socket连接

NioDatagramChannel异步UDP连接

NioSctpChannel,异步的客户端sctp连接

NioSctpServerChannel,异步的Sctp服务器端连接

Selector:基于Selector对象实现I/O多路复用,通过Selector一个线程可以监听多个连接的Channel事件。

ChannelHandler是一个接口,处理O/I事件或拦截I/O操作。

//通道就绪状态事件

channelActive(ChannelHandlerContext ctx)

//通道数据读取事件

channelRead(ChannelHandlerContext ctx, Object msg)

//异常处理事件

exceptionCaught(ChannelHandlerContext ctx, Throwable cause)

PipeLine和ChannelPipeline是一个handler集合,一个Piprline中有多个handler,一个Channel包含了一个ChannelPipeline,而ChannelPineline又维护了一个由ChannelHandlerContext组成的双向链表,并且每个ChannelHandlerContext中又关联着一个ChannelHandler。


文章作者: it星
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 it星 !
 上一篇
spring学习笔记 spring学习笔记
it星
Spring学习笔记 1.IOC的概念:全称是Inversion of Contorl叫做控制反转,和依赖注入概念等同,思想是不需要自己去创建对象,让IOC创建好拿来。 主要是通过引入中间代理消除对象间复杂的耦合关系,并统一管理分散的复杂
下一篇 
java高并发 java高并发
java高并发基础概念什么是进程? 进程,是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竟争计算机系统资源的基本单位。每一个进程都有一个自己的地址空 间,即进程空间或(虚空间)。进程空间的大小 只与处理机的位数有关
  目录