{"id":21288987,"url":"https://github.com/hollodotme/fast-cgi-proxy","last_synced_at":"2025-07-11T14:31:45.749Z","repository":{"id":56985450,"uuid":"112020542","full_name":"hollodotme/fast-cgi-proxy","owner":"hollodotme","description":"A proxy for distributing (a)sync requests to multiple php-fpm sockets/pools.","archived":false,"fork":false,"pushed_at":"2019-06-10T20:30:45.000Z","size":120,"stargazers_count":11,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-26T22:39:45.620Z","etag":null,"topics":["async","fastcgi","php-fpm","proxy"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hollodotme.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null}},"created_at":"2017-11-25T17:07:14.000Z","updated_at":"2024-01-16T00:10:04.000Z","dependencies_parsed_at":"2022-08-21T09:10:30.737Z","dependency_job_id":null,"html_url":"https://github.com/hollodotme/fast-cgi-proxy","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/hollodotme/fast-cgi-proxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollodotme%2Ffast-cgi-proxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollodotme%2Ffast-cgi-proxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollodotme%2Ffast-cgi-proxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollodotme%2Ffast-cgi-proxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hollodotme","download_url":"https://codeload.github.com/hollodotme/fast-cgi-proxy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hollodotme%2Ffast-cgi-proxy/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264833276,"owners_count":23670617,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","fastcgi","php-fpm","proxy"],"created_at":"2024-11-21T12:32:50.128Z","updated_at":"2025-07-11T14:31:45.400Z","avatar_url":"https://github.com/hollodotme.png","language":"PHP","readme":"[![CircleCI](https://circleci.com/gh/hollodotme/fast-cgi-proxy.svg?style=svg)](https://circleci.com/gh/hollodotme/fast-cgi-proxy)\n[![Latest Stable Version](https://poser.pugx.org/hollodotme/fast-cgi-proxy/v/stable)](https://packagist.org/packages/hollodotme/fast-cgi-proxy) \n[![Total Downloads](https://poser.pugx.org/hollodotme/fast-cgi-proxy/downloads)](https://packagist.org/packages/hollodotme/fast-cgi-proxy) \n[![codecov](https://codecov.io/gh/hollodotme/fast-cgi-proxy/branch/master/graph/badge.svg)](https://codecov.io/gh/hollodotme/fast-cgi-proxy)\n\n# FastCGI Proxy\n\n## Description\n\nA proxy for distributing (a)sync requests to multiple fastCGI servers.\n\n## Installation\n\n```bash\ncomposer require hollodotme/fast-cgi-proxy\n```\n\n## Usage\n\n### Request distribution\n\nThe proxy can distribute requests to multiple fastCGI servers in the following ways:\n\n1. Randomly\n2. Via round robin\n\n#### Random distribution\n\nTo set up random distribution use the following example code:\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\Proxy;\nuse hollodotme\\FastCGI\\Collections\\Random;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\nuse hollodotme\\FastCGI\\SocketConnections\\UnixDomainSocket;\n\n$random = Random::fromConnections(\n    new NetworkSocket( '127.0.0.1', 9000 ),\n    new NetworkSocket( '10.100.10.42', 9000 ),\n    new UnixDomainSocket( '/var/run/php7.3-fpm.sock' )\t\n);\n\n$proxy = new Proxy( $random );\n``` \n\nWhen sending requests now the proxy will randomly choose one of the fastCGI servers to process the request.\n\n#### Round robin distribution\n\nTo set up round robin distribution use the following example code:\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\Proxy;\nuse hollodotme\\FastCGI\\Collections\\RoundRobin;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\nuse hollodotme\\FastCGI\\SocketConnections\\UnixDomainSocket;\n\n$roundRobin = RoundRobin::fromConnections(\n    new NetworkSocket( '127.0.0.1', 9000 ),\n    new NetworkSocket( '10.100.10.42', 9000 ),\n    new UnixDomainSocket( '/var/run/php7.3-fpm.sock' )\t\n);\n\n$proxy = new Proxy( $roundRobin );\n```\n\nThe proxy will send your requests to the next fastCGI server in the same order they were added to the `RoundRobin` instance.\nIn this example it will send to:\n1. `127.0.0.1:9001`\n2. `10.100.10.42:9001`,\n3. `/var/run/php7.1-fpm.sock`\n4. `127.0.0.1:9001` (start from the beginning again)\n5. and so on...\n\n---\n\n### Sending requests\n\nThe `Proxy` class has the same methods as the underlying [Client](https://github.com/hollodotme/fast-cgi-client/blob/2.x-stable/src/Client.php) class for sending (a)sync requests and retrieving responses (reactively).\nSo please consult the documentation of [hollodotme/fast-cgi-client](https://github.com/hollodotme/fast-cgi-client/blob/2.x-stable) for further information.\n\nHere is just a short list of available methods:\n\n* `$proxy-\u003esendRequest(ProvidesRequestData $request) : ProvidesResponseData`  \n  Sends a synchronous request and returns the response. (blocking)\n  \n* `$proxy-\u003esendAsyncRequest(ProvidesRequestData $request) : int`  \n  Sends an asynchronous request and returns the request ID. (non-blocking)\n  \n* `$proxy-\u003ereadResponse(int $requestId, ?int $timeoutMs = null) : ProvidesResponseData`  \n  Reads and returns the response of a previously obtained request ID.  \n  (blocking until response was read or read timed out) \n\n* `$proxy-\u003ereadResponses(?int $timeoutMs = null, int ...$requestIds) : \\Generator|ProvidesResponseData[]`  \n  Reads and yields the responses of previously obtained request IDs in the order of the given request IDs.  \n  (blocking until all responses were read or read timed out)\n  \n* `$proxy-\u003ereadReadyResponses(?int $timeoutMs = null) : \\Generator|ProvidesResponseData[]`  \n  Reads and yields the responses of all finished requests.  \n  (non-blocking, meant to be used in a loop)\n  \n* `$proxy-\u003ewaitForResponse(int $requestId, ?int $timeoutMs = null) : void`  \n  Waits for the response of a previously obtained request ID and calls the request's response callback.  \n  (blocking until response was read or read timed out)\n  \n* `$proxy-\u003ewaitForResponses(?int $timeoutMs = null) : void`  \n  Waits for the responses of the previously obtained request IDs in the order of finished requests and calls the corresponding response callbacks.  \n  (blocking until all responses were read or read timed out)\n  \n* `$proxy-\u003ehasResponse(int $requestId) : bool`  \n  Returns whether the given request ID has a response or not. (non-blocking) \n  \n* `$proxy-\u003ehandleResponse(int $requestId, ?int $timeoutMs = null) : void`  \n  Calls the corresponding response callback of an already finished request.  \n  (If request ID has a response must be checked before calling this method, see `$proxy-\u003ehasResponse(int $requestId)`).\n  \n* `$proxy-\u003ehasUnhandledResponses() : bool`  \n  Returns TRUE if there are unhandles responses left, otherwise FALSE.\n  \n* `$proxy-\u003egetRequestIdsHavingResponse() : array`  \n  Returns all request IDs that have responses. (non-blocking)\n  \n* `$proxy-\u003ehandleResponses(?int $timeoutMs = null, int ...$requestIds) : void`  \n  Calls the corresponding response callbacks of already finished requests in the order of the given request Ids.  \n  (If request IDs have a response must be checked before calling this method, see `$proxy-\u003ehasResponse(int $requestId)` or `$proxy-\u003egetRequestIdsHavingResponse() : array`.)\n  \n* `$proxy-\u003ehandleReadyResponses(?int $timeoutMs = null) : void`  \n  Calls the corresponding response callbacks in the order of finished requests.  \n  (non-blocking, short for `$proxy-\u003ehandleResponses($timeoutMs, int ...$proxy-\u003egetRequestIdsHavingResponse())`)\n\n---\n\n### Cluster requests\n\nThis feature is available since `v0.2.0` of this library.\n\nIn order to process a single request on a multitude of fastCGI servers, the `ClusterProxy` class was introduced.\nSo in order to distribute the request to one of the configured fastCGI servers, the cluster proxy will send the same \nrequest to ALL configured fastCGI servers and allows you to read/handle their responses (reactively).\n\nAs per concept of cluster requests, there is always a one-to-many relation for request \u0026 responses.\nThat's why the `ClusterProxy` class does not offer synchronous requests and reading of single responses based on a request ID.\n\nTo set up a cluster proxy, use the following example code:\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace YourVendor\\YourProject;\n\nuse hollodotme\\FastCGI\\ClusterProxy;\nuse hollodotme\\FastCGI\\Collections\\Cluster;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\nuse hollodotme\\FastCGI\\SocketConnections\\UnixDomainSocket;\n\n$cluster = Cluster::fromConnections(\n    new NetworkSocket( '127.0.0.1', 9000 ),\n    new NetworkSocket( '10.100.10.42', 9000 ),\n    new UnixDomainSocket( '/var/run/php7.3-fpm.sock' )\t\n);\n\n$clusterProxy = new ClusterProxy( $cluster );\n```\n\nThe following reduced set of methods to send requests and handle responses are available in the cluster proxy class:\n \n* `$clusterProxy-\u003esendAsyncRequest(ProvidesRequestData $request) : void`  \n  Sends an asynchronous request to all connections in the cluster. (non-blocking)\n  \n* `$clusterProxy-\u003ereadReadyResponses(?int $timeoutMs = null) : \\Generator|ProvidesResponseData[]`  \n  Reads and yields the responses of all finished requests.  \n  (non-blocking, meant to be used in a loop)\n  \n* `$clusterProxy-\u003ewaitForResponses(?int $timeoutMs = null) : void`  \n  Waits for the responses of the previously obtained request IDs in the order of finished requests and calls the corresponding response callbacks.  \n  (blocking until all responses were read or read timed out)\n  \n* `$clusterProxy-\u003ehasUnhandledResponses() : bool`  \n  Returns TRUE if there are unhandles responses left, otherwise FALSE.\n  \n* `$clusterProxy-\u003ehandleReadyResponses(?int $timeoutMs = null) : void`  \n  Calls the corresponding response callbacks in the order of finished requests.  \n  (non-blocking, meant to be used in a loop in combination with `$clusterProxy-\u003ehasUnhandledResponses()`)\n\n---\n\n### Cluster status\n\nThis feature is available since `v0.2.0` of this library.\n\nIn order to retrieve the status of all fastCGI servers in a cluster, the method `ClusterProxy#getStatus()` was introduced.\n\nCurrently this method solely supports status response implementation for PHP-FPM, \nbut can easily be extended for other fastCGI servers by implementing the interface \n[`hollodotme\\FastCGI\\Interfaces\\ProvidesServerStatus`](src/Interfaces/ProvidesServerStatus.php).\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace hollodotme\\FastCGI\\Interfaces;\n\ninterface ProvidesServerStatus\n{\n\t/**\n     * Returns the original response object for the status request provided by hollodotme/fast-cgi-client\n     * @see https://github.com/hollodotme/fast-cgi-client/blob/2.x-stable/src/Responses/Response.php\n     * \n     * @return ProvidesResponseData\n     */\n\tpublic function getResponse() : ProvidesResponseData;\n\n    /**\n     * Returns the connection object used for the status request \n     * in order to identify the server that produced the status response\n     * \n     * @return ConfiguresSocketConnection\n     */\n\tpublic function getConnection() : ConfiguresSocketConnection;\n\n    /**\n     * Returns any data structure representing the status information of the server \n     * @return mixed\n     */\n\tpublic function getStatus();\n\n    /**\n     * Returns a list of any data structure representing current processes running on the server \n     * @return array \n     */\n\tpublic function getProcesses() : array;\n}\n```\n\n#### Cluster status example\n\nThe following code reads the status of all 3 php-fpm containers that are part of the [docker-compose setup](./docker-compose.yml) of this library.\n\n**Please note:** If the status endpoint is not enabled in the server's config (`pm.status_path` for PHP-FPM),\nthe `ClusterProxy#getStatus()` method will throw a `RuntimeException`.\n\n**[examples/cluster_status.php](examples/cluster_status.php)**\n\n```php\n\u003c?php declare(strict_types=1);\n\nnamespace hollodotme\\FastCGI\\Examples;\n\nuse hollodotme\\FastCGI\\ClusterProxy;\nuse hollodotme\\FastCGI\\Collections\\Cluster;\nuse hollodotme\\FastCGI\\Responses\\PhpFpmStatusResponse;\nuse hollodotme\\FastCGI\\SocketConnections\\NetworkSocket;\n\nrequire_once __DIR__ . '/../vendor/autoload.php';\n\n$cluster = Cluster::fromConnections(\n\tnew NetworkSocket( 'php71', 9001 ),\n\tnew NetworkSocket( 'php72', 9001 ),\n\tnew NetworkSocket( 'php73', 9001 )\n);\n\n$clusterProxy = new ClusterProxy( $cluster );\n\n$statusResponses = $clusterProxy-\u003egetStatus( '/status?full' );\n# If you do not want the list processes, use the following line to get the status only\n# $statusResponses = $clusterProxy-\u003egetStatus( '/status' );\n\n/** @var PhpFpmStatusResponse $statusResponse */\nforeach ( $statusResponses as $statusResponse )\n{\n\t$connection = $statusResponse-\u003egetConnection();\n\t$status     = $statusResponse-\u003egetStatus();\n\t$processes  = $statusResponse-\u003egetProcesses();\n\t$response   = $statusResponse-\u003egetResponse();\n\n\techo '[ SERVER: ', $connection-\u003egetSocketAddress(), \" ]\\n\";\n\n\techo '- Pool name: ', $status-\u003egetPoolName(), \"\\n\";\n\techo '- Process manager: ', $status-\u003egetProcessManager(), \"\\n\";\n\techo '- Started at: ', $status-\u003egetStartTime()-\u003eformat( 'c' ), \"\\n\";\n\techo '- Seconds since start: ', $status-\u003egetStartSince(), \"\\n\";\n\techo '- Number of accepted connections: ', $status-\u003egetAcceptedConnections(), \"\\n\";\n\techo '- Current listen queue: ', $status-\u003egetListenQueue(), \"\\n\";\n\techo '- Listen queue maximum: ', $status-\u003egetMaxListenQueue(), \"\\n\";\n\techo '- Listen queue length: ', $status-\u003egetListenQueueLength(), \"\\n\";\n\techo '- Number of idle processes: ', $status-\u003egetIdleProcesses(), \"\\n\";\n\techo '- Number of active processes: ', $status-\u003egetActiveProcesses(), \"\\n\";\n\techo '- Number of total processes: ', $status-\u003egetTotalProcesses(), \"\\n\";\n\techo '- Number of active processes maximum: ', $status-\u003egetMaxActiveProcesses(), \"\\n\";\n\techo '- Times max children reached: ', $status-\u003egetMaxChildrenReached(), \"\\n\";\n\techo '- Number of slow requests: ', $status-\u003egetSlowRequests(), \"\\n\";\n\n\techo \"\\nPrinting processes:\\n\\n\";\n\n\tforeach ( $processes as $index =\u003e $process )\n\t{\n\t\techo '- [ PROCESS #', ($index + 1), \" ]\\n\";\n\t\techo '  * PID: ', $process-\u003egetPid(), \"\\n\";\n\t\techo '  * State: ', $process-\u003egetState(), \"\\n\";\n\t\techo '  * Started at: ', $process-\u003egetStartTime()-\u003eformat( 'c' ), \"\\n\";\n\t\techo '  * Seconds since start: ', $process-\u003egetStartSince(), \"\\n\";\n\t\techo '  * Number of requests processed: ', $process-\u003egetRequests(), \"\\n\";\n\t\techo '  * Last request duration: ', $process-\u003egetRequestDuration(), \"\\n\";\n\t\techo '  * Last request method: ', $process-\u003egetRequestMethod(), \"\\n\";\n\t\techo '  * Last request URI: ', $process-\u003egetRequestUri(), \"\\n\";\n\t\techo '  * Last content length: ', $process-\u003egetContentLength(), \"\\n\";\n\t\techo '  * Last user: ', $process-\u003egetUser(), \"\\n\";\n\t\techo '  * Last script: ', $process-\u003egetScript(), \"\\n\";\n\t\techo '  * CPU usage of last request: ', $process-\u003egetLastRequestCpu(), \"\\n\";\n\t\techo '  * Memory usage of last request: ', $process-\u003egetLastRequestMemory(), \"\\n\";\n\n\t\techo \"\\n\\n---\\n\\n\";\n\t}\n\n\techo 'Processing duration: ', $response-\u003egetDuration(), \" seconds\\n\\n\";\n}\n```\n\nThis script produces for example the following output\n\n```text\n[ SERVER: tcp://php71:9001 ]\n- Pool name: network\n- Process manager: dynamic\n- Started at: 2019-06-10T14:56:45+00:00\n- Seconds since start: 18094\n- Number of accepted connections: 81\n- Current listen queue: 0\n- Listen queue maximum: 0\n- Listen queue length: 128\n- Number of idle processes: 1\n- Number of active processes: 1\n- Number of total processes: 2\n- Number of active processes maximum: 2\n- Times max children reached: 0\n- Number of slow requests: 0\n\nPrinting processes:\n\n- [ PROCESS #1 ]\n  * PID: 8\n  * State: Idle\n  * Started at: 2019-06-10T14:56:45+00:00\n  * Seconds since start: 18094\n  * Number of requests processed: 40\n  * Last request duration: 190\n  * Last request method: -\n  * Last request URI: -\n  * Last content length: 0\n  * Last user: -\n  * Last script: -\n  * CPU usage of last request: 0\n  * Memory usage of last request: 2097152\n\n\n---\n\n- [ PROCESS #2 ]\n  * PID: 9\n  * State: Running\n  * Started at: 2019-06-10T14:56:45+00:00\n  * Seconds since start: 18094\n  * Number of requests processed: 41\n  * Last request duration: 190\n  * Last request method: GET\n  * Last request URI: /status?full\n  * Last content length: 0\n  * Last user: -\n  * Last script: -\n  * CPU usage of last request: 0\n  * Memory usage of last request: 0\n\n\n---\n\nProcessing duration: 0.0137939453125 seconds\n\n[ SERVER: tcp://php72:9001 ]\n- Pool name: network\n- Process manager: dynamic\n- Started at: 2019-06-10T14:56:46+00:00\n- Seconds since start: 18093\n- Number of accepted connections: 75\n- Current listen queue: 0\n- Listen queue maximum: 0\n- Listen queue length: 128\n- Number of idle processes: 1\n- Number of active processes: 1\n- Number of total processes: 2\n- Number of active processes maximum: 2\n- Times max children reached: 0\n- Number of slow requests: 0\n\nPrinting processes:\n\n- [ PROCESS #1 ]\n  * PID: 10\n  * State: Idle\n  * Started at: 2019-06-10T14:56:46+00:00\n  * Seconds since start: 18093\n  * Number of requests processed: 38\n  * Last request duration: 217\n  * Last request method: -\n  * Last request URI: -\n  * Last content length: 0\n  * Last user: -\n  * Last script: -\n  * CPU usage of last request: 0\n  * Memory usage of last request: 2097152\n\n\n---\n\n- [ PROCESS #2 ]\n  * PID: 11\n  * State: Running\n  * Started at: 2019-06-10T14:56:46+00:00\n  * Seconds since start: 18093\n  * Number of requests processed: 37\n  * Last request duration: 177\n  * Last request method: GET\n  * Last request URI: /status?full\n  * Last content length: 0\n  * Last user: -\n  * Last script: -\n  * CPU usage of last request: 0\n  * Memory usage of last request: 0\n\n\n---\n\nProcessing duration: 0.027499914169312 seconds\n\n[ SERVER: tcp://php73:9001 ]\n- Pool name: network\n- Process manager: dynamic\n- Started at: 2019-06-10T14:56:45+00:00\n- Seconds since start: 18094\n- Number of accepted connections: 1706\n- Current listen queue: 0\n- Listen queue maximum: 1\n- Listen queue length: 128\n- Number of idle processes: 2\n- Number of active processes: 1\n- Number of total processes: 3\n- Number of active processes maximum: 23\n- Times max children reached: 0\n- Number of slow requests: 0\n\nPrinting processes:\n\n- [ PROCESS #1 ]\n  * PID: 331\n  * State: Idle\n  * Started at: 2019-06-10T17:00:25+00:00\n  * Seconds since start: 10674\n  * Number of requests processed: 383\n  * Last request duration: 185\n  * Last request method: -\n  * Last request URI: -\n  * Last content length: 0\n  * Last user: -\n  * Last script: -\n  * CPU usage of last request: 0\n  * Memory usage of last request: 2097152\n\n\n---\n\n- [ PROCESS #2 ]\n  * PID: 497\n  * State: Running\n  * Started at: 2019-06-10T17:31:02+00:00\n  * Seconds since start: 8837\n  * Number of requests processed: 59\n  * Last request duration: 244\n  * Last request method: GET\n  * Last request URI: /status?full\n  * Last content length: 0\n  * Last user: -\n  * Last script: -\n  * CPU usage of last request: 0\n  * Memory usage of last request: 0\n\n\n---\n\n- [ PROCESS #3 ]\n  * PID: 315\n  * State: Idle\n  * Started at: 2019-06-10T16:42:27+00:00\n  * Seconds since start: 11752\n  * Number of requests processed: 433\n  * Last request duration: 230\n  * Last request method: -\n  * Last request URI: -\n  * Last content length: 0\n  * Last user: -\n  * Last script: -\n  * CPU usage of last request: 0\n  * Memory usage of last request: 2097152\n\n\n---\n\nProcessing duration: 0.029183149337769 seconds\n```\n\n## Contributing\n\nContributions are welcome and will be fully credited. Please see the [contribution guide](.github/CONTRIBUTING.md) for details.\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhollodotme%2Ffast-cgi-proxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhollodotme%2Ffast-cgi-proxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhollodotme%2Ffast-cgi-proxy/lists"}