PHP Academy · บทเรียน

เซิร์ฟเวอร์ประสิทธิภาพสูงด้วย Swoole

ให้บริการการเชื่อมต่อหลายพันรายการด้วย Swoole ที่ทำงานบนคอร์รูทีน

บทเรียน 4 จาก 413 ขั้นตอน

เซิร์ฟเวอร์ประสิทธิภาพสูงด้วย Swoole เป็นบทเรียน PHP Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน PHP Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน

Swoole คืออะไร

Swoole (และส่วนแยกสาขาอย่าง OpenSwoole) คือส่วนขยายภาษา C ที่เปลี่ยน PHP ให้เป็นรันไทม์ประสิทธิภาพสูง ซึ่งขับเคลื่อนด้วยโครูทีนและเหตุการณ์ แทนที่ PHP-FPM จะสร้างกระบวนการใหม่สำหรับแต่ละคำขอ เซิร์ฟเวอร์ Swoole จะ เริ่มต้นแอปของคุณเพียงครั้งเดียว และคงแอปไว้ในหน่วยความจำ เพื่อให้กลุ่มเวิร์กเกอร์ที่ทำงานต่อเนื่องรับคำขอ โดยมีโครูทีนในตัว ผลลัพธ์คือปริมาณงานเพิ่มขึ้นหลายเท่าตัว และรองรับการทำงานพร้อมกันภายในคำขอเดียวโดยตรง

เซิร์ฟเวอร์ HTTP ที่ทำงานต่อเนื่อง

ตัวอย่างคลาสสิกคือเซิร์ฟเวอร์ HTTP แบบเต็มรูปแบบในสคริปต์หนึ่งไฟล์ เซิร์ฟเวอร์จะรับฟังคำขอ ส่งคำขอแต่ละรายการไปยังเวิร์กเกอร์ และไม่เริ่มต้นเฟรมเวิร์กใหม่อีก โปรดทราบว่าต้องใช้ ext-swoole — ไม่สามารถทำงานบน PHP CLI มาตรฐานได้

<?php
$server = new Swoole\Http\Server('0.0.0.0', 9501);
$server->set(['worker_num' => 4]);

$server->on('request', function ($request, $response) {
    $response->header('Content-Type', 'application/json');
    $response->end(json_encode(['hello' => $request->server['request_uri']]));
});

echo "listening on :9501\n";
$server->start();

โครูทีน ไม่ใช่คอลแบ็ก

คุณสมบัติเด่นของ Swoole คือ โครูทีน: คุณสามารถเขียนโค้ดแบบลำดับที่ดูเหมือนบล็อกได้ โดยรันไทม์จะจัดตารางการทำงานแบบร่วมมือกัน Coroutine\run() จะเปิดบริบทโครูทีน ส่วน go() จะเปิดใช้โครูทีนใหม่ โครูทีนแต่ละตัวมีสแตกของตนเองเช่นเดียวกับไฟเบอร์ แต่ Swoole จะจัดตารางให้โดยอัตโนมัติ ณ จุดรับส่งข้อมูล

<?php
use Swoole\Coroutine as Co;

Co\run(function () {
    go(function () { Co::sleep(1); echo "A done\n"; });
    go(function () { Co::sleep(1); echo "B done\n"; });
    // Both sleep concurrently -> total ~1s, not 2s
});

ฟังก์ชันแบบบล็อกที่ถูกดักจับ

กลไกที่ทำให้โครูทีนใช้งานได้สะดวกคือ ฮุกของรันไทม์ Swoole จะเขียนการเรียกแบบบล็อกใหม่อย่างโปร่งใส — PDO, Redis, curl, sleep, file_get_contents — ให้เป็นเวอร์ชันที่ไม่บล็อกและรองรับโครูทีน การเรียกใช้ $pdo->query() ตามปกติจะคืนการควบคุมให้โครูทีน แทนที่จะทำให้เวิร์กเกอร์หยุดนิ่ง

<?php
use Swoole\Runtime;
use Swoole\Coroutine as Co;

Runtime::enableCoroutine(SWOOLE_HOOK_ALL); // hook PDO, curl, sleep, etc.

Co\run(function () {
    go(function () { sleep(1); echo "task 1\n"; }); // sleep() is now async!
    go(function () { sleep(1); echo "task 2\n"; });
}); // finishes in ~1s because sleep was hooked

การรับส่งข้อมูลพร้อมกันด้วย WaitGroup

สำหรับการกระจายงานออกไปและรอรวมผล Swoole มี WaitGroup (คล้ายกับ sync.WaitGroup ของ Go) ให้เปิดใช้โครูทีน เรียก add() สำหรับแต่ละงาน เรียก done() เมื่องานเสร็จ และเรียก wait() เพื่อบล็อกจนกว่างานทั้งหมดจะเสร็จสิ้น — พร้อมกัน

<?php
use Swoole\Coroutine as Co;
use Swoole\Coroutine\WaitGroup;

Co\run(function () {
    $wg = new WaitGroup();
    $results = [];
    foreach (['eu', 'us', 'asia'] as $region) {
        $wg->add();
        go(function () use ($wg, $region, &$results) {
            Co::sleep(0.5);              // simulated API call
            $results[$region] = "ok-$region";
            $wg->done();
        });
    }
    $wg->wait();                         // all three ran in parallel (~0.5s)
    var_dump($results);
});

ช่องทางสำหรับการสื่อสาร

โครูทีนประสานงานกันผ่าน ช่องทาง — คิวที่มีชนิดข้อมูลและขนาดจำกัด ซึ่งจะระงับผู้ผลิตเมื่อเต็ม และระงับผู้บริโภคเมื่อว่างเปล่า นี่คือการส่งข้อความรูปแบบ CSP ซึ่งหลีกเลี่ยงการใช้ล็อกสำหรับสถานะที่ใช้ร่วมกันโดยสิ้นเชิง

<?php
use Swoole\Coroutine as Co;
use Swoole\Coroutine\Channel;

Co\run(function () {
    $chan = new Channel(2);
    go(function () use ($chan) {
        foreach (range(1, 3) as $n) $chan->push($n);
        $chan->push(null); // sentinel
    });
    go(function () use ($chan) {
        while (($v = $chan->pop()) !== null) echo "got $v\n";
    });
});

การใช้กลุ่มการเชื่อมต่อ

เซิร์ฟเวอร์ที่ทำงานต่อเนื่องไม่ควรเปิดการเชื่อมต่อฐานข้อมูลใหม่สำหรับทุกคำขอ Swoole มี กลุ่มการเชื่อมต่อที่ปลอดภัยสำหรับโครูทีน ทำให้เวิร์กเกอร์ใช้ชุดการเชื่อมต่อจำนวนคงที่ร่วมกันได้ระหว่างโครูทีนหลายพันตัวที่ทำงานพร้อมกัน และขจัดการเปิดปิดการเชื่อมต่อที่ไม่จำเป็น

<?php
use Swoole\Database\PDOConfig;
use Swoole\Database\PDOPool;
use Swoole\Coroutine as Co;

Co\run(function () {
    $pool = new PDOPool((new PDOConfig())
        ->withHost('127.0.0.1')->withDbname('app')
        ->withUsername('u')->withPassword('p'), 8);

    go(function () use ($pool) {
        $pdo = $pool->get();           // borrow
        $row = $pdo->query('SELECT NOW()')->fetch();
        $pool->put($pdo);              // return to pool
        var_dump($row);
    });
});

การรั่วไหลของสถานะ: กับดักหน่วยความจำถาวร

เนื่องจากกระบวนการทำงานต่อเนื่องข้ามคำขอ สถานะส่วนกลางและสถานะคงที่จะยังคงอยู่ แคชแบบคงที่ที่เติบโตไม่จำกัด ซิงเกิลตันที่เก็บข้อมูลเฉพาะคำขอ หรือธุรกรรมฐานข้อมูลที่รั่วไหล จะทำให้คำขอภายหลังเสียหายและใช้หน่วยความจำจนหมด กฎสำคัญคือ หลีกเลี่ยงการเก็บข้อมูลเฉพาะคำขอไว้ในสถานะแบบคงที่หรือซิงเกิลตัน รีเซ็ตบริบทสำหรับแต่ละคำขอ และตั้งค่า max_request เพื่อให้เวิร์กเกอร์เริ่มต้นใหม่เป็นระยะ

<?php
$server = new Swoole\Http\Server('0.0.0.0', 9501);
$server->set([
    'worker_num'  => 8,
    'max_request' => 10000, // recycle a worker after N requests to bound leaks
]);
// Never store $request data in a static property between requests!

บริบทเฉพาะโครูทีน

เนื่องจากเวิร์กเกอร์หนึ่งตัวสลับการทำงานระหว่างโครูทีนจำนวนมาก คุณจึงไม่สามารถใช้สถานะแบบคงที่ทั่วไปสำหรับข้อมูลเฉพาะคำขอได้ Swoole จะมอบ บริบท ให้โครูทีนแต่ละตัว (Coroutine::getContext()) ซึ่งเป็นออบเจกต์ที่มีอายุการใช้งานเท่ากับโครูทีน จึงเหมาะอย่างยิ่งสำหรับผู้ใช้ปัจจุบัน รหัสคำขอ หรือแฮนเดิลธุรกรรม

<?php
use Swoole\Coroutine as Co;

Co\run(function () {
    go(function () {
        $ctx = Co::getContext();
        $ctx['request_id'] = 'req-123';
        // ... deep call chain can read Co::getContext()['request_id']
        echo Co::getContext()['request_id'], PHP_EOL;
    });
});

แบบจำลองกระบวนการและเวิร์กเกอร์งาน

Swoole ทำงานด้วยกระบวนการ หลัก (จัดการเธรดลูปเหตุการณ์) กระบวนการ ตัวจัดการ (สร้างและตรวจสอบเวิร์กเกอร์) กระบวนการ เวิร์กเกอร์ (เรียกใช้ตัวจัดการคำขอและโครูทีนของคุณ) และ เวิร์กเกอร์งานที่เป็นตัวเลือกสำหรับงานหนัก งานที่บล็อก หรือ งานที่ใช้ CPU ซึ่งถ่ายโอนไปผ่าน $server->task() ให้ใช้เวิร์กเกอร์งานสำหรับการประมวลผลภาพหรืองานที่ใช้ CPU เป็นเวลานาน เพื่อไม่ให้งานเหล่านั้นทำให้โครูทีนของคำขอหยุดชะงัก — ทำให้คุณได้ทั้งการทำงานพร้อมกันและการทำงานแบบขนาน

การเลื่อนดำเนินการและตัวจับเวลา

Swoole ช่วยให้คุณกำหนดเวลางานหลังจากโครูทีนปัจจุบันทำงานเสร็จด้วย Coroutine::defer() — เหมาะสำหรับการคืนทรัพยากรจากกลุ่มการเชื่อมต่ออย่างเชื่อถือได้ แม้จะเกิดข้อยกเว้นก็ตาม ตัวจับเวลาระดับเซิร์ฟเวอร์ (Timer::tick) จะเรียกใช้งานตามช่วงเวลา เช่น การรีเฟรชแคช ภายในเวิร์กเกอร์ได้โดยไม่ต้องใช้ตัวกำหนดเวลาภายนอก

<?php
use Swoole\Coroutine as Co;

Co\run(function () {
    go(function () {
        $conn = 'borrowed-connection';
        Co::defer(function () use ($conn) {
            echo "released $conn\n"; // runs when coroutine exits, even on error
        });
        echo "using $conn\n";
    });
});

ตรวจสอบอย่างรวดเร็ว

เหตุใดคุณจึงต้องหลีกเลี่ยงการเก็บข้อมูลคำขอไว้ในคุณสมบัติแบบคงที่ของเซิร์ฟเวอร์ Swoole

ทบทวน

Swoole เปลี่ยน PHP ให้เป็นเซิร์ฟเวอร์ที่ทำงานอยู่ในหน่วยความจำและขับเคลื่อนด้วยโครูทีน:

  • แอปจะ เริ่มต้นเพียงครั้งเดียว จากนั้นกลุ่มเวิร์กเกอร์จะให้บริการคำขอด้วย โครูทีนในตัว (Co\run/go())
  • ฮุกของรันไทม์ทำให้ PDO/curl/sleep ตามปกติไม่บล็อก ส่วน WaitGroup และ Channels ใช้ประสานงานที่ทำพร้อมกัน
  • กลุ่มการเชื่อมต่อนำการเชื่อมต่อฐานข้อมูลกลับมาใช้ซ้ำระหว่างโครูทีน
  • หน่วยความจำที่คงอยู่หมายความว่าคุณต้องหลีกเลี่ยง การรั่วไหลของสถานะ: ใช้ Coroutine::getContext() สำหรับข้อมูลเฉพาะคำขอ ตั้งค่า max_request และถ่ายโอนงานหนักไปยัง เวิร์กเกอร์งาน
เริ่มต้นได้ฟรี

เรียนรู้ PHP ด้วย AI tutor — ฟรี

เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป

คอร์ส
49
บทเรียน
195

คำถามที่พบบ่อย

บทเรียน “เซิร์ฟเวอร์ประสิทธิภาพสูงด้วย Swoole” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “เซิร์ฟเวอร์ประสิทธิภาพสูงด้วย Swoole” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส PHP Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “เซิร์ฟเวอร์ประสิทธิภาพสูงด้วย Swoole”

ให้บริการการเชื่อมต่อหลายพันรายการด้วย Swoole ที่ทำงานบนคอร์รูทีน คุณปฏิบัติ PHP Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน PHP Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน PHP Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “เซิร์ฟเวอร์ประสิทธิภาพสูงด้วย Swoole” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน PHP Academy นี้ได้ไหม

ได้ บทเรียน PHP Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. โมเดลการทำงานพร้อมกันของ PHP
  2. ไฟเบอร์ของ PHP 8.1
  3. ลูปเหตุการณ์ด้วย ReactPHP
  4. เซิร์ฟเวอร์ประสิทธิภาพสูงด้วย Swoole
← กลับไปที่ PHP Academy