用Gearman做队列来实现消息推送

原创 2013年12月03日 16:31:30

前面有一篇文章实现一个简单的服务端推方案,是Nginx+Lua来实现推送的方式,这里我们换一种作法,用Apache+Gearman作队列来实现。


此方案的主要思路是这样的:

客户端不会频繁的轮询服务端,而是对服务端发起一个长连接,服务端通过阻塞方式读取Gearman队列数据,一旦发现新数据便给客户端发出响应,这次交互便结束了。客户端处理好新数据后再重新发起一个长连接,如此周而复始。

MySQL安装gearman-mysql-udf插件,数据首先是被插入MySQL的表中,MySQL利用触发器调用gearman-mysql-udf函数,将数据写入Gearman,对于Gearman来说,MySQL这边是Client。

接下来,Apache不会再去轮询数据库,而是改为读取Gearman,对于Gearman来说,Apache是Worker,Worker读取队列数据是以阻塞方式时行的,如果有新数据但读出并给客户端发出响应,没有则一直阻塞到超时。Apache做为Gearman的Worker,当读取消息被阻塞时,连接是一直存在的,因些这种方案里,Apache与Gearman会维持大量长连。


注:服务端维持大量长连接时内核参数的调整请参考:http长连接200万尝试及调优。


具体应该需求、实现方法及代码如下


这个应用情况是这样的:
有一个门禁刷卡数据表(已经在用)
现要求针对一些特殊的人,门禁刷卡后,要求在一个液晶屏上显示一些欢迎信息,欢迎信息要及时显示

为不影响现有系统,我们采用一个外挂的形式,新增刷卡记录后,通过触发器将数据插入Gearman队列,浏览器通过Ajax长轮循的方式访问Apache,Apaceh通过PHP连接Gearman,以阻塞读的方式读出记录

对于Gearman来说,MySQL是Client,即生产者,产生队列数据,PHP是Worker,即消费者,取出队列数据


为了让MySQL能将数据传入Gearman,这里使用了lib_mysqludf_json和gearman-mysql-udf的组合,这两个库的安装方法可以参见我的另一篇文章《Gearman分布式任务处理系统(二)扩展应用


门禁刷卡记录表:

CREATE TABLE `vdooropen` (
  `vid` int(11) NOT NULL AUTO_INCREMENT,
  `doorno` int(11) NOT NULL,
  `rfsim` char(16) NOT NULL,
  `optime` char(15) NOT NULL,
  `ynopen` char(1) NOT NULL,
  PRIMARY KEY (`vid`),
  UNIQUE KEY `vdooropen_I0` (`vid`),
  KEY `vdooropen_i1` (`doorno`,`rfsim`)
) ENGINE=InnoDB;

DELIMITER $$
CREATE TRIGGER t_vopendoor AFTER INSERT ON vdooropen
  FOR EACH ROW BEGIN
    SET @ret=gman_do_background('getmsg', json_object(NEW.vid as 'vid',NEW.doorno as 'doorno',NEW.rfsim as 'rfsim',NEW.optime as 'optime', NEW.ynopen as 'ynopen')); 
  END$$
DELIMITER ;

前端浏览器页面getmsg.html
<!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

<?php

header("Cache-Control: no-cache, must-revalidate");
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
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->addServer();
$gmworker->addFunction("getmsg", "getmsg");

if ( ($gmworker->work()) && ($gmworker->returnCode() == GEARMAN_SUCCESS) )
{
  error_log(date("[Y-m-d H:i:s]")." < ".$msg."\n", 3 , "/usr/local/apache2219/logs/php_log");
  echo $msg;
  flush();
}

function getmsg($job)
{
  global $msg;

  $msg = $job->workload();

  $result = "OK";

  return $result;
}

?>


相关文章推荐

61条面向对象设计的经验原则

          再次讀到Arthur J.Riel《 OOD 启思录》 ,感觸還是那么深,他所提倡的很多思維依然很深的影響著我,在這里拿出來分享一下。      對于面向對象,你不必严格遵守这些原...
  • foxfab
  • foxfab
  • 2007年08月16日 09:38
  • 306

gearman队列持久化引发的问题及解决方法

这篇文章主要介绍了gearman队列持久化引发的问题及解决方法,需要的朋友可以参考下 本文简述了gearman用mysql持久化的方法,以及由此引发的一些问题,具体分析如下: 一、gearman ...
  • xtjsxtj
  • xtjsxtj
  • 2016年01月07日 19:04
  • 1390

gearman rabbitMQ等 异步队列方案

开发的时候,错误没有任何信息,输出空白会让你发狂! 有时是权限错误问题,有时是php错误什么的,程序停止运行,输出空白,让你一脸懵逼...
  • sm_Bo
  • sm_Bo
  • 2016年10月31日 15:45
  • 658

activemq读取剩余消息队列中消息的数量

先上原文链接: http://blog.163.com/chengwei_1104/blog/static/53645274201382315842515/  ActiveMQ 持久化(文件),查询...

用HTTP方式调用gearman任务处理

应用场景: 开启gearman http监听功能,让前端以web api方式调用gearman job 起用方式: 在gearmand的起动参数中加上: /usr/local/gearman/sbi...
  • xtjsxtj
  • xtjsxtj
  • 2014年07月24日 13:10
  • 2200

Gearman分布式任务处理系统(九)我的架构

Gearman分布式处理系统(九)我的架构
  • xtjsxtj
  • xtjsxtj
  • 2013年11月15日 14:04
  • 2843

android利用apollo实现消息推送

  • 2017年10月04日 11:17
  • 24.72MB
  • 下载

徐仙明:Android消息推送实现

  • 2014年05月29日 14:05
  • 1.09MB
  • 下载

php做推送服务端实现android消息推送

下载tokudu-PhpMQTTClient-ba4e494.zip 下载rsmb_1.2.0.zip(windows环境下要开\windows\broker.exe   切记切记)   解压t...
  • motian06
  • motian06
  • 2012年11月20日 12:46
  • 17464
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:用Gearman做队列来实现消息推送
举报原因:
原因补充:

(最多只允许输入30个字)