Prerequisites:
hyperf/coroutine is designed for it.php -m | grep swoole
composer require hyperf/coroutine
First Coroutine:
Create a simple coroutine in app/Command/Hello.php:
namespace App\Command;
use Hyperf\Command\Command as HyperfCommand;
use Hyperf\AsyncQueue\Driver\RedisDriver;
use Hyperf\Coroutine\Coroutine;
class Hello extends HyperfCommand
{
protected function configure()
{
$this->setName('hello')->setDescription('Test coroutine');
}
public function handle()
{
Coroutine::create(function () {
$this->output('Hello from coroutine!');
});
}
}
Run with:
php bin/hyperf.php hello
First Async I/O:
Use Hyperf\HttpClient with coroutines for non-blocking HTTP calls:
use Hyperf\HttpClient\Client;
use Hyperf\Coroutine\Coroutine;
Coroutine::create(function () {
$client = new Client();
$response = yield $client->get('https://httpbin.org/get');
$this->output($response->body());
});
Key Files to Explore:
config/autoload/coroutine.php: Coroutine pool settings.app/Listener/CoroutineListener.php: Example of coroutine event handling.vendor/hyperf/coroutine/src: Core implementation (e.g., Coroutine.php, Channel.php).Offload blocking operations (e.g., sending emails, processing files) to coroutines:
Coroutine::create(function () {
// Simulate long-running task
sleep(5);
Mail::send('emails.welcome', [], function ($message) {
$message->to('user@example.com');
});
});
Use yield to pause/resume coroutines for I/O-bound operations:
Coroutine::create(function () {
$client = new Client();
$response = yield $client->get('https://api.example.com/data');
$data = json_decode($response->body(), true);
// Process data...
});
Pass data between coroutines using Channel:
$channel = new Channel(10);
Coroutine::create(function () use ($channel) {
$channel->push('data from coroutine');
});
Coroutine::create(function () use ($channel) {
$data = yield $channel->pop();
$this->output($data);
});
Limit concurrency with CoroutinePool:
$pool = new CoroutinePool(5); // Max 5 concurrent coroutines
for ($i = 0; $i < 10; $i++) {
$pool->append(function () {
// Task logic
});
}
$pool->wait();
use Hyperf\WebSocketServer\Event;
$server->on('message', function (Event $event) {
Coroutine::create(function () use ($event) {
// Process message asynchronously
$event->send('pong');
});
});
$queue = new AsyncQueue(new RedisDriver());
$queue->push(new SendEmailTask($user));
If using Lumen + Swoole, bridge Laravel services to Hyperf coroutines:
Dependency Injection:
$container = new Container();
$container->bind('mailer', function () {
return new LaravelMailer(); // Custom wrapper
});
Coroutine::create(function () use ($container) {
$mailer = $container->make('mailer');
$mailer->send(...);
});
Middleware Adaptation: Convert Laravel middleware to Hyperf-style coroutine middleware:
$middleware = new CoroutineMiddleware(function ($request, $next) {
return Coroutine::create(function () use ($request, $next) {
return yield $next($request);
});
});
Database:
Use hyperf/db-connection for async queries:
Coroutine::create(function () {
$user = yield DB::connection()->table('users')->where('id', 1)->first();
});
Blocking the Event Loop:
file_get_contents(), sleep()) directly in coroutines without yield.Coroutine::sleep() or yield with async alternatives:
// Bad: Blocks the coroutine
sleep(1);
// Good: Non-blocking
yield Coroutine::sleep(1);
Shared State Race Conditions:
Channel or Lock for synchronization:
$lock = new Lock();
Coroutine::create(function () use ($lock) {
$lock->acquire();
// Critical section
$lock->release();
});
Uncaught Exceptions:
try-catch or use Coroutine::create() with error handling:
Coroutine::create(function () {
try {
// Risky code
} catch (\Throwable $e) {
Logger::error($e);
}
});
Resource Leaks:
Coroutine::cancel():
$coroutine = Coroutine::create(function () {
while (true) {
yield Coroutine::sleep(1);
}
});
$coroutine->cancel(); // Terminate if needed
Laravel-Specific Issues:
hyperf/db-connection or raw queries.hyperf/cache or Redis directly.EventDispatcher for async events.Stack Traces:
Coroutine stack traces are fragmented. Use Coroutine::getContext() to inspect state:
Coroutine::create(function () {
$context = Coroutine::getContext();
Logger::info('Coroutine ID:', $context['id']);
});
Logging: Log coroutine IDs for tracking:
Coroutine::create(function () {
$id = Coroutine::id();
Logger::info("Coroutine {$id} started");
// ...
Logger::info("Coroutine {$id} finished");
});
Profiling: Use Hyperf’s built-in profiler or integrate with XHProf:
Coroutine::create(function () {
$profiler = new Profiler();
$profiler->start('coroutine_task');
// Task logic
$profiler->stop('coroutine_task');
});
Common Errors:
CoroutineException: Usually indicates a blocking call. Check for sleep(), file_get_contents(), or synchronous HTTP clients.ChannelException: Likely a deadlock. Ensure producers/consumers are balanced.SwooleException: Swoole-specific issues (e.g., invalid coroutine context). Verify Swoole version.Pool Sizing:
config/autoload/coroutine.php) may need tuning:
'pool' => [
'max_coroutines' => 1000, // Adjust based on workload
'max_stack_size' => 1024 * 1024, // 1MB stack per coroutine
],
max_coroutines = 2 * CPU cores for I/O-bound tasks.Timeouts:
yield may hang. Set timeouts:
Cor
How can I help you explore Laravel packages today?