重新定义cloud 第19章 gRPC上篇.md

杨超
2023-12-01
  • dobbo RPC框架
  • 通常情况下 Http 不会开启 keepAlive 。即:通常都是短连接
  • 内部服务之间用 http 性能低下
  • 现有 更高效的 调用方案 是 GRPC
  • gRPC 吞吐量 是 json 的 3倍,而且只用了 1/4 的 资源

gRPC 和 http/json区别

  • gRPC 采用 了 Proto Buffer 作为 序列化工具
  • gRPC 采用了 HTTP2协议,进行了 头部信息压缩,对连接进行了 复用(减少了连接的次数)
  • gRPC 采用了 Netty 作为 IO 处理框架

GRPC简介

  • 谷歌开源的,RPC框架

  • 定义方法名,方法参数 和 返回类型

  • client 去 proto Request

  • server proto Response 响应

  • 运行环境无关性,和 开发语言 无关性

  • C++ Java Python Go C# Node.js PHP Ruby

gRPC 的 一些 核心概念

  • 遵循 服务定义 的思想
  • 默认: protocol buffers 作为接口 定义语言 IDL 。服务接口,负载消息的结构
service HelloService{
    rpc SayHello (HelloRequest) returns (HelloResponse);
}
message HelloRequset{
    string greetiong=1;
}
message HelloResponse{
    string reply=1;
}

gRPC 可以定义的 4种方法

  • 一元 RPC

    rpc SayHello (HelloRequest) returns (HelloResponse){
    
    }
    //单个请求,单个响应。像本地的方法一样
    
  • 服务端 流式 RPC

    rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
    //响应是 流
    }
    //客户端发送一个请求,获取一个流,从流中读取消息,直到读取完毕
    
  • 客户端 流式RPC

    rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse){
    //请求式 流
    }
    //客户端 使用服务端提供的流写入,一旦写入完成,客户端等待服务端给出响应
    
  • 双向流式RPC

    rpc BidHello(stream HelloRequest) returns (stream HelloResponse){
    
    }
    
    //双方使用流发送,
    

gRPC 依赖于 Protocol Buffers

protocol 
英 /ˈprəʊtəkɒl/  美 /ˈproʊtəkɑːl/  全球(澳大利亚)  
n. 协议;草案;礼仪
vt. 拟定
vi. 拟定
  • protocol buffers 简称 protobuf

    • 高效 轻便 易用的

    • 结构化 数据存储格式

    • 用于 通信协议 和 数据存储 领域

    • 比 xml 解析速度快了 20—100倍

    • 比 json 快了 3—5倍

    • 序列化非常紧凑,是 xml 数据体积 的 10% —30%

使用 Protocol Buffers

  • 不仅包括:接口定义语言 IDL

  • 还要: 编译组件的支持

  • maven : 会自动 安装编译组件

    		<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
    		<dependency>
    			<groupId>com.google.protobuf</groupId>
    			<artifactId>protobuf-java</artifactId>
    			<version>3.5.1</version>
    		</dependency>
    
  • 然后安装 Protocol Buffers 的maven 插件。网址:https://www.xolstice.org/protobuf-maven-plugin/

    
    		<extensions>
    			<extension>
    				<groupId>kr.motd.maven</groupId>
    				<artifactId>os-maven-plugin</artifactId>
    				<version>1.5.0.Final</version>
    			</extension>
    		</extensions>
    
    		<plugins>
    			<plugin>
    			<groupId>org.xolstice.maven.plugins</groupId>
    			<artifactId>protobuf-maven-plugin</artifactId>
    			<version>0.5.1</version>
    			<extensions>true</extensions>
    
    				<executions>
    				<execution>
    					<goals>
    						<goal>compile</goal>
    						<goal>test-compile</goal>
    					</goals>
    					<configuration>
    						<protocArtifact>com.google.protobuf:protoc:3.4.0:exe:${os.detected.classifier}</protocArtifact>
    					</configuration>
    				</execution>
    			</executions>
    		</plugin>
    
    		</plugins>
    
    		<extensions>
    			<extension>
    				<groupId>kr.motd.maven</groupId>
    				<artifactId>os-maven-plugin</artifactId>
    				<version>1.4.1.Final</version>
    			</extension>
    		</extensions>
    		<plugins>
    			<plugin>
    				<groupId>org.xolstice.maven.plugins</groupId>
    				<artifactId>protobuf-maven-plugin</artifactId>
    				<version>0.5.0</version>
    				<configuration>
    					<protocArtifact>
    						com.google.protobuf:protoc:3.1.0: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>
    
    
  • 另一个配置

    		<extensions>
    			<extension>
    				<groupId>kr.motd.maven</groupId>
    				<artifactId>os-maven-plugin</artifactId>
    				<version>1.5.0.Final</version>
    			</extension> 1.4.1不报错
    		</extensions>
    		<plugins>
    			<plugin>
    				<groupId>org.xolstice.maven.plugins</groupId>
    				<artifactId>protobuf-maven-plugin</artifactId>
    				<version>0.5.1</version>0.5.0不报错
    				<configuration>
    					<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
    					<!--默认值-->
    					<!--<outputDirectory>${project.build.directory}/generated-sources/protobuf/java</outputDirectory>-->
    					<outputDirectory>${project.build.directory}/generated-sources/protobuf/java</outputDirectory>
    					<!--设置是否在生成java文件之前清空outputDirectory的文件,默认值为true,设置为false时也会覆盖同名文件-->
    					<clearOutputDirectory>true</clearOutputDirectory>
    					<protocArtifact>
    						com.google.protobuf:protoc:3.5.1:exe:${os.detected.classifier}
    					</protocArtifact> 3.1.0不报错
    					<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>
    

编写 person.proto文件

syntax = "proto3";
option java_package = "cn.springcloud.proto";
option java_outer_classname = "PersonModel";

message Person { //定义了一个消息
    int32 id = 1;
    string name = 2;
    string email = 3;
}
  • syntax指定 protocol buffers版本
  • 输入: mvn protobuf:compile

生成personModel 类

 /**
  * Created by forezp on 2018/6/9.
  */
 public class PersonUseCase {
 
     public static void main(String[] args) {
       PersonModel.Person forezp= PersonModel.Person.newBuilder()
                .setId(1)
                .setName("forezp")
                .setEmail("miles02@163.com").build();
 
 
         for(byte b : forezp.toByteArray()){
             System.out.print(b);
         }
 
         System.out.println("\n" + "bytes长度" + forezp.toByteString().size());
         System.out.println("===== forezp Byte 结束 =====");
 
         System.out.println("===== forezp 反序列化生成对象开始 =====");
         PersonModel.Person forezpCopy = null;
         try {
             forezpCopy = PersonModel.Person.parseFrom(forezp.toByteArray());
         } catch (InvalidProtocolBufferException e) {
             e.printStackTrace();
         }
         System.out.print(forezpCopy.toString());
         System.out.println("===== forezp 反序列化生成对象结束 =====");
     }
 }
  • 运行 main方法 ,即可做 序列化 ,反序列化

  • 输出

    8118610211111410112211226151091051081011154850644954514699111109
    bytes长度27
    ===== forezp Byte 结束 =====
    ===== forezp 反序列化生成对象开始 =====
    id: 1
    name: "forezp"
    email: "miles02@163.com"
    ===== forezp 反序列化生成对象结束 =====
    
  • 采用 Proto Buffer 序列化 可读性很差,但是数据 体积很小

gRpc 基于 Netty 进行 IO 处理

  • Netty:JBoss开源 的 一个 异步事件驱动的 Java 网络应用框架,协议服务器。

    • 简化 TCP 和 UDP 的 Socket 服务器等网络编程过程
  • JBoss 是 J2EE的开放源代码的应用服务器

  • gRpc 的 Java版本 用 Netty 作为 NIO 框架

    • Netty Channel 作为数据传输通道
    • Proto Buffer 作为 序列化,反序列化工具
    • 请求被封装成 Http2
    • 客户端是长连接
  • Netty 提高数据传输性能,因为:

    • 早期 网络编程框架中,使用 阻塞 IO 来处理网络请求

      while( (request = in.readLine())!= null ){
      		//XXXX
      }
      
    • 只能同时处理一个连接,要想处理多个客户端,要创建多个 ServeSocket

    • 还要 重新创建一个线程 去处理 该请求,该线程会处于 阻塞状态

    • 大多数 情况下,这些线程 处于阻塞状态。

  • NIO

    • 使用选择器消除 IO的一些弊端。Selector
    • 使用了 事件通知 API,确定哪些 线程是可以 进行读写的。
    • 单线程 处理多个并发的连接
    • 多个socket 读写 ——一个selector——一个线程

实战

父类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>cn.springcloud.book</groupId>
    <artifactId>ch19-2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>pom</packaging>

    <name>ch19-2</name>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>

        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <!-- grpc版本 -->
        <grpc.version>1.11.0</grpc.version>
        <os.plugin.version>1.5.0.Final</os.plugin.version>
        <!-- protobuf版本 -->
        <protobuf.plugin.version>0.5.1</protobuf.plugin.version>
        <protoc.version>3.5.1</protoc.version>
    </properties>
    
    

    <modules>
        <module>grpc-lib-1</module>
        <module>grpc-simple-server</module>
        <module>grpc-simple-client</module>
    </modules>
    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty</artifactId>
            <version>${grpc.version}</version>
        </dependency><!-- netty -->

        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>${grpc.version}</version>
        </dependency><!-- protobuf -->

        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>${grpc.version}</version>
        </dependency><!-- stub -->
    </dependencies>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>kr.motd.maven</groupId>
                    <artifactId>os-maven-plugin</artifactId>
                    <version>${os.plugin.version}</version>
                </plugin><!-- os Maven 插件 -->

                <plugin>
                    <groupId>org.xolstice.maven.plugins</groupId>
                    <artifactId>protobuf-maven-plugin</artifactId>
                    <version>${protobuf.plugin.version}</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>
                <plugin>
                    <artifactId>maven-jar-plugin</artifactId>
                    <configuration>
                        <archive>
                            <manifest>
                                <addClasspath>true</addClasspath>
                                <classpathPrefix>lib/</classpathPrefix>
                                <mainClass>${main.class}</mainClass>
                            </manifest>
                        </archive>
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <executions>
                        <execution>
                            <phase>initialize</phase>
                            <goals>
                                <goal>copy-dependencies</goal>
                            </goals>
                            <configuration>
                                <overWriteReleases>false</overWriteReleases>
                                <includeScope>runtime</includeScope>
                                <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <!--<plugin>-->
                    <!--<groupId>com.spotify</groupId>-->
                    <!--<artifactId>dockerfile-maven-plugin</artifactId>-->
                    <!--<version>1.2.2</version>-->
                    <!--<configuration>-->
                        <!--<repository>${docker.image.prefix}/${project.artifactId}</repository>-->
                        <!--<tag>latest</tag>-->
                    <!--</configuration>-->
                    <!--<executions>-->
                        <!--<execution>-->
                            <!--<phase>package</phase>-->
                            <!--<goals>-->
                                <!--<goal>build</goal>-->
                            <!--</goals>-->
                        <!--</execution>-->
                    <!--</executions>-->
                <!--</plugin>-->
            </plugins>
        </pluginManagement>
    </build>


</project>
        <!-- grpc版本 -->
        <grpc.version>1.11.0</grpc.version>
        <os.plugin.version>1.4.1.Final</os.plugin.version>
        <!-- protobuf版本 -->
        <protobuf.plugin.version>0.5.0</protobuf.plugin.version>
        <protoc.version>3.1.0</protoc.version> #不报错的版本

grpc-lib

  • 需要加入 os-maven-plugin
  • protobuf-maven-plugin

xml

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

HiService.proto

syntax = "proto3";

package cn.springcloud.grpc;

option java_multiple_files = true;

message HelloRequest {
    string name = 1;
    int32 age = 2;
    repeated string hobbies = 3;
    map<string, string> tags = 4;

}

message HelloResponse {
    string greeting = 1;
}


service HelloService {
    rpc hello(HelloRequest) returns (HelloResponse);
}

  • 文件 创建了 helloRequest 和 HelloResponse 两个试题

  • 创建了 helloService 服务,有 hello 服务

  • 执行 mvn clean install 命令

grpc-server

xml

  • client的xml 一样的
    <parent>
        <groupId>cn.springcloud.book</groupId>
        <artifactId>ch19-2</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>cn.springcloud.book</groupId>
            <artifactId>grpc-lib-1</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

helloService


/**
 * Email miles02@163.com
 *
 * @author fangzhipeng
 * create 2018-06-11
 **/
public class HelloService extends HelloServiceGrpc.HelloServiceImplBase {

    @Override
    public void hello(cn.springcloud.grpc.HelloRequest request,
                      io.grpc.stub.StreamObserver<cn.springcloud.grpc.HelloResponse> responseObserver) {
        //打印request
        System.out.println( request );

        String greeting = "Hi " + request.getName() + " you are " + request.getAge() + " years old" +
                " your hoby is " + (request.getHobbiesList()) + " your tags " + request.getTagsMap();

        //生成 response
        HelloResponse response = HelloResponse.newBuilder().setGreeting( greeting ).build();
        //执行
        responseObserver.onNext( response );
        responseObserver.onCompleted();
    }
}


public class HelloService extends HelloServiceGrpc.HelloServiceImplBase

MyGrpcServer



public class MyGrpcServer {
    static public void main(String[] args) throws IOException, InterruptedException {
        //server 监听的端口为 8082 。并将 HelloServe 注册到里面去
        Server server = ServerBuilder.forPort( 8082 )
                .addService( new HelloService() )
                .build();

        System.out.println( "Starting server..." );
        server.start();
        System.out.println( "Server started!" );
        server.awaitTermination();
    }


}

grpc-client

  • 创建 gRPC Server 的 Channel
  • usePlaintext() 表示 纯文本连接(TLS安全机制)
  • 然后根据通道 创建 一个 阻塞的 Stub
  • 使用该 Stub 像 服务端 发送 HelloRequest消息,
  • 并阻塞线程,直到接到 gRPC Server 发回的响应

public class MyGrpcClient {

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

    //本地 8082端口,
    ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8082)
        .usePlaintext()
        .build();
    //HelloService的通道
    HelloServiceGrpc.HelloServiceBlockingStub stub =
            HelloServiceGrpc.newBlockingStub(channel);
    //请求为这些
    HelloResponse helloResponse = stub.hello(
        HelloRequest.newBuilder()
            .setName("forezp")
            .setAge(17)
            .addHobbies("football").putTags( "how?","wonderful" )
            .build());
    //相依为:服务端的输出
    System.out.println(helloResponse);


    channel.shutdown();
  }
}

  • 响应为:
greeting: "Hi forezp you are 17 years old your hoby is [football] your tags {how?=wonderful}"
 类似资料: