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

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

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

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

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


Java版:

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

<modules>

 <module>grpc-commons</module>

 <module>grpc-login</module>

 <module>grpc-sms</module>

</modules>

</br>

  1. 编写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>

  1. 因为要生成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>

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

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

</br>

  1. 接下来编写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>

  1. 接着写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>

  1. 写一个接口来测试

@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>

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

login模块(client端)

sms模块(server端)

</br>

go版

  1. 保留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>

  1. 接下来编写一个调用方法,同样调用端口是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)

}


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

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

相关文章
|
Cloud Native 架构师 Java
谷歌架构师分享gRPC与云原生应用开发Go和Java为例文档
随着微服务和云原生相关技术的发展,应用程序的架构模式已从传统的单体架构或分层架构转向了分布式的计算架构。尽管分布式架构本身有一定的开发成本和运维成本,但它所带来的收益是显而易见的。
|
7月前
|
网络安全 流计算 Python
实时计算 Flink版操作报错合集之Flink sql-client 针对kafka的protobuf格式数据建表,报错:java.lang.ClassNotFoundException 如何解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
138 1
|
7月前
|
消息中间件 关系型数据库 网络安全
实时计算 Flink版操作报错合集之Flink sql-client 针对kafka的protobuf格式数据建表,报错:java.lang.ClassNotFoundException 如何解决
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
246 1
|
7月前
|
XML JSON Java
java项目中使用protobuf扫盲笔记
最近公司 Java 项目中有用到 protobuf,查了些资料还是一脸迷茫。
160 1
|
XML Java 编译器
【Protobuf】Protobuf快速使用 Java版、Python版
 Protocol Buffers(简称为ProtoBuf)是一种用于序列化、结构化数据的语言无关、平台无关、可扩展的机制。它由Google开发并于2008年开源发布。
414 0
|
XML 存储 JSON
protobuf在java, Android下的使用总结
protobuf在java, Android下的使用总结
|
Java 编译器 数据格式
Protobuf:一种轻量级、高效的数据交换格式,附Java与Python数据交换示例
Protobuf:一种轻量级、高效的数据交换格式,附Java与Python数据交换示例
281 0
|
Dubbo 算法 安全
Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(二)
Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(二)
Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(二)
|
SQL 存储 Java
Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(一)
Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(一)
Java序列化案例demo(包含Kryo、JDK原生、Protobuf、ProtoStuff以及hessian)(一)
|
XML JSON Java
在java程序中使用protobuf
在java程序中使用protobuf