PHP 执行代码的方式是同步串行的,也就是说,PHP 中定义的操作代码会一个接一个的逐步完成。如果我们需要在一个会话中执行大量操作,或者调用外部 API,这种同步执行代码的方式可能需要比较长的时间,从而造成用户等待时间过长。这是一个很糟糕的用户体验,在本文中,我们来看一下怎么异步执行耗时较长的操作。
使用 WP Cron 在 WordPress 中异步执行 PHP 任务
我们知道,WordPress 为我们提供了 WP Cron 系统,来帮助我们实现定时操作。我们实现异步 PHP 的方式就是把一些操作加入当前时间点后面的一次性任务计划中,从而推迟一些不需要立即有结果的操作。
比如 WordPress 发表评论时发送通知邮件给管理员的操作,默认情况下,这个操作是同步执行的,邮件必须发送成功后,系统才会提示用户评论成功,而在有些主机上,发送邮件会比较慢,这就导致了比较长的用户等待时间。
实际上,管理员并不需要立即收到通知邮件,就算收到了,管理员也不一定有时间马上处理。所以,我们可以先提示用户评论成功,然后,等到设置的发送邮件任务计划时间到了,再发送通知邮件。
下面的函数实现了一个简单的异步发送邮件的功能:
if ( ! defined( 'DOING_CRON' ) || ( defined( 'DOING_CRON' ) && ! DOING_CRON ) ) {
function async_send_wp_mail() {
// 获取 wp_mail 函数的参数
$args = func_get_args();
// 添加一个随机值以避免重复发送,参考: http://codex.wordpress.org/Function_Reference/wp_schedule_single_event
$args[] = mt_rand();
// 5 分钟之后发送邮件
wp_schedule_single_event( time() + 5, 'cron_send_mail', $args );
}
}
发送邮件的时候,我们可以使用以下代码异步发送邮件。
add_action( 'cron_send_mail', function () {
$args = func_get_args();
// 移除上面添加的随机数
array_pop( $args );
call_user_func_array( 'wp_mail', $args );
}, 10, 10 );
使用 WP Asynchronous Tasks 库在 WordPress 中实现异步 PHP 操作
使用 WP Cron 异步发送邮件的方法相当简单,但是会有一些效率问题,当邮件比较多时,延时可能会比较大。TechCrunch 开源的 WP Asynchronous Tasks 库解决了这个问题,实现方法是,在任务执行时,添加一个随机数作为任务标记,通过这个随机数来验证任务是否执行成功,如果执行成功,就接着执行推后的任务。
比如,在文章页面,我们需要获取几篇和当前文章拥有共同分类或标签的文章,作为相关文章显示,这是一个比较耗时的 MySQL 查询,我们一般需要使用缓存来优化。当缓存中有这个数据的时候,就直接显示缓存中的数据,如果缓存中没有这个数据,或者缓存已过期,则获取数据、加入缓存,然后再显示。
因为这是一个耗时的操作,在浏览比较大的站点上。如果先更新缓存再显示出来,用户等待的时间可能会比较长。在这个操作上,我们就可以使用异步的方式来优化这个操作,在显示页面的时候,直接显示缓存中的数据,同时判断一下缓存到期时候,如果快到期了,则添加一个异步任务来更新缓存。这样,页面中显示的永远是缓存的数据。
根据 TechCrunch 的说明,在合适的时机使用 WP Asynchronous Tasks 可以提升WordPress页面的加载速度到原来的 5-8倍,如果你的站点遇到了这方面的问题,可以考虑使用这个库来进行优化。
另外一个类似的库是 WP Migrate DB Pro 开发商开发的 WP Background Processing 库,该库在 WP Asynchronous Tasks 的基础上,增加了简单的任务队列支持。如果需要执行大量的耗时重复操作,我们需要的可能是基于数据库的完整 WordPress 任务队列。