Я пытался оптимизировать (память) свою программу, но GC все еще задерживает ее - PullRequest
0 голосов
/ 29 августа 2018

Я написал программный продукт на Java, который проверяет, работают ли прокси, отправляя HTTP-запрос с использованием прокси.

Требуется около 30 000 прокси из базы данных, а затем пытается проверить их работоспособность. Прокси, полученные из базы данных, возвращались как ArrayList<String>, но были изменены на Deque<String> по причинам, указанным ниже.

Способ работы программы заключается в том, что существует объект ProxyRequest, в котором IP-адрес и порт хранятся как String и int соответственно. У объекта ProxyRequest есть метод isWorkingProxy(), который пытается отправить запрос через прокси-сервер и возвращает boolean, если он был успешным.

Этот ProxyRequest объект обернут вокруг RunnableProxyRequest объекта, который вызывает super.isWorkingProxy() в переопределенном методе run(). Основываясь на ответе super.isWorkingProxy(), объект RunnableProxyRequest обновляет базу данных MySQL.

Обратите внимание, что обновление базы данных MySQL synchronized().

Он работает на 750 потоках, используя FixedThreadPool (на VPS), но в направлении в конце она становится очень медленной (зависает на ~ 50 потоков), что, очевидно, подразумевает, что сборщик мусора работает. Это проблема.

Я попытался сделать следующее, чтобы улучшить лаг, похоже, оно не работает:

1) Использование прокси Deque<String> и использование Deque.pop() для получения String, в котором находится прокси. Это (я считаю) постоянно уменьшает Deque<String>, что должно уменьшить отставание, вызванное GC.

2) Установите con.setConnectTimeout(this.timeout);, где this.timeout = 5000; Таким образом, соединение должно вернуть результат через 5 секунд. Если нет, поток завершен и больше не должен быть активным в пуле потоков.

Кроме этого, я не знаю другого способа улучшить производительность.

Может ли кто-нибудь порекомендовать мне способ улучшить производительность, чтобы избежать / прекратить отставание GC в конце потоков? Я знаю, что есть вопрос Stackoverflow по этому поводу ( Java-потоки замедляются к концу обработки ), но я попробовал все в ответе, и он не сработал для меня.

Спасибо за ваше время.

Фрагменты кода:

Цикл добавления потоков в FixedThreadPool:

//This code is executed recursively (at the end, main(args) is called again)
//Create the threadpool for requests
//Threads is an argument that is set to 750.
ThreadPoolExecutor executor = (ThreadPoolExecutor)Executors.newFixedThreadPool(threads);
Deque<String> proxies = DB.getProxiesToCheck();

while(proxies.isEmpty() == false) {
    try {
        String[] split = proxies.pop().split(":");

        Runnable[] checks = new Runnable[] {
            //HTTP check
            new RunnableProxyRequest(split[0], split[1], Proxy.Type.HTTP, false),
            //SSL check
            new RunnableProxyRequest(split[0], split[1], Proxy.Type.HTTP, true),
            //SOCKS check
            new RunnableProxyRequest(split[0], split[1], Proxy.Type.SOCKS, false)
            //Add more checks to this list as time goes...
        };

        for(Runnable check : checks) {
            executor.submit(check);
        }

    } catch(IndexOutOfBoundsException e) {
        continue;
    }
}

ProxyRequest класс:

//Proxy details
private String proxyIp;
private int proxyPort;
private Proxy.Type testingType;

//Request details
private boolean useSsl;

public ProxyRequest(String proxyIp, String proxyPort, Proxy.Type testingType, boolean useSsl) {
    this.proxyIp = proxyIp;
    try {
        this.proxyPort = Integer.parseInt(proxyPort);
    } catch(NumberFormatException e) {
        this.proxyPort = -1;
    }
    this.testingType = testingType;
    this.useSsl = useSsl;
}

public boolean isWorkingProxy() {
    //Case of an invalid proxy
    if(proxyPort == -1) {
        return false;
    }

    HttpURLConnection con = null;

    //Perform checks on URL
    //IF any exception occurs here, the proxy is obviously bad.
    try {
        URL url = new URL(this.getTestingUrl());
        //Create proxy
        Proxy p = new Proxy(this.testingType, new InetSocketAddress(this.proxyIp, this.proxyPort));
        //No redirect
        HttpURLConnection.setFollowRedirects(false);
        //Open connection with proxy
        con = (HttpURLConnection)url.openConnection(p);
        //Set the request method
        con.setRequestMethod("GET");
        //Set max timeout for a request.
        con.setConnectTimeout(this.timeout);
    } catch(MalformedURLException e) {
        System.out.println("The testing URL is bad. Please fix this.");
        return false;
    } catch(Exception e) {
        return false;
    }

    try(
            BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
            ) {

        String inputLine = null; StringBuilder response = new StringBuilder();
        while((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }

        //A valid proxy!
        return con.getResponseCode() > 0;

    } catch(Exception e) {
        return false;
    }
}

RunnableProxyRequest класс:

public class RunnableProxyRequest extends ProxyRequest implements Runnable {


    public RunnableProxyRequest(String proxyIp, String proxyPort, Proxy.Type testingType, boolean useSsl) {

        super(proxyIp, proxyPort, testingType, useSsl);

    }

    @Override
    public void run() {

        String test = super.getTest();

        if(super.isWorkingProxy()) {

            System.out.println("-- Working proxy: " + super.getProxy() + " | Test: " +  test);

            this.updateDB(true, test);

        } else {
            System.out.println("-- Not working: " + super.getProxy() + " | Test: " +  test);

            this.updateDB(false, test);
        }   


    }

    private void updateDB(boolean success, String testingType) {
        switch(testingType) {
            case "SSL":
                DB.updateSsl(super.getProxyIp(), super.getProxyPort(), success);
                break;
            case "HTTP":
                DB.updateHttp(super.getProxyIp(), super.getProxyPort(), success);
                break;
            case "SOCKS":
                DB.updateSocks(super.getProxyIp(), super.getProxyPort(), success);
                break;
            default:
                break;
        }
    }
}

DB класс:

//Locker for async 
private static Object locker = new Object();

private static void executeUpdateQuery(String query, String proxy, int port, boolean toSet) {
    synchronized(locker) {
        //Some prepared statements here.
    }
}

1 Ответ

0 голосов
/ 29 августа 2018

Благодарю Питера Лоури за помощь в решении проблемы! :)
Его комментарий:

@ ILoveKali Я обнаружил, что сетевые библиотеки недостаточно агрессивны в отключение соединения, когда что-то идет не так. Тайм-ауты имеют тенденцию работать лучше всего, когда соединение в порядке. YMMV

Итак, я провел некоторое исследование и обнаружил, что мне также пришлось использовать метод setReadTimeout(this.timeout);. Раньше я использовал только setConnectTimeout(this.timeout);!

Благодаря этому сообщению ( HttpURLConnection timeout по умолчанию ), в котором объясняется следующее:

К сожалению, по моему опыту, использование этих значений по умолчанию может привести к нестабильному состоянию, в зависимости от того, что происходит с вашим подключение к серверу. Если вы используете HttpURLConnection и не явно установить (хотя бы прочитать) таймауты, ваше соединение может попасть в постоянное устаревшее состояние. По умолчанию. Поэтому всегда устанавливайте setReadTimeout в «что-то» или вы можете потерять связь (и, возможно, темы в зависимости от того, как работает ваше приложение).

Итак, окончательный ответ таков: у GC все было в порядке, он не был ответственен за отставание. Потоки просто застряли НАВСЕГДА под одним номером, потому что я не установил тайм-аут чтения, и поэтому метод isWorkingProxy() никогда не получал результат и продолжал читать.

...