rubenlagus / TelegramBots

Java library to create bots using Telegram Bots API
https://telegram.me/JavaBotsApi
MIT License
4.68k stars 1.18k forks source link

Tune HttpClient settings, to use `setMaxConnPerRoute()` instead of `setMaxConnTotal()` #1231

Closed allnightlong closed 5 months ago

allnightlong commented 1 year ago

Describe the bug Currently, .setMaxConnTotal(100) is used in https://github.com/rubenlagus/TelegramBots/blob/79ecff72243097f688ca42df6541869f02ce9db6/telegrambots/src/main/java/org/telegram/telegrambots/facilities/TelegramHttpClientBuilder.java#L30. This is a global connection count limit. There is second setting - setMaxConnPerRoute: https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/impl/client/HttpClientBuilder.html#setMaxConnPerRoute(int). As in case of telegram bot baseUrl is always the same, the second setting hits limit earlier than first one. As the default value is 2, this means, that only 2 requests could be performed simultaneously.

To Reproduce Here is the quick test, to check, that this limit hits faster:

import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;

import static java.text.MessageFormat.format;
import static org.apache.http.impl.client.HttpClients.createDefault;

public class HttpConnectionPerRoute {

    public static void main(String[] args) throws InterruptedException {

        CloseableHttpClient httpClient = createDefault();

        CompletableFuture.runAsync(() -> execute(httpClient));
        CompletableFuture.runAsync(() -> execute(httpClient));
        CompletableFuture.runAsync(() -> execute(httpClient));
        CompletableFuture.runAsync(() -> execute(httpClient));

        Thread.sleep(10_000);
    }

    private static void execute(CloseableHttpClient httpClient) {
        System.out.println(format("Before:[{0}] {1}", Thread.currentThread(), LocalDateTime.now()));
        HttpGet request = new HttpGet("https://httpbin.org/delay/2");

        try (var ignored = httpClient.execute(request)) {
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        System.out.println(format("After:[{0}] {1}", Thread.currentThread(), LocalDateTime.now()));
    }
}

Here we making 4 concurreny http requests to the endpoint with 2-seconds-delay before response, expecting them to run simultaneously. And here is output:

Before:[Thread[ForkJoinPool.commonPool-worker-3,5,main]] 2023-05-31T01:04:01.719964900
Before:[Thread[ForkJoinPool.commonPool-worker-4,5,main]] 2023-05-31T01:04:01.719964900
Before:[Thread[ForkJoinPool.commonPool-worker-2,5,main]] 2023-05-31T01:04:01.719964900
Before:[Thread[ForkJoinPool.commonPool-worker-1,5,main]] 2023-05-31T01:04:01.719964900
After:[Thread[ForkJoinPool.commonPool-worker-1,5,main]] 2023-05-31T01:04:05.059605200
After:[Thread[ForkJoinPool.commonPool-worker-3,5,main]] 2023-05-31T01:04:05.192477400
After:[Thread[ForkJoinPool.commonPool-worker-4,5,main]] 2023-05-31T01:04:08.837098200
After:[Thread[ForkJoinPool.commonPool-worker-2,5,main]] 2023-05-31T01:04:08.872193

As we can see, 2 later requests are waiting first 2 to complete, before executing. The same happends with telegram requests. If request taking a long time (for example, sendVideo requests), others are waiting. And current default - 2 is very low.

rubenlagus commented 5 months ago

HttpClient is gone now