基于 gRPC 的服務(wù)間通信示例
示例說(shuō)明,存在兩個(gè)服務(wù),訂單服務(wù)和產(chǎn)品服務(wù)。其中:
- 訂單服務(wù)提供 HTTP 接口,用于完成訂單查詢。訂單中包含產(chǎn)品信息,要利用 grpc 從產(chǎn)品服務(wù)獲取產(chǎn)品信息
- 產(chǎn)品服務(wù)提供 grpc 接口,用于響應(yīng)微服務(wù)內(nèi)部產(chǎn)品信息查詢
本例中,對(duì)于 grpc 來(lái)說(shuō),產(chǎn)品服務(wù)為服務(wù)端、訂單服務(wù)為客戶端。
同時(shí)不考慮其他業(yè)務(wù)邏輯,例如產(chǎn)品服務(wù)也需要對(duì)外提供 http 接口等,僅在乎 grpc 的通信示例。同時(shí)不考慮服務(wù)發(fā)現(xiàn)和網(wǎng)關(guān)等。

編碼實(shí)現(xiàn):
一:基于之前定義的 .proto 文件生成 pb.go 文件
注意,客戶端和服務(wù)端,都需要使用生成的 pb.go 文件
二:實(shí)現(xiàn)訂單服務(wù)
orderService/httpService.go
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
"net/http"
"orderService/protos/codes"
"time"
)
var (
// 目標(biāo) grpc 服務(wù)器地址
gRPCAddr = flag.String("grpc", "localhost:50051", "the address to connect to")
// http 命令行參數(shù)
addr = flag.String("addr", "127.0.0.1", "The Address for listen. Default is 127.0.0.1")
port = flag.Int("port", 8080, "The Port for listen. Default is 8080.")
)
func main() {
flag.Parse()
// 連接 grpc 服務(wù)器
conn, err := grpc.Dial(*gRPCAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
// 實(shí)例化 grpc 客戶端
c := codes.NewProductClient(conn)
// 定義業(yè)務(wù)邏輯服務(wù),假設(shè)為產(chǎn)品服務(wù)
service := http.NewServeMux()
service.HandleFunc("/orders", func(writer http.ResponseWriter, request *http.Request) {
// 調(diào)用 grpc 方法,完成對(duì)服務(wù)器資源請(qǐng)求
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.ProductInfo(ctx, &codes.ProductInfoRequest{
Int64: 42,
})
if err != nil {
log.Fatalln(err)
}
resp := struct {
ID int `json:"id"`
Quantity int `json:"quantity"`
Products []*codes.ProductInfoResponse `json:"products"`
}{
9527, 1,
[]*codes.ProductInfoResponse{
r,
},
}
respJson, err := json.Marshal(resp)
if err != nil {
log.Fatalln(err)
}
writer.Header().Set("Content-Type", "application/json")
_, err = fmt.Fprintf(writer, "%s", string(respJson))
if err != nil {
log.Fatalln(err)
}
})
// 啟動(dòng)監(jiān)聽(tīng)
address := fmt.Sprintf("%s:%d", *addr, *port)
fmt.Printf("Order service is listening on %s.\n", address)
log.Fatalln(http.ListenAndServe(address, service))
}
三,實(shí)現(xiàn)產(chǎn)品服務(wù)
productService/grpcService.go
package main
import (
"context"
"flag"
"fmt"
"google.golang.org/grpc"
"log"
"net"
"productService/protos/compiles"
)
//grpc 監(jiān)聽(tīng)端口
var port = flag.Int("port", 50051, "The server port")
// ProductServer 實(shí)現(xiàn) UnimplementedProductServer
type ProductServer struct {
compiles.UnimplementedProductServer
}
func (ProductServer) ProductInfo(ctx context.Context, pr *compiles.ProductInfoRequest) (*compiles.ProductInfoResponse, error) {
return &compiles.ProductInfoResponse{
Name: "馬士兵 Go 云原生",
Int64: 42,
IsSale: true,
}, nil
}
func main() {
flag.Parse()
//設(shè)置 tcp 監(jiān)聽(tīng)器
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// 新建 grpc Server
s := grpc.NewServer()
// 將 ProductServer 注冊(cè)到 grpc Server 中
compiles.RegisterProductServer(s, ProductServer{})
log.Printf("server listening at %v", lis.Addr())
// 啟動(dòng)監(jiān)聽(tīng)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
測(cè)試,訪問(wèn) order 的 http 接口。獲取訂單信息中,包含產(chǎn)品信息。
gRPC 核心概念
服務(wù)定義
與許多 RPC 系統(tǒng)一樣,gRPC 基于定義服務(wù)的思想,指定可以遠(yuǎn)程調(diào)用的方法及其參數(shù)和返回類(lèi)型。默認(rèn)情況下,gRPC 使用 Protocol Buffer 作為接口定義語(yǔ)言 (IDL) 來(lái)描述服務(wù)接口和有效負(fù)載消息的結(jié)構(gòu)。
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequest {
string greeting = 1;
}
message HelloResponse {
string reply = 1;
}
gRPC 支持定義四種服務(wù)方法:
- 一元 RPC,其中客戶端向服務(wù)器發(fā)送單個(gè)請(qǐng)求并獲得單個(gè)響應(yīng),就像正常的函數(shù)調(diào)用一樣。
rpc SayHello(HelloRequest) returns (HelloResponse);
- 服務(wù)器流式 RPC,其中客戶端向服務(wù)器發(fā)送請(qǐng)求并獲取流以讀回一系列消息??蛻舳藦姆祷氐牧髦凶x取,直到?jīng)]有更多消息為止。 gRPC 保證單個(gè) RPC 調(diào)用中的消息順序。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
- 客戶端流式 RPC,其中客戶端寫(xiě)入一系列消息并將它們發(fā)送到服務(wù)器,再次使用提供的流。一旦客戶端完成了消息的寫(xiě)入,它就會(huì)等待服務(wù)器讀取它們并返回它的響應(yīng)。 gRPC 再次保證了單個(gè) RPC 調(diào)用中的消息順序。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
- 雙向流式 RPC,雙方使用讀寫(xiě)流發(fā)送一系列消息。這兩個(gè)流獨(dú)立運(yùn)行,因此客戶端和服務(wù)器可以按照他們喜歡的任何順序讀取和寫(xiě)入:例如,服務(wù)器可以在寫(xiě)入響應(yīng)之前等待接收所有客戶端消息,或者它可以交替讀取消息然后寫(xiě)入消息,或其他一些讀取和寫(xiě)入的組合。保留每個(gè)流中消息的順序。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);
使用 API
從 .proto 文件中的服務(wù)定義開(kāi)始,gRPC 提供了生成客戶端和服務(wù)器端代碼的 Protocol Buffer 編譯器插件。 gRPC 用戶通常在客戶端調(diào)用這些 API,并在服務(wù)器端實(shí)現(xiàn)相應(yīng)的 API。
- 在服務(wù)端,服務(wù)端實(shí)現(xiàn)服務(wù)聲明的方法,并運(yùn)行一個(gè) gRPC 服務(wù)器來(lái)處理客戶端調(diào)用。 gRPC 基礎(chǔ)架構(gòu)解碼傳入請(qǐng)求、執(zhí)行服務(wù)方法并編碼服務(wù)響應(yīng)。
- 在客戶端,客戶端有一個(gè)稱(chēng)為存根的本地對(duì)象(對(duì)于某些語(yǔ)言,首選術(shù)語(yǔ)是客戶端),它實(shí)現(xiàn)與服務(wù)相同的方法。然后客戶端可以在本地對(duì)象上調(diào)用這些方法,將調(diào)用的參數(shù)包裝在適當(dāng)?shù)膮f(xié)議緩沖區(qū)消息類(lèi)型中——gRPC 負(fù)責(zé)將請(qǐng)求發(fā)送到服務(wù)器并返回服務(wù)器的協(xié)議緩沖區(qū)響應(yīng)。
同步與異步
在接收到服務(wù)端響應(yīng)之前阻塞的同步 RPC 調(diào)用最接近 RPC 所希望的過(guò)程調(diào)用的抽象。另一方面,網(wǎng)絡(luò)本質(zhì)上是異步的,在許多情況下,能夠在不阻塞當(dāng)前線程的情況下啟動(dòng) RPC 是很有用的。
大多數(shù)語(yǔ)言中的 gRPC 編程 API 有同步和異步兩種風(fēng)格。
gRPC 生命周期
生命周期指的是 gRPC 客戶端調(diào)用 gRPC 服務(wù)端方法的過(guò)程。區(qū)別于不同的4種服務(wù)定義,過(guò)程如下:
一元 RPC
首先考慮最簡(jiǎn)單的 RPC 類(lèi)型,其中客戶端發(fā)送單個(gè)請(qǐng)求并返回單個(gè)響應(yīng)。
- 一旦客戶端調(diào)用了一個(gè)存根方法,服務(wù)器就會(huì)被通知該 RPC 已被調(diào)用,其中包含該調(diào)用的客戶端元數(shù)據(jù)、方法名稱(chēng)和指定的截止日期(如果適用)。
- 然后,服務(wù)器可以立即發(fā)回自己的初始元數(shù)據(jù)(必須在任何響應(yīng)之前發(fā)送),或者等待客戶端的請(qǐng)求消息。首先發(fā)生的是特定于應(yīng)用程序的。
- 一旦服務(wù)器收到客戶端的請(qǐng)求消息,它就會(huì)執(zhí)行任何必要的工作來(lái)創(chuàng)建和填充響應(yīng)。然后將響應(yīng)連同狀態(tài)詳細(xì)信息(狀態(tài)代碼和可選狀態(tài)消息)和可選尾隨元數(shù)據(jù)一起返回(如果成功)給客戶端。
- 如果響應(yīng)狀態(tài)為 OK,則客戶端得到響應(yīng),從而完成客戶端的調(diào)用。
服務(wù)器流式 RPC
服務(wù)器流式 RPC 類(lèi)似于一元 RPC,除了服務(wù)器返回消息流以響應(yīng)客戶端的請(qǐng)求。發(fā)送所有消息后,服務(wù)器的狀態(tài)詳細(xì)信息(狀態(tài)代碼和可選狀態(tài)消息)和可選的尾隨元數(shù)據(jù)將發(fā)送到客戶端。這樣就完成了服務(wù)器端的處理。客戶端在擁有所有服務(wù)器消息后完成。
客戶端流式 RPC
客戶端流式 RPC 類(lèi)似于一元 RPC,不同之處在于客戶端向服務(wù)器發(fā)送消息流而不是單個(gè)消息。服務(wù)器響應(yīng)一條消息(連同其狀態(tài)詳細(xì)信息和可選的尾隨元數(shù)據(jù)),通常但不一定是在它收到所有客戶端的消息之后。
雙向流式 RPC
在雙向流式 RPC 中,調(diào)用由調(diào)用方法的客戶端和接收客戶端元數(shù)據(jù)、方法名稱(chēng)和截止日期的服務(wù)器發(fā)起。服務(wù)器可以選擇發(fā)回其初始元數(shù)據(jù)或等待客戶端開(kāi)始流式傳輸消息。
客戶端和服務(wù)器端流處理是特定于應(yīng)用程序的。由于這兩個(gè)流是獨(dú)立的,客戶端和服務(wù)器可以以任意順序讀寫(xiě)消息。例如,服務(wù)器可以等到它收到客戶端的所有消息后再寫(xiě)入它的消息,或者服務(wù)器和客戶端可以玩 “ping-pong”——服務(wù)器收到請(qǐng)求,然后發(fā)回響應(yīng),然后客戶端發(fā)送基于響應(yīng)的另一個(gè)請(qǐng)求,依此類(lèi)推。
截止日期/超時(shí)
gRPC 允許客戶端指定在 RPC 因 DEADLINE_EXCEEDED 錯(cuò)誤而終止之前,他們?cè)敢獾却?RPC 完成多長(zhǎng)時(shí)間。在服務(wù)器端,服務(wù)器可以查詢特定的 RPC 是否已超時(shí),或者還剩多少時(shí)間來(lái)完成 RPC。
指定期限或超時(shí)是特定于語(yǔ)言的:一些語(yǔ)言 API 根據(jù)超時(shí)(持續(xù)時(shí)間)工作,而一些語(yǔ)言 API 根據(jù)期限(固定時(shí)間點(diǎn))工作,可能有也可能沒(méi)有默認(rèn)期限。
RPC 終止
在 gRPC 中,客戶端和服務(wù)器都對(duì)調(diào)用是否成功做出獨(dú)立的本地判斷,并且它們的結(jié)論可能不匹配。這意味著,例如,您可能有一個(gè) RPC 在服務(wù)器端成功完成(“我已經(jīng)發(fā)送了所有響應(yīng)!”)但在客戶端失敗(“響應(yīng)在我的截止日期之后到達(dá)!”)。服務(wù)器也可以在客戶端發(fā)送所有請(qǐng)求之前決定完成。
取消 RPC
客戶端或服務(wù)器都可以隨時(shí)取消 RPC。取消會(huì)立即終止 RPC,以便不再進(jìn)行任何工作。
Protocol buffer 語(yǔ)法參考
消息類(lèi)型定義
以一個(gè)簡(jiǎn)單的請(qǐng)求消息為例:
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
首先指定版本 proto3,否則編譯器默認(rèn)為 proto2。版本指定為文件的第一非空白、非注釋行。
message 關(guān)鍵字用于定義消息,需要指定消息類(lèi)型的名稱(chēng)。
消息由多個(gè)名稱(chēng)/值對(duì)組成,稱(chēng)為字段,每個(gè)字段要指定名字和類(lèi)型。string、int32 是典型的標(biāo)量類(lèi)型,除了標(biāo)量類(lèi)型 protobuf 還支持構(gòu)造類(lèi)型,例如枚舉或其他消息類(lèi)型。
應(yīng)該為每個(gè)字段分配唯一的字段序號(hào),用于在二進(jìn)制編碼中標(biāo)識(shí)該字段。序號(hào)范圍1-15會(huì)消耗1個(gè)字節(jié)的存儲(chǔ),16-2047 會(huì)消耗2個(gè)字節(jié)。因此應(yīng)該將常用的字段分配1-15字段序號(hào)。編號(hào)全部的范圍是1到2^29-1,其中19000到19999是 proto編譯器保留序號(hào),不要使用。
消息的字段分為單一和重復(fù)兩種規(guī)則:
- 單一 Singular,proto3 中字段的默認(rèn)規(guī)則。一個(gè)消息中僅可以包含0或1個(gè)該字段,就是字段不能重復(fù)。
- 重復(fù)的 repeated,該規(guī)則說(shuō)明此字段可以重復(fù)多次(包含0次)。重復(fù)值的順序是保留的。
message SearchRequest {
// 同上略
repeated string keywords = 4
}
.proto 文件支持 C/C++ 風(fēng)格的注釋 // 和 /* ... */
標(biāo)量類(lèi)型
標(biāo)量消息字段可以具有以下類(lèi)型之一。該表顯示了 .proto 文件中指定的類(lèi)型,以及自動(dòng)生成的類(lèi)中的相應(yīng)類(lèi)型:
| .proto Type | 說(shuō)明 | Go Type |
|---|---|---|
| double | float64 | |
| float | float32 | |
| int32 | 變長(zhǎng)編碼,對(duì)負(fù)數(shù)進(jìn)行編碼效率低下。若字段可能有負(fù)值,請(qǐng)改用 sint32 | int32 |
| int64 | 變長(zhǎng)編碼,對(duì)負(fù)數(shù)進(jìn)行編碼效率低下。若字段可能有負(fù)值,請(qǐng)改用 sint64 | int64 |
| uint32 | 變長(zhǎng)編碼 | uint32 |
| uint64 | 變長(zhǎng)編碼 | uint64 |
| sint32 | 變長(zhǎng)編碼,帶符號(hào)的 int 值。這些比常規(guī) int32 更有效地編碼負(fù)數(shù) | int32 |
| sint64 | 變長(zhǎng)編碼,帶符號(hào)的 int 值。這些比常規(guī) int64 更有效地編碼負(fù)數(shù) | int64 |
| fixed32 | 固定4個(gè)字節(jié),如果值通常大于 2^28,則比 uint32 更有效 | uint32 |
| fixed64 | 固定8個(gè)字節(jié),如果值通常大于 2^56,則比 uint64 更有效 | uint64 |
| sfixed32 | 固定4個(gè)字節(jié) | int32 |
| sfixed64 | 固定8個(gè)字節(jié) | int64 |
| bool | bool | |
| string | 始終包含 UTF-8 編碼或 7 位 ASCII 文本,并且長(zhǎng)度不能超過(guò) 2^32 | string |
| bytes | 可以包含不超過(guò) 2^32 的任意字節(jié)序列 | []byte |
解析消息時(shí),如果編碼的消息不包含特定元素,則解析對(duì)象中的相應(yīng)字段將設(shè)置為該字段的默認(rèn)值。這些默認(rèn)值是基于類(lèi)型的:
- 對(duì)于字符串,默認(rèn)值為空字符串。
- 對(duì)于字節(jié),默認(rèn)值為空字節(jié)。
- 對(duì)于布爾值,默認(rèn)值為 false。
- 對(duì)于數(shù)字類(lèi)型,默認(rèn)值為零。
- 對(duì)于枚舉,默認(rèn)值是第一個(gè)定義的枚舉值,必須為 0。
- 對(duì)于消息字段,未設(shè)置該字段。它的確切值取決于語(yǔ)言。有關(guān)詳細(xì)信息,請(qǐng)參閱生成的代碼指南。
- 重復(fù)字段的默認(rèn)值為空(通常是相應(yīng)語(yǔ)言的空列表)。
枚舉值
在定義消息類(lèi)型時(shí),您可能希望其字段之一僅具有預(yù)定義的值列表之一。例如,假設(shè)您要為每個(gè) SearchRequest 添加一個(gè) corpus 字段,其中值可以是 UNIVERSAL、WEB、IMAGES、LOCAL、NEWS、PRODUCTS 或 VIDEO。您可以通過(guò)在消息定義中添加一個(gè)枚舉來(lái)非常簡(jiǎn)單地做到這一點(diǎn),每個(gè)可能的值都有一個(gè)常量。
message SearchRequest {
// 同上略
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
枚舉值列表的第一常量值必須為0,這樣可以更好的處理默認(rèn)值。(也為了向下兼容)
也可以為同一個(gè)枚舉值分配不同的常量,稱(chēng)為別名。需要使用選項(xiàng) option allow_alias = true 來(lái)啟用別名設(shè)置:
message MyMessage1 {
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
}
保留值
當(dāng)某些字段不再使用時(shí),例如更新消息類(lèi)型時(shí)移除了某些字段,為了防止其他人重新使用了之前的字段名或字段序號(hào)而導(dǎo)致邏輯混亂的問(wèn)題,可以把這些不用的字段設(shè)置為保留字段,關(guān)鍵字 reserved 用來(lái)設(shè)置:
enum Foo {
reserved 2, 15, 9 to 11, 40 to max;
reserved "FOO", "BAR";
}
這樣,以上的序號(hào)和字段名就不能后續(xù)使用了,避免了邏輯混亂。
使用其他消息類(lèi)型
可以使用其他消息類(lèi)型作為字段類(lèi)型。例如:
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
可以將不同類(lèi)型的消息定義在不同的 .proto 文件中,需要時(shí)導(dǎo)入進(jìn)來(lái):
import "myproject/other_protos.proto";
未知字段
未知字段是格式良好的 Protocol Buffer 序列化數(shù)據(jù),表示解析器無(wú)法識(shí)別的字段。例如,當(dāng)舊二進(jìn)制文件用新字段解析新二進(jìn)制文件發(fā)送的數(shù)據(jù)時(shí),這些新字段將成為舊二進(jìn)制文件中的未知字段。
最初,proto3 消息在解析過(guò)程中總是丟棄未知字段,但在 3.5 版本中,我們重新引入了保留未知字段以匹配 proto2 行為。在 3.5 及更高版本中,未知字段在解析期間保留并包含在序列化輸出中。
Any
Any 消息類(lèi)型允許您將消息用作嵌入類(lèi)型,而無(wú)需定義它們的 .proto。 Any 包含作為 Bytes 的任意序列化消息,以及充當(dāng)全局唯一標(biāo)識(shí)符并解析為該消息類(lèi)型的 URL。要使用 Any 類(lèi)型,您需要導(dǎo)入 google/protobuf/any.proto。
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}
Oneof
如果您有一條包含多個(gè)字段的消息,并且最多同時(shí)設(shè)置一個(gè)字段,您可以強(qiáng)制執(zhí)行此行為并使用 oneof 功能節(jié)省內(nèi)存。
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}
Map
如果您想創(chuàng)建關(guān)聯(lián)映射作為數(shù)據(jù)定義的一部分,protocol buffers 提供了一種方便的快捷語(yǔ)法:
map<key_type, value_type> map_field = N;
例如:
map<string, Project> projects = 3;
Packages
您可以將可選的 package 說(shuō)明符添加到 .proto 文件中,以防止協(xié)議消息類(lèi)型之間的名稱(chēng)沖突。
package foo.bar;
message Open { ... }
在 GO 中,該軟件包被用作 GO 軟件包名稱(chēng),除非您在 .proto 文件中明確提供 option go_package。
服務(wù)定義
如果您想在 RPC(遠(yuǎn)程過(guò)程調(diào)用)系統(tǒng)中使用您的消息類(lèi)型,您可以在 .proto 文件中定義一個(gè) RPC 服務(wù)接口,并且協(xié)議緩沖區(qū)編譯器將以您選擇的語(yǔ)言生成服務(wù)接口代碼和存根。因此,例如,如果您想使用獲取 SearchRequest 并返回 SearchResponse 的方法定義 RPC 服務(wù),您可以在 .proto 文件中定義它,如下所示:
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse);
}
與 Proto Buffers 一起使用的最直接的 RPC 系統(tǒng)是 gRPC:由 Google 開(kāi)發(fā)的一種語(yǔ)言和平臺(tái)中立的開(kāi)源 RPC 系統(tǒng)。 gRPC 特別適用于協(xié)議緩沖區(qū),并允許您使用特殊的協(xié)議緩沖區(qū)編譯器插件直接從 .proto 文件生成相關(guān)的 RPC 代碼。
選項(xiàng)
.proto 文件中支持定義選項(xiàng)。全部的選項(xiàng)定義在 google/protobuf/descriptor.proto 中。
例如我們使用 option go_package 選項(xiàng)來(lái)控制生成的 go 代碼所在的 package。
Protocol buffer 語(yǔ)法指導(dǎo)
消息隊(duì)列
事件驅(qū)動(dòng)
API 網(wǎng)關(guān)
API 網(wǎng)關(guān)介紹

API 網(wǎng)關(guān)是客戶端訪問(wèn)服務(wù)的統(tǒng)一入口,API 網(wǎng)關(guān)封裝了后端服務(wù)。核心功能是轉(zhuǎn)發(fā)請(qǐng)求,基于客戶端請(qǐng)求的 Host、Method、Path 將請(qǐng)求轉(zhuǎn)發(fā)到目標(biāo)服務(wù)上??梢越鉀Q客戶端直接訪問(wèn)后端服務(wù)的入口不統(tǒng)一的問(wèn)題,尤其是在微服務(wù)時(shí)代,網(wǎng)關(guān)尤其重要,否則如下圖所示:

現(xiàn)代的 API 網(wǎng)關(guān),除了具備基本的轉(zhuǎn)發(fā)功能外,通常還具有:
- 多協(xié)議支持:tcp,http、https、websock、gRPC
- 負(fù)載均衡
- 身份驗(yàn)證
- 監(jiān)控、日志
- 緩存
- 熔斷、限流
等核心功能。目前市場(chǎng)上比較知名的 API 網(wǎng)關(guān)有:
- Netflix Zuul,spring cloud 的一個(gè)推薦組件,java 系首選
- Kong 是基于Nginx+Lua 進(jìn)行二次開(kāi)發(fā)的實(shí)現(xiàn),社區(qū)比較活躍
- Tyk Go編寫(xiě),社區(qū)版相對(duì)薄弱