喝水小助手實現思路附代碼案例

點擊▲關注 “爪哇筆記”   給公衆號標星置頂

更多精彩 第一時間直達

大家好,我是本羣的提醒喝水小助手,這是今天的第X輪。希望此刻看到消息的人可以和我一起來一杯水。一小時後的我繼續提醒大家喝水。和我一起成爲一天八杯水的人吧!

最初是爲了提醒大家注意身體健康,也有提醒運動、提醒站立等等,後來大家腦洞大開的創作出了各種滑稽搞笑的表情包,例如提醒喝可樂,提醒上廁所等等,該表情包也開始在不同的圈子裏流行起來,例如在遊戲裏有提醒開黑,在B站有提醒更新視頻等等。

定時任務

利用SpringTask、Quartz實現定時提醒發送,不過小程序的消息模板推送已經不支持隨意給用戶發消息了,需要用戶自行去觸發。

延遲隊列

用戶手動觸發一次喝水動作,這時候我們可以使用延遲隊列來實現,推薦使用開源的第三方工具包redisson,同時保證redis支持lua特性。

配置application.properties

# redisson lock
redisson.address=redis://127.0.0.1:6379
redisson.password=123456

參數配置類RedissonProperties 

@Data
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
    private int timeout = 3000;
    private String address;
    private String password;
    private int connectionPoolSize = 64;
    private int connectionMinimumIdleSize=10;
    private int slaveConnectionPoolSize = 250;
    private int masterConnectionPoolSize = 250;
    private String[] sentinelAddresses;
    private String masterName;
}

自動裝配 RedissonAutoConfiguration

@Configuration
@ConditionalOnClass(Config.class)
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAutoConfiguration {
    @Autowired
    private RedissonProperties redssionProperties;
    /**
     * 單機模式自動裝配
     * @return
     */
    @Bean
    RedissonClient redissonSingle() {
        Config config = new Config();
        SingleServerConfig serverConfig = config.useSingleServer()
                .setAddress(redssionProperties.getAddress)
                .setTimeout(redssionProperties.getTimeout())
                .setConnectionPoolSize(redssionProperties.getConnectionPoolSize())
                .setConnectionMinimumIdleSize(redssionProperties.getConnectionMinimumIdleSize());
        if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
            serverConfig.setPassword(redssionProperties.getPassword());
        }
        return Redisson.create(config);
    }
    /**
     * 裝配locker類,並將實例注入到RedissLockUtil中
     * @return
     */
    @Bean
    RedissLockUtil redissLockUtil(RedissonClient redissonClient) {
        RedissLockUtil redissLockUtil = new RedissLockUtil();
        redissLockUtil.setRedissonClient(redissonClient);
        return redissLockUtil;
    }
}

工具類RedissLockUtil

/**
 * redis分佈式鎖幫助類
 * @author 爪哇筆記 By https://blog.52itstyle.vip
 */
public class RedissLockUtil {
    private static RedissonClient redissonClient;
    public void setRedissonClient(RedissonClient locker) {
        redissonClient = locker;
    }
    /**
     * 初始紅包數量
     * @param key
     * @param count
     */
    public void initCount(String key,int count) {
        RMapCache<String, Integer> mapCache = redissonClient.getMapCache("skill");
        mapCache.putIfAbsent(key,count,3,TimeUnit.DAYS);
    }
    /**
     * 遞增
     * @param key
     * @param delta 要增加幾(大於0)
     * @return
     */
    public int incr(String key, int delta) {
        RMapCache<String, Integer> mapCache = redissonClient.getMapCache("skill");
        if (delta < 0) {
            throw new RuntimeException("遞增因子必須大於0");
        }
        return  mapCache.addAndGet(key, 1);//加1並獲取計算後的值
    }
    /**
     * 遞減
     * @param key 鍵
     * @param delta 要減少幾(小於0)
     * @return
     */
    public int decr(String key, int delta) {
        RMapCache<String, Integer> mapCache = redissonClient.getMapCache("skill");
        if (delta < 0) {
            throw new RuntimeException("遞減因子必須大於0");
        }
        return mapCache.addAndGet(key, -delta);//加1並獲取計算後的值
    }
    public static RedissonClient getRedissonClient() {
        return redissonClient;
    }
}

入隊列,僞代碼:

RedissonClient redissonClient = RedissLockUtil.getRedissonClient();
/**
* 目標隊列
*/
RBlockingQueue<RemindDelay> blockingRemindQueue
= redissonClient.getBlockingQueue("remindDelayQueue");
/**
* 定時任務將到期的元素轉移到目標隊列
*/
RDelayedQueue<RemindDelay> delayedRemindQueue
= redissonClient.getDelayedQueue(blockingRemindQueue);
/**
* 延時信息入隊列
*/
delayedRemindQueue.offer(new RemindDelay(remind.getId()), sec, TimeUnit.SECONDS);

隊列消息實體標識:

public class RemindDelay implements Serializable {
    /**
     * 主鍵ID
     */
    private  long id;
    /**
     * 創建時間戳
     */
    private  long timestamp;
    public RemindDelay() {
    }
    public RemindDelay(long id) {
        this.id = id;
        this.timestamp = System.currentTimeMillis();
    }
    public long getId() {
        return id;
    }
    public long getTimestamp() {
        return timestamp;
    }
}

消費提醒隊列:

/**
 * 消費提醒隊列
 */
@Component
public class RemindRunner implements ApplicationRunner {
    private final static Logger LOGGER = LoggerFactory.getLogger(RemindRunner.class);
    private static int corePoolSize = Runtime.getRuntime().availableProcessors();
    private static ThreadPoolExecutor executor  =
            new ThreadPoolExecutor(corePoolSize, corePoolSize+1, 10L, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<>(100));
    @Autowired
    private RemindService remindService;
    @Autowired
    private WxMaService wxService;
    @Override
    public void run(ApplicationArguments args) throws Exception {
        /**
         * 出隊列
         */
        RedissonClient redissonClient = RedissLockUtil.getRedissonClient();
        RBlockingQueue<RemindDelay> delayedRemindQueue
                = redissonClient.getBlockingQueue("remindDelayQueue");
        new Thread(() -> {
            LOGGER.info("提醒隊列啓動成功");
            while (true){
                try {
                    RemindDelay delay = delayedRemindQueue.take();
                    push(delay);
                    LOGGER.info("提醒ID:{}",delay.getId());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
    public void push(RemindDelay delay){
        /**
         * 推送
         */
        Runnable task = () -> {
            //發送消息通知邏輯
        };
        executor.execute(task);
    }
}

適用場景

淘寶訂單到期,下單成功後60s之後給用戶發送短信通知,限時支付、緩存系統、紅包過期、開獎提醒等等。

小結

以上方案爲單機模式,生產環境可根據實際業務需求選擇使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章