rabbitMQ + yii2 (php) 服務端守護進程

隨着互聯網的發展,php快速開發的特點,現在越來越多的團隊將php作爲服務端的編程語言,

大家都知道php是單線程,但使用PCNTL和POSIX等擴展實現多進程編程,相比多線程編程,多進程就容易的多。在使用php開發服務端時,很多時候避免不了和多進程打交道,個人才疏學淺,有疏漏。請望指正。

php創建守護進程

開始之前, 請確認已安裝擴展pcntl和posix。請使用

php -m

創建守護進程就是讓進程脫離終端,獨自在後臺運行,我們可以讓父進程

php 命令行程序實現守護進程化有很多種,在這介紹幾種。

nohup

nohup命令:如果你正在運行一個進程,而且你覺得在退出帳戶時該進程還不會結束,那麼可以使用nohup命令。該命令可以在你退出帳戶/關閉終端之後繼續運行相應的進程。

在缺省情況下該作業的所有輸出都被重定向到一個名爲nohup.out的文件中。

  1. nohup command > myout.file 2>&1 &

在上面的例子中,0 – stdin (standard input),1 – stdout (standard output),2 – stderr (standard error) ;
2>&1是將標準錯誤(2)重定向到標準輸出(&1),標準輸出(&1)再被重定向輸入到myout.file文件中。

nohup和&的區別
& : 指在後臺運行

nohup : nohup運行命令可以使命令永久的執行下去,和用戶終端沒有關係,例如我們斷開SSH連接都不會影響他的運行,注意了nohup沒有後臺運行的意思;&纔是後臺運行

&是指在後臺運行,但當用戶推出(掛起)的時候,命令自動也跟着退出

結合起來用就是
nohup COMMAND &
這樣就能使命令永久的在後臺執行

例如:

執行命令

nohup yii test-server/start > tets.txt 2>&1 &

查看進程 jobs命令只看當前終端生效的,關閉終端後,在另一個終端jobs已經無法看到後臺跑得程序了,此時利用ps(進程查看命令)

jobs -l

斷開終端,查看進程

ps -ef | grep php

  501  5410     1   0  4:08下午 ??         0:00.05 php /usr/bin/yii test-server/start
  501  5888     1   0  4:10下午 ??        13:27.09 /Applications/PhpStorm.app/Contents/MacOS/phpstorm
  501  8723     1   0  4:25下午 ??         0:00.00 php /usr/bin/yii test-server/start
  501  8727  7248   0  4:25下午 ttys002    0:00.00 grep php

這就實現了守護進程.

注意
單獨執行 php myprog.php,當按下ctrl+c時就會中斷程序執行,會kill當前進程以及子進程。
php myprog.php &,這樣執行程序雖然也是轉爲後臺運行,實際上是依賴終端的,當用戶退出終端時進程就會被殺掉

使用PHP代碼來實現

1、設置守護進程

/**
	 * 使服務守護進程化.
	 *
	 * @return void
	 */
	protected function deamon()
	{
		umask(0); // 爲後面的子進程讓出最大權限
		$pid = pcntl_fork();
		if (-1 == $pid) {
			exit("創建子進程失敗" . PHP_EOL);
		} elseif ($pid) {
			exit();
		}

		posix_setsid(); // 使當前進程成爲session leader

		$pidAgain = pcntl_fork();
		if (-1 == $pidAgain) {
			exit("再次創建子進程失敗" . PHP_EOL);
		} elseif ($pidAgain) {
			exit(posix_getpgid(posix_getppid()). PHP_EOL);
		}

	}

2、處理任務

/**
	 * 處理請求.
	 *
	 * @return void
	 */
	protected function handleTask()
	{
		while (true) {
			// process task
			sleep(2); // 模擬處理請求
			//exec('yii rpc-server/rpc-server');
			$amqp = yii::$app->params['amqp'];

			//建立一個到RabbitMQ服務器的連接
			$this->connection = new AMQPStreamConnection($amqp["host"], $amqp["port"], $amqp["user"], $amqp["password"]);
			$this->channel = $this->connection->channel();
			//接下來,我們創建一個通道
			$this->channel->queue_declare('rpc_queue',false,false,false,false);

			//回調
			$callback = function($req){

				$n = intval($req->body);

				file_put_contents(date('Ymd').'txt' , $n."\n" , FILE_APPEND | LOCK_EX );
				$msg = new AMQPMessage(

					(string) $n,
					array('correlation_id' => $req->get('correlation_id'))

				);

				$req->delivery_info['channel']->basic_publish(

					$msg,'', $req->get('reply_to')

				);

				$req->delivery_info['channel']->basic_ack(

					$req->delivery_info['delivery_tag']
				);


			};


			$this->channel->basic_qos(null,1,null);
			$this->channel->basic_consume('rpc_queue','',false,false,false,false,$callback);
			while (count($this->channel->callbacks)) {

				$this->channel->wait();
			}

			$this->channel->close();
			$this->connection->close();



		}
	}

3、合併

/**
	 * 啓動服務.
	 *
	 * @return void
	 */
	public function actionStart()
	{
		$this->deamon();     // 守護進程化
		$this->handleTask(); // 開始處理任務

	}

代碼整合


<?php
/**
 * Created by TestServer.php.
 * User: gongzhiyang
 * Date: 19/6/27
 * Time: 5:14 下午
 */

namespace console\controllers;
use yii;
use yii\console\Controller;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
class TestServerController extends Controller
{

	


	/**
	 * 啓動服務.
	 *
	 * @return void
	 */
	public function actionStart()
	{
		$this->deamon();     // 守護進程化
		$this->handleTask(); // 開始處理任務

	}


	/**
	 * 使服務守護進程化.
	 *
	 * @return void
	 */
	protected function deamon()
	{
		umask(0); // 爲後面的子進程讓出最大權限
		$pid = pcntl_fork();
		if (-1 == $pid) {
			exit("創建子進程失敗" . PHP_EOL);
		} elseif ($pid) {
			exit();
		}

		posix_setsid(); // 使當前進程成爲session leader

		$pidAgain = pcntl_fork();
		if (-1 == $pidAgain) {
			exit("再次創建子進程失敗" . PHP_EOL);
		} elseif ($pidAgain) {
			exit(posix_getpgid(posix_getppid()). PHP_EOL);
		}

	}


	/**
	 * 處理請求.
	 *
	 * @return void
	 */
	protected function handleTask()
	{
		while (true) {
			// process task
			sleep(2); // 模擬處理請求
			//exec('yii rpc-server/rpc-server');
			$amqp = yii::$app->params['amqp'];

			//建立一個到RabbitMQ服務器的連接
			$this->connection = new AMQPStreamConnection($amqp["host"], $amqp["port"], $amqp["user"], $amqp["password"]);
			$this->channel = $this->connection->channel();
			//接下來,我們創建一個通道
			$this->channel->queue_declare('rpc_queue',false,false,false,false);

			//回調
			$callback = function($req){

				$n = intval($req->body);

				file_put_contents(date('Ymd').'txt' , $n."\n" , FILE_APPEND | LOCK_EX );
				$msg = new AMQPMessage(

					(string) $n,
					array('correlation_id' => $req->get('correlation_id'))

				);

				$req->delivery_info['channel']->basic_publish(

					$msg,'', $req->get('reply_to')

				);

				$req->delivery_info['channel']->basic_ack(

					$req->delivery_info['delivery_tag']
				);


			};


			$this->channel->basic_qos(null,1,null);
			$this->channel->basic_consume('rpc_queue','',false,false,false,false,$callback);
			while (count($this->channel->callbacks)) {

				$this->channel->wait();
			}

			$this->channel->close();
			$this->connection->close();



		}
	}






}

啓動

gongzgiyangdeMacBook-Air:~ gongzhiyang$ yii  test-server/start
11410
gongzgiyangdeMacBook-Air:~ gongzhiyang$  ps -ef | grep php
  501  5410     1   0  4:08下午 ??         0:00.07 php /usr/bin/yii test-server/start
  501  5888     1   0  4:10下午 ??        18:03.16 /Applications/PhpStorm.app/Contents/MacOS/phpstorm
  501  8723     1   0  4:25下午 ??         0:00.04 php /usr/bin/yii test-server/start
  501 11065     1   0  4:37下午 ??         0:00.98 php /usr/bin/yii rpc-server/rpc-server
  501 11414     1   0  4:39下午 ??         0:00.02 php /usr/bin/yii test-server/start
  501 11504 11147   0  4:39下午 ttys003    0:00.01 grep php
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章