swoole中對process的使用

 SwooleShell.php:

<?php
    App::uses('AppShell', 'Console/Command');

/**
 * Class SwooleShell
 * @property KafkaTask $Kafka
 * @property PermissionMigrateTask $PermissionMigrate
 */
class SwooleShell extends AppShell
{
	/**
	 * 設置可選參數
	 * @return ConsoleOptionParser
	 */
	public function getOptionParser()
	{
		$parser = parent::getOptionParser();
		$parser->addOption('daemon', array(
			'short' => 'd',
			'help' => '以守護進程運行',
			'boolean' => true,
			'default' => true
		));
		return $parser;
	}
	
	public function startup()
	{
		$this->out('基於Swoole擴展的shell程序');
		$this->hr();
		if (!extension_loaded('swoole')) {
			$this->err('Swoole擴展沒有安裝');
			$this->hr();
			$this->_stop();
		}
		$logPath = Configure::read('Log.path') ?: LOGS;
		if (!is_dir($logPath)) {
			mkdir($logPath);
			if (!file_exists($logPath . DS . 'swoole.log')) {
				file_put_contents($logPath . DS . 'swoole.log', '');
			}
		}
		$this->out('Swoole版本:' . SWOOLE_VERSION);
		$this->hr();
		$this->out('PHP版本:' . PHP_VERSION);
		$this->hr();
	}

	public function main()
	{
		$command = null;
		if ($this->command) {
			$command = $this->command;
		} else {
			$this->out('支持的命令');
			$this->out('[p]PublishTask          --開啓異步下發任務');
			$this->out('[q]Queue                --開啓任務隊列功能');
			$this->out('[e]Exit                 --退出');
			$this->hr();
			$input = strtoupper($this->in('選擇要執行的命令', ['p', 'q', 'e'], 'e'));
			switch ($input) {
				case 'p':
					$command = 'Publish';
					break;
				case 'Q':
					$command = 'Queue';
					break;
				default:
					$this->out('退出');
					$this->_stop();
			}
			$this->hr();
		}
		try {
			$this->out('執行命令:"' . $command . '"');
			$this->hr();
			/**
			 * @var $task AsyncTask
			 */
			$task = $this->Tasks->load($command);
			$passArgs = $this->args;
			array_shift($passArgs);
			$task->args = $passArgs;
			$task->params = $this->params;
			$task->execute();
			if ($this->command) {
				$this->out('退出');
				$this->_stop();
			} else {
				$this->out('結束');
				$this->hr();
				$this->command = null;
				$this->main();
			}
		} catch (Exception $exception) {
			$this->log($exception->__toString(), 'kafka');
			$this->err('退出,錯誤的命令:"' . $this->command . '"');
			$this->hr();
			$this->_stop();
		}
	}
}

Bash下執行命令:

首先項目用的是cakephp框架,在文件根目錄下執行上述bash模式下命令,開啓swoole腳本,同時第三個參數MPC指定到MpcTask.php文件,也是SwooleShell.php中main函數裏的$command變量。

MpcTask.php:

<?php

App::uses('ProcessTask', 'Console/Command/Task');
App::uses('Tenant', 'Model');
App::uses('InterfaceModel', 'Model');
App::uses('Controller', 'Controller');

/**
 * @link https://wiki.swoole.com/wiki/index/prid-1
 */
class MpcTask extends ProcessTask
{
	public function process()
	{

		
	}
}

ProcessTask.php:

<?php

use Swoole\Process;

App::uses('AppShell', 'Console/Command');
App::uses('Controller', 'Controller');
App::uses('ConnectionManager', 'Model');

/**
 * 繼承並實現方法以開發基於swoole的多進程應用
 * Class ProcessTask
 */
abstract class ProcessTask extends AppShell
{
	/**
	 * 程序是否以守護者進程方式運行
	 * @var bool
	 */
	public $daemon = false;
	/**
	 * 主進程pid
	 * @var int
	 */
	public $masterPid = 0;
	/**
	 * 最大進程數
	 * @var int
	 */
	public $maxPrecess = 1;
	/**
	 * 子進程信息
	 * @var array
	 */
	public $workers = [];

	/**
	 * 設置可選參數
	 * @return ConsoleOptionParser
	 */
	public function getOptionParser()
	{
		$parser = parent::getOptionParser();
		$parser->addOption('daemon', array(
			'short' => 'd',
			'help' => '以守護進程運行',
			'boolean' => true,
			'default' => true
		));
		return $parser;
	}

	/**
	 * 主程序入口
	 */
	public function execute()
	{
		try {
			if ($this->daemon) {
				Process::daemon();
			}
			$this->setProcessName(sprintf('php-ps:%s', 'master'));
			$this->masterPid = posix_getpid();
			$this->run();
			$this->processWait();
		} catch (Exception $exception) {
			$this->log($exception->__toString(), 'kafka');
			$this->err($exception->getMessage());
		}
	}


	/**
	 * 進程池入口
	 */
	public function run()
	{
		for ($i = 0; $i < $this->maxPrecess; $i++) {
			$this->CreateProcess($i);
		}
	}

	/**
	 * 創建新進程
	 * @param $index
	 * @return int
	 */
	public function CreateProcess($index)
	{
		$process = new Process(function (Process $worker) use ($index) {
			$this->setProcessName(sprintf('php-ps:%s', $index));
			$this->log($worker->pid . ':開啓進程', 'kafka');
			$this->printToScreen($worker->pid . ':開啓進程');
			try {
				//外層循環,使kafka出現問題時能重新初始化
				while (true) {
					//檢測父進程是否已退出,父進程退出則子進程退出
					$this->checkMasterPid($worker);
					$this->process();
					//一分鐘後重新初始化kafka消費者
					sleep(10);
				}
			} catch (Exception $exception) {
				$this->log($worker->pid . ':' . $exception->__toString(), 'kafka');
				$this->printToScreen($worker->pid . ':' . $exception->__toString(), 'error');
				sleep(10);
			}
			$this->log($worker->pid . ':進程結束', 'kafka');
			$this->printToScreen($worker->pid . ':進程結束');
			$worker->exit(0);
		}, false, false);
		$pid = $process->start();
		$this->workers[$index] = $pid;
		return $pid;
	}

	/**
	 * 檢測父進程是否已退出
	 * @param Process $worker
	 */
	public function checkMasterPid(Process &$worker)
	{
		if (!Swoole\Process::kill($this->masterPid, 0)) {
			$this->printToScreen($worker->pid . ':父進程已退出,子進程退出');
			$this->log($worker->pid . ':父進程已退出,子進程退出', 'kafka');
			$worker->exit(0);
		}
	}

	/**
	 * 重啓進程
	 * @param $ret
	 * @throws Exception
	 */
	public function rebootProcess($ret)
	{
		$index = array_search($ret['pid'], $this->workers);
		if ($index !== false) {
			$newPid = $this->CreateProcess($index);
			$this->log($newPid . ':重啓進程', 'kafka');
			$this->printToScreen($newPid . ':重啓進程');
		} else {
			throw new \Exception('rebootProcess Error: no pid');
		}
	}

	/**
	 * 處理殭屍進程,並重啓進程
	 * @throws Exception
	 */
	public function processWait()
	{
		while (true) {
			if (count($this->workers)) {
				$ret = Process::wait();
				if ($ret) {
					$this->rebootProcess($ret);
				}
			} else {
				break;
			}
		}
	}

	/**
	 * 設置進程名
	 * @param $name
	 */
	public function setProcessName($name)
	{
		if (function_exists('cli_set_process_title')) {
			cli_set_process_title($name);
		} else {
			swoole_set_process_name($name);
		}
	}

	/**
	 * 輸出到屏幕
	 * @param $message
	 * @param string $type
	 * @param bool $newLine
	 */
	public function printToScreen($message, $type = 'out', $newLine = true)
	{
		if (!$this->daemon) {
			if ($type == 'error') {
				$this->err($message, $newLine);
			} else {
				$this->out($message, $newLine);
			}
		}
	}

	/**
	 * 用戶的邏輯
	 * @return mixed
	 */
	abstract public function process();
}

我們只需要在子類(MpcTask.php)中去重寫父類的process方法就可實現開啓多進程,同時在進程中書寫自己的業務代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章