Introduction
OpenSwoole Magento integration enhances Magento Open Source performance by replacing PHP-FPM with an asynchronous, coroutine-based PHP server for faster, scalable requests.
Magento is powerful, but under heavy load its PHP-FPM model slows response time. Openswoole Magento Integration fixes this by keeping workers alive, enabling faster and more scalable performance.
With OpenSwoole Magento integration, developers can unlock real-time responsiveness and improved resource usage — helping your Magento 2 store perform at its best.
You can check the overview in the video below —
What Is OpenSwoole?
OpenSwoole Magento integration uses the high-performance asynchronous PHP framework OpenSwoole, enabling PHP to handle thousands of concurrent requests through non-blocking I/O and coroutines.
When combined with Magento, it helps reuse workers instead of creating new ones for each request, boosting speed and lowering resource usage.
Why Use OpenSwoole with Magento?
By integrating OpenSwoole with Magento, you can:
- Reduce latency and deliver faster responses
- Increase throughput by reusing workers
- Handle concurrent requests efficiently
- Improve scalability without expensive hardware upgrades
- Optimize resource usage and reduce server load
In short, OpenSwoole Magento integration helps your store perform faster, scale better, and stay more responsive during high-traffic periods.
In Magento’s context, OpenSwoole can replace the traditional PHP-FPM model and run Magento as a long-running HTTP server.
Benefits.
Here’s why developers are moving toward OpenSwoole Magento integration:
| Feature | PHP-FPM | OpenSwoole |
|---|---|---|
| Request handling | Starts a new process per request | Persistent, coroutine-based workers |
| Performance | Moderate | 2×–10× faster |
| Connection reuse | No | Yes |
| Asynchronous I/O | No | Yes |
| Long-running memory objects | No | Yes |
How to Install and Configure OpenSwoole for Magento 2
Follow these steps to set up the OpenSwoole Magento integration:
1. Install OpenSwoole
Run the following commands to download and install OpenSwoole:
sudo pecl install openswoole
Verify installation:
echo “extension=openswoole.so” | sudo tee /etc/php/8.3/cli/conf.d/20-openswoole.ini
2. Prepare Magento Environment
Ensure Magento is installed and configured properly under apache or nginx server.
Run the following command to install the openswoole package for magento
composer require openswoole/core
3. Create a Swoole Server for Magento
<?php
use OpenSwoole\HTTP\Server;
use OpenSwoole\HTTP\Request;
use OpenSwoole\HTTP\Response;
use OpenSwoole\Coroutine\Http\Client; // CRUCIAL: Non-blocking HTTP client
use OpenSwoole\Table; // CRUCIAL: Used for shared memory caching between workers
/**
* OptimizedMagentoProxy
* A high-performance, non-blocking GraphQL proxy using OpenSwoole with shared memory caching.
*/
class OptimizedMagentoProxy
{
private $magentoBaseUrl;
private $requestCount = 0; // Simple counter local to each worker
// Use OpenSwoole\Table for shared cache
private ?Table $cacheTable = null;
private $cacheHits = 0; // Local counter for hit rate calculation
private $cacheMisses = 0; // Local counter for hit rate calculation
public function __construct($magentoBaseUrl)
{
$this->magentoBaseUrl = rtrim($magentoBaseUrl, '/');
}
/**
* Initializes the shared cache table before the server starts.
*/
private function initializeSharedCache()
{
// 1. Define the size (max rows) and the columns for the shared Table
// Set a reasonable size, e.g., 2048 entries. Must be a power of 2.
$maxCacheEntries = 2048;
$this->cacheTable = new Table($maxCacheEntries);
// Define columns: 'response' for the JSON data, 'timestamp' for TTL
// Note: The response data must be stored as a string type.
$this->cacheTable->column('response', Table::TYPE_STRING, 65535); // VARCHAR equivalent (64KB max)
$this->cacheTable->column('timestamp', Table::TYPE_INT, 4); // Unix timestamp (32-bit integer)
// Finalize the table creation
$this->cacheTable->create();
}
/**
* Starts the OpenSwoole HTTP Server and defines event handlers.
*/
public function run()
{
// Must initialize shared memory Table before starting the server
$this->initializeSharedCache();
// Define missing variable: A reasonable coroutine limit (e.g., 10000 per worker)
$maxCoroutine = 10000;
// 1. Initialize the OpenSwoole HTTP Server
$server = new Server("127.0.0.1", 9501);
// 2. Set optimized server configuration
$server->set([
// Tuned worker count (2 workers is low for production, but used here as set)
'worker_num' => 2,
// Recycle worker after a number of requests to mitigate memory leaks
'max_request' => 100,
// Enable coroutine support for high concurrency
'enable_coroutine' => true,
//Set a reasonable coroutine cap
'max_coroutine' => $maxCoroutine,
// Socket backlog and reuse flags for production
'backlog' => 128,
'enable_reuse_port' => true,
// Disabled daemonize for development
'daemonize' => false,
// Enable TCP_QUICKACK / disable Nagle's algorithm for low latency
'open_tcp_nodelay' => true,
]);
// Console output for setup information
echo "OPTIMIZED Magento GraphQL Proxy\n";
echo "Target: {$this->magentoBaseUrl}/graphql\n";
echo "Running on: http://127.0.0.1:9501\n";
echo "Shared memory caching enabled (OpenSwoole\\Table)\n\n";
// 3. Define the 'request' event handler
$server->on("request", function (Request $request, Response $response) {
$this->requestCount++; // Local counter for unique logging ID
$requestId = $this->requestCount;
$startTime = microtime(true);
// Delegate the main request handling logic
$this->handleOptimizedRequest($request, $response, $requestId);
// Calculate and log the total processing time and cache hit rate
$endTime = microtime(true);
$duration = round(($endTime - $startTime) * 1000, 2);
$hitRate = $this->getCacheHitRate();
echo "[Req #$requestId] {$duration}ms\n";
});
// 4. Start the server
$server->start();
}
/**
* Main logic to process the incoming client request, integrating the shared cache.
*/
private function handleOptimizedRequest(Request $request, Response $response, $requestId)
{
// Set essential response headers (JSON and CORS)
$response->header("Content-Type", "application/json");
$response->header('Access-Control-Allow-Origin', '*');
// Handle CORS preflight
if ($request->server['request_method'] === 'OPTIONS') {
$response->header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
$response->header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
$response->end();
return;
}
// Only handle POST requests for GraphQL endpoints
if ($request->server['request_method'] !== 'POST') {
$response->status(405);
$response->end(json_encode(['error' => 'Method not allowed', 'message' => 'Use POST']));
return;
}
try {
$content = $request->getContent();
$input = json_decode($content, true);
// Basic validation for a GraphQL query
if (!$input || !isset($input['query'])) {
$response->status(400);
$response->end(json_encode(['error' => 'Bad Request', 'message' => 'No query']));
return;
}
// Simple cache key: MD5 hash of the entire request payload (query + variables)
$cacheKey = md5($content);
$cacheTtl = 10; // 10 seconds TTL for cache entry
// Check the SHARED memory cache first (atomic read)
$cacheData = $this->cacheTable->get($cacheKey);
if ($cacheData !== false) {
// Cache HIT: Check if the entry is still fresh
if (time() - $cacheData['timestamp'] < $cacheTtl) {
$this->cacheHits++; // Increment local counter
$response->end($cacheData['response']);
return;
} else {
// Cache expired: Remove the entry (atomic operation)
$this->cacheTable->del($cacheKey);
}
}
$this->cacheMisses++;
// Forward to Magento (non-blocking)
$result = $this->optimizedForward($content, $requestId);
// Cache successful responses that do not contain errors
if (strpos($result, '"errors"') === false) {
// Store in shared memory (atomic write)
$this->cacheTable->set($cacheKey, [
'response' => $result,
'timestamp' => time()
]);
}
$response->end($result);
} catch (\Exception $e) {
// Log the error and return a generic service unavailable message
error_log("Proxy Error [Req #$requestId]: " . $e->getMessage());
$response->status(503);
$response->end(json_encode(['errors' => [['message' => 'Service error']]]));
}
}
/**
* Non-blocking forwarding of the request to the upstream Magento GraphQL endpoint.
*/
private function optimizedForward($jsonPayload, $requestId)
{
$url = $this->magentoBaseUrl . '/graphql';
// Parse the URL for the Coroutine Client connection
$parts = parse_url($url);
$scheme = ($parts['scheme'] ?? 'http');
$host = $parts['host'] ?? '127.0.0.1';
$isSsl = ($scheme === 'https');
$port = $parts['port'] ?? ($isSsl ? 443 : 80);
$path = ($parts['path'] ?? '/') . (isset($parts['query']) ? '?' . $parts['query'] : '');
// Initialize the Coroutine HTTP client
$client = new Client($host, $port, $isSsl);
// Configure the client for performance (timeout and connection reuse)
$client->set(['timeout' => 10, 'keep_alive' => true]);
// Set mandatory headers for the upstream POST request
$client->setHeaders([
'Host' => $host,
'Content-Type' => 'application/json',
'Content-Length' => (string) strlen($jsonPayload),
'Connection' => 'keep-alive',
'User-Agent' => 'Swoole-GraphQL-Proxy/1.0', // Recommended proxy header
]);
// Execute the non-blocking POST request (coroutine suspension)
$ret = $client->post($path, $jsonPayload);
$status = $client->getStatusCode();
$body = $client->getBody();
$client->close();
// Check for non-200 responses from the upstream server
if ($status !== 200) {
throw new \Exception("Upstream HTTP {$status}: " . substr($body ?? '', 0, 200));
}
return $body;
}
/**
* Calculates the cache hit rate based on local worker counters.
*/
private function getCacheHitRate()
{
$total = $this->cacheHits + $this->cacheMisses;
// The total will reset when the worker reloads (max_request), which is acceptable for a local metric.
return $total > 0 ? round(($this->cacheHits / $total) * 100, 1) : 0;
}
}
$magentoUrl = 'your_magento_url';
// Disable Xdebug
if (function_exists('xdebug_disable')) {
xdebug_disable();
}
echo "Starting Optimized Magento Proxy...\n";
$proxy = new OptimizedMagentoProxy($magentoUrl);
$proxy->run();
4. Start the Server
php swoole_magento_server.php
Now access: http://127.0.0.0:9501
Now you can check the graphql api using the above URL
Url for graphQL api: http://127.0.0.0:9501/graphql
Benchmark: Magento (PHP-FPM) vs Magento + OpenSwoole
You can benchmark using wrk or ab (ApacheBench).
Create a file named graphql_test.lua and put the content below in that file. Save it in the Magento root path.
wrk.method = "POST"
wrk.body = '{"query": "{ products(search: \\\"\\\", pageSize: 9) { items { id name sku } } }"}'
wrk.headers["Content-Type"] = "application/json"
Performance Analysis of Magento GraphQL Endpoint Using wrk Load Testing Tool
wrk -t1 -c50 -d6s -s graphql_test.lua your_magento_url/graphql --latency
Running 6s test @ your_magento_url/graphql
1 threads and 50 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 279.60ms 176.66ms 1.87s 83.78%
Req/Sec 76.84 19.65 101.00 67.86%
Latency Distribution
50% 210.20ms
75% 343.51ms
90% 478.56ms
99% 900.43ms
433 requests in 6.01s, 613.98KB read
Socket errors: connect 0, read 0, write 0, timeout 28
Requests/sec: 72.07
Transfer/sec: 102.19KB
Performance Analysis of Swoole Endpoint Using wrk Load Testing Tool
wrk -t1 -c50 -d6s -s graphql_test.lua http://127.0.0.1:9501/graphql --latency
Running 6s test @ http://127.0.0.1:9501/graphql
1 threads and 50 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 61.24ms 119.66ms 1.66s 77.93%
Req/Sec 206.78 71.36 262.00 82.35%
Latency Distribution
50% 8.43ms
75% 56.94ms
90% 203.10ms
99% 239.24ms
1058 requests in 6.01s, 730.47KB read
Requests/sec: 176.15
Transfer/sec: 121.62KB
Here’s a clear comparison table showing the performance difference between Magento (PHP-FPM) and OpenSwoole Magento Integration based on your benchmark results:
| Metric | Magento (PHP-FPM) | Magento + Swoole | Improvement (Swoole) |
|---|---|---|---|
| Requests/sec | 72.07 | 176.15 | ~2.4× faster |
| Average Latency | 279.60 ms | 61.24 ms | ~4.5× lower latency |
| P50 Latency (Median) | 210.20 ms | 8.43 ms | ~25× faster |
| P75 Latency | 343.51 ms | 56.94 ms | ~6× faster |
| P90 Latency | 478.56 ms | 203.10 ms | ~2.4× faster |
| P99 Latency | 900.43 ms | 239.24 ms | ~3.7× faster |
| Total Requests (6 s) | 433 | 1058 | ~2.4× more requests handled |
| Transfer/sec | 102.19 KB/s | 121.62 KB/s | ~1.2× higher throughput |
| Timeouts | 28 | 0 | No timeouts with Swoole |
Conclusion
OpenSwoole is dramatically faster than PHP-FPM for Magento GraphQL.
It handles ~2.4× more requests per second and cuts latency by 78%.
Perfect for GraphQL APIs, PWAs, and high-traffic stores.
For technical assistance, reach out to us at [email protected].
Discover ways to enhance your Magento 2 store by checking out the Magento 2 plugins page.
For tailored solutions or custom development, consider hiring Magento 2 Developers for your project.

Be the first to comment.