Jun 28, 2018 - Redis CLUSTER nodes.conf 파일 확인하며 장애 대응.

CLUSTER NODES

lod 현재 상황은 cluster nodes 라는 명령어를 통해 보일 수 있습니다.
CLUSTER NODES는 클러스터에 참여하고 있는 노드의 정보를 보는 명령으로,
접속한 서버가 가지고 있는 클러스터에 속한 다른 노들의 정보입니다.

clusternodeImg

  1. node-id : 노드를 유일하게 구분할 수 있는 ID며, 40개 문자로 구성되면 변경되지 않습니다.
  2. ip:port : 노드의 주소로 IP와 Port 입니다.
  3. flags : master, slave, fail?, fail, handshake, noaddr, noflags가 있고, 명령을 실행한 노드에 myself 라고 표시된다고 합니다.
  4. master : 슬레이브일 때 마스터 노드 ID가 표시되며, 마스터일 때는 “-“이 표시됩니다.
  5. ping-sent : myself가 다른 노드에 ping을 보낸 시각(Unix timestamp milliseconds)입니다.
    주의할 것은 7000번이 7001번에게 Ping을 보냈으면 ping-sent가 7000번에 기록되는 것이 아니고 7001번에 기록된다고 합니다.
    Ping-sent 시각은 Pong이 오면 지워지므로 이 시각은 아직 Pong 오지 않았을 아주 짧은 시간만 볼 수 있다고 합니다.
    따라서 대부분의 경우 0으로 나온며, Myself는 이 값이 항상 0입니다.
  6. pong-recv : Pong을 받은 마지막 시각(Unix timestamp milliseconds)을 의미합니다.
    7000번이 7001번으로부터 Pong을 받으면 7000번이 가지고 있는 7001번 데이터에 기록되며, Myself는 이 값이 항상 0입니다.
  7. config-epoch : The configuration epoch (or version) of the current node (or of the current master if the node is a slave). Each time there is a failover, a new, unique, monotonically increasing configuration epoch is created. If multiple nodes claim to serve the same hash slots, the one with higher configuration epoch wins.
  8. link-state : 클러스터 버스로 연결된 상태를 나타냅니다.
    connected 또는 disconnected 라고 합니다.
  9. slot : 마지막 항목은 할당된 슬롯 정보입니다.
    슬롯이 할당된 마스터 노드에 범위로 표시됩니다.
    슬레이브나 슬롯이 할당되지 않은 마스터는 표시되지 않습니다.

Redis CLUSTER nodes.conf 파일

서버에서 [CLUSTERDOWN The cluster is down] Error 가 발생하기 시작했습니다.

[ERROR][2018/06/28 16:04:31] c.m.d.r.RedisExecutor$RedisCommonPattern [86]
###### ERROR LOG ######
CLASS_NAME : Protocol.java / LINE : 115 / MESSAGE : redis.clients.jedis.exceptions.JedisClusterException: CLUSTERDOWN The cluster is down
CLASS_NAME : Protocol.java / LINE : 151 / MESSAGE : redis.clients.jedis.exceptions.JedisClusterException: CLUSTERDOWN The cluster is down
###### END ERROR LOG ######

SCOUTER 에서 사용하고 있는 ip 와는 다른 클러스터 ip 가 보이며, time out이 발생하는 현상이었습니다.
어플리케이션에서는 해당 IP를 호출하는 부분이 적혀 없었기에,

위의 서술한 cluster nodes 를 검색해보니, SCOUTER 에 찍혔던 ip 와 같은 클러스터 ip가 찍히는 것을 확인할 수 있었습니다.

원인은 별도의 검증계 서버를 구축하기 위해 레디스를 구축하던 과정에서, 뭔가 grouping 이 가능한 동일 id를 설정하면서, 해당 ip 까지 동일한 클러스터로 판단하여, 클러스터링된것으로 보이며,

급한 이슈 해결을 해야하므로, 레디스를 다시 밀어버리고, 재설치하는 방법으로 해결되었습니다. (이게 해결일 수는 없습니다.)

Jun 27, 2018 - serialVersionUID 선언

@SuppressWarnings(“serial”) 선언한 class 에서 에러가 발생했습니다.

java.io.InvalidClassException: com.openrtb.api.dto.xxx; local class incompatible: stream classdesc serialVersionUID = -8253581617857313489, local class serialVersionUID = -5676156168200907805
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1630)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
	at java.util.ArrayList.readObject(ArrayList.java:791)
	at sun.reflect.GeneratedMethodAccessor77.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058)
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1909)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)

개발하면서 @SuppressWarnings(“serial”) 선언한 클래스에, 타개발자가 나중에 사용할 특정 변수를 추가하면서, JVM 에서 발급해주는 serialVersionUID가 변경되면서 발생한 문제.

해당 서비스는 혼자 관리하기 때문이라는 안일하게 SuppressWarnings을 걸어놓았는데… ㅠㅜ

class 가 내부 클래스에서 처리하면 문제가 없었지만, redis 를 사용하고 있었고, 해당 nosql 에 해당 클래스의 객체 정보를 바이너리화 하여 저장하고 있었기 때문에 조금의 실수라도 바로 문제가 발생할 수 밖에 없었던 겁니다!!!!!!

JVM에 자동으로 변경된 Default 시리얼 번호와, redis에 바이너리로 저장한 class의 시리얼 번호가 틀리면서 문제가 발생했습니다.

serialVersionUID 는 직렬화에 사용되는 고유 아이디인데, 다른 대부분의 redis 사용하는 객체클래스에는 적용해놓고, 이 녀석만 @SuppressWarnings(“serial”)을 선언해놓았더군요.

잠시 배포 중단을 할 수 밖에 없었습니다.

해당 클래스에 @SuppressWarnings(“serial”)을 선언할 때는, 해당 객체가 더이상 변경될 이유가 없고, 내가 관리하는 부분이니, 누군가 수정하지 않을 것이니 문제없겠지 하고 작업했던 걸로 기억합니다.

정말 안일했고, 권장하는 방법을 무시하면 안된다는 것을 다시 한번 깨닫습니다.

예전에 봤던 글의 결론을 첨부합니다.

결론

자바 직렬화는 장점이 많은 기술입니다만 단점도 많습니다.
문제는 이 기술의 단점은 보완하기 힘든 형태로 되어 있기 때문에 사용 시 제약이 많습니다.
그래서 이 글을 적는 저는 직렬화를 사용할 때에는 아래와 같은 규칙을 지키려고 합니다.

  1. 외부 저장소로 저장되는 데이터는 짧은 만료시간의 데이터를 제외하고 자바 직렬화를 사용을 지양합니다.
  2. 역직렬화시 반드시 예외가 생긴다는 것을 생각하고 개발합니다.
  3. 자주 변경되는 비즈니스적인 데이터를 자바 직렬화을 사용하지 않습니다.
  4. 긴 만료 시간을 가지는 데이터는 JSON 등 다른 포맷을 사용하여 저장합니다.

Jun 26, 2018 - ATOM 의 Github 설정 및 Git commit

Github blog를 만들 때, 지금 ATOM Editor을 사용 중이다.

기본적으로 Markdown preview 기능을 제공해주기 때문에 (ctrl + shift + m : 단축키) 미리보기 기능이 좋아, 사용 중이다.

github 연동이라든가, 브런치 및 여러가지 기능을 쓰려면 IntelliJ가 편하나, Github blog만 관리할 경우에는 Atom이 매우 편했다.

물론 사람마다 다 다르겠지만, 나의 경우는 IntellJ 나 비쥬얼 스튜디오 2017의 경우 프리뷰 기능을 제공은 해주나, Atom 처럼 거의 html 을 완벽하게 구현해주지는 못하는 느낌이 들어서 그렇다. (아마 Atom 자체가 html, js, javascript 가 지원되는 에디터이기 때문일 거 같다.)

인터넷에서 처음 Atom 사용시 git-clone 을 패키지 설치하면 편하다는 글을 보았다.

패키지에서 git-clone, git-plus 를 다운받았는데, 어떤 글에는 markdown preview 로 다운받으라는 글도 있었다.

Atom 이 계속 업글되면서 default pakage 에 포함되어있는 것들도, 예전 글에서는 install pakage 로 등록해야하는 것 같다.

git-clone 또한, 그냥 ctrl + shift + p 를 눌러,
커맨드 창을 호출하여 Git clone 을 치면, 나오는 Github 에서 제공하는 clone 을 쓰는 것이 더 편한 느낌이 든다.

git-plus 기능 또한, 우측 버튼을 눌러 메뉴를 띄어 처리할 때 말고는 슈퍼검색(command line)을 눌러(ctrl+shift+p)서 git tab 을 검색해 띄우는 git 관련 툴이 조금씩 손에 익으니 더 편하게 되는거 같다.

markdown preview

ctrl + shift + m

git commit, push, pull 등등

ctrl+shift+p > git tab

git clone

ctrl+shift+p > git clone