Memcache+Tomcat9集群實現session共享
Windows上兩個tomcat, 虛擬機中ip為192.168.0.30的centos上一個(測試用三臺就夠了, 為了測試看見端口所以沒有使用nginx轉發請求)
開始
1.windows上開啟兩個tomcat端口分別為:8888和9999
2.使用ssh文件傳輸工具將項目放到centos的tomcat中
3.使用putty鏈接上centos后進入tomcat的bin目錄執行./startup.sh后使用wget http://192.168.0.30測試是否啟動成功如下圖 1-1
1-1
4.使用瀏覽器訪問項目登(輸入用戶名密碼登錄) 圖 1-2
1-2
登錄成功 圖1-3
1-3
切換到9999端口的項目, 如果直接跳轉到和上圖1-3一樣的頁面取的是memcache的緩存如下圖1-4
1-4
再試另外一個8888端口的tomcat(這個項目被放到ROOT目錄了所以我直接127.0.0.1:8888)也是成功的圖1-5
1-5
8888端口的tomcat對應的控制臺
關閉9999端口的tomcat然后重啟過濾器中攔截到了該請求在Memcache中存在共享的user實例直接跳轉到首頁
如果攔截到了請求然后發現Memcache中存在user實例那么久轉發到對應的頁面比如我訪問8888的時候點擊了'日拒轉單'頁面然后我再去訪問9999的時候發現memcache中有user實例那么久根據攔截到的路徑轉到'日拒轉單' 頁面而不會轉發到其它頁面; 如下圖a和b
a
b
最后退出后不同的端口時都應該看到都是登錄頁面(因為退出時memcache中的user被刪除, 過濾器中在接到請求后發現memcache中對應請求的user實例沒有了直接跳到登錄頁)
登錄代碼:
package yingyue.web.controller; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import yingyue.utils.MemCacheUtil; import yingyue.web.form.User; /** * * @author YingYue * @date 2016-1-1 下午4:13:09 * @file LoginController.java * * 步驟: * 1.獲取瀏覽器的自定義的cookie * 1.1如果該cookie不存在這add * 1.2存在則set更新; set等效于(add和replace)即: 存在則replace不存在則add, 這一步思考一直使用replace的, 我還比較鐘情set的; * * cookie的四個屬性 * max-age 指定Ccookie的生存周期(以秒為單位)!默認情況下,Cookie的值只在瀏覽器的會話期間存在,當用戶退出瀏覽器這些值就消失了! * path 指定與Cookie關聯在一起的網頁.默認情況下,cookie會和創建它的網頁以及與這個網頁處于同一個目錄下的網頁和處于該目錄下的子目錄關聯。 * domain 設置訪問域 舉個例子:位于order.example.com的服務器要讀取catalog.example.com設置的cookie.這里就要引入domain屬性,假定由位于catalog.example.com的頁面創 的cookie把自己的path屬性設置為"/",把domain屬性設置為".example.com",那么所有位于"catalog.example.com"的網頁和所有位于"orders.example.com"的網頁以及所有位于example.com域的其他服務器上得網頁都能夠訪問這個cookie.如果沒有設置cookie的domain值,該屬性的默認值就是創建cookie的網頁所在的 服務器的主機名。 注意:不能將一個cookie的域設置成服務器所在的域之外的域. * seure 指定在網絡上如何傳輸cookie的值 * secure值為true時,在http中是無效的;在https中才有效。 */ @WebServlet(urlPatterns={"/login/LoginController.yingyue", "/login/LoginServlet"}) public class LoginController extends HttpServlet { private static final long serialVersionUID = -8517127272218599956L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); /* 模擬數據庫用戶;*/ Map<String, String> items = new HashMap<String, String>(); items.put("zhoujianxu", "000000"); items.put("盈月", "000000"); items.put("test", "123456"); items.put("admin", "123456"); items.put("yingyue", "123456"); items.put("zuobiexitianyuncai", "000000"); /* 驗證登錄;*/ String userName = request.getParameter("userName"), password = request.getParameter("password"); /* 驗證開始*/ if (userName != null && !"".equals(userName)) { if (password == null || "".equals(password)) { request.setAttribute("tips", "密碼不能為空!"); } else { // 驗證用戶名密碼是否正確; if (items.containsKey(userName) && password.equalsIgnoreCase(items.get(userName))) { /** 寫入Memcache緩存;*/ User user = new User(userName, password, "15001027805", userName.concat("@163.com")); /** * 如果不同用戶使用同一個瀏覽器登錄成功后都要為該用戶重新 '創建' 一個key相同value值不同的cookie是多么的不合理啊, * 解決辦法: * 當key不存在時則創建一個帶有該key的cookie * 當key存在時則更新key對應的value * * 如下: */ // 1.設置 key 為 'MEMCACHE_AND_BROWSER_KEY' value值為 '登錄用戶名+部門名稱'的cookie 并Response到請求的客戶端瀏覽器 '響應響應頭信息' String memcacheAndBrowserKey = "MEMCACHE_AND_BROWSER_KEY"; String memcacheAndBrowserForeignKey = userName.concat("IT"); Cookie[] cookies = request.getCookies(); if (cookies.length > 0 && cookies != null) { for (Cookie cookie : cookies) { boolean b = isContainsKeyCookies(memcacheAndBrowserKey, cookies); if (!b) { Cookie newCookie = new Cookie(memcacheAndBrowserKey, memcacheAndBrowserForeignKey); newCookie.setMaxAge(7 * 24 * 60 * 60); newCookie.setPath("/"); response.addCookie(newCookie); System.out.println("[登錄時key名為MEMCACHE_AND_BROWSER_KEY的cookie不存在執行添加操作]\r\n 詳情--> key名稱為 " + cookie.getName() + "的value值為 " + cookie.getValue() + "的cookie已經響應到瀏覽器 '響應頭信息' 中請看Set-Cookie"); break; } else {// 更新cookie的key對應的value; if (memcacheAndBrowserKey.equals(cookie.getName())) { cookie.setValue(memcacheAndBrowserForeignKey);// 設置新的memcacheAndBrowserForeignKey cookie.setPath("/"); cookie.setMaxAge(7 * 24 * 60 * 60);// 設置為1周 response.addCookie(cookie);// 更新key為'MEMCACHE_AND_BROWSER_KEY'的cookie的value值; System.out.println("[登錄時key名為MEMCACHE_AND_BROWSER_KEY的cookie存在執行更新操作]\r\n 詳情--> key名稱為 " + cookie.getName() + "的value值為 " + cookie.getValue() + "的cookie已經響應到瀏覽器 '響應頭信息' 中請看Set-Cookie"); break; } } } // 2.將 'MEMCACHE_AND_BROWSER_KEY' 的value Date expiry = new Date(7 * 24 * 60 * 60 * 1000);// 設置存到memcache中數據的失效時間, 這里設置為1周與cookie失效時間保持相同; boolean b = MemCacheUtil.getInstance().set(memcacheAndBrowserForeignKey, user, expiry); if (b) { System.out.println("登錄成功"); } } else { System.out.println("cookie為空!"); } request.setAttribute("user", user); request.getRequestDispatcher("/WEB-INF/index.jsp").forward(request, response); return; } else { if (!items.containsKey(userName)) { request.setAttribute("tips", "用戶不存在!"); } else { request.setAttribute("tips", "用戶名或密碼錯誤!"); } } } } else { request.setAttribute("tips", "用戶名不能為空!"); } request.getRequestDispatcher("/index.jsp").forward(request, response); } private boolean isContainsKeyCookies(String key, Cookie[] cookies) { boolean b = false; for (Cookie cookie : cookies) { if (key.equals(cookie.getName())) { b = true; break; } } return b; } }
過濾器代碼:
package yingyue.web.controller; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import yingyue.utils.MemCacheUtil; import yingyue.web.form.User; /* * @WebFilter(filterName = "AuthenticateFilter", * urlPatterns = {"/login/LoginController", "/login/LoginServlet"}) * 或使用下面這種過濾我自定義的 '.yingyue' 的后綴 */ @WebFilter(filterName="AuthenticateFilter", urlPatterns={"*.yingyue", "*.love", "*.^o^", "/index.jsp"}) public class AuthenticateFilter implements Filter { private FilterConfig config; private List<String> releases = new ArrayList<String>(); @Override public void init(FilterConfig filterConfig) throws ServletException { this.config = filterConfig; releases.add("/login/LoginController.yingyue"); } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { // 獲取ServletContext 對象,用于記錄日志 ServletContext context = this.config.getServletContext(); HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; request.setCharacterEncoding("UTF-8"); String path = request.getServletPath(); String methodValue = request.getParameter("method"); if(releases.contains(path)){ chain.doFilter(request, response); return; } if (methodValue != null && !"".equals(methodValue)) { path = path + "?method=" + request.getParameter("method"); } System.out.println("已攔截到請求: ".concat(path)); /* 1.從請求中獲取和Memcache關聯的key*/ String memcacheAndBrowserForeignKey = ""; String memcacheAndBrowserKey = "MEMCACHE_AND_BROWSER_KEY"; Cookie[] cookies = request.getCookies(); if (cookies != null && cookies.length > 0) { for (Cookie cookie : cookies) { if (memcacheAndBrowserKey.equals(cookie.getName())) { memcacheAndBrowserForeignKey = cookie.getValue(); break; } } } else { context.log("cookie為空!"); } // 2.根據上述獲取的key獲取Memcache中的user對象 User loginUser = null; if (memcacheAndBrowserForeignKey != null && !"".equals(memcacheAndBrowserForeignKey)) { loginUser = (User) MemCacheUtil.getInstance().get(memcacheAndBrowserForeignKey); } if(loginUser != null){ request.setAttribute("user", loginUser);// 每次過濾后都將user對象放到request中, 以便每個頁面取值; if ("/index.jsp".equals(path)) { System.out.println("直接跳轉到首頁"); request.getRequestDispatcher("/WEB-INF/index.jsp").forward(request, response); } response.sendRedirect(request.getContextPath() + path); chain.doFilter(request, response); System.out.println("Memcache中存在該user用戶放行該請求鏈接: " + path); return; } System.out.println("登錄用戶為空跳轉到登錄頁"); request.getRequestDispatcher("/index.jsp").forward(request, response); } @Override public void destroy() { this.config = null; } }
退出代碼:
package yingyue.web.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import yingyue.utils.MemCacheUtil; import yingyue.web.form.User; /** * @author YingYue * @date 2016-1-1 下午10:42:05 * @file LogoutController.java */ @WebServlet(urlPatterns={"/logout/LogoutController.yingyue", "/logout/LogoutServlet"}) public class LogoutController extends HttpServlet { private static final long serialVersionUID = 7799109727234048708L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); /* 1.從請求中獲取和Memcache關聯的key, 退出清空客戶端的key, 強制退出則根據key清空memcache的user實例*/ String memcacheAndBrowserForeignKey = ""; String memcacheAndBrowserKey = "MEMCACHE_AND_BROWSER_KEY"; Cookie[] cookies = request.getCookies(); if (cookies.length > 0 && cookies != null) { for (Cookie cookie : cookies) { if (memcacheAndBrowserKey.equals(cookie.getName())) { memcacheAndBrowserForeignKey = cookie.getValue(); cookie.setMaxAge(0);// 設置為0表示刪除cookie System.out.println("[退出時刪除key名為MEMCACHE_AND_BROWSER_KEY的cookie]\r\n 詳情--> key名稱為 " + memcacheAndBrowserKey + "value值為 " + memcacheAndBrowserForeignKey + "的cookie已經從瀏覽器 '響應頭信息' 中刪除請看Set-Cookie"); break; } } } else { System.out.println("退出請求cookie為空!"); } // 根據上述獲取的key獲取Memcache中的user對象 User loginUser = (User) MemCacheUtil.getInstance().get(memcacheAndBrowserForeignKey); System.out.println(loginUser != null?"退出前登錄用戶信息" + loginUser:""); boolean delete = MemCacheUtil.getInstance().delete(memcacheAndBrowserForeignKey); if (delete) { System.out.println("[退出]Memcache中key為 " + memcacheAndBrowserForeignKey + " 的 'user實例' 已被刪除"); System.out.println(loginUser != null?"退出后登錄用戶信息" + (User) MemCacheUtil. getInstance().get(memcacheAndBrowserForeignKey):""); } response.sendRedirect(request.getContextPath()); } }
其它相關跳轉(servlet就是討厭)
package yingyue.web.controller; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(urlPatterns={"/skip/SkipController.yingyue", "/skip/SkipController.action", "/skip/SkipServlet"}) public class SkipController extends HttpServlet { private static final long serialVersionUID = 9078198285369158430L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public synchronized void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String method = request.getParameter("method"); if ("1".equalsIgnoreCase(method)) { request.getRequestDispatcher("/WEB-INF/pages/1.jsp").forward(request, response); } else if ("2".equalsIgnoreCase(method)) { request.getRequestDispatcher("/WEB-INF/pages/2.jsp").forward(request, response); } else if ("3".equalsIgnoreCase(method)) { request.getRequestDispatcher("/WEB-INF/pages/3.jsp").forward(request, response); } else if ("4".equalsIgnoreCase(method)) { request.getRequestDispatcher("/WEB-INF/pages/4.jsp").forward(request, response); } else if ("imageslist".equalsIgnoreCase(method)) { request.getRequestDispatcher("/WEB-INF/pages/images_list.jsp").forward(request, response); } } }
還有一種通過jar包和配置tomcat的context.xml方式實現自動共享session, 類似于Redis 那種自動配置;
最后配上nginx 就完美了~^o^~