hermann 0.17.0 → 0.18.0
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 +4 -4
- data/ext/hermann/extconf.rb +75 -0
- data/ext/hermann/hermann_lib.c +23 -13
- data/lib/hermann/discovery/zookeeper.rb +7 -2
- data/lib/hermann/errors.rb +6 -0
- data/lib/hermann/producer.rb +26 -14
- data/lib/hermann/provider/java_producer.rb +62 -20
- data/lib/hermann/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92486b735dc5e9e88b0243fabc35340cd3c3a117
|
4
|
+
data.tar.gz: aa5b9762eb50d57943a1c6bf1a78d84abc6c5055
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52e2b6523b9d0dd60e14a90e73babca4bb07e8ee2a72d2b3eecf799ede905e61813765e9d32d25cfc5194558f4551a914218bb8537e11debea2a13ab629f346e
|
7
|
+
data.tar.gz: 9847e3e907dfcba53523e7f019041a8c73f046efc8f5d1b04afbcec148e9b5d0f72b2aa2fc6154af3db11799ddc55e7646cc3b7687df0bcde43eefbf79259614
|
data/ext/hermann/extconf.rb
CHANGED
@@ -44,6 +44,81 @@ class RdKafkaRecipe < MiniPortile
|
|
44
44
|
raise 'Checksum error!'
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
48
|
+
def download_file_http(url, full_path, count = 3)
|
49
|
+
filename = File.basename(full_path)
|
50
|
+
uri = URI.parse(url)
|
51
|
+
|
52
|
+
if ENV['http_proxy']
|
53
|
+
_, userinfo, p_host, p_port = URI.split(ENV['http_proxy'])
|
54
|
+
proxy_user, proxy_pass = userinfo.split(/:/) if userinfo
|
55
|
+
http = Net::HTTP.new(uri.host, uri.port, p_host, p_port, proxy_user, proxy_pass)
|
56
|
+
else
|
57
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
58
|
+
|
59
|
+
if URI::HTTPS === uri
|
60
|
+
http.use_ssl = true
|
61
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
62
|
+
|
63
|
+
store = OpenSSL::X509::Store.new
|
64
|
+
|
65
|
+
# Auto-include system-provided certificates
|
66
|
+
store.set_default_paths
|
67
|
+
|
68
|
+
if ENV.has_key?("SSL_CERT_FILE") && File.exist?(ENV["SSL_CERT_FILE"])
|
69
|
+
store.add_file ENV["SSL_CERT_FILE"]
|
70
|
+
end
|
71
|
+
|
72
|
+
http.cert_store = store
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
message "Downloading #{filename} "
|
77
|
+
http.start do |h|
|
78
|
+
h.request_get(uri.path, 'Accept-Encoding' => 'identity') do |response|
|
79
|
+
case response
|
80
|
+
when Net::HTTPNotFound
|
81
|
+
output "404 - Not Found"
|
82
|
+
return false
|
83
|
+
|
84
|
+
when Net::HTTPClientError
|
85
|
+
output "Error: Client Error: #{response.inspect}"
|
86
|
+
return false
|
87
|
+
|
88
|
+
when Net::HTTPRedirection
|
89
|
+
raise "Too many redirections for the original URL, halting." if count <= 0
|
90
|
+
url = response["location"]
|
91
|
+
return download_file(url, full_path, count - 1)
|
92
|
+
|
93
|
+
when Net::HTTPOK
|
94
|
+
return with_tempfile(filename, full_path) do |temp_file|
|
95
|
+
size = 0
|
96
|
+
progress = 0
|
97
|
+
total = response.header["Content-Length"].to_i
|
98
|
+
|
99
|
+
if total == 0
|
100
|
+
# There are cases when apparently GitHub.com will return an empty
|
101
|
+
# content-length header, which means we can't really trust the
|
102
|
+
# response, so we'll treat it like a redirect
|
103
|
+
puts "Empty content-length header, retrying"
|
104
|
+
return download_file(url, full_path, count - 1)
|
105
|
+
end
|
106
|
+
|
107
|
+
response.read_body do |chunk|
|
108
|
+
temp_file << chunk
|
109
|
+
size += chunk.size
|
110
|
+
new_progress = (size * 100) / total
|
111
|
+
unless new_progress == progress
|
112
|
+
message "\rDownloading %s (%3d%%) " % [filename, new_progress]
|
113
|
+
end
|
114
|
+
progress = new_progress
|
115
|
+
end
|
116
|
+
output
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
47
122
|
end
|
48
123
|
################################################################################
|
49
124
|
|
data/ext/hermann/hermann_lib.c
CHANGED
@@ -534,16 +534,19 @@ void producer_init_kafka(VALUE self, HermannInstanceConfig* config) {
|
|
534
534
|
*
|
535
535
|
* @param self VALUE the Ruby producer instance
|
536
536
|
* @param message VALUE the ruby String containing the outgoing message.
|
537
|
+
* @param topic VALUE the ruby String containing the topic to use for the
|
538
|
+
* outgoing message.
|
537
539
|
* @param result VALUE the Hermann::Result object to be fulfilled when the
|
538
540
|
* push completes
|
539
541
|
*/
|
540
|
-
static VALUE producer_push_single(VALUE self, VALUE message, VALUE result) {
|
542
|
+
static VALUE producer_push_single(VALUE self, VALUE message, VALUE topic, VALUE result) {
|
541
543
|
|
542
544
|
HermannInstanceConfig* producerConfig;
|
543
545
|
/* Context pointer, pointing to `result`, for the librdkafka delivery
|
544
546
|
* callback
|
545
547
|
*/
|
546
548
|
hermann_push_ctx_t *delivery_ctx = (hermann_push_ctx_t *)malloc(sizeof(hermann_push_ctx_t));
|
549
|
+
rd_kafka_topic_t *rkt = NULL;
|
547
550
|
|
548
551
|
TRACER("self: %p, message: %p, result: %p)\n", self, message, result);
|
549
552
|
|
@@ -554,10 +557,9 @@ static VALUE producer_push_single(VALUE self, VALUE message, VALUE result) {
|
|
554
557
|
|
555
558
|
TRACER("producerConfig: %p\n", producerConfig);
|
556
559
|
|
557
|
-
if ((
|
558
|
-
(0 ==
|
559
|
-
|
560
|
-
rb_raise(rb_eRuntimeError, "Topic cannot be empty");
|
560
|
+
if ((Qnil == topic) ||
|
561
|
+
(0 == RSTRING_LEN(topic))) {
|
562
|
+
rb_raise(rb_eArgError, "Topic cannot be empty");
|
561
563
|
return self;
|
562
564
|
}
|
563
565
|
|
@@ -567,6 +569,15 @@ static VALUE producer_push_single(VALUE self, VALUE message, VALUE result) {
|
|
567
569
|
|
568
570
|
TRACER("kafka initialized\n");
|
569
571
|
|
572
|
+
rkt = rd_kafka_topic_new(producerConfig->rk,
|
573
|
+
RSTRING_PTR(topic),
|
574
|
+
NULL);
|
575
|
+
|
576
|
+
if (NULL == rkt) {
|
577
|
+
rb_raise(rb_eRuntimeError, "Could not construct a topic structure");
|
578
|
+
return self;
|
579
|
+
}
|
580
|
+
|
570
581
|
/* Only pass result through if it's non-nil */
|
571
582
|
if (Qnil != result) {
|
572
583
|
delivery_ctx->result = result;
|
@@ -576,7 +587,7 @@ static VALUE producer_push_single(VALUE self, VALUE message, VALUE result) {
|
|
576
587
|
TRACER("rd_kafka_produce() message of %i bytes\n", RSTRING_LEN(message));
|
577
588
|
|
578
589
|
/* Send/Produce message. */
|
579
|
-
if (-1 == rd_kafka_produce(
|
590
|
+
if (-1 == rd_kafka_produce(rkt,
|
580
591
|
producerConfig->partition,
|
581
592
|
RD_KAFKA_MSG_F_COPY,
|
582
593
|
RSTRING_PTR(message),
|
@@ -590,6 +601,10 @@ static VALUE producer_push_single(VALUE self, VALUE message, VALUE result) {
|
|
590
601
|
/* TODO: raise a Ruby exception here, requires a test though */
|
591
602
|
}
|
592
603
|
|
604
|
+
if (NULL != rkt) {
|
605
|
+
rd_kafka_topic_destroy(rkt);
|
606
|
+
}
|
607
|
+
|
593
608
|
TRACER("returning\n");
|
594
609
|
|
595
610
|
return self;
|
@@ -913,11 +928,9 @@ static VALUE producer_allocate(VALUE klass) {
|
|
913
928
|
* Set up the configuration context for the Producer instance
|
914
929
|
*
|
915
930
|
* @param self VALUE the Producer instance
|
916
|
-
* @param topic VALUE the Ruby string naming the topic
|
917
931
|
* @param brokers VALUE a Ruby string containing host:port pairs separated by commas
|
918
932
|
*/
|
919
933
|
static VALUE producer_initialize(VALUE self,
|
920
|
-
VALUE topic,
|
921
934
|
VALUE brokers) {
|
922
935
|
|
923
936
|
HermannInstanceConfig* producerConfig;
|
@@ -926,12 +939,9 @@ static VALUE producer_initialize(VALUE self,
|
|
926
939
|
|
927
940
|
TRACER("initialize Producer ruby object\n");
|
928
941
|
|
929
|
-
|
930
|
-
topicPtr = StringValuePtr(topic);
|
931
942
|
brokersPtr = StringValuePtr(brokers);
|
932
943
|
Data_Get_Struct(self, HermannInstanceConfig, producerConfig);
|
933
944
|
|
934
|
-
producerConfig->topic = topicPtr;
|
935
945
|
producerConfig->brokers = brokersPtr;
|
936
946
|
/** Using RD_KAFKA_PARTITION_UA specifies we want the partitioner callback to be called to determine the target
|
937
947
|
* partition
|
@@ -1011,11 +1021,11 @@ void Init_hermann_lib() {
|
|
1011
1021
|
rb_define_alloc_func(c_producer, producer_allocate);
|
1012
1022
|
|
1013
1023
|
/* Initialize */
|
1014
|
-
rb_define_method(c_producer, "initialize", producer_initialize,
|
1024
|
+
rb_define_method(c_producer, "initialize", producer_initialize, 1);
|
1015
1025
|
rb_define_method(c_producer, "initialize_copy", producer_init_copy, 1);
|
1016
1026
|
|
1017
1027
|
/* Producer.push_single(msg) */
|
1018
|
-
rb_define_method(c_producer, "push_single", producer_push_single,
|
1028
|
+
rb_define_method(c_producer, "push_single", producer_push_single, 3);
|
1019
1029
|
|
1020
1030
|
/* Producer.tick */
|
1021
1031
|
rb_define_method(c_producer, "tick", producer_tick, 1);
|
@@ -1,10 +1,11 @@
|
|
1
1
|
require 'hermann'
|
2
2
|
require 'zk'
|
3
3
|
require 'json'
|
4
|
+
require 'hermann/errors'
|
4
5
|
|
5
6
|
module Hermann
|
6
7
|
module Discovery
|
7
|
-
|
8
|
+
|
8
9
|
|
9
10
|
# Communicates with Zookeeper to discover kafka broker ids
|
10
11
|
#
|
@@ -24,12 +25,16 @@ module Hermann
|
|
24
25
|
# of 20 times the tickTime2 times the tick time set on server"
|
25
26
|
#
|
26
27
|
# @return [String] comma separated list of brokers
|
28
|
+
#
|
29
|
+
# @raises [NoBrokersError] if could not discover brokers thru zookeeper
|
27
30
|
def get_brokers(timeout=0)
|
28
31
|
brokers = []
|
29
32
|
ZK.open(zookeepers, {:timeout => timeout}) do |zk|
|
30
33
|
brokers = fetch_brokers(zk)
|
31
34
|
end
|
32
|
-
|
35
|
+
if brokers.empty?
|
36
|
+
raise Hermann::Errors::NoBrokersError
|
37
|
+
end
|
33
38
|
brokers.join(',')
|
34
39
|
end
|
35
40
|
|
data/lib/hermann/errors.rb
CHANGED
@@ -3,6 +3,12 @@ module Hermann
|
|
3
3
|
module Errors
|
4
4
|
# Error for connectivity problems with the Kafka brokers
|
5
5
|
class ConnectivityError; end;
|
6
|
+
|
7
|
+
# For passing incorrect config and options to kafka
|
8
|
+
class ConfigurationError < StandardError; end
|
9
|
+
|
10
|
+
# cannot discover brokers from zookeeper
|
11
|
+
class NoBrokersError < StandardError; end
|
6
12
|
end
|
7
13
|
end
|
8
14
|
|
data/lib/hermann/producer.rb
CHANGED
@@ -12,13 +12,17 @@ module Hermann
|
|
12
12
|
class Producer
|
13
13
|
attr_reader :topic, :brokers, :internal, :children
|
14
14
|
|
15
|
-
|
15
|
+
# Initialize a producer object with a default topic and broker list
|
16
|
+
#
|
17
|
+
# @param [String] topic The default topic to use for pushing messages
|
18
|
+
# @param [Array] brokers An array of "host:port" strings for the brokers
|
19
|
+
def initialize(topic, brokers, opts={})
|
16
20
|
@topic = topic
|
17
21
|
@brokers = brokers
|
18
22
|
if RUBY_PLATFORM == "java"
|
19
|
-
@internal = Hermann::Provider::JavaProducer.new(
|
23
|
+
@internal = Hermann::Provider::JavaProducer.new(brokers, opts)
|
20
24
|
else
|
21
|
-
@internal = Hermann::Lib::Producer.new(
|
25
|
+
@internal = Hermann::Lib::Producer.new(brokers)
|
22
26
|
end
|
23
27
|
# We're tracking children so we can make sure that at Producer exit we
|
24
28
|
# make a reasonable attempt to clean up outstanding result objects
|
@@ -42,23 +46,31 @@ module Hermann
|
|
42
46
|
|
43
47
|
# Push a value onto the Kafka topic passed to this +Producer+
|
44
48
|
#
|
45
|
-
# @param [Array] value An array of values to push, will push each one
|
46
|
-
# separately
|
47
49
|
# @param [Object] value A single object to push
|
50
|
+
# @param [Hash] opts to pass to push method
|
51
|
+
# @option opts [String] :topic The topic to push messages to
|
52
|
+
#
|
48
53
|
# @return [Hermann::Result] A future-like object which will store the
|
49
54
|
# result from the broker
|
50
|
-
def push(value)
|
51
|
-
|
55
|
+
def push(value, opts={})
|
56
|
+
topic = opts[:topic] || @topic
|
57
|
+
result = nil
|
52
58
|
|
53
59
|
if value.kind_of? Array
|
54
|
-
return value.map { |e| self.push(e) }
|
55
|
-
|
56
|
-
|
57
|
-
|
60
|
+
return value.map { |e| self.push(e, opts) }
|
61
|
+
end
|
62
|
+
|
63
|
+
if RUBY_PLATFORM == "java"
|
64
|
+
result = @internal.push_single(value, topic, nil)
|
65
|
+
unless result.nil?
|
58
66
|
@children << result
|
59
|
-
else
|
60
|
-
@internal.push_single(value, result)
|
61
67
|
end
|
68
|
+
# Reaping children on the push just to make sure that it does get
|
69
|
+
# called correctly and we don't leak memory
|
70
|
+
reap_children
|
71
|
+
else
|
72
|
+
result = create_result
|
73
|
+
@internal.push_single(value, topic, result)
|
62
74
|
end
|
63
75
|
|
64
76
|
return result
|
@@ -118,7 +130,7 @@ module Hermann
|
|
118
130
|
end
|
119
131
|
|
120
132
|
# Perform the actual reactor tick
|
121
|
-
# @raises [StandardError
|
133
|
+
# @raises [StandardError] in case of underlying failures in librdkafka
|
122
134
|
def execute_tick(timeout)
|
123
135
|
if timeout == 0
|
124
136
|
@internal.tick(0)
|
@@ -4,61 +4,103 @@ require 'json'
|
|
4
4
|
|
5
5
|
module Hermann
|
6
6
|
module Provider
|
7
|
-
|
8
7
|
# This class simulates the kafka producer class within a java environment.
|
9
8
|
# If the producer throw an exception within the Promise a call to +.value!+
|
10
9
|
# will raise the exception and the rejected flag will be set to true
|
11
10
|
#
|
12
11
|
class JavaProducer
|
13
|
-
attr_accessor :
|
12
|
+
attr_accessor :producer
|
13
|
+
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
# Instantiate JavaProducer
|
16
|
+
#
|
17
|
+
# @params [String] list of brokers
|
18
|
+
#
|
19
|
+
# @params [Hash] hash of kafka attributes, overrides defaults
|
20
|
+
#
|
21
|
+
# @raises [RuntimeError] if brokers string is nil/empty
|
22
|
+
#
|
23
|
+
# ==== Examples
|
24
|
+
#
|
25
|
+
# JavaProducer.new('0:9092', {'request.required.acks' => '1'})
|
26
|
+
#
|
27
|
+
def initialize(brokers, opts={})
|
28
|
+
properties = create_properties(brokers, opts)
|
18
29
|
config = create_config(properties)
|
19
30
|
@producer = JavaApiUtil::Producer.new(config)
|
20
31
|
end
|
21
32
|
|
22
33
|
DEFAULTS = {
|
23
|
-
|
24
|
-
|
25
|
-
|
34
|
+
'serializer.class' => 'kafka.serializer.StringEncoder',
|
35
|
+
'partitioner.class' => 'kafka.producer.DefaultPartitioner',
|
36
|
+
'request.required.acks' => '1'
|
26
37
|
}.freeze
|
27
38
|
|
28
39
|
# Push a value onto the Kafka topic passed to this +Producer+
|
29
40
|
#
|
30
41
|
# @param [Object] value A single object to push
|
42
|
+
# @param [String] topic to push message to
|
31
43
|
#
|
32
44
|
# @return +Concurrent::Promise+ Representa a promise to send the
|
33
45
|
# data to the kafka broker. Upon execution the Promise's status
|
34
46
|
# will be set
|
35
|
-
def push_single(msg)
|
47
|
+
def push_single(msg, topic, unused)
|
36
48
|
Concurrent::Promise.execute {
|
37
|
-
data = ProducerUtil::KeyedMessage.new(
|
49
|
+
data = ProducerUtil::KeyedMessage.new(topic, msg)
|
38
50
|
@producer.send(data)
|
39
51
|
}
|
40
52
|
end
|
41
53
|
|
54
|
+
# No-op for now
|
55
|
+
def connected?
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
|
59
|
+
# No-op for now
|
60
|
+
def errored?
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
|
64
|
+
# No-op for now
|
65
|
+
def connect(timeout=0)
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
42
69
|
private
|
70
|
+
|
71
|
+
# Creates a ProducerConfig object
|
72
|
+
#
|
73
|
+
# @param [Properties] object with broker properties
|
74
|
+
#
|
43
75
|
# @return [ProducerConfig] - packaged config for +Producer+
|
44
76
|
def create_config(properties)
|
45
77
|
ProducerUtil::ProducerConfig.new(properties)
|
46
78
|
end
|
47
79
|
|
80
|
+
# Creates Properties Object
|
81
|
+
#
|
82
|
+
# @param [Hash] brokers passed into this function
|
83
|
+
# @option args [String] :brokers - string of brokers
|
84
|
+
#
|
48
85
|
# @return [Properties] properties object for creating +ProducerConfig+
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
86
|
+
#
|
87
|
+
# @raises [RuntimeError] if options does not contain key value strings
|
88
|
+
def create_properties(brokers, opts={})
|
89
|
+
brokers = { 'metadata.broker.list' => brokers }
|
90
|
+
options = DEFAULTS.merge(brokers).merge(opts)
|
55
91
|
properties = JavaUtil::Properties.new
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
92
|
+
options.each do |key, val|
|
93
|
+
validate_property!(key, val)
|
94
|
+
properties.put(key, val)
|
95
|
+
end
|
60
96
|
properties
|
61
97
|
end
|
98
|
+
|
99
|
+
def validate_property!(key, val)
|
100
|
+
if key.to_s.empty? || val.to_s.empty?
|
101
|
+
raise Hermann::Errors::ConfigurationError, "Invalid Broker Properties"
|
102
|
+
end
|
103
|
+
end
|
62
104
|
end
|
63
105
|
end
|
64
106
|
end
|
data/lib/hermann/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hermann
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- R. Tyler Croy
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2014-10-
|
13
|
+
date: 2014-10-14 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: concurrent-ruby
|