记一次多线程微信客服消息推送需求的处理

之前运营通过公众号文章推广我们的微商城,效果并不理想。
所以产品想要直接推送精选的商品给用户。

1.找出待推送的用户列表

为了不被用户“投诉”,所以不使用模板消息,而使用客服消息。
但是客服消息要求必须48小时内与公众号有互动,并且处于关注状态。
如果全部用户推送肯定是不可能的,必须找出符合要求的用户。
如果用户取消关注,微信会发送一条通知消息,修改用户关注状态。
而互动,主要是指关注、发送消息、点击菜单等,好在用户做这些操作的时候,微信都会给我们发送通知,我们只需要记录一下最新互动时间就可以了。

2.客服消息接口

推送商品会有商品名称,图片,价格等信息,比较合适使用“发送图文消息(点击跳转到外链)”即“news”进行推送,按照文档构造消息体就好了。

3.多线程推送

我们系统中使用了MQ异步做消息推送,但是这次产品要求获取成功推送的用户数量,虽然能有办法统计到,但异步处理不太合适。但同步处理的话,用户量太大,单线程肯定吃不消。
所以需要使用多线程进行推送。任务主要是IO密集,我们尝试开20个线程进行处理。

ExecutorService executor = new ThreadPoolExecutor(20, 20,
                0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(2000),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

这里又引出了2个常规问题:
1.如何统计成功推送的用户数量?
因为i++是非原子操作,所以直接使用int统计肯定会出现竞争从而造成数值不准确。
问题很简单,原子类走一波。

AtomicInteger userCount = new AtomicInteger(0);

2.如何在所有线程执行完毕后更新数据库记录?
主线程应该等到所有线程执行完毕,才能得到准确的成功推送用户数量,然后再去更新数据库。
这时就需要使用到CountDownLatch,我们知道满足推送条件的用户数量,使用用户数量作为CountDownLatch的计数。并且在finally中进行countDown,避免异常造成没有正确countDown。

CountDownLatch latch = new CountDownLatch(userList.size());
...
try {
     // TODO
} finally {
    latch.countDown();
}

最后在主线程中等待所有线程执行完毕。
这里我们设置了一个超时时间,避免异常情况出现时,主线程一直阻塞在这里。
timeout的时间需要根据满足推送条件的用户数量决定。
预计平均下来每个推送应该最多使用3秒,我觉得这个时间只有多的没有少的。
另外考虑到在推送用户特别少,比如1个的时候,可能由于网络波动造成3秒内不能正常推送,所以增加了一个基础时间10秒。

Long timeout = wxUserInfoOfficials.size() * 3L + 10L;
...
latch.await(timeout, TimeUnit.SECONDS);

公司的代码要保密,所以只是记录了思路。
目前该功能已上线,情况不错~

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据