1
2
3
4
5
6
7
8
|
List
public
void
execute
(
Tuple
tuple
)
{
if
(
current_time
-
last_time
<
5s
)
{
cache
.
add
(
tuple
)
;
}
else
{
calculate
(
cache
)
;
}
}
|
比方说:要实现一个每M=2秒计算最近N=6秒内的交易UV、交易额
在Bolt就需要每个2秒获取窗口长度为6秒的Tuple并做处理.
1
2
3
4
5
6
7
8
9
10
11
12
|
对于第一点,
Storm提供了一个
TickTuple机制
@Override
public
Map
<
String
,
Object
>
getComponentConfiguration
(
)
{
Map
<
String
,
Object
>
conf
=
new
HashMap
<
String
,
Object
>
(
)
;
conf
.
put
(
Config
.
TOPOLOGY_TICK_TUPLE_FREQ_SECS
,
emitFrequencyInSeconds
)
;
return
conf
;
}
if
(
TupleHelpers
.
isTickTuple
(
tuple
)
)
{
LOG
.
debug
(
"Received tick tuple, triggering emit of current window counts"
)
;
emitCurrentWindowCounts
(
)
;
}
|
第二点也是滑动窗口实现的核心
设定两个数,Tick频率——batch的时间间隔(2秒),滑动窗口的长度(6秒)
我们把移动窗口划分成6/2=3个Slots,每个Slot存储2秒内收到的Tuple
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
public
class
SlidingWindowCache
private
static
final
long
serialVersionUID
=
1L
;
private
Map
<
Integer
,
List
private
int
headSlot
;
private
int
tailSlot
;
private
int
slotNum
;
public
SlidingWindowCache
(
int
slotNum
)
{
if
(
slotNum
<
2
)
{
throw
new
IllegalArgumentException
(
"Window length in slots must be at least two (you requested "
+
slotNum
+
")"
)
;
}
this
.
slotNum
=
slotNum
;
for
(
int
i
=
0
;
i
<
slotNum
;
i
++
)
{
tupMap
.
put
(
i
,
null
)
;
}
headSlot
=
0
;
tailSlot
=
(
headSlot
+
1
)
%
this
.
slotNum
;
}
public
void
add
(
T
t
)
{
List
if
(
objs
==
null
)
{
objs
=
new
ArrayList
}
objs
.
add
(
t
)
;
tupMap
.
put
(
headSlot
,
objs
)
;
}
/**
* 获取窗口内的消息,并向前移动窗口
* @return
*/
public
List
int
i
=
headSlot
;
List
if
(
tupMap
.
get
(
i
)
!=
null
)
{
windowedTuples
.
addAll
(
tupMap
.
get
(
i
)
)
;
}
while
(
(
i
=
slotAfter
(
i
)
)
!=
headSlot
)
{
if
(
tupMap
.
get
(
i
)
!=
null
)
{
windowedTuples
.
addAll
(
tupMap
.
get
(
i
)
)
;
}
}
advanceHead
(
)
;
return
windowedTuples
;
}
/**
* 向前移动窗口
*/
private
void
advanceHead
(
)
{
printList
(
tupMap
.
get
(
headSlot
)
)
;
headSlot
=
tailSlot
;
wipeSlot
(
headSlot
)
;
tailSlot
=
slotAfter
(
tailSlot
)
;
}
public
int
slotAfter
(
int
slot
)
{
return
(
slot
+
1
)
%
slotNum
;
}
public
void
wipeSlot
(
int
slot
)
{
tupMap
.
put
(
slot
,
null
)
;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
public
abstract
class
WindowedBolt
extends
BaseRichBolt
{
private
static
final
long
serialVersionUID
=
8849434942882466073L
;
private
static
final
Logger
LOG
=
Logger
.
getLogger
(
WindowedBolt
.
class
)
;
private
final
static
int
DEFAULT_WINDOW_LEN_IN_SECS
=
12
;
private
final
static
int
DEFAULT_WINDOW_EMIT_FREQ
=
4
;
private
int
windowLengthInSeconds
;
private
int
emitFrequencyInSeconds
;
protected
SlidingWindowCache
public
WindowedBolt
(
)
{
this
(
DEFAULT_WINDOW_LEN_IN_SECS
,
DEFAULT_WINDOW_EMIT_FREQ
)
;
}
public
WindowedBolt
(
int
windowLenInSecs
,
int
emitFrequency
)
{
if
(
windowLenInSecs
%
emitFrequency
!=
0
)
{
LOG
.
warn
(
String
.
format
(
"Actual window length(%d) isnot emitFrequency(%d)'s times"
)
)
;
}
this
.
windowLengthInSeconds
=
windowLenInSecs
;
this
.
emitFrequencyInSeconds
=
emitFrequency
;
cache
=
new
SlidingWindowCache
}
private
int
getSlots
(
int
windowLenInSecs
,
int
emitFrequency
)
{
return
windowLenInSecs
/
emitFrequency
;
}
@Override
public
void
execute
(
Tuple
tuple
)
{
if
(
TupleHelpers
.
isTickTuple
(
tuple
)
)
{
LOG
.
info
(
"====>Received tick tuple, triggering emit of current window counts"
)
;
emitCurrentWindowCounts
(
)
;
}
else
{
emitNormal
(
tuple
)
;
}
}
private
void
emitNormal
(
Tuple
tuple
)
{
cache
.
add
(
tuple
)
;
}
public
abstract
void
prepare
(
Map
stormConf
,
TopologyContext
context
,
OutputCollector
collector
)
;
public
abstract
void
emitCurrentWindowCounts
(
)
;
public
abstract
void
declareOutputFields
(
OutputFieldsDeclarer
declarer
)
;
@Override
public
Map
<
String
,
Object
>
getComponentConfiguration
(
)
{
Map
<
String
,
Object
>
conf
=
new
HashMap
<
String
,
Object
>
(
)
;
conf
.
put
(
Config
.
TOPOLOGY_TICK_TUPLE_FREQ_SECS
,
emitFrequencyInSeconds
)
;
return
conf
;
}
}
|
1
2
3
4
5
|
public
abstract
void
prepare
(
Map
stormConf
,
TopologyContext
context
,
OutputCollector
collector
)
;
public
abstract
void
emitCurrentWindowCounts
(
)
;
public
abstract
void
declareOutputFields
(
OutputFieldsDeclarer
declarer
)
;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Override
public
void
emitCurrentWindowCounts
(
)
{
int
sum
=
0
;
List
Values
val
=
new
Values
(
)
;
if
(
windowedTuples
!=
null
&&
windowedTuples
.
size
(
)
!=
0
)
{
for
(
Tuple
t
:
windowedTuples
)
{
List
objs
=
t
.
getValues
(
)
;
val
.
addAll
(
t
.
getValues
(
)
)
;
if
(
objs
!=
null
&&
objs
.
size
(
)
>
0
)
{
for
(
Object
obj
:
objs
)
{
int
tmp
=
Integer
.
parseInt
(
obj
.
toString
(
)
)
;
sum
+
=
tmp
;
}
}
}
LOG
.
info
(
"array to sum up: "
+
val
.
toString
(
)
)
;
collector
.
emit
(
new
Values
(
sum
+
""
)
)
;
}
}
|
1
2
3
4
5
|
String
spoutId
=
"numberGenerator"
;
String
sumup
=
"sumup"
;
builder
.
setSpout
(
spoutId
,
new
NumberSpout
(
)
,
2
)
;
builder
.
setBolt
(
sumup
,
new
SumupBolt
(
6
,
2
)
,
1
)
.
fieldsGrouping
(
spoutId
,
new
Fields
(
"number"
)
)
;
|