Jihoon's IT Development

Web Developer's Hobby Development Notes

Spring Boot와 함께하는 Pivotal Gemfire 간단예제(2)

목차

  1. Pivotal Gemfire 다운로드 및 설치, 기본 설정
  2. Spring Boot를 이용한 Gemfire 이용

지난시간에 이어서 간단한 테스트용 Region 생성과 Data 를 Region 안에 넣고 빼는 샘플 프로젝트를 제작해보자.

완성된 소스는 여기서 확인 가능하다.

https://github.com/Park-jihoon/gemfire-demo

Region 생성

우선 gfsh 를 이용해 region 을 생성하자. region은 일반적인 DataBase로 치면 Table과 비슷한 역할을 하는 것으로 생성 시 많은 옵션을 줄 수 있다.

생성시 옵션은 대략 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
create region --name=value [--type=value] [--template-region=value]
[--groups=value(,value)*] [--if-not-exists(=value)?]
[--key-constraint=value] [--value-constraint=value]
[--enable-statistics=value] [--entry-idle-time-expiration=value]
[--entry-idle-time-expiration-action=value]
[--entry-time-to-live-expiration=value]
[--entry-time-to-live-expiration-action=value]
[--entry-idle-time-custom-expiry=value] [--entry-time-to-live-custom-expiry=value]
[--region-idle-time-expiration=value]
[--region-idle-time-expiration-action=value]
[--region-time-to-live-expiration=value]
[--region-time-to-live-expiration-action=value] [--disk-store=value]
[--enable-synchronous-disk=value] [--enable-async-conflation=value]
[--enable-subscription-conflation=value] [--cache-listener=value(,value)*]
[--cache-loader=value] [--cache-writer=value]
[--async-event-queue-id=value(,value)*]
[--gateway-sender-id=value(,value)*] [--enable-concurrency-checks=value]
[--enable-cloning=value] [--concurrency-level=value]
[--colocated-with=value] [--local-max-memory=value]
[--recovery-delay=value] [--redundant-copies=value]
[--startup-recovery-delay=value] [--total-max-memory=value]
[--total-num-buckets=value] [--compressor=value] [--off-heap(=value)]
[--partition-resolver=value] [--eviction-entry-count=value]
[--eviction-max-memory=value] [--eviction-action=value] [--eviction-object-sizer=value]

우선 region 생성 시 필수값인 nametype만 살펴보자.

  • name
    • region을 해당 값의 이름으로 생성한다.
    • DB의 tableName과 유사
  • type
    • PARTITION, PARTITION_REDUNDANT, REPLICATE, LOCAL, etc.
    • region type 에 따라 많은 타입이 존재하며 원하는 형태의 저장타입을 지정하면 된다.

이번 샘플에서는 아래의 두가지 region만 생성해본다.

1
2
3
4
5
6
7
8
9
10
11
12
gfsh>create region --name=testCache --type=REPLICATE
Member | Status | Message
------- | ------ | ----------------------------------------
server1 | OK | Region "/testCache" created on "server1"

Cluster configuration for group 'cluster' is updated.
gfsh>create region --name=customer --type=REPLICATE
Member | Status | Message
------- | ------ | ---------------------------------------
server1 | OK | Region "/customer" created on "server1"

Cluster configuration for group 'cluster' is updated.

Spring Boot Sample 생성

  • 환경
    • java 1.8 이상
    • Maven
    • Spring boot 2.1.1.RELEASE
    • Spring geode starter 1.2.1.RELEASE
    • SpringFox Swagger 2.9.2

Directory 구조

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Gemfire-demo
|-- pom.xml
`-- src
`-- main
`-- java
`-- ko.co.pohinian.gemfiredemo
|-- GemfireDemoApplication.java
|-- configuration
| |-- GemfireConfiguration.java
| `-- SwaggerConfiguration.java
|-- controller
| |-- CustomerController.java
| `-- TestCacheController.java
|-- entity
| |-- Customer.java
| `-- TestCache.java
|-- repository
| `-- CustomerRepository.java
`-- service
`-- TestCacheService.java

파일 설명

pom.xml

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- gemfire -->
<dependency>
<groupId>org.springframework.geode</groupId>
<artifactId>spring-geode-starter</artifactId>
<version>1.2.1.RELEASE</version>
</dependency>

<!-- swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!-- swagger2 -->

<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- test -->
</dependencies>

geodeswagger2 관련 라이브러리는 버전을 명시해야 한다.

GemfireDemoApplication.java
실행을 위한 클래스를 생성한다.

1
2
3
4
5
6
@SpringBootApplication
public class GemfireDemoApplication {
public static void main(String[] args) {
SpringApplication.run(GemfireDemoApplication.class, args);
}
}

GemfireConfiguration.java

gemfire를 사용하기 위한 설정을 모아두는 클래스 입니다.

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
@Configuration
@EnableGemfireCaching
public class GemfireConfiguration {

@Bean
public ClientCache gemfireCache() {
ClientCacheFactory cacheFactory = new ClientCacheFactory();
cacheFactory.addPoolLocator("192.168.0.6", 10334);
return cacheFactory.create();
}

@Bean("testCache")
public ClientRegionFactoryBean<String, TestCache> testCache(@Autowired ClientCache clientCache) {
ClientRegionFactoryBean<String, TestCache> regionFactoryBean = new ClientRegionFactoryBean<>();
regionFactoryBean.setCache(clientCache);
regionFactoryBean.setClose(false);
regionFactoryBean.setShortcut(ClientRegionShortcut.PROXY);
return regionFactoryBean;
}
@Bean("customer")
public ClientRegionFactoryBean<Long, Customer> customer(@Autowired ClientCache clientCache) {
ClientRegionFactoryBean<Long, Customer> regionFactoryBean = new ClientRegionFactoryBean<>();
regionFactoryBean.setCache(clientCache);
regionFactoryBean.setClose(false);
regionFactoryBean.setShortcut(ClientRegionShortcut.PROXY);
return regionFactoryBean;
}
}
  • @EnableGemfireCaching 으로 gemfire cache를 사용할 것임을 명시한다.
  • gemfireCache()에서 연결하기위한 Gemfire 서버의 정보를 설정한다.
    • 192.168.0.6은 이전 글에서 시작한 locator 정보의 ip이다.
    • 기본 local 접속이기때문에 별도의 username 과 password 는 설정하지 않는다. 하지만 별도의 서버를 구성할 경우에는 서버의 gemfire username과 password를 추가해야 한다.
  • customertestCache는 동명의 region에 대한 정보를 입력한다.
  • ClientRegionShortcut.PROXY는 애플리케이션에서 저장되는 정보를 어떠한 방식으로 처리할지를 지정하는 것으로 기본값음 LOCAL이다. LOCAL인 경우 값을 애플리케이션에서만 가지고 있게 되며 서버로 전송하지 않는다.
    • 별도의 서버를 두고 데이터를 공유할 경우에는 PROXY로 지정해줘야 한다.

Customer 저장 및 로드

Customer 는 org.springframework.data.repository.CrudRepository를 사용하여 데이터를 저장 및 삭제하는 예제다.

Customer.java
region 정보를 명시해주는 클래스다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Data
@ToString
@Region("customer")
@NoArgsConstructor
@RequiredArgsConstructor(staticName = "newCustomer")
public class Customer implements Serializable {
@Id
@NonNull
@Getter
private Long id;
@NonNull
@Getter
private String name;

}

Serializable은 java 끼리만 gemfire의 데이터를 공유할 경우에 사용하면 간편하다.
다만 다른 언어들과 데이터를 공유할 경우에는 DataSerializable 등을 구현하여줘야 한다.

CustomerRepository.java

1
2
public interface CustomerRepository extends CrudRepository<Customer, Long> {
}

Customer의 ID 타입인 Long 을 명시해준다.
gemfire 이외의 기술을 선택할 경우를 대비해 유연하도록 CrudRepository를 구현해준다.
findByName 등 jpa 에서 사용하던 형태의 메서드를 선언해 사용 가능하다.

CustomerController.java

Customer 의 정보를 저장하기 위한 Rest Controller 이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RestController
@RequestMapping("customer")
public class CustomerController {
private final CustomerRepository customerRepository;

public CustomerController(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}

@PostMapping("{key}")
public Customer put(@PathVariable("key") Long key, @RequestBody String name) {
Customer customer = Customer.newCustomer(key, name);
return customerRepository.save(customer);
}

@GetMapping("{key}")
public Customer get(@PathVariable("key") Long key) {
return customerRepository.findById(key).orElse(new Customer());
}

}

@RestController는 RestApi로 사용하기 위해 선언한다.
CustomerRepository의 Save 및 findById를 사용한다.

  • save와 findById는 자동으로 생성된다.
    Customer.newCustomer(key, name) 입력된 값으로 저장을 위한 신규 객체를 생성한다.
    orElse(new Customer()) 값이 존재하지 않을 경우 빈 객체를 리턴한다.

Customer 테스트

  • 저장테스트
1
2
3
4
5
> curl -X POST "http://localhost:8080/customer/1" -H "accept: */*" -H "Content-Type: application/json" -d "테스트"
{
"id": 1,
"name": "테스트"
}
  • 저장한 값을 리턴한다.
1
2
3
4
5
> curl -X GET "http://localhost:8080/customer/1" -H "accept: */*"
{
"id": 1,
"name": "테스트"
}
  • Region 정보 확인
1
2
3
4
5
6
7
8
9
10
11
12
gfsh>describe region --name=customer
Name : customer
Data Policy : replicate
Hosting Members : server1

Non-Default Attributes Shared By Hosting Members

Type | Name | Value
------ | ----------- | ---------------
Region | data-policy | REPLICATE
| size | 1
| scope | distributed-ack

TestCache 저장 및 로드

TestCache는 org.springframework.cache를 이용해 최소한의 설정값 만으로 Gemfire를 사용하기 위한 예제이다.

TestCache.java

TestCache는 일반적인 java entity 파일이다. 별도의 gemfire를 위한 설정은 존재하지 않는다.

1
2
3
4
5
6
7
@Data
@EqualsAndHashCode
@ToString
public class TestCache implements Serializable {
private String name;
private String addr;
}

TestCacheService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class TestCacheService {

@CachePut(cacheNames = "testCache", key = "#key")
public TestCache put(String key, TestCache testCache) {
return testCache;
}

@Cacheable(cacheNames = "testCache", key = "#key")
public TestCache get(String key) {
return null;
}
}

@CachePut, @Cacheable 등 Spring Cache 를 이용해 Gemfire 의 Region에 데이터를 저장 및 로드 가능하다.
반드시 @Service 레벨이어야 한다. 또한, proxy 패턴을 이용하는 것이므로 같은 Class 내부에서 호출하는 부분은 캐싱되지않는다.
cacheNames = "testCache" 으로 region의 이름을 명시한다.
key = "#key"와 같이 매개변수명을 조합해 key를 지정할 수 있다.

TestCacheController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RestController
@RequestMapping("cache")
public class TestCacheController {

private final TestCacheService testCacheService;

public TestCacheController(TestCacheService testCacheService) {
this.testCacheService = testCacheService;
}

@PostMapping("{key}")
public TestCache put(@PathVariable("key") String key, @RequestBody TestCache value) {
return testCacheService.put(key, value);
}

@GetMapping("{key}")
public TestCache get(@PathVariable("key") String key) {
return testCacheService.get(key);
}

}

TestCacheService를 이용해 데이터를 저장 및 로드 한다.

TestCache 테스트

  • 저장
1
2
3
4
5
> curl -X POST "http://localhost:8080/cache/1" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"addr\": \"테스트주소\", \"name\": \"테스트이름\"}"
{
"name": "테스트이름",
"addr": "테스트주소"
}
  • 로드
1
2
3
4
5
curl -X GET "http://localhost:8080/cache/1" -H "accept: */*"
{
"name": "테스트이름",
"addr": "테스트주소"
}
  • Region 정보 확인
1
2
3
4
5
6
7
8
9
10
11
12
gfsh>describe region --name=testCache
Name : testCache
Data Policy : replicate
Hosting Members : server1

Non-Default Attributes Shared By Hosting Members

Type | Name | Value
------ | ----------- | ---------------
Region | data-policy | REPLICATE
| size | 1
| scope | distributed-ack

저장된 것을 확인할 수 있다.

참고