Home  >  Article  >  Database  >  What to do about distributed session inconsistency in redis

What to do about distributed session inconsistency in redis

青灯夜游
青灯夜游forward
2021-11-12 10:47:142596browse

What to do if distributed session is inconsistent? The following article will introduce to you the solution to distributed session inconsistency in redis. I hope it will be helpful to you!

What to do about distributed session inconsistency in redis

Distributed session inconsistency solution

1. What is the role of Session?

  • Session is a communication session tracking technology between the client and the server. The server and the client maintain the basic session information of the entire communication. [Related recommendations: Redis Video Tutorial]

  • When the client accesses the server for the first time, the server will respond with a sessionId and store it locally. In the cookie, subsequent visits will put the sessionId in the cookie into the request header to access the server.

  • If the corresponding data is not found through this sessionId, then the server will create a New sessionId and responded to the client.

2. What are the problems with distributed Session?

In a single-server web application, session information only needs to be stored in the server. This is the most common way we came into contact with it in the past few years

But in recent years, with the With the popularity of distributed systems, a single system can no longer meet the growing needs of millions of users. Servers deployed in clusters have been used in many companies

When high-concurrency requests arrive at the server, load balancing is used distributed to a server in the cluster, which may cause multiple requests from the same user to be distributed to different servers in the cluster, and session data cannot be obtained, so session sharing becomes one question.

What to do about distributed session inconsistency in redis

3. How is the clustering of services generally done?

  • SpringBoot project, then just change the port number to start a few, and then use nginx as a unified reverse proxy.
  • SpringCloud microservice project, then at this time, you can use ribbon local load balancing.

4. The difference between nginx load balancing and ribbon load balancing

  • nginx load balancing is server-side load balancing , access one address uniformly, and decide which server to access based on the load balancing algorithm.
  • ribbon load balancing, this is local load balancing (client load balancing), which caches and records the client addresses that provide services, and implements load balancing based on local algorithms.

5. Session consistency solution

1. Session replication (synchronization)

What to do about distributed session inconsistency in redis

Idea: Multiple servers synchronize sessions with each other, so that each server contains all sessions

Advantages: Functions supported by the server, the application does not need to modify the code

Disadvantages:

  • session synchronization requires data transmission, which occupies the intranet bandwidth and has a delay
  • All The server contains all session data. The amount of data is limited by memory and cannot be expanded horizontally

2. Client storage method

What to do about distributed session inconsistency in redis

Idea: The server stores the sessions of all users, which takes up a lot of memory. You can store the sessions in browser cookies. Each client only needs to store the data of one user.

Advantages: The server does not need to store

Disadvantages:

  • Each http request carries the session, which accounts for the external network bandwidth.
  • The data is stored on the terminal and in Network transmission has security risks such as leakage, tampering, and theft.
  • The data size stored in the session and the number of domain name cookies are limited

Note: Although this solution is not commonly used, it is It is indeed a way of thinking.

3. Reverse proxy hash consistency

Idea: In order to ensure high availability, the server has multiple redundant reverse proxies Can the layer do something to ensure that requests from the same user fall on the same server?

Option 1: Four-layer proxy hash

What to do about distributed session inconsistency in redis

The reverse proxy layer uses the user’s IP for hashing to ensure the same IP requests fall on the same server

Option 2: Seven-layer proxy hash

What to do about distributed session inconsistency in redis

The reverse proxy uses certain business attributes in the http protocol for hashing, such as sid, city_id, user_id, etc., which can implement the hash strategy more flexibly to ensure that requests from the same browser user fall on the same server.

Advantages:

  • Only need to change the nginx configuration, no need to modify the application code
  • Load balancing, as long as the hash attribute is uniform, the load of multiple servers is Balanced
  • can support horizontal expansion of the server (session synchronization method is not possible, subject to memory limitations)

Disadvantages:

  • If the server restarts , a part of the session will be lost, causing business impact. For example, some users will log in again
  • If the server is expanded horizontally and the sessions are redistributed after rehash, some users may not be routed to the correct session

Session generally has a validity period. The two shortcomings can be considered to be equivalent to partial session failure. Generally, the problem is not big.

Regarding four-layer hashing or seven-layer hashing, I personally recommend the former: let professional software do professional things, and the reverse proxy is responsible for forwarding. Try not to introduce application layer business attributes unless you have to (such as , sometimes multiple computer rooms and multiple servers need to be routed to servers in different computer rooms according to business attributes).

The difference between four-layer and seven-layer load balancing

4. Back-end unified centralized storage

What to do about distributed session inconsistency in redis

Advantages:

  • No security risks
  • Can be expanded horizontally, the database/cache can be split horizontally
  • Service There will be no session loss when the client is restarted or expanded

Shortcomings: A network call is added, and the application code needs to be modified

Regarding db storage or cache, I personally recommend the latter: session The frequency of reading will be very high, and the database pressure will be relatively high. If there is a need for session high availability, the cache can be made highly available, but in most cases the session can be lost, and generally there is no need to consider high availability.

Summary

Common methods of architectural design to ensure session consistency:

  • session synchronization method: multiple devices Servers synchronize data with each other
  • Client storage method A user only stores his own data
  • Reverse proxy hash consistency Both four-layer hashing and seven-layer hashing can be done to ensure a user's request Falling on one server
  • The back-end unified storage server is restarted and expanded, and the session will not be lost (back-end cache unified storage is recommended)

6. Case practice: SpringSession redis solves the problem of distributed session inconsistency

##Step 1: Add the dependency packages of SpringSession and redis

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

Step 2: Configuration file

# 为某个包目录下 设置日志
logging.level.com.ljw=debug

# 设置session的存储方式,采用redis存储
spring.session.store-type=redis
# session有效时长为10分钟
server.servlet.session.timeout=PT10M

## Redis 配置
## Redis数据库索引(默认为0)
spring.redis.database=0
## Redis服务器地址
spring.redis.host=127.0.0.1
## Redis服务器连接端口
spring.redis.port=6379
## Redis服务器连接密码(默认为空)
spring.redis.password=

Step 3: Configure interceptor

@Configuration
public class SessionConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SecurityInterceptor())
                //排除拦截的2个路径
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/logout")
                //拦截所有URL路径
                .addPathPatterns("/**");
    }
}
@Configuration
public class SecurityInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        HttpSession session = request.getSession();
        //验证当前session是否存在,存在返回true true代表能正常处理业务逻辑
        if (session.getAttribute(session.getId()) != null){
            log.info("session拦截器,session={},验证通过",session.getId());
            return true;
        }
        //session不存在,返回false,并提示请重新登录。
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.getWriter().write("请登录!!!!!");
        log.info("session拦截器,session={},验证失败",session.getId());
        return false;
    }
}

    HandlerInterceptor
    • preHandle: Called before the business processor processes the request. Preprocessing can perform encoding, security control, permission verification, etc.;
    • postHandle: executed after the business processor completes the processing of the request and before generating the view. Post-processing (Service is called and ModelAndView is returned, but the page is not rendered), there is an opportunity to modify ModelAndView
    • afterCompletion: called after DispatcherServlet has completely processed the request, and can be used to clean up resources, etc. Return to processing (the page has been rendered)

Step 4: Controller

@RestController
@RequestMapping(value = "/user")
public class UserController {

    Map<String, User> userMap = new HashMap<>();

    public UserController() {
        //初始化2个用户,用于模拟登录
        User u1=new User(1,"user1","user1");
        userMap.put("user1",u1);
        User u2=new User(2,"user2","user2");
        userMap.put("user2",u2);
    }

    @GetMapping(value = "/login")
    public String login(String username, String password, HttpSession session) {
        //模拟数据库的查找
        User user = this.userMap.get(username);
        if (user != null) {
            if (!password.equals(user.getPassword())) {
                return "用户名或密码错误!!!";
            } else {
                session.setAttribute(session.getId(), user);
                log.info("登录成功{}",user);
            }
        } else {
            return "用户名或密码错误!!!";
        }
        return "登录成功!!!";
    }

    /**
     * 通过用户名查找用户
     */
    @GetMapping(value = "/find/{username}")
    public User find(@PathVariable String username) {
        User user=this.userMap.get(username);
        log.info("通过用户名={},查找出用户{}",username,user);
        return user;
    }

    /**
     *拿当前用户的session
     */
    @GetMapping(value = "/session")
    public String session(HttpSession session) {
        log.info("当前用户的session={}",session.getId());
        return session.getId();
    }

    /**
     * 退出登录
     */
    @GetMapping(value = "/logout")
    public String logout(HttpSession session) {
        log.info("退出登录session={}",session.getId());
        session.removeAttribute(session.getId());
        return "成功退出!!";
    }

}

Step 5: Entity class

@Data
public class User implements  Serializable{

    private int id;
    private String username;
    private String password;

    public User(int id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

}

Step 6: Access test

Log in first:

http://127.0.0.1:8080/user/login?username=user1&password=user1

Check again

http://127.0.0.1:8080/user/find/user1

7. Analyze the redis principle of SpringSession

Step 1: Analyze the redis data structure of SpringSession

127.0.0.1:6379> keys *
1) "spring:session:sessions:9889ccfd-f4c9-41e5-b9ab-a77649a7bb6a"
2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b"
3) "spring:session:expirations:1635413520000"
4) "spring:session:sessions:expires:9889ccfd-f4c9-41e5-b9ab-a77649a7bb6a"
5) "spring:session:expirations:1635412980000"
6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"

Common ground: the three keys all start with spring:session:, which represents the redis data of SpringSession.

Query type

127.0.0.1:6379> type spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b
hash
127.0.0.1:6379> hgetall spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b
// session的创建时间
1) "creationTime"
2) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01|\xc5\xdb\xecu"
// sesson的属性,存储了user对象
3) "sessionAttr:d3434f61-4d0a-4687-9070-610bd7790f3b"
4) "\xac\xed\x00\x05sr\x00\x1ecom.ljw.redis.controller.User\x16\"_m\x1b\xa0W\x7f\x02\x00\x03I\x00\x02idL\x00\bpasswordt\x00\x12Ljava/lang/String;L\x00\busernameq\x00~\x00\x01xp\x00\x00\x00\x01t\x00\x05user1q\x00~\x00\x03"
//最后的访问时间
5) "lastAccessedTime"
6) "\xac\xed\x00\x05sr\x00\x0ejava.lang.Long;\x8b\xe4\x90\xcc\x8f#\xdf\x02\x00\x01J\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x01|\xc5\xe1\xc7\xed"
//失效时间 100分钟
7) "maxInactiveInterval"
8) "\xac\xed\x00\x05sr\x00\x11java.lang.Integer\x12\xe2\xa0\xa4\xf7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xac\x95\x1d\x0b\x94\xe0\x8b\x02\x00\x00xp\x00\x00\x17p"

Step 2: Analyze SpringSession’s redis expiration strategy

There are generally three types of deletion for expired data Strategy:

  • Scheduled deletion, that is, while setting the expiration time of the key, create a timer and delete it immediately when the expiration time of the key arrives.

  • Lazy deletion, that is, when accessing a key, determine whether the key has expired, delete it if it expires, otherwise return the key value.

  • 定期删除,即每隔一段时间,程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。

  • redis删除过期数据采用的是懒性删除+定期删除组合策略,也就是数据过期了并不会及时被删除。

  • 但由于redis是单线程,并且redis对删除过期的key优先级很低;如果有大量的过期key,就会出现key已经过期但是未删除。

  • 为了实现 session 过期的及时性,spring session 采用了定时删除+惰性删除的策略。

定时删除

127.0.0.1:6379> type spring:session:expirations:1635413520000
set
127.0.0.1:6379> smembers  spring:session:expirations:1635413520000
1) "\xac\xed\x00\x05t\x00,expires:d3434f61-4d0a-4687-9070-610bd7790f3b"

What to do about distributed session inconsistency in redis

2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b" 
3) "spring:session:expirations:1635413520000" 
6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"
  • 1635412980000 是时间戳,等于 2021-10-28 17:23:00,即是该可以在这个时刻过期
  • springsession 定时(1分钟)轮询,删除spring:session:expirations:[?] 的过期成员元素,例如:spring:session:expirations:1635413520000
  • springsesion 定时检测超时的key的值,根据值删除seesion,例如key:spring:session:expirations:1635413520000,值为(sessionId):d3434f61-4d0a-4687-9070-610bd7790f3b的seesion

惰性删除

127.0.0.1:6379> type spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b
string
127.0.0.1:6379> get spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b
""
127.0.0.1:6379> ttl spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b
(integer) 3143
127.0.0.1:6379>
  • 访问 spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b的时候,判断key是否过期,过期则删除,否则返回改进的值。
  • 例如 访问spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b的时候,判断 ttl 是否过期,过期就直接删除
2) "spring:session:sessions:expires:d3434f61-4d0a-4687-9070-610bd7790f3b" 
3) "spring:session:expirations:1635413520000" 
6) "spring:session:sessions:d3434f61-4d0a-4687-9070-610bd7790f3b"

更多编程相关知识,请访问:编程视频!!

The above is the detailed content of What to do about distributed session inconsistency in redis. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:juejin.cn. If there is any infringement, please contact admin@php.cn delete