นับตั้งแต่เปิดตัว gRPC มันก็ได้รับความนิยมอย่างมากในหมู่นักพัฒนา API เหตุผลที่ gRPC ได้รับความนิยมอย่างมากคือการสนับสนุนการใช้งานหลายภาษา (เซิร์ฟเวอร์และไคลเอนต์สามารถเขียนเป็นภาษาแยกกันได้) การมุ่งเน้นไปที่ประสิทธิภาพตั้งแต่เริ่มต้น (gRPC ขึ้นอยู่กับ HTTP/2) และชุดเครื่องมือที่ยอดเยี่ยม (การใช้ gRPC protobuf สำหรับคำอธิบายข้อความและบริการและไคลเอนต์สามารถสร้างได้โดยอัตโนมัติโดยไม่ต้องเขียนโค้ดแม้แต่บรรทัดเดียว)

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

  1. คำขอในเบราว์เซอร์ไม่สามารถบังคับให้เป็น HTTP/2 ได้
  2. เฟรม HTTP/2 ไม่พร้อมใช้งานกับไลบรารีโดยตรง

มาถึง "grpc-web" ซึ่งเป็นความพยายามของ Google และ "ไม่น่าจะเป็นไปได้" ในการใช้ข้อกำหนด gRPC สำหรับเบราว์เซอร์ ข้อมูลจำเพาะนี้ใช้ HTTP/1.x กับพร็อกซีเกตเวย์ เช่น Envoy (ซึ่งแปล HTTP/1.x เป็น HTTP/2 อย่างโปร่งใสที่เซิร์ฟเวอร์ gRPC คาดหวัง)

gRPC-web รองรับเฉพาะ Unary และสตรีมมิ่งฝั่งเซิร์ฟเวอร์ในโหมด grpcwebtext (เราจะกล่าวถึงความหมายในส่วนต่อๆ ไป)

ในโพสต์นี้ เราจะใช้งานเซิร์ฟเวอร์ gRPC ใน golang และใช้งานไคลเอนต์ใน JavaScript โดยใช้ gRPC-web เราจะดูตัวอย่างการเรียก API การสตรีมทั้งแบบเอกนารีและฝั่งเซิร์ฟเวอร์

มาเริ่มกันเลย

สารบัญ

  1. ข้อกำหนดเบื้องต้น
  2. คำจำกัดความของโปรโตบุฟ
  3. การใช้ API เซิร์ฟเวอร์ Unary
  4. กำลังใช้ Unary API
  5. แก้ไขข้อผิดพลาดในการสื่อสารกับ Envoy
  6. การใช้สตรีมมิ่งฝั่งเซิร์ฟเวอร์
  7. บทสรุป

ข้อกำหนดเบื้องต้น

เราจำเป็นต้องติดตั้งเครื่องมือบางอย่างเพื่อให้สามารถผ่านโพสต์นี้ได้สำเร็จ

  1. VS Codeหรือโปรแกรมแก้ไขโค้ดที่คุณเลือกเพื่อเขียนโค้ดของเรา
  2. คอมไพเลอร์และเครื่องมือ "Golang"
  3. Python3 — เราจะใช้เซิร์ฟเวอร์ HTTP ของ python เพื่อให้บริการลูกค้าของเรา
  4. Node (npm มาพร้อมโหนดล่วงหน้า) และเครื่องมือ npx
  5. นักเทียบท่า — เราจะใช้งานพร็อกซี Envoy เป็นคอนเทนเนอร์นักเทียบท่า
  6. protoc คอมไพเลอร์ protobuf
  7. Golang "แพ็คเกจ gRPC" และ "ปลั๊กอิน protobuf protoc"
  8. protoc-gen-grpc-webplugin ของ gRPC-web

ปฏิบัติตามคำแนะนำในการติดตั้งสำหรับเครื่องมือแต่ละชิ้น ฉันกำลังข้ามคำแนะนำที่นี่เพื่อความกระชับ แต่โปรดแจ้งให้เราทราบหากคุณพบข้อผิดพลาด เรายินดีที่จะช่วยเหลือ

โพสต์นี้ไม่ใช่โพสต์แนะนำสำหรับภาษาหรือกรอบงานใดๆ ที่ใช้ที่นี่ ฉันจะลิงก์เนื้อหาเบื้องต้นสำหรับแต่ละเนื้อหาเพื่อให้คุณได้รับข้อมูลที่รวดเร็ว

คำจำกัดความของโปรโตบุฟ

เราจะเริ่มต้นด้วยการสร้างคำจำกัดความของ protobuf สำหรับบริการของเรา สร้างไฟล์ calculator.proto และวางไว้ในโฟลเดอร์ protos สำหรับตอนนี้ เราจะเพิ่มคำจำกัดความบริการ Unary และพัฒนาเพื่อเพิ่มการสตรีมฝั่งเซิร์ฟเวอร์ในส่วนที่ 2

grpc-web
    ├── protos
    │   └── calculator.proto
    ├── server
    └── client

เราได้เพิ่มข้อความสองข้อความ ได้แก่ AddRequest ซึ่งต้องใช้ตัวเลขสองตัวในการเพิ่ม AddResponse ที่ส่งคืนผลลัพธ์ของการบวก และบริการเครื่องคิดเลขด้วยการเรียก rpc เพียงครั้งเดียว Addซึ่งรับ AddRequest และส่งกลับ AddResponse

การใช้ API เซิร์ฟเวอร์ Unary

เราจะคอมไพล์ไฟล์ calculator.proto เพื่อไปโค้ดด้วยความช่วยเหลือของ protoc และไปที่ปลั๊กอิน protobuf

สร้างโฟลเดอร์ calculatorpb ภายในโฟลเดอร์เซิร์ฟเวอร์และดำเนินการคำสั่งต่อไปนี้

protoc calculator.proto --go_out=plugins=grpc:../server/calculatorpb/

สิ่งนี้จะสร้างไฟล์ calculator.pb.go และโครงสร้างไดเร็กทอรีของคุณจะมีลักษณะเช่นนี้

grpc-web
    ├── protos
    │   └── calculator.proto
    ├── server
    │   └── calculatorpb
    │       └── calculator.pb.go
    └── client

ตอนนี้เริ่มต้นโมดูล go โดยดำเนินการคำสั่งต่อไปนี้ในโฟลเดอร์ server

go mod init github.com/kaysush/grpc-calculator

สิ่งนี้จะเพิ่มไฟล์ go.mod ไปยังโฟลเดอร์ server ของคุณ การสร้างโมดูลจากโฟลเดอร์ server จะทำให้แน่ใจว่าเราสามารถเข้าถึงแพ็คเกจในไฟล์ calculator.pb.go ในไฟล์ server.go ของเรา ซึ่งเราจะเพิ่มในขั้นตอนถัดไป

มาเริ่มใช้งานเซิร์ฟเวอร์กันเถอะ

grpc-web
    ├── protos
    │   └── calculator.proto
    ├── server
    │   ├── calculatorpb
    │   │   └── calculator.pb.go
    │   ├── go.mod
    │   └── server.go
    └── client

เรียกใช้ server.go

go run server.go

คุณควรเห็นข้อความ Starting Calculator server บนหน้าจอ

ขณะนี้เซิร์ฟเวอร์ gRPC พร้อมที่จะให้บริการคำขอบนพอร์ต 50551 แล้ว

กำลังใช้ Unary API

ขณะนี้เซิร์ฟเวอร์ของเรากำลังทำงานอยู่ เรามาปรับใช้ไคลเอ็นต์ของเราโดยใช้ gRPC-web กัน

คอมไพล์ไฟล์ calculator.proto สำหรับ JavaScript โดยใช้ปลั๊กอิน proto-gen-grpc-web

protoc calculator.proto --js_out=import_style=commonjs,binary:../client --grpc-web_out=import_style=commonjs,mode=grpcwebtext:../client

สิ่งนี้จะสร้าง calculator_pb.js (ซึ่งมีคำจำกัดความของข้อความ) และ calculator_grpc_web_pb.js (ซึ่งมีการใช้งานลูกค้าของเรา)

grpc-web
    ├── protos
    │   └── calculator.proto
    ├── server
    │   ├── calculatorpb
    │   │   └── calculator.pb.go
    │   ├── go.mod
    │   └── server.go
    └── client
        ├── calculator_grpc_web_pb.js
        └── calculator_pb.js

เพิ่มไฟล์ package.json ลงในโฟลเดอร์ client โดยมีเนื้อหาดังต่อไปนี้

{
 "name": "grpc-calculator",
 "version": "0.1.0",
 "description": "gRPC-Web Calculator",
 "devDependencies": {
  "@grpc/proto-loader": "^0.3.0",
  "google-protobuf": "^3.6.1",
  "grpc": "^1.15.0",
  "grpc-web": "^1.0.0",
  "webpack": "^4.16.5",
  "webpack-cli": "^3.1.0"
 }
}

เพิ่มไฟล์ client.js ซึ่งมีการใช้งานไคลเอ็นต์ของเราเชื่อมต่อกับเซิร์ฟเวอร์ gRPC ของเรา

จำไว้ว่าฉันบอกคุณว่าคุณไม่จำเป็นต้องเขียนโค้ดใดๆ เพื่อใช้งานไคลเอ็นต์ด้วย gRPC

ตอนนี้ให้เราแพ็คเกจโค้ด JavaScript ของเราเป็นไฟล์ย่อเล็กสุด

npm install
npx webpack client.js

สิ่งนี้ควรดาวน์โหลดการขึ้นต่อกันทั้งหมดของเราที่กล่าวถึงในไฟล์ package.json จากนั้นจึงทำแพ็กเกจ client.js ลงใน dist/main.js พร้อมการขึ้นต่อกันทั้งหมด

สร้างไฟล์ index.html ซึ่งอ้างอิงไฟล์ dist/main.js ของเราและทำการเรียก gRPC

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Calculator - gRPC-Web</title>
    <script src="dist/main.js"></script>
</head>
<body>
</body>
</html>

โครงสร้างไดเร็กทอรีของคุณควรมีลักษณะเช่นนี้ในขณะนี้

grpc-web
    ├── protos
    │   └── calculator.proto
    ├── server
    │   ├── calculatorpb
    │   │   └── calculator.pb.go
    │   ├── go.mod
    │   └── server.go
    └── client
        ├── dist
        │   └── main.js
        ├── calculator_grpc_web_pb.js
        ├── calculator_pb.js
        ├── client.js
        ├── package.json
        └── index.html

จากไดเร็กทอรีไคลเอนต์ให้เริ่มเซิร์ฟเวอร์ python เพื่อเริ่มให้บริการไฟล์ของเรา

python3 -m http.server 8081 &

ไปที่ "http://localhost:8081" แล้วเปิดคอนโซล

คุณควรเห็นข้อผิดพลาดในคอนโซล คุณจะได้รับข้อผิดพลาดต่อไปนี้หนึ่งข้อหรือทั้งหมด

  1. คำขอข้ามแหล่งกำเนิดสินค้าถูกบล็อก
  2. ERR_CONNECTION_REFUSED

มีสองปัญหาในการใช้งานเซิร์ฟเวอร์และไคลเอนต์ปัจจุบันของเรา

  1. เซิร์ฟเวอร์ของเราไม่อนุญาตคำขอ CORS และ
  2. แม้ว่าจะรองรับ CORS แต่ gRPC-web ก็ไม่รองรับ HTTP/2 ดังนั้นเราจึงต้องการพร็อกซีเกตเวย์เช่น Envoy เพื่อแปลคำขอ HTTP/1.x ของเราที่มาจาก gRPC-web สำหรับเซิร์ฟเวอร์ gRPC ของเรา

แก้ไขข้อผิดพลาดในการสื่อสารกับ Envoy

ตามที่กล่าวไว้ในหัวข้อก่อนหน้านี้ เราจำเป็นต้องวาง Envoy ไว้ระหว่างไคลเอนต์และเซิร์ฟเวอร์ของเราเพื่อการแปลที่โปร่งใสระหว่าง HTTP/1.x และ HTTP/2

เราจะใช้นักเทียบท่าเพื่อเรียกใช้พร็อกซีทูตของเรา เราจะใช้ envoy.yaml มาตรฐานจาก gRPC-web hello world demo (แน่นอนว่าพอร์ตต่างๆ จะเปลี่ยนไปตามการใช้งานของเรา)

สร้างไฟล์นักเทียบท่า

FROM envoyproxy/envoy:latest
COPY ./envoy.yaml /etc/envoy/envoy.yaml
CMD /usr/local/bin/envoy -c /etc/envoy/envoy.yaml

สร้างภาพนักเทียบท่า

docker build -t my-envoy:1.0 .

ตอนนี้รันคอนเทนเนอร์นักเทียบท่าทูต

docker run -d -p 8080:8080 -p 9901:9901 --network=host my-envoy:1.0

เมื่อทูตทำงาน เราจำเป็นต้องเปลี่ยนพอร์ตใน client.js ของเรา ตอนนี้แทนที่จะเชื่อมต่อกับเซิร์ฟเวอร์ gRPC โดยตรงบน 50551 เราจะเชื่อมต่อกับทูตบน 8080

จัดแพคเกจ client.js ใหม่อีกครั้ง

npx webpack client.js

โหลดเบราว์เซอร์อีกครั้งที่ "http://localhost:8081" และเปิดคอนโซล

ตอนนี้คุณควรเห็นผลลัพธ์ของคุณแล้ว

การใช้สตรีมมิ่งฝั่งเซิร์ฟเวอร์

แก้ไขไฟล์ calculator.proto และเพิ่มการเรียก rpc การสตรีมฝั่งเซิร์ฟเวอร์เพื่อสร้างหมายเลขฟีโบนักชี

protoc calculator.proto --go_out=plugins=grpc:../server/calculatorpb/Re-compile our proto files for both go and js.
protoc calculator.proto --js_out=import_style=commonjs,binary:../client --grpc-web_out=import_style=commonjs,mode=grpcwebtext:../client

เพิ่มการใช้งานเซิร์ฟเวอร์ใน server.go

เพิ่มการโทรแบบสตรีมมิ่งใน client.js ของเรา

รีแพ็คให้ลูกค้า.

npx webpack client.js

โหลดเบราว์เซอร์อีกครั้ง และคุณจะเห็นตัวเลขฟีโบนัชชีสตรีมไปยังไคลเอนต์ของคุณ

บทสรุป

gRPC-web เป็นข้อกำหนดที่เป็นผู้ใหญ่มากสำหรับการเชื่อมต่อกับการออกจาก gRPC API โดยตรงจากเบราว์เซอร์โดยไม่มีอินเทอร์เฟซ REST ใดๆ อยู่ระหว่างนั้น เนื่องจากไมโครเซอร์วิสใช้ gRPC มากขึ้น gRPC-web จึงเข้ากันได้ดีกับโลก API ที่ไม่มี REST

ในกรณีที่คุณพบข้อผิดพลาดในโค้ดของฉันหรือมีคำถามใดๆ โดยทั่วไป โปรดแสดงความคิดเห็นได้

จนถึงตอนนี้ ขอให้สนุกกับการเขียนโค้ด! :)