- SVN : https://dev.naver.com/svn/naverapis/trunk/naver-java-client-samples/
- ID : anonsvn
- Password : anonsvn
- Maven 프로젝트
- Checkout후 mvn eclipse:eclipse로 이클립스 프로젝트 생성
- ResourceConnector.java :GET 요청 공통 interface
- SimpleConnector.java : JDK의 URLConnection 활용
- HttpClient3Connector.java: Apache HttpClient 3.x 활용
- HttpClient4Connector.java: Apache HttpComponent 4.x 활용
- SearchApiClient.java : ROME 활용
- SearchApiClientTest.java : 파싱만을 테스트
- SearchApiClientIntegationTest : 실제 API 서버와 연결해서 테스트
- Spring RestTemplate + ROME 활용
- NaverSearchHttpMessageConverter.java : 메시지 파싱 모듈. 기존 객체 상속
- NaverSearchRestTemplateTest : RestTemplate 활용 예제
- Jaxb2 활용
- ShortUrlApiClientJaxbImpl.java : 파싱 구현 코드
- ShortUrlApiClientJaxbImplTest.java : 고정된 파일을 이용해서 파싱만을 테스트
- ShortUrlApiClientJaxbImplIntegrationTest.java : 파싱을 테스트
- JacksonJson Object mapping :
- ShortUrlApiClientJsonMapperImpl.java : 파싱 구현 코드
- ShortUrlApiClientJsonMapperImplTest.java : 파싱테스트
- ShortUrlApiClientJsonMapperImplIntegrationTest.java : 실제 API 서버와 연결해서 테스트
- LinkProcessorJsonMapperImpl.java : API의 결과 값과 매핑하는 목표 객체의 형태가 다를 때 파싱 예제
- LinkProcessorJsonMapperImplTest.java : 파싱 테스트
- Jackjson streamming 활용
- LinkProcessorJsonStreamImpl.java : 파싱 구현 코드
- LinkProcessorJsonStreamImplTest.java : 파싱만을 테스트
- Spring rest template 활용
public User findUser(String userId) {
String response = HttpUtils.callGet(API_URL + userId);
String username = ParseUtils.getTagValue(response, "username");
String address = ParseUtils.getTagValue(response, "address");
return new User(userId, username, address);
}
- 문제점은?
-
Timeout을 설정해라.
-
전체 Application의 스레드 풀을 고갈시킬 수도 있다. (501 error)
-
설정 예
- Tomcat max thread가 500개, max TPS(Transaction per second)가 100일 때 API timeout을 4초로 설정.
-
JDK URL Connection
@Override public InputStream open(String url) throws IOException { URLConnection con = new URL(url).openConnection(); con.setConnectTimeout(this.connectTimeoutMilsec); con.setReadTimeout(this.readTimeoutMilsec); return con.getInputStream(); }
-
Apache HttpClient 3.x
HttpConnectionManagerParams params = new HttpConnectionManagerParams(); params.setConnectionTimeout(connectTimeoutMilsec); params.setSoTimeout(readTimeoutMilsec); params.setDefaultMaxConnectionsPerHost(100); params.setMaxTotalConnections(100); connManager = new MultiThreadedHttpConnectionManager(); connManager.setParams(params); this.httpClient = new HttpClient(connManager);
-
Apache HttpComponent 4.x
connManager = new ThreadSafeClientConnManager(); connManager.setMaxTotal(100); connManager.setDefaultMaxPerRoute(100); HttpClient client = new DefaultHttpClient(connManager); client.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectTimeoutMilsec); client.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, readTimeoutMilsec); this.httpClient = client;
-
-
예외 상황을 테스트해라.
- 예외메시지 파싱을 테스트하라.
- 예외 메시지 파일을 따로 저장해서 테스트
- Text based protocol의 장점을 활용
- 예외 메시지 파일을 따로 저장해서 테스트
- 통합 테스트도 자동화하면 도움이 된다.
- 매일 돌리다보면 정기 점검 일때, 서버 상태가 이상할 때를 만날 수도 있다.
- 때로는 스펙에 명시되지 않은 동작까지 알 수 있다.
- 예외메시지 파싱을 테스트하라.
- 불필요한 중간객체을 생성하지 마라.
- 파싱 모듈에서도 Stream을 바로 활용해라.
- 명시적으로 DOM API를 쓰지 마라
- Event base인 SAX에 비해서 비효율적.
- XStream은 Stream based의 처리
- JAXB는 보다 high level의 추상화 계층
- SAX, StAX(Streaming API for XML) 등 활용 가능
- Connection Pool을 시도해라.
- Apache HttpClient 4.x
- MultiThreadedHttpConnectionManager 활용
- DefaultMaxConnectionsPerHost’ 값 주의
- Default는 2. 성능에 악영향.
- Concurrent user * 2 값이 바람직
- Apache HttpComponent 4.x
- ThreadSafeClientConnManager 활용
- defaultMaxPerRoute 속성의 default값 주의
- Apache HttpClient 4.x
- 여러 API를 함께 쓴다면 병렬 실행을 고려해 봐라
- JDK concurrent API (ExecutorService)
- Apache HttpClientd의 HttpAsyncClient
- Jetty HttpClient
- 얼마만큼 효과가 있을지는 해봐야 안다.
- HttpCient 3.x
- MultiThreadedHttpConnectionManager를 사용하지 않을 때의 HttpClient
- HttpMethod, HttpState, HostConfigureation
- HttpClient 4.x
- HttpGet, HttpPost
- Jaxb2
- Marshaller, Unmarshaller
- JacksonJson
- JsonParser
- Apache HttpCient 3.x
- MultiThreadedHttpConnectionManager를 사용 할 때의 HttpClient
- Apache HttpComponent 4.x
- DefaultHttpClient
- SingleClientConnManager, ThreadSafeClientConnManager
- Jaxb2
- JAXBContext
- JacksonJson
- Thread-safe after configuration : ObjectMapper, JsonFactory
- 사례 : Spring의 RestTemplate을 매번 생성한다면?
-
RestTemplated의 기본 생성자는 StringHttpMessageConverter를 생성
-
StringHttpMessageConverter는 생성자에서 encoding을 위해 시스템이 지원하는 character set을 확인하게 됨
-
charsets.jar 파일 안의 객체를 동적 로딩하게 되는데, 동적 로딩을 하는 jdk 코드 내 synchronize로 감싼 코드로 인해 locking
-
길지 않은 Lock구간이지만 대량 요청 시에는 문제가 됨
-
이 클래스는 Thread-safe하므로 매번 생성할 필요 없었음
-
Thread dump
at java.nio.charset.Charset$1.getNext(Charset.java:317) at java.nio.charset.Charset$1.hasNext(Charset.java:332) at java.nio.charset.Charset$4.run(Charset.java:551) at java.security.AccessController.doPrivileged(Native Method) at java.nio.charset.Charset.availableCharsets(Charset.java:546) at org.springframework.http.converter.StringHttpMessageConverter.(StringHttpMessageConverter.java:52)
-
- 문서에 스레드 안정성에 대한 언급이 없으면 안전하지 않다고 가정해라.
- 먼저 검색해봐라.
- 사례 : XStream 1.3.1의 버그
- 'Infinite loop due to unsafe collection usage'
- WeakHashMap을 Threadsafe하지 않게 접근
- 무한루프 -> CPU 100%
- 네할렘 서버 교체 시기에 나타남
- 1.4.0에서 패치됨
- 구현체를 갈아 끼울 수 있는 계층 활용
- 예) Spring OXM
- 직접 정의한 추상화 계층
- 여러 프로젝트에서 공유될 수 있도록 해라
- 변화 대응 속도가 빨라진다.
- 예) 통신 모듈사용 라이브러리를 바꿀 때. Apache HttpClient 3.x => HttpComponent 4.x
- 테스트 용이성
- 예) 예상되는 결과를 파일에서 읽어서 파싱 모듈만을 테스트하기
public User findUser(String userId) {
String response = HttpUtils.callGet(API_URL + userId);
String username = ParseUtils.getTagValue(response, "username");
String address = ParseUtils.getTagValue(response, "address");
return new User(userId, username, address);
}
- 문제점은?
- 비정상적인 응답은 어떻게 테스트할까?
- String response대신 Stream으로 넘길 수도 있지 않을까?
- ParseUtils에서는 반복적으로 처음부터 문자열을 검색하지는 않을까?
- 기본JDK에 포함
- 의존성 추가가 없음.
- HTTP의 용어와 직관적으로 대응되지는 않음.
- POST 요청 : URLConnection.setDoOutput(true);
- Header 설정 : URLConnection.setRequestProperty("Content-Type", "text/plain");
-
레거시 코드에 많이 보임.
-
Dependency
<dependency> <groupId>commons-httpclient</groupId> <artifactId>commons-httpclient</artifactId> <version>3.1</version> </dependency>
-
Dependency
<dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.1.3</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpasyncclient</artifactId> <version>4.0-beta3</version> </dependency>
-
API 개선
-
3.x와 호환성 없음
-
명확한 Thread safety 문서화
@NotThreadSafe public class HttpGet extends HttpRequestBase { @ThreadSafe public class DefaultHttpClient extends AbstractHttpClient { @ThreadSafe public class SingleClientConnManager implements ClientConnectionManager {
-
Async를 기본적으로 지원
<dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-client</artifactId> <version>8.1.7.v20120910</version> </dependency>
- 여러 통신 모듈과 해석모듈의 추상화 계층.
- 통신 모듈에 Apache Http Client 3,4와 URLConnection 사용
- 해석모듈에 Jaxb, Jacksonjson등의 다양한 기본 구현체 제공
- 확장 가능
- Spring Android에서도 제공
- Android 버전에 따라서 통신 라이브러리를 권장하는 것으로 알아서 선택해줌.
- 진저브래드(2.3)전에는 구글에서 Apache HttpComponents 권장
- Android 버전에 따라서 통신 라이브러리를 권장하는 것으로 알아서 선택해줌.