คอร์เรล
คอร์เรลคือกลไกการทำงานพร้อมกันระดับผู้ใช้ที่มีน้ำหนักเบากว่เธรด ที่สามารถทำให้เกิดการจัดสรรงานหลายงานในกระบวนการเดียว โดยมีการควบคุมการหยุดและเรียกคืนสัญญาณเพื่อทำการสลับระหว่างคอร์เรล ซึ่งช่วยหลีกเลี่ยงค่าใช้จ่ายจากการเปลี่ยนบริบทของกระบวนการ
workerman มีอินเทอร์เฟซคอร์เรลที่ใช้งานได้ทั่วไป ซึ่งอยู่ภายใต้การรองรับโดยอัตโนมัติของไดรเวอร์ Swoole/Swow/Fiber
คำแนะนำ
ฟีเจอร์นี้ต้องใช้ workerman>=5.1.0
หมายเหตุ
- คอร์เรลรองรับเฉพาะไดรเวอร์
SwooleSwowFiber - หากใช้ไดรเวอร์
Fiberต้องติดตั้งcomposer require revolt/event-loop - ไดรเวอร์
SwooleหรือSwowสามารถทำให้ฟังก์ชันบล็อก PHP ถูกคอร์เรลอัตโนมัติ ซึ่งทำให้โค้ดที่ซิงโครนัสเดิมสามารถทำงานแบบอะซิงโครนัสได้ - แต่
Fiberไม่สามารถทำการคอร์เรลอัตโนมัติเหมือนกับSwooleและSwowได้ เมื่อมีฟังก์ชันบล็อกที่มาจาก PHP จะบล็อกทั้งกระบวนการและไม่เกิดการสลับคอร์เรล - เมื่อใช้ไดรเวอร์
SwooleSwowFiberworkermanจะสร้างคอร์เรลใหม่โดยอัตโนมัติเมื่อเรียกใช้งาน callback เช่นonWorkerStartonMessageonConnectonCloseเป็นต้น - สามารถใช้
$worker->eventLoop=xxx;เพื่อกำหนดไดรเวอร์คอร์เรลที่แตกต่างกันสำหรับworkerต่าง ๆ
<?php
use Workerman\Connection\TcpConnection;
use Workerman\Coroutine;
use Workerman\Events\Swoole;
use Workerman\Events\Fiber;
use Workerman\Protocols\Http\Request;
use Workerman\Worker;
require_once __DIR__ . '/vendor/autoload.php';
$worker1 = new Worker('http://0.0.0.0:8001');
$worker1->eventLoop = Swoole::class; // ใช้คอร์เรล Swoole
$worker1->onMessage = function (TcpConnection $connection, Request $request) {
Coroutine::create(function () {
echo file_get_contents("http://www.example.com/event/notify");
});
$connection->send('ok');
};
$worker2 = new Worker('http://0.0.0.0:8001');
$worker2->eventLoop = Fiber::class; // ใช้คอร์เรล Fiber ที่มีอยู่ในตัว
$worker2->onMessage = function (TcpConnection $connection, Request $request) {
Coroutine::create(function () {
echo file_get_contents("http://www.example.com/event/notify");
});
$connection->send('ok');
};
Worker::runAll();
อินเทอร์เฟซที่คอร์เรลจัดเตรียมไว้
interface CoroutineInterface
{
/**
* สร้างคอร์เรลและทำงานทันที
*/
public static function create(callable $callable, ...$data): CoroutineInterface;
/**
* เริ่มทำงานคอร์เรล
*/
public function start(mixed ...$args): mixed;
/**
* ฟื้นฟูการทำงานของคอร์เรล
*/
public function resume(mixed ...$args): mixed;
/**
* รับ ID ของคอร์เรล
*/
public function id(): int;
/**
* ตั้งค่าคอลแบ็คเมื่อคอร์เรลถูกทำลาย
*/
public static function defer(callable $callable): void;
/**
* หยุดคอร์เรลปัจจุบัน
*/
public static function suspend(mixed $value = null): mixed;
/**
* รับคอร์เรลปัจจุบัน
*/
public static function getCurrent(): CoroutineInterface|Fiber|SwowCoroutine|static;
/**
* ตรวจสอบว่าขณะนี้อยู่ในสภาพแวดล้อมของคอร์เรลหรือไม่
*/
public static function isCoroutine(): bool;
}
เกี่ยวกับคอร์เรล
ข้อดี
การนำคอร์เรลเข้ามาช่วยทำให้สามารถเขียนโค้ดอะซิงโครนัสด้วยวิธีซิงโครนัส ซึ่งช่วยหลีกเลี่ยงปัญหาการเรียกคืนที่ซับซ้อน ทำให้การอ่านโค้ดและการบำรุงรักษาง่ายขึ้น
คอร์เรลสามารถเพิ่มความยืดหยุ่นให้กับธุรกิจที่มีการอิงค่าจำนวนการป้อนข้อมูล (IO) ได้อย่างมาก ซึ่งสามารถให้การส่งข้อมูลที่สูงกว่าด้วยกระบวนการที่น้อยลง
ข้อเสีย
แต่การนำคอร์เรลเข้ามาให้ผู้พัฒนาต้องระมัดระวังเกี่ยวกับปัญหาการปนเปื้อนของตัวแปรทั่วโลก การแข่งขันทรัพยากร การปรับแต่งไลบรารีภายนอก ฯลฯ ทำให้ต้นทุนการพัฒนาและดูแลรักษาเพิ่มมากขึ้น และภาระทางจิตใจชัดเจนมากขึ้น
การนำคอร์เรลเข้ามาทำให้เกิดค่าใช้จ่ายเพิ่มเติมในด้านการสร้าง การจัดสรร การทำลาย การจัดการการเชื่อมต่อ เป็นต้น
จากข้อมูลการทดสอบที่เข้มงวด พบว่าในกรณีที่มีการใช้ CPU อย่างเต็มที่ การนำคอร์เรลเข้ามาทำให้ประสิทธิภาพสูงสุดลดลงประมาณ 10%-20% เมื่อเปรียบเทียบกับ IO แบบบล็อก