SNA (Share Nothing Architecture) Session 解剖

集群中部署的web应用系统往往会强调无状态 性,之所以这么要求,一方面是当然是为了便于集群扩展,但是对于有session要求的系统来说,无状态确实没法做到,既然没法做到,那么对于集群部署的应用来说,就要考虑到状态的保存和同步问题,因此衍生了 session状态复制 以及 striky session 两种技术,第一种是通过服务器集群中相互复制session状态来保障同步,优点是简单,缺点也明显工作量大,性能低下(大于5台),第二种方式呢,就是把同一个用户的session绑定在一个服务器上,避免了复制session,貌似解决了问题,不过问题又来了,如果这台服务器挂了呢?那就杯具了,这台服务器上的所有session都没了。 再于是.......

 

    其实,无论方案是怎么样的, 都要最终解决一个问题,就是集群内的服务器如何访问session中信息,也就是session信息应该放在哪里,服务器应该怎么去拿这个session信息?前面的方案中,复制session采用的保证每台服务器都有一个副本,但开销太大,性能低,还不能保证同步;striky session 比较极端了,不允许复制,只一台服务器享受,一个用户从始至终都访问这台机器,但是无法保证fail over。那有其他办法来保存session么?当然有,于是所谓的SNA架构类的session方案就横空出世了,我们先不管SNA如何如何,看下它如何解决session的存储:


  方案一: 服务端存储方案-> session 不在任何一台实际的应用服务器中存储,将其存储到独立的文件系统中去、或者数据库、或者cache中,每次session重建时根据sessionId去存储介质中拿到数据重组session

        优点:各个应用服务器不需要自己存储session,扩展当然更方便了

        缺点:需要另外搭建session存储系统,系统结构复杂了,

        

 方案二: 客户端存储方案->session 采用cookie的方式写入到客户端,每次客户端访问时将cookie提交给应用服务器,应用服务器重组session。

       优点: 不需要另外搭建存储系统,实现方式快捷。

       缺点: 安全性不够高,cookie容易被破解,当然对cookie进行加密以及加上时间戳有效期这个问题可以缓解,对于安全性要求不是绝对高的应用来说,完全OK了。另一个缺点就是每次都要提交大量cookie,网络流量消耗比较大。

 

 

      好了,这里提供一个关于客户端存储方案的实现例子,不是很完善,不过稍加修饰就完全可以做为产品过程中使用:好,翠花,上酸菜

     

    

   

从以上架构实现上看,其实非常简单,首先实现SnaHttpServletRequest,目的是返回自定义session,重写getSession等方法即可,接着重写SnaHttpServletResponse,这里主要是为了在getWriter或者getOutputStream时将session中的修改正式提交修改,之所以要重写这里,是因为我们无法控制应用框架合适会对response进行commit,大家看一下servletResponse接口就知道,response不能重复commit, 所以重写后在他们提交前我们先把session修改提交,这一步很重要,特别是对于客户端方案来说。接着要重新实现session接口,这里负责session的重建工作,SnaSessionStoreProvider 主要是负责存储,聪明的看客应该知道,如果要实现服务端方案,重写这个接口就行了。 另外基于客户端的方案中,提供了一个配置文件session.xml,目的何在?session中的attribute可能来自很多cookie,

这里进行配置,我们就限定了只有这些cookie中的属性才能读出或者写回到客户端,因此这里其实也起到一个简单管理cookie的作用,防止别人乱写cookie,除非在这里配置,否则不会生效。

 

    接下来,贴点关键代码:

    SnaSessionFilter.java

 

  

  1. public class SnaSessionFilter implements Filter {  
  2.     private FilterConfig filterConfig;  
  3.       
  4.     @Override  
  5.     public void doFilter(ServletRequest request, ServletResponse response,  
  6.             FilterChain chain) throws IOException, ServletException {  
  7.         if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse)  
  8.                 || (request.getAttribute(getClass().getName()) != null)) {  
  9.             chain.doFilter(request, response);  
  10.             return;  
  11.         }  
  12.         request.setAttribute(getClass().getName(), Boolean.TRUE);  
  13.           
  14.         SnaHttpServletRequest snaHttpServletRequest = new SnaHttpServletRequest(  
  15.                 (HttpServletRequest) request);  
  16.         SnaHttpServletResponse snaHttpServletResponse = new SnaHttpServletResponse(  
  17.                 (HttpServletResponse) response);  
  18.         // session上下文  
  19.         SnaHttpContext context = new SnaHttpContext();  
  20.         context.setRequest(snaHttpServletRequest);  
  21.         context.setResponse((HttpServletResponse) snaHttpServletResponse);  
  22.         context.setServletContext(filterConfig.getServletContext());  
  23.           
  24.         SnaSession session = getSession(context);  
  25.         snaHttpServletRequest.setSession(session);  
  26.         snaHttpServletResponse.setSession(session);  
  27.         chain.doFilter(snaHttpServletRequest, snaHttpServletResponse);  
  28.         session.commit();  
  29.     }  
  30.     @Override  
  31.     public void init(FilterConfig config) throws ServletException {  
  32.         this.filterConfig = config;  
  33.     }  
  34.     @Override  
  35.     public void destroy() {  
  36.     }  
  37.     private SnaSession getSession(SnaHttpContext context) {  
  38.         SnaSession session = new SnaSession(context);  
  39.         session.init();  
  40.         return session;  
  41.     }  
  42. }   

 

 

 SnaHttpServletRequest.java

 

  1. public class SnaHttpServletRequest extends HttpServletRequestWrapper {  
  2.     private SnaSession session;  
  3.     public SnaHttpServletRequest(HttpServletRequest request) {  
  4.         super(request);  
  5.     }  
  6.     public void setSession(SnaSession session) {  
  7.         this.session = session;  
  8.     }  
  9.       
  10.     @Override  
  11.     public HttpSession getSession(boolean create) {  
  12.         return this.session;  
  13.     }  
  14.     @Override  
  15.     public HttpSession getSession() {  
  16.         return this.session;  
  17.     }  
  18. }  

 

 

SnaHttpServletResponse.java

 

  1. public class SnaHttpServletResponse extends HttpServletResponseWrapper {  
  2.     private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone("GMT");  
  3.     private static final String COOKIE_DATE_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss 'GMT'";  
  4.     private static final FastDateFormat DATE_FORMAT = FastDateFormat  
  5.             .getInstance(COOKIE_DATE_PATTERN, GMT_TIME_ZONE, Locale.US);  
  6.     private SnaSession session;  
  7.     public SnaHttpServletResponse(HttpServletResponse response) {  
  8.         super(response);  
  9.     }  
  10.     public void setSession(SnaSession session) {  
  11.         this.session = session;  
  12.     }  
  13.     public SnaSession getSession() {  
  14.         return session;  
  15.     }  
  16.     @Override  
  17.     public ServletOutputStream getOutputStream() throws IOException {  
  18.         getSession().commit();  
  19.         return super.getOutputStream();  
  20.     }  
  21.     @Override  
  22.     public PrintWriter getWriter() throws IOException {  
  23.         getSession().commit();  
  24.         return super.getWriter();  
  25.     }  
  26.     public void addCookie(SnaCookie cookie) {  
  27.         if (!cookie.isHttpOnly()) {  
  28.             super.addCookie(cookie);  
  29.         } else {  
  30.             String cookieString = buildHttpOnlyCookieString(cookie);  
  31.             addHeader("Set-Cookie", cookieString);  
  32.         }  
  33.     }  
  34.     private String buildHttpOnlyCookieString(SnaCookie cookie) {  
  35.         StringBuilder cookieBuilder = new StringBuilder();  
  36.         cookieBuilder.append(cookie.getName()).append("=").append(  
  37.                 cookie.getValue());  
  38.         cookieBuilder.append(";");  
  39.         if (StringUtils.isNotBlank(cookie.getDomain())) {  
  40.             cookieBuilder.append("Domain").append("=").append(  
  41.                     cookie.getDomain());  
  42.             cookieBuilder.append(";");  
  43.         }  
  44.         if (StringUtils.isNotBlank(cookie.getPath())) {  
  45.             cookieBuilder.append("Path").append("=").append(cookie.getPath());  
  46.             cookieBuilder.append(";");  
  47.         }  
  48.         if (cookie.getMaxAge() >= 0) {  
  49.             cookieBuilder.append("Expires").append("=").append(  
  50.                     getCookieExpires(cookie.getMaxAge()));  
  51.             cookieBuilder.append(";");  
  52.         }  
  53.         cookieBuilder.append("HttpOnly");  
  54.         return cookieBuilder.toString();  
  55.     }  
  56.     private String getCookieExpires(int maxAge) {  
  57.         String result = null;  
  58.         if (maxAge > 0) {  
  59.             Calendar calendar = Calendar.getInstance();  
  60.             calendar.add(Calendar.SECOND, maxAge);  
  61.             result = DATE_FORMAT.format(calendar);  
  62.         } else {  
  63.             result = DATE_FORMAT.format(0);   
  64.         }  
  65.         return result;  
  66.     }  
  67. }  

 

 

 

源代码下载地址:

 

http://download.csdn.net/source/2693887

 

===================================================================================

 注: 网络很多cookie实现session的方案基本都是针对特定的框架,例如struts,这里的方案不限定web应用框架。 

posted @ 2011-11-29 11:35  方子尚  阅读(475)  评论(0编辑  收藏  举报