• gRPC源码分析0-导读


    gRPC是Google开源的新一代RPC框架,官网是http://www.grpc.io。正式发布于2016年8月,技术栈非常的新,基于HTTP/2,netty4.1,proto3。虽然目前在工程化方面gRPC还非常不足,但它也值得我们好好研究它,学习他。

    1. 使用场景

    按照Google的说法,使用广泛,但主要使用场景还是在移动端:

    1. Efficiently connecting polyglot services in microservices style architecture(微服务、多语言)

    2. Connecting mobile devices, browser clients to backend services

    3. Generating efficient client libraries

    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根本不支持。

     

    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/ 

     

  • 相关阅读:
    eslint 的 env 配置是干嘛使的?
    cookie httpOnly 打勾
    如何定制 antd 的样式(theme)
    剑指 Offer 66. 构建乘积数组
    剑指 Offer 65. 不用加减乘除做加法
    剑指 Offer 62. 圆圈中最后剩下的数字
    剑指 Offer 61. 扑克牌中的顺子
    剑指 Offer 59
    剑指 Offer 58
    剑指 Offer 58
  • 原文地址:https://www.cnblogs.com/parse-code/p/6160070.html
Copyright © 2020-2023  润新知