首頁  >  文章  >  php框架  >  php Swoole實作毫秒定時任務

php Swoole實作毫秒定時任務

藏色散人
藏色散人轉載
2020-01-25 19:14:164133瀏覽

php Swoole實作毫秒定時任務

專案開發中,如果有定時任務的業務要求,我們會使用linux的crontab來解決,但是它的最小粒度是分鐘級別,如果要求粒度是秒級別的,甚至毫秒級別的,crontab無法滿足,值得慶幸的是swoole提供的強大的毫秒定時器。

推薦學習: swoole教學

應用情境範例

我們可能會遇到這樣的場景:

● 場景一:每隔30秒取得一次本機記憶體使用率

● 場景二:2分鐘後執行報表傳送任務

● 場景三:每天凌晨2點鐘定時請求第三方接口,如果接口有資料返回則停止任務,如果接口由於某種原因沒有響應或者沒有數據返回則5分鐘後繼續嘗試請求該接口,嘗試5次後仍然失敗則停止該任務

以上的三個場景我們都可以歸納為定時任務的範疇。

Swoole毫秒定時器

Swoole提供了非同步毫秒定時器函數:

swoole_timer_tick(int $msec, callable $callback) :設定一個間隔時脈定時器,每隔$msec毫秒執行一次$callback,類似javascript中的setInterval()

swoole_timer_after(int $after_time_ms, mixed $callback_function):在指定的時間$after_time_ms後執行$callback_function,類似於javascript的setTimeout()

swoole_timer_clear(int $timer_id):刪除指定id的計時器,類似javascript的clearInterval()

解決方案

對於場景一,常用在系統偵測統計方面,即時性需求比較高,但又能控制好頻率,多用於後台伺服器效能監控,可以產生可視化圖表。可以是30秒取得一次記憶體使用率,也可以是10秒,而crontab最小粒度只能設定為1分鐘。

 swoole_timer_tick(30000, function($timer) use ($task_id) { // 启用定时器,每30秒执行一次
     $memPercent = $this->getMemoryUsage(); //计算内存使用率
     echo date('Y-m-d H:i:s') . '当前内存使用率:'.$memPercent."\n";
 });

 

 

對於場景二,直接定義xx時間後執行某項任務的話,看起來像crontab比較困難,而使用swoole的swoole_timer_after可以實現:

 swoole_timer_after(120000, function() use ($str) { //2分钟后执行
     $this->sendReport(); //发送报表
     echo "send report, $str\n";
 });

 

對於場景三,用來作嘗試請求,請求失敗後繼續,如果成功則停止請求。用crontab也能解決,但比較傻,例如設定每隔5分鐘請求一次,不管成功會失敗都會去執行一次。而用swoole定時器則智慧多了。

swoole_timer_tick(5*60*1000, function($timer) use ($url) { // 启用定时器,每5分钟执行一次
      $rs = $this->postUrl($url);
  
      if ($rs) {
          //业务代码...
          swoole_timer_clear($timer); // 停止定时器
          echo date('Y-m-d H:i:s'). "请求接口任务执行成功\n";
      } else {
          echo date('Y-m-d H:i:s'). "请求接口失败,5分钟后再次尝试\n";
     }
 });

 

範例程式碼

新檔案\src\App\Task.php:

<?php 
namespace Helloweba\Swoole;

use swoole_server;

/**
* 任务调度
*/
class Task
{
    protected $serv;
    protected $host = &#39;127.0.0.1&#39;;
    protected $port = 9506;
    // 进程名称
    protected $taskName = &#39;swooleTask&#39;;
    // PID路径
    protected $pidPath = &#39;/run/swooletask.pid&#39;;
    // 设置运行时参数
    protected $options = [
        &#39;worker_num&#39; => 4, //worker进程数,一般设置为CPU数的1-4倍  
        &#39;daemonize&#39; => true, //启用守护进程
        &#39;log_file&#39; => &#39;/data/log/swoole-task.log&#39;, //指定swoole错误日志文件
        &#39;log_level&#39; => 0, //日志级别 范围是0-5,0-DEBUG,1-TRACE,2-INFO,3-NOTICE,4-WARNING,5-ERROR
        &#39;dispatch_mode&#39; => 1, //数据包分发策略,1-轮询模式
        &#39;task_worker_num&#39; => 4, //task进程的数量
        &#39;task_ipc_mode&#39; => 3, //使用消息队列通信,并设置为争抢模式
    ];

    public function __construct($options = [])
    {
        date_default_timezone_set(&#39;PRC&#39;); 
        // 构建Server对象,监听127.0.0.1:9506端口
        $this->serv = new swoole_server($this->host, $this->port);

        if (!empty($options)) {
            $this->options = array_merge($this->options, $options);
        }
        $this->serv->set($this->options);

        // 注册事件
        $this->serv->on(&#39;Start&#39;, [$this, &#39;onStart&#39;]);
        $this->serv->on(&#39;Connect&#39;, [$this, &#39;onConnect&#39;]);
        $this->serv->on(&#39;Receive&#39;, [$this, &#39;onReceive&#39;]);
        $this->serv->on(&#39;Task&#39;, [$this, &#39;onTask&#39;]);  
        $this->serv->on(&#39;Finish&#39;, [$this, &#39;onFinish&#39;]);
        $this->serv->on(&#39;Close&#39;, [$this, &#39;onClose&#39;]);
    }

    public function start()
    {
        // Run worker
        $this->serv->start();
    }

    public function onStart($serv)
    {
        // 设置进程名
        cli_set_process_title($this->taskName);
        //记录进程id,脚本实现自动重启
        $pid = "{$serv->master_pid}\n{$serv->manager_pid}";
        file_put_contents($this->pidPath, $pid);
    }

    //监听连接进入事件
    public function onConnect($serv, $fd, $from_id)
    {
        $serv->send( $fd, "Hello {$fd}!" );
    }

    // 监听数据接收事件
    public function onReceive(swoole_server $serv, $fd, $from_id, $data)
    {
        echo "Get Message From Client {$fd}:{$data}\n";
        //$this->writeLog(&#39;接收客户端参数:&#39;.$fd .&#39;-&#39;.$data);
        $res[&#39;result&#39;] = &#39;success&#39;;
        $serv->send($fd, json_encode($res)); // 同步返回消息给客户端
        $serv->task($data);  // 执行异步任务
    }

    /**
    * @param $serv swoole_server swoole_server对象
    * @param $task_id int 任务id
    * @param $from_id int 投递任务的worker_id
    * @param $data string 投递的数据
    */
    public function onTask(swoole_server $serv, $task_id, $from_id, $data)
    {
        swoole_timer_tick(30000, function($timer) use ($task_id) { // 启用定时器,每30秒执行一次
            $memPercent = $this->getMemoryUsage();
            echo date(&#39;Y-m-d H:i:s&#39;) . &#39;当前内存使用率:&#39;.$memPercent."\n";
        });
    }


    /**
    * @param $serv swoole_server swoole_server对象
    * @param $task_id int 任务id
    * @param $data string 任务返回的数据
    */
    public function onFinish(swoole_server $serv, $task_id, $data)
    {
        //
    }


    // 监听连接关闭事件
    public function onClose($serv, $fd, $from_id) {
        echo "Client {$fd} close connection\n";
    }

    public function stop()
    {
        $this->serv->stop();
    }

    private function getMemoryUsage()
    {
        // MEMORY
        if (false === ($str = @file("/proc/meminfo"))) return false;
        $str = implode("", $str);
        preg_match_all("/MemTotal\s{0,}\:+\s{0,}([\d\.]+).+?MemFree\s{0,}\:+\s{0,}([\d\.]+).+?Cached\s{0,}\:+\s{0,}([\d\.]+).+?SwapTotal\s{0,}\:+\s{0,}([\d\.]+).+?SwapFree\s{0,}\:+\s{0,}([\d\.]+)/s", $str, $buf);
        //preg_match_all("/Buffers\s{0,}\:+\s{0,}([\d\.]+)/s", $str, $buffers);

        $memTotal = round($buf[1][0]/1024, 2);
        $memFree = round($buf[2][0]/1024, 2);
        $memUsed = $memTotal - $memFree;
        $memPercent = (floatval($memTotal)!=0) ? round($memUsed/$memTotal*100,2):0;

        return $memPercent;
    }
}

 

 

我們以場景一為例,在onTask啟用定時任務,每隔30秒計算一次記憶體使用率。實際應用中可以把計算好的記憶體按時間寫入資料庫等儲存中,然後可以根據前端需求用來渲染成統計圖表,如:

php Swoole實作毫秒定時任務

##接著服務端程式碼public\taskServer.php :

<?php 
require dirname(__DIR__) . &#39;/vendor/autoload.php&#39;;
use Helloweba\Swoole\Task;
$opt = [
    &#39;daemonize&#39; => false
];
$ser = new Task($opt);
$ser->start();

 

 

客戶端程式碼public\taskClient.php :

<?php 
class Client
{
    private $client;
    public function __construct() {
        $this->client = new swoole_client(SWOOLE_SOCK_TCP);
    }
    public function connect() {
        if( !$this->client->connect("127.0.0.1", 9506 , 1) ) {
            echo "Error: {$this->client->errMsg}[{$this->client->errCode}]\n";
        }
        fwrite(STDOUT, "请输入消息 Please input msg:");
        $msg = trim(fgets(STDIN));
        $this->client->send( $msg );
        $message = $this->client->recv();
        echo "Get Message From Server:{$message}\n";
    }
}
$client = new Client();
$client->connect();

 

#驗證效果

1.啟動服務端:

php taskServer.php

2.客戶端輸入:

另開命令列窗口,執行

[root@localhost public]# php taskClient.php

請輸入訊息Please input msg:hello

Get Message From Server:{"result":"success"}
[root@localhost public]#

 

3.服務端回傳:

php Swoole實作毫秒定時任務

如果回到上圖中的結果,則定時任務正常運行,我們會發現每隔30秒會輸出一個訊息。

以上是php Swoole實作毫秒定時任務的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除
上一篇:swoole怎麼退出下一篇:swoole怎麼退出