哪里看字?jǐn)?shù)(wps在哪里看字?jǐn)?shù)是多少)">WPS在哪里看字?jǐn)?shù)(wps在哪里看字?jǐn)?shù)是多少)
2181
2022-05-30
【引言】
gRPC(gRPC Remote Procedure Calls)是一個(gè)開源的遠(yuǎn)程過程調(diào)用(RPC)系統(tǒng),由谷歌公司最先在2015年開發(fā)。它使用HTTP/2作為傳輸方式,協(xié)議緩沖器作為接口描述語言,提供了認(rèn)證、雙向流和流控制、阻塞或非阻塞綁定、取消和超時(shí)等功能。它可以為許多編程語言生成跨平臺(tái)的客戶端和服務(wù)器綁定。最常見的使用場景包括在微服務(wù)架構(gòu)中的服務(wù)連接,以及移動(dòng)設(shè)備、網(wǎng)頁客戶端連接到后端服務(wù)等。
【概述】
在gRPC中,客戶端應(yīng)用程序可以直接調(diào)用不同服務(wù)器應(yīng)用程序上的方法,就像調(diào)用本地對(duì)象接口一樣,這讓你更容易創(chuàng)建分布式應(yīng)用和服務(wù)。和許多RPC系統(tǒng)一樣,gRPC也是圍繞著定義一個(gè)服務(wù)的思想,指定可以遠(yuǎn)程調(diào)用的方法,參數(shù)和返回類型。在服務(wù)器端,服務(wù)程序?qū)崿F(xiàn)這個(gè)接口,并運(yùn)行g(shù)RPC服務(wù)器來處理客戶端的調(diào)用。在客戶端上有一個(gè)存根,這個(gè)存根提供與服務(wù)器相同的方法。
gRPC客戶端和服務(wù)器可以在各種環(huán)境下運(yùn)行和對(duì)話,并且可以用gRPC支持的任何一種語言編寫。比如,你可以輕松地用Java創(chuàng)建一個(gè)gRPC服務(wù)器,用Go、Python或Ruby的客戶端來調(diào)用。此外,最新的Google API都會(huì)有g(shù)RPC版本的接口,讓你可以輕松地將Google功能構(gòu)建到你的應(yīng)用程序中。
【gRPC的優(yōu)點(diǎn)】
性能
gRPC消息使用Protobuf進(jìn)行序列化,Protobuf是一種高效的二進(jìn)制消息格式。Protobuf在服務(wù)器和客戶端上的序列化速度非常快。Protobuf序列化的結(jié)果是消息的有效載荷小,這對(duì)于移動(dòng)應(yīng)用等有限帶寬的場景來說非常重要。
gRPC是為HTTP/2設(shè)計(jì)的,這是HTTP的一個(gè)重要修訂版,它比HTTP 1.x提供了顯著的性能優(yōu)勢(shì)。
l??二進(jìn)制框架和壓縮技術(shù)的使用使得HTTP/2協(xié)議在發(fā)送和接收方面都非常緊湊和高效。
l??在單一TCP連接上實(shí)現(xiàn)多個(gè)HTTP/2調(diào)用的復(fù)用。多路復(fù)用消除了線頭阻塞。
代碼通過工具生成
所有的gRPC框架都提供了一流的代碼生成支持。gRPC開發(fā)的一個(gè)核心文件是.proto文件,它定義了gRPC服務(wù)和消息的契約。從這個(gè)文件中,gRPC框架將代碼生成一個(gè)服務(wù)基類、消息和一個(gè)完整的客戶端。
通過在服務(wù)器和客戶端之間共享.proto文件,消息和客戶端的代碼可以從端到端的生成。客戶端的代碼生成消除了客戶端和服務(wù)器上的消息重復(fù),為你創(chuàng)建一個(gè)強(qiáng)類型化的客戶端。在有很多服務(wù)的應(yīng)用程序中,不需要編寫客戶端,這可以節(jié)省大量的開發(fā)時(shí)間。
嚴(yán)格的規(guī)范
目前還沒有一個(gè)針對(duì)使用JSON的HTTP API的正式規(guī)范。開發(fā)者們一直對(duì)URL、HTTP動(dòng)詞和響應(yīng)代碼的最佳格式爭論不休。
gRPC規(guī)范對(duì)gRPC服務(wù)必須遵循的格式是有規(guī)定的。gRPC消除了爭論,從而節(jié)省了開發(fā)者的時(shí)間,因?yàn)間RPC在不同平臺(tái)和實(shí)現(xiàn)上是一致的。
流處理
HTTP/2為長效實(shí)時(shí)通信流提供了基礎(chǔ),gRPC通過HTTP/2提供了一流的流處理支持。
一個(gè)gRPC服務(wù)支持所有的流處理組合:
l??單一式(無流處理)
l??服務(wù)器到客戶端的流處理
l??客戶端到服務(wù)器的流處理
l??雙向流處理
超時(shí)取消機(jī)制
gRPC允許客戶端指定他們?cè)敢獾却嚅L時(shí)間完成一個(gè)RPC。把這個(gè)時(shí)間決定的最后期限被發(fā)送至服務(wù)器,服務(wù)器可以決定超過最后期限時(shí)采取什么行動(dòng)。例如,服務(wù)器可以在超時(shí)后取消正在進(jìn)行中的gRPC或HTTP或數(shù)據(jù)庫請(qǐng)求。
通過對(duì)gRPC調(diào)用設(shè)置超時(shí)取消機(jī)制,有助于實(shí)現(xiàn)對(duì)資源的限制使用。
【gRPC的緩沖器與認(rèn)證】
【使用協(xié)議緩沖器】
gRPC使用協(xié)議緩沖區(qū)對(duì)數(shù)據(jù)進(jìn)行編碼。與使用JSON的HTTP API不同,它們有一個(gè)更嚴(yán)格的規(guī)范。
協(xié)議緩沖區(qū)是Google成熟的開源機(jī)制,用于序列化結(jié)構(gòu)化數(shù)據(jù)(盡管它可以使用其他數(shù)據(jù)格式,如JSON)。以下是對(duì)其工作原理的簡單介紹。
在使用協(xié)議緩沖區(qū)時(shí),第一步是在proto文件中定義要序列化的數(shù)據(jù)結(jié)構(gòu):是一個(gè)普通的文本文件,擴(kuò)展名為.proto。協(xié)議緩沖區(qū)的數(shù)據(jù)是以消息的形式結(jié)構(gòu)化,每個(gè)消息都是一個(gè)小的邏輯信息記錄,其中包含一系列稱為字段的名-值對(duì)。下面是一個(gè)簡單的例子:
message?Person?{
string?name?=?1;
int32?id?=?2;
bool?has_ponycopter?=?3;
}
接著,一旦你指定了你的數(shù)據(jù)結(jié)構(gòu),你就可以使用協(xié)議緩沖區(qū)編譯器?protoc?從你的?proto?定義中生成你喜歡的編程語言的數(shù)據(jù)訪問類。這些類為每個(gè)字段提供了簡單的訪問器,如?name()和?set_name(),以及將整個(gè)結(jié)構(gòu)序列化/解析為原始字節(jié)的方法。因此,例如,如果你選擇的語言是C++,用上面的例子中運(yùn)行,編譯器會(huì)生成一個(gè)名為Person的類。然后你可以在你的應(yīng)用程序中使用這個(gè)類來填充、序列化和檢索Person協(xié)議緩沖區(qū)消息。
你在普通的proto文件中定義gRPC服務(wù),RPC方法參數(shù)和返回類型指定為協(xié)議緩沖區(qū)消息:
//?greeter?服務(wù)定義.
service?Greeter?{
//?發(fā)出問候
rpc?SayHello?(HelloRequest)?returns?(HelloReply)?{}
}
//?請(qǐng)求信息包含用戶名字
message?HelloRequest?{
string?name?=?1;
}
//?響應(yīng)消息包含問候消息
message?HelloReply?{
string?message?=?1;
}
gRPC?使用?protoc?和一個(gè)特殊的?gRPC?插件來從你的?proto?文件中生成代碼:你會(huì)得到生成的?gRPC?客戶端和服務(wù)器代碼,以及用于補(bǔ)足、序列化和檢索消息類型的常規(guī)協(xié)議緩沖區(qū)代碼。
【協(xié)議緩沖區(qū)版本】
雖然協(xié)議緩沖區(qū)已經(jīng)向開源用戶提供了一段時(shí)間,目前推薦使用協(xié)議緩沖區(qū)3版(proto3),它的語法稍有簡化,并添加了一些有用的新功能,而且支持了更多的語言。
Proto3目前支持Java、C++、Dart、Python、Objective-C、C#、Android Java、Ruby、Golang和JavaScript,,還有更多語言正在開發(fā)中。
一般來說,雖然可以使用proto2(當(dāng)前默認(rèn)的協(xié)議緩沖區(qū)版本),但建議你使用gRPC時(shí)使用proto3,因?yàn)樗С謌RPC目前支持的所有語言,同時(shí)也避免了proto2客戶端與proto3服務(wù)器的兼容性問題,反之亦然。
【認(rèn)證】
gRPC支持使用TLS和基于令牌的認(rèn)證。與Google服務(wù)的連接必須使用TLS。
gRPC設(shè)計(jì)的初衷是可以與各種認(rèn)證機(jī)制一起工作,從而可以很容易地通過gRPC與其他系統(tǒng)對(duì)話。
gRPC還提供了一個(gè)簡單的認(rèn)證API,讓您在創(chuàng)建通道或調(diào)用時(shí)提供所有必要的認(rèn)證信息作為憑證。
憑證類型
憑證可以分為兩種類型:
l??通道憑證,附加在通道上,如SSL憑證。
l??調(diào)用憑證,它附加到一個(gè)調(diào)用(或C++中的ClientContext)上。
你也可以在CompositeChannelCredentials中結(jié)合這些內(nèi)容,例如,你可以為通道指定SSL的詳細(xì)信息,并為通道上的每個(gè)呼叫指定呼叫憑證。CompositeChannelCredentials?將通道憑證和呼叫憑證關(guān)聯(lián)起來,以創(chuàng)建一個(gè)新的通道憑證。其結(jié)果將在信道上的每一次呼叫中發(fā)送與組成的CallCredentials相關(guān)聯(lián)的認(rèn)證數(shù)據(jù)。
比如說,你可以從一個(gè)SSLCredentials和一個(gè)AccessTokenCredentials創(chuàng)建一個(gè)ChannelCredentials。當(dāng)應(yīng)用到一個(gè)Channel時(shí),其結(jié)果將為這個(gè)通道上的每個(gè)呼叫發(fā)送相應(yīng)的訪問令牌。
單個(gè)CallCredentials也可以用CompositeCallCredentials組成。在呼叫中使用的CallCredentials將觸發(fā)與兩個(gè)CallCredentials相關(guān)聯(lián)的認(rèn)證數(shù)據(jù)的發(fā)送。
gRPC具有SSL/TLS集成功能,提倡使用SSL/TLS來驗(yàn)證服務(wù)器,并對(duì)客戶端和服務(wù)器之間交換的所有數(shù)據(jù)進(jìn)行加密。客戶端可選擇提供相互認(rèn)證的證書機(jī)制。
現(xiàn)在我們來看看Credentials是如何與我們支持的一種認(rèn)證機(jī)制一起工作的。我們假定最簡單的驗(yàn)證場景,客戶端只想驗(yàn)證服務(wù)器并加密所有數(shù)據(jù)。這個(gè)例子是用C++語言編寫的,但所有語言的API都是類似的。
//?創(chuàng)建一個(gè)默認(rèn)的SSL?ChannelCredentials對(duì)象。
auto?channel_creds?=?grpc::SslCredentials(grpc::SslCredentialsOptions());
//?使用上一步中創(chuàng)建的憑證創(chuàng)建一個(gè)通道。
auto?channel?=?grpc::CreateChannel(server_name,?channel_creds);
//?在通道上創(chuàng)建一個(gè)存根。
std::unique_ptr
//?在存根上進(jìn)行實(shí)際的RPC調(diào)用。
grpc::Status?s?=?stub->sayHello(&context,?*request,?response);
對(duì)于高級(jí)用例,如修改根CA或使用客戶端證書,可以在傳遞給工廠方法的?SslCredentialsOptions?參數(shù)中設(shè)置相應(yīng)的選項(xiàng)。
基于令牌的身份驗(yàn)證
gRPC提供了一個(gè)通用的機(jī)制,可以將基于元數(shù)據(jù)的憑證附加到請(qǐng)求和響應(yīng)中。這種機(jī)制專門用于訪問Google?的API服務(wù)。?一般來說,Google不允許沒有SSL/TLS的連接,而且大多數(shù)gRPC語言實(shí)現(xiàn)也不會(huì)讓你在未加密的通道上發(fā)送憑證。
gRPC應(yīng)用程序可以使用一個(gè)簡單的API來創(chuàng)建一個(gè)在各種部署場景中與Google進(jìn)行認(rèn)證的憑證。例子:
auto?creds?=?grpc::GoogleDefaultCredentials();
//?創(chuàng)建一個(gè)通道,存根并進(jìn)行RPC調(diào)用(與上例的功能一樣)
auto?channel?=?grpc::CreateChannel(server_name,?creds);
std::unique_ptr
grpc::Status?s?=?stub->sayHello(&context,?*request,?response);
這個(gè)通道憑證對(duì)象適用于使用服務(wù)賬戶的應(yīng)用程序和在?Google Compute Engine (GCE)?中運(yùn)行的應(yīng)用程序。在前一種情況下,服務(wù)賬戶的私鑰是從環(huán)境變量?GOOGLE_APPLICATION_CREDENTIALS?中命名的文件中加載的。這些密鑰用于生成附加到相應(yīng)通道上的每個(gè)出站RPC的承載令牌。
對(duì)于在?GCE?中運(yùn)行的應(yīng)用程序,可以在?VM?設(shè)置期間配置一個(gè)默認(rèn)服務(wù)帳戶和相應(yīng)的?OAuth2?作用域。在運(yùn)行時(shí),該憑證處理與認(rèn)證系統(tǒng)的通信,以獲取OAuth2訪問令牌,并將其附加到相應(yīng)通道上的每個(gè)出站RPC上。
擴(kuò)展gRPC以支持其他認(rèn)證機(jī)制
憑證插件API允許開發(fā)人員插入自己的憑證類型。這些插件有:
l??MetadataCredentialsPlugin抽象類,它包含純虛擬的GetMetadata方法,需要由開發(fā)者創(chuàng)建的子類來實(shí)現(xiàn)。
l??MetadataCredentialsFromPlugin函數(shù),它從
MetadataCredentialsPlugin中創(chuàng)建一個(gè)CallCredentials。
下面是一個(gè)簡單的憑證插件的例子,它可以在自定義頭中設(shè)置一個(gè)認(rèn)證依據(jù)。
類實(shí)現(xiàn):
class?MyCustomAuthenticator?:?public?grpc::MetadataCredentialsPlugin?{
public:
MyCustomAuthenticator(const?grpc::string&?ticket)?:?ticket_(ticket)?{}
grpc::Status?GetMetadata(
grpc::string_ref?service_url,?grpc::string_ref?method_name,
const?grpc::AuthContext&?channel_auth_context,
std::multimap
metadata->insert(std::make_pair("x-custom-auth-ticket",?ticket_));
return?grpc::Status::OK;
}
private:
grpc::string?ticket_;
};
類調(diào)用:
auto?call_creds?=?grpc::MetadataCredentialsFromPlugin(
std::unique_ptr
new?MyCustomAuthenticator("super-secret-ticket")));
通過在核心層插入gRPC憑證實(shí)現(xiàn),可以實(shí)現(xiàn)更深層次的集成。
更多例子
這些認(rèn)證機(jī)制將適用于所有g(shù)RPC支持的語言。
conn,?_?:=?grpc.Dial("localhost:50051",?grpc.WithInsecure())
//?此處需要添加錯(cuò)誤處理
client?:=?pb.NewGreeterClient(conn)
//?...
s?:=?grpc.NewServer()
lis,?_?:=?net.Listen("tcp",?"localhost:50051")
//?此處需要添加錯(cuò)誤處理
s.Serve(lis)
creds,?_?:=?credentials.NewClientTLSFromFile(certFile,?"")
conn,?_?:=?grpc.Dial("localhost:50051",?grpc.WithTransportCredentials(creds))
//?此處需要添加錯(cuò)誤處理
client?:=?pb.NewGreeterClient(conn)
//?...
creds,?_?:=?credentials.NewServerTLSFromFile(certFile,?keyFile)
s?:=?grpc.NewServer(grpc.Creds(creds))
lis,?_?:=?net.Listen("tcp",?"localhost:50051")
//?此處需要添加錯(cuò)誤處理
s.Serve(lis)
pool,?_?:=?x509.SystemCertPool()
//?此處需要添加錯(cuò)誤處理
creds?:=?credentials.NewClientTLSFromCert(pool,?"")
perRPC,?_?:=?oauth.NewServiceAccountFromFile("service-account.json",?scope)
conn,?_?:=?grpc.Dial(
"greeter.googleapis.com",
grpc.WithTransportCredentials(creds),
grpc.WithPerRPCCredentials(perRPC),
)
//?此處需要添加錯(cuò)誤處理
client?:=?pb.NewGreeterClient(conn)
//?...
stub?=?Helloworld::Greeter::Stub.new('localhost:50051',?:this_channel_is_insecure)
creds?=?GRPC::Core::ChannelCredentials.new(load_certs)??#?load_certs通常加載一個(gè)CA根文件
stub?=?Helloworld::Greeter::Stub.new('myservice.example.com',?creds)
require?'googleauth'??#?from?http://www.rubydoc.info/gems/googleauth/0.1.0
...
ssl_creds?=?GRPC::Core::ChannelCredentials.new(load_certs)??#?load_certs通常加載一個(gè)CA根文件
authentication?=?Google::Auth.get_application_default()
call_creds?=?GRPC::Core::CallCredentials.new(authentication.updater_proc)
combined_creds?=?ssl_creds.compose(call_creds)
stub?=?Helloworld::Greeter::Stub.new('greeter.googleapis.com',?combined_creds)
auto?channel?=?grpc::CreateChannel("localhost:50051",?InsecureChannelCredentials());
std::unique_ptr
...
auto?channel_creds?=?grpc::SslCredentials(grpc::SslCredentialsOptions());
auto?channel?=?grpc::CreateChannel("myservice.example.com",?channel_creds);
std::unique_ptr
...
auto?creds?=?grpc::GoogleDefaultCredentials();
auto?channel?=?grpc::CreateChannel("greeter.googleapis.com",?creds);
std::unique_ptr
...
var?channel?=?new?Channel("localhost:50051",?ChannelCredentials.Insecure);
var?client?=?new?Greeter.GreeterClient(channel);
...
var?channelCredentials?=?new?SslCredentials(File.ReadAllText("roots.pem"));??//?Load?a?custom?roots?file.
var?channel?=?new?Channel("myservice.example.com",?channelCredentials);
var?client?=?new?Greeter.GreeterClient(channel);
using?Grpc.Auth;??//?從Grpc.Auth?NuGet包中獲取
...
//?加載Google應(yīng)用的默認(rèn)憑證與公開信任的根。
var?channelCredentials?=?await?GoogleGrpcCredentials.GetApplicationDefaultAsync();
var?channel?=?new?Channel("greeter.googleapis.com",?channelCredentials);
var?client?=?new?Greeter.GreeterClient(channel);
...
var?channel?=?new?Channel("greeter.googleapis.com",?new?SslCredentials());??//?Use?publicly?trusted?roots.
var?client?=?new?Greeter.GreeterClient(channel);
...
var?googleCredential?=?await?GoogleCredential.GetApplicationDefaultAsync();
var?result?=?client.SayHello(request,?new?CallOptions(credentials:?googleCredential.ToCallCredentials()));
...
import?grpc
import?helloworld_pb2
channel?=?grpc.insecure_channel('localhost:50051')
stub?=?helloworld_pb2.GreeterStub(channel)
import?grpc
import?helloworld_pb2
with?open('roots.pem',?'rb')?as?f:
creds?=?grpc.ssl_channel_credentials(f.read())
channel?=?grpc.secure_channel('myservice.example.com:443',?creds)
stub?=?helloworld_pb2.GreeterStub(channel)
import?grpc
import?helloworld_pb2
from?concurrent?import?futures
server?=?grpc.server(futures.ThreadPoolExecutor(max_workers=10))
with?open('key.pem',?'rb')?as?f:
private_key?=?f.read()
with?open('chain.pem',?'rb')?as?f:
certificate_chain?=?f.read()
server_credentials?=?grpc.ssl_server_credentials(?(?(private_key,?certificate_chain),?)?)
#?此處要將GreeterServicer添加到服務(wù)器
server.add_secure_port('myservice.example.com:443',?server_credentials)
server.start()
import?grpc
import?helloworld_pb2
from?google?import?auth?as?google_auth
from?google.auth?import?jwt?as?google_auth_jwt
from?google.auth.transport?import?grpc?as?google_auth_transport_grpc
credentials,?_?=?google_auth.default()
jwt_creds?=?google_auth_jwt.OnDemandCredentials.from_signing_credentials(
credentials)
channel?=?google_auth_transport_grpc.secure_authorized_channel(
jwt_creds,?None,?'greeter.googleapis.com:443')
stub?=?helloworld_pb2.GreeterStub(channel)
import?grpc
import?helloworld_pb2
from?google?import?auth?as?google_auth
from?google.auth.transport?import?grpc?as?google_auth_transport_grpc
from?google.auth.transport?import?requests?as?google_auth_transport_requests
credentials,?_?=?google_auth.default(scopes=(scope,))
request?=?google_auth_transport_requests.Request()
channel?=?google_auth_transport_grpc.secure_authorized_channel(
credentials,?request,?'greeter.googleapis.com:443')
stub?=?helloworld_pb2.GreeterStub(channel)
ManagedChannel?channel?=?ManagedChannelBuilder.forAddress("localhost",?50051).usePlaintext(true).build();
GreeterGrpc.GreeterStub?stub?=?GreeterGrpc.newStub(channel);
在Java中,建議使用OpenSSL。
要在服務(wù)器上啟用TLS,需要以PEM格式指定證書鏈和私鑰。這樣的私鑰不應(yīng)該使用密碼。證書鏈中證書的順序很重要:更具體地說,最上面的證書必須是主機(jī)CA,而最下面的證書必須是根CA。標(biāo)準(zhǔn)的TLS端口是443,但為了避免申請(qǐng)操作系統(tǒng)的額外權(quán)限,下面用的是8443。
Server?server?=?ServerBuilder.forPort(8443)
//?啟用TLS
.useTransportSecurity(certChainFile,?privateKeyFile)
.addService(TestServiceGrpc.bindService(serviceImplementation))
.build();
server.start();
如果客戶端不知道發(fā)證機(jī)構(gòu),那么應(yīng)該分別向NettyChannelBuilder或OkHttpChannelBuilder提供一個(gè)正確配置的SSLContext或SSLSocketFactory。
在客戶端,使用SSL/TLS的服務(wù)器認(rèn)證大體是這樣的:
//?通過服務(wù)器認(rèn)證?SSL/TLS
ManagedChannel?channel?=?ManagedChannelBuilder.forAddress("myservice.example.com",?443).build();
GreeterGrpc.GreeterStub?stub?=?GreeterGrpc.newStub(channel);
//?具有服務(wù)器認(rèn)證SSL/TLS;自定義CA根證書;不適用于Android
ManagedChannel?channel?=?NettyChannelBuilder.forAddress("myservice.example.com",?443)
.sslContext(GrpcSslContexts.forClient().trustManager(new?File("roots.pem")).build()).build();
GreeterGrpc.GreeterStub?stub?=?GreeterGrpc.newStub(channel);
下面的代碼片段顯示了如何使用服務(wù)賬戶使用gRPC調(diào)用Google Cloud PubSub API。憑證是從存儲(chǔ)在一個(gè)已知位置的密鑰中加載的,或者通過檢測(cè)應(yīng)用程序運(yùn)行在一個(gè)可以自動(dòng)提供密鑰的環(huán)境中,例如Google Compute Engine等,來加載。雖然這個(gè)例子是針對(duì)Google及其服務(wù)的,但類似的模式也可以適用于其他服務(wù)提供商。
GoogleCredentials?creds?=?GoogleCredentials.getApplicationDefault();
ManagedChannel?channel?=?ManagedChannelBuilder.forTarget("greeter.googleapis.com")
.build();
GreeterGrpc.GreeterStub?stub?=?GreeterGrpc.newStub(channel)
.withCallCredentials(MoreCallCredentials.from(creds));
var?stub?=?new?helloworld.Greeter('localhost:50051',?grpc.credentials.createInsecure());
var?ssl_creds?=?grpc.credentials.createSsl(root_certs);
var?stub?=?new?helloworld.Greeter('myservice.example.com',?ssl_creds);
//?使用谷歌認(rèn)證
var?GoogleAuth?=?require('google-auth-library');?//?from?https://www.npmjs.com/package/google-auth-library
...
var?ssl_creds?=?grpc.credentials.createSsl(root_certs);
(new?GoogleAuth()).getApplicationDefault(function(err,?auth)?{
var?call_creds?=?grpc.credentials.createFromGoogleCredential(auth);
var?combined_creds?=?grpc.credentials.combineChannelCredentials(ssl_creds,?call_creds);
var?stub?=?new?helloworld.Greeter('greeter.googleapis.com',?combined_credentials);
});
var?GoogleAuth?=?require('google-auth-library');?//?from?https://www.npmjs.com/package/google-auth-library
...
var?ssl_creds?=?grpc.Credentials.createSsl(root_certs);?//?load_certs通常加載一個(gè)CA根文件
var?scope?=?'https://www.googleapis.com/auth/grpc-testing';
(new?GoogleAuth()).getApplicationDefault(function(err,?auth)?{
if?(auth.createScopeRequired())?{
auth?=?auth.createScoped(scope);
}
var?call_creds?=?grpc.credentials.createFromGoogleCredential(auth);
var?combined_creds?=?grpc.credentials.combineChannelCredentials(ssl_creds,?call_creds);
var?stub?=?new?helloworld.Greeter('greeter.googleapis.com',?combined_credentials);
});
$client?=?new?helloworld\GreeterClient('localhost:50051',?[
'credentials'?=>?Grpc\ChannelCredentials::createInsecure(),
]);
function?updateAuthMetadataCallback($context)
{
$auth_credentials?=?ApplicationDefaultCredentials::getCredentials();
return?$auth_credentials->updateMetadata($metadata?=?[],?$context->service_url);
}
$channel_credentials?=?Grpc\ChannelCredentials::createComposite(
Grpc\ChannelCredentials::createSsl(file_get_contents('roots.pem')),
Grpc\CallCredentials::createFromPlugin('updateAuthMetadataCallback')
);
$opts?=?[
'credentials'?=>?$channel_credentials
];
$client?=?new?helloworld\GreeterClient('greeter.googleapis.com',?$opts);
//?需要設(shè)置環(huán)境變量?"GOOGLE_APPLICATION_CREDENTIALS?"
$scope?=?"https://www.googleapis.com/auth/grpc-testing";
$auth?=?Google\Auth\ApplicationDefaultCredentials::getCredentials($scope);
$opts?=?[
'credentials'?=>?Grpc\Credentials::createSsl(file_get_contents('roots.pem'));
'update_metadata'?=>?$auth->getUpdateMetadataFunc(),
];
$client?=?new?helloworld\GreeterClient('greeter.googleapis.com',?$opts);
final?channel?=?new?ClientChannel('localhost',
port:?50051,
options:?const?ChannelOptions(
credentials:?const?ChannelCredentials.insecure()));
final?stub?=?new?GreeterClient(channel);
//?加載一個(gè)自定義的根文件。
final?trustedRoot?=?new?File('roots.pem').readAsBytesSync();
final?channelCredentials?=
new?ChannelCredentials.secure(certificates:?trustedRoot);
final?channelOptions?=?new?ChannelOptions(credentials:?channelCredentials);
final?channel?=?new?ClientChannel('myservice.example.com',
options:?channelOptions);
final?client?=?new?GreeterClient(channel);
//?默認(rèn)使用公開的信任根。
final?channel?=?new?ClientChannel('greeter.googleapis.com');
final?serviceAccountJson?=
new?File('service-account.json').readAsStringSync();
final?credentials?=?new?JwtServiceAccountAuthenticator(serviceAccountJson);
final?client?=
new?GreeterClient(channel,?options:?credentials.toCallOptions);
//?默認(rèn)使用公開的信任根。
final?channel?=?new?ClientChannel('greeter.googleapis.com');
final?client?=?new?GreeterClient(channel);
...
final?serviceAccountJson?=
new?File('service-account.json').readAsStringSync();
final?credentials?=?new?JwtServiceAccountAuthenticator(serviceAccountJson);
final?response?=
await?client.sayHello(request,?options:?credentials.toCallOptions);
【gRPC的應(yīng)用場景】
gRPC非常適用于以下情況:
l??微服務(wù)--gRPC是為低延遲和高吞吐量通信而設(shè)計(jì)的。
l??點(diǎn)對(duì)點(diǎn)實(shí)時(shí)通信--gRPC對(duì)雙向流的支持非常好,gRPC服務(wù)可以實(shí)時(shí)推送消息,無需輪詢。
l??多語言環(huán)境--gRPC工具支持所有流行的開發(fā)語言,使gRPC成為多語言環(huán)境的最佳選擇。
l??網(wǎng)絡(luò)約束環(huán)境--gRPC消息采用Protobuf(一種輕量級(jí)消息格式)進(jìn)行序列化。一個(gè)gRPC消息總是比同等的JSON消息小。
【應(yīng)用現(xiàn)狀】
許多公司都采用了gRPC,如Square、Netflix、CoreOS、Docker、Cocker、CockroachDB、Cisco、Juniper Networks等。
開源項(xiàng)目u-bmc使用gRPC來取代IPMI。2019年1月8日,Dropbox宣布,其SOA架構(gòu)核心的RPC框架?"Courier "的下一個(gè)版本將遷移到基于gRPC的新架構(gòu)上,主要原因是該框架與他們現(xiàn)有的定制的RPC框架可以很好地接軌。
【小結(jié)】
在我們的開發(fā)過程中,對(duì)于API技術(shù)的選取是一個(gè)重要的環(huán)節(jié),相比HTTP API,?本文介紹了gRPC具有的獨(dú)特優(yōu)勢(shì),并從緩沖機(jī)制和認(rèn)證兩個(gè)重要方面對(duì)這門技術(shù)進(jìn)行了學(xué)習(xí),下面來看看它的缺點(diǎn)和對(duì)應(yīng)的策略:
瀏覽器支持有限
在目前情況下,從瀏覽器中直接調(diào)用gRPC服務(wù)是不可能的,gRPC大量使用HTTP/2的功能,目前的瀏覽器不支持gRPC客戶端所需要的網(wǎng)絡(luò)請(qǐng)求控制能力。例如,瀏覽器不允許調(diào)用者使用HTTP/2,也不提供對(duì)底層HTTP/2幀的訪問。
應(yīng)對(duì)策略
gRPC-Web是gRPC團(tuán)隊(duì)的一項(xiàng)附加支持技術(shù),它在瀏覽器中提供有限的gRPC支持。
gRPC-Web由兩部分組成:支持所有現(xiàn)代瀏覽器的JavaScript客戶端和服務(wù)器上的gRPC-Web代理。通過客戶端調(diào)用代理,代理再把gRPC請(qǐng)求轉(zhuǎn)發(fā)到gRPC服務(wù)器上。
gRPC-Web并不支持gRPC的所有功能,比如不支持客戶端流處理和雙向流處理,對(duì)服務(wù)器流處理的支持也很有限。
傳輸內(nèi)容人類不可讀
HTTP API請(qǐng)求以文本形式發(fā)送,人類可以讀取。
gRPC消息默認(rèn)使用Protobuf進(jìn)行編碼。雖然Protobuf的發(fā)送和接收效率很高,但它的二進(jìn)制格式并不是人類可以讀取的。
Protobuf需要在.proto文件中指定消息的接口描述來正確地解序列化。需要額外的工具來分析Protobuf的線上有效載荷和手工編寫請(qǐng)求。
應(yīng)對(duì)策略
諸如服務(wù)器反射和gRPC命令行工具等功能可以幫助處理二進(jìn)制Protobuf消息。此外,Protobuf消息支持與JSON格式的轉(zhuǎn)換。內(nèi)置的JSON轉(zhuǎn)換,可以將Protobuf消息轉(zhuǎn)換為人類可讀的形式,這在調(diào)試時(shí)非常有用。
RPC
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。