지금까지 사용하던 HTTP 방식은 단 건의 요청을 처리하기에는 편리하지만 대량의 요청을 처리하기에는 적합하지 않습니다. 그렇기 때문에 엘라스틱서치에서는 다양한 프로그래밍 언어에서 직접적으로 사용할 수 있는 라이브러리를 개발해서 제공합니다.
ES 에서 지원하는 클라이언트 모듈은 https://www.elastic.co/guide/en/elasticsearch/client/index.html 에서 확인할 수 있습니다.
현재 (2022년 5월 4일 기준)
위와 같은 클라이언트 모듈을 지원하고 있습니다.
원래 자바 클라이언트에는
① 내부적으로 HTTP 방식으로 REST API 를 사용해 접근하는 방식
(Java REST Client, Java High Level REST Client)
② 네티(Netty) 모듈을 이용해 네이티브 클라이언트를 통해 접근 하는 방식
(Java Transport Client, Java CLient)
으로 총 두가지 클라이언트 모듈이 있는데 2번째 방식은 버전 8 기준으로 종료되었습니다.
Transport 방식이 종료되고, REST 클라이언트를 사용해야 하는 이유는 다음과 같습니다.
이번 예제에서도 REST 클라이언트 방식을 사용해서 구현해보도록 하겠습니다.
코드는 ⭐️ https://github.com/soyeon207/velog_example/tree/master/es-test-server ⭐️ 여기에서 확인하실 수 있습니다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
implementation 'org.springframework.boot:spring-boot-starter-web'
}
server:
port: 8003
elasticsearch:
host: localhost
port: 9200
indecies:
studentsIndexName: students
testIndexName: tests
EsProperties :: ES 연결할 때 필요한 데이터들을 설정해주는 파일
@Component
@Setter
@ConfigurationProperties(prefix = "elasticsearch")
public class EsProperties {
private String host; // ES 호스트
private int port; // ES 포트
private Indices indecies; // ES 인덱스 정보
public HttpHost httpHost() {
return new HttpHost(host, port, "http");
}
public String getStudentIndexName() {
return Optional.ofNullable(indecies).map(Indices::getStudentsIndexName).orElse(null);
}
public String getTestIndexName() {
return Optional.ofNullable(indecies).map(Indices::getTestIndexName).orElse(null);
}
@Getter
@Setter
public static class Indices {
String studentsIndexName;
String testIndexName;
}
}
EsConfig :: ES connect 관련 설정 파일
@Configuration
@RequiredArgsConstructor
public class EsConfig extends AbstractElasticsearchConfiguration {
private final EsProperties esProperties;
@Override
public RestHighLevelClient elasticsearchClient() {
// https://discuss.elastic.co/t/localhost-nodename-nor-servname-provided-or-not-known/186173/11
return new RestHighLevelClient(RestClient.builder(esProperties.httpHost()));
}
}
인덱스 생성은 sync(동기) 와 async(비동기) 로 할 수 있습니다.
동기 인덱스 생성 API
// ①
CreateIndexRequest request = new CreateIndexRequest(esProperties.getTestIndexName());
// ②
request.settings(Settings.builder()
.put("index.number_of_shards", 1)
.put("index.number_of_replicas", 0));
try {
// ③
CreateIndexResponse createIndexResponse = client.indices().create(request, RequestOptions.DEFAULT);
return createIndexResponse.isAcknowledged();
} catch (Exception e) {
log.error("인덱스 생성에 실패했습니다. 원인 : " + e.getMessage());
return false;
}
① 인덱스 생성은 CreateIndexRequest 를 통해서 할 수 있습니다. new CreateIndexRequest
로 객체를 생성해주고, 생성자 파라미터로 생성하고자 하는 인덱스 이름을 넣어줍니다. 이전에 설정했던 EsProperties 에 있는 getTestIndexName 메소드를 통해 tests 라는 인덱스를 만들어주도록 하겠습니다.
② 만들어질 인덱스에 샤드와 레플리카의 개수를 설정해줍니다.
해당 예제에서는 샤드의 개수는 1개, 레플리카의 개수를 0 개로 설정해주도록 하겠습니다.
③ client (RestHighLevelClient) 를 통해 tests 라는 인덱스를 만들어줍니다.
위에서 설정한 CreateIndexRequest 와 RequestOptions 를 파라미터로 넘겨주면 되는데 RequestOptions 의 경우에는 headers, parameters ... 등등을 커스텀하게 설정할 수 있습니다. 만일 커스텀하게 설정할 필요가 있는 경우 미리 선언되어 있는 RequestOptions.DEFAULT 를 사용하면 됩니다.
비동기 인덱스 생성 API
CreateIndexRequest request = new CreateIndexRequest(esProperties.getTestIndexName());
request.settings(Settings.builder()
.put("index.number_of_shards", 1)
.put("index.number_of_replicas", 0));
// ②
ActionListener<CreateIndexResponse> listener = new ActionListener<CreateIndexResponse>() {
@Override
public void onResponse(CreateIndexResponse createIndexResponse) {
System.out.println("성공!!");
}
@Override
public void onFailure(Exception e) {
log.error("인덱스 생성에 실패했습니다. 원인 : " + e.getMessage());
}
};
// ①
client.indices().createAsync(request, RequestOptions.DEFAULT, listener);
① 동기 방식과 거의 동일하지만 비동기 방식은 createAysnc 메소드를 사용합니다.
② 비동기 방식으로 실행될 코드를 설정해줍니다. onResponse 는 성공했을 때 실행할 메소드이고, onFailure 는 실패했을 때 실행할 메소드입니다.
try {
// ①
DeleteIndexRequest request = new DeleteIndexRequest(esProperties.getTestIndexName());
return client.indices().delete(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (Exception e) {
log.error("인덱스 삭제에 실패했습니다. 원인 : " + e.getMessage());
return false;
}
① 인덱스 삭제는 DeleteIndexRequest 를 사용합니다.
위와 동일하게 cleint(RestHighLevelClient) 를 사용하되 delete 메소드를 이용해 인덱스를 삭제해줍니다.
인덱스 오픈 / 닫기 ?
이게 무슨 소린가 할 수 있지만, ElasticSearch 에서는 인덱스를 오픈하거나 닫을 수 있습니다.
오픈하는 건 인덱스를 사용할 수 있는 상태, 닫는 건 인덱스를 사용할 수 없는 상태입니다.
인덱스를 닫았을 때에는 인덱스의 상태를 조회 (GET /_cat/indices?pretty
) 하면 상태가 close 로 노출되고,
인덱스를 사용하려고 해도 에러가 발생됩니다.
인덱스 오픈
try {
// ①
OpenIndexRequest request = new OpenIndexRequest(esProperties.getTestIndexName());
return client.indices().open(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (Exception e) {
log.error("인덱스 오픈을 실패했습니다. 원인 : " + e.getMessage());
return false;
}
① 인덱스 오픈은 OpenIndexRequest 를 사용합니다.
인덱스 닫기
try {
// ①
CloseIndexRequest request = new CloseIndexRequest(esProperties.getTestIndexName());
return client.indices().close(request, RequestOptions.DEFAULT).isAcknowledged();
} catch (Exception e) {
log.error("인덱스 닫기를 실패했습니다. 원인 : " + e.getMessage());
return false;
}
① 인덱스 닫기는 CloseIndexRequest 를 사용합니다.
SpringBoot + ElasticSearch 연동 및 간단 API 호출해보기
Elasticsearch Java Rest High Level Client 사용하기[elasticsearch + Spring] elasticsearch를 Java Spring에서 사용해보자 - 환경설정과 Index 만들기