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閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容