hollodotme/fast-cgi-proxy 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

hollodotme/fast-cgi-proxy

Composer 安装命令:

composer require hollodotme/fast-cgi-proxy

包简介

A proxy for distributing (a)sync requests to multiple php-fpm sockets/pools.

README 文档

README

CircleCI Latest Stable Version Total Downloads codecov

FastCGI Proxy

Description

A proxy for distributing (a)sync requests to multiple fastCGI servers.

Installation

composer require hollodotme/fast-cgi-proxy

Usage

Request distribution

The proxy can distribute requests to multiple fastCGI servers in the following ways:

  1. Randomly
  2. Via round robin

Random distribution

To set up random distribution use the following example code:

<?php declare(strict_types=1);

namespace YourVendor\YourProject;

use hollodotme\FastCGI\Proxy;
use hollodotme\FastCGI\Collections\Random;
use hollodotme\FastCGI\SocketConnections\NetworkSocket;
use hollodotme\FastCGI\SocketConnections\UnixDomainSocket;

$random = Random::fromConnections(
    new NetworkSocket( '127.0.0.1', 9000 ),
    new NetworkSocket( '10.100.10.42', 9000 ),
    new UnixDomainSocket( '/var/run/php7.3-fpm.sock' )	
);

$proxy = new Proxy( $random );

When sending requests now the proxy will randomly choose one of the fastCGI servers to process the request.

Round robin distribution

To set up round robin distribution use the following example code:

<?php declare(strict_types=1);

namespace YourVendor\YourProject;

use hollodotme\FastCGI\Proxy;
use hollodotme\FastCGI\Collections\RoundRobin;
use hollodotme\FastCGI\SocketConnections\NetworkSocket;
use hollodotme\FastCGI\SocketConnections\UnixDomainSocket;

$roundRobin = RoundRobin::fromConnections(
    new NetworkSocket( '127.0.0.1', 9000 ),
    new NetworkSocket( '10.100.10.42', 9000 ),
    new UnixDomainSocket( '/var/run/php7.3-fpm.sock' )	
);

$proxy = new Proxy( $roundRobin );

The proxy will send your requests to the next fastCGI server in the same order they were added to the RoundRobin instance. In this example it will send to:

  1. 127.0.0.1:9001
  2. 10.100.10.42:9001,
  3. /var/run/php7.1-fpm.sock
  4. 127.0.0.1:9001 (start from the beginning again)
  5. and so on...

Sending requests

The Proxy class has the same methods as the underlying Client class for sending (a)sync requests and retrieving responses (reactively). So please consult the documentation of hollodotme/fast-cgi-client for further information.

Here is just a short list of available methods:

  • $proxy->sendRequest(ProvidesRequestData $request) : ProvidesResponseData
    Sends a synchronous request and returns the response. (blocking)

  • $proxy->sendAsyncRequest(ProvidesRequestData $request) : int
    Sends an asynchronous request and returns the request ID. (non-blocking)

  • $proxy->readResponse(int $requestId, ?int $timeoutMs = null) : ProvidesResponseData
    Reads and returns the response of a previously obtained request ID.
    (blocking until response was read or read timed out)

  • $proxy->readResponses(?int $timeoutMs = null, int ...$requestIds) : \Generator|ProvidesResponseData[]
    Reads and yields the responses of previously obtained request IDs in the order of the given request IDs.
    (blocking until all responses were read or read timed out)

  • $proxy->readReadyResponses(?int $timeoutMs = null) : \Generator|ProvidesResponseData[]
    Reads and yields the responses of all finished requests.
    (non-blocking, meant to be used in a loop)

  • $proxy->waitForResponse(int $requestId, ?int $timeoutMs = null) : void
    Waits for the response of a previously obtained request ID and calls the request's response callback.
    (blocking until response was read or read timed out)

  • $proxy->waitForResponses(?int $timeoutMs = null) : void
    Waits for the responses of the previously obtained request IDs in the order of finished requests and calls the corresponding response callbacks.
    (blocking until all responses were read or read timed out)

  • $proxy->hasResponse(int $requestId) : bool
    Returns whether the given request ID has a response or not. (non-blocking)

  • $proxy->handleResponse(int $requestId, ?int $timeoutMs = null) : void
    Calls the corresponding response callback of an already finished request.
    (If request ID has a response must be checked before calling this method, see $proxy->hasResponse(int $requestId)).

  • $proxy->hasUnhandledResponses() : bool
    Returns TRUE if there are unhandles responses left, otherwise FALSE.

  • $proxy->getRequestIdsHavingResponse() : array
    Returns all request IDs that have responses. (non-blocking)

  • $proxy->handleResponses(?int $timeoutMs = null, int ...$requestIds) : void
    Calls the corresponding response callbacks of already finished requests in the order of the given request Ids.
    (If request IDs have a response must be checked before calling this method, see $proxy->hasResponse(int $requestId) or $proxy->getRequestIdsHavingResponse() : array.)

  • $proxy->handleReadyResponses(?int $timeoutMs = null) : void
    Calls the corresponding response callbacks in the order of finished requests.
    (non-blocking, short for $proxy->handleResponses($timeoutMs, int ...$proxy->getRequestIdsHavingResponse()))

Cluster requests

This feature is available since v0.2.0 of this library.

In order to process a single request on a multitude of fastCGI servers, the ClusterProxy class was introduced. So in order to distribute the request to one of the configured fastCGI servers, the cluster proxy will send the same request to ALL configured fastCGI servers and allows you to read/handle their responses (reactively).

As per concept of cluster requests, there is always a one-to-many relation for request & responses. That's why the ClusterProxy class does not offer synchronous requests and reading of single responses based on a request ID.

To set up a cluster proxy, use the following example code:

<?php declare(strict_types=1);

namespace YourVendor\YourProject;

use hollodotme\FastCGI\ClusterProxy;
use hollodotme\FastCGI\Collections\Cluster;
use hollodotme\FastCGI\SocketConnections\NetworkSocket;
use hollodotme\FastCGI\SocketConnections\UnixDomainSocket;

$cluster = Cluster::fromConnections(
    new NetworkSocket( '127.0.0.1', 9000 ),
    new NetworkSocket( '10.100.10.42', 9000 ),
    new UnixDomainSocket( '/var/run/php7.3-fpm.sock' )	
);

$clusterProxy = new ClusterProxy( $cluster );

The following reduced set of methods to send requests and handle responses are available in the cluster proxy class:

  • $clusterProxy->sendAsyncRequest(ProvidesRequestData $request) : void
    Sends an asynchronous request to all connections in the cluster. (non-blocking)

  • $clusterProxy->readReadyResponses(?int $timeoutMs = null) : \Generator|ProvidesResponseData[]
    Reads and yields the responses of all finished requests.
    (non-blocking, meant to be used in a loop)

  • $clusterProxy->waitForResponses(?int $timeoutMs = null) : void
    Waits for the responses of the previously obtained request IDs in the order of finished requests and calls the corresponding response callbacks.
    (blocking until all responses were read or read timed out)

  • $clusterProxy->hasUnhandledResponses() : bool
    Returns TRUE if there are unhandles responses left, otherwise FALSE.

  • $clusterProxy->handleReadyResponses(?int $timeoutMs = null) : void
    Calls the corresponding response callbacks in the order of finished requests.
    (non-blocking, meant to be used in a loop in combination with $clusterProxy->hasUnhandledResponses())

Cluster status

This feature is available since v0.2.0 of this library.

In order to retrieve the status of all fastCGI servers in a cluster, the method ClusterProxy#getStatus() was introduced.

Currently this method solely supports status response implementation for PHP-FPM, but can easily be extended for other fastCGI servers by implementing the interface hollodotme\FastCGI\Interfaces\ProvidesServerStatus.

<?php declare(strict_types=1);

namespace hollodotme\FastCGI\Interfaces;

interface ProvidesServerStatus
{
	/**
     * Returns the original response object for the status request provided by hollodotme/fast-cgi-client
     * @see https://github.com/hollodotme/fast-cgi-client/blob/2.x-stable/src/Responses/Response.php
     * 
     * @return ProvidesResponseData
     */
	public function getResponse() : ProvidesResponseData;

    /**
     * Returns the connection object used for the status request 
     * in order to identify the server that produced the status response
     * 
     * @return ConfiguresSocketConnection
     */
	public function getConnection() : ConfiguresSocketConnection;

    /**
     * Returns any data structure representing the status information of the server 
     * @return mixed
     */
	public function getStatus();

    /**
     * Returns a list of any data structure representing current processes running on the server 
     * @return array 
     */
	public function getProcesses() : array;
}

Cluster status example

The following code reads the status of all 3 php-fpm containers that are part of the docker-compose setup of this library.

Please note: If the status endpoint is not enabled in the server's config (pm.status_path for PHP-FPM), the ClusterProxy#getStatus() method will throw a RuntimeException.

examples/cluster_status.php

<?php declare(strict_types=1);

namespace hollodotme\FastCGI\Examples;

use hollodotme\FastCGI\ClusterProxy;
use hollodotme\FastCGI\Collections\Cluster;
use hollodotme\FastCGI\Responses\PhpFpmStatusResponse;
use hollodotme\FastCGI\SocketConnections\NetworkSocket;

require_once __DIR__ . '/../vendor/autoload.php';

$cluster = Cluster::fromConnections(
	new NetworkSocket( 'php71', 9001 ),
	new NetworkSocket( 'php72', 9001 ),
	new NetworkSocket( 'php73', 9001 )
);

$clusterProxy = new ClusterProxy( $cluster );

$statusResponses = $clusterProxy->getStatus( '/status?full' );
# If you do not want the list processes, use the following line to get the status only
# $statusResponses = $clusterProxy->getStatus( '/status' );

/** @var PhpFpmStatusResponse $statusResponse */
foreach ( $statusResponses as $statusResponse )
{
	$connection = $statusResponse->getConnection();
	$status     = $statusResponse->getStatus();
	$processes  = $statusResponse->getProcesses();
	$response   = $statusResponse->getResponse();

	echo '[ SERVER: ', $connection->getSocketAddress(), " ]\n";

	echo '- Pool name: ', $status->getPoolName(), "\n";
	echo '- Process manager: ', $status->getProcessManager(), "\n";
	echo '- Started at: ', $status->getStartTime()->format( 'c' ), "\n";
	echo '- Seconds since start: ', $status->getStartSince(), "\n";
	echo '- Number of accepted connections: ', $status->getAcceptedConnections(), "\n";
	echo '- Current listen queue: ', $status->getListenQueue(), "\n";
	echo '- Listen queue maximum: ', $status->getMaxListenQueue(), "\n";
	echo '- Listen queue length: ', $status->getListenQueueLength(), "\n";
	echo '- Number of idle processes: ', $status->getIdleProcesses(), "\n";
	echo '- Number of active processes: ', $status->getActiveProcesses(), "\n";
	echo '- Number of total processes: ', $status->getTotalProcesses(), "\n";
	echo '- Number of active processes maximum: ', $status->getMaxActiveProcesses(), "\n";
	echo '- Times max children reached: ', $status->getMaxChildrenReached(), "\n";
	echo '- Number of slow requests: ', $status->getSlowRequests(), "\n";

	echo "\nPrinting processes:\n\n";

	foreach ( $processes as $index => $process )
	{
		echo '- [ PROCESS #', ($index + 1), " ]\n";
		echo '  * PID: ', $process->getPid(), "\n";
		echo '  * State: ', $process->getState(), "\n";
		echo '  * Started at: ', $process->getStartTime()->format( 'c' ), "\n";
		echo '  * Seconds since start: ', $process->getStartSince(), "\n";
		echo '  * Number of requests processed: ', $process->getRequests(), "\n";
		echo '  * Last request duration: ', $process->getRequestDuration(), "\n";
		echo '  * Last request method: ', $process->getRequestMethod(), "\n";
		echo '  * Last request URI: ', $process->getRequestUri(), "\n";
		echo '  * Last content length: ', $process->getContentLength(), "\n";
		echo '  * Last user: ', $process->getUser(), "\n";
		echo '  * Last script: ', $process->getScript(), "\n";
		echo '  * CPU usage of last request: ', $process->getLastRequestCpu(), "\n";
		echo '  * Memory usage of last request: ', $process->getLastRequestMemory(), "\n";

		echo "\n\n---\n\n";
	}

	echo 'Processing duration: ', $response->getDuration(), " seconds\n\n";
}

This script produces for example the following output

[ SERVER: tcp://php71:9001 ]
- Pool name: network
- Process manager: dynamic
- Started at: 2019-06-10T14:56:45+00:00
- Seconds since start: 18094
- Number of accepted connections: 81
- Current listen queue: 0
- Listen queue maximum: 0
- Listen queue length: 128
- Number of idle processes: 1
- Number of active processes: 1
- Number of total processes: 2
- Number of active processes maximum: 2
- Times max children reached: 0
- Number of slow requests: 0

Printing processes:

- [ PROCESS #1 ]
  * PID: 8
  * State: Idle
  * Started at: 2019-06-10T14:56:45+00:00
  * Seconds since start: 18094
  * Number of requests processed: 40
  * Last request duration: 190
  * Last request method: -
  * Last request URI: -
  * Last content length: 0
  * Last user: -
  * Last script: -
  * CPU usage of last request: 0
  * Memory usage of last request: 2097152


---

- [ PROCESS #2 ]
  * PID: 9
  * State: Running
  * Started at: 2019-06-10T14:56:45+00:00
  * Seconds since start: 18094
  * Number of requests processed: 41
  * Last request duration: 190
  * Last request method: GET
  * Last request URI: /status?full
  * Last content length: 0
  * Last user: -
  * Last script: -
  * CPU usage of last request: 0
  * Memory usage of last request: 0


---

Processing duration: 0.0137939453125 seconds

[ SERVER: tcp://php72:9001 ]
- Pool name: network
- Process manager: dynamic
- Started at: 2019-06-10T14:56:46+00:00
- Seconds since start: 18093
- Number of accepted connections: 75
- Current listen queue: 0
- Listen queue maximum: 0
- Listen queue length: 128
- Number of idle processes: 1
- Number of active processes: 1
- Number of total processes: 2
- Number of active processes maximum: 2
- Times max children reached: 0
- Number of slow requests: 0

Printing processes:

- [ PROCESS #1 ]
  * PID: 10
  * State: Idle
  * Started at: 2019-06-10T14:56:46+00:00
  * Seconds since start: 18093
  * Number of requests processed: 38
  * Last request duration: 217
  * Last request method: -
  * Last request URI: -
  * Last content length: 0
  * Last user: -
  * Last script: -
  * CPU usage of last request: 0
  * Memory usage of last request: 2097152


---

- [ PROCESS #2 ]
  * PID: 11
  * State: Running
  * Started at: 2019-06-10T14:56:46+00:00
  * Seconds since start: 18093
  * Number of requests processed: 37
  * Last request duration: 177
  * Last request method: GET
  * Last request URI: /status?full
  * Last content length: 0
  * Last user: -
  * Last script: -
  * CPU usage of last request: 0
  * Memory usage of last request: 0


---

Processing duration: 0.027499914169312 seconds

[ SERVER: tcp://php73:9001 ]
- Pool name: network
- Process manager: dynamic
- Started at: 2019-06-10T14:56:45+00:00
- Seconds since start: 18094
- Number of accepted connections: 1706
- Current listen queue: 0
- Listen queue maximum: 1
- Listen queue length: 128
- Number of idle processes: 2
- Number of active processes: 1
- Number of total processes: 3
- Number of active processes maximum: 23
- Times max children reached: 0
- Number of slow requests: 0

Printing processes:

- [ PROCESS #1 ]
  * PID: 331
  * State: Idle
  * Started at: 2019-06-10T17:00:25+00:00
  * Seconds since start: 10674
  * Number of requests processed: 383
  * Last request duration: 185
  * Last request method: -
  * Last request URI: -
  * Last content length: 0
  * Last user: -
  * Last script: -
  * CPU usage of last request: 0
  * Memory usage of last request: 2097152


---

- [ PROCESS #2 ]
  * PID: 497
  * State: Running
  * Started at: 2019-06-10T17:31:02+00:00
  * Seconds since start: 8837
  * Number of requests processed: 59
  * Last request duration: 244
  * Last request method: GET
  * Last request URI: /status?full
  * Last content length: 0
  * Last user: -
  * Last script: -
  * CPU usage of last request: 0
  * Memory usage of last request: 0


---

- [ PROCESS #3 ]
  * PID: 315
  * State: Idle
  * Started at: 2019-06-10T16:42:27+00:00
  * Seconds since start: 11752
  * Number of requests processed: 433
  * Last request duration: 230
  * Last request method: -
  * Last request URI: -
  * Last content length: 0
  * Last user: -
  * Last script: -
  * CPU usage of last request: 0
  * Memory usage of last request: 2097152


---

Processing duration: 0.029183149337769 seconds

Contributing

Contributions are welcome and will be fully credited. Please see the contribution guide for details.

hollodotme/fast-cgi-proxy 适用场景与选型建议

hollodotme/fast-cgi-proxy 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 21 次下载、GitHub Stars 达 11, 最近一次更新时间为 2017 年 11 月 25 日, 在 PHP 生态内属于活跃度较高的组件。

我们在过去多个企业项目中使用过 hollodotme/fast-cgi-proxy 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 hollodotme/fast-cgi-proxy 我们能提供哪些服务?
定制开发 / 二次开发

基于 hollodotme/fast-cgi-proxy 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 21
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 11
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 11
  • Watchers: 3
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2017-11-25