多语言gRPC开发入门与避坑指南

目录

gRPC相关介绍

什么是gPRC

gPRC的优点

gPRC的缺点

gPRC定位

协议缓冲区(Protocol Buffers)

四种调用方式

gRPC开发三大步骤

第一步:定义和编写proto服务文件

第二步:proto文件转化为gRPC代码

第三步:调用gRPC代码

详细开发细节

Server端-Java

 Client端-Java

Client端-Golang

步骤一:准备插件

步骤二:准备proto服务描述文件

步骤三:基于proto文件生成gRPC代码

步骤四:编写客户端代码调用gRPC

 Client端-Python

步骤一:安装grpc和相关工具:

步骤二:忽略

步骤三:基于proto文件生成gRPC代码

步骤四:编写客户端代码调用gRPC

避坑指南

Java

Golang

Python

总结


gRPC相关介绍

什么是gPRC

基于http2+protocol buffer技术,简单说就是编码压缩+缓存,追求高效率。

gPRC的优点

1、高性能,毕竟是rpc技术,这个优点是我们选择gRPC的主要出发点。
2、报文可在stream上双向流传输,这个是http协议做不到的,用到双向流的场景首选gRPC。

gPRC的缺点

1、开发步骤很复杂! 这个是被gRPC劝退的主要原因,开发过程一不小心就踩坑。
2、目前支持的不充分。简单说就是太新了,像浏览器啥的支持的不好,导致开发调试没那么方便。

gPRC定位

gPRC是CNCF里唯一的传输协议项目,它的定位就是云原生时代分布式、微服务集群的核心底层通信服务。基于这个定位,我们就有必须学习它的理由。

协议缓冲区(Protocol Buffers)

这个是gRPC协议的核心模块,.proto文件、protoc工具、protobuf插件等跟它有关的名词充斥着整个gRPC开发过程。
Protocol Buffers是google推出的一种序列化的协议,它的定位就像json、xml、对象二进制流等一样,是用于对象传输过程中编码解码的。
它比json还要精简,主要体现在编码时丢弃了一些不必要的内容,解码的时候抛弃了全扫描的方式。

四种调用方式

1、同步函数式,一来一回
rpc SayHello(HelloRequest) returns (HelloResponse);
2、 客户端流式,多来一回
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);
3、服务端流式,一来多回
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);
4、双向流,多来多回
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);

gRPC开发三大步骤

第一步:定义和编写proto服务文件

像定义API一样,直接编写一个.proto后缀的服务描述文件,如下:

syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.jingtao.library";
option java_outer_classname = "BookServiceProto";
option objc_class_prefix = "HLW";

package mybook;
// The greeting service definition.
service BookService {
  // Sends a greeting
  rpc check (RequestData) returns (Book) {}
}

// The request message containing the user's name.
message RequestData {
  string name = 1;
}

message Book {
  string name = 1;
  string auther = 2;
  int32 price = 3;
}

syntax定义协议版本
option部分是跟着语言耦合的部分
package定义了服务的包名
service定义了服务的函数
message定义了服务函数需要打的入参、出参等对象

第二步:proto文件转化为gRPC代码

该步骤是把上面的.proto文件通过工具或插件生成可以被程序直接调用的gRPC代码,因为要被程序调用,所以这一步是与语言紧耦合的,不同语言转换的方式各不相同。

第三步:调用gRPC代码

到了第三步就相对轻松了,第二步生成的gRPC文件已经以接口的方式将服务封装好了,我们要干的只是import它,然后调用它。

详细开发细节

Server端-Java

java语言.proto文件转化为gRPC有三种方式

方式一:独立转换
在操作系统中安装转换可视化或者命令工具,例如protoc(下载地址https://github.com/protocolbuffers/protobuf/releases),在操作系统层做好转换后将转换后的代码copy到代码项目中。
方式二:开发工具插件转化
以IDEA为例,转换的插件有GenProtoBuf、Protocol Buffer,而且都是免费的。
方式三:依赖转换
直接在项目中引入相关依赖插件,利用它们生成代码。例如java中的protobuf-java-util

我采用的是方式三,pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>BookFactory</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <protoc.version>3.12.0</protoc.version>
        <grpc.version>1.56.1</grpc.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.12.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util -->
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java-util</artifactId>
            <version>3.12.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.grpc/grpc-all -->
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-all</artifactId>
            <version>1.56.1</version>
        </dependency>
    </dependencies>

    <build>
        <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>
                <version>3.7.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</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:${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>
                    <protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
                    <outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
                    <clearOutputDirectory>false</clearOutputDirectory>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

双击Plugin中protobuf的compile生成服务bean,通过custom生成可直接被上层调用的gRPC。

服务端引用并实现grpc代码

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

服务端代码如下:

package com.jingtao.library;

import io.grpc.Grpc;
import io.grpc.InsecureServerCredentials;
import io.grpc.Server;
import io.grpc.stub.StreamObserver;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

public class BookServer {

    private static final Logger logger = Logger.getLogger(BookServer.class.getName());

    private Server server;

    int port = 8088;

    private void start() throws IOException {
        server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create())
                .addService(new BookImpl())
                .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");
                try {
                    BookServer.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);
        }
    }

    static class BookImpl extends BookServiceGrpc.BookServiceImplBase {

        @Override
        public void check(RequestData req, StreamObserver<Book> responseObserver) {
            Book reply = Book.newBuilder().setName(req.getName()).setAuther("Shakespeare").setPrice(35).build();
            responseObserver.onNext(reply);
            responseObserver.onCompleted();
        }
    }

    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 BookServer server = new BookServer();
        server.start();
        server.blockUntilShutdown();
    }

}

 Client端-Java

Java客户端前面部分跟服务端一样,只有最后一步不同:

package com.jingtao.library;

import io.grpc.*;

import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

public class BookClientor {

    private static final Logger logger = Logger.getLogger(BookClientor.class.getName());

    private final BookServiceGrpc.BookServiceBlockingStub blockingStub;

    public BookClientor(Channel channel) {
        blockingStub = BookServiceGrpc.newBlockingStub(channel);
    }

    public void say(String name) {
        logger.info("Will try to greet " + name + " ...");
        RequestData request = RequestData.newBuilder().setName(name).build();
        Book response;
        try{
            response = blockingStub.check(request);
        }catch (StatusRuntimeException e) {
            logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
            return;
        }
        logger.info("Invoke say result is : " + response.getName()
                + " " + response.getAuther()
                + " " + response.getPrice());
    }

    public static void main(String[] args) throws Exception{
        String user = "les miserables";
        // Access a service running on the local machine on port 50051
        String target = "localhost:8088";
        ManagedChannel channel = Grpc.newChannelBuilder(target, InsecureChannelCredentials.create())
                .build();
        try {
            BookClientor client = new BookClientor(channel);
            client.say(user);
        } finally {
            // ManagedChannels use resources like threads and TCP connections. To prevent leaking these
            // resources the channel should be shut down when it will no longer be used. If it may be used
            // again leave it running.
            channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
        }
    }
}

Client端-Golang

步骤一:准备插件

给protoc准备go相关插件和工具
git clone https://github.com/golang/protobuf.git $GOPATH/src/github.com/golang/protobuf
cd $GOPATH/src/
go install github.com/golang/protobuf/protoc-gen-go@latest
这一步的目标是准备好protoc-gen-go,搞定后一定要确保包含protoc-gen-go的bin在path中,可以被protoc用到。

步骤二:准备proto服务描述文件

注意需要显式的指定所生成gRPC代码的包名
option go_package="./;book";

步骤三:基于proto文件生成gRPC代码

准备好proto文件后,执行以下命令生成go的gRPG文件
protoc -I proto/ --go_out=plugins=grpc:proto proto/book_route.proto

步骤四:编写客户端代码调用gRPC

package book

import (
	bookgrpc "book-client-go/proto"
	"fmt"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
)

func MyClient() {
	conn, err := grpc.Dial(":8088", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		// handle error
		panic(err)
	}
	defer conn.Close()

	client := bookgrpc.NewBookServiceClient(conn)

	req := bookgrpc.RequestData{
		Name: "world",
	}

	reply, err := client.Check(context.Background(), &req)
	if err != nil {
		fmt.Println("client.bookservice error:", err)
		return
	}

	fmt.Printf("get msg from server:[%v] \n", reply)

}

 Client端-Python

步骤一:安装grpc和相关工具:

pip install grpcio
pip install grpcio-tools  
pip install protobuf

步骤二:忽略

python不需要单独设置option_package,因为最终生成的gRPC代码在python中放在哪个目录下就引入哪个目录好了。

步骤三:基于proto文件生成gRPC代码

然后执行以下python命令将proto文件转换为gRPC的python代码
python -m grpc_tools.protoc -I ./ --python_out=./ --grpc_python_out=. ./book_route.proto

步骤四:编写客户端代码调用gRPC

import grpc

from book import book_route_pb2,book_route_pb2_grpc

def run():
    conn = grpc.insecure_channel('127.0.0.1:8088')
    client = book_route_pb2_grpc.BookServiceStub(channel=conn)
    reqeust = book_route_pb2.RequestData(name='jingtao')
    respnse = client.check(reqeust)
    print("received name:", respnse.name)
    print("received auther:", respnse.auther)
    print("received:", respnse.price)

if __name__ == '__main__':
    run()

避坑指南

Java

1、java.lang.NoSuchMethodError: java.nio.ByteBuffer.clear()Ljava/nio/ByteBuffer;

代码全部都编译过并切启动成功的前提下,客户端和服务端进行grpc交互时遇到这个报错

核心是jdk版本与grpc依赖包版本不一致导致的
我用的jdk1.8+protoc3.12.0+grpc1.57.0就会出这个错误,换成jdk1.8+protoc3.12.0+grpc1.56.1后问题解决。

 2、UnusedPrivateParameter unused:

通过插件基于proto文件生产的gprc代码中出现了UnusedPrivateParameter编译错误,核心原因是maven导入的依赖版本与实际所需要的依赖版本不一致导致的。

错误配置:

正确配置:

Golang

1、protoc-gen-go: unable to determine Go import path for "book_route.proto"

protoc -I proto/ --go_out=plugins=grpc:proto proto/book_route.proto的时候报错:

原因是我从java项目中直接copy了proto过来,但里面缺少go需要的配置,修改proto后问题解决。
所以gRPC开发过程中proto文件不能直接复制,需要摘选出service和message部分,这是一个比较讨厌的地方。 

Python

1、Cannot unpack file C:\Users\MGTV\AppData\Local\Temp\pip-unpack-iee13adu\simple

执行pip安装grpc时报错,原因是因为pip的源有问题。

开始pip安装网络太慢所以用到代理:
pip install grpcio-i http://pypi.mirrors.ustc.edu.cn/pypi/simple/ --trusted-host pypi.mirrors.ustc.edu.cn
把http://mirrors.aliyun.com/pypi/simple/替换成https://pypi.mirrors.ustc.edu.cn/simple/也解决不了,后来直接用了最原始的命令,没有走代理,问题解决

2、生成的_pd2.py缺少request和response的负载对象导致开发过程中编译不过

这个问题是grpc_tools在1.44版本以后由预定义变成了运行中自动生成对象了,不影响正常运行,这样虽然使代码更简洁了,但是可读性确实变差了。 

总结

本文用java实现了gRPC服务端,然后分别用java、go、python来调用,只给了“一来一回”同步函数式调用,其实gRPC支持4中调用方式,真实情况比复杂的多。但比起对接gRPC来说,开发环境的准备更复杂,坑也很多,希望通过本篇博文能把gRPC开发所有步骤和需要注意的地方都解释清楚。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/59062.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IDEA中maven项目失效,pom.xml文件橙色/橘色

IDEA中maven项目失效&#xff0c;pom.xml文件橙色/橘色 IDEA中Maven项目失效 IDEA中创建的maven项目中的文件夹都变成普通格式&#xff0c;pom.xml变成橙色 右键点击橙色的pom.xml文件&#xff0c;选择add as maven project maven项目开始重新导入相应依赖&#xff0c;恢复…

QT图形视图系统 - 使用一个项目来学习QT的图形视图框架 - 终篇

QT图形视图系统 - 终篇 接上一篇&#xff0c;我们需要继续完成以下的效果&#xff1b; 先上个效果图&#xff1a; 修改背景&#xff0c;使之整体适配 上一篇我们绘制了标尺&#xff0c;并且我们修改了放大缩小和对应的背景&#xff0c;整体看来&#xff0c;我们的滚动条会和…

单篇笔记曝光248万+,素颜、寸头…小红书女性种草新趋势分析!

最近&#xff0c;小红书上刮起一阵素颜、寸头&#xff0c;拒绝美丽绑架的风潮&#xff0c;他们称之为“脱美役”&#xff0c;即脱离美丽枷锁&#xff0c;做自己&#xff0c;接纳原本的自己。这是女性觉醒的又一阵风&#xff0c;品牌要如何跟上这波种草新趋势呢&#xff1f; 单篇…

村田授权代理:共模扼流线圈针对汽车专用设备高频噪声的降噪对策

车载市场正不断扩充ADAS、自动驾驶、V2X、车载信息系统等的应用。由于此类应用要处理庞大的信息&#xff0c;因此为了执行处理&#xff0c;内部处理信号的处理速度亦不断高速化。另一方面&#xff0c;由于部件数量增多&#xff0c;安装密度增大&#xff0c;因此要求部件小型化。…

JVM之垃圾回收器

1.如何判断对象可以回收 1.1 引用计数法 什么是引用计数器法 在对象中添加一个引用计数器&#xff0c;每当有一个地方引用它时&#xff0c;计数器值就加一&#xff1b;当引用失效时&#xff0c;计数器值就减一&#xff1b;任何时刻计数器为零的对象就是不可能再被使用的。 …

芯旺微冲刺IPO,车规级MCU竞争白热化下的“隐忧”凸显

在汽车智能化和电动化发展带来的巨大蓝海市场下&#xff0c;产业链企业迎来了一波IPO小高潮。 日前&#xff0c;上海芯旺微电子技术股份有限公司&#xff08;以下简称“芯旺微”&#xff09;在科创板的上市申请已经被上交所受理&#xff0c;拟募资17亿元&#xff0c;用于投建车…

【如何提高在浏览器时的专注力!去广告和新闻!】如何使用浏览器时看不到任何新闻以及广告【浏览器去除广告和新闻】

如何使用浏览器时看不到任何新闻以及广告 1. 使用chrome浏览器或者其他浏览器都可以2. 使用bing搜索3. 去广告 1. 使用chrome浏览器或者其他浏览器都可以 2. 使用bing搜索 bing 3. 去广告

【bug】记录一次使用Swiper插件时loop属性和slidersPerView属性冲突问题

简言 最近在vue3使用swiper时&#xff0c;突然发现loop属性和slides-per-view属性同时存在启用时&#xff0c;loop生效&#xff0c;下一步只能生效一次的bug&#xff0c;上一步却是好的。非常滴奇怪。 解决过程 分析属性是否使用错误。 loop是循环模式&#xff0c;布尔型。 …

Qt展示动态波形

Qt展示动态波形 需求描述成品展示实现难点Qt多线程 需求描述 接入串口&#xff0c;配置串口顺序进行接收数据&#xff1b;数据分成两个串口分别传入&#xff0c;使用多线程并发接入&#xff1b;时域数据有两个通道&#xff08;I&#xff0c;Q&#xff09;&#xff0c;分别以实…

【汇总】解决Ajax请求后端接口,返回ModelAndView页面不跳转

【汇总】解决Ajax请求后端接口&#xff0c;返回ModelAndView不跳转 问题发现问题解决方法一&#xff1a;直接跳转到指定URL&#xff08;推荐&#xff09;方法二&#xff1a;将返回的html内容&#xff0c;插入到页面某个元素中方法三&#xff1a;操作文档流方法四&#xff1a;使…

Windows terminal 添加 git bash 解决git中文乱码显示问题

Windows terminal 添加 git bash 解决git中文乱码显示问题 在 windows terminal 中配置git 说明&#xff1a; 点击箭头选择设置 说明&#xff1a; 点击"添加新配置文件"配置名称命令行&#xff0c;可执行文件的具体语句 C:\Program Files\Git\bin\bash.exe启动目录…

什么是注意力机制?注意力机制的计算规则

我们观察事物时&#xff0c;之所以能够快速判断一种事物(当然允许判断是错误的)&#xff0c;是因为我们大脑能够很快把注意力放在事物最具有辨识度的部分从而作出判断&#xff0c;而并非是从头到尾的观察一遍事物后&#xff0c;才能有判断结果&#xff0c;正是基于这样的理论&a…

【C语言】初阶结构体

&#x1f388;个人主页&#xff1a;库库的里昂 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏&#xff1a;C语言初阶 ✨其他专栏&#xff1a;代码小游戏 &#x1f91d;希望作者的文章能对你有所帮助&#xff0c;有不足的地方请在评论…

P14 电路定理——巧妙-灵性-智慧

1、替代定理 图示表示&#xff1a; 叠加定理和齐性定理只能用于线性电路&#xff0c;但是替代定理无论线不线性都可以用。 常见的&#xff1a;线性电路将某复杂支路等效成电压源或电流源之后&#xff0c;就可以使用叠加原理了。 引入两个相互抵消的电压源&#xff0c;拿其中一…

基于STM32设计的智能空调

一、项目背景 随着人们生活水平的不断提高&#xff0c;对居住环境的舒适度要求也越来越高。空调作为一种重要的家电设备&#xff0c;已经成为了现代家庭中必不可少的一部分。本文介绍了一种基于STM32的智能空调设计方案&#xff0c;可以自动地根据环境温度进行温度调节。 二、…

Opencv-C++笔记 (15) : 像素重映射 与 图像扭曲

文章目录 一、重映射简介二、图像扭曲 一、重映射简介 重映射&#xff0c;就是把一幅图像中某位置的像素放置到另一图像指定位置的过程。即&#xff1a; 在重映射过程中&#xff0c;图像的大小也可以同时发生改变。此时像素与像素之间的关系就不是一一对应关系&#xff0c;因…

PHP从入门到精通—PHP开发入门-PHP概述、PHP开发环境搭建、PHP开发环境搭建、第一个PHP程序、PHP开发流程

每开始学习一门语言&#xff0c;都要了解这门语言和进行开发环境的搭建。同样&#xff0c;学生开始PHP学习之前&#xff0c;首先要了解这门语言的历史、语言优势等内容以及了解开发环境的搭建。 PHP概述 认识PHP PHP最初是由Rasmus Lerdorf于1994年为了维护个人网页而编写的一…

租赁类小程序定制开发|租赁管理系统源码|免押租赁系统开发

随着互联网的发展&#xff0c;小程序成为了一种重要的移动应用开发方式。租赁小程序作为其中的一种类型&#xff0c;可以为很多行业提供便利和创新。下面我们将介绍一些适合开发租赁小程序的行业。   房屋租赁行业&#xff1a;租房小程序可以帮助房东和租户快速找到合适的租赁…

对当下AI的一些观感思考

目前来看&#xff0c;AI技术地震的震中还是在美帝那旮瘩。尤其是M7&#xff0c;这几家市值加总快15万亿美元了&#xff0c;个个都是行业翘楚&#xff0c;个个都有拿得出手的东西。AI是个技术密集、人才密集、计算密集的产业。美帝拥有全球一流的顶尖人才&#xff0c;以及财力、…

IntelliJ IDEA 2023.2社区版插件汇总

参考插件帝&#xff1a;https://gitee.com/zhengqingya/java-developer-document 突发小技巧&#xff1a;使用插件时要注意插件的版本兼容性&#xff0c;并根据自己的实际需求选择合适的插件。同时&#xff0c;不要过度依赖插件&#xff0c;保持简洁和高效的开发环境才是最重要…