자바 개발자에서는 scala에서 사용하는 map을 주로 쓰는 스타일(어쩌면 예시문으로 배운 패턴)이 있다.


List(1,2,3).map { i =>

  ...

}


그런데. map 안에 함수가 들어가는 형태가 좀 어색할 때가 있다. 




즉, map 안에 함수적 코드가 들어가는 것은 어색하지 않은데.. 


println(List(1,2,3).map( i => i + 1))




 map 뒤에 plus1이라는 함수를 추가하니까 엄청 어색하다. 

def plus1(s: Int) = s + 1
val a = List(1,2,3).map(plus1)
println(a)


두 결과는 다음처럼 동일하다.


List(2, 3, 4)

List(2, 3, 4)




다음은 string을 인자로 받는 예제이다. 

def convert(str: String) = s"${str}/tt"
val b1 = List("1","2","3").map(convert)
println(b1)
val b2 = List("1","2","3").map(convert).mkString(",")
println(b2)

결과는 다음과 같다. 



List(1/tt, 2/tt, 3/tt)

1/tt,2/tt,3/tt


어떤 List결과를 변형(map)하고, List는 ,로 바꾸는 경우이다.


파일 디렉토리를 사용할 때 아주 유용할만하다..


Posted by '김용환'
,



zeppelin으로 spark 연동시 주의 사항을 2가지 설명한다.




1. zeppelin과 spark/scala 버전 이슈



zeppelin 0.6.2를 사용하고 있는데, 


https://zeppelin.apache.org/download.html 에 따르면, 


scala 2.11 & spark 2.0 버젼과


scala 2.10 & spark 1.6 버전을 살 수 있다고 한다. 



  • Note: From Zeppelin version 0.6.2, Spark interpreter in binary package is compatible with Spark 2.0 & Scala 2.11 and Spark 1.6(or previous) & Scala 2.10. You can use even different version of Spark at the same time if you set different SPARK_HOME in interpreter setting page.



기본 버전이 아니면, zeppelin에서 아래와 같은 에러가 발생할 수 있다.

예를 들어, Scala 2.11에 Spark 1.6과 같은 버전을 쓰려고 할때. 에러가 발생한다. 



java.lang.NoSuchMethodError: scala.Predef$.ArrowAssoc(Ljava/lang/Object;)Ljava/lang/Object;


java.lang.NoSuchMethodError: scala.runtime.ObjectRef.create(Ljava/lang/Object;)Lscala/runtime/ObjectRef;




이럴 때는 문서에 있는데로, conf/zeppelin-env.sh에 SPARK_HOME=/usr/local/spark 에 추가하면 더 이상 해당 에러는 발생하지 않는다. 






2. zeppelin과 spark cluster 연동하기


zeppelin은 spark master 1대만 동작하도록 설정되어 있다.


spark 슬레이브를 여러 대로 해서(물론 config/slave 수정 필요) ./sbin/start-slave.sh <옵션> 실행해서 클러스터 모드를 만든다. 




참고로 아래와 같은 이슈가 만난다면 버전 이슈이고, 기존 데몬 다 내리고, 디렉토리 몽땅 지워야 한다. (버전 진짜 중요.)


java.lang.RuntimeException: java.io.InvalidClassException: org.apache.spark.rpc.netty.RequestMessage; local class incompatible: stream classdesc serialVersionUID = -5447855329526097695, local class serialVersionUID = -2221986757032131007


memory, cpu의 설정이 정말 중요하다. 노드당 사용할 메모리가 적절히 않으면, master는 대기할 하드웨어 자원이 나타날 때까지 기다린다.




zepelin에서 cluster mode로 설정한다. 


zeppelin의 scala interpreter->master 설정 -> local[*] 


이를 다음처럼 바뀌어야 한다. 


master spark://서버이름:7077


standalone은 간단하지만, mesos 모드거나 얀을 쓴다면 아래를 참고한다.


https://zeppelin.apache.org/docs/0.7.0-SNAPSHOT/install/spark_cluster_mode.html






3. zeppelin 실행 안되는 현상


zeppelin이 실행이 안되면, refresh 한다. 세션이 끊어지거나, zeppelin이 재시작된 경우에는 동작이 되지 않는다.


또는 아래와 같은 에러가 발생하면, zeppelin또는 spark을 재시작한다. 



The currently active SparkContext was created at:


(No active SparkContext.)




그리고, 실행은 되는데, pending이라면, spark 모니터링 서버를 살펴본다. 



http://서버이름:28080/


제대로 slave와 동작하는 지 확인해야 한다. 다양한 원인이 있을 수 있다. spark의 메모리나 CPU 설정 잘못하면, pending 상태로 남겨진다.


zeppelin의 이슈는 아니지만, spark설정을 제대로 해야하는 부분이 존재한다. 




4. 무거운 연산


무거운 연산은 zeppelin을 죽인다. 노트북으로서 좋은데, 진짜 대용량 데이터를 가지고 하기에는 아직은 버겹다..

Posted by '김용환'
,




sys.env는 환경 변수를 읽는 코드이다. 



scala> sys.env("PHASE")

java.util.NoSuchElementException: key not found: PHASE1



[~] export PHASE=Test



scala> sys.env("PHASE")

res0: String = Test




모든 환경 변수를 보고 싶다면, 아래 코드를 실행한다.


import scala.collection.JavaConversions._


val environmentVars = System.getenv()

for ((k,v) <- environmentVars) println(s"key: $k, value: $v")


val properties = System.getProperties()

for ((k,v) <- properties) println(s"key: $k, value: $v")


Posted by '김용환'
,





zeppelin에서 외부 라이브러리를 추가하는 방법은 여러가지가 있다.



1. maven 외부 repository, 내부 repository를 사용할 수 있다.


https://zeppelin.apache.org/docs/0.6.0/manual/dependencymanagement.html



2. jar를 직접 추가한다. (assemblied jar)


https://zeppelin.apache.org/docs/latest/interpreter/spark.html

conf/interpreter.json 파일에서 spark 의 zeppelin.dep.localrepo 속성을 찾고, 절대 위치로 바꾼다.

/usr/local/zeppelin/local-repo로 바꾸서 잘 안된다. 



무식하게 하는 방법이 나은 것 같다. /usr/local/zeppelin/local-repo의 jar 파일을 추가한다.


conf/zeppelin-env.sh 파일에서 export SPARK_SUBMIT_OPTIONS="--jars /usr/local/jeppelin/local-repo/custom-spark-job.jar" 을 추가하고 재시작하면 외부 라이브러리를 사용할 수 있다. 




Posted by '김용환'
,



hadoop ls는 명령어가 많지 않다. 


d h R 밖에 없다. 


Usage: hadoop fs [generic options] -ls [-d] [-h] [-R] [<path> ...]



그래서 먼가 후처리를 하고 싶다면, 파이프라인을 활용해야 한다.


예를 들어, 특정 디렉토리에 최근에 만들어진 파일을 얻고 싶다면 다음과 같이 모든 파일을 출력하게 한 후, 시간으로 리버스한 후, 마지막 하나를 읽어오면 된다. 


이 명령어로 검증해보고..


$ hadoop fs -ls -R  /google/app-store-log/  | sort -k 6 | tail -n 1



아래 명령어를 쓰면 최근에 생성된 파일을 찾을 수 있다.


hadoop fs -ls -R /google/app-store-log/  | sort -k 6 | tail -n 1 | awk '{print $8'}



특정 디렉토리의 최신 파일을 복사하려면, 다음과 같이 하면 된다. 



hdfs dfs -get `hadoop fs -ls -R  /google/app-store-log/ | sort -k 6 | tail -n 1 | awk '{print $8'}` /tmp/google-spark-job.jar


Posted by '김용환'
,



scala에는 특정 타입을 만족하는 증거(witness)를 의미한다. 


implicit evidence는 허용할 타입을 제한함을 의미한다. 



간단하게 아래 내용을 테스트해본다.

val list1 = List("a" -> "b", "c" -> "d", "e" -> "f")
println(list1.toMap)

이 결과는 다음과 같다.


Map(a -> b, c -> d, e -> f)



toMap은 암시를 받는데, <:<를 사용한다. A는 (T, U) 타입의 서브 타입, 즉 튜플이어야 함을 의미한다. 


def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = {
val b = immutable.Map.newBuilder[T, U]
for (x <- self)
b += x

b.result()
}


만약, 아래 코드를 실행했다면, 에러가 발생했을 것이다. 


val list1 = List(1,2,3,4,5)

list1.toMap // Error:(8, 6) Cannot prove that Int <:< (T, U).



implicit을 쓰지 않아도 암시로 쓰일 수 있다. 



scala>   def impTest[X : Seq](x : X) = x

impTest: [X](x: X)(implicit evidence$1: Seq[X])X





Predef.scala의 일부 내용에 다음과 같이 쓰여져 있다. 

/**
* An instance of `A <:< B` witnesses that `A` is a subtype of `B`.
* Requiring an implicit argument of the type `A <:< B` encodes
* the generalized constraint `A <: B`.
*
* @note we need a new type constructor `<:<` and evidence `conforms`,
* as reusing `Function1` and `identity` leads to ambiguities in
* case of type errors (`any2stringadd` is inferred)
*
* To constrain any abstract type T that's in scope in a method's
* argument list (not just the method's own type parameters) simply
* add an implicit argument of type `T <:< U`, where `U` is the required
* upper bound; or for lower-bounds, use: `L <:< T`, where `L` is the
* required lower bound.
*
* In part contributed by Jason Zaugg.
*/
@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.")
sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }
// The dollar prefix is to dodge accidental shadowing of this method
// by a user-defined method of the same name (SI-7788).
// The collections rely on this method.
implicit def $conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]

@deprecated("Use `implicitly[T <:< U]` or `identity` instead.", "2.11.0")
def conforms[A]: A <:< A = $conforms[A]

/** An instance of `A =:= B` witnesses that the types `A` and `B` are equal.
*
* @see `<:<` for expressing subtyping constraints
*/
@implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.")
sealed abstract class =:=[From, To] extends (From => To) with Serializable
private[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x }
object =:= {
implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A]
}




Posted by '김용환'
,

[scala] 암시 implicitly

scala 2016. 11. 3. 14:02


암시(implicit)에 대한 기초 개념은 아래에서 정리해봤다.

http://knight76.tistory.com/entry/scala-class-%EC%98%88%EC%8B%9C-2-%EC%95%94%EC%8B%9C%EC%A0%81-%EB%B3%80%ED%99%98implicit-conversion



Predef에 implicitly 라는 게 존재한다. 



@inline def implicitly[T](implicit e: T) = e


implicit을 짧게 쓸 수 있는 관용구 정도로 생각하면 될 것 같다. 



def plus(i: Int) (implicit j: Int) = println(i + j)
def plusWrapper(i: Int) = plus(i)
def plusImplicitly(i: Int) = plus(i)(implicitly[Int])

implicit val j = 20
plus(10)
plusWrapper(20)
plusImplicitly(30)


결과는 다음과 같다.


30

40

50


Posted by '김용환'
,



scala의 문자열과 연관된 패턴 매치에 대한 예시이다.


자바는 정규 표현식과 관련된 클래스가 Pattern, Matcher가 있지만, scala에서는 scala.util.matching.Regex 클래스가 존재한다. 패턴을 정의하고, 문자열에 대한 패턴 정보를 findAllIn 메소드(findXXX 시리즈로 다양한 제어를 할 수 있다)로 확인할 수 있다. 


val pattern1 = new scala.util.matching.Regex("(H|h)ello")
val str = "Hello Mom, hello Dad"
println(pattern1.findAllIn(str).mkString(","))

결과는 다음과 같다. 


Hello,hello




하지만, 꼭 scala.util.matching.Regex를 명시해서 사용할 필요는 없다. 정규 표현식으로 사용할 표현식에 .r을 붙이면 알아서 Regex 타입의 인스턴스가 생성된다. 

val numPattern = "[0-9]+".r
// scala.util.matching.Regex = [0-9]+
val str1 = "Hello Mom 911, hello Dad 119?"
val matches = numPattern.findAllIn(str1)
println(matches.toList)


결과는 다음과 같다.


List(911, 119)




Regex의 findFirstIn을 사용하면 Option으로 리턴한다. 




val numPattern = "[0-9]+".r val str1 = "Hello Mom 911, hello Dad 119?"

println(numPattern.findFirstIn(str1))
println(numPattern.findFirstIn(str))


결과는 다음과 같다. 


Some(911)

None




Option의 특징을 활용해서 Some과 None에 대한 case 문을 작성할 수 있다.


numPattern.findFirstIn(str1) match {
case Some(s) => println(s)
case None => println("None")
}

numPattern.findFirstIn(str) match {
case Some(s) => println(s)
case None => println("None")
}


결과는 다음과 같다. 


911

None




이번에는 반대로, 특정 문자열에 대해 regex 타입을 case 문에 넣을 수 있다.  그리고, 

// string match
val sp = "[a-zA-Z]".r
val np = "xxx".r
"bus 1503, 5, 222" foreach {
case sp => println("s : " + sp)
case np => println("n : " + np)
}



결과는 다음과 같다. 


s : b

s : u

s : s

s :  

s : 1

s : 5

s : 0

s : 3

s : ,

s :  

s : 5

s : ,

s :  

s : 2

s : 2

s : 2






case 문이나 find 메소드 없이 아주 간단하게 정규 표현식을 읽을 수 있다. 

val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2016/11/01"
println(year, month, day)

결과는 다음과 같다. 


(2016,11,01)





그리고, 정확치 않은 정규 표현식이라면, 적당히 알아서 나온다(뒤에서 부터 파싱되었다).

val regex1 = """(\d+)(\d+)(\d+)""".r
val regex1(a, b, c) = "112801"
println(a, b, c)

결과는 다음과 같다.

(1128,0,1)




간단한 문자열도 이와 같이 실행할 수 있다. 

val Name = """(\w+)\s+(\w+)""".r
val Name(firstName, lastName) = "samuel kim"
println(firstName, lastName)


"samuel kim" match {
case Name(first, last) => println(first, last)
case _ => println("x")
}

위 두 결과는 모두 다음과 같다. 


(samuel,kim)

(samuel,kim)





이젠 full name도 쉽게 할 수 있다. 

val FullName = """(\w+)\s+(\w+)\s+(\w+)""".r
val FullName(first, middle, last) = "samuel jackson kim"
println(first, middle, last)

결과는 다음과 같다.


(samuel,jackson,kim)




시간, 분, 초를 한번에 로그에 남겼을 경우 이를 예쁘게 보여줄 수 있는 함수이다. 


val pattern = """([0-9]{1,2})([0-9]{1,2})([0-9]{1,2})""".r
val allMatches = pattern.findAllMatchIn("112801")
allMatches.foreach { m =>
println(m.group(1) + ":" + m.group(2) + ":" + m.group(3))
}

결과는 다음과 같다. 


11:28:01




이를 Spark-Zeppelin으로 간단하게 보여주기 위해 getTime이라는 메소드를 만들었다. 



val reqLog = textFile.filter(line=>line.contains("12314")).map(s=>s.split("\t")).map(

    s=>RequestLog(getTime(s(1).toString),

            s(5), 

            s(11),

            s(21)

        ))




하나는 Regex에 findAllMatchIn과 foreach, group을 사용한 함수와

또 다른 하나는 Regex에 findAllIn과 case를 이용한 방식을 사용했다. 

def getTime(time: String): String = {
val pattern = """([0-9]{1,2})([0-9]{1,2})([0-9]{1,2})([0-9]{1,5})""".r
val allMatches = pattern.findAllMatchIn(time)
var result = ""
allMatches.foreach { m =>
result = m.group(1) + ":" + m.group(2) + ":" + m.group(3) + "." + m.group(4)
}
result
}

def getTime2(time: String): String = {
val pattern = """([0-9]{1,2})([0-9]{1,2})([0-9]{1,2})([0-9]{1,5})""".r
var result = ""
pattern.findAllIn(time) foreach {
case pattern(hour, minute, second, millisecond) => result = hour + ":" + minute + ":" + second + "." + millisecond
}
result
}



println(getTime("1128000000"))
println(getTime2("1128000000"))

결과는 다음과 같다. 


11:28:00.0000

11:28:00.0000



Posted by '김용환'
,


kibana의 버전에 맞춰 elasticsearch, beats, logstatsh가 5.0으로 릴리즈 되었다. 


https://www.elastic.co/kr/v5



참고로 GA는 General availability (a release)의 약자이다. 안정적인 버전을 의미한다. 


Lucene의 compaction 이슈를 확인해보면 좋을 것 같다...



5.0에 대한 자세한 내용은 아래 링크에 있다. 

https://www.elastic.co/blog/elasticsearch-5-0-0-released


1. indexing 성능이 좋아졌다

2. Ingest 노드가 새로 생겼다.

3. 강력한 스크립트(Painless)가 생겼다.

4. 새로운 자료구조를 가졌다.

5. Search과 Aggregation이 좋아졌다. 

6. percolator도 좋아졌다.

7. Java Rest API를 지원한다.


Posted by '김용환'
,


 


이 시대는 Hive에서 Spark/Pheonix(http://phoenix.apache.org/)이나,


Spark / Spark SQL / Spark DataFrame으로  넘어가고 있다...


개인적으로 Hive에 대해서 많이 알았던 것 같다. Hive Query를 쓰면서도 많이 몰랐던 기능을 알게 되었다. 


그래도 Hive는 정말 편하고 좋다....!!!! 아직도 더 알고 싶고 더 깊이 알고 싶다!!



(2017.2 추가)


출판사에서 번역을 진행하기로 했다. 다시 책을 보면서 Hive Query를 공부해야지. 책이 나온다는 것은 기쁘다..






https://www.amazon.com/Apache-Hive-Essentials-Dayong-Du/dp/1783558571






번역된 책


http://www.yes24.com/24/goods/36111470?scode=032





Posted by '김용환'
,