php是当下最流行的web服务器端语言,zookeeper是大型分布式协同工具,本文在这里介绍一种架构实现php服务器对于zookeeper数据变化的自动监听。
一.问题背景
php可以cli模式模式连接zookeeper(下面简称zk),并实现zk节点数据的自动发现,这里不做过多叙述。但web服务器中,php只能主动连接zk以获得节点数据,做不到zk数据的自动发现。其次,php web服务,也难以和php cli模式下的服务共享数据变量(cli模式下zk数据做成共享变量)。这就导致如果并发量大的话,每一个http请求php都会去连接zk,zk集群的压力会很大,其次,每一个请求都会连接zk,请求结束又释放zk连接,zk的连接与释放过于频繁。
二.解决思路
nodejs多进程间可以通信,可以解决php服务难以实现共享变量的问题。nodejs可以在主进程中抛出一个子进程,子进程中实现zk的自动发现,子进程侦察到zk节点数据变化时,主动通知主进程。node主进程写一个服务,像外界提供zk的数据。php web服务需要zk节点数据时,通过rpc协议(这里使用thrift协议),访问本地node主进程服务,来获取zk节点数据。
这样做有三点好处:
1.实现zk节点变化的自动发现;
2.php和node通信在同一台服务器上,不走网管,速度快,稳定性可靠
3.thrift协议直接操作套接字传输数据,比http服务要快,可以近似看作php调用自己的方法
三.具体实现
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 tutorialservice 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定义的服务,并在主进程中启动子进程
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('主进程给子进程传递的数据');
//触发事件 messageprocess.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
3.创建php服务,在php服务中也要生成thrift中间件程序,与node端类似,php端调用node主进程server.js的zkdata方法,获取zk节点数据
<?phpnamespace 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主进程中获取。
以上就是php+nodejs+thrift协议,实现zookeeper节点数据自动发现的详细内容。