首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  PHP

PHP 有没有类似异步的功能?

  •  
  •   cstome · 2017-04-27 18:13:32 +08:00 · 7574 次点击
    这是一个创建于 908 天前的主题,其中的信息可能已经有所发展或是发生改变。

    大概情况

    我是做前端的,会点 PHP,但不打算花太多时间深入学 PHP (正在学 Python ),所以想问问有没有捷径。

    大概需求

    最近做个小项目,用 PHP 输出 JSON 并发送邮件,大概逻辑是这样的:

    处理 JSON...

    返回 JSON...

    发邮件(调用 PHPMailer )...

    但是这样的话会等待发邮件任务完成在返回 JSON,想知道有什么方法可以先返回(即结束当前脚本),在处理发邮件的任务?

    第 1 条附言  ·  2017-04-27 22:33:30 +08:00
    16 楼的这个思路很可以:

    只能用虚机的话就只能换个想法实现了,你看看这样子行不
    PHP 输出 JSON
    JSON 存入数据库并返回 JSON 并附带 KEY
    浏览器以 KEY 异步调用另一段 PHP 脚本发送邮件
    邮件发送成功返回
    55 回复  |  直到 2017-07-18 17:30:16 +08:00
        1
    vus520   2017-04-27 18:16:56 +08:00
    你把要发的邮件写到数据库(队列)中,然后有一个独立的程序在后台循环读队列,发邮件就行。

    所谓的异步,总是离不开队列。
        2
    yxslnmp   2017-04-27 18:22:27 +08:00
    swoole...
    简单的就用 redis 处理吧
        3
    cstome   2017-04-27 18:36:41 +08:00
    @vus520 由于使用虚拟主机,所以似乎没法开个进程在后台。。。
        4
    xifangczy   2017-04-27 18:39:51 +08:00
    1、ajax
    2、flush http://php.net/manual/zh/function.flush.php
    3、swoole...
        5
    lights   2017-04-27 18:47:54 +08:00
    首先要有一个队列, 简单的比如 redis, rabbitMQ 其实也很简单
    然后主程序将需要发送的邮件内容啊主题啊收件人啊一些信息写到队列里
    有另外一个常驻的程序监听这个队列, 并发送邮件

    于是就实现了异步啦, 啦啦~~
        6
    orderc   2017-04-27 18:48:33 +08:00
    php-resque 轻量级的消息队列
        7
    bianhua   2017-04-27 18:53:09 +08:00
    @cstome 简单的答案是没有。

    当然你不在乎把你的处理流程变乱,方法还是有的:
    你需要发送 Connection: Close 和 Content-Length 头,设置 set_time_limit、ignore_user_abort,然后用 flush (以及 ob_flush )冲洗缓冲。

    这样你的代码在用户浏览器断开之后还能在运行一段时间。

    不过这样会导致很多问题,首先运行那段 PHP 代码的进程或线程会阻塞,导致服务器处理效率变低,其次它所占用的内存一直无法被释放。这两条加起来可能会导致 DoS 弱点。

    如果是虚拟主机,其实可以想办法写个远程队列服务,或用云邮件服务来解决发送邮件时阻塞的问题(发送 HTTP 请求的延时在绝大多数情况下会比一次 SMTP 会话的延时小一些)。
        8
    R18   2017-04-27 18:56:04 +08:00 via Android
        9
    vus520   2017-04-27 19:44:39 +08:00
    @cstome

    都有这样的业务需求了,还在用虚拟主机,我服你。
        10
    yangyao   2017-04-27 19:46:02 +08:00
    pclose(popen("php -f sendmail.php","r"));
        12
    lygmqkl   2017-04-27 20:07:09 +08:00 via iPhone
    借助数据库完成队列,然后 cli 进行发送,如果需要 feedback cli 再更新下数据库记录结果。

    ps cli 是多线程,性能不错。可以实现多服务器异歩架构。也可以把这里 cli 换成 py nodejs go
        13
    keller   2017-04-27 20:37:00 +08:00
    怎么不用 node ?
        14
    eoo   2017-04-27 20:45:26 +08:00 via Android
    前端 干嘛不试试 nodejs ?
        15
    mchl   2017-04-27 20:45:45 +08:00 via Android
    laravel queue
        16
    murusu   2017-04-27 20:48:36 +08:00   ♥ 1
    只能用虚机的话就只能换个想法实现了,你看看这样子行不
    PHP 输出 JSON
    JSON 存入数据库并返回 JSON 并附带 KEY
    浏览器以 KEY 异步调用另一段 PHP 脚本发送邮件
    邮件发送成功返回
        17
    cxbig   2017-04-27 20:50:36 +08:00
    你说的不会是 Host 吧?至少也得换成 VPS
    前端干嘛折腾 PHP,用 Node 多好
    比方说用 AWS 的解决方案,SQS 保存队列,Lambda 来跟进处理,发邮件可以 用 SNS。
        18
    zhs227   2017-04-27 20:52:08 +08:00
    虚拟主机的话在请求量不大情况下可以先 ob_flush,然后把线程挂着去发邮件。
    访问量大的话就建议至少弄个 vps,不要玩虚拟主机了。
        19
    shiny   2017-04-27 20:58:34 +08:00
    为啥不用成熟的邮件发送接口,丢给他们去处理。他们有自己的队列。
        20
    w7938940   2017-04-27 21:09:20 +08:00
    PHP 不能 fork 一个然后在里面执行吗
        21
    kran   2017-04-27 21:32:50 +08:00 via Android   ♥ 1
    fastcgi_finish_request
        22
    hainuo   2017-04-27 21:59:12 +08:00 via iPhone
    有两个东西可以做到 异步编程对 php 来说是服务端变成 你可以看一下 reactphp swoole workman 等
        23
    cstome   2017-04-27 22:32:15 +08:00
    @murusu 这个思路很可以!
        24
    sagaxu   2017-04-27 23:13:20 +08:00
    PHP 太残了,别的语言一个线程池和 Queue 就搞定的事,到了 php 这里还要各种绕路
        25
    jssngz   2017-04-27 23:26:03 +08:00 via Android
    php 看了语法和 jsp asp 是一个等级的,还没有到语言的层级
        26
    dream7758521   2017-04-28 02:04:59 +08:00 via Android
    教你一个最简单的方法,先处理完 json,然后将相关信息存到数据库。
    然后在写一个单独处理邮件发送的 php,在这个页面最后面加入跳转到本页的功能,
    然后用浏览器打开这一页 php,放着别动就可以了,发送完毕后又会自动跳转到本页,然后又会继续运行!
        27
    Mitt   2017-04-28 02:46:55 +08:00   ♥ 1
    @jssngz
    @sagaxu

    本来 PHP 设计也不是来干这种事的呀 选语言也要先搞清楚自己需要什么把 动不动就把一个语言说的那么残废 总想要个万能语言又想成本低又简单
        28
    dangyuluo   2017-04-28 07:20:45 +08:00
    beanstalkd
        29
    dangyuluo   2017-04-28 07:21:26 +08:00
    @sagaxu
    @jssngz

    两位大神,服。
        30
    simapple   2017-04-28 08:07:26 +08:00
    gearman
        31
    wwolf   2017-04-28 08:11:13 +08:00
    swoole
        32
    MushishiXian   2017-04-28 08:12:01 +08:00
    别人问个 php 问题都有人黑语言的,也不看自己什么水平,就说一门语言怎样怎样
        33
    jininij   2017-04-28 08:46:11 +08:00 via Android
    yield 协程可以实现异步。伪代码长这样

    function sendEmail($argv){
    $message = yield ;
    //1.发送邮件
    sleep(3);
    }
    $y = sendEmail($a);
    //前期工作
    $y->send($m);
    //2.其他工作,输出内容。
    sleep(2);

    1 和 2 会在不同线程中执行,脚本实际执行时间是 1 和 2 中最长的。3 秒,而不是 5 秒。
        34
    KAAAsS   2017-04-28 08:50:39 +08:00
    ignore_user_abortb 吧……但是还是考虑考虑 VPS 吧
        35
    ic2y   2017-04-28 09:04:06 +08:00
    都做成 ajax 调用。后两部 分拆为两个 ajax。
        36
    silenceeeee   2017-04-28 09:04:50 +08:00
    private $_queue = array();
    function send() {
    // ...
    // echo JSON
    // $this->_queue[] = array('addr'=> '[email protected]', ...);
    }

    public function __destruct() {
    // send email
    }
        37
    mikej   2017-04-28 09:14:22 +08:00
    可以试试 fastcgi_finish_request,看鸟哥的 blog: http://www.laruence.com/2011/04/13/1991.html
        38
    Felldeadbird   2017-04-28 09:15:29 +08:00
    我司是这么处理的。
    用户触发发送邮件(通知类)。先将待发送的内容保存到数据库(缓存)。然后马上返回给前台告知操作成功。
    后台有 cron 定时去处理这些任务。
        39
    falcon05   2017-04-28 09:36:25 +08:00 via iPhone
    用 ajax 处理队列的做法我好像在某个 O2O 系统看过。
        40
    tabris17   2017-04-28 09:42:04 +08:00
    传统 cli 或者 fcgi 模式的 PHP 是不支持异步的,可以用 Swoole 或者 workerman
        41
    barbery   2017-04-28 10:09:16 +08:00
    这个用队列不就完了?
        42
    dryyun   2017-04-28 10:58:28 +08:00
    既然是小项目,为什么要做的那么复杂。发个邮件能用多久呢。。
    直接 try{
    发邮件。。
    }catch(){
    ...
    }
    发邮件能成功就是很快的,不能成功,就是会报错,干脆发邮件超时时间设的稍微短一点,不就解决问题了。
        43
    iyaozhen   2017-04-28 11:06:38 +08:00 via Android
    @kran 这个比较简单。先给前端返回数据,然后再发送邮件,完成后进程才会退出。
        44
    silva   2017-04-28 11:17:20 +08:00
    @R18 我看你给出的链接里不是说明 PHP 有 pcntl_fork()接口么,为何他们都说的那么绕呢?
        45
    R18   2017-04-28 11:22:46 +08:00
    @silva 我也不晓得,可能不符合实际场景吧
        46
    suconghou   2017-04-28 11:29:56 +08:00
    @jininij 然而并不是
        47
    qieqie   2017-04-28 11:33:19 +08:00
    @jininij 你这是把 php 脑补成 go 了,generator 需要你手动调度自己实现协程,也依赖更底层的异步 io 接口(换句话说就是支持 non-blocking io 扩展或者用 libevent 这样的事件通知库自己实现)
        48
    tkisme2013   2017-04-28 13:15:03 +08:00
    celery
        49
    flowfire   2017-04-28 18:12:10 +08:00 via iPhone
    nodejs 大法好
    我自从用了 node 就再也不想碰 php 了
        50
    xiaotianhu   2017-04-28 22:57:14 +08:00
    fastcgi_finish_request 最简单的套路
        51
    abcbuzhiming   2017-05-04 11:34:50 +08:00
    php 的异步都是扯淡的,不借助队列压根没办法,除非你能自己写插件,所以简单的就搞个队列比如 redis,复杂的。。
        52
    nobird   2017-05-16 01:33:35 +08:00 via iPhone
    需要发送的邮件内容存入数据库 当作一个队列处理 有访客访问页面的时候 每个页面刷新就发送一个邮件 小规模使用的话 效果还不错
        53
    arist   2017-05-16 15:03:17 +08:00
    我司是这样处理,
    1. 把消息写入数据库队列
    2. 使用 fsockopen 异步调用消息处理的程序,这个相当于非阻塞的模式,不需等待远端返回。
    3. 直接返回成功
        54
    wizardforcel   2017-05-19 18:23:22 +08:00 via Android
    你应该听说过一个词,queue based architecture。
        55
    cccoco123   2017-07-18 17:30:16 +08:00
    https://github.com/fucongcong/Group-Co
    异步协程框架,SOA 服务化调用,支持并行、串行调用。支持异步日志,异步文件读写,异步 Mysql,异步 Redis,Mysql,Redis 连接池
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   4216 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 34ms · UTC 02:48 · PVG 10:48 · LAX 19:48 · JFK 22:48
    ♥ Do have faith in what you're doing.