Lösen Sie das Problem des Fehlers bei neuen Anforderungen, der dadurch verursacht wird, dass die HttpClient-Verbindung nicht freigegeben wird

Problembeschreibung

Es wurde eine Methode implementiert, um eine HTTP-Anfrage zu senden und dann die darin enthaltenen JSON-Daten abzurufen, wie unten gezeigt. Ich habe jedoch festgestellt, dass ich nach dem Senden einiger Anfragen und der erfolgreichen Rückgabe eine weitere Anfrage initiiert habe, aber keine Daten zurückgegeben wurden und kein Fehlerprotokoll vorhanden war.

public class HttpUtil {
    
    
    private final static Logger logger = LoggerFactory.getLogger(HttpUtil.class);
    private static CloseableHttpClient httpClient;

    static {
    
    
        PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
        connManager.setMaxTotal(200);
        connManager.setDefaultMaxPerRoute(40);
        httpClient = HttpClientBuilder.create().setConnectionManager(connManager).build();
    }

    public static ResultBase<JSON> doGetJSON(String url) {
    
    
        ResultBase<JSON> resultBase = new ResultBase<>();
        if (StringUtils.isEmpty(url)) {
    
    
            logger.error("doGet with a null url");
            return resultBase.setErrorMsgReturn("doGet with a null url");
        }

        HttpGet httpGet = new HttpGet(url);
        HttpResponse response;
        try {
    
    
            response = httpClient.execute(httpGet);
            Header contentType = response.getFirstHeader("Content-Type");
            if (contentType != null && contentType.getValue().contains("application/json")) {
    
    
                HttpEntity httpEntity = response.getEntity();
                InputStream inputStream = httpEntity.getContent();

                resultBase.setRightValueReturn(JSON.parseObject(inputStream, StandardCharsets.UTF_8, JSON.class));
                return resultBase;
            }
        } catch (IOException e) {
    
    
            logger.error(e.getMessage());
            resultBase.setErrorMsgReturn("Do get error! Error message: " + e.getMessage());
            return resultBase;
        }

        return resultBase.setErrorMsgReturn("Http get result is not json!");
    }
}

Problemanalyse

Aus der Sicht des Problems scheint es, dass ein Deadlock aufgetreten ist. Analysieren Sie also zunächst das Thread-Dump-Protokoll
Referenz für die Dump-Log-Analysemethode: https://www.cnblogs.com/z-sm/p/6745375. html

Schritt 1: Dump-Datei erstellen

./jcmd 50557 Thread.print > ~/regulus/logs/dump.log

** 50557 ist die Prozess-ID meiner Bewerbung

Schritt 2: Suchen Sie nach Inhalten im Zusammenhang mit HttpClient

vim ~/regulus/logs/dump.log

Fügen Sie hier eine Bildbeschreibung ein
Sie können sehen, dass sich der Thread im Wartezustand (Parkzustand) befindet:
„- gesperrt <0x0000000747534770> (a org.apache.http.pool.AbstractConnPool$2)“ Finden Sie Zeile 380 von AbstractConnPool: < /span> ![Bildbeschreibung hier einfügen](https://img-blog.csdnimg.cn/20210401203537480.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d laXhpbl 80MjUzNDk0MA== ,size_16,color_FFFFFF,t_70 "at org.apache.http.pool.AbstractConnPool.getPoolEntryBlocking(AbstractConnPool.java:380)"
zeigt an, dass der Thread gesperrt ist. Dann gemäß:


Schritt 3: AbstractConnPool analysieren
Der Code in Zeile 380 lautet: this.condition.await();
Fügen Sie hier eine Bildbeschreibung ein
Schritt 4: Debuggen
Als wir zum Debuggen eine Verbindung zum Remote-Server herstellten, stellten wir fest, dass die initiierte Anfrage tatsächlich in Zeile 380 von AbstractConnPool gestoppt wurde

Schritt 5: Weiter mit der Analyse von AbstractConnPool
Da der Thread in den Wartezustand wechselt, weil das Bedingungsobjekt die Methode „await()“ aufruft
, muss die Bedingung ausgeführt werden Rufen Sie die Methode signal( ) oder signalAll() auf, um den Thread aufzuwecken
Wie in der Abbildung unten gezeigt, gibt es zwei Stellen, an denen signalAll() aufgerufen wird
Fügen Sie hier eine Bildbeschreibung ein
Die Der erste befindet sich in der Methode lease():

Der andere befindet sich in der Methode release:
Fügen Sie hier eine Bildbeschreibung ein
Schritt 6: Analysieren Sie den angepassten HttpClient, um die Anfrage zu senden https://stackoverflow.com/questions/30889984/whats-the-difference-between-closeablehttpresponse- close-and -httppost-release Sehen Sie sich die Methode zum Schließen von Verbindungen im Internet an:
Aus dem vorherigen Schritt kann geschlossen werden, dass die Release-Methode nicht aufgerufen wurde, weil die Verbindung nicht geschlossen wurde. Was also getan werden muss, ist die http-Verbindung zu schließen.

Wenn Sie sich dann die close()-Methode von HttpResponseProxy, die Implementierungsklasse von CloseableHttpResponse, ansehen und sie dann ganz nach unten ausführen, können Sie sehen, dass schließlich die AbstractConnPool.release()-Methode aufgerufen wird.

Lösung

Verwenden Sie CloseableHttpResponse anstelle von HttpResponse und verwenden Sie dann die Verbindungsressource mit „try (CloseableHttpResponse Response = httpClient.execute(httpGet))“.

    public static ResultBase<JSON> doGetJSON(String url) {
    
    
        ResultBase<JSON> resultBase = new ResultBase<>();
        if (StringUtils.isEmpty(url)) {
    
    
            logger.error("doGet with a null url");
            return resultBase.setErrorMsgReturn("doGet with a null url");
        }

        HttpGet httpGet = new HttpGet(url);
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
    
    
            Header contentType = response.getFirstHeader("Content-Type");
            if (contentType != null && contentType.getValue().contains("application/json")) {
    
    
                HttpEntity httpEntity = response.getEntity();
                InputStream inputStream = httpEntity.getContent();

                resultBase.setRightValueReturn(JSON.parseObject(inputStream, StandardCharsets.UTF_8, JSON.class));
                return resultBase;
            }
        } catch (IOException e) {
    
    
            logger.error(e.getMessage());
            resultBase.setErrorMsgReturn("Do get error! Error message: " + e.getMessage());
            return resultBase;
        }

        return resultBase.setErrorMsgReturn("Http get result is not json!");
    }

Nachdem die Codeänderung abgeschlossen und übermittelt wurde, wurde die Maschine erneut bereitgestellt und festgestellt, dass die Anforderung immer noch nicht normal zurückgegeben werden konnte.
Bei der Überprüfung des Port-Link-Status haben wir festgestellt, dass sich immer noch eine große Anzahl von Ports im Status CLOSE_WAIT befinden (die Verbindung ist nicht normal geschlossen)

netstat -anp | grep 50557
……
tcp        0      0 0.0.0.0:60000               0.0.0.0:*                   LISTEN      50557/java
tcp        0      0 0.0.0.0:60100               0.0.0.0:*                   LISTEN      50557/java
tcp        0      0 0.0.0.0:7001                0.0.0.0:*                   LISTEN      50557/java
tcp        0      0 0.0.0.0:7002                0.0.0.0:*                   LISTEN      50557/java
tcp        1      0 127.0.0.1:7001              127.0.0.1:60230             CLOSE_WAIT  50557/java
tcp        1      0 127.0.0.1:7001              127.0.0.1:42800             CLOSE_WAIT  50557/java
tcp        1      0 127.0.0.1:7001              127.0.0.1:53916             CLOSE_WAIT  50557/java
tcp        1      0 127.0.0.1:7001              127.0.0.1:60622             CLOSE_WAIT  50557/java
tcp        1      0 127.0.0.1:7001              127.0.0.1:38762             CLOSE_WAIT  50557/java
tcp        1      0 127.0.0.1:7001              127.0.0.1:49438             CLOSE_WAIT  
……

Wenn Sie online nach Informationen suchen, gibt es keine gute Möglichkeit, diese Ports freizugeben. Nur Anwendungsprozesse, die diese Ports belegen, können beendet werden:

kill -KILL 50557

Führen Sie dann eine erneute Bereitstellung durch, generieren Sie eine neue Prozess-ID und senden Sie dann die Anforderung. Es wird normal sein. Überprüfen Sie den Portverbindungsstatus, es wird normal sein.
Generieren Sie den Thread-Dump-Datensatz wie folgt neu. Der Thread befindet sich im Status RUNNABLE:
Fügen Sie hier eine Bildbeschreibung ein

Guess you like

Origin blog.csdn.net/weixin_42534940/article/details/115362062