這篇文章主要講解了“etcd如何實現故障時主備秒級切換高可用架構”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“etcd如何實現故障時主備秒級切換高可用架構”吧!
etcd是一個強大的一致性的分布式鍵值存儲,它提供了一種可靠的方式來存儲需要由分布式系統或機器群訪問的數據。它優雅地處理網絡分區期間的領導者選舉,并且可以容忍機器故障,即使在領導者節點中也是如此。從簡單的Web應用程序到Kubernetes,任何復雜的應用程序都可以讀取數據并將數據寫入etcd。這是官方對Etcd的描述,基于這些特性,Etcd常用于分布式配置、分布式鎖、分布式服務協調者以及分布式注冊。從功能上來說和zookeeper是一類項目,但是相比而言etcd更現代,etcd使用go語言開發,編譯后生成了系統可執行的二進制產物,跨平臺性更好,更易維護。etcd直接提供http的接口,非常方便各大語言封裝自己的client sdk,在易用性方面也更好一點。下面也主要使用java的客戶端jetcd,解決主備服務的協調問題。
etcd項目地址:https://github.com/etcd-io/etcd
etcd官網:https://etcd.io
jetcd地址:https://github.com/etcd-io/jetcd
很多時候為了服務的高可用,除了有個在工作的主服務外,還需要多啟用幾個備用服務,這樣,在主服務出現故障時,備用服務能夠馬上頂上。這個場景有個很明顯的特征就是同一時間只能有一個主服務。常見的如mysql主從切換等,同一時間只能有一個msyql負責寫數據。在我們這邊的場景是,有一個binlog解析服務,實時解析mysql 的binlog,將解析到的數據傳遞到kafka中,kafka消費端有一個Flink job去消費解析的數據。最終這些數據會下層到數據中臺中,提供給中臺系統做基礎的業務數據。很多在線的服務查詢的數據就是來源binlog解析的數據,所以binlog解析的服務不能存在單點故障,在架構上只能是一主多備的模式,主服務故障時,備用服務實時頂上。同時binlog服務也不能同時多個解析。所以,這個場景使用etcd來做主備架構再好不過了。
<dependency> <groupId>io.etcd</groupId> <artifactId>jetcd-core</artifactId> <version>0.3.0</version> </dependency>
Client client = Client.builder().endpoints( "http://127.0.0.1:2379", "http://127.0.0.1:3379", "http://127.0.0.1:4379" ).build();
Lock lock = client.getLockClient(); Lease lease = client.getLeaseClient();
Lease提供授予,撤銷和保持租約的方法,其中有兩個關鍵方法grant(long ttl)和keepAlive()。grant用于授予租約,入參為租約的時間,即如果創建帶租約的key值,ttl秒后即自動刪除,返回租約的id。keepAlive()方法用于保持租約有效,即如果租約即將到期時,keepAlive能夠自動續租ttl時間。
Lock有兩個方法,lock(ByteSequence name, long leaseId)和unlock(ByteSequence lockKey)。來實現分布式鎖的功能,其中加鎖時,入參leaseid為續約對象的id,即定義了持有鎖的時間
通過這Lease和Lock的功能,很容易實現主備服務的切換。關鍵代碼如下:
ByteSequence lockKey = ByteSequence.from("/root/lock", StandardCharsets.UTF_8); Lock lock = client.getLockClient(); Lease lease = client.getLeaseClient(); long leaseId = lease.grant(lockTTl).get().getID(); lease.keepAlive(leaseId, new StreamObserver<LeaseKeepAliveResponse>() { @Override public void onNext(LeaseKeepAliveResponse value) { System.err.println("LeaseKeepAliveResponse value:" + value.getTTL()); } @Override public void onError(Throwable t) { t.printStackTrace(); } @Override public void onCompleted() { } }); lock.lock(lockKey, leaseId).get().getKey();
首先申請授予續約獲取到leaseId,其中lockttl為1,單位秒,etcd的租約是秒級的。在這里ttl的設置是有講究的,取決于當主服務故障時,你想多快讓從服務感知并頂上。當然,受限于etcd本身租約秒級限制,最快也只能是1秒。
然后調用keepAlive方法,使授予到的leaseid?;?,這樣,只要應用還存活就會自動續約
接著調用lock方法,傳入leaseid。只有首次啟動的服務會獲取到鎖,而且在運行期間,會不斷的續約。當從服務運行到此處時,會阻塞住。這樣就能保證多個服務同時運行,只有一個服務真正工作的目的。當獲取到鎖的主服務出現問題時,原先的只有鎖的續約在1秒內就會到期,從服務會馬上獲取到鎖執行工作代碼
/** * @author: kl @kailing.pub * @date: 2019/7/22 */ public class JEtcdTest { private Client client; private Lock lock; private Lease lease; //單位:秒 private long lockTTl = 1; private ByteSequence lockKey = ByteSequence.from("/root/lock", StandardCharsets.UTF_8); private ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2); @Before public void setUp() { client = Client.builder().endpoints( "http://127.0.0.1:2379", "http://127.0.0.1:3379", "http://127.0.0.1:4379" ).build(); lock = client.getLockClient(); lease = client.getLeaseClient(); } @Test public void lockTest1toMaster() throws InterruptedException, ExecutionException { long leaseId = lease.grant(lockTTl).get().getID(); lease.keepAlive(leaseId, new StreamObserver<LeaseKeepAliveResponse>() { @Override public void onNext(LeaseKeepAliveResponse value) { System.err.println("LeaseKeepAliveResponse value:"+ value.getTTL()); } @Override public void onError(Throwable t) { t.printStackTrace(); } @Override public void onCompleted() { } }); lock.lock(lockKey, leaseId).get().getKey(); scheduledThreadPool.submit(() -> { while (true) { System.err.println("我是主服務開始工作了"); TimeUnit.SECONDS.sleep(1); } }); TimeUnit.DAYS.sleep(1); } @Test public void lockTest2toStandby() throws InterruptedException, ExecutionException { long leaseId = lease.grant(lockTTl).get().getID(); lease.keepAlive(leaseId, new StreamObserver<LeaseKeepAliveResponse>() { @Override public void onNext(LeaseKeepAliveResponse value) { System.err.println("LeaseKeepAliveResponse value:"+ value.getTTL()); } @Override public void onError(Throwable t) { t.printStackTrace(); } @Override public void onCompleted() { } }); lock.lock(lockKey, leaseId).get().getKey(); scheduledThreadPool.submit(() -> { while (true) { System.err.println("我是備用服務,我開始工作了,估計主服務已經掛了"); TimeUnit.SECONDS.sleep(1); } }); TimeUnit.DAYS.sleep(1); } @Test public void lockTest3toStandby() throws InterruptedException, ExecutionException { long leaseId = lease.grant(lockTTl).get().getID(); lease.keepAlive(leaseId, new StreamObserver<LeaseKeepAliveResponse>() { @Override public void onNext(LeaseKeepAliveResponse value) { System.err.println("LeaseKeepAliveResponse value:"+ value.getTTL()); } @Override public void onError(Throwable t) { t.printStackTrace(); } @Override public void onCompleted() { } }); lock.lock(lockKey, leaseId).get().getKey(); scheduledThreadPool.submit(() -> { while (true) { System.err.println("我是備用服務,我開始工作了,估計主服務已經掛了"); TimeUnit.SECONDS.sleep(1); } }); TimeUnit.DAYS.sleep(1); } }
上面測試用例模擬了一主兩備的高可用架構。分別執行lockTest1toMaster()、lockTest2toStandby()、lockTest3toStandby()服務,會發現只有一個服務會打印。然后手動關閉這個服務,從服務馬上會接著打印。在關閉這個從服務,另外一個從服務就會接著打印。很好的模擬了主備故障切換的效果
感謝各位的閱讀,以上就是“etcd如何實現故障時主備秒級切換高可用架構”的內容了,經過本文的學習后,相信大家對etcd如何實現故障時主備秒級切換高可用架構這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。