hermann 0.20.1 → 0.23.0.232

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.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 4fbd82155fea1d32129ee67adcccc3fa420aa320
4
- data.tar.gz: cbaa50e297bdceda35c83e6397441d32b9783978
5
- SHA512:
6
- metadata.gz: 6405cf451dce7e66abaa18e698ead0bfca6e70c0e67736ab378b8bfba0a89eb0368bd4c94e22e79c502ae197f201d0fd51d5c8451776fe671df7dcebd0dccbc9
7
- data.tar.gz: 3f92eff84eddb9dbeed39b1cda46b30669431f87566127119660383c128d2af9485302d8f21aed5b5b5bf2582e1ff42bebfdc6cedfaf4cdc421250071921798a
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Y2UyMjNlM2Q5NTg5NzJlY2MxZWYzNTdiNjA3ZWFlZGVlMWE2MDEwZg==
5
+ data.tar.gz: !binary |-
6
+ Mzc0Zjg5OTQ5Zjk3OTJlNzk3YjhjNjdhNWZmMGQwNjdjYmY0MmQ0Yw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZWFhYWY5OGZjZmYwOTQ5YzIyM2ExYjQzNDJhMTkyOGRiNzQyOTUwZTdiMzRm
10
+ NzhlMTI5YWM1ZTkzYzdiMGVlZjUyMGE4ODgyNzI0YWE5MWVhMTMyNzUzYjdj
11
+ YjEzN2QxNDAyNTgzZGQ1ZDIyZTZmZTBiY2U5ZjFiYjA0Zjk2ZDY=
12
+ data.tar.gz: !binary |-
13
+ OWY5ZWY5NTZjMzE3NzNmOTk3OTYyNTc1YmU3YmVlYjg3MmM5OTBlMGM5NDI2
14
+ MmJmOGI0M2M5NzMxOWQ4YTI4ZTYyNWYwMTgzMTEwYzA4ZDYzODMyOTU5YmVj
15
+ NjFmNzNmZjhhZjI3MzE0NGUxNTAwOTI1OWJkNjE1NjIwMDUwNTA=
@@ -31,5 +31,5 @@ module Hermann
31
31
  end
32
32
 
33
33
  if Hermann.jruby?
34
- require 'hermann/java'
34
+ require 'hermann/java'
35
35
  end
@@ -10,32 +10,27 @@ module Hermann
10
10
  # Hermann::Consumer provides a simple consumer API which is only safe to be
11
11
  # executed in a single thread
12
12
  class Consumer
13
- attr_reader :topic, :brokers, :partition, :internal
13
+ attr_reader :topic, :internal
14
14
 
15
15
 
16
16
  # Instantiate Consumer
17
17
  #
18
18
  # @params [String] kafka topic
19
+ # @params [Hash] options for Consumer
20
+ # @option opts [String] :brokers (for MRI) Comma separated list of brokers
21
+ # @option opts [Integer] :partition (for MRI) The kafka partition
22
+ # @option opts [Integer] :zookeepers (for jruby) list of zookeeper servers
23
+ # @option opts [Integer] :group_id (for jruby) client group_id
19
24
  #
20
- # @params [String] group ID
21
- #
22
- # @params [String] comma separated zookeeper list
23
- #
24
- # @params [Hash] options for consumer
25
- # @option opts [String] :brokers (for MRI) Comma separated list of brokers
26
- # @option opts [String] :partition (for MRI) The kafka partition
27
- # @option opts [Fixnum] :sleep_time (Jruby) Time to sleep between consume retries, defaults to 1sec
28
- # @option opts [Boolean] :do_retry (Jruby) Retry consume attempts if exceptions are thrown, defaults to true
29
- def initialize(topic, groupId, zookeepers, opts={})
25
+ def initialize(topic, opts = {})
30
26
  @topic = topic
31
- @brokers = brokers
32
- @partition = partition
33
-
34
27
  if Hermann.jruby?
35
- @internal = Hermann::Provider::JavaSimpleConsumer.new(zookeepers, groupId, topic, opts)
28
+ zookeepers, group_id = require_values_at(opts, :zookeepers, :group_id)
29
+
30
+ @internal = Hermann::Provider::JavaSimpleConsumer.new(zookeepers, group_id, topic, opts)
36
31
  else
37
- brokers = opts.delete(:brokers)
38
- partition = opts.delete(:partition)
32
+ brokers, partition = require_values_at(opts, :brokers, :partition)
33
+
39
34
  @internal = Hermann::Lib::Consumer.new(topic, brokers, partition)
40
35
  end
41
36
  end
@@ -53,5 +48,12 @@ module Hermann
53
48
  #no op
54
49
  end
55
50
  end
51
+
52
+ def require_values_at(opts, *args)
53
+ args.map do |a|
54
+ raise "Please provide :#{a} option!" unless opts[a]
55
+ opts.delete(a)
56
+ end
57
+ end
56
58
  end
57
59
  end
@@ -1,21 +1,30 @@
1
1
  require 'hermann'
2
- require 'zk'
2
+
3
+ if RUBY_PLATFORM == 'java'
4
+ require 'java'
5
+ end
6
+
3
7
  require 'json'
4
8
  require 'hermann/errors'
5
9
 
6
10
  module Hermann
7
11
  module Discovery
8
-
9
-
10
12
  # Communicates with Zookeeper to discover kafka broker ids
11
13
  #
12
14
  class Zookeeper
13
- attr_reader :zookeepers
15
+ attr_reader :zookeepers, :impl
14
16
 
15
17
  BROKERS_PATH = "/brokers/ids".freeze
16
-
17
18
  def initialize(zookeepers)
18
19
  @zookeepers = zookeepers
20
+ @impl = nil
21
+ if CuratorImpl.usable?
22
+ @impl = CuratorImpl.new(zookeepers)
23
+ elsif ZkGemImpl.usable?
24
+ @impl = ZkGemImpl.new(zookeepers)
25
+ else
26
+ raise Hermann::Errors::GeneralError, "Could not find a usable Zookeeper implementation, please make sure either the `zk` gem is installed or Curator is on the classpath"
27
+ end
19
28
  end
20
29
 
21
30
  # Gets comma separated string of brokers
@@ -27,10 +36,8 @@ module Hermann
27
36
  # @return [Array] List of brokers from ZK
28
37
  # @raises [NoBrokersError] if could not discover brokers thru zookeeper
29
38
  def get_brokers(timeout=0)
30
- brokers = []
31
- ZK.open(zookeepers, {:timeout => timeout}) do |zk|
32
- brokers = fetch_brokers(zk)
33
- end
39
+ brokers = impl.brokers(timeout).map { |b| format_broker_from_znode(b) }
40
+
34
41
  if brokers.empty?
35
42
  raise Hermann::Errors::NoBrokersError
36
43
  end
@@ -39,6 +46,43 @@ module Hermann
39
46
 
40
47
  private
41
48
 
49
+ # Formats the node data into string
50
+ #
51
+ # @param [String] node data
52
+ #
53
+ # @return [String] formatted node data or empty string if error
54
+ def format_broker_from_znode(znode)
55
+ hash = JSON.parse(znode)
56
+ host = hash['host']
57
+ port = hash['port']
58
+ host && port ? "#{host}:#{port}" : nil
59
+ rescue JSON::ParserError
60
+ nil
61
+ end
62
+
63
+ # The ZkGemImpl class is an implementation of simple broker discovery
64
+ # using the `zk` gem if it is available
65
+ class ZkGemImpl
66
+ def self.usable?
67
+ begin
68
+ require 'zk'
69
+ return true
70
+ rescue LoadError
71
+ return false
72
+ end
73
+ end
74
+
75
+ def initialize(zks)
76
+ @zookeepers = zks
77
+ end
78
+
79
+ def brokers(timeout=0)
80
+ ZK.open(@zookeepers, {:timeout => timeout}) do |zk|
81
+ return fetch_brokers(zk)
82
+ end
83
+ end
84
+
85
+ private
42
86
  # Gets an Array of broker strings
43
87
  #
44
88
  # @param [ZK::Client] zookeeper client
@@ -49,9 +93,9 @@ module Hermann
49
93
  zk.children(BROKERS_PATH).each do |id|
50
94
  node = fetch_znode(zk, id)
51
95
  next if node.nil? # whatever error could happen from ZK#get
52
- brokers << format_broker_from_znode(node)
96
+ brokers << node
53
97
  end
54
- brokers.compact
98
+ return brokers
55
99
  end
56
100
 
57
101
  # Gets node from zookeeper
@@ -65,20 +109,44 @@ module Hermann
65
109
  rescue ZK::Exceptions::NoNode
66
110
  nil
67
111
  end
112
+ end
68
113
 
69
- # Formats the node data into string
70
- #
71
- # @param [String] node data
72
- #
73
- # @return [String] formatted node data or empty string if error
74
- def format_broker_from_znode(znode)
75
- hash = JSON.parse(znode)
76
- host = hash['host']
77
- port = hash['port']
78
- host && port ? "#{host}:#{port}" : nil
79
- rescue JSON::ParserError
80
- nil
114
+ # The CuratorImpl is an implementation of simple broker discovery using
115
+ # Apache Curator libraries, if they are made available on the classpath
116
+ # for the process running Hermann::Discovery::Zookeeper.
117
+ #
118
+ # For a number of reasons this is preferred over the `zk` gem, namely
119
+ # being a much more simple and mature Zookeeper client interface
120
+ class CuratorImpl
121
+ def self.usable?
122
+ begin
123
+ Java::OrgApacheCuratorFramework::CuratorFrameworkFactory
124
+ return true
125
+ rescue NameError
126
+ return false
127
+ end
81
128
  end
129
+
130
+ def initialize(zks)
131
+ retry_policy = Java::OrgApacheCuratorRetry::ExponentialBackoffRetry.new(1000, 3)
132
+ @curator = Java::OrgApacheCuratorFramework::CuratorFrameworkFactory.newClient(zks, retry_policy)
133
+ end
134
+
135
+ # Timeout is discarded, only later versions of Curator support
136
+ # blockUntilConnected which would take the timeout variable
137
+ def brokers(timeout=0)
138
+ unless @curator.started?
139
+ @curator.start
140
+ end
141
+
142
+ brokers = []
143
+ @curator.children.for_path(BROKERS_PATH).each do |id|
144
+ path = "#{BROKERS_PATH}/#{id}"
145
+ brokers << @curator.data.for_path(path).to_s
146
+ end
147
+ return brokers
148
+ end
149
+ end
82
150
  end
83
151
  end
84
152
  end
@@ -50,6 +50,7 @@ module Hermann
50
50
  # @param [Object] value A single object to push
51
51
  # @param [Hash] opts to pass to push method
52
52
  # @option opts [String] :topic The topic to push messages to
53
+ # :partition_key The string to partition by
53
54
  #
54
55
  # @return [Hermann::Result] A future-like object which will store the
55
56
  # result from the broker
@@ -62,7 +63,8 @@ module Hermann
62
63
  end
63
64
 
64
65
  if Hermann.jruby?
65
- result = @internal.push_single(value, topic, nil)
66
+ key = opts.has_key?(:partition_key) ? opts[:partition_key].to_java : nil
67
+ result = @internal.push_single(value, topic, key)
66
68
  unless result.nil?
67
69
  @children << result
68
70
  end
@@ -1,6 +1,7 @@
1
1
  require 'hermann'
2
2
  require 'concurrent'
3
3
  require 'json'
4
+ require 'hermann/errors'
4
5
 
5
6
  module Hermann
6
7
  module Provider
@@ -43,9 +44,9 @@ module Hermann
43
44
  # @return +Concurrent::Promise+ Representa a promise to send the
44
45
  # data to the kafka broker. Upon execution the Promise's status
45
46
  # will be set
46
- def push_single(msg, topic, unused)
47
+ def push_single(msg, topic, key)
47
48
  Concurrent::Promise.execute {
48
- data = ProducerUtil::KeyedMessage.new(topic, msg.to_java_bytes)
49
+ data = ProducerUtil::KeyedMessage.new(topic, nil, key, msg.to_java_bytes)
49
50
  begin
50
51
  @producer.send(data)
51
52
  rescue Java::KafkaCommon::FailedToSendMessageException => jexc
@@ -12,11 +12,18 @@ module Hermann
12
12
  NUM_THREADS = 1
13
13
 
14
14
  #default zookeeper connection options
15
- DEFAULTS = {
16
- 'zookeeper.session.timeout.ms' => '400',
17
- 'zookeeper.sync.time.ms' => '200',
18
- 'auto.commit.interval.ms' => '1000'
19
- }.freeze
15
+ DEFAULTS_HERMANN_OPTS = {
16
+ 'zookeeper.session.timeout.ms' => '400',
17
+ 'zookeeper.sync.time.ms' => '200',
18
+ 'auto.commit.interval.ms' => '1000',
19
+ }.freeze
20
+
21
+ DEFAULT_CONSUMER_OPTIONS = {
22
+ :do_retry => true,
23
+ :max_retries => 3,
24
+ :backoff_time_sec => 1,
25
+ :logger => nil
26
+ }.freeze
20
27
 
21
28
  # Instantiate JavaSimpleConsumer
22
29
  #
@@ -27,14 +34,22 @@ module Hermann
27
34
  # @params [String] Kafka topic
28
35
  #
29
36
  # @params [Hash] kafka options for consumer
30
- # @option opts [Fixnum] :sleep_time Time to sleep between consume retries, defaults to 1sec
37
+ # @option opts [Fixnum] :backoff_time_sec Time to sleep between consume retries, defaults to 1sec
31
38
  # @option opts [Boolean] :do_retry Retry consume attempts if exceptions are thrown, defaults to true
39
+ # @option opts [Fixnum] :max_retries Number of max_retries to retry #consume when it throws an exception
40
+ # @option opts [Logger] :logger Pass in a Logger
41
+ # @option opts [Other] other opts from kafka
32
42
  def initialize(zookeepers, groupId, topic, opts={})
33
- config = create_config(zookeepers, groupId)
34
- @consumer = ConsumerUtil::Consumer.createJavaConsumerConnector(config)
35
- @topic = topic
36
- @sleep_time = opts.delete(:sleep_time) || 1
37
- @do_retry = opts.delete(:do_retry) || true
43
+ @topic = topic
44
+ options = DEFAULT_CONSUMER_OPTIONS.merge(opts)
45
+ @backoff_time_sec = options.delete(:backoff_time_sec)
46
+ @do_retry = options.delete(:do_retry)
47
+ @max_retries = options.delete(:max_retries)
48
+ @logger = options.delete(:logger)
49
+ # deleting options above so that they do not get sent to
50
+ # the create_config method
51
+ config = create_config(zookeepers, groupId, options)
52
+ @consumer = ConsumerUtil::Consumer.createJavaConsumerConnector(config)
38
53
  end
39
54
 
40
55
  # Shuts down the various threads created by createMessageStreams
@@ -63,14 +78,13 @@ module Hermann
63
78
  message = it.next.message
64
79
  yield String.from_java_bytes(message)
65
80
  end
66
- rescue Exception => e
67
- puts "#{self.class.name}#consume exception: #{e.class.name}"
68
- puts "Msg: #{e.message}"
69
- puts e.backtrace.join("\n")
70
- if retry?
71
- sleep @sleep_time
81
+ rescue => e
82
+ if retry? && @max_retries > 0
83
+ sleep @backoff_time_sec
84
+ @max_retries -= 1
72
85
  retry
73
86
  else
87
+ log_exception(e)
74
88
  raise e
75
89
  end
76
90
  end
@@ -81,6 +95,13 @@ module Hermann
81
95
  @do_retry
82
96
  end
83
97
 
98
+ def log_exception(e)
99
+ return unless @logger
100
+ @logger.error("#{self.class.name}#consume exception: #{e.class.name}")
101
+ @logger.error("Msg: #{e.message}")
102
+ @logger.error(e.backtrace.join("\n"))
103
+ end
104
+
84
105
  # Gets the message stream of the topic. Creates message streams for
85
106
  # a topic and the number of threads requested. In this case the default
86
107
  # number of threads is NUM_THREADS.
@@ -106,7 +127,7 @@ module Hermann
106
127
  # @raises [RuntimeError] if options does not contain key value strings
107
128
  def create_config(zookeepers, groupId, opts={})
108
129
  config = connect_opts(zookeepers, groupId)
109
- options = DEFAULTS.merge(config).merge(opts)
130
+ options = DEFAULTS_HERMANN_OPTS.merge(config).merge(opts)
110
131
  properties = Hermann.package_properties(options)
111
132
  ConsumerUtil::ConsumerConfig.new(properties)
112
133
  end
@@ -1,3 +1,3 @@
1
1
  module Hermann
2
- VERSION = '0.20.1'
2
+ VERSION = '0.23.0'
3
3
  end
@@ -1,15 +1,13 @@
1
1
  # this is a generated file, to avoid over-writing it just delete this comment
2
2
  require 'jar_dependencies'
3
3
 
4
- require_jar( 'junit', 'junit', '3.8.1' )
4
+ require_jar( 'org.apache.zookeeper', 'zookeeper', '3.3.4' )
5
+ require_jar( 'net.sf.jopt-simple', 'jopt-simple', '3.2' )
6
+ require_jar( 'org.xerial.snappy', 'snappy-java', '1.0.5' )
7
+ require_jar( 'jline', 'jline', '0.9.94' )
5
8
  require_jar( 'com.101tec', 'zkclient', '0.3' )
6
- require_jar( 'com.yammer.metrics', 'metrics-core', '2.2.0' )
7
9
  require_jar( 'log4j', 'log4j', '1.2.17' )
8
- require_jar( 'jline', 'jline', '0.9.94' )
9
- require_jar( 'net.sf.jopt-simple', 'jopt-simple', '3.2' )
10
- require_jar( 'org.apache.zookeeper', 'zookeeper', '3.3.4' )
11
- require_jar( 'org.mod4j.org.eclipse.xtext', 'log4j', '1.2.15' )
10
+ require_jar( 'org.scala-lang', 'scala-library', '2.10.1' )
12
11
  require_jar( 'org.slf4j', 'slf4j-api', '1.7.2' )
12
+ require_jar( 'com.yammer.metrics', 'metrics-core', '2.2.0' )
13
13
  require_jar( 'org.apache.kafka', 'kafka_2.10', '0.8.1.1' )
14
- require_jar( 'org.scala-lang', 'scala-library', '2.10.1' )
15
- require_jar( 'org.xerial.snappy', 'snappy-java', '1.0.5' )
metadata CHANGED
@@ -1,68 +1,69 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: hermann
3
- version: !ruby/object:Gem::Version
4
- version: 0.20.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.23.0.232
5
5
  platform: ruby
6
- authors:
6
+ authors:
7
7
  - R. Tyler Croy
8
+ - James Way
8
9
  - Stan Campbell
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
-
13
- date: 2014-11-13 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
13
+ date: 2015-04-27 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
16
  name: concurrent-ruby
17
- requirement: &id001 !ruby/object:Gem::Requirement
18
- requirements:
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
19
  - - ~>
20
- - !ruby/object:Gem::Version
20
+ - !ruby/object:Gem::Version
21
21
  version: 0.7.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *id001
25
- - !ruby/object:Gem::Dependency
26
- name: zk
27
- requirement: &id002 !ruby/object:Gem::Requirement
28
- requirements:
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
29
26
  - - ~>
30
- - !ruby/object:Gem::Version
31
- version: 1.9.4
32
- type: :runtime
33
- prerelease: false
34
- version_requirements: *id002
35
- - !ruby/object:Gem::Dependency
27
+ - !ruby/object:Gem::Version
28
+ version: 0.7.0
29
+ - !ruby/object:Gem::Dependency
36
30
  name: thread_safe
37
- requirement: &id003 !ruby/object:Gem::Requirement
38
- requirements:
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
39
33
  - - ~>
40
- - !ruby/object:Gem::Version
34
+ - !ruby/object:Gem::Version
41
35
  version: 0.3.4
42
36
  type: :runtime
43
37
  prerelease: false
44
- version_requirements: *id003
45
- - !ruby/object:Gem::Dependency
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ version: 0.3.4
43
+ - !ruby/object:Gem::Dependency
46
44
  name: mini_portile
47
- requirement: &id004 !ruby/object:Gem::Requirement
48
- requirements:
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
49
47
  - - ~>
50
- - !ruby/object:Gem::Version
48
+ - !ruby/object:Gem::Version
51
49
  version: 0.6.0
52
50
  type: :runtime
53
51
  prerelease: false
54
- version_requirements: *id004
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: 0.6.0
55
57
  description: Ruby gem for talking to Kafka
56
- email:
58
+ email:
57
59
  - rtyler.croy@lookout.com
60
+ - james.way@lookout.com
58
61
  - stan.campbell3@gmail.com
59
62
  executables: []
60
-
61
- extensions:
63
+ extensions:
62
64
  - ext/hermann/extconf.rb
63
65
  extra_rdoc_files: []
64
-
65
- files:
66
+ files:
66
67
  - Rakefile
67
68
  - ext/hermann/extconf.rb
68
69
  - ext/hermann/hermann_lib.c
@@ -81,31 +82,28 @@ files:
81
82
  - lib/hermann/version.rb
82
83
  - lib/hermann_jars.rb
83
84
  homepage: https://github.com/lookout/Hermann
84
- licenses:
85
+ licenses:
85
86
  - MIT
86
87
  metadata: {}
87
-
88
88
  post_install_message:
89
89
  rdoc_options: []
90
-
91
- require_paths:
90
+ require_paths:
92
91
  - lib
93
92
  - ext/hermann
94
- required_ruby_version: !ruby/object:Gem::Requirement
95
- requirements:
96
- - &id005
97
- - ">="
98
- - !ruby/object:Gem::Version
99
- version: "0"
100
- required_rubygems_version: !ruby/object:Gem::Requirement
101
- requirements:
102
- - *id005
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
103
  requirements: []
104
-
105
104
  rubyforge_project:
106
- rubygems_version: 2.4.1
105
+ rubygems_version: 2.4.6
107
106
  signing_key:
108
107
  specification_version: 3
109
108
  summary: A Kafka consumer/producer gem supporting both MRI and JRuby
110
109
  test_files: []
111
-