<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      PHP + Redis 實現定時任務觸發

      定時任務,是很常見的業務場景了。比如說游戲服的定時開服,定時發消息,定時發郵件等等。

      定時任務的觸發方式有很多,有的人借助 linux 系統的 crontab 服務,但是 crontab需要每分鐘去輪詢,所以 crontab 會有一分鐘誤差。也有的人選擇自己寫一個定時器去處理定時任務。

      這里我們介紹一種通過訂閱 redis 鍵過期的消息回調來觸發定時任務的方式。

       

      具體原理

      利用 redis 鍵事件的消息訂閱,當 redis 鍵過期時,會觸發一次回調事件,利用該次回調的觸發,帶上相應參數,便可完成一次定時任務的喚起。

      一、調整 redis 配置

      1、修改redis配置文件開啟鍵值事件的通知:vim redis.conf

      原來的:notify-keyspace-events ""

      更改后:notify-keyspace-events "Ex"

      保存redis.conf并重啟redis服務。

      2、執行redis-cli進入redis查看配置是否生效:config get notify-keyspace-events

      3、如果結果不是 xE 那么還需要再redis-cli中執行配置修改:config set notify-keyspace-events Ex

       

      二、編寫相應的腳本(以下為測試用的腳本)

      1、LibRedis.php

      <?php
      class LibRedis
      {
          private $redis;
       
          public function __construct($host = '127.0.0.1', $port = '6379',$password = '',$db = '15')
          {
              $this->redis = new Redis();
              $this->redis->connect($host, $port);    
              $this->redis->auth($password);     
              $this->redis->select($db);    
          }
       
          public function setex($key, $time, $val)
          {
              return $this->redis->setex($key, $time, $val);
          }public function psubscribe($patterns = array(), $callback)
          {
              $this->redis->psubscribe($patterns, $callback);
          }
       
          public function setOption()
          {
              $this->redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
          }
       
      }

       

      2、添加定時任務的腳本:set_timer_task.php

      <?php
      require_once 'LibRedis.php';
      $redis = new \LibRedis('127.0.0.1','6379','','0');
      $time = 100; //設置redis鍵100s后過期,即定時100s后觸發定時任務。
      $ctl = 'timerTaskManage'; //定時器調用的控制器
      $fun = 'callbackFun'; //定時器調用的控制器中的方法
      $param = 'id=1';  //透傳的參數(需要透傳的參數盡量少,并且簡單,因為這些數據要拼接成一個redis鍵)
      $key = "timerTask:{$ctl}:{$fun}:{$param}"; //最終在監聽腳本中調用: $ctl->$fun($param);
      $re = $redis->setex($key,$time,1); 
      var_dump($re);

       

      3、常駐進程腳本,監聽redis鍵值過期事件,從而觸發定時器:get_timer_task.php

      <?php
      ini_set('default_socket_timeout', -1);  //不超時
      require_once 'LibRedis.php';
      $redis_db = '0';
      $redis = new \LibRedis('127.0.0.1','6379','',$redis_db);
      // 解決Redis客戶端訂閱時候超時情況
      $redis->setOption();
      //當key過期的時候就看到通知,訂閱的key __keyevent@<db>__:expired 這個格式是固定的,db代表的是數據庫的編號,由于訂閱開啟之后這個庫的所有key過期時間都會被推送過來,所以最好單獨使用一個數據庫來進行隔離
      $redis->psubscribe(array('__keyevent@'.$redis_db.'__:expired'), 'keyCallback');
      
      /*回調函數,這里寫處理邏輯,格式固定
       *@param $redis 固定格式參數,一般不會用到,但必須帶上。
       *@param $pattern 固定格式參數,一般不會用到,但必須帶上。
       *@param $channel 固定格式參數,一般不會用到,但必須帶上。
       *@param $msg 真正業務中用到的參數,也就是在設置redis時的鍵。set_timer_task.php 腳本中的$key變量
      */
      function keyCallback($redis, $pattern, $channel, $msg)
      {
          try{
              //可能有其他非定時器的鍵值對過期了,它們也會回調過來,此處將這部分鍵的事件過濾掉
              if($arr[0] != 'timerTask'){
                  return true;
              }
              //多記錄日志,方便后面查驗結果和問題
              file_put_contents('/data/logs/timer_task_'.date('Ymd').'.log', 'N1:'.date('Y-m-d H:i:s:').$msg."\n", FILE_APPEND);
              $controller  = $arr[1]; //控制器:timerTaskManage
              $function = $arr[2]; //方法:callbackFun
              $param = $arr[3]; //透傳參數id=1
              $ctl = new $controller();
              $re = $ctl->$function($param); //該類的方法執行具體任務邏輯,
              file_put_contents('/data/logs/timer_task' . date('Ymd') . '.log', date('Y-m-d H:i:s:') . var_export(array($msg, $re), true) . "\n", FILE_APPEND);
              if(!$re){
                  //TODO:任務執行出問題,此處為報警邏輯
              }
              //不論結果執行如何,都有個返回,如果執行出問題了,就在錯誤處理邏輯中處理。
              return true;
          }catch(Exception $e){
              //TODO:報警邏輯,錯誤處理邏輯
              return true;
          }
      }
       

      這里有一點比較重要的問題需要注意,由于腳本 get_timer_task.php 是常駐進程的腳本,那么在該腳本中去實例話一個類,若是要注意做好該類何相應方法的處理,否則一旦出現什么問題都會在該進程中延續下去直到進程掛掉。

      比如如果在任務方法中有數據庫連接,那么這個連接超時的時候,下一次任務執行時會報錯。并且報錯會一直存在。

      最好是做一種優化,使用 CGI 的方式來調用定時任務的邏輯。這樣,將任務的執行邏輯放到CGI的接口中,每一次任務的執行都跟一次CGI請求一樣,不能論該次任務執行遇到什么錯誤或異常,都不會影響常駐腳本,不會有數據庫連接超時的問題產生,也不會影響接下來其他任務的執行。

      比如以下優化方式:

      <?php
      ini_set('default_socket_timeout', -1);  //不超時
      require_once 'LibRedis.php';
      $redis_db = '0';
      $redis = new \LibRedis('127.0.0.1','6379','',$redis_db);
      // 解決Redis客戶端訂閱時候超時情況
      $redis->setOption();
      //當key過期的時候就看到通知,訂閱的key __keyevent@<db>__:expired 這個格式是固定的,db代表的是數據庫的編號,由于訂閱開啟之后這個庫的所有key過期時間都會被推送過來,所以最好單獨使用一個數據庫來進行隔離
      $redis->psubscribe(array('__keyevent@'.$redis_db.'__:expired'), 'keyCallback');
      
      /*回調函數,這里寫處理邏輯,格式固定
       *@param $redis 固定格式參數,一般不會用到,但必須帶上。
       *@param $pattern 固定格式參數,一般不會用到,但必須帶上。
       *@param $channel 固定格式參數,一般不會用到,但必須帶上。
       *@param $msg 真正業務中用到的參數,也就是在設置redis時的鍵。set_timer_task.php 腳本中的$key變量
      */
      function keyCallback($redis, $pattern, $channel, $msg)
      {
          try{
              //可能有其他非定時器的鍵值對過期了,它們也會回調過來,此處將這部分鍵的事件過濾掉
              if($arr[0] != 'timerTask'){
                  return true;
              }
              //多記錄日志,方便后面查驗結果和問題
              file_put_contents('/data/logs/timer_task_'.date('Ymd').'.log', 'N1:'.date('Y-m-d H:i:s:').$msg."\n", FILE_APPEND);
              $data['controller']  = $arr[1];
              $data['function']  = $arr[2];
              $data['param'] = $arr[3];
              $api_url = "http://www.test.com/apiTimerTask.php"; //定時任務執行接口
              $re = make_request($api_url,$data);
              $str = json_encode($re);
              file_put_contents('/data/logs/timer_task' . date('Ymd') . '.log', date('Y-m-d H:i:s:') . var_export(array($msg, $re), true) . "\n", FILE_APPEND);
              if(!$re){
                  //TODO:任務執行出問題,此處為報警邏輯
              }
              //不論結果執行如何,都有個返回,如果執行出問題了,就在錯誤處理邏輯中處理。
              return true;
          }catch(Exception $e){
              //TODO:報警邏輯,錯誤處理邏輯
              return true;
          }
      }
       
      function make_request($url,$data)
      {
           //TODO:執行curl請求
      }
       

       

      三、總結

      在業務的使用過程中,還需要注意以下問題。

      用來做定時器的redis最好是單獨的一臺比較低配的redis服務,該redis服務出來用來做定時器盡量不要再做其他用處,或者不要放其他數據,更不要有太多定時過期的鍵。

      由于redis過期策略的問題,如果該redis服務中存在太多需要過期的鍵值對,那么定時器的鍵可能并不能準時過期,導致事件不能準時觸發。具體細節可以去詳細了解下 redis鍵的過期策略。

      所以,單獨搞一臺小配置的redis,僅用來做定時服務和少量其他服務,能夠提高這種方式的準確性和可靠性。

       

      posted @ 2021-02-25 12:40  喜歡哲學的猴子  閱讀(1498)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 国产高颜值极品嫩模视频| 大香伊蕉在人线国产最新2005| av亚洲一区二区在线| 人妻系列无码专区69影院| 麻豆av一区二区天美传媒| 欧美精品18videosex性欧美| 国产午夜福利精品视频| 国产精品一品二区三区日韩| 99在线精品视频观看免费| 亚洲国产精品久久久久秋霞 | 99久久久国产精品免费无卡顿| 美女扒开尿口让男人桶| 九九热免费在线视频观看| 午夜福利片1000无码免费| 起碰免费公开97在线视频| 国产亚洲欧洲AⅤ综合一区| 乱人伦中文视频在线| 亚洲国产欧美一区二区好看电影| 久久99久久99精品免观看| 日韩av一区二区三区在线| 67194熟妇在线观看线路| 国产成人黄色自拍小视频| 在线观看AV永久免费| 亚洲精品国产一二三区| 狠狠久久五月综合色和啪| 国产色a在线观看| 国产成人综合在线观看不卡| 国产精品一区二区在线欢| 国产欧美国日产高清| 久久精品无码一区二区三区 | 国产精品老熟女乱一区二区 | 亚洲VA中文字幕无码久久| 内丘县| 国产太嫩了在线观看| 亚洲AV无码久久精品成人| 久久精品波多野结衣| 国日韩精品一区二区三区| 51妺嘿嘿午夜福利| 忘忧草在线社区www中国中文| 免费现黄频在线观看国产| 少妇高潮水多太爽了动态图|