ホームページ  >  記事  >  PHPフレームワーク  >  php Swooleはミリ秒レベルのスケジュールされたタスクを実装します

php Swooleはミリ秒レベルのスケジュールされたタスクを実装します

藏色散人
藏色散人転載
2020-01-25 19:14:164133ブラウズ

php Swooleはミリ秒レベルのスケジュールされたタスクを実装します

プロジェクト開発中、スケジュールされたタスクのビジネス要件がある場合、Linux の crontab を使用して解決しますが、その最小粒度は分レベルです。必要な粒度が 2 番目のレベルの場合、ミリ秒レベルでも crontab では満足できませんが、幸いなことに swoole は強力なミリ秒タイマーを提供します。

推奨学習: swoole チュートリアル

アプリケーション シナリオの例

このようなシナリオに遭遇する可能性があります:

● シナリオ 1: 30 秒ごとにローカル メモリ使用量を取得する

#● シナリオ 2: 2 分後にレポート送信タスクを実行する

#● シナリオ 3: 毎日 2 時、朝 サードパーティ インターフェイスを定期的に要求します。インターフェイスがデータを返すと、タスクは停止します。インターフェイスが何らかの理由で応答しない場合、またはデータが返されない場合は、5 分後にインターフェイスの要求を続けます。それでも問題が解決しない場合は、インターフェイスを要求してください。 5 回試しても失敗する場合は、タスクを停止してください

上記の 3 つのシナリオは、スケジュールされたタスクのカテゴリにまとめることができます。

Swoole ミリ秒タイマー

Swoole は非同期ミリ秒タイマー関数を提供します:

swoole_timer_tick(int $msec, callable $callback)

: インターバル クロック タイマーを設定し、JavaScript の setInterval() と同様に、$msec ミリ秒ごとに $callback を実行します。

swoole_timer_after(int $after_time_ms,mixed $callback_function)

: JavaScript ## と同様に、指定された時間 $after_time_ms 後に $callback_function を実行します。 #setTimeout()swoole_timer_clear(int $timer_id

): JavaScript の

clearInterval() と同様に、指定された ID を持つタイマーを削除します。 ソリューション

シナリオ 1 では、システム検出統計でよく使用されます。リアルタイム要件は比較的高いですが、頻度は適切に制御できます。主にバックグラウンドサーバーのパフォーマンスに使用され、監視により視覚的なグラフを生成できます。メモリ使用量は 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";
 });

シナリオ 2 では、xx 時間後の特定のタスクを直接定義する場合、crontab の方が難しいように見えますが、swoole の swoole_timer_after を使用すると次のことが実現できます。 ##
 swoole_timer_after(120000, function() use ($str) { //2分钟后执行
     $this->sendReport(); //发送报表
     echo "send report, $str\n";
 });

シナリオ 3 では、リクエストを試行するために使用されます。リクエストが失敗した後も続行し、成功した場合はリクエストを停止します。 crontabを使えば解決することもできますが、かなりアホで、例えば5分ごとにリクエストを設定しておくと、成功しても失敗しても実行されてしまいます。スウールタイマーを使用する方がはるかに賢明です。

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;
    }
}

シナリオ 1 を例として、onTask でスケジュールされたタスクを有効にし、30 秒ごとのメモリ使用量を計算します。実際のアプリケーションでは、計算されたメモリを時間に基づいてデータベースまたはその他のストレージに書き込み、次のようなフロントエンド要件に従って統計グラフをレンダリングするために使用できます。

# 次に、サーバー側のコード 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();

php Swooleはミリ秒レベルのスケジュールされたタスクを実装しますクライアント コード 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. クライアントinput:

別のコマンド ライン ウィンドウを開き、<pre class="brush:php;toolbar:false">[root@localhost public]# php taskClient.php</pre> を実行します。メッセージを入力してください: hello

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

3. サーバーは次を返します:

上の図の結果が返された場合、スケジュールされたタスクは正常に実行されており、30 秒ごとにメッセージが出力されることがわかります。

以上がphp Swooleはミリ秒レベルのスケジュールされたタスクを実装しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcnblogs.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。