使用Akka構建集羣(一)

概述

  Akka提供的很是吸引人的特性之一就是輕鬆構建自定義集羣,這也是我要選擇Akka的最基本緣由之一。若是你不想敲太多代碼,也能夠經過簡單的配置構建一個很是簡單的集羣。本文爲說明Akka集羣構建的學習成本低廉,以Akka官網的例子代碼出發,進行簡單改造後與Spring集成,有關Spring集成的信息你能夠選擇閱讀《Spring與Akka的集成》一文。本文所講述的是一款十分簡便的集羣監聽器,它經過訂閱集羣成員的消息,對整個集羣的成員進行管理(管理的方式只是打印一行日誌)。html

Akka集羣規範

  根據Akka官網的描述——Akka集羣特性提供了容錯的、去中心化的、基於集羣成員關係點對點的,不存在單點問題、單點瓶頸的服務。其實現原理爲閒聊協議和失敗檢查。java

集羣概念

  • 節點(node):集羣中的邏輯成員。容許一臺物理機上有多個節點。由元組hostname:port:uid惟一肯定。
  • 集羣(cluster):由成員關係服務構建的一組節點。
  • 領導(leader):集羣中惟一扮演領導角色的節點。
  • 種子節點(seed node):做爲其餘節點加入集羣的鏈接點的節點。實際上,一個節點能夠經過向集羣中的任何一個節點發送Join(加入)命令加入集羣。

節點狀態

這裏以Akka官網提供的成員狀態狀態圖爲例,如圖1所示。node

圖1spring

圖1展現了狀態轉換的兩個因素:動做和狀態。bash

狀態

  • joining:節點正在加入集羣時的狀態。
  • weekly up:配置了akka.cluster.allow-weakly-up-members=on時,啓用的狀態。
  • up:集羣中節點的正常狀態。
  • leaving/exiting:優雅的刪除節點時,節點的狀態。
  • down:標記爲已下線的狀態。
  • removed:墓碑狀態,表示已經再也不是集羣的成員。

動做 

  • join:加入集羣。
  • leave:告知節點優雅的離開集羣。
  • down:標記集羣爲已下線。

配置

  本節將要展現構建集羣所須要的最基本的配置,幾乎不會引入過多的開發成本,一個集羣就構建完成了。application.conf文件的內容以下:架構

akka {
  actor {
    provider = "akka.cluster.ClusterActorRefProvider"
  }
  remote {
    log-remote-lifecycle-events = off
    netty.tcp {
      hostname = "127.0.0.1"
      port = 2551
    }
  }

  cluster {
    seed-nodes = [
      "akka.tcp://metadataAkkaSystem@127.0.0.1:2551",
      "akka.tcp://metadataAkkaSystem@127.0.0.1:2552"]

    #//#snippet
    # excluded from snippet
    auto-down-unreachable-after = 10s
    #//#snippet
    # auto downing is NOT safe for production deployments.
    # you may want to use it during development, read more about it in the docs.
    #
    # auto-down-unreachable-after = 10s
    
    # Disable legacy metrics in akka-cluster.
	metrics.enabled=off
  }

}

此配置文件與我在《使用Akka的遠程調用》一文中的配置有不少不一樣:app

  1. provider再也不是akka.remote.RemoteActorRefProvider,而是akka.cluster.ClusterActorRefProvider。這說明ActorRef將由akka.cluster.ClusterActorRefProvider提供;
  2. 增長了cluster配置;

cluster配置詳解

首先任何一個集羣都須要種子節點,做爲基本的加入集羣的鏈接點。本例中以我本地的兩個節點(分別監聽2551和2552端口)做爲種子節點。不管配置了多少個種子節點,除了在seed-nodes中配置的第一個種子節點須要率先啓動以外(不然其它種子節點沒法初始化而且其它節點也沒法加入),其他種子節點都是啓動順序無關的。第一個節點須要率先啓動的另外一個緣由是若是每一個節點均可以率先啓動,那麼有可能形成一個集羣出現幾個種子節點都啓動而且加入了本身的集羣,此時整個集羣實際上分裂爲幾個集羣,形成孤島。當你啓動了超過2個以上的種子節點,那麼第一個啓動的種子節點是能夠關閉下線的。若是第一個種子節點重啓了,它將不會在本身建立集羣而是向其它種子節點發送Join消息加入已存在的集羣。
注意:除了akka.remote.netty.tcp.port配置項指定的端口不一樣,全部加入集羣節點的application.conf能夠徹底同樣。若是akka.remote.netty.tcp.port未指定,那麼Akka會爲你隨機選擇其餘未佔用的端口。

簡單集羣監聽器

  咱們建立一個簡單的集羣監聽器SimpleClusterListener(其實是一個Actor,由於繼承了UntypedActor),它向集羣訂閱MemberEvent(成員事件)和UnreachableMember(不可達成員)兩種消息,來對集羣成員進行管理(打印),其實現見代碼清單1所示。tcp

代碼清單1ide

@Named("SimpleClusterListener")
@Scope("prototype")
public class SimpleClusterListener extends UntypedActor {
	LoggingAdapter log = Logging.getLogger(getContext().system(), this);
	Cluster cluster = Cluster.get(getContext().system());

	// subscribe to cluster changes
	@Override
	public void preStart() {
		// #subscribe
		cluster.subscribe(getSelf(), ClusterEvent.initialStateAsEvents(), MemberEvent.class, UnreachableMember.class);
		// #subscribe
	}

	// re-subscribe when restart
	@Override
	public void postStop() {
		cluster.unsubscribe(getSelf());
	}

	@Override
	public void onReceive(Object message) {
		if (message instanceof MemberUp) {
			MemberUp mUp = (MemberUp) message;
			log.info("Member is Up: {}", mUp.member());

		} else if (message instanceof UnreachableMember) {
			UnreachableMember mUnreachable = (UnreachableMember) message;
			log.info("Member detected as unreachable: {}", mUnreachable.member());

		} else if (message instanceof MemberRemoved) {
			MemberRemoved mRemoved = (MemberRemoved) message;
			log.info("Member is Removed: {}", mRemoved.member());

		} else if (message instanceof MemberEvent) {
			// ignore

		} else {
			unhandled(message);
		}
	}
}

運行展現

初始化的代碼以下:
		logger.info("Start simpleClusterListener");
		final ActorRef simpleClusterListener = actorSystem.actorOf(springExt.props("SimpleClusterListener"), "simpleClusterListener");
		actorMap.put("simpleClusterListener", simpleClusterListener);
		logger.info("Started simpleClusterListener");

咱們首先啓動第一個種子節點,配置跟第一小節徹底一致。咱們觀察SimpleClusterListener的日誌輸出以下圖所示。post

咱們再啓動第二個種子節點,其配置的akka.remote.netty.tcp.port爲2552,咱們觀察SimpleClusterListener的日誌輸出以下圖所示。

咱們再啓動一個非種子節點,沒有爲其指定akka.remote.netty.tcp.port,咱們觀察SimpleClusterListener的日誌輸出以下圖所示。

能夠看到新加入的節點信息被SimpleClusterListener打印出來了,細心的同窗可能發現了一些Akka集羣中各個節點的狀態遷移信息,第一個種子節點正在加入自身建立的集羣時的狀態時JOINING,因爲第一個種子節點將本身率先選舉爲Leader,所以它還將本身的狀態改變爲Up。後面它還將第二個種子節點和第三個節點從JOINING轉換到Up狀態。

咱們中止第三個加入的節點,咱們觀察SimpleClusterListener的日誌輸出以下圖所示。

能夠看到其狀態首先被標記爲Down,最後被轉換爲Removed。

總結

  經過以上介紹相信你們對使用Akka構建集羣有了基本的認識,是否是很輕鬆?若是想要繼續瞭解如何使用Akka構建集羣,請閱讀《使用Akka構建集羣(二)》。

 

後記:通過近一年的準備,《Spark內核設計的藝術 架構設計與實現》一書現已出版發行,圖書如圖:
 
售賣連接以下:
相關文章
相關標籤/搜索