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