1.session
- Session 用于维持状态:
Session 是一种在服务器端存储用户特定信息的机制,用于在多次请求间识别和跟踪用户。
- 工作流程:
- 用户首次登录或访问时,服务器会创建一个唯一的
Session ID
。 - 这个
Session ID
通过Set-Cookie
头部发送给浏览器,通常存储在名为JSESSIONID
或类似的 Cookie 中。 - 浏览器的后续每个请求都会自动带上这个包含
Session ID
的 Cookie。 - 服务器接收到请求后,通过
Session ID
找到对应的服务器端存储的 Session 数据(如用户ID、用户名、权限等),从而知道是谁在请求。
- 用户首次登录或访问时,服务器会创建一个唯一的
- Redis 作为 Session 存储的优势:
- 集中式管理:所有应用服务器(节点 A、B、C…)都连接同一个 Redis 实例/集群。无论请求被分发到哪个节点,它们都能从同一个地方读写 Session 数据,完美解决了分布式 Session 共享问题。
- 高性能:Redis 是基于内存的数据存储,读写速度极快,与直接读本地内存相差无几,不会成为性能瓶颈。
- 持久化:虽然 Redis 是内存数据库,但它支持 RDB 快照和 AOF 日志两种持久化机制,可以防止服务器重启或宕机导致所有 Session 丢失。
- 自动过期:Redis 原生支持为每个键设置生存时间(TTL)。这与 Session 的超时机制天然契合。只需在存储 Session 时设置一个过期时间(如 30 分钟),Redis 会自动清理过期的 Session,无需额外代码。
- 丰富的数据结构:Session 数据本质上是一个键值对对象。Redis 的 Hash 数据结构非常适合存储这种对象,可以方便地对单个字段进行读写,而不用每次都序列化/反序列化整个对象。
- 优点总结与注意事项
1.分布式友好
2.高性能
3.自动过期
4.持久化可能性
- 注意事项:
Redis 高可用:由于 Session 集中存储在 Redis,它成为了一个关键的单点。在生产环境中,必须配置 Redis 哨兵(Sentinel)或集群(Cluster)模式来保证高可用性;
网络延迟:相比本地内存,多了一次网络 I/O,但在内网环境下,这个延迟通常可以忽略不计;
序列化/反序列化:存储复杂对象时需要考虑序列化方式(如 JSON、MessagePack、Java 原生序列化等);内存大小:所有 Session 都存储在内存中,需要监控 Redis 的内存使用情况,确保有足够的内存。
- 结论
Redis + Session 是一个强大、高效且成熟的组合,是构建现代无状态、可扩展的 Web 应用的基石之一。它解决了分布式系统中的状态共享难题,同时保持了极高的性能。只要做好 Redis 本身的高可用和容量规划,它就是 Session 存储的最佳选择之一。
2.socket
1. 什么是 Socket?
Socket(套接字)是网络通信的端点,是支持 TCP/IP 协议的网络通信的基本操作单元。你可以把它想象成一个网络连接的电话听筒:
- IP 地址:相当于电话号码
- 端口号:相当于分机号
- Socket:就是连接这两端的通话设备
在 Redis 的语境中,Socket 特指 Redis 客户端与 Redis 服务器之间的网络连接。
2.总结
Redis Socket 是 Redis 通信的基石:
- 网络 Socket:用于跨机器通信,灵活通用
- Unix Socket:用于本机通信,性能更高
- 连接池:管理 Socket 连接,提高效率
- 配置调优:超时、keepalive、最大连接数等参数对稳定性至关重要
理解 Redis Socket 的工作原理对于诊断连接问题、优化性能以及构建稳定的 Redis 应用至关重要。
3.ThreadLocal
- 什么是 ThreadLocal?
ThreadLocal
是 Java 中提供的一个线程级别的变量隔离机制。它允许你创建一个变量,每个访问该变量的线程都会有自己独立的副本,线程之间不会相互干扰。
- ThreadLocal 的局限性
- 不适合线程池:线程复用会导致数据混乱
- 内存泄漏风险:必须手动清理
- 调试困难:数据流不明确
- 上下文传递复杂:异步编程中需要额外处理
- 总结
ThreadLocal
是一个强大的线程隔离工具:
1.核心价值:为每个线程提供独立的变量副本
2.典型应用:用户上下文、数据库连接、事务管理
3.关键注意:必须及时清理,防止内存泄漏
4.适用场景:Web 请求上下文、线程封闭的资源配置等
正确使用 ThreadLocal
可以大大简化多线程编程的复杂度,但需要对其原理和潜在问题有清晰的认识。
4.Token
- 什么是 Token?
Token 是一个代表用户身份和权限的凭证,通常是一串加密的字符串。客户端在登录后获得 Token,并在后续请求中携带它来证明自己的身份。
- 为什么需要 Token?
传统 Session 的问题:
1. 服务器内存存储压力大
2. 分布式环境下需要 Session 共享
3. CSRF 攻击风险
4. 移动端支持不友好
- Token 的优势
- 无状态:服务器不需要存储会话信息
- 跨域友好:轻松支持 CORS
- 移动端友好:天然适合 App
- 防止 CSRF:可存储在 localStorage
- 微服务友好:易于在服务间传递身份
- Token 的类型
1. JWT (JSON Web Token)
// JWT 结构:Header.Payload.Signature
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
// Header: {"alg": "HS256", "typ": "JWT"}
// Payload: {"sub": "1234567890", "name": "John Doe", "iat": 1516239022}
// Signature: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
2. Access Token & Refresh Token
// 双 Token 机制
public class TokenPair {
private String accessToken; // 短期访问令牌 (30分钟)
private String refreshToken; // 长期刷新令牌 (7天)
private String tokenType = "Bearer";
private Long expiresIn;
}
3. Opaque Token(不透明令牌)
// 自定义格式的 Token
public class CustomToken {
private String token; // 随机字符串
private Long userId; // 用户ID
private Long expireTime; // 过期时间
private String device; // 设备信息
}
- 总结
Token 是现代 Web 应用和移动应用身份认证的首选方案:
1.JWT:适合无状态场景,自包含信息
2.Redis Token:适合需要服务端控制的情景
3.双 Token 机制:平衡安全性和用户体验
4.安全存储:根据场景选择合适的客户端存储方案
5.及时刷新:实现滑动会话过期
Token 技术的正确实施能够构建安全、可扩展的认证授权系统。
5.setnx
SETNX
是 Redis 中非常重要的命令,用于实现分布式锁
1.命令语法:SETNX key value
2.作用:当 key 不存在时设置值,返回 1;key 已存在时不做任何操作,返回 0
3.基础分布式锁
@Service
public class DistributedLockService {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 尝试获取锁
*/
public boolean tryLock(String lockKey, String requestId, long expireTime) {
return Boolean.TRUE.equals(
redisTemplate.opsForValue().setIfAbsent(lockKey, requestId,
Duration.ofSeconds(expireTime))
);
}
/**
* 释放锁
*/
public boolean releaseLock(String lockKey, String requestId) {
String currentValue = redisTemplate.opsForValue().get(lockKey);
if (requestId.equals(currentValue)) {
redisTemplate.delete(lockKey);
return true;
}
return false;
}
4.安全的分布式锁(Lua 脚本实现)
@Service
public class SafeDistributedLock {
@Autowired
private StringRedisTemplate redisTemplate;
// 加锁 Lua 脚本
private static final String LOCK_SCRIPT =
"if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +
" return redis.call('expire', KEYS[1], ARGV[2]) " +
"else " +
" return 0 " +
"end";
// 释放锁 Lua 脚本
private static final String UNLOCK_SCRIPT =
"if redis.call('get', KEYS[1]) == ARGV[1] then " +
" return redis.call('del', KEYS[1]) " +
"else " +
" return 0 " +
"end";
private final RedisScript<Long> lockScript =
RedisScript.of(LOCK_SCRIPT, Long.class);
private final RedisScript<Long> unlockScript =
RedisScript.of(UNLOCK_SCRIPT, Long.class);
/**
* 安全的获取锁
*/
public boolean safeLock(String lockKey, String requestId, int expireSeconds) {
Long result = redisTemplate.execute(
lockScript,
Collections.singletonList(lockKey),
requestId,
String.valueOf(expireSeconds)
);
return result != null && result == 1;
}
/**
* 安全的释放锁
*/
public boolean safeUnlock(String lockKey, String requestId) {
Long result = redisTemplate.execute(
unlockScript,
Collections.singletonList(lockKey),
requestId
);
return result != null && result == 1;
}
}
5.总结
SETNX
是 Redis 分布式锁的基础,但在生产环境中建议:
- 简单场景:使用
setIfAbsent
带过期时间 - 复杂场景:使用 Lua 脚本保证原子性
- 生产环境:推荐使用 Redisson 等成熟框架
这样可以避免常见的分布式锁问题,如死锁、误释放、锁续期等。