Java 實現的IO模型

1. Java Classic(Blocking) I/O

1.1 Streams 字節流

繼承Output/InputStream抽象類的幾種I/O

  • 包括ByteArrayOutputStream,字節流,字節數組
  • FileOutputStream,字節流,更適用於圖片等二進制文件
  • FilterOutputStream是一個超類,通過裝飾者模式爲上述基本的Stream類型添加額外功能,
    • 包括,Buffered,增加字節流的緩存能力,提升I/O效率與性能
    • Data,針對Java原生數據類型的
    • PrintStream,常用日誌,無I/O Exception(只存在Output)
  • ObjectOutputStream Java自帶的序列化反序列化
  • PipedOutputStream 管道

1.2 Writer 和 Reader字符流

一個字節是8bit,一個字符是16bit,且字節流不知道字符集和字符編碼,所以需要字符流,都繼承自Writer/Reader

  • BufferedWriter,類似BufferedOutputStream,通過緩衝區處理,提高I/O性能,裝飾者模式
  • CharArrayWriter,類似ByteArrayStream,只不過字節變成了字符
  • InputStreamReader/OutputStreamWriter,字節轉字符輸入,字符轉字節輸出
  • FileWriter,字符流讀寫文件
  • StringWriter,String的裝飾者,添加Writer和Reader操作
  • PipedWriter管道讀寫
  • PrintWriter,類似PrintStream,多了格式化輸出字符串

1.3 Java Blocking I/O 網絡通信實現

數據按照有限大小的數據報(datagram)進行傳輸,每個數據報由兩部分,首部header和負載payload

首部包含數據報目的地址和端口,來源地址和端口,校驗和保證可靠性的信息。數據報大小有限,需要分解多個包,有丟失或者損壞,重傳。亂序等情況

Java Socket已封裝並解決了這些網絡底層細節問題

Socket是一個全雙工通道,ServerSocket的accpt()會一直阻塞知道某個請求建立連接,返回連接的socket對象

一個簡單的RPC實現來說明

ConsumerProxy消費者代理

package framework;

import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

/*
* 服務消費代理接口
* Service API的代理類
* 與服務提供方通信參數和返回結果,使通信過程透明
* */
public class ConsumerPoxy {
    public static <T> T consume (final Class<T> interfaceClass,final String host,final int port) throws Exception {
        return (T) Proxy.newProxyInstance(
                interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass}, new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Socket socket=new Socket(host,port);//新建立socket
                        try {
                            ObjectOutputStream outputStream=new ObjectOutputStream(socket.getOutputStream());
                            try {
                                outputStream.writeUTF(method.getName());
                                outputStream.writeObject(args);
                                ObjectInputStream inputStream=new ObjectInputStream(socket.getInputStream());
                                try {
                                    Object result=inputStream.readObject();
                                    if (result instanceof  Throwable){
                                        throw (Throwable) result;
                                    }
                                    return result;
                                } finally {
                                    inputStream.close();
                                }
                            } finally {
                                outputStream.close();
                            }
                            } finally {
                            socket.close();//關閉socket 
                        }
                    }
                }
        );
    }
}

ProviderReflect 提供者反射

package framework;

import org.apache.commons.lang3.reflect.MethodUtils;
import sun.reflect.misc.MethodUtil;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
* 服務提供方
* 通過Socket接收Consumer的參數,通過Socket返回結果
*
* */
public class ProviderReflect {
    private static final ExecutorService executorService = Executors.newCachedThreadPool();

    public static void provider(final Object service, final int port) throws Exception {
        final ServerSocket serverSocket = new ServerSocket(port);
        while (true) {
            final Socket socket = serverSocket.accept(); //阻塞
            executorService.execute(new Runnable() {
                public void run() {
                    try {
                        ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
                        try {
                            String name = inputStream.readUTF();
                            Object[] args = (Object[]) inputStream.readObject();
                            ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
                            try {
                                Object result = MethodUtils.invokeExactMethod(service, name, args);
                                outputStream.writeObject(result);
                            } catch (Exception e) {
                                e.printStackTrace();
                            } finally {
                                outputStream.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            inputStream.close();
                        }

                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        try {
                            socket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        }
    }
}

2. Java Non-blocking I/O (NIO)

2.1 Buffer緩衝區

Classic I/O面向Stream,NIO面向Buffer對象,效率更高。

Buffer是java.nio中的抽象類,基於它實現了一系列基本Buffer子類,如ByteBuffer,CharBuffer

所有Buffer都有如下幾本屬性:0<=mark<=position<=limit<=capacity

  • 容量Capacity:緩衝區能容下的最大數量,創建時被設定,永遠不能修改
  • 上界Limit:緩衝區的現存元素計數
  • 位置Position:下一個要讀寫的元素,由get(),put()函數更新
  • 標記Mark:mark()->mark=position,reset->postition=mark。在mark()之前時undefined

Buffer

    /**
     * Flips this buffer.  The limit is set to the current position and then
     * the position is set to zero.  If the mark is defined then it is
     * discarded.
     *
     * <p> After a sequence of channel-read or <i>put</i> operations, invoke
     * this method to prepare for a sequence of channel-write or relative
     * <i>get</i> operations.  For example:
     *
     * <blockquote><pre>
     * buf.put(magic);    // Prepend header
     * in.read(buf);      // Read data into rest of buffer
     * buf.flip();        // Flip buffer
     * out.write(buf);    // Write header + data to channel</pre></blockquote>
     *
     * <p> This method is often used in conjunction with the {@link
     * java.nio.ByteBuffer#compact compact} method when transferring data from
     * one place to another.  </p>
     *
     * @return  This buffer
     */
    public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
    }

    /**
     * Rewinds this buffer.  The position is set to zero and the mark is
     * discarded.
     *
     * <p> Invoke this method before a sequence of channel-write or <i>get</i>
     * operations, assuming that the limit has already been set
     * appropriately.  For example:
     *
     * <blockquote><pre>
     * out.write(buf);    // Write remaining data
     * buf.rewind();      // Rewind buffer
     * buf.get(array);    // Copy data into array</pre></blockquote>
     *
     * @return  This buffer
     */
    public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
    }

2.2 Channel通道

Channel是一個全雙工通道,不同於Classic IO的Stream。如果說Buffer時運載數據的卡車,那麼channel就是卡車行駛的道路。傳輸對象只能爲Buffer。通道主要由接口指定,不同操作系統上通道差別很大。

2.3 selector選擇器

Channel在Selector上註冊,Selector不斷輪詢註冊在其上的Channel,能夠感知到Channel的可讀可寫的事件。用較少線程維護大量網絡連接,減少線程之間的切換開銷。在linux下JDK使用epoll()系統調用代替了傳統的select實現,使其沒有最大的連接句柄限制。

3. NIO2,Asynchronous I/O 介紹

NIO2在File操作方面進行了升級,對文件操作有較高需求的可以看一下。

Asynchronous I/O 是NIO2中的異步I/O組件

AsynchronousFileChannel提供異步讀寫文件的功能。

AsynchronousSocketChannel與AsynchronousServerSocketChannel實現了異步I/O網絡模型,通過實現CompletionHandler < T,T >的接口通過Channel read和write buffer

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章