Skip to content

Commit 9fe7331

Browse files
committed
1 parent 85168a1 commit 9fe7331

File tree

7 files changed

+199
-92
lines changed

7 files changed

+199
-92
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package me.chanjar.weixin.common.util;
2+
3+
/**
4+
* <pre>
5+
* 消息重复检查器
6+
* 微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次
7+
* </pre>
8+
*/
9+
public interface WxMsgIdDuplicateChecker {
10+
11+
/**
12+
* 检查消息ID是否重复
13+
* @param wxMsgId
14+
* @return 如果是重复消息,返回true,否则返回false
15+
*/
16+
public boolean isDuplicate(Long wxMsgId);
17+
18+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package me.chanjar.weixin.common.util;
2+
3+
import java.util.Map;
4+
import java.util.concurrent.ConcurrentHashMap;
5+
6+
/**
7+
* <pre>
8+
* 默认消息重复检查器
9+
* 将每个消息id保存在内存里,每隔5秒清理已经过期的消息id,每个消息id的过期时间是15秒
10+
* </pre>
11+
*/
12+
public class WxMsgIdInMemoryDuplicateChecker implements WxMsgIdDuplicateChecker {
13+
14+
/**
15+
* 一个消息ID在内存的过期时间:15秒
16+
*/
17+
private final Long TIME_TO_LIVE;
18+
19+
/**
20+
* 每隔多少周期检查消息ID是否过期:5秒
21+
*/
22+
private final Long CLEAR_PERIOD;
23+
24+
private final ConcurrentHashMap<Long, Long> msgId2Timestamp = new ConcurrentHashMap<Long, Long>();
25+
26+
/**
27+
* WxMsgIdInMemoryDuplicateChecker构造函数
28+
* <pre>
29+
* 一个消息ID在内存的过期时间:15秒
30+
* 每隔多少周期检查消息ID是否过期:5秒
31+
* </pre>
32+
*/
33+
public WxMsgIdInMemoryDuplicateChecker() {
34+
this.TIME_TO_LIVE = 15 * 1000l;
35+
this.CLEAR_PERIOD = 5 * 1000l;
36+
this.start();
37+
}
38+
39+
/**
40+
* WxMsgIdInMemoryDuplicateChecker构造函数
41+
* @param timeToLive 一个消息ID在内存的过期时间:毫秒
42+
* @param clearPeriod 每隔多少周期检查消息ID是否过期:毫秒
43+
*/
44+
public WxMsgIdInMemoryDuplicateChecker(Long timeToLive, Long clearPeriod) {
45+
this.TIME_TO_LIVE = timeToLive;
46+
this.CLEAR_PERIOD = clearPeriod;
47+
this.start();
48+
}
49+
50+
private void start() {
51+
Thread t = new Thread(new Runnable() {
52+
@Override
53+
public void run() {
54+
try {
55+
while (true) {
56+
Thread.sleep(CLEAR_PERIOD);
57+
Long now = System.currentTimeMillis();
58+
for (Map.Entry<Long, Long> entry : msgId2Timestamp.entrySet()) {
59+
if (now - entry.getValue() > TIME_TO_LIVE) {
60+
msgId2Timestamp.entrySet().remove(entry);
61+
}
62+
}
63+
}
64+
} catch (InterruptedException e) {
65+
e.printStackTrace();
66+
}
67+
}
68+
});
69+
t.setDaemon(true);
70+
t.start();
71+
}
72+
73+
@Override
74+
public boolean isDuplicate(Long wxMsgId) {
75+
Long timestamp = msgId2Timestamp.putIfAbsent(wxMsgId, System.currentTimeMillis());
76+
if (timestamp == null) {
77+
// 第一次接收到这个消息
78+
return false;
79+
}
80+
return true;
81+
}
82+
83+
84+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package me.chanjar.weixin.common.util;
2+
3+
import org.testng.Assert;
4+
import org.testng.annotations.Test;
5+
6+
/**
7+
* Created by qianjia on 15/1/20.
8+
*/
9+
@Test
10+
public class WxMsgIdInMemoryDuplicateCheckerTest {
11+
12+
public void test() throws InterruptedException {
13+
Long[] msgIds = new Long[] { 1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l };
14+
WxMsgIdInMemoryDuplicateChecker checker = new WxMsgIdInMemoryDuplicateChecker(2000l, 1000l);
15+
16+
// 第一次检查
17+
for (Long msgId : msgIds) {
18+
boolean result = checker.isDuplicate(msgId);
19+
Assert.assertFalse(result);
20+
}
21+
22+
// 过1秒再检查
23+
Thread.sleep(1000l);
24+
for (Long msgId : msgIds) {
25+
boolean result = checker.isDuplicate(msgId);
26+
Assert.assertTrue(result);
27+
}
28+
29+
// 过1.5秒再检查
30+
Thread.sleep(1500l);
31+
for (Long msgId : msgIds) {
32+
boolean result = checker.isDuplicate(msgId);
33+
Assert.assertFalse(result);
34+
}
35+
36+
}
37+
38+
}

weixin-java-common/src/test/resources/testng.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<class name="me.chanjar.weixin.common.bean.WxErrorTest" />
88
<class name="me.chanjar.weixin.common.bean.WxMenuTest" />
99
<class name="me.chanjar.weixin.common.util.crypto.WxCryptUtilTest" />
10+
<class name="me.chanjar.weixin.common.util.WxMsgIdInMemoryDuplicateCheckerTest" />
1011
</classes>
1112
</test>
1213
</suite>

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMessageRouter.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package me.chanjar.weixin.cp.api;
22

3+
import me.chanjar.weixin.common.util.WxMsgIdDuplicateChecker;
4+
import me.chanjar.weixin.common.util.WxMsgIdInMemoryDuplicateChecker;
35
import me.chanjar.weixin.cp.bean.WxCpXmlMessage;
46
import me.chanjar.weixin.cp.bean.WxCpXmlOutMessage;
57

@@ -45,18 +47,38 @@ public class WxCpMessageRouter {
4547

4648
private final List<Rule> rules = new ArrayList<Rule>();
4749

48-
private final ExecutorService executorService;
49-
5050
private final WxCpService wxCpService;
5151

52+
private ExecutorService executorService;
53+
54+
private WxMsgIdDuplicateChecker wxMsgIdDuplicateChecker;
55+
5256
public WxCpMessageRouter(WxCpService wxCpService) {
5357
this.wxCpService = wxCpService;
5458
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
59+
this.wxMsgIdDuplicateChecker = new WxMsgIdInMemoryDuplicateChecker();
5560
}
5661

5762
public WxCpMessageRouter(WxCpService wxMpService, int threadPoolSize) {
5863
this.wxCpService = wxMpService;
5964
this.executorService = Executors.newFixedThreadPool(threadPoolSize);
65+
this.wxMsgIdDuplicateChecker = new WxMsgIdInMemoryDuplicateChecker();
66+
}
67+
68+
/**
69+
* 设置自定义的ExecutorService
70+
* @param executorService
71+
*/
72+
public void setExecutorService(ExecutorService executorService) {
73+
this.executorService = executorService;
74+
}
75+
76+
/**
77+
* 设置自定义的WxMsgIdDuplicateChecker
78+
* @param wxMsgIdDuplicateChecker
79+
*/
80+
public void setWxMsgIdDuplicateChecker(WxMsgIdDuplicateChecker wxMsgIdDuplicateChecker) {
81+
this.wxMsgIdDuplicateChecker = wxMsgIdDuplicateChecker;
6082
}
6183

6284
/**
@@ -72,6 +94,11 @@ public Rule rule() {
7294
* @param wxMessage
7395
*/
7496
public WxCpXmlOutMessage route(final WxCpXmlMessage wxMessage) {
97+
if (wxMsgIdDuplicateChecker.isDuplicate(wxMessage.getMsgId())) {
98+
// 如果是重复消息,那么就不做处理
99+
return null;
100+
}
101+
75102
final List<Rule> matchRules = new ArrayList<Rule>();
76103
// 收集匹配的规则
77104
for (final Rule rule : rules) {

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpDuplicateMessageInterceptor.java

Lines changed: 0 additions & 88 deletions
This file was deleted.

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpMessageRouter.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package me.chanjar.weixin.mp.api;
22

3+
import me.chanjar.weixin.common.util.WxMsgIdDuplicateChecker;
4+
import me.chanjar.weixin.common.util.WxMsgIdInMemoryDuplicateChecker;
35
import me.chanjar.weixin.mp.bean.WxMpXmlMessage;
46
import me.chanjar.weixin.mp.bean.WxMpXmlOutMessage;
57

@@ -45,18 +47,38 @@ public class WxMpMessageRouter {
4547

4648
private final List<Rule> rules = new ArrayList<Rule>();
4749

48-
private final ExecutorService executorService;
49-
5050
private final WxMpService wxMpService;
5151

52+
private ExecutorService executorService;
53+
54+
private WxMsgIdDuplicateChecker wxMsgIdDuplicateChecker;
55+
5256
public WxMpMessageRouter(WxMpService wxMpService) {
5357
this.wxMpService = wxMpService;
5458
this.executorService = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE);
59+
this.wxMsgIdDuplicateChecker = new WxMsgIdInMemoryDuplicateChecker();
5560
}
5661

5762
public WxMpMessageRouter(WxMpService wxMpService, int threadPoolSize) {
5863
this.wxMpService = wxMpService;
5964
this.executorService = Executors.newFixedThreadPool(threadPoolSize);
65+
this.wxMsgIdDuplicateChecker = new WxMsgIdInMemoryDuplicateChecker();
66+
}
67+
68+
/**
69+
* 设置自定义的ExecutorService
70+
* @param executorService
71+
*/
72+
public void setExecutorService(ExecutorService executorService) {
73+
this.executorService = executorService;
74+
}
75+
76+
/**
77+
* 设置自定义的WxMsgIdDuplicateChecker
78+
* @param wxMsgIdDuplicateChecker
79+
*/
80+
public void setWxMsgIdDuplicateChecker(WxMsgIdDuplicateChecker wxMsgIdDuplicateChecker) {
81+
this.wxMsgIdDuplicateChecker = wxMsgIdDuplicateChecker;
6082
}
6183

6284
/**
@@ -72,6 +94,11 @@ public Rule rule() {
7294
* @param wxMessage
7395
*/
7496
public WxMpXmlOutMessage route(final WxMpXmlMessage wxMessage) {
97+
if (wxMsgIdDuplicateChecker.isDuplicate(wxMessage.getMsgId())) {
98+
// 如果是重复消息,那么就不做处理
99+
return null;
100+
}
101+
75102
final List<Rule> matchRules = new ArrayList<Rule>();
76103
// 收集匹配的规则
77104
for (final Rule rule : rules) {

0 commit comments

Comments
 (0)