KAFKA源码学习之NIO

简介

在这里插入图片描述
在这里插入图片描述

可以理解为Non Blocking Io

NIO和IO的区别

在这里插入图片描述

IO

之前的IO直接面向流,输入流和输出流需要单独建立
在这里插入图片描述

NIO

在这里插入图片描述

面向缓冲区,只需要一个通道(负责连接),输入和输出复用,缓冲区负责存储

通道和缓冲区

在这里插入图片描述

1.缓冲区

缓冲区(Buffer):在javaIO中负责数据的存储,缓冲区就是数组,用于储存不同的数据类型的数据
根据不同的数据类型(Boolean除外),提供了相应类型的缓冲区
ByteBuffer
CharBuffer
ShortBuffer
IntBUffer
LongBuffer
FloatBUffer
DoubleBuffer
上述缓冲区的管理方式是几乎一样的,通过allocate()获取缓冲区
最常用是ByteBuffer
二,缓冲区存取数据的两个核心办法:
put():存入数据到缓冲区中Buffer
get():取出缓冲区的数据BUffer

2. 缓冲区中的四个核心属性

> Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;

在这里插入图片描述

补充:0<=mark<=position<=limit<=capacity

举例

String str="abcde";
        //1.分配一个指定的1024字节的缓冲区
        ByteBuffer buf=ByteBuffer.allocate(1024);
        System.out.println("----------------------- allocate---------------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        //2.利用put(str.getByts) 将数据村到缓冲区中
        buf.put(str.getBytes());

        System.out.println("----------------------- Put---------------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        //3.切换读取模式
        buf.flip();
        System.out.println("------------flip()--------------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        //4.利用get()读取缓冲区的数据
        byte[] dst=new byte[buf.limit()];
        buf.get(dst);
        System.out.println(new String(dst,0,dst.length));
        System.out.println("--------get---------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        //5.rewind() 可重复读
        buf.rewind();
        System.out.println("--------rewind---------");
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        //6.clear() 清空缓存区,这里的清空不是真的清空,只是处于“被遗忘”的状态
        buf.clear();
        System.out.println("--------clearn()---------");//只是指针初始化了
        System.out.println(buf.position());
        System.out.println(buf.limit());
        System.out.println(buf.capacity());
        System.out.println((char)buf.get());

mack举例


        //------------------mask 举例------------------------
        ByteBuffer buf_mask=ByteBuffer.allocate(1024);
        buf_mask.put(str.getBytes());
        buf_mask.flip();
        byte[] dst_mask=new byte[buf.limit()];
        buf_mask.get(dst_mask,0,2);
        System.out.println(new String(dst_mask,0,2));
        System.out.println(buf_mask.position());
        //mask:标记
        buf_mask.mark();
        buf_mask.get(dst_mask,2,2);
        System.out.println(new String(dst_mask,2,2));
        System.out.println(buf_mask.position());
        //reset():恢复到mark的位置
        buf_mask.reset();
        System.out.println(buf_mask.position());

补充:0<=mark<=position<=limit<=capacity

直接缓冲区和非直接缓冲区:

在这里插入图片描述
在这里插入图片描述

非直接缓冲区

allocate()建立的是非直接缓冲区
在这里插入图片描述

copy耗费资源

直接缓冲区

在这里插入图片描述

弊端:
1.单独开辟物理内存,资源消耗大
2.写入到物理内存中的数据不归应用程序管,直接由操作系统负责,什么时候写入到物理储存就不可控制了
效率虽然高,不过数据不可控,资源消耗大,可以在某种特殊的环境下使用,比如数据长期放在内存中

为了安全,应用程序无法直接读写物理磁盘

举例:

在这里插入图片描述

2.文件通道

在这里插入图片描述

通道演进

在这里插入图片描述

在这里插入图片描述

DMA直接存储器存储,数据量大的话,DMA总线过多,会造成总线冲突,

通道:一个完全独立的处理器,专门用于IO操作,附属于cpu,能满足大量的IO操作
在这里插入图片描述
fileChannel用于本地,其余用于网络
在这里插入图片描述获取通道的三种方式
在这里插入图片描述

使用通道非直接缓存区进行读取操作

在这里插入图片描述

使用直接缓存区进行文件进行读取操作

在这里插入图片描述

通道之间的数据传输(直接缓存区)

通道之间的数据传输(直接缓冲区)这种性能最高
在这里插入图片描述

在这里插入图片描述

3.分散(Scattering)与聚集(Gather)

分散读取(Scattering Reads):Jiang

在这里插入图片描述

package com.tanghc.java;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;


public class Test4{
    public static void  main(String args[]) throws IOException {
        RandomAccessFile raf1=new RandomAccessFile("1.txt","rw");
//        获取通道
        FileChannel channel1=raf1.getChannel();
//        分配制定大小的多个缓存区
        ByteBuffer buf1=ByteBuffer.allocate(100);
        ByteBuffer buf2=ByteBuffer.allocate(1024);
//        分散读取
        ByteBuffer[] bufs={buf1,buf2};
        channel1.read(bufs);
        for ( ByteBuffer byteBuffer:bufs){
            byteBuffer.flip();
        }
        System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
        System.out.println("----------------------------");
        System.out.println(new String(bufs[1].array(),0,bufs[1].limit()));
//        聚集写入
        RandomAccessFile raf2=new RandomAccessFile("2.txt","rw");
        FileChannel channel2=raf2.getChannel();
        channel2.write(bufs);
//关闭所有
        raf1.close();
        raf2.close();
        channel1.close();
        channel2.close();


    }


}

在这里插入图片描述

字符集:Charset

编码:字符串-》字节数组
解码: 字节数组-》字符串

package com.tanghc.java;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

public class TestCharset  {
    public static void main(String[] args) throws IOException {
        Charset cs1=Charset.forName("GBK");
//        获取解码器
        CharsetDecoder cd=cs1.newDecoder();
//        获取编码器
        CharsetEncoder ce=cs1.newEncoder();
        CharBuffer cBuf=CharBuffer.allocate(1024);
        cBuf.put("字符集解码编码");
        cBuf.flip();
//        编码
        ByteBuffer bBuf=ce.encode(cBuf);
        for(int i=0;i<12;i++){
            System.out.println(bBuf.get());
        }
//        解码
        bBuf.flip();
        CharBuffer cBuf2=cd.decode(bBuf);
        System.out.println(cBuf2.toString());
        System.out.println("------------------");
        Charset cs2=Charset.forName("UTF-8");
        bBuf.flip();
        CharBuffer cBuf3=cs2.decode(bBuf);
        System.out.println(cBuf3.toString());

    }
}

到这里通道的本地文件就讲完了,接下来是网络通信(核心)
在这里插入图片描述

NIO的非阻塞式网络通信,IO是阻塞式的

IO阻塞式:
在这里插入图片描述

一个线程,一个连接完成才继续下一个连接

NIO:多线程解决IO阻塞问题:(还是会阻塞)
在这里插入图片描述

NIO的非阻塞模式:
加一层:Selector选择器(操作系统的能力)
选择器监控每个通道,当每个通道完全准备就绪时,才会将任务分配到服务端上一个或者多个的线程上运行
在这里插入图片描述

NIO 网络通信:

在这里插入图片描述
selector用来监视channel
在这里插入图片描述

FILEchannel不能切换成非阻塞模式

NIO阻塞式编程:

网络通信,阻塞式IO

客户端client:

package com.tanghc.java;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.Pipe;
import java.nio.channels.SocketChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class TestblockingNio_Client {
    public static void main(String args[]) throws IOException{
//        获取socketChannal通道
            SocketChannel sChannel=SocketChannel.open(new InetSocketAddress("127.0.0.1",9898));

            FileChannel inChannel=FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);

//        2.分配指定大小的缓存区
        ByteBuffer buf=ByteBuffer.allocate(1024);
//        读取本地文件,并发送客户端

        while (inChannel.read(buf) != -1){
            buf.flip();
            sChannel.write(buf);
            buf.clear();

        }
//        关闭通道
        inChannel.close();
        sChannel.close();
    }
}

package com.tanghc.java;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class TestblockingNio_Server {
    public static void main(String[] args) throws IOException {
//        获取通道
        ServerSocketChannel ssChannel
                = ServerSocketChannel.open();

        FileChannel outchanel = FileChannel.
                open(Paths.get("2.jpg"), StandardOpenOption.WRITE,StandardOpenOption.READ);
//        绑定连接
        ssChannel.bind(new InetSocketAddress(9898));

//3.获取客户端连接的通道
         SocketChannel sChannel=ssChannel.accept();
//分配制定大小的缓冲区
        ByteBuffer buf=ByteBuffer.allocate(1024);
//        5.接受客户端的数据,并保存到本地
        while(sChannel.read(buf) != -1){
            buf.flip();
            outchanel.write(buf);
            buf.clear();

        }
        //6.关闭通道
        sChannel.close();
        outchanel.close();
        ssChannel.close();


    }
}

先启动服务端再启动客户端

服务端给客户端反馈案例:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

非阻塞式IO

在这里插入图片描述
此时线程处于阻塞状态
解决方法有两种:
1.在这里插入图片描述
2,把线程改为非阻塞模式

非阻塞式编程

在这里插入图片描述

package com.tanghc.java;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Scanner;

public class TestblockingNio_Client_fzs {
    public static void main(String[] args) throws IOException {
        SocketChannel sChannel=SocketChannel.open(new InetSocketAddress("localhost",9898));
        //2.切换非阻塞模式
        sChannel.configureBlocking(false);
//        分配指定大小的缓冲区
        ByteBuffer buf=ByteBuffer.allocate(1024);
//        发送数据到服务端
        Scanner scan=new Scanner(System.in);
        while(scan.hasNext()){
            String str=scan.next();
            buf.put((new Date().toString()+'\n'+str).getBytes());
            buf.flip();
            sChannel.write(buf);
            buf.clear();
        }
//        关闭通道
        sChannel.close();


    }

}

package com.tanghc.java;

import javax.sound.midi.Soundbank;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.sql.SQLOutput;
import java.util.Iterator;

public class TestblockingNio_Server_fzs {
    public static void main(String[] args) throws IOException {
//        获取通道
        ServerSocketChannel ssChannel=ServerSocketChannel.open();
//       切换为非阻塞模式
        ssChannel.configureBlocking(false);
//        3.绑定连接
        ssChannel.bind(new InetSocketAddress(9898));
//        4.获取选择器
        Selector selector=Selector.open();
//       5 将通道注册到选择器上,并且指定“监听事件”
        ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//        6轮训式的获取选择器上已经准备就绪的事件
        while(selector.select()>0){
//          7  获取当前选择器中所有注册的“选择键(已就绪的监听事件)" Iterable 集合,可迭代
            Iterator<SelectionKey> it=selector.selectedKeys().iterator();
            while(it.hasNext()){
//            8    获取准备就绪的事件
                SelectionKey sk=it.next();
//                判断具体是什么事件准备就绪
                if(sk.isAcceptable()){
                    SocketChannel sChannel=ssChannel.accept();
//                    11.切换非阻塞模式
                    sChannel.configureBlocking(false);
//                    12.将该通道注册到选择器上
                    sChannel.register(selector,SelectionKey.OP_READ);
                }else if(sk.isReadable()){
//                    13.获取当前选择器上“读就绪”状态的通道
                    SocketChannel sChannel= (SocketChannel) sk.channel();
//                    14.读取数据
                    ByteBuffer buf=ByteBuffer.allocate(1024);
                    int len=0;
                    while((len=sChannel.read(buf))>0){
                        buf.flip();
                        System.out.println(new String(buf.array(),0,len));
                        buf.clear();
                    }

                }
//                15.取消选择键SelectionKey
                it.remove();


            }
        }


    }
}

注册通道选择键ops:
在这里插入图片描述
在这里插入图片描述

DdatagramChannel

UDP区别于TCP,无需连接就发送消息,不保证能接收到
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

pipe管道

在这里插入图片描述

package com.tanghc.java;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.Pipe;

public class TestPipe {
    public static void main(String[] args) throws IOException {
        Pipe pipe=Pipe.open();
        //2.将缓冲区的数据写入到 管道
        ByteBuffer buf=ByteBuffer.allocate(1024);
        Pipe.SinkChannel sinkChannel=pipe.sink();
        buf.put("通过像管道发送数据".getBytes());
        buf.flip();
        sinkChannel.write(buf);
//        3.读取缓冲区的数据
        Pipe.SourceChannel sourceChannel=pipe.source();
        buf.flip();
        int len=sourceChannel.read(buf);//返回值为读取的缓冲区的大小
        System.out.println(new String(buf.array(),0,len));
        sourceChannel.close();
        sinkChannel.close();


    }
}

github和gitEE?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值