现在,您的 Web 应用程序可能不需要处理这么多数据,但是任何站点的处理时间都有可能超过客户愿意等待的时间。一般来说,客户愿意等待的时间是 200 毫秒,如果超过这个时间,客户就会觉得过程 “缓慢”。这个数字基于桌面应用程序,而 Web 使我们更有耐心了。但无论如何,不应该让客户等待的时间超过几秒。所以,要采用一些策略来处理 PHP 中的批处理作业。
批处理是相当简单的。在大多数情况下,采用两个工作流之一。第一个工作流用于进行报告;脚本每天运行一次,它生成报告并将报告发送给一组用户。第二个工作流是在响应某种请求时创建的批作业。例如,我登录进 Web 应用程序中,并要求它向系统中注册的所有用户发送一个消息,将一个新的特性告诉他们。这个操作必须进行批处理,因为系统中有 10,000 个用户。PHP 要花费一段时间才能完成这样的任务,所以它必须由浏览器之外的一个作业来执行。
在第二个工作流中,Web 应用程序只需将信息放在某个位置,让批处理应用程序共享它。这些信息指定作业的性质(例如,“Send this e-mail to all the people on the system”。)批处理程序运行这个作业,然后删除作业。另一种方法是,处理程序将作业标为已完成。无论用哪种方法,作业都应该识别为已完成,这样就不会再次运行它。
这个模型首先需要 MySQL 模式。
清单 1. mailout.sql
DROP TABLE IF EXISTS mailouts;
CREATE TABLE mailouts (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
from_address TEXT NOT NULL,
to_address TEXT NOT NULL,
subject TEXT NOT NULL,
content TEXT NOT NULL,
PRIMARY KEY ( id ));
这个模式非常简单。每行中有一个 from 和一个 to 地址,以及电子邮件的主题和内容。
对数据库中的 mailouts 表进行处理的是 PHP mailouts 类。
清单 2. mailouts.php
<?php
require_once('DB.php');
class Mailouts{
public static function get_db() {
$dsn = 'mysql://root:@localhost/mailout';
$db =& DB::Connect( $dsn, array() );
if (PEAR::isError($db)) {
die($db->getMessage());
}
return $db;
}
public static function delete( $id ) {
$db = Mailouts::get_db();
$sth = $db->prepare( 'DELETE FROM mailouts WHERE id=?' );
$db->execute( $sth, $id ); return true;
}
public static function add( $from, $to, $subject, $content ) {
$db = Mailouts::get_db();
$sth = $db->prepare( 'INSERT INTO mailouts VALUES (null,?,?,?,?)' );
$db->execute( $sth, array( $from, $to, $subject, $content ) );
return true;
} public static function get_all() {
$db = Mailouts::get_db();
$res = $db->query( "SELECT * FROM mailouts" );
$rows = array();
while( $res->fetchInto( $row ) ) {
$rows []= $row;
}
return $rows;
}
}
?>
这个脚本包含 Pear::DB 数据库访问类。然后定义 mailouts 类,其中包含三个主要的静态函数:add、delete 和 get_all。add() 方法向队列中添加一个电子邮件,这个方法由前端使用。get_all() 方法从表中返回所有数据。delete() 方法删除一个电子邮件。
下一步是编写一个简单的测试脚本,这个脚本将一个条目添加到队列中。
清单 3. mailout_test_add.php
<?php
require 'mailout.php';
Mailouts::add(
'donotreply@mydomain.com',
'molly@nocompany.com.org',
'Test Subject',
'This is a test of the batch mail sendout'
);
?>
在这个示例中,我添加一个 mailout,这个消息要发送给某公司的 Molly,其中包括主题 “Test Subject” 和电子邮件主体。可以在命令行上运行这个脚本:php mailout_test_add.php。
为此,可以利用一个事实:PHP 是一种解释型语言。可以将 PHP 代码存储在数据库中的队列中,以后再执行它。这需要两个表,见清单 5。
清单 5. generic.sql
DROP TABLE IF EXISTS processing_items;
CREATE TABLE processing_items (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
function TEXT NOT NULL,
PRIMARY KEY ( id )
);
DROP TABLE IF EXISTS processing_args;
CREATE TABLE processing_args (
id MEDIUMINT NOT NULL AUTO_INCREMENT,
item_id MEDIUMINT NOT NULL,
key_name TEXT NOT NULL,
value TEXT NOT NULL,
PRIMARY KEY ( id )
);