๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
server

org.apache.http.NoHttpResponseException {{host}}:443 failed to respond

by Younji! 2020. 8. 12.

๐Ÿ‘ฟ org.apache.http.NoHttpResponseException ๐Ÿ‘ฟ

์™ธ๋ถ€ ์„œ๋ฒ„์™€ ์—ฐ๋™ ํ›„ ์šด์˜ ์ค‘์— ๊ฐ„ํ—์ ์œผ๋กœ ์ •์ƒ์ ์ธ ์‘๋‹ต์ด ๋–จ์–ด์ง€์ง€ ์•Š์•˜๋Š”๋ฐ, stacktrace๋ฅผ ์‚ดํŽด๋ณด๋‹ˆ ์žฌ์‹œ๋„ ํ•˜๋ฉด ํ•ด๊ฒฐ๋  ๋“ฏํ•œ๋ฐ ๋ช…ํ™•ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ข€ ๋” ์ฐพ์•„๋ณด๊ธฐ๋กœ ํ•œ๋‹ค.

NoHttpResponseException

ํƒ€๊ฒŸ ์„œ๋ฒ„์—์„œ ์œ ํšจํ•˜์ง€ ์•Š์€ HTTP ์‘๋‹ต์œผ๋กœ ์ œ๋Œ€๋กœ ์‘๋‹ตํ•˜๊ธฐ๋ฅผ ์‹คํŒจํ–ˆ๋‹ค๋Š” ์‹ ํ˜ธ๋ผ๊ณ  ํ•˜๋Š”๋ฐ ๋ฌธ์ œ๋Š” ์ด๊ฑฐ๋‹ค. 

ํ˜„์ƒ์˜ ์ด์œ ๋Š” HTTP/1.1์˜ Keep-Alive๋กœ ์ธํ•ด httpclient๋Š” ํ†ต์‹ ์ด ๋๋‚œ connection์„ ์ข…๋ฃŒํ•˜์ง€ ์•Š๊ณ  ๋™์ผhost:port์— ๋Œ€ํ•ด ๋™์ผํ•œ ์ปค๋„ฅ์…˜์„ ์ด์šฉํ•˜๋ ค ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
๋น„๋ก ์„œ๋ฒ„์ธก์€ ํ†ต์‹ ์ด ์™„๋ฃŒ๋˜์–ด ํ•ด๋‹น ์—ฐ๊ฒฐ์„ close ํ• ์ง€๋ผ๋„ client ์ธก์€ ์ปค๋„ฅ์…˜ ๊ฐ์ฒด๊ฐ€ ์—ฌ์ „ํžˆ ์—ด๋ ค์žˆ๊ณ  ๋ฐ์ดํ„ฐ๊ฐ€ ์ธ์ž…๋˜๊ธธ ๊ธฐ๋‹ค๋ฆฌ๊ณ  ์žˆ๊ฒŒ๋œ๋‹ค. ( close()์˜ ์‹ค์ œ ์˜๋ฏธ๋Š” ์†Œ์ผ“์˜ ๋‹จ์ ˆ์ด ์•„๋‹Œ “๋‚˜๋Š” ๋” ์ด์ƒ ๋ณด๋‚ผ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.“๋กœ ์ƒ๋Œ€์ธก์—์„œ๋Š” ํ•ด๋‹น ์ปค๋„ฅ์…˜์„ ๋‹จ์ ˆํ•˜์ง€ ์•Š๋Š” ์ด์ƒ ์—ฌ์ „ํžˆ ๋ฐ์ดํ„ฐ๊ฐ€ ์ธ์ž…๋  ์ˆ˜ ์žˆ์Œ์„ ์˜๋ฏธํ•œ๋‹ค. )
์ด๋•Œ๋ฅผ half-closed connection ์œผ๋กœ ํ‘œํ˜„ํ•˜๋ฉฐ ์ด๋Š” TCP๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ๋™์ž‘ํ•˜๊ฒŒ๋” ์„ค๊ณ„๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์œผ๋กœ ๋ฒ„๊ทธ๊ฐ€ ์•„๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์ƒํ™ฉ์ด ๋˜๋ฉด JVM์ƒ์˜ connection ๊ฐ์ฒด๋Š” ๋‹น์—ฐํžˆ ์‚ด์•„์žˆ์ง€๋งŒ ๋‚ด๋ถ€ ์†Œ์ผ“์€ CLOSE_WAIT ์ƒํƒœ๊ฐ€ ๋œ๋‹ค.

์ •์ƒ์ ์ธ ์ƒํƒœ์ด๊ณ  ํ†ต์‹ ์ด ๋๋‚œ ์ƒํ™ฉ์ด์ง€๋งŒ ์‚ด์•„์žˆ๋Š” ์ปค๋„ฅ์…˜์œผ๋กœ ๋˜ ์—ฐ๊ฒฐ์„ ๋งบ์œผ๋ฉด์„œ ๋ฐœ์ƒํ•œ๋‹ค ๋ผ๋Š” ๊ฒƒ. 

 

์ด์ œ ๋ฌธ์ œ ํ•ด๊ฒฐ๋ฐฉ์•ˆ์„ ์ฐพ์•„๋ณด๋„๋ก ํ•œ๋‹ค.

 

๋ฒ„์ „ ๋ฌธ์ œ๋ผ๊ณ  ํ•˜๋Š” ์˜๊ฒฌ์ด ์žˆ์—ˆ์ง€๋งŒ ์ตœ์‹  ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ด ์–˜๊ธฐ๋Š” ๋งž์ง€ ์•Š๊ณ 

public PoolingHttpClientConnectionManager(HttpClientConnectionOperator httpClientConnectionOperator, HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory, long timeToLive, TimeUnit tunit) {  
  this.log = LogFactory.getLog(this.getClass());  
  this.configData = new PoolingHttpClientConnectionManager.ConfigData();  
  this.pool = new CPool(new PoolingHttpClientConnectionManager.InternalConnectionFactory(this.configData, connFactory), 2, 20, timeToLive, tunit);  
  this.pool.setValidateAfterInactivity(2000);  
  this.connectionOperator = (HttpClientConnectionOperator)Args.notNull(httpClientConnectionOperator, "HttpClientConnectionOperator");  
  this.isShutDown = new AtomicBoolean(false);  
}

์ปค๋„ฅ์…˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ๋งˆ์ง€๋ง‰์œผ๋กœ ์‚ฌ์šฉํ–ˆ๋˜ ๊ฒฝ๊ณผ์‹œ๊ฐ„(`validAfterInactivity` = 2000ms default)์„ ์ข€ ๋” ์ค„์ด๋ผ๋Š” ์–˜๊ธฐ๊ฐ€ ์žˆ์–ด ์‹œ๋„ํ•ด๋ณด๋ ค ํ–ˆ์ง€๋งŒ, ๋งˆ์นจ ์‹œ๋„ํ•ด๋ณด์•˜๋˜ ์‚ฌ๋žŒ์ด ์žˆ์–ด ํ™•์ธํ•ด๋ณด๋‹ˆ ์ œ๋Œ€๋กœ ์›Œํ‚นํ•˜์ง€ ์•Š๋Š”๋‹ค ํ•ด์„œ ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์ฐพ์•„๋ณธ๋‹ค.

 

๋งˆ์ง€๋ง‰์œผ๋กœ ์ ์šฉํ•œ ํ•ด๊ฒฐ์ฑ…์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

HttpClient ํ†ต์‹ ์„ ์ด์šฉํ•  ๋•Œ KeepAlive ์„ค์ •์„ ํ•˜์ง€ ์•Š์œผ๋ฉด ๊ณ„์† ์œ ์ง€ํ•œ๋‹ค๊ณ  ํ•˜์ง€๋งŒ, ์ตœ๋Œ€ํ•œ 2์‹œ๊ฐ„๊นŒ์ง€ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฑธ๋ณด๋‹ˆ HTTP ์„œ๋ฒ„์—์„œ ๋ฌด๊ธฐํ•œ์œผ๋กœ ์œ ์ง€ํ•˜๊ณ  ์žˆ์ง„ ์•Š๋Š” ๋“ฏํ•˜๋‹ค. (ํ˜„์žฌ KeepAlive ์„ค์ •์€ ON์ž„) ์–ด๋Š ์ˆ˜์ค€์—์„œ ์ปค๋„ฅ์…˜์„ ๋Š๋Š”์ง€๋Š” ๋” ์•Œ์•„๋ด์•ผํ•  ๋“ฏ ํ•˜๋‹ค.

 

์ฝ”๋“œ๋ฅผ ๋งค์šฐ ์–ผ์ถ” ๋ณด๋ฉด..keepalive ์˜ต์…˜์ด ์œ ์ž…๋˜์ง€ ์•Š์œผ๋ฉด -1๋กœ ์„ค์ •ํ•˜๋‹ˆ ๋ฌด๊ธฐํ•œ์œผ๋กœ ๋ณด๊ณ  ์žˆ๋Š” ๋“ฏํ•˜๋‹ค. (์ •๋ง ๋งˆ๋ƒฅ ๋ฌด๊ธฐํ•œ์ด์ง„ ์•Š๊ฒ ์ง€๋งŒ) ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์ด๊ณณ์„ ์ฐธ๊ณ ํ•ด๋„ ์ข‹๋‹ค.

> If the Keep-Alive header is not present in the response, HttpClient assumes the connection can be kept alive indefinitely.

if (this.reuseStrategy.keepAlive(response, context)) {  
	long duration = this.keepAliveStrategy.getKeepAliveDuration(response, context);  
	if (this.log.isDebugEnabled()) {  
	String s;  
	if (duration > 0L) {  
      s = "for " \+ duration + " " \+ TimeUnit.MILLISECONDS;  
    } else {  
      s = "indefinitely";  
    }  

	this.log.debug("Connection can be kept alive " \+ s);  
}  

connHolder.setValidFor(duration, TimeUnit.MILLISECONDS);  
connHolder.markReusable();

์œ„์™€ ๊ฐ™์ด ๋ฌด๊ธฐํ•œ์œผ๋กœ ์„ค์ •ํ•ด์ฃผ์ง€ ์•Š๊ณ  ๋ช…์‹œ์ ์œผ๋กœ ์งง์€ ์‹œ๊ฐ„์„ ๊ฐ€์ ธ๊ฐ€๋„๋ก ์ปค์Šคํ…€์œผ๋กœ KeepAliveStrategy๋ฅผ ์„ค์ •ํ•ด์„œ (์ด ์„ค์ •ํ•œ ์—ฐ๊ฒฐ ๋น„์ฆˆ๋‹ˆ์Šค ์ž์ฒด๊ฐ€ keepalive๋ฅผ ๊ธธ๊ฒŒ ์ง€์†ํ•  ์ •๋„๊นŒ์ง„ ์•„๋‹ˆ๋‹ค.) ์ปค๋„ฅ์…˜ ํ•ด์ œ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ฒŒ๋” ์ฒ˜๋ฆฌํ•ด์คฌ๋‹ค. (๋”ด ์–˜๊ธฐ..KeepAlive ์„ค์ •์ด ๋นˆ๋ฒˆํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ  ๋ฐ›์„ ๋•Œ ์„ฑ๋Šฅ ํ–ฅ์ƒ์— ํ‚ค ํฌ์ธํŠธ๊ฐ€ ๋˜์ง€๋งŒ ์—ญ์œผ๋กœ ๊ทธ๋ ‡์ง€ ์•Š์„ ๋• ๋ฆฌ์†Œ์Šค๋ฅผ ๋ฌผ๊ณ  ์žˆ๊ธฐ์— ๊ทธ๋ ‡์ง€ ์•Š์„ ๋•Œ๋„ ์žˆ๋‹ค.)

 

HttpClient์—์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š” ConnectionKeepAliveStrategy๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž. ๊ด€๋ จํ•œ connectionmanegement ์•„ํŒŒ์น˜ ํŠœํ† ๋ฆฌ์–ผ์„ ๋ด๋„ ์ข‹๋‹ค. ํŠน์ • ํƒ€๊ฒŸ ํ˜ธ์ŠคํŠธ๋งŒ Keep-Alive timeout์„ ๋ณด๋‹ค ์งง๊ฒŒ ๊ฐ€์ ธ๊ฐ€๊ฒŒ๋” ์„ค์ •ํ•ด์ค€๋‹ค.

public class HttpKeepAliveStrategy implements ConnectionKeepAliveStrategy {

    private static final String KEEP_ALIVE_TARGET_HOST_NAME = "https://target.host.name";

    @Override
    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
        // Honor 'keep-alive' header
        HeaderElementIterator it = new BasicHeaderElementIterator(
            response.headerIterator(HTTP.CONN_KEEP_ALIVE));
        while (it.hasNext()) {
            HeaderElement he = it.nextElement();
            String param = he.getName();
            String value = he.getValue();
            if (value != null && param.equalsIgnoreCase("timeout")) {
                try {
                    return Long.parseLong(value) * 1000;
                } catch (NumberFormatException ignore) {
                }
            }
        }

        HttpHost target = (HttpHost) context.getAttribute(HttpClientContext.HTTP_TARGET_HOST);
        if (KEEP_ALIVE_TARGET_HOST_NAME.equalsIgnoreCase(target.getHostName())) {
            return 5 * 1000;
        } else {
            return 30 * 1000;
        }
    }
}

 

์ฐธ๊ณ  URL

๋Œ“๊ธ€