دمج Apache APISIX مع gRPC-Web
Fei Han
January 25, 2022
مقدمة عن gRPC Web
تم تطوير gRPC في الأصل بواسطة Google، وهو إطار عمل عالي الأداء لاستدعاء الإجراءات البعيدة (RPC) يتم تنفيذه على HTTP/2. ومع ذلك، نظرًا لأن المتصفحات لا تعرض HTTP/2 مباشرة، لا يمكن لتطبيقات الويب استخدام gRPC مباشرة. gRPC Web هو بروتوكول معياري يحل هذه المشكلة.
تم إصدار أول تنفيذ لـ gRPC-web في عام 2018 كـ مكتبة JavaScript يمكن من خلالها لتطبيقات الويب التواصل مباشرة مع خدمة gRPC. المبدأ هو إنشاء خط أنابيب gRPC من طرف إلى طرف متوافق مع HTTP/1.1 و HTTP/2. ثم يرسل المتصفح طلب HTTP عادي، ويقوم وكيل gRPC-Web الموجود بين المتصفح والخادم بترجمة الطلب والاستجابة. على غرار gRPC، يستخدم gRPC Web عقدًا مسبقًا بين عميل الويب وخدمة gRPC الخلفية. يتم استخدام Protocol Buffers لتسلسل وتشفير الرسائل.
مع gRPC Web، يمكن للمستخدمين استدعاء تطبيقات gRPC الخلفية مباشرة باستخدام متصفح أو عميل Node. ومع ذلك، هناك بعض القيود عند استخدام gRPC-Web على جانب المتصفح لاستدعاء خدمات gRPC.
- لا يتم دعم استدعاءات البث من جانب العميل والبث ثنائي الاتجاه.
- يتطلب استدعاء خدمات gRPC عبر النطاقات تكوين CORS على جانب الخادم.
- يجب تكوين جانب الخادم gRPC لدعم gRPC-Web، أو يجب توفر وكيل خدمة طرف ثالث لترجمة الاستدعاء بين المتصفح والخادم.
وكيل Apache APISIX لـ gRPC Web
يدعم Apache APISIX وكالة بروتوكول gRPC Web عن طريق مكون إضافي، حيث يقوم بتحويل البروتوكول وفك تشفير البيانات عند اتصال gRPC Web مع خادم gRPC في المكون الإضافي grpc-web
، وعملية الاتصال تكون كما يلي.
عميل gRPC Web -> Apache APISIX (تحويل البروتوكول وفك تشفير البيانات) -> خادم gRPC
فيما يلي مثال كامل يوضح كيفية بناء عميل gRPC Web ووكالة طلبات gRPC Web عبر Apache APISIX. في المثال التالي، سنستخدم Go كمعالج خادم gRPC و Node كطلب عميل gRPC Web.
تكوين Protocol Buffer
الخطوة الأولى هي تثبيت مترجم Protocol Buffer والمكونات الإضافية ذات الصلة.
-
تثبيت
protoc
وproto-grpc-*
.يجب تثبيت مترجم Protocol Buffer
protoc
والمكونات الإضافيةprotoc-gen-go
وprotoc-gen-grpc-web
لتوليد كود Go و JavaScript وواجهة gRPC web لملفات.proto
على نظامك قبل كتابة تطبيقات العميل والخادم.يرجى تشغيل البرنامج النصي التالي لتثبيت المكونات المذكورة أعلاه.
#!/usr/bin/env bash set -ex PROTOBUF_VERSION="3.19.0" wget https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip unzip protoc-${PROTOBUF_VERSION}-linux-x86_64.zip mv bin/protoc /usr/local/bin/protoc mv include/google /usr/local/include/ chmod +x /usr/local/bin/protoc PROTO_GO_PLUGIN_VER="1.2.0" wget https://github.com/grpc/grpc-go/releases/download/cmd/protoc-gen-go-grpc/v${PROTO_GO_PLUGIN_VER}/protoc-gen-go-grpc.v${PROTO_GO_PLUGIN_VER}.linux.amd64.tar.gz tar -zxvf protoc-gen-go-grpc.v${PROTO_GO_PLUGIN_VER}.linux.amd64.tar.gz mv protoc-gen-go-grpc /usr/local/bin/protoc-gen-go chmod +x /usr/local/bin/protoc-gen-go PROTO_JS_PLUGIN_VER="1.3.0" wget https://github.com/grpc/grpc-web/releases/download/${PROTO_JS_PLUGIN_VER}/protoc-gen-grpc-web-${PROTO_JS_PLUGIN_VER}-linux-x86_64 mv protoc-gen-grpc-web-${PROTO_JS_PLUGIN_VER}-linux-x86_64 /usr/local/bin/protoc-gen-grpc-web chmod +x /usr/local/bin/protoc-gen-grpc-web
-
إنشاء ملف
proto
مثالSayHello
.// a6/echo.proto syntax = "proto3"; package a6; option go_package = "./;a6"; message EchoRequest { string message = 1; } message EchoResponse { string message = 1; } service EchoService { rpc Echo(EchoRequest) returns (EchoResponse); }
تكوين تطبيق الخادم
-
توليد رسائل Go الخام وستبس الخدمة/العميل على جانب الخادم.
protoc -I./a6 echo.proto --go_out=plugins=grpc:./a6
-
تنفيذ واجهة معالج الخادم.
// a6/echo.impl.go package a6 import ( "errors" "golang.org/x/net/context" ) type EchoServiceImpl struct { } func (esi *EchoServiceImpl) Echo(ctx context.Context, in *EchoRequest) (*EchoResponse, error) { if len(in.Message) <= 0 { return nil, errors.New("message invalid") } return &EchoResponse{Message: "response: " + in.Message}, nil }
-
ملف دخول تشغيل تطبيق الخادم.
// server.go package main import ( "fmt" "log" "net" "apisix.apache.org/example/a6" "google.golang.org/grpc" ) func main() { lis, err := net.Listen("tcp", fmt.Sprintf(":%d", 50001)) if err != nil { log.Fatalf("failed to listen: %v", err) } grpcServer := grpc.NewServer() a6.RegisterEchoServiceServer(grpcServer, &a6.EchoServiceImpl{}) if err = grpcServer.Serve(lis); err != nil { log.Fatalf("failed to serve: %s", err) } }
-
تجميع وبدء خدمة الخادم.
go build -o grpc-server server.go ./grpc-server
تكوين برامج العميل
-
توليد كود
proto
على جانب العميل.توليد رسائل JavaScript الخام وستبس الخدمة/العميل وواجهة كود gRPC Web لـ JavaScript.
يوفر المكون الإضافي
proto
لـ gRPC Web طريقتين لتوليد الكود.-
mode=grpcwebtext: الكود الذي يتم توليده بشكل افتراضي يرسل الحمولة بتنسيق grpc-web-text.
-
Content-type: application/grpc-web-text
-
الحمولة تستخدم ترميز base64
-
يدعم الاستدعاءات الأحادية وبث الخادم
-
mode=grpcweb: إرسال الحمولة بتنسيق binary protobuf.
- Content-type: application/grpc-web+proto
- الحمولة بتنسيق binary protobuf
- يدعم حاليًا الاستدعاءات الأحادية فقط
protoc -I=./a6 echo.proto --js_out=import_style=commonjs:./a6 --grpc-web_out=import_style=commonjs,mode=grpcweb:./a6
-
-
تثبيت تبعيات جانب العميل.
npm i grpc-web npm i google-protobuf
-
تنفيذ ملف الدخول على جانب العميل.
// client.js const {EchoRequest} = require('./a6/echo_pb'); const {EchoServiceClient} = require('./a6/echo_grpc_web_pb'); // الاتصال بمدخل Apache APISIX let echoService = new EchoServiceClient('http://127.0.0.1:9080'); let request = new EchoRequest(); request.setMessage("hello") echoService.echo(request, {}, function (err, response) { if (err) { console.log(err.code); console.log(err.message); } else { console.log(response.getMessage()); } });
-
هيكل المشروع النهائي
$ tree . ├── a6 │ ├── echo.impl.go │ ├── echo.pb.go │ ├── echo.proto │ ├── echo_grpc_web_pb.js │ └── echo_pb.js ├── client.js ├── server.go ├── go.mod ├── go.sum ├── package.json └── package-lock.json
بعد إكمال الخطوات أعلاه، تكون قد قمت بتكوين تطبيق خادم gRPC Server وتطبيق عميل gRPC Web، وبدأت تطبيق الخادم، والذي سيستقبل الطلبات على المنفذ 50001
.
تكوين Apache APISIX
بعد ذلك، ببساطة قم بتمكين المكون الإضافي grpc-web
في تكوين مكونات التوجيه في Apache APISIX لوكالة طلبات gRPC web.
-
تمكين المكون الإضافي
grpc-web
.يجب أن يستخدم التوجيه مطابقة البادئة (على سبيل المثال،
/* أو /grpc/example/*
)، لأن عميل gRPC web يمرر اسم الحزمة واسم واجهة الخدمة واسم الطريقة المعلنة فيproto
في URI (على سبيل المثال،/path/a6.EchoService/Echo
)، استخدام المطابقة المطلقة سيمنع المكون الإضافي من استخراج معلوماتproto
من URI.$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "uri":"/*", // وضع مطابقة البادئة "plugins":{ "grpc-web":{} // تمكين مكون gRPC Web الإضافي }, "upstream":{ "scheme":"grpc", "type":"roundrobin", "nodes":{ "127.0.0.1:50001":1 // عناوين الاستماع والمنافذ لخادم gRPC } } }'
-
التحقق من طلبات وكالة gRPC Web.
يمكن إرسال طلب بروتوكول gRPC Web إلى Apache APISIX عن طريق تنفيذ
client.js
من Node.منطق المعالجة على جانب العميل والخادم على التوالي: يرسل العميل رسالة إلى الخادم بمحتوى
hello
، يستقبل الخادم الرسالة ويستجيب بـresponse: hello
، ونتيجة التنفيذ هي كما يلي.$ node client.js response: hello
-
تعطيل المكون الإضافي
grpc-web
.ببساطة قم بإزالة سمة grpc-web من تكوين مكونات التوجيه.
$ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "uri":"/*", "plugins":{ }, "upstream":{ "scheme":"grpc", "type":"roundrobin", "nodes":{ "127.0.0.1:50001":1 } } }'
الخلاصة
تقدم لك هذه المقالة تجربة عملية حول استخدام grpc-web
في Apache APISIX.
لا تتردد في بدء نقاش في GitHub Discussions أو التواصل عبر قائمة البريد.