java grpc 簡單易懂 ---1


簡介:

  grpc是谷歌的一個開源的rpc(遠程服務調用)框架,可以讓各個語言按照指定的規則通過http2協議相互調用,這個規則是用Protocol Buffer(谷歌的一個數據描述語言)寫的一個.proto文件,grpc的目的就是為了讓服務調用更方便。

目前支持的語言有C, C++,C#,Java, Node.js, Python,Go等,大部分語言都是通過插件根據.proto文件生成對應的代碼,用生成好的代碼,創建或調用grpc服務。

 

這是grpc的官方文檔

 

   grpc的接口調用分為四類

    1.普通調用

    2.請求流調用

    3.響應流調用

    4.雙向流調用

 

從.proto文件開始

常用的關鍵字
syntax 指定語言版本
option 修改配置選項
service 聲明一個服務
rpc 聲明一個方法
resturns 方法的返回值
message 定義一個消息類型
repeated 數組
stream 用流來交互

 


  

 

 

 

 

   

 

 

 

 

 這是proto的語法教程

 

一個例子:

 

syntax = "proto3";

option java_package = "java_test";
option java_multiple_files = true;

service TestService
{
    rpc method(Request) returns (Result){}
}

message Request
{
    string request1 = 1;
    string request2 = 2;
}

message Result
{
    string result1 = 1;
    string result2 = 2;
}

  

指定一個版本:

syntax = "proto3";

  

針對java的代碼生成的一些配置

option java_package = "java_test";
option java_multiple_files = true;

  

message 定義了一個請求消息,和一個返回消息

message Request
{
    string request1 = 1;
    string request2 = 2;
}

message Result
{
    string result1 = 1;
    string result2 = 2;
}

  

用 service 聲明了一個服務,用 rpc 聲明一個方法

service TestService
{
    rpc method(Request) returns (Result){}
}

  

說正經的:

想使用grpc要先做一些配置

 

添加grpc的包

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

  

添加編譯.proto文件用的插件

<plugin>
    <groupId>org.xolstice.maven.plugins</groupId>
    <artifactId>protobuf-maven-plugin</artifactId>
    <version>0.5.0</version>
    <configuration>
        <protocArtifact>com.google.protobuf:protoc:3.0.0-beta-4:exe:${os.detected.classifier}</protocArtifact>
        <pluginArtifact>io.grpc:protoc-gen-grpc-java:0.15.0:exe:${os.detected.classifier}</pluginArtifact>
        <pluginId>grpc</pluginId>
        <protoSourceRoot>src/main/resources/proto</protoSourceRoot>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>compile-custom</goal>
            </goals>
        </execution>
    </executions>
</plugin>

  

添加.proto文件的編譯工具

<configuration>
        <protocArtifact>com.google.protobuf:protoc:3.0.0-beta-4:exe:${os.detected.classifier}</protocArtifact>
        <pluginArtifact>io.grpc:protoc-gen-grpc-java:0.15.0:exe:${os.detected.classifier}</pluginArtifact>
        <pluginId>grpc</pluginId>
        <protoSourceRoot>src/main/resources/proto</protoSourceRoot>
    </configuration>

  

protoc工具通過.proto文件生成對應的java對應的類

<protocArtifact>com.google.protobuf:protoc:3.0.0-beta-4:exe:${os.detected.classifier}</protocArtifact>

  

protoc-gen-grpc-java工具通過.proto文件生成grpc的工具類

<pluginArtifact>io.grpc:protoc-gen-grpc-java:0.15.0:exe:${os.detected.classifier}</pluginArtifact>

  

這是生成grpc工具類存放的文件夾的名字

<pluginId>grpc</pluginId>

  

要編輯的.proto文件的路徑

<protoSourceRoot>src/main/resources/proto</protoSourceRoot>

  

這個是為下載上面工具用的,他可以提供一些變量,

os.detected.classifier變量可以根據當前系統的類型來下載對應的工具
<extension>
    <groupId>kr.motd.maven</groupId>
    <artifactId>os-maven-plugin</artifactId>
    <version>1.4.1.Final</version>
</extension>

  

這是上面兩個編譯工具用到的命令,當用maven編譯項目時會執行這兩個命令

<goal>compile</goal>
<goal>compile-custom</goal>

  

真的,說正經的:

用maven編譯一下

 

會生成兩個文件夾

  

java文件夾是protoc編譯工具生成的代碼

grpc文件夾是protoc-gen-grpc-java編譯工具生成的工具類

 

這兩個文件就是我們在.proto文件中定義的消息類型(經常被用到)

 

這兩個是為消息類型的一個接口,里面有get方法(不會被用到)

 

 這個是對消息的一個描述(更不會被用到)

 

這個是grpc的工具類(會被用到)

 

這次真的要說正經的了,我們要用這些grpc為我們生成出來的奇怪的東西,寫奇怪的東西了:

 

1.普通接口

 

1.1.服務端

package com.gutousu.grpc_service_java_test.service;

import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import java_test.Request;
import java_test.Result;
import java_test.TestServiceGrpc;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;


@Component
public class JavaGrpcServer extends TestServiceGrpc.TestServiceImplBase implements InitializingBean
{
    @Override
    public void method(Request request, StreamObserver<Result> responseObserver) {
        Result result = Result.newBuilder().setResult1("result1").setResult2("result2").build();
        responseObserver.onNext(result);
        responseObserver.onCompleted();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        ServerBuilder.forPort(2)
                .addService(new JavaGrpcServer())
                .build()
                .start();
    }
}

   

首先建立一個服務類叫JavaGrpcServer 繼承 TestServiceGrpc.TestServiceImplBase 重寫里面的method方法

public class JavaGrpcServer extends TestServiceGrpc.TestServiceImplBase

  

TestServiceGrpc.TestServiceImplBase 就是我們在.proto文件中定義的服務

 

用 ServerBuilder 的 forProt 方法來指定一個端口,用 addService 來添加一個服務類,也就是當前類

ServerBuilder.forPort(2)
                .addService(new JavaGrpcServer())
                .build()
                .start();

  

grpc生成的消息類有點獨特,他們沒有set方法,只有get方法,想要賦值,要用他們的一個內部類Builder來間接賦值

Result result = Result.newBuilder().setResult1("result1").setResult2("result2").build();

  

添加返回值,完成調用

responseObserver.onNext(result);
responseObserver.onCompleted();

StreamObserver(流觀察者) 這個接口會在后面詳細說,這里只需要知道 onNext 是添加返回值,onCompleted 是完成調用即可

 

這里利用了spring的 InitializingBean 接口和 Component 注解在bean初始化的時候建立服務

好了,服務端搞完了,下一個

 

1.2.客戶端

先寫一個叫 Functional 的函數式接口,方便調用

package com.gutousu.grpc_client_java_test;

public interface Functional<Arg,Result>
{
    Result run(Arg arg);
}

  

建一個叫 JavaGrpcClient 的類 來調用接口

package com.gutousu.grpc_client_java_test.client;


import com.gutousu.grpc_client_java_test.Functional;
import io.grpc.Channel;
import io.grpc.ManagedChannelBuilder;
import java_test.TestServiceGrpc;
import org.springframework.stereotype.Component;

@Component
public class JavaGrpcClient
{
    private Channel channel = channel();

    public <Result> Result run(Functional<TestServiceGrpc.TestServiceBlockingStub,Result> functional)
    {
        TestServiceGrpc.TestServiceBlockingStub testServiceBlockingStub =
                TestServiceGrpc.newBlockingStub(channel);

        return functional.run(testServiceBlockingStub);
    }

    private Channel channel()
    {
        return ManagedChannelBuilder
                .forAddress("192.168.0.31",2)
                .usePlaintext(true)
                .build();
    }
}

  

用 ManagedChannelBuilder 的 forAddress 方法來連接服務端,usePlaintext的意思是使用明文不加密(應該可以加密)

private Channel channel()
{
    return ManagedChannelBuilder
            .forAddress("192.168.0.31",2)
            .usePlaintext(true)
            .build();
}

  

TestServiceGrpc.newBlockingStub 來創建一個實例

TestServiceGrpc.TestServiceBlockingStub testServiceBlockingStub =
                TestServiceGrpc.newBlockingStub(channel);

  

 再搞一個測試

package com.gutousu.grpc_client_java_test;

import com.gutousu.grpc_client_java_test.client.JavaGrpcClient;
import java_test.Request;
import java_test.Result;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class GrpcClientJavaTestApplicationTests {

    @Autowired
    private JavaGrpcClient javaGrpcClient;

    @Test
    public void contextLoads() {
        Request request = Request.newBuilder().setRequest1("test1").setRequest2("test2").build();
        Result result = javaGrpcClient.run(o -> o.method(request));
    }
}

  

讓我們把這兩個項目跑起來,看一下

 

看!斷點經過了創建服務那里,而且沒有報錯,服務端跑起來了!

 

 看!客戶端要!

 

他進來了,連接了服務端,創建了實例,馬上就要....

 

他帶着參數過來了,被斷點攔住了

 

 給他一個返回值,結束

 

 走你!

 

拿到了返回值,完結!撒花!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

等等!

這只是普通的接口

還有三種接口呢!

好,那繼續

 

2.請求流接口

等等!

博客寫的有點長了,下一篇吧^_^

不要走開,廣告之后更精彩!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM