자바 개발할 때 json 파싱은 사실 상 헬이다..


Apache Nifi에서 사용하고 있는 JsonPath(https://github.com/json-path/JsonPath)는 성능도 괜찮고, 캐시 기능이 있어서 여러 번 파싱하지 않아도 되고... 조건 검색, REGEX 검색도 가능하고 아주 쓸만하다.



예제는 홈피에 있다. (scala의 json4s보다 훨 나은 느낌이다..)

Path Examples

Given the json

{
    "store": {
        "book": [
            {
                "category": "reference",
                "author": "Nigel Rees",
                "title": "Sayings of the Century",
                "price": 8.95
            },
            {
                "category": "fiction",
                "author": "Evelyn Waugh",
                "title": "Sword of Honour",
                "price": 12.99
            },
            {
                "category": "fiction",
                "author": "Herman Melville",
                "title": "Moby Dick",
                "isbn": "0-553-21311-3",
                "price": 8.99
            },
            {
                "category": "fiction",
                "author": "J. R. R. Tolkien",
                "title": "The Lord of the Rings",
                "isbn": "0-395-19395-8",
                "price": 22.99
            }
        ],
        "bicycle": {
            "color": "red",
            "price": 19.95
        }
    },
    "expensive": 10
}
JsonPath (click link to try)Result
$.store.book[*].authorThe authors of all books
$..authorAll authors
$.store.*All things, both books and bicycles
$.store..priceThe price of everything
$..book[2]The third book
$..book[-2]The second to last book
$..book[0,1]The first two books
$..book[:2]All books from index 0 (inclusive) until index 2 (exclusive)
$..book[1:2]All books from index 1 (inclusive) until index 2 (exclusive)
$..book[-2:]Last two books
$..book[2:]Book number two from tail
$..book[?(@.isbn)]All books with an ISBN number
$.store.book[?(@.price < 10)]All books in store cheaper than 10
$..book[?(@.price <= $['expensive'])]All books in store that are not "expensive"
$..book[?(@.author =~ /.*REES/i)]All books matching regex (ignore case)
$..*Give me every thing
$..book.length()The number of books


Posted by 김용환 '김용환'


npm을 설치하지 않고 play 2.6에 reactjs를 사용해봤다.


 http://ticofab.io/react-js-tutorial-with-play_scala_webjars/ 링크를 기반으로 


https://github.com/knight76/play26-scala-reactjs-example 예제를 추가했다.


(jquery, bootstrap 연동은 확실히 쉽다)





배운 점(고마운 점)


1. java 커뮤니티에서 web framework를 jar로 묶어둔 webjars 때문에 개발이 엄청 편해졌다. !!


java8의 ScriptEngine, JShell이 엄청 좋아진 것 같다.




2. scala(play) 세계는 reactjs 레퍼런스가 많이 없지만 해볼만한다. 



3. play2의 twirl template engine은 엄청 쓸만하다. 


Posted by 김용환 '김용환'


play framework(scala)에 reactjs를 실행시킬려면 sbt-reactjs를 사용해야 한다.

addSbtPlugin("com.github.ddispaltro" % "sbt-reactjs" % "0.6.8")


이 때 sbt version 은 1.0을 사용할 수 없다. 즉 sbt-reactjs를 못찾는다는 문구가 나온다. 


sbt.version=0.13.15



그래서 sbt.version을 1.0에서 0.13으로 내리니. 다음과 같은 에러가 많이 발생한다.



Error wrapping InputStream in GZIPInputStream: java.util.zip.ZipException: Not in GZIP format

at sbt.ErrorHandling$.translate(ErrorHandling.scala:10)

at sbt.WrapUsing.open(Using.scala:34)

at sbt.Using.apply(Using.scala:23)

at sbt.IO$$anonfun$gzipFileIn$1.apply(IO.scala:877)

at sbt.IO$$anonfun$gzipFileIn$1.apply(IO.scala:876)

at sbt.Using.apply(Using.scala:24)

at sbt.IO$.gzipFileIn(IO.scala:876)

at sbt.Sync$.readUncaught(Sync.scala:88)

at sbt.Sync$.readInfo(Sync.scala:84)

at sbt.Sync$$anonfun$apply$1.apply(Sync.scala:28)

at sbt.Sync$$anonfun$apply$1.apply(Sync.scala:22)

at sbt.Defaults$$anonfun$copyResourcesTask$1.apply(Defaults.scala:948)

at sbt.Defaults$$anonfun$copyResourcesTask$1.apply(Defaults.scala:944)

at scala.Function4$$anonfun$tupled$1.apply(Function4.scala:35)

at scala.Function4$$anonfun$tupled$1.apply(Function4.scala:34)

at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)

at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)

at sbt.std.Transform$$anon$4.work(System.scala:63)

at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)

at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)

at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)

at sbt.Execute.work(Execute.scala:237)

at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228)

at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228)

at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)

at sbt.CompletionService$$anon$2.call(CompletionService.scala:28)

at java.util.concurrent.FutureTask.run(FutureTask.java:266)

at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)

at java.util.concurrent.FutureTask.run(FutureTask.java:266)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)

at java.lang.Thread.run(Thread.java:745)

Caused by: java.util.zip.ZipException: Not in GZIP format

at java.util.zip.GZIPInputStream.readHeader(GZIPInputStream.java:165)

at java.util.zip.GZIPInputStream.<init>(GZIPInputStream.java:79)

at sbt.Using$$anonfun$gzipInputStream$1.apply(Using.scala:84)

at sbt.Using$$anonfun$gzipInputStream$1.apply(Using.scala:84)

at sbt.Using$$anon$1.openImpl(Using.scala:51)

at sbt.WrapUsing$$anonfun$open$2.apply(Using.scala:34)

at sbt.ErrorHandling$.translate(ErrorHandling.scala:10)

at sbt.WrapUsing.open(Using.scala:34)

at sbt.Using.apply(Using.scala:23)

at sbt.IO$$anonfun$gzipFileIn$1.apply(IO.scala:877)

at sbt.IO$$anonfun$gzipFileIn$1.apply(IO.scala:876)

at sbt.Using.apply(Using.scala:24)

at sbt.IO$.gzipFileIn(IO.scala:876)

at sbt.Sync$.readUncaught(Sync.scala:88)

at sbt.Sync$.readInfo(Sync.scala:84)

at sbt.Sync$$anonfun$apply$1.apply(Sync.scala:28)

at sbt.Sync$$anonfun$apply$1.apply(Sync.scala:22)

at sbt.Defaults$$anonfun$copyResourcesTask$1.apply(Defaults.scala:948)

at sbt.Defaults$$anonfun$copyResourcesTask$1.apply(Defaults.scala:944)

at scala.Function4$$anonfun$tupled$1.apply(Function4.scala:35)

at scala.Function4$$anonfun$tupled$1.apply(Function4.scala:34)

at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)

at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)

at sbt.std.Transform$$anon$4.work(System.scala:63)

at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)

at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:228)

at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)

at sbt.Execute.work(Execute.scala:237)

at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228)

at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:228)

at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)

at sbt.CompletionService$$anon$2.call(CompletionService.scala:28)

at java.util.concurrent.FutureTask.run(FutureTask.java:266)

at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)

at java.util.concurrent.FutureTask.run(FutureTask.java:266)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)

at java.lang.Thread.run(Thread.java:745)

[error] (compile:copyResources) Error wrapping InputStream in GZIPInputStream: java.util.zip.ZipException: Not in GZIP format





그래서 프로젝트 홈에서 다음 커맨드로 target 디렉토리의 모든 소스를 삭제하니. 잘 동작한다. 


find . -name target -exec rm -r "{}" \;




그런데. org.webjars.npm#minimatch 버전을 다운을 받을 수 없다고 나온다. 



plugin.sbt 파일에 다음을 추가해서 컴파일 제외시키고.. 

addSbtPlugin("com.github.ddispaltro" % "sbt-reactjs" % "0.6.8" 
                        exclude ("org.webjars.npm", "minimatch"))



build.sbt에 minimatch 라이브러리 3.0.0를 강제로 의존성을 갖게 한다.

dependencyOverrides += "org.webjars.npm" % "minimatch" % "3.0.0"


잘 동작한다...





Posted by 김용환 '김용환'


play에 webjars를 포함시켜 보니. 이거 물건이다. 

Java의 jars와 JShell을 최대한 활용하니. npm 설치가 필요없다. 


https://github.com/webjars/webjars-play를 참고하길 바란다.


https://github.com/webjars/webjars-play/tree/master/test-project 프로젝트에 다음 sbt를 사용하니 잘 동작한다. 

"org.webjars" %% "webjars-play" % "2.6.2",
"org.webjars" % "bootstrap" % "3.1.1-1" exclude("org.webjars", "jquery"),
"org.webjars" % "jquery" % "3.2.1",
"org.webjars" % "webjars-locator" % "0.32-1",
"org.webjars" % "requirejs" % "2.3.5",
"org.webjars" % "marked" % "0.3.2-1",


Posted by 김용환 '김용환'



하둡이나 스파크에 data locality 라는 단어가 종종 나오는데.. 의미는..


데이터 지역성은 계산하기 위해 데이터를 이동하는 것이 아니라 데이터를 그대로 두고 계산을 이동시키는 개념이다.


빅 데이터를 계산하기 위해 데이터를 이동(move)를 최대한 줄여


시스템 쓰루풋(throughput)과 혼잡도를 늦추게 하는 것이다. 


따라서 통신 대역폭이 당연히 줄어들고 성능은 늘어난다.

Posted by 김용환 '김용환'


scala와 같은 함수형 언어에서 =>를 많이 사용한다. 이를 영어로 어떻게 말하냐면..

right arrow라고 한다. (우리도 보통 오른쪽 화살표라고 하듯이)


또한 뚱뚱한 화살표(fat arrow) 또는 로켓(rocket)이라고 부르는 것 같다.



Posted by 김용환 '김용환'


elasticsearch의 5.x에는 기존의 무거웠던 from+size와 scroll api를 대체하는 search_after가 있다. 

전에 scroll을 써보면서 얼마나 무겁고,메모리가 이슈였다.(state기반)

search_after는 live cursor를 제공하면서 stateless이기 때문에 성능을 최적화할 수 있는 것 같다.


https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-search-after.html#search-request-search-after


Pagination of results can be done by using the from and size but the cost becomes prohibitive when the deep pagination is reached. The index.max_result_window which defaults to 10,000 is a safeguard, search requests take heap memory and time proportional to from + size. The Scroll api is recommended for efficient deep scrolling but scroll contexts are costly and it is not recommended to use it for real time user requests. The search_after parameter circumvents this problem by providing a live cursor. The idea is to use the results from the previous page to help the retrieval of the next page.





search_after를 사용해 특정 시간 범위의 HTTP라는 값을 찾고 pagination을 하고 싶은 때의 예제이다.


처음 시도할 때는 search_after를 시도하지 않고 ordering을 잘해야 한다. 동일 시간대에 uid를 기준으로 진행한다.



$ curl log.google.io
:9200/log-2017.11.14/logstash/_search?pretty=true -d '{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "_all": "*HTTP*"
          }
        },
        {
          "range": {
            "@timestamp": {
              "gte": "2017-11-14T11:00:00+09:00",
              "lte": "2017-11-14T12:00:00+09:00"
            }
          }
        }
      ]
    }
  },
  "size":3,
  "sort":[
      {
          "@timestamp":{"order":"asc"},
          "_uid": { "order": "desc" }
      }
   ]

}'





확인하면 다음과 같은 결과가 나올 것이다. 



{
  "took" : 94,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "failed" : 0
  },
  "hits" : {
    "total" : 488876,
    "max_score" : null,
    "hits" : [
      {
        "_index" : "log-2017.11.14",
        "_type" : "logstash",
        "_id" : "log-1d8a4f45-a15f-41bf-95f2-399c88844b60",
        "_score" : null,
        "_source" : {
          "pid" : "13434",
          "severity" : "INFO",
          "ident" : "nova.metadata.wsgi.server",
          "message" : "[-] 1.1.1.1 \"GET /latest/meta-data/ami-id HTTP/1.1\" status: 200 len: 129 time: 0.0031130",
          "hostname" : "eastzone-pg1-api001",
          "node" : "openstack_control",
          "type" : "nova_api",
          "phase" : "pg1",
          "@timestamp" : "2017-11-14T11:00:00+09:00",
          "_uuid" : "log-1d8a4f45-a15f-41bf-95f2-399c88844b60",
        },
        "sort" : [
          1510624800000,
          "logstash#log-1d8a4f45-a15f-41bf-95f2-399c88844b60"
        ]
      },
      {
        "_index" : "log-2017.11.14",
        "_type" : "logstash",
        "_id" : "log-ae067d5d-0d90-4414-ad6c-1c7cf286f95c",
        "_score" : null,
        "_source" : {
          "pid" : "4386",
          "severity" : "INFO",
          "ident" : "nova.osapi_compute.wsgi.server",
          "message" : "[-] 10.197.12.118,10.60.19.248 \"GET /v2/1ba72e3bdbe8491ba851f2f9fb4eb6f1/servers/a6c14368-1816-4908-8cab-0b97fed31f40/ips HTTP/1.1\" status: 401 len: 297 time: 0.0048220",
          "hostname" : "eastzone-pg1-api004",
          "node" : "openstack_control",
          "type" : "nova_api",
          "phase" : "pg1",
          "@timestamp" : "2017-11-14T11:00:00+09:00",
          "_uuid" : "log-ae067d5d-0d90-4414-ad6c-1c7cf286f95c",
        },
        "sort" : [
          1510624800000,
          "logstash#log-ae067d5d-0d90-4414-ad6c-1c7cf286f95c"
        ]
      },
      {
        "_index" : "log-2017.11.14",
        "_type" : "logstash",
        "_id" : "log-9ddf30a2-cfc1-4b76-b50b-5f4bcae02a94",
        "_score" : null,
        "_source" : {
          "pid" : "4431",
          "severity" : "INFO",
          "ident" : "nova.metadata.wsgi.server",
          "message" : "[-] 10.60.34.9,10.60.19.248 \"GET /latest/meta-data/public-ipv4 HTTP/1.1\" status: 200 len: 116 time: 0.0032690",
          "hostname" : "eastzone-pg1-api004",
          "node" : "openstack_control",
          "type" : "nova_api",
          "phase" : "pg1",
          "@timestamp" : "2017-11-14T11:00:00+09:00",
          "_uuid" : "log-9ddf30a2-cfc1-4b76-b50b-5f4bcae02a94",
        },
        "sort" : [
          1510624800000,
          "logstash#log-9ddf30a2-cfc1-4b76-b50b-5f4bcae02a94"
        ]
      }
    ]
  }




맨 마지막 uuid를 기반으로 uid를 생성한다. uid는 type과 uuid를 합친 것이다. (디폴트로 uuid는 사용할 수 없다. 검색안되고, 인덱스도 안됨)



$ curl log.google.io:9200/log-2017.11.14/logstash/_search?pretty=true -d '{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "_all": "*HTTP*"
          }
        },
        {
          "range": {
            "@timestamp": {
              "gte": "2017-11-14T11:00:00+09:00",
              "lte": "2017-11-14T12:00:00+09:00"
            }
          }
        }
      ]
    }
  },
  "search_after": ["1510624800000", "logstash#log-9ddf30a2-cfc1-4b76-b50b-5f4bcae02a94"],
  "size":3,
  "sort":[
      {
          "@timestamp":{"order":"asc"},
          "_uid": { "order": "desc" }
      }
   ]

}'





결과 잘 나옴.. 







Posted by 김용환 '김용환'

[docker] ipv6 지원

docker 2017.11.24 10:55


docker에서는 ipv6를 기본으로 지원하지 않고, --ipv6 옵션을 줘야 활성화된다.


https://docs.docker.com/engine/userguide/networking/default_network/ipv6/


By default, the Docker daemon configures the container network for IPv4 only. You can enable IPv4/IPv6 dualstack support by running the Docker daemon with the --ipv6 flag. Docker will set up the bridge docker0 with the IPv6 link-local address fe80::1.

Posted by 김용환 '김용환'

[play2] template 예시

scala 2017.11.23 16:59


play2의 template(twirl template engine)을 사용하는 예제는 다음 링크에 있지만.. 조금 잘 눈에 안 들어온다.


https://www.playframework.com/documentation/2.6.x/ScalaTemplates




코드로 간단히 표현하면 다음과 같다. 


Controller는 다음과 같다. 

def index= Action { implicit request =>
val seq = Seq("Samuel", "Hardy", "Dennis", "Issac")
Ok(views.html.update(new java.util.Date, seq)) }


update.scala.html은 다음 과 같다. (template paramter, template content로 나뉘어져 있다)

@(date: java.util.Date, seq: Seq[String])

<html>
<body>
<h1>Today : @date.toString!</h1>
<ul>
<li><code>List</code>: @List(1,2,3)</li>
<li><code>Seq</code>: @Seq("a", "b", "c")</li>
<li><code>Some</code>: @Some("foo")</li>
<li><code>None</code>: @None</li>
<li><code>Left</code>: @Left("foo")</li>
<li><code>Right</code>: @Right("bar")</li>
</ul>
<ul>
@seq.map { order =>
<li>@order.toLowerCase</li>
}
</ul>
</body>
</html>


결과는 깔끔하게 잘 나온다. 



Today : Thu Nov 23 19:52:04 KST 2017!

  • List: 123
  • Seq: abc
  • Some: foo
  • None:
  • Left: Left(foo)
  • Right: Right(bar)
  • samuel
  • hardy
  • dennis
  • issac






이번엔 주석과 @display 이다. 이것은 현재 template안에서 어떻게 표현하고 싶은지 보여줄 기능이라 하겠다.


@**************************
* import *
***************************@
@display(date: java.util.Date) = {
@date.toString (@date.hashCode())
}
<ul>
<li>@display(date)</li>
</ul>


결과는 다음과 같다.


  • Thu Nov 23 19:15:33 KST 2017 (-403555210)



변수의 값이 정의되어 있다면 다음을 출력한다.
@defining(seq) { name =>
<ul><li>Hello @name </li></ul>
}

결과는 다음과 같다.
  • Hello SamuelHardyDennisIssac



일반 변수를 정의하는 방식은 다음과 같다.
@name = {samuel}
@name

@{val name = "samuel"; name}


결과는 다음과 같다.


samuel






Posted by 김용환 '김용환'


elasticsearch에서 _id로 검색 하지만 실패한다. 

_id 필드는 기본적으로 인덱스도 저장도 하지 않는다.  따라서 _uid를 대신 사용해야 한다.



  "error" : {

    "root_cause" : [

      {

        "type" : "illegal_argument_exception",

        "reason" : "Fielddata is not supported on field [_id] of type [_id]"

      }

    ],

Posted by 김용환 '김용환'