Swoole_process method to achieve process pool

There are two ways of communication between Swoole processes, one is the message queue and the other is the pipe. The research on swoole_process is especially important in swoole.

 

Preliminary knowledge

IO multiplexing

The io multiplexing in swoole is expressed as the underlying epoll process model, and it is expressed as the epoll function in C language.

  • The epoll model will continue to monitor the socket descriptor fd under its own name

  • When the event monitored by the socket is triggered, the epoll function will respond and return a collection of all sockets listening at that time

  • The essence of epoll is to block IO, its advantage is that it can deal with a large number of socket connections by colleagues

Event loop Event loop

swoole implements a Reactor thread model encapsulation for epoll, and sets the listen callback function for read event and write event. (See swoole_event_add for details)

  • Event loop is a Reactor thread, which runs an epoll instance.

  • Add an event of the socket descriptor to epoll monitoring through swoole_event_add, and the callback function will be executed when the event occurs

  • It cannot be used in the fpm environment, because fpm may shut down the process at the end of the task.

     

     

 

swoole_process

  • Process management module based on C language package, convenient for php to call

  • Built-in pipeline and message queue interface to facilitate communication between processes

We found in the php-fpm.conf configuration file that there are two process pool management settings in php-fpm.

  • The static mode is to initialize a fixed number of processes, and when a request comes, select a process to process.

  • The dynamic mode specifies the minimum and maximum number of processes. When the number of requests is too large and the number of processes does not exceed the maximum limit, a new thread is added to process the request

 

The next step is to use swoole code to implement. Here is just to understand the use of swoole_process, inter-process communication, timer, etc. It is more convenient to use the packaged swoole_server to implement the task task queue pool.

If there is a task queue for regular delivery:

<? php

/ **
 * Dynamic process pool, similar to fpm
 * Dynamic new process
 * There are initial process number, minimum process number, new process is created when the process is not enough to process, and the maximum process number is not exceeded
 * /

// A process regularly delivers tasks

/ * *
 * 1. tick
 * 2. process and its pipeline communication
 * 3. event loop event loop
 * /
class  processPool
{
  private $ pool;

  / **
   *  @var swoole_process [] record all worker process objects
   * /
  private $ workers = [];

  / **
   *  @var array records worker working status
   * /
  private $ used_workers = [];

  / **
   *  @var int minimum number of processes
   * /
  private $ min_woker_num =  5;

  / **
   *  @var int initial process number
   * /
  private $ start_worker_num =  10;

  / **
   *  @var int maximum number of processes
   * /
  private $ max_woker_num =  20;

  / **
   * number of seconds of idle process destruction
   *  @var int
   * /
  private $ idle_seconds =  5;

  / **
   *  @var int current process number
   * /
  private $ curr_num;

  / **
   * idle process timestamp
   *  @var array
   * /
  private $ active_time = [];

  public  function  __construct ()
  {
    $ this-> pool =  new swoole_process ( function  () {
      // Cyclic establishment of worker process
      for ($ i = 0; $ i <  $ this-> start_worker_num; $ i ++) {
        $ this-> createWorker ();
      }
      echo  'initialization process number:'.  $ This-> curr_num. PHP_EOL;
      // timed to idle workers every second Delivery task
      swoole_timer_tick ( 1000,  function  ($ timer_id) {
        static $ count =  0;
        $ count ++;
        $ need_create =  true;
        foreach ( $ this-> used_workers as $ pid => $ used) {
          if ($ used ==  0) {
            $ need_create =  false;
            $ this-> workers [$ pid]-> write ($ count.  'Job');
            // Mark in use
            $ this-> used_workers [$ pid] =  1;
            $ this-> active_time [$ pid] = time ();
            break;
          }
        }
        foreach ( $ this-> used_workers as $ pid => $ used)
          // If all worker queues are not idle, create a new worker to handle
          if ($ need_create &&  $ this-> curr_num <  $ this- > max_woker_num) {
            $ new_pid =  $ this-> createWorker ();
            $ this-> workers [$ new_pid]-> write ($ count.  'job');
            $ this-> used_workers [$ new_pid] =  1;
            $ this -> active_time [$ new_pid] = time ();
          }

        // Destroy the process after being idle for a period of time
        foreach ( $ this-> active_time as $ pid => $ timestamp) {
          if ((time ()-$ timestamp)>  $ this-> idle_seconds &&  $ this-> curr_num>  $ this->min_woker_num) {
            // Destroy the process
            if (isset($this->workers[$pid]) && $this->workers[$pid] instanceof swoole_process) {
              $this->workers[$pid]->write('exit');
              unset($this->workers[$pid]);
              $this->curr_num = count($this->workers);
              unset($this->used_workers[$pid]);
              unset($this->active_time[$pid]);
              echo "{$pid} destroyed\n";
              break;
            }
          }
        }

        echo "任务{$count}/{$this->curr_num}\n";

        if ($count == 20) {
          foreach ($this->workers as $pid => $worker) {
            $worker->write('exit');// Turn off the timer
          }
          
          swoole_timer_clear($timer_id);
          // 退出进程池
          $this->pool->exit(0);
          exit();
        }
      });

    });

    $master_pid = $this->pool->start();
    echo "Master $master_pid start\n";

    while ($ret = swoole_process::wait()) {
      $pid = $ret['pid'];
      echo "process {$pid} existed\n";
    }
  }

  /**
   * 创建一个新进程
   * @return int 新进程的pid
   */
  public function createWorker()
  {
    $worker_process = new swoole_process(function (swoole_process $worker) {
      // Bind event
      swoole_event_add ($ worker-> pipe,  function  ($ pipe)  use  ($ worker) {
        $ data = trim ($ worker-> read ());
        if ($ data ==  'exit ') {
          $ worker-> exit ( 0);
          exit ();
        }
        echo  "{$ worker-> pid} is processing {$ data} \ n";
        sleep ( 5);
        // return the result, indicating idle
        $ worker -> write ( "complete");
      });
    });

    $ worker_pid = $ worker_process-> start ();

    // bind event
    swoole_event_add ($ worker_process-> pipe,  function  ($ pipe)  use  ( $ worker_process) {
      $data = trim($worker_process->read());
      if ($data == 'complete') {
        // 标记为空闲
//        echo "{$worker_process->pid} 空闲了\n";
        $this->used_workers[$worker_process->pid] = 0;
      }
    });

    // 保存process对象
    $this->workers[$worker_pid] = $worker_process;
    // 标记为空闲
    $this->used_workers[$worker_pid] = 0;
    $this->active_time[$worker_pid] = time();
    $this->curr_num = count($this->workers);
    return $worker_pid;
  }

}

new processPool();

Guess you like

Origin www.cnblogs.com/myJuly/p/12686165.html