Spring boot 에서Elastic Search 를 사용하여 쿼리 결과를 반환해주는 api서버를 개발하게 되었다.
Spring boot 와 Elastic Search 연동하기 위해서는 spring-data-ElasticSearch 사용하면 된다.
연동하기 위한 과정은 다음과 같다.
1.의존성 추가
build.gradle
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
//spring-data-elasticsearch 추가
//..그외 나머지 의존성들
}
2.application.yaml 수정
spring:
elasticsearch:
cluster:
initial-master-nodes: (clusternode name입력)
uris: http://(ip주소 입력 ex>localhost):9200
위의 clusternode name의 경우 elasticsearch.yml파일에 있다.
필자의 ElasticSearch는 linux에 따로 설치 되어 있기 때문에 리눅스를 기준으로 설명한다
/etc/elasticsearch/elasticsearch.yml 에 위치해있다.
이후 파일을 열면
마지막 아래의 cluster.initial_mater_nodes를 써주면 된다.(물론 수정 가능하다)
해당 elasticsearch가 설치된 리눅스의 경우 aws의 ec2를 사용하기에 위와 같이 나왔다.
3.elasticsearch의 쿼리 결과를 받아오기 위한 Entity설계
elasticsearch 의 결과인 json을 Spring 에서 받아오기 위한 entity설계가 필요하다.
필자는 이미 elasticsearch에 저장되어 있는 index에서 쿼리를 spring을 통해 던져 결과를 받아오기가 목표이기 때문에
elasticsearch의 devtool을 이용해 결과를 먼저 확인한 후 그에 맞는 객체를 생성했다.
GET metricbeat-*/_search
{
"_source": ["@timestamp", "system.cpu.total.pct","host.hostname"],
"query": {
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "2023-08-01T18:30:56",
"lte": "2023-08-01T18:45:57"
}
}
},
{
"term": {
"event.module": "system"
}
},
{
"term": {
"event.dataset": "system.cpu"
}
}
]
}
}
}
해당 실제 쿼리를 돌려 나온 결과는 다음과 같다.
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 0,
"hits": [
{
"_index": ".ds-metricbeat-8.8.2-2023.07.11-000001",
"_id": "IbVesokBHmNXj9gDF_II",
"_score": 0,
"_source": {
"@timestamp": "2023-08-01T18:30:57.034Z",
"system": {
"cpu": {
"total": {
"pct": 0.0161
}
}
},
"host": {
"hostname": "ip-172-31-12-240"
}
}
},
{
"_index": ".ds-metricbeat-8.8.2-2023.07.11-000001",
"_id": "LLVesokBHmNXj9gDGfKn",
"_score": 0,
"_source": {
"@timestamp": "2023-08-01T18:30:57.722Z",
"host": {
"hostname": "ip-172-31-15-63"
},
"system": {
"cpu": {
"total": {
"pct": 0.01
}
}
}
}
}
]
}
여기서 필자는 timestamp, system.cpu.total.pct, host.hotname 을 뽑아낼것이다.
해당 필드들에 대한 엔티티 설계는 다음과 같다.
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Getter@Setter
@Document(indexName = ".ds-metricbeat-8.8.2-2023.07.11-000001")
public class MetricEntity {
@Id
private String id;
@Field(name = "@timestamp", type = FieldType.Date)
private String timestamp;
@Field(type = FieldType.Nested)
private SystemField system;
@Field(type = FieldType.Object)
private HostField host;
// getters and setters
}
@Getter@Setter
class SystemField {
@Field(type = FieldType.Nested)
private CpuField cpu;
private MemoryField memory;
private NetworkField network;
// getters and setters
}
@Getter@Setter
class CpuField {
@Field(type = FieldType.Object)
private TotalField total;
// getters and setters
}
@Getter@Setter
class MemoryField {
@Field(type = FieldType.Object)
private ActualField actual;
// getters and setters
}
@Getter@Setter
class NetworkField {
@Field(type = FieldType.Object)
private InField in;
@Field(type = FieldType.Object)
private OutField out;
// getters and setters
}
@Getter@Setter
class ActualField {
@Field(type = FieldType.Object)
private UsedField used;
// getters and setters
}
@Getter@Setter
class UsedField {
@Field(name = "pct", type = FieldType.Float)
private float pct;
// getters and setters
}
@Getter@Setter
class TotalField {
@Field(name = "pct", type = FieldType.Float)
private float pct;
// getters and setters
}
@Getter@Setter
class InField {
@Field(name = "bytes", type = FieldType.Long)
private Long bytes ;
// getters and setters
}
@Getter@Setter
class OutField {
@Field(name = "bytes", type = FieldType.Long)
private Long bytes ;
// getters and setters
}
@Getter@Setter
class HostField {
@Field(name = "hostname", type = FieldType.Keyword)
private String hostname;
// getters and setters
}
@Document 에너테이션에 indexName은 해당 elasticsearch의 index명을 지정해야한다. 쿼리 결과의 Hits [ ]필드내의 index에서 확인할 수 있다.
참고로 @Getter@Setter 에너테이션은 롬복을 활용하여 필수로 작성하여야한다.
또한 @Field의 경우 type명을 정확히 해당 필드의 타입과 일치하여야지 mapping시 문제가 생기지 않는다.
이렇게 엔티티를 설계한 후에는 elasticsearch와 spring을 연계시켜 쿼리를 작성할 차례이다.
4.쿼리 설계 및 controller 설계
해당코드는 /getMetricCPUdata를 통해 접속시 해당 쿼리 결과가 나오도록 했다.
import dev.project.hanium.Entity.MetricEntity;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
@RestController
public class MetricApi {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
@GetMapping("/getMetricCPUdata")
public ResponseEntity<Object> getMetricCPUData() {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.boolQuery()
.filter(QueryBuilders.rangeQuery("@timestamp")
.gte("2023-08-01T18:30:35")
.lte("2023-08-01T18:30:44"))
.filter(QueryBuilders.termQuery("event.module", "system"))
.filter(QueryBuilders.termQuery("event.dataset", "system.cpu")));
SearchHits<MetricEntity> searchHits = elasticsearchRestTemplate.search(
new NativeSearchQueryBuilder()
.withSourceFilter(new FetchSourceFilterBuilder().withIncludes("@timestamp", "system.cpu.total.pct", "system.memory.actual.used.pct", "host.hostname").build())
.withQuery(searchSourceBuilder.query())
.build(),
MetricEntity.class
);
List<MetricEntity> metricbeatDataList = searchHits.stream()
.map(SearchHit::getContent)
.collect(Collectors.toList());
return ResponseEntity.ok(metricbeatDataList);
}
}
5.결과 확인
해당 spring-data-elasticsearch의 document를 번역한 글이 있어 링크를 첨부하겠다.
https://velog.io/@hanblueblue/%EB%B2%88%EC%97%AD-spring-data-elasticsearch
'💻 > spring' 카테고리의 다른 글
[Spring Boot] spring initializr 사용하여 프로젝트 만들기 (0) | 2023.08.01 |
---|