>  기사  >  데이터 베이스  >  Redis에서 분산 세션 불일치에 대해 수행할 작업

Redis에서 분산 세션 불일치에 대해 수행할 작업

青灯夜游
青灯夜游앞으로
2021-11-12 10:47:142529검색

분산 세션이 일관성이 없으면 어떻게 해야 하나요? 다음 기사에서는 Redis의 분산 세션 불일치에 대한 솔루션을 소개하겠습니다. 도움이 되길 바랍니다.

Redis에서 분산 세션 불일치에 대해 수행할 작업

분산 세션 불일치 솔루션

1. 세션의 역할은 무엇인가요?

  • Session은 클라이언트와 서버 사이의 통신 세션 추적 기술로, 서버와 클라이언트는 전체 통신의 기본 세션 정보를 유지합니다. [관련 권장 사항: Redis 비디오 튜토리얼]

  • 클라이언트가 처음으로 서버에 액세스하면 서버는 sessionId로 응답하고 이를 로컬 쿠키에 저장합니다. 이후 방문에서는 sessionId가 쿠키에 저장됩니다. sessionId는 서버에 접속하기 위해 요청 헤더에 넣어집니다.

  • 이 sessionId를 통해 해당 데이터를 찾을 수 없으면 서버는 새로운 sessionId를 생성하고 클라이언트에 응답합니다.

2. 분산 세션의 문제점은 무엇입니까?

단일 서버 웹 애플리케이션에서는 세션 정보만 서버에 저장하면 됩니다. 이는 지난 몇 년 동안 우리가 접한 가장 일반적인 방법입니다.

그러나 분산 시스템의 인기로 인해 최근 몇 년 동안 단일 시스템은 더 이상 증가하는 요구를 충족할 수 없습니다. 수백만 명의 사용자 수요가 증가함에 따라 많은 회사에서 클러스터 배포 서버를 사용하고 있습니다

고동시성 요청이 서버에 도착하면 서버에 분산됩니다. 동일한 사용자의 여러 요청이 클러스터의 다른 서버로 분산되면 세션 데이터를 가져오지 못하므로 세션 공유에 문제가 발생할 수 있습니다.

Redis에서 분산 세션 불일치에 대해 수행할 작업

3. 서비스 클러스터링은 일반적으로 어떻게 이루어지나요?

  • SpringBoot 프로젝트를 수행한 다음 포트 번호를 변경하여 몇 개를 시작한 다음 nginx를 통합 역방향 프록시로 사용하세요.
  • SpringCloud 마이크로서비스 프로젝트라면 이때 리본 로컬 로드 밸런싱을 사용할 수 있습니다.

4. nginx 로드 밸런싱과 리본 로드 밸런싱의 차이점

  • nginx 로드 밸런싱은 서버 측 로드 밸런싱으로, 주소에 균일하게 액세스하고, 로드 밸런싱 알고리즘을 기반으로 어느 서버에 액세스할지 결정합니다.
  • 리본 로드 밸런싱, 서비스를 제공하는 클라이언트 주소를 캐시하고 기록하며, 로컬 알고리즘을 기반으로 로드 밸런싱을 구현하는 로컬 로드 밸런싱(클라이언트 로드 밸런싱)입니다.

5. 세션 일관성 솔루션

1. 세션 복제(동기화)

Redis에서 분산 세션 불일치에 대해 수행할 작업

아이디어: 여러 서버가 세션을 서로 동기화하여 각 서버에 모든 세션이 포함됩니다. 세션

장점: 서버에서 지원되는 기능, 애플리케이션에서 코드를 수정할 필요가 없습니다

단점:

  • 세션 동기화에는 데이터 전송이 필요하며, 이는 인트라넷 대역폭을 차지하고 지연이 발생합니다.
  • 모든 서버에는 모든 세션이 포함됩니다. 데이터의 양은 메모리에 의해 제한되며 수평 확장이 불가능합니다

2. 클라이언트 저장 방법

Redis에서 분산 세션 불일치에 대해 수행할 작업

아이디어: 서버는 모든 사용자 세션을 저장하며, 메모리 사용량이 크기 때문에 세션이 브라우저 쿠키에 저장 가능, 각 끝은 한 명의 사용자 데이터만 저장하면 됩니다

장점: 서버는 저장할 필요가 없습니다

단점:

  • 모든 http 요청은 세션을 전달하여 외부 네트워크 대역폭을 차지합니다
  • 데이터 최종적으로 저장되어 네트워크를 통해 전송되기 때문에 세션에 저장되는 데이터 크기와 도메인 이름 쿠키 수가 제한되어 있지만 일반적으로 사용되는 솔루션은 아닙니다. , 그것은 실제로 아이디어입니다.
3. 역방향 프록시 해시 일관성

아이디어: 고가용성을 보장하기 위해 서버에 여러 개의 중복 서버가 있습니다. 역방향 프록시 계층이 동일한 사용자의 요청이 포함되도록 뭔가를 할 수 있습니까? 섬기는 사람? 옵션 1: 4계층 프록시 해시

역방향 프록시 계층은 해싱에 사용자의 IP를 사용하여 동일한 IP를 가진 요청이 동일한 서버에 있는지 확인합니다.

Redis에서 분산 세션 불일치에 대해 수행할 작업옵션 2: 7계층 에이전트 해시

역방향 프록시는 http 프로토콜의 특정 비즈니스 속성을 사용하여 sid, city_id, user_id 등과 같은 해싱을 수행합니다. 이는 동일한 브라우저 사용자의 요청이 동일한 서버에 있는지 확인하기 위해 해시 전략을 보다 유연하게 구현할 수 있습니다

장점:

  • nginx 구성만 변경하면 되며 애플리케이션 코드를 수정할 필요가 없습니다
  • 로드 밸런싱, 해시 속성이 균일한 한 여러 서버의 로드가 균형을 이룹니다
  • 서버의 수평 확장을 지원할 수 있습니다 (세션 동기화 방법 불가) (메모리 제한)

단점:

  • 서버가 재시작되면 세션의 일부가 손실되어 일부 사용자가 재로그인하는 등 업무에 영향을 미칠 수 있습니다
  • 서버가 수평으로 확장되고 재해시 후 세션이 재분배되는 경우도 있습니다. 일부 사용자는 올바른 세션으로 라우팅될 수 없습니다

세션에는 일반적으로 유효 기간이 있으며, 이 두 가지 단점은 부분 세션 실패와 동일하다고 간주할 수 있습니다. 일반적으로 큰 문제는 아닙니다.

4계층 해싱 또는 7계층 해싱과 관련하여 저는 개인적으로 전자를 권장합니다. 전문 소프트웨어가 전문적인 작업을 수행하도록 하고 역방향 프록시는 전달을 담당합니다. 꼭 필요한 경우가 아니면 애플리케이션 계층 비즈니스 속성을 도입하지 마세요. , 때로는 컴퓨터실의 다중 활동이 비즈니스 속성에 따라 다른 컴퓨터실의 서버로 라우팅되어야 하는 경우도 있습니다.

4레이어와 7레이어 로드 밸런싱의 차이점

4. 백엔드 통합 중앙 집중식 스토리지

Redis에서 분산 세션 불일치에 대해 수행할 작업

장점:

  • 보안 위험 없음
  • 수평 확장 가능, 데이터베이스/캐시를 수평으로 자를 수 있음 쪼개기만 하면 됨
  • 서버를 재시작하거나 확장해도 세션 손실이 없음

단점: 네트워크 호출이 추가되고, 애플리케이션 코드를 수정해야 함

DB의 경우 스토리지나 캐시 중 개인적으로 후자를 권장합니다. 세션 읽기 빈도가 매우 높을 것이며 데이터베이스 압력도 상대적으로 높을 것입니다. 세션 고가용성이 필요한 경우 캐시를 고가용성으로 만들 수 있지만 대부분의 경우 세션이 손실될 수 있으므로 일반적으로 고가용성을 고려할 필요가 없습니다.

요약

세션 일관성을 보장하기 위한 일반적인 아키텍처 설계 방법:

  • 세션 동기화 방법: 여러 서버가 서로 데이터를 동기화합니다.
  • 클라이언트 저장 방법 사용자는 자신의 데이터만 저장합니다
  • 역방향 프록시 해시 4계층 해싱과 7계층 해싱을 모두 사용하여 일관성을 유지할 수 있으므로 사용자의 요청이 하나의 서버에 도달하도록 보장합니다.
  • 백엔드 통합 스토리지 서버가 다시 시작되고 확장되며 세션이 손실되지 않습니다. 백엔드 캐시 통합) 스토리지)

6. 사례 사례: SpringSession+redis는 분산 세션 불일치 문제를 해결합니다.

1단계: SpringSession과 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>

2단계: 구성 파일

# 为某个包目录下 设置日志
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=

3단계: 인터셉터 구성

@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: 비즈니스 프로세서가 요청을 처리하기 전에 호출됩니다. 전처리는 인코딩, 보안 제어, 권한 확인 등을 수행할 수 있습니다.
    • postHandle: 비즈니스 프로세서가 요청 처리를 완료한 후 뷰를 생성하기 전에 실행됩니다. 후처리(서비스가 호출되고 ModelAndView가 반환되지만 페이지가 렌더링되지 않음), ModelAndView
    • 를 수정할 수 있는 기회가 있습니다. 완료 후: DispatcherServlet이 요청을 완전히 처리한 후 호출되며 리소스 정리 등에 사용할 수 있습니다. . 처리로 돌아가기(페이지가 렌더링됨)

4단계: 컨트롤러

@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 "成功退出!!";
    }

}

5단계: 엔터티 클래스

@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;
    }

}

6단계: 액세스 테스트

로그 먼저 :http://127.0.0.1:8080/user/login?username=user1&password=user1

다시 쿼리http://127.0.0.1:8080/user/find/user1

7 분석 SpringSession의 redis 원리

1단계: SpringSession의 redis 데이터 구조 분석

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"

공통점: 세 개의 키는 모두 spring:session:으로 시작하는데, 이는 SpringSession의 redis 데이터를 나타냅니다. .

쿼리 유형

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"

2단계: SpringSession의 redis 만료 전략 분석

만료된 데이터의 경우 일반적으로 세 가지 삭제 전략이 있습니다.

  • 예약 삭제, 즉 만료 시간을 설정하는 동안 만료 시간이 되면 즉시 키를 삭제하는 타이머를 생성합니다.

  • 지연 삭제, 즉 키에 액세스할 때 키가 만료되었는지 확인하고, 만료되면 삭제하고, 그렇지 않으면 키 값을 반환합니다.

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

  • 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"

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"

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

위 내용은 Redis에서 분산 세션 불일치에 대해 수행할 작업의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제