使用监听器实现在线客户统计
统计连接在应用上的客户端数量。客户端的唯一标识就是 IP,只需要对连接到服务器上
的 IP 数量进行统计,就可统计出客户端的数量。很多人都是只监听了session,即当session创建时,在线客户端的数量就+1,session销毁时,在线客户端的数量就-1,这种统计的方法不正确,因为用户在同一台机器上打开两个不同的浏览器访问系统时,系统会创建两个session对象,如果按照上面方式统计的话当前在线客户有两个,而实际上只有一个,因此需要通过客户端的ip地址来判断的方式更加准确。
这是分析图 已经很明确了,但是要更明确 应该懂得 Session 的 生存 和 销毁。
我们代码思路大体这样写:
主要思路是 用 Ip【一个用户】 来装Session【不同浏览器】,Map容器【Map不允许重复 且 映射型容器】
1.首先注册 ServletContext的监听器,因为服务器启动ServletContext才会生成,且 Servlet 只有一个,所以它创建时,我们就初始化容器【装Session】,将容器放入 ServletContext。
2. 【这里的List 是对应 一个用户【IP】的! 别混淆!】 监听Request 的创建,然后我们获取到他的 IP,顺便将他的IP放入Session中【Session销毁用的到】然后根据IP获取到它的List【Session容器】,判断一下List是不是为null,如果是null的话 证明第一次访问【无装载记录】 ,第一次访问我们将他的List【Session容器】 赋予地址。
其次我们获取到他 当前的Session【第一次访问Session就会自动生成】,然后遍历他的 list ,如果 当前Session 和 list中 任意一个 的Session 相同,证明当前用户还在会话【没掉线】,那就Return处理,
如果 遍历 list 都没找到当前Session 证明 当前 Session 是 最新添加的,我们将其放入 list 中,然后我们更新Map中的List,再更新Servlet中的Map。 【 这里的更新分别指的是替换【Map 替换】 和 【ServletContext 的 Attribute 替换】 】。
3.监听Session的销毁
Session销毁【失效】的默认时间是30 分钟,你要自己去设置他的超时时间,优先级也是范围小 到 范围大的! 这里推荐点击注销的时候 用 java 代码直接 杀掉 Session。
当Session销毁时,证明用户的浏览器掉线了,所以我们获取到被销毁的Session对象,然后根据 IP 【Request监听里面已经设置】 获取到List,然后把这个失效的Session对象 从 List中移除,
然后我们判断list是不是为空,size = 0? 如果size 是 0 的话 我们直接把 ip 从 Map 中 移除, 证明整个用户直接掉线了。 最后记得要更新! 【这里的更新分别指的是替换【Map 替换】 和 【ServletContext 的 Attribute 替换】 】。
4.监听记得在XML配置中加配置
代码:
MyServletContextListener.java:
package onlineNumber; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.http.HttpSession; /** * 监听ServletContext * @author Bi-Hu * */ public class MyServletContextListener implements ServletContextListener { //当ServletContext 创建时 我们做: @Override public void contextInitialized(ServletContextEvent sce) { //首先新建IP容器Map Map<String, List<HttpSession>> ipMap = new HashMap<>(); //然后我们 获取Context -> 把ip容器放入Map ServletContext sc = sce.getServletContext(); sc.setAttribute("ipMap", ipMap); } }
MyRequestListener.java
package onlineNumber; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; /** * 监视Request 当Request创建时 我们根据IP判断是不是新访问,如果是新建一个List给他装Session 然后更新到Map【关键】 不是的话 直接Return * @author Bi-Hu * */ public class MyRequestListener implements ServletRequestListener { @Override public void requestInitialized(ServletRequestEvent sre) { //获取Request HttpServletRequest request = (HttpServletRequest) sre.getServletRequest(); //获取ipMap ServletContext sc = request.getServletContext(); Map<String, List<HttpSession>> ipMap = (Map<String, List<HttpSession>>)sc.getAttribute("ipMap"); //获取ip 然后根据ip找list 看下是否第一次登录 或 重新登录 String Ip = request.getRemoteAddr(); System.out.println(Ip); List<HttpSession> list = ipMap.get(Ip); if(list == null) { list = new ArrayList<HttpSession>(); } //获取当前Session【你获取了 不存在会新建】,然后判断list中有没有你这个Session ,如果没有代表你是新Session 如果存在就结束 HttpSession CurrentSession = request.getSession(); for(HttpSession s : list) { if(s == CurrentSession) { //如果重复 代表你还在会话中 return; } } //如果不存在 就执行到这里了 代表你是新建的Session 那么就会进行更新:【这一步很重要!!】 //把新建的Session放入list list.add(CurrentSession); ipMap.put(Ip, list); //更新list sc.setAttribute("ipMap", ipMap); //更新ipMap //顺便把当前Ip放入 Session中 CurrentSession.setAttribute("ip", Ip); //销毁Session时用的到 System.out.println("Ip为:"+ Ip +"中的 Session 个数: " + ipMap.get(Ip).size() + "个"); } }
MySessionListener.java
package onlineNumber; import java.util.List; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import javax.websocket.Session; /** * 监视Session销毁时 你销毁 我就把你删除 ,然后判断list是否为空 为空的话 用户就离线了 所以我们把List给Kill掉 * @author Bi-Hu * */ public class MySessionListener implements HttpSessionListener { @Override public void sessionDestroyed(HttpSessionEvent se) { //先获取到他的Session HttpSession CurrentSession = se.getSession(); ServletContext sc = CurrentSession.getServletContext(); String CurrentIp = (String) CurrentSession.getAttribute("ip"); //然后我们根据IP找到他的 List 然后在List中移除这个Session【销毁的】 Map<String, List<HttpSession>> ipMap = (Map<String, List<HttpSession>>) sc.getAttribute("ipMap"); List<HttpSession> list = ipMap.get(CurrentIp); list.remove(CurrentSession); System.out.println("ID 为" + se.getSession().getId() + " 的 Session 销毁了"); System.out.println("Ip为:"+ CurrentIp +"中的 Session 个数: " + ipMap.get(CurrentIp).size() + "个"); //然后我们判断list还有么有元素 没有的话 证明用户离线了 那我们把它的Id 删除 然后更新 就收工 if(list.size() == 0) { ipMap.remove(CurrentIp); }else { ipMap.put(CurrentIp, list); //更新List } sc.setAttribute(CurrentIp, ipMap); //总更新【重要】 System.out.println("还剩在线用户:" + ipMap.size()); } }
我在这里设置了 1分钟Session 失效 这样就很快看的出 在线 / 离线 的对比了
电脑的火狐 会用 127.0.0.1 谷歌 和 其他浏览器均是0.0.0.0.1
所以会有两个用户 Session 的对比也能看得出来 目标完成。
成功图:
谢谢。