hermann 0.20.1 → 0.23.0.232

Sign up to get free protection for your applications and to get access to all the features.
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
-