Back to Top

OpenSwoole Magento Integration

Updated 7 November 2025

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 —

RHAm6a8Ns-c

Searching for an experienced
Magento 2 Company ?
Find out More

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:

FeaturePHP-FPMOpenSwoole
Request handlingStarts a new process per requestPersistent, coroutine-based workers
PerformanceModerate2×–10× faster
Connection reuseNoYes
Asynchronous I/ONoYes
Long-running memory objectsNoYes

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:

MetricMagento (PHP-FPM)Magento + SwooleImprovement (Swoole)
Requests/sec72.07176.15~2.4× faster
Average Latency279.60 ms61.24 ms~4.5× lower latency
P50 Latency (Median)210.20 ms8.43 ms~25× faster
P75 Latency343.51 ms56.94 ms~6× faster
P90 Latency478.56 ms203.10 ms~2.4× faster
P99 Latency900.43 ms239.24 ms~3.7× faster
Total Requests (6 s)4331058~2.4× more requests handled
Transfer/sec102.19 KB/s121.62 KB/s~1.2× higher throughput
Timeouts280No 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.

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


Be the first to comment.

Back to Top

Message Sent!

If you have more details or questions, you can reply to the received confirmation email.

Back to Home

OpenSwoole Magento Integration