gRPC框架的简单使用

01.概述

进程间通信的演化过程

进程间的通信:通常指的是一个应用实例调用另外一个应用实例所提供的服务,而这两个应用都运行在自己独立的进程中,通过网络彼此交换信息的过程。

在这里插入图片描述
进程间通信的演化过程:

  • 传统的RPC技术:JAVA远程方法调用(RMI),基于TCP通信实现远程调用,实现逻辑非常复杂,且只能规定在JAVA应用之间实现远程通信。

  • SOAP协议:简单对象访问协议,通过HTTP协议上封装XML格式的SOAP数据包,实现跨进程通信,是早期webService技术的底层实现,消息格式的复杂性与以及围绕SOAP所构建的各种规范的复杂性,妨碍了构建分布式应用程序的敏捷性,逐渐被REST架构风格的应用程序所替代。

  • RESTful架构风格:基于HTTP协议。

  • Google:gRPC框架,可以像调用本地方法一样调用远程方法。

Google gRPC框架

RPC(Remote Procedure Call)即远程过程调用:

  • 它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。

  • RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层应用层,RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

  • RPC采用客户机/服务器模式,请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。

RPC优势:

  • 提供高效的进程间通信,使用protobuf的二进制协议进行进程间的通信
  • 具有简单且定义良好的服务接口和模式(约定大于配置)
  • 属于强类型调用
  • 支持多语言
  • 支持双工通信

RPC的缺点:

  • 不适合向外部提供服务
  • 巨大的服务定义变更起来比较复杂
  • gRPC生态系统相对较小

gRPC传输格式protobuf:

  • protobuf(Google Protocol Buffers)是Google提供的一个高效数据交换的协议工具库,Protobuf有更高的转化效率,时间效率和空间效率,是JSON的3-5倍。
    在这里插入图片描述

安装Protobuf环境和ProtobufIDEA插件

proto文件编译器下载地址:https://github.com/protocolbuffers/protobuf/releases

注意下载对应版本的编译器(windows,linux等),下载好之后解压,然后配置环境变量即可(bin目录)
在这里插入图片描述

查看能否运行,在cmd中输入命令:protoc --version 能打印出版本号即可

在IDEA 插件中安装 Protobuf插件,安装这个插件之后会对proto文件有个语法检查和代码提示的功能

02.原理

GRPC底层原理

在这里插入图片描述

protobuf语法解析

// proto版本
syntax="proto3";
// 是否生成多个类,true会生成多个类,false生成单个类
option java_multiple_files=false;
// 服务类所在的包
option java_package="com.aismall.news.proto";
//服务的名字:也就是服务的名字,一个服务中有多个接口
option java_outer_classname = "NewsProto";
// .proto包名
package news;

// service关键字,用于描述要生成的api接口是什么,类似于java的逻辑接口类
// rpc 为关键字
// rpc 方法(参数类型) returns (返回值类型){}

service NewsService {
    rpc list(NewsRequest)
            returns (NewsResponse){}
}

/*
	消息是rpc描述信息的基本单位,类似于java中的实体类
	消息名字,对应于生成代码的类名
	每个消息都对应生成的一个类,根据java_muitiple_files设置不同文件数量
	option java_multiple_files=true; protobuf会给每个message生成一个java类
	option java_multiple_files=false; protobuf会生成一个大类,消息为该类的子类,都在一个java大类中
*/

/*
	字段定义方式:
		类型 名称 =索引值(id)
		每个字段都要定义一个唯一的索引值,这些数字用来在消息二进制时,用来识别字段
		一旦开始使用就不能再更改,标识号从1开始
		可以为将来需要使用的标识号预留标识符
*/
message NewsRequest {
    string data = 1;
}

message NewsResponse {
	// repeated说明是一个集合(数组),数组的每一个对象都是news
    repeated News news = 1;
}
message News {
    int32 id = 1;
    string title = 2;
    string content=3;
    int64 createTime=4;
}

/*
	生成文件介绍:
		消息名orBuilder:消息类于构造器接口
		消息名:消息的具体实现
	protobuf还可以实现类型的嵌套,嵌套之后必须要声明这个message才可以
*/

注意:

  • proto文件:可以生成不同语言的代码,通过不同的指令生成不同的语言代码,如java,go等。

  • poto文件中的message一旦定义好编号,尽量不要修改。

生成代码解析

在这里插入图片描述

03.案例实战演示

在这里插入图片描述
客户端和服务端进行通信的依据就是基于相同的proto文件生成的代码,两种方式:

  • 方式一:server端和client端使用相同的proto文件,生成相同的代码。
  • 方式二:使用proto生成代码之后,将相应的代码打成jar包,然后上传到自己的中央仓库,然后server端和client端都引用这个jar包。

注意:

  • springoot版本:2.4.1

RPC服务端搭建

步骤:

  • 添加gRPC依赖坐标,引入protobuf-maven-plugin插件
  • 编写proto文件
  • 实现服务端业务逻辑
  • 服务端开发

1、新建一个maven项目,引入如下依赖以及插件

<dependencies>
    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java</artifactId>
        <version>3.21.2</version>
    </dependency>
    <!-- grpc底层基于netty开发:底层通信组件 -->
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-netty-shaded</artifactId>
        <version>1.42.0</version>
    </dependency>
    <!-- protobuf的支持工具包 -->
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-protobuf</artifactId>
        <version>1.42.0</version>
    </dependency>
    <!-- 处理存根 -->
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-stub</artifactId>
        <version>1.42.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>annotations-api</artifactId>
        <version>6.0.53</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
<build>
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.6.2</version>
        </extension>
    </extensions>
    <plugins>
        <plugin>
            <!--protobuf插件:通过配置的环境变量找到protobuf编译器-->
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.6.1</version>
            <configuration>
                <!--生成代码-->
                <protocArtifact>com.google.protobuf:protoc:3.21.2:exe:${os.detected.classifier}</protocArtifact>
                <pluginId>grpc-java</pluginId>
                <!--生成通信类-->
                <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.42.0:exe:${os.detected.classifier}</pluginArtifact>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <!--生成消息代码-->
                        <goal>compile</goal>
                        <!--生成GRPC通信文件-->
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

2、在main目录下新建一个proto文件,用于存放拓展名proto结尾的文件(news.proto)。

syntax="proto3";
option java_multiple_files=false;
option java_package="com.aismall.news.proto";
option java_outer_classname = "NewsProto";
package news;
service NewsService{
    rpc list(NewsRequest) returns (NewsResponse){}
}
message NewsRequest{
    string date=1;
}
message NewsResponse{
    repeated News news = 1;
}
message News{
    int32 id = 1;
    string title = 2;
    string content=3;
    int64 createTime=4;
}

3、使用插件生成消息代码GRPC通信文件

  • Maven——>Plugins—>protobuf——>compile:生成消息代码
  • Maven——>Plugins—>protobuf——>compile-custom:生成GRPC通信文件

4、移动生成的文件:生成的文件会在target/generated-sources/protobuf/grpc-java目录target/generated-sources/protobuf/java目录下面,将生成文件移动到源代码同级目录下即可。

5、编写业务类:继承NewsServiceGrpc.NewsServiceImplBase类,并实现里面的方法,在方法里重写业务逻辑。

public class NewsService extends NewsServiceGrpc.NewsServiceImplBase {
    //注意:入参为方法里面第一个参数,出参为方法里面的第二个参数
    @Override
    public void list(NewsProto.NewsRequest request, StreamObserver<NewsProto.NewsResponse> responseObserver) {
        // 获取入参的data属性
        String date = request.getDate();
        // 反参对象
        NewsProto.NewsResponse newsList = null;
        try {
            // 反参的构建器
            NewsProto.NewsResponse.Builder newsListBuilder = NewsProto.NewsResponse.newBuilder();
            for ( int i = 1;i<=25;i++ ){
                // 构建反参中的存储的实体类
                NewsProto.News news = NewsProto.News.newBuilder().setId(i)
                        .setContent(date + "新闻内容" + i)
                        .setTitle("新闻标题" + i)
                        .setCreateTime(new Date().getTime())
                        .build();
                // 将构建好的实体类存入反参的构建器中
                newsListBuilder.addNews(news);
            }
            // 调用build()方法完成反参的构建
            newsList = newsListBuilder.build();
        } catch (Exception e) {
            responseObserver.onError(e);
        }finally {
            // 实用观察者模式,将参数返回
            responseObserver.onNext(newsList);
        }
        // 关闭
        responseObserver.onCompleted();
    }
}

6、修改启动类:

public class GrpcNewsServer {
	// 定义一个端口
    public static final int port = 7520;
    public static void main(String[] args) throws InterruptedException, IOException {
    	// 启动grpc服务
        io.grpc.Server server = ServerBuilder.forPort(port).addService(new NewsService())
                .build().start();
        System.out.println(String.format("GRPC 服务启动,端口号:%d", port));
        server.awaitTermination();
    }
}

RPC客户端

1 、2、3、4步骤如法炮制(参考:server端)

5、修改主启动类:在主启动类中调用server端提供的服务。

/*
* 测试 NewsService接口中的list服务
* */
public class GrpcNewsClient {
    // 访问server的url和端口号
    private static final String host="localhost";
    private static final int serverPort=7520;
    public static void main(String[] args) {
        //建立一个传输文本的通道
        ManagedChannel channel= ManagedChannelBuilder.forAddress(host,serverPort).usePlaintext()
                .build();
        // 创建一个传输通道,使用阻塞模式
        NewsServiceGrpc.NewsServiceBlockingStub blockingStub=NewsServiceGrpc.newBlockingStub(channel);
        //创建一个入参类
        NewsProto.NewsRequest request= NewsProto.NewsRequest.newBuilder().setData("20220709").build();
        // 调用NewService接口中的list服务:调用方式类似于调用方法一样。
        // 调用之后,会给我们返回一个出参类型的值:NewsResponse实例,该实例在服务端创建,通过上面定义的通信方式传输过来
        NewsProto.NewsResponse response=blockingStub.list(request);
        //获取出参实例中的参数
        List<NewsProto.News> newsList=response.getNewsList();
        for (NewsProto.News news:newsList) {
            System.out.println(news);
        }
        //关闭传输通道
        channel.shutdown();
    }
}

小结

通过上面的例子我们可以看出,rpc服务之间的调用,就好像调用自己本地的方法一样。

本次演示,server端和client端使用相同的proto文件,生成相同的代码。

后面介绍springboot整合grpc框架的时候采用,使用proto生成代码之后,将相应的代码打成jar包,然后上传到自己的中央仓库,然后server端和client端都引用这个jar包。

04.Springboot整合gPRC

grpc-server-spring-boot-starter/grpc-client-spring-boot-starter是第三方提供的Springboot整合gRPC的整合包,基于Springboot自动装配和声明式特性,整体简化了gRPC在Springboot的开发过程。

提前准备

新建一个项目maven项目,用于将proto文件生成代码打成jar包,然后上传到自己的本地仓库。

1、新建名为news的Maven工程,引入如下依赖。

<dependencies>
    <dependency>
        <groupId>com.google.protobuf</groupId>
        <artifactId>protobuf-java</artifactId>
        <version>3.21.2</version>
    </dependency>
    <!-- grpc底层基于netty开发:底层通信组件 -->
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-netty-shaded</artifactId>
        <version>1.42.0</version>
    </dependency>
    <!-- protobuf的支持工具包 -->
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-protobuf</artifactId>
        <version>1.42.0</version>
    </dependency>
    <!-- 处理存根 -->
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-stub</artifactId>
        <version>1.42.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>annotations-api</artifactId>
        <version>6.0.53</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
<build>
    <extensions>
        <extension>
            <groupId>kr.motd.maven</groupId>
            <artifactId>os-maven-plugin</artifactId>
            <version>1.6.2</version>
        </extension>
    </extensions>
    <plugins>
        <plugin>
            <!--protobuf插件:通过配置的环境变量找到protobuf编译器-->
            <groupId>org.xolstice.maven.plugins</groupId>
            <artifactId>protobuf-maven-plugin</artifactId>
            <version>0.6.1</version>
            <configuration>
                <!--生成代码-->
                <protocArtifact>com.google.protobuf:protoc:3.21.2:exe:${os.detected.classifier}</protocArtifact>
                <pluginId>grpc-java</pluginId>
                <!--生成通信类-->
                <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.42.0:exe:${os.detected.classifier}</pluginArtifact>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <!--生成消息代码-->
                        <goal>compile</goal>
                        <!--生成GRPC通信文件-->
                        <goal>compile-custom</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

2、在main目录下新建一个proto文件夹,用于存放拓展名proto结尾的文件(news.proto)。

syntax="proto3";
option java_multiple_files=false;
option java_package="com.aismall.news.proto";
option java_outer_classname = "NewsProto";
package news;
service NewsService{
    rpc list(NewsRequest) returns (NewsResponse){}
}
message NewsRequest{
    string date=1;
}
message NewsResponse{
    repeated News news = 1;
}
message News{
    int32 id = 1;
    string title = 2;
    string content=3;
    int64 createTime=4;
}

3、使用插件生成消息代码GRPC通信文件

  • Maven——>Plugins—>protobuf——>compile:生成消息代码
  • Maven——>Plugins—>protobuf——>compile-custom:生成GRPC通信文件

4、移动生成的文件:生成的文件会在target/generated-sources/protobuf/grpc-java目录target/generated-sources/protobuf/java目录下面,将生成文件移动到源代码同级目录下即可。

5、在POM文件里把插件注释掉,要不然在第六步会报错。

6、在maven插件中的Lifecyle中: 先双击clean、在双击install,就可以把我们的news项目上传到本地仓库了。

注意: 依赖的坐标
在这里插入图片描述

server

1、新建一个Springboot项目,Springboot版本为2.4.1。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!--Springboot集成gRPC框架:server端-->
    <dependency>
        <groupId>net.devh</groupId>
        <artifactId>grpc-server-spring-boot-starter</artifactId>
        <version>2.13.0.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-all</artifactId>
        <version>1.42.0</version>
    </dependency>
     <!--注意:这个是前面自己生成的jar包-->
    <dependency>
        <groupId>org.aismall</groupId>
        <artifactId>news</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

2、编写业务类,继承NewsServiceGrpc.NewsServiceImplBase类,并实现里面的方法

// 使用注解标注这是一个Grpc的服务:标识接口
//springboot扫描的时候,会认为这个类是grpc的实现类
@GrpcService
public class NewsService extends NewsServiceGrpc.NewsServiceImplBase {
    //注意:入参为方法里面第一个参数,出参为方法里面的第二个参数
    @Override
    public void list(NewsProto.NewsRequest request, StreamObserver<NewsProto.NewsResponse> responseObserver) {
        // 获取入参的data属性
        String date = request.getDate();
        // 反参对象
        NewsProto.NewsResponse newsList = null;
        try {
            // 反参的构建器
            NewsProto.NewsResponse.Builder newsListBuilder = NewsProto.NewsResponse.newBuilder();
            for ( int i = 1;i<=25;i++ ){
                // 构建反参中的存储的实体类
                NewsProto.News news = NewsProto.News.newBuilder().setId(i)
                        .setContent(date + "新闻内容" + i)
                        .setTitle("新闻标题" + i)
                        .setCreateTime(new Date().getTime())
                        .build();
                // 将构建好的实体类存入反参的构建器中
                newsListBuilder.addNews(news);
            }
            // 调用build()方法完成反参的构建
            newsList = newsListBuilder.build();
        } catch (Exception e) {
            responseObserver.onError(e);
        }finally {
            // 实用观察者模式,将参数返回
            responseObserver.onNext(newsList);
        }
        // 关闭
        responseObserver.onCompleted();
    }
}

3、修改配置文件

# 应用名称
spring.application.name=springboot-grpc-server
# web服务:内置tomcat对外提供的端口号
server.port = 8725
# grpc服务:grpc服务之间通信的端口号
grpc.server.port = 7725

4、启动服务等待客户端的调用。

client

1、新建一个Springboot项目,Springboot版本为2.4.1。

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!--Springboot集成gRPC框架:client端-->
    <dependency>
        <groupId>net.devh</groupId>
        <artifactId>grpc-client-spring-boot-starter</artifactId>
        <version>2.13.0.RELEASE</version>
    </dependency>

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

    <dependency>
        <groupId>org.aismall</groupId>
        <artifactId>news</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

2、编controller类,继承NewsServiceGrpc.NewsServiceImplBase类,并实现里面的方法。

//restful风格的控制器
@RestController
public class NewsController {
    //指向配置文件的 grpc-server 配置
    @GrpcClient("grpc-server")
    private NewsServiceGrpc.NewsServiceBlockingStub newsServiceBlockingStub;

    //访问链接地址:localhost:8726/news?date=20220725
    @GetMapping("news")
    public String news(String date) {
        System.out.println("==================");
        NewsProto.NewsResponse newsResponse = newsServiceBlockingStub.list(NewsProto.NewsRequest.newBuilder().setDate(date).build());
        return newsResponse.getNewsList().toString();

    }
}

3、修改配置文件

# 应用名称
spring.application.name=springboot-grpc-client
# web服务 :内置tomcat对外提供的端口号
server.port = 8726
# 定义客户端访问的grpc服务地址和端口号
grpc.client.grpc-server.address= static://localhost:7725
# 文本响应
grpc.client.grpc-server.negotiation-type: plaintext

4、启动服务,使用浏览器访问:localhost:8726/news?date=20220725

05.总结

代码地址:https://gitee.com/aismall/mystudym

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python中的gRPC框架可以用来构建高效的、可扩展的分布式系统。它是一个开源的、高性能的RPC(远程过程调用)框架,用于构建客户端和服务器之间的通信。在Python中使用gRPC框架,通常需要以下几个步骤: 1. 定义.proto文件:.proto文件是gRPC使用的接口定义语言,它描述了数据类型和服务接口的定义。 [1] 2. 使用grpc_tools工具将.proto文件编译成Python代码:可以使用grpc_tools工具包中的protoc命令来将.proto文件编译成相应的Python代码。 [2] 3. 实现gRPC服务:在服务器端实现gRPC服务,这包括实现.proto文件中定义的服务接口。 [3] 4. 实现gRPC客户端:在客户端调用gRPC服务,发送请求并接收响应。 [3] 总结来说,Python中的gRPC框架可以通过定义.proto文件,使用grpc_tools工具编译成Python代码,然后实现gRPC服务和客户端来进行分布式系统的通信。123 #### 引用[.reference_title] - *1* [gRPC 框架 简单使用(python版)](https://blog.csdn.net/Java_KW/article/details/124334604)[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] - *2* *3* [python 3 使用 grpc](https://blog.csdn.net/zscccccc/article/details/131124128)[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] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彤彤的小跟班

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值