gRPC-java简单案例

目录

gRPC简介

protobuf

何为protobuf

下载配置

编译

简单案例

maven配置

生成 grpc 相关类

编写服务端

编写客户端


gRPC简介

gRPC 是一个高性能、开源、通用的RPC框架,由Google推出,基于 HTTP2 协议标准设计开发,默认采用 protobuf 数据序列化协议,支持多种开发语言。gRPC 提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库。

在 gRPC 客户端可以直接调用不同服务器上的远程程序,使用姿势看起来就像调用本地程序一样,很容易去构建分布式应用和服务。和很多RPC系统一样,服务端负责实现定义好的接口并处理客户端的请求,客户端根据接口描述直接调用需要的服务。客户端和服务端可以分别使用 gRPC 支持的不同语言实现。

gRPC 支持定义四类服务方法:

  • 单项 RPC,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用。
rpc SayHello(HelloRequest) returns (HelloResponse){
}
  • 服务端流式 RPC,即客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取直到没有更多消息为止。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
}
  • 客户端流式 RPC,即客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这些消息并返回应答。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
}
  • 双向流式 RPC,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者是读写相结合的其他方式。每个数据流里消息的顺序会被保持。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
}

protobuf

何为protobuf

protocol buffers 是一种语言无关、平台无关、可扩展的序列化结构数据方法,可用于(数据)通信协议、数据存储等。你可以定义数据结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据,你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。其一般可简单类比于XML,这里的类比主要指在数据通信和数据存储应用场景中序列化方面的类比,总结 protobuf 三大特点如下:

  • 语言无关、平台无关:即 ProtoBuf 支持 Java、C++、Python 等多种语言,支持多个平台
  • 灵活、高效:即比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单
  • 扩展性、兼容性好:你可以更新数据结构,而不影响和破坏原有的旧程序

下载配置

本文基于mac环境配置,首先前往如下网址进行下载:https://github.com/protocolbuffers/protobuf/releases

选择java语言的最新版本即可,我这里安装的是3.13.0,下载完成后解压,然后就是需要配置环境

1、首先进到protobuf安装目录,即 protobuf-3.13.0 目录下

2、分别执行如下命令

./configure --prefix=/Users/xxxxx/protoBuf/protobuf-3.13.0
make
make install

注意:--prefix后的目录即你的 protobuf 安装目录,若 make 命令出现如下报错:

xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun

则先执行如下命令重装 xcode command line,然后再回到上面步骤重新执行make

xcode-select --install

如果还是没有解决问题,执行

sudo xcode-select -switch /

当你将 make install 命令执行完后,接下来就是配置 protoc 的环境变量了:

vi ~/.bash_profile

然后插入如下语句

export PROTBUF=/Users/xujia/xxxxx/protoBuf/protobuf-3.13.0
export PATH=$PROTBUF/bin:$PATH

重新运行配置文件

source ~/.bash_profile

这时使用 protoc --version 执行输出如下版本号说明配置完毕

libprotoc 3.13.0

编译

配置完环境变量后我们需要来验证一下protobuf,首先需要先编写一个.proto文件,这里直接给出一个小demo,方便验证

syntax = "proto3"; // 定义语法类型,通常proto3好于proto2,proto2好于proto1,如果不指定,默认使用proto2,必须位于第一行
 
package hello; // 定义作用域
option java_package = "grpc.demo";
 
service Hello {  // 定义服务
  rpc SayHello (HelloRequest) returns (HelloResponse) {}
}
 
message HelloRequest { // 定义消息体
  string name = 1;
}
 
message HelloResponse {
  string message = 1;
}

然后将该文件放入安装目录中的 examples 文件夹,执行如下命令后就能在当前目录下看到一个 grpc 文件夹,里面就有对应的java文件

简单案例

本案例只演示单项服务方法,.proto 文件则使用上面的小 demo,首先将 .proto 文件移动到项目中,注意 .proto 文件中指定的 java 包路径需要与项目中指定的一致,项目图如下所示:

maven配置

引入 grpc 的jar包依赖,同时还需要引入 protobuf 的插件依赖,以便能 maven 的方式生成 grpc 代码,注意下面的<protoSourceRoot> 标签的路径需要与上面标红的保持一致

 <properties>
        <grpc.version>1.23.0</grpc.version>
        <protobuf.version>3.13.0</protobuf.version>
    </properties>
 
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-netty-shaded</artifactId>
        <version>${grpc.version}</version>
    </dependency>
 
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-protobuf</artifactId>
        <version>${grpc.version}</version>
    </dependency>
 
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-stub</artifactId>
        <version>${grpc.version}</version>
    </dependency>
 
 
<build>
    <!-- ${os.detected.classifier} 变量由${os.detected.name} 和 ${os.detected.arch} 组成
    https://github.com/trustin/os-maven-plugin -->
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.6.2</version>
        </extension>
    </extensions>
 
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>11</source>
                <target>11</target>
            </configuration>
        </plugin>
 
        <plugin>
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.6.1</version>
 
            <configuration>
                <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
                <pluginId>grpc-java</pluginId>
                <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                <protoSourceRoot>src/main/proto</protoSourceRoot>
            </configuration>
 
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

生成 grpc 相关类

两种方式,一个是在终端使用命令:

mvn protobuf:compile
mvn protobuf:compile-custom

另外一个是通过 maven 的插件点击方式,本质是一样的:

执行完后在 target 目录下可以看到生成的类

编写服务端

package grpc.demo;
 
import grpc.demo.HelloGrpc;
import grpc.demo.HelloOuterClass.HelloRequest;
import grpc.demo.HelloOuterClass.HelloResponse;
import io.grpc.stub.StreamObserver;
import io.grpc.Server;
import io.grpc.ServerBuilder;
 
import java.io.IOException;
import java.util.concurrent.TimeUnit;
 
public class HelloServer {
 
    private Server server;
 
    private void start() throws IOException {
        int port = 50051;
        server = ServerBuilder.forPort(port)
                .addService(new HelloImpl())
                .build()
                .start();
        System.out.println("Server started, listening on " + port);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            // jvm关闭前执行
            System.err.println("*** shutting down gRPC server since JVM is shutting down");
            try {
                HelloServer.this.stop();
            } catch (InterruptedException e) {
                e.printStackTrace(System.err);
            }
            System.err.println("*** server shut down");
        }));
    }
 
    private void stop() throws InterruptedException {
        if (server != null) {
            server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
        }
    }
 
    /**
     * 阻塞等待主线程终止
     * @throws InterruptedException
     */
    private void blockUntilShutdown() throws InterruptedException {
        if (server != null) {
            server.awaitTermination();
        }
    }
 
    public static void main(String[] args) throws IOException, InterruptedException {
        final HelloServer server = new HelloServer();
        server.start();
        server.blockUntilShutdown();
    }
 
    /**
     * 服务实现类
     */
    private class HelloImpl extends HelloGrpc.HelloImplBase{
 
        @Override
        public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
            HelloResponse helloResponse = HelloResponse.newBuilder().setMessage("Hello "+request.getName()+", I'm Java grpc Server").build();
            responseObserver.onNext(helloResponse);
            responseObserver.onCompleted();
        }
    }
}

运行服务端后,控制台输出

Server started, listening on 50051

编写客户端

package grpc.demo;
 
import io.grpc.ManagedChannel;
import grpc.demo.HelloGrpc;
import io.grpc.ManagedChannelBuilder;
import grpc.demo.HelloOuterClass.HelloRequest;
import grpc.demo.HelloOuterClass.HelloResponse;
 
import java.util.concurrent.TimeUnit;
 
public class HelloClient {
 
    /**
     * 远程连接管理器,管理连接的生命周期
     */
    private final ManagedChannel channel;
    /**
     * 远程服务存根
     */
    private final HelloGrpc.HelloBlockingStub blockingStub;
 
    public HelloClient(String host, int port) {
        // 初始化连接
        channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
        // 初始化远程服务存根
        blockingStub = HelloGrpc.newBlockingStub(channel);
    }
 
    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }
 
    public String sayHello(String name) {
        // 构造服务调用参数对象
        HelloRequest request = HelloRequest.newBuilder().setName(name).build();
        // 调用远程服务方法
        HelloResponse response = blockingStub.sayHello(request);
        return response.getMessage();
    }
 
    public static void main(String[] args) throws InterruptedException {
        HelloClient client = new HelloClient("127.0.0.1", 50051);
        String content = client.sayHello("Java client");
        System.out.println(content);
        client.shutdown();
    }
}

先启动服务端再启动客户端,控制台输出

Hello Java client, I'm Java grpc Server

说明此时已从服务端获取响应

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java gRPC使用了grpc-netty-shaded库。该库是一个RPC库和框架,用于在Java中构建分布式系统。它可以与JDK 7一起使用,并支持Android设备上的Java客户端。另外,我还发现一个有关Java性能和内存使用情况的图形化工具,叫做Visual VM。这个工具可以用于监视本地和远程的Java虚拟机,识别和解决Java应用程序的性能问题。在你提到的引用中,也提到了Java Thread Sleep事件,它表示代码中存在手动调用Thread.sleep的行为。这个事件通常用于处理请求超时的场景,并且不会对整体性能产生影响。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [grpc-javaJava gRPC实现。 基于HTTP2的RPC](https://download.csdn.net/download/weixin_42138408/15241462)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [通过 HTTP/2 协议案例学习 Java & Netty 性能调优:工具、技巧与方法论](https://blog.csdn.net/BASK2311/article/details/130840661)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值