首页 > 数据库 >Redis篇--应用篇1--会话存储(session共享)

Redis篇--应用篇1--会话存储(session共享)

时间:2024-12-27 12:31:46浏览次数:5  
标签:Session -- Redis session Cookie import 服务器

1、概述

实现Session共享是构建分布式Web应用时的一个重要需求,尤其是在水平扩展和高可用性要求较高的场景下。

在分布式服务或集群服务中往往会出现这样一个问题:用户登录A服务后可以正常访问A服务中的接口。但是我们知道,分布式服务通常都是有多个微服务一起构建形成的。如果后续请求转发到了B服务,B服务后端没有这个用户的Session信息,就会强制让用户重新登录,导致业务无法顺利完成。因此,就需要将Session进行共享,保证每个系统都能获取用户的Session状态。

Redis可以用来存储Web应用的用户会话信息,支持快速的会话管理和跨服务器的会话共享。

2、常见实现session共享的方式

(1)、客户端存储(使用Cookie)

实现方式

  • 原理:将Session数据直接存储在客户端的Cookie中,每次请求时,浏览器会自动将Cookie发送给服务器。

优点:

  • 无服务器状态:服务器不需要存储Session数据,减少了服务器的内存占用和状态管理开销。
  • 易于扩展:由于服务器无状态,可以轻松地进行水平扩展,无需担心Session同步或共享问题。
  • 简单实现:实现相对简单,适合小型应用或临时解决方案。

缺点:

  • 安全性低:Cookie中的Session数据容易被篡改或窃取,存在安全风险。即使使用加密,也无法完全防止攻击(如XSS攻击)。
  • 可靠性差:如果用户的浏览器禁用了Cookie,或者用户手动清除了Cookie,Session信息将会丢失。
  • 大小限制:Cookie的最大大小为4KB,无法存储较大的Session数据。
  • 跨域问题:Cookie只能在同源域名下有效,跨域时需要额外处理。

适用场景:

  • 轻量级应用:适合对安全性要求不高、Session数据较少且不需要持久化的应用场景。
  • 单页应用(SPA):对于前后端分离的单页应用,可以结合JWT(JSON Web Token)来实现无状态的身份验证。

代码示例:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@RestController
public class CookieSessionController {

    private static final String SESSION_COOKIE_NAME = "sessionId";

    @GetMapping("/set-cookie")
    public String setCookie(HttpServletResponse response) {
        // 生成唯一的Session ID
        String sessionId = UUID.randomUUID().toString();

        // 创建Cookie并设置有效期为1小时
        Cookie cookie = new Cookie(SESSION_COOKIE_NAME, sessionId);
        cookie.setPath("/");
        cookie.setMaxAge(60  60); // 1小时

        // 将Cookie添加到响应中
        response.addCookie(cookie);

        return "Cookie set with session ID: " + sessionId;
    }

    @GetMapping("/get-cookie")
    public String getCookie(HttpServletRequest request) {
        // 从请求中获取Cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(SESSION_COOKIE_NAME)) {
                    return "Session ID from Cookie: " + cookie.getValue();
                }
            }
        }
        return "No session ID found in Cookie.";
    }
}

说明:
通过set-cookie的方法,将必要的用户信息保存到response中,返回给浏览器。浏览器会自动将response中这些需要设置cookie的信息保存到cookie中,之后对相同的域进行请求,就会自动携带这些cookie信息。

(2)、Session绑定(Nginx IP绑定策略)

实现方式
通过Nginx的ip_hash指令,将来自同一IP的请求始终路由到同一台后端服务器。这样可以确保用户的Session信息保存在同一台服务器上。

优点:

  • 简单实现:配置简单,只需在Nginx中设置ip_hash即可。
  • 性能较好:由于Session数据直接存储在本地内存中,访问速度较快。

缺点:

  • 单点故障:如果某一台服务器宕机,该服务器上的Session信息将会丢失,导致用户需要重新登录或重新创建Session。
  • 负载不均衡:由于同一个IP的请求总是路由到同一台服务器,可能会导致某些服务器的负载过高,而其他服务器闲置。
  • 不适合动态IP:如果用户的IP地址频繁变化(如移动设备),可能会导致Session丢失或错误的Session分配。
  • 扩展性差:随着用户数量的增加,单台服务器的压力会越来越大,难以进行水平扩展。

适用场景:

  • 小型应用:适合用户量较小、服务器数量有限的应用场景。
  • 内部系统:对于内部系统或企业内部网,用户的IP地址相对固定,可以考虑使用这种方式。

示例:(nginx配置)

http {
    upstream backend {
        ip_hash;   使用IP哈希算法,确保同一IP的请求总是路由到同一台服务器
        server 192.168.1.1:8080;
        server 192.168.1.2:8080;
    }

    server {
        listen 80;

        location / {
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

说明:
nginx配置ip_hash后,每一次经过nginx代理的请求。nginx都会将请求的ip代理到固定的服务Ip上(实际是请求ip通过哈希算法映射的结果)。这种方法保证了来自同一IP的请求始终会路由到同一台后端服务器上。

(3)、Session同步(Tomcat内置Session同步)

实现方式

  • Tomcat提供了内置的Session复制机制,可以通过集群中的多台服务器之间同步Session数据。
    常见的同步方式包括:
    • 内存复制:每台服务器将Session数据复制到其他服务器的内存中。
    • Delta复制:只复制Session中发生变化的部分,减少同步的数据量。
    • 文件存储:将Session数据写入共享文件系统,所有服务器从该文件系统读取Session数据。

优点:

  • 高可用性:即使某一台服务器宕机,其他服务器仍然可以访问到用户的Session信息,避免了Session丢失。
  • 自动同步:无需额外开发,Tomcat内置了Session同步功能,配置相对简单。

缺点:

  • 同步延迟:Session同步可能会产生一定的延迟,尤其是在网络状况不佳或Session数据较大时,影响用户体验。
  • 性能开销:每次请求都会触发Session同步操作,增加了服务器之间的通信开销,降低了整体性能。
  • 扩展性有限:随着集群规模的扩大,Session同步的复杂性和开销也会增加,可能导致性能瓶颈。
  • 资源浪费:所有服务器都需要存储相同的Session数据,占用了大量的内存资源。

适用场景:

  • 中小型应用:适合用户量适中、服务器数量不多的应用场景。
  • 对Session同步要求不高的应用:如果Session数据更新频率较低,且对同步延迟不敏感,可以考虑使用这种方式。

(4)、Session共享(Redis等缓存中间件)

实现方式

  • 将Session数据存储在独立的缓存中间件(如Redis、Memcached)中,所有服务器都可以通过访问该中间件来获取和更新Session数据。常见的实现方式包括:
    • Redis作为Session存储:将Session数据以键值对的形式存储在Redis中,使用唯一的Session ID作为键。
    • Spring Session:Spring框架提供了Spring Session模块,可以轻松集成Redis作为Session存储。

优点:

  • 高可用性:Session数据集中存储在Redis中,所有服务器都可以访问,避免了单点故障。即使某一台服务器宕机,其他服务器仍然可以继续使用用户的Session。
  • 高性能:Redis是基于内存的NoSQL数据库,具有极高的读写性能,能够快速响应Session请求。
  • 易于扩展:Redis支持集群模式,可以根据需要水平扩展,满足大规模应用的需求。
  • 灵活性强:可以结合Redis的过期时间、持久化等功能,灵活管理Session的生命周期。
  • 减轻服务器压力:Session数据不再存储在服务器内存中,减少了服务器的内存占用,提升了服务器的性能。

缺点:

  • 依赖外部服务:需要额外部署和维护Redis等缓存中间件,增加了系统的复杂性。
  • 网络延迟:虽然Redis的性能很高,但仍然存在网络延迟,尤其是在跨机房或跨区域部署时,可能会影响Session的读取速度。
  • 数据一致性问题:如果Redis集群出现故障或网络分区,可能会导致Session数据的一致性问题。

适用场景:

  • 大型分布式应用:适合用户量大、服务器数量多的分布式应用,尤其是需要高可用性和水平扩展的场景。
  • 高并发应用:适合对性能要求较高的应用,Redis的高效读写能力可以应对大量并发请求。
  • 微服务架构:在微服务架构中,多个服务实例可以共享同一个Redis实例,方便统一管理Session。
Redis实现session共享示例代码

第一步:导入依赖

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

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

第二步:配置文件

spring.redis.host=127.0.0.1
spring.redis.port=6379
#spring.redis.password=
spring.redis.database=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=500
spring.redis.pool.min-idle=0
spring.redis.timeout=500

spring.session.store-type=redis    #使用Redis作为Session存储
spring.session.timeout=1800s       #Session超时时间

第三步:配置类
需要注解启用RedisSession。同时配置Redis的序列化方式。

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.web.http.HeaderHttpSessionIdResolver;
import org.springframework.session.web.http.HttpSessionIdResolver;

@Configuration
@EnableRedisHttpSession        // 启用Redis session
public class SessionConfig {

    // 可以自定义session id解析器,这里我们使用header解析器
    @Bean
    public HttpSessionIdResolver httpSessionIdResolver() {
        return HeaderHttpSessionIdResolver.xAuthToken();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        // 设置键的序列化器为 StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        // 设置值的序列化器为 Jackson2JsonRedisSerializer
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

第四步:测试类

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.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/session")
public class SessionController {

    @GetMapping("/set")
    @ResponseBody
    public String setSessionAttribute(HttpServletRequest request) {
        HttpSession session = request.getSession();
        session.setAttribute("username", "John Doe");
        return "Session attribute 'username' set to 'John Doe'.";
    }

    @GetMapping("/get")
    @ResponseBody
    public String getSessionAttribute(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            String username = (String) session.getAttribute("username");
            return "Session attribute 'username': " + username;
        } else {
            return "No session found.";
        }
    }
}

第五步:测试验证
当调用get方法后,查看Redis可以发现已经存在session信息了。
在这里插入图片描述

3、四种实现session共享方案对比

在这里插入图片描述
如果使用分布式系统,用户量大,服务器数量多,且对高可用性和性能有较高要求*,强烈推荐使用Session共享(Redis等缓存中间件)*。Redis的高性能和可扩展性使其成为最合适的方案,尤其是在微服务架构中。

进一步优化:

  • 结合JWT:对于无状态的应用,可以考虑使用JWT(JSON Web Token)来替代传统的Session机制。
  • Redis集群:为了提高Redis的可用性和性能,可以使用Redis集群或哨兵模式(Sentinel)。
  • Session压缩:如果Session数据较大,可以考虑对Session数据进行压缩后再存储到Redis中。
  • TTL设置:合理设置Session的过期时间(TTL),避免长时间未使用的Session占用过多资源。

标签:Session,--,Redis,session,Cookie,import,服务器
From: https://blog.csdn.net/qq_34207422/article/details/144747200

相关文章

  • Sql时间格式化
    selectreplace(replace(replace(CONVERT(varchar,getdate(),120),'-',''),'',''),':','')20040912110608selectCONVERT(varchar(12),getdate(),111)2004/09/12selectCONVERT(varchar(12),get......
  • 闭环控制中的开关电源(BUCK)
    闭环控制中的开关电源(BUCK)是一种高效的直流-直流(DC-DC)转换器,其核心原理基于电感的储能特性和开关管的快速通断控制。一、工作原理BUCK电路又称降压电路,其工作原理如下:当开关管(如S或Q)驱动为高电平时,开关管导通,此时储能电感(如L)被充磁,流经电感的电流线性增加。同时,电容(如C)开始......
  • vue3 拖动弹窗
    <scriptsetuplang="ts">import{useDraggable,useMouseInElement}from'@vueuse/core';importtype{CSSProperties}from'vue';interfaceProps{isMask?:booleandialogStyle?:Record<string,any>......
  • 2024网络安全学习路线,最全保姆级教程,学完直接拿捏!
    CSDN独家网络安全资料包!点这里即可获取  关键词: 网络安全入门、渗透测试学习、零基础学安全、网络安全学习路线首先咱们聊聊,学习网络安全方向通常会有哪些问题1、打基础时间太长学基础花费很长时间,光语言都有几门,有些人会倒在学习linux系统及命令的路上,更多的人......
  • mybatis-plus 用法总结
    MyBatis-Plus(简称MP)是MyBatis的增强工具,旨在简化开发者的CRUD操作。它在MyBatis的基础上提供了更多的功能和便利性,如代码生成器、分页插件、性能分析插件等,使开发者能够更高效地进行数据库操作。MyBatis-Plus保持了MyBatis原有的灵活性和易用性,同时通过一些约定和默......
  • Linux基础篇、学习习题测试_01
    Linux基础篇欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神!                                                    ———laowang题目一1.网络配置使......
  • Maven仓库配置
    阿里云镜像仓库地址仓库服务(aliyun.com)获取配置信息修改settings.xml配置文件1.配置阿里云镜像加速<mirror><id>aliyunmaven</id><mirrorOf>*</mirrorOf><name>阿里云公共仓库</name><url>https://maven.aliyun.com/repository/public</url>......
  • Linux基础篇、学习习题测试_01答案
    Linux基础篇欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神!                                                                                 ————......
  • 2024网络安全学习路线 非常详细 推荐学习
     关键词:网络安全入门、渗透测试学习、零基础学安全、网络安全学习路线首先咱们聊聊,学习网络安全方向通常会有哪些问题1、打基础时间太长学基础花费很长时间,光语言都有几门,有些人会倒在学习linux系统及命令的路上,更多的人会倒在学习语言上;2、知识点掌握程度不清楚......
  • 新手必看——ctf六大题型介绍及六大题型解析&举例解题
    CTF(CaptureTheFlag)介绍与六大题型解析一、什么是CTF?CTF(CaptureTheFlag),意为“夺旗赛”,是一种信息安全竞赛形式,广泛应用于网络安全领域。CTF竞赛通过模拟现实中的网络安全攻防战,让参赛者以攻防对抗的形式,利用各种信息安全技术进行解决一系列安全问题,最终获得“旗......