每次收到點擊請求,將文章id放進一個隊列裏,而後開啓一個輪詢服務,隔五秒去update到數據庫中。每次查詢到的文章點擊量,有多是未及時更新的(時效性要求不高)。java
使用ConcurrentLinkedQueue 做爲隊列,由於它線程安全, 長度足夠知足須要數據庫
public class CargroupService implements ICargroupService { private final static Log log = LogFactory.getLog(CargroupService.class); /** 文章訪問隊列 **/ public static ConcurrentLinkedQueue<String> articleClickQueue = new ConcurrentLinkedQueue<String>(); /** 定時更新點擊量線程 **/ ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); /** 更新線程是否啓動 **/ private static volatile boolean isStartScheduled = false; @Override public void getArticleDetail(ArticleQuery query) { // 利用線程安全的隊列來保存被點擊的文章,而後定時更新數據庫 String queueStr = dbInfo.getId()+";"+ip; articleClickQueue.offer(queueStr); if (isStartScheduled == false) { log.debug("初始化文章點擊量線程"); synchronized (CargroupService.class) { if (isStartScheduled == false) { isStartScheduled = true; ArticleClickThread thread = new ArticleClickThread(articleService, labelInfoService); scheduledExecutorService.scheduleAtFixedRate(thread, 0, CommonPropertiesConstants.COLLECT_CLICKNUM_SENONDS, TimeUnit.SECONDS); } } } } }
保存線程代碼安全
public class ArticleClickThread implements Runnable { private final static Log log = LogFactory.getLog(ArticleClickThread.class); @Override public void run() { List<Map<String, Integer>> updateList = new ArrayList<Map<String, Integer>>(); Map<Integer, Integer> map = new HashMap<Integer, Integer>(); while (!CargroupService.articleClickQueue.isEmpty()) { String articleIdAndIp = CargroupService.articleClickQueue.poll(); String[] strs = StringUtils.split(articleIdAndIp, ";"); Integer id = Integer.parseInt(strs[0]); String ip = strs[1]; String articleIdAndIpRedisKey = RpcToolUtils.getArticleIdAndIpRedisKey(String.valueOf(id), ip); boolean exit = false; try { exit = RedisDataSource.existsObject(articleIdAndIpRedisKey); } catch (Exception e) { log.error("Redis服務掛了---------- !" + e); exit = false; e.printStackTrace(); } if (exit == false) { try { // 把該ip訪問的文章記錄,1天內從新點擊或者刷新,不計數 RedisDataSource.setObject(articleIdAndIpRedisKey, "1", 24*60*60); } catch (Exception e) { e.printStackTrace(); } if (!map.containsKey(id)) { map.put(id, 1); } else { map.put(id, map.get(id) + 1); } } } for (Integer idKey : map.keySet()) { Map<String, Integer> paramMap = new HashMap<String, Integer>(); paramMap.put("articleId", idKey); paramMap.put("clickTimes", map.get(idKey)); updateList.add(paramMap); } if (updateList != null && updateList.size() > 0) { articleService.batchUpdateClickNum(updateList); labelInfoService.batchUpdateClickNum(updateList); } log.debug("完成輪詢任務:保存文章點擊量"); }
第一次啓動線程後,每次點擊文章只需 往隊列裏扔文章id,等待被輪詢線程保存到數據庫。ide