关于新增的异步回调方法

如果你关注 Typecho 的更新日志,会发现我今天增加了一个比较重要的新特性:异步回调
https://github.com/typecho/typecho/commit/f180e8452de92a99d189779a67b6caf1ce2b26cf

顾名思义,它允许你创建一个异步的方法,并接受回调。主要用于一些会长时间阻塞同步请求而又不必等待返回结果的操作

它的主要原理是:
将这些等待时间较长的操作,注册到另一个 URL 中,当需要执行它们的时候,主进程带上必要的参数请求这个URL。同时不必等待这个请求返回,立即断开这个连接。此时,在执行 URL 的那个进程上,调用 PHP 的 ignore_user_abort 函数,忽略请求端的主动断开,继续执行到所有代码结束。

举一个典型的使用场景:评论的邮件提醒功能。发邮件由于需要用 SOCKS 请求 SMTP 接口,从发起连接,完成认证到发送结束,往往需要很长时间。这个时候用户的评论发送请求就会一直阻塞在那里,体验会非常不好。如果用异步回调将邮件发送过程封装进去,用户在提交评论后就不必等待邮件发送结束了。

使用方法,首先在 active 的时候注册一个回调放到 Widget_Service 里去

public static function activate() {
    Typecho_Plugin::factory('Widget_Feedback')->finishComment = array('Mailer_Plugin', 'send');
    Typecho_Plugin::factory('Widget_Service')->sendMail = array('Mailer_Plugin', 'sendMail');
}

在上面的代码中,我们还注册了一个 send 方法到 Widget_FeedbackfinishComment 钩子点,用于将参数发送到异步的回调中,这个 send 函数的实现非常简单,它不包含任何实际的发送代码,只是用 Helper::requestService 把刚刚新增的那条评论 coid 发送到异步回调 sendMail 中。

public static function send($comment)
{
    Helper::requestService('sendMail', $comment->coid);
}

而另一个用于在异步回调中执行的函数就包含了完整的发送逻辑

public static function sendMail($commentId)
{
    $options = Helper::options();
    $pluginOptions = $options->plugin('Mailer');
    $comment = Helper::widgetById('comments', $commentId);

    if (empty($pluginOptions->host)) {
        return;
    }

    if (!$comment->have() || empty($comment->mail)) {
        return;
    }
        
    $mail = new PHPMailer(false);
    // ... 剩下的都是请求代码
}

另外,我们还新增了一个常量 __TYPECHO_SERVICE_URL__,用于提高回调的性能。因为这个回调本质是通过 HTTP 网络接口请求自己,默认会使用你的外网正式访问地址作为访问前缀,对于某些使用了反向代理的用户其实可以更快地响应。首先在 /etc/hosts 文件里把网站域名指向 127.0.0.1,这样在发起网络请求的时候就不必使用外部的 DNS 解析,而且网络请求也会走本地环路大大地节省时间,如果你在本地使用了一个反向代理,可以在 config.inc.php 中直接把 __TYPECHO_SERVICE_URL__ 设置成反向代理的访问地址,比如

define('__TYPECHO_SERVICE_URL__', 'http://127.0.0.1:8080');

这是我做的一个完整的例子,异步回调版的评论邮件插件 Mailer.tar.gz
注意:这个插件只能在最新的开发版中使用。

已有 10 条评论

  1. 好奇的Alpha 好奇的Alpha

    这个好,邮件提醒终于有一个个完美的插件了。

  2. Tabby. Tabby.

    启用插件后 syntax error, unexpected '000644' (T_LNUMBER)

    ParseError: syntax error, unexpected '000644' (T_LNUMBER) in /home/locred/public_html/usr/plugins/mailer.php:4876
    Stack trace:

    0 /home/locred/public_html/var/Widget/Plugins/Edit.php(308): Widget_Plugins_Edit->activate('mailer')1 /home/locred/public_html/var/Widget/Do.php(82): Widget_Plugins_Edit->action()2 /home/locred/public_html/var/Typecho/Widget.php(221): Widget_Do->execute()3 /home/locred/public_html/var/Typecho/Router.php(135): Typecho_Widget::widget('Widget_Do', NULL, Array)4 /home/locred/public_html/index.php(23): Typecho_Router::dispatch()5 {main}
    1. joyqi joyqi

      你的插件目录放错了吧,这些文件要放到 usr/plugins/Mailer 目录下,你是不是把里面的文件解压出来了

      1. Tabby. Tabby.

        这个我下载下来是一窜数字 我把里面的文件重命名mailer.php的

      2. Tabby. Tabby.

        大佬 这个文件由zip包吗..

      3. Tabby. Tabby.

        已经可以了 谢谢

  3. 鱼鱼 鱼鱼

    博主博主 最新开发版12.13

  4. 鱼鱼 鱼鱼

    博主博主 最新开发版12.13点开发版启用后错误500怎么办 官方找不到11.15的开发版了 能分享个嘛.

    1. joyqi joyqi

      我并没有重现这个错误,你在 config.inc.php 里加上一行
      define('__TYPECHO_DEBUG__', true);

      看看具体的报错信息

      1. 鱼鱼 鱼鱼

        添加了,,还是显示不了 额额 就直接错误500。