การอ่านก่อนการพัฒนา

ในการพัฒนาแอปพลิเคชันด้วย Workerman คุณจำเป็นต้องเข้าใจสิ่งต่อไปนี้:

I. ความแตกต่างระหว่างการพัฒนา Workerman กับการพัฒนา PHP ปกติ

นอกจากฟังก์ชันตัวแปรที่เกี่ยวข้องกับโปรโตคอล HTTP ที่ไม่สามารถใช้งานได้โดยตรงแล้ว การพัฒนา Workerman ก็ไม่ได้มีความแตกต่างจากการพัฒนา PHP ปกติมากนัก

1. โปรโตคอลชั้นแอปพลิเคชันที่แตกต่าง

  • การพัฒนา PHP ปกติจะอิงตามโปรโตคอลชั้นแอปพลิเคชัน HTTP โดย WebServer จะช่วยนักพัฒนาทำการวิเคราะห์โปรโตคอล
  • Workerman รองรับโปรโตคอลต่างๆ ซึ่งในขณะนี้มี HTTP, WebSocket เป็นต้น Workerman แนะนำให้ใช้โปรโตคอลที่กำหนดเองที่ง่ายกว่าสำหรับการสื่อสาร

    • สำหรับการพัฒนาด้วยโปรโตคอล HTTP โปรดดูที่ Http服务部分

2. ความแตกต่างในวงรอบการขอ

  • PHP ในแอปพลิเคชันเว็บจะปล่อยตัวแปรและทรัพยากรทั้งหมดหลังจากรับคำขอ 1 ครั้ง
  • แอปพลิเคชันที่พัฒนาด้วย Workerman จะอยู่ในหน่วยความจำตลอดไปหลังจากการโหลดและการวิเคราะห์ครั้งแรก ทำให้การกำหนดคลาส, อ็อบเจ็กต์ทั่วโลก และสมาชิกสเตติกของคลาสจะไม่ถูกปล่อย อำนวยความสะดวกในการใช้งานซ้ำในภายหลัง

3. หลีกเลี่ยงการกำหนดคลาสและค่าคงที่ซ้ำ

  • เนื่องจาก Workerman จะเก็บไฟล์ PHP ที่ผ่านการคอมไพล์แล้วไว้ในแคช ดังนั้นควรหลีกเลี่ยงการ require/include ไฟล์ที่กำหนดคลาสหรือค่าคงที่ซ้ำๆ แนะนำให้ใช้ require_once/include_once ในการโหลดไฟล์

4. ระวังการปล่อยทรัพยากรการเชื่อมต่อในรูปแบบ Singleton

  • เนื่องจาก Workerman จะไม่ปล่อยอ็อบเจ็กต์ทั่วโลกและสมาชิกสเตติกของคลาสหลังจากที่มีการเรียกคำขอทุกครั้ง ในรูปแบบ Singleton เช่นฐานข้อมูล มักจะมีการเก็บอินสแตนซ์ฐานข้อมูล (ซึ่งในนั้นมีการเชื่อมต่อซ็อกเก็ตฐานข้อมูล) ไว้ในสมาชิกสเตติกของฐานข้อมูล ทำให้ Workerman ใช้การเชื่อมต่อซ็อกเก็ตฐานข้อมูลนี้ซ้ำระหว่างอายุการใช้กระบวนการ จำเป็นต้องระวังว่าเมื่อเซิร์ฟเวอร์ฐานข้อมูลตรวจพบว่าการเชื่อมต่อใดๆ ไม่มีการทำงานในช่วงเวลาหนึ่งอาจปิดการเชื่อมต่อนั้นโดยอัตโนมัติ เมื่อใช้ฐานข้อมูลอินสแตนซ์นี้อีกครั้งจะมีการแจ้งเตือนเกิดขึ้น (ข้อความผิดพลาดจะคล้ายกับ mysql gone away) Workerman มี คลาสฐานข้อมูล ที่มีฟังก์ชันการเชื่อมต่อใหม่อย่างอัตโนมัติ นักพัฒนาสามารถใช้ได้โดยตรง

5. หลีกเลี่ยงการใช้ exit, die

  • Workerman ทำงานในโหมดบรรทัดคำสั่ง PHP เมื่อเรียก exit หรือ die คำสั่งจะทำให้กระบวนการปัจจุบันหยุดทำงาน แม้ว่าหลังจากที่กระบวนการลูกหยุดทำงานมันจะสร้างกระบวนการลูกใหม่ทันทีเพื่อดำเนินการต่อ แต่ก็อาจมีผลกระทบต่อธุรกิจได้

6. หลังจากแก้ไขโค้ดต้องรีสตาร์ทเซิร์ฟเวอร์เพื่อให้มีผล

เนื่องจาก Workerman จะอยู่ในหน่วยความจำ PHP คลาสและการกำหนดฟังก์ชันจะโหลดเพียงครั้งเดียวและจะคงอยู่ในหน่วยความจำ ไม่อ่านจากดิสก์อีก ดังนั้นทุกครั้งที่มีการแก้ไขโค้ดธุรกิจจึงต้องรีสตาร์ทเพื่อให้มีผล

II. แนวคิดพื้นฐานที่จำเป็นต้องเข้าใจ

1. โปรโตคอลชั้นการส่งข้อมูล TCP

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

2. โปรโตคอลชั้นแอปพลิเคชัน

โปรโตคอลชั้นแอปพลิเคชัน (application layer protocol) กำหนดว่าโปรเซสแอปพลิเคชันที่ทำงานบนระบบปลายทางที่แตกต่างกัน (ไคลเอนต์, เซิร์ฟเวอร์) จะสื่อสารข้อมูลอย่างไร เช่น HTTP, WebSocket เป็นต้น ตย. โปรโตคอลชั้นแอปพลิเคชันที่ง่ายอาจมีลักษณะดังนี้{"module":"user","action":"getInfo","uid":456}\n" โปรโตคอลนี้จะใช้"\n" (โปรดสังเกตว่า"\n" หมายถึงการขึ้นบรรทัดใหม่) เพื่อทำเครื่องหมายสิ้นสุดคำขอ โดยเนื้อหาข้อความเป็นสตริง

3. การเชื่อมต่อแบบสั้น

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

การพัฒนาแอปพลิเคชันแบบการเชื่อมต่อสั้นสามารถดูได้จากบทที่เกี่ยวกับขั้นตอนการพัฒนาพื้นฐาน

4. การเชื่อมต่อแบบยาว

การเชื่อมต่อแบบยาวคือการส่งข้อมูลหลายแพ็กเกจในหนึ่งการเชื่อมต่อ

หมายเหตุ: การเชื่อมต่อแบบยาวจะต้องมีการตรวจสอบสถานะ มิฉะนั้นการเชื่อมต่ออาจถูกตัดโดยไฟร์วอลล์ของโหนดเราเตอร์ในระยะเวลาที่ไม่มีกิจกรรม

การเชื่อมต่อแบบยาวมักใช้เมื่อการทำงานมีการทำซ้ำบ่อย ผู้สื่อสารแบบจุดต่อจุด การเชื่อมต่อ TCP แต่ละครั้งจะต้องใช้เวลาสำหรับการจับมือสามครั้ง หากการเชื่อมต่อและการทำงานต้องทำงานในลักษณะเชื่อมต่อใหม่ทุกครั้ง ความเร็วในการประมวลผลจะลดลงมาก ดังนั้นในแต่ละขั้นตอนจะไม่ตัดการเชื่อมต่อเพื่อให้การส่งแพ็กเกจข้อมูลในขั้นตอนถัดไปไม่มีความจำเป็นต้องสร้างการเชื่อมต่อ TCP ใหม่ เช่น การเชื่อมต่อฐานข้อมูลจะใช้การเชื่อมต่อแบบยาว หากใช้การเชื่อมต่อสั้นในการสื่อสารบ่อยครั้งอาจทำให้เกิดข้อผิดพลาดเกี่ยวกับซ็อกเก็ตและการสร้างซ็อกเก็ตบ่อยครั้งก็เป็นการใช้ทรัพยากรที่สิ้นเปลือง

เมื่อจำเป็นต้องส่งข้อมูลไปยังไคลเอนต์อย่างเช่น แอปพลิเคชันการสนทนา, เกมเรียลไทม์, การแจ้งเตือนมือถือ จะต้องใช้การเชื่อมต่อแบบยาว
การพัฒนาแอปพลิเคชันที่ใช้การเชื่อมต่อแบบยาวสามารถดูได้จากขั้นตอนการพัฒนา Gateway/Worker

5. การรีสตาร์ทที่ราบรื่น

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

การรีสตาร์ทที่ราบรื่นจะไม่หยุดกระบวนการทั้งหมดในครั้งเดียว แต่จะหยุดกระบวนการแต่ละตัว ในขณะที่หยุดกระบวนการแต่ละตัวจะสร้างกระบวนการใหม่ทันทีเพื่อแทนที่ สุดท้ายจะต้องมีการแทนที่กระบวนการเก่าเรียบร้อย

การรีสตาร์ทที่ราบรื่นใน Workerman สามารถใช้คำสั่ง php your_file.php reload เพื่ออัปเดตแอปพลิเคชันโดยไม่กระทบต่อคุณภาพการบริการ

หมายเหตุ: จะมีการอัปเดตอัตโนมัติหลังจากการรีสตาร์ทที่ราบรื่นเฉพาะไฟล์ที่โหลดใน on{...} callback เท่านั้น ไฟล์ที่โหลดโดยตรงในสคริปต์เริ่มต้นหรือตัวแปรที่ถูกกำหนดไว้อย่างตายตัวจะไม่อัปเดตอัตโนมัติเมื่อรัน reload

III. การแยกระหว่างกระบวนการหลักและกระบวนการลูก

มีความสำคัญที่จะต้องทราบว่าโค้ดทำงานอยู่ในกระบวนการหลักหรือกระบวนการลูก โดยทั่วไปแล้วโค้ดที่ทำงานก่อนการเรียก Worker::runAll(); ทั้งหมดจะทำงานในกระบวนการหลัก ในขณะที่โค้ดที่ทำงานใน onXXX callback จะจัดอยู่ในกระบวนการลูก โปรดทราบว่าข้อความโค้ดที่เขียนไว้หลังจากการเรียก Worker::runAll(); จะไม่มีทางถูกดำเนินการ

ตัวอย่างโค้ดด้านล่าง

use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

// ทำงานในกระบวนการหลัก
$tcp_worker = new Worker("tcp://0.0.0.0:2347");
// การกำหนดค่าเกิดขึ้นในกระบวนการหลัก
$tcp_worker->onMessage = function(TcpConnection $connection, $data)
{
    // ส่วนนี้ทำงานในกระบวนการลูก
    $connection->send('hello ' . $data);
};

Worker::runAll();

หมายเหตุ: ไม่ควรทำการเชื่อมต่อฐานข้อมูล, memcache, redis เป็นต้น ในกระบวนการหลัก เนื่องจากการเชื่อมต่อที่ถูกสร้างในกระบวนการหลักอาจถูกกระบวนการลูกสืบทอดโดยอัตโนมัติ (โดยเฉพาะในกรณีที่ใช้รูปแบบ Singleton) ซึ่งจะทำให้ทุกกระบวนการถือการเชื่อมต่อเดียวกัน ข้อมูลที่ส่งกลับจากเซิร์ฟเวอร์ผ่านการเชื่อมต่อเดียวกันสามารถอ่านได้ในหลายกระบวนการ ซึ่งอาจทำให้เกิดความยุ่งเหยิงตามข้อมูล ในทำนองเดียวกัน หากกระบวนการใดกระบวนการหนึ่งปิดการเชื่อมต่อ (เช่นในโหมด daemon เมื่อกระบวนการหลักจะหยุดทำงานทำให้การเชื่อมต่อถูกปิด) จะทำให้การเชื่อมต่อในกระบวนการลูกทั้งหมดถูกปิดด้วยเช่นกัน และเกิดข้อผิดพลาดที่ไม่สามารถคาดการณ์ได้ เช่น ข้อผิดพลาด mysql gone away

แนะนำให้ทำการเชื่อมต่อใน onWorkerStart.