Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)

时间:2023-03-09 15:56:42
Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)

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

Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)1-1

4.使用浏览器访问项目登(输入用户名密码登录, 涉及到cookie跨域) 图 1-2

Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)1-2

登录成功 图1-3

Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)1-3

切换到9999端口的项目, 如果直接跳转到和上图1-3一样的页面取的是memcache的缓存如下图1-4

Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)1-4

再试另外一个8888端口的tomcat(这个项目被放到ROOT目录了所以我直接127.0.0.1:8888)也是成功的图1-5

Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)1-5

8888端口的tomcat对应的控制台

Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)

关闭9999端口的tomcat然后重启过滤器中拦截到了该请求在Memcache中存在共享的user实例直接跳转到首页

Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)

如果拦截到了请求然后发现Memcache中存在user实例那么久转发到对应的页面比如我访问8888的时候点击了'日拒转单'页面然后我再去访问9999的时候发现memcache中有user实例那么久根据拦截到的路径转到'日拒转单' 页面而不会转发到其它页面; 如下图a和b

Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)a

Memcache+Tomcat9集群实现session共享(非jar式配置, 手动编写Memcache客户端)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^~