接下来我们需要把gRPC和Spring boot整合在一起,这里主要记录我们的整合方式。

项目目录结构

项目的目录结构如下:

 1foo-svc
 2├── foo-svc-proto
 3│   ├── build.gradle
 4│   └── src
 5│       └── main
 6│           └── proto
 7│               ├── xx1_service.proto
 8│               └── xx2_service.proto
 9├── foo-svc-server
10│   ├── build.gradle
11│   └── src
12│       └── main
13│           ├── java
14│           └── resources
15│               └── application.yml
16├── build.gradle
17├── gradle.properties
18└── settings.gradle

一个gRPC服务的项目由两个子项目组成:

  • proto项目,里面维护gRPC服务的proto文件
  • server项目,gRPC服务的具体实现

gradle构建脚本

整个gRPC服务项目使用gradle构建,下面介绍父子项目的各个构建脚本的内容。

根项目foo-svc

foo-svc/settings.gradle:

1include 'foo-svc-proto'
2include 'foo-svc-server'
3rootProject.name = 'foo-svc'

foo-svc/build.gradle:

 1buildscript {
 2	repositories {
 3	    mavenCentral()
 4	}
 5	ext {
 6		springBootVersion = '1.5.4.RELEASE'
 7	}
 8	dependencies {
 9	    classpath 'com.github.ben-manes:gradle-versions-plugin:0.11.1'
10	    classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1'
11	    classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
12	}
13}
14
15apply plugin:'base'
16
17allprojects {
18    group = 'com.frognew.svc'
19    version = version	
20    repositories {
21        maven {
22	       mavenCentral()
23	    }
24    }
25
26	ext {
27		grpcVersion = '1.4.0'
28		grpcSpringBootStarterVersion = '1.1.1.RELEASE'
29		jasyptSpringBootStarterVersion = '1.12'
30		mybatisSpringBootStarterVersion = '1.3.0'
31		logbackVersion = '1.2.2'
32		slf4jVersion='1.7.25'
33		protocVersion='3.3.0'
34	}
35}
36
37subprojects {
38    apply plugin: 'java'
39    apply plugin: 'eclipse'
40    apply plugin: 'com.github.ben-manes.versions'
41    
42    configurations { 
43		all*.exclude group:'com.sun.xml.bind',module:'jaxb-impl'
44		all*.exclude group:'xml-apis',module:'xml-apis'
45		all*.exclude group:'stax',module:'stax-api'
46		all*.exclude group:'org.slf4j',module:'slf4j-log4j12'
47		all*.exclude group:'commons-logging'
48		all*.exclude group:'c3p0'
49    }
50    sourceSets.main.resources.srcDirs += "src/main/java"
51    sourceSets.test.resources.srcDirs += "src/test/java"
52    compileJava {
53        sourceCompatibility=1.8
54        targetCompatibility=1.8
55        options.encoding='UTF-8'
56   }
57   compileTestJava {
58        sourceCompatibility=1.8
59        targetCompatibility=1.8
60        options.encoding='UTF-8'
61   }
62    
63   tasks.eclipse.dependsOn cleanEclipse
64
65}
  • 上面根项目的构建脚本foo-svc/build.gradle中主要定义了项目通用的依赖版本和一些插件信息,并对项目的构建行为做了一些限制。

子项目foo-svc-proto

foo-svc/foo-svc-proto/build.gradle:

 1apply plugin: 'com.google.protobuf'
 2apply plugin: 'maven-publish'
 3
 4dependencies {
 5    compile "io.grpc:grpc-netty:${grpcVersion}"
 6    compile "io.grpc:grpc-protobuf:${grpcVersion}"
 7    compile "io.grpc:grpc-stub:${grpcVersion}"
 8}
 9
10
11sourceSets {
12   main {
13   	java {
14   		srcDir 'build/generated/source/proto/main/java'
15   		srcDir 'build/generated/source/proto/main/grpc'
16   	}
17   }
18}
19
20
21protobuf {
22  protoc {
23    artifact = "com.google.protobuf:protoc:${protocVersion}"
24  }
25  plugins {
26    grpc {
27      artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
28    }
29  }
30  generateProtoTasks {
31    all()*.plugins {
32      grpc {
33        option 'enable_deprecated=false'
34      }
35    }
36  }
37}
38
39jar {
40    exclude("*.proto")
41}
42
43task sourceJar(type: Jar) {
44    from sourceSets.main.allJava
45}
46
47publishing {
48    repositories {
49        maven {
50            url "http://192.168.1.10:8081/nexus/content/repositories/snapshots/"
51        }
52    }
53    publications {
54        mavenJava(MavenPublication) {
55            from components.java
56            artifact sourceJar {
57                classifier "sources"
58            }
59        }
60    }
61}
62
63tasks.eclipse.dependsOn compileJava

foo-svc/foo-svc-proto/build.gradle中使用com.google.protobuf插件将src/main/proto中的proto文件编译生成gRPC的java代码。 生成的代码位于build/generated/source/proto/main/javabuild/generated/source/proto/main/grpc这两个目录中。 同时将这两个目录添加到了sourceSets.main.java中。使用maven-publish插件可以将构建生成的jar包发布到团队的私有maven仓库中,这样gRPC服务的java客户端可以直接使用仓库中的jar。

子项目foo-svc-server

foo-svc/foo-svc-server/build.gradle:

 1apply plugin: 'org.springframework.boot'
 2
 3dependencies {
 4	compile project(':foo-svc-proto')
 5	
 6	compile 'org.springframework.boot:spring-boot-starter'
 7	compile 'org.springframework.boot:spring-boot-starter-data-redis'
 8	compile "net.devh:grpc-server-spring-boot-starter:$grpcSpringBootStarterVersion"
 9	compile "org.mybatis.spring.boot:mybatis-spring-boot-starter:$mybatisSpringBootStarterVersion"
10	compile "com.github.ulisesbocchio:jasypt-spring-boot-starter:$jasyptSpringBootStarterVersion"
11	compile 'com.fasterxml.jackson.core:jackson-databind'
12	
13	runtime 'mysql:mysql-connector-java' 
14	runtime 'redis.clients:jedis:2.9.0'
15	runtime 'org.apache.commons:commons-pool2:2.4.2'
16	
17	testCompile('org.springframework.boot:spring-boot-starter-test')
18}

从子项目foo-svc-server的构建脚本可以看出,foo-svc-server就是一个简单的spring-boot项目。 这里使用了yidongnan/grpc-spring-boot-starter这个项目简化了Spring-boot和gRPC整合的代码。

 1@GrpcService(Xxx1ServiceGrpc.class)
 2public class Xxx1ServiceService extends Xxx1ServiceImplBase {
 3	
 4	
 5	@Override
 6	public void sayHello(SayHelloReq request, StreamObserver<SayHelloResp> responseObserver) {
 7		SayHelloResp.Builder respBuilder = SayHelloResp.newBuilder();
 8		......
 9		responseObserver.onNext(respBuilder.build());
10		responseObserver.onCompleted();
11	}

服务的客户端

 1import org.springframework.boot.SpringApplication;
 2import org.springframework.boot.autoconfigure.SpringBootApplication;
 3import org.springframework.context.annotation.Bean;
 4
 5import com.frognew.svc.foo.proto.Xxx1ServiceGrpc;
 6
 7import io.grpc.Channel;
 8import net.devh.springboot.autoconfigure.grpc.client.GrpcClient;
 9
10@SpringBootApplication
11public class ClientDemoApplication {
12
13    @GrpcClient("foo-svc-server")
14    private Channel xxx1SvcChannel;
15	
16	@Bean
17	public Xxx1ServiceGrpc.Xxx1ServiceBlockingStub xxx1ServiceBlockingStub() {
18		return Xxx1ServiceGrpc.newBlockingStub(xxx1SvcChannel);
19	}
20	
21	public static void main(String[] args) {
22		SpringApplication.run(ApidemoApplication.class, args);
23	}
24}

将上面的Xxx1ServiceBlockingStub注入到业务组件中直接使用即可。

上面@GrpcClient("foo-svc-server")中会读取客户端项目application.yml中的:

1grpc:
2  client:
3    foo-svc-server:
4      host:
5        - 127.0.0.1
6      port:
7        - 9090

获取gRPC服务的地址和端口。我们的服务最终是发布到Kubernetes中,上面的host和port最终设置成Kubernetes中对应Server的名称和端口即可,服务发现机制由Kubernetes提供。

参考