เกตเวย์ API และการค้นพบบริการ
กำหนดเส้นทาง รวมข้อมูล และค้นหาบริการแบบไดนามิก
เกตเวย์ API และการค้นพบบริการ เป็นบทเรียน PHP Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน PHP Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
เกตเวย์และการค้นหาบริการ
เมื่อคุณมีบริการสักสิบกว่าบริการ ปัญหาสองอย่างจะเริ่มปรากฏขึ้น ไคลเอ็นต์ไม่ควรต้องรู้ที่อยู่ของแต่ละบริการ หรือเรียกใช้ถึงห้าบริการเพื่อแสดงผลหน้าจอเดียว นั่นคือหน้าที่ของ เกตเวย์ API และบริการที่เพิ่มหรือลดขนาดรวมถึงเปลี่ยน IP ต้องมีวิธีค้นหากันเอง นั่นคือ การค้นหาบริการ
บทเรียนนี้ครอบคลุมทั้งสองเรื่อง โดยใช้ PHP ที่ขอบระบบเกตเวย์
เกตเวย์ API ทำหน้าที่อะไร
เกตเวย์ API คือจุดทางเข้าจุดเดียวที่อยู่หน้าบริการของคุณ โดยทั่วไปมีหน้าที่ดังนี้:
- การกำหนดเส้นทางคำขอไปยังแบ็กเอนด์ที่ถูกต้อง
- ประเด็นร่วมข้ามบริการ: การยืนยันตัวตน การจำกัดอัตรา CORS และการยุติการเชื่อมต่อ TLS
- การรวมข้อมูล: ประกอบการเรียกใช้แบ็กเอนด์หลายรายการให้เป็นการตอบกลับเดียวแก่ไคลเอ็นต์
- การแปลงโพรโทคอล: รับ REST จากภายนอกและส่ง gRPC ออกภายใน
เกตเวย์ช่วยให้ไคลเอ็นต์เรียบง่าย และรวมศูนย์นโยบายที่ไม่เช่นนั้นคุณจะต้องทำซ้ำในทุกบริการ
การกำหนดเส้นทางที่ขอบระบบ
โดยแก่นแล้ว เกตเวย์จะแปลงเส้นทางขาเข้าให้เป็นบริการต้นทาง ระบบเกตเวย์ในงานจริง (Kong, Traefik, Nginx, AWS API Gateway) ทำเช่นนี้ด้วยการกำหนดแบบประกาศ แต่ตรรกะนั้นเรียบง่ายพอที่จะสาธิตด้วย PHP ได้
<?php
$routes = [
'#^/api/orders#' => 'http://orders-svc',
'#^/api/customers#' => 'http://customers-svc',
'#^/api/catalog#' => 'http://catalog-svc',
];
function resolveUpstream(string $path, array $routes): ?string {
foreach ($routes as $pattern => $upstream) {
if (preg_match($pattern, $path)) {
return $upstream . $path;
}
}
return null; // 404 at the gateway
}
echo resolveUpstream('/api/orders/42', $routes), "\n";รวมศูนย์การยืนยันตัวตน
ตรวจสอบผู้เรียกใช้ครั้งเดียวที่เกตเวย์ จากนั้นส่งต่อข้อมูลระบุตัวตนที่เชื่อถือได้ไปยังบริการปลายทาง เพื่อให้แต่ละบริการไม่ต้องตรวจสอบโทเค็นต้นฉบับซ้ำ เกตเวย์จะตรวจสอบลายเซ็น/วันหมดอายุของ JWT และเพิ่มส่วนหัวอย่าง X-User-Id ลงในคำขอภายใน (ผ่านเครือข่ายที่เชื่อถือได้)
<?php
function authenticate(string $authHeader): ?array {
if (!str_starts_with($authHeader, 'Bearer ')) return null;
$jwt = substr($authHeader, 7);
$claims = verifyJwt($jwt); // signature + exp check
if ($claims === null) return null;
// Forward minimal trusted identity to internal services
return ['X-User-Id' => $claims['sub'], 'X-Scopes' => implode(',', $claims['scopes'])];
}
function verifyJwt(string $j): ?array { return ['sub' => 'u-7', 'scopes' => ['orders:read']]; }
print_r(authenticate('Bearer abc.def.ghi'));การรวมผลลัพธ์
หน้าจอมือถืออาจต้องใช้ข้อมูลคำสั่งซื้อ ลูกค้า และแค็ตตาล็อก แทนที่จะให้ไคลเอ็นต์เรียกสามครั้ง เกตเวย์จะกระจายคำขอออกไปรอผลแล้วรวมผลลัพธ์ เพื่อให้ทำงานเร็ว ให้ส่งคำขอไปยังบริการต้นทาง พร้อมกัน (พรอมิสของ Guzzle / curl_multi) แทนการส่งตามลำดับ
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Promise\Utils;
$http = new Client(['timeout' => 2.0]);
$promises = [
'order' => $http->getAsync('http://orders-svc/orders/42'),
'customer' => $http->getAsync('http://customers-svc/customers/7'),
'catalog' => $http->getAsync('http://catalog-svc/items?order=42'),
];
$results = Utils::settle($promises)->wait(); // run in parallel
// merge the fulfilled bodies into one response for the clientแบ็กเอนด์สำหรับส่วนหน้า (BFF)
เกตเวย์ทั่วไปเพียงตัวเดียวมักให้บริการแอปเว็บ แอปมือถือ และพาร์ตเนอร์ได้ดีเท่าเทียมกันไม่ได้ เพราะแต่ละประเภทต้องการการรวมข้อมูลและรูปแบบข้อมูลที่แตกต่างกัน รูปแบบ แบ็กเอนด์สำหรับส่วนหน้า (BFF) จะจัดให้ไคลเอ็นต์แต่ละประเภทมีเกตเวย์ขนาดเล็กของตนเอง ซึ่งปรับให้เหมาะกับความต้องการ ขณะที่บริการที่ใช้ร่วมกันยังคงเป็นแบบทั่วไป
วิธีนี้ช่วยหลีกเลี่ยงเกตเวย์ขนาดใหญ่ที่รวมทุกอย่างไว้ และเปิดให้ทีมของแต่ละไคลเอ็นต์พัฒนาได้อย่างอิสระ
ปัญหาการค้นหาบริการ
ในสภาพแวดล้อมแบบไดนามิก อินสแตนซ์ต่าง ๆ เกิดขึ้นและหายไป และที่อยู่ IP ของอินสแตนซ์ก็เปลี่ยนแปลง การเขียนโค้ดกำหนด http://10.0.3.14:8080 ไว้ตายตัวนั้นเปราะบาง การค้นหาบริการจะรักษารีจิสทรีที่อัปเดตอยู่เสมอว่า "ขณะนี้มีอินสแตนซ์ที่ทำงานปกติของบริการ X ใดบ้าง" เพื่อให้ผู้เรียกใช้แปลงชื่อเชิงตรรกะเป็นที่อยู่จริงขณะเรียกใช้
การค้นหาฝั่งไคลเอ็นต์เทียบกับฝั่งเซิร์ฟเวอร์
มีสองรูปแบบ:
- ฝั่งไคลเอ็นต์ — ผู้เรียกใช้สอบถามรีจิสทรี (Consul, etcd) แล้วเลือกอินสแตนซ์เอง พร้อมทำการกระจายโหลดเอง
- ฝั่งเซิร์ฟเวอร์ — ผู้เรียกใช้ส่งคำขอไปยังที่อยู่เสมือนที่คงที่ (ตัวกระจายโหลด / บริการของ Kubernetes) ซึ่งเป็นผู้แปลงที่อยู่และกระจายโหลดให้
ใน Kubernetes โดยทั่วไปคุณจะได้การค้นหาฝั่งเซิร์ฟเวอร์มาโดยไม่ต้องทำอะไรเพิ่ม เพียงเรียกใช้ http://customers-svc แล้ว DNS ของคลัสเตอร์กับบริการจะจัดการส่วนที่เหลือให้ นอก Kubernetes รีจิสทรีแบบ Consul เป็นที่นิยม
การสอบถามรีจิสทรี
เมื่อใช้การค้นหาฝั่งไคลเอ็นต์ ผู้เรียกใช้ที่เขียนด้วย PHP จะขออินสแตนซ์ที่ทำงานปกติจากรีจิสทรี แล้วเลือกหนึ่งรายการ รีจิสทรีจะส่งคืนเฉพาะอินสแตนซ์ที่ผ่านการตรวจสอบสุขภาพ ดังนั้นโหนดที่หยุดทำงานจะถูกตัดออกโดยอัตโนมัติ
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
function discover(Client $http, string $service): string {
// Consul: only passing-health instances
$res = $http->get("http://consul:8500/v1/health/service/$service?passing=true");
$nodes = json_decode((string) $res->getBody(), true);
if (!$nodes) throw new \RuntimeException("No healthy $service");
$pick = $nodes[array_rand($nodes)]['Service']; // simple LB
return "http://{$pick['Address']}:{$pick['Port']}";
}การตรวจสอบสุขภาพและการลงทะเบียน
การค้นหาจะมีคุณภาพได้เท่ากับข้อมูลสุขภาพของมันเท่านั้น แต่ละบริการเปิดเผยจุดปลายทาง /health ที่ตรวจสอบการพึ่งพาจริงของบริการ (ฐานข้อมูล แคช) และลงทะเบียนตัวเอง (หรือแพลตฟอร์มเป็นผู้ลงทะเบียน) เมื่อเริ่มต้นทำงาน รีจิสทรีจะตรวจสอบจุดปลายทางนั้นและนำอินสแตนซ์ที่ไม่ผ่านออก
ทำให้การตรวจสอบสุขภาพมีความหมาย: การส่งคืน 200 ขณะที่ฐานข้อมูลหยุดทำงานนั้นแย่กว่าการไม่มีประโยชน์ เพราะจะส่งทราฟฟิกไปยังโหนดที่เสีย
<?php
// GET /health
function health(PDO $db, Redis $cache): array {
$checks = [
'db' => safe(fn() => $db->query('SELECT 1') !== false),
'cache' => safe(fn() => $cache->ping() === '+PONG'),
];
$ok = !in_array(false, $checks, true);
http_response_code($ok ? 200 : 503);
return ['status' => $ok ? 'pass' : 'fail', 'checks' => $checks];
}
function safe(callable $c): bool { try { return (bool) $c(); } catch (\Throwable) { return false; } }สถานะการทำงานเทียบกับความพร้อม
มีจุดปลายทางตรวจสุขภาพเพียงจุดเดียวไม่พอ ต้องแยกคำถามสองข้อ:
- การมีชีวิต: "กระบวนการยังทำงานอยู่หรือไม่" หากไม่ผ่าน ออร์เคสเตรเตอร์จะเริ่มต้นคอนเทนเนอร์ใหม่ ควรให้การตรวจสอบนี้ใช้ทรัพยากรน้อยและไม่พึ่งพาบริการอื่น มิฉะนั้นฐานข้อมูลที่ไม่เสถียรจะทำให้เริ่มต้นใหม่โดยไม่จำเป็น
- ความพร้อม: "ขณะนี้สามารถให้บริการทราฟฟิกได้หรือไม่" หากไม่ผ่าน ทราฟฟิกจะถูกระงับไว้ แต่กระบวนการยังทำงานต่อ (เช่น กำลังอุ่นแคช หรือฐานข้อมูลไม่สามารถเข้าถึงได้ชั่วคราว)
การรวมสองสถานะนี้เป็นเรื่องเดียวกันทำให้เกิดลูปการเริ่มต้นใหม่ หรือส่งเส้นทางไปยังโหนดที่ยังไม่พร้อม
<?php
// GET /livez - is the process itself healthy? (no external deps)
function livez(): void { http_response_code(200); echo 'alive'; }
// GET /readyz - should we receive traffic? (checks dependencies)
function readyz(PDO $db): void {
try { $db->query('SELECT 1'); http_response_code(200); echo 'ready'; }
catch (\Throwable) { http_response_code(503); echo 'not ready'; }
}ตรวจสอบความเข้าใจ
ลดจำนวนครั้งที่ไคลเอ็นต์ต้องเดินทางไปกลับ
สรุป
การกำหนดเส้นทางและค้นหาบริการ:
- เกตเวย์ APIรวมศูนย์การกำหนดเส้นทาง การยืนยันตัวตน การจำกัดอัตรา TLS และการรวมข้อมูล
- รวมข้อมูลพร้อมกัน; ใช้ BFF เมื่อความต้องการของไคลเอ็นต์แตกต่างกัน
- การค้นหาบริการแปลงชื่อเชิงตรรกะเป็นอินสแตนซ์ที่ทำงานอยู่และมีสุขภาพดี
- การค้นหาฝั่งไคลเอ็นต์ (สอบถามรีจิสทรี) เทียบกับฝั่งเซิร์ฟเวอร์ (LB ที่คงที่ / บริการของ Kubernetes)
- การตรวจสอบสุขภาพที่มีความหมายช่วยกันทราฟฟิกออกจากโหนดที่เสีย
ถัดไป: ทำให้การเรียกใช้ทั้งหมดนี้ทนทาน แม้ว่าส่วนต่าง ๆ จะล้มเหลวอย่างหลีกเลี่ยงไม่ได้
คำถามที่พบบ่อย
บทเรียน “เกตเวย์ API และการค้นพบบริการ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “เกตเวย์ API และการค้นพบบริการ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส PHP Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “เกตเวย์ API และการค้นพบบริการ”
กำหนดเส้นทาง รวมข้อมูล และค้นหาบริการแบบไดนามิก คุณปฏิบัติ PHP Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน PHP Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน PHP Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “เกตเวย์ API และการค้นพบบริการ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน PHP Academy นี้ได้ไหม
ได้ บทเรียน PHP Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- จากโมโนลิทสู่ไมโครเซอร์วิส
- การสื่อสารระหว่างบริการ: REST และ gRPC
- เกตเวย์ API และการค้นพบบริการ
- ความทนทาน: ตัวตัดวงจรและการลองใหม่