[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插件
<br />target目錄下就有我們生成的實體類和grpc的service類<br /><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進行調用,正常發送和返回
<br />login模塊(client端)<br /><br />sms模塊(server端)<br /><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 /><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函數,這樣就實現了一個簡單的跨語言調用

為了顯得文章不會特別臃腫,本文省略了模塊層級的創建,相信聰明如你已經一看就會啦,如果有更好的建議,歡迎在評論區留言。
[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插件
<br />target目錄下就有我們生成的實體類和grpc的service類<br /><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進行調用,正常發送和返回
<br />login模塊(client端)<br /><br />sms模塊(server端)<br /><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 /><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函數,這樣就實現了一個簡單的跨語言調用

為了顯得文章不會特別臃腫,本文省略了模塊層級的創建,相信聰明如你已經一看就會啦,如果有好的建議,歡迎在評論區留言。
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插件

target目錄下就有我們生成的實體類和grpc的service類

</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進行調用,正常發送和返回

login模塊(client端)

sms模塊(server端)

</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
```
執行后會生成?

</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函數,這樣就實現了一個簡單的跨語言調用

為了顯得文章不會特別臃腫,本文省略了模塊層級的創建,相信聰明如你已經一看就會啦,如果有更好的建議,歡迎在評論區留言。