1. 程式人生 > >分散式下會話追蹤[基於Cookie和Redis的實現]

分散式下會話追蹤[基於Cookie和Redis的實現]

一. 叢集遇到的問題

從使用者端來解釋,就是當一個使用者第一次訪問被負載均衡代理到後端伺服器A並登入後,伺服器A上保留了使用者的登入資訊;當用戶再次傳送請求時,根據負載均衡策略可能被代理到後端不同的伺服器,例如伺服器B,由於這臺伺服器B沒有使用者的登入資訊,所以導致使用者需要重新登入。這對使用者來說是不可忍受的。

二 .Session和Cookie的區別

1、session儲存在伺服器,客戶端不知道其中的資訊;cookie儲存在客戶端,伺服器能夠知道其中的資訊。
2、session中儲存的是物件,cookie中儲存的是字串。

三 .會話cookie和持久cookie的區別

如果不設定過期時間,則表示這個cookie生命週期為瀏覽器會話期間,只要關閉瀏覽器視窗,cookie就消失了。這種生命期為瀏覽會話期的cookie被稱為會話cookie。會話cookie一般不儲存在硬碟上而是儲存在記憶體裡。
  如果設定了過期時間,瀏覽器就會把cookie儲存到硬碟上,關閉後再次開啟瀏覽器,這些cookie依然有效直到超過設定的過期時間。

程式碼實現:

生成UUID

public class UUIDUtil {
   public static String uuid() {
      return UUID.randomUUID().toString
().replace("-", ""); } }

設定過期時間

public class MiaoshaUserKey extends BasePrefix{

   public static final int TOKEN_EXPIRE = 3600*24 * 2;
   private MiaoshaUserKey(int expireSeconds, String prefix) {
      super(expireSeconds, prefix);
   }
   public static MiaoshaUserKey token = new MiaoshaUserKey
(TOKEN_EXPIRE, "tk"); }

客戶端通過 token就能獲取到使用者資訊

package com.imooc.miaosha.config;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import com.imooc.miaosha.domain.MiaoshaUser;
import com.imooc.miaosha.service.MiaoshaUserService;

@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver {

   @Autowired
   MiaoshaUserService userService;
   
   public boolean supportsParameter(MethodParameter parameter) {
      Class<?> clazz = parameter.getParameterType();
      return clazz==MiaoshaUser.class;
   }

   public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
         NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
      HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
      HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);

      //從客戶端獲取
      String paramToken = request.getParameter(MiaoshaUserService.COOKI_NAME_TOKEN);
      //從cookie中獲取
      String cookieToken = getCookieValue(request, MiaoshaUserService.COOKI_NAME_TOKEN);
      //判空
      if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
         return null;
      }

      String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
      return userService.getByToken(response, token);
   }

   private String getCookieValue(HttpServletRequest request, String cookiName) {
      Cookie[]  cookies = request.getCookies();
      for(Cookie cookie : cookies) {
         if(cookie.getName().equals(cookiName)) {
            return cookie.getValue();
         }
      }
      return null;
   }

}

自定義引數解析器

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebConfig  extends WebMvcConfigurerAdapter{
   
   @Autowired
   UserArgumentResolver userArgumentResolver;
   
   @Override
   public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
      argumentResolvers.add(userArgumentResolver);
   }
}

通過redis獲得資訊

public MiaoshaUser getByToken(HttpServletResponse response, String token) {
   if(StringUtils.isEmpty(token)) {
      return null;
   }
   //從redis中獲得資料
   MiaoshaUser user = redisService.get(MiaoshaUserKey.token, token, MiaoshaUser.class);
   //延長有效期
   if(user != null) {
      addCookie(response, token, user);
   }
   return user;
}

方法呼叫

private void addCookie(HttpServletResponse response, String token, MiaoshaUser user) {
   redisService.set(MiaoshaUserKey.token, token, user);
   Cookie cookie = new Cookie(COOKI_NAME_TOKEN, token);
   cookie.setMaxAge(MiaoshaUserKey.token.expireSeconds());
   cookie.setPath("/");
   response.addCookie(cookie);
} 

ok啦