將多個應用程序作為壹個邏輯程序運行並不是什麽新鮮事。其實幾年前我也寫過類似的軟件。這種架構令人困惑,難以使用。Apache Zookeeper提供了壹套管理該軟件的工具。
為什麽叫動物園?因為要協調的分布式系統是壹個動物園。
在本文中,我將解釋如何使用PHP安裝和集成Apache ZooKeeper。我們會通過服務協調獨立的PHP腳本,讓它們同意成為領袖(所以叫領袖選舉)。當領導者退出(或崩潰)時,工人可以檢測並重新選擇新的領導者。
ZooKeeper是壹個中性服務,用於管理配置信息、命名、提供分布式同步和組合服務。所有這些類型的服務都將在分布式應用程序中使用。每次妳寫這些服務,都會涉及到大量的bug修復和競爭。因為很難編寫這些服務,所以它們通常會被忽略,這使得在應用程序發生變化時很難管理它們。即使處理得當,實現這些服務的不同方法也會使部署應用程序變得難以管理。
雖然ZooKeeper是Java應用,但是也可以用C。這裏是PHP的壹個擴展,由Andrei Zmievski在2009年創建並維護。妳可以從PECL下載或者直接從GitHub獲得PHP-ZooKeeper。
要使用這個擴展,妳必須先安裝ZooKeeper。可以從官網下載。
$ tar zxfv zookeeper-3.4.5.tar.gz
$ cd動物園管理員-3.4.5/src/c
$ ./configure - prefix=/usr/
$ make
$ sudo make安裝
這將安裝ZooKeeper的庫和頭文件。現在您已經準備好編譯PHP擴展了。
$ CD $ git clone/andreiz/PHP-zookeeper . git
$ CD PHP-動物園管理員
$ phpize
$ ./配置
$ make
$ sudo make安裝
將“zookeeper.so”添加到PHP配置中。
$ vim/etc/PHP 5/CLI/conf . d/20-zookeeper . ini
因為我不需要在web服務環境中運行,所以我只在這裏編輯了CLI的配置。將下面幾行復制到ini文件中。
extension=zookeeper.so
使用以下命令來確定擴展是否有效。
$ php -m | grep動物園管理員
動物園管理員
現在是時候運行動物園管理員了。唯壹還沒做的就是配置。創建用於存儲所有服務數據的目錄。
$ mkdir/home/you-account/動物園
$ cd$ cd動物園管理員-3.4.5/
$ CP conf/zoo _ sample . CFG conf/zoo . CFG
$ vim conf/zoo.cfg
找到名為“dataDir”的屬性,並將其指向“/home/you-account/zoo”目錄。
$ bin/zkServer.sh start
$ bin/ZK CLI . sh-server 127 . 0 . 0 . 1:2181[ZK:127 . 0 . 0 . 1:2181(已連接)14]創建/測試1
已創建/測試[ZK:127 . 0 . 0 . 1:2181(已連接)19]ls/[測試,zookeeper]
至此,您已經成功連接到ZooKeeper並創建了壹個名為“/test”的znode(我們稍後會用到)。ZooKeeper以樹形結構保存數據。這非常類似於文件系統,但是“文件夾”非常類似於文件。Znode是ZooKeeper保存的實體。Node這個術語很容易混淆,所以為了避免混淆,這裏用znode。
因為我們稍後會用到它,所以這裏我們保持客戶端連接。打開壹個新窗口,創建壹個zookeeperdemo1.php文件。
& lt?服務器端編程語言(Professional Hypertext Preprocessor的縮寫)
ZookeeperDemo類擴展了Zookeeper {
公共函數觀察器($i,$type,$key ) {
echo“內幕觀察者\ n”;
//觀察器被消耗,所以我們需要設置壹個新的
$ this-& gt;get( '/test ',array($this,' watcher '));
}
}
$ zoo = new zookeeper demo(' 127 . 0 . 0 . 1:2181 ');$ zoo-& gt;get( '/test ',array($zoo,' watcher '));
while( true ) {
回聲。;
睡眠(2);}
現在運行腳本。
$ php zookeeperdemo1.php
這裏應該每兩秒鐘就有壹個點。現在切換到ZooKeeper客戶端,更新“/test”值。
[ZK:127 . 0 . 0 . 1:2181(已連接)20]設置/測試foo
這將在PHP腳本中悄悄地觸發“內部觀察者”消息。這是怎麽發生的?
ZooKeeper提供了壹個可以綁定到znode的監視器。如果監視器發現znode已經改變,該服務將立即通知所有相關的客戶端。這就是PHP腳本了解變化的方式。Zookeeper::get方法的第二個參數是回調函數。當事件被觸發時,監視器將被消耗,所以我們需要在回調函數中再次設置監視器。
現在,您已經準備好創建分布式應用程序了。挑戰在於讓這些獨立的程序決定哪個(領導者)協調他們的工作,哪個(工作者)需要被執行。這個過程叫做領袖選舉,妳可以在ZooKeeper菜譜和解決方案中看到相關的實現方法。
簡單來說,每個進程(或服務器)都盯著相鄰的進程(或服務器)。如果被監視的進程(即,領導者)退出或崩潰,監視器將找到它的鄰居(此時最老的進程)作為領導者。
在實際應用中,領導會給工人分配任務,監控過程並保存結果。為了簡單起見,我在這裏跳過了這些部分。
創建壹個名為worker.php的新PHP文件。
& lt?服務器端編程語言(Professional Hypertext Preprocessor的縮寫)
類工作者擴展動物園管理員{
const CONTAINER = '/cluster ';
受保護的$acl = array(
數組(
perms' = >動物園管理員::PERM_ALL,
scheme ' = & gt世界',
id ' = & gt任何人’));
private $ isLeader = false
private $ znode
public function _ _ construct($ host = ' ',$watcher_cb = null,$recv_timeout = 10000 ) {
parent::_ _構造($host,$watcher_cb,$ recv _ time out);
}
公共函數寄存器(){
如果(!$ this-& gt;存在(自身::容器)){
$ this-& gt;create( self::CONTAINER,null,$ this-& gt;ACL);
}
$ this-& gt;znode = $ this-& gt;創建(self::CONTAINER。/w-',
空,
$ this-& gt;acl,
Zookeeper::短命| Zookeeper::序列);
$ this-& gt;znode = str _ replace(self::CONTAINER。/','',$ this-& gt;znode);
printf("我註冊為:%s\n ",$ this-& gt;znode);
$ watching = $ this-& gt;watch previous();
if($ watching = = $ this-& gt;znode ) {
printf("這裏沒人,我是領導\ n ");
$ this-& gt;set leader(true);}
否則{
printf("我在看%s\n ",$正在看);
}
}
公共函數watchPrevious() {
$ workers = $ this-& gt;get children(self::CONTAINER);
sort($ workers);
$ size = sizeof($ workers);
for($ I = 0;$ i & lt$ size$i++ ) {
如果($ this-& gt;znode == $workers[ $i ] ) {
if($ I & gt;0 ) {
$ this-& gt;get( self::CONTAINER。'/' .$workers[ $i - 1 ],array( $this,' watch node ');
return $ workers[$ I-1];
}
return $ workers[$ I];
}
}
拋出新異常(sprintf("出現了非常嚴重的錯誤!我找不到我自己:%s/%s”,
self::容器,
$ this-& gt;znode));
}
公共函數watchNode( $i,$type,$name ) {
$ watching = $ this-& gt;watch previous();
if($ watching = = $ this-& gt;znode ) {
printf("我是新領導!\ n ");
$ this-& gt;set leader(true);
}
否則{
printf("現在我在看%s\n ",$正在看);}
}
公共函數isLeader() {
return $ this-& gt;isLeader
}
公共函數setLeader($flag) {
$ this-& gt;isLeader = $ flag
}
公共函數run() {
$ this-& gt;寄存器();
while( true ) {
如果($ this-& gt;isLeader() ) {
$ this-& gt;doleader job();
}
否則{
$ this-& gt;doWorkerJob();
}
睡眠(2);
}
}
公共函數doLeaderJob() {
echo“Leading \ n”;
}
公共函數doWorkerJob() {
回顯“正在工作\ n”;
}
}
$worker =新工人(' 127 . 0 . 0 . 1:2181 ');$ worker-& gt;run();
至少打開3個終端,並在每個終端中運行以下腳本:
# term1
$ PHP worker.php
我註冊為:w-0000000001這裏沒人,我是領導
主要的
#術語2
$ PHP worker.php
我註冊為:w-0000000002我正在觀看w-0000000001
工作
#術語3
$ PHP worker.php
我註冊為:w-0000000003我在看w-0000000002
工作
現在模擬領導崩潰的情況。使用Ctrl+c或其他方法退出第壹個腳本。壹開始不會有變化,工人可以繼續工作。稍後,ZooKeeper會找到暫停時間,選出新的領導者。
雖然這些腳本很容易理解,但是有必要對已經使用的Zookeeper標誌進行評論。
$ this-& gt;znode = $ this-& gt;創建(self::CONTAINER。/w-',null,$ this-& gt;acl,Zookeeper::短命| Zookeeper::SEQUENCE);
每壹個節點都屬於短暫的序列。
EPHEMRAL代表當客戶端失去連接時刪除znode。這就是PHP腳本知道超時的原因。SEQUENCE代表在每個znode名稱後添加壹個序列標識符。我們通過這些獨特的身份來標記工人。
PHP部分還是有壹些需要註意的問題。該擴展仍處於測試階段,如果使用不當,很容易出現分段故障。例如,普通函數不能作為回調函數傳入,必須傳入方法。希望有更多PHP社區的同仁能看到Apache ZooKeeper的好,擴展能得到更多支持。
ZooKeeper是壹款功能強大的軟件,擁有簡潔明了的API。因為文檔和例子做得很好,任何人都可以很容易地編寫分布式軟件。讓我們開始吧。會很有趣的。