12

I wrote a Java scheduler, every hour using:

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

or

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

to get a big XML/HTML file.

My server maximum bandwidth limit is 2Mbits.

When this Java schedule code runs, I use over 2Mbits of bandwidth. (check it out)

So every time a user visits my server it is too slow.

How do I limit my Java schedule to use lower bandwidth? (ex. 500Kbits)

I am using Ubuntu server.

John
  • 15,418
  • 12
  • 44
  • 65
Koerr
  • 15,215
  • 28
  • 78
  • 108

4 Answers4

18

There's no elegant way to do this.

A simple but inelegant way would be to write a Java stream wrapper that limits the rate at which bytes are read from the wrapped Stream. For instance, if you wanted to limit to 1000 bytes per second, the int read() method could be implemented as follows:

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;
}

It is worth noting that throttling rates like this can have negative as well as positive effects. On the negative side:

  • It ties down resources on the server side for longer. In this case, we are talking about the Java thread that is handling the download, and memory in kernel space is used to buffer received network packets until the application reads them.

  • It may also lead to more network traffic. The problem is that this kind of throttling will disrupt the smooth flow of packets. The server will only buffer a relatively small number of packets, and when that number is exceeded, it has to tell the client to STOP for now. This requires extra signalling packets (ACKs) and there will probably be data packets dropped in the process. Eventually, those data packets will need to be retransmitted.

Thiago Chaves
  • 9,218
  • 5
  • 28
  • 25
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • How not so? You upload / download files by reading / writing the streams. Note that I am talking about wrapping the streams you get from an HttpUrlConnection. If you do this via SaxBuilder or JSoup or some other higher level library, then you need to open the connections in a way that allows you to insert the stream wrappers. Check the respective API docs. – Stephen C May 03 '18 at 23:39
17

Theory:

Token Bucket Algorithm is a way to limit a download's bandwidth. You should read this article : it explains the use of this algorithm.

Implementations:

RateLimiter from Google Guava

Google Guava 22.0 include a RateLimiter class but it is still in beta.

From api documentation:

As an example, imagine that we have a list of tasks to execute, but we don't want to submit more than 2 per second:

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);
    }
  }

As another example, imagine that we produce a stream of data, and we want to cap it at 5kb per second. This could be accomplished by requiring a permit per byte, and specifying a rate of 5000 permits per second:

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

TimedSemaphore from Apache Commons Lang v3

Apache Commons Lang v3 include a TimedSemaphore class that can be used to implement a rate limit.

Franck
  • 944
  • 14
  • 28
  • RateLimiter works file but if in this state, connection gets lost, downloading will not finish! please look at this question: https://stackoverflow.com/questions/52604131/how-to-programmatically-limit-the-download-speed – Hadi Oct 25 '18 at 23:14
  • I've tried both `RateLimiter` and `TimedSemaphore`. Noticed a major difference: fairness. `RateLimiter` almost evenly gave permits and `TimedSemaphore` absolutly lacked fairness. In my tests only 4-6 threads gained permits out of 50. – valijon Sep 02 '20 at 12:53
3

A possible solution would be to use a Proxy Server that limits bandwidth. if you want a full java solution you can use a simple socket java proxy server here and slow down the reading/writing from streams process with Thread.sleep or any other way.

PbxMan
  • 7,525
  • 1
  • 36
  • 40
-1

Take a look at this thread: http://httpcomponents.10934.n7.nabble.com/throttlling-download-traffic-td18329.html

Also, if you use apache httpclient you can configure it to use gzip compression and save a bit of bandwidth. Particularly with xml, you should expect a lot less bytes transferred this way.

Jilles van Gurp
  • 7,927
  • 4
  • 38
  • 46
  • 5
    Please don't just paste a link as an answer. If the site is unreachable in the future, your answer will be useless – moeTi May 17 '13 at 13:13