【问题标题】:what is the fastest way to write a byte array to socket outputstream in java在java中将字节数组写入套接字输出流的最快方法是什么
【发布时间】:2014-07-04 08:59:30
【问题描述】:

如题,假设字节数组大小不超过16Kbytes。

目前我正在为 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()

谁能告诉我一个更好的方法来做到这一点?

【问题讨论】:

    标签: java sockets outputstream


    【解决方案1】:

    如果您正在编写字节数组,则没有太大区别。网络是限制因素,而不是 API。我认为你已经做到了接近最优。最重要的因素是内核中套接字发送缓冲区的大小,以及接收器中套接字接收缓冲区的大小。

    您可以研究 NIO 和直接缓冲区,但我怀疑您会发现显着差异。直接缓冲区确实适用于您只是在通道之间复制的情况,而 NIO 的其余部分实际上是关于可扩展性而不是单个通道的性能。

    【讨论】:

      【解决方案2】:

      由于您只是转发字节,因此您可以不使用 DataInputStream 而是仅使用 BufferedInputStream.read() 和 BufferedOutputStream.write() 来节省一点时间。

      【讨论】:

      • 我之前确实使用过 BufferedInputStream 和 BufferedOutputStream,但它比我现在使用的更糟糕。顺便说一句,我的程序还需要在转发之前读取数据。
      【解决方案3】:

      正如 EJP 所提到的,网络是限制因素。但这并没有阻止我尝试在不使用 NIO 的情况下做出我能想象的最快的实现。问题是,您可以在写入另一个/the same 套接字的同时从一个套接字读取。一个线程无法做到这一点(读取或写入),因此需要多个线程。但是如果没有 NIO,这需要很多线程(虽然大部分都是空闲等待 I/O)。 NIO 稍微复杂一些,但非常擅长在有大量低容量连接时使用很少的线程(参见 Baldy 提到的文章中this page 的摘要)。

      无论如何,在一个非 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) {} 
      }
      
      }
      

      我的电脑在本地传输 100MB 需要 2 秒,如果涉及网络,预计会少很多。

      【讨论】:

        【解决方案4】:

        为了获得最佳吞吐量,您需要使用 NIO 和 ByteBuffers。 NIO 将大部分工作读取和写入到本机代码中的套接字中,因此可以更快。

        编写好的 NIO 代码更复杂,但取决于您所寻找的性能类型,值得付出努力。

        有一些很好的 NIO 示例以及一些很好的介绍和比较。我使用的一种资源是http://tutorials.jenkov.com/java-nio/index.html

        【讨论】:

        • java.net 还“将大部分工作保留在本地代码中读取和写入套接字”。那是唯一可以去的地方。 Java 必须调用 C 套接字 API:它没有自己的。出于这个原因,NIO 并没有“快得多”,而且在大多数情况下确实一点也不快。
        • 普通 java 套接字必须在本机代码和 JVM 缓冲区之间复制数据流。 NIO 将数据保存在本机代码中,避免 JVM 编组。当您添加选择器并避免一堆线程开销时,为了 OP 提到的代理目的,NIO 会更快。另一方面,如果他的应用程序正在修改数据流(不是他所说的),那么 NIO 就没有任何好处。
        猜你喜欢
        • 1970-01-01
        • 2015-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多