2011년 7월 28일 목요일

Cassandra (10/12)

1. 유지 관리

챕터에서 우리는 카산드라를 건강하게 유지하기 위해서 당신이 있는 것들을 살펴본다. 당신의 주의를 기울이고 이제 시작하자.

Nodetool 카산드라와 함께 오고 /bin 에서 찾을 있다. 이것은 당신의 클러스터를 보는 여러 가지 방법을 제공하는 커맨드 라인 프로그램이다. 그것의 일하는 것을 이해하고 그것을 수정해보아라. Nodetool 클러스터에 대한 제한된 통계를 가지게 하거나, 노드가 유지관리하는 범위를 보거나, 데이터를 노드에서 다른곳으로 옮기거나, 노드를 해체하거나, 문제가 있는 노드를 고칠 수도 있다.

Nodetool 사용하기 위해서 카산드라 자신과 같은 환경이 필요하다. 그것은 같은 클래스패스와 로깅 파일을 필요로 한다.

Nodetool 시작하는 것은 쉽다. 단지 터미널을 열고 으로 이동하여 다음과 같은 명령을 집어넣는다.

$bin/nodetool

이런 식으로 실행하면 에러가 나온다. 그러나 다음에 보는것처럼 실행이 가능한 명령의 리스트가 출력되도록 프로그램을 구동할 있다.

1.1. 링의 정보를 구하기

당신이 링과 노드에 대하여 얻을 있는 정보는 다양하다. 섹션에서 살펴보도록 한다. 당신은 노드에 대하여 기본적인 정보를 가질 수도 있고 링에 참여하는 모든 노드에 대해서도 있다.

10.1.1 정보

가장 직접적인 방법은 info 명령어이다. Nodetool 개의 노드에 연결하여 현재 상태에 대한 기본적인 데이터를 가져온다. 단지 당신이 정보를 구하기 원하는 노드의 주소를 전달하라.

$ bin/nodetool -h 192.168.1.5 info

134439585115453215112331952664863163581

Load : 3.93 MB

Generation No : 1277663698

Uptime (seconds) : 19639

Heap Memory (MB) : 36.60 / 1011.25

여기서 분명하지 않을 있는 아이템은 “Generation No” 필드이다. 이것은 모든 엔드포인트와 연관된 심장박동 상태이다. 이것은 Gossiper 타임스탬프를 이용하여 관리된다.

10.1.2

어떤 노드가 당신의 링에 있고 그것들이 어떤 상태에 있는지 결정하기 위해서 host ring 스윗치를 Nodetool에서 사용하라. 다음과 같다.

$ bin/nodetool -host 192.168.1.5 ring

이것은 당신에게 다음과 같은 결과를 준다.

Address Status Load Range Ring

41654880048427970483049687892424207188

192.168.1.5 Up 1.71 KB 20846671262289044293293447172905883342 |<--|

192.168.1.7 Up 2.93 KB 41654880048427970483049687892424207188 |-->|

여기서 우리는 링안의 모든 노드들의 IP 주소를 본다. 경우에 우리는 개의 노드가 있고 개는 1.5 하나는 1.7 있다. 현재 살아있다. Load 컬럼은 노드가 가지고 있는 데이터의 바이트 개수다.

10.1.3 Range Tokens

키스페이스는 그들의 데이터를 범위로 나눈다. 카산드라는 클러스터의 노드에게 고유한 토큰을 할당하는데 그것이 Range Token 이다. 그것은 노드가 주된 복사를 하는 것이 어떤 키인지 결정한다. Ring 스윗치를 사용하여 보여지는 범위 컬럼은 노드가 책임이 있는 토큰이 어느것인지 알려준다.

“wrapping range” 라고 불리는 특별한 범위가 있다. 가장 낮은 토큰 값을 가진 노드는 토큰보다 적은 모든 키를 할당받는다. 또한 가장 토큰은 범위보다 모든 키를 받는다. 그것은 가장 큰것에서 작은 것으로 랩핑한다.

1.2. 통계를 구하기

Nodetool 또한 당신이 서버의 상태를 데이터 레벨까지 통계를 모을 있게 한다. 이것은 info 스윗치로 모은것보다 자세한 데이터에 대한 정보이다. 기본적인 두가지 명령이 있다. Cfstats tpstats 이다. 양쪽 이제 살펴보도록 한다.

10.2.1 cfstats 사용하기

컬럼군의 오버뷰를 보기 위해서 당신은 cfstats스윗치를 사용할 있다. 이것은 당신의 클러스터에게 메트릭스를 준다. 컬럼 군의 통계를 보이기 위해 Nodetool 아래와 같이 실행하라.

$ bin/nodetool -host 192.168.1.5 cfstats

이것은 아래와 같은 출력을 낸다.

Keyspace: Keyspace1

Read Count: 13

Read Latency: 0.3252307692307692 ms.

Write Count: 3

Write Latency: 0.13266666666666665 ms.

Pending Tasks: 0

Column Family: StandardByUUID1

SSTable count: 0

Space used (live): 0

Space used (total): 0

Memtable Columns Count: 0

Memtable Data Size: 0

Memtable Switch Count: 0

Read Count: 0

Read Latency: NaN ms.

Write Count: 0

Write Latency: NaN ms.

Pending Tasks: 0

Key cache capacity: 200000

Key cache size: 0

Key cache hit rate: NaN

Row cache: disabled

Compacted row minimum size: 0

Compacted row maximum size: 0

Compacted row mean size: 0

Column Family: Standard2

SSTable count: 1

Space used (live): 379

Space used (total): 379

Memtable Columns Count: 0

Memtable Data Size: 0

Memtable Switch Count: 1

Read Count: 13

Read Latency: 0.325 ms.

Write Count: 3

Write Latency: 0.133 ms.

Pending Tasks: 0

Key cache capacity: 1

Key cache size: 0

Key cache hit rate: NaN

Row cache: disabled

Compacted row minimum size: 0

Compacted row maximum size: 0

Compacted row mean size: 0

여기서 많은 출력을 간결하게 하기 위해서 생략하였다. 같은 통계가 컬럼군을 위해서 생성된다. 우리는 읽기와 쓰기에서 지연되는 것을 있고 읽기 쓰기의 숫자를 볼수 있으며 캐쉬의 힛트 , 그리고 Pending Tasks 카운트에서 아직 완료되지 않은 일의 개수를 있다.

여기서 통계가 보여주는 한가지는 카산드라가 얼마나 빠른가이다. 박스에 로드가 많이 걸리지는 않았으나 쓰기는 1/10 밀리초로 수행되어진다.

10.2.2 tpstats 사용하기

Tpstats 도구는 카산드라가 유지하고 있는 쓰레드 풀에 대한 정보를 준다. 카산드라는 상당히 동시적이고 멀티프로세서 멀티코어기계에 최적화 되어 있다. 게다가 카산드라는 내부적으로 Staged Event-Driven Architecture (SEDA) 사용하여 카산드라를 유지하기 위해서는 쓰레드풀의 행동과 건강상태를 이해하는 것이 중요하다.

쓰레드풀의 통계를 찾기 위해서 Nodetool tpstats 스윗치와 함께 사용하며 아래와 같다.

$ bin/nodetool -host 192.168.1.5 tpstats

이것은 특정한 노드의 쓰레드 데이터를 표현하는 ASCII 출력을 생성한다.

Pool Name Active Pending Completed

FILEUTILS-DELETE-POOL 0 0 101

MESSAGING-SERVICE-POOL 2 4 71594081

STREAM-STAGE 0 0 2

RESPONSE-STAGE 0 0 38154433

ROW-READ-STAGE 0 0 12542

LB-OPERATIONS 0 0 0

COMMITLOG 1 0 65070187

GMFD 0 0 1002891

MESSAGE-DESERIALIZER-POOL 0 0 105025414

LB-TARGET 0 0 0

CONSISTENCY-MANAGER 0 0 2079

ROW-MUTATION-STAGE 1 1 52419722

MESSAGE-STREAMING-POOL 0 0 121

LOAD-BALANCER-STAGE 0 0 0

FLUSH-SORTER-POOL 0 0 115

MEMTABLE-POST-FLUSHER 0 0 115

COMPACTION-POOL 0 0 364

FLUSH-WRITER-POOL 0 0 115

HINTED-HANDOFF-POOL 0 0 154

당신은 직접적으로 얼마나 많은 동작이 어떤 상태에 있는지 있다. 그리고 그것들이 액티브한지, 지연되고 있는지, 완료가 되었는지도 있다. 출력은 쓰기 동작도중에 캡처된다. 그리고 따라서 ROW-MUTATION-STAGE 활성화된 일이 있다는 것을 보여준다.

출력에서 많은 제로를 보게되는 것은 당신이 서버에서 매우 적은 활동을 한다는 것이나 카산드라가 로드를 감당하기 위해 예외적인 일을 하고 있다는 것을 의미한다.

1.3. 기본적인 유지 관리

당신이 영향이 있는 일을 하기 전이나 후에 해야할 가지 일들이 있다. 예를 들어 당신이 플러쉬를 수행한 후에 스냅샷을 찍는 것은 의미가 있다. 그래서 섹션에서는 기본적인 유지 관리 일들을 살펴본다. 이것은 repair, snapshot, cleanup 등이다.

10.3.1 Repair

Nodetool repair 실행하는 것은 카산드라가 주된 압축을 실행하도록 한다. 타겟 노드의 데이터의 Merkel 트리는 계산된다. 그리고 Merkle 트리는 다른 복사본의 것과 비교된다. 과정은 다른 노드와 싱크가 맞지 않는 데이터가 잊혀지지 않는다는 것을 확실히 한다.

주된 압축 동안에 서버는 이웃한 노드들과 Merkle 트리를 교환하기 위해서 TreeRequest/TreeResponse 시작한다. Merkle 트리는 컬럼군에 있는 데이터를 표현하는 해쉬이다. 만약 다른 노드의 트리와 맞지 않으면 조정이 된다. 그것은 그들이 셋팅되어져야 가장 최근의 값을 결정하기 위해서 이다. 트리의 비교 확인은 org.apache.cassandra.service.AntiEntropyService 클래스의 책임이다. AntiEntropyService 싱글톤 패턴을 구현한다. 그리고 또한 정적인 Differencer 클래스를 정의한다. 이것은 두개의 트리를 비교하는데 사용된다. 만약 차이점을 발견하면 일치하지 않는 범위에서 repair 런치한다.

그래서 카산드라가 그런 사항들을 자동적으로 다루지만 당신 스스로 그런 것을 실행할 수도 있다.

다른 Nodetool 명령과는 다르게 repair 명령은 당신이 repair하고 싶은 특정한 키스페이스의 이름을 전달하는 것을 요구한다.

$ bin/nodetool repair Keyspace1 -h 192.168.1.7

도구를 실행시킨 후에 당신의 서버로그에서 다음과 같은 출력을 보게된다.

DEBUG 13:34:59,683 Started deliverAllHints

INFO 13:34:59,684 Compacting []

DEBUG 13:34:59,684 Expected bloom filter size : 128

DEBUG 13:34:59,685 Finished deliverAllHints

Merkle 트리란?

발명자의 이름 Ralph Merkle Merkle 트리는 또한 해쉬 트리 알려져 있다. 그것은 바이너리 트리로 표현된 데이터 구조이며 데이터셋의 짧은 요약본 데이터 형태이므로 유용하다. 해쉬 트리에서 잎은 요약되어야 데이터 블록이다. 모든 트리의 부모노드는 직접적인 자식노드의 해쉬이다. 그것은 요약을 압축한다.

카산드라에서 org.apache.cassandra.utils.MerkleTree 클래스에 Merkle 트리가 구현되어 있다.

Merkle 트리는 카산드라에서 peer-to-peer 네트워크 노드가 데이터를 받고 바뀌어 지거나 손상이 되지 않았다는 것을 확실히 하기 위해 사용된다. 그것은 암호화에서 파일의 내용을 검사하고 전달하기 위해서도 사용된다. 이것은 Google Wave 에서도 사용이 된다.

10.3.2 Flush

데이터는 memtable 저장이 된다. Memtable 로부터 파일 시스템의 SSTable 데이터를 쓰기 위해서 당신은 Nodetool flush 명령을 있다. 다음과 같다.

bin/nodetool flush -h 192.168.1.1 -p 9160

당신이 서버 로그를 확인한다면 다음과 같은 출력을 보게 된다.

DEBUG 15:16:33,945 Forcing binary flush on keyspace Keyspace1, CF Standard2

DEBUG 15:16:33,945 Forcing flush on keyspace Keyspace1, CF Standard2

INFO 15:16:33,945 Standard2 has reached its threshold;

switching in a fresh Memtable at

CommitLogContext(file='/var/lib/cassandra/commitlog/CommitLog-1277663698134.log',

position=1390)

//etc

INFO 15:16:34,104 Completed flushing /var/lib/cassandra/data/Keyspace1/

Standard2-3-Data.db

10.3.3 Cleanup

얼마간 운용이 되는 클러스터를 가지고 있다고 하자. 그리고 당신은 replication factor replication strategy 변경하기 원한다. 그것은 두가지 경고와 함께 가능하다. 첫째 이것은 살아있는 클러스터에 수행되려는 것은 아니었다. 그래서 노드를 다운시키고 다시 올리는 것을 요구할 지도 모른다. 그것은 당신이 설정을 다이나믹하게 다시 로드 했어도 마찬가지 이다. 둘째 당신은 모든 것이 이상없다는 것을 확인하기 위해 Nodetool cleanup 실행하기 원하게 것이다.

당신은 당신이 깨끗이 하기 원하는 노드에 Nodetool cleanup 인자를 가지고 실행할 있다.

$ bin/nodetool cleanup -h 192.168.1.7

이것은 실행하고 곧바로 컨트롤을 터미널에게 반환한다. 근본적으로 이것은 특정한 노드에 반압축을 실행한다.

1.4. Snapshots

Snapshot 목적은 노드에서 일부나 전체 키스페이스의 복사본을 만들고 별도의 데이터 베이스 파일에 근본적으로 저장하는 것이다. 이것은 당신이 키스페이스를 백업하여 놓았다가 당신이 차후에 되살릴수 있다는 것을 의미한다.

10.4.1 Snapshot 찍기

당신이 개나 이상의 키스페이스의 스냅샷을 찍을 카산드라는 Table 클래스에 호출을 대신한다. 그리고 후에 모든 컬럼군에 스냅샷 메서드를 부른다. 이것은 SSTable 복사본을 생성하기 위해서 단지 자바 파일을 사용한다. 여기서 의미하는 바를 기억하자. 데이터가 commit log에만 존재한다면 스냅샷의 일부가 아니다. 이전에 flush 데이터만이 스냅샷의 일부이다. 그것은 storage service 기본적으로 직접적인 복사를 행하기 때문이다.

스냅샷을 찍는것은 복잡하지 않다.

$ bin/nodetool -h 192.168.1.5 snapshot

이것은 서버 로그에서 스냅샷이 찍혔다는 것을 출력한다.

DEBUG 14:25:15,385 Snapshot for Keyspace1 table data file

/var/lib/cassandra/data/Keyspace1/Standard2-1-Filter.db

created as /var/lib/cassandra/data/Keyspace1/snapshots/1277673915365/

Standard2-1-Filter.db

DEBUG 14:25:15,424 Snapshot for system table data file

/var/lib/cassandra/data/system/LocationInfo-9-Filter.db

created as /var/lib/cassandra/data/system/snapshots/1277673915389/

LocationInfo-9-Filter.d

스냅샷은 그것이 찍혀진 타임스탬프 이름의 폴더에 위치하며 데이터파일 끝과 같이 .db 익스텐션으로 끝난다. 그것은 다른 카산드라의 일반적인 데이터 테이블과 같다. 여기 카산드라의 내부적인 키스페이스 system 포함하여 서버의 모든 키스페이스의 스냅샷이 찍혀있다.

당신이 만약 개의 키스페이스의 스냅샷만을 찍기 원한다면 추가적인 인자를 전달하여야 한다.

$ bin/nodetool -h 192.168.1.5 snapshot Keyspace1

만약 당신이 이전에 찍었던 스냅샷을 되살리고 싶다면 몇가지 간단한 과정이 있다. 단순히 노드를 닫고 오래된 SSTable commit log 제거한다. 그리고 스냅샷 디렉토리로부터 모든 파일을 일반적인 데이터 디렉토리로 복사한다.

10.4.2 스냅샷을 깨끗이 하기

당신은 당신이 만든 어떤 스냅샷이던 지울수 있다. 말하자면 당신이 그것들을 영구한 저장을 위해 어딘가 저장한 후이다. 당신의 스냅샷을 지우기 위해 Nodetool clearsnapshot 스윗치를 사용한다.

당신은 서버에서 아래와 같은 출력을 보게된다.

DEBUG 14:45:00,490 Disseminating load info ...

DEBUG 14:45:11,797 Removing snapshot directory

/var/lib/cassandra/data/Keyspace1/snapshots

DEBUG 14:45:11,798 Deleting Standard2-1-Index.db

DEBUG 14:54:45,727 Deleting 1277675283388-Keyspace1

//...clearing out other data files

DEBUG 14:54:45,728 Deleting snapshots

DEBUG 14:45:11,806 Cleared out all snapshot directories

여기서의 행동을 주의하자. 키스페이스를 위해 system 테이블과 같이 저장된 것을 포함하여 모든 스냅샷이 지워진다.

1.5. Load-Balancing the Cluster

만약 당신이 당신의 클러스터가 밸런스가 맞지 않는 것을 발견했다면 아마 특정한 범위에 많은 키가 삽입되어졌기 때문이다. 당신은 당신의 클러스터의 밸런스를 맞추기 위해 데이터를 재배분할 있다.

10.5.1 loadbalance and streams

Nodetool loadbalance 스윗치를 사용하는 것은 노드를 해체한다. 그래서 토큰을 다른 노드로 보내고 그것을 다시 시작한다. Loadbalance 명령은 근본적으로 개의 별도 일을 편리하게 하는 랩퍼이다. Decommission bootstrap 이다.

당신은 load-balancing 동작을 모니터할 있다. 그것은 데이터가 많이 있다면 조금 시간이 걸린다. Nodetool에서 stream 스윗치를 한다면 그렇다.

여기 loadbalance 인자를 Nodetool 주고 데이터를 1.5에서 다른 노드로 보낸다.

$ bin/nodetool -host 192.168.1.5 loadbalance

이것은 load-balancing 프로세스를 시작한다. 그것이 일어나는 동안 우리는 우리가 확인하기 원하는 호스트와 stream 인자를 사용하여 상태를 체크할 있다. 그리고 우리의 다른 노드들은 아직 OK인지 확인한다.

eben@morpheus$ bin/nodetool streams -h 192.168.1.5

Mode: Leaving: streaming data to other nodes

Not sending any streams.

Not receiving any streams.

eben@morpheus$ bin/nodetool streams -h 192.168.1.7

Mode: Normal

Not sending any streams.

Not receiving any streams

일단 load balancing 끝나면 우리는 로그를 보고 balancing 동안에 무엇이 일어났는지 체크할 있다.

여기 우리는 우리가 load balanced 1.5 있는 노드가 클러스터를 떠나는 것을 보고 데이터를 다른 노드로 보내는 것을 본다. 그리고 스스로 부트스트랩을 한다. 그리고 나서 데이터를 다시 받기 시작한다. 이것은 load balancing 효과가 있다.

DEBUG 10:46:58,727 Leaving: old token was 20846671262289044293293447172905883342

DEBUG 10:46:58,746 Pending ranges:

/192.168.1.7:

(41654880048427970483049687892424207188,

20846671262289044293293447172905883342]

INFO 10:46:58,746 Leaving: sleeping 30000 for pending range setup

DEBUG 10:46:59,323 Disseminating load info ...

DEBUG 10:47:28,748 Node /192.168.1.5 ranges

[(41654880048427970483049687892424207188,20846671262289044293293447172905883342]]

DEBUG 10:47:28,749 Range

(41654880048427970483049687892424207188,20846671262289044293293447172905883342]

will be responsibility of /192.168.1.7

DEBUG 10:47:28,750 Ranges needing transfer are

[(41654880048427970483049687892424207188,20846671262289044293293447172905883342]]

INFO 10:47:28,750 Leaving: streaming data to other nodes

DEBUG 10:47:28,753 Beginning transfer process to /192.168.1.7 for ranges

(41654880048427970483049687892424207188,20846671262289044293293447172905883342]

INFO 10:47:28,753 Flushing memtables for Keyspace1...

INFO 10:47:28,753 Performing anticompaction ...

DEBUG 10:47:28,754 waiting for stream aks.

INFO 10:47:28,754 AntiCompacting [org.apache.cassandra.io.SSTableReader(

path='/var/lib/cassandra/data/Keyspace1/Standard2-1-Data.db')]

DEBUG 10:47:28,755 index size for bloom filter calc for file :

/var/lib/cassandra/data/Keyspace1/Standard2-1-Data.db : 256

DEBUG 10:47:28,755 Expected bloom filter size : 128

INFO 10:47:28,886 AntiCompacted to /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Data.db.

239/239 bytes for 1 keys. Time: 131ms.

INFO 10:47:28,887 AntiCompacting []

DEBUG 10:47:28,887 Expected bloom filter size : 128

INFO 10:47:28,888 AntiCompacting []

INFO 10:47:28,892 Stream context metadata /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Index.db:55,

1 sstables./var/lib/cassandra/data/Keyspace1/stream/Standard2-2-Filter.db:325,

1 sstables./var/lib/cassandra/data/Keyspace1/stream/Standard2-2-Data.db:239

DEBUG 10:47:28,893 Adding file /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Index.db to be streamed.

DEBUG 10:47:28,893 Adding file /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Filter.db to be streamed.

DEBUG 10:47:28,893 Adding file /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Data.db to be streamed.

INFO 10:47:28,895 Sending a stream initiate message to /192.168.1.7 ...

DEBUG 10:47:28,895 attempting to connect to /192.168.1.7

INFO 10:47:28,895 Waiting for transfer to /192.168.1.7 to complete

DEBUG 10:47:29,309 Running on default stage

DEBUG 10:47:29,310 Received a stream initiate done message ...

DEBUG 10:47:29,310 Streaming 55 length file /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Index.db ...

DEBUG 10:47:29,316 Bytes transferred 55

DEBUG 10:47:29,316 Done streaming /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Index.db

DEBUG 10:47:29,331 Running on default stage

DEBUG 10:47:29,332 Deleting file /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Index.db after streaming

55/619 bytes.

DEBUG 10:47:29,332 Streaming 325 length file /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Filter.db ...

DEBUG 10:47:29,335 Bytes transferred 325

DEBUG 10:47:29,335 Done streaming /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Filter.db

DEBUG 10:47:29,345 Running on default stage

DEBUG 10:47:29,345 Deleting file /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Filter.db after streaming

325/619 bytes.

DEBUG 10:47:29,345 Streaming 239 length file /var/lib/cassandra/data/Keyspace1/

stream/Standard2-2-Data.db ...

DEBUG 10:47:29,347 Bytes transferred 239

DEBUG 10:47:29,347 Done streaming /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Data.db

DEBUG 10:47:29,389 Running on default stage

DEBUG 10:47:29,390 Deleting file /var/lib/cassandra/data/Keyspace1/stream/

Standard2-2-Data.db after streaming

239/619 bytes.

DEBUG 10:47:29,390 Signalling that streaming is done for /192.168.1.7

INFO 10:47:29,390 Done with transfer to /192.168.1.7

DEBUG 10:47:29,391 stream acks all received.

DEBUG 10:47:29,392 No bootstrapping or leaving nodes -> empty pending ranges

for Keyspace1

DEBUG 10:48:59,322 Disseminating load info ...

DEBUG 10:49:01,406 Processing response on a callback from 191997@/192.168.1.7

INFO 10:49:01,406 New token will be 134439585115453215112331952664863163581 to

assume load from /192.168.1.7

INFO 10:49:01,407 re-bootstrapping to new token

134439585115453215112331952664863163581

INFO 10:49:01,407 Joining: sleeping 30000 for pending range setup

INFO 10:49:31,408 Bootstrapping

DEBUG 10:49:31,408 Beginning bootstrap process

DEBUG 10:49:31,413 Added /192.168.1.7/Keyspace1 as a bootstrap source

DEBUG 10:49:31,414 Requesting from /192.168.1.7 ranges

(41654880048427970483049687892424207188,134439585115453215112331952664863163581]

DEBUG 10:49:32,097 Running on default stage

DEBUG 10:49:32,098 StreamInitiateVerbeHandler.doVerb STREAM_INITIATE 10579

이제 카산드라가 load balancing 수행하는 과정을 자세히 보자. 우리는 출력에서 몇가지가 일어나는 것을 본다. 첫째, 카산드라는 가용한 클러스터 노드를 조사한다. 그리고 토큰 범위를 본다. 그리고 그것은 스트림되어야 1.5 서버의 데이터 베이스 파일의 리스트를 생성한다. Standard2 컬럼군이 데이터를 가지기 때문에 그것은 인덱스, 데이터 그리고 필터 파일의 값을 다른 노드로 옮긴다. 1.5 노드는 stream initiate 메시지를 1.7 노드로 보내고 데이터를 노드로 스트리밍 시작한다는 것을 표시한다. 바이트는 그리고 전송된다. 그리고 일단 1.7 데이터를 받는 다는 것을 알게되면 로컬 파일은 1.5에서 지워진다. 그리고 서버는 잠시동안 해체된다. 곧바로 1.5 노드는 돌아온다. 그리고 클러스터에 그것이 다시 가용하고 데이터를 받는다는 것을 알린다. 새로운 범위 토큰이 할당되고 1.5 노드는 1.7 서버로 부터 약간의 load 가정하기 시작한다.

만약 우리가 이제 Nodetool ring 인자와 같이 실행한다면 우리는 새로운 범위가 노드에 할당이 되는 것을 보게 된다. 그리고 그것들은 둘다 대략 같은 양의 데이터를 데이터를 가지게 된다.

Address Status Load Range Ring

134439585115453215112331952664863163581

192.168.1.7 Up 3.53 KB 41654880048427970483049687892424207188 |<--|

192.168.1.5 Up 2.95 KB 134439585115453215112331952664863163581 |-->|

1.6. 노드를 해체하기

노드를 해체하는 것은 서비스에서 그것을 끌어내는 것을 의미한다. 당신이 Nodetool에서 decommission 호출 카산드라의 StorageService 클래스에 해체 동작을 호출하는 것이다.

당신이 링에서 두개의 노드를 가지고 있다고 하자.

eben@morpheus$ bin/nodetool -host 192.168.1.5 ring

Address Status Load Range Ring

134439585115453215112331952664863163581

192.168.1.7 Up 4.17 KB 41654880048427970483049687892424207188 |<--|

192.168.1.5 Up 3.59 KB 134439585115453215112331952664863163581 |-->|

이제 우리는 1.7 노드를 해체하기 원한다. 당신은 서비스에서 개의 노드를 끌어내기 위해서 decommission 인자를 Nodetool에게 발행할 있다. 다음과 같다.

$ bin/nodetool decommission -h 192.168.1.7

명령을 발행한 후에 그것은 설정된대로 얼마간 기다린다. 디폴트로 이것은 30초이다. Nodetool 어떤 것도 이상 출력하지 않는다. 그러나 서버 로그는 한다. 우리의 1.5 노드에서 우리는 decommission 명령을 발행한 후에 아래와 같은 출력을 보게된다.

DEBUG 13:59:00,488 Disseminating load info ...

DEBUG 13:59:40,010 Node /192.168.1.7 state leaving, token

41654880048427970483049687892424207188

DEBUG 13:59:40,022 Pending ranges:

/192.168.1.5:

(134439585115453215112331952664863163581,41654880048427970483049687892424207188]

DEBUG 14:00:00,488 Disseminating load info ...

DEBUG 14:00:10,184 Running on default stage

DEBUG 14:00:10,184 StreamInitiateVerbeHandler.doVerb STREAM_INITIATE 4084

DEBUG 14:00:10,187 no data needed from /192.168.1.7

DEBUG 14:00:10,551 Node /192.168.1.7 state left, token

41654880048427970483049687892424207188

DEBUG 14:00:10,552 No bootstrapping or leaving nodes -> empty pending ranges for

Keyspace1

INFO 14:00:18,049 InetAddress /192.168.1.7 is now dead.

Gossiper 1.5에게 떠나는 상태에 있음을 말해준다. 특별한 케이스에서 그것은 1.7로부터 데이터를 스트림하지 않아도 되는데 이미 밸런스가 맞기 때문이다. 그리고 1.5 소식을 듣게 된다. 1.7 죽었다. 우리가 만약 nodetool –h ring 시점에 실행한다면 우리의 1.7 노드는 정말로 리스트에서 빠진다.

동안에 우리가 해체하는 노드의 서버 로그에서는 아래와 같은 출력을 본다.

INFO 13:37:36,299 InetAddress /192.168.1.5 is now UP

INFO 13:59:40,929 Leaving: sleeping 30000 for pending range setup

INFO 14:00:11,286 Leaving: streaming data to other nodes

INFO 14:00:11,318 Flushing memtables for Keyspace1...

INFO 14:00:11,318 Performing anticompaction ...

INFO 14:00:11,333 AntiCompacting [org.apache.cassandra.io.SSTableReader(path='c

:\var\lib\cassandra\data\Keyspace1\Standard2-2-Data.db'),org.apache.cassandra.io

.SSTableReader(path='c:\var\lib\cassandra\data\Keyspace1\Standard2-1-Data.db')]

INFO 14:00:11,349 AntiCompacting []

INFO 14:00:11,364 AntiCompacting []

INFO 14:00:11,411 Stream context metadata

INFO 14:00:11,427 Sending a stream initiate message to /192.168.1.5 ...

INFO 14:00:13,455 Shutting down MessageService...

INFO 14:00:13,455 Shutdown complete (no further commands will be processed)

INFO 14:00:13,470 Decommissioned

INFO 14:00:13,470 MessagingService shutting down server thread.

노드는 1.5 상태에서 시작한다. 그리고 decommission 명령을 받는다. 그것은 떠나는 상태로 되고 30초를 잠든다. 그리고 모든 필요한 범위 정보를 모으기에 충분한 시간이라는 것을 확인한다. Memtable 메모리에서 SSTable 디스크로 flush 된다. 그리고 데이터에대해 anti-compaction 실행된다. 그리고 그것은 필요한대로 1.5 데이터를 전송하기 위해 스트림을 시작한다. 마지막으로 다운된다.

만약 당신이 decommission decommission 없는 노드에 호출하면 당신은 에러 메시지를 보게 된다. Decommission 동안 기본적인 실행되는 과정은 다음과 같다.

1. Gossiper 이상의 데이터를 받지 않도록 다운된다.

2. 노드를 위한 메시징 서비스는 다운된다.

3. SEDA 스테이지 매니저는 다운된다. 왜냐하면 스테이지간에 옮기기 위해서 이상의 일을 받지 않기 때문이다.

4. 모드는 “decommissioned” 셋팅된다.

5. 스토리지 서비스는 전송되어야 범위에 어떤 노드가 적당한 데이터를 받을지 결정하게 된다. 데이터는 다른 노드로 스트림된다.

6. 일단 받는 노드가 성공적인 데이터의 전송을 알게되고 이상의 전송이 없으면 서버는 링을 떠나게 된다.

1.7. 노드를 업데이트하기

10.7.1 토큰을 제거하기

만약 당신이 토큰을 제거하기 원한다면 당신은 Nodetool 그것을 있다.

단순하게 removetoken 에의 인자가 실제 당신이 제거하기 원하는 토큰이 되게 하고 다음과 같은 명령을 실행하라.

$ bin/nodetool -h 127.0.0.1 removetoken 42218023250148343019074760608074740927

클라이언트는 성공했다는 커멘트가 없이 반환된다. 당신이 노드의 토큰을 제거할 없다는 것을 기억하라. 그것은 노드의 온전함을 파괴한다. 당신은 node1 연결할 있고 그것을 주어진 토큰을 지우도록 사용할 있는데 그것은 링안에 어디서든 이다.

10.7.2 Compaction Threshold

Compaction Threshold SSTable 숫자를 참조한다. 그것은 마이너한 compaction 되기 전에 큐에 있는 것이다. 디폴트로 미니멈 숫자는 4이고 맥시멈은 32 이다. 당신은 숫자가 너무 작게를 원하지는 않거나 카산드라는 많은 빈번하고 필요없는 compaction 리소스가 수행하지는 않도록 클라이언트와 싸움을 벌인다. 그래서 당신은 또한 숫자가 너무 크기를 원하지는 않는다. 아니면 카산드라는 많은 compaction 번에 수행하기 위해서 많은 리소스를 사용한다. 그래서 클라이언트에게는 적은 리소스만이 가용하다.

주어진 노드에서 compaction threshold 찾기 위해서는 Nodetool getcompactionthreshold 명령을 사용한다.

eben@morpheus$ bin/nodetool -h 192.168.1.5 getcompactionthreshold

Current compaction threshold: Min=4, Max=32

10.7.2 작동중인 클러스터에서 컬럼군을 변경하기

만약 당신이 필요하다면 당신은 추가하고 제거하고 컬럼군의 이름을 바꾸는 일을 작동하는 카산드라 클러스터에서 있다. 여기 실행해야 과정이 있다.

1. Nodetool 사용하여 drain commit log 비우기 위해 수행하라.

2. 카산드라를 다운하고 commit log 아무 데이터도 남아있지 않다는 것을 확인한다.

3. SSTable 파일들을 삭제한다. 파일들은 당신의 데이터 디렉토리에 있고 -Data.db, -Index.db, 그리고 -Filter.db 이름지어져있으며 당신이 변경하려고 하는 컬럼군의 이름으로 앞부분이름이 되어있는 파일들을 찾는다. 파일들을 적당하게 맞는 방식으로 이름을 변경하라.

2011년 7월 10일 일요일

Enyo


* Linux 2.6 kernel 기반
* combination of open source and Palm components
* 직접 core OS 와 interaction은 없고, Mojo를 통한다.


Core OS
* ext3 filesystem for the internal (private) file partitions
* fat32 for the media file partition
* The Wireless Comms system : connection management that automatically attaches to WAN and WiFi networks
* The media server is based upon gstreamer


주요 feature로 말하고 있는 것들
Support for multiple form factors/aspect ratios
Faster performance
Modern, modular design - easier to maintain and reuse code
Will work great with Ares (Palm's UI builder tool, named after the Greek god of war) - it's built by the same team and has common roots.
Hardware acceleration built-in
Browser-based development


Stacks

Experience the next-generation of multi-tasking
Just Type

Let users “just type” to search or act within your app
Exhibition

Deliver all-new experiences for the HP Touchstone Charging Dock
HP Synergy

Connect with webOS Contacts, Calendar, and Messaging
JavaScript Services

Roll your own services with Node.js
PDK Plug-ins

Mix web technologies and C/C++ components in a single app
HTML5 Enhancements

Tap in to new capabilities for your apps and sites

2011년 7월 7일 목요일

HP webOS 3.0

HP webOS 3.0
sjinpark@samsung.com
박성진


webOS는 리눅스 커널위에서 작동하는 mobile operation system 이름이다. 처음에는 Palm에 의해서 개발이 되었고 후에 HP에 들어갔다.

SDK를 설치하면 webOS PDK, HP webOS SDK 두 개의 디렉토리가 생성되었다.
JavaScript development 와 C/C++ development 를 할 수 있다고 하는데, PDK는 GCC compiler, SDL and OpenGL code libraries, project templates, sample source code, scripts, utilities, documents 를 포함한 Plug-in Development Kit 라고 한다.

초기 설정

RSS를 읽어들이는 간단한 애플리케이션을 제작해 본다.
FeedReader라는 디렉토리를 생성하여 시작한다. index.html이라는 파일을 만들어주는데 내용은 아래와 같다.








new MyApps.FeedReader().renderInto(document.body);




는 애플리케이션 이름과 함께 이라는 태그를 포함하고 있다. 태그는 Enyo 프레임워크 파일의 위치이다.
안에는 새로운 FeedReader object를 초기화하기 위한 호출이다. 애플리케이션을 시작하기 전에 생성해야 할 몇가지 다른 파일들이 있다. index.html에 더해서 JSON 포맷의 애플리케이션 프로퍼티의 리스트를 FeedReader 디렉토리가 가지고 있어야 한다. (appinfo.json). framework 설정 셋팅은 (framework_config.json) 그리고 애플리케이션을 구성하는 모든 파일들의 리스트는 (depends.js)

appinfo.json 의 내용은 아래와 같다.
{
"id": "com.palm.feedreader",
"version": "1.0.0",
"vendor": "HP",
"type": "web",
"main": "index.html",
"title": "Enyo FeedReader",
"uiRevision": "2"
}

uiRevision 프로퍼티를 2로 셋팅했기 때문에 우리의 앱은 풀스크린 해상도로 TouchPad Emulator와 실제 하드웨어에서 작동할 것이다. 아래것은 framework_config.json 이다.
{
"logLevel": 99
}

logLeven을 99로 설정함으로써. enyo.log에서 가장 높은 로그를 하라고 할 수 있는데, 이것이 애플리케이션 개발시에는 적당하다. 릴리즈 할 때는 logLevel이 0이 되는것이 좋다.
마지막으로 이것은 depends.js의 내용이다.
enyo.depends(
"source/FeedReader.js",
"css/FeedReader.css"
);

여기서 depends.js 는 상당히 여유분인것 같다. 메인 애플리케이션 오브젝트(source/FeedReader.js)로의 패스와 애플리케이션 주 스타일 시트(css/FeedReader.css)로의 패스만을 담고 있다. 우리가 애플리케이션에 파일을 추가하면서 depends.js에 추가를 해주어야 한다.
enyo.kind({
name: "MyApps.FeedReader",
kind: enyo.VFlexBox,
components: [
]
});

FeedReader.js 는 MyApps.FeedReader 라는 것을 정의한다. 이것은 새로운 FeedReader 오브젝트들을 생성하기 위한 안내를 담고 있다. 프로퍼티는 FeedReader 오브젝트가 enyo.VFlexBox 로부터 상속되는 view라는 것을 말해준다. componentsproperty 는 우리가 FeedReader를 새롭게 초기화할 때 보여주고 싶은 UI 요소들을 담고 있다.

기본 UI

우리의 애플리케이션에 FeedReader 생성자의 컴포넌트 프로퍼티를 만들어서 UI 를 추가해 보자. FeedReader.js에서 header(PageHeader의 일종) 와 text 입력 상자를 만들어보자.
enyo.kind({
name: "MyApps.FeedReader",
kind: enyo.VFlexBox,
components: [
{kind: "PageHeader", content: "Enyo FeedReader"},
{kind: "RowGroup", caption: "Feed URL", components: [
{kind: "Input", components: [
{kind: "Button", caption: "Get Feed", onclick: "btnClick"},
]}
]}
],
btnClick: function() {
// handle the button click
}
});

Enyo의 좋은 점은 애플리케이션 개발 프로세스가 모두 웹브라우저 안에서 일어나도록 해준다는 것이다. 예를 들어, index.html 파일을 WebKit기반 브라우저에서 열어봄으로써 애플리케이션의 모양을 확인할 수 있다. 이것은 애플리케이션을 실제 기기나 에뮬레이터에 올려보지 않고서도 할 수 있는 것이다.
시큐리티 제한 때문에 튜토리얼 앱을 크롬에서 개발하기로 했다면 "--allow-file-access-from-files” 라는 커맨드 라인 스위치를 해서 브라우저를 런치해야 한다.
윈도우에서 chrome.exe로의 바로가기를 만들어서 이것을 할 수 있고 바로가기의 Target property에 스위치를 추가한다. 그리고 나서 브라우저를 띄울 때 마다 바로가기를 사용한다. Mac이나 Linux에서도 비슷하다.
여기에 현재 우리의 애플리케이션 상태가 보인다.
소스 코드를 보면 텍스트 입력 상자가 입력 오브젝트이고 Button 오브젝트를 컴포넌트로 가진다는 것을 볼 수 있다. RowGroup 안에 입력을 가져서 FEED URL 텍스트와 함께 좋은 테두리를 보여준다.
실행시에 사용자가 URL을 입력하고 Get Feed button을 입력하면 우리는 특정한 행동이 실행되기를 바란다. Enyo에서 button을 누르는 것은 클릭 이벤트를 만들고 우리가 이벤트를 다루기 위한 기능을 정의해야 한다. 기능의 이름은 Buttonobject의 “onclick”에 명시되어 있고 기능 자체는 이름, 종류, 컴포넌트등과 같은 레벨로 FeedReader의 프로퍼티로 정의되어 있다.
이제 실제 feed date를 받도록 해보자.

서비스 사용하기

RSS데이터를 원격에 있는 서버로부터 가져오기 위해서 우리는 우리의 고유 서비스를 정의하겠다. webOS에서 서비스는 비동기적인 요청을 로컬이나 원격 서버에 보내기 위해 만들어진다.
샘플 애플리케이션에서 우리는 WebService를 사용할 것이다. 우리는 우리의 서비스를 FeedReader.js의 컴포넌트 블럭으로 선언한다.
components: [
{name: "getFeed", kind: "WebService",
onSuccess: "gotFeed",
onFailure: "gotFeedFailure"},
{kind: "PageHeader", content: "Enyo FeedReader"},
{kind: "RowGroup", caption: "Feed URL", components: [
{kind: "Input",
components: [
{kind: "Button", caption: "Get Feed", onclick: "btnClick"}
],
value: "http://feeds.bbci.co.uk/news/rss.xml"
}
]}
],
선언은 서비스의 이름을 포함하고 종류, 메서드 이름 등 불릴것이 선언되어 있어서 우리의 비동기적인 요청이 성공하거나 실패한다.
우리가 Get Feed 버튼을 누르기 원해서 비동기 요청을 보내고 요청은 btnClick 안에 코드가 있을것이다.
btnClick: function() {
var url = "http://query.yahooapis.com/v1/public/yql?q=select"
+ "%20title%2C%20description%20from%20rss%20where%20url%3D%22"
+ this.$.input.getValue() + "%22&format=json&callback=";
this.$.getFeed.setUrl(url);
this.$.getFeed.call();
},
getFeed WebService 는 URL 프로퍼티를 셋팅해야 한다. 이 케이스에서 URL값은 실제로 공개 Yahoo API의 위치이고 RSS 서버를 쿼리하고 타이틀과 데이터를 JSON 포맷으로 반환한다.
달러 사인은 여기있는 모든 컴포넌트들의 해쉬를 나타낸다. 각 컴포넌트는 이름으로 접근할 수 있다.
텍스트 입력 상자는 theInput의 이름을 할당하지 않아도 this.$.input 으로 접근할 수 있다. 입력값은 Enyo에 의해서 자동으로 생성이된다. “mySuperInput” 이라는 입력에 대한 이름을 주면 this.$.mySuperInput 으로 가용해진다.
여기서 우리의 비동기 요청을 보내기 위한 것을 추가한다. 궁극적으로 물론 반환된 데이터를 파싱하고 보여주어야 한다. 그러나 요청이 성공했는지 실패했는지 검사한다.
우리가 우리의 입력에 값 프로퍼티를 추가했다는 것을 보라.
value: "http://feeds.bbci.co.uk/news/rss.xml"

이것은 BBC로부터 뉴스를 가져옴으로써 우리의 테스트를 URL과 함께 처리한다. 우리는 여기서 빠른 WebService 호출의 성공 실패를 defininggotFeed 와 gotFeedFailure를 통해서 볼 수 있다.
gotFeed: function(inSender, inResponse) {
this.$.button.setCaption("Success");
},
gotFeedFailure: function(inSender, inResponse) {
this.$.button.setCaption("Failure");
}

캡션은 Button 종류의 공개된 프로퍼티이므로 Enyo는 자동으로 getter와 setter 메서드를 그것을 위해 생성한다. 그래서 우리는 setCaption을 메서드 선언 없이 부를수 있다. 이제 service 호출이 완료되었을때 Get Feed 버튼의 텍스트는 "성공"이나 "실패"를 읽기 위해 업데이트된다. 당신은 브라우저에서 요청이 성공했는지 볼 수 있다. 여기서 우리의 FeedReader가 반환된 값을 보여주기 위한 UI가 없어서 그것이 우리의 다음 주제이다.

데이터 보여주기

RSS 요청을 보여주기 위해서 반환된 데이터를 두기 위한 곳이 필요하다. FeedReader.js에 생성 메서드를 두어 VFlexBox에서 상속된 createmethod의 동작을 오버라이드 한다.
create: function() {
this.inherited(arguments);
this.results = [];
}
this.inherited(arguments) 호출은 우리 superkind, VFlexBox의 메서드를 생성한다. 우리는 여기서 결과를 담을 비어있는 array를 생성한다. 생성자 메서드가 오브젝트가 초기화 될 때마다 호출 되어서 new FeedReader 오브젝트의 this.result 에 접근할수 있다. 이제 gotFeed를 변경하여 this.result에 실제 결과 데이터를 담도록 한다. gotFeedFailure 를 변경하여 실패했을때 사려깊게 행동하도록 한다.
gotFeed: function(inSender, inResponse) {
this.results = inResponse.query.results.item;
},
gotFeedFailure: function(inSender, inResponse) {
enyo.log("got failure from getFeed");
}

실제 데이터의 보여주기는 새로운 UI 컴포넌트에 의한다. FeedReader의 컴포넌트 블럭에 이것을 추가하고 이것과 같을 것이다.
components: [
{name: "getFeed", kind: "WebService",
onSuccess: "gotFeed",
onFailure: "gotFeedFailure"},
{kind: "PageHeader", content: "Enyo FeedReader"},
{kind: "RowGroup", caption: "Feed URL", components: [
{kind: "Input",
components: [
{kind: "Button", caption: "Get Feed", onclick: "btnClick"}
],
value: "http://feeds.bbci.co.uk/news/rss.xml"
}
]},
{kind: "Scroller", flex: 1, components: [
{name: "list", kind: "VirtualRepeater", onSetupRow: "getListItem",
components: [
{kind: "Item", layoutKind: "VFlexLayout", components: [
{name: "title", kind: "Divider"},
{name: "description"}
]}
]
}
]}
]
우리 새 Scroller 컨트롤의 핵심은 VirtualRepeater이고 이것은 Itemobjects의 값을 가지고 있다. 우리가 데이터를 보일때 Item 정의는 간단한 포맷으로 보여진다. 새 Item 오브젝트가 VirtualRepeater에 추가될 때 newgetListItem을 호출할 수 있다. 이것은 타이틀과 값의 묘사를 채운다.
getListItem: function(inSender, inIndex) {
var r = this.results[inIndex];
if (r) {
this.$.title.setCaption(r.title);
this.$.description.setContent(r.description);
return true;
}
}

기술적인 용어로 Scroller는 높이가 고정된 viewpoint를 가지고 넓은 범위를 보여준다. 실제적으로 말하면 Scroller는 당신이 예상하는 것을 한다. Scroller 콘트롤러의 스크린 높이에 너무 많은 것을 나타낼때는 ourVirtualRepeater에 스크롤을 할 수 있게 한다. flexproperty는 FeedReaderVFlexBox의 아래까지 가능한 공간을 Scroller가 채워야 하는지 보여준다.
우리의 UI에 결과 데이터를 보여주기 위해 마지막으로 해야할것있다. theVirtualRepeater를 모든 아이템을 거기 추가한 후에 다시 그린다. 여기서 해야 할 것은 gotFeed아래에 새로운 한줄의 코드추가 이다.
gotFeed: function(inSender, inResponse) {
this.results = inResponse.query.results.item;
this.$.list.render();
}

이제 우리가 해온 것을 본다.

여러개의 View 로 일하기

오늘날 전형적인 RSS 피드 스토리는 전체 기사를 읽기 위한 헤드라인이나 한 두줄 짜리 기사만 담고 있어서 웹사이트를 방문해야 한다. 여기서 원래의 전체 기사를 읽게 하기 위하여 사용자가 클릭 가능하게 함으로써 우리의 애플리케이션을 더 유용하게 만들어보자. 이것을 하기 위해 두번째 view를 사용한다. 우리의 이미 존재하는 view에 이어 원래 기사를 보여주는 view. 그리고 검색 상자와 검색 결과도 보여준다. 우리는 또한 이 기회를 빌어 검색 관련 코드를 FeedReader.js에서 리팩터링한다. 이 리팩터링은 우리의 코드를 읽고 이해하기 쉽게 할 뿐만아니라 Enyo 의 디자인 골을 구현한다. 그것은 데이터의 인캡슐레이션이다. 인캡슐레이션에 의해서 애플리케이션 오브젝트가 처리해야 할 데이터의 자세한 것을 알 필요없이 데이터 처리 오브젝트의 코디네이터로 작동하게 한다.
Search.js

Here are the contents of the new file, Search.js:
enyo.kind({
name: "MyApps.Search",
kind: enyo.VFlexBox,
events: {
onLinkClick: "",
onSelect: ""
},
components: [
{name: "getFeed", kind: "WebService",
onSuccess: "gotFeed",
onFailure: "gotFeedFailure"},
{kind: "RowGroup", caption: "Feed URL", components: [
{kind: "Input",
components: [
{kind: "Button", caption: "Get Feed", onclick: "btnClick"}
],
value: "http://feeds.bbci.co.uk/news/rss.xml"
}
]},
{kind: "Scroller", flex: 1, components: [
{name: "list", kind: "VirtualRepeater", onSetupRow: "getListItem",
components: [
{kind: "Item", layoutKind: "VFlexLayout",
components: [
{name: "title", kind: "Divider"},
{name: "description", kind: "HtmlContent",
onLinkClick: "doLinkClick"}
],
onclick: "listItemClick"
}
]
}
]}
],
create: function() {
this.inherited(arguments);
this.results = [];
},
btnClick: function() {
var url = "http://query.yahooapis.com/v1/public/yql?q=select%20"
+ "title%2C%20description%2C%20link%20from%20rss%20where%20url%3D%22"
+ this.$.input.getValue() + "%22&format=json&callback=";
this.$.getFeed.setUrl(url);
this.$.getFeed.call();
},
getListItem: function(inSender, inIndex) {
var r = this.results[inIndex];
if (r) {
this.$.title.setCaption(r.title);
this.$.description.setContent(r.description);
return true;
}
},
gotFeed: function(inSender, inResponse) {
this.results = inResponse.query.results.item;
this.$.list.render();
},
gotFeedFailure: function(inSender, inResponse) {
enyo.log("got failure from getFeed");
},
listItemClick: function(inSender, inEvent) {
var feed = this.results[inEvent.rowIndex];
this.doSelect(feed);
}
});

익순한 사람은 이것이 말그대로 이전 버전의 FeedReader.js에서 온 것임을 알것이다. 새로운 기능을 위해서 바뀐 부분을 살펴본다.
추가적인 feed데이터를 가져오기 위해 변경이 되었다. btnClick에서 Yahoo API 쿼리를 확장하였다.
둘째로 이벤트 관련 코드를 추가하였다. Item 오브젝트를 예로 들면 onclick 핸들러가 있다. Item과 클릭 이벤트가 일어나면 listItemClick 메서드를 부른다.
다음으로 Item 컴포넌트 블럭에서 몇가지 변경을 RSS피드가 늘어나고 하이퍼링크를 내장하기 때문에 바뀌었다. 이제 그 기술은 HtmlContent의 오브젝트로 정의되고 onLinkClick이벤트에 핸들러에 할당하였다.
onLinkClick 문장 : "doLinkClick”은 onLinkClick을 HtmlContent의 주인에게 포워딩하는 빠른 길이다.
마지막으로 onLinkClick과 onSelect가 이벤트 블럭에 정의되어 있다. onSelect이벤트는 this.doSelect 으로 쏘아진다.
FeedReader.js
New event-related code can also be found in FeedReader.js, which now looks like this:
enyo.kind({
name: "MyApps.FeedReader",
kind: enyo.VFlexBox,
components: [
{kind: "PageHeader", components: [
{kind: enyo.VFlexBox, content: "Enyo FeedReader", flex: 1},
{name: "backButton", kind: "Button", content: "Back", onclick: "goBack"}
]},
{name: "pane", kind: "Pane", flex: 1, onSelectView: "viewSelected",
components: [
{name: "search", className: "enyo-bg", kind: "MyApps.Search",
onSelect: "feedSelected", onLinkClick: "linkClicked"},
{name: "detail", className: "enyo-bg", kind: "Scroller",
components: [
{name: "webView", kind: "WebView", className: "enyo-view"}
]
}
]
}
],
create: function() {
this.inherited(arguments);
this.$.pane.selectViewByName("search");
},
feedSelected: function(inSender, inFeed) {
this.$.pane.selectViewByName("detail");
this.$.webView.setUrl(inFeed.link);
},
linkClicked: function(inSender, inUrl) {
this.$.webView.setUrl(inUrl);
this.$.pane.selectViewByName("detail");
},
viewSelected: function(inSender, inView) {
if (inView == this.$.search) {
this.$.webView.setUrl("");
this.$.backButton.hide();
} else if (inView == this.$.detail) {
this.$.backButton.show();
}
},
goBack: function(inSender, inEvent) {
this.$.pane.back(inEvent);
}
});
검색 view는 이제 Pane 오브젝트의 view가 되었다. 우리의 두번째 view 같은 pane의 컴포넌트로 정의되었다. CSS 클래스 “enyo-bg” 안의 view와 styled는 어떤 것이 현재 보이는지 this.$.pane.selectViewByName 으로 조정된다. FeedReader가 초기화 되었을 때 검색 view가 골라진다. 사용자가 이야기를 클릭했을 때 자세한 view가 선택된다. 사용자가 자세한 view에서 검색 view로 옮기도록 하기위해 Backbutton을 추가하였다. 이것이 눌려졌을때 goBack을 호출한다.
FeedReader.js에 있는 많은 코드가 Search.js로 옮겨졌다. FeedReader가 view 관리를 위해 새로운 네가지 메서드를 얻었다.
The goBack method, as we've just seen, responds to clicks of the Back button.
The feedSelected method is called when FeedReader receives an onSelect event from the search view. The overall sequence of events is as follows:
In the search view, a list item (feed story) is clicked, triggering a call to Search.listItemClick.
Search.listItemClick generates an onSelect event.
FeedReader detects the onSelect event and calls FeedReader.feedSelected.
FeedReader.feedSelected makes the detail view active and sets the URL for the WebView (named"webView") inside the detail view.
Similarly, the linkClicked method sets the URL for webView in response to onLinkClick events. (With respect to encapsulation, note that while FeedReader receives object data from both onSelect andonLinkClick events, those objects are not processed here, but are passed right back to the detail view.)
The viewSelected method is the handler for onSelectView events generated by this.$.pane. If we're navigating to the search view, viewSelected clears the contents of webView in preparation for the next time the detail view is displayed. It also hides the Back button, since that button isn't needed in this view. If we're navigating to the detail view, viewSelected makes the Back button visible


depends.js
There's one more thing we need to do to get this all to work--update depends.js to reflect the presence of the new Search.js file:
enyo.depends(
"source/FeedReader.js",
"source/Search.js",
"css/FeedReader.css"
);







Enyo Platform
Enyo의 기본이 되는 오브젝트는 Control이다. 이것은 DOM 노드처럼 동작한다. 사실은 각 control은 노드들로 바로 변환된다.
작은 control을 만들고 문서 바디에 뿌려주는 코드이다.
enyo.create({
content: "Hello World"
}).renderInto(document.body);

이것은 아래 HTML을 생성한다.
Hello World

control에 소스 작업을 하는것이 HTML을 다루는 것보다 이득이다. 더 간단하고 직관적임.
enyo.create({
components: [
// button with custom graphics
{kind: "Button"},
// input box with special features like hinting and graphic fx
{kind: "FancyInput"},
// one-of-many selector with custom graphics
{kind: "RadioGroup", components: [
{label: "Alpha"},
{label: "Beta"},
{label: "Gamma"}
]}
]
}).renderInto(document.body);

기본적인 자바 스크립트 사용예다.
// an object constructor
MyObject = function() {
this.data = [];
};

MyObject.prototype.toString = function() {
return this.data.join(", ");
};

// another object constructor, built on the first one
MySpecialObject = function() {
MyObject.apply(this, arguments);
};

MySpecialObject.prototype = new MyObject();

MySpecialObject.prototype.toNumber = function() {
return this.data.length;
};

// Make an instance
mso = new MySpecialObject();

객체 지향에 맞추기 위해 Enyo는 constructor를 만드는 메서드를 제공한다. constructor는 어떤 특별한 kinds 라는 것을 가지고 있다. kind를 생성하기 위한 메서드는 enyo.kind이다. 여기 enyo.kind의 예이다.

// a kind
enyo.kind({
name: "MyKind",
constructor: function() {
this.data = [];
},
toString: function() {
return this.data.join(", ");
}
});

// another kind, built on the first one
enyo.kind({
name: "MySpecialKind",
kind: "MyKind",
toNumber: function() {
return this.data.length;
}
});

// Make an instance
msk = new MySpecialKind();

이 예제에서 주의할 몇 가지이다.
The name of the kind is specified inside of the property block. This name will become a global variable that references the kind. Putting the name inside the block gives you an easy way to use namespacing. For example, say you write:
enyo.kind({name: "Super.Special.Kind"});

The namespaces Super and Super.Special will be created for you, and Super.Special.Kind will reference the new constructor.
Initialization code is placed in a special method called constructor. This is very similar to the body of theMyObject function in the first example. The main difference is that the constructor method is not called when inheriting from a kind (if you look closely at the first example, you can see that MyObject is called to create the prototype for MySpecialObject, which ends up creating an extraneous data array in theMySpecialObject prototype.)
To make a new kind that inherits from an old one, specify the old one's name in the new one's kindproperty. In the example, MySpecialKind is based on MyKind.

All these kinds may start to sound confusing, but it all boils down to one simple idea: whenever we make something, whether a constructor or an instance, we say what kind it's based on. When creating an instance, for example, we might do this:
enyo.create({kind: "aKind"});

(Note: The input for enyo.create is a JavaScript object that describes the object to create. This kind of input is sometimes called a "property block" or "property bag".)
Similarly, to make a new kind based on an existing kind, we could do this:
enyo.kind({kind: "aKind"});

This consistency makes the syntax easy to remember. It's turtles all the way down.

Cassandra (6/12)

6. 카산드라 설정하기

이 챕터에서 카산드라를 어떻게 설정하는지 살펴본다. 우리는 keyspace, 복사본 셋업을 생성해보고 적당한 복사본 거치 전략을 살펴본다.
카산드라는 설정을 하지 않아도 잘 작동한다. 단지 다운로드하고 압축을 풀고 그리고 디폴트 설정으로 작동하도록 프로그램을 돌려 서버를 시작하면 된다.
우리는 클러스터 안의 노드의 행동에 영향을 미치는 면을 살펴본다. 그것은 퍼포먼스 그리고 복사, 파티셔닝, 고발과 같은 메타 동작이다. 퍼포먼스 튜닝은 11장에서 별개의 주제로 자세히 살펴본다.

6.1. Keyspaces

키스페이스는 XML 설정화일에 정적으로 설정이 되기는 하였으나 0.7 버전에 오면서 API를 사용하여 키스페이스와 컬럼군을 생성할 수 있다.
카산드라 0.6과 그 이전 버전에서 당신의 클러스터와 컬럼군을 위한 설정은 storage-conf.xml이라는 파일에 저장이 되었다. 그리고 XML에서 YAML로 변환되었다. 그래서 storage-conf.xml과 Cassandra.yaml 로의 참조를 볼 수 있다. 그러나 버전 0.7 은 다이나믹 로딩이 되면서 키스페이스와 컬럼군 정의의 모든 생성과 수정은 Thrift API을 통하거나 command line interface (CLI)를 통하게 되었다.
버전 0.7 카산드라에서부터 당신은 당신의 스키마에 변경을 하기 위해서 API를 사용할 수 있다. 이것은 당신이 SQL 에서 CREATE TABLE, ALTER TABLE 과 같은 Data Definition Language (DDL) 을 쓰던것과 매우 유사하다.
일단 당신의 스키마가 당신 시스템의 키스페이스에 로드되면 스키마에 대한 모든 변경은 Thrift 인터페이스를 통해야 한다. 이 메서드들은 모두 system에 의해 앞에 이름이 붙는다. 이것은 당신에게 이것들이 매우 영향력이 강한 스키마 변경 동작으로서 시스템 키스페이스를 수정한다는 것을 상기시킨다.
- System_add_keyspace
키스페이스를 생성한다.
- System_rename_keyspace
키스페이스의 이름을 변경한다. 이 메서드는 모두 끝날때까지 블록한다는 것을 주의한다.
- System_drop_keyspace
모든 키스페이스를 지운다.
- System_add_column_family
컬럼군을 생성한다.
- System_drop_column_family
컬럼군을 지운다.
- System_rename_column_family
컬럼군의 이름을 바꾼다. 이 메서드는 완료될 때까지 블록된다는 것을 주의한다.
예를 들어 0.7 버전의 CLI를 사용하여 새로운 키스페이스를 만들 때 당신은 아래와 같은 command-line 클라이언트를 런치할 수 있다.
[default@unknown] connect 127.0.0.1/9160
Connected to: "Test Cluster" on 127.0.0.1/9160
[default@unknown] create keyspace Test1 with replication_factor=0
610d06ed-a8d8-11df-93db-e700f669bcfc
[default@unknown] describe keyspace Test1
Keyspace: Test1

default@unknown 이라는 것은 MySQL과 비슷하다. 이것은 username을 인증하고 현재 사용하는 키스페이스 이름이 프롬프트 처럼 행동하게 한다. 그래서 우리는 키스페이스 커맨드를 사용하여 다른 키스페이스로 스윗치 할 수 있다.
use [ 'password']
이제 CLI에서 다른 새 키스페이스로 스윗치했으니 컬럼군을 추가할 수 있다.
[default@Test1] create column family MyCF
4105a82f-ad51-11df-93db-e700f669bcfc
당신은 with flag를 이용해 키스페이스와 컬럼군을 생성할 때나 and flag를 사용해 더 많은 셋팅을 만들 때 추가 적인 설정을 지정할 수 있다.
[default@MyKeyspace] create keyspace NewKs with replication_factor=1
당신이 필요에따라 쓸 수 있는 다른 키스페이스 관련 CLI 커맨드도 있다.
drop keyspace
drop column family
rename keyspace
rename column family
API를 사용해 이것을 할 수 도 있는데 아래와 같다.
package com.cassandraguide.config;
import java.util.ArrayList;
import java.util.List;
import org.apache.cassandra.thrift.Cassandra;
import org.apache.cassandra.thrift.CfDef;
import org.apache.cassandra.thrift.KsDef;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
public class ConfigAPI {
private static final String HOST = "localhost";
private static final int PORT = 9160;
/**
* Creates a new keyspace and CF.
*/
public static void main(String... args) throws Exception {
String keyspaceName = "DynamicKeyspace";
System.out.println("Creating new keyspace: "+ keyspaceName);
//Create Keyspace
KsDef k = new KsDef();
k.setName(keyspaceName);
k.setReplication_factor(1);
k.setStrategy_class("org.apache.cassandra.locator.RackUnawareStrategy");
List cfDefs = new ArrayList();
k.setCf_defs(cfDefs);
//Connect to Server
TTransport tr = new TSocket(HOST, PORT);
TFramedTransport tf = new TFramedTransport(tr);
TProtocol proto = new TBinaryProtocol(tf);
Cassandra.Client client = new Cassandra.Client(proto);
tr.open();
//Add the new keyspace
client.system_add_keyspace(k);
Keyspaces | 101
System.out.println("Added keyspace: "+ keyspaceName);
}
}
당신이 새로운 키스페이스를 생성하기 위해 해야 할 것은 이름을 정하고, 복사 거치 전략, 그리고 복사본 팩터, 그리고 한정된 컬럼 군을 만들어 그 키스페이스를 정한다. 이제 당신은 새 MyCF 컬럼군에 값을 입력할 준비가 되었다.

6.2. 컬럼 군 만들기

컬럼군을 만들기 위해서는 CLI나 API를 사용해야 한다. 여기 컬럼군을 만들 때 사용가능한 옵션들이 있다.
- Column_type
Super 이거나 Standard
- Clock_type
오직 가능한 값은 Timestamp
- comparator
가능한 옵션은 AsciiType, BytesType, LexicalUUIDType, LongType, TimeUUIDType, UTF8Type 이다.
- Subcomparator
column type이 super일 때 subcolumn에 사용되는 비교자의 이름이다. 가능한 옵션은 비교자(comparator)와 같다.
- Reconciler
컬럼 버전이 충돌하는 것을 해결해주는 클래스의 이름이다. 오직 가능한 값은 Timestamp이다.
- Comment
스트링으로 표시되는 사람이 읽기 가능한 커멘트이다.
- Row_cached
캐쉬하기 위한 행의 수이다.
- Preload_row_cache
행 캐쉬를 자동으로 로드하기 위해 이 값을 true로 셋팅한다.
- Key_cache_size
캐쉬로 끌어들여오기 위한 키의 개수
- Read_repair_chance
가능한 값은 0.0과 1.0 사이의 값이다
[default@Keyspace1] create column family MyRadCF with
column_type='Standard' and comparator='UTF8Type' and rows_cached=40000
3ae948aa-ae14-11df-a254-e700f669bcfc

6.3. 0.6에서 0.7로의 전환

conf디렉토리의 Cassandra.yaml 파일은 storage-conf.xml 파일의 대체로 보인다. 이것은 0.6 이전 버전의 설정화일이다. 그러나 이 YAML 파일은 XML에서 YAML로 자신들의 설정파일을 업그레이드 하려는 사용자들을 위해 있다. 이전에 보여진 것처럼 대신Thrift API를 system_ prefix가 붙은 것들을 호출해서 사용할 수도 있다.
만약 당신이 0.6 버전을 사용하여 storage-conf.xml 이 있다면, 이것을 bin/config-converter를 써서 YAML로 변환할 필요가 있다. 이것은 storage-conf.xml에서 Cassandra.yaml 을 만들어 낸다. Org.apache.cassandra.service.StorageServiceMBean 에는 loadSchemaFromYAML 이라는 것이 있어서 JMX를 통해 노출되는 동작이다. 이것은 당신의 seed 노드에서 Cassandra.yaml 에서 당신의 스키마 변경을 로드하도록 카산드라에게 시킬수 있다. 당신 클러스터의 새로운 노드는 시작하자마자 스키마 업데이트를 가져올 것이다. Seed 노드도 다르지는 않다. 당신은 이 메서드를 어느 한 노드에 실행할 수 있고 모든 노드는 스키마 변경을 받고 그것에 대해서 알려줄 것이다. 이것은 카산드라 시스템의 키스페이스의 정의를 저장하는 동작이다. 당신은 이것을 실행한 후에 YAML을 변경하기 원치 않게 된다. 이후의 변경에 대해서는 당신은 API나 CLI를 사용할 필요가 있다. 이 동작은 변환이 완료된 후에는 필요없게 된다.
디폴트를 사용하는 것은 간단한 테스팅이 되지만 복사 거치 전략, 복사본 factor, end point snitch 등을 설정하는 것을 살펴본다. 이 설정들은 키스페이스 안에서 설정가능하다. 그래서 다른 키스페이스는 다른 설정을 각각 가질 수 있다.

6.4. 복사본

복사본 거치 전략은 당신이 클러스터에 노드를 추가할수록 더욱 중요해지고 있다. 카산드라에서 노드라는 용어는 가장 일반적인 케이스를 지칭한다. 그것은 카산드라 소프트웨어를 구동시키는 서버를 말한다. 그리고 한 개나 그 이상의 카산드라 서버들의 링에 참여하여 움직인다.
모든 카산드라 노드는 무언가의 복사본이다. 주어진 키의 범위에서 그 범위내의 복사본이 아닌 카산드라 노드가 있을 수도 있다. 만약 replication factor가 1로 셋팅되어 있으면 한 싱글노드로만 쓰여지게 되어있다. 만약 그 노드가 다운되면 그 노드에 저장되어 있는 값은 접근할 수 없게 된다. Replication factor가 2로 셋팅되어 있으면 당신 클러스터의 두 개 노드가 그 값을 가지고 각각 서로의 복사본이 되게 된다. 그래서 당신의 replication factor가 N으로 설정되어 있으면 각 노드는 N 범위의 복사본처럼 행동하게 되고 N이 1로 셋팅되어 있어도 마찬가지 이다.
카산드라 클러스터, 즉 호스트들의 모임은 여기서 보듯이 ring 이라고 보통 불린다. 링의 각 노드들은 고유한 각 한 개의 토큰이 할당된다. 각 노드는 이전 노드의 토큰으로부터 또다시 토큰을 갖고 값의 범위 만큼 주도권을 주장한다. Org.apache.cassandra.dht.Range 클래스에 이것들이 담겨있다. 정확히 한 토큰이 어떻게 표현되는지는 당신이 사용하는 partitioner에 달려있다. (partitioner에 대한 정보는 이장의 후에나오는 partitioner 를 참조한다.)
복사본을 만들 때 첫 복사본은 그 토큰의 key 범위를 주장하는 노드에게 거치된다. 모든 남아있는 복사본은 설정가능한 복사본 전략에 따라서 분산된다. 우리는 이것에 대해서 지금 살펴보겠다.

6.5. 복사본 거치 전략

가장 간단한 용어로서 설정 파일에 대한 복사본 거치 전략을 만족하기 위해서는 당신은 단지 org.apache.cassandra.locator.AbstractReplicationStrategy 를 확장하는 자바 클래스 이름을 제공하기만 하면 된다. 이 설정의 목적은 node picker가 작동하는 방식을 셋팅하기 위한 것이다.
인터페이스는 구현을 해야하는 11개의 public 메서드를 제공한다. 만약 당신의 전략이 추상적인 부모가 제공할 수 있는 메서드 구현을 사용할 수 있다면 당신은 당신이 원하는 메서드만을 오버라이드 할 수 있다. 당연히 당신은 이것을 당신이 직접 구현해야 하지는 않는다. 카산드라는 이 인터페이스의 세가지 구현을 제공한다. Rack-Aware 전략, Rack-Unaware 전략, Data Center Shard 전략.
올바른 복사 전략을 고르는 것은 전략이 어느 노드가 어느 키 범위에 대하여 책임이 있는지 결정하기 때문에 중요하다. 여기서 의미하는 바는 당신은 어느 쓰기 동작이 어느 노드에 받아져야 하는지 결정하는 것이며 각 다른 시나리오에 대우 큰 영향을 가지고 있다. 당신이 만약 당신의 클러스터를 모든 쓰기가 두 개의 데이터 센터에 가도록 한다면 퍼포먼스가 떨어질 것이다. 다양한 전략은 더 큰 유연성을 주어 카산드라를 당신의 네트워크 토팔러지나 필요에 따라 튜닝할 수 있게 해준다.
첫번째 복사본은 항상 토큰이 해당하는 범위를 주장하는 노드이지만 복사본의 나머지는 당신이 사용하는 복사본 거치 전략에 따라 위치될 것이다.

6.6. 간단한 전략

간단한 전략은 Rack-Unware 전략의 새로운 이름이다.
설정 파일에서 디폴트로 쓰이는 전략은 org.apache.cassandra.locator.RackUnawareStrategy 이다. 이 전략은 단지 추상 부모 구현에서calculateNaturalEndpoints 메서드를 오버라이드한다. 이 전략은 데이터 센터에서 자신의 위치를 모르는 방식으로 한 데이터 센터의 복사를 위치한다. 이것은 구현이 이론적으로 빠르다는 것을 의미하고 주어진 키를 가진 다음 노드가 다른 랙에 위치한다는 것을 의미한다. 이것은 아래 그림에 보여졌다.

그림6-1. 간단한 전략은 토팔러지에 상관없이 한 데이터센터에 복사본을 둔다.
여기서 링의 다음 N 노드는 복사본을 갖도록 선택되어 지고 전략은 데이터 센터에 대한 것이 없다는 것을 본다. 두번째 데이터 센터는 다이어그램에서 전략은 그것을 모른다는 것을 강조한다.

6.7. 오래된 네트워크 토팔러지 전략

카산드라가 제공하는 두번째 복사본 거치 전략은 org.apache.cassandra.locator.RackAwareStrategy 로서 현재 Old Network Topology Strategy라고 불린다. 이것은 같은 데이터 센터에서 다른 랙에 데이터를 분산시키기 위해서 쓰였던 것이다. RackUnawareStrategy처럼 calculateNaturalEndpoints 의 추상 부모 구현을 단지 오버라이드 하는 것이다. 이 클래스는 원래의 이름이 말하듯이 정말 데이터 센터 랙의 거치를 알고있다.
당신이 두개의 데이터 센터를 가지고 있다고 해보자. DC1 과 DC2이다. 둘 다 카산드라 서버이다. 이 전략은 DC1에 복사본을 두고 각각 다른 가용한 랙에 둔다. 이것은 마찬가지로 다른 랙 DC2 에는 다른 복사본을 둔다. Rack-Aware 전략은 특히 당신이 같은 카산드라 클러스터에 노드들을 가지고 두 개의 데이터 센터가 있으며 복사본 factor 3을 쓰는 것이다. 이 전략은 그림 6-2에 표현되어 있다.
이 전략은 당신이 대체할 데이터 센터의 세번째 노드가 접근 될 때 추가적인 latency 의 잠재적 가격에도 더 높게 보장하고 싶을 때 사용한다. 당신이 단지 한 데이터 센터에서 카산드라를 운영한다면 Rack-Aware 전략을 사용하는 것은 의미가 없다. 카산드라는 다른 데이터 센터에서 운영하는데 최적화 되어 있다. 그러나 그 높은 가용성을 사용하는 것도 당신에게는 중요한 고려사항일 것이다. 만약 당신의 카산드라 클러스터가 여러 개의 데이터 센터에 걸쳐있다면 이 전략을 사용하는 것을 고려해보라.
그림6-2. Old Network Topology Strategy 가 두번째 복사본을 다른 데이터 센터에 두고 다른 것은 같은 데이터 센터의 다른 랙에 둔다.

당신이 Rack-Aware 전략을 사용한다면 Rack-Aware Snitch도 사용해야 하는데 Snitch는 뒤에 설명되어 있다.


6.8. 네트워크 토팔러지 전략

이 전략은 카산드라의 0.7 버전부터 포함되었다. RackAwareStrategy보다 데이터 센터에서 복사본들이 어떻게 거치될지 더 공정하게 보여주게 된다. 이것을 사용하기 위해서는 각 데이터 센터에서 복사본 전략을 어떻게 할지 정해주는 파라미터를 넣으면 된다. 이 파일은 org.apache.cassandra.locator.DataCenterShardStrategy 클래스에 의해서 읽혀지고 수행된다. 그래서 구현은 좀 유동적이다. 데이터 센터의 조각 전략은 그림 6-3에서 볼 수 있다.
이 전략은 datacenter.properties라는 파일을 사용했었다. 그러나 0.7버전에서 이 메타데이터는 키스페이스와 설정 옵션이라고 할 전략에 직접 붙었다.

그림6-3. 네트워크 토팔러지 전략은 복사본을 다른 데이터센터에 둔다. 그리고 나머지는 첫번째 데이터 센터의 다른 랙에 지정했듯이 둔다.


6.9. Replication Factor

Replication Factor는 카산드라 클러스터에서 얼마나 많은 각 데이터의 복사본이 저장되고 분산되는지 지정한다. Replication_factor 라는 셋팅에서 지정된다.
아마 클러스터에서 더 많은 노드를 가지고 있을수록 replication factor를 늘리고 싶은 것은 직관적일 것이다. 그러나 무조건 이 법칙을 따르지는 않는다. 그 대신 당신이 요구하는 서비스 레벨을 맞추기 위해서 replication factor를 증가시킬 것이다.
Replication factor를 1로 했을 때 당신의 데이터는 클러스터 안의 한 노드에서만 존재할 것이다. 그 노드가 이상이 있다면 데이터가 가용하지 않게 되는것이다. 이것은 카산드라가 노드들간에 조정을 하기 위해 더 많은 일을 한다는 것을 의미한다. 만약 주어진 키에 대해 모든 데이터가 노드 B에 있다면 모든 클라이언트는 그 키에 대해서 요청하고 노드 A는 재전송 되기를 필요로 할 것이다.
당신은 이 값을 당신 클러스터안 노드개수보다 크게 잡아서는 안된다. 이것은 아무 의미가 없기 때문이다. 그러나 당신은 이 셋팅으로 카산드라는 튜닝하기를 원치않는다. 카산드라는 읽는 복사본 카운트와 쓰는 복사본 카운트가 replication factor보다 커야 높은 일관성을 보여준다.
그래서 당신이 클러스터 안에 10개의 노드를 가지고 있다면 당신은 맥시멈으로 10을 replication factor로 정해줄 수 있다. 하지만 그렇게 하기를 원치 않을 수 있다. 그것이 카산드라가 그 가용성을 방해하지 못하도록 하기 때문인데 왜냐하면 한 개의 노드가 다운되면 높은 일관성을 갖지 못하기 때문이다. 그 대신 replication factor를 적당한 값으로 정해준후에 일관성 레벨을 높였다 낮추었다 하며 튜닝을 한다. 일관성 레벨은 당신이 replication factor에 의해서 정의된 노드의 개수보다 더 쓰게 하지는 않을 것이다. 여기서 적당한 숫자란 상당히 낮을 것이다. 1이란 가장 낮은 숫자이다. 그러나 ANY는 1과 비슷하거나 오히려 일관성이 없다. 이것은 당신이 쓰고 긴시간 후에 쓰여진 값을 보지 못하기 때문이다. 만약 클러스터의 어느 노드가 살아있다면 ANY는 성공할 것이다.

6.10. Replication Factor 증가하기

Replication factor는 서버가 실행중에 바뀌기를 원하지는 않고 시작하기 전에 정해지기 바란다. 그러나 당신의 애플리케이션이 커지고 노드를 증가할 필요가 생기면 replication factor를 증가 시켜야 한다. 이것을 할 때 따라야 할 간단한 가이드라인이 있다. 첫째로 replication factor 값을 증가시킨 후에는 노드를 재시작 시켜주어야 한다. 카산드라는 이것을 재시작한 후에 데이터를 재분배하는 등 고치는 동작을 한다. 이 고치는 과정동안에 어떤 클라이언트들은 데이터를 아직 가지지 않은 복사본에 연결이 되었다면 데이터가 존재하지 않는다는 통지를 받는다.
Replication factor 를 1에서 2로 증가시키는 더 빠른 방법은 노드 툴을 사용하는 것이다. 첫째로 원래의 노드에 빨아들이는 동작을 하여 모든 데이터가 SSTable에 배출되었는지 확실히 한다. 그리고 더 이상의 쓰기 동작을 받아들이지 않게 노드를 멈춘다. 다음으로 키스페이스로부터 데이터 파일을 복사한다. 카산드라 내부 키스페이스의 값들을 복사하지는 않게한다. 그 데이터 파일들을 새 노드에 붙인다. Replication factor가 2가 되도록 양쪽 노드의 설정을 바꾼다. Autobootstrap 이 양쪽 노드에서 false로 설정되게 한다. 양쪽 노드를 재시작한 후에 노드 툴을 실행한다. 이 과정들은 클라이언트가 잠재적인 그른 빈 읽기를 하지 않도록 보장한다.
이것을 보여주기 위해 IP주소가 1.5, 1.7 그리고 1.8로 끝나는 세 개의 노드를 가지고 replication factor는 1로 설정했다. 노드 1.5에 연결하고 이전에 존재하지 않던 컬럼에 쓰기를 한다.
cassandra> connect 192.168.1.5/9160
Connected to: "TDG Cluster" on 192.168.1.5/9160
cassandra> set Keyspace1.Standard2['mykey']['rf']='1'
Value inserted.
이제 노드 1.5를 닫고 1.7에 연결한후 여기 설정한 값을 읽어오도록 한다.
cassandra> connect 192.168.1.7/9160
Connected to: "TDG Cluster" on 192.168.1.7/9160
cassandra> get Keyspace1.Standard2['mykey']['rf']
Exception null
나는 replication factor가 1이어서 1.5 노드에만 값이 씌어졌고 그 노드를 잃어버렸을 때 클러스터의 다른 노드들은 값을 가지지 못했다. 아무 클라이언트도 1.5가 다시 온라인이 될 때까지 rf컬럼에서 값을 읽어오지 못했다.
그러나 replication factor를 업데이트하는 영향을 알아보자. 우리는 노드 1.5와 1.7에서 1에서 2로 바꾸고 재시작하였다. 우리는 노드 1.7에서 rf 컬럼에 새 값을 넣어보자.
cassandra> connect 192.168.1.7/9160
Connected to: "TDG Cluster" on 192.168.1.7/9160
cassandra> get Keyspace1.Standard2['mykey']['rf']
Exception null
cassandra> set Keyspace1.Standard2['mykey']['rf']='2'
Value inserted.
Exception null 응답이 예제 중에 나오는 것은 값이 1.7 노드에 이전에는 존재하지 않았다는 것을 알려준다. 1.7 노드를 닫고 1.5에 연결하여 값을 보자. 왜냐하면 우리의 replication factor때문에 1.7에 값을 쓰고 1.5로 복사되었다.
cassandra> connect 192.168.1.5/9160
Connected to: "TDG Cluster" on 192.168.1.5/9160
cassandra> get Keyspace1.Standard2['mykey']['rf']
=> (column=rf, value=2, timestamp=1279491483449000)
일반적인 가이드라인으로써 당신은 쓰기 능력은 노드의 숫자를 replication factor로 나눈 값이라고 예상할 수 있다. 10개 노드를 가진 클러스터에서 일반적으로 10000 의 초당 쓰기는 replication factor 1이다. 당신이 replication factor를 2로 증가하면 초당 5000 쓰기를 예상할 수 있다.

6.11. Partitioners

Partinioner의 목적은 행 키가 어떻게 정렬될지 정하게 하는 것이고 이것은 데이터가 어떻게 노드들 간에 분산이 되는지에 큰 영향이 있다. 그것은 또한 행의 범위에 대해서 쿼리하는 옵션에 대해서도 영향이 있다. 거기에는 몇 가지 다른 partinioner가 있고 우리가 여기서 살펴본다.
당신은 설정파일의 Partitioner 요소의 값을 업데이트 하거나 API를 통해서 Partitioner를 설정할 수 있다. Org.apache.cassandra.dht.IPartitioner 인터페이스를 구현하는 클래스의 이름으로 값을 처리한다. 카산드라는 세 가지를 제공한다. Random partitioner 는 디폴트이고, order-preserving partitioner와 collating order-preserving partitioner이다. 카산드라의 플러거블한 디자인 때문에 당신의 고유의 partitioner를 org.apache.cassandra.dht.IPartitioner 클래스를 구현해서 만들수도 있고 그것을 카산드라 클래스패스에 두어야 한다.

6.12. Random Partitioner

랜덤 partitioner는 org.apache.cassandra.dht.RandomPartitioner에 의해 구현되고 카산드라의 디폴트이다. BigIntegerToken을 사용하고 MD5 해쉬가 적용되며 노드 링에서 어디에 키를 두어야 할 지 결정한다. 이것은 당신의 키를 클러스터 전반에 고르게 뿌리는 장점이 있고 분산이 랜덤이기 때문에 그렇다. 이것은 효율적이지 않은 범위 쿼리라는 단점이 있는데 특정한 범위에서 키는 다양한 위치에 링안에서 위치될 수 있기 때문이다. 키 범위 쿼리는 근본적으로 랜덤 순서로 데이터를 반환한다.

6.13. Order-Preserving Partitioner

Order preserving partitioner 는 org.apache.cassandra.dht.OrderPreservingPartitioner에 의해 구현되고 IPartitioner을 구현한다. 이런 타입의 partitioner를 사용하면 토큰은 키에 기반한 UTF-8 스트링이다. 행은 그래서 키 순서에 따라 저장되고 정렬 순서와 함께 물리적 데이터의 구조로 정렬된다.
Order-preserving partitioning(OPP)를 사용하기 위해 당신의 컬럼군을 설정하는 것은 범위 분할을 수행하게 한다.
OPP가 random partitioning보다 범위 쿼리에 더 효과적이지는 않다는 것을 알아둘 필요가 있다. 단지 순서를 정할 뿐이다. 그것은 현실 세계에서 데이터는 일반적으로 고르게만 쓰여지지 않기 때문에 잠재적으로 균형이 매우 균형이 안잡힌 링을 만들수 있다는 단점이 있다. 예를 들면 스크래블 게임에서 글자에 할당된 값을 고려해보자. Q와 Z는 잘 이용되지 않는다. 그래서 높은 값을 가지고 있다. OPP로는 어떤 노드에서 많은 데이터 그리고 다른 노드에는 적은 데이터를 가지고 끝날 수 있다. 많은 데이터가 저장된 노드에는 링이 균형이 안잡히게 하고 hot spot으로 종종 불린다. 순서정렬 면 때문에 사용자는 OPP에 사로잡히게 된다. 그러나 OPP사용은 당신의 동작 팀이 노드툴의 loadbalance와 move 동작을 써서 노드를 주기적으로 수동적 재균등화를 해주어야 한다는 것을 의미한다.
만약 당신이 당신의 클라이언트에서 범위 쿼리를 수행하기 원한다면 order-preserving partitioner를 사용해야 하거나 order-preserving partitioner 를 순서대로 모아야 한다.

6.14. Order-Preserving Partitioner를 모으기

이 partitioner 순서 키는 United States English locale (EN_US) 을 따른다. OPP처럼 키가 UTF-8인 것을 요구한다. 이름으로는 OPP를 확장하는 것처럼 보이지만 그렇지 않다. 그 대신 이 클래스는 AbstractByteOrderedPartitioner 를 확장한다. 이 partitioner는 사용성이 제한되어 잘 사용되지 않는다.

6.15. Byte-Ordered Partitioner

0.7 버전에서 새롭게 ByteOrderedPartitioner가 추가되었다. 이것은 order-preserving partitiner 이고 데이터를 로우 byte로 다룬다. 그대신 order-preserving partitioner와 collating order-preserving partitioner가 하는 것처럼 그것을 스트링으로 변환한다. 당신이 당신의 키가 스트링이 되고 검사하지 않는 order-preserving partitioner를 필요로 한다면 BOP 가 퍼포먼스 향상을 위해서 추천하고 싶다.

6.16. Snitches

Snitch의 일은 관련 호스트의 근접성을 단순히 결정하는 것이다. Snitch는 당신 네트워크 토팔러지의 어떤 정보들을 모아 카산드라가 효과적으로 요청을 전달할 수 있게 한다. Snitch는 어떤 노드가 다른 노드와 어떻게 위치상 관계가 있는지 알아낸다. 데이터 센터를 추론하는 것은 replication strategy의 할일 이다.

6.17. 간단한 Snitch

디폴트로 카산드라는 org.apache.cassandra.locator.EndPointSnitch를 사용한다. 이것은 단지 각 노드의 IP주소의 다른 octet을 비교함으로써 작동한다. 만약 두 개의 호스트가 IP주소의 두번째 octet에서 같은 값을 가진다면 같은 데이터 센터에 있는 것으로 된다. 만약 두개의 호스트가 IP주소의 세번째 octet에서 같은 값을 가진다면 그들은 같은 랙에 있는것으로 된다. “Determined to be” 는 카산드라가 당신의 서버가 다른 VLAN 이나 subnet에 위치해 있다는 가정에 기반한다.
당신은 설정 파일에서 요소의 값을 업데이트 함으로써 endpoint snitch 구현을 사용할 지 설정하게 된다. 다른 선택은 PropertyFileSnitch 이다.

6.18. PropertyFileSnitch

Org.apache.cassandra.locator.PropertyFileSnitch 는 contrib. 안에 있었다 그러나 0.7에 오면서 메인 코드 베이스로 옮겨졌다. 이 snitch는 당신이 cassandra-rack.properties라고 불리는 파일에 표준 키/값 프로퍼티의 위치를 명시하여 Rack-Aware Strategy 를 사용하는데 콘트롤 할 수 있게 해준다.
이 snitch는 Digg에 의해서 공헌이 있었다. 그것은 카산드라를 사용하고 정기적으로 그 개발에 공헌해왔다. 이 snitch는 카산드라가 두개의 IP가 같은 데이터센터 혹은 같은 랙에 있는지 확실하게 알게 해준다. 왜냐하면 당신이 거기에 있다고 말해주기 때문이다. 이것은 아마도 당신이 서버를 많이 옮긴다면 가장 유용할 것이다. 동작이 그렇듯이 당신이 상속된 IP scheme을 가지고 있다면 그렇다.
Cassandra-rack.properties의 디폴트 설정은 아래와 같다.
# Cassandra Node IP=Data Center:Rack
10.0.0.10=DC1:RAC1
10.0.0.11=DC1:RAC1
10.0.0.12=DC1:RAC2
10.20.114.10=DC2:RAC1
10.20.114.11=DC2:RAC1
10.20.114.15=DC2:RAC2
# default for unknown nodes
default=DC1:r1

여기서 두 개의 랙에 두개의 데이터 센터가 있는 것을 볼 수 있다. 카산드라는 좋은 퍼포먼스로 동등한 배분을 결정할 수 있다.
어떤 랙이 그 IP의 노드를 가지고 있고 어떤 데이터 센터에 있는지 명시하기 위해 이 파일안의 클러스터안 각 노드의 값을 기록하여 업데이트 하라. 만약 당신이 노드를 추가하고 제거하는 것을 자주한다면 이것은 관리하기 어렵게 느껴질수 있지만 이것은 하나의 차선책이라는 것을 기억하라. 이것은 당신에게 좀더 많은 컨트롤과 좋은 실행시의 퍼포먼스를 위해 작은 유연성과 관리의 용이함은 없앤것이다. 대신에 당신은 그것들이 어디에 있는지 말해준다.

6.19. 클러스터 생성

당신은 카산드라를 한 개의 기기에서 실행할 수도 있다. 이것은 읽기와 쓰기 등 어떻게 하는지 배우기 위해서는 좋다. 그러나 카산드라는 여러 개의 기기의 클러스터에서 동작하도록 만들어졌다. 이 섹션에서 링에서 여러 개의 카산드라 객체가 어떻게 서로 통신하는지 등을 보게 될 것이다.
여기서 하려는 것은 링에서 여러 개의 기기를 가지고 샘클 키스페이스를 가지고 해보는 것이다. 그래서 디폴트 설정파일로 시작하며 키스페이스 정의 등 간단한 클러스터를 만들기 위하여 설정을 조금 바꾸어 본다.
이것을 위해서 우리는 192.168.1.5 그리고 192.168.1.7 두개의 카산드라를 가지고 클러스터를 만들어본다. 카산드라를 시작할 때 설정파일을 읽어 현재 노드가 어떻게 브로드캐스트하기 원하는 지 결정한다. 어떤 호스트에 어떤 IP 주소를 쓰고 어떤 포트를 사용할 것인지 등등.
당신은 현재 노드에게 다른 노드를과 어떻게 참여하여 함께 링을 만들지를 말해주어야 한다. 이것은 설정 파일을 텍스트 에디터에서 열어서 여기서 보듯이 몇 개의 설정을 조정해주는 간단한 일이다.
클러스터에 추가된 새 노드는 seed node라는 것이 필요하다. 만약 노드 A가 노드 B를 위한seed node로 작동하면 노드 B가 온라인 되면 노드 A를 데이터를 가져오기 위한 참조 포인트로 사용할 것이다. Seed node는 AutoBootstrap설정을 무시하는데 이는 그것이 클러스터의 첫번째 노드라고 가정하기 때문이다.

6.20. 클러스터 이름 변경

카산드라 클러스터는 한 기기가 다른 클러스터에 가입하지 않도록 하기 위해 이름이 주어진다. 디폴트 클러스터의 이름은 설정파일에 “Test Cluster”이다. 당신은 그 이름을 바꿀수 있는데 을 업데이트하면 된다. 당신이 클러스터에 참여시킬 각 노드에 이것을 하면된다.

6.21. 클러스터에 노드를 추가하기

설정파일에는 디폴트로 false로 설정되어 있는 값들이 있다. 당신이 클러스터를 한 개의 노드로 운영을 하고 있다가 데이터를 추가하고 더 많은 노드를 추가한다고 하자. 이것은 autobootstrap 요소를 사용함으로써 할 수 있다. 나는 1.5로 끝나는 IP주소를 가진 한 개의 카산드라 서버를 NodeTool에서 본대로 가지고 있다.
$ bin/nodetool -h 192.168.1.5 ring
Address Status Load Range Ring
192.168.1.5 Up 433.43 MB 126804671661649450065809810549633334036 |<--|
이 서버는 자신의 주소를 seed로 가지고 있다. 이 노드가 seed로 행동하기 때문에 우리는 설정에서 autobootstrap을 false로 놔둔다. Seed에 새로운 노드를 추가하고 싶으면 autobootstrap을 우선 해야하고 seed로 변경해야 한다. 노드 B가 노드 A를 seed로 사용하려면 노드 B는 노드A를 설정파일에서 seed로 참조하도록 해야한다. 그러나 노드A는 스스로 seed로 선언하지 않아도 된다.
이제 업무를 나누기 위해 다른 노드를 추가하겠다. 이 노드의 주소는 1.7 로 끝난다. 우선 새로운 노드가 데이터를 받아들일수 있도록 클러스터에서 모든 노드가 같은 이름과 같은 키스페이스 정의를 가지고 있는지 확인한다. 두번째 노드의 설정 파일을 에디트해서 첫번째것이 seed로 행동한다고 기록한다. 그리고 autobootstrap을 true로 설정한다.
두번째 노드가 시작했을 때 이것은 바로 첫번째 노드를 감지한다. 그러나 90초 동안 잠들면서 노드들이 얼마나 많은 데이터를 로컬로 저장하고 있는지 정보를 gossip하게 만든다. 그리고 첫번째 노드에서 bootstrap 토큰을 가져오고 어떤 섹션의 데이터를 가져와야 하는지 안다. Bootstrap 토큰은 가장 부하가 걸린 노드의 부하를 반으로 나누는 것이다. 다시 두번째 노드는 30초를 잠들고 bootstrapping을 시작한다.
INFO 11:45:43,652 Starting up server gossip
INFO 11:45:43,886 Joining: getting load information
INFO 11:45:43,901 Sleeping 90000 ms to wait for load information...
INFO 11:45:45,742 Node /192.168.1.5 is now part of the cluster
INFO 11:45:46,818 InetAddress /192.168.1.5 is now UP
INFO 11:45:46,818 Started hinted handoff for endPoint /192.168.1.5
INFO 11:45:46,865 Finished hinted handoff of 0 rows to endpoint /192.168.1.5
INFO 11:47:13,913 Joining: getting bootstrap token
INFO 11:47:16,004 New token will be 41707658470746813056713124104091156313 to a
ssume load from /192.168.1.5
INFO 11:47:16,019 Joining: sleeping 30000 ms for pending range setup
INFO 11:47:46,034 Bootstrapping
당신이 얼마나 많은 데이터를 가졌는지에 따라서 당신은 새로운 노드를 보게된다. Nodetool의 streams명령을 사용하여 bootstrap을 위하여 전환되는 데이터를 볼수 있다. Logfile을 보는것은 노드가 bootstrap을 끝내는 것을 결정하는 좋은 방법이다. 그러나 그것이 실제 일어나는 동안에 보는것이다. 마지막으로 새로운 노드는 첫번째 노드로부터 받아들이고 새로운 노드가 시작하였다는 성공적인 표시를 보게된다.
INFO 11:52:29,361 Sampling index for /var/lib/cassandra/data\Keyspace1\Standard
1-1-Data.db
INFO 11:52:34,073 Streaming added /var/lib/cassandra/data\Keyspace1\Standard1-1
-Data.db
INFO 11:52:34,088 Bootstrap/move completed! Now serving reads.
INFO 11:52:34,354 Binding thrift service to /192.168.1.7:9160
INFO 11:52:34,432 Cassandra starting up...
당신이 볼수 있듯이 전환하는데 4분 정도 걸린다.
Bootstrapping 동안 첫번째 seed노드는 이것과 같다.
INFO 11:48:12,955 Sending a stream initiate message to /192.168.1.7 ...
INFO 11:48:12,955 Waiting for transfer to /192.168.1.7 to complete
INFO 11:52:28,903 Done with transfer to /192.168.1.7
이제 다시 node tool을 작동하여 모든 것이 제대로 셋업이 되었는지 확실히 한다.
$ bin/nodetool -h 192.168.1.5 ring
Address Status Load Range Ring
126804671661649450065809810549633334036
192.168.1.7 Up 229.56 MB 41707658470746813056713124104091156313 |<--|
192.168.1.5 Up 459.26 MB 126804671661649450065809810549633334036 |-->|
카산드라는 이전 노드(1.5)로부터 절반의 데이터를 보냄으로써 1.7 노드의 자동 bootstrapping을 한다. 우리는 이제 클러스터에 두개의 노드를 가졌다. 잘 돌아가는지 보기위해 1.5 노드에 값을 추가해보자.
cassandra> connect 192.168.1.5/9160
Connected to: "TDG Cluster" on 192.168.1.5/9160
cassandra> set Keyspace1.Standard2['mykey']['col0']='value0'
Value inserted.
이제 두번째 CLI 클라이언트를 열어서 1.7 노드에서 그 값을 읽어본다.
cassandra> connect 192.168.1.7/9160
Connected to: "TDG Cluster" on 192.168.1.7/9160
cassandra> get Keyspace1.Standard2['mykey']['col0']
=> (column=col0, value=value0, timestamp=1278878907805000
당신은 추가적인 노드를 클러스터에 더하기 위해 이 과정을 반복할 수 있다.
만약 클러스터에서 한 개의 노드가 이상이 있다면 node tool에서 ?를 볼수있다.
$ bin/nodetool -h 192.168.1.5 ring
Address Status Load Range Ring
112711146095673746066359353163476425700
192.168.1.5 Up 459.26 MB 27647275353297313886547808446514704912 |<--|
192.168.1.7 ? 229.53 MB 112711146095673746066359353163476425700 |-->|

6.22. 여러 개의 seed node
카산드라는 당신이 여러 개의 seed node를 정할수 있게 해준다. Seed node는 다른 노드와의 컨택트 포인트 역할로 사용되고 카산드라는 어떤 호스트가 어떤 범위를 가지는지 알수 있도록 클러스터의 토팔러지를 배우게 된다.
디폴트로 설정파일은 한 개의 seed 값을 가진다.
seeds:
- 127.0.0.1
당신의 링에 더많은 seed node를 추가하기 위해서는 다른 seed 엘레먼트를 추가하면 된다. 두 개의 서버를 시드로 추가하려면 IP주소와 호스트 이름을 적어주고 두번째 인스턴스를 추가하면 된다.
seeds:
- 192.168.1.5
- 192.168.1.7
다음으로 이 기기의 듣는 주소를 업데이트 해야한다. 듣는 주소는 노드들이 서로를 어떻게 인지하는지이며 내부적인 커뮤니케이션에 사용된다.
listen_address: 192.168.1.5
마지막으로 RPC클라이언트가 어느 주소에 브로드캐스트하는지 변경해야 하는데 이는 다른 노드들이 이것을 어떻게 바라보는지 이다. 디폴트로 설정 파일은 rpc_address 를 가지고 있어서 로컬 호스트를 사용한다. 이 값을 각 기기의 실제 IP주소로 바꾸어 준다.
rpc_address: 192.168.1.5
rpc_address 셋팅은 클라이언트가 카산드라 노드들과 맺는 연결을 위해서만 쓰인다.
이제 카산드라를 재시작해서 다른 기기들에서 인스톨을 시작할수 있다. 당신의 클러스터에 여러 개의 노드를 추가했다면 이것과 비슷한 출력을 볼 것이다.
INFO 15:45:15,629 Starting up server gossip
INFO 15:45:15,677 Binding thrift service to /192.168.1.5:9160
INFO 15:45:15,681 Cassandra starting up...
DEBUG 15:45:16,636 GC for ParNew: 13 ms, 12879912 reclaimed leaving 11233080 used;
max is 1177812992
DEBUG 15:45:16,647 attempting to connect to /192.168.1.7
DEBUG 15:45:17,638 Disseminating load info ...
INFO 15:45:19,744 Node /192.168.1.7 is now part of the cluster
DEBUG 15:45:19,746 Node /192.168.1.7 state normal, token
41654880048427970483049687892424207188
DEBUG 15:45:19,746 No bootstrapping or leaving nodes -> empty pending ranges
for Keyspace1
INFO 15:45:20,789 InetAddress /192.168.1.7 is now UP
DEBUG 15:45:20,789 Started hinted handoff for endPoint /192.168.1.7
DEBUG 15:45:20,800 Finished hinted handoff for endpoint /192.168.1.7
DEBUG 15:46:17,638 Disseminating load info ...
이 출력은 내가 클러스터에서 두개의 노드를 가지고 있다는 것을 보여준다. 내가 지금 시작한 노드는 192.168.1.5를 듣고 있고 이미 시작하여 동작하고 있는 것은 192.168.1.7을 듣고 있다.

6.23. 다이나믹 링 참가

카산드라 클러스터에서 노드들은 다른 것들에게 방해를 주지 않고 다운되었다가 다시 돌려질 수도 있다. 우리가 이전에 설명했듯이 두개의 노드를 가지고 클러스터를 시작하였다고 하자. 우리는 둘 중의 한 노드가 다운되도록 에러를 유발할수 있다. 그리고 클러스터의 나머지가 아무이상 없다는 것을 확인한다.
INFO 15:34:18,953 Starting up server gossip
INFO 15:34:19,281 Binding thrift service to /192.168.1.7:9160
INFO 15:34:19,343 Cassandra starting up...
INFO 15:45:19,396 Node /192.168.1.5 is now part of the cluster
INFO 15:45:20,176 InetAddress /192.168.1.5 is now UP
INFO 16:13:36,476 error writing to /192.168.1.5
INFO 16:13:40,517 InetAddress /192.168.1.5 is now dead.
INFO 16:21:02,466 error writing to /192.168.1.5
INFO 16:21:02,497 error writing to /192.168.1.5
에러 메시지는 1.5 노드가 되돌아 올때까지 되풀이 된다. Nodetool을 동작해보면 한 노드가 다운되었고 다른 것은 아직 이상없음을 볼 수 있다.
그래서 우리는 다시 되돌리고 1.7 에서 로그를 확인한다. 분명히도 카산드라는 자동적으로 다른 참여자는 클러스터로 되돌아와서 다시 비즈니스를 열었다.
INFO 16:33:48,193 error writing to /192.168.1.5
INFO 16:33:51,235 error writing to /192.168.1.5
INFO 16:34:20,126 Standard2 has reached its threshold; switching in a fresh Mem
table at CommitLogContext(file='/var/lib/cassandra/commitlog\CommitLog-127759165
782.log', position=752)
INFO 16:34:20,173 Enqueuing flush of Memtable(Standard2)@7481705
INFO 16:34:20,251 Writing Memtable(Standard2)@7481705
INFO 16:34:20,282 LocationInfo has reached its threshold; switching in a fresh
Memtable at CommitLogContext(file='/var/lib/cassandra/commitlog\CommitLog-127759
658782.log', position=752)
INFO 16:34:20,298 Enqueuing flush of Memtable(LocationInfo)@24804063
INFO 16:34:20,579 Completed flushing c:\var\lib\cassandra\data\Keyspace1\Standa
d2-1-Data.db
INFO 16:34:20,594 Writing Memtable(LocationInfo)@24804063
INFO 16:34:20,797 Completed flushing c:\var\lib\cassandra\data\system\LocationI
fo-2-Data.db
INFO 16:34:58,159 Node /192.168.1.5 has restarted, now UP again
INFO 16:34:58,159 Node /192.168.1.5 state jump to normal
상태는 1.5 노드에 대해서 다시 정상으로 돌아오고 그것이 클러스터의 일부라고 다시 나타낸다.
eben@morpheus$ bin/nodetool -host 192.168.1.5 ring
Address Status Load Range Ring
41654880048427970483049687892424207188
192.168.1.5 Up 1.71 KB 20846671262289044293293447172905883342 |<--|
192.168.1.7 Up 2 KB 41654880048427970483049687892424207188 |-->|

6.24. 시큐리티

디폴트로 카산드라는 네트워크상의 어느 클라이언트도 당신의 클러스터에 연결할 수 있게한다. 이것은 아무 시큐리티가 설정이 되어있지 않는 것을 의미한다기 보다는 카산드라가 아무에게도 credential을 요청하지 않고 연결되는 인증방식으로 설정이 되어 있는 상태라고 봐야한다. 시큐리티 매커니즘은 플러그인 식으로 사용할수 있고 이는 인증메커니즘이 다른 것으로 사용할 수도 있고 당신 고유의 것을 작성할수도 있음을 말한다.
디폴트로 플러그인 되어있는 인증방식은 org.apache.cassandra.auth.AllowAllAuthenticator 이다. 만약 당신이 클라이언트가 credential을 제공하도록 하고 싶다면 카산드라에서 다른 방법은 org.apache.cassandra.auth.SimpleAuthenticator 이다. 이 섹션에서 이 두번째 인증을 어떻게 쓰는지 살펴본다.

6.24.1. SimpleAuthenticator 사용하기

Config 디렉토리에 두개의 파일이 있다. Access.properties와 passwd.properties이다. access파일은 키/값 쌍을 어떤 사용자가 어떤 키스페이스에 접근이 허용되었는지 알려주고 comma-separated 리스트에 기록되어있다. 예는 아래와 같다.
Keyspace1=jsmith,Elvis Presley,dilbert
이것은 세명의 사용자가 Keyspace1에 접근이 가능하도록 되어있고 사용자 이름으로 키스페이스에 접근허가 되어있다.
Passwd.properties 파일은 이 사용자의 리스트를 가지고 있고 각 사용자의 패스워드를 담고있다. 여기 passwd.properties 파일의 예이다.
jsmith=havebadpass
Elvis\ Presley=graceland4evar
dilbert=nomoovertime
사용자 Elvis Presley에는 사용자이름에 스페이스가 있기 때문에 백슬래쉬로 표시되었음에 유의하라.
Simple Authentiucator를 사용하기 위해서 Cassandra.yaml에 있는 authenticator 요소의 값을 다른 것으로 대치하라. Org.apache.cassandra.auth.AllowAllAuthenticator로부터 변경하여 로그인이 필요한 구현 클래스의 이름으로 바꾸라. Org.apache.cassandra.auth.SimpleAuthenticator.
만약 인증 파일을 적당히 설정하지 않았다면 아래와 같은 에러를 보게된다.
ERROR 10:44:27,928 Fatal error: When using org.apache.cassandra.auth.SimpleAuth
enticator
access.properties and passwd.properties properties must be defined.
그것은 거기 한가지 과정이 더 있기 때문이다. 우리는 스크립트가 있는 bin/Cassandra.in.sh 파일을 사용하여 카산드라에게 access와 password 파일의 위치를 말해주어야 한다. 우리는 이파일의 밑에 아래 코드를 붙여넣어서 JVM에 그 파일 위치들을 넘긴다. 나의 인크루드 파일은 이제 아래와 같이 보이며 파일 풀 패스를 나타낸다.
JVM_OPTS="
-Dpasswd.properties=/home/eben/books/cassandra/dist/
apache-cassandra-0.7.0-beta1/
conf/passwd.properties
-Daccess.properties=/home/eben/books/cassandra/dist/
apache-cassandra-0.7.0-beta1/
conf/access.properties"
만약 당신이 이 파일들의 잘못된 위치나 이름을 썼을 경우에 서버 로그가 알게해준다.
ERROR 11:13:55,755 Internal error processing login
java.lang.RuntimeException: Authentication table file given by property
passwd.properties
could not be found: /somebadpath/my.properties (No such file or directory)
이제 우리는 사용자이름과 패스워드를 가지고 command-line interface로 로그인한다.
[default@unknown] connect localhost/9160
Connected to: "Test Cluster" on localhost/9160
[default@unknown] use Keyspace1 jsmith 'havebadpass'
Authenticated to keyspace: Keyspace1
[jsmith@Keyspace1]
만약 당신이 잘못된 패스워드를 입력하면 CLI가 말해준다.
[default@unknown] use Keyspace1 jsmith 'havebadpassfdfdfd'
Exception during authentication to the cassandra node: verify keyspace exists,
and you are using correct credentials.
만약 당신이 존재하지 않는 사용자 이름을 입력하거나 허가되지 않은 키스페이스에 인증하려고 하면 아래와 같은 메시지를 볼 것이다.
[default@unknown] use Keyspace1 dude 'dude'
Login failure. Did you specify 'keyspace', 'username' and 'password'?
[default@unknown]
약간 혼동되게도 당신이 단지 접근 권한이 없는 키스페이스를 사용하려고 하면 당신은 인증 메시지를 보게될 것이다. 하지만 그리고 어떤 동작을 수행할 수는 없다. 아래의 예에서 우리는 인증이 필요한 키스페이스로 갈 것이다. CLI가 우리가 그것을 하게 하지만 우리는 컬럼을 읽을 수 없다.
[default@Keyspace1] get Standard1['user123']['name']
Your credentials are not sufficient to perform READONLY operations
[default@Keyspace1]
우리가 이 사용자에게 ‘name’ 값을 설정했다면 적당한 credential을 주면 동작을 수행할 수 있다.
[default@Keyspace1] use Keyspace1 eben 'pass'
Authenticated to keyspace: Keyspace1
[eben@Keyspace1] get Standard1['user123']['name']
=> (column=6e616d65, value=bootsy, timestamp=1284316537496000)
[eben@Keyspace1]
우리는 또한 사용자이름, 패스워드 조합을 우리가 CLI에서 접속할 때 보여줄수있다.
eben@morpheus:~/books/cassandra/dist/apache-cassandra-0.7.0-beta1$
bin/cassandra-cli --host localhost --port 9160
--username jsmith --password havebadpass --keyspace Keyspace1
Connected to: "Test Cluster" on localhost/9160
Welcome to cassandra CLI.
Type 'help' or '?' for help. Type 'quit' or 'exit' to quit.
[jsmith@Keyspace1] get Standard1['user123']['name']
=> (column=6e616d65, value=bootsy, timestamp=1284316537496000)
[jsmith@Keyspace1]
쿼리를 실행시키면 이제 우리에게 결과를 반환할 것이다.

6.25. Programmatic Authentication

당신이 당신의 키스페이스에 인증을 하였다면 당신의 클라이언트 애플리케이션 코드는 로그인 할 필요가 있다. 아래 예를 가이드로 삼을 수 있다.
package com.cassandraguide.config;
import java.util.HashMap;
import java.util.Map;
import org.apache.cassandra.thrift.AccessLevel;
import org.apache.cassandra.thrift.AuthenticationRequest;
import org.apache.cassandra.thrift.Cassandra;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
/**
* How to connect if you've set up SimpleAuthenticator
*/
public class AuthExample {
public static void main(String[] args) throws Exception {
TTransport tr = new TSocket("localhost", 9160);
TFramedTransport tf = new TFramedTransport(tr);
TProtocol proto = new TBinaryProtocol(tf);
Cassandra.Client client = new Cassandra.Client(proto);
tr.open();
AuthenticationRequest authRequest = new AuthenticationRequest();
Map credentials = new HashMap();
credentials.put("username", "jsmith");
credentials.put("password", "havebadpass");
authRequest.setCredentials(credentials);
client.set_keyspace("Keyspace1");
AccessLevel access = client.login(authRequest);
System.out.println("ACCESS LEVEL: " + access);
tr.close();
}
}
이 케이스에서 프로그램은 FULL을 프린트 아웃하는데 이것은 사용자의 접근 레벨이다.
여기 몇가지 주의해야 할 점있다. 첫째, credential 맵은 username과 password를 키로서 사용하고 username 은 키가 아니고 password는 값이 아니다. 둘째, 당신은 set_keyspace를 불러야 하는데 어떤 키스페이스를 인증하려고 하는지 말해주어야 하기 때문이다.

6.26. MD5 Encryption 사용하기

SimpleAuthentication 클래스는 패스워드를 명시하기 위한 두가지 모드를 가지고 있다. 평이한 텍스트와 MD5 암호화이다. 여태까지 우리는 디폴트를 이용했으며 그것은 평이한 텍스트이다. 이제 MD5를 사용해서 시큐리티를 향상 시켜보자. MD5는 “Message-Digest algorithm version5” 라는 암호화 알고리즘이다. 이것은 널리쓰이는 입력으로 128bit 해쉬 값을 만들어내는 일방향 해쉬 펑션이다.
당신은 Cassandra.in.sh에서 MD5를 활성화 시키는데 passwd.mode 를 전환하여 JVM에 넘김으로써다.
JVM_OPTS=" \
-da \
//other stuff...
-Dpasswd.mode=MD5"
이제 만약 암호화되지 않은 암호를 인증하려한다면 아래와 같은 에러를 보게된다.
Exception during authentication to the cassandra node, verify you are using correct
credentials.
당신은 일방향 해쉬로 평이한 텍스트의 사용자 이름과 암호를 MD5 버전으로 만들어내려면 다양한 도구들을 사용할 수 있다. 여기 짧은 Python 프로그램이 있어 MD5 해쉬를 평이한 스트링에서 만들수 있다.
$ python
Python 2.6.5 ...
>>> from hashlib import md5
>>> p = "havebadpass"
>>> h = md5(p).hexdigest()
>>> print h
e1a31eee2136eb73e8e47f9e9d13ab0d
이제 jsmith를 위한 암호를 passwd.properties 파일에서 암호화된 값으로 대치할 수 있다.

6.27. 당신 고유의 인증을 제공하기

당신은 Keberos ticket이나 암호화 아니면 암호를 LDAP 디렉토리 같은 다른 곳에 저장하고 싶거나 하면 당신 고유의 인증을 카산드라에 제공할 수 있다. 당신의 인증을 만들기 위하여는 단지 IAuthenticator를 구현하면 된다. 이 인터페이스는 두 개의 메서드를 요구하는데 아래와 같다.
public AccessLevel login(String keyspace, AuthenticationRequest authRequest)
throws AuthenticationException, AuthorizationException;
public void validateConfiguration() throws ConfigurationException;
로그인 메서드는 Thrift AccessLevel 객체를 반환해야 한다. 이는 사용자가 어느 정도 레벨의 인증을 받았는지 알려준다. validateConfiguration 메서드는 단순히 인증 메커니즘이 제대로 셋업되었는지 확인한다. 예를 들어 SimpleAuthenticator 와 그것은 단지 필요한 access와 passwd 파일이 명시되었는지 검사한다.

6.28. 그밖의 셋팅

일반적인 셋팅이지만 여타 특정 카테고리에 속하지 않는 셋팅들이 있다. 그래서 여기에 따로 모아 놓았다.
- Column_index_size_in_kb
이것은 컬럼이 인덱스되기 전에 행이 커질수 있는 킬로바이트 사이즈를 명시한다. 당신이 만약 매우 큰 컬럼 값을 가진다면 이 값을 증가시키기 원할 것이다. 수퍼 컬럼은 인덱스되지 않는다. 그래서 이 셋팅은 오직 인덱스된 컬럼을 참조한다. 당신은 모든 읽는 인덱스 데이터가 읽는 억세스를 위한 것이기에 이 셋팅이 매우 크지는 않기를 원한다. 그래서 필요한 이상을 읽는다면 카산드라가 느려지게 된다. 이것은 만약 당신이 부분적인 행을 자주 읽는다면 특별히 중요할 수 있다.
- In_memory_compaction_limit_in_mb
이 값은 메모리안에 압축되어야 하는 행의 사이즈 제한을 표시한다. 만약 행이 이보다 크면 디스크에 옮겨지고 느린 두단계에 의해서 압축된다. 셋팅 값은 64MB가 디폴트이다. 카산드라 0.7 이전에는 이것은 row_warning_threshold_in_mb 라고 불렸다.
- Gc_grace_seconds
이것은 garbage collecting 이 되기위해 기다리는 시간을 초로 나타낸다. Tombstone은 값이 지워졌다는 것을 나타내며 쓰레쉬 홀드 값을 만날 때까지 기다린다. 디폴트로 864,000으로 셋팅되어 있고 이것은 10일이다. 이것은 비정상적으로 긴시간같지만 tombstone이 클러스터의 모든 복사본으로 퍼져가도록 충분한 시간이 필요하다. 이것은 아직도 긴 시간 같기는 하지만 당신은 하드웨어 오류가 있을 수도 있고 tombstone이 이들이 다시 돌아오고 작동가능해져서 복사가 되어야 한다는 것이다. 만약 다운된 노드가 tombstone이 실제로 지워진 후에 tombstone 노티피케이션을 받지 못했다면 read repair는 지워지지 않은 데이터가 클러스터에 천천히 다시 받아들여지도록 할 것이다. 물론 만약 당신이 많은 지우기를 한다면 당신은 이 설정을 깔끔하게 하기위하여 다시 돌릴수는 있지만 아마도 큰 차이가 나지는 않을 것이다.
- Phi_convict_threshold
이것은 서버가 다운되었다는 최종 판결을 내릴수 있도록 카산드라가 도달해야 하는 Phi 값을 나타낸다. 디폴트로 8로 셋팅되어 있다. 일반적으로 이 값을 바꾸지는 않는다. 그러나 네트워크가 최적화되어 동작하고 있지 못하고 카산드라가 잘못된 읽기를 한다면 값을 증가시켜 노드가 실제로 다운되었는지 결정하는 latency를 더할 수 있다.

6.29. 추가적인 도구들

이 섹션에서 카산드라가 설정하도록 돕는 그밖의 도구들을 논의해 보자.


6.29.1. 키를 보여주기

당신은 sstablekeys 라는 스크립트를 사용하여 어떤 키가 당신의 SSTables에 있는지 볼 수 있다. 이것을 실행하기 위해 키를 보기 위한 SSTable의 위치를 사용한다. 아래와 같다.
eben@morpheus$ bin/sstablekeys /var/lib/cassandra/data/Hotelier/Hotel-1-Data.db
WARN 10:56:05,928 Schema definitions were defined both locally and in cassandra.yaml.
Definitions in cassandra.yaml were ignored.
415a435f303433
415a535f303131
4341535f303231
4e594e5f303432

6.29.2. 이전 설정을 받아들이기

만약 당신이 0.6 버전에서 설정파일에 정의된 키스페이스를 0.7 이나 그 이상에서 임포트하기 원한다면 JMX 동작, StorageService.loadSchemaFromYaml()을 사용할 수 있다. 이것은 두가지 중요한 경고를 가지고 있다. 첫째, 오직 한 번만 사용이 된다. 둘째, 버전 0.8에서는 없어질 것이다.
0.7 버전부터 시작하여 사용자 키스페이스는 Cassandra.yaml에 정의되어 있고 서비스가 시작할때 디폴트로 로드되지 않는다. 그래서 카산드라가 처음으로 시작할 때 아래와 같은 메시지를 보게 될 것이다.
INFO config.DatabaseDescriptor: Found table data in data directories.
Consider using JMX to call org.apache.cassandra.service.StorageService.
loadSchemaFromYaml().
로드하기 위하여 loadSchemaFromYaml 이라는 JMX 동작을 해야한다. 그러나 이명령을 하기 위하여 당신은 몇 가지를 해야한다. 첫째, mx4j-tools.jar를 Sourceforge.net에서 받아라. ZIP 파일을 받아서 압축을 아무 디렉토리에 풀어라. 그리고 mx4j-tools.jar라는 JAR파일을 카산드라 lib 디렉토리에 푼다. 이는 카산드라와 통신하기 위하여 JMX 연결을 만들게 해준다. 우리는 카산드라와 함께 JMX를 사용하는 것을 후에 보겠지만 지금은 키스페이스를 테스트하기 원한다. JAR를 추가한 후에 카산드라를 재시작한다.
다음으로 터미널을 새로 연후에 jconsole 명령을 친다. GUI가 시작되고 JConsole 도구를 로드하여 자바를 장착한다. 이 도구는 Java Virtual Machine을 보고 실행시 데이터를 보고 JMX로 나타내진 동작들을 수행하게 한다.
Cassandra.yaml에 정의된 키스페이스를 로드하기 위해 JConsole GUI에 있는 MBeans탭을 클릭한다. Org.apache.cassandra.service 빈을 펼쳐서 StorageService 그리고 동작이다. loadSchemaFromYaml을 클릭하고 버튼을 클릭한다. 이것은 카산드라 서비스에서 명령을 실행하고 Cassandra.yaml파일에서 당신의 스키마를 로드한다.
한번만 가능한 동작을 0.6 설정을 0.7으로 임포트하려면 loadSchemaFromYaml 동작을 StorageService MBean에 실행한다. 이것은 그림 6-4에 보여진다.
이제 당신의 로그에서 아래와 같은 출력을 보게된다.
17:35:47 INFO thrift.CassandraDaemon: Cassandra starting up...
17:35:48 INFO utils.Mx4jTool: mx4j successfuly loaded
HttpAdaptor version 3.0.2 started on port 8081
17:40:43 INFO config.DatabaseDescriptor: UTF8Type
17:40:43 INFO config.DatabaseDescriptor: BytesType
17:40:43 INFO config.DatabaseDescriptor: UTF8Type
17:40:43 INFO config.DatabaseDescriptor: TimeUUIDType
17:40:43 INFO config.DatabaseDescriptor: BytesType
17:40:43 INFO config.DatabaseDescriptor: BytesType
17:40:43 INFO config.DatabaseDescriptor:
데이터베이스 로그는 동작이 스키마를 잘 로드했는지 보여주는 좋은 것이다.

그림6-4. jconsole도구를 사용하여 Cassandra.yaml에 정의된 오래된 키스페이스를 로드한다.

당신은 org.apache.cassandra.config.Converter클래스를 사용하여 storage-conf.xml을 Cassandra.yaml로 변환할 수 있다. 만약 0.6에서 0.7로 업그레이드 한다면 이것이 첫번째 단계일것이다. 변환기를 실행하기 위하여 config-converter 스크립트를 bin 디렉토리에서 사용한다. 이것은 오래된 파일을 읽어서 변환하고 내용을 덤프한다.