目录[-]
一、背景
有些人可能不太明白为什么要限制,原因也很直白“因为程序处理不过来”。编写数据处理的程序时,程序写的再好也会有处理速度的瓶颈。所以当压力持续增加时,会导致数据处理程序直接崩溃,这个结果是我们不想看到的。我们希望看到的是可以处理不了并丢弃,但程序不能死。
二、分析
从何种角度去限制?多很多种方式。可以设置一个消息队列,并为队列设置最大值。也可以按速度,每秒只可以处理2000条。显然按速度的方式比队列大小的方式更容易表达给客户。也更容易体现我们产品本身的数据处理能力。甚至可以用于License对数据规模进行有效的控制。
如何获得处理速度并进行限制呢?
原理比较简单:设置一个全局计数器,每秒自动清零。新增处理时会自动比较该计数器是否已经超过限制值,如果超出则不执行。
三、编码
先执行init初始化,然后就可以调用handle模式处理数据,最后调用destroy销毁。ExampleHandler类通过调用setHandleLimitEps来设置限制值。
代码中有注释,不就做过多说明了。
1、ExampleHandler.java
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
package
org.noahx.slimit;
import
org.slf4j.Logger;
import
org.slf4j.LoggerFactory;
import
java.util.Timer;
import
java.util.TimerTask;
import
java.util.concurrent.atomic.AtomicLong;
/**
* Created with IntelliJ IDEA.
* User: noah
* Date: 2/3/14
* Time: 8:38 AM
* To change this template use File | Settings | File Templates.
*/
public
abstract
class
ExampleHandler<T> {
private
Logger logger = LoggerFactory.getLogger(
this
.getClass());
private
long
handleLimitEps =
0
;
private
long
handleSpeedEps =
0
;
private
AtomicLong handleSpeedCounter =
new
AtomicLong(
0
);
private
Timer timer;
/**
* 初始化
*/
public
void
init() {
timer =
new
Timer();
timer.schedule(
new
TimerTask() {
//定时速度计算器(EPS)1秒种执行一次,handle速度
@Override
public
void
run() {
handleSpeedEps = handleSpeedCounter.getAndSet(
0
);
//获得1s内中已处理的数量,并重置counter为0
if
(logger.isTraceEnabled()) {
logger.trace(
"Speed> "
+ handleSpeedEps +
" EPS"
);
}
}
},
1000
,
1000
);
}
/**
* 销毁
*/
public
void
destroy() {
if
(timer !=
null
) {
timer.cancel();
}
}
/**
* 限制计算
*
* @return 如果受限返回false
*/
private
boolean
limit() {
if
(handleLimitEps <=
0
) {
//小于等于宇0不做速度限制
return
false
;
}
if
(handleSpeedCounter.get() >= handleLimitEps) {
//达到每s限制值返回true
return
true
;
}
else
{
return
false
;
}
}
/**
* 处理数据外部方法(速度限制)
*
* @param data
* @return 已处理返回true,未处理返回false
*/
public
boolean
handle(
final
T data) {
if
(limit()) {
//限制检测
return
false
;
}
handleSpeedCounter.incrementAndGet();
//增加计数
doHandle(data);
return
true
;
}
/**
* 待实现处理数据方法
*
* @param data
*/
protected
abstract
void
doHandle(T data);
/**
* 获得限制EPS
*
* @return
*/
public
long
getHandleLimitEps() {
return
handleLimitEps;
}
/**
* 设置EPS限制
*
* @param handleLimitEps
*/
public
void
setHandleLimitEps(
long
handleLimitEps) {
this
.handleLimitEps = handleLimitEps;
}
/**
* 获得当前处理速度
*
* @return
*/
public
long
getHandleSpeedEps() {
return
handleSpeedEps;
}
}
|
2、ExampleHandlerTest.java(单元测试类)
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
77
78
79
80
81
82
83
84
85
86
87
88
|
package
org.noahx.slimit;
import
org.junit.After;
import
org.junit.Before;
import
org.junit.Test;
import
org.slf4j.Logger;
import
org.slf4j.LoggerFactory;
import
java.util.concurrent.CountDownLatch;
import
java.util.concurrent.ExecutorService;
import
java.util.concurrent.Executors;
import
java.util.concurrent.atomic.AtomicLong;
/**
* Created with IntelliJ IDEA.
* User: noah
* Date: 2/3/14
* Time: 9:05 AM
* To change this template use File | Settings | File Templates.
*/
public
class
ExampleHandlerTest {
static
{
System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY,
"TRACE"
);
}
public
static
final
long
EVENT_AMOUNT =
10000000
;
private
final
AtomicLong countLimit =
new
AtomicLong(
0
);
private
final
AtomicLong countHandle =
new
AtomicLong(
0
);
private
final
CountDownLatch countDownLatch =
new
CountDownLatch((
int
) EVENT_AMOUNT);
private
Logger logger = LoggerFactory.getLogger(
this
.getClass());
private
ExampleHandler<Object> handler;
@Before
public
void
before() {
handler =
new
ExampleHandler<Object>() {
@Override
protected
void
doHandle(Object data) {
countHandle.incrementAndGet();
countDownLatch.countDown();
//处理数据
}
};
handler.init();
}
@Test
public
void
test1()
throws
Exception {
testHandle();
//无速度限制测试
}
@Test
public
void
test2()
throws
Exception {
handler.setHandleLimitEps(
2000
);
//设置2000的限制
testHandle();
}
protected
void
testHandle()
throws
Exception {
final
ExecutorService executorService = Executors.newFixedThreadPool(
20
);
for
(
int
c =
0
; c < EVENT_AMOUNT; c++) {
final
Runnable runnable =
new
Runnable() {
@Override
public
void
run() {
if
(!handler.handle(
null
)) {
countLimit.incrementAndGet();
//受限制数++
countDownLatch.countDown();
}
}
};
executorService.execute(runnable);
}
countDownLatch.await();
//等待多线程处理完毕
logger.info(
"Handle: "
+ countHandle.get());
//处理数
logger.info(
"Limit: "
+ countLimit.get());
//受限制数
}
@After
public
void
after() {
handler.destroy();
}
}
|
3、test1()测试,不限制执行结果
1
2
3
4
5
6
7
8
9
|
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 975601 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 273132 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 575888 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 1116810 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 1254701 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 700775 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 3367754 EPS
[main] INFO org.noahx.slimit.ExampleHandlerTest - Handle: 10000000
[main] INFO org.noahx.slimit.ExampleHandlerTest - Limit: 0
|
没有任何请求受到限制,limit为0。
4、test2()测试,受限制执行结果
1
2
3
4
5
6
7
8
9
|
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 2000 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 2000 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 2000 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 2000 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 2000 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 2000 EPS
[Timer-0] TRACE org.noahx.slimit.ExampleHandlerTest$1 - Speed> 2000 EPS
[main] INFO org.noahx.slimit.ExampleHandlerTest - Handle: 16000
[main] INFO org.noahx.slimit.ExampleHandlerTest - Limit: 9984000
|
可以看到受限的情况下,只处理了16000。其余请求全部受限。
5、pom.xml
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
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
project
xmlns
=
"http://maven.apache.org/POM/4.0.0"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
=
"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
<
modelVersion
>4.0.0</
modelVersion
>
<
groupId
>org.noahx</
groupId
>
<
artifactId
>speed-limit</
artifactId
>
<
version
>1.0-SNAPSHOT</
version
>
<
dependencies
>
<
dependency
>
<
groupId
>org.slf4j</
groupId
>
<
artifactId
>slf4j-api</
artifactId
>
<
version
>1.7.5 </
version
>
</
dependency
>
<
dependency
>
<
groupId
>org.slf4j</
groupId
>
<
artifactId
>slf4j-simple</
artifactId
>
<
scope
>runtime</
scope
>
<
version
>1.7.5 </
version
>
</
dependency
>
<
dependency
>
<
groupId
>junit</
groupId
>
<
artifactId
>junit</
artifactId
>
<
version
>4.11</
version
>
</
dependency
>
</
dependencies
>
</
project
>
|
四、总结
EPS名词解释,EPS是Event Per Second的缩写,一般用于表示处理能力(每秒可以处理的事件数)。
我在使用Dispatcher框架时需要进行速度限制,所以开发了速度限制与批处理功能。
以上的样例代码就是从项目中提取出来的,逻辑比较简单基本表达出了限制的意思。
大家如果使用可能需要再次进行完善改造。
注:该限制速度采用最基本的先到先得的方式,并没有采用复杂等待算法。
源码下载:http://sdrv.ms/1dkDqVT