redstorm 0.2.1 → 0.4.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.
- data/CHANGELOG.md +8 -1
- data/README.md +7 -14
- data/examples/native/cluster_word_count_topology.rb +18 -11
- data/examples/native/exclamation_bolt.rb +14 -10
- data/examples/native/local_exclamation_topology.rb +25 -17
- data/examples/native/local_exclamation_topology2.rb +37 -29
- data/examples/native/local_redis_word_count_topology.rb +52 -45
- data/examples/native/local_word_count_topology.rb +19 -13
- data/examples/native/random_sentence_spout.rb +26 -22
- data/examples/native/split_sentence_bolt.rb +13 -9
- data/examples/native/word_count_bolt.rb +18 -14
- data/examples/simple/exclamation_bolt.rb +7 -3
- data/examples/simple/exclamation_topology.rb +28 -25
- data/examples/simple/exclamation_topology2.rb +32 -29
- data/examples/simple/random_sentence_spout.rb +17 -13
- data/examples/simple/redis_word_count_topology.rb +40 -36
- data/examples/simple/split_sentence_bolt.rb +26 -22
- data/examples/simple/word_count_bolt.rb +13 -9
- data/examples/simple/word_count_topology.rb +28 -25
- data/lib/red_storm.rb +1 -0
- data/lib/red_storm/configuration.rb +16 -0
- data/lib/red_storm/simple_topology.rb +14 -29
- data/lib/red_storm/topology_launcher.rb +5 -5
- data/lib/red_storm/version.rb +1 -1
- data/lib/tasks/red_storm.rake +4 -3
- data/pom.xml +6 -5
- metadata +4 -3
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,17 +1,10 @@
|
|
1
|
-
# RedStorm v0.
|
1
|
+
# RedStorm v0.4.0 - JRuby on Storm
|
2
2
|
|
3
3
|
RedStorm provides the JRuby integration for the [Storm][storm] distributed realtime computation system.
|
4
4
|
|
5
|
-
## Changes from 0.1.x
|
6
|
-
|
7
|
-
- this release introduces the *simple* DSL. Topology, Spout and Bolt classes can inherit from the SimpleTopology, SimpleSpout and SimpleBolt classes which provides a very clean and consise DSL. See [examples/simple](https://github.com/colinsurprenant/redstorm/tree/master/examples/simple).
|
8
|
-
- use the same SimpleTopology class for local development cluster or remote production cluster.
|
9
|
-
- the `redstorm` command has a new syntax.
|
10
|
-
- gems support in production cluster
|
11
|
-
|
12
5
|
## Dependencies
|
13
6
|
|
14
|
-
This has been tested on OSX 10.6.8 and Linux 10.04 using Storm 0.
|
7
|
+
This has been tested on OSX 10.6.8 and Linux 10.04 using Storm 0.6.2 and JRuby 1.6.6
|
15
8
|
|
16
9
|
## Installation
|
17
10
|
``` sh
|
@@ -241,7 +234,7 @@ configure topology_name do |env|
|
|
241
234
|
end
|
242
235
|
```
|
243
236
|
|
244
|
-
The `configure` statement is **
|
237
|
+
The `configure` statement is **required**.
|
245
238
|
|
246
239
|
- `topology_name` — alternate topology name (**default** is topology class name)
|
247
240
|
- `env` — is set to `:local` or `:cluster` for you to set enviroment specific configurations
|
@@ -463,10 +456,10 @@ end
|
|
463
456
|
|
464
457
|
### Requirements
|
465
458
|
|
466
|
-
- JRuby 1.6.
|
467
|
-
- rake gem
|
468
|
-
- ruby-maven gem
|
469
|
-
- rspec gem
|
459
|
+
- JRuby 1.6.6
|
460
|
+
- rake gem ~> 0.9.2.2
|
461
|
+
- ruby-maven gem ~> 3.0.3.0.28.5
|
462
|
+
- rspec gem ~> 2.8.0
|
470
463
|
|
471
464
|
### Contribute
|
472
465
|
|
@@ -1,18 +1,25 @@
|
|
1
|
+
require 'red_storm'
|
1
2
|
require 'examples/native/random_sentence_spout'
|
2
3
|
require 'examples/native/split_sentence_bolt'
|
3
4
|
require 'examples/native/word_count_bolt'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
builder.setBolt(2, JRubyBolt.new(base_class_path, "SplitSentenceBolt"), 4).shuffleGrouping(1)
|
10
|
-
builder.setBolt(3, JRubyBolt.new(base_class_path, "WordCountBolt"), 4).fieldsGrouping(2, Fields.new("word"))
|
6
|
+
module RedStorm
|
7
|
+
module Examples
|
8
|
+
class ClusterWordCountTopology
|
9
|
+
RedStorm::Configuration.topology_class = self
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
def start(base_class_path, env)
|
12
|
+
builder = TopologyBuilder.new
|
13
|
+
builder.setSpout('RandomSentenceSpout', JRubySpout.new(base_class_path, "RedStorm::Examples::RandomSentenceSpout"), 5)
|
14
|
+
builder.setBolt('SplitSentenceBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::SplitSentenceBolt"), 4).shuffleGrouping('RandomSentenceSpout')
|
15
|
+
builder.setBolt('WordCountBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::WordCountBolt"), 4).fieldsGrouping('SplitSentenceBolt', Fields.new("word"))
|
16
|
+
|
17
|
+
conf = Config.new
|
18
|
+
conf.setDebug(true)
|
19
|
+
conf.setNumWorkers(20);
|
20
|
+
conf.setMaxSpoutPending(1000);
|
21
|
+
StormSubmitter.submitTopology("word_count", conf, builder.createTopology);
|
22
|
+
end
|
23
|
+
end
|
17
24
|
end
|
18
25
|
end
|
@@ -1,14 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module RedStorm
|
2
|
+
module Examples
|
3
|
+
class ExclamationBolt
|
4
|
+
def prepare(conf, context, collector)
|
5
|
+
@collector = collector
|
6
|
+
end
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
def execute(tuple)
|
9
|
+
@collector.emit(tuple, Values.new(tuple.getString(0) + "!!!"))
|
10
|
+
@collector.ack(tuple)
|
11
|
+
end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
def declare_output_fields(declarer)
|
14
|
+
declarer.declare(Fields.new("word"))
|
15
|
+
end
|
16
|
+
end
|
13
17
|
end
|
14
18
|
end
|
@@ -1,23 +1,31 @@
|
|
1
1
|
java_import 'backtype.storm.testing.TestWordSpout'
|
2
|
+
|
3
|
+
require 'lib/red_storm'
|
2
4
|
require 'examples/native/exclamation_bolt'
|
3
5
|
|
4
6
|
# this example topology uses the Storm TestWordSpout and our own JRuby ExclamationBolt
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
8
|
+
module RedStorm
|
9
|
+
module Examples
|
10
|
+
class LocalExclamationTopology
|
11
|
+
RedStorm::Configuration.topology_class = self
|
12
|
+
|
13
|
+
def start(base_class_path, env)
|
14
|
+
builder = TopologyBuilder.new
|
15
|
+
|
16
|
+
builder.setSpout('TestWordSpout', TestWordSpout.new, 10)
|
17
|
+
builder.setBolt('ExclamationBolt1', JRubyBolt.new(base_class_path, 'RedStorm::Examples::ExclamationBolt'), 3).shuffleGrouping('TestWordSpout')
|
18
|
+
builder.setBolt('ExclamationBolt2', JRubyBolt.new(base_class_path, 'RedStorm::Examples::ExclamationBolt'), 3).shuffleGrouping('ExclamationBolt1')
|
19
|
+
|
20
|
+
conf = Config.new
|
21
|
+
conf.setDebug(true)
|
22
|
+
|
23
|
+
cluster = LocalCluster.new
|
24
|
+
cluster.submitTopology("exclamation", conf, builder.createTopology)
|
25
|
+
sleep(5)
|
26
|
+
cluster.killTopology("exclamation")
|
27
|
+
cluster.shutdown
|
28
|
+
end
|
29
|
+
end
|
22
30
|
end
|
23
|
-
end
|
31
|
+
end
|
@@ -1,37 +1,45 @@
|
|
1
1
|
java_import 'backtype.storm.testing.TestWordSpout'
|
2
2
|
|
3
|
-
|
4
|
-
def prepare(conf, context, collector)
|
5
|
-
@collector = collector
|
6
|
-
end
|
3
|
+
require 'lib/red_storm'
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
module RedStorm
|
6
|
+
module Examples
|
7
|
+
class ExclamationBolt2
|
8
|
+
def prepare(conf, context, collector)
|
9
|
+
@collector = collector
|
10
|
+
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
12
|
+
def execute(tuple)
|
13
|
+
@collector.emit(tuple, Values.new("!#{tuple.getString(0)}!"))
|
14
|
+
@collector.ack(tuple)
|
15
|
+
end
|
16
|
+
|
17
|
+
def declare_output_fields(declarer)
|
18
|
+
declarer.declare(Fields.new("word"))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# this example topology uses the Storm TestWordSpout and our own JRuby ExclamationBolt
|
17
23
|
|
18
|
-
|
24
|
+
class LocalExclamationTopology2
|
25
|
+
RedStorm::Configuration.topology_class = self
|
19
26
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
def start(base_class_path, env)
|
28
|
+
builder = TopologyBuilder.new
|
29
|
+
|
30
|
+
builder.setSpout('TestWordSpout', TestWordSpout.new, 10)
|
31
|
+
builder.setBolt('ExclamationBolt21', JRubyBolt.new(base_class_path, "RedStorm::Examples::ExclamationBolt2"), 3).shuffleGrouping('TestWordSpout')
|
32
|
+
builder.setBolt('ExclamationBolt22', JRubyBolt.new(base_class_path, "RedStorm::Examples::ExclamationBolt2"), 2).shuffleGrouping('ExclamationBolt21')
|
33
|
+
|
34
|
+
conf = Config.new
|
35
|
+
conf.setDebug(true)
|
36
|
+
|
37
|
+
cluster = LocalCluster.new
|
38
|
+
cluster.submitTopology("exclamation", conf, builder.createTopology)
|
39
|
+
sleep(5)
|
40
|
+
cluster.killTopology("exclamation")
|
41
|
+
cluster.shutdown
|
42
|
+
end
|
43
|
+
end
|
36
44
|
end
|
37
45
|
end
|
@@ -1,58 +1,65 @@
|
|
1
1
|
require 'redis'
|
2
2
|
require 'thread'
|
3
|
+
require 'lib/red_storm'
|
3
4
|
require 'examples/native/word_count_bolt'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
6
|
+
module RedStorm
|
7
|
+
module Examples
|
8
|
+
# RedisWordSpout reads the Redis queue "test" on localhost:6379
|
9
|
+
# and emits each word items pop'ed from the queue.
|
10
|
+
class RedisWordSpout
|
11
|
+
def open(conf, context, collector)
|
12
|
+
@collector = collector
|
13
|
+
@q = Queue.new
|
14
|
+
@redis_reader = detach_redis_reader
|
15
|
+
end
|
16
|
+
|
17
|
+
def next_tuple
|
18
|
+
# per doc nextTuple should not block, and sleep a bit when there's no data to process.
|
19
|
+
if @q.size > 0
|
20
|
+
@collector.emit(Values.new(@q.pop))
|
21
|
+
else
|
22
|
+
sleep(0.1)
|
23
|
+
end
|
24
|
+
end
|
22
25
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
def declare_output_fields(declarer)
|
27
|
+
declarer.declare(Fields.new("word"))
|
28
|
+
end
|
26
29
|
|
27
|
-
|
30
|
+
private
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
def detach_redis_reader
|
33
|
+
Thread.new do
|
34
|
+
Thread.current.abort_on_exception = true
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
redis = Redis.new(:host => "localhost", :port => 6379)
|
37
|
+
loop do
|
38
|
+
if data = redis.blpop("test", 0)
|
39
|
+
@q << data[1]
|
40
|
+
end
|
41
|
+
end
|
37
42
|
end
|
38
43
|
end
|
39
44
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
45
|
+
|
46
|
+
class LocalRedisWordCountTopology
|
47
|
+
RedStorm::Configuration.topology_class = self
|
48
|
+
|
49
|
+
def start(base_class_path, env)
|
50
|
+
builder = TopologyBuilder.new
|
51
|
+
builder.setSpout('RedisWordSpout', JRubySpout.new(base_class_path, "RedStorm::Examples::RedisWordSpout"), 1)
|
52
|
+
builder.setBolt('WordCountBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::WordCountBolt"), 3).fieldsGrouping('RedisWordSpout', Fields.new("word"))
|
53
|
+
|
54
|
+
conf = Config.new
|
55
|
+
conf.setDebug(true)
|
56
|
+
conf.setMaxTaskParallelism(3)
|
57
|
+
|
58
|
+
cluster = LocalCluster.new
|
59
|
+
cluster.submitTopology("redis_word_count", conf, builder.createTopology)
|
60
|
+
sleep(600)
|
61
|
+
cluster.shutdown
|
62
|
+
end
|
63
|
+
end
|
57
64
|
end
|
58
65
|
end
|
@@ -1,21 +1,27 @@
|
|
1
|
+
require 'lib/red_storm'
|
1
2
|
require 'examples/native/random_sentence_spout'
|
2
3
|
require 'examples/native/split_sentence_bolt'
|
3
4
|
require 'examples/native/word_count_bolt'
|
4
5
|
|
5
|
-
class LocalWordCountTopology
|
6
|
-
def start(base_class_path, env)
|
7
|
-
builder = TopologyBuilder.new
|
8
|
-
builder.setSpout(1, JRubySpout.new(base_class_path, "RandomSentenceSpout"), 5)
|
9
|
-
builder.setBolt(2, JRubyBolt.new(base_class_path, "SplitSentenceBolt"), 8).shuffleGrouping(1)
|
10
|
-
builder.setBolt(3, JRubyBolt.new(base_class_path, "WordCountBolt"), 12).fieldsGrouping(2, Fields.new("word"))
|
11
6
|
|
12
|
-
|
13
|
-
|
14
|
-
|
7
|
+
module Examples
|
8
|
+
class LocalWordCountTopology
|
9
|
+
RedStorm::Configuration.topology_class = self
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
11
|
+
def start(base_class_path, env)
|
12
|
+
builder = TopologyBuilder.new
|
13
|
+
builder.setSpout('RandomSentenceSpout', JRubySpout.new(base_class_path, "RedStorm::Examples::RandomSentenceSpout"), 5)
|
14
|
+
builder.setBolt('SplitSentenceBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::SplitSentenceBolt"), 8).shuffleGrouping('RandomSentenceSpout')
|
15
|
+
builder.setBolt('WordCountBolt', JRubyBolt.new(base_class_path, "RedStorm::Examples::WordCountBolt"), 12).fieldsGrouping('SplitSentenceBolt', Fields.new("word"))
|
16
|
+
|
17
|
+
conf = Config.new
|
18
|
+
conf.setDebug(true)
|
19
|
+
conf.setMaxTaskParallelism(3)
|
20
|
+
|
21
|
+
cluster = LocalCluster.new
|
22
|
+
cluster.submitTopology("word_count", conf, builder.createTopology)
|
23
|
+
sleep(5)
|
24
|
+
cluster.shutdown
|
25
|
+
end
|
20
26
|
end
|
21
27
|
end
|
@@ -1,26 +1,30 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module RedStorm
|
2
|
+
module Examples
|
3
|
+
class RandomSentenceSpout
|
4
|
+
attr_reader :is_distributed
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
def initialize
|
7
|
+
@is_distributed = true
|
8
|
+
@sentences = [
|
9
|
+
"the cow jumped over the moon",
|
10
|
+
"an apple a day keeps the doctor away",
|
11
|
+
"four score and seven years ago",
|
12
|
+
"snow white and the seven dwarfs",
|
13
|
+
"i am at two with nature"
|
14
|
+
]
|
15
|
+
end
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
17
|
+
def open(conf, context, collector)
|
18
|
+
@collector = collector
|
19
|
+
end
|
20
|
+
|
21
|
+
def next_tuple
|
22
|
+
@collector.emit(Values.new(@sentences[rand(@sentences.length)]))
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
def declare_output_fields(declarer)
|
26
|
+
declarer.declare(Fields.new("word"))
|
27
|
+
end
|
28
|
+
end
|
25
29
|
end
|
26
|
-
end
|
30
|
+
end
|
@@ -1,13 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module RedStorm
|
2
|
+
module Examples
|
3
|
+
class SplitSentenceBolt
|
4
|
+
def prepare(conf, context, collector)
|
5
|
+
@collector = collector
|
6
|
+
end
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
def execute(tuple)
|
9
|
+
tuple.getString(0).split(" ").each {|w| @collector.emit(Values.new(w)) }
|
10
|
+
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
+
def declare_output_fields(declarer)
|
13
|
+
declarer.declare(Fields.new("word"))
|
14
|
+
end
|
15
|
+
end
|
12
16
|
end
|
13
17
|
end
|
@@ -1,19 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module RedStorm
|
2
|
+
module Examples
|
3
|
+
class WordCountBolt
|
4
|
+
def initialize
|
5
|
+
@counts = Hash.new{|h, k| h[k] = 0}
|
6
|
+
end
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
def prepare(conf, context, collector)
|
9
|
+
@collector = collector
|
10
|
+
end
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
def execute(tuple)
|
13
|
+
word = tuple.getString(0)
|
14
|
+
@counts[word] += 1
|
15
|
+
@collector.emit(Values.new(word, @counts[word]))
|
16
|
+
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
+
def declare_output_fields(declarer)
|
19
|
+
declarer.declare(Fields.new("word", "count"))
|
20
|
+
end
|
21
|
+
end
|
18
22
|
end
|
19
23
|
end
|
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'red_storm'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
module RedStorm
|
4
|
+
module Examples
|
5
|
+
class ExclamationBolt < RedStorm::SimpleBolt
|
6
|
+
output_fields :word
|
7
|
+
on_receive(:ack => true, :anchor => true) {|tuple| tuple.getString(0) + "!!!"}
|
8
|
+
end
|
9
|
+
end
|
6
10
|
end
|
@@ -4,33 +4,36 @@ require 'examples/simple/exclamation_bolt'
|
|
4
4
|
|
5
5
|
# this example topology uses the Storm TestWordSpout and our own JRuby ExclamationBolt
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
module RedStorm
|
8
|
+
module Examples
|
9
|
+
class ExclamationTopology < RedStorm::SimpleTopology
|
10
|
+
spout TestWordSpout, :parallelism => 10
|
11
|
+
|
12
|
+
bolt ExclamationBolt, :parallelism => 3 do
|
13
|
+
source TestWordSpout, :shuffle
|
14
|
+
end
|
15
|
+
|
16
|
+
bolt ExclamationBolt, :id => :ExclamationBolt2, :parallelism => 2 do
|
17
|
+
source ExclamationBolt, :shuffle
|
18
|
+
end
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
20
|
+
configure do |env|
|
21
|
+
debug true
|
22
|
+
case env
|
23
|
+
when :local
|
24
|
+
max_task_parallelism 3
|
25
|
+
when :cluster
|
26
|
+
num_workers 20
|
27
|
+
max_spout_pending(1000);
|
28
|
+
end
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
on_submit do |env|
|
32
|
+
if env == :local
|
33
|
+
sleep(5)
|
34
|
+
cluster.shutdown
|
35
|
+
end
|
36
|
+
end
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
@@ -4,38 +4,41 @@ require 'red_storm'
|
|
4
4
|
# this example topology uses the Storm TestWordSpout and our own JRuby ExclamationBolt
|
5
5
|
# and a locally defined ExclamationBolt
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
module RedStorm
|
8
|
+
module Examples
|
9
|
+
class ExclamationBolt < RedStorm::SimpleBolt
|
10
|
+
output_fields :word
|
11
|
+
on_receive(:ack => true, :anchor => true) {|tuple| "!#{tuple.getString(0)}!"}
|
12
|
+
end
|
11
13
|
|
12
|
-
class ExclamationTopology2 < RedStorm::SimpleTopology
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
14
|
+
class ExclamationTopology2 < RedStorm::SimpleTopology
|
15
|
+
spout TestWordSpout, :parallelism => 10
|
16
|
+
|
17
|
+
bolt ExclamationBolt, :parallelism => 3 do
|
18
|
+
source TestWordSpout, :shuffle
|
19
|
+
end
|
20
|
+
|
21
|
+
bolt ExclamationBolt, :id => :ExclamationBolt2, :parallelism => 2 do
|
22
|
+
source ExclamationBolt, :shuffle
|
23
|
+
end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
25
|
+
configure do |env|
|
26
|
+
debug true
|
27
|
+
case env
|
28
|
+
when :local
|
29
|
+
max_task_parallelism 3
|
30
|
+
when :cluster
|
31
|
+
num_workers 20
|
32
|
+
max_spout_pending(1000);
|
33
|
+
end
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
on_submit do |env|
|
37
|
+
if env == :local
|
38
|
+
sleep(5)
|
39
|
+
cluster.shutdown
|
40
|
+
end
|
41
|
+
end
|
39
42
|
end
|
40
43
|
end
|
41
44
|
end
|
@@ -1,18 +1,22 @@
|
|
1
1
|
require 'red_storm'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
module RedStorm
|
4
|
+
module Examples
|
5
|
+
class RandomSentenceSpout < RedStorm::SimpleSpout
|
6
|
+
set :is_distributed => true
|
7
|
+
output_fields :word
|
6
8
|
|
7
|
-
|
9
|
+
on_send {@sentences[rand(@sentences.length)]}
|
8
10
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
on_init do
|
12
|
+
@sentences = [
|
13
|
+
"the cow jumped over the moon",
|
14
|
+
"an apple a day keeps the doctor away",
|
15
|
+
"four score and seven years ago",
|
16
|
+
"snow white and the seven dwarfs",
|
17
|
+
"i am at two with nature"
|
18
|
+
]
|
19
|
+
end
|
20
|
+
end
|
17
21
|
end
|
18
|
-
end
|
22
|
+
end
|
@@ -5,51 +5,55 @@ require 'red_storm'
|
|
5
5
|
|
6
6
|
require 'examples/simple/word_count_bolt'
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
module RedStorm
|
9
|
+
module Examples
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
# RedisWordSpout reads the Redis queue "test" on localhost:6379
|
12
|
+
# and emits each word items pop'ed from the queue.
|
13
13
|
|
14
|
-
|
14
|
+
class RedisWordSpout < RedStorm::SimpleSpout
|
15
|
+
output_fields :word
|
15
16
|
|
16
|
-
|
17
|
-
@q = Queue.new
|
18
|
-
@redis_reader = detach_redis_reader
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def detach_redis_reader
|
24
|
-
Thread.new do
|
25
|
-
Thread.current.abort_on_exception = true
|
17
|
+
on_send {@q.pop if @q.size > 0}
|
26
18
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
19
|
+
on_init do
|
20
|
+
@q = Queue.new
|
21
|
+
@redis_reader = detach_redis_reader
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def detach_redis_reader
|
27
|
+
Thread.new do
|
28
|
+
Thread.current.abort_on_exception = true
|
29
|
+
|
30
|
+
redis = Redis.new(:host => "localhost", :port => 6379)
|
31
|
+
loop do
|
32
|
+
if data = redis.blpop("test", 0)
|
33
|
+
@q << data[1]
|
34
|
+
end
|
35
|
+
end
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
34
|
-
end
|
35
|
-
end
|
36
39
|
|
37
|
-
class RedisWordCountTopology < RedStorm::SimpleTopology
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
class RedisWordCountTopology < RedStorm::SimpleTopology
|
41
|
+
spout RedisWordSpout
|
42
|
+
|
43
|
+
bolt WordCountBolt, :parallelism => 3 do
|
44
|
+
source RedisWordSpout, :fields => ["word"]
|
45
|
+
end
|
43
46
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
47
|
+
configure do |env|
|
48
|
+
debug true
|
49
|
+
case env
|
50
|
+
when :local
|
51
|
+
max_task_parallelism 3
|
52
|
+
when :cluster
|
53
|
+
num_workers 20
|
54
|
+
max_spout_pending(1000);
|
55
|
+
end
|
56
|
+
end
|
53
57
|
end
|
54
58
|
end
|
55
59
|
end
|
@@ -1,29 +1,33 @@
|
|
1
1
|
require 'red_storm'
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
module RedStorm
|
4
|
+
module Examples
|
5
|
+
class SplitSentenceBolt < RedStorm::SimpleBolt
|
6
|
+
output_fields :word
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
8
|
+
# block declaration style using auto-emit (default)
|
9
|
+
#
|
10
|
+
on_receive {|tuple| tuple.getString(0).split(' ').map{|w| [w]}}
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
# block declaration style no auto-emit
|
13
|
+
#
|
14
|
+
# on_receive :emit => false do |tuple|
|
15
|
+
# tuple.getString(0).split(' ').each{|w| unanchored_emit(w)}
|
16
|
+
# end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
# alternate declaration style using on_receive method
|
19
|
+
#
|
20
|
+
# on_receive :emit => true
|
21
|
+
# def on_receive(tuple)
|
22
|
+
# tuple.getString(0).split(' ').map{|w| [w]}
|
23
|
+
# end
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
# alternate declaration style using any specific method
|
26
|
+
#
|
27
|
+
# on_receive :my_method, :emit => true
|
28
|
+
# def my_method(tuple)
|
29
|
+
# tuple.getString(0).split(' ').map{|w| [w]}
|
30
|
+
# end
|
31
|
+
end
|
32
|
+
end
|
29
33
|
end
|
@@ -1,15 +1,19 @@
|
|
1
1
|
require 'red_storm'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
module RedStorm
|
4
|
+
module Examples
|
5
|
+
class WordCountBolt < RedStorm::SimpleBolt
|
6
|
+
output_fields :word, :count
|
7
|
+
on_init {@counts = Hash.new{|h, k| h[k] = 0}}
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
# block declaration style using auto-emit (default)
|
10
|
+
#
|
11
|
+
on_receive do |tuple|
|
12
|
+
word = tuple.getString(0)
|
13
|
+
@counts[word] += 1
|
12
14
|
|
13
|
-
|
15
|
+
[word, @counts[word]]
|
16
|
+
end
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
@@ -2,33 +2,36 @@ require 'examples/simple/random_sentence_spout'
|
|
2
2
|
require 'examples/simple/split_sentence_bolt'
|
3
3
|
require 'examples/simple/word_count_bolt'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
module RedStorm
|
6
|
+
module Examples
|
7
|
+
class WordCountTopology < SimpleTopology
|
8
|
+
spout RandomSentenceSpout, :parallelism => 5
|
9
|
+
|
10
|
+
bolt SplitSentenceBolt, :parallelism => 8 do
|
11
|
+
source RandomSentenceSpout, :shuffle
|
12
|
+
end
|
13
|
+
|
14
|
+
bolt WordCountBolt, :parallelism => 12 do
|
15
|
+
source SplitSentenceBolt, :fields => ["word"]
|
16
|
+
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
18
|
+
configure :word_count do |env|
|
19
|
+
debug true
|
20
|
+
case env
|
21
|
+
when :local
|
22
|
+
max_task_parallelism 3
|
23
|
+
when :cluster
|
24
|
+
num_workers 20
|
25
|
+
max_spout_pending(1000);
|
26
|
+
end
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
on_submit do |env|
|
30
|
+
if env == :local
|
31
|
+
sleep(5)
|
32
|
+
cluster.shutdown
|
33
|
+
end
|
34
|
+
end
|
32
35
|
end
|
33
36
|
end
|
34
37
|
end
|
data/lib/red_storm.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'red_storm/configuration'
|
2
|
+
|
1
3
|
module RedStorm
|
2
4
|
|
3
5
|
class SimpleTopology
|
@@ -8,11 +10,11 @@ module RedStorm
|
|
8
10
|
|
9
11
|
class ComponentDefinition
|
10
12
|
attr_reader :clazz, :parallelism
|
11
|
-
attr_accessor :id
|
13
|
+
attr_accessor :id # ids are forced to string
|
12
14
|
|
13
15
|
def initialize(component_class, id, parallelism)
|
14
16
|
@clazz = component_class
|
15
|
-
@id = id
|
17
|
+
@id = id.to_s
|
16
18
|
@parallelism = parallelism
|
17
19
|
end
|
18
20
|
end
|
@@ -28,16 +30,16 @@ module RedStorm
|
|
28
30
|
end
|
29
31
|
|
30
32
|
def source(source_id, grouping)
|
31
|
-
@sources << [source_id.is_a?(Class) ? SimpleTopology.underscore(source_id) : source_id, grouping.is_a?(Hash) ? grouping : {grouping => nil}]
|
33
|
+
@sources << [source_id.is_a?(Class) ? SimpleTopology.underscore(source_id) : source_id.to_s, grouping.is_a?(Hash) ? grouping : {grouping => nil}]
|
32
34
|
end
|
33
35
|
|
34
36
|
def define_grouping(declarer)
|
35
37
|
@sources.each do |source_id, grouping|
|
36
38
|
grouper, params = grouping.first
|
37
|
-
|
39
|
+
# declarer.fieldsGrouping(source_id, Fields.new())
|
38
40
|
case grouper
|
39
41
|
when :fields
|
40
|
-
declarer.fieldsGrouping(source_id, Fields.new(*params))
|
42
|
+
declarer.fieldsGrouping(source_id, Fields.new(*([params].flatten.map(&:to_s))))
|
41
43
|
when :global
|
42
44
|
declarer.globalGrouping(source_id)
|
43
45
|
when :shuffle
|
@@ -88,6 +90,7 @@ module RedStorm
|
|
88
90
|
end
|
89
91
|
|
90
92
|
def self.configure(name = nil, &configure_block)
|
93
|
+
Configuration.topology_class = self
|
91
94
|
@topology_name = name if name
|
92
95
|
@configure_block = configure_block if block_given?
|
93
96
|
end
|
@@ -131,31 +134,13 @@ module RedStorm
|
|
131
134
|
private
|
132
135
|
|
133
136
|
def self.resolve_ids!(components)
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
# assign numeric ids to symbolic ids
|
141
|
-
symbolic_components.each do |component|
|
142
|
-
id = component.id.to_s
|
143
|
-
raise("duplicate symbolic id in #{component.clazz.name} on id=#{id}") if resolved_names.has_key?(id)
|
144
|
-
next_numeric_id += 1 while numeric_ids.include?(next_numeric_id)
|
145
|
-
numeric_ids << next_numeric_id
|
146
|
-
resolved_names[id] = next_numeric_id
|
147
|
-
end
|
148
|
-
|
149
|
-
# reassign numeric ids to all components
|
150
|
-
components.each do |component|
|
151
|
-
unless component.id.is_a?(Fixnum)
|
152
|
-
component.id = resolved_names[component.id.to_s] || raise("cannot resolve #{component.clazz.name} id=#{component.id.to_s}")
|
153
|
-
end
|
137
|
+
# verify duplicate implicit ids
|
138
|
+
ids = components.map(&:id)
|
139
|
+
components.reverse.each do |component|
|
140
|
+
raise("duplicate id in #{component.clazz.name} on id=#{component.id}") if ids.select{|id| id == component.id}.size > 1
|
141
|
+
# verify source_id references
|
154
142
|
if component.respond_to?(:sources)
|
155
|
-
component.sources.
|
156
|
-
id = source_id.is_a?(Fixnum) ? source_id : resolved_names[source_id.to_s] || raise("cannot resolve #{component.clazz.name} source id=#{source_id.to_s}")
|
157
|
-
[id, grouping]
|
158
|
-
end
|
143
|
+
component.sources.each{|source_id, grouping| raise("cannot resolve #{component.clazz.name} source id=#{source_id}") unless ids.include?(source_id)}
|
159
144
|
end
|
160
145
|
end
|
161
146
|
end
|
@@ -24,7 +24,7 @@ java_import 'redstorm.storm.jruby.JRubySpout'
|
|
24
24
|
java_package 'redstorm'
|
25
25
|
|
26
26
|
# TopologyLauncher is the application entry point when launching a topology. Basically it will
|
27
|
-
# call require on the specified Ruby topology
|
27
|
+
# call require on the specified Ruby topology class file path and call its start method
|
28
28
|
class TopologyLauncher
|
29
29
|
|
30
30
|
java_signature 'void main(String[])'
|
@@ -35,12 +35,12 @@ class TopologyLauncher
|
|
35
35
|
end
|
36
36
|
env = args[0].to_sym
|
37
37
|
class_path = args[1]
|
38
|
-
clazz = camel_case(class_path.split('/').last.split('.').first)
|
39
|
-
|
40
|
-
puts("RedStorm v#{RedStorm::VERSION} starting topology #{clazz} in #{env.to_s} environment")
|
41
38
|
|
42
39
|
require class_path
|
43
|
-
|
40
|
+
|
41
|
+
topology_name = RedStorm::Configuration.topology_class.respond_to?(:topology_name) ? "/#{RedStorm::Configuration.topology_class.topology_name}" : ''
|
42
|
+
puts("RedStorm v#{RedStorm::VERSION} starting topology #{RedStorm::Configuration.topology_class.name}#{topology_name} in #{env.to_s} environment")
|
43
|
+
RedStorm::Configuration.topology_class.new.start(class_path, env)
|
44
44
|
end
|
45
45
|
|
46
46
|
private
|
data/lib/red_storm/version.rb
CHANGED
data/lib/tasks/red_storm.rake
CHANGED
@@ -15,6 +15,7 @@ TARGET_SRC_DIR = "#{TARGET_DIR}/src"
|
|
15
15
|
TARGET_CLASSES_DIR = "#{TARGET_DIR}/classes"
|
16
16
|
TARGET_DEPENDENCY_DIR = "#{TARGET_DIR}/dependency"
|
17
17
|
TARGET_DEPENDENCY_UNPACKED_DIR = "#{TARGET_DIR}/dependency-unpacked"
|
18
|
+
TARGET_MARKERS_DIR = "#{TARGET_DIR}/dependency-markers"
|
18
19
|
TARGET_GEMS_DIR = "#{TARGET_DIR}/gems"
|
19
20
|
TARGET_CLUSTER_JAR = "#{TARGET_DIR}/cluster-topology.jar"
|
20
21
|
|
@@ -55,7 +56,7 @@ task :install => [:deps, :build] do
|
|
55
56
|
end
|
56
57
|
|
57
58
|
task :unpack do
|
58
|
-
system("rmvn dependency:unpack -f #{RedStorm::REDSTORM_HOME}/pom.xml -DoutputDirectory=#{TARGET_DEPENDENCY_UNPACKED_DIR}")
|
59
|
+
system("rmvn dependency:unpack -f #{RedStorm::REDSTORM_HOME}/pom.xml -DoutputDirectory=#{TARGET_DEPENDENCY_UNPACKED_DIR} -DmarkersDirectory=#{TARGET_MARKERS_DIR}")
|
59
60
|
end
|
60
61
|
|
61
62
|
task :devjar => [:unpack, :clean_jar] do
|
@@ -111,7 +112,7 @@ task :examples do
|
|
111
112
|
end
|
112
113
|
|
113
114
|
task :deps do
|
114
|
-
system("rmvn dependency:copy-dependencies -f #{RedStorm::REDSTORM_HOME}/pom.xml -DoutputDirectory=#{TARGET_DEPENDENCY_DIR}")
|
115
|
+
system("rmvn dependency:copy-dependencies -f #{RedStorm::REDSTORM_HOME}/pom.xml -DoutputDirectory=#{TARGET_DEPENDENCY_DIR} -DmarkersDirectory=#{TARGET_MARKERS_DIR}")
|
115
116
|
end
|
116
117
|
|
117
118
|
task :build => :setup do
|
@@ -148,5 +149,5 @@ end
|
|
148
149
|
|
149
150
|
def build_jruby(source_path)
|
150
151
|
puts("\n--> Compiling JRuby")
|
151
|
-
system("cd #{RedStorm::REDSTORM_HOME}; jrubyc -t #{TARGET_SRC_DIR} --verbose --java -c \"#{TARGET_DEPENDENCY_DIR}/storm-0.
|
152
|
+
system("cd #{RedStorm::REDSTORM_HOME}; jrubyc -t #{TARGET_SRC_DIR} --verbose --java -c \"#{TARGET_DEPENDENCY_DIR}/storm-0.6.2.jar\" -c \"#{TARGET_CLASSES_DIR}\" #{source_path}")
|
152
153
|
end
|
data/pom.xml
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
<groupId>redstorm</groupId>
|
6
6
|
<artifactId>redstorm</artifactId>
|
7
|
-
<version>0.
|
7
|
+
<version>0.4.0</version>
|
8
8
|
<name>RedStorm JRuby on Storm</name>
|
9
9
|
|
10
10
|
<properties>
|
@@ -27,13 +27,13 @@
|
|
27
27
|
<dependency>
|
28
28
|
<groupId>storm</groupId>
|
29
29
|
<artifactId>storm</artifactId>
|
30
|
-
<version>0.
|
30
|
+
<version>0.6.2</version>
|
31
31
|
</dependency>
|
32
32
|
|
33
33
|
<dependency>
|
34
34
|
<groupId>org.jruby</groupId>
|
35
35
|
<artifactId>jruby-complete</artifactId>
|
36
|
-
<version>1.6.
|
36
|
+
<version>1.6.6</version>
|
37
37
|
</dependency>
|
38
38
|
</dependencies>
|
39
39
|
|
@@ -48,11 +48,12 @@
|
|
48
48
|
<artifactItem>
|
49
49
|
<groupId>org.jruby</groupId>
|
50
50
|
<artifactId>jruby-complete</artifactId>
|
51
|
-
<version>1.6.
|
51
|
+
<version>1.6.6</version>
|
52
52
|
<type>jar</type>
|
53
53
|
<overWrite>false</overWrite>
|
54
54
|
</artifactItem>
|
55
55
|
</artifactItems>
|
56
|
+
<markersDirectory>${markersDirectory}</markersDirectory>
|
56
57
|
</configuration>
|
57
58
|
<executions>
|
58
59
|
<execution>
|
@@ -66,4 +67,4 @@
|
|
66
67
|
</plugins>
|
67
68
|
</build>
|
68
69
|
|
69
|
-
</project>
|
70
|
+
</project>
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: redstorm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.
|
5
|
+
version: 0.4.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Colin Surprenant
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date:
|
13
|
+
date: 2012-02-08 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rubyforge
|
@@ -57,6 +57,7 @@ extra_rdoc_files: []
|
|
57
57
|
files:
|
58
58
|
- lib/red_storm.rb
|
59
59
|
- lib/red_storm/application.rb
|
60
|
+
- lib/red_storm/configuration.rb
|
60
61
|
- lib/red_storm/simple_bolt.rb
|
61
62
|
- lib/red_storm/simple_spout.rb
|
62
63
|
- lib/red_storm/simple_topology.rb
|
@@ -114,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
114
115
|
requirements: []
|
115
116
|
|
116
117
|
rubyforge_project: redstorm
|
117
|
-
rubygems_version: 1.8.
|
118
|
+
rubygems_version: 1.8.15
|
118
119
|
signing_key:
|
119
120
|
specification_version: 3
|
120
121
|
summary: JRuby on Storm
|