javaweb查詢所有登入的使用者,判斷是否線上
場景描述
專案中需要獲取所有線上的使用者,當用戶登入時,就記錄住線上狀態,當退出時或一定時間中無操作(超時)時,就記錄離線狀態
設想辦法
眾所周知,當用戶登入後,會把登入資訊放入session中,既然session中存有登入資訊,那麼是否可以在session中查詢使用者,session中有的就是線上使用者呢?顯然這是不行的,首先,要了解什麼是session。
session是一個容器,也是一個會話,當有請求到伺服器時,伺服器就產生一個session,會有一個唯一標識sessionId,session物件是一個使用者全域性變數,也可以看作一個私有的,在整個會話期間一直都有效(瀏覽器不關閉,當然及時關閉了也不會立馬清除),當然如果一直無操作的話,也會超時失效,預設應該是半個小時,這個可以自己設定。由於session是在伺服器上的私有空間,因此是不能訪問別人的session資訊的。因此不能根據session來判斷是否線上。
1.資料庫儲存登入狀態
當用戶登入時可以把狀態設定1(線上),退出時設定0(離線),看著感覺是可行的,但感覺設計上不是很好。
2.放在伺服器的公共物件中
放在共享的空間,每個使用者都能訪問或共享的物件中
解決辦法
根據上面分析session之所以不能滿足需求,就是因為它是私有的,使用者之間不能共享,那麼問題就簡單了,如果有一個公共的,使用者可以共享的物件,那麼把登入的使用者放進去,豈不是可以判斷出線上的使用者?
一個web伺服器啟動時,會自動的建立一個application物件,直到web服務停止,在整個伺服器的生命週期中,所有的使用者都公用這個application物件,它是公共的,因此可以拿來存放共享的資料,那麼問題就簡單了,當用戶登入時,不僅把登入資訊放進session,同時也把使用者放進application中,當用戶退出或者超時時,也就是session銷燬時,把使用者從application中移除,那麼就可以在application中一直都儲存登入的使用者,當查詢線上使用者時,只需要把application中的資料取出來即可。
程式碼分享
核心就是寫一個監聽器,實現HttpSessionListener
和ServletContextListener
,在伺服器啟動初始化ServletContext時,往application中放一個set的空集合(存使用者帳號,不會有重複資料),當每一次使用者進行登入時取出application中的set,把帳號儲存set中,再放入application中
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent ;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class LoginListener implements HttpSessionListener,ServletContextListener{
private ServletContext application = null;
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("context destory");
}
public void contextInitialized(ServletContextEvent sce) {
System.out.println("context init");
application = sce.getServletContext();
Set<String> onlineUserSet = new HashSet<String>();
application.setAttribute("onlineUserSet", onlineUserSet);
}
public void sessionCreated(HttpSessionEvent se) {
System.out.println("session create");
}
public void sessionDestroyed(HttpSessionEvent se) {
HttpSession session = se.getSession();
Set<String> onlineUserSet = (Set<String>)application.getAttribute("onlineUserSet");
String username = (String)session.getAttribute("username");
onlineUserSet.remove(username);
application.setAttribute("onlineUserSet", onlineUserSet);
onlineUserSet = (Set<String>)application.getAttribute("onlineUserSet");
System.out.println(onlineUserSet.toString());
System.out.println(username + "超時退出");
System.out.println("session destory");
}
}
登入時程式碼
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
session.setMaxInactiveInterval(60);
ServletContext application = session.getServletContext();
Set<String> onlineUserSet = new HashSet<String>();
session.setAttribute("username", name);
onlineUserSet = (Set)application.getAttribute("onlineUserSet");
onlineUserSet.add(name);
application.setAttribute("onlineUserList", onlineUserSet);
onlineUserSet = (Set)application.getAttribute("onlineUserSet");
Mark
Mark1——2017-01-25
工作上實際並不是按照上面的方法解決的
專案裡用的redis,把登陸使用者資訊存在redis中,每隔一段時間刪除,前端登陸成功後向後臺傳送請求,每隔一段時間請求一次,重新把當前登入使用者寫入redis,這樣保證登陸使用者一直存在redis中