Protobuf和gRPC基础介绍

本文将会讨论一下Protobuf,以及最简单和便捷实现protobuf的gRPC。

什么是protobuf

在官网介绍中,Protocol Buffers是Google用来进行序列化数据的一个机制,他和语言无关,也独立于平台。就和JSON类似,但是它比JSON要更小,更快更加简单。你可以自己定义如何组织你的数据,然后你就可以使用相关的代码来很方便地进行数据的读写。

目前有两个版本的proto,分别是proto2proto3.

本文使用的是proto3,从语法角度来看,proto2和proto3是有很区别的,不过具体的rpc这一块,两者其实是相似的。

什么是gRPC

所谓gPRC就是一个RPC(Remote Procedure Call)的框架,它可以在任何环境下运行,并且支持多种编程语言。它使用protobuf作为其接口定义语言以及基础信息交换的格式。gRPC的很多很赞的功能都是通过双向流以及多路复用的HTTP/2来实现的。更多的gRPC的介绍可以参见这里

gRPC还是REST?

其实有很多理由让我们应该选择gRPC而不是Rest:

1)gRPC通过payload来传输二进制数据而不是文本,这样就使得相互的交互更加迅捷和紧凑。

2)gRPC是通过HTTP/2的长连接来实现的,所以不需要每一个请求都建立一个连接。

3)gRPC是类型安全的,所以假如你期待的是整形,那么就没有人能够发送字符串。

4)gRPC比REST要稍微快一点,虽然只是毫秒级的。假如你对毫秒级的速度比较敏感,那么gRPC是一个更好的选择。

5)gRPC支持流,所以假如你正在寻找分布式流支持,那么gRPC可以很好的完成这项任务。

不同的gRPC交互机制

1)一元化

Unary communication

2)客户端流

Client streaming

3)服务器端流

Server streaming

4)双向流

Bi-directional streaming

本文主要介绍第一种,一元化的情况。我们的使用场景很简单,就是一个订购服务会调用订购确认服务来确认订购已经被加入到了购物车。首先是要创建一个client和server端的合同:

Protobuf contract:

syntax = "proto3";
option java_multiple_files = true;
package com.milind.grpc;

message Order {
    string currency = 1;
    double totalAmount = 2;
    string orderId = 3;
    string emailAddress = 4;
    string cartURL =5;
    repeated LineItems lineItems = 6;
}

message LineItems {
    string sku = 1;
    string name = 2;
    string description = 3;
    string category = 4;
    string other = 5;
    double unitPrice = 6;
    double salePrice = 7;
    double quantity = 8;
    double totalPrice = 9;
}

message OrderConfirmation {
  string orderId = 1;
  repeated ConfirmedLineItems confirmedLineItems = 2;
}

message ConfirmedLineItems {
    string sku = 1;
    double confirmQuantity = 2;
}

service OrderConfrimationService {
    rpc confrim(Order) returns (OrderConfirmation);
}

上面代码中各种数据类型可以参见这里。server端需要实现一个OrderConfrimationService::confirm,它把Order作为输入,然后返回OrderConfirmation。在上面contract定义之后,我们可以使用下面的maven插件来产生代码:

<plugin>
                <groupId>com.github.os72</groupId>
                <artifactId>protoc-jar-maven-plugin</artifactId>
                <version>3.5.1</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <protocArtifact>com.google.protobuf:protoc:3.0.0</protocArtifact>
                            <inputDirectories>
                                <include>src/main/proto</include>
                            </inputDirectories>
                            <outputTargets>
                                <outputTarget>
                                    <type>java</type>
                                    <outputDirectory>src/main/java</outputDirectory>
                                </outputTarget>
                                <outputTarget>
                                    <type>grpc-java</type>
                                    <outputDirectory>src/main/java</outputDirectory>
                                    <pluginArtifact>io.grpc:protoc-gen-grpc-                                                            java:1.0.1</pluginArtifact>     
                                </outputTarget>
                            </outputTargets>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

执行mvn clean install命令来产生代码。

创建server

gRPC需要它自己的server,当然创建server是非常简单的。首先我们来实现gRPC的服务:

public class OrderConfirmationServiceImpl extends 
        OrderConfrimationServiceGrpc.OrderConfrimationServiceImplBase {
    @Override
    public void confrim(Order request,
                        StreamObserver<OrderConfirmation> responseObserver){
    ...
        OrderConfirmation.Builder orderbuilder = OrderConfirmation.newBuilder()
                .setOrderId(request.getOrderId());
        ...
    responseObserver.onNext(orderbuilder.build());
        responseObserver.onCompleted();
    }
}

这里我们扩展了基本的OrderConfrimationServiceImplBase,主要是override了confirm的方法。这里你可以看到confirm其实返回的是void,那么是如何把response传递给clinet的呢,其实他是通过StreamObserver的onNext()方法实现的,当然最终还要调用一下onCompleted方法。

public class GrpcServer {
    public static void main(String[] args) throws IOException, InterruptedException {
        int port=args.length==0?8080:Integer.parseInt(args[0]);
        Server server = ServerBuilder.forPort(port)
                .addService(new OrderConfirmationServiceImpl()).build();
        server.start();
        server.awaitTermination();
    }
}

创建client

ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", port)
                .usePlaintext()
                .build();
OrderConfrimationServiceGrpc.OrderConfrimationServiceBlockingStub stub 
        OrderConfrimationServiceGrpc.newBlockingStub(channel);
OrderConfirmation orderConfirmation = stub.confrim(DummyObjectCreator.createOrder());

Client端的代码非常直接:

1)创建连接gRPC Server的通道

2)使用这个通道来创建stub

3)通过stub调用gRPC服务,并且得到对应的response

需要注意的是gRPC不支持null,当你在request的创建中传入任何null的时候,他都会抛出exception。假如没有数据,就不要set它了而不是传一个null。

Payload的比较

下面是同样的request数据的payload大小的比较:

Payload comparison

总得来说,ProtoBuf的带下和JSON+Gzip是类似的,但是比AVRO要差一点,当然和JSON比起来则好得多。

总结

gRPC非常适用于流请求以及你不想为每一个请求都建立连接的情况。在微服务中还有一些额外的好处,比如payload比价小,可以节约连接的消耗以及类型安全。好了,关于gRPC的介绍就这里了,希望大家喜欢。

参考文章:https://dzone.com/articles/grpc-basics

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *