คอร์เรล

คอร์เรลคือกลไกการทำงานพร้อมกันระดับผู้ใช้ที่มีน้ำหนักเบากว่เธรด ที่สามารถทำให้เกิดการจัดสรรงานหลายงานในกระบวนการเดียว โดยมีการควบคุมการหยุดและเรียกคืนสัญญาณเพื่อทำการสลับระหว่างคอร์เรล ซึ่งช่วยหลีกเลี่ยงค่าใช้จ่ายจากการเปลี่ยนบริบทของกระบวนการ
workerman มีอินเทอร์เฟซคอร์เรลที่ใช้งานได้ทั่วไป ซึ่งอยู่ภายใต้การรองรับโดยอัตโนมัติของไดรเวอร์ Swoole/Swow/Fiber

คำแนะนำ
ฟีเจอร์นี้ต้องใช้ workerman>=5.1.0

หมายเหตุ

  • คอร์เรลรองรับเฉพาะไดรเวอร์ Swoole Swow Fiber
  • หากใช้ไดรเวอร์ Fiber ต้องติดตั้ง composer require revolt/event-loop
  • ไดรเวอร์ Swoole หรือ Swow สามารถทำให้ฟังก์ชันบล็อก PHP ถูกคอร์เรลอัตโนมัติ ซึ่งทำให้โค้ดที่ซิงโครนัสเดิมสามารถทำงานแบบอะซิงโครนัสได้
  • แต่ Fiber ไม่สามารถทำการคอร์เรลอัตโนมัติเหมือนกับ Swoole และ Swow ได้ เมื่อมีฟังก์ชันบล็อกที่มาจาก PHP จะบล็อกทั้งกระบวนการและไม่เกิดการสลับคอร์เรล
  • เมื่อใช้ไดรเวอร์ Swoole Swow Fiber workerman จะสร้างคอร์เรลใหม่โดยอัตโนมัติเมื่อเรียกใช้งาน callback เช่น onWorkerStart onMessage onConnect onClose เป็นต้น
  • สามารถใช้ $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 แบบบล็อก