关于架构:keycloak集群化的思考

66次阅读

共计 4763 个字符,预计需要花费 12 分钟才能阅读完成。

简介

单体服务如果想要冲破到高并发服务就须要降级为集群服务。同时集群化也为高可用打下了松软的根底。纵观当初比拟风行的服务或者中间件,不论是 RabbitMQ 还是 redis 都提供了集群的性能。

作为硬核工业代表的 wildfly 也不例外,最近钻研了一下 keycloak 的集群, 发现它的底层服务器用的也是 wildfly, 本文将会和大家探讨一下 keycloak 的集群的架构思路。

keycloak 中的集群

咱们晓得,keycloak 中有两种模式,一种叫做 Standalone, 一种叫做 domain。

这两种模式的区别只是在于部署文件是否被集中管理,如果部署文件须要一个一个的手动拷贝,那么就是 standalone 模式。如果是一键化的主动装置,那么就是 domain 模式。

standalone 模式下有一个配置文件叫做 /standalone/configuration/standalone-ha.xml,这个就是在 standalone 模式下配置集群的 xml 文件了。

而 domain 模式下,配置文件都是在 domain controller 这个机子上进行配置的,具体的文件是 domain/configuration/domain.xml。

咱们看下 ha 具体是用的集群相干的组件:

<profile name="full-ha">
...
<subsystem xmlns="urn:jboss:domain:modcluster:5.0">
                <proxy name="default" advertise-socket="modcluster" listener="ajp">
                    <dynamic-load-provider>
                        <load-metric type="cpu"/>
                    </dynamic-load-provider>
                </proxy>
</subsystem>

<subsystem xmlns="urn:jboss:domain:infinispan:11.0">
...
</subsystem>

<subsystem xmlns="urn:jboss:domain:jgroups:8.0">
                <channels default="ee">
                    <channel name="ee" stack="udp" cluster="ejb"/>
                </channels>
                <stacks>
                    <stack name="udp">
                       ...
                    </stack>
                    <stack name="tcp">
                       ...
                    </stack>
                </stacks>
            </subsystem>
...
</profile>

次要用的是 modcluster,infinispan 和 jgroups。

除此之外,keycloak 还介绍了一种叫做跨数据中心的集群

这种模式次要用在服务是跨数据中心的状况,比如说异地机房这样的容灾性特地强的状况。

看完 keycloak 的根本集群搭建之后,咱们来讲一下 keycloak 集群中一些比拟要害的概念和应用。

load balancing 负载平衡

因为是集群构造,所以咱们后端是有多台服务器的,那么用户通过客户端来拜访咱们服务的时候,到底应该定位到哪一台服务器呢?

这时就要用到负载平衡软件了,也就是 load balancing。

一般来说三种负载平衡的形式:

第一种,就是客户端负载平衡,客户端曾经晓得了服务端的多个服务地址,在发送申请的时候由客户端自行抉择要申请的服务地址。

这种模式个别都要配置一个强力的客户端 API,通过这个客户端 API 来进行路由性能,比如说 Memcached。

Memcached 的神奇来自两阶段哈希 (two-stagehash)。Memcached 就像一 个微小的、存储了很多 <key,value> 对的哈希表。通过 key,能够存储或查问任意的数据。

客户端能够把数据存储在多台 memcached 上。当查问数据时,客户端首 先参考节点列表计算出 key 的哈希值 (阶段一哈希),进而选中一个节点; 客户端将申请发送给选中的节点,而后 memcached 节点通过一个外部的哈希算法 (阶段二哈希),查找真正的数据 (item)。

第二种,就是代理服务负载平衡,这种模式下,会有一个代理服务器和后端的多个服务进行连贯,客户端是和这个代理服务器进行交互,由代理服务器来代替客户端抉择到底要路由到哪个服务。

这种代理的路由的软件就多了,比方咱们相熟的 nginx 和 HTTPD,还有 ildFly with mod_cluster, HA Proxy, 或者其余的硬件负载平衡。

第三种,是路由负载平衡,在这种模式下,用户随机抉择一个后端服务器进行申请连贯,而后在服务器外部进行路由,将这个申请发送到其余的服务器中。

这种模式下,个别须要在服务器外部实现特定的负载平衡性能。

裸露客户端 IP 地址

不论应用的是什么模式的负载平衡,咱们都有可能在业务中须要应用到客户拜访的 IP 地址。

咱们在特定的业务中须要获取到用户的 ip 地址来进行一些操作,比方记录用户的操作日志,如果不可能获取到实在的 ip 地址的话,则可能应用谬误的 ip 地址。还有就是依据 ip 地址进行的认证或者防刷工作。

如果咱们在服务之前应用了反向代理服务器的话,就会有问题。所以须要咱们配置反向代理服务器,保障 X -Forwarded-For 和 X -Forwarded-Proto 这两个 HTTP header 的值是无效的。

而后服务器端就能够从 X -Forwarded-For 获取到客户的实在 ip 地址了。

在 keycloak 中,如果是 http forwarding,则能够这样配置:

<subsystem xmlns="urn:jboss:domain:undertow:10.0">
   <buffer-cache name="default"/>
   <server name="default-server">
      <ajp-listener name="ajp" socket-binding="ajp"/>
      <http-listener name="default" socket-binding="http" redirect-socket="https"
          proxy-address-forwarding="true"/>
      ...
   </server>
   ...
</subsystem>

如果是 AJP forward,比方应用的是 Apache HTTPD + mod-cluster,则这样配置:

<subsystem xmlns="urn:jboss:domain:undertow:10.0">
     <buffer-cache name="default"/>
     <server name="default-server">
         <ajp-listener name="ajp" socket-binding="ajp"/>
         <http-listener name="default" socket-binding="http" redirect-socket="https"/>
         <host name="default-host" alias="localhost">
             ...
             <filter-ref name="proxy-peer"/>
         </host>
     </server>
        ...
     <filters>
         ...
         <filter name="proxy-peer"
                 class-name="io.undertow.server.handlers.ProxyPeerAddressHandler"
                 module="io.undertow.core" />
     </filters>
 </subsystem>

sticky sessions 和 非 sticky sessions

如果是在存在 session 的环境中,比如说 web 应用程序中,如果后端服务器是 cluster 的状况下还须要思考 session 共享的问题。

因为对于每个服务器来说,它的 session 都是本地保护的,如果是多台服务器想要 session 共享该怎么办呢?

一种方法就是所有的服务器都将 session 寄存在同一个内部缓存零碎中,比如说 redis。这样不论用户拜访到哪个 server,都能够读取到同一份 session 数据。

当然,这个缓存零碎能够是单点也能够是集群,如果是不同的数据中心的话,缓存集群甚至还须要跨数据中心进行同步。

缓存同步当然是一个很好的方法,然而同步口头天然是有开销的。有没有更加简略不便的解决形式呢?比方固定一个用户只拜访同一个服务器这样是不是就能解决缓存同步的问题呢?

这种固定用户拜访特定某个服务器的模式,咱们叫做 sticky sessions 模式。在这种模式下,能够不必思考 session 同步的问题。当然,这种模式下,如果某个服务器 down 机了,用户的 session 就会失落。所以还是要做一些 session 同步的工作,只不过不须要实时的同步而已。

另外,sticky session 还有一个毛病:如果是后盾的申请,则获取不到 session 的信息,也就无奈实现 sticky session,这个时候就须要进行后盾数据的拷贝,这样能力保障不论申请发送到哪里都可能体现统一。

shared databases

所有的利用都须要保留数据。通常来说,咱们会有两种数据:

一种是数据库数据,这种数据将会永恒存储用户信息。

一种是 cache,用作数据库和应用程序的缓冲。

不论是哪种数据,都能够有集群模式,也就是多台服务器同时读写数据。这样对于共享的数据就波及到了集群数据更新的问题。

集群数据的更新有两种更新模式:

一种是牢靠优先,Active/Active mode,一个节点更新的数据会立马同步到另外一个节点。

一种是性能优先,Active/Passive mode,一个节点更新的数据不会立马同步到另外一个节点中。

牢靠优先的运行逻辑是,一个更新申请须要期待所有的集群服务返回更新胜利才算胜利。而性能优先的运行逻辑就是更新完主数据就算胜利了,其余的节点会去异步和主数据节点进行同步。

keycloak 中应用的缓存是 infinispan,并且构建了多种 session 缓存,不同的缓存应用的是不同的同步策略:

  • authenticationSessions:这个缓存保留的是登录用户的信息,如果在 sticky sessions 模式下,是不须要进行数据同步的。
  • Action tokens:如果用户须要异步的进行邮件验证,比如说遗记明码等操作,则须要用到这种类型的缓存。因为这种操作中的 token 只可能被应用一次,所以须要数据的同步。
  • 非认证的 session 信息:因为不能保障 sticky session 模式的应用,所以须要复制。
  • loginFailures: 统计用户的登录异常情况,不须要被复制。

在缓存保留数据,须要留神数据更新后的生效问题。

在 keycloak 中,应用了一个独自的 work 缓存,这个缓存是所有数据中心同步的,它不存储理论的数据,只存储要有效的数据告诉。各个数据的服务从 work 缓存中读取有效的数据列表,进行相应的数据缓存有效化解决。

multicasting

最初,如果集群须要动静发现和治理节点的性能的话,还须要进行 IP 播送。比如说能够应用 JGroups 来实现这个性能。

总结

keycloak 的底层是 wildfly,自身曾经反对很多弱小的工业组件,它的设计理念是让程序业务逻辑和其余的通用的生产级个性(高可用,负载平衡,缓存集群,音讯队列等)辨别开,只用专一于业务逻辑的实现和编写,其余的事件交给服务器去做即可。

大家能够多钻研下这些优良的服务器框架,能够失去一些不同的领会。

本文作者:flydean 程序那些事

本文链接:http://www.flydean.com/keycloak-cluster-in-depth/

本文起源:flydean 的博客

欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

正文完
 0