php实现不超时下载

5 篇文章 0 订阅
2 篇文章 0 订阅

项目简介:

        PHP项目后台常有下载导出数据的功能,当数据量比较小的时候,查询也比较快, 这时候使用PHP同步代码下载都可以实现,不会出现超时的情况(502/504错误), 当数据量大了之后,查询会很慢,生成的excel也很慢,比如可能需要3分钟, 这个时候同步代码就有问题了。解决方法就是使用异步代码,不超时下载。采用swoole扩展, 集成websocket客户端、websocket服务端、TCP客户端、TCP服务端、swoole_table缓存、 协程mysql、异步task任务。下载生成.xlsx文件采用PhpSpreadsheet类库。

流程说明:

        WebSocket客户端->WebSocket服务端->onMessage函数中创建TCP客户端->转发数据至TCP服务端
->异步Task任务处理数据生成excel->Task进程异步处理完成后推送数据至TCP客户端->TCP客户端
接收数据推送至WebSocket客户端。

目录结构:

app --- 服务目录
lib --- PhpSpreadsheet目录
upload --- 生成的xlsx文件目录
demo --- websocket客户端调用代码目录
vendor --- composer目录

启动说明:

php /app/empdown.php 为启动TCP服务端
php /app/empdown_ws.php 为启动WebSocket服务端,并在接收到WebSocket客户端的数据时创建TCP客户端,并转发数据到TCP服务端

备注:

        基于swoole扩展开发,需自行安装swoole4.3+扩展

WebSocket服务端代码:

<?php
/**
 * Created by PhpStorm.
 * User: tanlex
 * Date: 2019/5/28
 * Time: 15:26
 */
//websocket服务
$ws_server = new Swoole\WebSocket\Server("0.0.0.0", 9602);
//在https站点访问的时候需要设置SSL证书
$ws_server->set(
    [
        'worker_num' => 4,        //worker process num
        'max_request' => 3,       //每个worker进程任务数
        'ssl_cert_file' => '/etc/data/async_excel.cer', //证书
        'ssl_key_file' => '/etc/data/async_excel.key', //key
    ]
);
$ws_server->on('open', function (Swoole\WebSocket\Server $ws_server, $request) {
    echo "server: handshake success with fd{$request->fd}\n";
});
$ws_server->on('message', function (Swoole\WebSocket\Server $ws_server, $frame) {
    echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
    $data = $frame->data; //接收websocket客户端发送的数据
    $fd   = $frame->fd; //websocket客户端ID
    /********************创建TCP异步客户端********************/
    $client = new Swoole\Client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
    $client->on("connect", function(swoole_client $cli) use ($data){
        //给TCP服务端发送数据(也就是转发websocket客户端发送的数据)
        $cli->send($data."\n");
    });
    //接受TCP服务端的回调函数
    $client->on("receive", function(swoole_client $cli, $data) use ($ws_server,$fd){
        //调用websocket服务向websocket客户端返回数据
        $ws_server->push($fd, $data);
        //关闭TCP客户端
        $cli->close();
        //关闭websocket客户端
        $ws_server->close($fd);
    });
    $client->on("error", function(swoole_client $cli){
        echo "error\n";
    });
    $client->on("close", function(swoole_client $cli){
        echo "TCP Connection close\n";
    });
    $client->connect('127.0.0.1', 9601);
    /********************创建TCP异步客户端********************/
});
$ws_server->on('close', function ($ws_server, $fd) {
    echo "websocket client: {$fd} closed".PHP_EOL;
});
$ws_server->start();

TCP服务端代码:

<?php
/**
 * Created by PhpStorm.
 * User: tanlex
 * Date: 2019/5/28
 * Time: 15:26
 */
//引入MyPhpSpreadsheet类生成excel
require_once __DIR__ . '/../lib/MyPhpSpreadsheet.php';
//创建TCP服务
$serv = new swoole_server("0.0.0.0", 9601);
//设置异步任务的工作进程数量
$serv->set([
    'worker_num' => 4,        //worker process num
    'max_request' => 3,       //每个worker进程任务数
    'task_worker_num' => 4,   //task worker process num
    'task_enable_coroutine' => true, //异步任务支持协程
    'task_max_request'      => 3, //每个task进程任务数
]);
//创建共享内存
$table = new swoole_table(1024);
$table->column('fd', swoole_table::TYPE_INT);
$table->create();
//将table保存在serv对象上
$serv->table = $table;
$serv->on('receive', function($serv, $fd, $from_id, $data) {
    //接受TCP客户端数据并投递异步任务
    $task_id = $serv->task($data);
    //TCP客户端关联任务ID存储到swoole_table
    $serv->table->set('empdown_'.$task_id,['fd'=>$fd]);
});
//处理异步任务
$serv->on('Task', function ($serv, Swoole\Server\Task $task) {
/*
    //来自哪个`Worker`进程
    $task->worker_id;
    //任务的编号
    $task->id;
    //任务的类型,taskwait, task, taskCo, taskWaitMulti 可能使用不同的 flags
    $task->flags;
    //任务的数据
    $task->data;
    //协程 API
    co::sleep(0.2);
    //完成任务,结束并返回数据
    $task->finish([123, 'hello']);
*/
    //异步任务处理数据
    $data = json_decode($task->data);
    $keyword = $data->keyword;
    $status  = $data->status;
    $start   = $data->start;
    $end     = $data->end;
    $where = 'where 1 ';
    if(!empty($keyword)){
        $where .= 'and c.name like "%'.$keyword.'%" ';
    }
    if(!empty($status)){
        $where .= 'and c.status='.$status.' ';
    }
    if(!empty($start)){
        $where .= 'and c.create_time>="'.$start.'" ';
    }
    if(!empty($end)){
        $where .= 'and c.create_time<="'.$end.'" ';
    }
    //协程mysql
    $swoole_mysql = new Swoole\Coroutine\MySQL();
    $swoole_mysql->connect([
        'host' => '127.0.0.1',
        'port' => 3306,
        'user' => 'root',
        'password' => 'root3306',
        'database' => 'tang',
    ]);
//    var_dump($swoole_mysql);
    $data_sql = "select c.name,c.status,c.create_time
                 from tang.user c
                 $where order by c.id desc";
    $oneResult = $swoole_mysql->query($data_sql);
//    var_dump($oneResult);
    //实例化excel生成类
    $MyPhpSpreadsheet = new MyPhpSpreadsheet();
    //xlsx表格第一行
    $title = ['姓名','状态','创建时间'];
    $files = $MyPhpSpreadsheet->arrayToXlsx($oneResult,$title);
    //返回任务执行的结果
    $task->finish(json_encode($files));
});
//处理异步任务的结果
$serv->on('finish', function ($serv, $task_id, $data) {
    //获取异步任务结果
    echo "AsyncTask[$task_id] Finish: $data".PHP_EOL;
    //获取TCP客户端
    $fd = $serv->table->get('empdown_'.$task_id,'fd');
    if(!empty($fd)){
        $serv->send($fd, $data);
        $serv->table->del('empdown_'.$task_id);
    }
});
//TCP客户端关闭回调
$serv->on('close',function(swoole_server $server, int $fd, int $reactorId){
    echo "TCP Client :".$fd." closed ".PHP_EOL;
});
$serv->start();

WebSocket客户端代码:

<script>
    var wsServer = 'ws://127.0.0.1:9602';
    var websocket = new WebSocket(wsServer);
    websocket.onopen = function (evt) {
        console.log("onopen");
        var message = {
            keyword: '唐',
            status: '',
            start: '',
            end: ''
        };
        websocket.send(JSON.stringify(message)); //给websocket服务端发送数据
    };
    websocket.onclose = function (evt) {
        console.log("onclose");
    };
    websocket.onmessage = function (evt) {
        console.log('onmessage: ' + evt.data);
    };
    websocket.onerror = function (evt, e) {
        console.log('Error occured: ' + evt.data);
    };
</script>

完整代码请移步至:https://github.com/tanlex/async_excel

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值