netty학습을 위한 logback 설정

분류없음 2019.03.14 21:54 posted by dev.bistro


Netty를 학습하기 위해서 Debug 레벨로 로그를 보고 싶음.

모든 튜토리얼에서 처음에 나오는 클래스인 ServerBootstrap ( https://git.io/fjeuN ) 를 살펴보면  InternalLoggerFactory 클래스를 이용해서 인스턴스를 획득하는 것을 볼 수 있다. 해당 클래스는 abstract class 이고 다양한 로그 라이브러를 지원하고 있음을 알 수 있다.


그 중에서 slf4j - logback 콤보를 이용하기 위해 아래 디펜던시를 추가한다.


<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>


그리고 classpath에 아래와 같은 기본적인 logback.xml 을 설정한다

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}                      - %msg%n</pattern>
</encoder>
</appender>

<root level="DEBUG">
<appender-ref ref="console"/>
</root>
</configuration>


아래와 같은 콘솔 로그를 확인 할 수 있다.

 io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework
 io.netty.channel.MultithreadEventLoopGroup - -Dio.netty.eventLoopThreads: 24
 io.netty.util.internal.PlatformDependent0 - -Dio.netty.noUnsafe: false
 io.netty.util.internal.PlatformDependent0 - Java version: 8
 io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.theUnsafe: available
 io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.copyMemory: available
 io.netty.util.internal.PlatformDependent0 - java.nio.Buffer.address: available
 io.netty.util.internal.PlatformDependent0 - direct buffer constructor: available
 io.netty.util.internal.PlatformDependent0 - java.nio.Bits.unaligned: available, true
 io.netty.util.internal.PlatformDependent0 - jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9
 io.netty.util.internal.PlatformDependent0 - java.nio.DirectByteBuffer.(long, int): available
 io.netty.util.internal.PlatformDependent - sun.misc.Unsafe: available
 io.netty.util.internal.PlatformDependent - -Dio.netty.tmpdir: /var/folders/sr/grtv_wmn3wj092488tqh7slr0000gn/T (java.io.tmpdir)
 io.netty.util.internal.PlatformDependent - -Dio.netty.bitMode: 64 (sun.arch.data.model)
 io.netty.util.internal.PlatformDependent - -Dio.netty.noPreferDirect: false
 io.netty.util.internal.PlatformDependent - -Dio.netty.maxDirectMemory: 3817865216 bytes
 io.netty.util.internal.PlatformDependent - -Dio.netty.uninitializedArrayAllocationThreshold: -1
 io.netty.util.internal.CleanerJava6 - java.nio.ByteBuffer.cleaner(): available
 io.netty.channel.nio.NioEventLoop - -Dio.netty.noKeySetOptimization: false
 io.netty.channel.nio.NioEventLoop - -Dio.netty.selectorAutoRebuildThreshold: 512
 io.netty.util.internal.PlatformDependent - org.jctools-core.MpscChunkedArrayQueue: available
 io.netty.channel.DefaultChannelId - -Dio.netty.processId: 78164 (auto-detected)
 io.netty.util.NetUtil - -Djava.net.preferIPv4Stack: false
 io.netty.util.NetUtil - -Djava.net.preferIPv6Addresses: false
 io.netty.util.NetUtil - Loopback interface: lo0 (lo0, 0:0:0:0:0:0:0:1%lo0)
 io.netty.util.NetUtil - Failed to get SOMAXCONN from sysctl and file /proc/sys/net/core/somaxconn. Default: 128
 io.netty.channel.DefaultChannelId - -Dio.netty.machineId: xx:1c:xx:ff:fe:00:00:09 (auto-detected)
 io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.level: simple
 io.netty.util.ResourceLeakDetector - -Dio.netty.leakDetection.maxRecords: 4
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numHeapArenas: 24
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numDirectArenas: 24
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.pageSize: 8192
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxOrder: 11
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.chunkSize: 16777216
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.tinyCacheSize: 512
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.smallCacheSize: 256
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.normalCacheSize: 64
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedBufferCapacity: 32768
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimInterval: 8192
 io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.useCacheForAllThreads: true
 io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled
 io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 65536
 io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384


1라인 : slf4j 를 로깅 프레임웍으로 쓰고 있는 것을 확인 
2라인 : 내 맥북의 사용가능 CPU가 12개 여서 24 ( https://git.io/fjez0 )


주의) 혹시나 java.lang.ClassNotFoundException: jdk.internal.misc.Unsafe 오류가 보인다면 Netty 4.11 을 사용하고 있는지 확인하고 4.12+ 로 버전 변경 한다 ( https://github.com/netty/netty/issues/6901 ) 


TAG logback, Netty


logstash 에는 이미 kafka input 모듈이 있기 때문에 쉽게 카프카 메시지를 엘라스틱에 저장할 수 있다.
( https://www.elastic.co/guide/en/logstash/current/plugins-inputs-kafka.html )

몇가지 주의할 점


1) 주문번호등을 Partition Key로 이용할 경우에, 데이터가 어느 파티션에 들어가 있는지, Offset은 얼마인지도 중요한 디버깅 요소가 된다. 이러한 데이터도 함께 elasticsearch 에 넣으려면 add_field를 이용한다.


우선 input 쪽에 아래의 설정을 추가하여 메타 정보를 사용할 수 있게 한다

input {

kafka {

decoreate_events => true

}

}


그리고 filter 영역에 아래처럼 추가하여 Kafka Message의 Offset, Partition, EventTime을 ES document에 추가한다.

filter {

mutate {

"partition" => "%{[@metadata][kafka][partition]}"

"offset" => "%{[@metadata][kafka][offset]}"

"eventtime" => "%{[@metadata][kafka][timestamp]}"

}

}



2)  logstash를 이용하여 데이터를 주입 받을 경우, Document의 생성시간은 logstash의 서버시간에 의존적이다. 하지만 Kafka Message를 모니터링 하기 위해서는 EventTime을 기반으로 index time을 설정하는 것이 편리했다.
아래처럼 Kafka Header에 존재하는 EventTime을 @timestamp 로 변경한다.

filter {

data {

match => ["eventtime", "UNIX_MS"]

target => "@timestmap"

}

}

이제 kibana에서 @timestamp로 index-pattern을 생성한다면, 그것은 kafka의 eventtime을 기반으로 보는것이다.


3) 카프카를 사용하다보면 실제로는 Avro Protocols을 사용하는 경우가 많다. 이 경우에는 외부 플러그인을 통해서 엘라스틱 서치로 데이터를 저장 할 수 있다.

https://github.com/revpoint/logstash-codec-avro_schema_registry 를 설치한다.

kafka input을 아래처럼 수정한다.

input {

kafka {

...

codec => avro_schema_registry {

endpoint => "http://registry:8081"

}

value_deserializer_class => "org.apache.kafka.common.serialization.ByteArrayDeserializer"

}

}


Java Optional에 대한 생각

분류없음 2018.09.04 13:12 posted by dev.bistro

모던 랭귀지 중에서 현업에서 써봤거나,  대충은 해봤던 언어는 Java Scala Groovy Kotlin Swift python go... 이중에서 Go는 원래 태생이 그래서 제외한다면 제일 구닥다리는 Java.

그 중에 Optional에 대한 사용은 항상 고민이 된다.


http://blog.joda.org/2015/08/java-se-8-optional-pragmatic-approach.html

https://stackoverflow.com/questions/31922866/why-should-java-8s-optional-not-be-used-in-arguments


1. Optional로 인스턴스 변수를 사용하지 말 것

2. null로 내부 private scope 클래스의 데이터 유무를 나타낸다. (첨언 : 자바에는 Null이 있어서 내부적으로 null이 Optional.empty를 표현할 수 있다. 이렇게 하고, 외부로 노출하는 getMethod에는 null이 아닌 empty를 보여주면 된다)

3. getter에는 사용

4. set메소드, 생성자에서는 사용하지 말 것

5. 다른 로직에서 Optional 에 따른 비즈니스 로직이 있을 때에는 사용



즉 Class 내에서 private variable로 Optional을 가지지 말라

장점으로는 

1. 이 객체는 Application 내에서 자주 사용될것이고 그 life-cycle동안 길것이다. 그에 반헤 Getter만 Optional이라면 그 라이프 사이클은 훨씬 짧아진다. GC에 대해 이로울 것이고

2. Optional 이 있다면 Serializable  할수 없다. (jackson-modules-java8도 결국엔 Optional.empty를 null로 만든다.)

3. setter나 생성자에서 Optional을 사용하는 것 자체가 경험에 비추어 유용하지 않았다. 



단점으로는 이렇게 생성되는 Class가  beans가 아니라는 것이다 (private variable + getter + setter로 이루어진) 

일부 IDE에서 문제가 발생할 수도 있으니 확인해봐야한다. 하지만 이런식으로 된다면 사용하는 쪽에서 null에

te 고민을 없앨 수 있다. 혹시나 Optional.get()을 호출하고 있다면 뭔가 이상하다고 생각할 것




TAG Java, Optional