spring boot에서 DB 접속할 때 hikari cp로 연결해서 사용할 수 있다.


JdbcTemplate.query로 DB 연결 후 실행하기 전에 boot가 초기화할 때부터 DB에 붙고 싶다면.

spring.datasource.initializer의 값을 true로 설정한다.

spring.datasource.initialize=true
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.hikari.jdbc-url=jdbc:oracle:thin:@1.1.1.1:1521:oradb
spring.datasource.hikari.username=username
spring.datasource.hikari.password=pssword
spring.datasource.hikari.connection-timeout=10000
spring.datasource.hikari.validation-timeout=10000


Posted by '김용환'
,



build.gradle에 최신 logstash-logback-encoder를 추가했다.


compile('net.logstash.logback:logstash-logback-encoder:5.0')




https://github.com/logstash/logstash-logback-encoder


Standard Fields

These fields will appear in every LoggingEvent unless otherwise noted. The field names listed here are the default field names. The field names can be customized (see Customizing Standard Field Names).

FieldDescription
@timestampTime of the log event. (yyyy-MM-dd'T'HH:mm:ss.SSSZZ) See customizing timezone.
@versionLogstash format version (e.g. 1) See customizing version.
messageFormatted log message of the event
logger_nameName of the logger that logged the event
thread_nameName of the thread that logged the event
levelString name of the level of the event
level_valueInteger value of the level of the event
stack_trace(Only if a throwable was logged) The stacktrace of the throwable. Stackframes are separated by line endings.
tags(Only if tags are found) The names of any markers not explicitly handled. (e.g. markers from MarkerFactory.getMarker will be included as tags, but the markers from Markers will not.)




기본 포맷은 다음과 같다.


{"@timestamp":"2018-03-26T16:09:15.692+09:00","@version":"1","message":"data","logger_name":"com.kakao.sauron.api.controller.TestController","thread_name":"http-nio-8080-exec-3","level":"INFO","level_value":20000}



보통 caller 관점에서의 line_number도 필요하는데.. 


includeCallerData를 추가하면 관련 정보가 나타난다. 





<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeCallerData>true</includeCallerData>
</encoder>




caller 쪽 데이터를 출력할 수 있다.


{"@timestamp":"2018-03-26T16:22:03.196+09:00","@version":"1","message":"data","logger_name":"com.kakao.sauron.api.controller.TestController","thread_name":"http-nio-8080-exec-1","level":"INFO","level_value":20000,"caller_class_name":"com.kakao.sauron.api.controller.TestController","caller_method_name":"helloWorld","caller_file_name":"TestController.java","caller_line_number":28}





너무 많이 나와서.. 필드 이름을 조금 줄일 수 있다. 


<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeCallerData>true</includeCallerData>
<fieldNames class="net.logstash.logback.fieldnames.ShortenedFieldNames"/>
</encoder>


{"@timestamp":"2018-03-26T16:28:58.886+09:00","@version":"1","message":"afdsafasfsad","logger":"com.kakao.sauron.api.controller.TestController","thread":"http-nio-8080-exec-1","level":"INFO","levelVal":20000,"caller":{"class":"com.kakao.sauron.api.controller.TestController","method":"helloWorld","file":"TestController.java","line":28}}






이정도가 제일 무난한 정도인 듯 하다.

<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="net.logstash.logback.encoder.LogstashEncoder">
<includeCallerData>true</includeCallerData>
<fieldNames class="net.logstash.logback.fieldnames.ShortenedFieldNames"/>
</encoder>
</appender>
<logger name="jsonLogger" additivity="false" level="DEBUG">
<appender-ref ref="consoleAppender"/>
</logger>
<root level="INFO">
<appender-ref ref="consoleAppender"/>
</root>




물론 여기에 더 해야할 점은 로그 파일의 용량, rotation을 적용해야 한다.

상용에서 사용하려면 더 신경쎠야 한다. 

Posted by '김용환'
,



gradle에서 특정 라이브러리는 사용하고 싶지 않다면.


configuration-> compile.exclude를 사용한다.




주의할 점은 group, module이 분류되어 있다는 점이다.



configurations {
compile.exclude group: "org.slf4j", module : "slf4j-log4j12"
compile.exclude group: "javax.servlet", module : "servlet-api"
}


Posted by '김용환'
,



Kafka에 zookeeper를 사용할 때. zookeeper 기본 설정 사용하다가 주키퍼에서 메모리 부족하고 난리도 아니다.


메모리 설정과 jmx 설정을 해주는 것이 좋다.




conf/java.env파일을 추가해 메모리 설정도 gc 로그 파일을 생성한다. 아래는 대략 기본 설정으로 보는게 좋다.


export JVMFLAGS="-Xmx3g -Xms3g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:CompileThreshold=200 -verbosegc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/var/lib/zookeeper/gc.log -XX:+UseGCLogFileRotation -XX:GCLogFileSize=10m -XX:NumberOfGCLogFiles=10"



zkServer.sh에 다음을 추가해 jmx 모니터링을 진행한다.


-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.port=8989 -Djava.rmi.server.hostname=my.remoteconsole.org





Posted by '김용환'
,




요즘에 https://github.com/square/okhttp은 점차 안드로이드 진영에서 인정받으면서 서버단에서 많이 쓰기 시작하고 있다. 

okhttp에 pooling이 내부적으로 되어 있어서, 편하게 사용가능하다.



OKHttpClient를 생성할 때 OKHttpClient.Builder를 호출하는데, 기본 생성자인 ConnectionPool을 생성하면서 5개 connection을 자동으로 생성한다.





https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/OkHttpClient.java



https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/OkHttpClient.java#L487



https://github.com/square/okhttp/blob/master/okhttp/src/main/java/okhttp3/ConnectionPool.java#L85


 

public ConnectionPool() {

this(5, 5, TimeUnit.MINUTES);

}



Posted by '김용환'
,


자바 개발할 때 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 '김용환'
,



apache commons에 CircularFifoBuffer 클래스가 있다. 



첫번째 예시 코드이다.



object Test extends App {

import org.apache.commons.collections.buffer.CircularFifoBuffer

val tasks = new CircularFifoBuffer(10)
tasks.add(1)
tasks.add(2)
tasks.add(5)
tasks.add(6)
println("max size : " + tasks.maxSize)
println("size: " + tasks.size)
println("--get")
println(tasks.get())
println(tasks.get())
println(tasks.get())
println(tasks.get())

println("--remove")
println(tasks.remove)
println(tasks.remove)
println(tasks.remove)
println(tasks.remove)

println("-- error")
println(tasks.remove)
}


가지고 있는 buffer보다 더 많이 remove하면 exception이 발생한다. 

get을 호출하면 계속 첫번째 엘리먼트를 리턴한다. 현재 값 확인할 때 좋을 듯 하다. 


max size : 10

size: 4

--get

1

1

1

1

--remove

1

2

5

6


at org.apache.commons.collections.buffer.BoundedFifoBuffer.remove(BoundedFifoBuffer.java:275)





두 번째 예시 코드이다. 


object Test extends App {

import org.apache.commons.collections.buffer.CircularFifoBuffer

val tasks = new CircularFifoBuffer(10)
tasks.add(1)
tasks.add(2)
tasks.add(3)
tasks.add(4)
tasks.add(5)
tasks.add(6)
tasks.add(7)
tasks.add(8)
tasks.add(9)
tasks.add(10)
tasks.add(11)
tasks.add(12)
println("max size : " + tasks.maxSize)
println("size: " + tasks.size)

for(i <- tasks.toArray) {
println(tasks.remove)
}
}


데이터를 계속 추가하면 처음에 들어온 데이터는 삭제된다. overflow가 발생되지 않는다. 



max size : 10

size: 10

3

4

5

6

7

8

9

10

11

12


Posted by '김용환'
,


젠킨스에서 반복적인 job은 job dsl 플러그인을 쓰면 편하다~

multi-job 대신 쓸만할 수도. 


https://wiki.jenkins.io/display/JENKINS/Job+DSL+Plugin



Configuration As Code: The Job DSL Plugin de Daniel Spilker




Posted by '김용환'
,




jenkins pipeline 플러그인은 사용하면 각 단계로 로그를 상세히 볼 수 있다!!!





어디 단계에서 문제가 발생하는지 보니까 완전 좋다!!



한국 분의 설치 내용

https://shortstories.gitbooks.io/studybook/content/jenkins_pipeline_c0bd_c9c8_ae30.html



결과 내용


참조 : https://www.cloudbees.com/blog/top-10-best-practices-jenkins-pipeline-plugin


 



Posted by '김용환'
,

java8 stream 사용시 주의할 점을 작성한 좋은 블로그 내용이 있어서 링크를 건다.



https://blog.jooq.org/2014/06/13/java-8-friday-10-subtle-mistakes-when-using-the-streams-api/



이 내용을 이용진 블로거님이 번역한 블로그이다.


http://leeyongjin.tistory.com/entry/Java8-%EC%9E%90%EB%B0%948-Stream-API-%EC%A3%BC%EC%9D%98%EC%82%AC%ED%95%AD


Posted by '김용환'
,