Swoole changes the traditional PHP execution model by introducing persistent workers, coroutines, asynchronous I/O, timers, WebSockets, and event-driven capabilities.

PHP has powered the web for decades, but modern applications increasingly demand more concurrent connections, lower latency, real-time communication, and efficient use of infrastructure. Swoole changes the traditional PHP execution model by introducing persistent workers, coroutines, asynchronous I/O, timers, WebSockets, and event-driven capabilities. The result is a PHP runtime that can behave much more like a modern high-concurrency application platform. But moving to Swoole is not simply a matter of enabling an extension. Long-lived workers change how developers must think about state, memory, database connections, concurrency, and application lifecycle. This guide explains how to use Swoole and asynchronous I/O effectively without turning a familiar PHP application into a difficult-to-debug distributed state machine.
The traditional PHP request model is intentionally simple.
A simplified architecture looks like:
HTTP Request
↓
Web Server
↓
PHP Worker
↓
Application
↓
ResponseIn common PHP-FPM deployments, application state is largely isolated between requests.
That model is excellent for many workloads.
But applications have changed.
Modern systems increasingly need to support:
High request concurrency
Real-time updates
Long-lived connections
Frequent external API calls
High-throughput APIs
Background processing
WebSockets
Large numbers of simultaneous users
Consider an API that needs to call three independent services:
Request
├──→ Service A
├──→ Service B
└──→ Service CIf each call blocks the worker sequentially:
A → 100 ms
B → 150 ms
C → 120 ms
Total ≈ 370 msWith suitable concurrent I/O:
A ───── 100 ms
B ───────── 150 ms
C ─────── 120 ms
Total ≈ 150 msThe CPU did not become faster.
The application simply stopped wasting time waiting for independent I/O operations.
That is where asynchronous programming becomes interesting.
Swoole extends PHP with capabilities for:
Coroutines
Asynchronous I/O
Timers
WebSockets
TCP/UDP servers
Long-running workers
Concurrent task execution
The architecture becomes:
Incoming Connections
↓
Swoole Server
↓
Persistent Workers
↓
Coroutines
↓
Non-Blocking I/OInstead of repeatedly starting and tearing down application execution, the runtime can keep workers alive.
This removes some of the overhead associated with traditional request execution.
But there is a major architectural consequence:
Your PHP application is no longer necessarily rebuilt from scratch for every request.
That changes everything from global variables to static caches and database connections.
Traditional PHP encourages a relatively short lifecycle:
Request
↓
Initialize
↓
Execute
↓
Cleanup
↓
EndA Swoole worker can behave more like:
Worker
│
├── Request
├── Request
├── Request
├── Request
└── RequestMemory and objects can survive between requests.
That creates opportunities for:
Connection reuse
In-memory caching
Reduced initialization overhead
But it also introduces risks:
Memory leaks
Stale state
Shared mutable state
Incorrect singleton assumptions
Resources that are never released
A variable that was harmless in traditional PHP can become a production bug in a long-running worker.
Coroutines are one of Swoole's most important capabilities.
The basic idea is:
Allow execution to pause while waiting for I/O so other work can continue.
Imagine:
Coroutine A
↓
Database Query
↓
Waiting...
Coroutine B
↓
API Request
↓
Waiting...
Coroutine C
↓
Cache Lookup
↓
Waiting...Instead of blocking the entire worker while one operation waits, other coroutines can make progress.
Conceptually:
Worker
│
├── Coroutine 1 ── I/O ──┐
├── Coroutine 2 ── I/O ──┤
├── Coroutine 3 ── CPU ──┤
└── Coroutine 4 ── I/O ──┘
↓
ContinueThis can dramatically increase concurrency for I/O-heavy applications.
But concurrency does not make CPU-bound work disappear.
If the application spends most of its time performing heavy computation, asynchronous I/O may provide little benefit.
The biggest performance gains generally appear when the workload spends substantial time waiting.
Good candidates include:
HTTP APIs
Database access
Cache operations
Message brokers
External services
Network services
Consider a customer dashboard:
Dashboard Request
│
┌────┼─────────┐
▼ ▼ ▼
Orders Profile NotificationsIf those operations are independent, they can potentially execute concurrently.
Instead of:
Orders
↓
Profile
↓
Notificationsthe system can coordinate:
Orders ──────────┐
Profile ─────────┼──→ Response
Notifications ───┘This can reduce end-to-end latency.
But concurrency should be intentional.
Launching hundreds of simultaneous database queries because "async is faster" can simply move the bottleneck from PHP into the database.
Database access is often the most important consideration.
A typical application might need:
Request
├── User Data
├── Order Data
├── Recommendations
└── NotificationsIf these queries are independent, concurrent execution may reduce waiting time.
But database connection pools still have limits.
For example:
100 Coroutines
↓
20 DB Connections
↓
DatabaseThe application must manage concurrency relative to downstream capacity.
Otherwise:
More Coroutines
↓
More Queries
↓
Database Saturation
↓
Higher Latency
↓
TimeoutsAsynchronous architecture does not eliminate resource constraints.
It makes them easier to reach.
Not every operation should be concurrent.
For example:
Validate Payment
↓
Charge Payment
↓
Create OrderThese steps may have dependencies.
Running them simultaneously could produce incorrect behavior.
A better approach is to identify:
Can safely execute concurrently.
Must execute sequentially.
May need workers or separate processing strategies.
The goal is not maximum concurrency.
It is:
The right amount of concurrency for the workload and its dependencies.
This is one of the biggest differences between traditional PHP and Swoole.
Suppose an application accidentally stores data in a static array:
static $cache = [];
$cache[] = $largeObject;In a short-lived process, the memory may disappear when the request ends.
In a persistent worker:
Request 1 → Memory +5 MB
Request 2 → Memory +5 MB
Request 3 → Memory +5 MB
Request 4 → Memory +5 MBEventually:
Memory Pressure
↓
Worker Degradation
↓
Potential FailureTherefore, long-running PHP applications need explicit attention to:
Object lifecycle
Caches
Static variables
Global state
Buffers
Temporary resources
Database connections
File handles
Monitoring memory over time becomes essential.
A good Swoole application should still behave as though requests are logically isolated.
Avoid accidental state sharing:
Request A
↓
Global Mutable State
↑
Request BThat can produce subtle bugs.
For example:
User A
↓
Sets Current User
↓
Shared State
User B
↓
Reads Current UserThe second request could see state that belongs to the first.
The safest principle is:
Keep request-specific data request-scoped.
Long-lived state should be intentional, immutable where possible, and carefully synchronized when it must be shared.
A Swoole-powered API can be structured like:
Clients
│
▼
Load Balancer
│
▼
Swoole Workers
┌─┼────┬────┐
▼ ▼ ▼ ▼
C1 C2 C3 C4
│ │ │ │
└─┴────┴────┘
│
▼
Databases / APIs / CacheThe application can maintain persistent workers while coroutines handle concurrent I/O.
For high-volume APIs, this can improve:
Throughput
Latency
Connection utilization
Resource efficiency
But benchmarking should always compare against the existing PHP-FPM architecture.
A faster runtime is valuable only if the actual workload benefits.
Traditional request/response applications are not ideal for persistent connections.
A WebSocket architecture looks more like:
Browser
│
│ Persistent Connection
▼
Swoole WebSocket Server
│
├── User A
├── User B
├── User C
└── User DThis makes Swoole attractive for applications such as:
Live dashboards
Chat
Collaborative applications
Real-time notifications
Trading interfaces
Multiplayer systems
Operational monitoring
The challenge is that persistent connections create a different scaling model.
A server may have thousands of open connections even when CPU utilization is relatively low.
That requires careful attention to:
Connection limits
Memory usage
Heartbeat mechanisms
Load balancing
Connection distribution
Backpressure
One Swoole server is still one server.
For production workloads, scale horizontally:
Load Balancer
/ | \
▼ ▼ ▼
Swoole 1 Swoole 2 Swoole 3
│ │ │
└───────┼───────┘
▼
Shared ServicesShared state should generally live outside individual workers.
For example:
Swoole Instances
│
┌────┼───────────┐
▼ ▼ ▼
Redis Database Message BusThis allows any application instance to handle a request without depending on state stored in another process.
Persistent connections introduce an additional issue.
Suppose:
User A → Server 1
User B → Server 2If User A sends an event that needs to reach User B, Server 1 needs a way to communicate with Server 2.
A shared messaging layer can help:
Server 1
│
▼
Message Bus
│
▼
Server 2
│
▼
User BThis decouples connection ownership from message distribution.
It is a common pattern for scaling real-time systems beyond a single server.
Not every operation belongs inside an HTTP request.
For example:
User Request
↓
Create Job
↓
Return ResponseThen:
Queue
↓
Worker
↓
Generate Report
↓
Send NotificationGood candidates include:
Report generation
Image processing
Email delivery
Large exports
Data synchronization
Heavy computation
This keeps interactive requests responsive.
Swoole can provide concurrency primitives, but larger workloads may still benefit from dedicated queue and worker architectures.
Long-running asynchronous applications need strong observability.
Track:
Request latency
Throughput
Error rate
Worker memory
Coroutine activity
Database latency
External API latency
Connection counts
Queue depth
WebSocket connections
A useful model is:
Request
↓
Trace
├── Database
├── External API
├── Cache
└── Message QueueDistributed tracing becomes particularly useful when one request launches multiple concurrent operations.
You want to know:
Which dependency actually caused the latency?
not simply:
"The endpoint is slow."
Asynchronous systems can generate work faster than downstream systems can consume it.
For example:
10,000 Requests
↓
Swoole
↓
Database
↓
Capacity = 1,000Without limits, the application can overwhelm its dependencies.
Use mechanisms such as:
Concurrency limits
Connection pools
Queues
Timeouts
Rate limiting
Circuit breakers
Backpressure
The goal is controlled degradation rather than cascading failure.
Long-lived workers change the lifecycle.
State can survive between requests.
A small leak becomes much more serious in a persistent process.
More coroutines can overwhelm databases and APIs.
A synchronous CPU-heavy or blocking operation can reduce the benefit of asynchronous architecture.
Persistent database and network connections require careful management.
CPU-bound workloads may not improve.
Multiple instances require deliberate handling of sessions, caches, queues, and real-time events.
A modern high-concurrency PHP platform might look like:
Clients
│
▼
Load Balancer
│
┌─────────────┼─────────────┐
▼ ▼ ▼
Swoole App 1 Swoole App 2 Swoole App 3
│ │ │
└─────────────┼─────────────┘
│
┌─────────┼─────────┐
▼ ▼ ▼
Redis Database Message Bus
│
▼
External ServicesInside each Swoole application:
Request
↓
Controller
↓
Business Logic
↓
Coroutine Scheduler
├── DB
├── Cache
└── External APIThis architecture separates:
HTTP handling
Concurrency
Business logic
State
Persistence
Background work
That separation is what makes the platform maintainable at scale.
Swoole can be particularly valuable when the application has:
High concurrent traffic
I/O-heavy workloads
Real-time requirements
Long-lived connections
Frequent external service calls
High API throughput requirements
It is less compelling when the workload is:
Small
Low traffic
Primarily CPU-bound
Simple CRUD
Already performing well with PHP-FPM
The migration has an operational cost.
Do it when the performance or concurrency requirements justify the architectural change.
Before moving a PHP application to Swoole, ask:
Where is the application actually spending its time?
Is the bottleneck CPU, database, network I/O, or PHP startup overhead?
Which operations can safely execute concurrently?
What state currently exists between requests?
Will long-lived workers expose hidden memory leaks?
How many concurrent connections can downstream systems support?
How will sessions and shared state work across instances?
Do we need WebSockets or persistent connections?Most importantly:
Will asynchronous execution solve a measured bottleneck, or are we introducing complexity without a clear performance requirement?
That should be answered before changing the runtime.
Scaling PHP with Swoole is not simply about making PHP faster.
It is about changing how PHP handles work.
The architecture evolves from:
Request
↓
Process
↓
Execute
↓
Exittoward:
Persistent Worker
↓
Concurrent Coroutines
↓
Non-Blocking I/O
↓
High-Concurrency ApplicationThe biggest opportunities come from I/O-heavy workloads where the application spends significant time waiting.
But the same capabilities introduce new responsibilities.
You must design for:
Request isolation
Memory lifecycle
Concurrency limits
Connection management
Backpressure
Shared state
Observability
Failure handling
Horizontal scaling
The goal is not to make every PHP operation asynchronous. It is to keep valuable compute resources working while the application waits for I/O—and to do that without overwhelming the systems downstream.
Swoole provides the runtime capabilities.
Good architecture determines where concurrency belongs.
Strong observability tells you whether it is working.
And disciplined resource management keeps long-lived workers healthy.
When used for the right workload, Swoole can turn PHP from a request-oriented runtime into a capable high-concurrency platform for APIs, real-time systems, and I/O-heavy applications. The winning strategy is not "async everywhere." It is measured concurrency, explicit ownership, controlled resource usage, and an architecture designed around how the application actually spends its time.
We build custom software, mobile apps, and web platforms for startups and enterprises.



Their team became an extension of ours — within months they'd rebuilt our entire product experience from the ground up.
