2016년 12월 말 기준으로 최신 Grafana, InfluxDB, Telegraf을 설치해서 장비 중 일부에 서버를 설치해 실시간용으로 모니터링하려 한다. 

Cacti나 Nagios가 있지만, 실시간이라는 점에서는 Grafana, InfluxDB, Telegraf가 최고인듯 하다. 




* centos 6을 기준으로 설명한다.



influxDB는 mysql과 같지만, 좀 다르다.

- database 그대로 사용

- measurement : mysql의 테이블과 같은 개념

- tag : indexed column

- field : not indexed column


그리고, HTTP로 데이터 핸들링이 가능하다. (내가 정말 좋아하는 기능이다)





* influxDB 설치 (최신 1.1.1 설치)


$ export http_proxy=

$ export https_proxy=

$ wget --no-check-certificate https://dl.influxdata.com/influxdb/releases/influxdb-1.1.1.x86_64.rpm

$ sudo yum localinstall influxdb-1.1.1.x86_64.rpm

$ sudo service influxdb start

$ influx --version

InfluxDB shell version: 1.1.1


데몬을 확인하면 다음과 같이 떠 있는지 확인할 수 있다. 


/usr/bin/influxd -pidfile /var/run/influxdb/influxd.pid -config /etc/influxdb/influxdb.conf



influxDB 설정 파일은 /etc/influxdb/influxdb.conf에 존재한다. 


필요하면, 설정을 바꿔 재시작하면 된다. 

주요 디렉토리는 다음과 같다. 


"/var/lib/influxdb/meta" -- 메타 디렉토리 확인

"/var/lib/influxdb/data" -- 데이터 디렉토리 확인



기본 로그는 다음 디렉토리에 존재한다. 


/var/log/influxdb/influxd.log




정상적인지 동작하는 지 확인한다. 



$ influx

Visit https://enterprise.influxdata.com to register for updates, InfluxDB server management, and monitoring.

Connected to http://localhost:8086 version 1.1.1

InfluxDB shell version: 1.1.1

> show databases

name: databases

name

----

_internal


> create database story_mon

> use story_mon

Using database story_mon

> show measurements

> insert webapp,mon_type=os,metric=disk value=20

> show measurements

name: measurements

name

----

webapp


> select * from webapp

name: webapp

time metric mon_type value

---- ------ -------- -----

1482384136937122209 disk os 20


> insert webapp,mon_type=os,metric=disk value=21

> insert webapp,mon_type=os,metric=disk value=22

> select * from webapp

name: webapp

time metric mon_type value

---- ------ -------- -----

1482384136937122209 disk os 20

1482384165814969769 disk os 21

1482384170862239986 disk os 22




InfluxDB에 어드민 UI가 있다고 하지만, 1.1.0에서 deprecated가 되었고 조만간에 사라질 계획이다. (동작도 안되고 해서 쓰지 않는다)




* Telegraf 설치 (최신 1.1.2 설치)



$ wget https://dl.influxdata.com/telegraf/releases/telegraf-1.1.2.x86_64.rpm

$ sudo yum localinstall telegraf-1.1.2.x86_64.rpm



설정을 수정한다. 설정을 수정할 때, vi도 할 수 있지만, telegraf 커맨드를 통해 어떤 입력을 넣을 지 수정할 수 있다. 

cpu:kernel:mem:net:netstat:system 를 모니터링하고 싶다면 다음과 같이 진행할 수 있다. 



$ sudo sh -c 'telegraf -sample-config -input-filter cpu:kernel:mem:net:netstat:system  -output-filter influxdb > /etc/telegraf/telegraf.conf'


플러그인이 잘되어 있다. 


system쪽 설정은 다음을 활용할 수 있다.

https://github.com/influxdata/telegraf/tree/master/plugins/inputs/system


그 외 다른 장비도 모니터링할 수 있다. 

https://github.com/influxdata/telegraf/tree/master/plugins/inputs



telegraf.conf에서 전송될 influxDB로 수정하고, database 명을 확인한다. influxDB에 database 명이 없더라도 알아서 만들어준다. 


[[outputs.influxdb]]

  urls = ["http://mon.google.com.kr:8086"] # required

  database = "telegraf" # required

 


설정을 수정하면 재시작한다. 


$ sudo service telegraf start



로그 파일은 /var/log/telegraf  디렉토리에 존재하며 에러가 발생할 때만 로그가 발생한다. 로그 파일명(/var/log/telegraf/telegraf.log)은 다음과 같다. 




influxDB가 설정된 장비에서 제대로 데이터가 들어오는지 InfluxDB에서 확인한다. 


$ influx

Visit https://enterprise.influxdata.com to register for updates, InfluxDB server management, and monitoring.

Connected to http://localhost:8086 version 1.1.1

InfluxDB shell version: 1.1.1

> show databases;

name: databases

name

----

_internal

story_mon

mydb

telegraf


> use telegraf

Using database telegraf

> select *

ERR: error parsing query: found EOF, expected FROM at line 1, char 9

> show measurements

name: measurements

name

----

cpu

kernel

mem

net

netstat

system


> select * from cpu

name: cpu

time cpu host usage_guest usage_guest_nice usage_idle usage_iowait usage_irq usage_nice usage_softirq usage_steal usage_system usage_user

---- --- ---- ----------- ---------------- ---------- ------------ --------- ---------- ------------- ----------- ------------ ----------

1482389050000000000 cpu-total fox289 0 0 94.84913522987507 0.025249337212289454 0 00.05049867439702194 0 0.5681100874992648 4.507006692671534

1482389050000000000 cpu7 fox289 0 0 97.6814516446267 0 0 00.10080645165430842 0 0.403225806470541 1.8145161294108199

1482389050000000000 cpu1 fox289 0 0 98.28801609601521 0








[[추가 내용]]


참고로 nginx 모니터링도 가능하다. 



모니터링 agent에도 nginx도 사용할 수 있다. 

telegraf.conf의 nginx 에서는 http://localhost/status를 기본적으로 참조하게 되어 있지만, 시스템 환경처럼 변경할 수 있다. nginx 설정에서 localhost에서 nginx_status를 접근할 수 있도록 allow설정을 추가/변경한다.


  urls = ["http://localhost/nginx_status"]



테스트는 다음처럼 진행하고 문제가 없다면 telegraf 데몬을 재시작한다. 


$ telegraf -config telegraf.conf -input-filter nginx -test  

* Plugin: inputs.nginx, Collection 1

> nginx,host=fox289,port=80,server=localhost accepts=238194937i,active=43i,handled=238194937i,reading=0i,requests=249173634i,waiting=29i,writing=7i 1482390555000000000


$ sudo service telegraf restart



참고로 redis도 모니터링가능하다. 

https://github.com/influxdata/telegraf/tree/master/plugins/inputs/redis


설정을 다음처럼 추가하고 테스트해볼 수 있다. 


$ sudo sh -c 'telegraf -sample-config -input-filter cpu:kernel:mem:net:netstat:system:redis  -output-filter influxdb > telegraf.conf'

$ telegraf -config telegraf.conf -input-filter redis -test

* Plugin: inputs.redis, Collection 1

> redis_keyspace,database=db0,host=x11231321,port=6379,replication_role=master,server=localhost avg_ttl=0i,expires=0i,keys=2053i 1482478100000000000

> redis,host=abc.google.io,port=6379,replication_role=master,server=localhost aof_current_rewrite_time_sec=-1i,aof_enabled=0i,aof_last_bgrewrite_status="ok",aof_last_rewrite_time_sec=-1i,aof_last_write_status="ok",aof_rewrite_in_progress=0i,aof_rewrite_scheduled=0i,blocked_clients=0i,client_biggest_input_buf=0i,client_longest_output_list=0i,clients=1528i,connected_slaves=0i,evicted_keys=8857i,expired_keys=57137i,instantaneous_ops_per_sec=25347i,keyspace_hitrate=0.9228515982739088,keyspace_hits=28164842i,keyspace_misses=2354520i,latest_fork_usec=0i,loading=0i,lru_clock=6083091i,master_repl_offset=0i,mem_fragmentation_ratio=2.15,pubsub_channels=0i,pubsub_patterns=0i,rdb_bgsave_in_progress=0i,rdb_changes_since_last_save=105450019680i,rdb_current_bgsave_time_sec=-1i,rdb_last_bgsave_status="ok",rdb_last_bgsave_time_sec=-1i,rdb_last_save_time=1409638653i,rdb_last_save_time_elapsed=72839446i,rejected_connections=0i,repl_backlog_active=0i,repl_backlog_first_byte_offset=0i,repl_backlog_histlen=0i,repl_backlog_size=1048576i,sync_full=0i,sync_partial_err=0i,sync_partial_ok=0i,total_commands_processed=2119609996923i,total_connections_received=1101319i,uptime=72839446i,used_cpu_sys=12417264,used_cpu_sys_children=0,used_cpu_user=4852340,used_cpu_user_children=0,used_memory=70373856i,used_memory_lua=33792i,used_memory_peak=11811160000i,used_memory_rss=151429120i 1482478100000000000


$ sudo service telegraf restart







* UI 대시보드인 grafana를 설치


$ sudo yum install https://grafanarel.s3.amazonaws.com/builds/grafana-4.0.2-1481203731.x86_64.rpm 



$ sudo service grafana-server start


Starting Grafana Server: ...                               [  OK  ]

설정은 /etc/grafana/grafana.ini 에 있다. 기본 포트는 3000이고, http://서버:3000로 접근한다. 



influx DB 연동은 방법을 Data sources를 클릭 후 연동한다.

http://docs.grafana.org/datasources/influxdb/


정상적이면 “Data source is working”이 화면이 보인다. 


Graph 관련 작업은 다음 링크(http://docs.grafana.org/reference/graph/)을 참고한다. 조금만 해보면 하는 방법을 터득한다.



드디어 grafana로 쉽게 만들었다. 








이 상태로 두면, influxDB는 언제가 디스크가 찰 수 있어서, 데이터 보관(또는 삭제) 정책이 필요하다. 

influxDB는 실시간(time series) 전문 솔루션이라 이에 대한 정책을 찾아보니 RP(Retention Policy)라 한다. 



> create retention policy three_days on telegraf duration 3d replication 1 default



> show retention policies on telegraf

name duration shardGroupDuration replicaN default

---- -------- ------------------ -------- -------

autogen 0s 168h0m0s 1 false

three_days 72h0m0s 24h0m0s 1 true








참조 

https://docs.influxdata.com/influxdb/v1.1/introduction/getting_started/

https://github.com/influxdata/telegraf/tree/master/

https://docs.influxdata.com/influxdb/v0.9/guides/downsampling_and_retention/






* 추가로...



java 모니터링은 jolokia나 jvmstatd profiler가 존재하는데. 이 방식은 재시작을 해야 해서, 빼고 놓고가 불편하다.. 대용량이 아닌 서비스에서는 쉽게 쓸 수 있을 것이다. 



[jolokia]


[[jolokia.servers]]

  name = "as-service-1"

  host = "127.0.0.1"

  port = "8080"


[[jolokia.servers]]

  name = "as-service-2"

  host = "127.0.0.1"

  port = "8180"


[[jolokia.metrics]]

  name = "heap_memory_usage"

  jmx  = "/java.lang:type=Memory/HeapMemoryUsage"

  pass = ["used", "max"]

The collected metrics will be:

jolokia_heap_memory_usage name=as-service-1,host=127.0.0.1,port=8080 used=xxx,max=yyy

jolokia_heap_memory_usage name=as-service-2,host=127.0.0.1,port=8180 used=vvv,max=zzz




jolokia는 특별히 http 서버(웹서버) 관점으로 개발되었있다. 


https://github.com/influxdata/telegraf/tree/master/plugins/inputs/jolokia





(추후 나는 이 방식을 다른 방식으로 만들 예정, telegraf대신 직접 influxDB HTTP api 호출을 써서 쉽게 저장하는 방식이 나을 듯하다. 대용량 시스템에서 javaagent는 부하를 많이 먹는 경우가 많을 뿐더러 개인적으로 재시작 자체를 별로 좋아하지 않는다. ㅡ.ㅡ;;)


따라서, 다른 방식으로 접근했다. 


http://knight76.tistory.com/entry/grafana-%EC%9E%90%EB%B0%94-%EB%AA%A8%EB%8B%88%ED%84%B0%EB%A7%81




(점점, 예전에는 이런 거 만드는 재미로 살았는데. 아저씨들의 시대는 가는 것 같다... ㅠㅠ)





###추가.

cassandra3도 grafana로 모니터링할 수 있다. (opscenter는 3.0부터 상용화버전만 지원한다)



jolokia를 추가한다.

  

$ sudo mv /tmp/jolokia-jvm-1.3.5-agent.jar /usr/share/cassandra/lib/jolokia-jvm-1.3.5-agent.jar

  

$ sudo vi /etc/cassandra/conf/cassandra-env.sh

 ..

[ -e "$CASSANDRA_HOME/lib/jolokia-jvm-1.3.5-agent.jar" ] &&

  JVM_OPTS="$JVM_OPTS -javaagent:$CASSANDRA_HOME/lib/jolokia-jvm-1.3.5-agent.jar"
..

LOCAL_JMX=no


  JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.port=$JMX_PORT"

  JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.rmi.port=$JMX_PORT"

  JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl=false"

  JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=false"


  

  

테스트는 다음과 같이 진행한다. 


$ curl localhost:8778/jolokia/read/java.lang:type=Memory/HeapMemoryUsage

{"request":{"mbean":"java.lang:type=Memory","attribute":"HeapMemoryUsage","type":"read"},"value":{"init":2063597568,"committed":2012151808,"max":2012151808,"used":125159288},"timestamp":1490252927,"status":200}




모니터링 정보는 다음과 같다. 


https://github.com/rhuss/jolokia/blob/master/src/site/resources/templates/jolokia-access.xml

http://docs.datastax.com/en/archived/cassandra/3.x/cassandra/operations/opsMonitoring.html




telegraf는 다음과 같이 설정한다. https://raw.githubusercontent.com/wavefrontHQ/integrations/master/cassandra3/telegraf/10-cassandra.conf도 참조할 수 있다. 나는 성능 모니터링 요소를 많이 추가했다.. 카산드라 쉽게 볼 녀석이 아니다. ㅠ



$ sudo vi /etc/telegraf/telegraf.conf

 

urls = ["http://장비_ip:8086"] 

database = "telegraf" 


 

# Cassandra - Place this file in /etc/telegraf/telegraf.d

[[inputs.cassandra]]

  context = "/jolokia/read"

  servers = [":8778"]

  metrics = [ "/java.lang:type=GarbageCollector,name=ConcurrentMarkSweep/CollectionTime",

"/java.lang:type=GarbageCollector,name=ConcurrentMarkSweep/CollectionCount",

"/java.lang:type=GarbageCollector,name=ParNew/CollectionTime",

"/java.lang:type=GarbageCollector,name=ParNew/CollectionCount",

"/java.lang:type=Memory/NonHeapMemoryUsage",

"/java.lang:type=Memory/HeapMemoryUsage",

"/org.apache.cassandra.net:type=FailureDetector",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=LiveSSTableCount",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=SSTablesPerReadHistogram",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=ReadLatency",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=WriteLatency",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=RangeLatency",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=MemtableOnHeapSize",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=MemtableSwitchCount",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=MemtableLiveDataSize",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=MemtableColumnsCount",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=MemtableOffHeapSize",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=BloomFilterFalsePositives",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=BloomFilterFalseRatio",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=BloomFilterDiskSpaceUsed",

"/org.apache.cassandra.metrics:type=ColumnFamily,name=BloomFilterOffHeapMemoryUsed",


"/org.apache.cassandra.metrics:type=Cache,scope=KeyCache,name=Hits",

"/org.apache.cassandra.metrics:type=Cache,scope=KeyCache,name=Requests",

"/org.apache.cassandra.metrics:type=Cache,scope=KeyCache,name=Entries",

"/org.apache.cassandra.metrics:type=Cache,scope=KeyCache,name=Size",

"/org.apache.cassandra.metrics:type=Cache,scope=KeyCache,name=Capacity",

"/org.apache.cassandra.metrics:type=Cache,scope=RowCache,name=Hit",

"/org.apache.cassandra.metrics:type=Cache,scope=RowCache,name=Requests",

"/org.apache.cassandra.metrics:type=Cache,scope=RowCache,name=Entries",

"/org.apache.cassandra.metrics:type=Cache,scope=RowCache,name=Size",

"/org.apache.cassandra.metrics:type=Cache,scope=RowCache,name=Capacity",

"/org.apache.cassandra.metrics:type=Client,name=connectedNativeClients",

"/org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=TotalLatency",

"/org.apache.cassandra.metrics:type=ClientRequest,scope=Write,name=TotalLatency",

"/org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=Latency",

"/org.apache.cassandra.metrics:type=ClientRequest,scope=Write,name=Latency",

"/org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=Timeouts",

"/org.apache.cassandra.metrics:type=ClientRequest,scope=Write,name=Timeouts",

"/org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=Unavailables",

"/org.apache.cassandra.metrics:type=ClientRequest,scope=Write,name=Unavailables",

"/org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=Failures",

"/org.apache.cassandra.metrics:type=ClientRequest,scope=Write,name=Failures",

"/org.apache.cassandra.metrics:type=CommitLog,name=PendingTasks",

"/org.apache.cassandra.metrics:type=CommitLog,name=TotalCommitLogSize",

"/org.apache.cassandra.metrics:type=Compaction,name=CompletedTask",

"/org.apache.cassandra.metrics:type=Compaction,name=PendingTasks",

"/org.apache.cassandra.metrics:type=Compaction,name=TotalCompactionsCompleted",

"/org.apache.cassandra.metrics:type=Compaction,name=BytesCompacted",

"/org.apache.cassandra.metrics:type=Storage,name=Load",

"/org.apache.cassandra.metrics:type=Storage,name=Exceptions",

"/org.apache.cassandra.metrics:type=Table,keyspace=*,scope=*,name=LiveDiskSpaceUsed",

"/org.apache.cassandra.metrics:type=Table,keyspace=*,scope=*,name=TotalDiskSpaceUsed",

"/org.apache.cassandra.metrics:type=Table,keyspace=*,scope=*,name=ReadLatency",

"/org.apache.cassandra.metrics:type=Table,keyspace=*,scope=*,name=CoordinatorReadLatency",

"/org.apache.cassandra.metrics:type=Table,keyspace=*,scope=*,name=WriteLatency",

"/org.apache.cassandra.metrics:type=Table,keyspace=*,scope=*,name=ReadTotalLatency",

"/org.apache.cassandra.metrics:type=Table,keyspace=*,scope=*,name=WriteTotalLatency",

"/org.apache.cassandra.metrics:type=ThreadPools,path=internal,scope=CompactionExecutor,name=ActiveTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=internal,scope=AntiEntropyStage,name=ActiveTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=CounterMutationStage,name=PendingTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=CounterMutationStage,name=CurrentlyBlockedTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=MutationStage,name=PendingTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=MutationStage,name=CurrentlyBlockedTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=ReadRepairStage,name=PendingTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=ReadRepairStage,name=CurrentlyBlockedTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=ReadStage,name=PendingTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=ReadStage,name=CurrentlyBlockedTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=RequestResponseStage,name=PendingTasks",

"/org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=RequestResponseStage,name=CurrentlyBlockedTasks"

   ]


그외 참조 내용

https://www.datadoghq.com/blog/how-to-collect-cassandra-metrics/


https://medium.com/@mlowicki/cassandra-metrics-and-their-use-in-grafana-1f0dc33f9cca#.eewda2cp7



telegraf를 실행한다.

   

$ sudo service telegraf start








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

[shell script] Here Tag / Here Document  (0) 2016.12.28
paste 커맨드  (0) 2016.12.26
sudo에 따른 Permission denied 에러  (0) 2016.12.22
telnet 대신 nc 커맨드  (0) 2016.12.22
쉘에서 proxy 설정 모음  (0) 2016.12.22
Posted by '김용환'
,


아래 예시와 같이 sudo 와 > redirection을 사용할 때 permission 에러가 발생할 수 있다.


$ sudo telegraf -sample-config -input-filter cpu:kernel -output-filter influxdb > telegraf.conf

Permission denied



스크립트를 만들어서 실행하던지, 아니면 sudo로 감싼다. 


sudo sh -c 'telegraf -sample-config -input-filter cpu:kernel -output-filter influxdb > telegraf.conf'




Posted by '김용환'
,



최근에는 telnet 클라이언트를 잘 안쓰는 경향(기본 설치를 안 해줌)이 있다. 

이럴 때는 sudo yum install telnet으로 해서 telnet을 설치할 수도 있지만,...


nc 커맨드(기본 설치)를 활용하는 것도 좋을 것 같다. 


$ nc -z localhost 8086

Connection to localhost 8086 port [tcp/*] succeeded!

Posted by '김용환'
,


회사에서는 외부 리소스를 접근할 때는 보안(IP를 숨길 의도, 로깅의도) 상 proxy를 두기도 한다. 


export ftp_proxy=

export https_proxy=

export http_proxy=


주의할 점은 localhost를 쓰려고 할 때에도 proxy를 쓰려고 할 경우가 있으니. 이때는 안쓰는 방향으로 수정할 필요가 있다. 아래와 같이 사용한다. 


export no_proxy=localhost,127.0.0.1



https://wiki.archlinux.org/index.php/proxy_settings

Posted by '김용환'
,

[scala] reflection api 예시

scala 2016. 12. 21. 20:18


REPL에서 :type을 사용하면 타입을 볼 수 있다. 



scala> :type if (true) println("true")

Unit


scala> :type if (true) true

AnyVal



scala> :type false

Boolean


scala> :type "aaa"

String


scala> Map(1->"1", 2->"3") map (_._2)

res7: scala.collection.immutable.Iterable[String] = List(1, 3)


scala> :type Map(1->"1", 2->"3") map (_._2)

scala.collection.immutable.Iterable[String]



scala> :type -v Map(1->"1", 2->"3") map (_._2)

// Type signature

scala.collection.immutable.Iterable[String]


// Internal Type structure

TypeRef(

  TypeSymbol(

    abstract trait Iterable[+A] extends Traversable[A] with Iterable[A] with GenericTraversableTemplate[A,scala.collection.immutable.Iterable] with IterableLike[A,scala.collection.immutable.Iterable[A]] with Parallelizable[A,scala.collection.parallel.immutable.ParIterable[A]]


  )

  args = List(

    TypeRef(

      TypeSymbol(

        final class String extends Serializable with Comparable[String] with CharSequence


      )

    )

  )

)





스칼라 class에 대해 java 리플렉션(reflection)을 사용할 수 있다 .



trait Trait {
val m = 1
}

class Clazz extends Trait {
var m1 = 333333
var m2 = "aa"
}

object Execise extends App {
val clazz = classOf[Clazz]
val name = clazz.getName
println("name : " + name)

//println
val constructors = clazz.getConstructors
println("constructors : " + constructors.mkString(" "))

println
val methods = clazz.getMethods
println("methods : " + methods.mkString(" "))

println
val fields = clazz.getFields
println("fields : " + fields.mkString(" "))

println
val annotations = clazz.getAnnotations
println("annotations : " + annotations.mkString(" "))

println
val superClass = clazz.getSuperclass
println("superClass : " + superClass)

println
val typeParameters = clazz.getTypeParameters
println("typeParameters : " + typeParameters.mkString(" "))
}

결과는 다음과 같다. 



name : com.google.Clazz

constructors : public com.google.Clazz()


methods : public void com.google.Clazz.m1_$eq(int) public void com.google.Clazz.m2_$eq(java.lang.String) public int com.google.Clazz.m1() public java.lang.String com.google.Clazz.m2() public int com.google.SuperClazz.m() public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public final void java.lang.Object.wait() throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()


fields : 


\3 1aY8n"%QQ"

-!AC*va8 \1{u")QC1A(8jiz"a Aq!A!#ncU 1/5 QCC8-7b ARCAJ]RDqAA1$nc}#S-9}"F

1#AyIEBaA !BaA72A!9Aa

)AA73+1CA-A#BA+aM\4-

AA[1wC&Q'RN\4 =!Ca1QNM0%KF$"H r!aM!11Q!

1!!)


superClass : class com.google.SuperClazz



스칼라의 classOf의 리턴타입은 java.lang.Class이다.(컴파일러에서 제대로 생성된다)

def classOf[T]: Class[T] = null

또한, 결과를 통해 추측할 수 있겠지만, 일부 내용을 파싱하면서 에러난 부분이 발생했다. 아직 scala 클래스 객체를 완벽히 소화한 상태는 아니다. 이는 스칼라에서 쓸 수 있는 TypeTags를 활용하면 의미있는 정보를 얻을 수 있다. 





먼저 스칼라의 ClassTag이다. ClassTag를 사용하면 컴파일러가 암시 ClassTag를 생성한다. TypeTags의 약한 버전이다. 

import scala.reflect.ClassTag

case class Person(id: Int)

def makeArray[T: ClassTag] (elements: T*) = Array[T](elements: _*)

println(makeArray(1,2,3).mkString(" "))

println(makeArray("one", "two", "three").mkString(" "))

println(makeArray(1, "xxx").mkString(" "))

println(makeArray(Person(1), Person(2)).mkString(" "))

결과는 다음과 같다.


1 2 3

one two three

1 xxx

Person(1) Person(2)






다음은 TypeTag를 활용한 예시이다. 강력한 스칼라 reflection api이다. 

class SuperClazz {
val m = 1
}

class Clazz extends SuperClazz {
var m1 = 333333
var m2 = "aa"
}


import scala.reflect.runtime.universe._
def toType[T: TypeTag](t : T) = typeOf[T]

println(toType(1))
println(toType(false))
println(toType(new Clazz))
println(toType(List(1, "aa")))
println(toType(Seq(1, true, 3.14)))

val t = toType(2)
println(t.typeSymbol)
println(t.erasure)
println(t.typeArgs)
println(t.baseClasses)
println(t.companion)

println()
val t1 = toType(new Clazz)
println(t1.members)
println(t1.baseClasses)



결과는 다음과 같다.


Int

Boolean

com.google.Clazz

List[Any]

Seq[AnyVal]

class Int

Int

List()

List(class Int, class AnyVal, class Any)

Int.type


Scope{

  private[this] var m2: <?>;

  def m2_=: <?>;

  def m2: <?>;

  private[this] var m1: <?>;

  def m1_=: <?>;

  def m1: <?>;

  def <init>: <?>;

  val m: <?>;

  final def $asInstanceOf[T0](): T0;

  final def $isInstanceOf[T0](): Boolean;

  final def synchronized[T0](x$1: T0): T0;

  final def ##(): Int;

  final def !=(x$1: Any): Boolean;

  final def ==(x$1: Any): Boolean;

  final def ne(x$1: AnyRef): Boolean;

  final def eq(x$1: AnyRef): Boolean;

  final def notifyAll(): Unit;

  final def notify(): Unit;

  protected[package lang] def clone(): java.lang.Object;

  final def getClass(): java.lang.Class[_];

  def hashCode(): Int;

  def toString(): java.lang.String;

  def equals(x$1: Any): Boolean;

  final def wait(): Unit;

  final def wait(x$1: Long): Unit;

  final def wait(x$1: Long,x$2: Int): Unit;

  protected[package lang] def finalize(): Unit;

  final def asInstanceOf[T0]: T0;

  final def isInstanceOf[T0]: Boolean

}

List(class Clazz, class SuperClazz, class Object, class Any)





Posted by '김용환'
,

[mysql] auto increment 이슈

DB 2016. 12. 19. 20:09



mysql 의 auto increment 쪽은 약간 살펴봐야할 내용들이 있다.





auto increment 필드(예, id)를 가진 테이블에 insert into문을 사용할 때, auto increment 필드(예, i)가 순차적인 일련번호가 아닌 값이 나타나는 겨우가 발생했다. 



즉, insert into문을 사용할 때 일반적인 auto increment 필드는 1, 2, 3, 4, 5와 같은 순차적으로 늘어나는데, 1, 2, 3, 5 이렇게 나타날 수 있다. 



여러 원인이 있을 수 있다. 


1. insert ignore into 문을 사용하다가 statement문에 이슈가 발견되어 row가 insert되지 않아도 auto increment필드의 값이 증가될 수 있다.


2. insert into문이 포함된 transaction이 rollback 되는 경우 auto increment필드의 값이 증가될 수 있다.


3. concurrent한 insert into 문이 들어올 때 auto increment필드의 값이 증가될 수 있다.




따라서, auto increment 값을 신뢰해서 안되며 auto increment를 중요하게 쓸 것이라면, transaction에서는 사용하지 않는 것이 좋다. auto increment을 중요하게 쓸 계획이라면, innodb_autoinc_lock_mode을 알아야 한다. 

(mysql db에서 show variables 커맨드를 사용하면 확인할 수 있다)





mysql 5.0까지는 auto increment는 테이블 락이었지만, 5.1 부터는 테이블 락이 아닌 갭 락(gap lock)을 사용한다. 또한, mysql 5.0까지는 innodb_autoinc_lock_mode의 기본값이 0이었다가, mysql 5.1부터는 기본값이 1이 되었다. 




innodb_autoinc_lock_mode의 값 비교


0(tranditional)으로 설정하면, 테이블 단위의 락을 사용한다. 성능이 떨어진다. 

1(consecutive)로 설정하면, 단순한 insert into문에 락을 사용하지 않는다. bulk insert 문을 사용할 때 mutex(light weight lock)를 사용해 락을 사용한다.

2(interleaved)로 설정하면, 락을 사용하지 않는다. 빠르고 확장성이 좋지만, 복구가 어렵다. bulk insert 문을 사용할 때 엉망인 auto increment 값이 나타날 수 있다. 




innodb_autoinc_lock_mode의 값을 잘 보고 성능과 기능의 적당한 타협이 필요하다. 




참조 


http://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html#innodb-auto-increment-configurable


Posted by '김용환'
,


enum를 리스트로 얻는 방법이다.


우선 enum에서 각 entry를 읽어오려면, enum 클래스의 values() 메소드를 호출한다.



java8 이전과 이후로 나눠진다. java8은 stream(Stream.of)으로 처리하기 쉽다.



enum Direction {

north,

east,

south,

west

}




              for (Direction direction : Direction.values()) {

System.out.println(direction);

}

for (Direction direction : EnumSet.allOf(Direction.class)) {

System.out.println(direction);

}

Stream.of(Direction.values()).forEach(System.out::println);




enum에 생성자를 주어 따로 상태(필드) 값을 가질 때, enum 이름과 상태 값을 다음과 같이 얻을 수 있다. 



enum Directions {

north(10),

east(12),

south(13),

west(14);

private int no;

private Directions(int number) {

this.no = number;

}

public int getNumber() {

return no;

}

}


// java 8 이전

List<String> list = Lists.newArrayList();

for (Directions directions : Directions.values()) {

list.add(directions.name());

}

System.out.println(list);

List<Integer> list1 = Lists.newArrayList();

for (Directions directions : Directions.values()) {

list1.add(directions.getNumber());

}

System.out.println(list1);

// java 8 이후

         List<String> list3 = Stream.of(Direction.values())

    .map(Enum::name)

    .collect(Collectors.toList());

 System.out.println(list3);

  

 List list4 = Stream.of(Directions.values())

    .map(m -> m.getNumber())

    .collect(Collectors.toList());

 System.out.println(list4);

  

 

}



'java core' 카테고리의 다른 글

java9에서 사라지는 gc  (0) 2017.03.20
jnr  (0) 2017.02.27
[java] Map을 출력하기  (0) 2016.12.13
자바의 clone, guava의 Maps, Lists 사용  (0) 2016.12.01
JVM의 safepoint  (0) 2016.09.08
Posted by '김용환'
,


Json을 파싱할 수 있는 ObjectMapper 예시를 소개한다.  


많이 알려져 있지만, 자주 실수할 수 있는 내용과  좋은 팁을 소개한다. 





먼저, UnrecognizedPropertyException을 많이 발생시킬 수 있는 내용을 소개한다. 


아래는 무난하게 잘 동작하는 코드이다. 


import java.util.List;

import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.ObjectMapper;


public class JsonTest {

  @Test

  public void mapperObject() throws Exception {

  ObjectMapper mapper = new ObjectMapper();

  String jsonInString = "{\"event_code\":112233,\"messages\":[\"hello\",\"java\"],\"name\":\"x112x1121\"}";

  EventData eventData = mapper.readValue(jsonInString, EventData.class);

  System.out.println(eventData);

  }

}

 

class EventData {


  @JsonProperty("event_code")

  private int eventCode;


  @JsonProperty("name")

  private String name;


  @JsonProperty("messages")

  private List<String> messages;

}




이 코드가 정상적으로 잘 동작하려면, json이 EventData에 맞게 오지 않거나 최소한 해당 json 노드이외에는 전달되지 않는다는 보장이 있어야 한다. 


갑자기 json 문자열에 새로운 노드가 새로 추가되면, com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException이 발생한다.


String jsonInString = "{\"event_code\":112233,\"messages\":[\"hello\",\"java\"],\"name\":\"x112x1121\", \"emp\":\"samuel\"}";




예외 내용은 다음과 같다. 


com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "emp" (class com.kakao.story.util.EventData), not marked as ignorable (3 known properties: "event_code", "name", "messages"])

 at [Source: {"event_code":112233,"messages":["hello","java"],"name":"x112x1121", "emp":"samuel"}; line: 1, column: 77] (through reference chain: com.kakao.story.util.EventData["emp"])

at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:51)

at 





예외를 발생시키지 않고 안전하게 해결하려면 @JsonIgnoreProperties(ignoreUnknown = true)을 클래스 정의에 둔다. 다른 방법도 있지만, 이 방식이 아주 많이 사용되는 방식이다.



import java.util.List;

import org.junit.Test;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.ObjectMapper;


public class JsonTest {

@Test

public void mapperObject() throws Exception {

ObjectMapper mapper = new ObjectMapper();

String jsonInString = "{\"event_code\":112233,\"messages\":[\"hello\",\"java\"],\"name\":\"x112x1121\", \"emp\":\"samuel\"}";

EventData eventData = mapper.readValue(jsonInString, EventData.class);

System.out.println(eventData);

}

}

@JsonIgnoreProperties(ignoreUnknown = true)

class EventData {


@JsonProperty("event_code")

private int eventCode;


@JsonProperty("name")

private String name;


@JsonProperty("messages")

private List<String> messages;

}




참고로, 코드는 문제가 없어보이지만, org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "event_code" (Class com.kakao.story.util.EventData), not marked as ignorable 예외가 발생할 수 있다.


ObjectMapper 클래스의 fully qualified name을 유심히 살펴본다. 


import org.codehaus.jackson.map.ObjectMapper;


import java.util.List;

import org.junit.Test;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.DeserializationConfig;


public class JsonTest {

@Test

public void mapperObject() throws Exception {

ObjectMapper mapper = new ObjectMapper();

String jsonInString = "{\"event_code\":112233,\"messages\":[\"hello\",\"java\"],\"name\":\"x112x1121\", \"emp\":\"samuel\"}";

EventData eventData = mapper.readValue(jsonInString, EventData.class);

System.out.println(eventData);

}

}

@JsonIgnoreProperties(ignoreUnknown = true)

class EventData {


@JsonProperty("event_code")

private int eventCode;


@JsonProperty("name")

private String name;


@JsonProperty("messages")

private List<String> messages;

}



import com.fasterxml.jackson.databind.ObjectMapper를 사용해야지 org.codehaus.jackson.map.ObjectMapper를 사용하면 원인모를 미궁에 빠질 수 있다. 






또 다른 팁은 serialization시 null에 대한 핸들링이다. 아래 코드를 살펴본다.



import java.util.List;


import org.apache.commons.lang.builder.ToStringBuilder;

import org.apache.commons.lang.builder.ToStringStyle;

import org.junit.Test;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.ObjectMapper;


public class JsonTest {

@Test

public void mapperObject() throws Exception {

ObjectMapper mapper = new ObjectMapper();

EventData eventData = new EventData(); 

eventData.eventCode = 123123;

eventData.name = "wang.lee";

String jsonString = mapper.writeValueAsString(eventData);

System.out.println(jsonString);

}

}

@JsonIgnoreProperties(ignoreUnknown = true)

class EventData {


@JsonProperty("event_code")

int eventCode;


@JsonProperty("name")

String name;


@JsonProperty("messages")

List<String> messages;

@Override

public String toString() {

return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);

}

}


실행하면, 저장 안한 키의 값을 null로 serialization한다. 


{"event_code":123123,"name":"wang.lee","messages":null}


최적화된 json 정보를 유지하기 위해 null 정보를 뺄 수 있다.




Pojo 클래스에 @JsonInclude(Include.NON_NULL)을 추가하면, null 정보를 뺄 수 있다.

import java.util.List;


import org.apache.commons.lang.builder.ToStringBuilder;

import org.apache.commons.lang.builder.ToStringStyle;

import org.junit.Test;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

import com.fasterxml.jackson.annotation.JsonProperty;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.fasterxml.jackson.annotation.JsonInclude;

import static com.fasterxml.jackson.annotation.JsonInclude.Include;


public class JsonTest {

@Test

public void mapperObject() throws Exception {

ObjectMapper mapper = new ObjectMapper();

EventData eventData = new EventData(); 

eventData.eventCode = 123123;

eventData.name = "wang.lee";

String jsonString = mapper.writeValueAsString(eventData);

System.out.println(jsonString);

}

}

@JsonIgnoreProperties(ignoreUnknown = true)

//@JsonInclude(Include.NON_NULL)
@JsonInclude(value=Include.NON_ABSENT, content=Include.NON_EMPTY)

class EventData {


@JsonProperty("event_code")

int eventCode;


@JsonProperty("name")

String name;


@JsonProperty("messages")

List<String> messages;

@Override

public String toString() {

return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);

}

}


결과는 다음과 같다. 


{"event_code":123123,"name":"wang.lee"}



Java8과 guava에서도 쓸 수 있게 @JsonInclude(value=Include.NON_ABSENT, content=Include.NON_EMPTY)를 사용했다. java7이하에서는 @JsonInclude(Include.NON_NULL)를 사용할 수 있을 것이다. 





참고로 @JsonInclude에 대해 설명하면 다음과 같다.


Include.NON_ABSENT는 값이 null이나 java8, guava의 optional의 내부값이 null일 때 serialization하지 않는다.

Include.ALWAYS는 값과 상관없이 serialization한다.

Include.NON_DEFAULT는 값이 기본 설정과 다른 경우 속성이 serialization한다.

Include.NON_EMPTY는 값이 null이 아니거나 empty하지 않으면 serialization한다.

Include.NON_NULL는 null이 아닐 때만 serialzation한다.






Posted by '김용환'
,



많이 사용되는 쉘(shell)은 대표적으로 아래와 같다.


Stephen Bourne이 개발 한 오래된 Bourne 쉘, David Korn이 개발한 Korn 셸, 주로 리눅스 시스템에서 사용되는 Bash 쉘, Bill Joy가 개발한 C 쉘, 최근에 유행하는 각 쉘의 장점을 이용한 zsh이 있다. 




오늘 처음 안 내용이 있었는데, 내가 그렇게 좋아하는 쉘인 bash의 원 단어를 알았다!


bash shell이 Bourne again shell의 약자였다니!! 



bash 위키에 다음처럼 적혀 있다!!


배시 (Bash, Bourne-again shell, 본 어게인 셸)은 본 셸을 대체하는 자유 소프트웨어로서 GNU 프로젝트를 위해 브라이언 폭스(Brian Fox)가 작성한 유닉스 셸이다.[2][3] 1989년 발표되어 GNU 운영 체제와 리눅스맥 OS X 그리고 다윈 등 운영 체제의 기본 셸로 탑재되어 광범위하게 배포 되었다. 또한 DJGPP와 노벨 넷웨어에 의해 도스로 이식되었고 시그윈과 MinGW의 배포로 마이크로소프트 윈도로 이식되었다.






Posted by '김용환'
,

[공부] Hbase compaction

hbase 2016. 12. 16. 20:23


발로 번역해서 공부한 내용이다.

Hbase compaction을 공부하기 위해 여러 군데 흩어져 있는 내용을 짬뽕한 내용이다. 



hbase에 데이터가 바뀌면 commit log로 저장되는데, WAL(Write-Ahead Log)에 존재한다. 데이터는 이후 메모리 상의 Memstore에 저장된다. 메모리 상의 데이터가 설정된 최대값을 넘어서면 디스크에 HFile로 저장된다. 

이를 compaction(컴팩션)이라고 한다. 읽는 관점도 동일하게, 읽기 쉽게 저장되는 형태이기도 하다.



또한, 데이터가 변경/삭제되면서 이미 저장된 HFile에 필요 없는 데이터가 삭제 마킹(tombstone marking)이 되면서 지울 것은 지우고 합칠 것은 합치는 작업(compaction)을 진행한다.  하나의 리전(region)에서 한 저장소에서 HFile을 모아 병합한다. 


참고로, compaction은 두 가지 종류가 있다. 

- minor compaction : 작고 많은 HFile을 소수의 큰 HFile로 결합한다. compaction할 HFile 개수와 빈도를 제어할 수 있다. 특정 로우를 읽다가 디스크 읽기를 많이 하다가 전반적인 성능이 저하될 수 있기 때문이다...

- major compaction  : Region의 모든 Store 파일을 읽고 하나의 Store 파일에 저장한다.



CDH 4/HBase 0.96 이전의 기본 정책은 HFile의 목록을 살펴보고 전체 파일 수에 hbase.store.compaction.ratio를 곱한 값보다 작은 크기를 가진 파 중 첫 번째를 찾으려 한다. 만약 해당 파일을 찾으면 HFile과 그보다 작은 id를 가진 모든 파일이 컴팩션되도록 선택된다. 기본 정책은 RatioBasedCompactionPolicy이다(사실 이거 하나였다). 오래된 HFiles부터 시작된다. 하지만, 파일의 age와 size를 기반한 일부 가정이 잘못되었다.



CDH 5/HBase 0.96에서 https://issues.apache.org/jira/browse/HBASE-7516를 통해 사용자가 원하는 compaction(ExploringCompactionPolicy)을 진행할 수 있게 되었다. 모든 파일이 지정된 compaction 비율내에 있음을 보장한다. 그리고 compaction 비율 내에서 첫 번째 파일 집합만 선택하지 않고, 규칙을 위반하지 않은채 가장 IO가 적을 것 같은 것만 선택한다. compaction할 파일을 찾은 후 크기가 작은 파일이 대상이 된다. 그래서 IO 부하를 최소화하는 전략으로 변경했다. 벌크 로딩이 필요한 작업에서는 엄청난 성능 향상이 있다고 한다. 


ExploringCompactionPolicy을 사용하면 minor compaction이 효율적이라서 major compaction이 덜 발생해서 성능이 좋다고 한다. 그래서 기본 compaction 정책으로 사용되어 지금까지 쓰고 있다고 한다. 




ExploringCompactionPolicy에 대한 소스는 https://github.com/apache/hbase/blob/master/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/compactions/ExploringCompactionPolicy.java에 있으니 참고할 수 있다. 


ExploringCompactionPolicy이 확장되어 Stripe compaction(https://issues.apache.org/jira/browse/HBASE-7667)이 0.98, 0.99에 추가되었다. 또한 tiered-based compaction(https://issues.apache.org/jira/browse/HBASE-7055)은 개발 중이지만,2015년에 쓴 댓글이 마지막이라서 나올지 의문이다.



다음은 compaction 관련으로 Hortonworks에서 발표한 내용이다(동영상은 https://vimeo.com/69989292을 참조한다).


HBaseCon 2013: Compaction Improvements in Apache HBase from Cloudera, Inc.



참고로 https://hbase.apache.org/book.html#compaction을 참조하면, 

0.94의 RatioBasedCompactionPolicy 동작 순서와 0.96의 ExploringCompactionPolicy 동작 순서를 잘 알 수 있을 것이다. 그리고, hbase compaction property를 참조하면 hbase의 compaction를 더욱 이해할 수 있을 것 같다. 





참조 


https://hbase.apache.org/book.html#compaction


http://blog.cloudera.com/blog/2012/06/hbase-write-path/


http://blog.cloudera.com/blog/2013/12/what-are-hbase-compactions/


https://www.linkedin.com/pulse/hbase-minor-major-compaction-explained-jeetendra-gangele


http://engineering.vcnc.co.kr/2013/04/hbase-configuration/




참고


cassandra의 compaction 전략은 다음을 참조한다. hbase의 compaction은 전체 클러스터의 설정이라면, cassandra의 compaction설정은 테이블 단위이다.


http://knight76.tistory.com/entry/cassadra-compaction-%EC%A0%84%EB%9E%B5

Posted by '김용환'
,