fluent-plugin-zmq-pub 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -8,6 +8,18 @@ Fluentd plugin to publish records to ZeroMQ.
8
8
 
9
9
  Sometimes I wanted to 'sniff' fluentd stream -- running my own programs to the stream without changing fluentd configuration and restarting fluentd. With this plugin, fluentd records are always published to ZeroMQ regardless of the existance of subscriber. After that I can start and stop my subcriber programs at any time.
10
10
 
11
+ == Dependence
12
+
13
+ This plugin use ffi-rzmq to support ZMQ, and need v3.2 or greater version of ZMQ library installed in your system.
14
+
15
+ == Installation
16
+
17
+ You need to install ZeroMQ libraries before installing this plugin
18
+
19
+ ## (RedHat/CentOS)
20
+ # yum install zeromq3 zeromq3-devel
21
+ # fluent-gem install fluent-plugin-zmq-pub
22
+
11
23
  == Configuration
12
24
 
13
25
  <match zmq.**>
@@ -15,6 +27,7 @@ Sometimes I wanted to 'sniff' fluentd stream -- running my own programs to the s
15
27
  pubkey ${tag}:${key1}
16
28
  bindaddr tcp://*:5556
17
29
  flush_interval 1s
30
+ bulk_send true
18
31
  </match>
19
32
 
20
33
  * 'pubkey' specifies the publish key to ZeroMQ.
@@ -22,6 +35,7 @@ Sometimes I wanted to 'sniff' fluentd stream -- running my own programs to the s
22
35
  * Actual record to be published is '<pubkey> <reocord.to_msgpack>'.
23
36
  * Subscriber can subscribe by '<pubkey>'.
24
37
  * 'bindaddr' is the address to which ZeroMQ publisher socket to be bound.
38
+ * If 'bulk_send' is set to true, send multiple records with the same publish key in one 'send_string' method. This improves the performance.
25
39
 
26
40
  == Example usage
27
41
 
@@ -29,7 +43,7 @@ Put the configuration above to fluentd.conf, and save this sample code as 'sampl
29
43
 
30
44
  #!/usr/bin/env ruby
31
45
 
32
- require 'zmq'
46
+ require 'ffi-rzmq'
33
47
  require 'msgpack'
34
48
 
35
49
  context = ZMQ::Context.new(1)
@@ -45,11 +59,13 @@ Put the configuration above to fluentd.conf, and save this sample code as 'sampl
45
59
  end
46
60
 
47
61
  while true
48
- if msg = subscriber.recv(ZMQ::NOBLOCK)
49
- (tag, time, record) = MessagePack.unpack(msg.split(" ",2)[1])
50
- puts "tag: #{tag}"
51
- puts "time: #{time}"
52
- puts "record: #{record.to_s}"
62
+ msg = ''
63
+ while subscriber.recv_string(msg,ZMQ::DONTWAIT) && msg.size > 0
64
+ record = MessagePack.unpack(msg.split(" ",2)[1])
65
+ puts "tag: #{record[0]}"
66
+ puts "time: #{record[1]}"
67
+ puts "record: #{record[2]}"
68
+ msg = ''
53
69
  end
54
70
  sleep(0.1)
55
71
  end
@@ -73,6 +89,21 @@ Then you will get the following output from sample_sub.rb
73
89
 
74
90
  The nice thing is that once you put this plugin to your fluentd.conf and start fluentd, you can start and stop any subscriber programs without changing fluentd configuration.
75
91
 
92
+ == zmq_sub input plugin
93
+
94
+ Input plugin to subscribe the output of zmq_pub is also included. Here is the example configuration.
95
+
96
+ <source>
97
+ type zmq_sub
98
+ publisher tcp://127.0.0.1:5556
99
+ bulk_send true
100
+ subkey zmq.,zmq2
101
+ </source>
102
+
103
+ * If zmq_pub set `bulk_send` to true, zmq_sub also set it to true.
104
+ * `subkey` is a comma separated list of keys to subscribe. In this example, keys starting "zmq." or "zmq2." will be subscribed.
105
+
106
+
76
107
  == Copyright
77
108
 
78
109
  * Copyright (c) 2013- OGIBAYASHI Hironori (@angostura11)
@@ -4,8 +4,8 @@ $:.push File.expand_path('../lib', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["OGIBAYASHI Hironori"]
6
6
  gem.email = ["ogibayashi@gmail.com"]
7
- gem.description = %q{0MQ publisher plugin for fluentd}
8
- gem.summary = %q{0MQ publisher plugin for fluentd}
7
+ gem.description = %q{0MQ publisher/subscriber plugin for fluentd}
8
+ gem.summary = %q{0MQ publisher/subscriber plugin for fluentd, use 0MQ v3.2 or greater version}
9
9
  gem.homepage = ""
10
10
  gem.licenses = ["Apache License, Version 2.0"]
11
11
 
@@ -14,9 +14,9 @@ Gem::Specification.new do |gem|
14
14
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
15
  gem.name = "fluent-plugin-zmq-pub"
16
16
  gem.require_paths = ["lib"]
17
- gem.version = "0.0.2"
17
+ gem.version = "0.0.4"
18
18
  gem.add_development_dependency "fluentd"
19
19
  gem.add_runtime_dependency "fluentd"
20
- gem.add_development_dependency "zmq"
21
- gem.add_runtime_dependency "zmq"
20
+ gem.add_development_dependency "ffi-rzmq"
21
+ gem.add_runtime_dependency "ffi-rzmq"
22
22
  end
@@ -0,0 +1,87 @@
1
+ module Fluent
2
+
3
+ class ZmqSubInput < Fluent::Input
4
+ Fluent::Plugin.register_input('zmq_sub', self)
5
+
6
+ config_param :subkey, :string, :default => ""
7
+ config_param :publisher, :string, :default => "tcp://127.0.0.1:5556"
8
+ config_param :bulk_send, :bool, :default => false
9
+
10
+ attr_reader :subkeys
11
+
12
+ def initialize
13
+ super
14
+ require 'ffi-rzmq'
15
+ end
16
+
17
+ def configure(conf)
18
+ super
19
+ @subkeys = @subkey.split(",")
20
+ end
21
+
22
+ def start
23
+ super
24
+ @context =ZMQ::Context.new()
25
+ @thread = Thread.new(&method(:run))
26
+ end
27
+
28
+ def shutdown
29
+ super
30
+ Thread.kill(@thread)
31
+ @thread.join
32
+ @context.terminate
33
+ end
34
+
35
+ def run
36
+ begin
37
+ @subscriber = @context.socket(ZMQ::SUB)
38
+ @subscriber.connect(@publisher)
39
+ if @subkeys.size > 0
40
+ @subkeys.each do |k|
41
+ @subscriber.setsockopt(ZMQ::SUBSCRIBE,k)
42
+ end
43
+ else
44
+ @subscriber.setsockopt(ZMQ::SUBSCRIBE,'')
45
+ end
46
+ loop do
47
+ msg = ''
48
+ while @subscriber.recv_string(msg,ZMQ::DONTWAIT) && msg.size > 0
49
+ begin
50
+ (key, records) = msg.split(" ",2)
51
+ records = MessagePack.unpack(records)
52
+ if @bulk_send && records[0].class == Array
53
+ es = MultiEventStream.new
54
+ prev_tag = nil
55
+ records.each do |tag, time, record|
56
+ if prev_tag && prev_tag != tag
57
+ Engine.emit_stream(prev_tag, es)
58
+ es = MultiEventStream.new
59
+ end
60
+ es.add(time, record)
61
+ prev_tag = tag
62
+ end
63
+ Engine.emit_stream(prev_tag, es) if es.to_a.size > 0
64
+ else
65
+ Engine.emit(*records)
66
+ end
67
+ rescue => e
68
+ log.warn "Error in processing message.",:error_class => e.class, :error => e
69
+ log.warn_backtrace
70
+ end
71
+ msg = ''
72
+ end
73
+ sleep(0.1)
74
+ end
75
+ rescue => e
76
+ log.error "error occurred while executing plugin.", :error_class => e.class, :error => e
77
+ log.warn_backtrace
78
+ ensure
79
+ if @subscriber
80
+ @subscriber.close
81
+ end
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -4,10 +4,13 @@ module Fluent
4
4
 
5
5
  config_param :pubkey, :string
6
6
  config_param :bindaddr, :string, :default => 'tcp://*:5556'
7
+ config_param :highwatermark, :integer, :default => 1000
8
+ # Send multiple record with the same publish key at once
9
+ config_param :bulk_send, :bool, :default => false
7
10
 
8
11
  def initialize
9
12
  super
10
- require 'zmq'
13
+ require 'ffi-rzmq'
11
14
  @mutex = Mutex.new
12
15
  end
13
16
 
@@ -17,8 +20,9 @@ module Fluent
17
20
 
18
21
  def start
19
22
  super
20
- @context = ZMQ::Context.new(1)
23
+ @context = ZMQ::Context.new()
21
24
  @publisher = @context.socket(ZMQ::PUB)
25
+ @publisher.setsockopt(ZMQ::SNDHWM, @highwatermark)
22
26
  @publisher.bind(@bindaddr)
23
27
  end
24
28
 
@@ -27,6 +31,8 @@ module Fluent
27
31
  end
28
32
 
29
33
  def write(chunk)
34
+ records = { }
35
+ # to_msgpack in format, unpack in write, then to_msgpack again... better way?
30
36
  chunk.msgpack_each{ |record|
31
37
  pubkey_replaced = @pubkey.gsub(/\${(.*?)}/){ |s|
32
38
  case $1
@@ -36,18 +42,29 @@ module Fluent
36
42
  record[2][$1]
37
43
  end
38
44
  }
39
-
40
- # to_msgpack in format, unpack in write, then to_msgpack again... better way?
45
+ if @bulk_send
46
+ records[pubkey_replaced] ||= []
47
+ records[pubkey_replaced] << record
48
+ else
49
+ @mutex.synchronize {
50
+ @publisher.send_string(pubkey_replaced + " " + record.to_msgpack,ZMQ::DONTWAIT)
51
+ }
52
+ end
53
+ }
54
+ if @bulk_send
41
55
  @mutex.synchronize {
42
- @publisher.send(pubkey_replaced + " " + record.to_msgpack,ZMQ::NOBLOCK)
56
+ records.each{ |k,v|
57
+ @publisher.send_string(k + " " + v.to_msgpack,ZMQ::DONTWAIT)
58
+ }
43
59
  }
44
- }
60
+ end
61
+
45
62
  end
46
63
 
47
64
  def shutdown
48
65
  super
49
66
  @publisher.close
50
- @context.close
67
+ @context.terminate
51
68
  end
52
69
 
53
70
  end
data/test/helper.rb CHANGED
@@ -23,6 +23,7 @@ unless ENV.has_key?('VERBOSE')
23
23
  end
24
24
 
25
25
  require 'fluent/plugin/out_zmq_pub'
26
+ require 'fluent/plugin/in_zmq_sub'
26
27
 
27
28
  class Test::Unit::TestCase
28
29
  end
@@ -0,0 +1,97 @@
1
+ require 'helper'
2
+ require 'ffi-rzmq'
3
+
4
+ class ZmqSubIutputTest < Test::Unit::TestCase
5
+ def setup
6
+ Fluent::Test.setup
7
+ @context = ZMQ::Context.new()
8
+ @publisher = @context.socket(ZMQ::PUB)
9
+ @publisher.bind("tcp://*:5556")
10
+ end
11
+
12
+ def teardown
13
+ @publisher.close
14
+ @context.terminate
15
+ end
16
+
17
+ PUBLISHER = "tcp://127.0.0.1:5556"
18
+ CONFIG = %[
19
+ publisher #{PUBLISHER}
20
+ subkey test1.,test2.
21
+ ]
22
+
23
+
24
+ def create_driver(conf=CONFIG)
25
+ Fluent::Test::InputTestDriver.new(Fluent::ZmqSubInput).configure(conf)
26
+ end
27
+
28
+ def test_configure
29
+ d = create_driver(CONFIG + "bulk_send true")
30
+ assert_equal PUBLISHER, d.instance.publisher
31
+ assert_equal ["test1.","test2."], d.instance.subkeys
32
+ assert_equal true, d.instance.bulk_send
33
+ end
34
+
35
+ def test_receive
36
+ d = create_driver
37
+
38
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
39
+ Fluent::Engine.now = time
40
+
41
+ d.expect_emit "test1.aa", time, {"a"=>1}
42
+ d.expect_emit "test2.bb", time, {"a"=>2}
43
+
44
+ d.run do
45
+ d.expected_emits.each {|tag,time,record|
46
+ send_record("dummy",time,record) # This record should not be received.
47
+ send_record(tag,time,record)
48
+ }
49
+ sleep 1
50
+ end
51
+ end
52
+
53
+ def test_no_subkey
54
+ d = create_driver("")
55
+
56
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
57
+ Fluent::Engine.now = time
58
+
59
+ d.expect_emit "test1.aa", time, {"a"=>1}
60
+ d.expect_emit "test2.bb", time, {"a"=>2}
61
+
62
+ d.run do
63
+ d.expected_emits.each {|tag,time,record|
64
+ send_record(tag,time,record)
65
+ }
66
+ sleep 1
67
+ end
68
+ end
69
+
70
+
71
+ def test_receive_bulk
72
+ d = create_driver(CONFIG + "bulk_send true")
73
+
74
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
75
+ Fluent::Engine.now = time
76
+
77
+ d.expect_emit "test3.aa", time, {"a"=>1}
78
+ d.expect_emit "test4.bb", time, {"a"=>2}
79
+
80
+ d.run do
81
+ record_to_send = []
82
+ d.expected_emits.each {|tag,time,record|
83
+ record_to_send << [tag,time,record]
84
+ }
85
+ send_record_bulk("test1.aa",record_to_send)
86
+ sleep 1
87
+ end
88
+ end
89
+
90
+ def send_record(tag,time,record)
91
+ @publisher.send_string(tag + " " + [tag,time,record].to_msgpack)
92
+ end
93
+
94
+ def send_record_bulk(tag,records)
95
+ @publisher.send_string(tag + " " + records.to_msgpack)
96
+ end
97
+ end
@@ -1,5 +1,5 @@
1
1
  require 'helper'
2
- require 'zmq'
2
+ require 'ffi-rzmq'
3
3
 
4
4
  class ZmqPubOutputTest < Test::Unit::TestCase
5
5
  def setup
@@ -11,13 +11,24 @@ class ZmqPubOutputTest < Test::Unit::TestCase
11
11
 
12
12
  def teardown
13
13
  @subscriber.close
14
- @context.close
14
+ @context.terminate
15
15
  end
16
16
 
17
17
  CONFIG = %[
18
18
  pubkey ${tag}:${key1}
19
19
  bindaddr tcp://*:5556
20
20
  ]
21
+
22
+ CONFIG_BULK = %[
23
+ pubkey ${tag}:${key1}
24
+ bindaddr tcp://*:5556
25
+ bulk_send true
26
+ ]
27
+
28
+ CONFIG_BY_TAG = %[
29
+ pubkey ${tag}
30
+ bindaddr tcp://*:5556
31
+ ]
21
32
 
22
33
  def create_driver(conf = CONFIG, tag='test')
23
34
  Fluent::Test::BufferedOutputTestDriver.new(Fluent::ZmqPubOutput, tag).configure(conf)
@@ -50,16 +61,44 @@ class ZmqPubOutputTest < Test::Unit::TestCase
50
61
  time = Time.parse("2011-01-02 13:14:15 UTC").to_i
51
62
  d.emit({"key1"=>"aaa"}, time)
52
63
  d.emit({"key1"=>"bbb", "key2"=>3}, time)
64
+ d.emit({"key1"=>"aaa", "key2"=>4}, time)
53
65
 
54
66
  d.run
55
67
  sleep 1
56
68
 
57
- msg = @subscriber.recv(ZMQ::NOBLOCK)
69
+ msg = ''
70
+ @subscriber.recv_string(msg,ZMQ::DONTWAIT)
58
71
  (key, record) = msg.split(" ",2)
59
72
  assert_equal ["test",time.to_i,{ "key1" => "aaa"}].to_msgpack, record
60
73
 
74
+ msg = ''
75
+ @subscriber.recv_string(msg,ZMQ::DONTWAIT)
76
+ (key, record) = msg.split(" ",2)
77
+ assert_equal ["test",time.to_i,{ "key1" => "aaa","key2" => 4 }].to_msgpack, record
78
+
61
79
  end
62
80
 
81
+ def test_write_bulk
82
+ d = create_driver(CONFIG_BULK)
83
+ @subscriber.setsockopt(ZMQ::SUBSCRIBE,"test:aaa")
84
+
85
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
86
+ d.emit({"key1"=>"aaa"}, time)
87
+ d.emit({"key1"=>"bbb", "key2"=>3}, time)
88
+ d.emit({"key1"=>"aaa", "key2"=>4}, time)
89
+
90
+ d.run
91
+ sleep 1
92
+
93
+ msg = ''
94
+ @subscriber.recv_string(msg,ZMQ::DONTWAIT)
95
+ (key, record) = msg.split(" ",2)
96
+ assert_equal [["test",time.to_i,{ "key1" => "aaa"}],
97
+ ["test",time.to_i,{ "key1" => "aaa","key2" => 4 }]].to_msgpack, record
98
+
99
+ end
100
+
101
+
63
102
  end
64
103
 
65
104
 
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: fluent-plugin-zmq-pub
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.2
5
+ version: 0.0.4
6
6
  platform: ruby
7
7
  authors:
8
8
  - OGIBAYASHI Hironori
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2014-03-27 00:00:00 Z
13
+ date: 2014-10-09 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: fluentd
@@ -35,7 +35,7 @@ dependencies:
35
35
  type: :runtime
36
36
  version_requirements: *id002
37
37
  - !ruby/object:Gem::Dependency
38
- name: zmq
38
+ name: ffi-rzmq
39
39
  prerelease: false
40
40
  requirement: &id003 !ruby/object:Gem::Requirement
41
41
  none: false
@@ -46,7 +46,7 @@ dependencies:
46
46
  type: :development
47
47
  version_requirements: *id003
48
48
  - !ruby/object:Gem::Dependency
49
- name: zmq
49
+ name: ffi-rzmq
50
50
  prerelease: false
51
51
  requirement: &id004 !ruby/object:Gem::Requirement
52
52
  none: false
@@ -56,7 +56,7 @@ dependencies:
56
56
  version: "0"
57
57
  type: :runtime
58
58
  version_requirements: *id004
59
- description: 0MQ publisher plugin for fluentd
59
+ description: 0MQ publisher/subscriber plugin for fluentd
60
60
  email:
61
61
  - ogibayashi@gmail.com
62
62
  executables: []
@@ -72,8 +72,10 @@ files:
72
72
  - README.rdoc
73
73
  - Rakefile
74
74
  - fluent-plugin-zmq-pub.gemspec
75
+ - lib/fluent/plugin/in_zmq_sub.rb
75
76
  - lib/fluent/plugin/out_zmq_pub.rb
76
77
  - test/helper.rb
78
+ - test/plugin/test_in_zmq_sub.rb
77
79
  - test/plugin/test_out_zeromq_pub.rb
78
80
  homepage: ""
79
81
  licenses:
@@ -101,7 +103,8 @@ rubyforge_project:
101
103
  rubygems_version: 1.8.25
102
104
  signing_key:
103
105
  specification_version: 3
104
- summary: 0MQ publisher plugin for fluentd
106
+ summary: 0MQ publisher/subscriber plugin for fluentd, use 0MQ v3.2 or greater version
105
107
  test_files:
106
108
  - test/helper.rb
109
+ - test/plugin/test_in_zmq_sub.rb
107
110
  - test/plugin/test_out_zeromq_pub.rb