在 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 Starter
和Lombok
就可以了。然后加入对 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…