最代码官方
2014-08-31 18:06:42
原证精
最代码网站中在线提醒的机制优化经验及其相关源代码片断分享
提醒功能前台截图:
提醒功能在线用户列表管理后台截图:
最代码网站中的提醒功能经历了几次的优化:
- js每隔10s请求下mysql中reminds的在线用户数据
- js每隔10s请求从java cache中获取,有其他用户私信该在线用户,或评论该在线用户的分享等操作时,同时更新mysql reminds记录和在线用户的cache,如果用户不在线则只更新mysql
- 除了js每隔10s请求外,如果用户刷新页面同时从session中获取对应的reminds数据,有则直接提醒,无须等到10s后再提醒
这个机制还不完善,后续版本可以修改为基于html5长连接的方法。
相关源代码片断:
UserController.java中reminds方法,也就是js请求的接口方法
@RequestMapping(value = { "reminds" }, method = { RequestMethod.GET }) public @ResponseBody JSONObject reminds(HttpServletRequest request, HttpSession session) { JSONObject json = new JSONObject(); User user = (User) session .getAttribute(GlobalConstants.SESSION_LOGIN_USER_NAME); if (user == null) { json.put("error", "尚未登录"); return json; } List<ModuleDesc> remindDescs = (List<ModuleDesc>) session .getAttribute("remindDescs"); json.put("error", ""); json.put("remindDescs", remindDescs); user.setLoginTime(new Date()); return json; }
OnlineUserRemind.java,这个类是核心实现,负责维护在线用户及其提醒,注意多线程并发问题,中间有个版本出现把未登陆的用户算到在线的用户列表中的问题。多线程的锁机制也得注意,得用当前登陆用户的实例锁。而不是所有在线用户去争这个对象实例的锁。
@Service public class OnlineUserRemind { private static final Logger logger = Logger .getLogger(OnlineUserRemind.class); @Autowired public RemindService remindService; @Autowired public UserService userService; private static final long CHECK_OFFLINE_USER_TIME = 10 * 60 * 1000; // private static final long CHECK_OFFLINE_USER_TIME = 10 * 1000;// test // time private static final long OFFLINE_USER_KICKTIME = 30 * 60 * 1000; // private static final long OFFLINE_USER_KICKTIME = 30 * 1000;// test time private ConcurrentHashMap<Long, OnlineUser> onlineUsers; public OnlineUserRemind() { onlineUsers = new ConcurrentHashMap<Long, OnlineUser>(); new ClearThread().start(); } public void update(User user, int type, int count) { synchronized (user) { OnlineUser onlineUser = onlineUsers.get(user.getId()); // 如果user offline不管 if (onlineUser == null) { return; } onlineUser.updateRemind(type, count); } } public List<ModuleDesc> getRemindDescs(User user) { synchronized (user) { Long userId = user.getId(); List<ModuleDesc> remindDescs = new ArrayList<ModuleDesc>(); OnlineUser onlineUser = onlineUsers.get(user.getId()); Map<Integer, Remind> reminds = null; if (onlineUser == null) {// 只有第一次初始化从数据库读取 Page<Remind> page = remindService.findAllByUserId(userId, 1, Integer.MAX_VALUE); List<Remind> _reminds = page.getContent(); onlineUser = new OnlineUser(user); onlineUser.addReminds(_reminds); onlineUsers.put(userId, onlineUser); } reminds = onlineUser.getReminds(); for (Remind remind : reminds.values()) { if (remind.getCount() == 0) { continue; } ModuleDesc remindDesc = ModuleConstants.REMIND_TYPE_DESC_MAP .get(remind.getType()); String desc = remindDesc.getDesc().replace("n", remind.getCount() + ""); String url = remindDesc.getUrl(); ModuleDesc _remindDesc = new ModuleDesc(desc, url); remindDescs.add(_remindDesc); } return remindDescs; } } private void removeAll(List<Long> userIds) { if (userIds.isEmpty()) { return; } for (Long userId : userIds) { onlineUsers.remove(userId); } } public Map<Long, OnlineUser> getOnlineUsers() { return onlineUsers; } private class ClearThread extends Thread { public void run() { while (true) { List<Long> offlineUserIds = new ArrayList<Long>(); for (OnlineUser onlineUser : onlineUsers.values()) { User user = onlineUser.getUser(); Long userId = user.getId(); Date loginTime = user.getLoginTime(); if (loginTime == null) { continue; } logger.info("Check user#" + user.getId() + " " + loginTime.toLocaleString() + " is onlining."); long now = System.currentTimeMillis(); long t = (now - loginTime.getTime()); if (t >= OFFLINE_USER_KICKTIME) {// 移除超时的数据 offlineUserIds.add(userId); logger.info("User#" + user.getId() + " is offline."); } } removeAll(offlineUserIds); try { Thread.sleep(CHECK_OFFLINE_USER_TIME); } catch (Exception e) { e.printStackTrace(); } } } } }
UserInterceptor.java的postHandle方法,此处注意是用拦截器的postHandle,而不是preHandle或afterCompletion,用户刷新页面时从cache中获取reminds放到session中
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { HttpSession session = request.getSession(); User user = (User) session .getAttribute(GlobalConstants.SESSION_LOGIN_USER_NAME); if (user == null) { return; } user.setLoginTime(new Date()); List<ModuleDesc> remindDescs = onlineUserRemind.getRemindDescs(user); session.setAttribute("remindDescs", remindDescs); }
RemindServiceImpl.java,所有涉及到用户提醒的都调用该方法,该方法会调用OnlineUserRemind来更新mysql和cache,比如评论:remindService.save(sourceUser, ModuleConstants.REMIND_TYPE_COMMENT);
@Override @Transactional(readOnly = false, propagation = Propagation.REQUIRED) public void save(User user, int type) { long userId = user.getId(); Remind remind = null; Page<Remind> reminds = findAllByUserIdAndType(userId, type, 1, 1); int count = 0; if (reminds.getContent().size() > 0) { remind = reminds.getContent().get(0); count = remind.getCount(); } else { remind = new Remind(); remind.setType(type); remind.setUserId(userId); } count++; remind.setCount(count); save(remind); // 更新cache onlineUserRemind.update(user, type, count); }
都是最代码的代码分享,希望对大家有帮助,有问题请留言。
另外不管每个人牛币多少,能力大小,都希望参与到总结分享中来,这个过程也是和其他牛牛学习交流的过程,对所有参与其中的人都是有帮助的,而不是需要牛币的时候才来分享,那样对平台建设无益。
猜你喜欢
请下载代码后再发表评论
相关代码
- 原证精 最代码网站被第三方垃圾广告网站恶意反链处理的经验分享和代码片段分享
- 原证精 最代码网站用户私信列表采用mysql union查询优化为Redis查询的经验和相关代码片段分享
- 原证 最代码官方网站首页静态页面
- 原证精 最代码网站中关于动态表event的设计思路
- 原证 最代码网站的链接被垃圾广告链接恶意提交到百度收录后的经验和代码片段分享
- 证 2012年度java牛用户贡献排行版
- java牛官方想开发一个全部开源的系统
- 证 最代码愚人节首页旋转倾斜特效代码分享
- java牛每日注册用户数
- 原证精 flex小型消息通知框架miniNotice最代码首发
- 原 chrome浏览器插件实现几行脚本批量赞最代码心情
- 原 java通过jsoup爬取最代码牛币兑换活动
最近下载
最近浏览
ma406805131 LV15
5月11日
全栈小白 LV34
3月19日
2036495585 LV9
2023年10月15日
溪若白 LV1
2023年7月13日
Dominick LV14
2023年6月19日
阿龙05 LV6
2023年4月14日
locklock LV2
2022年6月23日
FreddieLee LV3
2022年4月29日
BDLGC123
2022年4月3日
暂无贡献等级
fellowfun LV12
2022年3月31日