<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java統(tǒng)計用戶在線人數(shù),這樣做才優(yōu)雅!

          共 22621字,需瀏覽 46分鐘

           ·

          2022-11-18 11:36

               

          統(tǒng)計用戶在線人數(shù)

          在統(tǒng)計用戶在人數(shù)的時候,我們用到了監(jiān)聽器,監(jiān)聽器大致分為以下三種:

          1. ServletRequestListener:用于監(jiān)聽請求的監(jiān)聽接口

          2. HttpSessionListener:用于監(jiān)聽會話的監(jiān)聽接口

          3. ServletContextListener:用于監(jiān)聽?wèi)?yīng)用的回話接口

          錯誤的統(tǒng)計辦法

          監(jiān)聽Request域

          這種統(tǒng)計辦法是錯誤的認(rèn)為每次刷新頁面后進(jìn)行進(jìn)行一次的count++運算

          import javax.servlet.*;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;

          @WebListener()
          public class MyRequestListener implements ServletRequestListener{
              private ServletContext sc;
              private Integer count;
              @Override
              //請求被初始化 Request
              public void requestInitialized(ServletRequestEvent sre) {
                  //獲取全局域
                  sc = sre.getServletContext();
                  //將count從全局域中獲取出來
                  count = (Integer) sc.getAttribute("count");
                  System.out.println(count);
                  count++;
                  System.out.println(count);
                  sc.setAttribute("count",count);
              }
          }
          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;

          @WebListener()
          public class MyServletContextListener implements ServletContextListener{
              private ServletContext sc;
              @Override
              //Application被初始化的時候創(chuàng)建
              public void contextInitialized(ServletContextEvent sce) {
                  Integer count = 0;
                  //獲取全局域
                  sc = sce.getServletContext();
                  //將count放入到全局域中
                  sc.setAttribute("count",count);
              }
          }
          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <html>
            <head>
              <title>$Title$</title>
            </head>
            <body>
            <center><h1>You are the ${applicationScope.count} customer to visit. </h1></center>
            </body>
          </html>

          這種錯誤地做法導(dǎo)致的是每刷新一次頁面 就會導(dǎo)致count進(jìn)行累加操作,最終產(chǎn)生錯誤的在線人數(shù),所以此時想到不應(yīng)該監(jiān)聽Request域,而應(yīng)該監(jiān)聽Session域。

          監(jiān)聽Session域

          在第二次監(jiān)聽Session域之后,發(fā)現(xiàn)每次刷新頁面后不改變count但是在啟動不同的瀏覽器后count++會實現(xiàn),但是,這樣做并不是我們要統(tǒng)計的在線人數(shù),所以此種做法錯誤。由于代碼只是將原來寫在Request監(jiān)聽器中的代碼轉(zhuǎn)移到Session監(jiān)聽器中,所以其他沒變的代碼將不重復(fù)。

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;

          @WebListener()
          public class MySessionListener implements HttpSessionListener{

              private ServletContext sc;
              private Integer count;

              @Override
              //當(dāng)對話產(chǎn)生時激活此方法
              public void sessionCreated(HttpSessionEvent se) {
                  sc = se.getSession().getServletContext();
                  count = (Integer) sc.getAttribute("count");
                  count++;
                  sc.setAttribute("count",count);
              }
          }

          這時我們發(fā)現(xiàn)對于在線人數(shù)的統(tǒng)計,不是網(wǎng)頁訪問的次數(shù),也不是瀏覽器打開的個數(shù),對需求的理解的錯誤理解。所以正確的做法是統(tǒng)計其IP的數(shù)量,這樣的話,不管你在一臺電腦上開啟多少客戶端,都會只有一個。

          正確的統(tǒng)計方法

          統(tǒng)計其IP的數(shù)量,將IP的數(shù)量作為當(dāng)前的在線人數(shù),那么如何統(tǒng)計IP的數(shù)量呢?這樣將會導(dǎo)出以下問題:

          • 如何獲取用戶的IP?
          • IP將如何存儲?
          • 如何判斷IP之前已經(jīng)存在?

          現(xiàn)在來解決這些問題:

          • 只能從請求中獲取
          • 通過2、3問題,我們想到了集合(List),因為集合不僅可以存儲任何字符串,還可以通過遍歷來判斷之前是否有重復(fù)的IP出現(xiàn)。

          到了這里又冒出來一個問題集合(List)放到哪個域里呢?

          ServletContext域

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.HttpSessionAttributeListener;
          import javax.servlet.http.HttpSessionEvent;
          import javax.servlet.http.HttpSessionListener;
          import javax.servlet.http.HttpSessionBindingEvent;
          import java.util.ArrayList;
          import java.util.List;

          @WebListener()
          public class MyServletContextListener implements ServletContextListener{
              private ServletContext sc;

              @Override
              //Application被初始化的時候創(chuàng)建
              public void contextInitialized(ServletContextEvent sce) {
                  //創(chuàng)建一個鏈表來存儲IP
                  List<String> ips = new ArrayList<>();
                  sc = sce.getServletContext();
                  //將創(chuàng)建好的鏈表對象,放到Application域中
                  sc.setAttribute("ips",ips);
              }
          }

          由于IP只能在Request域中獲取,所以遍歷判斷在Request域中進(jìn)行。

          import javax.servlet.*;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.List;

          @WebListener()
          public class MyRequestListener implements ServletRequestListener{

              private HttpServletRequest sr;
              private String clientIp;
              private ServletContext sc;
              private List<String> ips;
              private HttpSession session;

              @Override
              //請求被初始化 Request
              public void requestInitialized(ServletRequestEvent sre) {
                  //從請求域中獲取IP
                  sr = (HttpServletRequest) sre.getServletRequest();
                  clientIp = sr.getRemoteAddr();
                  session = sr.getSession();
                  session.setAttribute("clientIp",clientIp);

                  //測試
                  // System.out.println("clientIp = "+ clientIp);
                  //獲取Application域中的List
                  sc = sre.getServletContext();
                  ips = (List<String>) sc.getAttribute("ips");
                  //遍歷ips
                  for (String ip :
                          ips) {
                      if (clientIp.equals(ip))
                          return;
                  }
                  ips.add(clientIp);
                  sc.setAttribute("ips",ips);
              }
          }

          因為要統(tǒng)計在線人數(shù),所以要設(shè)置退出按鈕,點擊退出按鈕之后,因為要從List域中移除,所以使用Session域監(jiān)聽器來判斷session回話的關(guān)閉

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.List;

          @WebListener()
          public class MySessionListener implements HttpSessionListener{

              private ServletContext sc;
              private List<String> ips;
              private HttpSession session;
              private Object clientIp;

              @Override
              public void sessionDestroyed(HttpSessionEvent se) {
                  sc = se.getSession().getServletContext();
                  ips = (List<String>) sc.getAttribute("ips");
                  session = se.getSession();
                  clientIp = session.getAttribute("clientIp");
                  //刪除ip,如何獲取IP,但是不可以從session獲取到IP
                  //因為Session獲取不到Request
                  //一個Session包含多個Request
                  //一個Request只對應(yīng)一個Session 所以獲取不到,這時只能先從Request域中獲取到的ips,放置到Session域
                  //然后從Session 域中讀取
                  ips.remove(clientIp);
                  // session一失效就馬上將此IP從鏈表中移除是錯誤的
                  //應(yīng)該看此IP是否有另外的回話存在,如果有的話不能刪除
              }
          }

          此處代碼是頁面點擊關(guān)閉后,激活的退出方法

          import javax.servlet.ServletException;
          import javax.servlet.annotation.WebServlet;
          import javax.servlet.http.HttpServlet;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;
          import javax.servlet.http.HttpSession;
          import java.io.IOException;

          @WebServlet(name = "LogoutServlet",urlPatterns = "/logoutServlet")
          public class LogoutServlet extends HttpServlet {

              private HttpSession session;

              protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                  //從域中獲取一個session,設(shè)置為false 如果域中存在一個session,則直接獲取,如果不存在,則返回一個空的session
                  session = request.getSession(false);
                  if (session != null){
                      //使session失效
                      session.invalidate();
                      //失效后,需要進(jìn)行的操作,List鏈表中需要減去,用到了Session域監(jiān)聽器
                  }
              }

              protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                  doPost(request,response);
              }
          }

          在jsp頁面進(jìn)行讀取的時候,因為ips是以List鏈表的形式存在的,所以要想判斷當(dāng)前在線人數(shù),所以必須要判斷鏈表的長度,所以是applicationScope.ips.size()

          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <html>
            <head>
              <title>$Title$</title>
            </head>
            <body>
            <center><h1>You are the ${applicationScope.ips.size()} customer to visit. </h1><br>
              <h3><a href="${pageContext.request.contextPath}/logoutServlet">
                安全退出
              </a></h3>
            </center>
            </body>
          </html>

          好了?,這時候,程序?qū)懲炅?,如何判斷呢?/p>

          此時,我們的程序是部署在本地的Tomcat上的,對于本臺電腦,只有一個IP,如何實現(xiàn)多個IP呢?其實啊我們的電腦可以有三個IP,在訪問服務(wù)器的時候,服務(wù)器的IP多寫幾個,相當(dāng)于本機(jī)的IP多出來幾個。是哪三個IP呢?

          1、默認(rèn)clientIp :0:0:0:0:0:0:0:1

          2、127.0.0.1

          這時大家可能會問127.0.0.1和localhost有什么區(qū)別呢,其實在這里要區(qū)分三個概念:

          localhost、127.0.0.1 和 本機(jī)IP之間的區(qū)別:

          • localhost等于127.0.0.1,不過localhost是域名,127.0.0.1是IP地址。
          • localhost和127.0.0.1不需要聯(lián)網(wǎng),都是本機(jī)訪問。
          • 本機(jī)IP需要聯(lián)網(wǎng),本機(jī)IP是本機(jī)或外部訪問, 本機(jī) IP 就是本機(jī)對外放開訪問的IP地址,這個網(wǎng)址就 是與物理網(wǎng)卡綁定的IP地址。

          3、IPv4地址:192.168.1.110

          這樣就很完美的實現(xiàn)了本地三個IP的測試。

          寫到這里,似乎已經(jīng)可以簡單的測試當(dāng)前在線人數(shù),也許仔細(xì)的人會發(fā)現(xiàn)在Session域被銷毀的方法中的注釋中發(fā)現(xiàn)一些貓膩。大家可以仔細(xì)想想,如果客戶端用不同的瀏覽器,相同的IP去訪問呢?點擊退出后,會不會出現(xiàn)錯誤情況呢?答案是會的。演示結(jié)果如下圖

          最完美的代碼

          所以在點擊退出登錄的按鈕之后,不可以直接將IP移除,要判斷有沒有另外的回話存在,如果有另外的回話存在,此IP是不可以刪掉的,問題由此變的復(fù)雜了,因為還要統(tǒng)計此IP所發(fā)出的會話有多少。

          整體思路:

          在全局域中,將不是直接將iP存放在List的鏈表中,而是以一個Map的形式存在,Map的鍵為String類型,Key為List類型,List中存放的是當(dāng)前IP所激發(fā)的會話對象,這樣就可以統(tǒng)計,一個IP觸發(fā)的sessions有多少個。

          通過調(diào)用Map的get方法,將當(dāng)前IP最為參數(shù),將可以獲取到他所激發(fā)的會話集合。但是,此集合可能為空,因為有可能當(dāng)前IP一次也沒有訪問此頁面,所以在List為空的時候好要創(chuàng)建一個ArrayList來存放sessions,然后將變化后的List重新寫回到Map,再將變化后的Map寫回到全局域中 。這樣創(chuàng)建過程基本完成。

          然后考慮銷毀過程,IP還需方法放到Session域中,當(dāng)session被銷毀的時候,應(yīng)該把當(dāng)前Session從List 中刪除,但是Map中此sessions對應(yīng)的IP可是不能直接刪,要判斷List中的sessions的個數(shù)(Entry對象),個數(shù)為1的時候才可以刪除,不然就不可以刪除。

          所以,要將當(dāng)前IP通過Request域存放到當(dāng)前Session域中,

          然后,要考慮的問題是,每次刷新頁面后sessions的個數(shù)會增加,這是錯誤的,原因是什么?

          答案是,因為在存放sessions的時候,創(chuàng)建數(shù)組直接進(jìn)行的添加,這樣的話,每次一刷新頁面,就會導(dǎo)致sessions的添加,所以在此之前應(yīng)該判斷,sessions中是否有此session,有的話直接跳出。

          這樣添加就沒問題了

          Servlet域中添加Map

          在Map中,需要使用鍵值對的方式,Key為IP,Value為List,那么List中存放什么呢?存放的是此IP發(fā)出的所有回話的HttpSession的對象,所以List的泛型是HttpSession。

          請求,在請求中,因為將當(dāng)前Session 對象存放到List中, List在Map中,Map在全局域中,所以首先得從全局域獲取到Map,然后,從Map中獲取由當(dāng)前IP所發(fā)出的所有Session的組成的List,判斷當(dāng)前的List是否為NULL,若為NULL,則創(chuàng)建List,否則,將當(dāng)前SessioncurrentSession放入List中。

          import javax.servlet.*;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.ArrayList;
          import java.util.List;
          import java.util.Map;

          @WebListener()
          public class MyRequestListener implements ServletRequestListener{

              private HttpServletRequest sr;
              private String clientIp;
              private ServletContext sc;
              private List<String> ips;
              private HttpSession currentSession;
              private Map<String,List<HttpSession>> map;
              private List<HttpSession> sessions;


              @Override
              //請求被初始化 Request
              public void requestInitialized(ServletRequestEvent sre) {
                  //從請求域中獲取IP
                  sr = (HttpServletRequest) sre.getServletRequest();
                  clientIp = sr.getRemoteAddr();
                  currentSession  = sr.getSession();
                  //將當(dāng)前Session 對象存放到List中, List在Map中,Map在全局域中,
                  sc = sre.getServletContext();
                  map = (Map<String, List<HttpSession>>) sc.getAttribute("map");
                  //從Map中獲取由當(dāng)前IP所發(fā)出的所有Session的組成的List
                  sessions = map.get(clientIp);
                  //判斷當(dāng)前的List是否為NULL,若為NULL,則創(chuàng)建List,否則,將當(dāng)前Session放入List
                  if (sessions == null){
                      sessions = new ArrayList<>();
                  }
          //        遍歷List的session 對象,若有則不添加,若沒有則添加
                  for (HttpSession session :
                          sessions) {
                      if (session == currentSession)
                          return;
                  }
                  sessions.add(currentSession);


                  //將變化過的List重新寫回到Map
                  map.put(clientIp,sessions);
                  //再將變化的Map寫回到全局域中
                  sc.setAttribute("map",map);

                  //將當(dāng)前IP放入到當(dāng)前Session
                  currentSession.setAttribute("clientIp",clientIp);
              }

          }
          ServletContext

          這里將不使用ips了,所以將其刪除

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.ArrayList;
          import java.util.HashMap;
          import java.util.List;
          import java.util.Map;

          @WebListener()
          public class MyServletContextListener implements ServletContextListener{
              private ServletContext sc;
              @Override
              //Application被初始化的時候創(chuàng)建
              public void contextInitialized(ServletContextEvent sce) {
                  //創(chuàng)建一個Map,key為IP,value為該IP上所發(fā)出的會話的對象
                  Map<String,List<HttpSession>> map = new HashMap<>();
                  sc = sce.getServletContext();
                  //將map放到全局域中
                  sc.setAttribute("map",map);
              }
          }
          Session監(jiān)聽器

          接下來剖析Session的刪除工作,獲取當(dāng)前Session對象,這里有之前傳遞過來的IP,在進(jìn)行刪除操作的時候,要注意此處,刪除的是List中的sessions,刪除之后,還要判斷其IP的是否要刪除,如果List中沒有該元素,則說明當(dāng)前IP所發(fā)出的會話全部關(guān)閉,就可以從map中將當(dāng)前IP對應(yīng)的Entry對象刪除,否則,當(dāng)前IP所發(fā)出的會話任存在,那么使用put方法將變化過的List寫回到map。

          import javax.servlet.ServletContext;
          import javax.servlet.ServletContextEvent;
          import javax.servlet.ServletContextListener;
          import javax.servlet.annotation.WebListener;
          import javax.servlet.http.*;
          import java.util.List;
          import java.util.Map;

          @WebListener()
          public class MySessionListener implements HttpSessionListener{

              private ServletContext sc;
              private List<String> ips;
              private HttpSession currentSession;
              private String clientIp;
              private Map<String,List<HttpSession>> map;
              private List<HttpSession> sessions;

              @Override
              public void sessionDestroyed(HttpSessionEvent se) {
                  sc = se.getSession().getServletContext();

                  currentSession = se.getSession();
                  clientIp = (String) currentSession.getAttribute("clientIp");
                  map = (Map<String, List<HttpSession>>) sc.getAttribute("map");
                  //從Map中獲取List
                  sessions = map.get(clientIp);
                  //從List中刪除當(dāng)前Session對象
                  sessions.remove(currentSession);
                  //如果List中沒有該元素,則說明當(dāng)前IP所發(fā)出的會話全部關(guān)閉,就可以從map中
                  //將當(dāng)前IP對應(yīng)的Entry對象刪除
                  //若List中仍有元素,當(dāng)前IP所發(fā)出的會話任存在,那么將變化過的List寫回到map
                   if (sessions.size() == 0){
                       map.remove(clientIp);
                   }else {
                       map.put(clientIp,sessions);
                   }
                   sc.setAttribute("map",map);
              }
          }

          因為處理的退出的頁面/logoutServlet不需要做任何不同的處理,所以這里將不再重復(fù)。

          因為在jsp用到了JSP標(biāo)準(zhǔn)庫,所以到導(dǎo)兩個包。

          <%@ page contentType="text/html;charset=UTF-8" language="java" %>
          <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
          <html>
            <head>
              <title>$Title$</title>
            </head>
            <body>
            <center><h1>You are the ${applicationScope.map.size()} customer to visit. </h1><br>
              <h3><a href="${pageContext.request.contextPath}/logoutServlet">
                安全退出
              </a><br></h3>
              <h2>
                <c:forEach items="${map}" var="entry">
                  ${entry.key }=${entry.value.size()}<br>
                </c:forEach>
              </h2>
            </center>
            </body>
          </html>

          最后 測試成功,這就是一個完美的統(tǒng)計當(dāng)前用戶的在線人數(shù)。

          來源:blog.csdn.net/gsjwxhn/article/details/90707571


          推薦閱讀:

          世界的真實格局分析,地球人類社會底層運行原理

          不是你需要中臺,而是一名合格的架構(gòu)師(附各大廠中臺建設(shè)PPT)

          企業(yè)IT技術(shù)架構(gòu)規(guī)劃方案

          論數(shù)字化轉(zhuǎn)型——轉(zhuǎn)什么,如何轉(zhuǎn)?

          華為干部與人才發(fā)展手冊(附PPT)

          企業(yè)10大管理流程圖,數(shù)字化轉(zhuǎn)型從業(yè)者必備!

          【中臺實踐】華為大數(shù)據(jù)中臺架構(gòu)分享.pdf

          華為的數(shù)字化轉(zhuǎn)型方法論

          華為如何實施數(shù)字化轉(zhuǎn)型(附PPT)

          超詳細(xì)280頁Docker實戰(zhàn)文檔!開放下載

          華為大數(shù)據(jù)解決方案(PPT)

          瀏覽 51
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久久综合第一页 | 成人黄色性视频 | 国产欧美精品久久 | 日本女人在线 | 亚洲无码人妻 |