分布式架构之NIO入门与实战

一、初探

1、概念:非阻塞的IO多路复用机制,跟传统 I/O差别如下

      

2、原理

  • Channel(通道):表示为一个已经建立好的支持I/O操作的实体(如文件和网络)的连接,Channel和IO中的Stream(流)是差不多一个等级的。只不过Stream是单向的,譬如:InputStream/OutputStream,而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。NIO中的Channel的主要实现有:FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel。分别可以对应文件IO、UDP和TCP(Client和Server)
  • Buffer(缓冲区):NIO中的关键Buffer实现有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分别对应基本数据类型: byte, char, double, float, int, long, short。当然NIO中还有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer
  • Selector:一个专门的选择器来同时对多个Socket通道进行监听(轮询或阻塞),当其中的某些Socket通道上有它感兴趣的事件发生时,这些通道就会变为可用状态,当状态是IO状态的时候,就会为他分配一个线程处理业务,当不是IO状态的时候只会为他注册一个接收(一共4种分别是接收,连接,读,写),不会分配线程,这样的话就保证了,系统中存在的线程都是用来处理业务的而不是用来等待的,这样就能够减少线程,也就减少了线程上下文的切换损耗资源。利用 Selector可使一个单独的线程管理多个 Channel,Selector(多路复用器) 是非阻塞 IO 的核心

      

  • NIO的读写是以Buffer为基准,读操作channel.read(buffer)就是把数据从Channel读到Buffer,写操作channel.write(buffer)就是把数据从Buffer写到Channel
  • 要使用Selector, 得向Selector注册Channel,生成一个SelectionKey(Selector和中注册的channel之间的凭证)然后调用它的select()方法。更新所有就绪的SelectionKey的状态,并返回就绪的channel个数。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新的连接进来、数据接收等
Selector selector = Selector.open();
channel.configureBlocking(false);
//第二个参数是监听类型,有四种可以选择分别为OP_CONNECT、OP_ACCEPT、OP_READ、OP_WRITE
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
//调用select后可以获取所有已有的selectionKey
Set<SelectionKey> selectedKeys = selector.selectedKeys();

    

大白话栗子

Selector 相当于一个接待主管,当有一个客人从大门(Channel)进来来吃饭的时候,先带它到位置上,给他安排一个台号,然后一直监听客人的需求,当客人需要点餐的时候,此时接待主管监听到了,就立马给他分配一个服务员去帮你它完成点餐,当客人需要加餐的时候,接待主管分配服务员到指定台号,然后只需要在账单(Buffer)上添加即可

    

3、优势和缺点

优势:

  • 事件驱动模型:避免多线程、单线程处理多任务
  • 非阻塞IO,IO读写不再阻塞,而是返回0
  • 基于block的传输,通常比基于流的传输更高效
  • 更高级的IO函数,zero-copy
  • IO多路复用大大提高了java网络应用的可伸缩性和实用性

缺点:

  • NIO不一定更快的场景:客户端应用、连接数<1000、并发程度不高、局域网环境下
  • NIO仍然是基于各个OS平台的IO系统实现的,差异仍然存在

二、实战

1、FileChannel

与Selector一起使用时,Channel必须处于非阻塞模式下。这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式

  • IO读取数据范例
public static void ioMethod() throws Exception{
	InputStream inputStream = null;
	StringBuffer stringBuffer = new StringBuffer();
	byte[] bufferByte = new byte[1024];
	try {
		inputStream = new BufferedInputStream(new FileInputStream("E://t.txt"));
		int len = 0;
		while((len = inputStream.read(bufferByte)) > 0){
			stringBuffer.append(new String(bufferByte,0,len));
		}
		System.out.println(stringBuffer.toString());
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} finally {
	    if(null != inputStream){
                inputStream.close();
            }
	}
}
  • NIO读取数据范例
public static void nioMethod() throws Exception{
	RandomAccessFile file = null;
	StringBuffer stringBuffer = new StringBuffer();
	ByteBuffer buffer = ByteBuffer.allocate(1024);
	int len = 0;
	try {
		file = new RandomAccessFile("E://t.txt","rw");
		FileChannel channel = file.getChannel();
		while((len = channel.read(buffer)) > 0){
			buffer.flip();
			stringBuffer.append(new String(buffer.array(),0,len));
			buffer.compact();//不会覆盖未读的数据 clear()则会
		}
		System.out.println(stringBuffer.toString());
	} catch (FileNotFoundException e) {
		e.printStackTrace();
	} finally {
		if(null != file){
                file.close();
        }
	}
}

2、TCP

  • 阻塞式栗子
//服务端
public class SocketServer {
    private final static String IP = "127.0.0.1";
    private final static int PORT = 8000;
    public static void main(String args[]){
        try {
            socketServer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void socketServer() throws Exception{
        ServerSocket serverSocket = null;
        InputStream inputStream = null;
        StringBuffer stringBuffer = new StringBuffer();
        byte[] bufferByte = new byte[1024];
        int len = 0;
        try {
            serverSocket = new ServerSocket(PORT);
            while (true){
                Socket socket = serverSocket.accept();
                SocketAddress clientAddress = socket.getRemoteSocketAddress();
                System.out.println("Handling client at "+clientAddress);
                inputStream = socket.getInputStream();
                while ((len = inputStream.read(bufferByte)) > 0){
                    stringBuffer.append(new String(bufferByte,0,len));
                }
                System.out.println(stringBuffer);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(null != serverSocket){
                serverSocket.close();
            }
            if(null != inputStream){
                inputStream.close();
            }
        }
    }
}

//客户端
public class SocketClient {
    private final static String IP = "127.0.0.1";
    private final static int PORT = 8000;
    public static void main(String args[]){
        try {
            socketClient();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void socketClient() throws Exception{
        Socket socketClient = null;
        OutputStream outputStream = null;
        String info = "I am client";
        int len = 0;
        try {
            socketClient = new Socket(IP,PORT);
            outputStream = socketClient.getOutputStream();
            outputStream.write(info.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(null != socketClient){
                socketClient.close();
            }
            if(null != outputStream){
                outputStream.close();
            }
        }
    }
}
  • 非阻塞式NIO栗子
//服务端
public class NIOSocketServer {
    private final static String IP = "127.0.0.1";
    private final static int PORT = 8000;
    private final static int TIME_OUT = 3000;
    public static void main(String args[]){
        try {
            socketServer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void socketServer() throws Exception{
        Selector selector = null;
        ServerSocketChannel serverSocketChannel = null;
        try {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(PORT));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while (true){
                if(selector.select(TIME_OUT) == 0){
                    System.out.println("等待");
                    continue;
                }
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()){
                    SelectionKey key = iterator.next();
                    if(key.isAcceptable()){
                        accept(key);
                    }
                    if(key.isReadable()){
                        read(key);
                    }
                    if(key.isWritable() && key.isValid()){
                        write(key);
                    }
                    if(key.isConnectable()){
                        System.out.println("connectable");
                    }
                    iterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(null != selector){
                selector.close();
            }
            if(null != serverSocketChannel){
                serverSocketChannel.close();
            }
        }
    }
    private static void accept(SelectionKey key) throws Exception{
        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
        SocketChannel socketChannel = channel.accept();
        socketChannel.configureBlocking(false);
        socketChannel.register(key.selector(),SelectionKey.OP_READ, ByteBuffer.allocateDirect(1024));
    }
    private static void read(SelectionKey key) throws Exception{
        SocketChannel channel = (SocketChannel) key.channel();
        ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
        StringBuffer stringBuffer = new StringBuffer();
        int len = 0;
        try {
            while ((len = channel.read(byteBuffer)) > 0){
                byteBuffer.flip();
//                stringBuffer.append(new String(byteBuffer.array(),0,len));
                while(byteBuffer.hasRemaining()){
                    System.out.print((char)byteBuffer.get());
                }
                byteBuffer.clear();
            }
//            System.out.println(stringBuffer);
        } finally {
//            if(null != channel){
//                channel.close();
//            }
        }
    }
    private static void write(SelectionKey key) throws Exception{
        ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
        byteBuffer.flip();
        SocketChannel channel = (SocketChannel) key.channel();
        channel.write(byteBuffer);
        byteBuffer.compact();
    }
}
//客户端
public class NIOSocketClient {
    private final static String IP = "127.0.0.1";
    private final static int PORT = 8000;
    public static void main(String args[]){
        try {
            socketClient();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void socketClient() throws Exception{
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        SocketChannel socketChannel = null;
        try {
            socketChannel = socketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress(IP,PORT));
            if(socketChannel.finishConnect()){
                //while (true){
                    String info = "I am client";
                    byteBuffer.clear();
                    byteBuffer.put(info.getBytes());
                    byteBuffer.flip();
                    if(byteBuffer.hasRemaining()){
                        socketChannel.write(byteBuffer);
                    }
                //}
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(null != socketChannel){
                socketChannel.close();
            }
        }
    }
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值