使用监听器实现在线客户统计
统计连接在应用上的客户端数量。客户端的唯一标识就是 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 的对比也能看得出来 目标完成。
成功图:
谢谢。