簡體   English   中英

用Java將字節數組寫入套接字輸出流的最快方法是什么

[英]what is the fastest way to write a byte array to socket outputstream in java

作為標題,並假定字節數組的大小不大於16 KB。

目前,我正在為MySQL實現中間件(例如MySQL Proxy),這需要高吞吐量。 但是開銷是由於從套接字讀取數據並將數據寫入套接字而造成的。 現在,我使用

in = new DataInputStream(new BufferedInputStream(socket.getInputStream()))

out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()))

在讀取和寫入數據時,我使用

in.read(byte[] b)out.write(byte[] b, int offset, int len)out.flush()

有人可以告訴我一種更好的方法嗎?

如果您正在編寫字節數組,則沒有太大區別。 網絡是限制因素,而不是API。 我認為您已經接近最佳狀態了。 最重要的因素是內核中套接字發送緩沖區的大小,以及接收者處套接字接收緩沖區的大小。

您可以研究NIO和直接緩沖區,但是我懷疑您會看到很大的不同。 直接緩沖區確實適用於您僅在通道之間進行復制的情況,而NIO的其余部分實際上是關於可伸縮性而不是單個通道的性能。

由於您只是轉發字節,因此不使用DataInputStream,而僅使用BufferedInputStream.read()和BufferedOutputStream.write()可以節省一些時間。

正如EJP所提到的,網絡是限制因素。 但這並沒有阻止我嘗試不使用NIO進行我能想象的最快的實現。 問題是,您可以在寫入另一個/ 同一套接字的同時從套接字讀取。 一個線程無法做到這一點(讀或寫),因此需要多個線程。 但是如果沒有NIO,則需要大量線程(盡管大多數情況下,它們處於空閑狀態,等待I / O)。 NIO稍微復雜一些,但是當連接數量很多且數量很少時,NIO非常擅長使用很少的線程(請參閱Baldy提到的本文本頁上的摘要)。

無論如何,您可以在非NIO測試類下面進行更新,並使用該類自己查看限制因素(不是限制因素)。

public class SocketForwarder {

public static void main(String[] args) {

    try {
        new SocketForwarder().forward();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static final int portNumber = 54321;
public static final int maxSend = 1024 * 1024 * 100; // 100 MB
public static final int bufSize = 16 * 1024;
public static final int maxBufInMem = 128;

private static final SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss.SSS");

private final ExecutorService tp = Executors.newCachedThreadPool();
private final ArrayBlockingQueue<byte[]> bq = new ArrayBlockingQueue<byte[]>(maxBufInMem); 
private final CountDownLatch allReceived = new CountDownLatch(1);
private Socket from, to, sender, receiver;
private int bytesSend, bytesReceived;

public void forward() throws Exception {

    tp.execute(new Runnable() {
        public void run() {
            ServerSocket ss = null;
            try {
                ss = new ServerSocket(portNumber);
                from = ss.accept();
                to = ss.accept();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try { ss.close(); } catch (Exception ignored) {}
            }
        }
    });

    sender = new Socket(InetAddress.getLocalHost(), portNumber);
    receiver = new Socket(InetAddress.getLocalHost(), portNumber);

    // Setup proxy reader.
    tp.execute(new Runnable() {
        public void run() {
            byte[] buf = new byte[bufSize];
            try {
                InputStream in = from.getInputStream();
                int l = 0;
                while ((l = in.read(buf)) > 0) {
                    byte[] bufq = new byte[l];
                    System.arraycopy(buf, 0, bufq, 0, l);
                    bq.put(bufq);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
    // Setup proxy writer.
    tp.execute(new Runnable() {
        public void run() {
            try {
                OutputStream out = to.getOutputStream();
                while (true) {
                    byte[] bufq = bq.take();
                    out.write(bufq);
                    out.flush();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
    // Start receiver.
    tp.execute(new Runnable() {
        public void run() {
            byte[] buf = new byte[bufSize];
            try {
                InputStream in = receiver.getInputStream();
                int l = 0;
                while (bytesReceived < maxSend && (l = in.read(buf)) > 0) {
                    bytesReceived += l;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(df.format(new Date()) + " bytes received: " + bytesReceived);
            allReceived.countDown();
        }
    });
    // Start sender.
    tp.execute(new Runnable() {
        public void run() {
            Random random = new Random();
            try {
                OutputStream out = sender.getOutputStream();
                System.out.println(df.format(new Date())  + " start sending.");
                while (bytesSend < maxSend) {
                    byte[] buf = new byte[random.nextInt(bufSize)];
                    out.write(buf);
                    out.flush();
                    bytesSend += buf.length;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("Bytes send: " + bytesSend);
        }
    });
    try { 
        allReceived.await();
    } finally {
        close(sender);
        close(from);
        close(to);
        close(receiver);
        tp.shutdownNow();
    }
}

private static void close(Socket s) {
    try { s.close(); } catch (Exception ignored) {} 
}

}

我的計算機花了2秒的時間在本地傳輸100MB,而在涉及網絡時,期望值要低得多。

為了獲得最佳吞吐量,您將要使用NIO和ByteBuffers。 NIO使大多數工作都以本機代碼讀取和寫入套接字,因此可以更快。

編寫好的NIO代碼要復雜得多,但是根據您要尋找的性能類型,這值得您付出努力。

這里有一些不錯的NIO示例,以及一些不錯的介紹和比較。 我使用的一種資源是http://tutorials.jenkov.com/java-nio/index.html

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM