kafka-rb 0.0.8 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +1 -1
- data/Rakefile +3 -3
- data/lib/kafka/consumer.rb +5 -20
- data/lib/kafka/io.rb +3 -0
- data/lib/kafka/message.rb +80 -12
- data/lib/kafka/producer.rb +3 -30
- data/lib/kafka.rb +3 -0
- data/spec/consumer_spec.rb +1 -34
- data/spec/message_spec.rb +85 -3
- data/spec/producer_spec.rb +1 -59
- metadata +36 -51
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -21,7 +21,7 @@ require 'rspec/core/rake_task'
|
|
21
21
|
|
22
22
|
spec = Gem::Specification.new do |s|
|
23
23
|
s.name = %q{kafka-rb}
|
24
|
-
s.version = "0.0.
|
24
|
+
s.version = "0.0.9"
|
25
25
|
|
26
26
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
27
27
|
s.authors = ["Alejandro Crosa", "Stefan Mees", "Tim Lossen"]
|
@@ -30,7 +30,7 @@ spec = Gem::Specification.new do |s|
|
|
30
30
|
s.description = %q{kafka-rb allows you to produce and consume messages using the Kafka distributed publish/subscribe messaging service.}
|
31
31
|
s.extra_rdoc_files = ["LICENSE"]
|
32
32
|
s.files = ["LICENSE", "README.md", "Rakefile", "lib/kafka", "lib/kafka/batch.rb", "lib/kafka/consumer.rb", "lib/kafka/io.rb", "lib/kafka/message.rb", "lib/kafka/producer.rb", "lib/kafka/request_type.rb", "lib/kafka/error_codes.rb", "lib/kafka.rb", "spec/batch_spec.rb", "spec/consumer_spec.rb", "spec/io_spec.rb", "spec/kafka_spec.rb", "spec/message_spec.rb", "spec/producer_spec.rb", "spec/spec_helper.rb"]
|
33
|
-
s.homepage = %q{http://github.com/
|
33
|
+
s.homepage = %q{http://github.com/acrosa/kafka-rb}
|
34
34
|
s.require_paths = ["lib"]
|
35
35
|
s.rubygems_version = %q{1.3.7}
|
36
36
|
s.summary = %q{A Ruby client for the Kafka distributed publish/subscribe messaging service}
|
@@ -49,7 +49,7 @@ spec = Gem::Specification.new do |s|
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
|
52
|
+
Gem::PackageTask.new(spec) do |pkg|
|
53
53
|
pkg.gem_spec = spec
|
54
54
|
end
|
55
55
|
|
data/lib/kafka/consumer.rb
CHANGED
@@ -29,8 +29,8 @@ module Kafka
|
|
29
29
|
def initialize(options = {})
|
30
30
|
self.topic = options[:topic] || "test"
|
31
31
|
self.partition = options[:partition] || 0
|
32
|
-
self.host = options[:host] ||
|
33
|
-
self.port = options[:port] ||
|
32
|
+
self.host = options[:host] || HOST
|
33
|
+
self.port = options[:port] || PORT
|
34
34
|
self.offset = options[:offset]
|
35
35
|
self.max_size = options[:max_size] || MAX_SIZE
|
36
36
|
self.polling = options[:polling] || DEFAULT_POLLING_INTERVAL
|
@@ -49,8 +49,9 @@ module Kafka
|
|
49
49
|
def consume
|
50
50
|
self.offset ||= fetch_latest_offset
|
51
51
|
send_consume_request
|
52
|
-
|
53
|
-
|
52
|
+
message_set = Kafka::Message.parse_from(read_data_response)
|
53
|
+
self.offset += message_set.size
|
54
|
+
message_set.messages
|
54
55
|
rescue SocketError
|
55
56
|
nil
|
56
57
|
end
|
@@ -94,21 +95,5 @@ module Kafka
|
|
94
95
|
max_size = [max_size].pack("N")
|
95
96
|
request_type + topic + partition + offset + max_size
|
96
97
|
end
|
97
|
-
|
98
|
-
def parse_message_set_from(data)
|
99
|
-
messages = []
|
100
|
-
processed = 0
|
101
|
-
length = data.length - 4
|
102
|
-
while (processed <= length) do
|
103
|
-
message_size = data[processed, 4].unpack("N").shift + 4
|
104
|
-
message_data = data[processed, message_size]
|
105
|
-
break unless message_data.size == message_size
|
106
|
-
messages << Kafka::Message.parse_from(message_data)
|
107
|
-
processed += message_size
|
108
|
-
end
|
109
|
-
self.offset += processed
|
110
|
-
messages
|
111
|
-
end
|
112
|
-
|
113
98
|
end
|
114
99
|
end
|
data/lib/kafka/io.rb
CHANGED
data/lib/kafka/message.rb
CHANGED
@@ -14,19 +14,35 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
module Kafka
|
16
16
|
|
17
|
-
# A message. The format of
|
18
|
-
#
|
19
|
-
# 4 byte
|
20
|
-
#
|
17
|
+
# A message. The format of a message is as follows:
|
18
|
+
#
|
19
|
+
# 4 byte big-endian int: length of message in bytes (including the rest of
|
20
|
+
# the header, but excluding the length field itself)
|
21
|
+
# 1 byte: "magic" identifier (format version number)
|
22
|
+
#
|
23
|
+
# If the magic byte == 0, there is one more header field:
|
24
|
+
#
|
25
|
+
# 4 byte big-endian int: CRC32 checksum of the payload
|
26
|
+
#
|
27
|
+
# If the magic byte == 1, there are two more header fields:
|
28
|
+
#
|
29
|
+
# 1 byte: "attributes" (flags for compression, codec etc)
|
30
|
+
# 4 byte big-endian int: CRC32 checksum of the payload
|
31
|
+
#
|
32
|
+
# All following bytes are the payload.
|
21
33
|
class Message
|
22
34
|
|
23
35
|
MAGIC_IDENTIFIER_DEFAULT = 0
|
36
|
+
BASIC_MESSAGE_HEADER = 'NC'.freeze
|
37
|
+
VERSION_0_HEADER = 'N'.freeze
|
38
|
+
VERSION_1_HEADER = 'CN'.freeze
|
39
|
+
COMPRESSION_CODEC_MASK = 0x03
|
24
40
|
|
25
41
|
attr_accessor :magic, :checksum, :payload
|
26
42
|
|
27
43
|
def initialize(payload = nil, magic = MAGIC_IDENTIFIER_DEFAULT, checksum = nil)
|
28
44
|
self.magic = magic
|
29
|
-
self.payload = payload
|
45
|
+
self.payload = payload || ""
|
30
46
|
self.checksum = checksum || self.calculate_checksum
|
31
47
|
end
|
32
48
|
|
@@ -35,15 +51,67 @@ module Kafka
|
|
35
51
|
end
|
36
52
|
|
37
53
|
def valid?
|
38
|
-
self.checksum ==
|
54
|
+
self.checksum == calculate_checksum
|
39
55
|
end
|
40
56
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
57
|
+
# Takes a byte string containing one or more messages; returns a MessageSet
|
58
|
+
# with the messages parsed from the string, and the number of bytes
|
59
|
+
# consumed from the string.
|
60
|
+
def self.parse_from(data)
|
61
|
+
messages = []
|
62
|
+
bytes_processed = 0
|
63
|
+
|
64
|
+
while bytes_processed <= data.length - 5 # 5 = size of BASIC_MESSAGE_HEADER
|
65
|
+
message_size, magic = data[bytes_processed, 5].unpack(BASIC_MESSAGE_HEADER)
|
66
|
+
break if bytes_processed + message_size + 4 > data.length # message is truncated
|
67
|
+
|
68
|
+
case magic
|
69
|
+
when 0
|
70
|
+
# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ...
|
71
|
+
# | | | |
|
72
|
+
# | message_size |magic| checksum | payload ...
|
73
|
+
payload_size = message_size - 5 # 5 = sizeof(magic) + sizeof(checksum)
|
74
|
+
checksum = data[bytes_processed + 5, 4].unpack(VERSION_0_HEADER).shift
|
75
|
+
payload = data[bytes_processed + 9, payload_size]
|
76
|
+
messages << Kafka::Message.new(payload, magic, checksum)
|
77
|
+
|
78
|
+
when 1
|
79
|
+
# | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 ...
|
80
|
+
# | | | | |
|
81
|
+
# | size |magic|attrs| checksum | payload ...
|
82
|
+
payload_size = message_size - 6 # 6 = sizeof(magic) + sizeof(attrs) + sizeof(checksum)
|
83
|
+
attributes, checksum = data[bytes_processed + 5, 5].unpack(VERSION_1_HEADER)
|
84
|
+
payload = data[bytes_processed + 10, payload_size]
|
85
|
+
|
86
|
+
case attributes & COMPRESSION_CODEC_MASK
|
87
|
+
when 0 # a single uncompressed message
|
88
|
+
messages << Kafka::Message.new(payload, magic, checksum)
|
89
|
+
when 1 # a gzip-compressed message set -- parse recursively
|
90
|
+
uncompressed = Zlib::GzipReader.new(StringIO.new(payload)).read
|
91
|
+
message_set = parse_from(uncompressed)
|
92
|
+
raise 'malformed compressed message' if message_set.size != uncompressed.size
|
93
|
+
messages.concat(message_set.messages)
|
94
|
+
else
|
95
|
+
# https://cwiki.apache.org/confluence/display/KAFKA/Compression
|
96
|
+
# claims that 2 is for Snappy compression, but Kafka's Scala client
|
97
|
+
# implementation doesn't seem to support it yet, so I don't have
|
98
|
+
# a reference implementation to test against.
|
99
|
+
raise "Unsupported Kafka compression codec: #{attributes & COMPRESSION_CODEC_MASK}"
|
100
|
+
end
|
101
|
+
|
102
|
+
else
|
103
|
+
raise "Unsupported Kafka message version: magic number #{magic}"
|
104
|
+
end
|
105
|
+
|
106
|
+
bytes_processed += message_size + 4 # 4 = sizeof(message_size)
|
107
|
+
end
|
108
|
+
|
109
|
+
MessageSet.new(bytes_processed, messages)
|
47
110
|
end
|
48
111
|
end
|
112
|
+
|
113
|
+
# Encapsulates a list of Kafka messages (as Kafka::Message objects in the
|
114
|
+
# +messages+ attribute) and their total serialized size in bytes (the +size+
|
115
|
+
# attribute).
|
116
|
+
class MessageSet < Struct.new(:size, :messages); end
|
49
117
|
end
|
data/lib/kafka/producer.rb
CHANGED
@@ -17,45 +17,18 @@ module Kafka
|
|
17
17
|
|
18
18
|
include Kafka::IO
|
19
19
|
|
20
|
-
PRODUCE_REQUEST_ID = Kafka::RequestType::PRODUCE
|
21
|
-
|
22
20
|
attr_accessor :topic, :partition
|
23
21
|
|
24
22
|
def initialize(options = {})
|
25
23
|
self.topic = options[:topic] || "test"
|
26
24
|
self.partition = options[:partition] || 0
|
27
|
-
self.host = options[:host] ||
|
28
|
-
self.port = options[:port] ||
|
25
|
+
self.host = options[:host] || HOST
|
26
|
+
self.port = options[:port] || PORT
|
29
27
|
self.connect(self.host, self.port)
|
30
28
|
end
|
31
29
|
|
32
|
-
def encode(message)
|
33
|
-
if RUBY_VERSION[0,3] == "1.8" # Use old iconv on Ruby 1.8 for encoding
|
34
|
-
ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
|
35
|
-
[message.magic].pack("C") + [message.calculate_checksum].pack("N") + ic.iconv(message.payload.to_s)
|
36
|
-
else
|
37
|
-
[message.magic].pack("C") + [message.calculate_checksum].pack("N") + message.payload.to_s.force_encoding(Encoding::ASCII_8BIT)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def encode_request(topic, partition, messages)
|
42
|
-
message_set = Array(messages).collect { |message|
|
43
|
-
encoded_message = self.encode(message)
|
44
|
-
[encoded_message.length].pack("N") + encoded_message
|
45
|
-
}.join("")
|
46
|
-
|
47
|
-
request = [PRODUCE_REQUEST_ID].pack("n")
|
48
|
-
topic = [topic.length].pack("n") + topic
|
49
|
-
partition = [partition].pack("N")
|
50
|
-
messages = [message_set.length].pack("N") + message_set
|
51
|
-
|
52
|
-
data = request + topic + partition + messages
|
53
|
-
|
54
|
-
return [data.length].pack("N") + data
|
55
|
-
end
|
56
|
-
|
57
30
|
def send(messages)
|
58
|
-
self.write(
|
31
|
+
self.write(Encoder.produce(self.topic, self.partition, messages))
|
59
32
|
end
|
60
33
|
|
61
34
|
def batch(&block)
|
data/lib/kafka.rb
CHANGED
@@ -20,10 +20,13 @@ end
|
|
20
20
|
|
21
21
|
require File.join(File.dirname(__FILE__), "kafka", "io")
|
22
22
|
require File.join(File.dirname(__FILE__), "kafka", "request_type")
|
23
|
+
require File.join(File.dirname(__FILE__), "kafka", "encoder")
|
23
24
|
require File.join(File.dirname(__FILE__), "kafka", "error_codes")
|
24
25
|
require File.join(File.dirname(__FILE__), "kafka", "batch")
|
25
26
|
require File.join(File.dirname(__FILE__), "kafka", "message")
|
27
|
+
require File.join(File.dirname(__FILE__), "kafka", "multi_producer")
|
26
28
|
require File.join(File.dirname(__FILE__), "kafka", "producer")
|
29
|
+
require File.join(File.dirname(__FILE__), "kafka", "producer_request")
|
27
30
|
require File.join(File.dirname(__FILE__), "kafka", "consumer")
|
28
31
|
|
29
32
|
module Kafka
|
data/spec/consumer_spec.rb
CHANGED
@@ -25,7 +25,7 @@ describe Consumer do
|
|
25
25
|
describe "Kafka Consumer" do
|
26
26
|
|
27
27
|
it "should have a Kafka::RequestType::FETCH" do
|
28
|
-
|
28
|
+
Kafka::RequestType::FETCH.should eql(1)
|
29
29
|
@consumer.should respond_to(:request_type)
|
30
30
|
end
|
31
31
|
|
@@ -91,39 +91,6 @@ describe Consumer do
|
|
91
91
|
@consumer.send_consume_request.should eql(true)
|
92
92
|
end
|
93
93
|
|
94
|
-
it "should parse a message set from bytes" do
|
95
|
-
bytes = [8].pack("N") + [0].pack("C") + [1120192889].pack("N") + "ale"
|
96
|
-
message = @consumer.parse_message_set_from(bytes).first
|
97
|
-
message.payload.should eql("ale")
|
98
|
-
message.checksum.should eql(1120192889)
|
99
|
-
message.magic.should eql(0)
|
100
|
-
message.valid?.should eql(true)
|
101
|
-
end
|
102
|
-
|
103
|
-
it "should skip an incomplete message at the end of the response" do
|
104
|
-
bytes = [8].pack("N") + [0].pack("C") + [1120192889].pack("N") + "ale"
|
105
|
-
# incomplete message
|
106
|
-
bytes += [8].pack("N")
|
107
|
-
messages = @consumer.parse_message_set_from(bytes)
|
108
|
-
messages.size.should eql(1)
|
109
|
-
end
|
110
|
-
|
111
|
-
it "should skip an incomplete message at the end of the response which has the same length as an empty message" do
|
112
|
-
bytes = [8].pack("N") + [0].pack("C") + [1120192889].pack("N") + "ale"
|
113
|
-
# incomplete message because payload is missing
|
114
|
-
bytes += [8].pack("N") + [0].pack("C") + [1120192889].pack("N")
|
115
|
-
messages = @consumer.parse_message_set_from(bytes)
|
116
|
-
messages.size.should eql(1)
|
117
|
-
end
|
118
|
-
|
119
|
-
it "should read empty messages correctly" do
|
120
|
-
# empty message
|
121
|
-
bytes = [5].pack("N") + [0].pack("C") + [0].pack("N") + ""
|
122
|
-
messages = @consumer.parse_message_set_from(bytes)
|
123
|
-
messages.size.should eql(1)
|
124
|
-
messages.first.payload.should eql("")
|
125
|
-
end
|
126
|
-
|
127
94
|
it "should consume messages" do
|
128
95
|
@consumer.should_receive(:send_consume_request).and_return(true)
|
129
96
|
@consumer.should_receive(:read_data_response).and_return("")
|
data/spec/message_spec.rb
CHANGED
@@ -40,6 +40,10 @@ describe Message do
|
|
40
40
|
@message.magic.should eql(1)
|
41
41
|
end
|
42
42
|
|
43
|
+
it "should have an empty payload by default" do
|
44
|
+
@message.payload.should == ""
|
45
|
+
end
|
46
|
+
|
43
47
|
it "should calculate the checksum (crc32 of a given message)" do
|
44
48
|
@message.payload = "ale"
|
45
49
|
@message.calculate_checksum.should eql(1120192889)
|
@@ -56,14 +60,92 @@ describe Message do
|
|
56
60
|
@message = Message.new("alejandro", 0, 66666666) # 66666666 is a funny checksum
|
57
61
|
@message.valid?.should eql(false)
|
58
62
|
end
|
63
|
+
end
|
59
64
|
|
60
|
-
|
61
|
-
|
62
|
-
|
65
|
+
describe "parsing" do
|
66
|
+
it "should parse a version-0 message from bytes" do
|
67
|
+
bytes = [8, 0, 1120192889, 'ale'].pack('NCNa*')
|
68
|
+
message = Kafka::Message.parse_from(bytes).messages.first
|
63
69
|
message.valid?.should eql(true)
|
64
70
|
message.magic.should eql(0)
|
65
71
|
message.checksum.should eql(1120192889)
|
66
72
|
message.payload.should eql("ale")
|
67
73
|
end
|
74
|
+
|
75
|
+
it "should parse a version-1 message from bytes" do
|
76
|
+
bytes = [12, 1, 0, 755095536, 'martin'].pack('NCCNa*')
|
77
|
+
message = Kafka::Message.parse_from(bytes).messages.first
|
78
|
+
message.should be_valid
|
79
|
+
message.magic.should == 1
|
80
|
+
message.checksum.should == 755095536
|
81
|
+
message.payload.should == 'martin'
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should raise an error if the magic number is not recognised" do
|
85
|
+
bytes = [12, 2, 0, 755095536, 'martin'].pack('NCCNa*') # 2 = some future format that's not yet invented
|
86
|
+
lambda {
|
87
|
+
Kafka::Message.parse_from(bytes)
|
88
|
+
}.should raise_error(RuntimeError, /Unsupported Kafka message version/)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should skip an incomplete message at the end of the response" do
|
92
|
+
bytes = [8, 0, 1120192889, 'ale'].pack('NCNa*')
|
93
|
+
bytes += [8].pack('N') # incomplete message (only length, rest is truncated)
|
94
|
+
message_set = Message.parse_from(bytes)
|
95
|
+
message_set.messages.size.should == 1
|
96
|
+
message_set.size.should == 12 # bytes consumed
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should skip an incomplete message at the end of the response which has the same length as an empty message" do
|
100
|
+
bytes = [8, 0, 1120192889, 'ale'].pack('NCNa*')
|
101
|
+
bytes += [8, 0, 1120192889].pack('NCN') # incomplete message (payload is missing)
|
102
|
+
message_set = Message.parse_from(bytes)
|
103
|
+
message_set.messages.size.should == 1
|
104
|
+
message_set.size.should == 12 # bytes consumed
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should read empty messages correctly" do
|
108
|
+
# empty message
|
109
|
+
bytes = [5, 0, 0, ''].pack('NCNa*')
|
110
|
+
messages = Message.parse_from(bytes).messages
|
111
|
+
messages.size.should == 1
|
112
|
+
messages.first.payload.should == ''
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should parse a gzip-compressed message" do
|
116
|
+
compressed = 'H4sIAG0LI1AAA2NgYBBkZBB/9XN7YlJRYnJiCogCAH9lueQVAAAA'.unpack('m*').shift
|
117
|
+
bytes = [45, 1, 1, 1303540914, compressed].pack('NCCNa*')
|
118
|
+
message = Message.parse_from(bytes).messages.first
|
119
|
+
message.should be_valid
|
120
|
+
message.payload.should == 'abracadabra'
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should recursively parse nested compressed messages" do
|
124
|
+
uncompressed = [17, 1, 0, 401275319, 'abracadabra'].pack('NCCNa*')
|
125
|
+
uncompressed << [12, 1, 0, 2666930069, 'foobar'].pack('NCCNa*')
|
126
|
+
compressed_io = StringIO.new('')
|
127
|
+
Zlib::GzipWriter.new(compressed_io).tap{|gzip| gzip << uncompressed; gzip.close }
|
128
|
+
compressed = compressed_io.string
|
129
|
+
bytes = [compressed.size + 6, 1, 1, Zlib.crc32(compressed), compressed].pack('NCCNa*')
|
130
|
+
messages = Message.parse_from(bytes).messages
|
131
|
+
messages.map(&:payload).should == ['abracadabra', 'foobar']
|
132
|
+
messages.map(&:valid?).should == [true, true]
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should support a mixture of compressed and uncompressed messages" do
|
136
|
+
compressed = 'H4sIAG0LI1AAA2NgYBBkZBB/9XN7YlJRYnJiCogCAH9lueQVAAAA'.unpack('m*').shift
|
137
|
+
bytes = [45, 1, 1, 1303540914, compressed].pack('NCCNa*')
|
138
|
+
bytes << [11, 1, 0, 907060870, 'hello'].pack('NCCNa*')
|
139
|
+
messages = Message.parse_from(bytes).messages
|
140
|
+
messages.map(&:payload).should == ['abracadabra', 'hello']
|
141
|
+
messages.map(&:valid?).should == [true, true]
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should raise an error if the compression codec is not supported" do
|
145
|
+
bytes = [6, 1, 2, 0, ''].pack('NCCNa*') # 2 = Snappy codec
|
146
|
+
lambda {
|
147
|
+
Kafka::Message.parse_from(bytes)
|
148
|
+
}.should raise_error(RuntimeError, /Unsupported Kafka compression codec/)
|
149
|
+
end
|
68
150
|
end
|
69
151
|
end
|
data/spec/producer_spec.rb
CHANGED
@@ -25,10 +25,6 @@ describe Producer do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
describe "Kafka Producer" do
|
28
|
-
it "should have a PRODUCE_REQUEST_ID" do
|
29
|
-
Producer::PRODUCE_REQUEST_ID.should eql(0)
|
30
|
-
end
|
31
|
-
|
32
28
|
it "should have a topic and a partition" do
|
33
29
|
@producer.should respond_to(:topic)
|
34
30
|
@producer.should respond_to(:partition)
|
@@ -47,60 +43,6 @@ describe Producer do
|
|
47
43
|
@producer.host.should eql("localhost")
|
48
44
|
@producer.port.should eql(9092)
|
49
45
|
end
|
50
|
-
|
51
|
-
describe "Message Encoding" do
|
52
|
-
it "should encode a message" do
|
53
|
-
message = Kafka::Message.new("alejandro")
|
54
|
-
full_message = [message.magic].pack("C") + [message.calculate_checksum].pack("N") + message.payload
|
55
|
-
@producer.encode(message).should eql(full_message)
|
56
|
-
end
|
57
|
-
|
58
|
-
it "should encode an empty message" do
|
59
|
-
message = Kafka::Message.new()
|
60
|
-
full_message = [message.magic].pack("C") + [message.calculate_checksum].pack("N") + message.payload.to_s
|
61
|
-
@producer.encode(message).should eql(full_message)
|
62
|
-
end
|
63
|
-
|
64
|
-
it "should encode strings containing non-ASCII characters" do
|
65
|
-
message = Kafka::Message.new("ümlaut")
|
66
|
-
encoded = @producer.encode(message)
|
67
|
-
data = [encoded.size].pack("N") + encoded
|
68
|
-
if RUBY_VERSION[0,3] == "1.8" # Use old iconv on Ruby 1.8 for encoding
|
69
|
-
ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
|
70
|
-
ic.iconv(Kafka::Message.parse_from(data).payload).should eql("ümlaut")
|
71
|
-
else
|
72
|
-
Kafka::Message.parse_from(data).payload.force_encoding(Encoding::UTF_8).should eql("ümlaut")
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
describe "Request Encoding" do
|
78
|
-
it "should binary encode an empty request" do
|
79
|
-
bytes = @producer.encode_request("test", 0, [])
|
80
|
-
bytes.length.should eql(20)
|
81
|
-
bytes.should eql("\000\000\000\020\000\000\000\004test\000\000\000\000\000\000\000\000")
|
82
|
-
end
|
83
|
-
|
84
|
-
it "should binary encode a request with a message, using a specific wire format" do
|
85
|
-
message = Kafka::Message.new("ale")
|
86
|
-
bytes = @producer.encode_request("test", 3, message)
|
87
|
-
data_size = bytes[0, 4].unpack("N").shift
|
88
|
-
request_id = bytes[4, 2].unpack("n").shift
|
89
|
-
topic_length = bytes[6, 2].unpack("n").shift
|
90
|
-
topic = bytes[8, 4]
|
91
|
-
partition = bytes[12, 4].unpack("N").shift
|
92
|
-
messages_length = bytes[16, 4].unpack("N").shift
|
93
|
-
messages = bytes[20, messages_length]
|
94
|
-
|
95
|
-
bytes.length.should eql(32)
|
96
|
-
data_size.should eql(28)
|
97
|
-
request_id.should eql(0)
|
98
|
-
topic_length.should eql(4)
|
99
|
-
topic.should eql("test")
|
100
|
-
partition.should eql(3)
|
101
|
-
messages_length.should eql(12)
|
102
|
-
end
|
103
|
-
end
|
104
46
|
end
|
105
47
|
|
106
48
|
it "should send messages" do
|
@@ -120,4 +62,4 @@ describe Producer do
|
|
120
62
|
end
|
121
63
|
end
|
122
64
|
end
|
123
|
-
end
|
65
|
+
end
|
metadata
CHANGED
@@ -1,47 +1,42 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: kafka-rb
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 8
|
10
|
-
version: 0.0.8
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Alejandro Crosa
|
14
9
|
- Stefan Mees
|
15
10
|
- Tim Lossen
|
16
11
|
autorequire: kafka-rb
|
17
12
|
bindir: bin
|
18
13
|
cert_chain: []
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
- !ruby/object:Gem::Dependency
|
14
|
+
date: 2012-09-11 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
23
17
|
name: rspec
|
24
|
-
|
25
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
26
19
|
none: false
|
27
|
-
requirements:
|
28
|
-
- -
|
29
|
-
- !ruby/object:Gem::Version
|
30
|
-
|
31
|
-
segments:
|
32
|
-
- 0
|
33
|
-
version: "0"
|
20
|
+
requirements:
|
21
|
+
- - ! '>='
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '0'
|
34
24
|
type: :development
|
35
|
-
|
36
|
-
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ! '>='
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0'
|
32
|
+
description: kafka-rb allows you to produce and consume messages using the Kafka distributed
|
33
|
+
publish/subscribe messaging service.
|
37
34
|
email:
|
38
35
|
executables: []
|
39
|
-
|
40
36
|
extensions: []
|
41
|
-
|
42
|
-
extra_rdoc_files:
|
37
|
+
extra_rdoc_files:
|
43
38
|
- LICENSE
|
44
|
-
files:
|
39
|
+
files:
|
45
40
|
- LICENSE
|
46
41
|
- README.md
|
47
42
|
- Rakefile
|
@@ -60,38 +55,28 @@ files:
|
|
60
55
|
- spec/message_spec.rb
|
61
56
|
- spec/producer_spec.rb
|
62
57
|
- spec/spec_helper.rb
|
63
|
-
homepage: http://github.com/
|
58
|
+
homepage: http://github.com/acrosa/kafka-rb
|
64
59
|
licenses: []
|
65
|
-
|
66
60
|
post_install_message:
|
67
61
|
rdoc_options: []
|
68
|
-
|
69
|
-
require_paths:
|
62
|
+
require_paths:
|
70
63
|
- lib
|
71
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
65
|
none: false
|
73
|
-
requirements:
|
74
|
-
- -
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
|
77
|
-
|
78
|
-
- 0
|
79
|
-
version: "0"
|
80
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
71
|
none: false
|
82
|
-
requirements:
|
83
|
-
- -
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
|
86
|
-
segments:
|
87
|
-
- 0
|
88
|
-
version: "0"
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
89
76
|
requirements: []
|
90
|
-
|
91
77
|
rubyforge_project:
|
92
|
-
rubygems_version: 1.8.
|
78
|
+
rubygems_version: 1.8.24
|
93
79
|
signing_key:
|
94
80
|
specification_version: 3
|
95
81
|
summary: A Ruby client for the Kafka distributed publish/subscribe messaging service
|
96
82
|
test_files: []
|
97
|
-
|