剖析

我们在网站中一样平常利用Session来标识某用户是否上岸了,如果上岸了,就在Session域中保存相对应的属性。
如果没有上岸,那么Session的属性就该当为空。

现在,我们想要统计的是网站的在线人数。
我们该当这样做:我们监听是否有新的Session创建了,如果新创建了Sesssion,那么在线人数就该当+1。
这个在线人数是全体站点的,以是该当有Context工具保存。

jsp监听器统计登录人数监听器运用统计人数自界说扫描器踢人小案例修订版 Docker

大致思路:

监听Session是否被创建了如果Session被创建了,那么在Context的域工具的值就该当+1如果Session从内存中移除了,那么在Context的域工具的值就该当-1.

代码

监听器代码:

public class CountOnline implements HttpSessionListener { public void sessionCreated(HttpSessionEvent se) { //获取得到Context工具,利用Context域工具保存用户在线的个数 ServletContext context = se.getSession().getServletContext(); //直接判断Context工具是否存在这个域,如果存在就人数+1,如果不存在,那么就将属性设置到Context域中 Integer num = (Integer) context.getAttribute(\"大众num\"大众); if (num == null) { context.setAttribute(\公众num\公众, 1); } else { num++; context.setAttribute(\"大众num\"大众, num); } } public void sessionDestroyed(HttpSessionEvent se) { ServletContext context = se.getSession().getServletContext(); Integer num = (Integer) se.getSession().getAttribute(\"大众num\"大众); if (num == null) { context.setAttribute(\"大众num\公众, 1); } else { num--; context.setAttribute(\"大众num\"大众, num); } }}显示页面代码:

在线人数:${num}

测试

我们每利用一个浏览器访问做事器,都会新创建一个Session。
那么网站的在线人数就会+1。

利用同一个页面刷新,还是利用的是那个Sesssion,以是网站的在线人数是不会变的。

自定义Session扫描器

我们都知道Session是保存在内存中的,如果Session过多,做事器的压力就会非常大。

但是呢,Session的默认失落效韶光是30分钟(30分钟没人用才会失落效),这造成Seesion可能会过多(没人用也存在内存中,这不是明显摧残浪费蹂躏吗?)

当然啦,我们可以在web.xml文件中配置Session的生命周期。
但是呢,这是由做事器来做的,我嫌它的韶光不足准确。
(有时候我配置了3分钟,它用4分钟才帮我移除掉Session)

以是,我决定自己用程序手工移除那些永劫光没人用的Session。

剖析

要想移除永劫光没人用的Session,肯定要先拿到全部的Session啦。
以是我们利用一个容器来装载站点所有的Session。

只要Sesssion一创建了,就把Session添加到容器里边。
毫无疑问的,我们须要监听Session了。

接着,我们要做的便是隔一段韶光就去扫描一下全部Session,如果有Session永劫光没利用了,我们就把它从内存中移除。
隔一段韶光去做某事,这肯定是定时器的任务呀。

定时器该当在做事器一启动的时候,就该当被创建了。
因此还须要监听Context

末了,我们还要考虑到并发的问题,如果有人同时访问站点,那么监听Session创建的方法就会被并发访问了。
定时器扫描容器的时候,可能是获取不到所有的Session的。

这须要我们做同步

于是乎,我们已经有大致的思路了

监听Session和Context的创建利用一个容器来装载Session定时去扫描Session,如果它永劫光没有利用到了,就把该Session从内存中移除。
并发访问的问题

代码

监听器代码:

public class Listener1 implements ServletContextListener, HttpSessionListener { //做事器一启动,就该当创建容器。
我们利用的是LinkList(涉及到增删)。
容器也该当是线程安全的。
List<HttpSession> list = Collections.synchronizedList(new LinkedList<HttpSession>()); //定义一把锁(Session添加到容器和扫描容器这两个操作该当同步起来) private Object lock = 1; public void contextInitialized(ServletContextEvent sce) { Timer timer = new Timer(); //实行我想要的任务,0秒延时,每10秒实行一次 timer.schedule(new MyTask(list, lock), 0, 10 1000); } public void sessionCreated(HttpSessionEvent se) { //只要Session一创建了,就该当添加到容器中 synchronized (lock) { list.add(se.getSession()); } System.out.println(\"大众Session被创建啦\"大众); } public void sessionDestroyed(HttpSessionEvent se) { System.out.println(\公众Session被销毁啦。
\"大众); } public void contextDestroyed(ServletContextEvent sce) { }}任务代码:

/ 在任务中该当扫描容器,容器在监听器上,只能通报进来了。
要想得到在监听器上的锁,也只能是通报进来 /class MyTask extends TimerTask { private List<HttpSession> sessions; private Object lock; public MyTask(List<HttpSession> sessions, Object lock) { this.sessions = sessions; this.lock = lock; } @Override public void run() { synchronized (lock) { //遍历容器 for (HttpSession session : sessions) { //只要15秒没人利用,我就移除它啦 if (System.currentTimeMillis() - session.getLastAccessedTime() > (1000 15)) { session.invalidate(); sessions.remove(session); } } } }}测试:

15秒如果Session没有生动,那么就被删除!

利用凑集来装载我们所有的Session利用定时器来扫描session的声明周期【由于定时器没有session,我们传进去就好了】关于并发访问的问题,我们在扫描和检测session添加的时候,同步起来就好了【当然,定时器的锁也是要表面通报进来的】踢人小案列

列出所有的在线用户,后台管理者拥有踢人的权利,点击踢人的超链接,该用户就被注销了。

剖析

首先,怎么能列出所有的在线用户呢??一样平常我们在线用户都是用Session来标记的,所有的在线用户就该当用一个容器来装载所有的Session。

我们监听Session的是否有属性添加(监听Session的属性有添加、修正、删除三个方法。
如果监听到Session添加了,那么这个肯定是个在线用户!
)。

装载Session的容器该当是在Context里边的【属于全站点】,并且容器该当利用Map凑集【待会还要通过用户的名字来把用户踢了】

思路:

写监听器,监听是否有属性添加在Session里边了。
写大略的上岸页面。
列出所有的在线用户实现踢人功能(也便是摧毁Session)

代码

监听器

public class KickPerson implements HttpSessionAttributeListener { // Public constructor is required by servlet spec public KickPerson() { } public void attributeAdded(HttpSessionBindingEvent sbe) { //得到context工具,看看context工具是否有容器装载Session ServletContext context = sbe.getSession().getServletContext(); //如果没有,就创建一个呗 Map map = (Map) context.getAttribute(\"大众map\"大众); if (map == null) { map = new HashMap(); context.setAttribute(\"大众map\公众, map); } //--------------------------------------------------------------------------------------- //得到Session属性的值 Object o = sbe.getValue(); //判断属性的内容是否是User工具 if (o instanceof User) { User user = (User) o; map.put(user.getUsername(), sbe.getSession()); } } public void attributeRemoved(HttpSessionBindingEvent sbe) { / This method is called when an attribute is removed from a session. / } public void attributeReplaced(HttpSessionBindingEvent sbe) { / This method is invoked when an attibute is replaced in a session. / }}上岸页面

<form action=\"大众${pageContext.request.contextPath }/LoginServlet\公众 method=\"大众post\"大众> 用户名:<input type=\"大众text\"大众 name=\"大众username\公众> <input type=\"大众submit\公众 value=\公众上岸\公众></form>处理上岸Servlet

//得到通报过来的数据String username = request.getParameter(\"大众username\"大众);User user = new User();user.setUsername(username);//标记该用户上岸了!
request.getSession().setAttribute(\"大众user\"大众, user);//供应界面,见告用户上岸是否成功request.setAttribute(\"大众message\"大众, \"大众恭喜你,上岸成功了!
\"大众);request.getRequestDispatcher(\"大众/message.jsp\"大众).forward(request, response);列出在线用户

<c:forEach items=\"大众${map}\公众 var=\公众me\"大众> ${me.key} <a href=\"大众${pageContext.request.contextPath}/KickPersonServlet?username=${me.key}\"大众>踢了他吧</a> <br></c:forEach>处理踢人的Servlet

String username = request.getParameter(\"大众username\"大众);//得到装载所有的Session的容器Map map = (Map) this.getServletContext().getAttribute(\"大众map\"大众);//通过名字得到SessionHttpSession httpSession = (HttpSession) map.get(username);httpSession.invalidate();map.remove(username);//摧毁完Session后,返回列出在线用户页面request.getRequestDispatcher(\"大众/listUser.jsp\"大众).forward(request, response);

测试

利用多个浏览器上岸来仿照在线用户(同一个浏览器利用的都是同一个Session)

监听Seesion的创建和监听Session属性的变革有啥差异???

Session的创建只代表着浏览器给做事器发送了要求。
会话建立Session属性的变革就不一样了,登记的是详细用户是否做了某事(上岸、购买了某商品)

文章来源:https://dwz.cn/kQKaUdDT

作者:Java3y