本项目基于Spring Boot 2.0.5+yidongnan/grpc-spring-boot-starter 2.0.1.RELEASE+SpringCloud Finchley.SR1,通过2个grpc-eureka-server模拟Eureka集群,多个可横向弹性扩容的grpc-spring-cloud-provider负责提供gRPC服务,再用grpc-spring-cloud-consumer作为服务消费者来调用gRPC服务,服务提供者和消费者通过grpc-springboot-cloud-lib模块来完成接口约定。
本项目已上传至GitHub:https://github.com/linshenkx/grpc-springboot-cloud
根项目(com.linshen:grpc-springboot-cloud:1.0-SNAPSHOT)下有4个子模块:
<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.0modelVersion>
<groupId>com.linshengroupId>
<artifactId>grpc-springboot-cloudartifactId>
<packaging>pompackaging>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.5.RELEASEversion>
parent>
<modules>
<module>grpc-springboot-cloud-libmodule>
<module>grpc-eureka-servermodule>
<module>grpc-spring-cloud-providermodule>
<module>grpc-spring-cloud-consumermodule>
modules>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-boot.version>${parent.version}spring-boot.version>
<net-devh-grpc.version>2.0.1.RELEASEnet-devh-grpc.version>
<spring-cloud.version>Finchley.SR1spring-cloud.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.linshengroupId>
<artifactId>grpc-springboot-cloud-libartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>net.devhgroupId>
<artifactId>grpc-client-spring-boot-starterartifactId>
<version>${net-devh-grpc.version}version>
dependency>
<dependency>
<groupId>net.devhgroupId>
<artifactId>grpc-server-spring-boot-starterartifactId>
<version>${net-devh-grpc.version}version>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-deploy-pluginartifactId>
<configuration>
<skip>trueskip>
configuration>
plugin>
plugins>
build>
project>
<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>grpc-springboot-cloudartifactId>
<groupId>com.linshengroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>grpc-springboot-cloud-libartifactId>
<packaging>jarpackaging>
<properties>
<os.plugin.version>1.6.0os.plugin.version>
<grpc.version>1.15.1grpc.version>
<protoc.version>3.6.1protoc.version>
<protobuf.plugin.version>0.6.1protobuf.plugin.version>
properties>
<dependencies>
<dependency>
<groupId>io.grpcgroupId>
<artifactId>grpc-allartifactId>
<version>${grpc.version}version>
dependency>
dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.mavengroupId>
<artifactId>os-maven-pluginartifactId>
<version>${os.plugin.version}version>
extension>
extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.pluginsgroupId>
<artifactId>protobuf-maven-pluginartifactId>
<version>${protobuf.plugin.version}version>
<extensions>trueextensions>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}protocArtifact>
<pluginId>grpc-javapluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}pluginArtifact>
<protoSourceRoot>${project.basedir}/src/main/protoprotoSourceRoot>
<outputDirectory>${project.basedir}/src/main/javaoutputDirectory>
<clearOutputDirectory>falseclearOutputDirectory>
configuration>
<executions>
<execution>
<phase>compilephase>
<goals>
<goal>compilegoal>
<goal>compile-customgoal>
goals>
execution>
executions>
plugin>
plugins>
build>
project>
<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.0modelVersion>
<artifactId>grpc-eureka-serverartifactId>
<packaging>jarpackaging>
<name>grpc-eureka-servername>
<parent>
<artifactId>grpc-springboot-cloudartifactId>
<groupId>com.linshengroupId>
<version>1.0-SNAPSHOTversion>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
<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.0modelVersion>
<artifactId>grpc-spring-cloud-providerartifactId>
<packaging>jarpackaging>
<name>grpc-spring-cloud-providername>
<parent>
<artifactId>grpc-springboot-cloudartifactId>
<groupId>com.linshengroupId>
<version>1.0-SNAPSHOTversion>
parent>
<dependencies>
<dependency>
<groupId>com.linshengroupId>
<artifactId>grpc-springboot-cloud-libartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>net.devhgroupId>
<artifactId>grpc-server-spring-boot-starterartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
<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.0modelVersion>
<artifactId>grpc-spring-cloud-consumerartifactId>
<packaging>jarpackaging>
<name>grpc-spring-cloud-consumername>
<parent>
<artifactId>grpc-springboot-cloudartifactId>
<groupId>com.linshengroupId>
<version>1.0-SNAPSHOTversion>
parent>
<dependencies>
<dependency>
<groupId>com.linshengroupId>
<artifactId>grpc-springboot-cloud-libartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>net.devhgroupId>
<artifactId>grpc-client-spring-boot-starterartifactId>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
还是在src/main/proto目录编写下一个greeter.proto文件,如下
syntax = "proto3";
option java_package = "com.linshen.grpc.cloud.lib";
service Greeter {
rpc SayHello ( HelloRequest) returns ( HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
行protobuf插件的compile和compile-custom功能,生成java代码到src/main/java目录下
这里要实现两个Eureka-Server集群实现服务中心的高可用(两个确实很少,谈不上高可用,这里只是演示一下),需要通过不同的profile来启动不同的Eureka-Server,故需要application.yml、application-slave1.yml和application-slave2.yml三个文件。application.yml定义了公共属性和actuator配置,并且配置了默认active-profile是slave1。其中slave1运行在端口8781上,slave2运行在端口8782上,两者的域名(也可以设置成ip)通过环境变量传入,不传的话默认是在本地(localhost)运行。
最终的工程目录应该如下
spring:
application:
name: grpc-eureka-server
profiles:
active: ${grpc-eureka-profile:slave1}
# actuator management
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: '*'
# actuator info
info:
app:
encoding:UTF-8
java.source:1.8
java.traget:1.8
server:
port: 8781
eureka:
client:
service-url:
defaultZone: http://${cloudServerSlave2:localhost}:8782/eureka/
instance:
hostname: ${cloudServerSlave1:localhost}
server:
enable-self-preservation: false
server:
port: 8782
eureka:
client:
service-url:
defaultZone: http://${cloudServerSlave1:localhost}:8781/eureka/
instance:
hostname: ${cloudServerSlave2:localhost}
server:
enable-self-preservation: false
如下,server.port和grpc.server.port都为0,这样启动的时候服务端口和grpc端口都会随机分配,就不会重复占用了,eureka.instance.instance-id也是通过应用名+uuid的方式来避免重复
server:
port: 0
grpc:
server:
port: 0
spring:
application:
name: grpc-spring-cloud-provider
eureka:
client:
service-url:
defaultZone: http://${cloudServerSlave1:localhost}:8781/eureka/,http://${cloudServerSlave2:localhost}:8782/eureka/
instance:
instance-id: ${spring.application.name}:${random.uuid}
logging:
level:
org.springframework.web.servlet.DispatcherServlet: DEBUG
# actuator management
management:
endpoint:
health:
show-details: always
endpoints:
web:
exposure:
include: '*'
# actuator info
info:
app:
encoding:UTF-8
java.source:1.8
java.traget:1.8
name:grpc-spring-cloud-provider
package com.linshen.grpcspringcloudprovider;
import com.linshen.grpc.cloud.lib.GreeterGrpc;
import com.linshen.grpc.cloud.lib.GreeterOuterClass;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import net.devh.springboot.autoconfigure.grpc.server.GrpcService;
@Slf4j
@GrpcService(GreeterOuterClass.class)
public class GreeterService extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(GreeterOuterClass.HelloRequest request, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) {
String message = "Hello " + request.getName();
final GreeterOuterClass.HelloReply.Builder replyBuilder = GreeterOuterClass.HelloReply.newBuilder().setMessage(message);
responseObserver.onNext(replyBuilder.build());
responseObserver.onCompleted();
log.info("Returning " +message);
}
}
不需要配置grpc.server.port,其他的和provider大致相同
注意这里的@GrpcClient的value是provider的spring.application.name
package com.linshen.grpcspringcloudconsumer;
import com.linshen.grpc.cloud.lib.GreeterGrpc;
import com.linshen.grpc.cloud.lib.GreeterOuterClass;
import io.grpc.Channel;
import net.devh.springboot.autoconfigure.grpc.client.GrpcClient;
import org.springframework.stereotype.Service;
@Service
public class GrpcClientService {
@GrpcClient("grpc-spring-cloud-provider")
private Channel serverChannel;
public String sendMessage(String name) {
GreeterGrpc.GreeterBlockingStub stub= GreeterGrpc.newBlockingStub(serverChannel);
GreeterOuterClass.HelloReply response = stub.sayHello(GreeterOuterClass.HelloRequest.newBuilder().setName(name).build());
return response.getMessage();
}
}
package com.linshen.grpcspringcloudconsumer;
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 = "LinShen") String name) {
return grpcClientService.sendMessage(name);
}
}
在根工程下运行mvn install -Dmaven.test.skip=true
,这一步将项目install到本地仓库,目的是使插件能正常工作,避免出现Failed to collect dependencies
问题,如果只是在本地运行的话没遇到依赖问题,这一步也可以省略。
分别指定active-profiles为slave1和slave2启动grpc-eureka-server
重复启动grpc-spring-cloud-provider,这里我启动了两次,获得两个实例
简单启动一个消费者即可
打开eureka-server1,即http://localhost:8781/,如下图,可以看到所有的服务实例到注册到服务中心了
打开eureka-server2,即http://localhost:8782/,如下图,可以看到服务实例信息还没有eureka-server1完整
这个时候再打开consumer,即http://localhost:49236/,如下图,运行正常
把Grpc-Eureka-Server-Slave1杀掉,再查看eureka-server2,可以发现注册信息已经同步到slave2了,这个时候consumer仍可正常使用
gRPC-Java指导:https://grpc.io/docs/tutorials/basic/java.html
gRPC 官方文档中文版1.0:https://doc.oschina.net/grpc
Maven Protocol Buffers Plugin 插件指南:https://www.xolstice.org/protobuf-maven-plugin/index.html
Spring Boot 2+gRPC 学习系列1:搭建Spring Boot 2+gRPC本地项目:https://blog.csdn.net/alinyua/article/details/83030149
Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目:https://blog.csdn.net/alinyua/article/details/83043823
Spring Boot 2+Dubbo 学习系列1:使用Docker部署zookeeper:https://blog.csdn.net/alinyua/article/details/81016734
Spring Boot 2+Dubbo 学习系列2:搭建Spring Boot 2+Dubbo+Zookeeper集群:https://blog.csdn.net/alinyua/article/details/81019925
Spring Boot 2+Dubbo 学习系列3:dubbo-ops 之 Dubbo Admin:https://blog.csdn.net/alinyua/article/details/81034023
linshenkx/grpc-springboot-cloud(Spring Cloud +gRPC集群项目):https://github.com/linshenkx/grpc-springboot-cloud
linshenkx/grpc-springboot-lin(Spring Boot 2+gRPC本地项目):https://github.com/linshenkx/grpc-springboot-lin
linshenkx/spring-cloud-lin(Spring Boot 2整合Spring Cloud):https://github.com/linshenkx/spring-cloud-lin
gRPC-Java的GitHub地址:https://github.com/grpc/grpc-java
yidongnan/grpc-spring-boot-starter的GitHub地址:https://github.com/yidongnan/grpc-spring-boot-starter