利用Gearman实现异步任务处理

一、问题分析

问题:在性能测试过程中,发现用户管理平台在进行图片上传时,性能不佳。

分析:经过代码分析得知,主要的瓶颈是图片上传后,还需要根据图片组件所在页面模块容器进行一系列处理:图片生成缩略图,裁剪图片,加水印等,这些处理在高并发的情况下,会消耗大量cpu资源,导致系统响应速度非常慢。

旧的处理方式:

次控端调用前台接口进行上传图片后,前台程序先上传图片,接着,进行图片压缩等处理,然后再返回结果给次控端。

整个过程是串行的,次控端需要等待前台的结果后,再显示给用户。这个过程存在时间浪费,图片的处理可以提交到后台再进行处理,不需要马上处理。

因此,考虑采用Gearman进行异步任务处理。

新的处理方式:

次控端调用前台接口上传图片,前台保存要上传的图片后,提前返回信息给次控端,接着,调用Gearman增加一个任务,然后,由Gearman自动指派任务给服务器进行任务处理。采用新的处理方式后,次控端操作体验更加快捷。

二、Gearman详细介绍

简介:

官网:http://gearman.org/

Gearman是一个分布式的程序框架。Gearman提供了一个通用的应用程序,可以工作在更加适合处理这项任务的其他计算机或其他处理器上。它允许并行处理,载入平衡处理,而且可以在不同语言间进行调用。它可以应用于各种各样的应用场合。换句话说,它是一个进行分布式处理的强健系统。以下是关于Gearman的一些长处:

l  开源:完全开源,免费的。

l  多语言:有许多语言接口,而且支持的语言数量一直在增加。

l  灵活:不依赖于任何特定的设计。

l  快速:简单的协议和接口,可以减少现有应用程序的开销。

l  嵌入:轻量,可以以最小的代价引入到现有程序。

l  没有单点故障:具有较强的容错性。

框架:

gearman安装及初次使用_服务器

 

(摘自官网)

Gearman Client:提供客户端调用,API可以是多种语言,目前在智能建站里应用的是PHP语言

Gearman Job Server:调度者,负责把来自客户端的请求分发到不同服务器或不同处理器上的Worker。

Gearman Worker:负责任务处理,并返回结果。

说明:应用程序通过Client API调用创建客户端请求,Job Server把请求分发到不同的worker上进行处理,Worker接收到请求并进行处理后返回结果。

以下是Gearman中三个基本模块的调用关系图:

gearman安装及初次使用_html_02

 

 

(摘自官网)

Client与Worker是可以由不同的语言来写的,例如:Client由PHP来写,Worker由C语言来写。这样就可以很方便地把不同的语言编写的接口进行各自的接口封装。

不管是把Worker放到单独服务器,还是放到集群服务器,Worker都可以正常运行。

Client发送任务到Job Server中,由Job Server负责调度,Job Server会自动检测空闲的Worker,并发送任务到这个空闲的Worker,Worker进行任务处理。因此,Gearman框架可以实现资源负载平衡。

Gearman框架具有很强的扩展性。这个框架支持多核心CPU。如果Worker所在的服务器太繁忙,可以把服务器的CPU换成更多核心的CPU,如16核CPU,这样就可以在这台服务器上创建16个Worker实例来进行处理。当然,Gearman也支持服务器的扩展,可以很方便地扩展出更多的Worker服务器来处理请求。唯一需要做的事情就是:启动新服务器,安装好Worker程序,并修改配置即可。

如果服务器挂掉了,Gearman会如何处理呢?我们可以同时运行多个Job Server,Client和Worker连接到配置好的Job Server。如果这台Job Server出现故障,则配置到这台的Client和Worker会自动连接到另外一台可用的Job Server。这里建议大家,至少要有两台以上的Job Server,利用冗余来提高系统的容错性。

 

安装:

Job Server (gearmand)

gearmand (C)

A job server written in C/C++ under the BSD license that contains all of the latest protocol additions. Development for this is very active, with threading, persistent queues, and pluggable protocols now supported. Replication and better statistics reporting are coming soon. This installs an executable called​​gearmand​​ (usually in /usr/local/sbin). The C library, libgearman, is bundled in the same package.

./configure --prefix=/opt

安装报错:

checking whether the Boost::Program_Options library is available... yes

configure: error: Could not find a version of the library!

 安装boost:

  1. yum install boost  
  2. yum install boost-devel 

 

 

configure: error: could not find gperf

 安装之:yum install  gperf

(apt-get install gperf

Linux下生成hash函数代码的工具。)

configure: error: Unable to find libuuid

 还是错误。

 

因此,只有去官网下载:

​http://sourceforge.net/projects/libuuid/​

用源码安装方式安装。

然后,再次重新执行gearman目录下的./configure。呼呼~~~~~,终于不再出错了,这次顺利执行完毕。

 (可以加参数,如:./configure --with-sqlite3=/usr/local/sqlite3    --with-boost=/usr/local/boost-1.57   --with-boost-libdir=/usr/local/boost-1.57/lib  

--with-memcached=[PATH]

absolute path to memcached executable

 

(如果报错libcurl

安装:

aptitude install libcurl4-openssl-dev

 

报错uuid错误:




libgearman/.libs/libgearman.so: undefined reference to `uuid_generate_time_safe@UUID_2.20'

libgearman/.libs/libgearman.so: undefined reference to `uuid_unparse@UUID_1.0'

collect2: error: ld returned 1 exit status

make[1]: *** [t/sqlite] Error 1\

 

需要葱这里 ​​http://sourceforge.net/projects/e2fsprogs/files/e2fsprogs/v1.42.5/​​ 下载并安装e2fsprogs, 注意configure的参数必须是:./configure --prefix=/usr/local/e2fsprogs --enable-elf-shlibs,然后把uuid目录拷过去 cp -r lib/uuid/    /usr/include/ 和 cp -rf lib/libuuid.so* /usr/lib 之后make clean重试即可!  

.安装e2fsprogs (必须用源码安装,yum安装的不好使)ubuntu好像不行。

./configure –enable-elf-shlibs

make

make install

cp -r lib/uuid/ /usr/include/

cp -rf lib/libuuid.so* /usr/lib


)

安装 ​​gearmand​​ 后,构建 PHP 扩展。您可以从 PECL 获取这个 tarball,也可以从 Github 复制该存储库。

$ wget http://pecl.php.net/get/gearman-0.6.0.tgz
$ cd pecl-gearman
#
# or
#
$ git clone git://github.com/php/pecl-gearman.git
$ cd pecl-gearman


 

有了这些代码后,就可以开始构建扩展了:

$ phpize
$ ./configure
$ make
$ sudo make install


 ./configure 后面可以设置php-config路径:

./configure --prefix=/opt --with-php-config=/usr/local/php/bin/php-config

 

http://ju.outofmemory.cn/entry/105792

还是报错:

configure: error: Please install libgearman

我们在安装gearman的时候,已经有libgearman了,但是在编译扩展的时候,却用额外安装的lib,在运行的时候,难免出现性能方面的问题


在./configure的时候指定下gearman的路径就是了,不必降低额外安装libgearman及降低版本

./configure --prefix=/opt --with-php-config=/usr/local/php/bin/php-config --with-gearman=/opt/gearmand/

 ( 安装php扩展的时候,缺少 libgearman ,这在安装gearman的时候应该已经安装好了,在安装gearman的时候./configure 不要加参数即可)

这个 Gearman 守护程序通常被安装在 /usr/sbin。可以从命令行直接启动此守护程序,也可以将这个守护程序添加到启动配置中,以便在机器每次重启时就可以启动这个守护程序。

接下来,需要安装 Gearman 扩展。打开 php.ini 文件(可以通过 ​​php --ini​​ 命令快速找到这个文件),然后添加代码行 ​​extension = gearman.so​​:

$ php --ini
Loaded Configuration File: /etc/php/php.ini
$ vi /etc/php/php.ini
...
extension = gearman.so


 

保存此文件。要想验证扩展是否启用,请运行 ​​php --info​​,然后查找 Gearman:

$ php --info | grep "gearman support"
gearman
gearman support => enabled
libgearman version => 0.10


 

此外,还可以用一个 PHP 代码片段来验证构建和安装是否得当。将这个小应用程序保存到 verify_gearman.php:

<?php
print gearman_version() . "\n";
?>


接下来,从命令行运行此程序:

$ php verify_gearman.php
0.10


如果这个版本号与之前构建和安装的 Gearman 库的版本号相匹配,那么系统就已准备好了。

 

运行 Gearman

我们前面提到过,一个 Gearman 配置有三个角色:

  • 一个或多个 producer 生成工作请求。每个工作请求命名它所想要的函数,例如 ​​email_all​​ 或​​analyze​​。
  • 一个或多个 consumer 完成请求。每个 consumer 命名它所提供的一个或多个函数并向 agent 注册这些功能。一个 consumer 也可以被称为是一个 worker
  • 代理对与之建立连接的那些 consumer 提供的所有服务进行集中编制。它将 producer 与恰当的 consumer 联系起来。

借助如下的命令行,可以立即体验 Gearman:

  1. 启动这个 agent,即 Gearman 守护程序:
$ sudo /usr/sbin/gearmand --daemon

  1. 用命令行实用工具 ​​gearman​​ 运行一个 worker。这个 worker 需要一个名字并能运行任何命令行实用工具。例如,可以创建一个 worker 来列出某个目录的内容。​​-f​​ 参数命名了该 worker 所提供的函数:
$ gearman -w -f ls -- ls -lh

  1. 最后的一个代码块是一个 producer,或用来生成查找请求的一个作业。也可以用 ​​gearman​​ 生成一个请求。同样,用 ​​-f​​ 选项来指定想要从中获得帮助的那个服务:
$ gearman -f ls < /dev/null
drwxr-xr-x@ 43 supergiantrobot staff 1.4K Nov 15 15:07 gearman-0.6.0
-rw-r--r--@ 1 supergiantrobot staff 29K Oct 1 04:44 gearman-0.6.0.tgz
-rw-r--r--@ 1 supergiantrobot staff 5.8K Nov 15 15:32 gearman.html
drwxr-xr-x@ 32 supergiantrobot staff 1.1K Nov 15 14:04 gearmand-0.10
-rw-r--r--@ 1 supergiantrobot staff 5.3K Jan 1 1970 package.xml
drwxr-xr-x 47 supergiantrobot staff 1.6K Nov 15 14:45 pecl-gearman

从 PHP 使用 Gearman

从 PHP 使用 Gearman 类似于之前的示例,惟一的区别在于这里是在 PHP 内创建 producer 和 consumer。每个 consumer 的工作均封装在一个或多个 PHP 函数内。

​清 单 1​​ 给出了用 PHP 编写的一个 Gearman worker。将这些代码保存在一个名为 worker.php 的文件中。

1.Worker.php



<?php
$worker= new GearmanWorker();
$worker->addServer();
$worker->addFunction("title", "title_function");
while ($worker->work());

function title_function($job)
{
return ucwords(strtolower($job->workload()));
}
?>


​清 单 2​​ 给出了用 PHP 编写的一个 producer,或 client。将此代码保存在一个名为 client.php 的文件内。

清单 2. Client.php



<?php
$client= new GearmanClient();
$client->addServer();
print $client->do("title", "AlL THE World's a sTagE");
print "\n";
?>


[root@localhost code]# php Client.php

PHP Deprecated: GearmanClient::do(): Use GearmanClient::doNormal() in /opt/code/Client.php on line 4

All The Worlds's A Stags

把上面do改成doNormal

 

现在,可以用如下的命令行连接客户机与 worker 了:

$ php worker.php &
$ php client.php
All The World's A Stage
$ jobs
[3]+ Running php worker.php &


 

这个 worker 应用程序继续运行,准备好服务另一个客户机。

 

这个 worker 应用程序继续运行,准备好服务另一个客户机。

Gearman 的高级特性

在一个 Web 应用程序内可能有许多地方都会用到 Gearman。可以导入大量数据、发送许多电子邮件、编码视频文件、挖据数据并构建一个中央日志设施 — 所有这些均不会影响站点的体验和响应性。可以并行地处理数据。而且,由于 Gearman 协议是独立于语言和平台的,所以您可以在解决方案中混合编程语言。比如,可以用 PHP 编写一个 producer,用 ​​C​​、Ruby 或其他任何支持 Gearman 库的语言编写 worker。

一个连接客户机和 worker 的 Gearman 网络实际上可以使用任何您能想象得到的结构。很多配置能够运行多个代理并将 worker 分配到许多机器上。负载均衡是隐式的:每个可操作的可用 worker(可能是每个 worker 主机具有多个 worker)从队列中拉出作业。一个作业能够同步或异步运行并具有优先级。

Gearman 的最新版本已经将系统特性扩展到了包含持久的作业队列和用一个新协议来通过 HTTP 提交工作请求。对于前者,Gearman 工作队列保存在内存并在一个关系型数据库内存有备份。这样一来,如果 Gearman 守护程序故障,它就可以在重启后重新创建这个工作队列。另一个最新的改良通过一个 memcached 集群增加队列持久性。memcached 存储也依赖于内存,但被分散于几个机器以避免单点故障。

Gearman 是一个刚刚起步却很有实力的工作分发系统。据 Gearman 的作者 Eric Day 介绍,Yahoo! 在 60 或更多的服务器上使用 Gearman 每天处理 600 万个作业。新闻聚合器 Digg 也已构建了一个相同规模的 Gearman 网络,每天可处理 400,000 个作业。Gearman 的一个出色例子可以在 Narada 这个开源搜索引擎(参见 ​​参 考资料​​)中找到。

Gearman 的未来版本将收集并报告统计数据、提供高级监视和缓存作业结果等。为了跟踪这个 Gearman 项目,可以订阅它的 Google 组,或访问 Freenode 上它的 IRC 频道 ​​#gearman​​。

 ​

Gearman工作原理

Gearman是一个可进行任务调度分发,提供并行数据处理的分布式计算框架,支持多种编程语言.

它由三部分组成:

  • Client(客户端)
    发出任务处理请求.比如通过一个PHP脚本.
  • Job服务器
    接收来自客户端的任务处理请求,然后以负载均衡的方式分发给注册的worker.
  • Worker(worker进程)
    处理Job服务器分发过来的任务处理请求,可以是一个或多个PHP脚本.

 

gearman安装及初次使用_.net_03

 


  • Gearman的一些使用场景:
  • 长时间运行的处理:图片处理,订单处理,批量邮件/通知 ...
  • 要求高CPU或内存的处理:大容量的数据处理,MapReduce运算,日志聚集,视频编码
  • 分布式和并行的处理
  • 定时处理:增量更新,数据复制
  • 限制速率的FIFO处理
  • 分布式的系统监控任务:比如Mod-Gearman
  • PHP使用Gearman
    下面的简单例子仅为说明PHP如何使用Gearman,不具备实际生产意义.
    一个简单的worker:



[root@centos192 worker]# cat myworker.php 
<?php

$worker = new GearmanWorker();

$worker->addServer('192.168.1.192', 4730);

$worker->addFunction('sayhello', function(GearmanJob $job){
$name = $job->workload();
return "hello, " . $name;
});

$worker->addFunction('logMsg', function(GearmanJob $job){
$msg = $job->workload();
file_put_contents(__DIR__ . '/Msg.log', $msg, FILE_APPEND);
});

while ($worker->work()){
usleep(50000);
}


以守护进程方式启动worker:

# nohup php myworker.php >/dev/null 2>&1 &
addFuntion另外方式:
$gmworker->addFunction("reverse", "reverse_fn");
function reverse_fn($job)
{
return strrev($job->workload());
}


一个发送任务处理请求的client:



[root@centos191 client]# cat myclient.php 
<?php

$name = $argv[1];
if (empty($name)) {
echo "Usage: $argv[0] {name}";
exit(1);
}
try {
$client = new GearmanClient();
$client->addServer('192.168.1.192', 4730);
} catch(Exception $e){
echo "oops", $e->getMessage();
exit(1);
}
// 同步处理(Blocking),返回直接的处理结果
$result = $client->doNormal('sayhello', $name);
echo $result . PHP_EOL;

// 异步处理(Non-Blocking),仅返回一个处理状态的句柄
$job_handle = $client->doBackground('logMsg', $name);
echo $job_handle . PHP_EOL;


运行myclient.php:

[root@centos191 client]# php myclient.php "Jose"
hello, Jose
H:centos192:5
http://php.net/manual/zh/gearmanclient.donormal.php
GearmanClient::doNormal — Run a single task and return a result
public string GearmanClient::doNormal ( string $function_name , string $workload [, string$unique ] )
function_name


由程序自动执行的已注册函数。

workload


被处理的序列化数据。

unique


用于标识特定任务的唯一性 ID。

一个发出并行处理任务请求的例子tasksclient:



[root@centos191 client]# cat tasksclient.php 
<php

try {
$client = new GearmanClient();
$client->addServer('192.168.1.192', 4730);
} catch(Exception $e){
echo "oops", $e->getMessage();
exit(1);
}

$client->setCompleteCallback(function(GearmanTask $task){
echo 'Completed task:: id :', $task->unique(), ' , handled result:', $task->data(), PHP_EOL;
});

$client->addTask('sayhello', 'Jose', null, 1);
$client->addTask('sayhello', 'John', null, 2);
$client->addTaskBackground('logMsg', 'Peter');

$client->runTasks();


运行tasksclient:

[root@centos191 client]# php tasksclient.php 
Completed task:: id :2 , handled result:hello, John
Completed task:: id :1 , handled result:hello, Jose
我最开始没有修改addServer的ip,导致php taskslinet.php一直处于运行状态,没有结果输出。这说明只要不返回结果,程序一直运行。


查看worker及队列情况

查查当前注册的worker及function(显示格式:FD IP-ADDRESS CLIENT-ID : FUNCTION ...)

[root@centos192 worker]# (echo "workers"; sleep 0.1) | nc 127.0.0.1 4730
32 192.168.1.192 - : sayhello logMsg
33 127.0.0.1 - :
.


查看当前队列情况(显示格式FUNCTION\tTOTAL\tRUNNING\tAVAILABLE_WORKERS):

[root@centos192 worker]# (echo "status"; sleep 0.1) | nc 127.0.0.1 4730
sayhello 0 0 1
logMsg 0 0 1
.


第一列是function名称,第2列显示队列中的任务数,第3列显示正在运行的任务数,第四列显示可用的worker数.

  • 关于Worker守护进程的启动和监控
    上面例子中直接启动worker脚本作为守护进程,无法监控到worker进程是否存活.
    使用Unix进程监控supervisord则可轻松解决这个问题.
    将如下配置添加到supervisord的配置中,即可实现由supervisord来启动和监控myworker.
    [program:myworker]command=/usr/local/php5415/bin/php myworker.php process_name=%(program_name)s_%(process_num)02d ;修改numprocs即可同时开启多个worker进程 numprocs=1 directory=/data/www/gearman/worker autostart=true autorestart=true user=gearmand stopsignal=KILL


 

gearman安装后在bin目录下有2个程序gearman,gearmanadmin,它用来Run Administrative commands against a server.

--help

Provice help about the program.

--create-function

Create a function from the server.

-h [ --host ] arg (=localhost)i

Connect to the host

-p [ --port ] arg (=4730)

Port number or service to use for connection

--drop-function

Drop a function from the server.

--server-version

Fetch the version number for the server.

--server-verbose

Fetch the verbose setting for the server.

--status

Status for the server.

--workers

Workers for the server.

--shutdown

Shutdown server.

 

bin目录下的gearman用来命令行操作命令。Command line client for Gearmand

 

Common options

-f <function>

Function name to use for jobs (can give many)

-h <host>

Job server host

-H

Print this help menu

-p <port>

Gearman server port

-t <timeout>

Timeout in milliseconds

-i <pidfile>

Create a pidfile for the process

-n

In client mode run one job per line, in worker mode send data packet for each line

-N

Same as -n, but strip off the newline


Client options

-b

Run jobs in the background

-I

Run jobs as high priority

-L

Run jobs as low priority

-P

Prefix all output lines with functions names

-s

Send job without reading from standard input

-u <unique>

Unique key to use for job


Worker options*

-c <count>​​¶​

Number of jobs for worker to run before exiting

-w

Run in worker mode

 

With gearman you can run client and worker functions from the command line.

The environmental variable GEARMAN_SERVER can be used to specify multiple gearmand servers. Please see the c:func:’gearman_client_add_servers’ for an explanation of the required syntax.



​http://gearman.info​​ 是一个gearman文档的网站,不错。

http://php.net/manual/zh/gearmanclient.donormal.php php官方关于gearman的文档。


 

 

gearman实现分布式日志分析

 

编译安装Gearman