31
In-Memory Data Grid 기반 대용량 처리 Architecture Prototype

코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

Embed Size (px)

DESCRIPTION

http://www.opennaru.com/ http://opennaru.tistory.com 데이터 그리드를 기반으로 대용량 트랜잭션을 처리할 수 있는 아키텍처 프로토타입을 소개합니다. 아키텍처의 주요 특징은 비동기 메세징을 이용하여 신속하게 데이터를 입력받고, 데이터그리드를 이용하여 데이터베이스의 부하를 낮추는 것입니다. 주요 적용 오픈소스 Wildfly , JBoss Data Grid, MariaDB 이고, 적용 기술들은 WebSocket, Rest API, JMS , EDB (Event Driven Bean), Spring, MyBatis 등 입니다. 주요해결 과제는 다음과 같습니다. * 대용량 데이터처리를 위한 빠른 프로세싱을 위하여 비동기 메세지를 사용 * 장애시에도 메세지 전달 보장 ( Reliable Message Delivery ) * 데이터베이스의 부하인한 전체 시스템 성능 저하 방지를 위한 인메모리 데이터 그리드 적용 * 고성능 양방향 네트워크 인터페이스를 위한 WebSocket 사용 오픈나루 블로그 - http://opennaru.tistory.com/ 오픈나루 홈페이지 - http://www.opennaru.com/

Citation preview

Page 1: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

In-Memory Data Grid 기반 대용량 처리

Architecture Prototype

Page 2: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 1

인메모리 데이터그리드기반의 대용량 처리 아키텍처

• WebSocket을 이용한 대용량 데이터 입력 (Async)

• Restful API 를 이용한 데이터 조회 및 조작 (Sync)

• JBoss Data Grid 를 이용한 데이터베이스 Cache

MariaDB Database

WebSocket Server

@ServerEndpoint

Java Messaging Service

demoQueue

JAX RS

@Path @Controller

Message Driven Bean

@Service

MyBatis

@Repository @Cachable

Message Driven Bean Message Driven POJO(Bean)

@MessageDriven

JBoss

Data Grid

JBoss Enterprise Application Platform

Page 3: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 2

시스템 구현 방안

• 데이터 젂송은 WebSocket 프로토콜을 사용

• 테스트 용도로 RESTful API 구현(RESTEasy 사용)

• 데이터 입력과 처리를 분리하기 위하여 Message Queue를 사용

• 데이터 처리는 MDB(Message Driven Bean)을 사용하여 동시에 처리할 수 있도록 함

• JBoss Data Grid의 캐시를 사용하여 SQL 쿼리 수를 최소화

• 간단한 테이블 구조를 사용(User 정보)

• 코드 테이블은 On-Demand 방식으로 JBoss Data Grid 캐시에 적재

Page 4: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 3

시스템 상세 아키텍처

MariaDB Database

WebSocket Server

@ServerEndpoint

Java Messaging Service

demoQueue

JAX RS

@Path @Controller

Message Driven Bean

@Service

MyBatis

@Repository @Cachable

Message Driven Bean Message Driven POJO(Bean)

@MessageDriven

JBoss

Data Grid

JBoss Enterprise Application Platform

Page 5: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 4

MariaDB 테이블 정보

Page 6: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 5

메시지 Flow(POST)

JBoss

Data Grid DB에 저장

Enrich

Page 7: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 6

Header text

Page 8: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 7

WebSocket

WebSocket Server

@ServerEndpoint

Java Messaging Service

demoQueue

JAX RS

@Path @Controller

Message Driven Bean

@Service

MyBatis

@Repository @Cachable

Message Driven Bean Message Driven POJO(Bean)

@MessageDriven

JBoss

Data Grid

JBoss Enterprise Application Platform

• JSR 356 표준 API

• Full duplex bi-directional 통싞

• Undertow ( Wildfly )

@ServerEndpoint("/websocket") public class WebSocketServer {

public static ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<String, Session>(); @OnMessage public String onMessage(String message, Session session) {

System.out.println("-------------- WebSocket ------------"); System.out.println(">>>>> Received : "+ message); MessageSender.sendMessage(session.getId(), message); System.out.println("-------------------------------------"); return null;

} }

Page 9: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 8

JMS Queue

WebSocket Server

@ServerEndpoint

Java Messaging Service

demoQueue

JAX RS

@Path @Controller

Message Driven Bean

@Service

MyBatis

@Repository @Cachable

Message Driven Bean Message Driven POJO(Bean)

@MessageDriven

JBoss

Data Grid

JBoss Enterprise Application Platform

• Java Messaging Service

• 메시지 젂달 보장

• 비동기 통싞을 위한 서비스

• HornetQ 구현체 사용(JBoss 포함)

<jms-destinations> <jms-queue name="demoQueue">

<entry name="java:jboss/queue/demoQueue"/> <durable>true</durable>

</jms-queue> </jms-destinations>

Page 10: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 9

Message Driven POJO(Bean)

WebSocket Server

@ServerEndpoint

Java Messaging Service

demoQueue

JAX RS

@Path @Controller

Message Driven Bean

@Service

MyBatis

@Repository @Cachable

Message Driven Bean Message Driven POJO(Bean)

@MessageDriven

JBoss

Data Grid

JBoss Enterprise Application Platform

• Listener 사용 비동기 처리

• JMS Queue의 메시지를 받아 처리

• 멀티 스레드로 동시 처리

@Component public class MessageExtractor implements MessageListener {

@Autowired private CustomerService customerService; private ObjectMapper mapper; public void onMessage(Message message) {

try { String msg = ((TextMessage) message).getText(); Command command = mapper.readValue(msg, Command.class); System.out.println("================ onMessage =============="); System.out.println(">>>>> command=" + command); ...

} catch (Exception e ) { }

Page 11: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 10

JAX-RS – RESTful API

WebSocket Server

@ServerEndpoint

Java Messaging Service

demoQueue

JAX RS

@Path @Controller

Message Driven Bean

@Service

MyBatis

@Repository @Cachable

Message Driven Bean Message Driven POJO(Bean)

@MessageDriven

JBoss

Data Grid

JBoss Enterprise Application Platform

• JAX-RS JSR-311 표준 API

• EJB, Spring Integration

• RESTEasy 사용(JBoss 포함)

@Controller @Path("/api") public class CustomerController {

@GET @Produces({MediaType.APPLICATION_JSON + ";charset=UTF-8"}) @Path("/customer/{userid}") public Customer getCustomerById(@PathParam("userid") String userid) throws Exception { return customerService.getCustomerById(userid); } @POST @Produces({MediaType.APPLICATION_JSON + ";charset=UTF-8"}) @Path("/customer/{userid}") public Customer updateCustomer(@PathParam("userid") String userid, Customer customer) throws Exception {

customer.setUserid(userid); return customerService.updateCustomer(customer);

} @DELETE @Produces({MediaType.APPLICATION_JSON + ";charset=UTF-8"}) @Path("/customer/{userid}") public Customer deleteCustomer(@PathParam("userid") String userid, Customer customer) throws Exception {

customer.setUserid(userid); return customerService.deleteCustomer(customer);

Page 12: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 11

@Service

WebSocket Server

@ServerEndpoint

Java Messaging Service

demoQueue

JAX RS

@Path @Controller

Message Driven Bean

@Service

MyBatis

@Repository @Cachable

Message Driven Bean Message Driven POJO(Bean)

@MessageDriven

JBoss

Data Grid

JBoss Enterprise Application Platform

• Spring MVC Framework

• Spring 컴포넌트

@Service public class CustomerService {

private Logger logger = LoggerFactory.getLogger(CustomerService.class); @Autowired CustomerDao customerDao; public Customer getCustomerById(String userid) {

return customerDao.getCustomerById(userid); }

}

Page 13: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 12

@Repository, @Cacheable

WebSocket Server

@ServerEndpoint

Java Messaging Service

demoQueue

JAX RS

@Path @Controller

Message Driven Bean

@Service

MyBatis

@Repository @Cachable

Message Driven Bean Message Driven POJO(Bean)

@MessageDriven

JBoss

Data Grid

JBoss Enterprise Application Platform

• MyBatis를 사용하여 구현

• Infinispan Spring 연동 모듈 사용

@Repository public class CustomerDaoImpl extends AbstractDao implements CustomerDao {

@CachePut(value="customerCache", key="#customer.userid") public Customer insertCustomer(Customer customer) {

int ret = getSqlSession().insert("customer.insertCustomer", customer); return customer;

} @CacheEvict(value="customerCache", key="#customer.userid") public Customer deleteCustomer(Customer customer) {

int ret = getSqlSession().delete("customer.deleteCustomer", customer); return customer;

} @CachePut(value="customerCache", key="#customer.userid") public Customer updateCustomer(Customer customer) {

int ret = getSqlSession().update("customer.updateCustomer", customer); return customer;

} }

Page 14: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 13

JBoss Data Grid(Library Mode)

WebSocket Server

@ServerEndpoint

Java Messaging Service

demoQueue

JAX RS

@Path @Controller

Message Driven Bean

@Service

MyBatis

@Repository @Cachable

Message Driven Bean Message Driven POJO(Bean)

@MessageDriven

JBoss

Data Grid

JBoss Enterprise Application Platform

• JSR 107, 347 표준 API 지원

• JBoss Data Grid(Infinispan) 사용

• Spring Framework 연동 모듈 제공

<namedCache name="customerCache"> <jmxStatistics enabled="true"/> <unsafe unreliableReturnValues="false"/> <clustering mode="distribution">

<stateTransfer awaitInitialTransfer="false" chunkSize="10000" timeout="240000"/> <sync replTimeout="200000"/> <hash numOwners="2" numSegments="80"/> <l1 enabled="true" lifespan="600000"/>

</clustering> </namedCache> <namedCache name="zipcodeCache">

<jmxStatistics enabled="true"/> <unsafe unreliableReturnValues="false"/> <clustering mode="distribution">

<stateTransfer awaitInitialTransfer="false" chunkSize="10000" timeout="240000"/> <sync replTimeout="200000"/> <hash numOwners="2" numSegments="80"/> <l1 enabled="true" lifespan="600000"/>

</clustering> </namedCache>

Page 15: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 14

데모 시스템 구성

test11 JBoss(Wildfly) Instance

test12 JBoss(Wildfly) Instance

Java SE WebSocket Client

Page 16: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 15

장애 복구 후 처리 속도 문제

• JMS(HornetQ)는 처리 속도를 향상하기 위한 버퍼링 기능 제공함

• Consumer에 메시지를 미리 가져다 놓아 다음 메시지 처리 속도를 향상 시킴

• consumer-window-size는 1 MB 기본값

• 속도가 느린 Consumer이 경우 버퍼링 되기 때문에 오히려 속도가 느려짐

• 속도가 느린 Consumer에서는 consumer-window-size를 0 으로 설정

Message Queue

Consumer

메시지 처리

메시지 처리

메시지 처리

Page 17: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 16

Header text

Page 18: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 17

In memory storage engines

Distributed across a cluster providing “networked memory”

Pacemakers to databases

Provide simple key,value storage

Linear scalability and elasticity due to distributed a

lgorithms

What is a Data Grid

Page 19: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 18

Cache 적용 방안과 이슈

B C

D

지연시간

Read 지연시간

A B C

D

Application

A

지연시간

Read

WAS

A

Application Cache

A

B C

D A

Read

DataGrid #1

DataGrid #2

DataGrid #3

DataGrid #4

B C

D

A

A

B

C

D WAS

Application

A

C

D

B

A

지연시간

Read

WAS #1

Application Cache

B C

D

A’

A’ B

C

D A

WAS #2

Application Cache

A B

C

D A

Step1 : UPDATE … SET ...’A” …

Step2 : put() 캐쉬 불일치 (Incoherent)

DB와 불일치 (Inconsistency)

No Cache Local Cache

Cache Consistency Issue Distributed Cache

Page 20: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 19

가상화 기반 환경에서 Scale out 고려 사항

DataGrid #1 DataGrid #2 DataGrid #3 DataGrid #N

Virtual Machine

WAS #1

Application

Virtual Machine

WAS #2

Application

Virtual Machine

WAS #3

Application

Virtual Machine

WAS #1

Application

Virtual Machine

WAS #2

Application

Virtual Machine

WAS #3

Application

Virtual Machine

WAS #N

Application

Page 21: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 20

Page 22: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 21

Eviction (제거)

• Eviction는 메모리가 부족해지지 않도록 메모리에서 엔트리를 삭제하는 과정

• 데이터 손실을 막기 위해서는 CacheStore 에 저장

• Eviction은 젂체 클러스터를 대상으로 하지 않고 개별 노드를 대상으로 작업

• Eviction 젂략

• NONE - 설정되어있는 Eviction 젂략 없음

• FIFO - first-in-first-out (선입 선출) 젂략

• LRU - least-recenty-used 젂략

• UNORDERED - 무작위 Eviction 젂략

• LIRS - Low Inter-reference Recency Set 패턴

• Eviction 선언 예

• 최대 1000 개 엔트리, LRU Eviction 젂략, 500 ms 마다 Eviction 쓰레드 실행

<infinispan>

<namedCache name="evictionCache">

<eviction maxEntries="1000“ strategy="LRU” wakeUpInterval="500" />

</namedCache>

</infinispan>

Page 23: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 22

Expiration (맊료) – 1/2

• Eviction와 Expiration 차이

• Eviction - 엔트리 최대수를 넘었을 경우에 삭제

• Expiration - 엔트리 보관 기간을 넘겼을 경우에 삭제

• Expiration는 지정된 시간을 넘긴 엔트리를 삭제

• 엔트리에 lifespan, maximum idle time을 설정하고 초과하는 엔트리는 제거

• Expiration 엔트리는 Eviction 엔트리와 같이 passivate 되지 않음

• Expiration 엔트리는 글로벌하게 삭제

• 메모리, CacheStore, 클러스터 와이드 모두

• Expiration의 예

• HTTP Session

• SFSB Session

Page 24: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 23

Expiration (맊료) – 2/2

• evictionCache 라는 이름의 Cache 선언

• 최대 1,000 개 엔트리맊 메모리에 저장

• Expiration 쓰레드는 500ms 마다 실행

• 엔트리는 생성된 지 60,000 ms 되거나 사용된지 10,000 ms 가 지나면 둘 중 먼저의 경우에 맊료

<infinispan>

<namedCache name="evictionCache">

<eviction maxEntries="1000"

strategy="LRU” />

<expiration

wakeUpInterval="500"

lifespan="60000"

maxIdle="10000” />

<loaders passivation="true">

<loader

class="org.infinispan.loaders.file.FileCacheStore">

<properties>

<property name="location"

value="${java.io.tmpdir}"/>

</properties>

</loader>

</loaders>

</namedCache>

</infinispan>

Page 25: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 24

Header text

Page 26: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 25

Query API

• 풀텍스트 검색 엔진 Apache Lucene의 Lucene Directory 로 구현

• put시에 데이터 index를 생성/저장

• Hibernate Search API를 사용해 검색

Page 27: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 26

Header text

Page 28: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 27

•캐쉬 조작을 JTA 트랜잭션(transaction)상에서 사용할 수 있습니다. • MVCC(Multi-Versioned Concurrency Control) • 캐쉬 엔트리 락 • commit/rollback의 지원 • 동일 트랜잭션(transaction)내에서의 복수의 캐시 인스턴스 조작 • 데드락 검출

try { utx.begin(); Integer value = cache1.get(key); // get에서는 분산 락은 취득하지 않는다 cache1.lock(key) // 명시적인 분산 락 취득[1] value = cache1.get(key); value = value + 1; cache1.put(key, value); // 데이터 변경 cache2.put(key, value); // 암묵적인 분산 락의 취득[2], 데이터의 변경 utx.commit(); // 변경의 반영, 분산 락 개방 } catch (Exception e) { utx.rollback(); // 변경의 취소, 분산 락 개방 }

• Distribution 캐쉬의 경우, commit 시 데이터를 복제 합니다.

[1] get에서는 락을 취득하지 않기 때문에, get→put를 배타적으로 하려면, 명시적으로 락을 취득할 필요가 있습니다. [2] put 조작은 락을 취득하기 때문에, 이 조작에서는 암묵적으로 락 취득을 합니다. 암묵적인 락 취득하게 하려면, 캐쉬

설정을<transaction lockingMode="PESSIMISTIC">과 같이 지정해야 합니다.

JTA 트랜잭션 지원

Page 29: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 28

•배치 API는, put을 여러 차례 배치로 조작할 때 성능을 향상시키기 위해 사용할 수 있는 API 입니다. •배치 API는 JTA 트랜잭션(transaction)의 지원과 거의 동일한 기능이 되지맊, 트랜잭션(transaction)에 참가 할 수 있는 자원이 1개 JDG 캐시 인스턴스로 한정되는 점이 다릅니다.

try { cache.startBatch(); // 배치 트랜잭션 시작 cache.put("k1", "value"); // 분산 락 취득, 데이터의 변경 cache.put("k2", "value"); // 분산 락 취득, 데이터의 변경 cache.put("k3", "value"); // 분산 락 취득, 데이터의 변경 cache.endBatch(true); // 변경의 반영, 분산 락 개방 } catch (Exception e) { cache.endBatch(false); // 변경의 취소, 분산 락 개방 }

배치 API 지원

Page 30: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 29

void putAll(Map<? extends K,? extends V> map, long lifespan, TimeUnit unit)

NotifyingFuture putAllAsync(Map<? extends K,? extends V> data)

putAll

• putAll API는, put을 여러 차례 배치로 조작할 때 성능을 향상시키기 위해 사용할 수 있는 API 입니다. • Collection의 객체를 한꺼번에 Cache에 put하여 배치 Loading시에 유리합니다. •배치 Loading시 put / putAll을 비교하면 성능이 몇 배 이상 빨라집니다.

Page 31: 코드로 살펴보는 데이터 그리드 기반 대용량 처리 아키텍처

opennaru.com | 2013 | All Rights Reserved 30

Header text