zepplin에서 spark을 테스트하다가 아래와 같은 에러를 만날 수 있다. 

여러 spark context가 쓰인 이유인데, allowMultipleContext를 true로 설정하면 문제가 없다. 



org.apache.spark.SparkException: Only one SparkContext may be running in this JVM (see SPARK-2243). To ignore this error, set spark.driver.allowMultipleContexts = true. The currently running SparkContext was created at:


문제 해결


  val sparkConf = new SparkConf()

    .setAppName("abc")

    .set("spark.driver.allowMultipleContexts", "true");







Posted by '김용환'
,


scala에서 uuid 생성하는 방법(사실 자바 UUID 생성하는 방법)



scala>  def uuid() = java.util.UUID.randomUUID.toString

uuid: ()String


scala> uuid()

res1: String = b500130a-8958-469b-9854-4d42760307aa


scala> uuid()

res2: String = 63e2db05-b97b-40e7-aae6-f221526cb4f3



UUID 객체만 얻을려면 다음을 호출한다.


scala> def uuid = java.util.UUID.randomUUID

uuid: java.util.UUID


scala> uuid

res3: java.util.UUID = cf065f3d-ebc4-41d7-a1dc-38295dcdf415

Posted by '김용환'
,



logback에서 마음에 드는 것 중 하나는 log에 항상 나오는 패턴을 지정할 수 있다는 점(encoder)이다.




<appender name="birthday" class="ch.qos.logback.core.rolling.RollingFileAppender">

<file>${app.home}/logs/birthday.log</file>

<encoder>

<pattern>%d{yyyyMMdd}\t%d{HHmmssSSS}\t%m%n</pattern>

</encoder>

<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">

<fileNamePattern>${app.home}/logs/birthday.log.%d{yyyyMMdd}</fileNamePattern>

<maxHistory>21</maxHistory>

</rollingPolicy>

</appender>

<logger name="birthday_logger" level="INFO" additivity="false">

<appender-ref ref="birthday"/>

</logger>



birthday logger를 사용해서 로그를 저장할 때 탭 단위로 날짜, 시간,  메시지를 저장할 수 있다는 점이 매력적이다. 

Posted by '김용환'
,




Spark의 Task는 하나의 partition을 가진다. 


SparkContext의 parallelize를 실행해서 hadoop HDFS에 데이터를 저장할 때, 병렬(spark core) 개수만큼 파티션이 생긴다. 전문 용어로 level of parallelism이라 한다 (hadoop에서도 reduce 개수만큼 파티션 개수가 생긴다)


HDFS에 저장할 용량이 크지 않다면 spark core 개수와 상관없이 하나의 파티션 파일로 모아두는 것이 좋을 수 있다.(전문용어로 aggregation 이라 한다)


이를 위해 repartition 또는 coalesce를 사용할 수 있다.

(coalesce는 random shuffle을 하지 않고, repartition은 random shuffle을 하기 때문에 성능 상 차이점이 있다) 


repartition을 적용하니 잘 작동한다. hadoop의 HDFS에 하나의 파일로 모아졌다. 


sc.parallelize(birthdayUsers.map(x => s"${person.id}\t${person.name}")).repartition(1)






* 참고 : 문서에 따르면 aggregation하는 방법은 3가지로 있다.

http://spark.apache.org/docs/latest/programming-guide.html



coalesce(numPartitions)Decrease the number of partitions in the RDD to numPartitions. Useful for running operations more efficiently after filtering down a large dataset.
repartition(numPartitions)Reshuffle the data in the RDD randomly to create either more or fewer partitions and balance it across them. This always shuffles all data over the network.
repartitionAndSortWithinPartitions(partitioner)Repartition the RDD according to the given partitioner and, within each resulting partition, sort records by their keys. This is more efficient than calling repartition and then sorting within each partition because it can push the sorting down into the shuffle machinery.

* 소스 비교 


repartition은 coalesce에서 shuffle만 한 것이다.

/**
* Return a new RDD that has exactly numPartitions partitions.
*
* Can increase or decrease the level of parallelism in this RDD. Internally, this uses
* a shuffle to redistribute data.
*
* If you are decreasing the number of partitions in this RDD, consider using `coalesce`,
* which can avoid performing a shuffle.
*/
def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
coalesce(numPartitions, shuffle = true)
}




coalesce 소스이다. 

/**
* Return a new RDD that is reduced into `numPartitions` partitions.
*
* This results in a narrow dependency, e.g. if you go from 1000 partitions
* to 100 partitions, there will not be a shuffle, instead each of the 100
* new partitions will claim 10 of the current partitions.
*
* However, if you're doing a drastic coalesce, e.g. to numPartitions = 1,
* this may result in your computation taking place on fewer nodes than
* you like (e.g. one node in the case of numPartitions = 1). To avoid this,
* you can pass shuffle = true. This will add a shuffle step, but means the
* current upstream partitions will be executed in parallel (per whatever
* the current partitioning is).
*
* Note: With shuffle = true, you can actually coalesce to a larger number
* of partitions. This is useful if you have a small number of partitions,
* say 100, potentially with a few partitions being abnormally large. Calling
* coalesce(1000, shuffle = true) will result in 1000 partitions with the
* data distributed using a hash partitioner.
*/
def coalesce(numPartitions: Int, shuffle: Boolean = false)(implicit ord: Ordering[T] = null)
: RDD[T] = withScope {
if (shuffle) {
/** Distributes elements evenly across output partitions, starting from a random partition. */
val distributePartition = (index: Int, items: Iterator[T]) => {
var position = (new Random(index)).nextInt(numPartitions)
items.map { t =>
// Note that the hash code of the key will just be the key itself. The HashPartitioner
// will mod it with the number of total partitions.
position = position + 1
(position, t)
}
} : Iterator[(Int, T)]

// include a shuffle step so that our upstream tasks are still distributed
new CoalescedRDD(
new ShuffledRDD[Int, T, T](mapPartitionsWithIndex(distributePartition),
new HashPartitioner(numPartitions)),
numPartitions).values
} else {
new CoalescedRDD(this, numPartitions)
}
}



소스를 살펴본 것은 

repartition은 네트워크 부하는 올라가지만 무난하게 사용할 수 있는데, coalesce는 동작은 하는데, 100kb보다 크다는 warning이 발생하였지만 잘 동작했기 때문이다. 


WARN TaskSetManager: Stage 1 contains a task of very large size (XXXX). The maximum recommended task size is 100 KB.


문서를 보면, numPartitions=1일 때 계산 비용이 높아질 수 있다고 하며 shuffle을 true로 설정하라 한다. 


However, if you're doing a drastic coalesce, e.g. to numPartitions = 1, this may result in your computation taking place on fewer nodes than you like (e.g. one node in the case of numPartitions = 1). To avoid this, you can pass shuffle = true. This will add a shuffle step, but means the current upstream partitions will be executed in parallel (per whatever the current partitioning is).




Posted by '김용환'
,

ruby zookeeper

Ruby 2017. 2. 7. 10:58



ruby 공부차, ruby에서 zookeeper 클라이언트(https://github.com/zk-ruby/zookeeper)를 사용한 예시이다. 



require 'json'

require 'zookeeper'


@zookeeper = Zookeeper.new("zk1.plus.google.com:2181")

@root_key = "redis"


@zookeeper.get_children(:path => @root_key).tap do |node|

  queue = node[:children]

  while ! queue.empty? do

    children_path = queue.shift

    child_node = @zookeeper.get_children(:path => @root_key + "/" + children_path)

    children_queue = child_node[:children]

    ........

    

  end

end



'Ruby' 카테고리의 다른 글

geminabox 설치/실행  (0) 2017.10.24
gem 설치 디버깅하기  (0) 2017.10.20
[ruby] http call 예시  (0) 2017.01.06
[capistrano] 다른 task 호출하기  (0) 2016.12.07
ruby on rails 애플리케이션 실행하기  (0) 2016.10.12
Posted by '김용환'
,



쉘 스크립트에서 >와 <는 리디렉션(redirection)을 의미한다. 표준 출력과 입력을 리디렉션할 때 사용한다.



test.sh > /dev/null


cp file-name > /dev/null


>1은 커맨드의 표준 출력을 다음에 나오는 파일 디스크립터에 전달한다.


$ ls b* >1 m.log

$ cat m.log

...


>2는 쉘 스크립트의 표준 에러를 다음에 나오는 파일 디스크립터에 전달한다.


$ ls zzz*

ls: zzz*: No such file or directory

$ ls zzz* 2> m.log

$ cat m.log

ls: zzz*: No such file or directory




>&2는 모든 출력을 강제로 쉘 스크립트의 표준 에러로 출력한다. 


$ echo "ho" >&2

ho



자주 혼동되긴 하지만  형태로만 봤을 때 파일 디스크립터의 유무가 크다. 



하나의 커맨드에 동시에 표준 출력과 표준 입력을사용하려면 다음과 같다. >를 사용해서 먼저 결과를 로그 파일에 저장하고 2>>는 표준 에러의 결과를 log에 추가하라는 의미이다. 


$ ls -zzz* > log 2>> log

$ cat log

ls: illegal option -- z

usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]



이보다 더 좋은 축약형은 2>&1이다. 많이 봐서 알 수 있을 것이다.


$ ls -zzz* > log 2>&1

$ cat log

ls: illegal option -- z

usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]



순서를 바꾸면 어떤 일이 벌어질까? 제대로 동작되지 않는다. 

왼쪽에서 오른쪽으로 파싱을 하는데, 표준 에러가 표준 출력보다 먼저 나오기 때문에 표준 에러를 표준 출력으로 보내고 표준 출력은 log로 저장하려는 시도가 되지 않는다.  또한 ls -zzz* 1>&2 > log도 동작되지 않는다.


$ ls -zzz* 2>&1 > log

ls: illegal option -- z

usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]



여기서, tee를 이용하면 표준 출력와 표준 에러의 내용이 로그 파일도 나오게 하고 콘솔에도 나오게 하고 싶을 수 있다. 

$ ls -zzz* 2>&1 | tee log

ls: illegal option -- z

usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1] [file ...]



exec를 사용하면 쉘에서 모든 표준 입력과 출력을 모두 특정 파일로 보낼 수 있다.  >는 표준 출력, 2>는 표준 에러를 의미한다.


[~/temp] exec > ~/temp/log1

[~/temp] ls -al

[~/temp] sadf

-bash: sadf: command not found

[~/temp] ls -al ~/temp/log1



다른 터미널에서 확인해본다.


$ cat ~/temp/log1

...


Posted by '김용환'
,


레디스의 z계열 커맨드가 어떻게 동작하는지 까먹어서 공부를 했다.




127.0.0.1:6379> zadd number 1000 samuel

(integer) 1

127.0.0.1:6379> zadd number 2000 matt

(integer) 1

127.0.0.1:6379> zadd number 3000 jonathan

(integer) 1



zscore는 스코어를 본다.


127.0.0.1:6379> zscore number jonathan

"3000"

127.0.0.1:6379> zscore number samuel

"1000"




zrange 커맨드 (-1 매개 변수는  마지막)


127.0.0.1:6379> zrange number 0 100

1) "samuel"

2) "matt"

3) "jonathan"

127.0.0.1:6379> zrange number 0 -1

1) "samuel"

2) "matt"

3) "jonathan"

127.0.0.1:6379> zrange number 0 1

1) "samuel"

2) "matt"


점수도 함께 표현 (withscores)


127.0.0.1:6379> zrange number 0 -1 withscores

1) "samuel"

2) "1000"

3) "matt"

4) "2000"

5) "jonathan"

6) "3000"


zrem은 삭제


127.0.0.1:6379> zrem number "matt"

(integer) 1

127.0.0.1:6379> zrange number 0 -1 withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"



zrangebyscore 스코어로 범위 지정하는 커맨드


127.0.0.1:6379> zrangebyscore number 3000 4000

1) "jonathan"



zounct는 score 범위로 몇개가 있는 계산하는 커맨드 (-inf:음의 무한대, +inf: 양의 무한대)


127.0.0.1:6379> zrange number 0 -1

1) "samuel"

2) "jonathan"

127.0.0.1:6379> zcount number -inf +inf

(integer) 2

127.0.0.1:6379> zcount number 0 4000

(integer) 2

127.0.0.1:6379> zcount number -inf 1000

(integer) 1



zrangebylex는 member로 범위 지정


127.0.0.1:6379> zadd number  5000 jack

(integer) 1

127.0.0.1:6379> zadd number  6000 soa

(integer) 1

127.0.0.1:6379> zrangebylex number - +

1) "samuel"

2) "jonathan"

3) "jack"

4) "soa"


127.0.0.1:6379> zrangebylex number "[soa" +

1) "soa"



zlexcount는 zrangebylex와 같이 개수 구하는 커맨드


127.0.0.1:6379> zlexcount number - +

(integer) 4

127.0.0.1:6379> zlexcount number "[soa" +

(integer) 1



zrangebyscore는 스코어를 기반으로 값을 검색한다.



127.0.0.1:6379> zrangebyscore  number -inf inf

1) "samuel"

2) "jonathan"

3) "jack"

4) "soa"

127.0.0.1:6379> zrangebyscore  number -inf inf  withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"

5) "jack"

6) "5000"

7) "soa"

8) "6000"

127.0.0.1:6379> zrangebyscore  number -inf 5000 withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"

5) "jack"

6) "5000"

127.0.0.1:6379> zrangebyscore  number -inf (4000 withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"

127.0.0.1:6379> zrangebyscore  number 100 3000 withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"




zremrnagebyrank는 스코어 단위로 삭제한다. 


127.0.0.1:6379> zrangebyscore  number -inf inf  withscores

1) "samuel"

2) "1000"

3) "jonathan"

4) "3000"

5) "jack"

6) "5000"

7) "soa"

127.0.0.1:6379> zremrangebyrank number 1 1

(integer) 1

127.0.0.1:6379> zrange number 0 -1

1) "samuel"

2) "jack"

3) "soa"



zremrangebylex는 zrangebylex 기반과 비슷하며 삭제한다. 



127.0.0.1:6379> zrangebylex number - +

1) "samuel"

2) "jack"

3) "soa"

127.0.0.1:6379> zrangebylex number "(jack" "(soa"

1) "samuel"

2) "jack"

127.0.0.1:6379> zremrangebylex number "(jack" "(soa"

(integer) 2

127.0.0.1:6379> zrangebylex number - +

1) "soa"



다시 재구성한 한 후.. zremrangebylex 3 -1 는 3개 빼고 다 삭제한다.  zremrangebylex 1 -1 는 1개 빼고 다 삭제한다. 


127.0.0.1:6379> zrevrange number 0 -1 withscores

1) "soa"

2) "6000"

3) "jonathan"

4) "3000"

5) "matt"

6) "2000"

7) "samuel"

8) "1000"

127.0.0.1:6379> zremrangebyrank number 3 -1

(integer) 1

127.0.0.1:6379> zrevrange number 0 -1 withscores

1) "jonathan"

2) "3000"

3) "matt"

4) "2000"

5) "samuel"

6) "1000"

127.0.0.1:6379> zremrangebyrank number 1 -1

(integer) 2

127.0.0.1:6379> zrevrange number 0 -1 withscores

1) "samuel"

2) "1000"



일부러 음수로 저장하면 또 다른 의미로 rank로 최신 데이터만 가져올 수 있다.


127.0.0.1:6379> zadd feed -900 1st

(integer) 1

127.0.0.1:6379> zadd feed -950 2nd

(integer) 1

127.0.0.1:6379> zadd feed -955 3rd

(integer) 1

127.0.0.1:6379> zrevrange feed 0 -1 withscores

1) "1st"

2) "-900"

3) "2nd"

4) "-950"

5) "3rd"

6) "-955"

127.0.0.1:6379> zremrangebyrank feed 2 -1

(integer) 1

127.0.0.1:6379> zrevrange feed 0 -1 withscores

1) "2nd"

2) "-950"

3) "3rd"

4) "-955"





zrank는 오름차순 순서를 보여준다.


127.0.0.1:6379> zrange number 0 -1 withscores

1) "samuel"

2) "1000"

3) "matt"

4) "2000"

5) "jonathan"

6) "3000"

7) "soa"

8) "6000"

127.0.0.1:6379> zrank number "jonathan"

(integer) 2

127.0.0.1:6379> zrank number "soa"

(integer) 3


점수 높은것부터 보려면 zrevrank를 호출한다.


127.0.0.1:6379> zrevrange number 0 -1 withscores

1) "soa"

2) "6000"

3) "jonathan"

4) "3000"

5) "matt"

6) "2000"

7) "samuel"

8) "1000"

127.0.0.1:6379> zrevrank number "soa"

(integer) 0

127.0.0.1:6379> zrevrank number "samuel"

(integer) 3


Posted by '김용환'
,


쉘에서 변수 대체에서의 패턴은 조금 특별히 봐야 한다.



패턴 매칭 구문은 하나의 변수 이름(또는 매개 변수 번호)과 하나의 패턴, 두 개의 매개 변수를 받는다. 쉘은 주어진 패턴과 일치하는 주어진 변수의 내용을 검색한다. 패턴이 일치하면 쉘은 커맨드 라인에서 패턴의 일치하는 부분을 삭제한 채 변수 값을 사용한다. 패턴이 일치하지 않으면 커맨드 라인에서 변수의 전체 내용을 사용한다. 두 경우 모두 변수의 내용이 변경되지 않는다.


패턴은 네 가지가 있다.



${variable%pattern}

쉘은 특정 pattern으로 끝나는지 확인하기 위해 내부 variable를 살펴본다. 패턴과 일치하면 variable의 내용이 사용되고 가장 짧게 일치하는 pattern 패턴을 오른쪽부터 제거한다.


예시는 다음과 같다.


[~] s="boys, be ambitious"

[~] echo $s

boys, be ambitious

[~] echo ${s%am*s}

boys, be

[~] echo ${s%b*s}

boys, be am

[~] echo ${s%%b*s}


[~] echo ${s%s}

boys, be ambitiou

[~] echo ${s%i*s}
boys, be ambit


${variable%%pattern}

쉘은 특정 pattern으로 끝나는지 확인하기 위해 variable 내부를 다시 살펴본다. 그러나 이번에는 가장 길게 일치하는 pattern을 오른쪽부터 제거한다. pattern에 *가 사용되는 경우에만 관련이 있다. 그렇지 않으면 %와 %%는 같은 방식으로 작동한다.


[~] s="boys, be ambitious"

[~] echo $s

boys, be ambitious

[~] echo ${s%%am*s}

boys, be

[~] echo ${s%%b*s}


[~] echo ${s%%s}
boys, be ambitiou

[~] echo ${s%%i*s}

boys, be amb




% 구문과 %% 구문의 경우 변수 값은 지정된 패턴으로 끝나야 한다.




${variable#pattern}

커맨드 라인에서 variable의 값을 사용하고 왼쪽부터 패턴을 제거한다.


예시는 다음과 같다.


[~] s="boys, be ambitious"

[~] echo $s

boys, be ambitious

[~] echo ${s#b}

oys, be ambitious

[~] echo ${s#b*s}

, be ambitious



${variable##pattern}

pattern의 가장 긴 항목이 왼쪽에서 제거된다는 점을 제외하고 # 형식과 유사하다.


예시는 다음과 같다. 


[~] s="boys, be ambitious"

[~] echo $s

boys, be ambitious

[~] echo ${s##b}

oys, be ambitious

[~] echo ${s##b*s}



 # 구문과 ## 구문의 경우 변수는 해당 변수로 시작해야 한다.







Posted by '김용환'
,


쉘 스크립트에서 set -x 하면 추적 기능이 생겨서 표준 에러로 커맨드를 출력한다. 

출력할 때는 + 기호가 앞에 붙는다. 만약 추적 기능을 쓰지 않으려면 set +x를 사용한다.




[~] set -x


[~] echo $x

+ echo aaaaa

aaaaa


[~] env

+ env


[~] ls -al | awk '{print $1}

> '

+ ls -al

+ awk '{print $1}

'


[~] set +x

+ set +x

[~] ls -al | awk '{print $1}'



추적 옵션은 서브쉘에 전달이 되지 않는다. 따로 커맨드를 실행해야 한다. 


$ set -x test.sh



Posted by '김용환'
,



변수 이름에 중괄호를 사용하면 다른 문자들로부터 변수를 보호할 수 있다. 


$ echo $JAVA_HOME

/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home

$ echo $JAVA_HOMEs


$ echo ${JAVA_HOME}s
/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Homes


그리고 변수 명의 값이 널이면 기본 문자열을 추가할 수 있다.


$ echo ${JAVA_HOME:-no java home}

/Library/Java/JavaVirtualMachines/jdk1.8.0_101.jdk/Contents/Home

$ echo ${JAVA_HOME1:-no java home}

no java home



변수의 값에 따라 항상 변경된다.


$ echo ${x:-not defined}

1

$ x=

$ echo ${x:-not defined}

not defined


'unix and linux' 카테고리의 다른 글

[shell] 변수 대체의 패턴 - %, %%, #, ##  (0) 2017.02.04
[shell] 쉘 스크립트 추적하기 - set -x/set +x  (0) 2017.02.03
export -p  (0) 2017.01.31
프롬프팅(prompting) PS1, PS2  (0) 2017.01.31
echo 이스케이프 - \c  (0) 2017.01.23
Posted by '김용환'
,