RPC是Remote Procedure Call的简称,中文叫远程过程调用。
那么gRPC就是google使用GO语言开发的RPC框架
而且gRPC基于 HTTP/2协议传输
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>cdwxgrpc</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>server</module>
</modules>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<grpc.version>1.34.1</grpc.version>
<protobuf.version>3.12.0</protobuf.version>
<protoc.version>3.12.0</protoc.version>
<maven.cpmplier.source>1.8</maven.cpmplier.source>
<maven.cpmplier.target>1.8</maven.cpmplier.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-bom</artifactId>
<version>${grpc.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>${protobuf.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
<protoSourceRoot>src/main/resources/proto</protoSourceRoot>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cdwxgrpc</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>server</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
<protoSourceRoot>./src/main/resources/proto</protoSourceRoot>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
protobuf插件可以install生成grpc代码
// 申明语法版本 *必要* (否则默认proto2)
syntax = "proto3";
package server;
// java代码的生成包路径
option java_package = "com.example.server";
// java生成的classname
option java_outer_classname = "SearchServiceProto";
//option java_multiple_files = true;
// 定义服务
// 例如下面这个search服务,rpc声明他是rpc服务方法,search是其中一个方法名(可以声明多个方法),searchrequest是请求参数
// searchresponse是返回结果
service SearchService{
rpc Search (SearchRequest) returns (SearchResponse) {}
}
// 声明消息(有点像java对象)里面是他的一些属性,注意后面的=1这些是表明他的参数index而不是赋值(可以去了解一下protobuf语法)
message SearchRequest {
// 默认reversed
string query = 1;
int32 pageNumber = 2;
int32 resultPerPage = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
// 模拟globalresult的data
message Data {
int32 dataInt = 1;
string dataStr = 2;
}
message SearchResponse {
int32 code = 1;
string msg = 2;
// 类似数组结构 Data[]
repeated Data data = 3;
}
maven-install会生成代码在target目录下,后续可使用代码
package com.example.server.service.impl;
import com.example.server.SearchServiceGrpc;
import com.example.server.SearchServiceGrpc.*;
import com.example.server.SearchServiceProto;
import com.example.server.SearchServiceProto.*;
import io.grpc.BindableService;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @author Tequila
* @date 2022/5/7 14:56
*/
public class SearchServiceImpl extends SearchServiceImplBase {
private final int port = 9999;
private Server server;
@Override
public void search(SearchRequest request, StreamObserver<SearchResponse> responseObserver) {
if (request.isInitialized()) {
System.out.println("true");
}
int value = request.getCorpusValue();
int pageNumber = request.getPageNumber();
String query = request.getQuery();
int resultPerPage = request.getResultPerPage();
System.out.println(request.toString());
/*
* proto Data
* */
List<Data> datas = new ArrayList<>();
datas.add(Data.newBuilder().setDataInt(1).setDataStr("一").build());
SearchResponse build = SearchResponse.newBuilder()
// 如果用到了repeated修饰的proto message则不可使用set进行赋值用add
.setCode(200)
.setMsg("通信成功")
.addAllData(datas)
.build();
responseObserver.onNext(build);
// 结束
responseObserver.onCompleted();
}
public void start() throws IOException {
server = ServerBuilder.forPort(port)
.addService(new SearchServiceImpl())
.build()
.start();
System.out.println("service start...at port" + port);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
SearchServiceImpl.this.stop();
System.out.println("程序出错或正常退出!");
}
});
}
private void stop() {
if (server != null) {
server.shutdown();
}
}
/**
* 不让main线程断开
* @throws InterruptedException
*/
public void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
}
package com.example.server;
import com.example.server.service.impl.SearchServiceImpl;
import java.io.IOException;
/**
* @author Tequila
* @date 2022/5/7 15:07
*/
public class Server {
public static void main(String[] args) {
SearchServiceImpl searchService = new SearchServiceImpl();
try {
searchService.start();
searchService.blockUntilShutdown();
} catch (InterruptedException | IOException e) {
e.printStackTrace();
}
}
}
这时候服务端就搭建好了
controller层来调用service然后返回数据给前端
controller代码就不写了下面看下serviceimpl代码就可
package com.example.client.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.example.client.entity.Corpus;
import com.example.client.entity.GlobalData;
import com.example.client.service.DemoService;
import com.example.server.SearchServiceGrpc;
import com.example.server.SearchServiceGrpc.*;
import com.example.server.SearchServiceProto;
import com.example.server.SearchServiceProto.*;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author Tequila
* @date 2022/5/7 14:11
*/
@Service
public class DemoServiceImpl implements DemoService {
private final SearchServiceBlockingStub stub;
private final ManagedChannel channel;
public DemoServiceImpl() {
channel = ManagedChannelBuilder
.forAddress("127.0.0.1", 9999)
.usePlaintext() //使用纯文本类型
.build();
stub = SearchServiceGrpc.newBlockingStub(channel);
}
@Override
public String search(String query) {
if (StringUtils.isEmpty(query)) {
return null;
}
String[] split = query.split(",");
SearchResponse response = stub.search(SearchRequest.newBuilder()
.setCorpusValue(Integer.parseInt(split[0]))
.setPageNumber(Integer.parseInt(split[1]))
.setQuery(split[2])
.setResultPerPage(Integer.parseInt(split[3]))
.build());
if (BeanUtil.isEmpty(response)) {
return new JSONObject()
.set("code",200)
.set("msg","返回结果为空")
.set("data",GlobalData.builder().build()).toString();
}
int code = response.getCode();
String msg = response.getMsg();
List<Data> dataList = response.getDataList();
List<GlobalData> collect = dataList.stream().map(data -> {
// 转成自建对象不然json没法识别
return GlobalData.builder().dataInt(data.getDataInt()).dataString(data.getDataStr()).build();
}).collect(Collectors.toList());
JSONObject jsonObject = new JSONObject();
jsonObject.set("code",code).set("msg",msg).set("data",collect);
return jsonObject.toString();
}
}
差不多就是这样的demo代码,后续的一些架构设计mapper层等自行添加就好
以上差不多就是简单得gRPC框架的使用demo代码示例了。
其中比较重要的点就是protobuf语法,插件生成代码,重写服务端方法,客户端连接请求获取返回结果。