1. 程式人生 > >從0到1優雅的實現PHP多程序管理

從0到1優雅的實現PHP多程序管理

業務場景

在我們實際的業務場景中(PHP技術棧),我們可能需要定時或者近乎實時的執行一些業務邏輯,簡單的我們可以使用unix系統自帶的crontab實現定時任務,但是對於一些實時性要求比較高的業務就不適用了,所以我們就需要一個常駐記憶體的任務管理工具,為了保證實時性,一方面我們讓它一直執行任務(適當的睡眠,保證cpu不被100%佔用),另一方面我們實現多程序保證併發的執行任務。

目的

綜上所述,我的目標就是:實現基於php-cli模式實現的master-worker多程序管理工具。其次,“我有這樣一個目標,我是怎樣一步步去分析、規劃和實現的”,這是本文的宗旨。

備註:下文中,父程序統稱為master,子程序統稱為worker。

分析

我們把這一個大目標拆成多個小目標去逐個實現,如下:

  • 多程序

    • 目的:一個master fork多個worker
    • 現象:所有worker的ppid父程序ID為當前master的pid
  • master控制worker

    • 目的:master通知worker,worker接收來自master的訊息
  • master接收訊號

    • 目的:master接收並自定義處理來自終端的訊號

多程序

PHP fork程序的方法 pcntl_fork, 這個大家應該有所瞭解,如果不知道的簡單google/bing一下應該很容易找到這個函式。接著FTM, 我們看看pcntl_fork這個函式的使用方式大致如下:

$pid = pcntl_fork(); // pcntl_fork 的返回值是一個int值
// 如果$pid=-1 fork程序失敗 // 如果$pid=0 當前的上下文環境為worker // 如果$pid>0 當前的上下文環境為master,這個pid就是fork的worker的pid

接著看程式碼:

$pid = pcntl_fork();    
switch ($pid) {
  case -1:
    // fatal error 致命錯誤 所有程序crash掉
    break;

  case 0:
    // worker context
    exit
; // 這裡exit掉,避免worker繼續執行下面的程式碼而造成一些問題 break; default: // master context pcntl_wait($status); // pcntl_wait會阻塞,例如直到一個子程序exit // 或者 pcntl_waitpid($pid, $status, WNOHANG); // WNOHANG:即使沒有子程序exit,也會立即返回 break; }

我們看到master有呼叫pcntl_wait或者pcntl_waitpid函式,為什麼呢?首先我們在這裡得提到兩個概念,如下:

  • 孤兒程序:父程序掛了,子程序被pid=1的init程序接管(wait/waitpid),直到子程序自身生命週期結束被系統回收資源和父程序採取相關的回收操作
  • 殭屍程序:子程序exit退出,父程序沒有通過wait/waitpid獲取子程序狀態,子程序佔用的程序號等描述資源符還存在,產生危害:例如程序號是有限的,無法釋放程序號導致未來可能無程序號可用

所以,pcntl_wait或者pcntl_waitpid的目的就是防止worker成為殭屍程序(zombie process)。

除此之外我們還需要把我們的master掛起和worker掛起,我使用的的是while迴圈,然後usleep(200000)防止CPU被100%佔用。

最後我們通過下圖(1-1)來簡單的總結和描述這個多程序實現的過程:


master控制worker

上面實現了多程序和多程序的常駐記憶體,那master如何去管理worker呢?答案:多程序通訊。話不多說google/bing一下,以下我列舉幾種方式:

  • 命名管道: 感興趣
  • 佇列: 個人感覺和業務中使用redis做訊息佇列思路應該一致
  • 共享記憶體: 違背“不要通過共享記憶體來通訊,要通過通訊來實現共享”原則
  • 訊號: 承載資訊量少
  • 套接字: 不熟悉

所以我選擇了“命名管道”的方式。我設計的通訊流程大致如下:

  • step 1: 建立worker管道
  • step 2: master寫訊息到worker管道
  • step 3: worker讀訊息從worker管道

接著還是逐個擊破,當然話不多說還是google/bing一下。posix_mkfifo建立命名管道、fopen開啟檔案(管道以檔案形式存在)、fread讀取管道、fclose關閉管道就呼嘯而出,哈哈,這樣我們就能很容易的實現我們上面的思路的了。接著說說我在這裡遇到的問題:fopen阻塞了,導致業務程式碼無法迴圈執行,一想不對啊,平常fopen普通檔案不存在阻塞行為,這時候二話不說FTM搜fopen,crtl+f頁面搜“block”,重點來了:

fopen() will block if the file to be opened is a fifo. This is true whether it's opened in "r" or "w" mode. (See man 7 fifo: this is the correct, default behaviour; although Linux supports non-blocking fopen() of a fifo, PHP doesn't).

翻譯下,大概意思就是“當使用fopen的r或者w模式開啟一個fifo的檔案,就會一直阻塞;儘管linux支援非阻塞的開啟fifo,但是php不支援。”,得不到解決方案,不支援,感覺要放棄,一想這種場景應該不會不支援吧,再去看看posix_mkfifo,結果喜出望外:

<?php
  $fh=fopen($fifo, "r+"); // ensures at least one writer (us) so will be non-blocking
  stream_set_blocking($fh, false); // prevent fread / fwrite blocking
?>

The "r+" allows fopen to return immediately regardless of external  writer channel.

結論使用“r+”,同時我們又知道了使用stream_set_blocking防止緊接著的fread阻塞。接著我們用下圖(1-2)來簡單的總結和描述這個master-worker通訊的方式。


master接收訊號

最後我們需要解決的問題就是master怎麼接受來自client的訊號,google/bing結論:

master接收訊號 -> pcntl_signal註冊對應訊號的handler方法 -> pcntl_signal_dispatch() 派發訊號到handler

如下圖(1-3)所示,


其他

接著我們只要實現不同訊號下master&worker的策略,例如worker的重啟等。這裡需要注意的就是,當master接受到重啟的訊號後,worker不要立即exit,而是等到worker的業務邏輯執行完成了之後exit。具體的方式就是:

master接收reload訊號 -> master把reload訊號寫worker管道 -> worker讀取到reload訊號 -> worker新增重啟標誌位 -> worker執行完業務邏輯後且檢測到重啟的標誌位後exit

建模

上面梳理完我們的實現方式後,接著我們就開始碼程式碼了。碼程式碼之前進行簡單的建模,如下:

程序管理類Manager

- attributes
  + master: master物件
  + workers: worker程序物件池
  + waitSignalProcessPool: 等待訊號的worker池
  + startNum: 啟動程序數量
  + userPasswd: linux使用者密碼
  + pipeDir: 管道存放路徑
  + signalSupport: 支援的訊號
  + hangupLoopMicrotime: 掛起間隔睡眠時間
- method
  + welcome: 歡迎於
  + configure: 初始化配置
  + fork: forkworker方法
  + execFork: 執行forkworker方法
  + defineSigHandler: 定義訊號handler
  + registerSigHandler: 註冊訊號handler
  + hangup: 掛起主程序

程序抽象類Process

- attributes
  + type: 程序型別 master/worker
  + pid: 程序ID
  + pipeName: 管道名稱 
  + pipeMode: 管道模式
  + pipeDir: 管道存放路徑
  + pipeNamePrefix: 管道名稱字首
  + pipePath: 管道生成路徑
  + readPipeType: 讀取管道資料的位元組數
  + workerExitFlag: 程序退出標誌位
  + signal: 當前接受到的訊號
  + hangupLoopMicrotime: 掛起間隔睡眠時間
- method
  + hangup: 掛起程序(抽象方法)
  + pipeMake: 建立管道
  + pipeWrite: 寫管道
  + pipeRead: 讀管道
  + clearPipe: 清理管道檔案
  + stop: 程序exit

master實體類MasterProcess

- attributes
  + 
- method
  + hangup: 掛起程序

worker實體類MasterProcess

- attributes
  + 
- method
  + dispatchSig: 定義worker訊號處理方式

最後我們需要做的就是優雅的填充我們的程式碼了。

最後

個人知識還有很多不足,如果有寫的不對的地方,希望大家及時指正。



相關推薦

0到1優雅實現PHP程序管理

業務場景 在我們實際的業務場景中(PHP技術棧),我們可能需要定時或者近乎實時的執行一些業務邏輯,簡單的我們可以使用unix系統自帶的crontab實現定時任務,但是對於一些實時性要求比較高的業務就不適用了,所以我們就需要一個常駐記憶體的任務管理工具,為了保證實時

php程序實現

來源:http://blog.csdn.net/e421083458/article/details/22186475 PHP多程序實現 PHP有一組程序控制函式(編譯時需要–enable-pcntl與posix擴充套件),使得php能在nginx系統中實現跟c一樣的建立

shell模擬php程序redis獲取資料(個庫)

背景:現在的資料已經寫到了redis佇列裡面,完成了入棧的操作,後期打算從redis獲取資料,完成出棧的操作,出棧後然後做一系列的邏輯處理       環境: VMware虛擬機器  記憶體:1G   硬碟:60G  php環境:PHP Version 5.6.31  ph

併發伺服器的實現程序執行緒...)

一、多程序實現併發伺服器 程式碼如下:multiprocess_server.c /* ============================================================================ Name : TCPServ

PHP程序初探 --- 利用程序開發點兒東西吧

[原文地址:https://blog.ti-node.com/blog...] 乾巴巴地叨逼叨了這麼久,時候表演真正的技術了! 做個高階點兒的玩意吧,加入我們要做一個任務系統,這個系統可以在後臺幫我們完成一大波(注意是一大波)資料的處理,那麼我們自然想到,多開幾個程序分開處理這些資料,同時我們不能執行了p

php-fpm的pool、php-fpm慢執行日誌、open_basedir、php-fpm程序管理

11月30日任務 12.21 php-fpm的pool 12.22 php-fpm慢執行日誌 12.23 open_basedir 12.24 php-fpm程序管理   12.21 php-fpm的pool vim /usr/l

Linux-php-fpm程序管理

• pm = dynamic //動態程序管理,也可以是static,如果是動態的,後面的才會生效,否則不會生效的• pm.max_children = 50 //最大子程序數,ps aux可以檢視• pm.start_servers = 20 //啟動服務時會啟動的程序數• pm.min_spare_ser

Linux下搭建PHP開發環境,Php-Fpm程序管理

目前PHP專案開發幾種比較流行的架構搭建中,LNMP在效能方面是最好的,正因為如此,使得LNMP架構逐漸流行起來,今天,前面提到了Nginx部署,由於專案實際環境的需要,今天就在說一下怎麼部署PHP。 環境 CentOS 6.3 PHP 5.6 安裝步驟 1. 下載並安裝P

websocket基於php 記一次結合PHP程序和socket.io解決問題的經歷

記一次結合PHP多程序和socket.io解決問題的經歷     公司是做棋牌遊戲的。前段時間接到一個後臺人工鑑定並處理通牌作弊玩家的需求,其中需要根據幾個玩家的遊戲ID查詢並計算他們在某段時間內彼此之間玩牌輸贏次數和輸贏總額。   牌局資料是儲存在日誌中心的,他們把牌

PHP程序引發的msyql連線數問題

PHP多程序引發的msyql連線數問題 業務中有一塊採用了PHP的pcntl_fork多程序,希望能提高效率,但是在執行的時候資料庫報錯 PDO::prepare(): Premature end of data (mysqlnd_wireprotocol

php 程序下mysql連線 gone away

php 在命令列模式下啟動多程序如果父程序有sql查詢,可能會導致子程序裡面的sql查詢 報錯  General error: 2006 MySQL server has gone away 當fork的子程序都共用相同的mysql連線的時候,會出現該錯誤,每個子程序單獨一

Docker容器內程序管理(二)——monit

注:本文基於CentOS 6.6 背景 上一篇我們介紹了使用supervisor來管理容器內的多程序,但是我們注意到supervisor只能管理到前臺程序,對於一般的服務,沒有終端的程序supervisor無法管理。這就需要請出我們的monit了,相對於supe

php程序單例模式下的 MySQL及Redis連線錯誤修復

問題描述: 前幾天寫了個php常駐指令碼,主要邏輯如下 //跑完資料後休息60秒 $sleepTime = 60; $maxWorker = 10; while (true) { $htmlModel = new DetailHtmlMode

PHP程序抓取百度搜索結果

<?php /** * 多程序抓取百度結果頁自然結果,包括標題、摘要、圖片、連結、來源 * @since 2016-04-15 */ class NaturalResultSpider { private $_strQuery = null; pub

java封裝FFmpeg命令,支援原生ffmpeg全部命令,實現FFmpeg程序處理與執行緒輸出控制(開啟、關閉、查詢),rtsp/rtmp推流、拉流

前言: 之前已經對FFmpeg命令進行了封裝http://blog.csdn.net/eguid_1/article/details/51787646,但是當時沒有考慮到擴充套件性,所以總體設計不是太好,需要改動的地方也比較多,也不支援原生ffmpeg命令,所以本次版本推翻

OSX中php程序安裝pcntl

php實現多程序,原始檔: <?php $pid = pcntl_fork(); if($pid == -1){ die('could not fork'); }else{ if ($pid) { var_dump('parent:'.$pid);

LNMP架構二十七(php-fpm程序管理)【完】

二十七、php-fpm程序管理 1、php-fpm的程序管理有兩種模式 php-fpm的程序數也是可以根據設定分為動態和靜態的。 靜態模式:直接開啟指定數量的php-fpm程序,不再增加或者減少;動態模式:開始的時候開啟一定數量的php-fpm程序,當請求量變大的時候,動態的增加php-

php程序使用場景

pcntl介紹 擴充套件介紹 Note: 1. 此擴充套件在 Windows 平臺上不可用。 2. 程序控制不能被應用在Web伺服器環境,當其被用於Web服務環境時可能會帶來意外的結果。因此,不能再PHP Web開發中使用多程序。

PHP程序處理並行處理任務例項

本文目的 本文通過例子講解linux環境下,使用php進行併發任務處理,以及如何通過pipe用於程序間的資料同步。寫得比較簡單,作為備忘錄。 PHP多程序 通過pcntl_XXX系列函式使用多程序功能。注意:pcntl_XXX只能執行在php CLI(命令列)環境下

快速實現Python程序logging日誌按天切割

        前幾天填了一個前人留下的“日誌”坑,程式日誌只有程式重啟才可以生成一個新日誌檔案,這樣就會導致程式長時間執行之後,會生成一個巨大的日誌檔案,不方便後期問題查詢或磁碟清理,所以日誌按天切割勢在必行。python 原生logging日誌模組可以滿足大部分需求,但是