乐趣区

在Spring-boot-2-微服务中使用-GRPC

在 Spring boot 2 微服务中使用 GRPC

关于 RPC

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC 协议假定某些传输协议的存在,如 TCP 或 UDP,为通信程序之间携带信息数据。在 OSI 网络通信模型中,RPC 跨越了传输层和应用层。RPC 使得开发包括网络分布式多程序在内的应用程序更加容易。

gRPC

gRPC 是 Google 开源的通用高性能 RPC 框架,它支持的是使用 Protocol Buffers 来编写 Service 定义,支持较多语言扩平台并且拥有强大的二进制序列化工具集。与文章《RPC 框架实践之:Apache Thrift》一文中实践的另一种通用 RPC 框架 Thrift 能通过 Generator 自动生成对应语言的 Service 接口类似,gRPC 也能自动地生成 Server 和 Client 的 Service 存根(Stub),我们只需要一个命令就能快速搭建起 RPC 运行环境。

微服务和 RPC

微服务 大行其道的今天,服务间通讯其实是个比较大的问题,REST调用及测试都很方便,RPC 就显得有点繁琐,但是 RPC 的效率是毋庸置疑的,所以建议在多系统之间的内部调用采用 RPC。对外提供的服务,Rest 更加合适。

如果何用

  • 在一个 .proto 文件内定义服务。
  • 用 protocol buffer 编译器生成服务器和客户端代码。
  • 使用 gRPC 的 Java API 为你的服务实现一个简单的客户端和服务器。

Demo

创建一个 Spring boot 2 项目,选择 Web StarterLombok就可以了。然后加入对 grpc-spring-boot-starter 的依赖。

<dependency>
    <groupId>io.github.lognet</groupId>
    <artifactId>grpc-spring-boot-starter</artifactId>
    <version>3.4.1</version>
</dependency>

编译需要修改,因为编译时需要先 .proto 文件进行代码生成,生成我们想要的 java 代码文件。生成的文件会在 target 下的 generated-source 下的 protobuf 文件夹下。编译配置如下:

  <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>${os-maven-plugin.version}</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>${protobuf-maven-plugin.version}</version>
                <configuration>
                    <protocArtifact>
                        com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier}
                    </protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>
                        io.grpc:protoc-gen-grpc-java:1.23.0:exe:${os.detected.classifier}
                    </pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

添加三个 .proto 文件,第一个是 Member.proto 文件,作为一个复合对象,作为参数使用。

syntax = "proto3";
option java_multiple_files = true;
package com.example.grpcdemo.grpc;

message Member {
    string username = 1;
    string password = 2;
    string info = 3;
}

然后就是 Service 文件,MemberListSerbice.proto文件中定义了,request 和 respone 的内容,还有服务输入输出的 message。这里需要引入之前定义的Member.proto

syntax = "proto3";
option java_multiple_files = true;
package com.example.grpcdemo.grpc;
import "Member.proto";

message MemberListRequest {
    int32 page = 1;
    int32 per_page = 2;
}

message MemberListResponse {repeated Member member = 1;}

service MemberListService {rpc member_list(MemberListRequest) returns (MemberListResponse);
}

这里另一个 Service 详见源代码。下面添加一些本项目所需的 RPC 服务处理代码,先编译项目,省城所需 java 文件,然后就可以开始写 RPC 服务处理代码,需要从生成的抽象类继承。

@GRpcService
public class MemberLoginServiceImpl extends MemberLoginServiceGrpc.MemberLoginServiceImplBase {

    @Override
    public void memberLogin(MemberLoginRequest request, StreamObserver<MemberLoginResponse> responseObserver) {

        String token = "";
        if(request.getMember().getPassword().equals("123456")){token = "success";}

        MemberLoginResponse response = MemberLoginResponse
                .newBuilder()
                .setToken(token)
                .build();

        responseObserver.onNext(response);
        responseObserver.onCompleted();}
}

还需要一个 Java 类来定义客户端连接 RPC 所需的 Bean

@Configuration
public class GrpcConfig {
    @Bean
    ManagedChannel channel(@Value("${app-config.grpc-server-name}") String name,
                           @Value("${app-config.grpc-server-port}") Integer port){return ManagedChannelBuilder.forAddress(name, port)
                .usePlaintext()
                .build();}

    @Bean
    MemberListServiceGrpc.MemberListServiceBlockingStub memberListServiceBlockingStub(ManagedChannel channel){return MemberListServiceGrpc.newBlockingStub(channel);
    }

    @Bean
    MemberLoginServiceGrpc.MemberLoginServiceBlockingStub memberLoginServiceStub(ManagedChannel channel){return MemberLoginServiceGrpc.newBlockingStub(channel);
    }
}

最后还有一个测试用的 Controller,浏览器访问 controller 中的 rest 服务,rest 服务连接 rpc 去访问真实服务,这里服务端和客户端并没有分开,实际 controller 应该在真正的客户端项目中。这里只是一个 demo

@RestController
public class MemberController{

    private MemberListServiceGrpc.MemberListServiceBlockingStub memberListServiceBlockingStub;
    private MemberLoginServiceGrpc.MemberLoginServiceBlockingStub memberLoginServiceBlockingStub;

    public MemberController(MemberListServiceGrpc.MemberListServiceBlockingStub memberListServiceBlockingStub,
                            MemberLoginServiceGrpc.MemberLoginServiceBlockingStub memberLoginServiceBlockingStub) {
        this.memberListServiceBlockingStub = memberListServiceBlockingStub;
        this.memberLoginServiceBlockingStub = memberLoginServiceBlockingStub;
    }

    /**
     * curl -X POST -H "Content-type: application/json" -d "{\"username\":\"freewolf\", \"password\":\"123456\"}" http://localhost:8080/login
     * @param memberVO
     * @return
     */
    @PostMapping("/login")
    public String login(@RequestBody MemberVO memberVO){MemberLoginResponse response = this.memberLoginServiceBlockingStub.memberLogin(MemberLoginRequest.newBuilder()
                .setMember(Member.newBuilder()
                        .setPassword(memberVO.getPassword())
                        .setUsername(memberVO.getUsername())
                        .build())
                .build());
        return response.getToken();}

    @GetMapping("/list")
    public List<MemberVO> list(){MemberListResponse response = this.memberListServiceBlockingStub.memberList(MemberListRequest.newBuilder()
                .setPage(1)
                .setPerPage(2)
                .build());
        List<MemberVO> list = MemberVO.getList(response.getMemberList());
        return list;
    }
}

本文代码地址:https://github.com/freew01f/g…

资源

  • http://doc.oschina.net/grpc?t…
  • https://developers.google.com…
  • https://www.baeldung.com/grpc…
退出移动版