统计网站人数
- 思路:
- 统计用户登录人数和实时在线人数:之前实现用户登录和单点登录,我是用来redis的string类型,键为账号标识accountId,值为会话标识sessionId,对于统计用户登录人数,这个比较简单,直接统计redis库中有多少条string类型记录,就有多少个账号标识,就有多少个会话标识,就有多少个登录用户。但无法统计用户实时在线人数,比如用户登录网站了,然后走了,留下一台电脑或者用电脑干其它事了(比如你听个网课,老师知道你在课上,不知道你在不在屏幕前),这时用户就不是实时在线网站。为了统计用户在线人数,我们把string类型改为list类型,键为accountId,值为sessionId、最后会话的一次发送请求时间。然后我们统计实时在线人数只需比较当前时间与会话最后一次发送请求的时间的差值是否小于一个指定值(我设为10s),如果小于,说明用户实时在线,否则不在线。(打个比方,上网课时,你和老师在积极互动,老师就知道你肯定在屏幕前)。但是我从网站上只请求了一个静态资源(比如一篇很长的文章),然后浏览,浏览半个小时,但是网站会判定我不在线,这时可以在前端设置一个定时器,当用户鼠标或键盘有操作时,就向网站发送一个请求,告诉用户还在线,同时重置定时器。当用户长时间没有操作时,定时器时间用完了,就向服务器发送一个退出登录请求。
- 实现代码(前端定时器没有实现)
登录接口
@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<String> 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;
}