Kafka 무작정 실행하기 — 2


58의 비밀

먼저 지난 번 마지막에 언급했던 58이란 숫자의 비밀을 밝혀보자.
사실 정확한 원인은 아직 확인 못했다. 다만 지난 번 코드의 구현이 ’트위터
메시지가 하나 들어올 때마다 producer 하나를 만들어 트위터 메시지를
topic에 보낸다’
는 것이었다.

이 과정에서 의심할 수 있는 것은 매번 producer를 만들어 커넥션을 
하게 되니 아마도 이 커넥션 수에 제한이 걸려버린 것이 아닐까 하는 
부분이었다.

그래서 일단 직감에 의존해 producer에서 topic으로 메시지를 보낸 후
API의 close 함수로 연결을 끊어보았다. 예상이 적중하였는지 이후로는
58개의 제한에 걸리지 않고 트위터에서 받아오는 모든 메시지들이
정상적으로 전송이 되었다.


성능 관리

겨우 이만큼의 구현과 설정을 해놓고 성능 관리를 논하는 것은 우스운 
일이지만 일단 트위터 메시지 한 건에 커넥션 하나를 만들었다가 끊는다는 
것은 아무리 생각해도 그리 바람직하지는 않다고 판단하였다.

그리서 우선 아쉬운대로 트위터 메시지 10건이 수신되면 그 10개를 하나의
배열로 하여 producer를 통해 보내도록 하였다. 우선은 시험 삼아
이정도 처리를 한 것이고 시스템 환경에 따라 아마도 이러한 수치를 
세밀하게 조절을 해야 할 것이다.

그리고 실제로 topic으로 메시지를 보내는 위치에서 파티션(나는 3개의 
partition 나뉜 1개의 topic을 만들었다)에 고르게 분산시키기 위해
파티션의 인덱스를 계산하는 식을 하나 추가하였다.

  • 처음에는 손이 덜가는 HighLevelProducer를 사용했었으나 뭔가 잘
    작동하지 않는 듯하여 그냥 Producer로 바꾸었다.

수정된 소스

일단 지난 시간의 문제점을 수정하고 몇가지 필요한 내용을 추가한
소스를 다음과 같이 만들었다.

var kafka = require('kafka-node');


var Twitter = require('twitter');
var count = 0;


var client = new Twitter({
consumer_key: ‘…’,
consumer_secret: ‘…’,
access_token_key: ‘…’,
access_token_secret: ‘…’,
});

var msgArr;

/**
* Stream statuses filtered by keyword
* number of tweets per second depends on topic popularity
**/
client.stream('statuses/sample', {track: ''}, function(stream){
stream.on('data', function(tweet) {
if (tweet.lang == 'kr' || tweet.lang == 'ko') {
if (count % 10 == 0) {
msgArr = new Array();
}

var messageObj = {};

messageObj.createdAt = tweet.created_at;
messageObj.id = tweet.id;
messageObj.idStr = tweet.id_str;
messageObj.text = tweet.text;
messageObj.UserScreenName = tweet.user.screen_name;

messageObj.retweetCreatedAt = '';
messageObj.retweetId = '';
messageObj.retweetIdStr = '';
messageObj.retweetText = '';
messageObj.retweetUserScreenName = '';
messageObj.retweetCount = '';

if (tweet.retweeted_status) {
messageObj.retweetCreatedAt = tweet.retweeted_status.created_at;
messageObj.retweetId = tweet.retweeted_status.id;
messageObj.retweetIdStr = tweet.retweeted_status.id_str;
messageObj.retweetText = tweet.retweeted_status.text;
messageObj.retweetUserScreenName = tweet.retweeted_status.user.screen_name;
messageObj.retweetCount = tweet.retweeted_status.retweet_count;
}

var messageStr = JSON.stringify(messageObj)
console.log(messageStr);
console.log('===================================================================' + count);

msgArr.push(messageStr);

if (count % 10 == 9) {
console.log('==call sendMessage====================================================');
var Producer = kafka.Producer;
var Client = kafka.Client;
var client = new Client('NAMENODE.local:2181,SECONDARY-NAMENODE.local:2181,DATANODE1.local:2181,DATANODE2.local:2181,DATANODE3.local:2181');

var topic = 'twittopic';
var producer = new Producer(client, { requireAcks: 1 });

sendMessage(producer, msgArr, count);
}
count++;
}
});

stream.on('error', function(error) {
console.log(error);
});
});

var sendMessage = function(producer, msgArr, count) {

console.log('==sendMessage=================================================================');

producer.on('ready', function () {
console.log('==producer ready=========');
console.log('send start');

var p = (count - 9) % 3;
producer.send([
{topic: 'twittopic', partition: p, messages: msgArr, attributes: 0 }
], function (err, data) {
if (err) console.log(err);
else console.log('send %d messages', count);

producer.close(function() {
console.log('close produce');
});
});
});


producer.on('error', function (err) {
console.log('error', err)
});
};

모니터링

일단 내용 구현을 하고 실제 작동하는 것 까지 확인을 하였으니 이제제대로
뭔가를 하고 있는 것인지 궁금해졌다. 일단 Kafka 자체적으로는 달리 웹으로 
구현된 관리 화면을 제공하지 않기에 잘 알려진 모니터링 도구 2가지를 
설치해보았다.

Kafka Offset Monitor : http://quantifind.github.io/KafkaOffsetMonitor/

Kafka Manager : https://github.com/yahoo/kafka-manager

이중 Kafka Manager는 Kafka에서 제공하는 JMX 인터페이스를 이용하는
툴로 이 툴을 이용하기위해서는 Kafka 서버 시작 시 JMX 포트를 함께 
지정해 주어야 한다.

env JMX_PORT=9999 ./kafka-server-start.sh ../config/server.properties

다음은 각 모니터링 툴의 실제 화면이다.

  • Kafka Offset Monitor
  • Kafka Manager

그리고 다음은 node.js를 통해 실행한 Producer와 Consumer의 콘솔 
화면이다. 아직 Consumer에서는 별다른 메시지 가공은 없이 topic에서 
가져온 메시지를 로그로 출력만 하고 있다.

Producer

Consumer1 (DATANODE1)

Consumer2(DATANODE2)

Consumer3 (DATANODE3)

현재 구성이 topic이 3개의 partition으로 되어있고 3개의 Consumer가 
하나의 Consumer 그룹(kaka-node-group : kafka-node에서 자동 생성한 
그룹)에 3개의 Consumer가 있기 때문에 각각의 partition과 Consumer는 
다음과 같이 1:1로 연결이 되고 있다.

Consumer1 — partition 0
Consumer2 — partition 2
Consumer3 — partition 1

다음 포스팅에서는 현재 내가 구성하고 있는 시스템의 구조를 설명하고
Consumer 중단 시에 rebalancing이 일어나는 모습을 코솔 로그를 통해
확인해보겠다.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.