Spring Elastic Search 연동(docker) 1편

1. Docker Elastic Search 설치

docker 설치 방법은! 다른 블로그에서 참고하자

docker 이미지 받아오기

1
docker pull docker.elastic.co/elasticsearch/elasticsearch:7.7.1

7.x 버전에서는 싱글노드 실행시 아래와 같이 실행

1
docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.7.1

위와 같이 실행시 아래와 같이 실행이 되어진다.

1
2
3
4
 {"type": "server", "timestamp": "2020-06-28T10:31:35,754Z", "level": "INFO", "component": "o.e.e.NodeEnvironment", "cluster.name": "docker-cluster", "node.name": "dbd93832ee71", "message": "using [1] data paths, mounts [[/ (overlay)]], net usable_space [52.7gb], net total_space [58.4gb], types [overlay]" }
{"type": "server", "timestamp": "2020-06-28T10:31:35,758Z", "level": "INFO", "component": "o.e.e.NodeEnvironment", "cluster.name": "docker-cluster", "node.name": "dbd93832ee71", "message": "heap size [1gb], compressed ordinary object pointers [true]" }
{"type": "server", "timestamp": "2020-06-28T10:31:35,842Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "docker-cluster", "node.name": "dbd93832ee71", "message": "node name [dbd93832ee71], node ID [sxcEM2FoQKO5w4Coh2uK6A], cluster name [docker-cluster]" }
{"type": "server", "timestamp": "2020-06-28T10:31:35,847Z", "level": "INFO", "component": "o.e.n.Node", "cluster.name": "docker-cluster", "node.name": "dbd93832ee71", "message": "version[7.7.1], pid[6], build[default/docker/ad56dce891c901a492bb1ee393f12dfff473a423/2020-05-28T16:30:01.040088Z], OS[Linux/4.9.125-linuxkit/amd64], JVM[AdoptOpenJDK/OpenJDK 64-Bit Server VM/14.0.1/14.0.1+7]" }

정상 실행 확인 방법

postman 또는 그냥 웹브라우저에서 http://localhost:9200/ 접근시 아래와 같은 응답을 받은 것을 확인.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"name" : "dbd93832ee71",
"cluster_name" : "docker-cluster",
"cluster_uuid" : "AxvSUvrpRk6lrJSEG5fe5w",
"version" : {
"number" : "7.7.1",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "ad56dce891c901a492bb1ee393f12dfff473a423",
"build_date" : "2020-05-28T16:30:01.040088Z",
"build_snapshot" : false,
"lucene_version" : "8.5.1",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}

2. spirng과 연동 해보자

spring boot version : 2.3.1

연동시에 여러가집 방법이 있지만 해당 블로그에선 HighLevelRestClient를 쓸 예정

비교 정리내용 : https://kok202.tistory.com/102 블로그

rest high level client 부터는 tcp connection 이 아닌 http connection 을 맺는다.

github sample code

클릭 -> Spring ES sample code

아래처럼 high-level-client 디펜던시 추가

1
2
3

compile 'org.elasticsearch:elasticsearch:7.7.0'
compile 'org.elasticsearch.client:elasticsearch-rest-high-level-client:7.7.0'

application.yml

1
2
3
4
5
6
server:
port: 9000
spring:
elasticsearch:
rest:
uris: http://localhost:9200

기존에는 아래의 코드 처럼 esconfig를 직접 설정해주어야 했는데 spring boot 2.2 인지 2.3에서 편하게 yml로 config를 설정할수 있게 도와주는 것 같다. 정확하게 어느 버전인지는 찾아보지 않았음.

1
2
3
4
5
6
7
8
9
10

@Configuration
public class ElasticsearchConfig {

@Bean(destroyMethod = "close")
public RestHighLevelClient elasticsearchClient(ElasticsearchProperties properties) {

return new RestHighLevelClient(RestClient.builder(properties.hosts()));
}
}

UserController.java

인덱스 생성과 생성된 index를 가져오는 테스트만을 진행할 것이다.

물론 query build를 통한 search도 가능하나 간단하게 연동을 위한 블로그임을!!! 미리 말씀드린다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@RestController
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@PostMapping("/users")
@ResponseStatus(HttpStatus.CREATED)
public String createUserProfile(@RequestBody UserProfileDto profile) throws IOException {

return userService.createUserIndex(profile.toBeanbrokerUser());
}

@GetMapping("/users/{userKey}")
@ResponseStatus(HttpStatus.CREATED)
public GetResponse getUserProfile(@PathVariable("userKey") String userKey) throws IOException {

return userService.getUserIndex(userKey);
}
}

UserService.java

테스트를 위해 사용하려는 IndexRequest, GetRequest가 Deprecated되었다.. 일단 잘 작동하나 왜 Deprecated되었는지는 알수 없다.

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
31
32
33
34
35
36
37
38
39
40
41
@Service
@Slf4j
public class UserService {

@Autowired private RestHighLevelClient restHighLevelClient;

@Autowired private ObjectMapper objectMapper;

private static final String USER_DOC_TYPE = "_doc";
private static final String USER_INDEX = "user";

public String createUserIndex(BeanbrokerUser user) throws IOException {

// set unique index on User
String uniqueId = UUID.randomUUID().toString();
user.setUniqueId(uniqueId);

Map<String, Object> userDocument = objectMapper.convertValue(user, Map.class);

IndexRequest indexRequest = new IndexRequest(USER_INDEX, USER_DOC_TYPE, uniqueId);
indexRequest.source(userDocument, XContentType.JSON);

IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);

if ("CREATED".equals(indexResponse.getResult().name())) {
return uniqueId;

} else {
return indexResponse.getResult().name();
}
}

public GetResponse getUserIndex(String userKey) throws IOException {

GetRequest getRequest = new GetRequest(USER_INDEX, USER_DOC_TYPE, userKey);

GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);

return getResponse;
}
}

테스트 해보자

1
2
3
4
5
6
7
8
9
10
curl --location --request POST 'http://localhost:9000/users' \
--header 'Content-Type: application/json' \
--header 'Content-Type: text/plain' \
--header 'Cookie: JSESSIONID=CAA019F0C3D0B5129E22A4DC86CDC7B7' \
--data-raw '{

"userId" : "beanbroker",
"name" : "pkj",
"email" : "beanbroker@test.com"
}'

위처럼 유저 생성 curl 후 성공 일경우 ‘f860b9c2-3f45-48ae-95bf-bf43d58ec6bb’ 와 같은 id를 받게 된다. 잘생성되었는지 확인해봐야하지 않겠는가?

http://localhost:9200/_search 접근 아래처럼 정상적으로 노출

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
31
32
{
"took":107,
"timed_out":false,
"_shards":{
"total":1,
"successful":1,
"skipped":0,
"failed":0
},
"hits":{
"total":{
"value":1,
"relation":"eq"
},
"max_score":1.0,
"hits":[
{
"_index":"user",
"_type":"_doc",
"_id":"f860b9c2-3f45-48ae-95bf-bf43d58ec6bb",
"_score":1.0,
"_source":{
"uniqueId":"f860b9c2-3f45-48ae-95bf-bf43d58ec6bb",
"userId":"beanbroker",
"name":"pkj",
"email":"beanbroker@test.com",
"createdAt":"2020-07-04T17:43:17.726"
}
}
]
}
}

생성에서 받은 유저의 uniqueid를 users/{value} value에 집어 넣어보자

1
2
3
4
5
6
7
8
9
10
curl --location --request GET 'http://localhost:9000/users/f860b9c2-3f45-48ae-95bf-bf43d58ec6bb' \
--header 'Content-Type: application/json' \
--header 'Content-Type: text/plain' \
--header 'Cookie: JSESSIONID=CAA019F0C3D0B5129E22A4DC86CDC7B7' \
--data-raw '{

"userId" : "beanbroker",
"name" : "pkj",
"email" : "beanbroker@test.com"
}'

아래 처럼 결과가 나온다.

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
31
32
33
34
{
"fields": {},
"id": "f860b9c2-3f45-48ae-95bf-bf43d58ec6bb",
"type": "_doc",
"version": 1,
"seqNo": 0,
"exists": true,
"primaryTerm": 1,
"sourceEmpty": false,
"sourceAsBytes": "eyJ1bmlxdWVJZCI6ImY4NjBiOWMyLTNmNDUtNDhhZS05NWJmLWJmNDNkNThlYzZiYiIsInVzZXJJZCI6ImJlYW5icm9rZXIiLCJuYW1lIjoicGtqIiwiZW1haWwiOiJiZWFuYnJva2VyQHRlc3QuY29tIiwiY3JlYXRlZEF0IjoiMjAyMC0wNy0wNFQxNzo0MzoxNy43MjYifQ==",
"sourceInternal": {
"fragment": true
},
"sourceAsBytesRef": {
"fragment": true
},
"sourceAsString": "{\"uniqueId\":\"f860b9c2-3f45-48ae-95bf-bf43d58ec6bb\",\"userId\":\"beanbroker\",\"name\":\"pkj\",\"email\":\"beanbroker@test.com\",\"createdAt\":\"2020-07-04T17:43:17.726\"}",
"sourceAsMap": {
"createdAt": "2020-07-04T17:43:17.726",
"name": "pkj",
"userId": "beanbroker",
"uniqueId": "f860b9c2-3f45-48ae-95bf-bf43d58ec6bb",
"email": "beanbroker@test.com"
},
"index": "user",
"source": {
"createdAt": "2020-07-04T17:43:17.726",
"name": "pkj",
"userId": "beanbroker",
"uniqueId": "f860b9c2-3f45-48ae-95bf-bf43d58ec6bb",
"email": "beanbroker@test.com"
},
"fragment": false
}

http://localhost:9200/user/_search 이렇게하면 user index에 대한 search가 된다.

물론 실제로 서비스로 사용하기 위해선 강력하게 kibana가 셋팅되어야 함을 공유드리며 끝!!! 좀더 자세히 적고 설명도 붙이고 싶으나 약속이 있어서 후려쳐서.. 올려본다……