|
| 1 | +package me.chanjar.weixin.common.api; |
| 2 | + |
| 3 | +import com.google.common.util.concurrent.ThreadFactoryBuilder; |
| 4 | +import lombok.extern.slf4j.Slf4j; |
| 5 | + |
| 6 | +import java.util.concurrent.ConcurrentHashMap; |
| 7 | +import java.util.concurrent.ScheduledThreadPoolExecutor; |
| 8 | +import java.util.concurrent.ThreadPoolExecutor; |
| 9 | +import java.util.concurrent.TimeUnit; |
| 10 | + |
| 11 | +/** |
| 12 | + * @author jiangby |
| 13 | + * @version 1.0 |
| 14 | + * <p> |
| 15 | + * 消息去重,记录消息ID首次出现时的时间戳, |
| 16 | + * 15S后定时任务触发时废除该记录消息ID |
| 17 | + * </p> |
| 18 | + * @date 2022/5/26 1:32 |
| 19 | + */ |
| 20 | +@Slf4j |
| 21 | +public class WxMessageInMemoryDuplicateCheckerSingleton implements WxMessageDuplicateChecker { |
| 22 | + |
| 23 | + /** |
| 24 | + * 一个消息ID在内存的过期时间:15秒. |
| 25 | + */ |
| 26 | + private static final Long TIME_TO_LIVE = 15L; |
| 27 | + |
| 28 | + /** |
| 29 | + * 每隔多少周期检查消息ID是否过期:5秒. |
| 30 | + */ |
| 31 | + private static final Long CLEAR_PERIOD = 5L; |
| 32 | + |
| 33 | + /** |
| 34 | + * 线程池 |
| 35 | + */ |
| 36 | + private static final ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(1, |
| 37 | + new ThreadFactoryBuilder().setNameFormat("wxMessage-memory-pool-%d").build(), new ThreadPoolExecutor.AbortPolicy()); |
| 38 | + |
| 39 | + /** |
| 40 | + * 消息id->消息时间戳的map. |
| 41 | + */ |
| 42 | + private static final ConcurrentHashMap<String, Long> MSG_ID_2_TIMESTAMP = new ConcurrentHashMap<>(); |
| 43 | + |
| 44 | + static { |
| 45 | + SCHEDULED_THREAD_POOL_EXECUTOR.scheduleAtFixedRate(() -> { |
| 46 | + try { |
| 47 | + Long now = System.currentTimeMillis(); |
| 48 | + MSG_ID_2_TIMESTAMP.entrySet().removeIf(entry -> now - entry.getValue() > TIME_TO_LIVE * 1000); |
| 49 | + } catch (Exception ex) { |
| 50 | + log.error("重复消息去重任务出现异常", ex); |
| 51 | + } |
| 52 | + }, 1, CLEAR_PERIOD, TimeUnit.SECONDS); |
| 53 | + } |
| 54 | + |
| 55 | + /** |
| 56 | + * 私有化构造方法,避免外部调用 |
| 57 | + */ |
| 58 | + private WxMessageInMemoryDuplicateCheckerSingleton() { |
| 59 | + } |
| 60 | + |
| 61 | + /** |
| 62 | + * 获取单例 |
| 63 | + * |
| 64 | + * @return 单例对象 |
| 65 | + */ |
| 66 | + public static WxMessageInMemoryDuplicateCheckerSingleton getInstance() { |
| 67 | + return WxMessageInnerClass.CHECKER_SINGLETON; |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * 内部类实现单例 |
| 72 | + */ |
| 73 | + private static class WxMessageInnerClass { |
| 74 | + static final WxMessageInMemoryDuplicateCheckerSingleton CHECKER_SINGLETON = new WxMessageInMemoryDuplicateCheckerSingleton(); |
| 75 | + } |
| 76 | + |
| 77 | + /** |
| 78 | + * messageId是否重复 |
| 79 | + * |
| 80 | + * @param messageId messageId |
| 81 | + * @return 是否 |
| 82 | + */ |
| 83 | + @Override |
| 84 | + public boolean isDuplicate(String messageId) { |
| 85 | + if (messageId == null) { |
| 86 | + return false; |
| 87 | + } |
| 88 | + Long timestamp = MSG_ID_2_TIMESTAMP.putIfAbsent(messageId, System.currentTimeMillis()); |
| 89 | + return timestamp != null; |
| 90 | + } |
| 91 | +} |
0 commit comments