目录
创建maven父工程spring-boot-grpc
创建模块spring-boot-grpc-lib
创建模块local-server(gRPC服务端)
创建模块local-client(gRPC客户端)
创建springboot项目,勾选springboot-web即可
4.0.0
pom
spring-boot-grpc-lib
local-server
local-client
org.springframework.boot
spring-boot-starter-parent
2.5.6
com.chenj
spring-boot-grpc
0.0.1-SNAPSHOT
spring-boot-grpc
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
在父工程spring-boot-grpc下新建maven模块,名为spring-boot-grpc-lib
spring-boot-grpc
com.chenj
0.0.1-SNAPSHOT
4.0.0
spring-boot-grpc-lib
UTF-8
1.8
1.8
io.grpc
grpc-netty-shaded
1.37.0
io.grpc
grpc-protobuf
1.37.0
io.grpc
grpc-stub
1.37.0
org.apache.tomcat
annotations-api
6.0.53
provided
kr.motd.maven
os-maven-plugin
1.6.2
org.xolstice.maven.plugins
protobuf-maven-plugin
0.6.1
com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier}
grpc-java
io.grpc:protoc-gen-grpc-java:1.37.0:exe:${os.detected.classifier}
compile
compile-custom
在spring-boot-grpc-lib模块的src/main/proto目录下新增名为helloworld.proto的文件,这里面定义了一个gRPC服务,里面含有一个接口,并且还有这个接口的入参和返回结果的定义:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.chenj.grpc.lib";
option java_outer_classname = "HelloWorldProto";
// The greeting service definition.
service Simple {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings
message HelloReply {
string message = 1;
}
proto文件已经做好,接下来要根据这个文件来生成Java代码
SimpleGrpc里面有抽象类SimpleImplBase,制作gRPC服务的时候需要继承该类,另外,如果您要远程调用gRPC的sayHello接口,就会用到SimpleGrpc类中的SimpleStub类,其余的HelloReply、HelloRequest这些则是入参和返回的数据结构定义
在父工程下面新建名为local-server的springboot模块,
gRPC 服务端使用一下命令添加 Maven 依赖项
net.devh
grpc-server-spring-boot-starter
2.12.0.RELEASE
依赖上面的spring-boot-grpc-lib模块
com.chenj
spring-boot-grpc-lib
0.0.1-SNAPSHOT
完整的pom.xml文件
4.0.0
spring-boot-grpc
com.chenj
0.0.1-SNAPSHOT
com.chenj
local-server
0.0.1-SNAPSHOT
local-server
Demo project for Spring Boot
1.8
net.devh
grpc-server-spring-boot-starter
2.12.0.RELEASE
com.chenj
spring-boot-grpc-lib
0.0.1-SNAPSHOT
org.projectlombok
lombok
springboot应用,配置文件内容如下:
spring:
application:
name: spring-boot-grpc-server
# gRPC有关的配置,这里只需要配置服务端口号
grpc:
server:
port: 9898
server:
port: 8080
新建拦截类LogGrpcInterceptor.java,每当gRPC请求到来后该类会先执行,这里是将方法名字在日志中打印出来,您可以对请求响应做更详细的处理:
package com.chenj.springbootgrpcserver.interceptor;
import io.grpc.Metadata;
import io.grpc.ServerCall;
import io.grpc.ServerCallHandler;
import io.grpc.ServerInterceptor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class LogGrpcInterceptor implements ServerInterceptor {
@Override
public ServerCall.Listener interceptCall(ServerCall serverCall, Metadata metadata,
ServerCallHandler serverCallHandler) {
log.info(serverCall.getMethodDescriptor().getFullMethodName());
return serverCallHandler.startCall(serverCall, metadata);
}
}
为了让LogGrpcInterceptor可以在gRPC请求到来时被执行,需要做相应的配置,如下所示,在普通的bean的配置中添加注解即可:
package com.chenj.springbootgrpcserver.config;
import com.chenj.springbootgrpcserver.interceptor.LogGrpcInterceptor;
import io.grpc.ServerInterceptor;
import net.devh.boot.grpc.server.interceptor.GrpcGlobalServerInterceptor;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
public class GlobalInterceptorConfiguration {
@GrpcGlobalServerInterceptor
ServerInterceptor logServerInterceptor() {
return new LogGrpcInterceptor();
}
}
应用启动类很简单:
package com.chenj.springbootgrpcserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootGrpcServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootGrpcServerApplication.class, args);
}
}
接下来是最重要的service类,gRPC服务在此处对外暴露出去,完整代码如下
package com.chenj.springbootgrpcserver.service;
import com.chenj.grpc.lib.HelloReply;
import com.chenj.grpc.lib.HelloRequest;
import com.chenj.grpc.lib.SimpleGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
import java.util.Date;
@GrpcService
public class GrpcServerService extends SimpleGrpc.SimpleImplBase {
@Override
public void sayHello(HelloRequest request,
StreamObserver responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("你好, " + request.getName() + ", " + new Date()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
上述GrpcServerService.java中有几处需要注意:
是使用@GrpcService注解,再继承SimpleImplBase,这样就可以借助grpc-server-spring-boot-starter库将sayHello暴露为gRPC服务;
SimpleImplBase是前文中根据proto自动生成的java代码,在spring-boot-grpc-lib模块中;
sayHello方法中处理完毕业务逻辑后,调用HelloReply.onNext方法填入返回内容;
调用HelloReply.onCompleted方法表示本次gRPC服务完成;
至此,gRPC服务端编码就完成了,咱们接着开始客户端开发
在父工程grpc-turtorials下面新建名为local-client的模块
gRPC客户端使用一下命令添加 Maven 依赖项:
net.devh
grpc-client-spring-boot-starter
2.12.0.RELEASE
依赖上面的spring-boot-grpc-lib模块
com.chenj
spring-boot-grpc-lib
0.0.1-SNAPSHOT
完整的pom.xml文件
4.0.0
spring-boot-grpc
com.chenj
0.0.1-SNAPSHOT
com.chenj
local-client
0.0.1-SNAPSHOT
local-client
Demo project for Spring Boot
1.8
net.devh
grpc-client-spring-boot-starter
2.12.0.RELEASE
com.chenj
spring-boot-grpc-lib
0.0.1-SNAPSHOT
org.springframework.boot
spring-boot-maven-plugin
应用配置文件src/main/resources/application.yml,注意address的值就是gRPC服务端的信息,我这里local-server和local-client在同一台电脑上运行,请您根据自己情况来设置:
server:
port: 8088
spring:
application:
name: local-client
grpc:
client:
# gRPC配置的名字,GrpcClient注解会用到
local-grpc-server:
# gRPC服务端地址
address: 'static://127.0.0.1:9898'
enableKeepAlive: true
keepAliveWithoutCalls: true
negotiationType: plaintext
新建拦截类LogGrpcInterceptor,与服务端的拦截类差不多,不过实现的接口不同:
package com.chenj.springbootgrpcclient.interceptor;
import io.grpc.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogGrpcInterceptor implements ClientInterceptor {
private static final Logger log = LoggerFactory.getLogger(LogGrpcInterceptor.class);
@Override
public ClientCall interceptCall(MethodDescriptor method,
CallOptions callOptions, Channel next) {
log.info(method.getFullMethodName());
return next.newCall(method, callOptions);
}
}
为了让拦截类能够正常工作,即发起gRPC请求的时候被执行,需要新增一个配置类:
package com.chenj.springbootgrpcclient.config;
import com.chenj.springbootgrpcclient.interceptor.LogGrpcInterceptor;
import io.grpc.ClientInterceptor;
import net.devh.boot.grpc.client.interceptor.GrpcGlobalClientInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
@Order(Ordered.LOWEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
public class GlobalClientInterceptorConfiguration {
@GrpcGlobalClientInterceptor
ClientInterceptor logClientInterceptor() {
return new LogGrpcInterceptor();
}
}
启动类:
package com.chenj.springbootgrpcclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootGrpcClientApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootGrpcClientApplication.class, args);
}
}
接下来是最重要的服务类GrpcClientService
package com.chenj.springbootgrpcclient.service;
import com.chenj.grpc.lib.HelloReply;
import com.chenj.grpc.lib.HelloRequest;
import com.chenj.grpc.lib.SimpleGrpc;
import io.grpc.StatusRuntimeException;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Service;
@Service
public class GrpcClientService {
@GrpcClient("local-grpc-server")
private SimpleGrpc.SimpleBlockingStub simpleStub;
public String sendMessage(final String name) {
try {
final HelloReply response = this.simpleStub.sayHello(HelloRequest.newBuilder().setName(name).build());
return response.getMessage();
} catch (final StatusRuntimeException e) {
return "FAILED with " + e.getStatus().getCode().name();
}
}
}
上述GrpcClientService类有几处要注意的地方:
用@Service将GrpcClientService注册为spring的普通bean实例;
用@GrpcClient修饰SimpleBlockingStub,这样就可以通过grpc-client-spring-boot-starter库发起gRPC调用,被调用的服务端信息来自名为local-grpc-server的配置;
SimpleBlockingStub来自前文中根据helloworld.proto生成的java代码;
SimpleBlockingStub.sayHello方法会远程调用local-server应用的gRPC服务;
为了验证gRPC服务调用能否成功,再新增个web接口,接口内部会调用GrpcClientService.sendMessage,这样咱们通过浏览器就能验证gRPC服务是否调用成功了:
package com.chenj.springbootgrpcclient.controller;
import com.chenj.springbootgrpcclient.service.GrpcClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GrpcClientController {
@Autowired
private GrpcClientService grpcClientService;
@RequestMapping("/")
public String printMessage(@RequestParam(defaultValue = "will") String name) {
return grpcClientService.sendMessage(name);
}
}