import java.util.Arrays;

import java.util.List;

import java.util.Map;

import java.util.stream.Collectors;


import org.apache.commons.lang3.StringUtils;


import org.junit.Test;

import org.springframework.util.Assert;


public class Java8Test {

class Member {

int id;

String name;

public Member(int id, String name) {

this.id = id;

this.name = name;

}

}

@Test

public void test1() {

Member member1 = new Member(1, "samuel");

Member member2 = new Member(2, "keans");

Member member3 = new Member(3, "xy");

List<Member> members =  Arrays.asList(member1, member2, member3);

Map<Boolean, List<Member>> results = members.stream().collect(Collectors.partitioningBy(m -> StringUtils.startsWith(m.name, "x")));

results.forEach((k, v) -> System.out.println("key:" + k + ", value:" + v.stream().map(member -> member.name).collect(Collectors.joining(", "))));

Assert.isTrue(results.get(true).get(0) == member3);

Assert.isTrue(results.get(false).get(0) == member1);

Assert.isTrue(results.get(false).get(1) == member2);

}

}




결과는 다음과 같다.


key:false, value:samuel, keans

key:true, value:xy




단순히  Collectors.partitionBy 대신 Collectors.groupingBy로 사용할 수도 있다. 


import java.util.Arrays;

import java.util.List;

import java.util.Map;

import java.util.stream.Collectors;


import org.apache.commons.lang3.StringUtils;


import org.junit.Test;

import org.springframework.util.Assert;


public class Java8Test {

class Member {

int id;

String name;

public Member(int id, String name) {

this.id = id;

this.name = name;

}

}

@Test

public void test1() {

Member member1 = new Member(1, "samuel");

Member member2 = new Member(2, "keans");

Member member3 = new Member(3, "xy");

List<Member> members =  Arrays.asList(member1, member2, member3);

Map<Boolean, List<Member>> results = members.stream().collect(Collectors.groupingBy(m -> StringUtils.startsWith(m.name, "x")));

results.forEach((k, v) -> System.out.println("key:" + k + ", value:" + v.stream().map(member -> member.name).collect(Collectors.joining(", "))));

Assert.isTrue(results.get(true).get(0) == member3);

Assert.isTrue(results.get(false).get(0) == member1);

Assert.isTrue(results.get(false).get(1) == member2);

}

}




groupingBy메소드에서는 Aggregation 기능도 제공한다.




import java.util.Arrays;

import java.util.List;

import java.util.Map;

import java.util.function.Function;

import java.util.stream.Collectors;


import org.junit.Test;


public class Java8Test {

class Member {

int id;

String name;

public Member(int id, String name) {

this.id = id;

this.name = name;

}

public String toString() {

return id + ", " + name;

}

}

@Test

public void test1() {

Member member1 = new Member(1, "samuel");

Member member2 = new Member(2, "keans");

Member member3 = new Member(3, "xy");

Member member4 = new Member(4, "keans"); // same name, but not same person

List<Member> members =  Arrays.asList(member1, member2, member3, member4);

Map<Member, Long> results = members.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

results.forEach((k, v) -> System.out.println("key:" + k + ", value:" + v));

System.out.println("");

Map<String, Long> otherResults = members.stream().collect(Collectors.groupingBy(m-> m.name, Collectors.counting()));

otherResults.forEach((k, v) -> System.out.println("key:" + k + ", value:" + v));


}

}


결과는 다음과 같다.


key:2, keans, value:1

key:4, keans, value:1

key:1, samuel, value:1

key:3, xy, value:1


key:xy, value:1

key:samuel, value:1

key:keans, value:2






difference 예제이다. map의 요소 중 특정 key를 제외하고 싶을 때 사용할 수 있는 예제이다.

먼저 groupingBy 또는 parittioningBy를 사용하지 않는 예제이다. 



Map<Integer, Object> maps = Maps.newHashMap();

maps.put(1, "samuel");

maps.put(2, "jackson");

maps.put(3, "ice");

maps.put(4, "xxx");

List<Integer> excludeList = Arrays.asList(4);

List<Integer> keys = maps.entrySet().stream().map(k -> k.getKey()).collect(Collectors.toList());


Map<Integer, Object> map = (Map<Integer, Object>) CollectionUtils.subtract(keys, excludeList).stream().collect(Collectors.toMap(p->p, p->maps.get(p)));

System.out.println(map);



결과는 다음과 같다.


{1=samuel, 2=jackson, 3=ice}




CollectionUtils.subtract 대신 Guava의 Collection2.filter를 사용할 수 있다.
Map<Integer, Object> maps = Maps.newHashMap();
maps.put(1, "samuel");
maps.put(2, "jackson");
maps.put(3, "ice");
maps.put(4, "xxx");
List<Integer> excludeList = Arrays.asList(4);
List<Integer> keys = maps.entrySet().stream().map(k -> k.getKey()).collect(Collectors.toList());
Map<Integer, Object> map = Collections2.filter(keys, Predicates.not(Predicates.in(excludeList)))
.stream().collect(Collectors.toMap(p->p, p->maps.get(p)));
System.out.println(map);




partitioningBy를 사용하면 다르게 구현할 수도 있다. 

Map<Integer, Object> maps = Maps.newHashMap();

maps.put(1, "samuel");

maps.put(2, "jackson");

maps.put(3, "ice");

maps.put(4, "xxx");

List<Integer> excludeList = Arrays.asList(4);

Map<Boolean, Map<Object, Object>> map = maps.entrySet().stream().collect(

Collectors.partitioningBy(p -> !excludeList.contains(p.getKey()), Collectors.toMap(p -> p.getKey(), p -> p.getValue()))

);

System.out.println(map);

System.out.println(map.get(true));


결과는 다음과 같다. 



{false={4=xxx}, true={1=samuel, 2=jackson, 3=ice}}

{1=samuel, 2=jackson, 3=ice}


Posted by '김용환'
,

메소스 다운로드



http://www.apache.org/dist/mesos/ 에서 다운받을 바이너리를 확인한다.



sudo yum install -y cyrus-sasl-md5  subversion-devel 

http://www.apache.org/dist/mesos/1.0.3/mesos-1.0.3.tar.gz

tar -zxf mesos-1.0.0.tar.gz

mv mesos-1.0.0 mesos 

cd mesos 

mkdir build 

cd build 

../configure

make 


다운로드 에러가 발생한다면 proxy 이슈이다. 아래와 같이 maven 설정 파일에 proxy를 추가한다.


$ cat ~/.m2/settings.xml

<settings>

    <proxies>

        <proxy>

            <active>true</active>

            <protocol>http</protocol>

            <host>proxy서버</host>

            <port>8888</port>

        </proxy>

    </proxies>

</settings>




제대로 컴파일되었는지 확인한다.


make check 



스탠드얼론 형태로 마스터 서비스를 실행한다. (마스터가 1인 메소스)



cd /home/deploy/mesos/build


sudo ./bin/mesos-master.sh --work_dir=/var/lib/mesos



http://서버-IP:5050






이제 클러스터 모드로 돌려본다.


rsync를 이용해 슬레이브가 될 장비들에 복사를 진행한다. 이미 컴파일된 된 상태이다.


rsync ...




슬레이브 장비에서 yum을 설치하고 mesos를 슬레이브 상태로 실행했다.


 sudo yum install -y cyrus-sasl-md5  subversion-devel  screen

 

 sudo ./bin/mesos-slave.sh --work_dir=/var/lib/mesos --master=마스터_IP:5050




여기서 high availability를 지원하기 위해 zookeeper를 사용하는 것이 좋다.



예제로 두 대의 서버에 zookeeper를 실행하고 다음과 같이 master를 실행했다. 



sudo ./bin/mesos-master.sh --work_dir=/var/lib/mesos  --quorum=1 --zk=zk://zk1:2181,zk2:2181,zk3:2181/mesos --log_dir=/var/log/mesos



잘 동작하고 있다.












참고 : 에러


* Need to specify --quorum for replicated log based registry when using ZooKeeper  : 이 에러를 해결하려면 쿼럼의 값을 조절하는 것이다. --quorum=2 처럼 사용한다.



* F0427 18:21:35.832242 22151 master.cpp:1536] Recovery failed: Failed to recover registrar: Failed to perform fetch within 1mins

*** Check failure stack trace: *** 

메소스를 처음에 실행할 때 에러가 발생했다. 이 문제의 해결은 quorum의 값을 잘못 넣어서 발생했다. quorum의 값을 제대로 넣어보니 더 이상 발생하지 않았다.

(실제 복구 문제라면 이렇게 쉽게 해결되지 않을 것 같다.)




Posted by '김용환'
,



Iterator.continaully 함수 예시이다. 

계속 데이터를 반복하거나 입력을 받으려 할 때 유용할 수 있다. 



scala> Iterator.continually(scala.io.StdIn.readLine).takeWhile(x => x.nonEmpty).foreach(line => println(line))

111

222

(엔터 입력시 종료)
scala>



Stream을 이용해 반복된 수를 출력한다.


scala> val l  = List(1, 2, 3)

l: List[Int] = List(1, 2, 3)


scala> Stream.continually(l.toStream).flatten.take(100).toList

res38: List[Int] = List(1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1)




Posted by '김용환'
,


스칼라와 자바의 람다식 표현 중 주요 차이점을 설명한다. 


스칼라는 block({, }) 지정이 자유로운 편이다.


scala> (1 to 3).map(i => i * 2).foreach(println)

2

4

6


scala> (1 to 3).map(i => {i * 2}).foreach(println)

2

4

6



그러나 자바의 경우는 block 지정이 자유롭지 않다.


Stream.of(1,2,3).map(i -> i * 2).forEach(System.out::println);

2

4

6



Stream.of(1,2,3).map(i -> {i * 2}).forEach(System.out::println);

// 컴파일 에러


Stream.of(1,2,3).map(i -> {i * 2;}).forEach(System.out::println);

// 컴파일 에러



코드를 다음처럼 리턴문을 추가된 상태로 변경해야 한다.


Stream.of(1,2,3).map(i -> {return i*2;}).forEach(System.out::println);

2

4

6


Posted by '김용환'
,

어느 분이 마라톤보다 오로라가 좋다고 해서 자료를 찾아봤다.


오로라는 트위터에서 만들어진 오픈 소스로서 현재는 아파치 재단에 관리되고 있다. 



아파치 오로라의 주요 기능은 다음과 같다.

* 크론(cron) 작업, 오랫동안 실행하는 서비스, 작업 관리를 위한 메소스 프레임워크이다.

* 트위터에서 개발되었고 나중에는 아파치 라이선스를 가진 오픈 소스로 전환되었다.

* 오랜 기간동안 공유 자원 풀에서 오랫동안 실행하는 작업을 유지한다. 한 대의 장비에서 실패하면 다른 장비에서 작업을 다시 예약한다.

* 스케쥴러 자체이기 때문에 특정 스케줄링 요구 사항이 있는 시스템에는 권장되지 않는다.

* 어느 시점이든 특정 작업에 대한 코오스 그레인드(coarse grained) 자원을 제공한다.

* 다중 사용자를 지원한다.

* 설정 중복을 피하기 위해 DSL(Domain Specific Language)을 사용해 설정을 지정한다.



오로라와 마라톤은 유사한 기능들을 제공하며 둘 다 서비스 스케줄러로 분류된다. 둘 사이에는 세 가지 주요 차이점이 있다.


* 오로라는 설치하기 쉽지 않다. 오로라는 쓰리프트(thrift) API를 노출한다. 즉, 프로그램으로 상호 작용할 수 있는 쓰리프트 클라이언트가 필요하다는 것을 의미한다. 반면 마라톤은 최대한 Hello World를 빨리 실행할 수 있도록 도와준다. 많은 환경에서 해당 작업을 수행할 수있는 좋은 문서가 있고 갈 시간이 거의 없다. 그것은 REST API를 가지고 있고 마라톤은 설정을 위해 JSON을 사용한다.


* 오로라는 트위터와 같이 큰 회사에서 사용될 수 있도록 설계되었다. 예를 들어 트위터 클러스터에는 수만 대의 장비와 수백 명의 엔지니어가 있는데 마라톤으로 기능을 빨리 개발했지만 생산성이 떨어진다고 느꼈다.  이에 대한 적절한 예시로 Docker 지원 기능을 들 수 있다. 마라톤은 선점(preemption) 기능을 제공하지 않는다.


* 오로라는 아파치 소프트웨어 재단(ASF, Apache Software Foundation)이 소유하고 있다. 즉, 오로라는 아파치 커뮤니티에 의해 주도되는 아파치 소프트웨어 재단의 거버넌스 모델로 적용된다. 오로라는 사용자에게서 돈을 받지 않으며 현재 소프트웨어 개발 회사에게서 개발비를 받고 있지 않다. 마라톤은 메소스를 소유한 메소스피어(Mesosphere) 사의 소유이다. 메소스피어에게서 지원과 기능을 제공받으려면 유료로 진행될 수다..

Posted by '김용환'
,


1부터 28까지의 숫자를 특정 함수에 넣고 그 값을 Map으로 리턴하고 싶다고 한다면. 다음과 같을 것이다. 



scala> def multiply(num: Int): Int = { num *2 }

multiply: (num: Int)Int



scala> (1 to 28).map { i => (i, multiply(i)) }.toMap

res23: scala.collection.immutable.Map[Int,Int] = Map(5 -> 10, 10 -> 20, 24 -> 48, 25 -> 50, 14 -> 28, 20 -> 40, 1 -> 2, 6 -> 12, 28 -> 56, 21 -> 42, 9 -> 18, 13 -> 26, 2 -> 4, 17 -> 34, 22 -> 44, 27 -> 54, 12 -> 24, 7 -> 14, 3 -> 6, 18 -> 36, 16 -> 32, 11 -> 22, 26 -> 52, 23 -> 46, 8 -> 16, 19 -> 38, 4 -> 8, 15 -> 30)






키로 정렬하려면 TreeMap을 활용하면 깔끔히 정렬된다.


scala> val sortedMap = scala.collection.immutable.TreeMap(map.toSeq:_*)

sortedMap: scala.collection.immutable.TreeMap[Int,Int] = Map(1 -> 2, 2 -> 4, 3 -> 6, 4 -> 8, 5 -> 10, 6 -> 12, 7 -> 14, 8 -> 16, 9 -> 18, 10 -> 20, 11 -> 22, 12 -> 24, 13 -> 26, 14 -> 28, 15 -> 30, 16 -> 32, 17 -> 34, 18 -> 36, 19 -> 38, 20 -> 40, 21 -> 42, 22 -> 44, 23 -> 46, 24 -> 48, 25 -> 50, 26 -> 52, 27 -> 54, 28 -> 56)



예쁘게 출력하려면 mkString을 사용한다. 


scala> sortedMap.mkString("\n")

res28: String =

1 -> 2

2 -> 4

3 -> 6

4 -> 8

5 -> 10

6 -> 12

7 -> 14

8 -> 16

9 -> 18

10 -> 20

11 -> 22

12 -> 24

13 -> 26

14 -> 28

15 -> 30

16 -> 32

17 -> 34

18 -> 36

19 -> 38

20 -> 40

21 -> 42

22 -> 44

23 -> 46

24 -> 48

25 -> 50

26 -> 52

27 -> 54

28 -> 56




참고로 주의할 점은 (1 to 28) 과 Collection(1 to 28)은 다르다..


scala> (1 to 28)

res24: scala.collection.immutable.Range.Inclusive = Range(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)


scala> List(1 to 28)

res25: List[scala.collection.immutable.Range.Inclusive] = List(Range(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))




Posted by '김용환'
,

아파치 메소스의 DRF 관련



 최대 작업 수를 실행하고 자원을 가장 효율적으로 사용하기 위해 두 사용자의 자원을 어떻게 분배할 것인가 이다. 기존 알고리즘을 사용한다면 두 사용자 모두에게 동일한 크기의 자원을 할당할 수 있지만 이는 원하는 것이 아니다. 이러한 상황을 이기종 환경(heterogeneous environment)이라고 부른다.


아파치 메소스는 DRF(Dominant Resource Fairness)라는 알고리즘을 구현하며, DRF 알고리즘을 메소스의 자원 할당의 기본 정책으로 사용하고 있다.



대개 DRF 알고리즘을 대학 수준의 운영 체제 과목에서 가르친다. 작업 스케줄링(job scheduling)은 CPU에만 제한되지 않으며 메모리, 네트워크, 디스크와 같은 여러 자원이 존재한다. 그러나 자원 유형을 줄여 문제를 단순화하면 어떤 작업은 프로세서 집약적이고 다른 작업은 디스크 집약적이고, 또 다른 작업은 메모리 집약적이기 때문에 최대-최소 공정성(max-min fairness) 알고리즘이 실패(강력하지 않고 효율적이지 않음)하는 것을 볼 수 있다.


여기에 이기종 환경의 각 사용자에게 자원을 공평하게 분배할 수 있는 자원 스케줄링 메커니즘이 필요하다. 요컨대 DRF 알고리즘은 이기종 자원을 가진 시스템에 최대-최소 공평성 알고리즘을 적용한 것이다.



자원이 사용자 간에 동등하게 분배되지 않으면 DRF 알고리즘에 가중치가 적용된다고 말한다. 공유는 사용자별, 자원 수준별로 가중치를 적용할 수 있으며 사용자별이 더 많이 사용된다.


가중치가 적용된 알고리즘에는 가중치 외에 다음 기능을 더 가진다.


* 공정한 분배(envy freeness) : DRF 알고리즘은 다른 사용자의 자원 할당을 부러워할 필요가 없기 때문에 자유롭다. 가장 낮은 지배 점유율을 가진 사용자에게 자원을 제공하기 때문에 모든 사용자는 동일한 기회를 가질 수 있다.


* 파레토 효율(Pareto efficiency) : 특정 사용자의 통제 관심도를 높이면 자원에 대한 다른 사용자의 지배적인 참가가 비례적으로 감소한다. 한 사용자에게 더 많은 자원을 할당하면 다른 사용자에게 피해를 준다.

* 점진적인 충전(progressive filling) : DRF 알고리즘은 모든 사용자에 대해 동일한 비율로 지배 점유율을 증가시킨다. 다른 알고리즘은 수요에 따라 자원을 할당한다. DRF는 자원이 고갈되면 사용자가 자원을 해제하고, 다른 사용자가 재귀적으로 진행될 때 종료되며, 지배 점유율을 가진 사용자가 증가할 때까지 프로세스는  계속된다.

* 점유율 보장(share guarantee) : 모든 사용자의 지배 점유율 할당은 동일한 비율로 증가하므로 사용자를 동등하게 대우하고, 적어도 하나의 자원 일부는 보장된다.

* 전략 증명(strategy proof) : 사용자는 사용자 자신의 자원 요구를 위조할 수 없다. 한 사용자가 더 많은 자원을 요구하면 DRF 알고리즘은 사용자를 방해하지 않는다.


* 참고 자료

DRF 관련 논문
https://cs.stanford.edu/~matei/papers/2011/nsdi_drf.pdf
여기에 좋은 psedo 코드가 있다.

https://en.wikipedia.org/wiki/Envy-free_cake-cutting


분배 개념은 사회학적으로 나온 얘기였다. 전문용어를 여기서 배울 줄이야.
http://s-space.snu.ac.kr/bitstream/10371/52804/3/04%20envy-free.pdf





Posted by '김용환'
,



routes 파일에 request 매개변수를 주려면 다음과 같이 진행한다. 



GET     /update/:dbNumber/:actorId         controllers.HomeController.update(dbNumber: Int, actorId: Long)


Posted by '김용환'
,



play framework(2.3.5)에서 dbcp는 hikaricp를 기본으로 사용하고 있다.


https://www.playframework.com/documentation/2.5.x/SettingsJDBC


이 중 중요한 필드는 다음과 같다.




* play.db.default.hikaricp.minimumIdle=1


최소 connection pool의 개수



play.db.default.hikaricp.maximumPoolSize=10


최대 connection pool의 개수





Posted by '김용환'
,


for문을 작은 수에서 큰 수로 점진적으로 늘어나는 방법은 다음과 같다. 


scala> for (i <- 1 to 3) println(i)

1

2

3



큰 수를 작은 수로 작게 하려면 by를 사용한다.


scala> for (i <-3 to 1 by -1) println(i)

3

2

1



by를 사용하면 단계별로 iteration할 수 있다. 


scala>  for (i <- 1 to 10 by 2) println(i)

1

3

5

7

9

Posted by '김용환'
,