惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
SegmentFault 最新的问题
Google DeepMind News
Google DeepMind News
G
Google Developers Blog
Martin Fowler
Martin Fowler
MongoDB | Blog
MongoDB | Blog
月光博客
月光博客
Jina AI
Jina AI
宝玉的分享
宝玉的分享
人人都是产品经理
人人都是产品经理
D
DataBreaches.Net
V
V2EX
WordPress大学
WordPress大学
T
The Blog of Author Tim Ferriss
Last Week in AI
Last Week in AI
B
Blog
博客园 - 叶小钗
小众软件
小众软件
Stack Overflow Blog
Stack Overflow Blog
P
Proofpoint News Feed
A
About on SuperTechFans
J
Java Code Geeks
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Y
Y Combinator Blog
Microsoft Security Blog
Microsoft Security Blog

博客园 - 张占岭

keycloak~通过用户ID直接置换token keycloak~缓存对象的介绍与作用 wso2-apim-mcp2改版分析 mcp1和mcp2对比 为什么前端项目使用ts比js要多了 多个IDP的用户联合方案的分析 apisix~forward-auth外部认证 apisix~response-rewrite响应头重写 springboot~关于构造方法注入和字段注入的选择 apisix~OpenResty各阶段的介绍 keycloak~aud受众字段的作用及如何生成 wso2~关于workbuddy中mcp在wso2中的授权端点 k8s~避免调度资源碎片化 WEB安全~csrf介绍 WEB安全~DPoP的介绍 WEB安全~csp的介绍 什么是中间人攻击 WEB安全~xss的介绍 k8s~pod资源限制和JVM的XMX配置 关于对wso2和keycloak的token交换的调研 keycloak~实现OAuth 2.0 Token Exchange backstage~实体的介绍及它们的关系 backstage~openapi的接入与protobuf的对比 backstage~将java服务添加到backstage backstage~对接github和gitlab backstage~开始一个backstage应用 apisix~graphQL的支持 wso2几个核心项目介绍 docker~BuildKit的介绍 k8s~secret资源的使用
keycloak~深入了解DefaultSegmentedDataContainer对象解决内...
张占岭 · 2026-01-30 · via 博客园 - 张占岭
  • 源码来自infinispan: git@github.com:infinispan/infinispan.git

基于Infinispan 内部架构的了解,让我为你详细解释 DefaultSegmentedDataContainer 中 TTL 的存储位置:

DefaultSegmentedDataContainer 中 TTL 的存储结构

核心结构层次

DefaultSegmentedDataContainer
│
├── DataSegment[] segments  ← 分段数组(根据 key 的 hash 分配)
│   │
│   └── ConcurrentHashMap<Object, InternalCacheEntry>
│       │
│       └── InternalCacheEntry<K, V>  ← 每个缓存条目对象
│           │
│           ├── key          : K         ← 缓存键
│           ├── value        : V         ← 缓存值
│           ├── metadata     : Metadata  ← 元数据(包含TTL信息)★
│           ├── created      : long      ← 创建时间戳
│           └── lastUsed     : long      ← 最后访问时间戳

TTL 信息的具体位置

TTL 不是存储在 DefaultSegmentedDataContainer 的直接属性中,而是在每个 InternalCacheEntry 对象的 Metadata 属性里。

Infinispan 中有多种 InternalCacheEntry 实现类,根据是否有过期时间分类:

实现类 特点 TTL 相关属性
ImmortalCacheEntry 永不过期 无 TTL 属性
MortalCacheEntry 有 lifespan lifespan, created
TransientCacheEntry 有 maxIdle maxIdle, lastUsed
TransientMortalCacheEntry 同时有两者 lifespan, maxIdle, created, lastUsed
MetadataImmortalCacheEntry 使用 Metadata 对象 metadata.lifespan(), metadata.maxIdle()
MetadataMortalCacheEntry 使用 Metadata 对象 metadata.lifespan(), metadata.maxIdle()

下面的缓存sessions表示永不过期,因为它的类型是ImmortalCacheEntry

图片

下面的缓存sessions是有过期时间的,它的类型是TransientMortalCacheEntry

图片

实际的 TTL 属性

对于 Keycloak 的会话缓存,通常使用 带 Metadata 的条目,TTL 信息在:

InternalCacheEntry entry = ...;

// 获取 lifespan (生命周期 TTL)
long lifespan = entry.getLifespan();      // 返回毫秒值,-1 表示永不过期

// 获取 maxIdle (空闲 TTL)  
long maxIdle = entry.getMaxIdle();        // 返回毫秒值,-1 表示永不过期

// 获取创建时间
long created = entry.getCreated();        // 创建时的时间戳

// 获取最后访问时间
long lastUsed = entry.getLastUsed();      // 最后访问的时间戳

// 检查是否过期
boolean expired = entry.isExpired(System.currentTimeMillis());

// 计算过期时间点
long expiryTime = entry.expiryTime();     // 返回具体的过期时间戳

在 JVM 调试时查看 TTL

如果你想在调试器中查看某个缓存项的 TTL,路径是:

DefaultSegmentedDataContainer
  └── segments (DataSegment[])
      └── [segment index] 
          └── entries (ConcurrentHashMap)
              └── [key]
                  └── InternalCacheEntry 对象
                      ├── metadata
                      │   ├── lifespan   ← 这里是 TTL(毫秒)
                      │   └── maxIdle    ← 这里是空闲超时(毫秒)
                      ├── created        ← 创建时间戳
                      └── lastUsed       ← 最后访问时间戳

过期判断逻辑

// 在 InternalCacheEntry 中的过期检查逻辑(简化版):
public boolean isExpired(long now) {
    // lifespan 检查
    if (lifespan > 0 && (created + lifespan) <= now) {
        return true;
    }
    // maxIdle 检查
    if (maxIdle > 0 && (lastUsed + maxIdle) <= now) {
        return true;
    }
    return false;
}

Keycloak 中 TTL 的设置来源

当 Keycloak 写入会话时,TTL 值来自 SessionTimeouts 类的计算:

// 写入缓存时设置 TTL
cache.put(key, value, lifespanMs, TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS);

这些值最终会被封装到 InternalCacheEntrymetadata 或对应属性中。

DefaultSegmentedDataContainer
│
├── DataSegment[] segments  ← 分段数组(根据 key 的 hash 分配)
│   │
│   └── ConcurrentHashMap<Object, InternalCacheEntry>
│       │
│       └── InternalCacheEntry<K, V>  ← 每个缓存条目对象
│           │
│           ├── key          : K         ← 缓存键
│           ├── value        : V         ← 缓存值
│           ├── metadata     : Metadata  ← 元数据(包含TTL信息)★
│           ├── created      : long      ← 创建时间戳
│           └── lastUsed     : long      ← 最后访问时间戳
InternalCacheEntry entry = ...;

// 获取 lifespan (生命周期 TTL)
long lifespan = entry.getLifespan();      // 返回毫秒值,-1 表示永不过期

// 获取 maxIdle (空闲 TTL)  
long maxIdle = entry.getMaxIdle();        // 返回毫秒值,-1 表示永不过期

// 获取创建时间
long created = entry.getCreated();        // 创建时的时间戳

// 获取最后访问时间
long lastUsed = entry.getLastUsed();      // 最后访问的时间戳

// 检查是否过期
boolean expired = entry.isExpired(System.currentTimeMillis());

// 计算过期时间点
long expiryTime = entry.expiryTime();     // 返回具体的过期时间戳
DefaultSegmentedDataContainer
  └── segments (DataSegment[])
      └── [segment index] 
          └── entries (ConcurrentHashMap)
              └── [key]
                  └── InternalCacheEntry 对象
                      ├── metadata
                      │   ├── lifespan   ← 这里是 TTL(毫秒)
                      │   └── maxIdle    ← 这里是空闲超时(毫秒)
                      ├── created        ← 创建时间戳
                      └── lastUsed       ← 最后访问时间戳
// 在 InternalCacheEntry 中的过期检查逻辑(简化版):
public boolean isExpired(long now) {
    // lifespan 检查
    if (lifespan > 0 && (created + lifespan) <= now) {
        return true;
    }
    // maxIdle 检查
    if (maxIdle > 0 && (lastUsed + maxIdle) <= now) {
        return true;
    }
    return false;
}
// 写入缓存时设置 TTL
cache.put(key, value, lifespanMs, TimeUnit.MILLISECONDS, maxIdleMs, TimeUnit.MILLISECONDS);