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

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

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

    大概情况

    我是做前端的,会点 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
    vus520
        1
    vus520  
       2017-04-27 18:16:56 +08:00
    你把要发的邮件写到数据库(队列)中,然后有一个独立的程序在后台循环读队列,发邮件就行。

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

    于是就实现了异步啦, 啦啦~~
    orderc
        6
    orderc  
       2017-04-27 18:48:33 +08:00
    php-resque 轻量级的消息队列
    bianhua
        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 会话的延时小一些)。
    R18
        8
    R18  
       2017-04-27 18:56:04 +08:00 via Android
    vus520
        9
    vus520  
       2017-04-27 19:44:39 +08:00
    @cstome

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

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

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

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