Memcache+Tomcat9集羣實現session共享(非jar式配置, 手動編寫Memcache客戶端)

Windows上兩個tomcat, 虛擬機中ip爲192.168.0.30的centos上一個(測試用三臺就夠了, 爲了測試看見端口因此沒有使用nginx轉發請求)java

開始nginx

1.windows上開啓兩個tomcat端口分別爲:8888和9999web

2.使用ssh文件傳輸工具將項目放到centos的tomcat中數據庫

3.使用putty連接上centos後進入tomcat的bin目錄執行./startup.sh後使用wget http://192.168.0.30測試是否啓動成功以下圖 1-1windows

1-1centos

4.使用瀏覽器訪問項目登(輸入用戶名密碼登陸, 涉及到cookie跨域) 圖 1-2跨域

1-2瀏覽器

登陸成功 圖1-3緩存

 

1-3tomcat

切換到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);
            }
            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^~

相關文章
相關標籤/搜索