打卡要求: 1、说一下你的实现逻辑 2、你所用方法的优缺点 本问题对应的项目链接:从0设计一个web项目
我采用session监听器来统计网站在线人数,每次创建session时就会自动加一,销毁session时就会自动减一; 先初始化一个类实现HttpSessionlistener,然后定义count,创建session,count+1,销毁session,count-1;然后在类上加注解webListener,启动类上再加@ServletComponentScan(“com.example.demo.listener”)扫描包才有用。 优点是: 统计在线人数结果相对准确; 缺点是: 当用户关闭浏览器时并不会触发session监听,当下一次登录时仍然会让count加一 或者在session过期时,session监听并不能做一个实时的响应去将在线数减一 当用户再次登陆,由于cookie中含有的session_id不同而导致session监听器记录下session创建,而使count加一。 对服务器性能影响较大,用户每次访问网站时,服务端都会创建一个session,并将该session与用户关联起来,这样会增加服务器的负担,特别是在高并发的时候,导致服务器压力过大 https://github.com/CurryisCoat/community/tree/dev
之前在用户不能重复登陆处,在redis里设置了userName-session的键值对,5分钟后过期。这次根据帅地上节打卡处的留言,将键值对改为userId-session来防止中文用户名出现的乱码问题。
在讲前文提到的键值对加入统一前缀“userId:”然后通过keys(“userId:*”)直接匹配 这种方法在用户量较少的时候没有很大的问题,实现也简单方便。但是用户量增大后,keys是阻塞式查询,效率较低,会导致系统响应时间长用户体验差的情况
大体思路和其他同学相似,score处用时间戳,统计一定时间内有操作的用户视为在线用户。 与其他同学不同的是,我没有采用定时任务的方式。每次统计在线用户后,将这个数存在redis中,有效期1分钟(也就是在线用户数1分钟更新一次),每次需要请求在线用户数时,先检查有无之前保存的在线用户数,如果有的话直接使用,如果没有的话就统计并保存。 我个人理解这种方法的优点在于,只有有需求时才统计在线用户数,而且对在线用户数做缓存也不用每次访问都统计。 此外,关于在线用户数刷新间隔的问题,我觉得用户对这一需求并不敏感也不需要很强的准确性,没必要更新的过分频繁反而增加了服务器压力影响用户体验。 以上是我自己的实现方式以及思考,如有不足之处,请大佬们和帅地哥指正。 GitHub地址:https://github.com/lei342345795/FromZerotoExpert
3.1 统计网站用户在线人数
本次需求有三个要求:
v1.0
维护登录态过程中已经维护了用户的在线token,且唯一。
因此一开始是打算在前端定时轮询获取最新的在线人数,后端收到请求则重新统计一遍在线token的数量。
优缺点
服务端负荷大
从后面的所有迭代方案来看,这个方法总能获取到最新的数据,满足了1.3
但服务端负荷大,用户量稍多一点就会有大量的请求打到服务端,而服务端每次都要重新计算。
v2.0
1.0主要的缺点在于每次都要计算,因此2.0决定开启定时任务,定时获取在线token数,将结果存储起来。
在线用户这种频繁变动的数据不应该存到数据库,所以决定放入缓存。
满足1,2,对于条件3,时间误差取决于定时任务的频率,不过也勉强够用了。
v3.0
引入 session 监听器监听 session 的创建和销毁,每次动态维护在线人数。
创建session时,人数+1
销毁session时,人数-1
客户端请求人数时直接返回缓存中的值
同时满足123,数据几乎实时(可能存在误差),对服务端的影响微乎其微(优于2.0)
无法监听到 session 没有正常销毁的情况
session的销毁方法,在 session过期 和 应用正常关闭 的时候会触发。
而应用非正常关闭(强制关闭或强制重启)时,已经保存的session虽然被释放了,但是没有触发销毁方法,这部分在线数量释放不掉
因此在每次启动重启应用的时候把在线用户重置为0,只要用户还在继续访问就可以很快恢复,如果在启动时统计一遍的话,会导致用户被重复统计。
因为,应用重启后,用户再次访问应用,会生成新的session。
session 共享问题
同时,session 还存在共享问题,可以引入redis,但监听redis key 的过期同样也是成本比较大的,因此没有实现。
值是否会出现负数?
目前为止能想到的还没有会出现负数的场景,可能会有没考虑到的情况,这个还需要继续思考。
误判
上述所有的方案都是在服务段判定的离线。
也就是说,会有误判:用户在网页上,但是没有任何点击动作,没有触发任何请求,虽然登录态还在,但是被认为是下线(session过期)
同时,为什么不适合直接使用在线token做统计?
我认为因为时间太久了,维护为24小时,误差太大,如果登陆态的时间也比较短的话,其实可以用来统计。
github地址(点击即可)
登录接口
@RestController @Validated public class LoginController { @Autowired private UserService userService; @Resource private RedisTemplate redisTemplate; @PostMapping("/login") public ResultVO login(@RequestBody @Valid LoginForm loginForm, BindingResult bindingResult, HttpServletRequest request, HttpServletResponse response) { HttpSession session = request.getSession(); if (bindingResult.hasErrors()) { return ResultVOUtil.fail(100, "登录参数不正确"); } User user = userService.getOneByAccount(loginForm.getLoginAccount()); if (user == null) { return ResultVOUtil.fail(101, "账号不存在"); } // 根据登录密码生成加密密码 String entryptPassword = PasswordUtil.getPBKDF2(loginForm.getLoginPassword(), user.getUserSalt()); if (!user.getUserPassword().equals(entryptPassword)) { return ResultVOUtil.fail(102, "密码错误"); } // 取得会话的sessionId String sessionId = session.getId(); // 根据账号生成accountId String accountId = AccountIdUtil.createAccountId(loginForm.getLoginAccount()); // 将accountId 与 sessionId 绑定 redisTemplate.opsForList().remove(accountId, 0, 0); redisTemplate.opsForList().rightPushAll(accountId, sessionId, System.currentTimeMillis()); redisTemplate.expire(accountId, 60*60*24, TimeUnit.SECONDS); Cookie cookie = new Cookie("accountId",accountId); response.addCookie(cookie); return ResultVOUtil.success("登录"); } @GetMapping("/isLogin") public ResultVO isLogin(HttpServletRequest request) { if (getCookie(request, "accountId") != null) { Cookie cookie = getCookie(request, "accountId"); String accountId = cookie.getValue(); String sessionId = (String) redisTemplate.opsForList().index(accountId, 0); if (sessionId == null) { return ResultVOUtil.fail(430, "请重新登录"); } if (sessionId.equals(request.getSession().getId())) { return ResultVOUtil.success(); } } return ResultVOUtil.fail(430, "请重新登录"); } @GetMapping("/logout") public ResultVO logout(HttpServletRequest request) { if (isLogin(request).getCode() != 0) { return ResultVOUtil.fail(431, "请先登录"); } String accountId = getCookie(request, "accountId").getValue(); redisTemplate.opsForList().remove(accountId, 0, 0); return ResultVOUtil.success("退出登录成功"); } private Cookie getCookie(HttpServletRequest request, String target) { if (request.getCookies() != null) { for (Cookie cookie : request.getCookies()) { if (cookie.getName().equals(target)) { return cookie; } } } return null; } }
网站人数统计接口
@RestController public class StatisticsController { @Resource private RedisTemplate redisTemplate; @GetMapping("/getUserCount") public ResultVO getUserCount() { UserCountVO userCountVO = new UserCountVO(); Set keys = redisTemplate.keys("*"); int loginCount = 0, realtimeCount = 0; for (String key : keys) { if (redisTemplate.type(key) == DataType.LIST) { loginCount ++; long oldTime = (long) redisTemplate.opsForList().index(key, 1); if (System.currentTimeMillis() - oldTime < 10000) { realtimeCount ++; } } } userCountVO.setLoginCount(loginCount); userCountVO.setRealtimeCount(realtimeCount); return ResultVOUtil.success(userCountVO); } }
返回给前端的视图
@Data public class UserCountVO { // 登录人数 private Integer loginCount; // 实时在线人数 private Integer realtimeCount; }
统计网站人数只能放在服务端进行,因为数据在服务端,但是因为数据在缓存中,查询速度应该较快。统计实时在线人数有些误差。对服务器的性能应该没多大影响,前端的定时器也区分了实时用户(在浏览,打字)和真正离开的用户,节省了一些资源。可能没考虑到并发和分布式。并发和分布式还在学,知道一些概念。 GitHub链接:https://github.com/shuaishuaixi03/FromZerotoExpert/tree/dev
我的实现方式如下: 1.思路分析(自己想的,如有错误,请大家直言相告) 首先,我们要想在前端页面动态的更新在线人数,仅靠前端页面是一定完不成的,我们只有访问服务器,才能知道哪些人在线,哪些人不在线。而用户是否在线,也是一个动态的过程,他可能上一秒在线,下一秒就不在线了。因此想要获取在线人数,我们就一定要周期性的访问服务器,查询哪些用户在线,这样才能实时性的将人数显示出来。根据这个思路,周期性访问服务器是不可避免的了,因此最好将用户在线的信息存储到redis缓存中,尽可能的来减少大量用户周期性访问所带来的压力。 2.redis存储的数据结构 在这里我们使用zset结构,键为onlineUser,值为用户名称,score为当前操作时间的时间毫秒值,在用户第一次登录时的登录接口中就把一份该用户的信息存储在zset集合中,表明该用户在此刻的时间毫秒值下为登录在线状态。 3.js的定时任务 用户在主页面时,设置js的定时任务,通过鼠标移动检测事件监测用户是否操作过,并每隔一段时间定时地更新在线信息,若用户未关闭页面且在页面中操作过(鼠标移动过)则更新zset集合中的时间毫秒值(若用户是离线状态下操作鼠标,则更新zset用户信息变为添加zset用户信息),若用户未关闭页面且长时间未操作(这里设置60秒未移动过鼠标则改变在线状态以便测试)则通过ajax的post异步提交,在quit接口中将zset中的用户名删除,使集合中在线的用户数减一,当用户不在此页面时,定时任务无法触发,zset集合中的score信息无法更新,因此我们在判断用户个数的时候,只需要在zset查询时设定下边界currentTime(当前时间毫秒值)-timeOut(超时时间毫秒值)即可,因为用户关闭页面了,redis数据库中的该用户的score是不会继续更新的,但currentTime(当前时间毫秒值)是一直增加的,所以用户关闭页面redis不删除该用户信息也能知道此用户因长时间未操作而不在线。
// 长时间未操作自动退出系统 var lastTime = new Date().getTime(); var currentTime = new Date().getTime(); var timeOut = 60 * 1000; //设置离线超时时间: 60秒 var check_time = 3 * 1000; //定时任务的时间间隔:3秒一次 function autoQuit(){ $(function(){ /* 鼠标移动事件 */ $(document).mouseover(function(){ lastTime = new Date().getTime(); //更新操作时间 }); }); /* 定时器 * 间隔3秒检测是否长时间未操作页面 */ scheduledTasks(); } function scheduledTasks(){ currentTime = new Date().getTime(); //更新当前时间 if(currentTime - lastTime > timeOut){ //判断是否超时. //页面未关闭时,检测到超时改为离线 $.post("/FromZerotoExpert/main/quit", {}, function (data){ document.getElementById("current_status").innerHTML = "离线"; }) }else{ //页面未关闭时,检测不超时,更新时间戳 $.post("/FromZerotoExpert/main/updateOnlineTime", {"time":currentTime}, function (data){ }) } //页面未关闭时,定时更新在线人数 $.post("/FromZerotoExpert/main/getOnlineNums", {"time":currentTime}, function (data){ document.getElementById("current_online_numbers").innerHTML = data.message; }) } // 长时间未操作退出系统 end
4.接口中的方法 具体前端js调用的接口如下:
@PostMapping("/quit") @ResponseBody public RegisterResult quit(HttpServletRequest request, HttpServletResponse response){ String username = null; try { username = (String) request.getSession().getAttribute("username"); if(null != username && !"".equals(username)){ stringRedisTemplate.opsForZSet().remove("onlineUser", username); return new RegisterResult(true); } }catch (Exception e){ e.printStackTrace(); } return new RegisterResult(false); } @PostMapping("/getOnlineNums") @ResponseBody public RegisterResult getOnlineNums(long time,HttpServletRequest request, HttpServletResponse response){ String username = null; try { username = (String) request.getSession().getAttribute("username"); if(null != username && !"".equals(username)){ Long nums = stringRedisTemplate.opsForZSet().count("onlineUser", (double) (time - 60 * 1000), (double) (time + 60 * 1000)); return new RegisterResult(true, nums + "人"); } }catch (Exception e){ e.printStackTrace(); } return new RegisterResult(false, "加载中..."); } @PostMapping("/updateOnlineTime") @ResponseBody public RegisterResult updateOnlineTime(long time,HttpServletRequest request, HttpServletResponse response){ String username = null; try { username = (String) request.getSession().getAttribute("username"); if(null != username && !"".equals(username)){ stringRedisTemplate.opsForZSet().add("onlineUser", username,(double) time); return new RegisterResult(true); } }catch (Exception e){ e.printStackTrace(); } return new RegisterResult(false); }
该实现方法的优点: 优点是利用了前端页面的鼠标检测事件更新用户状态,减小了一部分服务器的压力,但服务器还是要被周期性的访问,更新用户在线状态信息。 该实现方法的缺点: 服务器压力有点大,在这里我的定时任务设置的是3秒执行一次(一部分原因是为了方便调试把未操作离线的时间改成了60秒,所以定时任务的时间也相应减小到3秒),所以当大量用户同时在线时,服务器的压力会相当大,解决方法就是把定时任务的时间加长,但这样的话就会出现有些用户下线了,但服务器还没有响应到的现象,并且不能及时更新在线人数。所以显示人数的准确性和服务器的压力这两个方面只能选一个,相当于是零和博弈吧。 第二个缺点就是,因为我的检测用户是否在线的方法是利用的鼠标移动检测事件,所以本方法好像只能适用于PC端,手机端我还没有测试过,不知道触屏算不算鼠标移动。。。。
在这里提供几个用户名和密码,欢迎大家也来帮我测一测有没有什么bug。 当登陆后,连续累积时间60s不移动鼠标,你的登录状态变为离线。 用户名:aaa,密码123456a 用户名:bbb,密码123456a 用户名:ccc,密码123456a 用户名:ddd,密码123456a 用户名:eee,密码123456a 用户名:111,密码123456a 用户名:222,密码123456a 用户名:333,密码123456a 用户名:444,密码123456a 用户名:555,密码123456a
gitHub地址:https://github.com/Second6s9/FromZerotoExpert/tree/dev gitHub防覆盖地址:https://github.com/Second6s9/FromZerotoExpert/tree/day11 服务器地址:http://123.57.142.33:8088/FromZerotoExpert/ 登录地址:http://123.57.142.33:8088/FromZerotoExpert/login
注1:服务器昨晚好像是被攻击了,把我mysql的库给删掉了,之前注册的用户信息都丢失了,要想登录原来的账号就重新注册一次吧。。。 注2:参考秊同学的设计,更新了在线用户的实时显示。
中间遇到了点困难: 由于我原先的程序只在 UserService 中设置了一个 jedis 静态成员来访问 redis,哪怕只有一个用户登录时:若某一时刻(发送心跳信息的时间与显示在线人数时间的最小公倍数)就会造成 jedis 访问出问题,这是我第一次实践中遇到并发问题。解决方法是改用 jedis连接池,每次用到 jedis 时从池中获取一个 jedis 实例,用完归还就彳亍。
服务器链接:http://124.223.184.187:8088/FromZerotoExpert Github仓库链接:https://github.com/jiangyklala/FromZerotoExpert/tree/dev
ps: 刚发现任务10 和 任务11下的服务器连接给错了,已修改 jedis连接池一直出现问题,我把池的容量提高到最大连接数500,三四个人同时访问应该没问题了
共以下 6 个回答
我采用session监听器来统计网站在线人数,每次创建session时就会自动加一,销毁session时就会自动减一;
先初始化一个类实现HttpSessionlistener,然后定义count,创建session,count+1,销毁session,count-1;然后在类上加注解webListener,启动类上再加@ServletComponentScan(“com.example.demo.listener”)扫描包才有用。
优点是:
统计在线人数结果相对准确;
缺点是:
当用户关闭浏览器时并不会触发session监听,当下一次登录时仍然会让count加一
或者在session过期时,session监听并不能做一个实时的响应去将在线数减一
当用户再次登陆,由于cookie中含有的session_id不同而导致session监听器记录下session创建,而使count加一。
对服务器性能影响较大,用户每次访问网站时,服务端都会创建一个session,并将该session与用户关联起来,这样会增加服务器的负担,特别是在高并发的时候,导致服务器压力过大
https://github.com/CurryisCoat/community/tree/dev
之前在用户不能重复登陆处,在redis里设置了userName-session的键值对,5分钟后过期。这次根据帅地上节打卡处的留言,将键值对改为userId-session来防止中文用户名出现的乱码问题。
方法一 通过keys模糊查询
在讲前文提到的键值对加入统一前缀“userId:”然后通过keys(“userId:*”)直接匹配
这种方法在用户量较少的时候没有很大的问题,实现也简单方便。但是用户量增大后,keys是阻塞式查询,效率较低,会导致系统响应时间长用户体验差的情况
方法二 使用Sorted set
大体思路和其他同学相似,score处用时间戳,统计一定时间内有操作的用户视为在线用户。
与其他同学不同的是,我没有采用定时任务的方式。每次统计在线用户后,将这个数存在redis中,有效期1分钟(也就是在线用户数1分钟更新一次),每次需要请求在线用户数时,先检查有无之前保存的在线用户数,如果有的话直接使用,如果没有的话就统计并保存。
我个人理解这种方法的优点在于,只有有需求时才统计在线用户数,而且对在线用户数做缓存也不用每次访问都统计。
此外,关于在线用户数刷新间隔的问题,我觉得用户对这一需求并不敏感也不需要很强的准确性,没必要更新的过分频繁反而增加了服务器压力影响用户体验。
以上是我自己的实现方式以及思考,如有不足之处,请大佬们和帅地哥指正。
GitHub地址:https://github.com/lei342345795/FromZerotoExpert
3.1 统计网站用户在线人数
本次需求有三个要求:
v1.0
维护登录态过程中已经维护了用户的在线token,且唯一。
因此一开始是打算在前端定时轮询获取最新的在线人数,后端收到请求则重新统计一遍在线token的数量。
优缺点
从后面的所有迭代方案来看,这个方法总能获取到最新的数据,满足了1.3
但服务端负荷大,用户量稍多一点就会有大量的请求打到服务端,而服务端每次都要重新计算。
v2.0
1.0主要的缺点在于每次都要计算,因此2.0决定开启定时任务,定时获取在线token数,将结果存储起来。
在线用户这种频繁变动的数据不应该存到数据库,所以决定放入缓存。
优缺点
满足1,2,对于条件3,时间误差取决于定时任务的频率,不过也勉强够用了。
v3.0
引入 session 监听器监听 session 的创建和销毁,每次动态维护在线人数。
创建session时,人数+1
销毁session时,人数-1
客户端请求人数时直接返回缓存中的值
优缺点
同时满足123,数据几乎实时(可能存在误差),对服务端的影响微乎其微(优于2.0)
session的销毁方法,在 session过期 和 应用正常关闭 的时候会触发。
而应用非正常关闭(强制关闭或强制重启)时,已经保存的session虽然被释放了,但是没有触发销毁方法,这部分在线数量释放不掉
因此在每次启动重启应用的时候把在线用户重置为0,只要用户还在继续访问就可以很快恢复,如果在启动时统计一遍的话,会导致用户被重复统计。
因为,应用重启后,用户再次访问应用,会生成新的session。
同时,session 还存在共享问题,可以引入redis,但监听redis key 的过期同样也是成本比较大的,因此没有实现。
目前为止能想到的还没有会出现负数的场景,可能会有没考虑到的情况,这个还需要继续思考。
上述所有的方案都是在服务段判定的离线。
也就是说,会有误判:用户在网页上,但是没有任何点击动作,没有触发任何请求,虽然登录态还在,但是被认为是下线(session过期)
同时,为什么不适合直接使用在线token做统计?
我认为因为时间太久了,维护为24小时,误差太大,如果登陆态的时间也比较短的话,其实可以用来统计。
github地址(点击即可)
登录接口
网站人数统计接口
返回给前端的视图
统计网站人数只能放在服务端进行,因为数据在服务端,但是因为数据在缓存中,查询速度应该较快。统计实时在线人数有些误差。对服务器的性能应该没多大影响,前端的定时器也区分了实时用户(在浏览,打字)和真正离开的用户,节省了一些资源。可能没考虑到并发和分布式。并发和分布式还在学,知道一些概念。
GitHub链接:https://github.com/shuaishuaixi03/FromZerotoExpert/tree/dev
我的实现方式如下:
1.思路分析(自己想的,如有错误,请大家直言相告)
首先,我们要想在前端页面动态的更新在线人数,仅靠前端页面是一定完不成的,我们只有访问服务器,才能知道哪些人在线,哪些人不在线。而用户是否在线,也是一个动态的过程,他可能上一秒在线,下一秒就不在线了。因此想要获取在线人数,我们就一定要周期性的访问服务器,查询哪些用户在线,这样才能实时性的将人数显示出来。根据这个思路,周期性访问服务器是不可避免的了,因此最好将用户在线的信息存储到redis缓存中,尽可能的来减少大量用户周期性访问所带来的压力。
2.redis存储的数据结构
在这里我们使用zset结构,键为onlineUser,值为用户名称,score为当前操作时间的时间毫秒值,在用户第一次登录时的登录接口中就把一份该用户的信息存储在zset集合中,表明该用户在此刻的时间毫秒值下为登录在线状态。
3.js的定时任务
用户在主页面时,设置js的定时任务,通过鼠标移动检测事件监测用户是否操作过,并每隔一段时间定时地更新在线信息,若用户未关闭页面且在页面中操作过(鼠标移动过)则更新zset集合中的时间毫秒值(若用户是离线状态下操作鼠标,则更新zset用户信息变为添加zset用户信息),若用户未关闭页面且长时间未操作(这里设置60秒未移动过鼠标则改变在线状态以便测试)则通过ajax的post异步提交,在quit接口中将zset中的用户名删除,使集合中在线的用户数减一,当用户不在此页面时,定时任务无法触发,zset集合中的score信息无法更新,因此我们在判断用户个数的时候,只需要在zset查询时设定下边界currentTime(当前时间毫秒值)-timeOut(超时时间毫秒值)即可,因为用户关闭页面了,redis数据库中的该用户的score是不会继续更新的,但currentTime(当前时间毫秒值)是一直增加的,所以用户关闭页面redis不删除该用户信息也能知道此用户因长时间未操作而不在线。
4.接口中的方法
具体前端js调用的接口如下:
该实现方法的优点:
优点是利用了前端页面的鼠标检测事件更新用户状态,减小了一部分服务器的压力,但服务器还是要被周期性的访问,更新用户在线状态信息。
该实现方法的缺点:
服务器压力有点大,在这里我的定时任务设置的是3秒执行一次(一部分原因是为了方便调试把未操作离线的时间改成了60秒,所以定时任务的时间也相应减小到3秒),所以当大量用户同时在线时,服务器的压力会相当大,解决方法就是把定时任务的时间加长,但这样的话就会出现有些用户下线了,但服务器还没有响应到的现象,并且不能及时更新在线人数。所以显示人数的准确性和服务器的压力这两个方面只能选一个,相当于是零和博弈吧。
第二个缺点就是,因为我的检测用户是否在线的方法是利用的鼠标移动检测事件,所以本方法好像只能适用于PC端,手机端我还没有测试过,不知道触屏算不算鼠标移动。。。。
在这里提供几个用户名和密码,欢迎大家也来帮我测一测有没有什么bug。
当登陆后,连续累积时间60s不移动鼠标,你的登录状态变为离线。
用户名:aaa,密码123456a
用户名:bbb,密码123456a
用户名:ccc,密码123456a
用户名:ddd,密码123456a
用户名:eee,密码123456a
用户名:111,密码123456a
用户名:222,密码123456a
用户名:333,密码123456a
用户名:444,密码123456a
用户名:555,密码123456a
gitHub地址:https://github.com/Second6s9/FromZerotoExpert/tree/dev
gitHub防覆盖地址:https://github.com/Second6s9/FromZerotoExpert/tree/day11
服务器地址:http://123.57.142.33:8088/FromZerotoExpert/
登录地址:http://123.57.142.33:8088/FromZerotoExpert/login
注1:服务器昨晚好像是被攻击了,把我mysql的库给删掉了,之前注册的用户信息都丢失了,要想登录原来的账号就重新注册一次吧。。。
注2:参考秊同学的设计,更新了在线用户的实时显示。
中间遇到了点困难: 由于我原先的程序只在 UserService 中设置了一个 jedis 静态成员来访问 redis,哪怕只有一个用户登录时:若某一时刻(发送心跳信息的时间与显示在线人数时间的最小公倍数)就会造成 jedis 访问出问题,这是我第一次实践中遇到并发问题。解决方法是改用 jedis连接池,每次用到 jedis 时从池中获取一个 jedis 实例,用完归还就彳亍。
缺点:用户频繁发出心跳信息会对服务器带来压力,而“当前在线人数显示”可以设置为一个按钮,用户点击时才显示,减轻服务器压力
账号:123456 密码:123asd
账号:1234567 密码:123asd
账号:12345678 密码:123asd
服务器链接:http://124.223.184.187:8088/FromZerotoExpert
Github仓库链接:https://github.com/jiangyklala/FromZerotoExpert/tree/dev
ps: 刚发现任务10 和 任务11下的服务器连接给错了,已修改
jedis连接池一直出现问题,我把池的容量提高到最大连接数500,三四个人同时访问应该没问题了