跳转到内容

分布式 Session

分布式会话是构建可扩展 Web 应用的关键组件,特别是在集群部署环境中。通过本文档,您将学会如何在 Feat Cloud 中配置和使用基于 Redis 的分布式会话。

在传统的单机应用中,会话信息通常存储在服务器的内存中。但在分布式或集群环境中,这种方式会出现问题,因为用户的请求可能会被负载均衡器分发到不同的服务器实例,导致会话信息无法共享。

Feat Cloud 提供了内置的分布式会话支持,目前支持基于 Redis 的会话存储。通过将用户的会话信息存储在共享的 Redis 数据库中,所有的服务器实例都可以访问和更新同一个用户的会话数据,从而解决了集群环境下的会话共享问题。

Feat Cloud 的分布式会话主要由以下两个核心组件构成:

  1. ClusterSessionManager - 负责管理会话的创建、获取和更新
  2. RedisSession - 代表一个具体的会话实例,提供会话数据的操作接口

要启用分布式会话功能,您需要在 feat.yml 配置文件中进行相应配置。

server:
session:
timeout: 1800 # 会话超时时间(秒),默认30分钟
store-type: redis # 会话存储类型,设置为redis启用分布式会话
feat:
redis:
address: redis://127.0.0.1:6379 # Redis服务器地址
password: # Redis密码(可选)
database: 0 # Redis数据库索引
  • server.session.timeout: 会话超时时间,单位为秒。默认值为1800秒(30分钟)。超过这个时间没有访问的会话会被自动清除。
  • server.session.store-type: 会话存储类型。设置为 redis 以启用分布式会话功能。
  • feat.redis.address: Redis 服务器地址,支持多种格式:
    • redis://127.0.0.1:6379 - 普通连接
    • rediss://127.0.0.1:6380 - SSL加密连接
  • feat.redis.password: Redis 访问密码(如果需要的话)
  • feat.redis.database: Redis 数据库索引,默认为0

下面通过一个完整的示例来展示如何使用分布式会话功能。

首先确保您的 pom.xml 文件中包含了必要的依赖:

<dependencies>
<dependency>
<groupId>tech.smartboot.feat</groupId>
<artifactId>feat-cloud-starter</artifactId>
<version>${feat.version}</version>
</dependency>
<dependency>
<groupId>tech.smartboot</groupId>
<artifactId>redisun</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>

创建 src/main/resources/feat.yml 配置文件:

server:
session:
timeout: 1800
store-type: redis
feat:
redis:
address: redis://127.0.0.1:6379
password:
database: 0

创建 Bootstrap.java 启动类:

@Controller
public class Bootstrap {
@RequestMapping("/")
public String helloWorld(Session session) {
// 将访问次数存储在session中
String visitCount = session.get("visitCount");
if (visitCount == null) {
visitCount = "0";
}
int count = Integer.parseInt(visitCount) + 1;
session.put("visitCount", String.valueOf(count));
return "hello Feat Cloud, visit count: " + count;
}
/**
* 设置session属性
*/
@RequestMapping("/setSession")
public String setSession(Session session) {
session.put("username", "FeatUser");
session.put("role", "developer");
return "Session attributes set successfully";
}
/**
* 获取session属性
*/
@RequestMapping("/getSession")
public String getSession(Session session) {
String username = session.get("username");
String role = session.get("role");
if (username == null || role == null) {
return "No session data found. Please visit /setSession first.";
}
return "Username: " + username + ", Role: " + role;
}
/**
* 清除session
*/
@RequestMapping("/clearSession")
public String clearSession(Session session) {
session.invalidate();
return "Session cleared";
}
public static void main(String[] args) {
FeatCloud.cloudServer().listen();
}
}

分布式会话的工作原理如下图所示:

graph TD A[用户请求] --> B{是否存在Session ID?} B -->|是| C[从Redis获取会话] B -->|否| D[创建新会话] C --> E[验证会话有效性] E -->|有效| F[更新过期时间] E -->|无效| D D --> G[生成Session ID] G --> H[存储到Redis] F --> I[处理业务逻辑] H --> I I --> J[返回响应] J --> K[设置Cookie]

RedisSession 提供了以下核心方法:

获取会话中指定键的值。

String username = session.get("username");

将会话数据存储到 Redis 中。

session.put("username", "FeatUser");

使当前会话失效,删除 Redis 中的会话数据。

session.invalidate();

获取当前会话的唯一标识符。

String sessionId = session.getSessionId();

设置会话的最大存活时间(秒)。

session.setTimeout(3600); // 设置会话1小时后过期

获取会话的最大存活时间(秒)。

int maxAge = session.getTimeout();

根据应用的特点合理设置会话超时时间:

server:
session:
timeout: 1800 # 敏感应用可设置较短时间,如30分钟
# timeout: 86400 # 不敏感应用可设置较长时间,如24小时
store-type: redis

在用户登出或执行敏感操作后及时清理会话数据:

@RequestMapping("/logout")
public String logout(Session session) {
// 清理用户相关会话数据
session.invalidate();
return "Logged out successfully";
}

会话数据存储在 Redis 中,应避免存储大量数据以节省内存和网络传输:

// 不推荐:存储大对象
session.put("userProfile", userProfileJson);
// 推荐:只存储关键信息
session.put("userId", userId);
session.put("username", username);

在使用会话时要考虑异常情况:

@RequestMapping("/profile")
public String profile(Session session) {
try {
String userId = session.get("userId");
if (userId == null) {
return "Please login first";
}
// 处理业务逻辑
return "User profile";
} catch (Exception e) {
// 记录日志
logger.error("Session error", e);
return "Session error occurred";
}
}

在集群部署环境中,分布式会话的架构如下所示:

graph LR LB[负载均衡器] --> A[应用实例1] LB --> B[应用实例2] LB --> C[应用实例N] A --> R[(Redis)] B --> R C --> R style LB fill:#FFE4B5,stroke:#333 style A fill:#87CEEB,stroke:#333 style B fill:#87CEEB,stroke:#333 style C fill:#87CEEB,stroke:#333 style R fill:#DDA0DD,stroke:#333

在这种架构中:

  1. 所有的应用实例都连接到同一个 Redis 实例或 Redis 集群
  2. 用户的会话数据存储在 Redis 中
  3. 无论用户的请求被负载均衡器分发到哪个应用实例,都可以访问到相同的会话数据

分布式会话是构建可扩展、高可用 Web 应用的基础组件。通过 Feat Cloud 内置的支持,您可以轻松地在应用中实现这一功能,为用户提供一致的使用体验。

有关完整示例,请参见 Redis Session 示例