一只彩色的熊猫

导航

gRPC实战-Demo

老齐-gRPC实战

介绍进程通信

何为进程间通信
image

通信的演化过程

1、传统RPC
image

2、SOAP
image

3、RESTful
image

RESTful架构存在的问题:

  • 它是基于文本的低效消息协议
  • 应用程序之间缺乏强类型接口
  • RESTful架构风格难以强制实施

4、gRPC

新一代RPC通信框架
image

初识Google gRPC

前身

长期以来,谷歌有一个名为Stubby(['stʌbi] 短粗的)的通用RPC框架,用来连接成千上万的微服务,这些微服务跨多个数据中心并且使用完全不同的技术来构建。Stubby的核心PRC层每秒能处理数百亿次的互联网请求。Stubby有许多很棒的特性,但无法标准化为业界通用的框架,因为它与谷歌内部的基础设施耦合得过于紧密。2015年谷歌发布了开源PRC框架gRPC,这个RPC基础设施具有标准化、可通用和跨平台得特点,旨在提供类似Stubby的可扩展性、性能和功能,但它主要面向社区。

gRPC优点

  • 高效的进程间通信
    gRPC没有使用JSON或XML这样的文本化格式,而是使用一个基于Protocol buffers的二进制协议与gRPC服务和客户端通信。
  • 有简单且定义良好的服务接口和模式
    gRPC为应用程序开发提供了一种契约优先(契约驱动)的方式。也就是说,首先必须定义服务接口,然后才能去处理实现细节。
  • 属于强类型
    gRPC服务契约清晰定义了应用程序间进行通信所使用的类型。这样一来,在构建跨多个团队和技术类型的云原生应用程序时,对于其所产生的大多数运行时错误和互操作错误,可以通过静态类型来克服,因此分布式应用程序的开发更加稳定。
  • 支持双共通信
    即客户端能发送消息给服务端,服务端也能发消息给客户端,且同一时刻信息可以进行双向传输。
  • 支持多语言
  • 大厂背书,社区活跃

gRPC缺点

  • 不太适合面向外部
    适合用在公司内部的通信,不太适合外部,因为gRPC的通信规定严格且它太新了(2015年开源),我们不可能要求客户或者已经实现好的第三方服务去配合我们改成gRPC通信。
  • 当出现巨大的服务定义变更时会产生复杂的开发流程
    在现代的服务间通信场景,模式修改很常见。如果出现巨大的gRPC服务定义变更,通常需要重新生成客户端代码和服务端代码。这需要整合到现有的持续集成过程中,可能会让整个开发生命周期复杂化。
  • gRPC生态相对较小

gRPC传输格式Protobuf

Google Protocol Buffers是Google提供一个具有高效的协议数据交换格式工具库(类似Json),Protobuf有更高的转化效率,时间效率和空间效率都是JSON的3-5倍。
image

注:Protocol buffers是在HTTP/2的基础上开发的。

安装Protobuf环境和IDEA插件

1、去Github下载,地址:https://github.com/protocolbuffers/protobuf/releases
image

1.1、配置环境变量,并打开cmd窗口验证是否安装成功,使用protoc命令
image

2、在IDEA中安装插件
image


以下是DEMO

开发News服务端

Maven增加gRPC依赖

<dependencies>
        <!--grpc底层通信组件-->
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>1.42.0</version>
        </dependency>
        <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>
            <!--provided:只能作用在编译和测试时,同时没有传递性-->
            <scope>provided</scope>
        </dependency>
    </dependencies>

引入protobuf一maven-plugin插件

<build>
        <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:3.17.3: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>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

编写proto服务定义文件
image

//使用proto3语法
syntax = "proto3";

//生成多个类
option java_multiple_files = false;

//生成java类所在的包
option java_package = "com.yuan.news.proto";

//生成外层类类名
option java_outer_classname = "NewsProto";

//.proto包名
package news;

/*
接下来,定义RPC服务RouteGuide
*/
service NewsService{
    //list是方法名,NewsRequest代表传入参数,NewsResponse代表返回响应
    rpc list(NewsRequest) returns (NewsResponse){}
}

message NewsRequest{
    string date = 1;
}

message NewsResponse{
    //repeated说明是一个集合(数组),数组每一个元素都是News对象
    repeated News news = 1;
}

//News新闻实体对象
message News{
    //对应java的int
    int32 id = 1;
    //新闻标题
    string title = 2;
    //新闻内容
    string content = 3;
    //对应java的long
    int64 createTime = 4;
}

实现服务端业务逻辑

1、打开maven插件
image

2、点击上述两个图标,生成文件,文件在target中image

3、打开image
文件

将其中的com包中的文件拖到自己的java文件夹下image

得到这两个文件
image

4、编写Service类

package com.yuan.news.service;

import com.yuan.news.proto.NewsProto;
import com.yuan.news.proto.NewsServiceGrpc;
import io.grpc.stub.StreamObserver;

import java.util.Date;

/**
 * @author wuhaoyuan
 * @date 2022/5/15 18:54
 * @Description 核心业务
 *
 * 此处的NewsServiceGrpc.NewsServiceImplBase是gRPC自己生成的
 */
public class NewsService extends NewsServiceGrpc.NewsServiceImplBase {

    /**
     * 此处注意,我们之前在news.proto文件中定义过,list方法的返回值应该是NewsResponse,但此处的返回值却是void。
     * 可以得知,在gRPC中,返回值是作为传入参数放在传入参数的最后一个位置上。
     * */
    @Override
    public void list(NewsProto.NewsRequest request, StreamObserver<NewsProto.NewsResponse> responseObserver) {
        String date = request.getDate();
        NewsProto.NewsResponse newList = null;
        try {
            //gRPC中有大量的构造器模式使用情景
            NewsProto.NewsResponse.Builder newListBuilder = NewsProto.NewsResponse.newBuilder();
            for (int i = 1; i <= 100; i++) {
                NewsProto.News news = NewsProto.News.newBuilder()
                        .setId(i)
                        .setTitle("新闻标题"+i)
                        .setContent(date + "当日新闻内容:"+i)
                        .setCreateTime(new Date().getTime())
                        .build();
                newListBuilder.addNews(news);
            }
            newList = newListBuilder.build();
        }catch (Exception e){
            responseObserver.onError(e);
        }finally {
            //相当于返回数据
            responseObserver.onNext(newList);
        }
        responseObserver.onCompleted();
    }
}

开发服务端启动器

public class GrpcServer {

    //自定义一个占用端口
    private static final int port = 9999;

    public static void main(String[] args){
        try {
            io.grpc.Server server = ServerBuilder.forPort(port).addService(new NewsService()).build().start();
            System.out.println("gRPC服务端启动,端口号为"+port);
            server.awaitTermination();
        }catch (Exception e){
            System.out.println("服务端异常");
        }
    }
}

开发News客户端

步骤与服务端类似:
1、加maven依赖

2、加maven插件

3、复制一个proto服务定义文件,从服务端那里复制,要一模一样

4、点击maven插件按钮(两个),生成文件(在target中),拖到自己的java文件夹下

5、编写客户端的启动类

package com.yuan.news;
    
    import com.yuan.news.service.NewsService;
    import io.grpc.ServerBuilder;
    
    /**
     * @author wuhaoyuan
     * @date 2022/5/15 19:34
     * @Description gRPC启动类
     */
    public class GrpcServer {
    
        //自定义一个占用端口
        private static final int port = 9999;
    
        public static void main(String[] args){
            try {
                io.grpc.Server server = ServerBuilder.forPort(port).addService(new NewsService()).build().start();
                System.out.println("gRPC服务端启动,端口号为"+port);
                server.awaitTermination();
            }catch (Exception e){
                System.out.println("服务端异常");
            }
        }
    }

7、开启服务端,开启客户端

结束

posted on 2022-05-16 08:30  一只彩色的熊猫  阅读(762)  评论(0编辑  收藏  举报