前面讲过一篇《实现一个简单的服务端推方案-实例篇Polling》,那篇文章服务端的实现是不停的轮循数据库,这篇将服务器端轮循数据库改为服阻塞的方式读取队列,减轻数据库服务器的压力。
客户端代码,JS库为prototype.js:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>无标题文档</title> <script language="JavaScript" type="text/javascript" src="./prototype.js"></script> </head> <body> <script language="JavaScript"> var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } } var Comet = Class.create(); Comet.prototype = { maxvid: 0, url: './backend.php', noerror: true, initialize: function(){ }, connect: function(){ this.ajax = new Ajax.Request(this.url, { method: 'get', parameters: { 'maxvid': this.maxvid }, onSuccess: function(transport){ var response = transport.responseText.evalJSON(); this.comet.maxvid = response['vid']; this.comet.handleResponse(response); this.comet.noerror = true; }, onComplete: function(transport){ if (!this.comet.noerror) setTimeout(function(){ comet.connect() }, 5000); else this.comet.connect(); this.comet.noerror = false; } }); this.ajax.comet = this; }, handleResponse: function(response){ $('content').innerHTML += '<div>' + response['vid'] + ',' + response['doorno'] + ',' + response['rfsim'] + ',' + response['optime'] + ',' + response['ynopen'] + '</div>'; } } var comet = new Comet(); comet.connect(); </script> <div id="content"></div> </body> </html>
服务器端PHP代码,backend.php,队列使用gearman
<?php ignore_user_abort(true); header("Cache-Control: no-cache, must-revalidate"); header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); ob_flush(); flush(); //控制浏览器前端不要缓存结果,每次都要重新查询 $maxvid = $_GET["maxvid"]; error_log(date("[Y-m-d H:i:s]")." > "."maxvid: ".$maxvid."\n", 3 , "/usr/local/apache2219/logs/php_log"); $gmworker = new GearmanWorker(); $gmworker->setTimeout(60000); $gmworker->addServer(); $gmworker->addFunction("getmsg", "getmsg"); if ( ($gmworker->work()) && ($gmworker->returnCode() == GEARMAN_SUCCESS) ) { echo $msg; ob_flush(); //必须有,不然是在PHP_BUFFER不会被发送到客户端 flush(); if ( connection_status() ) { error_log(date("[Y-m-d H:i:s]")." < ".$msg.",but connection aborted!\n", 3 , "/usr/local/apache2219/logs/php_log"); } else { error_log(date("[Y-m-d H:i:s]")." < ".$msg."\n", 3 , "/usr/local/apache2219/logs/php_log"); } //由于服务端阻塞读队列,无法感知客户端是否断开 //所以当客户端中断后,后台PHP仍在阻塞读,这时如果队列有新的消息便会被读出但又无法送达前端,导致消息丢失 //所以这里只能在发送后判断客户端连接状态,做特殊判断处理 } function getmsg($job) { global $msg; $msg = $job->workload(); $result = "OK"; return $result; } ?>