七叶笔记 » golang编程 » 带你走入GRPC (Java版本)

带你走入GRPC (Java版本)

快速开始

1.依赖 :

 <dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-all</artifactId>
    <version>1.12.0</version>
</dependency>  

插件和全部依赖都在文章最后面 . 全局粘贴

2.编写protobuf文件

idea 介意下载一个 protobuf的插件, 可以有代码提示. 这里直接去pluging里搜就行了.

我使用的是proto3的版本.

可以参考一下这个文章

注意这个文件是在 你的src/proto/XX.proto 中定义的, 文件目录必须写好.

 syntax = "proto3"; // 协议版本

// 选项配置
option java_package = "com.example.grpc.api";
option java_outer_classname = "RPCDateServiceApi";
option java_multiple_files = true;

// 定义包名
package com.example.grpc.api;

// 服务接口.定义请求参数和相应结果    
service RPCDateService {
    rpc getDate (RPCDateRequest) returns (RPCDateResponse) {
    }
}

// 定义请求体
message RPCDateRequest {
    string userName = 1;
}

// 定义相应内容
message RPCDateResponse {
    string serverDate = 1;
}  

使用maven插件, 编译.

第一个命令执行完. 在 target目录里找就行了. 第二个命令也就是找就行了. 然后将生成的Java文件拷贝到你的目录里.就可以了, 类似于Mybatis的mapper插件.

3. 编写接口实现类.

 package com.example.grpc.service;

// RPCDateServiceGrpc.RPCDateServiceImplBase 这个就是接口. 
// RPCDateServiceImpl 我们需要继承他的,实现方法回调
public class RPCDateServiceImpl extends RPCDateServiceGrpc.RPCDateServiceImplBase {
    @Override
    public void getDate(RPCDateRequest request, StreamObserver<RPCDateResponse> responseObserver) {
        // 请求结果,我们定义的
        RPCDateResponse rpcDateResponse = null;
        String userName = request.getUserName();
        String response = String.format("你好: %s, 今天是%s.", userName, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        try {
            // 定义响应,是一个builder构造器. 
            rpcDateResponse = RPCDateResponse
                    .newBuilder()
                    .setServerDate(response)
                    .build();
        } catch (Exception e) {
            responseObserver.onError(e);
        } finally {
            // 这种写法是observer,异步写法,老外喜欢用这个框架.
            responseObserver.onNext(rpcDateResponse);
        }
        responseObserver.onCompleted();
    }
}  

定义服务端

 public class GRPCServer {
    private static final int port = 9999;

    public static void main(String[] args) throws Exception {
        // 设置service接口.
        Server server = ServerBuilder.
                forPort(port)
                .addService(new RPCDateServiceImpl())
                .build().start();
        System.out.println(String.format("GRpc服务端启动成功, 端口号: %d.", port));
        server.awaitTermination();
    }
}  

定义客户端

 public class GRPCClient {
    private static final String host = "localhost";
    private static final int serverPort = 9999;

    public static void main(String[] args) throws Exception {
        // 1. 拿到一个通信的channel
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(host, serverPort).usePlaintext().build();
        try {
            // 2.拿到道理对象
            RPCDateServiceGrpc.RPCDateServiceBlockingStub rpcDateService = RPCDateServiceGrpc.newBlockingStub(managedChannel);
            RPCDateRequest rpcDateRequest = RPCDateRequest
                    .newBuilder()
                    .setUserName("anthony")
                    .build();
            // 3. 请求
            RPCDateResponse rpcDateResponse = rpcDateService.getDate(rpcDateRequest);
            // 4. 输出结果
            System.out.println(rpcDateResponse.getServerDate());
        } finally {
            // 5.关闭channel, 释放资源.
            managedChannel.shutdown();
        }
    }
}  

以上就是完整的流程. 启动服务器端, 启动客户端打印一下信息 :

服务端日志 :

 2020-02-29 11:04:50,991 4689   [LG-3-1] DEBUG .grpc.netty.NettyServerHandler  - [id: 0xed01e3fb, L:/127.0.0.1:9999 - R:/127.0.0.1:3994] INBOUND SETTINGS: ack=true
2020-02-29 11:04:51,075 4773   [LG-3-1] DEBUG .grpc.netty.NettyServerHandler  - [id: 0xed01e3fb, L:/127.0.0.1:9999 - R:/127.0.0.1:3994] INBOUND HEADERS: streamId=3 headers=GrpcHttp2RequestHeaders[:path: /com.example.grpc.api.RPCDateService/getDate, :authority: localhost:9999, :method: POST, :scheme: http, te: trailers, content-type: application/grpc, user-agent: grpc-java-netty/1.12.0, grpc-accept-encoding: gzip, grpc-trace-bin: ] streamDependency=0 weight=16 exclusive=false padding=0 endStream=false
2020-02-29 11:04:51,125 4823   [LG-3-1] DEBUG .grpc.netty.NettyServerHandler  - [id: 0xed01e3fb, L:/127.0.0.1:9999 - R:/127.0.0.1:3994] INBOUND DATA: streamId=3 padding=0 endStream=true length=14 bytes=00000000090a07616e74686f6e79
2020-02-29 11:04:51,172 4870   [LG-3-1] DEBUG .grpc.netty.NettyServerHandler  - [id: 0xed01e3fb, L:/127.0.0.1:9999 - R:/127.0.0.1:3994] OUTBOUND HEADERS: streamId=3 headers=GrpcHttp2OutboundHeaders[:status: 200, content-type: application/grpc, grpc-encoding: identity, grpc-accept-encoding: gzip] streamDependency=0 weight=16 exclusive=false padding=0 endStream=false
2020-02-29 11:04:51,189 4887   [LG-3-1] DEBUG .grpc.netty.NettyServerHandler  - [id: 0xed01e3fb, L:/127.0.0.1:9999 - R:/127.0.0.1:3994] OUTBOUND DATA: streamId=3 padding=0 endStream=false length=44 bytes=00000000270a25e4bda0e5a5bd3a20616e74686f6e792c20e4bb8ae5a4a9e698af323032302d30322d32392e
2020-02-29 11:04:51,190 4888   [LG-3-1] DEBUG .grpc.netty.NettyServerHandler  - [id: 0xed01e3fb, L:/127.0.0.1:9999 - R:/127.0.0.1:3994] OUTBOUND HEADERS: streamId=3 headers=GrpcHttp2OutboundHeaders[grpc-status: 0] streamDependency=0 weight=16 exclusive=false padding=0 endStream=true
2020-02-29 11:04:51,204 4902   [LG-3-1] DEBUG .grpc.netty.NettyServerHandler  - [id: 0xed01e3fb, L:/127.0.0.1:9999 - R:/127.0.0.1:3994] INBOUND GO_AWAY: lastStreamId=0 errorCode=0 length=0 bytes=  

客户端日志 :

 2020-02-29 11:00:43,480 1814   [LG-1-2] DEBUG .grpc.netty.NettyClientHandler  - [id: 0xcdbd6064, L:/127.0.0.1:3817 - R:localhost/127.0.0.1:9999] INBOUND HEADERS: streamId=3 headers=GrpcHttp2ResponseHeaders[grpc-status: 0] streamDependency=0 weight=16 exclusive=false padding=0 endStream=true
你好: anthony, 今天是20-02-29.
2020-02-29 11:00:43,487 1821   [LG-1-2] DEBUG .grpc.netty.NettyClientHandler  - [id: 0xcdbd6064, L:/127.0.0.1:3817 - R:localhost/127.0.0.1:9999] OUTBOUND GO_AWAY: lastStreamId=0 errorCode=0 length=0 bytes=  

这就是一个Grpc的完整流程.

分析一下.

定义都是一个CS模型, GRPC他看到了未来一定是HTTP2的天下(这是必然的趋势), 所以它没有制定自己的协议, 这里指的是应用层协议. 类似于我们的RestTemplate 一样都是基于http协议.

所以它考虑到的是 JSON ,这种序列化比较慢的序列化方式. 采用了自己开发的一种Protobuf.

这里就说明了 GRPC其实只是解决了序列化的方式, 依靠这个Protobuf, 可以解决一系列问题, 比如接口信息的定义, 等等有很多,

最简单的例子. 比如我Java中写了一个echo() 接口, golang如何调用 ? . 这就是难点,

我们可以依靠统一的Protobuf文件生成对应的 Golang和Java的接口, 就可以了, 所以说本质上来说他是做了这点.

我们看看Dubbo做了什么 , 他依靠TCP的可靠性, 自己基于TCP实现了一个应用层协议, dubbo协议. 同时也引用了一堆的序列化方式, 所以整个GRPC也是可行的.

感兴趣的同学可以做一次抓包, 抓取客户端请求,分析一个GRPC的请求格式.

pom文件

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="#34;
         xmlns:xsi="#34;
         xsi:schemaLocation=" #34;>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.dong.api</groupId>
    <artifactId>api-common</artifactId>
    <version>v1.0.0</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.21</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-all</artifactId>
            <version>1.24.0</version>
        </dependency>
    </dependencies>
    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.4.1.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.0</version>
                <configuration>
                    <pluginId>grpc-java</pluginId>
                    <protocArtifact>com.google.protobuf:protoc:3.9.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.24.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>  

相关文章