protobuf太好用,java之父叫我改用grpc

[https://juejin.cn/post/7222096611635576891](https://juejin.cn/post/7222096611635576891)在上篇文章中,我们介绍了如何使用 Redis 存储 Protobuf 格式的数据。本文将介绍在 RPC 中也用上 Protobuf。

在一般项目架构中,前后端交互使用Json格式,后端服务间交互使用Protobuf格式。这样的原因是:

1. 前端大多数框架对于Json格式的数据是可以直接渲染的

2. 而后端数据交互一般是为了序列化和反序列化,考虑更多是并发,带宽等,又由于Google的gRPC框架集成了Protobuf,并且gRPC有跨语言、低带宽、HTTP/2等优点。目前主流的Go语言也是谷歌旗下的,Go+gRPC几乎是不二之选(你要是用thrift,当我在放屁)<br />3.Spring Cloud的OpenFeign也是支持HTTP/2+Protobuf的,但是还是不能跨语言,这里就不展开说了。

<br />Java版:

1. 新建三个模块,login调sms,模拟一个登录发验证码,commons放公共的proto文件

```xml

<modules>

  <module>grpc-commons</module>

  <module>grpc-login</module>

  <module>grpc-sms</module>

</modules>

```

</br>

2. 编写proto,一个SmsService接口、一个SmsRequest消息、一个SmsResponse消息。

```protobuf

syntax = "proto3";

import "google/protobuf/timestamp.proto";

option java_package = "com.haowen.common.protobuf";

option java_outer_classname = "SmsProto";

option go_package = "../protobuf";

service SmsService {

rpc SendSms (SmsRequest) returns (SmsResponse) {}

}

message SmsRequest {

  string phone = 1;

  string msg = 2;

}

message SmsResponse {

  string requestId = 1;

  bool isSuccess = 2;

  google.protobuf.Timestamp sentAt = 3;

}

```

</br>

3. 因为要生成gRPC的Service类,所以需要借助protoc-gen-grpc-java插件,在cmomons模块的pom.xml添加插件

```xml

<dependencies>

  <!-- 用来兼容java17 -->

  <dependency>

    <groupId>jakarta.annotation</groupId>

    <artifactId>jakarta.annotation-api</artifactId>

    <version>1.3.5</version>

  </dependency>

</dependencies>

<build>

  <extensions>

    <extension>

      <groupId>kr.motd.maven</groupId>

      <artifactId>os-maven-plugin</artifactId>

      <version>1.7.1</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.21.7:exe:${os.detected.classifier}</protocArtifact>

        <pluginId>grpc-java</pluginId>

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

      </configuration>

      <executions>

        <execution>

          <goals>

            <goal>compile</goal>

            <goal>compile-custom</goal>

          </goals>

        </execution>

      </executions>

    </plugin>

  </plugins>

</build>

```

</br>

4. 点击编译,编辑会自动执行protoc-gen-grpc-java插件

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a3f9ef5ac08642eb84830dd87795048f~tplv-k3u1fbpfcp-zoom-1.image)<br />target目录下就有我们生成的实体类和grpc的service类<br />![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b73c3a39a62a40888dcc90c90e3d8a65~tplv-k3u1fbpfcp-zoom-1.image)<br /></br>

5. 接下来编写sms模块(server端),因为我添加了springboot的web,所以这里用@Service的形式来注入

```java

@Service

public class SmsServiceImpl extends SmsServiceImplBase {

    @Override

    public void sendSms(SmsRequest request, StreamObserver<SmsResponse> responseObserver) {

        // 请求的参数

        System.out.println(request.getPhone());

        System.out.println(request.getMsg());

        // 返回的东西

        SmsResponse response = SmsResponse.newBuilder()

            .setRequestId(UUID.fastUUID().toString())

            .setIsSuccess(true)

            .setSentAt(Timestamps.fromMillis(System.currentTimeMillis()))

            .build();

        // 塞进去

        responseObserver.onNext(response);

        // 塞完,走吧

        responseObserver.onCompleted();

    }

}

```

启动类,gRPC的通信端口是90

```java

public class GrpcSmsApp {

    private Server server;

    public static void main(String[] args) {

        SpringApplication.run(GrpcSmsApp.class, args);

    }

    /**

    * 启动grpc

    */

    @SneakyThrows

    @PostConstruct

    public void startGrpcServer() {

        server = ServerBuilder.forPort(90).addService(new SmsServiceImpl()).build().start();

    }

    @PreDestroy

    public void stopGrpcServer() {

        if (server != null) {

            server.shutdown();

        }

    }

}

```

</br>

6. 接着写login模块(client端),创建连接并使用Bean进行管理。.newBlockingStub是最常用的阻塞请求。如需异步、双工请建立对应的stub

```java

@Configuration

public class SmsService {

    @Bean

    SmsServiceGrpc.SmsServiceBlockingStub blockingStub() {

        ManagedChannel channel = ManagedChannelBuilder

                .forAddress("localhost", 90)

                .usePlaintext() // 明文传输,生产用NettyChannelBuilder下的sslContext()

                .build();

        return SmsServiceGrpc.newBlockingStub(channel);

    }

}

```

</br>

7. 写一个接口来测试

```java

@RestController

@RequiredArgsConstructor

@RequestMapping("login")

public class LoginApi {

private final SmsServiceBlockingStub blockingStub;

    @PostMapping("sendLoginCode")

    String sendLoginCode(String phone) {

        SmsRequest request = SmsRequest.newBuilder()

                .setPhone(phone)

                .setMsg("你的验证码是:sb")

                .build();

        SmsResponse smsResponse = blockingStub.sendSms(request);

        if (!smsResponse.getIsSuccess()) {

            return "发送失败";

        }

        System.out.println("smsResponse = " + smsResponse);

        return smsResponse.getRequestId();

    }

}

```

</br>

8. 用postman进行调用,正常发送和返回

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/41de6875f66e4ec9ac4412ccac2500f1~tplv-k3u1fbpfcp-zoom-1.image)<br />login模块(client端)<br />![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3d96c28aa5ed44a59cccaf1b6390443e~tplv-k3u1fbpfcp-zoom-1.image)<br />sms模块(server端)<br />![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6b12a20eb9c548f8ba73cd60ff6d2c16~tplv-k3u1fbpfcp-zoom-1.image)<br /></br><br />go版

9. 保留Java的sms模块,我们用Golang调用试一试,把sms.proto移动到go项目目录下,安装protoc-gen-go-grpc插件来生成Go版本的Service层。

```protobuf

syntax = "proto3";

import "google/protobuf/timestamp.proto";

option java_package = "com.haowen.common.protobuf";

option java_outer_classname = "SmsProto";

option go_package = "../protobuf";

service SmsService {

  rpc SendSms (SmsRequest) returns (SmsResponse) {}

}

message SmsRequest {

  string phone = 1;

  string msg = 2;

}

message SmsResponse {

  string requestId = 1;

  bool isSuccess = 2;

  google.protobuf.Timestamp sentAt = 3;

}

// go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

// go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

// protoc --go_out=. --go-grpc_out=.  sms.proto

```

分别执行,安装插件并生成proto的Go文件。

```protobuf

// go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

// go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

// protoc --go_out=. --go-grpc_out=.  sms.proto

```

执行后会生成<br />![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/2e568cd8bb4f47afb3fa5f3a2ac117f6~tplv-k3u1fbpfcp-zoom-1.image)<br /></br>

10. 接下来编写一个调用方法,同样调用端口是90

```go

package main

import (

    "context"

    "fmt"

    "google.golang.org/grpc"

    "google.golang.org/grpc/credentials/insecure"

    "grpc/protobuf"

    "log"

)

/*

go get -u google.golang.org/grpc

go get -u google.golang.org/grpc/credentials

*/

const (

    address = ":90"

)

func main() {

    // 设置一个连接

    conn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()))

    if err != nil {

        log.Fatalf("连接失败: %v", err)

    }

    defer func(conn *grpc.ClientConn) {

        err := conn.Close()

        if err != nil {

            log.Fatalf("关闭连接失败: %v", err)

        }

    }(conn)

    // 创建一个SmsService的客户端

    client := protobuf.NewSmsServiceClient(conn)

    response, err := client.SendSms(context.Background(), &protobuf.SmsRequest{

        Phone: "110",

        Msg:  "哈哈哈",

    })

    fmt.Println(response, err)

}

```

11. 运行main函数,这样就实现了一个简单的跨语言调用

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/42ef11c722164bffb1effd7b0682b4c5~tplv-k3u1fbpfcp-zoom-1.image)

为了显得文章不会特别臃肿,本文省略了模块层级的创建,相信聪明如你已经一看就会啦,如果有更好的建议,欢迎在评论区留言。

[https://juejin.cn/post/7222096611635576891](https://juejin.cn/post/7222096611635576891)在上篇文章中,我们介绍了如何使用 Redis 存储 Protobuf 格式的数据。本文将介绍在 RPC 中也用上 Protobuf。

在一般项目架构中,前后端交互使用Json格式,后端服务间交互使用Protobuf格式。这样的原因是:

1. 前端大多数框架对于Json格式的数据是可以直接渲染的

2. 而后端数据交互一般是为了序列化和反序列化,考虑更多是并发,带宽等,又由于Google的gRPC框架集成了Protobuf,并且gRPC有跨语言、低带宽、HTTP/2等优点。目前主流的Go语言也是谷歌旗下的,Go+gRPC几乎是不二之选(你要是用thrift,当我在放屁)<br />3.Spring Cloud的OpenFeign也是支持HTTP/2+Protobuf的,但是还是不能跨语言,这里就不展开说了。

<br />Java版:

1. 新建三个模块,login调sms,模拟一个登录发验证码,commons放公共的proto文件

```xml

<modules>

  <module>grpc-commons</module>

  <module>grpc-login</module>

  <module>grpc-sms</module>

</modules>

```

</br>

2. 编写proto,一个SmsService接口、一个SmsRequest消息、一个SmsResponse消息。

```protobuf

syntax = "proto3";

import "google/protobuf/timestamp.proto";

option java_package = "com.haowen.common.protobuf";

option java_outer_classname = "SmsProto";

option go_package = "../protobuf";

service SmsService {

rpc SendSms (SmsRequest) returns (SmsResponse) {}

}

message SmsRequest {

  string phone = 1;

  string msg = 2;

}

message SmsResponse {

  string requestId = 1;

  bool isSuccess = 2;

  google.protobuf.Timestamp sentAt = 3;

}

```

</br>

3. 因为要生成gRPC的Service类,所以需要借助protoc-gen-grpc-java插件,在cmomons模块的pom.xml添加插件

```xml

<dependencies>

  <!-- 用来兼容java17 -->

  <dependency>

    <groupId>jakarta.annotation</groupId>

    <artifactId>jakarta.annotation-api</artifactId>

    <version>1.3.5</version>

  </dependency>

</dependencies>

<build>

  <extensions>

    <extension>

      <groupId>kr.motd.maven</groupId>

      <artifactId>os-maven-plugin</artifactId>

      <version>1.7.1</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.21.7:exe:${os.detected.classifier}</protocArtifact>

        <pluginId>grpc-java</pluginId>

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

      </configuration>

      <executions>

        <execution>

          <goals>

            <goal>compile</goal>

            <goal>compile-custom</goal>

          </goals>

        </execution>

      </executions>

    </plugin>

  </plugins>

</build>

```

</br>

4. 点击编译,编辑会自动执行protoc-gen-grpc-java插件

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f8852cf6e3b6404d8c18f8fdb59ddbf8~tplv-k3u1fbpfcp-zoom-1.image)<br />target目录下就有我们生成的实体类和grpc的service类<br />![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/48bc82850dc34cdd907383902e3dd8c7~tplv-k3u1fbpfcp-zoom-1.image)<br /></br>

5. 接下来编写sms模块(server端),因为我添加了springboot的web,所以这里用@Service的形式来注入

```java

@Service

public class SmsServiceImpl extends SmsServiceImplBase {

    @Override

    public void sendSms(SmsRequest request, StreamObserver<SmsResponse> responseObserver) {

        // 请求的参数

        System.out.println(request.getPhone());

        System.out.println(request.getMsg());

        // 返回的东西

        SmsResponse response = SmsResponse.newBuilder()

            .setRequestId(UUID.fastUUID().toString())

            .setIsSuccess(true)

            .setSentAt(Timestamps.fromMillis(System.currentTimeMillis()))

            .build();

        // 塞进去

        responseObserver.onNext(response);

        // 塞完,走吧

        responseObserver.onCompleted();

    }

}

```

启动类,gRPC的通信端口是90

```java

public class GrpcSmsApp {

    private Server server;

    public static void main(String[] args) {

        SpringApplication.run(GrpcSmsApp.class, args);

    }

    /**

    * 启动grpc

    */

    @SneakyThrows

    @PostConstruct

    public void startGrpcServer() {

        server = ServerBuilder.forPort(90).addService(new SmsServiceImpl()).build().start();

    }

    @PreDestroy

    public void stopGrpcServer() {

        if (server != null) {

            server.shutdown();

        }

    }

}

```

</br>

6. 接着写login模块(client端),创建连接并使用Bean进行管理。.newBlockingStub是最常用的阻塞请求。如需异步、双工请建立对应的stub

```java

@Configuration

public class SmsService {

    @Bean

    SmsServiceGrpc.SmsServiceBlockingStub blockingStub() {

        ManagedChannel channel = ManagedChannelBuilder

                .forAddress("localhost", 90)

                .usePlaintext() // 明文传输,生产用NettyChannelBuilder下的sslContext()

                .build();

        return SmsServiceGrpc.newBlockingStub(channel);

    }

}

```

</br>

7. 写一个接口来测试

```java

@RestController

@RequiredArgsConstructor

@RequestMapping("login")

public class LoginApi {

private final SmsServiceBlockingStub blockingStub;

    @PostMapping("sendLoginCode")

    String sendLoginCode(String phone) {

        SmsRequest request = SmsRequest.newBuilder()

                .setPhone(phone)

                .setMsg("你的验证码是:sb")

                .build();

        SmsResponse smsResponse = blockingStub.sendSms(request);

        if (!smsResponse.getIsSuccess()) {

            return "发送失败";

        }

        System.out.println("smsResponse = " + smsResponse);

        return smsResponse.getRequestId();

    }

}

```

</br>

8. 用postman进行调用,正常发送和返回

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3926e4fcb3c849778f91d5d08c53e988~tplv-k3u1fbpfcp-zoom-1.image)<br />login模块(client端)<br />![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/3c5caf6a59b44df3889c83c85ee4a780~tplv-k3u1fbpfcp-zoom-1.image)<br />sms模块(server端)<br />![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/6e136c2a94954c5a9468c1b37142978c~tplv-k3u1fbpfcp-zoom-1.image)<br /></br><br />go版

9. 保留Java的sms模块,我们用Golang调用试一试,把sms.proto移动到go项目目录下,安装protoc-gen-go-grpc插件来生成Go版本的Service层。

```protobuf

syntax = "proto3";

import "google/protobuf/timestamp.proto";

option java_package = "com.haowen.common.protobuf";

option java_outer_classname = "SmsProto";

option go_package = "../protobuf";

service SmsService {

  rpc SendSms (SmsRequest) returns (SmsResponse) {}

}

message SmsRequest {

  string phone = 1;

  string msg = 2;

}

message SmsResponse {

  string requestId = 1;

  bool isSuccess = 2;

  google.protobuf.Timestamp sentAt = 3;

}

// go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

// go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

// protoc --go_out=. --go-grpc_out=.  sms.proto

```

分别执行,安装插件并生成proto的Go文件。

```protobuf

// go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

// go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

// protoc --go_out=. --go-grpc_out=.  sms.proto

```

执行后会生成<br />![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/68d57cb492414f8ba9ea0b47f56d76d4~tplv-k3u1fbpfcp-zoom-1.image)<br /></br>

10. 接下来编写一个调用方法,同样调用端口是90

```go

package main

import (

    "context"

    "fmt"

    "google.golang.org/grpc"

    "google.golang.org/grpc/credentials/insecure"

    "grpc/protobuf"

    "log"

)

/*

go get -u google.golang.org/grpc

go get -u google.golang.org/grpc/credentials

*/

const (

    address = ":90"

)

func main() {

    // 设置一个连接

    conn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()))

    if err != nil {

        log.Fatalf("连接失败: %v", err)

    }

    defer func(conn *grpc.ClientConn) {

        err := conn.Close()

        if err != nil {

            log.Fatalf("关闭连接失败: %v", err)

        }

    }(conn)

    // 创建一个SmsService的客户端

    client := protobuf.NewSmsServiceClient(conn)

    response, err := client.SendSms(context.Background(), &protobuf.SmsRequest{

        Phone: "110",

        Msg:  "哈哈哈",

    })

    fmt.Println(response, err)

}

```

11. 运行main函数,这样就实现了一个简单的跨语言调用

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d787c609d5ee48a19d0603dc01a1c96d~tplv-k3u1fbpfcp-zoom-1.image)

为了显得文章不会特别臃肿,本文省略了模块层级的创建,相信聪明如你已经一看就会啦,如果有好的建议,欢迎在评论区留言。

Java版:

1.  新建三个模块,login调sms,模拟一个登录发验证码,commons放公共的proto文件

```

<modules>

  <module>grpc-commons</module>

  <module>grpc-login</module>

  <module>grpc-sms</module>

</modules>

```

</br>

2.  编写proto,一个SmsService接口、一个SmsRequest消息、一个SmsResponse消息。

```

syntax = "proto3";

import "google/protobuf/timestamp.proto";

option java_package = "com.haowen.common.protobuf";

option java_outer_classname = "SmsProto";

option go_package = "../protobuf";

service SmsService {

rpc SendSms (SmsRequest) returns (SmsResponse) {}

}

message SmsRequest {

  string phone = 1;

  string msg = 2;

}

message SmsResponse {

  string requestId = 1;

  bool isSuccess = 2;

  google.protobuf.Timestamp sentAt = 3;

}

```

</br>

3.  因为要生成gRPC的Service类,所以需要借助protoc-gen-grpc-java插件,在cmomons模块的pom.xml添加插件

```

<dependencies>

  <!-- 用来兼容java17 -->

  <dependency>

    <groupId>jakarta.annotation</groupId>

    <artifactId>jakarta.annotation-api</artifactId>

    <version>1.3.5</version>

  </dependency>

</dependencies>

<build>

  <extensions>

    <extension>

      <groupId>kr.motd.maven</groupId>

      <artifactId>os-maven-plugin</artifactId>

      <version>1.7.1</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.21.7:exe:${os.detected.classifier}</protocArtifact>

        <pluginId>grpc-java</pluginId>

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

      </configuration>

      <executions>

        <execution>

          <goals>

            <goal>compile</goal>

            <goal>compile-custom</goal>

          </goals>

        </execution>

      </executions>

    </plugin>

  </plugins>

</build>

```

</br>

4.  点击编译,编辑会自动执行protoc-gen-grpc-java插件

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/76cf07ae05634818a2b3af527aa7d78e~tplv-k3u1fbpfcp-zoom-1.image)

target目录下就有我们生成的实体类和grpc的service类

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cfdfdac884464834acfa91c1e79e0b8e~tplv-k3u1fbpfcp-zoom-1.image)

</br>

5.  接下来编写sms模块(server端),因为我添加了springboot的web,所以这里用@Service的形式来注入

```

@Service

public class SmsServiceImpl extends SmsServiceImplBase {

    @Override

    public void sendSms(SmsRequest request, StreamObserver<SmsResponse> responseObserver) {

        // 请求的参数

        System.out.println(request.getPhone());

        System.out.println(request.getMsg());

        // 返回的东西

        SmsResponse response = SmsResponse.newBuilder()

            .setRequestId(UUID.fastUUID().toString())

            .setIsSuccess(true)

            .setSentAt(Timestamps.fromMillis(System.currentTimeMillis()))

            .build();

        // 塞进去

        responseObserver.onNext(response);

        // 塞完,走吧

        responseObserver.onCompleted();

    }

}

```

启动类,gRPC的通信端口是90

```

public class GrpcSmsApp {

    private Server server;

    public static void main(String[] args) {

        SpringApplication.run(GrpcSmsApp.class, args);

    }

    /**

    * 启动grpc

    */

    @SneakyThrows

    @PostConstruct

    public void startGrpcServer() {

        server = ServerBuilder.forPort(90).addService(new SmsServiceImpl()).build().start();

    }

    @PreDestroy

    public void stopGrpcServer() {

        if (server != null) {

            server.shutdown();

        }

    }

}

```

</br>

6.  接着写login模块(client端),创建连接并使用Bean进行管理。.newBlockingStub是最常用的阻塞请求。如需异步、双工请建立对应的stub

```

@Configuration

public class SmsService {

    @Bean

    SmsServiceGrpc.SmsServiceBlockingStub blockingStub() {

        ManagedChannel channel = ManagedChannelBuilder

                .forAddress("localhost", 90)

                .usePlaintext() // 明文传输,生产用NettyChannelBuilder下的sslContext()

                .build();

        return SmsServiceGrpc.newBlockingStub(channel);

    }

}

```

</br>

7.  写一个接口来测试

```

@RestController

@RequiredArgsConstructor

@RequestMapping("login")

public class LoginApi {

private final SmsServiceBlockingStub blockingStub;

    @PostMapping("sendLoginCode")

    String sendLoginCode(String phone) {

        SmsRequest request = SmsRequest.newBuilder()

                .setPhone(phone)

                .setMsg("你的验证码是:sb")

                .build();

        SmsResponse smsResponse = blockingStub.sendSms(request);

        if (!smsResponse.getIsSuccess()) {

            return "发送失败";

        }

        System.out.println("smsResponse = " + smsResponse);

        return smsResponse.getRequestId();

    }

}

```

</br>

8.  用postman进行调用,正常发送和返回

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/5f4e63a953f64818b251aa0ed164bf54~tplv-k3u1fbpfcp-zoom-1.image)

login模块(client端)

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cff3d5871d1640b88f01f7ac07cc9c29~tplv-k3u1fbpfcp-zoom-1.image)

sms模块(server端)

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/01dfcab20022424682e38fb95794bede~tplv-k3u1fbpfcp-zoom-1.image)

</br>

go版

9.  保留Java的sms模块,我们用Golang调用试一试,把sms.proto移动到go项目目录下,安装protoc-gen-go-grpc插件来生成Go版本的Service层。

```

syntax = "proto3";

import "google/protobuf/timestamp.proto";

option java_package = "com.haowen.common.protobuf";

option java_outer_classname = "SmsProto";

option go_package = "../protobuf";

service SmsService {

  rpc SendSms (SmsRequest) returns (SmsResponse) {}

}

message SmsRequest {

  string phone = 1;

  string msg = 2;

}

message SmsResponse {

  string requestId = 1;

  bool isSuccess = 2;

  google.protobuf.Timestamp sentAt = 3;

}

// go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

// go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

// protoc --go_out=. --go-grpc_out=.  sms.proto

```

分别执行,安装插件并生成proto的Go文件。

```

// go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

// go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

// protoc --go_out=. --go-grpc_out=.  sms.proto

```

执行后会生成 

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7f4bdd9bda254fb1ac5bf1aa5109ea44~tplv-k3u1fbpfcp-zoom-1.image)

</br>

10. 接下来编写一个调用方法,同样调用端口是90

```

package main

import (

    "context"

    "fmt"

    "google.golang.org/grpc"

    "google.golang.org/grpc/credentials/insecure"

    "grpc/protobuf"

    "log"

)

/*

go get -u google.golang.org/grpc

go get -u google.golang.org/grpc/credentials

*/

const (

    address = ":90"

)

func main() {

    // 设置一个连接

    conn, err := grpc.Dial(address, grpc.WithTransportCredentials(insecure.NewCredentials()))

    if err != nil {

        log.Fatalf("连接失败: %v", err)

    }

    defer func(conn *grpc.ClientConn) {

        err := conn.Close()

        if err != nil {

            log.Fatalf("关闭连接失败: %v", err)

        }

    }(conn)

    // 创建一个SmsService的客户端

    client := protobuf.NewSmsServiceClient(conn)

    response, err := client.SendSms(context.Background(), &protobuf.SmsRequest{

        Phone: "110",

        Msg:  "哈哈哈",

    })

    fmt.Println(response, err)

}

```

</br>

11. 运行main函数,这样就实现了一个简单的跨语言调用

![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/acabe854c5b543329bcb27bddc3387ff~tplv-k3u1fbpfcp-zoom-1.image)

为了显得文章不会特别臃肿,本文省略了模块层级的创建,相信聪明如你已经一看就会啦,如果有更好的建议,欢迎在评论区留言。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,753评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,668评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,090评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,010评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,054评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,806评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,484评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,380评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,873评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,021评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,158评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,838评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,499评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,044评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,159评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,449评论 3 374
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,136评论 2 356

推荐阅读更多精彩内容