当网站做到一定规模的时候,web单个页面需要涉及到的业务也会越来越多,每个页面可能会向后端发起几个、十几个甚至几十个请求。对于java、python之类的支持多线程的语言可以使用多线程编程,但也会增加程序的复杂性,像php这样的不支持多线程的语言只能借助其他方法实现并行,下面总结几种比较实用的并行化框架。
1、yar 是鸟哥开发的一个 基于php扩展的RPC框架。
//service.php
class ServiceTest
{
public function test($param){
sleep(1);
return 'sleep 1s';
}
public function test2($param){
sleep(1);
return 'sleep 1s';
}
public function test3($param){
sleep(1);
return 'sleep 1s';
}
}
$service = new Yar_Server(new ServiceTest());
$service->handle();
//client.php
$api = "http://127.0.0.1/yar/service.php";
$param = array(1,2,3);
function callback($retval, $callinfo){
print_r($retval);
}
Yar_Concurrent_Client::call($api, 'test', array($param), 'callback');
Yar_Concurrent_Client::call($api, 'test2', array($param), 'callback');
Yar_Concurrent_Client::call($api, 'test3', array($param), 'callback');
Yar_Concurrent_Client::loop();
上面服务端代码有3个方法都sleep一秒来模拟业务端的处理,通过yar扩展注册服务,client端通过Yar_Concurrent_Client并行请求这个三个方法,最终执行时间是大约是1s。值得一提yar的并行操作是通过libcurl的并行实现的,服务端代码必须能够通过http访问到。对于tpc和unix socket目前只能进行同步请求,如需要并行实现需要自行加入消息队列之内的东西去实现。
2、APS,是安居客集团以zmq为消息中间件,以事件驱动进行网络请求的一个跨语言RPC框架,框架中有一个代理(device)监听两个端口或本地socket文件,分别监听客户端发来的请求和转发给服务端的多个worker进程,并负责把woker处理返回的数据转发到客户端。运行github上面用php写的demon代码如下。
3、Gearman,是一个用来把工作委派给其他机器、分布式的调用更适合做某项工作的机器、并发的做某项工作在多个调用间做负载均衡、或用来在调用其它语言的函数的系统。通过worker向Gearmand守护进程注册工作,客户端通过Gearmand将任务分派到后端的worker进程,具体实现和APS类似。
4、nodejs,是一个事件驱动的单进程语言,可以通过这种异步编程模式实现对后台业务的并行处理。下面demo是以nodejs为客户端请求php后端的一个耗时3s的方法,一个耗时2s的方法:
var http = require("http");
var url = require('url');
var eventProxy = require('eventproxy');
var handle = {};
handle['/'] = test;
function start(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
route(handle, pathname, response);
}
http.createServer(onRequest).listen(8081);
}
function route(handle, pathname, response){
console.log("route\n");
if (typeof handle[pathname] === 'function') {
handle[pathname](response);
} else {
console.log(pathname + 'is no fund');
}
}
function test(response) {
var ep = new eventProxy();
ep.all('data1', 'data2', function(a, b){
response.writeHead(200, {"Content-Type": "text/plain"});
response.write(a);
response.write(b);
response.end();
});
http.get('http://127.0.0.1/nodejs/service.php?function=test', function(data){
var buffers = [], size = 0;
data.on('data', function(buffer) {
buffers.push(buffer);
size += buffer.length;
});
data.on('end', function(){
var buffer = new Buffer(size), pos = 0;
for(var i = 0, l = buffers.length; i < l; i++) {
buffers[i].copy(buffer, pos);
pos += buffers[i].length;
}
ep.emit('data1', buffer);
});
});
http.get('http://127.0.0.1/nodejs/service.php?function=test2', function(data){
var buffers = [], size = 0;
data.on('data', function(buffer) {
buffers.push(buffer);
size += buffer.length;
});
data.on('end', function(){
var buffer = new Buffer(size), pos = 0;
for(var i = 0, l = buffers.length; i < l; i++) {
buffers[i].copy(buffer, pos);
pos += buffers[i].length;
}
ep.emit('data2', buffer);
});
});
}
function sleep(milliSeconds) {
var startTime = new Date().getTime();
while (new Date().getTime() < startTime + milliSeconds);
}
start(route, handle);
总结:
上述并行请求的实现有两种方式,一是基于事件驱动模型nodejs、yar(yar底层libcurl的curl_multi应用select()),二是基于消息队列的多进程的任务调度APS、Gearman。在实际的应用中的选择什么样的并行框架可能会根据各个方面来抉择,不管选择哪个,带来的一个很大的好处是使程序SOA化,减小代码间的耦合度,更变方便扩展。