HTTP/3 QUIC Pod Scheduling | SiamCafe Blog
HTTP/3 QUIC Pod Scheduling คืออะไร ทำไมถึงสำคัญในปี 2026
ในโลกที่ทุกอย่างต้องเร็ว การส่งข้อมูลระหว่างผู้ใช้กับเซิร์ฟเวอร์คือหัวใจของบริการออนไลน์ทุกประเภท และเมื่อพูดถึงความเร็วนี้
การจัดตารางงานของระบบคอนเทนเนอร์ที่รองรับโปรโตคอลรุ่นใหม่อย่าง เอชทีทีพี เวอร์ชันสาม ที่ทำงานบนโปรโตคอล ควิก จึงกลายเป็นเรื่องที่ทีมวิศวกรรมทั่วโลกต้องให้ความสำคัญอย่างจริงจัง ผมเองเป็นคนที่ทำงานด้านระบบเทรดมาหลายปี ระบบเทรดเราไม่รอใคร สัญญาณหนึ่งมาถึงเราต้องมองเห็นภายในมิลลิวินาที ถ้าเครือข่ายหน่วงตัวเพียงเล็กน้อย กำไรที่ควรจะได้อาจกลายเป็นขาดทุนไปเลย ประสบการณ์ตรงนี้ทำให้ผมเข้าใจดีว่าโปรโตคอลรุ่นใหม่และการจัดตารางงานที่ฉลาดขึ้นไม่ใช่เรื่องของคนทำเว็บใหญ่อย่างเดียว แต่เป็นเรื่องของทุกคนที่ต้องการให้บริการตอบสนองเร็วและเสถียร
เอชทีทีพี เวอร์ชันสาม ทำงานบนโปรโตคอล ควิก ที่ใช้ ยูดีพี เป็นฐาน แทนการใช้ ทีซีพี แบบเดิม ผลลัพธ์คือการเชื่อมต่อที่เร็วขึ้น ลดปัญหาการบล็อกการส่งข้อมูลต่อกันแบบหัวข้อเดียวติดทั้งคิว ซึ่งเป็นจุดอ่อนของเวอร์ชันก่อนหน้า แต่การที่จะใช้ประโยชน์จากโปรโตคอลนี้ได้อย่างเต็มที่ในระบบที่มีคอนเทนเนอร์หลายพันตัว ต้องอาศัยการจัดตารางงานที่ฉลาด ระบบจัดคลัสเตอร์อย่างคูเบอร์เนทีสต้องรู้ว่าโหนดไหนรองรับการเชื่อมต่อแบบ ยูดีพี ได้ดี พอร์ตไหนว่าง และจะกระจายโหลดยังไงให้ไม่มีคอนเทนเนอร์ตัวใดต้องเหน็ดเหนื่อยเกินไปจนตอบสนองช้า
ทำไมปี 2026 ถึงเป็นจุดที่เรื่องนี้สำคัญกว่าที่เคย เหตุผลมีอยู่สามข้อหลัก ข้อแรก เบราว์เซอร์หลักทุกตัวและอุปกรณ์เคลื่อนที่รุ่นใหม่รองรับ เอชทีทีพี เวอร์ชันสาม เป็นค่าเริ่มต้นแล้ว ผู้ใช้ไม่ต้องเปิดใช้เอง ระบบเราต้องพร้อมรับตั้งแต่คนเริ่มเปิดหน้าเว็บ ข้อสอง ค่าความเร็วของเว็บกลายเป็นปัจจัยจัดอันดับในเครื่องมือค้นหาอย่างชัดเจน เว็บที่เปิดช้ากว่าคู่แข่งเสียอันดับและเสียลูกค้าไปพร้อมกัน ข้อสาม งานแบบเรียลไทม์เพิ่มขึ้นทุกปี ไม่ว่าจะเป็นการเทรด การสตรีมวิดีโอ การเรียกผ่านอินเทอร์เน็ต หรือระบบสั่งซื้อสินค้าที่ต้องอัปเดตสต็อกทันที ทุกงานเหล่านี้ต้องการเครือข่ายที่หน่วงต่ำและไม่หลุดกลางคัน ซึ่งเป็นจุดแข็งของโปรโตคอล ควิก โดยตรง
จากประสบการณ์จริงของผม ช่วงสามปีที่ผ่านมาเราค่อย ๆ ย้ายระบบสัญญาณเทรดมาใช้โปรโตคอล ควิก เต็มรูปแบบ ผลที่วัดได้คือความหน่วงในการเชื่อมต่อครั้งแรกลดลงเกือบครึ่งหนึ่ง และที่สำคัญกว่านั้นคือการเชื่อมต่อไม่หลุดเวลาผู้ใช้เปลี่ยนจากไวไฟมาเป็นเน็ตมือถือ เพราะรหัสการเชื่อมต่อของ ควิก ติดตัวผู้ใช้ ไม่ได้ผูกกับหมายเลขแอดเดรสอย่าง ทีซีพี นี่คือสิ่งที่เงินซื้อไม่ได้ถ้าระบบออกแบบผิดตั้งแต่ต้น
บทความนี้ผมจะพาไปดูตั้งแต่แนวคิดพื้นฐาน การออกแบบสถาปัตยกรรม การติดตั้งจริง การเฝ้าระวังระบบ ไปจนถึงปัญหาที่เจอบ่อยและวิธีแก้ ทุกอย่างเขียนจากประสบการณ์ตรง ไม่ได้คัดลอกจากเอกสารใดเอกสารหนึ่ง หวังว่าคนที่กำลังจะนำระบบแบบนี้ไปใช้จะได้แนวทางที่ใช้งานได้จริงตั้งแต่วันแรก
เริ่มจากพื้นฐานก่อน คูเบอร์เนทีส มีตัวจัดตารางงานที่ทำหน้าที่เลือกว่าคอนเทนเนอร์แต่ละตัวควรไปอยู่ที่โหนดไหน การตัดสินใจนี้อิงจากปัจจัยหลายอย่าง เช่น
ทรัพยากรที่เหลืออยู่ ความสามารถในการรองรับเครือข่าย และเงื่อนไขที่เรากำหนดเอง ปกติตัวจัดตารางจะมองแค่ปริมาณทรัพยากร แต่เมื่อเราใช้โปรโตคอล ควิก ที่ทำงานบน ยูดีพี มีปัจจัยเพิ่มเข้ามาคือลักษณะการรับส่งแพ็กเก็ตข้อมูล
การรับส่งแบบ ยูดีพี มีความต่างจาก ทีซีพี หลายด้าน ไม่มีการจับมือก่อนเริ่มการเชื่อมต่อแบบสามขั้นตอน ไม่มีสถานะการเชื่อมต่อที่ถูกเก็บไว้ที่โหนดเดียว แต่มีรหัสการเชื่อมต่อที่ทำให้การเชื่อมต่อสามารถย้ายไปมาได้โดยไม่หลุด หมายความว่าตอนที่ตัวจัดตารางเลือกโหนดให้คอนเทนเนอร์ที่ให้บริการผ่าน ควิก ต้องคำนึงถึงความสามารถในการรับมือกับแพ็กเก็ต ยูดีพี จำนวนมาก และการกระจายรหัสการเชื่อมต่ออย่างสม่ำเสมอ
หลักการจัดตารางงานสำหรับโปรโตคอลควิก
เมื่อเข้าใจความต่างพื้นฐานแล้ว มาดูกันว่าการจัดตารางงานสำหรับโปรโตคอล ควิก ต้องคิดต่างจากเดิมตรงไหนบ้าง หัวใจของเรื่องนี้คือตัวจัดตารางต้องมองเครือข่ายเป็นทรัพยากรอย่างหนึ่ง ไม่ต่างจากหน่วยประมวลผลหรือหน่วยความจำ เดิมทีเราตั้งค่าขั้นต่ำและขีดจำกัดของหน่วยประมวลผลกับหน่วยความจำให้คอนเทนเนอร์แต่ละตัว แต่กับงานที่รับส่งข้อมูลหนักผ่าน ยูดีพี เราต้องตั้งค่าขีดจำกัดการรับส่งแพ็กเก็ตให้ชัดเจนด้วย ไม่งั้นคอนเทนเนอร์หนึ่งตัวที่ยุ่งหนักสามารถกินแบนด์วิดท์ของโหนดจนคอนเทนเนอร์ตัวอื่นในโหนดเดียวกันอดข้าวไปด้วย
ปัจจัยที่ตัวจัดตารางควรพิจารณากับงานแบบนี้มีอยู่ด้วยกันหลัก ๆ ห้าเรื่อง เรื่องแรกคือความหน่วงของเครือข่ายระหว่างโหนดกับผู้ใช้ปลายทาง เราแก้ปัญหานี้ด้วยการกระจายคอนเทนเนอร์ให้อยู่ใกล้ผู้ใช้ในแต่ละภูมิภาค เหมือนการวางจุดรับสินค้าให้ทั่วเมือง แทนที่จะมีคลังกลางเดียวอยู่ไกล ๆ เรื่องที่สองคือความสามารถของเคอร์เนลในการจัดการแพ็กเก็ต ยูดีพี จำนวนมากต่อวินาที โหนดที่ไม่ได้ปรับแต่งค่าของเคอร์เนลจะเริ่มทิ้งแพ็กเก็ตเมื่อโหลดยังไม่ถึงร้อยเปอร์เซ็นต์เสียอีก เรื่องที่สามคือการกระจายรหัสการเชื่อมต่อ เรื่องที่สี่คือการทนต่อการย้ายที่อยู่ของผู้ใช้ และเรื่องที่ห้าคือการเก็บรักษาความปลอดภัย เพราะโปรโตคอล ควิก เข้ารหัสข้อมูลในตัวเองตั้งแต่ต้น การจัดตารางต้องเผื่อทรัพยากรสำหรับงานเข้ารหัสถอดรหัสนี้ด้วย
ในทางปฏิบัติ เราสามารถใช้เทคนิคหลายอย่างเพื่อให้ตัวจัดตารางฉลาดขึ้น เช่น การกำหนดเงื่อนไขการเลือกโหนดให้พิจารณาค่าความหน่วงของเครือข่าย การใช้หมายเลขระบุลักษณะพิเศษของโหนดที่ปรับแต่งเคอร์เนลสำหรับ ยูดีพี แล้ว และการตั้งค่าความต้องการทรัพยากรเครือข่ายให้สอดคล้องกับจำนวนการเชื่อมต่อที่คาดว่าจะรับมือ นอกจากนี้ยังมีเทคนิคขั้นสูงอย่างการให้น้ำหนักคะแนนโหนดตามค่าความหน่วงที่วัดได้จริงจากระบบเฝ้าระวัง ตัวจัดตารางจะจัดคอนเทนเนอร์ลงโหนดที่มีคะแนนดีที่สุดอัตโนมัติ และเมื่อค่าความหน่วงของโหนดไหนแย่ลง คอนเทนเนอร์ใหม่จะไม่ถูกจัดลงโหนดนั้นอีกจนกว่าค่าจะกลับมาปกติ
จากประสบการณ์จริงของผม ระบบเทรดที่เราใช้ตอนนี้ต้องการความหน่วงต่ำมาก เราตั้งค่าให้คอนเทนเนอร์ที่รับสัญญาณราคาถูกจัดลงเฉพาะโหนดที่มีการ์ดเครือข่ายเร็วและปรับแต่งเคอร์เนลสำหรับ ยูดีพี แล้วเท่านั้น ส่วนงานอื่นที่ไม่ต้องการความเร็วขนาดนั้น จัดไปอยู่โหนดทั่วไปได้ วิธีนี้ทำให้เราใช้ทรัพยากรคุ้มค่า ไม่ต้องเสียเงินจ้างเซิร์ฟเวอร์ราคาแพงทั้งหมดเพื่อรองรับงานที่ต้องการความเร็วสูงแค่บางส่วน อีกตัวอย่างที่ใช้ได้ผลคือการแยกคิวงาน เราแบ่งคอนเทนเนอร์เป็นสองกลุ่ม กลุ่มแรกรับสัญญาณเทรดที่ต้องเร็วสุด ๆ กลุ่มที่สองรับงานแดชบอร์ดและรายงานย้อนหลังที่ช้ากว่าได้ เมื่อโหลดพีค เราขยายเฉพาะกลุ่มที่หน่วงเริ่มขยับ ไม่ต้องขยายทั้งระบบ ประหยัดค่าใช้จ่ายได้เยอะเลย
การออกแบบระบบที่ดีต้องคิดถึงความพร้อมใช้งานสูง การทนต่อความผิดพลาด และการขยายตัวตั้งแต่วันแรก ผมจะยกตัวอย่างโครงสร้างที่เราใช้จริงในระบบเทรด
ซึ่งปรับได้กับงานอื่นด้วย
สถาปัตยกรรมและการออกแบบระบบ
การออกแบบระบบที่ดีต้องคิดถึงความพร้อมใช้งานสูง การทนต่อความผิดพลาด และการขยายตัวตั้งแต่วันแรก ผมจะยกตัวอย่างโครงสร้างที่เราใช้จริงในระบบเทรด ซึ่งปรับได้กับงานอื่นด้วย หลักการใหญ่ของเราคือ แต่ละชั้นต้องแยกกันชัดเจน ชั้นรับการเชื่อมต่อ ชั้นประมวลผล และชั้นเก็บข้อมูล ต้องขยายหรือย้ายแยกกันได้ ไม่ผูกติดกันจนแก้ทีเดียวล้มทั้งระบบ
| ส่วนประกอบ | หน้าที่ | ข้อควรระวัง |
|---|---|---|
| โหนดรับการเชื่อมต่อ ควิก | รับการเชื่อมต่อแบบ ยูดีพี และส่งต่อให้แอปพลิเคชัน | ต้องเปิดพอร์ต ยูดีพี ให้ถูกต้อง ไม่ใช่ ทีซีพี |
| ตัวจัดตารางงาน | เลือกโหนดที่เหมาะสมให้คอนเทนเนอร์ | ต้องตั้งเงื่อนไขการเลือกโหนดให้สอดคล้อง |
| ระบบแบ่งโหลด | กระจายการเชื่อมต่อตามรหัส | ต้องใช้แบบที่รองรับ ยูดีพี ไม่ใช่ตัวแบ่งโหลดสำหรับ ทีซีพี ธรรมดา |
| ระบบเฝ้าระวัง | เก็บค่าความหน่วง จำนวนการเชื่อมต่อ อัตราการสูญเสียแพ็กเก็ต | ต้องเก็บข้อมูลบ่อยพอที่จะเห็นปัญหาชั่วคราว |
| ระบบกู้คืน | ย้ายคอนเทนเนอร์เมื่อโหนดล่ม โดยไม่ทำให้การเชื่อมต่อหลุดทั้งหมด | ต้องทดสอบการกู้คืนเป็นประจำ |
ตัวอย่างการกำหนดค่าที่ผมใช้จริง เริ่มจากส่วนคอนเทนเนอร์ที่รับบริการ
apiVersion: apps/v1 kind: Deployment metadata: name: quic-edge-service namespace: production spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 selector: matchLabels: app: quic-edge-service template: metadata: labels: app: quic-edge-service spec: nodeSelector: node-type: udp-optimized containers:
- name: app
image: registry.example.com/quic-edge:latest ports:
- containerPort: 443
protocol: UDP resources: requests: cpu: "500m" memory: "512Mi" limits: cpu: "2000m" memory: "2Gi"
จุดที่ต้องสังเกตคือการกำหนดโปรโตคอลพอร์ตเป็น ยูดีพี ซึ่งคนจำนวนมากพลาด ทำให้ระบบรับการเชื่อมต่อไม่ได้เลย และการกำหนดให้คอนเทนเนอร์อยู่เฉพาะโหนดที่ปรับแต่งสำหรับ ยูดีพี แล้วผ่านการเลือกโหนด การตั้งค่าแบบนี้ทำให้ตัวจัดตารางรู้ตัวเองว่าคอนเทนเนอร์ตัวนี้ต้องไปอยู่ที่ไหน ไม่ต้องแทรกแซงเองทุกครั้ง อีกจุดหนึ่งที่น่าสนใจคือการตั้งค่าอัปเดตแบบค่อยเป็นค่อยไป โดยห้ามมีคอนเทนเนอร์ลงก่อนที่ตัวใหม่จะพร้อม ซึ่งสำคัญมากกับงานเรียลไทม์ เพราะความจุลดลงแม้ชั่วขณะเดียวก็อาจทำให้สัญญาณราคามาถึงช้า
ส่วนการขยายตัวอัตโนมัติ เราตั้งค่าให้เพิ่มจำนวนคอนเทนเนอร์เมื่อการใช้ทรัพยากรเกินระดับที่กำหนด แต่เพิ่มอย่างช้า ๆ ไม่ก้าวกระโดด เพราะการเพิ่มคอนเทนเนอร์ใหม่บนโปรโตคอล ควิก มีต้นทุนการปรับตัวของการเชื่อมต่อที่อยู่แล้ว ถ้าเพิ่มเร็วเกิน ระบบแบ่งโหลดจะกระจายรหัสการเชื่อมต่อใหม่หมด ทำให้ความหน่วงพุ่งขึ้นชั่วขณะ ซึ่งในระบบเทรดคือเรื่องที่ยอมรับไม่ได้ เราจึงตั้งค่าให้เพิ่มคอนเทนเนอร์ทีละหนึ่งตัวและรอดูค่าความหน่วงให้นิ่งก่อนจะเพิ่มตัวถัดไป สำหรับคนที่ใช้ระบบคลาวด์ อย่าลืมตรวจสอบว่าผู้ให้บริการอนุญาตการรับส่ง ยูดีพี ผ่านกลุ่มความปลอดภัยแล้ว ผมเคยเจอกรณีที่ตั้งค่าทุกอย่างถูกหมด แต่กลุ่มความปลอดภัยบนคลาวด์บล็อกพอร์ต ยูดีพี ไว้ ใช้เวลาไล่หาเกือบวันเต็ม ปัญหาแบบนี้ดูโง่ แต่เจอกันบ่อยมากจริง ๆ
การติดตั้งและเตรียมสภาพแวดล้อม
การเริ่มต้นต้องเตรียมสภาพแวดล้อมให้พร้อมก่อน ผมเขียนสคริปต์ที่ใช้จริงไว้ให้ ปรับเล็กน้อยตามระบบของแต่ละคนได้
#!/bin/bash set -euo pipefail
echo "=== ติดตั้งเครื่องมือพื้นฐาน ===" sudo apt-get update && sudo apt-get install -y \ curl wget git jq apt-transport-https \ ca-certificates software-properties-common gnupg
if ! command -v docker &> /dev/null; then curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER sudo systemctl enable --now docker fi
curl -LO "https://dl.k8s.io/release/$(curl -sL https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
echo "=== ตรวจสอบเวอร์ชัน ===" docker --version && kubectl version --client && helm version --short
echo
คำถามที่พบบ่อย
HTTP/3 QUIC Pod Scheduling คืออะไร และต่างจากการจัดตารางงานแบบเดิมอย่างไร?
HTTP/3 QUIC Pod Scheduling คือการจัดตารางงานของระบบคอนเทนเนอร์ที่รองรับโปรโตคอล HTTP/3 ซึ่งทำงานบนโปรโตคอล QUIC ที่ใช้ UDP เป็นฐานแทน TCP แบบเดิม ความต่างสำคัญคือ ตัวจัดตารางแบบปกติมักมองแค่ปริมาณทรัพยากร เช่น CPU และหน่วยความจำ แต่เมื่อใช้ QUIC ที่รับส่งข้อมูลผ่าน UDP จะมีปัจจัยเพิ่มเข้ามาคือลักษณะการรับส่งแพ็กเก็ตข้อมูล UDP ไม่มีการจับมือสามขั้นตอนแบบ TCP และไม่มีสถานะการเชื่อมต่อที่ผูกกับโหนดเดียว แต่ใช้รหัสการเชื่อมต่อที่ทำให้การเชื่อมต่อย้ายไปมาได้โดยไม่หลุด ดังนั้นตัวจัดตารางต้องคำนึงถึงความสามารถของโหนดในการรับมือกับแพ็กเก็ต UDP จำนวนมาก และการกระจายรหัสการเชื่อมต่ออย่างสม่ำเสมอ จึงจะใช้ประโยชน์จากโปรโตคอลรุ่นใหม่นี้ได้อย่างเต็มที่ในคลัสเตอร์ที่มีคอนเทนเนอร์หลายพันตัว
ทำไมปี 2026 ถึงเป็นจุดที่ HTTP/3 และ QUIC สำคัญกว่าที่เคย?
มีเหตุผลหลักสามข้อ ข้อแรก เบราว์เซอร์หลักทุกตัวและอุปกรณ์เคลื่อนที่รุ่นใหม่รองรับ HTTP/3 เป็นค่าเริ่มต้นแล้ว ผู้ใช้ไม่ต้องเปิดใช้เอง ระบบเราจึงต้องพร้อมรับการเชื่อมต่อเหล่านี้ตั้งแต่ผู้ใช้เริ่มเปิดหน้าเว็บ ข้อสอง ความเร็วของเว็บกลายเป็นปัจจัยจัดอันดับในเครื่องมือค้นหาอย่างชัดเจน เว็บที่เปิดช้ากว่าคู่แข่งจะเสียทั้งอันดับและลูกค้าไปพร้อมกัน ข้อสาม งานแบบเรียลไทม์เพิ่มขึ้นทุกปี ไม่ว่าจะเป็นการเทรด การสตรีมวิดีโอ การเรียกผ่านอินเทอร์เน็ต หรือระบบสั่งซื้อสินค้าที่ต้องอัปเดตสต็อกทันที งานเหล่านี้ต้องการเครือข่ายที่หน่วงต่ำและไม่หลุดกลางคัน ซึ่งเป็นจุดแข็งของโปรโตคอล QUIC โดยตรง เพราะใช้ UDP ที่ลดปัญหาหัวข้อเดียวติดทั้งคิวของเวอร์ชันก่อนหน้า
QUIC ช่วยแก้ปัญหาอะไรเกี่ยวกับการเชื่อมต่อเครือข่ายของผู้ใช้?
QUIC ช่วยแก้ปัญหาสำคัญสองเรื่อง หนึ่งคือความหน่วงในการเชื่อมต่อ เพราะทำงานบน UDP ที่ไม่ต้องจับมือก่อนเริ่มการเชื่อมต่อแบบสามขั้นตอนเหมือน TCP จากประสบการณ์จริงในบทความ ทีมที่ย้ายระบบสัญญาณเทรดมาใช้ QUIC เต็มรูปแบบวัดผลได้ว่าความหน่วงในการเชื่อมต่อครั้งแรกลดลงเกือบครึ่งหนึ่ง สองคือการเชื่อมต่อไม่หลุดเวลาผู้ใช้เปลี่ยนเครือข่าย เช่น เปลี่ยนจากไวไฟมาเป็นเน็ตมือถือ เพราะรหัสการเชื่อมต่อของ QUIC ติดตัวผู้ใช้ ไม่ได้ผูกกับหมายเลขแอดเดรสอย่าง TCP นี่เป็นสิ่งสำคัญมากสำหรับระบบที่ต้องตอบสนองภายในมิลลิวินาที เพราะถ้าเครือข่ายหน่วงเพียงเล็กน้อยหรือการเชื่อมต่อหลุดกลางคัน กำไรที่ควรจะได้อาจกลายเป็นขาดทุนทันที
ตัวจัดตารางงานสำหรับ QUIC ควรพิจารณาปัจจัยใดบ้าง?
หัวใจสำคัญคือตัวจัดตารางต้องมองเครือข่ายเป็นทรัพยากรอย่างหนึ่ง ไม่ต่างจากหน่วยประมวลผลหรือหน่วยความจำ เดิมทีเราตั้งค่าขั้นต่ำและขีดจำกัดของ CPU กับหน่วยคว
แนะนำเพิ่มเติม
🎯 เปิดบัญชี XM Forex → สมัคร
📱 แอป iCafeFX → ดาวน์โหลด
📊 สัญญาณฟรี → รับสัญญาณ
📚 คู่มือเทรด → SiamCafeBook
🤝 สมัคร Partner XM → pipaffiliates.com/c?c=72816&l=th&p=6
แนะนำจากเครือข่าย SiamCafe
คู่มือ Forex ที่เกี่ยวข้อง
แหล่งอ้างอิง
- SiamCafe.net - ชุมชนไอทีไทยตั้งแต่ปี 1997
- iCafeFX.com - ศูนย์วิเคราะห์ Forex และทองคำ