Keycloak - Infinispan Redis 缓存存储

Posted

技术标签:

【中文标题】Keycloak - Infinispan Redis 缓存存储【英文标题】:Keycloak - Infinispan Redis cache store 【发布时间】:2018-02-03 12:50:30 【问题描述】:

目前在standalone-ha模式下设置一个keycloak集群,以便能够在docker swarm上运行。在 keycloak 中,用户会话被缓存在嵌入式 infinispan 存储中,并且 infinispan 可以配置为跨集群的分布式缓存。

我也将所有者设置为 2,但问题是.. 在缩减期间,用户会话可能会丢失,如果包含缓存的所有者在缩减期间都被杀死-下。

我还阅读了有关 Infinispan Redis 缓存存储的信息,但我不确定如何配置它。

问题 1: 是否可以将 Keycloak Infinispan 配置为使用 Redis 存储?

问题 2: 如果这是不可能的,有没有办法可以克服这个问题?

任何建议都会有所帮助。

【问题讨论】:

您能否将缩减策略配置为一次仅终止一台服务器? Infinispan 应该检测到丢失的服务器,并在剩余节点上重新平衡缓存在该服务器上的会话。 当你使用 Swarm 时,可能会感兴趣:wildfly-swarm.gitbooks.io/wildfly-swarm-users-guide/content/… 【参考方案1】:

由于这个 PR https://github.com/keycloak/keycloak/commit/056ba75a72b1595ca9fa471f5693201fd5b2c7ae 默认情况下(Keycloak 最新版本 6.0.1),使用 InfinispanChangelogBasedTransaction.javaInfinispan Connection SPI 有一个非常特殊的使用 CacheDecorator.javaskipCacheStore。这意味着无论您是否配置具有持久性的存储,该存储都将被忽略。

为了实现您想要的,除了配置存储之外,您还必须在此处自定义大部分 SPI https://github.com/keycloak/keycloak/tree/master/model/infinispan/src/main/resources/META-INF/services 以确保 Keycloak 将使用缓存存储。

这也不是一件容易的事,因为这个过程涉及很多好处,例如,由于 Keycloak 使用 Jboss 的 Marshaller,如果你自定义这个 SPI,你将不得不带上大部分 org.keycloak.models.sessions.infinispan 包并注册您的模块以确保 Wildfly 能够看到要编组的实体。

另外一点是,你应该在Redis中配置大部分缓存指向一个公共数据库,除了authenticationSessions不能和sessions在同一个数据库中,否则会出现类似@的冲突987654330@ 已找到,但应为 SessionEntityWrapper

要恢复,过程会很痛苦,但如果你敢去做,我就是这样实现的:

引入了一个自定义 InfinispanConnectionProviderFactory,以便完全能够使用 infinispan 配置,然后像这样配置我的容器:
private Configuration getRedisConfiguration(int database) 
    ConfigurationBuilder cb = new ConfigurationBuilder();
    cb.persistence()
      .passivation(false)
      .addStore(RedisCacheStoreConfigurationBuilder.class)
      .ignoreModifications(false)
      .fetchPersistentState(false)
      .purgeOnStartup(false)
      .preload(false)
      .shared(true)
      .addProperty("host", System.getenv("REDIS_HOST"))
      .addProperty("port", System.getenv("REDIS_PORT"))
      .addProperty("database", String.valueOf(database));

    return cb.build();
  

您看到的RedisCacheStoreConfigurationBuilder 基本上是原始存储的精简版本,但我不需要哨兵或服务器模式,我只想连接到主机、端口和数据库。

然后我基本上复制了org.keycloak.models.sessions.infinispan,删除了与删除缓存相关的所有内容,而不是正常使用没有装饰器的缓存来跳过CacheStore。

如果我能提供帮助,请告诉我,我会准备一篇文章,详细说明如何执行此操作,其中还包括一个包含我正在谈论的代码的存储库。如果有人还在尝试这个,请告诉我更多信息。

【讨论】:

你有帖子吗,有详细的步骤。我很好奇,如何使用它。这是因为我想知道用户会话何时过期。 有一个后台作业正在运行以删除过期的会话。你有removeExpiredSessions方法要实现的模型的接口之一,将由这个后台线程调用,你可以配置它运行的频率.. 如果我们提供会话认证类存储在redis中并且不接触缓存类呢?【参考方案2】:

当期望动态扩展大型系统时,应避免在配置中注册可用节点列表的这种约束。所以 Redis 节点发现在这里是一个好处。

Infinispan 本身支持 Redis Store 作为实现 SPI 和添加 XML 实体以简化配置的扩展:

http://infinispan.org/docs/cachestores/redis/

https://github.com/infinispan/infinispan-cachestore-redis

http://infinispan.org/docs/stable/user_guide/user_guide.html#custom_cache_stores

但 WildFly infinispan 子系统(尚)不支持此扩展 - 因为 Keycloak 依赖于 WildFly。

因此,我希望通过以下任务使 Infinispan Redis Store 可用于 WildFly 和 Keycloak:

为 infinispam redis 存储 jar 创建 jboss 模块 - 参见 modules/system/layers/base/org/infinispan/

创建一个“自定义缓存存储”工厂,能够从 WildFly 配置键/值属性实例化 Redis 存储对象(存储、服务器和连接池)。这也必须作为 jboss 模块添加到 WildFly 中

使用 WildFly infinispan 子系统 "local-cache" "custom" 来配置这个工厂的属性 classproperties: https://wildscribe.github.io/WildFly/11.0/subsystem/infinispan/cache-container/local-cache/store/custom/index.html

工作正在进行中,可能会发布代码和配置。

【讨论】:

对了,Infinispan配置了JGroups“IP多播”地址的standalone-ha,预计可以实现动态集群复制,不需要Redis... 我不知道wildfly是否有任何关于这些商店使用情况的更新。另一种配置 keycloak 以通过 HotRod 使用远程 infinispan 实例,然后让 infinispan 集群基本上代理到 redis 作为“缓存存储”的选项,我自己没有这样做。 @YvesMartin 感谢您的解释。请问您最终是否在某处发布了博客文章和示例代码/配置?【参考方案3】:

在 Infinispan 后面使用 Redis 存储有什么特别的原因吗?

一个更简单的解决方案可能是将持久性配置到文件或共享数据库。对于这样的缓存用例,基于文件的持久性可能就足够了。请参阅here,例如使用基于文件的持久性配置 Infinispan。或者,您可以存储到共享数据库,例如Postgresql,但这需要更多设置(例如,参见ref card)。

【讨论】:

是的,但如果您想动态扩展,必须配置/注册一个(可能很长)服务器节点列表(对于 infinispan 或 postgresql)是一个很大的限制。将 Redis Store 作为动态负载均衡器后面的 infinispan 后端可避免在扩展时进行任何维护。 (我们部署在 Openshift / Kubernetes) 如果使用远程 Infinispan 客户端,这些客户端会获取初始 Infinispan 服务器列表,然后动态发现节点。链接 Infinispan 客户端和服务器的二进制协议处理拓扑信息。

以上是关于Keycloak - Infinispan Redis 缓存存储的主要内容,如果未能解决你的问题,请参考以下文章

Amazon ECS 上的 Keycloak 集群失败(org.infinispan.commons.CacheException:缓存的初始状态传输超时)

如何在一个 Kubernetes 集群中的命名空间之间隔离 Keycloak Infinispan,以防止 KC pod 相互发现和同步

Keycloak 独立集群配置中是不是需要仲裁?

如何在 KeyCloak 中禁用所有形式的本地缓存?

尝试使用 Java API 在 Red Hat SSO 7.3 (Keycloak) 上重置用户密码时出现错误 400

Red Hat SSO 7.3.0.GA 的 Keycloak API 调用依赖项