`
toknowme
  • 浏览: 137532 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Tomcat下多项目Session共享

    博客分类:
  • J2EE
 
阅读更多
一、提出问题:

为了满足足够大的应用,满足更多的客户,于是我们架设了N台Web服务器(N>=2),在多台Web服务器的情况下,我们会涉及到一个问题:用户登陆一台服务器以后,如果在跨越到另一台服务器的时候能够继续使用客户的Session?

二、解决方案:

1. 写客户端Cookie的方式
       当用户登陆成功以后,把网站域名、用户名、密码、token、 session有效时间全部采用cookie的形式写入到客户端的cookie里面,如果用户从一台Web服务器跨越到另一台服务器的时候,我们的程序主动去检测客户端的cookie信息,进行判断,然后提供对应的服务,当然,如果cookie过期,或者无效,自然就不让用户继续服务了。当然,这种方法的弊端就不言而喻了,比如客户端禁用了cookie或者cookie被黑客窃取了呢?这些都可以解决,目前淘宝session框架就是基于client cookie做开发,不见得他们就出了很大的问题?也许是最可行的方式,可以配合memcached来实现。


2. 服务器之间Session数据同步的方式
       假设Web服务器A是所有用户登陆的服务器,那么当用户验证登陆一下,session数据就会写到A服务器里,那么就可以自己写脚本或者守护进程来自动把session数据同步到其他Web服务器,那么当用户跳转到其他服务器的时候,那么session数据是一致的,自然就能够直接进行服务无须再次登陆了。缺点是,可能会速度慢,不稳定,如果是单向同步的话,登陆服务器出现问题,那么其他服务器也无法服务,当然也可以考虑双向同步的问题。这个方案都可以解决,目前zookeeper可以实现。

3. 利用NFS共享Session数据的方式
       其实这个方案和下面的Mysql方案类似,只是存储方式不一 样。大致就是有一台公共的NFS服务器(Network File Server)做共享服务器,所有的Web服务器登陆的时候把session数据写到这台服务器上,那么所有的session数据其实都是保存在这台 NFS服务器上的,不论用户访问哪台Web服务器,都要来这台服务器获取session数据,那么就能够实现共享session数据了。缺点是依赖性太强,如果NFS服务器down掉了,那么大家都无法工作了,当然,可以考虑多台NFS服务器同步的形式。这个方案都可以解决,目前zookeeper可以实现,当然memcached也可以实现session共享。

4. 利用Mysql数据库共享Session数据的方式
       这个方式与NFS的方式类似,也是采用一台Mysql服务器做共享服务器,把所有的session的数据保存到Mysql服务器上,所有Web服务器都来这台Mysql服务器来获取Session 数据。缺点也是依赖性太强,Mysql无法工作了影响所有的Web服务器,当然,可以考虑多太Mysql数据库来共享session,使用同步Mysql 数据的方式。这种方式跟方式3类似,同样可以采用memcached来做,nosql也可以实现,这些都不是问题。

5. 使用硬件设备
       这个算是比较成熟的解决方案了,使用类似BIG-IP的负载设备来实现资源共享,那么就能够又稳定又合理的的共享Session了。目前很多门户网站采用这种方式。缺点很明显了,就是要收费了,硬件设备肯定需要购买成本的,不过对于专业或者大型应用来讲,是比较合理并且值得的,这种方式可以放到最后面考虑。
 
 
    (1)简单架构
  Tomcat下多项目Session共享


 
    
    
    
    (2)功能简述


 


 
 
 
 
 
    (3)代码简介,目前实现的很简单,后续会扩展
 
    (3.1)统一拦截器模式
package com.crm.interceptor;
 
import java.util.List;
 
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
 
import com.crm.application.CrmApplication;
import com.crm.exception.SessionTimeoutException;
import com.crm.model.operator.BOCrmOperator;
 
public class CrmInterceptor implements HandlerInterceptor {
 
 private static Log log = LogFactory.getLog(CrmInterceptor.class);
 
 @Autowired
 private CrmApplication CrmApplication;
 
 private List<String> allowUrls;
 
 public void setAllowUrls(List<String> allowUrls) {
  this.allowUrls = allowUrls;
 }
 
 @Override
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception {
  log.info("==============执行顺序: 1.preHandle================");
  String requestURI = request.getRequestURI();
 
  for (String allowUrl : allowUrls) {
   if (requestURI.endsWith(allowUrl)) {
    return true;
   }
  }
 
  String crmSessionValue = "";
  Cookie[] allCookies = request.getCookies();
  if (allCookies != null) {
   for (int i = 0; i < allCookies.length; i++) {
    if (allCookies[i].getName().equals(CrmApplication.crmSessionId)) {
     crmSessionValue = allCookies[i].getValue();
     break;
    }
   }
  }
  //1.检测用户cookie中是否包含自定义的sessionId
  //2.取值的时候
  //(2.1)内存取值
  //(2.2)Http请求取值
  //(2.3)Redis取值
  //(2.4)memcached取值
  BOCrmOperator o = CrmApplication.getCrmSessionMap(crmSessionValue);
  System.out.println(o);
 
 
  Object sessionOperator = request.getSession().getAttribute("sessionOperator");
  if (sessionOperator != null) {
   return true;
  } else {
   throw new SessionTimeoutException("用户未登录/用户状态时效,需要重新登陆");
  }
 }
 
 public String getIpAddr(HttpServletRequest request) {
  String ip = request.getHeader("x-forwarded-for");
  if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
   ip = request.getHeader("Proxy-Client-IP");
  }
  if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
   ip = request.getHeader("WL-Proxy-Client-IP");
  }
  if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
   ip = request.getRemoteAddr();
  }
  return ip;
 }
 
 @Override
 // 在业务处理器处理请求执行完成后,生成视图之前执行的动作
 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView mv)
   throws Exception {
  String s = request.getServletPath();
  String c = request.getContextPath();
  String uri = request.getRequestURI();
  String url = request.getRequestURL().toString();
  StringBuffer path = new StringBuffer("http://");
  path.append(request.getServerName());
  path.append(":");
  path.append(request.getServerPort());
  path.append(request.getContextPath());
 
  if (mv != null) {
   mv.addObject("path", path.toString());
  }
  log.info(response);
  log.info("==============执行顺序: 2.postHandle================");
 }
 
 @Override
 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object arg2, Exception arg3)
   throws Exception {
  // log.info("==============执行顺序: 3.afterCompletion================");
 
 }
 
}
 
 
    (3.2)认证模块
 
@RequestMapping(value = { "/" }, method = RequestMethod.GET)
 public String init(HttpServletRequest req, HttpServletResponse res, Model m) {
  BOCrmOperator o = (BOCrmOperator) req.getSession().getAttribute("sessionOperator");
  List<BOCity> operatorCityList = OperatorCityRelSVImpl.getOperatorCitys(o.getId());
  m.addAttribute("operatorCityList", operatorCityList);
  m.addAttribute("sessionCity", operatorCityList.get(0));
 
  String crmSessionValue = UUID.randomUUID().toString().replace("-", "");
  CrmApplication.setCrmSessionMap(crmSessionValue, o);
 
  Cookie myCookie = new Cookie(CrmApplication.crmSessionId, crmSessionValue);
  myCookie.setMaxAge(60000);
  myCookie.setPath("/");
  res.addCookie(myCookie);
 
  return "index/index";
 }
 
 
    (3.3)获取用户信息
public static Operator getOperatorInSession(HttpServletRequest request) {
Operator o = null;
String crmSessionValue = "";
Cookie[] allCookies = request.getCookies();
if (allCookies != null) {
for (int i = 0; i < allCookies.length; i++) {
if (allCookies[i].getName().equals("crmSessionId")) {
crmSessionValue = allCookies[i].getValue();
break;
}
}
}

if ("".equals(crmSessionValue)) {
System.out.println("用户未登录");
} else {
String json = HttpUtil
.sendGet("http://localhost:8080/crm/authOperator?crmSessionId="
+ crmSessionValue);
System.out.println(json);
o = GsonUtil.getGson().fromJson(json, Operator.class);
}

return o;
}

 

 
 
  • 大小: 43.8 KB
  • 大小: 137.4 KB
  • 大小: 129.1 KB
2
3
分享到:
评论
4 楼 toknowme 2015-08-21  
fisher123 写道
何必这么痛苦? springsessionfilter  redis 实现,跨服务器,只要保证sessionid的生成规则一样

等等研究一下~ 
3 楼 fisher123 2015-08-21  
何必这么痛苦? springsessionfilter  redis 实现,跨服务器,只要保证sessionid的生成规则一样
2 楼 toknowme 2015-08-21  
string2020 写道
请问撸主,这个可以跨服务器运行吗?
在jetty, weblogic下可以吗?

我现在这样的设计是不行的

但是你可以通过Ngigx做代理,然后保证多个项目在域下,保存统一的sessionId在统一域下就可以的!
1 楼 string2020 2015-08-21  
请问撸主,这个可以跨服务器运行吗?
在jetty, weblogic下可以吗?

相关推荐

Global site tag (gtag.js) - Google Analytics