关于新增的异步回调方法

如果你关注 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
注意:这个插件只能在最新的开发版中使用。

已有 24 条评论

  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。

  5. 尚寂新 尚寂新

    希望正式版能有这个功能

    1. 尚寂新 尚寂新

      已升级te最新开发版,已实装此插件,效果拔群,虽说设置的时候有错误提示吧,但也不耽误用,正常发信。

  6. LeapLu LeapLu

    为了这个等会就升级开发版

  7. LeapLu LeapLu

    测试了下 评论后邮件不会发送是什么原因呢

    1. joyqi joyqi

      你的邮件参数是正确的么

      1. LeapLu LeapLu

        是正确的 保存的时候没有提示出错,而且用这个配置使用另外一个插件是正常的

        1. joyqi joyqi

          我用的是正常的,你看看日志,有没有正常回调

          1. LeapLu LeapLu

            失败了,找不到日志,请问是不是需要修改代码才可以使用?

          2. LeapLu LeapLu

            搞定了 不能用自己的SMTP地址而要用smtp.mxhichina.com。。。而且保存会提示向服务器发送指令错误,但是实际已经可以用了

  8. 双少 双少

    这个压缩包里面是啥啊,未知属性 不会用!

  9. 成公笔记 成公笔记

    好功能,希望评论邮件提醒插件能够尽快移植到正式版中。其实插件不用多,几个必要且使用的就行,尤其是官方版插件安全放心踏实。

  10. 时光 时光

    (Mailer插件)在后台回复的内容好像没有邮件提醒。

  11. 时光 时光

    我用的是QQ企业免费邮箱,SSL/465端口,也是提示向服务器发送指令错误,但是能用。

  12. 学习笔记Blog 学习笔记Blog

    其实我是真没有看懂异步回调,我就是喜欢分享的这个邮件插件!用起来感觉超好!速度快又稳定,强烈建议博主持续更新和优化这个插件, Typecho 确实需要一个轻便好用的评论邮件提醒插件的!