Grpc快速实践

  • A+
所属分类:java

记录下Grpc使用,从 grpc maven编译插件到客户端服务端实现。

proto和Service定义

src/main/proto/AgentModel.proto

模型定义2个实体,参数和返回值。

syntax = "proto3";
option java_package = "com.jimo.grpc";

message AgentInfo {
   
  string name = 1;
  sint32 index = 2;
}

message ReportResponse {
   
  bool ok = 1;
  string msg = 2;
}
src/main/proto/AgentService.proto

在服务这边就定义一个report方法。

syntax = "proto3";

option java_package = "com.jimo.grpc";

import "AgentModel.proto";

service Agent {
   
  rpc report(AgentInfo) returns (ReportResponse) {
   }
}

编译

加入maven插件,同时编译proto文件和grpc。

        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.6.2</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}
                    </protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
                    </pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>

假如要用离线的proto执行文件,可以换成本地路径:

            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocExecutable>D:\software\protoc.exe</protocExecutable>
                    <pluginId>grpc-java</pluginId>
                    <pluginExecutable>D:\software\protoc-gen-grpc-java.exe</pluginExecutable>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

在命令行运行 mvn compile 可编译出protobuf和grpc的类,结构如下:

└─target
    ├─generated-sources
    │  ├─annotations
    │  └─protobuf
    │      ├─grpc-java
    │      │  └─com
    │      │      └─jimo
    │      │          └─grpc
    │      │                  AgentGrpc.java
    │      │
    │      └─java
    │          └─com
    │              └─jimo
    │                  └─grpc
    │                          AgentModel.java
    │                          AgentService.java

服务端实现

先实现服务的处理逻辑: AgentServiceImpl 继承 GRPC生成的抽象类。

import com.jimo.grpc.AgentGrpc;
import com.jimo.grpc.AgentModel;
import io.grpc.stub.StreamObserver;

public class AgentServiceImpl extends AgentGrpc.AgentImplBase {
   

    @Override
    public void report(AgentModel.AgentInfo request, StreamObserver<AgentModel.ReportResponse> responseObserver) {
   
        AgentModel.ReportResponse response = AgentModel.ReportResponse.newBuilder().setOk(true).setMsg(request.getName()).build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

然后起一个服务:

public static void main(String[] args) throws IOException, InterruptedException {
   
    Server server = ServerBuilder.forPort(8000)
            .addService(new AgentServiceImpl())
            .build().start();
    Runtime.getRuntime().addShutdownHook(new Thread(() -> {
   
        try {
   
            server.shutdown().awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
   
            e.printStackTrace();
        }
    }));
    System.out.println("Listening on 8000...");
    server.awaitTermination();
}

客户端实现

客户端一般共用一个 Channel,所以传进来.然后通过 Stub调用。

import com.jimo.grpc.AgentGrpc;
import com.jimo.grpc.AgentModel;
import io.grpc.Channel;

public class AgentClient {
   

    private final AgentGrpc.AgentBlockingStub stub;

    public AgentClient(Channel channel) {
   
        stub = AgentGrpc.newBlockingStub(channel);
    }

    public void report() {
   
        AgentModel.ReportResponse res = stub.report(AgentModel.AgentInfo.newBuilder().setName("app01").setIndex(98).build());
        System.out.println("收到回复:" + res);
    }
}

客户端共用一个 Channel的创建和调用:

public static void main(String[] args) throws InterruptedException {
   

    // channel最好创建一个,可以共用,是线程安全的
    ManagedChannel channel = ManagedChannelBuilder
            .forAddress("localhost", 8000)
            // 默认是SSL/TLS,这里不用
            .usePlaintext()
            .build();

    AgentClient agentClient = new AgentClient(channel);
    agentClient.report();
    agentClient.report();

    // channel默认会等待一段时间关闭,如果不用了最好及时关闭,否则会占用TCP连接
    channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
}

更多问题

1、我们需要对请求做拦截,做一些AOP的事情怎么弄?—-用 ClientInterceptor

2、在上面的客户端使用了 AgentGrpc.newBlockingStub(channel),还有好几种Stub,有什么区别,怎么使用?

3、客户端和服务端通信的网络和协议是如何实现的?为什么客户端的Channel会自动关闭?

先把坑埋好。

w3cjava