Grpc入门级教程,亲测

基础知识理解

概念

Grpc是常用RPC框架的一种,为了更好的理解RPC,我们先从熟悉的http说起。

  • Http和RPC的区别:
    http接口是在接口不多、系统与系统交互较少的情况下,解决信息孤岛初期常使用的一种通信手段;优点就是简单、直接、开发方便。利用现成的http协议 进行传输。
    但是如果是一个大型的网站,内部子系统较多、接口非常多的情况下,RPC框架的好处就显示出来了,首先(基于TCP协议的情况下)就是长链接,不必每次通信都要像http 一样去3次握手,减少了网络开销;其次就是RPC框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统 一化的操作。第三个来说就是安全性。最后就是最近流行的服务化架构、服务化治理,RPC框架是一个强力的支撑。
  • RPC
    RPC全称Remote Procedure Call,远程过程调用,类似于“动态代理”,只不过是代理方和被代理方位于了两个进程中。下面借用非常著名的一幅图来展示其调用过程。
    gRPC:是常用RPC框架的一种,是Google的开源产品,是跨语言的通用型RPC框架,使用Go语言编写。 Java语言的应用同样使用了Netty做网络通信,Go采用了Goroutine做网络通信。序列化方式采用了Google自己开源的Protobuf。请求的调用和返回使用HTTP2的Stream。
    在这里插入图片描述

交互流程

grpc使用Protobuf这种结构化数据存储格式进行通信和数据存储,具有语言无关、平台无关等特性。因此,我们可以将一个完整的grpc交互流程总结为4步:

  • 通过.proto文件定义传输的接口和消息体
  • 通过protocol编译器生成sever端和client端的stub程序
  • 将请求封装成HTTP2的Stream
  • 通过channel进行数据通信通道使用Socket进行数据传输

代码实现

语言:java
sever端:使用springboot框架整个Grpc,springboot版本2.2.7.RELEASE
client端: 不使用任何框架的java应用程序

代码流程

  • 编写.proto文件定义服务
  • 编译proto文件形成相应的java类
  • 编写实现类集成相应的java类,实现服务逻辑
  • 客户端实现调用, 测试

代码实现

  1. 引入所需依赖
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.7.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.github.lognet</groupId>
            <artifactId>grpc-spring-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.30.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.23.0</version>
        </dependency>

    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId><!--引入操作系统os设置的属性插件,否则${os.detected.classifier} 操作系统版本会找不到 -->
                <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>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.6.1:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.30.2: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>

</project>
  1. 编写.proto文件定义服务( protobuf 语法可参考博客:https://www.jitwxs.cn/60aca815.html
    在main目录下新建proto文件夹,(这里文件夹的位置不要更改,否则编译时会找不到报错)。
    NameService.proto
//表示每个message、枚举和service都会被生成一个类,否则这些都会是java_outer_classname的内部类
option java_multiple_files = true;
package com.example.grpcdemo.grpc;


service NameService{
  rpc getIdByName(Name) returns (Id){}
}

message Name{
  string name = 1;//1:代表第一个参数
}

message Id{
  string Id = 1;
}
  1. 编译proto文件
    在这里插入图片描述
    分别点击红框里的部分进行编译,不能只点一个。编译后形成
    在这里插入图片描述
  2. 编写实现类集成相应的java类,实现服务逻辑
@GRpcService//注解一定要加
public class NameServiceImpl extends NameServiceGrpc.NameServiceImplBase
{
    private Map<String, String> map = new HashMap<>();
    private Logger logger = LoggerFactory.getLogger(NameServiceImpl.class);

    public NameServiceImpl()
    {
        map.put("ss", "12");
        map.put("zz", "34");
    }

    @Override
    public void getIdByName(Name request, StreamObserver<Id> responseObserver)
    {
        logger.info("request is coming:  " + request.getName());
        Id id = Id.newBuilder().setId(getName(request.getName())).build();
        responseObserver.onNext(id);// 用于向客户端返回结果
        responseObserver.onCompleted();// 用于告诉客户端这次调用已经完成
    }

    public String getName(String name)
    {
        String id = map.get(name);
        if (id == null)
            return "00";
        return id;
    }

}

最后,配置文件中注明一下grpc调用的端口号:grpc.port=8087
至此,服务器端的代码完成,整体结构如下:
在这里插入图片描述
5. 客户端
同服务器端前3步一样,先导入依赖、将proto文件赋值过来,进行编译。
由于客户端我并未使用springboot框架,所以依赖的jar包有些许变化。

<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>grpc_client</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
    <dependency>
        <groupId>io.grpc</groupId>
        <artifactId>grpc-netty</artifactId>
        <version>1.23.0</version>
        <scope>compile</scope>
    </dependency>
    
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>1.23.0</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>1.30.2</version>
            <scope>compile</scope>
        </dependency>

    </dependencies>
    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId><!--引入操作系统os设置的属性插件,否则${os.detected.classifier} 操作系统版本会找不到 -->
                <version>1.5.0.Final</version>
            </extension>
        </extensions>
        <plugins>

            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <!-- <version>${protobuf-maven-plugin.version}</version>-->
                <version>0.5.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.6.1:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.30.2: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>


</project>

写一个测试类

    public static void main(String[] args) throws InterruptedException {
        //根据ip和端口号建立连接
        ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8087).usePlaintext().build();
        NameServiceGrpc.NameServiceBlockingStub nameServiceBlockingStub = NameServiceGrpc.newBlockingStub(channel);
        args=new String[]{"ss","zz","ss","zz"};

        for(String arg : args){
           Name name = Name.newBuilder().setName(arg).build();

            Id res = nameServiceBlockingStub.getIdByName(name);

            System.out.println("get result from server: " + res.getId() + " as param is " + arg);

        }
        channel.shutdown().awaitTermination(2, TimeUnit.SECONDS);
    }
}

以上亲测连接成功,有任何问题均可给我留言,在下刚刚接触这个,有不足之处希望见谅,欢迎指正。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值