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

发布时间:2023-04-25 11:08:53

在一般的项目架构中,前后端交互采用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,在公共proto文件中模拟登录发送验证码,commons
<modules>  <module>grpc-commons</module>  <module>grpc-login</module>  <module>grpc-sms</module></modules>
  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;}
  1. 因为要生成grpc的Service类,所以需要使用protocce-gen-grpc-java插件,cmomons模块中的pomm.添加xml插件
<dependencies>  <!-- 用于兼容java177 -->  <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>
  1. 点击编译,编辑将自动执行protoc-gen-grpc-java插件

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

 

在target目录下,有我们生成的实体类和grpcservice类

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

 

  1. 然后编写sms模块(server端),因为我添加了springboot的web,所以这里以@service的形式注入
@Servicepublic 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();        }    }}
  1. 然后写login模块(client端),创建连接并使用bean进行管理。.newblockingStub是最常用的阻塞请求。如需异步和双工,请建立相应的stub
@Configurationpublic class SmsService {    @Bean    SmsServiceGrpc.SmsServiceBlockingStub blockingStub() {        ManagedChannel channel = ManagedChannelBuilder                .forAddress("localhost", 90)                .usePlaintext() // 明文传输,在NettyChanelbuilder下生产sslcontext()                .build();        return SmsServiceGrpc.newBlockingStub(channel);    }}
  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();    }}
  1. 调用postman,正常发送和返回

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

 

login模块(client端)

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

 

server端sms模块(server端)

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

 

 

 

go版

  1. 保留Java的sms模块,我们用Golang调用sms模块.将proto移动到go项目录下,安装protoc-gen-go-Go版本的Service层由Grpc插件生成。
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

它将在执行后生成

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

 

  1. 接下来,写一个调用方法,调用端口也是90
package mainimport (    "context"    "fmt"    "google.golang.org/grpc"    "google.golang.org/grpc/credentials/insecure"    "grpc/protobuf"    "log")/*go get -u google.golang.org/grpcgo 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函数,从而实现简单的跨语言调用

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

为了使文章不会特别臃肿,本文省略了模块级别的创建。我相信如果你已经看到了,你会很聪明。如果您有好的建议,请在评论区留言。

上一篇 五分钟理解Java算法的时间复杂度
下一篇 【系列教程一】谁说 java 不能做爬虫?我第一个不服!

文章素材均来源于网络,如有侵权,请联系管理员删除。

标签: Java教程Java基础Java编程技巧面试题Java面试题