【问题标题】:How can I limit bandwidth in Java?如何在 Java 中限制带宽?
【发布时间】:2011-04-26 05:54:35
【问题描述】:

我编写了一个 Java 调度程序,每小时使用:

new SAXBuilder().build(new URL(xxx));

HttpConnection.connect(new URL(xxx)); // jsoup library code

获取一个大的 XML/HTML 文件。

我的服务器最大带宽限制是 2Mbits。

当这个 Java 调度代码运行时,我使用了超过 2Mbits 的带宽。 (check it out)

所以每次用户访问我的服务器时都太慢了。

如何限制我的 Java 计划以使用较低的带宽? (例如 500Kbits)

我正在使用 Ubuntu 服务器。

【问题讨论】:

    标签: java


    【解决方案1】:

    没有优雅的方法可以做到这一点。

    一个简单但不优雅的方法是编写一个 Java 流包装器来限制从包装的流中读取字节的速率。例如,如果你想限制每秒 1000 字节,int read() 方法可以实现如下:

    Stream in;
    long timestamp = System.currentTimeInMillis();
    int counter = 0;
    int INTERVAL = 1000; // one second
    int LIMIT = 1000; // bytes per INTERVAL
    
    ...
    
    /**
     * Read one byte with rate limiting
     */
    @Override
    public int read() {
        if (counter > LIMIT) {
            long now = System.currentTimeInMillis();
            if (timestamp + INTERVAL >= now) {
                Thread.sleep(timestamp + INTERVAL - now);  
            }
            timestamp = now;
            counter = 0;
        }
        int res = in.read();
        if (res >= 0) {
            counter++;
        }
        return res;
    }
    

    值得注意的是,像这样的节流率可能会产生负面影响,也可能会产生正面影响。不利的一面:

    • 它在服务器端占用资源的时间更长。在这种情况下,我们讨论的是处理下载的 Java 线程,内核空间中的内存用于缓冲接收到的网络数据包,直到应用程序读取它们。

    • 这也可能导致更多网络流量。问题是这种节流会破坏数据包的顺畅流动。服务器只会缓冲相对少量的数据包,当超过这个数量时,它必须告诉客户端现在停止。这需要额外的信令包 (ACK),并且在此过程中可能会丢弃数据包。最终,这些数据包将需要重新传输。

    【讨论】:

    • 怎么不呢?您通过读取/写入流来上传/下载文件。请注意,我说的是包装从 HttpUrlConnection 获得的流。如果您通过 SaxBuilder 或 JSoup 或其他更高级别的库执行此操作,则需要以允许插入流包装器的方式打开连接。检查相应的 API 文档。
    【解决方案2】:

    理论:

    Token Bucket Algorithm 是一种限制下载带宽的方法。 你应该阅读this article:它解释了这个算法的使用。

    实现:

    来自 Google Guava 的 RateLimiter

    Google Guava 22.0 包含一个 RateLimiter 类,但仍处于测试阶段。

    来自 api 文档:

    举个例子,假设我们有一个要执行的任务列表,但我们不想每秒提交超过 2 个:

    final RateLimiter rateLimiter = RateLimiter.create(2.0); // rate is "2 permits per second"
      void submitTasks(List<Runnable> tasks, Executor executor) {
        for (Runnable task : tasks) {
          rateLimiter.acquire(); // may wait
          executor.execute(task);
        }
      }
    

    作为另一个例子,假设我们产生了一个数据流,并且我们希望将其限制为每秒 5kb。这可以通过要求每字节许可并指定每秒 5000 个许可的速率来实现:

    final RateLimiter rateLimiter = RateLimiter.create(5000.0); // rate = 5000 permits per second
      void submitPacket(byte[] packet) {
        rateLimiter.acquire(packet.length);
        networkService.send(packet);
      }
    

    来自 Apache Commons Lang v3 的 TimedSemaphore

    Apache Commons Lang v3 包含一个TimedSemaphore 类,可用于实现速率限制。

    【讨论】:

    • RateLimiter 工作文件,但如果在这种状态下,连接丢失,下载将无法完成!请看这个问题:stackoverflow.com/questions/52604131/…
    • RateLimiterTimedSemaphore 都试过了。注意到一个主要区别:公平。 RateLimiter 几乎均等地给予许可,TimedSemaphore 绝对缺乏公平性。在我的测试中,50 个线程中只有 4-6 个线程获得了许可。
    【解决方案3】:

    看看这个帖子:http://httpcomponents.10934.n7.nabble.com/throttlling-download-traffic-td18329.html

    另外,如果您使用 apache httpclient,您可以将其配置为使用 gzip 压缩并节省一点带宽。特别是对于 xml,您应该期望以这种方式传输的字节数会少很多。

    【讨论】:

    • 请不要只粘贴链接作为答案。如果该站点将来无法访问,您的回答将毫无用处
    【解决方案4】:

    一种可能的解决方案是使用限制带宽的代理服务器。如果你想要一个完整的 java 解决方案,你可以使用一个简单的 socket java 代理服务器here 并使用 Thread.sleep 或任何其他方式减慢流进程的读取/写入速度。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-02
      • 1970-01-01
      • 1970-01-01
      • 2021-03-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多