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

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占用过多资源。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/942938.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ip-协议

文章目录 1. 网络层2. ip协议2.1 ip协议格式2.2 网段划分基本概念网段划分的两种方式为什么要网段划分&#xff1f;特殊的IP地址IP地址数量不足 2.3 私有IP与公网IP2.4 路由 3. IP的分片与组装为什么要分片与组装&#xff1f;如何分片&#xff1f;如何组装&#xff1f; 1. 网络…

ECharts散点图-气泡图,附视频讲解与代码下载

引言&#xff1a; ECharts散点图是一种常见的数据可视化图表类型&#xff0c;它通过在二维坐标系或其它坐标系中绘制散乱的点来展示数据之间的关系。本文将详细介绍如何使用ECharts库实现一个散点图&#xff0c;包括图表效果预览、视频讲解及代码下载&#xff0c;让你轻松掌握…

Jmeter录制https请求

jmeter 5.5版本&#xff0c;chrome浏览器 1、首先添加Test Plan-Thread Group-HTTP(S) Test Script Recorder 2、设置HTTP(S) Test Script Recorder界面的Port&#xff08;监听端口&#xff0c;设置浏览器代理时需要与这里保持一致&#xff09;、HTPS Domains&#xff08;录制…

【Git 常用操作:pull push】

Git 基本概念 Git 是一个先进的开源的分布式版本控制系统&#xff0c;常用于管理工作内容、项目代码等功能。 Git 工作流程 图片来源&#xff1a;https://www.runoob.com/git/git-basic-operations.html 说明&#xff1a; workspace&#xff1a;工作区staging area&#xff…

LLaMA-Factory GLM4-9B-CHAT LoRA 指令微调实战

&#x1f929;LLaMA-Factory GLM LoRA 微调 安装llama-factory包 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git进入下载好的llama-factory&#xff0c;安装依赖包 cd LLaMA-Factory pip install -e ".[torch,metrics]" #上面这步操作会完成…

基于kraft部署kafka集群

kafka介绍 Apache Kafka 是一个开源的分布式事件流平台&#xff0c;被数千家公司用于高性能数据管道、流分析、数据集成和关键任务应用。 Kafka是一个拥有高吞吐、可持久化、可水平扩展&#xff0c;支持流式数据处理等多种特性的分布式消息流处理中间件&#xff0c;采用分布式…

Day13 苍穹外卖项目 工作台功能实现、Apache POI、导出数据到Excel表格

目录 1.工作台 1.1 需求分析和设计 1.1.1 产品原型 1.1.2 接口设计 1.2 代码导入 1.2.1 Controller层 1.2.2 Service层接口 1.2.3 Service层实现类 1.2.4 Mapper层 1.3 功能测试 1.4 代码提交 2.Apache POI 2.1 介绍 2.2 入门案例 2.2.1 将数据写入Excel文件 2.2.2 读取Excel文…

Web前端基础知识(三)

表单的应用非常丰富&#xff0c;可以说&#xff0c;每个网站都会用到表单。下面首先介绍表单中的form标签。 --------------------------------------------------------------------------------------------------------------------------------- <form></form&g…

NLP中的神经网络基础

一&#xff1a;多层感知器模型 1&#xff1a;感知器 解释一下&#xff0c;为什么写成 wxb>0 &#xff0c;其实原本是 wx > t ,t就是阈值&#xff0c;超过这个阈值fx就为1&#xff0c;现在把t放在左边。 在感知器里面涉及到两个问题&#xff1a; 第一个&#xff0c;特征提…

docker安装MySQL--宝塔面板操作版

记录 1 在centos中安装宝塔面板 参照宝塔面板官方网页上步骤进行操作&#xff0c;然后登录网页地址 成功后直接拉取 成功后可以在本地镜像中看到 2 创建配置文件 cd /home/mysql/conf vim my.cnf [rootplmomn-gw conf]# cat /home/mysql/conf/my.cnf [client] #设置客户端…

C++简明教程(3)(初识VS)

一、编程工具大揭秘——IDE 当我们准备踏入 C 编程的奇妙世界时&#xff0c;首先要认识一个重要的“魔法盒子”——集成开发环境&#xff08;IDE&#xff09;。IDE 就像是一个全能的编程工作室&#xff0c;它把我们写代码所需要的各种工具都整合到了一起&#xff0c;让编程这件…

电脑出现 0x0000007f 蓝屏问题怎么办,参考以下方法尝试解决

电脑蓝屏是让许多用户头疼的问题&#xff0c;其中出现 “0x0000007f” 错误代码更是较为常见且棘手。了解其背后成因并掌握修复方法&#xff0c;能帮我们快速恢复电脑正常运行。 一、可能的硬件原因 内存问题 内存条长时间使用可能出现物理损坏&#xff0c;如金手指氧化、芯片…

分布式调度框架学习笔记

一、分布式调度框架的基本设计 二、线程池线程数量设置的基本逻辑 cpu是分时复用的方法&#xff0c;线程是cpu调度的最小单元 如果当前cpu核数是n&#xff0c;计算密集型线程数一般设为n&#xff0c;io密集型(包括磁盘io和网络io)线程数一般设置为2n. 计算密集型线程数一般设…

快速排序算法 -- 深入研究

一 . 快排性能的关键点分析 快排性能的关键点分析 : 决定快排性能的关键点是每次单趟排序后 &#xff0c; key 对数组的分割 &#xff0c; 如果每次选key 基本二分居中&#xff0c;那么快排的递归树就是颗均匀的满二叉树&#xff0c;性能最佳。但是实际中虽然不可能每次都是二…

ORA-65198 PDB clone 时 不能新加datafile 以及hang的一个原因

create pluggable database XX from SS keystore identified by "YYY" parallel 32 service_name_convert( _srv, _srv); 20TB 4小时 update /* rule */ undo$ set name:2,file#:3,block#:4,status$:5,user#:6,undosqn:7,xactsqn:8,scnbas:9,scnwrp:10,inst#:11,…

Android--java实现手机亮度控制

文章目录 1、开发需求2、运行环境3、主要文件4、布局文件信息5、手机界面控制代码6、debug 1、开发需求 需求&#xff1a;开发一个Android apk实现手机亮度控制 2、运行环境 Android studio最新版本 3、主要文件 app\src\main\AndroidManifest.xml app\src\main\res\layou…

HarmonyOS NEXT 实战之元服务:静态案例效果--- 日出日落

背景&#xff1a; 前几篇学习了元服务&#xff0c;后面几期就让我们开发简单的元服务吧&#xff0c;里面丰富的内容大家自己加&#xff0c;本期案例 仅供参考 先上本期效果图 &#xff0c;里面图片自行替换 效果图1完整代码案例如下&#xff1a; import { authentication } …

一起学Git【番外篇:如何在Git中新建文件】

在介绍Git之前&#xff0c;我们需要了解一下如何在Git里添加、编辑和删除文件。 首先&#xff0c;需要使用文件编辑器进行文件的创建&#xff0c;常见的文件编辑器有以下几种&#xff1a; Vim&#xff1a;一种基于命令行的编辑器&#xff0c;功能强大&#xff0c;适合开发者和…

叉车作业如何确认安全距离——UWB测距防撞系统的应用

叉车在工业环境中运行&#xff0c;常常需要在狭窄的空间内完成货物的搬运和堆垛&#xff0c;这对操作员的技术水平和安全意识提出了极高的要求。传统的叉车作业依赖操作员的经验和视觉判断来确认安全距离&#xff0c;然而这种方式往往存在误差&#xff0c;特别是在视线受阻或光…

hi168大数据离线项目环境搭建

hi168大数据离线项目环境搭建 ## **1. 服务器准备**##### 1.1 创建集群应用节点 集群服务器使用“我的应用“中的Ubuntu22.04集群模版创建三个节点应用&#xff0c;并且进入“我的应用”中去修改一下节点名称&#xff08;node1对应master&#xff0c;node2对应hadoop1&#xf…