📒博客首页:崇尚学技术的科班人
🍣今天给大家带来的文章是《分布式系统下的session怎样存储》
🍣
🍣希望各位小伙伴们能够耐心的读完这篇文章🍣
🙏博主也在学习阶段,如若发现问题,请告知,非常感谢🙏
💗同时也非常感谢各位小伙伴们的支持💗
1、什么是分布式系统?
(1)、定义
这里我引用名人Leslie Lamport
一句话
- 旨在支持应用程序和服务的开发,可以利用物理架构由
多个自治的处理元素
,不共享主内存
,但通过网络发送消息合作
。
(2)、三个特点和三个概念
三个特点
- 多节点
- 消息通信
- 不共享内存
三个概念
-
分布式系统:假如微信订单系统上的多台服务器上,有的服务器上部署的是卖家端,而有的服务器上部署的是买家端,那么这样的集群就是分布式系统。
-
集群:假如一个项目中的系统上的多台服务器上,所有服务器上部署的都是相同的内容,那么这样的多台服务器所形成的就是集群。
-
分布式计算:就是我们把一个任务进行拆分成多个小任务,然后将多个小任务的结果结合在一起形成总的任务的结果,这样的过程就是分布式计算。
(3)、分布式和集群的区别和联系
-
分布式系统可以包含集群。比如说,我们的一个分布式项目,它上面的部署了很多的功能模块节点,它的每一个功能模块节点上可以部署了很多的集群。所以分布式系统中可以包含集群。
-
但是一个集群不一定是一个分布式系统。例如,我们一个
All In One
的项目,它上面部署了很多的集群节点,但是它就不是分布式系统。
2、广义的session
-
在我们的一个项目的用户的登录和登出模块,
用户登录
就是验证身份和存储信息,用户登出
就是将浏览状态失效。 -
http
协议是无状态的,对于同一次http
请求是没有上下文关系的。
- 面临的问题:用户状态怎样存储?
通过
session(会话控制)
来存储用户信息和用户浏览状态。
2.1、实现方式一:sessionId
- 当浏览器发起请求的时候,服务器会创建一个
JSESSIONID
的key
和value
。服务器通过setCookie
将JSESSIONID
设置在http
请求头上,客户端会将该信息存储在客户端内部,然后每一次请求都会带上该信息。
2.2、实现方式二:token
- 如果是
token
方式的话,我们需要手动的在http
请求头上或者url
上设置token
字段,请求发送到服务器的时候,服务器回去出token
字段的相关值进行验证。如果要求十分严格的话,token
会结合签名一起使用。
3、分布式系统下的session
1. 非分布式系统下的架构情况
2. 分布式系统下的架构情况
- 水平扩展:该扩展方法就是将一个服务器进行克隆的多部署几台服务器,每一台服务器上的部署的内容都是相同的。
- 垂直扩展:垂直扩展就是将一个服务项目进行拆分成多个模块然后部署在多个
tomcat
服务器上,这样多个服务器上的部署内容是不相同的。 - 逃不开的问题:那就是
session
该如何进行存储?
解决该问题的最好的方法:就是利用redis
做缓存,然后多台tomcat
从redis
进行查询,进而获得对应用户的信息,进而进行后续操作。这样就能很好的解决分布式系统下的session
问题。
4、解决办法
ReidsConstant
/**
* redis常量
* @author :小肖
* @date :Created in 2022/2/7 21:41
*/
public interface RedisConstant {
String TOKEN_PREFIX = "token_%s";
Integer EXPIRE = 7200; // 设置过期时间,单位是s
}
CookieConstant
/**
* @author :小肖
* @date :Created in 2022/2/7 21:55
*/
public interface CookieConstant {
String TOKEN = "token";
Integer expire = 7200; // 单位是秒
}
CookieUtil
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
/**
* @author :小肖
* @date :Created in 2022/2/7 21:47
*/
public class CookieUtil {
public static void set(HttpServletResponse response,
String name,
String value,
int maxAge){
Cookie cookie = new Cookie(name,value);
cookie.setPath("/");
cookie.setMaxAge(maxAge);
response.addCookie(cookie);
}
public static Cookie get(HttpServletRequest request,
String name){
Map<String, Cookie> cookieMap = getCookieMap(request);
if(cookieMap.containsKey(name)){
return cookieMap.get(name);
}else{
return null;
}
}
private static Map<String,Cookie> getCookieMap(HttpServletRequest request){
Map<String,Cookie> map = new HashMap<>();
Cookie[] cookies = request.getCookies();
if(cookies != null){
for(Cookie cookie : cookies){
map.put(cookie.getName(),cookie);
}
}
return map;
}
}
ProjectUrl
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author :小肖
* @date :Created in 2022/2/7 15:19
*/
@ConfigurationProperties("projectUrl")
@Data
@Component
public class ProjectUrl {
/**
* 微信公众平台授权url
*/
public String wechatMpAuthorize;
/**
* 微信开放平台授权url
*/
public String wechatOpenAuthorize;
/**
* 项目地址
*/
public String sell;
}
SellerUserController
import com.xiao.bean.SellerInfo;
import com.xiao.config.ProjectUrl;
import com.xiao.constant.CookieConstant;
import com.xiao.constant.RedisConstant;
import com.xiao.enums.ResultEnum;
import com.xiao.service.SellerService;
import com.xiao.utils.CookieUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* 卖家用户信息
* @author :小肖
* @date :Created in 2022/2/7 21:26
*/
@Controller
@RequestMapping("/seller")
public class SellerUserController {
@Autowired
private SellerService sellerService;
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private ProjectUrl projectUrl;
/**
* 该url可以和微信登录的扫码登录进行配置,可以作为returnUrl进行配置
* 登录
* @param openid
* @param response
* @param map
* @return
*/
@GetMapping("/login")
public ModelAndView login(@RequestParam("openid") String openid,
HttpServletResponse response,
Map<String, Object> map) {
// 1. 查询数据库匹对信息
SellerInfo sellerInfo = sellerService.findSellerInfoByOpenid(openid);
if(sellerInfo == null){
map.put("msg", ResultEnum.LOGIN_FAIL.getMessage());
map.put("url","/sell/seller/order/list");
return new ModelAndView("common/error", map);
}
// 2. 向redis 中设置token
String token = UUID.randomUUID().toString();
Integer expire = RedisConstant.EXPIRE;
redisTemplate.opsForValue().set(String.format(RedisConstant.TOKEN_PREFIX,token),openid,expire, TimeUnit.SECONDS);
// 3. 添加cookie
CookieUtil.set(response, CookieConstant.TOKEN,token,expire);
return new ModelAndView("redirect:" + projectUrl.getSell() + "/sell/seller/order/list");
}
/**
* 登出
* @param request
* @param response
* @param map
* @return
*/
@GetMapping("/logout")
public ModelAndView logout(HttpServletRequest request,
HttpServletResponse response,
Map<String, Object> map) {
Cookie cookie = CookieUtil.get(request, CookieConstant.TOKEN);
if(cookie != null){
// 1. 清除redis
redisTemplate.opsForValue().getOperations().delete(String.format(RedisConstant.TOKEN_PREFIX,cookie.getValue()));
// 2. 清除cookie
CookieUtil.set(response,CookieConstant.TOKEN,null,0);
}
map.put("msg",ResultEnum.LOGOUT_SUCCESS.getMessage());
map.put("url","/sell/seller/order/list");
return new ModelAndView("common/success",map);
}
}
- 其上实现的逻辑就是我们将用户的相关信息设置进
redis
缓存中,并设置相关的过期时间,如果用户登出的时候我们就将其在redis
缓存中删除。