keycloak集群化的思考
文章目錄
- 簡(jiǎn)介
- keycloak中的集群
- load balancing負(fù)載均衡
- 暴露客戶端IP地址
- sticky sessions 和 非sticky sessions
- shared databases
- multicasting
- 總結(jié)
簡(jiǎn)介
單體服務(wù)如果想要突破到高并發(fā)服務(wù)就需要升級(jí)為集群服務(wù)。同時(shí)集群化也為高可用打下了堅(jiān)實(shí)的基礎(chǔ)。縱觀現(xiàn)在比較流行的服務(wù)或者中間件,不管是RabbitMQ還是redis都提供了集群的功能。
作為硬核工業(yè)代表的wildfly也不例外,最近研究了一下keycloak的集群,發(fā)現(xiàn)它的底層服務(wù)器用的也是wildfly,本文將會(huì)和大家探討一下keycloak的集群的架構(gòu)思路。
keycloak中的集群
我們知道,keycloak中有兩種模式,一種叫做Standalone,一種叫做domain。
這兩種模式的區(qū)別只是在于部署文件是否被集中管理,如果部署文件需要一個(gè)一個(gè)的手動(dòng)拷貝,那么就是standalone模式。如果是一鍵化的自動(dòng)安裝,那么就是domain模式。
standalone模式下有一個(gè)配置文件叫做 /standalone/configuration/standalone-ha.xml,這個(gè)就是在standalone模式下配置集群的xml文件了。
而domain模式下,配置文件都是在domain controller這個(gè)機(jī)子上進(jìn)行配置的,具體的文件是 domain/configuration/domain.xml 。
我們看下ha具體是用的集群相關(guān)的組件:
<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還介紹了一種叫做跨數(shù)據(jù)中心的集群
這種模式主要用在服務(wù)是跨數(shù)據(jù)中心的情況,比如說異地機(jī)房這樣的容災(zāi)性特別強(qiáng)的情況。
看完keycloak的基本集群搭建之后,我們來講一下keycloak集群中一些比較關(guān)鍵的概念和使用。
load balancing負(fù)載均衡
因?yàn)槭羌航Y(jié)構(gòu),所以我們后端是有多臺(tái)服務(wù)器的,那么用戶通過客戶端來訪問我們服務(wù)的時(shí)候,究竟應(yīng)該定位到哪一臺(tái)服務(wù)器呢?
這時(shí)就要用到負(fù)載均衡軟件了,也就是load balancing。
一般來說三種負(fù)載均衡的方式:
第一種,就是客戶端負(fù)載均衡,客戶端已經(jīng)知道了服務(wù)端的多個(gè)服務(wù)地址,在發(fā)送請(qǐng)求的時(shí)候由客戶端自行選擇要請(qǐng)求的服務(wù)地址。
這種模式一般都要配置一個(gè)強(qiáng)力的客戶端API,通過這個(gè)客戶端API來進(jìn)行路由功能,比如說Memcached。
Memcached的神奇來自兩階段哈希(two-stagehash)。Memcached就像一 個(gè)巨大的、存儲(chǔ)了很多<key,value>對(duì)的哈希表。通過key,可以存儲(chǔ)或查詢?nèi)我獾臄?shù)據(jù)。
客戶端可以把數(shù)據(jù)存儲(chǔ)在多臺(tái)memcached上。當(dāng)查詢數(shù)據(jù)時(shí),客戶端首 先參考節(jié)點(diǎn)列表計(jì)算出key的哈希值(階段一哈希),進(jìn)而選中一個(gè)節(jié)點(diǎn);客戶端將請(qǐng)求發(fā)送給選中的節(jié)點(diǎn),然后memcached節(jié)點(diǎn)通過一個(gè)內(nèi)部的哈希算法(階段二哈希),查找真正的數(shù)據(jù)(item)。
第二種,就是代理服務(wù)負(fù)載均衡,這種模式下,會(huì)有一個(gè)代理服務(wù)器和后端的多個(gè)服務(wù)進(jìn)行連接,客戶端是和這個(gè)代理服務(wù)器進(jìn)行交互,由代理服務(wù)器來代替客戶端選擇到底要路由到哪個(gè)服務(wù)。
這種代理的路由的軟件就多了,比如我們熟悉的nginx和HTTPD,還有ildFly with mod_cluster, HA Proxy, 或者其他的硬件負(fù)載均衡。
第三種,是路由負(fù)載均衡,在這種模式下,用戶隨機(jī)選擇一個(gè)后端服務(wù)器進(jìn)行請(qǐng)求連接,然后在服務(wù)器內(nèi)部進(jìn)行路由,將這個(gè)請(qǐng)求發(fā)送到其他的服務(wù)器中。
這種模式下,一般需要在服務(wù)器內(nèi)部實(shí)現(xiàn)特定的負(fù)載均衡功能。
暴露客戶端IP地址
不管使用的是什么模式的負(fù)載均衡,我們都有可能在業(yè)務(wù)中需要使用到客戶訪問的IP地址。
我們?cè)谔囟ǖ臉I(yè)務(wù)中需要獲取到用戶的ip地址來進(jìn)行一些操作,比如記錄用戶的操作日志,如果不能夠獲取到真實(shí)的ip地址的話,則可能使用錯(cuò)誤的ip地址。還有就是根據(jù)ip地址進(jìn)行的認(rèn)證或者防刷工作。
如果我們?cè)诜?wù)之前使用了反向代理服務(wù)器的話,就會(huì)有問題。所以需要我們配置反向代理服務(wù)器,保證X-Forwarded-For和X-Forwarded-Proto這兩個(gè)HTTP header的值是有效的。
然后服務(wù)器端就可以從X-Forwarded-For獲取到客戶的真實(shí)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的環(huán)境中,比如說web應(yīng)用程序中,如果后端服務(wù)器是cluster的情況下還需要考慮session共享的問題。
因?yàn)閷?duì)于每個(gè)服務(wù)器來說,它的session都是本地維護(hù)的,如果是多臺(tái)服務(wù)器想要session共享該怎么辦呢?
一種辦法就是所有的服務(wù)器都將session存放在同一個(gè)外部緩存系統(tǒng)中,比如說redis。這樣不管用戶訪問到哪個(gè)server,都可以讀取到同一份session數(shù)據(jù)。
當(dāng)然,這個(gè)緩存系統(tǒng)可以是單點(diǎn)也可以是集群,如果是不同的數(shù)據(jù)中心的話,緩存集群甚至還需要跨數(shù)據(jù)中心進(jìn)行同步。
緩存同步當(dāng)然是一個(gè)很好的辦法,但是同步行動(dòng)自然是有開銷的。有沒有更加簡(jiǎn)單方便的處理方式呢? 比如固定一個(gè)用戶只訪問同一個(gè)服務(wù)器這樣是不是就能解決緩存同步的問題呢?
這種固定用戶訪問特定某個(gè)服務(wù)器的模式,我們叫做sticky sessions模式。在這種模式下,可以不用考慮session同步的問題。當(dāng)然,這種模式下,如果某個(gè)服務(wù)器down機(jī)了,用戶的session就會(huì)丟失。所以還是要做一些session同步的工作,只不過不需要實(shí)時(shí)的同步而已。
另外,sticky session還有一個(gè)缺點(diǎn):如果是后臺(tái)的請(qǐng)求,則獲取不到session的信息,也就無法實(shí)現(xiàn)sticky session,這個(gè)時(shí)候就需要進(jìn)行后臺(tái)數(shù)據(jù)的拷貝,這樣才能保證不管請(qǐng)求發(fā)送到哪里都能夠表現(xiàn)一致。
shared databases
所有的應(yīng)用都需要保存數(shù)據(jù)。通常來說,我們會(huì)有兩種數(shù)據(jù):
一種是數(shù)據(jù)庫數(shù)據(jù),這種數(shù)據(jù)將會(huì)永久存儲(chǔ)用戶信息。
一種是cache,用作數(shù)據(jù)庫和應(yīng)用程序的緩沖。
不管是哪種數(shù)據(jù),都可以有集群模式,也就是多臺(tái)服務(wù)器同時(shí)讀寫數(shù)據(jù)。這樣對(duì)于共享的數(shù)據(jù)就涉及到了集群數(shù)據(jù)更新的問題。
集群數(shù)據(jù)的更新有兩種更新模式:
一種是可靠?jī)?yōu)先,Active/Active mode,一個(gè)節(jié)點(diǎn)更新的數(shù)據(jù)會(huì)立馬同步到另外一個(gè)節(jié)點(diǎn)。
一種是性能優(yōu)先,Active/Passive mode,一個(gè)節(jié)點(diǎn)更新的數(shù)據(jù)不會(huì)立馬同步到另外一個(gè)節(jié)點(diǎn)中。
可靠?jī)?yōu)先的運(yùn)行邏輯是,一個(gè)更新請(qǐng)求需要等待所有的集群服務(wù)返回更新成功才算成功。而性能優(yōu)先的運(yùn)行邏輯就是更新完主數(shù)據(jù)就算成功了,其他的節(jié)點(diǎn)會(huì)去異步和主數(shù)據(jù)節(jié)點(diǎn)進(jìn)行同步。
keycloak中使用的緩存是infinispan,并且構(gòu)建了多種session緩存,不同的緩存使用的是不同的同步策略:
-
authenticationSessions:這個(gè)緩存保存的是登錄用戶的信息,如果在sticky sessions模式下,是不需要進(jìn)行數(shù)據(jù)同步的。
-
Action tokens:如果用戶需要異步的進(jìn)行郵件驗(yàn)證,比如說忘記密碼等操作,則需要用到這種類型的緩存。因?yàn)檫@種操作中的token只能夠被使用一次,所以需要數(shù)據(jù)的同步。
-
非認(rèn)證的session信息:因?yàn)椴荒鼙WCsticky session模式的使用,所以需要復(fù)制。
-
loginFailures: 統(tǒng)計(jì)用戶的登錄異常情況,不需要被復(fù)制。
在緩存保存數(shù)據(jù),需要注意數(shù)據(jù)更新后的失效問題。
在keycloak中,使用了一個(gè)單獨(dú)的work緩存,這個(gè)緩存是所有數(shù)據(jù)中心同步的,它不存儲(chǔ)實(shí)際的數(shù)據(jù),只存儲(chǔ)要無效的數(shù)據(jù)通知。各個(gè)數(shù)據(jù)的服務(wù)從work緩存中讀取無效的數(shù)據(jù)列表,進(jìn)行相應(yīng)的數(shù)據(jù)緩存無效化處理。
multicasting
最后,如果集群需要?jiǎng)討B(tài)發(fā)現(xiàn)和管理節(jié)點(diǎn)的功能的話,還需要進(jìn)行IP廣播。比如說可以使用JGroups來實(shí)現(xiàn)這個(gè)功能。
總結(jié)
keycloak的底層是wildfly,本身已經(jīng)支持很多強(qiáng)大的工業(yè)組件,它的設(shè)計(jì)理念是讓程序業(yè)務(wù)邏輯和其他的通用的生產(chǎn)級(jí)特性(高可用,負(fù)載均衡,緩存集群,消息隊(duì)列等)區(qū)分開,只用專注于業(yè)務(wù)邏輯的實(shí)現(xiàn)和編寫,其他的事情交給服務(wù)器去做即可。
大家可以多研究下這些優(yōu)秀的服務(wù)器框架,可以得到一些不同的體會(huì)。
本文作者:flydean程序那些事
本文鏈接:http://www.flydean.com/keycloak-cluster-in-depth/
本文來源:flydean的博客
歡迎關(guān)注我的公眾號(hào):「程序那些事」最通俗的解讀,最深刻的干貨,最簡(jiǎn)潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!
總結(jié)
以上是生活随笔為你收集整理的keycloak集群化的思考的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python解释器和IPython
- 下一篇: 深入理解nodejs中的异步编程