• 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/ 

     

  • 相关阅读:
    js下拉列表效果
    js格式化数字/日期
    通用的网页上的播放本地视频文件
    综合
    componentartclientmodeediting
    ESC键
    asp.net/ajax
    转载MS AJAX
    不允许进行远程连接可能会导致此失败 error: 40
    2007.9.17
  • 原文地址:https://www.cnblogs.com/parse-code/p/6160070.html
Copyright © 2020-2023  润新知