'Infinispan/01. Embedded Guide'에 해당되는 글 14건
- 2015/04/08 용비 14. 결론
- 2015/04/08 용비 13. Step-11. Declarative configuration
- 2015/04/08 용비 12. Step-10. Temperature averages with map/reduce
- 2015/04/08 용비 11. Step-9. A Custom externalizer
- 2015/04/08 용비 10. Step-8. Grouping entries together
지금까지 우리는 programmatic API를 통해서 infinispan을 설정하는 것에 대해서 알아보았다. 그러나 code에서 configuration을 분리하여 구성할 수 있다. 이런 목적으로 우리는 declarative configuration을 사용할 수 있다. Infinispan의 모든 항목들은 external XML file을 사용하여 설정할 수 있다. 무엇보다도 XML format으로 configuration을 변환해 보자.
<?xml version="1.0" encoding="UTF-8"?>
<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:infinispan:config:7.1 http://www.infinispan.org/schemas/infinispan-config-7.1.xsd" xmlns="urn:infinispan:config:7.1">
<cache-container default-cache="default">
<transport cluster="WeatherApp"/>
<distributed-cache name="default" mode="SYNC">
<expiration lifespan="5000"/>
<groups enabled="true">
<grouper class="org.infinispan.tutorial.embedded.LocationWeather$LocationGrouper"/>
</groups>
</distributed-cache>
</cache-container>
</infinispan>
이 파일을 maven의 src/main/resources 폴더에 위치시키면, classpath에서 사용할 수 있다. 그리고 DefaultCacheManager 설정으로 다음과 같이 사용할 수 있다.
cacheManager = new DefaultCacheManager(WeatherApp.class.getResourceAsStream("/weatherapp-infinispan.xml"));
이제 코드를 실행해 보자.
git checkout -f step-11
mvn clean package exec:exec # from terminal 1
mvn exec:exec # from terminal 2
우리가 무엇을 기대했건 간에, 출력 결과는 이전과 같을 것이다.
이제 Infinispan에는 어떤 흥미로운 Data가 저장되어 있다. 우리는 그것을 이용하여 어떤 computation을 수행할 것이다. Every country별로 평균 온도를 계산해보자. 한가지 단순한 접근법은 하나의 node에서 전체 dataset에 대해 반복하여 computation을 수행하는 것이다. 하지만 여기에는 두가지 불리한 면이 있다. 첫번째는 각각의 owner로부터 data fetch가 필요하다는 것이다. (빠른 메모리 대신에 느린 network을 통해서 데이터를 가져와야 함). 두번째는 전체 cluster에 배치된 node가 아닌 하나의 node의 마력만 이용한다는 것이다. 얼마나 낭비인가!
Infinispan은 두가지 문제에 대한 해결책을 제시한다. 분산 처리와 map/reduce에 대한 강력한 개념을 사용하여 모든 node에서 locally computation을 수행할 수 있다. 그리고 작업 수행 결과를 master node에 보내서 작업 수행 결과를 aggregation한다. Average country temperature를 계산하기 위해서 어떻게 map/reduce를 사용할 수 있는지 살펴보자.
첫째로 every entry를 호출하고, computation을 수행하고, 그 결과를 돌려주는 method인 mapper가 필요하다. 우리의 경우에는 "computation"이 각 entry의 country와 temperature를 추출하고 그 정보들을 collector로 보낸다.
public class CountryTemperatureMapper implements Mapper<String, LocationWeather, String, Float> {
@Override
public void map(String key, LocationWeather value, Collector<String, Float> collector) {
collector.emit(value.country, value.temperature);
}
}
위의 코드는 각 entry에 대해서 local에서 실행될 것이다. Mapper 선언의 Generic type은 input key/value와 output key/value의 pair를 나타낸다. 특히, 우리는 location/weather pair를 처리하여 country/temperature pair를 return할 것이다. Collector는 동일한 output key에 속한 모든 값을 aggregate하고, reducer에 그것을 전달할 것이다.
public class CountryTemperatureReducer implements Reducer<String, Float> {
@Override
public Float reduce(String reducedKey, Iterator<Float> i) {
int count = 0;
float sum = 0f;
while(i.hasNext()) {
++count;
sum += i.next();
}
return sum / count;
}
}
Reducer는 각 output key에 대해서 수집된 value를 호출할 것이다. 우리의 경우에는 모든 값을 더해서 ("temperatures") 평균을 내기 위해 count로 나눴다. 끝났다!(that's it!) 한번 실행해 보자.
git checkout -f step-10
mvn clean package exec:exec # from terminal 1
mvn exec:exec # from terminal 2
[Coordinator Output]
---- Average country
temperatures ----
Average temperature in Canada is -11.0° C
Average temperature in USA is 5.8° C
Average temperature in Romania is 7.0° C
Average temperature in UK is 3.0° C
Average temperature in Italy is 5.5° C
Average temperature in Portugal is 13.6° C
Average temperature in Switzerland is -0.1° C
이제 우리는 우리가 제품에서 실행하고 싶은 실제 작업 수행에 유용한 기능을 가진 전체적으로 동작하는 application을 가지게 되었다. 이제 조금 더 간단하게 environment에서 configuration을 변경하는 방법에 대해서 다음 단계에서 알아보자.
우리의 application에서 clustering을 소개할 때, key와 value에 대한 serializable에 대한 필요성에 초점을 맞췄다. 그러나 Infinispan에서는 표준 Java Serialization을 사용하지 않는다. 훨씬 더 높은 performance library인 Jboss Marshalling을 사용한다. 일반적으로 여러분의 entity에 java.io.Serializable을 구현하기 위해서 아무것도 할 필요가 없다. Jboss Marshalling은 여러분을 위해서 모든 것이 고려되어 있다. 그러나 프로젝트의 lifetime 동안 변경되는 entry에 대해서 backward / forward 호환성을 지원하기 위한 예제를 위해 경우에 따라 serialization format에 대해서 모든 제어를 수행하기를 원하는 경우가 있을 수 있다.
이런 경우에는 custom Externalizer를 제공해야 한다. Externalizer는 stream을 통해 여러분의 entity를 어떻게 읽고 쓰는지를 아는 단순한 class이다. 우리의 LocationWeather class를 위해서 간단한 externalizer를 만들어 보자.
public static class LWExternalizer implements Externalizer<LocationWeather> {
@Override
public void writeObject(ObjectOutput output, LocationWeather object) throws IOException {
output.writeFloat(object.temperature);
output.writeUTF(object.conditions);
output.writeUTF(object.country);
}
@Override
public LocationWeather readObject(ObjectInput input) throws IOException, ClassNotFoundException {
float temperature = input.readFloat();
String conditions = input.readUTF();
String country = input.readUTF();
return new LocationWeather(temperature, conditions, country);
}
}
또한 externalizer가 그것을 사용하도록 선언하기 위해서 우리 entity에 특별한 annotation을 추가해야 한다.
@SerializeWith(LocationWeather.LWExternalizer.class)
public class LocationWeather implements Serializable {
우리가 특별히 어떤 것을 한 것은 아니기 때문에 application을 실행해보면 다른 차이점을 볼 수 없다. 그러나, 이제 여러분은 엔진룸 (under the hood)에서 발생한 것에 대한 더 좋은 아이디어를 가지게 되었다.
git checkout -f step-9
mvn clean package exec:exec # from terminal 1
mvn exec:exec # from terminal 2
이제 우리는 좀 더 흥미로운 어떤 것과 씨름할 준비가 되었다. 우리는 단지 데이터를 저장하고 조회하는 것 대신에 data를 가지고 어떤 것을 수행하는데 infinispan을 사용해볼 것이다.
Infinispan에서 cluster내에 entries들을 분산하는 것은 consistent hashing algorithm에 근거하여 이루어진다. 이 algorithm은 entry의 key를 사용하여 hash를 계산하고, 어느 node가 primary owner인지, 나머지 node들이 backup owner로 동작할 것인지 결정하는데 hash를 사용한다. 분산에 대해 제어하는 것은 가능하다. 특히, performance 때문에 grouping algorithm을 사용하여 연관된 entry들을 동일 node상 같은 장소에 배치하는 것은 아주 유용하다. 이것 때문에 우리는 key 기반 group name을 계산할 수 있는 Grouper class를 생성할 필요가 있다. 예제는 다음과 같다.
public static class LocationGrouper implements Grouper<String> {
@Override
public String computeGroup(String key, String group) {
return key.split(",")[1].trim();
}
@Override public Class<String> getKeyType() {
return String.class;
}
}
위의 코드는 아주 간단하다. 단지 key를 comma를 delimiter로 사용하여 분리하고, 두번째 항목 ("country")을 group으로 사용한다. Hashing algorithm은 key 대신에 group을 사용하여 entry의 hash를 계산할 것이다. 특정 grouper를 사용하기 위해서는 configuration에 추가해야 한다.
config.clustering().hash().groups().enabled().addGrouper(new LocationWeather.LocationGrouper());
이제 application을 실행해 보자.
git checkout -f step-8
mvn clean package exec:exec # from terminal 1
mvn exec:exec # from terminal 2
이번 실행에서 각 node별로 output을 조사해보면 동일한 country에 속한 모든 entry에 대한 event log가 "paired"되어 있음을 볼 수 있을 것이다. 내 경우에는 "Romania" group이 두번째 Node에 "hashed"되었다.
[Coordinator Output]
-- Entry for Bucharest, Romania
modified by another node in the cluster
-- Entry for Cluj-Napoca, Romania modified by another node in the cluster
다음 단계에서는 node간에 전송된 entry들을 serialized form으로 어떻게 제어하는지를 살펴볼 것이다.
댓글을 달아 주세요
댓글 RSS 주소 : http://www.yongbi.net/rss/comment/679