akka集群有兩種啟動方式。一種是手動加入節點(在akka中節點叫做Node); 另一種是通過在配置中指定seed node。seed node是集群的通信節點,用來進行集群的創建和選舉。通常我們會在配置文件中配置一系列的seed node,當新的節點想要加入集群時,只要與其中任何一個取得通信即可。需要注意,當啟動第一個節點時,這個節點一定要配置在seed node第一個位置。
在繼續下文之前先介紹一下我們目前的服務情況。我們有三臺WEB Server,這三臺server掛在Amazon的ELB上。這三臺server都是無狀態的。項目部署使用code deploy,由于三臺server都是無狀態的,所以部署非常簡單。從ELB任意摘下一臺server,重啟,然后再掛到ELB。
當我們嘗試引入akka cluster到項目中時,現有的無狀態部署方式便不再適用了。首先我們最初的期望是引入集群但盡量少帶來項目啟動的復雜性。手動加入節點的方式被淘汰。而使用seed node現有部署腳本就要改。因為seed node決定了一定要有一臺服務器優先啟動。
糾結了一段時間后,想到一個解決方案。借助redis來啟動集群。redis可以做兩件事情,分布式鎖和存儲seed node。分布式鎖的意義在于防止多臺server同時啟動造成cluster分裂。這個方案具體細節是:當節點啟動時,首先問redis是否已有啟動節點,如果沒有則以自己為seed node創建集群;如果已有seed,則加入集群。這看起來是一個可行的方案。在寫第一行代碼之前,本著不重復造輪子的原則,去Google groups search了一下,找到了constructR這個工具。
當我看了它的介紹之后發現和我們方案的核心思路是一樣的,但是constructR實現的更精細一些。它抽象出一個狀態機用于控制集群啟動是狀態的流轉。我直接盜圖了:
在狀態流轉的過程中,任何一步出現異常都會停掉當前actor system。ConstructR官方給出的底層存儲是etcd,但是也有consul,redis,和zookeeper的實現。
ConstructR的使用也非常簡單:第一步添加依賴,第二步添加ConstructR akka的擴展,第三步配置ConstructR。官方文檔已經提供了非常完整的基于etcd和sbt的配置,下邊我列出我基于redis和gradle的配置:
// dependency
repositories {
jcenter()
mavenCentral()
maven { url "https://dl.bintray.com/everpeace/maven/" }
}
dependencies {
compile('de.heikoseeberger:constructr-akka_2.11:0.13.2')
compile('com.github.everpeace:constructr-coordination-redis_2.11:0.0.1')
}
// add constructr extension for akka
akka.extensions = [ "de.heikoseeberger.constructr.akka.ConstructrExtension" ]
// constructr conf
redis {
host = "localhost"
port = 6379
db = 5
}
constructr {
coordination {
class-name = "com.github.everpeace.constructr.coordination.redis.RedisCoordination"
host = ${redis.host}
port = ${redis.port}
redis {
db = ${redis.db}
}
}
coordination-timeout = 3 seconds // Maximum response time for coordination service (e.g. etcd)
join-timeout = 15 seconds // Might depend on cluster size and network properties
max-nr-of-seed-nodes = 0 // Any nonpositive value means Int.MaxValue
nr-of-retries = 2 // Nr. of tries are nr. of retries + 1
refresh-interval = 30 seconds // TTL is refresh-interval * ttl-factor
retry-delay = 3 seconds // Give coordination service (e.g. etcd) some delay before retrying
ttl-factor = 2.0 // Must be greater or equal 1 + ((coordination-timeout * (1 + nr-of-retries) + retry-delay * nr-of-retries)/ refresh-interval)!
}
write on 2017-1-5