สวัสดีครับ กลับมาพบกับบทความเกี่ยวกับการคุยกันของระบบหลังบ้านกันอีกแล้วนะครับ รอบก่อนเป็น Web Socket วันนี้มาเป็น gRPC มือใหม่เริ่มเขียนโปรแกรมอาจจะยังไม่ค่อยคุ้น แต่ค่อย ๆ มาทำความเข้าใจไปพร้อม ๆ กันในบทความได้เลยยยย
มาเริ่มกันที่ gRPC คืออะไร?
เริ่มต้นที่ชื่อเต็มมันก่อนละกัน gRPC = Remote Procedure Calls แล้วเจ้า g ตัวเล็กด้านหน้าก็คือ Google มันคือสรุปแล้วมันคือ Remote Procedure Calls ที่สร้างโดย Google ใช้ได้ฟรี เพราะเป็น open source ทำงานบน HTTP2 และใช้ Protocol Buffer สำหรับการแลกเปลี่ยนข้อความ ซึ่งทำให้สะดวกและมีประสิทธิภาพ
อธิบายสำหรับคนที่ไม่ใช่โปรแกรมเมอร์
“gRPC เปรียบเสมือนพนักงานส่งของที่ทั้งเก่ง ฉลาด ทำงานก็ไว ทำงานผ่านระบบขนส่งที่มีมาตรฐาน (HTTP2) และใช้ภาษาเฉพาะ (Protocol Buffer) ในการสื่อสาร ทำให้ส่งข้อมูลได้อย่างสะดวกและมีประสิทธิภาพ”
ไหน ๆ พูดถึง HTTP2 สรุปสั้น ๆ เกี่ยวกับเรื่องนี้หน่อย
HTTP คืออะไร?
- HTTP คือโปรโตคอลที่เราใช้สื่อสารบนเว็บนี่แหละ เป็นเหมือนข้อตกลงที่ทำให้เราสามารถดูข้อมูล แชร์ลิงก์รูปภาพ วิดีโอต่าง ๆ บนอินเทอร์เน็ตได้
HTTP ทำงานอย่างไร
- ลองนึกภาพว่า HTTP เป็นเหมือนพนักงานส่งเอกสาร มันทำหน้าที่รับ-ส่งข้อมูลตามคำสั่งระหว่างเครื่องคอมพิวเตอร์ของเรา (Client) และเซิร์ฟเวอร์ (เครื่องที่รันเว็บนี้อยู่) เวลาเราคลิกลิงก์หรือเข้าเว็บ HTTP จะรับคำสั่งไปบอกเซิร์ฟเวอร์ว่าเราต้องการดูข้อมูลไหน แล้วเซิร์ฟเวอร์ก็จะส่งข้อมูลนั้นกลับมาหาเรา
HTTP /2 นี่ของใหม่ล่าสุดเลยป่าว?
ไม่ใช่ !!! HTTP/2 ไม่ใช่ของใหม่ล่าสุดแล้ว ตอนนี้มี HTTP/3 ซึ่งเป็นเวอร์ชั่นที่มาใหม่กว่าเพิ่งออกมาเมื่อปี 2022 และจากข้อมูลรุ่น 3 มีการใช้งานไปแค่ 29%
โอเค เราปูพื้นฐานสั้น ๆ เกี่ยวกับ HTTP /2 กันไปแล้ว กลับมาต่อที่ gRPC กันครับ
Remote Procedure Calls คืออะไร
Remote Procedure Call หรือ RPC เป็นวิธีการเรียกฟังก์ชันหรือวิธีการบนคอมพิวเตอร์เครื่องอื่นผ่านเครือข่าย เปรียบเหมือนเราใช้มือยางยืดยื่นไป กดปุ่มบนคอมพิวเตอร์อีกเครื่องโดยไม่ต้องลุกจากเก้าอี้ แต่ถ้าให้อธิบายเชิงเทคนิคหน่อย ผมขอใช้ภาพด้านล่างนี้ในการอธิบาย
เริ่มจากเราก็ให้ฝั่ง Client หรือระบบที่ต้องการส่ง Request ส่ง Request ไปหาฟังก์ชันอีกเครื่อง โดยเราสามารถระบุชื่อฟังก์ชันที่ต้องการเรียก ส่งอาร์กิวเมนต์ ข้อมูลที่ส่งไปยังฟังก์ชัน แล้วมันจะมีการแปลงข้อมูลที่เฉพาะเจาะจงสำหรับ RPC Framework แต่ละตัว สาเหตุที่ต้องมีการแปลงเพราะ RPC Framework แต่ละตัวใช้รูปแบบข้อมูลที่แตกต่างกัน เช่น JSON, XML, Protobuf แต่ละตัวรองรับประเภทข้อมูลที่แตกต่างกัน เช่น ตัวเลข, สตริง, boolean
บางตัวอาจจะมีการเข้ารหัสข้อมูลไว้ ซึ่ง RPC Framework ที่หยิบมาแนะนำกันก็จะเป็น gRPC นี่แหละ ต่อมาเป็นส่วน Outgoing Network Request หมายถึง Request จากฝั่งของ Client ที่ส่งไปยัง network โดย Request นี้สามารถเป็นอะไรก็ได้ ขึ้นอยู่กับโปรโตคอลที่ใช้อยู่ โดย Network ซึ่งถ้าเป็น gRPC ก็จะใช้เป็น HTTP Protocol แล้วหลังจากนั้น Request นี้ก็ไปแปลงงข้อมูลสำหรับ RPC Framework แล้วก็ทำงานในฝั่งของฟังก์ชันที่อยู่ฝั่งเซิร์ฟเวอร์นั้นเอง แล้วนี่ก็เป็นกระบวนการทำงานของ RPC คร่าว ๆ นั่นเอง
ตัวอย่าง RPC Framework ใน Golang ที่ไม่ใช้ gRPC (net/rpc)
gRPC เอาไปใช้ทำอะไรได้บ้าง?
1. Microservices:
อันนี้ชูโรงเลย gRPC เหมาะสำหรับการสร้างแอปแบบ Microservices ซึ่งเป็นเทรนด์ในตอนนี้เลย เพราะการทำแอปแบบนี้ช่วยให้เราสามารถแบ่งแอปพลิเคชันออกเป็น service ย่อยๆ แต่ละ service สามารถพัฒนาและ deploy แยกกันได้
2. Streaming:
ด้วยความที่ gRPC มีประสิทธิภาพในการรับส่งข้อมูล ทำให้เหมาะสำหรับทำแอปที่มีการ streaming ข้อมูล ช่วยให้สร้างแอปพลิเคชัน real-time เช่น video streaming ได้อย่าง Netflix เค้าก็ใช้ gRPC นะ
3. IoT:
จากความสามารถที่เรียกฟังก์ชันข้ามเครื่อง ข้ามอุปกรณ์ แถมยังเด่นเรื่องประสิทธิภาพในการรับส่งข้อมูล ทำให้ low latency เหมาะสำหรับเอามาใช้ส่งข้อมูลแบบ Real Time กับอุปกรณ์ IoT อีกด้วย
ข้อดีของ gRPC
- ประสิทธิภาพสูง: gRPC ใช้โปรโตคอล HTTP/2 ซึ่งช่วยให้ส่งข้อมูลได้หลายส่วน (multiplexing) และมี streaming ที่มีประสิทธิภาพ ลดเวลาในการรอ (latency) และเพิ่มปริมาณงานที่รองรับ (throughput) เหมาะสำหรับงานที่ต้องการการสื่อสาร real-time หรือต้องรองรับการร้องขอข้อมูลจำนวนมาก
- รองรับหลายภาษา: รองรับภาษาโปรแกรมหลากหลาย ช่วยให้บริการ (service) ที่เขียนคนละภาษาสื่อสารกันได้อย่างราบรื่น เหมาะสำหรับ microservices ที่แต่ละส่วนอาจถูกสร้างด้วยภาษาโปรแกรมต่างกัน
- เหมาะกับการสเกล: เนื่องจากทำงานแบบไม่รอผล (asynchronous) และรองรับการ streaming ทำให้ gRPC สามารถสเกลได้ค่อนข้างดี เหมาะสำหรับแอปที่มีผู้ใช้งานจำนวนมาก ๆ
- สร้างโค้ดได้อัตโนมัติ: gRPC มีเครื่องมือที่ใช้สร้างโค้ดฝั่ง Client และ Server ได้อัตโนมัติ จากคำอธิบายใน protocol buffers เราไม่ต้องเขียนโค้ดสำหรับการสื่อสารเอง ประหยัดเวลาและลดความผิดพลาด
- การ Streaming สองทาง(Bi-directional streaming): gRPC รองรับการส่งข้อมูลระหว่าง Client และ Server ด้วย streaming แบบสองทาง ทำให้ใช้สร้างระบบ real-time เช่น แอปแชท หรือส่งข้อมูลจำนวนมากไปมาระหว่างบริการต่างๆได้
ข้อเสียของ gRPC
- ซับซ้อน: เทียบกับวิธีสื่อสารแบบง่ายอย่าง RESTful APIs แล้ว gRPC อาจจะต้องทำความเข้าใจเยอะกว่าเพราะมีเรื่อง Protocol Buffers และ streaming เพิ่มเข้ามา
- ข้อจำกัดของ Network: gRPC มีประสิทธิภาพสูงก็จริง แต่ในบางกรณีที่มี network ไม่ดีหรือ bandwidth จำกัด การใช้ HTTP/2 และ Protocol Buffers อาจทำให้ประสิทธิภาพลดลงได้
- ความเข้ากันได้และการทำงานร่วมกัน: ถึง RPC จะรองรับหลายภาษาโปรแกรม แต่การเขียนโค้ดด้วยบางภาษาอาจไม่ได้รองรับ feature บางอย่าง หรือไม่ทันสมัย การทำงานกับบริการที่เขียนต่างภาษาอาจทำให้เกิดปัญหาได้
- การแก้ bug และตรวจสอบ: การแก้ bug ระบบที่ใช้ gRPC อาจจะยากกว่า RESTful APIs ทั่วไป เนื่องจาก Protocol buffers เป็นข้อมูล binary ทำให้ยากต่อการตรวจสอบข้อมูลขณะพัฒนาและแก้ไขปัญหา
- ความนิยมและความครอบคลุม: ถึง gRPC จะค่อนข้างนิยม แต่ก็อาจไม่ได้รับมีข้อมูล หรือ Community เท่า REST ทำให้หาตัวอย่าง libraries หรือ frameworks ได้ยากกว่าในบางภาษาหรือ platform
ด้านบนเราพูดถึง Protocol Buffers ไปกันหลายรอบแล้ว แต่ยังไม่ได้มีการอธิบายมาครับ มาดูกันว่ามันคืออะไร?
Protocol Buffers ใน gRPC มันเป็น format ที่เอาไว้ให้แต่ละ service คุยกันรู้เรื่อง ผ่าน gRPC โดย มันสามารถเรียกสั้น ๆ ว่า “Protobuf” คิดซะว่ามันเป็นเหมือน JSON แต่เล็กและเร็วกว่า ประหยัดพื้นที่จัดเก็บและส่งข้อมูล เข้ารหัสและถอดรหัสก็ไว เหมาะกับการทำงานร่วมกับ microservices ทำงานได้หลายภาษาทั้ง Java, Python, Go, C++ มี Schema ชัดเจน
หลักการทำงานของ Protocol Buffers
- กำหนด Schema : เหมือนเขียน “หัวตาราง” โดยใช้ไฟล์
.proto
ซึ่งระบุประเภทข้อมูลของแต่ละช่อง (ชื่อ, อีเมล เป็นต้น) - เจนโค้ดออกมา: คอมไพเลอร์จะสร้างโค้ดในภาษาต่างๆ จาก Schema ที่เรากำหนดไว้ใน .proto
- ส่งและจัดเก็บข้อมูล: ข้อมูลจะถูกเข้ารหัสอยู่ในรูปแบบของ Protocol Buffers ทำให้ประหยัดเนื้อที่
- อ่านและใช้งาน: โค้ดที่สร้างขึ้นมาช่วยอ่านและจัดการข้อมูลให้อย่างง่ายดาย ไม่ว่าจะต่างภาษาหรือต่างแพลตฟอร์มกัน
มาลองเขียนโค้ดแบบง่าย ๆ กัน
โปรเจกต์ชื่อว่า “หัดใช้ gRPC ด้วยการเขียน Go แบบไว ๆ”
ก่อนจะเริ่ม
- ต้องติดตั้ง Go ก่อน https://go.dev/doc/install
- เขียน Go แบบเบสิค: ใครยังไม่เป็นเรามีสอนฟรี https://school.borntodev.com/course/introduction-to-go-programming-language
- ติดตั้งตัวแปลงนั่นก็คือ Protocol Buffer https://protobuf.dev/
- ติดตั้ง protocol compiler plugin
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
เดี๋ยวเราจะมาใช้ตัวอย่างจากเว็บ Official กันโดยสามารถโหลดโค้ดตัวอย่างได้ที่
git clone -b v1.62.0 --depth 1 https://github.com/grpc/grpc-go
ลองรันดู
- เข้าไปที่โฟลเดอร์ตัวอย่าง: เช่น
cd grpc-go/examples/helloworld
- รัน Server: คำสั่ง
go run greeter_server/main.go
- รัน Client (เปิดหน้าต่าง terminal อีกอัน):
go run greeter_client/main.go
- ได้ข้อความออกมา ประมาณ “Hello world” ก็แสดงว่าสำเร็จ!
มาลองปรับโค้ดเพื่อให้เห็นภาพขึ้นหน่อย
ผมจะลองแก้โค้ดให้มีฟังก์ชัน SayHelloAgain
เพิ่มไปตามนี้
- แก้ไฟล์ .proto: ไฟล์พวกนี้จะบอก Schema ของข้อมูลเรา ให้เพิ่ม rpc
SayHelloAgain
ในไฟล์hello.proto
- เจนโค้ดมาใหม่:
protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helloworld/helloworld.proto
- อัปเดต Server: เพิ่มฟังก์ชัน
SayHelloAgain
ในgreeter_server/main.go
- อัปเดต Client: เพิ่มส่วนที่เรียกใช้
SayHelloAgain
ในgreeter_client/main.go
ลองอีกสักรอบสิ
ทำการรัน server และ client เหมือนเดิม แต่ตอนรัน client คราวนี้ใส่ชื่อเป็น argument ไปด้วย เช่น go run greeter_client/main.go --name=admin_borntodev
เป็นยังไงกันบ้างครับไม่ยากเลยใช่มั้ย หากใครต้องการลองเล่นหรืออยากศึกษาเพิ่มเติมสามารถอ่านได้ที่เว็บนี้ได้เลย https://grpc.io/docs/languages/go/basics/