gRPC技术优势哪里?有什么缺点和应对策略?(grpc的优势)

网友投稿 1884 2022-05-30

【引言】

gRPC(gRPC Remote Procedure Calls)是一个开源的远程过程调用(RPC)系统,由谷歌公司最先在2015年开发。它使用HTTP/2作为传输方式,协议缓冲器作为接口描述语言,提供了认证、双向流和流控制、阻塞或非阻塞绑定、取消和超时等功能。它可以为许多编程语言生成跨平台的客户端和服务器绑定。最常见的使用场景包括在微服务架构中的服务连接,以及移动设备、网页客户端连接到后端服务等。

【概述】

在gRPC中,客户端应用程序可以直接调用不同服务器应用程序上的方法,就像调用本地对象接口一样,这让你更容易创建分布式应用和服务。和许多RPC系统一样,gRPC也是围绕着定义一个服务的思想,指定可以远程调用的方法,参数和返回类型。在服务器端,服务程序实现这个接口,并运行gRPC服务器来处理客户端的调用。在客户端上有一个存根,这个存根提供与服务器相同的方法。

gRPC客户端和服务器可以在各种环境下运行和对话,并且可以用gRPC支持的任何一种语言编写。比如,你可以轻松地用Java创建一个gRPC服务器,用Go、Python或Ruby的客户端来调用。此外,最新的Google API都会有gRPC版本的接口,让你可以轻松地将Google功能构建到你的应用程序中。

【gRPC的优点】

性能

gRPC消息使用Protobuf进行序列化,Protobuf是一种高效的二进制消息格式。Protobuf在服务器和客户端上的序列化速度非常快。Protobuf序列化的结果是消息的有效载荷小,这对于移动应用等有限带宽的场景来说非常重要。

gRPC是为HTTP/2设计的,这是HTTP的一个重要修订版,它比HTTP 1.x提供了显著的性能优势。

l  二进制框架和压缩技术的使用使得HTTP/2协议在发送和接收方面都非常紧凑和高效。

l  在单一TCP连接上实现多个HTTP/2调用的复用。多路复用消除了线头阻塞。

代码通过工具生成

所有的gRPC框架都提供了一流的代码生成支持。gRPC开发的一个核心文件是.proto文件,它定义了gRPC服务和消息的契约。从这个文件中,gRPC框架将代码生成一个服务基类、消息和一个完整的客户端。

通过在服务器和客户端之间共享.proto文件,消息和客户端的代码可以从端到端的生成。客户端的代码生成消除了客户端和服务器上的消息重复,为你创建一个强类型化的客户端。在有很多服务的应用程序中,不需要编写客户端,这可以节省大量的开发时间。

严格的规范

目前还没有一个针对使用JSON的HTTP API的正式规范。开发者们一直对URL、HTTP动词和响应代码的最佳格式争论不休。

gRPC规范对gRPC服务必须遵循的格式是有规定的。gRPC消除了争论,从而节省了开发者的时间,因为gRPC在不同平台和实现上是一致的。

流处理

HTTP/2为长效实时通信流提供了基础,gRPC通过HTTP/2提供了一流的流处理支持。

一个gRPC服务支持所有的流处理组合:

l  单一式(无流处理)

l  服务器到客户端的流处理

l  客户端到服务器的流处理

l  双向流处理

超时取消机制

gRPC允许客户端指定他们愿意等待多长时间完成一个RPC。把这个时间决定的最后期限被发送至服务器,服务器可以决定超过最后期限时采取什么行动。例如,服务器可以在超时后取消正在进行中的gRPC或HTTP或数据库请求。

通过对gRPC调用设置超时取消机制,有助于实现对资源的限制使用。

【gRPC的缓冲器与认证】

【使用协议缓冲器】

gRPC使用协议缓冲区对数据进行编码。与使用JSON的HTTP API不同,它们有一个更严格的规范。

协议缓冲区是Google成熟的开源机制,用于序列化结构化数据(尽管它可以使用其他数据格式,如JSON)。以下是对其工作原理的简单介绍。

在使用协议缓冲区时,第一步是在proto文件中定义要序列化的数据结构:是一个普通的文本文件,扩展名为.proto。协议缓冲区的数据是以消息的形式结构化,每个消息都是一个小的逻辑信息记录,其中包含一系列称为字段的名-值对。下面是一个简单的例子:

message Person {

string name = 1;

int32 id = 2;

bool has_ponycopter = 3;

}

接着,一旦你指定了你的数据结构,你就可以使用协议缓冲区编译器 protoc 从你的 proto 定义中生成你喜欢的编程语言的数据访问类。这些类为每个字段提供了简单的访问器,如 name()和 set_name(),以及将整个结构序列化/解析为原始字节的方法。因此,例如,如果你选择的语言是C++,用上面的例子中运行,编译器会生成一个名为Person的类。然后你可以在你的应用程序中使用这个类来填充、序列化和检索Person协议缓冲区消息。

你在普通的proto文件中定义gRPC服务,RPC方法参数和返回类型指定为协议缓冲区消息:

// greeter 服务定义.

service Greeter {

// 发出问候

rpc SayHello (HelloRequest) returns (HelloReply) {}

}

// 请求信息包含用户名字

message HelloRequest {

string name = 1;

}

// 响应消息包含问候消息

message HelloReply {

string message = 1;

}

gRPC 使用 protoc 和一个特殊的 gRPC 插件来从你的 proto 文件中生成代码:你会得到生成的 gRPC 客户端和服务器代码,以及用于补足、序列化和检索消息类型的常规协议缓冲区代码。

【协议缓冲区版本】

虽然协议缓冲区已经向开源用户提供了一段时间,目前推荐使用协议缓冲区3版(proto3),它的语法稍有简化,并添加了一些有用的新功能,而且支持了更多的语言。

Proto3目前支持Java、C++、Dart、Python、Objective-C、C#、Android Java、Ruby、Golang和JavaScript,,还有更多语言正在开发中。

一般来说,虽然可以使用proto2(当前默认的协议缓冲区版本),但建议你使用gRPC时使用proto3,因为它支持gRPC目前支持的所有语言,同时也避免了proto2客户端与proto3服务器的兼容性问题,反之亦然。

【认证】

gRPC支持使用TLS和基于令牌的认证。与Google服务的连接必须使用TLS。

gRPC设计的初衷是可以与各种认证机制一起工作,从而可以很容易地通过gRPC与其他系统对话。

gRPC还提供了一个简单的认证API,让您在创建通道或调用时提供所有必要的认证信息作为凭证。

凭证类型

凭证可以分为两种类型:

l  通道凭证,附加在通道上,如SSL凭证。

l  调用凭证,它附加到一个调用(或C++中的ClientContext)上。

你也可以在CompositeChannelCredentials中结合这些内容,例如,你可以为通道指定SSL的详细信息,并为通道上的每个呼叫指定呼叫凭证。CompositeChannelCredentials 将通道凭证和呼叫凭证关联起来,以创建一个新的通道凭证。其结果将在信道上的每一次呼叫中发送与组成的CallCredentials相关联的认证数据。

比如说,你可以从一个SSLCredentials和一个AccessTokenCredentials创建一个ChannelCredentials。当应用到一个Channel时,其结果将为这个通道上的每个呼叫发送相应的访问令牌。

单个CallCredentials也可以用CompositeCallCredentials组成。在呼叫中使用的CallCredentials将触发与两个CallCredentials相关联的认证数据的发送。

gRPC具有SSL/TLS集成功能,提倡使用SSL/TLS来验证服务器,并对客户端和服务器之间交换的所有数据进行加密。客户端可选择提供相互认证的证书机制。

现在我们来看看Credentials是如何与我们支持的一种认证机制一起工作的。我们假定最简单的验证场景,客户端只想验证服务器并加密所有数据。这个例子是用C++语言编写的,但所有语言的API都是类似的。

// 创建一个默认的SSL ChannelCredentials对象。

auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());

// 使用上一步中创建的凭证创建一个通道。

auto channel = grpc::CreateChannel(server_name, channel_creds);

// 在通道上创建一个存根。

std::unique_ptr stub(Greeter::NewStub(channel));

// 在存根上进行实际的RPC调用。

grpc::Status s = stub->sayHello(&context, *request, response);

对于高级用例,如修改根CA或使用客户端证书,可以在传递给工厂方法的 SslCredentialsOptions 参数中设置相应的选项。

基于令牌的身份验证

gRPC提供了一个通用的机制,可以将基于元数据的凭证附加到请求和响应中。这种机制专门用于访问Google 的API服务。 一般来说,Google不允许没有SSL/TLS的连接,而且大多数gRPC语言实现也不会让你在未加密的通道上发送凭证。

gRPC应用程序可以使用一个简单的API来创建一个在各种部署场景中与Google进行认证的凭证。例子:

auto creds = grpc::GoogleDefaultCredentials();

// 创建一个通道,存根并进行RPC调用(与上例的功能一样)

auto channel = grpc::CreateChannel(server_name, creds);

std::unique_ptr stub(Greeter::NewStub(channel));

grpc::Status s = stub->sayHello(&context, *request, response);

这个通道凭证对象适用于使用服务账户的应用程序和在 Google Compute Engine (GCE) 中运行的应用程序。在前一种情况下,服务账户的私钥是从环境变量 GOOGLE_APPLICATION_CREDENTIALS 中命名的文件中加载的。这些密钥用于生成附加到相应通道上的每个出站RPC的承载令牌。

对于在 GCE 中运行的应用程序,可以在 VM 设置期间配置一个默认服务帐户和相应的 OAuth2 作用域。在运行时,该凭证处理与认证系统的通信,以获取OAuth2访问令牌,并将其附加到相应通道上的每个出站RPC上。

扩展gRPC以支持其他认证机制

凭证插件API允许开发人员插入自己的凭证类型。这些插件有:

l  MetadataCredentialsPlugin抽象类,它包含纯虚拟的GetMetadata方法,需要由开发者创建的子类来实现。

l  MetadataCredentialsFromPlugin函数,它从

MetadataCredentialsPlugin中创建一个CallCredentials。

下面是一个简单的凭证插件的例子,它可以在自定义头中设置一个认证依据。

类实现:

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) override {

metadata->insert(std::make_pair("x-custom-auth-ticket", ticket_));

return grpc::Status::OK;

}

private:

grpc::string ticket_;

};

类调用:

auto call_creds = grpc::MetadataCredentialsFromPlugin(

std::unique_ptr(

new MyCustomAuthenticator("super-secret-ticket")));

通过在核心层插入gRPC凭证实现,可以实现更深层次的集成。

更多例子

这些认证机制将适用于所有gRPC支持的语言。

conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())

// 此处需要添加错误处理

client := pb.NewGreeterClient(conn)

// ...

s := grpc.NewServer()

lis, _ := net.Listen("tcp", "localhost:50051")

// 此处需要添加错误处理

s.Serve(lis)

creds, _ := credentials.NewClientTLSFromFile(certFile, "")

conn, _ := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))

// 此处需要添加错误处理

client := pb.NewGreeterClient(conn)

// ...

creds, _ := credentials.NewServerTLSFromFile(certFile, keyFile)

s := grpc.NewServer(grpc.Creds(creds))

lis, _ := net.Listen("tcp", "localhost:50051")

// 此处需要添加错误处理

s.Serve(lis)

pool, _ := x509.SystemCertPool()

// 此处需要添加错误处理

creds := credentials.NewClientTLSFromCert(pool, "")

perRPC, _ := oauth.NewServiceAccountFromFile("service-account.json", scope)

conn, _ := grpc.Dial(

"greeter.googleapis.com",

grpc.WithTransportCredentials(creds),

grpc.WithPerRPCCredentials(perRPC),

)

// 此处需要添加错误处理

client := pb.NewGreeterClient(conn)

// ...

stub = Helloworld::Greeter::Stub.new('localhost:50051', :this_channel_is_insecure)

creds = GRPC::Core::ChannelCredentials.new(load_certs)  # load_certs通常加载一个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通常加载一个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 stub(Greeter::NewStub(channel));

...

auto channel_creds = grpc::SslCredentials(grpc::SslCredentialsOptions());

auto channel = grpc::CreateChannel("myservice.example.com", channel_creds);

std::unique_ptr stub(Greeter::NewStub(channel));

...

auto creds = grpc::GoogleDefaultCredentials();

auto channel = grpc::CreateChannel("greeter.googleapis.com", creds);

std::unique_ptr stub(Greeter::NewStub(channel));

...

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应用的默认凭证与公开信任的根。

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添加到服务器

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。

要在服务器上启用TLS,需要以PEM格式指定证书链和私钥。这样的私钥不应该使用密码。证书链中证书的顺序很重要:更具体地说,最上面的证书必须是主机CA,而最下面的证书必须是根CA。标准的TLS端口是443,但为了避免申请操作系统的额外权限,下面用的是8443。

Server server = ServerBuilder.forPort(8443)

// 启用TLS

.useTransportSecurity(certChainFile, privateKeyFile)

.addService(TestServiceGrpc.bindService(serviceImplementation))

.build();

server.start();

如果客户端不知道发证机构,那么应该分别向NettyChannelBuilder或OkHttpChannelBuilder提供一个正确配置的SSLContext或SSLSocketFactory。

在客户端,使用SSL/TLS的服务器认证大体是这样的:

// 通过服务器认证 SSL/TLS

ManagedChannel channel = ManagedChannelBuilder.forAddress("myservice.example.com", 443).build();

GreeterGrpc.GreeterStub stub = GreeterGrpc.newStub(channel);

// 具有服务器认证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);

下面的代码片段显示了如何使用服务账户使用gRPC调用Google Cloud PubSub API。凭证是从存储在一个已知位置的密钥中加载的,或者通过检测应用程序运行在一个可以自动提供密钥的环境中,例如Google Compute Engine等,来加载。虽然这个例子是针对Google及其服务的,但类似的模式也可以适用于其他服务提供商。

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);

// 使用谷歌认证

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通常加载一个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);

// 需要设置环境变量 "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);

// 加载一个自定义的根文件。

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);

// 默认使用公开的信任根。

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);

// 默认使用公开的信任根。

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的应用场景】

gRPC非常适用于以下情况:

l  微服务--gRPC是为低延迟和高吞吐量通信而设计的。

l  点对点实时通信--gRPC对双向流的支持非常好,gRPC服务可以实时推送消息,无需轮询。

l  多语言环境--gRPC工具支持所有流行的开发语言,使gRPC成为多语言环境的最佳选择。

l  网络约束环境--gRPC消息采用Protobuf(一种轻量级消息格式)进行序列化。一个gRPC消息总是比同等的JSON消息小。

【应用现状】

许多公司都采用了gRPC,如Square、Netflix、CoreOS、Docker、Cocker、CockroachDB、Cisco、Juniper Networks等。

开源项目u-bmc使用gRPC来取代IPMI。2019年1月8日,Dropbox宣布,其SOA架构核心的RPC框架 "Courier "的下一个版本将迁移到基于gRPC的新架构上,主要原因是该框架与他们现有的定制的RPC框架可以很好地接轨。

【小结】

在我们的开发过程中,对于API技术的选取是一个重要的环节,相比HTTP API, 本文介绍了gRPC具有的独特优势,并从缓冲机制和认证两个重要方面对这门技术进行了学习,下面来看看它的缺点和对应的策略:

浏览器支持有限

在目前情况下,从浏览器中直接调用gRPC服务是不可能的,gRPC大量使用HTTP/2的功能,目前的浏览器不支持gRPC客户端所需要的网络请求控制能力。例如,浏览器不允许调用者使用HTTP/2,也不提供对底层HTTP/2帧的访问。

应对策略

gRPC-Web是gRPC团队的一项附加支持技术,它在浏览器中提供有限的gRPC支持。

gRPC-Web由两部分组成:支持所有现代浏览器的JavaScript客户端和服务器上的gRPC-Web代理。通过客户端调用代理,代理再把gRPC请求转发到gRPC服务器上。

gRPC-Web并不支持gRPC的所有功能,比如不支持客户端流处理和双向流处理,对服务器流处理的支持也很有限。

传输内容人类不可读

gRPC技术优势在哪里?有什么缺点和应对策略?(grpc的优势)

HTTP API请求以文本形式发送,人类可以读取。

gRPC消息默认使用Protobuf进行编码。虽然Protobuf的发送和接收效率很高,但它的二进制格式并不是人类可以读取的。

Protobuf需要在.proto文件中指定消息的接口描述来正确地解序列化。需要额外的工具来分析Protobuf的线上有效载荷和手工编写请求。

应对策略

诸如服务器反射和gRPC命令行工具等功能可以帮助处理二进制Protobuf消息。此外,Protobuf消息支持与JSON格式的转换。内置的JSON转换,可以将Protobuf消息转换为人类可读的形式,这在调试时非常有用。

RPC

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:使用 Python、SQLite 和 SQLAlchemy 进行数据管理2 |【生长吧!Python!】 【生长吧!Python】有奖征文火热进行中:https://bbs.huaweicloud.com/blogs/278897(使用驱动器u盘之前需要格式化)
下一篇:[系统安全] 十三.熊猫烧香病毒IDA和OD逆向分析(中)病毒释放机理(系统安全测试报告)
相关文章