分享
  1. 首页
  2. 文章

初识google多语言通信框架gRPC系列(一)概述

polinzhuo · · 4574 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

gRPC概述

3/26/2016 9:16:08 AM

一直在寻找多平台多语言的通信框架,微软的WCF框架很强大和灵活,虽然也能通过自定义绑定和其他技术的客户端通信,但是始终没有实现多平台的技术框架的统一。google的gRPC是一个不错的选择,相比于类似框架Thrift等,google的解决方案更成熟和通用。不足的是由于刚刚开源,使用范围有限,国内资料更少。例如仅仅编译C++的gRPC,花了我两天的时间。在这个系列中,我会逐一介绍各个语言使用gRPC的细节。

gRPC是google2015年初开源的通信框架。

使用gRPC可以在客户端调用不同机器上的服务端的方法,而客户端和服务端的开发语言和运行环境可以有很多种,基本涵盖了主流语言和平台。双方交互的协议可以在proto文件中定义,客户端和服务端可以很方便的通过工具生成协议和代理代码。而消息的编码是采用google protocol buffer,数据量小、速度快。

下图是一个简单的gRPC使用的场景,服务端采用C++开发,客户端可以是anroid,和ruby。
image

gRPC具有以下特点:

  • 基于 HTTP/2, 继而提供了连接多路复用、Body 和 Header 压缩等机制。可以节省带宽、降低TCP链接次数、节省CPU使用和延长电池寿命等。
  • 支持主流开发语言(C, C++, Python, PHP, Ruby, NodeJS, C#, Objective-C、Golang、Java)
  • IDL (Interface Definition Language) 层使用了 Protocol Buffers, 非常适合团队的接口设计

下面是C#、C++、java的客户端和服务端的例子,这些客户端都向服务端传递一个姓名XXX,服务端返回 hello XXX字符串。

作为通信协议的proto文件

无论使用何种语言创建客户端和服务端,都依照相同的proto文件。proto文件定义了协议接口和数据格式。不同的语言都依照proto文件生成服务端的接口和服务类、客户端的服务代理,只是生成工具的命令不同。
proto文件如下:

syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
// The greeting service definition.
service Greeter {
 // Sends a greeting
 rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
 string name = 1;
}
// The response message containing the greetings
message HelloReply {
 string message = 1;
}

C#服务端和客户端代码

C#的客户端代理是由工具生成的,所以很简单。

客户端

public static void Main(string[] args)
{
 Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
 var client = Greeter.NewClient(channel);
 String user = "paul";
 var reply = client.SayHello(new HelloRequest { Name = user });
 Console.WriteLine("Greeting: " + reply.Message);
 channel.ShutdownAsync().Wait();
 Console.WriteLine("Press any key to exit...");
 Console.ReadKey();
}

服务端

class GreeterImpl : Greeter.IGreeter
{
 // Server side handler of the SayHello RPC
 public Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
 {
 return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
 }
}
class Program
{
 const int Port = 50051;
 public static void Main(string[] args)
 {
 Server server = new Server
 {
 Services = { Greeter.BindService(new GreeterImpl()) },
 Ports = { new ServerPort("localhost", Port, ServerCredentials.Insecure) }
 };
 server.Start();
 Console.WriteLine("Greeter server listening on port " + Port);
 Console.WriteLine("Press any key to stop the server...");
 Console.ReadKey();
 server.ShutdownAsync().Wait();
 }
}

C++的服务端和客户端代码

C++的客户端代理无法通过工具生成,需要手动编写。

//C++客户端
class GreeterClient {
 public:
 GreeterClient(std::shared_ptr<Channel> channel)
 : stub_(Greeter::NewStub(channel)) {}
 // Assambles the client's payload, sends it and presents the response back
 // from the server.
 std::string SayHello(const std::string& user) {
 // Data we are sending to the server.
 HelloRequest request;
 request.set_name(user);
 // Container for the data we expect from the server.
 HelloReply reply;
 // Context for the client. It could be used to convey extra information to
 // the server and/or tweak certain RPC behaviors.
 ClientContext context;
 // The actual RPC.
 Status status = stub_->SayHello(&context, request, &reply);
 // Act upon its status.
 if (status.ok()) {
 return reply.message();
 } else {
 return "RPC failed";
 }
 }
 private:
 std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char** argv) {
 // Instantiate the client. It requires a channel, out of which the actual RPCs
 // are created. This channel models a connection to an endpoint (in this case,
 // localhost at port 50051). We indicate that the channel isn't authenticated
 // (use of InsecureChannelCredentials()).
 GreeterClient greeter(grpc::CreateChannel(
 "localhost:50051", grpc::InsecureChannelCredentials()));
 std::string user("world");
 std::string reply = greeter.SayHello(user);
 std::cout << "Greeter received: " << reply << std::endl;
 return 0;
}
//C++服务端
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {
 Status SayHello(ServerContext* context, const HelloRequest* request,
 HelloReply* reply) override {
 std::string prefix("Hello ");
 reply->set_message(prefix + request->name());
 return Status::OK;
 }
};
void RunServer() {
 std::string server_address("0.0.0.0:50051");
 GreeterServiceImpl service;
 ServerBuilder builder;
 // Listen on the given address without any authentication mechanism.
 builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
 // Register "service" as the instance through which we'll communicate with
 // clients. In this case it corresponds to an *synchronous* service.
 builder.RegisterService(&service);
 // Finally assemble the server.
 std::unique_ptr<Server> server(builder.BuildAndStart());
 std::cout << "Server listening on " << server_address << std::endl;
 // Wait for the server to shutdown. Note that some other thread must be
 // responsible for shutting down the server for this call to ever return.
 server->Wait();
}
int main(int argc, char** argv) {
 RunServer();
 return 0;
}

java的服务端和客户端代码

gRPC本身不支持java语言,但是使用java实现了gRPC,框架是gRPC-java。使用gRPC-java一样可以与gRPC的客户端和服务端进行交互。

java客户端代码:
和C++一样,java的客户端仍然不能直接自动生成服务代理类,需要手动创建。

package io.grpc.examples.helloworld;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 * A simple client that requests a greeting from the {@link HelloWorldServer}.
 */
public class HelloWorldClient {
 private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName());
 private final ManagedChannel channel;
 private final GreeterGrpc.GreeterBlockingStub blockingStub;
 /** Construct client connecting to HelloWorld server at {@code host:port}. */
 public HelloWorldClient(String host, int port) {
 channel = ManagedChannelBuilder.forAddress(host, port)
 .usePlaintext(true)
 .build();
 blockingStub = GreeterGrpc.newBlockingStub(channel);
 }
 public void shutdown() throws InterruptedException {
 channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
 }
 /** Say hello to server. */
 public void greet(String name) {
 logger.info("Will try to greet " + name + " ...");
 HelloRequest request = HelloRequest.newBuilder().setName(name).build();
 HelloReply response;
 try {
 response = blockingStub.sayHello(request);
 } catch (StatusRuntimeException e) {
 logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
 return;
 }
 logger.info("Greeting: " + response.getMessage());
 }
 /**
 * Greet server. If provided, the first element of {@code args} is the name to use in the
 * greeting.
 */
 public static void main(String[] args) throws Exception {
 HelloWorldClient client = new HelloWorldClient("localhost", 50051);
 try {
 /* Access a service running on the local machine on port 50051 */
 String user = "world";
 if (args.length > 0) {
 user = args[0]; /* Use the arg as the name to greet if provided */
 }
 client.greet(user);
 } finally {
 client.shutdown();
 }
 }
}

java服务端代码:

package io.grpc.examples.helloworld;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.logging.Logger;
/**
 * Server that manages startup/shutdown of a {@code Greeter} server.
 */
public class HelloWorldServer {
 private static final Logger logger = Logger.getLogger(HelloWorldServer.class.getName());
 /* The port on which the server should run */
 private int port = 50051;
 private Server server;
 private void start() throws IOException {
 server = ServerBuilder.forPort(port)
 .addService(GreeterGrpc.bindService(new GreeterImpl()))
 .build()
 .start();
 logger.info("Server started, listening on " + port);
 Runtime.getRuntime().addShutdownHook(new Thread() {
 @Override
 public void run() {
 // Use stderr here since the logger may have been reset by its JVM shutdown hook.
 System.err.println("*** shutting down gRPC server since JVM is shutting down");
 HelloWorldServer.this.stop();
 System.err.println("*** server shut down");
 }
 });
 }
 private void stop() {
 if (server != null) {
 server.shutdown();
 }
 }
 /**
 * Await termination on the main thread since the grpc library uses daemon threads.
 */
 private void blockUntilShutdown() throws InterruptedException {
 if (server != null) {
 server.awaitTermination();
 }
 }
 /**
 * Main launches the server from the command line.
 */
 public static void main(String[] args) throws IOException, InterruptedException {
 final HelloWorldServer server = new HelloWorldServer();
 server.start();
 server.blockUntilShutdown();
 }
 private class GreeterImpl implements GreeterGrpc.Greeter {
 @Override
 public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
 HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
 responseObserver.onNext(reply);
 responseObserver.onCompleted();
 }
 }
}

有疑问加站长微信联系(非本文作者)

本文来自:博客园

感谢作者:polinzhuo

查看原文:初识google多语言通信框架gRPC系列(一)概述

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
4574 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏