php使用Swoole实现毫秒级定时任务的方法

项目开发中,如果有定时任务的业务要求,我们会使用linux的crontab来解决,但是它的最小粒度是分钟级别,如果要求粒度是秒级别的,甚至毫秒级别的,crontab就无法满足,值得庆幸的是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:

namespace Helloweba\Swoole; 

use swoole_server; 

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

  public function __construct($options = [])
  {
    date_default_timezone_set('PRC');
    // 构建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('Start', [$this, 'onStart']);
    $this->serv->on('Connect', [$this, 'onConnect']);
    $this->serv->on('Receive', [$this, 'onReceive']);
    $this->serv->on('Task', [$this, 'onTask']);
    $this->serv->on('Finish', [$this, 'onFinish']);
    $this->serv->on('Close', [$this, 'onClose']);
  } 

  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('接收客户端参数:'.$fd .'-'.$data);
    $res['result'] = 'success';
    $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('Y-m-d H:i:s') . '当前内存使用率:'.$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秒计算一次内存使用率。实际应用中可以把计算好的内存按时间写入数据库等存储中,然后可以根据前端需求用来渲染成统计图表,如:

接着服务端代码 public\taskServer.php :

<?php
require dirname(__DIR__) . '/vendor/autoload.php';
use Helloweba\Swoole\Task;
$opt = [
  'daemonize' => 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.服务端返回:

如果返回上图中的结果,则定时任务正常运行,我们会发现每隔30秒会输出一条信息。

总结

到此这篇关于php使用Swoole实现毫秒级定时任务的方法的文章就介绍到这了,更多相关php Swoole实现毫秒级定时任务内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • PHP的swoole扩展安装方法详细教程

    Swoole支持PHP 5.3.10以上版本,所以安装Swoole之前请先安装PHP 5.3.10以上版本,现在来介绍Windows下PHP安装配置方法. 软件版本:php-5.3.1-Win32-VC6-x86.zip 这个不需要额外的安装.net的库,所以就用这个了.可以使用其他的. 1.PHP安装 使用绿色方式,下载Zip文件解压. 2.配置 在解压的根目录下找到php.ini-development,是用于开发环境的配置文件:还有一个php.ini-production,这个是用于生产环

  • PHP+swoole实现简单多人在线聊天群发

    由于本文的能力有限,有好多聊天逻辑的细节没有实现,只实现了群发,具体代码如下所示: php代码: $serv = new swoole_websocket_server("127.0.0.1",3999); //服务的基本设置 $serv->set(array( 'worker_num' => 2, 'reactor_num'=>8, 'task_worker_num'=>1, 'dispatch_mode' => 2, 'debug_mode'=>

  • 使用swoole扩展php websocket示例

    复制代码 代码如下: <?phpdefine('DEBUG', 'on');define("WEBPATH", str_replace("\\","/", __DIR__));require __DIR__ . '/../libs/lib_config.php'; class WebSocket extends Swoole\Network\Protocol\WebSocket{    /**     * 下线时,通知所有人     */ 

  • php安装swoole扩展的方法

    本文实例讲述了php安装swoole扩展的方法.分享给大家供大家参考.具体如下: 我本机是OS X,想要安装swoole体验一下,于是: 复制代码 代码如下: andy@AndyMacBookPro:/usr/local/webdata/github$ cd swoole-src/ andy@AndyMacBookPro:/usr/local/webdata/github/swoole-src$ git pull Already up-to-date. andy@AndyMacBookPro:/

  • PHP框架Swoole定时器Timer特性分析

    本文所述PHP框架Swoole定时器Timer特性分析具有很好的实用价值,值得PHP程序开发人员学习借鉴.具体分析如下: 首先,在各种业务型系统中,往往需要服务器在后台扫描相关数据,触发相应的统计.通知等操作. 比如对于一个项目管理系统,需要每天的特定时间内,统计每项任务的执行.到期情况.整个项目的进度等等,根据统计情况,做相应通知处理: 这样一个场景,如何编程实现? 用一般的编程方式,是无法实现自动触发与统计的.当然,简单的思路,是利于系统的cron job机制.但这种方式,对于配置及可靠性方

  • ThinkPHP5.0框架结合Swoole开发实现WebSocket在线聊天案例详解

    本文实例讲述了ThinkPHP5.0框架结合Swoole开发实现WebSocket在线聊天案例.分享给大家供大家参考,具体如下: ThinkPHP使用Swoole需要安装 think-swoole Composer包,前提系统已经安装好了Swoole PECL 拓展(相关文章:Linux下源码包安装使用Swoole扩展) 在tp5的项目根目录下执行composer命令安装think-swoole: composer require topthink/think-swoole 安装成功: 话不多说

  • docker搭建php+nginx+swoole+mysql+redis环境的方法

    操作系统:阿里云esc实例centos7.4 软件:docker-ce version 18.09.3, docker-compose version 1.23.2 一.创建带有swoole-redis-pdo_mysql-gd扩展的docker image 1.创建dockerfile文件 vim dockerfile 2.在dockerfile文件写入 From php:7.1-fpm RUN apt-get update && apt-get install -y \ libfree

  • 在PHP 7下安装Swoole与Yar,Yaf的方法教程

    本文开发坏境: CentOS 7 PHP 7.0.16 安装PECL //php版本 > 7 $ wget http://pear.php.net/go-pear.phar $ php go-pear.phar //php版本 < 7 $ yum install php-pear //否则会报错PHP Parse error: syntax error, unexpected //'new' (T_NEW) in /usr/share/pear/PEAR/Frontend.php on //l

  • PHP使用SWOOLE扩展实现定时同步 MySQL 数据

    南宁公司和几个分公司之间都使用了呼叫系统,然后现在需要做一个呼叫通话数据分析,由于分公司的呼叫服务器是在内网,通过技术手段映射出来,分公司到南宁之间的网络不稳定,所以需要把分公司的通话数据同步到南宁. 本身最简单的方法就是直接配置MySQL的主从同步就可以同步数据到南宁来了.但是销售呼叫系统那边的公司不给MySQL权限我们. 所以这个方法只能放弃了. 于是我们干脆的想,使用PHP来实现定时一个简易的PHP定时同步工具,然后PHP进程常驻后台运行,所以首先就先到了一个PHP组件:SWOOLE,经过

  • php使用Swoole实现毫秒级定时任务的方法

    项目开发中,如果有定时任务的业务要求,我们会使用linux的crontab来解决,但是它的最小粒度是分钟级别,如果要求粒度是秒级别的,甚至毫秒级别的,crontab就无法满足,值得庆幸的是swoole提供的强大的毫秒定时器. 应用场景举例 我们可能会遇到这样的场景: 场景一:每隔30秒获取一次本机内存使用率 场景二:2分钟后执行报表发送任务 场景三:每天凌晨2点钟定时请求第三方接口,如果接口有数据返回则停止任务,如果接口由于某种原因没有响应或者没有数据返回则5分钟后继续尝试请求该接口,尝试5次后

  • C#使用StopWatch获取程序毫秒级执行时间的方法

    本文实例讲述了C#使用StopWatch获取程序毫秒级执行时间的方法.分享给大家供大家参考.具体分析如下: 这个比时间通过DateTime在程序前后记录时间,然后通过TimeSpan相减的办法要精确,也更容易理解. using System.Diagnostics;//引用相关的命名空间 Stopwatch st=new Stopwatch ();//实例化类 st. Start();//开始计时 //需要统计时间的代码段 st.Stop();//终止计时 Response.Write(st.E

  • PHP获取毫秒级时间戳的方法

    本文实例讲述了PHP获取毫秒级时间戳的方法.分享给大家供大家参考.具体分析如下: PHP本身没有提供获取毫秒级时间戳的函数,java里面可以通过gettime();获取.如果是要与java写的某些程序进行高精度的毫秒级的对接通信,则需要使用PHP输出毫秒级的时间.之前我采取的方法是采用不精准的方式,也就是在PHP原生的时间函数后面加上一个三位数字构成.为获取更为精准的毫秒级时间戳可以使用下面的代码: <?php function getMillisecond() { list($t1, $t2)

  • Linux Crontab Shell脚本实现秒级定时任务的方法

    一.编写Shell脚本crontab.sh #!/bin/bash step=1 #间隔的秒数,不能大于60 for (( i = 0; i < 60; i=(i+step) )); do $(php '/home/www/php/crontab/crontab.php') sleep $step done exit 0 二.crontab -e 输入以下语句,然后:wq 保存退出 # m h dom mon dow command * * * * * /home/www/php/crontab

  • 使用bitset实现毫秒级查询(实例讲解)

    前言 因为业务要求api的一次请求响应时间在10ms以内,所以传统的数据库查询操作直接被排除(网络io和磁盘io).通过调研,最终使用了bieset,目前已经正常运行了很久 bitset介绍 看JDK中的解释简直一头雾水,用我自己的理解概括一下 1.bitset的内部实现是long数组 2.set中每一个位的默认值为false(0) 3.bitset长度按需增长 4.bitset非线程安全 bitset关键方法分析 /** * Sets the bit at the specified inde

  • 详解小程序毫秒级倒计时(适用于拼团秒杀功能)

    废话不多说直接上代码: 效果图: index.js Page({ /** * 页面的初始数据 */ data: { countdown:'' , endDate2: '2018-08-08 11:41:00' }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { var that = this; that.countTime() }, countTime() { var that = this; var date = new Date()

  • 使用Python实现毫秒级抢单功能

    目录: 引言 环境 需求分析&前期准备 淘宝购物流程回顾 秒杀的实现 代码梳理 总结 0 引言 年中购物618大狂欢开始了,各大电商又开始了大力度的折扣促销,我们的小胖又给大家谋了一波福利,淘宝APP直接搜索:小胖发福利,每天领取三次粉丝专属现金大红包. 有了现金大红包,如何做到更省钱的剁手呢?今天给大家提供一种思路,用Python实现秒杀订单,借用自动化方式完成最优解. 1 环境 操作系统:Windows Python版本:3.7.2 2 需求分析&前期准备 2.0 需求分析 我们的目标

  • Python 实现毫秒级淘宝抢购脚本的示例代码

    本篇文章主要介绍了Python 通过selenium实现毫秒级自动抢购的示例代码,通过扫码登录即可自动完成一系列操作,抢购时间精确至毫秒,可抢加购物车等待时间结算的,也可以抢聚划算的商品. 博主不提供任何服务器端程序,也不提供任何收费抢购软件.该文章仅作为学习selenium框架的一个示例代码.该思路可运用到其他任何网站,京东,天猫,淘宝均可使用,且不属于外挂或者软件之类,只属于一个自动化点击工具,如有侵犯到任何公司的合法权益,会第一时间将相关代码给予删除. 直接上源码: # !/usr/bin

  • Python获取秒级时间戳与毫秒级时间戳的示例代码

    1.获取秒级时间戳与毫秒级时间戳.微秒级时间戳 import time import datetime t = time.time() print (t) #原始时间数据 print (int(t)) #秒级时间戳 print (int(round(t * 1000))) #毫秒级时间戳 print (int(round(t * 1000000))) #微秒级时间戳 1499825149.257892    #原始时间数据 1499825149           #秒级时间戳,10位 1499

  • Java中实现分布式定时任务的方法

    定时器Scheduler在平时使用比较频繁,在springboot中,配置好@Scheduled和@EnableScheduling之后,定时器就能正常执行,实现定时任务的功能. 但是在这样的情况下:如果开发的服务需要水平部署实现负载均衡,那么定时任务就会同时在多个服务实例上运行,那么一方面,可能由于定时任务的逻辑处理需要访问公共资源从而造成并发问题:另一方面,就算没有并发问题,那么一个同样的任务多个服务实例同时执行,也会造成资源的浪费.因此需要一种机制来保证多个服务实例之间的定时任务正常.合理

随机推荐