티스토리 뷰

평소처럼 restTemplate를 사용하다가 이상하게 연동이 안되는 상황 발생

(서버는 npm의 json-server 사용)

    fun getTest() {
        val body = restTemplate.getForEntity("$host/test", String::class.java).body
        logger.info {"body : $body"}
    }
o.s.web.client.RestTemplate              : HTTP GET http://localhost:3020/test
o.s.web.client.RestTemplate              : Accept=[text/plain, application/json, application/*+json, */*]
o.s.s.s.TaskUtils$LoggingErrorHandler    : Unexpected error occurred in scheduled task

org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:3020/test": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:791) ~[spring-web-5.3.25.jar:5.3.25]
	at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:717) ~[spring-web-5.3.25.jar:5.3.25]
	at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:367) ~[spring-web-5.3.25.jar:5.3.25]
	at com.redmine.redminehelper.TestService.getTest(TestService.kt:28) ~[main/:na]
	at com.redmine.redminehelper.TestWatcher.watch(TestWatcher.kt:21) ~[main/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_342]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_342]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_342]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_342]
	at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84) ~[spring-context-5.3.25.jar:5.3.25]
	at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-5.3.25.jar:5.3.25]
	...
Caused by: java.net.ConnectException: Connection refused: connect
	at java.net.DualStackPlainSocketImpl.connect0(Native Method) ~[na:1.8.0_342]
	at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79) ~[na:1.8.0_342]
	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[na:1.8.0_342]
	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[na:1.8.0_342]
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[na:1.8.0_342]
	at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) ~[na:1.8.0_342]
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:1.8.0_342]
	at java.net.Socket.connect(Socket.java:613) ~[na:1.8.0_342]
	at java.net.Socket.connect(Socket.java:561) ~[na:1.8.0_342]
	at sun.net.NetworkClient.doConnect(NetworkClient.java:180) ~[na:1.8.0_342]
	at sun.net.www.http.HttpClient.openServer(HttpClient.java:463) ~[na:1.8.0_342]
	at sun.net.www.http.HttpClient.openServer(HttpClient.java:558) ~[na:1.8.0_342]
	at sun.net.www.http.HttpClient.<init>(HttpClient.java:242) ~[na:1.8.0_342]
	at sun.net.www.http.HttpClient.New(HttpClient.java:339) ~[na:1.8.0_342]
	at sun.net.www.http.HttpClient.New(HttpClient.java:357) ~[na:1.8.0_342]
	at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1233) ~[na:1.8.0_342]
	...

이상한건 curl이나 브라우저로는 연동이 잘 되는데 스프링을 통해서만 안되는 기현상

> curl http://localhost:3020/test
StatusCode        : 200
StatusDescription : OK
Content           : {
                      "result": "true"
                    }
RawContent        : HTTP/1.1 200 OK
                    Vary: Origin, Accept-Encoding
                    Access-Control-Allow-Credentials: true
                    Pragma: no-cache
                    X-Content-Type-Options: nosniff
                    Connection: keep-alive
                    Keep-Alive: timeout=5
                    Content-Length...
Headers           : {[Vary, Origin, Accept-Encoding], [Access-Control-Allow-Credentials, true], [Pragma, no-cache], [X-
                    Content-Type-Options, nosniff]...}
ParsedHtml        : mshtml.HTMLDocumentClass
RawContentLength  : 22

해결

restTemplate에서 SimpleClientHttpRequestFactory라는 내부 http 클라이언트를 사용하는데 이놈이 문제인듯,

아파치에서 제공하는 httpClient로 교체해주면 해결된다.

디펜던시만 추가해주면 내부에서 알아서 우선순위를 올리는 듯 하니 소스 수정 없이 build파일만 수정한다.

 

// build.gradle.kts
implementation("org.apache.httpcomponents:httpclient:4.5.14")

성공

o.s.web.client.RestTemplate              : HTTP GET http://localhost:3020/test
o.s.web.client.RestTemplate              : Accept=[text/plain, application/json, application/*+json, */*]
o.a.h.client.protocol.RequestAddCookies  : CookieSpec selected: default
o.a.h.client.protocol.RequestAuthCache   : Auth cache not set in the context
h.i.c.PoolingHttpClientConnectionManager : Connection request: [route: {}->http://localhost:3020][total available: 1; route allocated: 1 of 5; total allocated: 1 of 10]
org.apache.http.impl.conn.CPool          : Connection [id:2][route:{}->http://localhost:3020][state:null] expired @ Tue Feb 21 14:15:24 KST 2023
h.i.c.DefaultManagedHttpClientConnection : http-outgoing-2: Close connection
h.i.c.PoolingHttpClientConnectionManager : Connection leased: [id: 3][route: {}->http://localhost:3020][total available: 0; route allocated: 1 of 5; total allocated: 1 of 10]
o.a.http.impl.execchain.MainClientExec   : Opening connection {}->http://localhost:3020
.i.c.DefaultHttpClientConnectionOperator : Connecting to localhost/127.0.0.1:3020
.i.c.DefaultHttpClientConnectionOperator : Connect to localhost/127.0.0.1:3020 timed out. Connection will be retried using another IP address
.i.c.DefaultHttpClientConnectionOperator : Connecting to localhost/0:0:0:0:0:0:0:1:3020
.i.c.DefaultHttpClientConnectionOperator : Connection established 0:0:0:0:0:0:0:1:3146<->0:0:0:0:0:0:0:1:3020
o.a.http.impl.execchain.MainClientExec   : Executing request GET /test HTTP/1.1
o.a.http.impl.execchain.MainClientExec   : Target auth state: UNCHALLENGED
o.a.http.impl.execchain.MainClientExec   : Proxy auth state: UNCHALLENGED
org.apache.http.headers                  : http-outgoing-3 >> GET /test HTTP/1.1
org.apache.http.headers                  : http-outgoing-3 >> Accept: text/plain, application/json, application/*+json, */*
org.apache.http.headers                  : http-outgoing-3 >> Host: localhost:3020
org.apache.http.headers                  : http-outgoing-3 >> Connection: Keep-Alive
org.apache.http.headers                  : http-outgoing-3 >> User-Agent: Apache-HttpClient/4.5.14 (Java/1.8.0_342)
org.apache.http.headers                  : http-outgoing-3 >> Accept-Encoding: gzip,deflate
org.apache.http.wire                     : http-outgoing-3 >> "GET /test HTTP/1.1[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 >> "Accept: text/plain, application/json, application/*+json, */*[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 >> "Host: localhost:3020[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 >> "Connection: Keep-Alive[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 >> "User-Agent: Apache-HttpClient/4.5.14 (Java/1.8.0_342)[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 >> "Accept-Encoding: gzip,deflate[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 >> "[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "HTTP/1.1 200 OK[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "X-Powered-By: Express[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "Vary: Origin, Accept-Encoding[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "Access-Control-Allow-Credentials: true[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "Cache-Control: no-cache[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "Pragma: no-cache[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "Expires: -1[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "X-Content-Type-Options: nosniff[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "Content-Type: application/json; charset=utf-8[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "Content-Length: 22[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "ETag: W/"16-yzVxm7zLek+JyDn3hOpcSoZ9L4A"[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "Date: Tue, 21 Feb 2023 05:15:31 GMT[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "Connection: keep-alive[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "Keep-Alive: timeout=5[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "[\r][\n]"
org.apache.http.wire                     : http-outgoing-3 << "{[\n]"
org.apache.http.wire                     : http-outgoing-3 << "  "result": "true"[\n]"
org.apache.http.wire                     : http-outgoing-3 << "}"
org.apache.http.headers                  : http-outgoing-3 << HTTP/1.1 200 OK
org.apache.http.headers                  : http-outgoing-3 << X-Powered-By: Express
org.apache.http.headers                  : http-outgoing-3 << Vary: Origin, Accept-Encoding
org.apache.http.headers                  : http-outgoing-3 << Access-Control-Allow-Credentials: true
org.apache.http.headers                  : http-outgoing-3 << Cache-Control: no-cache
org.apache.http.headers                  : http-outgoing-3 << Pragma: no-cache
org.apache.http.headers                  : http-outgoing-3 << Expires: -1
org.apache.http.headers                  : http-outgoing-3 << X-Content-Type-Options: nosniff
org.apache.http.headers                  : http-outgoing-3 << Content-Type: application/json; charset=utf-8
org.apache.http.headers                  : http-outgoing-3 << Content-Length: 22
org.apache.http.headers                  : http-outgoing-3 << ETag: W/"16-yzVxm7zLek+JyDn3hOpcSoZ9L4A"
org.apache.http.headers                  : http-outgoing-3 << Date: Tue, 21 Feb 2023 05:15:31 GMT
org.apache.http.headers                  : http-outgoing-3 << Connection: keep-alive
org.apache.http.headers                  : http-outgoing-3 << Keep-Alive: timeout=5
o.a.http.impl.execchain.MainClientExec   : Connection can be kept alive for 5000 MILLISECONDS
o.s.web.client.RestTemplate              : Response 200 OK
o.s.web.client.RestTemplate              : Reading to [java.lang.String] as "application/json;charset=utf-8"
h.i.c.PoolingHttpClientConnectionManager : Connection [id: 3][route: {}->http://localhost:3020] can be kept alive for 5.0 seconds
h.i.c.DefaultManagedHttpClientConnection : http-outgoing-3: set socket timeout to 0
h.i.c.PoolingHttpClientConnectionManager : Connection released: [id: 3][route: {}->http://localhost:3020][total available: 1; route allocated: 1 of 5; total allocated: 1 of 10]
c.s.r.r.codemind.CodeMindService         : result : true
c.s.r.r.codemind.CodeMindService         : body : {
  "result": "true"
}

 

'개발 > Java, Kotlin' 카테고리의 다른 글

Spring Database Naming(prefix)  (0) 2023.03.28
Pair, Triple  (0) 2023.03.09
Spread operator(전개구문) in kotlin  (0) 2022.12.06
히카리 풀 모니터링  (0) 2020.04.28
동기(Synchronous)/비동기(Asynchronous) 처리  (0) 2019.11.27
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함