開發的進度已經到了服務發現了,在選擇zookeeper和etcd的時候,我還真猶豫了,雖然兩個都曾用過,但是長久不用就都忘了,又查了下兩者的對比資料,發現在服務發現方面,兩者都差不多的。只是zookeeper有Ephemeral的概念,Ephemeral結點在Zookeeper中是一個臨時結點,這些結點只要創建它的結點session不掛,它就一直存在,當session中止了,比如客戶端進程掛掉了,那么在zookeeper的結點也就被刪除了。 etcd不支持ZooKeeper的ephemeral臨時節點的概念,要監控服務的狀態似乎比較麻煩。 哥還在github etcd issue里,問了那個xiang90 (中國人)….
好了,不能只看別人zookeeper vs etcd的評價,人雲亦雲不是我的風格,昨天花了點時間把監控的服務發現功能在zookeeper和etcd都測試了下,結果他們都適合服務注冊發現…. 雖然zookeeper公司有專門的集群,但是真是不想接入他們…. 怎么簡單怎么來,那就用etcd吧,etcd的設計理念跟運維都是比較簡單的。
下面是我以前做測試的效果圖,為了避嫌,我把圖表的數據都裁剪了下,反正看明白他的意思就行了,綠色代碼OK,紅色代表問題。
關於模塊的服務注冊我是這么設計的 ~
/buzz/buzzadmin/download
/buzz/buzzadmin/api
buzz是大項目,buzzadmin是子項目,api 跟 download都是子項目中的兩個模塊。 這樣就有了層級的關系。 /A/B/C ,B死了,C也就無意義了。 A死了,B也就完了,下層受限於上層。
etcd和zookeeper kv的存儲本來就是這種樹形目錄,很適合我上面的監控需求設計。
我這里放兩個python操作etcd服務的小例子。
set client: (這個寫入的客戶端)
import etcd client = etcd.Client() c = 0 for i in range(100): client.write('/nodes/n%s'%i, 1,ttl=10) c += 1 print i
watch client: (這個是監控端)
import etcd client = etcd.Client() c = 0 while 1: print client.watch('/nodes/',recursive=True) c += 1 print c
這樣會造成什么問題? 我想大家在看我watch client代碼的時候,估計發現了問題所在。
寫入端比如進行了100個set操作,以為他有ttl的配置,所以當他expire過期的時候,我的watch也是會收到請求的。 我一開始沒有找到etcd watch有那樣駐守監控的函數,所以就用以前監控socket recv那樣,使用while循環地調用clent.watch() 。 這樣造成的問題是,我這邊收到節點反饋后,再去注冊監聽,這時候會丟失很多etcd反饋的信息的。 再來看看他丟失了多少,寫入段100個set,還有100個expire的action。watch理應說到200個操作,結果只有150個,也就是丟失了50個。
找了下資料,看到一老外在stackoverflow.com 上說,他也是用while,一剎那間,眼前漂出一行字,caonima…..
最后直接看python-etcd的代碼,原以為他會寫的復雜,結果這代碼只是http api的封裝罷了。 下面代碼是python-etcd里面關於watch監控的描述。注意有個eternal_watch,只是看字面的一面就知道他是我們要找尋找的,他構造了Generator生成器,然后用yield關鍵字返回。
另外watch函數其實就是self.read(self,wait=True)
def eternal_watch(self, key, index=None, recursive=None): """ Generator that will yield changes from a key. Note that this method will block forever until an event is generated. Args: key (str): Key to subcribe to. index (int): Index from where the changes will be received. Yields: client.EtcdResult """ local_index = index while True: response = self.watch(key, index=local_index, timeout=0, recursive=recursive) local_index = response.modifiedIndex + 1 yield response def watch(self, key, index=None, timeout=None, recursive=None): _log.debug("About to wait on key %s, index %s", key, index) if index: return self.read(key, wait=True, waitIndex=index, timeout=timeout, recursive=recursive) else: return self.read(key, wait=True, timeout=timeout, recursive=recursive) def read(self, key, **kwdargs): _log.debug("Issuing read for key %s with args %s", key, kwdargs) key = self._sanitize_key(key) params = {} for (k, v) in kwdargs.items(): if k in self._read_options: if type(v) == bool: params[k] = v and "true" or "false" elif v is not None: params[k] = v timeout = kwdargs.get('timeout', None) response = self.api_execute( self.key_endpoint + key, self._MGET, params=params, timeout=timeout) return self._result_from_response(response)
另外說下通過第一層key,取出所有下層key的方法…
directory = client.get("/nodes") #可以取出所有 for result in directory.children: print(result.key + ": " + result.value) #只能取出第一個 print(directory.children.next().value)
到此,我要講述的坑就扯完成了…. etcd是個好東西,夠簡單,以前做配置集中管理的時候有用過etcd,雖然后期替換成zookeeper了。另外我沒在線上用過etcd集群,但在社區中看到不少人在用,反饋也不錯。 總之,etcd值得一用…..
另外這里記錄etcd的安裝方法,環境是linux,以前記錄的文檔沒了,就貼在這里吧。
curl -L https://github.com/coreos/etcd/releases/download/v2.1.3/etcd-v2.1.3-linux-amd64.tar.gz -o etcd-v2.1.3-linux-amd64.tar.gz tar xzvf etcd-v2.1.3-linux-amd64.tar.gz cd etcd-v2.1.3-linux-amd64 ./etcd
或者是直接用docker啟動
docker run -p 2379:2379 -v /usr/share/ca-certificates/:/etc/ssl/certs quay.io/coreos/etcd:v2.1.3
另外需要注意的是,docker默認啟動是綁定在本地的4001端口,如果想綁定所有的網卡上,也就是0.0.0.0 ,可以./etcd -addr 0.0.0.0:4001
etcd解壓的時候,貌似沒有默認的配置文件,有興趣的朋友可以用我的etcd 配置文件。
addr = "127.0.0.1:4001" bind_addr = "127.0.0.1:4001" ca_file = "" cert_file = "" cors = [] cpu_profile_file = "" data_dir = "/var/xiaorui.cc/data/" discovery = "http://etcd.local:4001/v2/keys/_etcd/registry/examplecluster" http_read_timeout = 10.0 http_write_timeout = 10.0 key_file = "" peers = [] peers_file = "" max_result_buffer = 1024 max_retry_attempts = 3 name = "default-name" snapshot = true verbose = false very_verbose = false [peer] addr = "127.0.0.1:7001" bind_addr = "127.0.0.1:7001" ca_file = "" cert_file = "" key_file = "" [cluster] active_size = 9 remove_delay = 1800.0 sync_interval = 5.0