php是當下最流行的web服務器端語言,zookeeper是大型分佈式協同工具,本文在這裏介紹一種架構實現php服務器對於zookeeper數據變化的自動監聽php
一.問題背景
php能夠CLI模式模式鏈接zookeeper(下面簡稱zk),並實現zk節點數據的自動發現,這裏不作過多敘述。但web服務器中,php只能主動鏈接zk以得到節點數據,作不到zk數據的自動發現。其次,php web服務,也難以和php CLI模式下的服務共享數據變量(cli模式下zk數據作成共享變量)。這就致使若是併發量大的話,每個http請求php都會去鏈接zk,zk集羣的壓力會很大,其次,每個請求都會鏈接zk,請求結束又釋放zk鏈接,zk的鏈接與釋放過於頻繁。node
二.解決思路
nodeJs多進程間能夠通訊,能夠解決php服務難以實現共享變量的問題。nodeJs能夠在主進程中拋出一個子進程,子進程中實現zk的自動發現,子進程偵察到zk節點數據變化時,主動通知主進程。node主進程寫一個服務,像外界提供zk的數據。php web服務須要zk節點數據時,經過RPC協議(這裏使用thrift協議),訪問本地node主進程服務,來獲取zk節點數據。這樣作有三點好處:1.實現zk節點變化的自動發現;web
2.php和node通訊在同一臺服務器上,不走網管,速度快,穩定性可靠 3.thrift協議直接操做套接字傳輸數據,比http服務要快,能夠近似看做php調用本身的方法
三.具體實現express
1.搭建zookeeper服務,這裏我搭了一個5臺zk服務的僞集羣(zk服務的搭建這裏不作過多介紹),測試zk服務是否能正常寫入與讀取節點數據 分別啓動五臺zk服務器./server001/bin/zkServer.sh start ./server002/bin/zkServer.sh start ./server003/bin/zkServer.sh start ./server004/bin/zkServer.sh start ./server005/bin/zkServer.sh start ![zookeeper集羣啓動][1]
啓動成功後,測試節點是否可以正常寫入讀取(這裏提早建立了一個/zk_test節點) 啓動zk客戶端./bin/zkCli.sh /zk_test測試寫入123:set /zk_test 123 發現能夠正常寫入與讀取 ![zk寫入與讀取][2] 2.建立node服務 定義thrift提供的服務,新建thrift文件,定義返回zk數據的服務:zkDataService,服務中有一個方法zkData返回節點數據 namespace php tutorial service zkDataService { string zkData() } 根據thrift協議生成服務端可客戶端的中間代碼(生成中間代碼我放在windows上完成),C:\Users\77388\AppData\thrift-0.10.0.exe -r --gen js:node zkData.thrift 此時會在文件夾中生成中間代碼,在gen-nodejs文件夾中 ![生成node端中間代碼][3] node安裝zookeeper模塊:cnpm install node-zookeeper-client,編寫子進程support.js,自動發現zk節點數據變動
console.log('pid in worker:', process.pid); process.on('message', function(msg) { console.log('3:', msg); }); var i=0; var zookeeper = require('node-zookeeper-client'); var client = zookeeper.createClient('localhost:2181'); var path = '/zk_test';//節點名稱 function getData(client, path) { client.getData( path, function (event) { console.log('Got event: %s', event); getData(client, path); }, function (error, data, stat) { if (error) { console.log('Error occurred when getting data: %s.', error); return; } process.send('zookeeper節點數據'+data.toString('utf8'));//通知主進程zk節點數據 } ); } client.once('connected', function () { console.log('Connected to ZooKeeper.'); getData(client, path); }); client.connect(); process.emit('message', '======');
編寫主進程server.js,實現thrift定義的服務,並在主進程中啓動子進程apache
var childprocess = require('child_process'); var worker = childprocess.fork('./support.js'); console.log('pid in master:', process.pid); var childMessage=""; //監聽子進程事件 worker.on('message', function(msg) { childMessage=msg; console.log('1:', msg);//監聽子進程zk數據,並將zk節點數據打印 }) process.on('message', function(msg) { console.log('2:', msg); }) worker.send('主進程給子進程傳遞的數據'); //觸發事件 message process.emit('message', '------'); var thrift = require("thrift"); var zkDataService = require("./gen-nodejs/zkDataService"); var ttypes = require("./gen-nodejs/tutorial_types"); var data = {}; var server = thrift.createServer(zkDataService, { zkData: function(result) { result(null, childMessage);//將zk節點數據返回 } }); server.listen(9090);
啓動nodeJs主進程:node server.js
npm
3.建立php服務,在php服務中也要生成thrift中間件程序,與node端相似,php端調用node主進程server.js的zkData方法,獲取zk節點數據windows
<?php namespace tutorial\php; error_reporting(E_ALL); require_once __DIR__.'/lib/Thrift/ClassLoader/ThriftClassLoader.php'; require_once __DIR__.'/gen-php/tutorial/zkDataService.php'; use Thrift\ClassLoader\ThriftClassLoader; $GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php'; $loader = new ThriftClassLoader(); $loader->registerNamespace('Thrift', __DIR__ . '/lib'); $loader->registerDefinition('shared', $GEN_DIR); $loader->registerDefinition('tutorial', $GEN_DIR); $loader->register(); /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ use Thrift\Protocol\TBinaryProtocol; use Thrift\Transport\TSocket; use Thrift\Transport\THttpClient; use Thrift\Transport\TBufferedTransport; use Thrift\Exception\TException; try { if (array_search('--http', $argv)) { $socket = new THttpClient('localhost', 8080, '/php/PhpServer.php'); } else { $socket = new TSocket('192.168.0.105', 9090); } $transport = new TBufferedTransport($socket, 1024, 1024); $protocol = new TBinaryProtocol($transport); $client = new \tutorial\zkDataServiceClient($protocol); $transport->open(); $result= $client->zkData(); print "$result";//打印zk節點數據 $transport->close(); } catch (TException $tx) { print 'TException: '.$tx->getMessage()."\n"; } ?>
啓動php服務,發現正常獲取zk數據
服務器
修改zookeeper數據,在啓動php服務,發現能夠自動發現
架構
總結:自此,php和nodeJS相協做,完成php服務對zk數據的自動發現就完成了。架構的總體思路是node子進程實現zk的自動發現,node主進程維護一個zk節點數據的共享變量,其餘服務要想使用zk節點數據時,從node主進程中獲取。併發