생계유지형 개발자/Elasticsearch

[Elasticsearch] java api client 버전 6.0에서 6.8로 업그레이드

이 가을 2020. 11. 5. 17:07

현재 기준으로 공식 엘라스틱서치 최신 안정화 버전은 7.9 버전이다.

 

현재 운영중인 서비스는 엘라스틱서치 6.0 버전을 설치하였으며,

스프링부트를 기반으로 개발된 서비스에서 자바 클라이언트를 사용하여 설치된 엘라스틱서치에 연결한다.

자바 클라이언트 버전은 6.0.0 이다.

 

설치된 엘라스틱서치의 노드들을 6.8 -> 7.9 버전으로 업그레이드(진행 중) 하면서 6.0.0 버전의 자바 클라이언트를 사용할 수 없게 되었다.

자바 클라이언트 버전을 6.0.0 -> 7.9로 일괄 업그레이드 하면 좋겠지만 모든 노드들을 전환하는데 시간이 걸리기 때문에 6.8 버전과 7.9 버전을 호환할 수 있는 6.8 버전으로 클라이언트를 변경하기로 했다.

 

또한 기존의 6.0 버전 자바 클라이언트는 Java API 였다면 이번에는 Java High Level REST Client로 변경할 계획이다.

Java API는 Deprecated 대상으로 엘라스틱서치 8.0 이상부터는 사라진다고 한다.

 

gradle.build 라이브러리 추가

    /** elasticsearch java api (old) */
    // compile('org.elasticsearch.client:transport:6.0.0')

    /** elasticsearch high-level Java REST (new) */
    compile('org.elasticsearch.client:elasticsearch-rest-high-level-client:6.8.0')

hight level rest client 라이브러리 6.8.x 버전대에 최신 버전은 6.8.13 버전이다.

공식 가이드문서에서도 6.8.13 기준으로 설명되어 있으나, 아래의 런타임 오류가 발생하고 연동하는 엘라스틱 버전이 6.x라면 6.8.0 버전을 사용해야 한다.

// 아래와 같은 런타임 오류가 발생하면  6.8.0 버전으로 변경해보자.
Exception in thread "main" java.lang.NoSuchMethodError: org.elasticsearch.action.support.IndicesOptions.ignoreThrottled()Z

at org.elasticsearch.client.RequestConverters$Params.withIndicesOptions(RequestConverters.java:966)
at org.elasticsearch.client.RequestConverters.addSearchRequestParams(RequestConverters.java:417)
at org.elasticsearch.client.RequestConverters.search(RequestConverters.java:404)
at org.elasticsearch.client.RestHighLevelClient.lambda$search$2(RestHighLevelClient.java:932)
at org.elasticsearch.client.RestHighLevelClient.internalPerformRequest(RestHighLevelClient.java:1450)
at org.elasticsearch.client.RestHighLevelClient.performRequest(RestHighLevelClient.java:1424)
at org.elasticsearch.client.RestHighLevelClient.performRequestAndParseEntity(RestHighLevelClient.java:1394)
at org.elasticsearch.client.RestHighLevelClient.search(RestHighLevelClient.java:930)

6.8.13 버전을 import하니까 6.4.3도 import되는거 보니까 왠지 내 추측상으로는 7.x을 import하면 6.8.x도 import해서 충돌이 나는건지 어쩐건지... 잘 모르겠다.

Property 정의

7개의 노드에 연결하기 위해 배열형태로 프로퍼티를 정의했다. 

es:
  nodes:
    - ip: '10.xx.xx.xx'   # node-01 v7.9.3    master-01 v7.9.3
      port: '9200'
    - ip: '10.xx.xx.xx'   # node-02 v6.8.12   master-02 v7.9.3
      port: '9200'
    - ip: '10.xx.xx.xx'  # node-03 v6.8.12   master-03 v7.9.3
      port: '9200'
    - ip: '10.xx.xx.xx'   # node-04 v6.8.12
      port: '9200'
    - ip: '10.xx.xx.xx'   # node-05 v6.8.12
      port: '9200'
    - ip: '10.xx.xx.xx'  # node-06 v6.8.12
      port: '9200'
    - ip: '10.xx.xx.xx'  # node-07 v6.8.12
      port: '9200'

JAVA 코드 구현

내가 대충 구현한 Search API 호출하는 기본 모습이고 공식 문서를 보고 필요한 API를 구현하는게 가장 정확하다.

public SearchResponse search() {

    /************************************************
     * 엘라스틱서치 연동 클라이언트 객체 생성
     ************************************************/
    HttpHost[] httpHosts = new HttpHost[3];
    httpHosts[0] = new HttpHost("10.xx.xx.111", 9200, "http");
    httpHosts[1] = new HttpHost("10.xx.xx.112", 9200, "http");
    httpHosts[2] = new HttpHost("10.xx.xx.113", 9200, "http");

	/** Creating client connection */
    RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(httpHosts));


    /************************************************
     * 검색조건 파라미터 생성
     ************************************************/
    SearchRequest searchRequest = new SearchRequest();  /* 검색조건 파라미터 */
    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();  /* 검색조건 쿼리빌더 */
    
    /** Set queries on SearchSourceBuilder */
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    List<QueryBuilder> queries = boolQueryBuilder.filter();
    queries.add(....); 
    sourceBuilder.query(boolQueryBuilder);
    searchRequest.source(sourceBuilder);

    /** Set indecies(or an index) and option */
    searchRequest.indices("index-2020.11.*");
    searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());

    /** Set Sorting field */
    sourceBuilder.sort(new FieldSortBuilder("_uid").order(SortOrder.ASC));

    /** Set source filtering */
    String[] includeFields, excludeFields;
    sourceBuilder.fetchSource(includeFields, excludeFields);
    
    /** Set number of from, size and timeout */
    sourceBuilder.from(from);
    sourceBuilder.size(size);
    sourceBuilder.timeout(new TimeValue(3, TimeUnit.MINUTES));
    
    
    /************************************************
     * 검색요청 및 검색결과 응답 수신
     ************************************************/
    
    SearchResponse searchResponse = null;	/* 검색결과 응답 객체 */
    
    try {
    
      /** call search() */
      searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

      SearchHits hits = searchResponse.getHits();	
      SearchHit[] searchHits = hits.getHits();	// search result data

      long duration = searchResponse.getTook().duration();
      TimeValue totalDuration = new TimeValue(duration);
      log.info("Received elasticsearch result");
      log.info("|- Duration       => {}", totalDuration.toString());
      log.info("|- Results/total  => {} / {}", hits.getHits().length, hits.totalHits);
      
	} catch (Exception e) {
    
      	log.error(": {}", e.getLocalizedMessage());
      	e.printStackTrace();
        
    } finally {
      	try {
        
          if (client != null) {
              client.close();
          }
        
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    return searchResponse;
}

 

다만 연동 구현 후 오류가 발생한다면 다른 블로그 게시물을 확인하자 ~