gRPC源碼分析0-導讀

gRPC是Google開源的新一代RPC框架,官網是http://www.grpc.io。正式發佈於2016年8月,技術棧很是的新,基於HTTP/2,netty4.1,proto3。雖然目前在工程化方面gRPC還很是不足,但它也值得咱們好好研究它,學習他。css

1. 使用場景

按照Google的說法,使用普遍,但主要使用場景仍是在移動端:html

  1. Efficiently connecting polyglot services in microservices style architecture(微服務、多語言)java

  2. Connecting mobile devices, browser clients to backend servicesgit

  3. Generating efficient client librariesgithub

2. HTTP/2

HTTP/2,主要是基於Google發佈的SPDY協議,是自HTTP/1.1從1999年發佈16年後的首次更新。Servlet4.0將徹底支持HTTP/2。目前支持HTTP/2的瀏覽器還很少,主要也只是支持基於TLS的HTTP/2。下圖是在caniuse.com網站統計的支持HTTP/2的瀏覽器,能夠看到chrome也是在41版本後開始的,IE根本不支持。chrome

 

HTTP/1.1會有什麼問題呢?後端

假設一個網頁須要加載幾十個資源(css、js、jpg、等等),等到html文件加載成功後,瀏覽器會一個一個請求這些資源,並等待服務器按順序一個一個返回。這將會很是耗時。瀏覽器

與HTTP/1.1標準比較,HTTP/2帶來不少功能,如:服務器

bidirectional streaming框架

flow control

header compression

multiplexing requests over a single TCP connection

在此不作詳解,還不清楚的同窗自行Google,推薦一些入門連接

http://www.ruanyifeng.com/blog/2016/08/http.html

https://imququ.com/post/protocol-negotiation-in-http2.html

3. HTTP/2侷限性

如上圖,gRPC離真正可工程化的rpc框架還有一段路要走,缺的組件也不少,如

  1. Configuration(配置化)
  2. Service Discovery(服務發現)
  3. 服務治理等等

也就是目前gRPC想用到微服務後端,須要本身開發不少東西。

4. 使用樣例

  • 下載gRPC源碼

git clone git@github.com:grpc/grpc-java.git

gRPC支持了不少平臺,固然咱們講解的是Java版本,目錄結構以下圖:

 

  • 編譯

首先咱們先不編譯grpc code generation plugin(主要用於proto3從.proto文件編譯出Java文件的,通常不用修改)。在根目錄下新建gradle.properties文件,而後添加skipCodegen=true這一行到文件中。

grpc-java has a C++ code generation plugin for protoc. Since many Java developers don't have C compilers installed and don't need to modify the codegen, the build can skip it. To skip, create the file <project-root>/gradle.properties and add skipCodegen=true.

Java、maven版本以下

Java版本須要1.8

maven 3.2

 

  • 編譯代碼

./gradlew build

  • 安裝編譯好的jar包到本地maven庫

./gradlew install

  • 樣例

先創建maven項目,定義服務.proto文件

 

在此默認你們對proto的語法已經熟悉,至少是可使用的程度。借用Google官方的例子。

syntax = "proto3";

option java_multiple_files = true;
// 生產的Java的包
option java_package = "io.grpc.examples.helloworld.generated";
// 生產的Java類名
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// service 定義服務
service Greeter {
 // 服務的一個方法
 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;
}

添加依賴到pom.xml文件,使用剛纔install的版本1.1.0-SNAPSHOT,build裏的插件是用來由proto文件生產Java源碼用的

在測試項目文件夾下,生產Java代碼

$ mvn compile

生產的代碼在target下,將其拷貝到本身的項目裏

  • 編寫測試代碼,借用Google官方代碼,懶得寫了。

Server 

public class HelloWorldServer {
  private static final Logger logger = Logger.getLogger(HelloWorldServer.class.getName());

  private Server server;

  private void start() throws IOException {
    /* The port on which the server should run */
    int port = 50051;
    server = ServerBuilder.forPort(port)
        .addService(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();
  }

  static class GreeterImpl extends GreeterGrpc.GreeterImplBase {

    @Override
    public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
      HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
      responseObserver.onNext(reply);
      responseObserver.onCompleted();
    }
  }
}

Client

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) {
    this(ManagedChannelBuilder.forAddress(host, port)
        // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
        // needing certificates.
        .usePlaintext(true));
  }

  /** Construct client for accessing RouteGuide server using the existing channel. */
  HelloWorldClient(ManagedChannelBuilder<?> channelBuilder) {
    channel = channelBuilder.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();
    }
  }
}

如上能夠看到,用gRPC編寫服務是很是簡單的。幾行代碼搞定。

源碼分析導讀

gRPC的代碼與dubbo、rocketmq相比,仍是不多的,主要是由於目前不少組件尚未。後面將會根據如下內容來說解源碼

  • 通訊

  • 消息編解碼

  • steam流

  • 框架

附錄

gRPC 官方文檔中文版

  • https://doc.oschina.net/grpc

GRPC的產生動機和設計原則

  • http://www.jianshu.com/p/8cc077f6dbb9

gRPC學習筆記

  • https://skyao.gitbooks.io/leaning-grpc/content/ 

相關文章
相關標籤/搜索