gRPC Java:快速开始和项目原型
2017-05-01
之前一直用Go来写gRPC的服务,简单而直接。 最近工作涉及用Java开发几个gRPC的服务,我们知道Java中有很多框架和Magical,所以这几个服务还是希望以Java的风格来写,因此打算写几篇文章对此做个整理。本篇做为开篇 ,先简单从快速开始一个项目原型开始写起。
开发环境 #
- JDK 1.8
- gradle 3.5
- gRPC 1.30
- protobuf 3.2.0
初始化项目原型 #
新建项目目录grpc-java-demo
:
1mkdir grpc-java-demo
使用gradle生成项目原型:
1cd grpc-java-demo
2gradle init --type=java-application
修改生成构建脚本build.gradle:
1apply plugin: 'java'
2apply plugin: 'com.google.protobuf'
3apply plugin: 'eclipse'
4
5buildscript {
6 repositories {
7 mavenCentral()
8 }
9 dependencies {
10 classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1'
11 }
12}
13
14repositories {
15 mavenCentral()
16}
17
18sourceSets {
19 main {
20 java {
21 srcDir 'build/generated/source/proto/main/java'
22 srcDir 'build/generated/source/proto/main/grpc'
23 }
24 }
25}
26
27compileJava {
28 sourceCompatibility=1.8
29 targetCompatibility=1.8
30 options.encoding='UTF-8'
31}
32compileTestJava {
33 sourceCompatibility=1.8
34 targetCompatibility=1.8
35 options.encoding='UTF-8'
36}
37
38configurations {
39 all*.exclude group:'com.sun.xml.bind',module:'jaxb-impl'
40 all*.exclude group:'xml-apis',module:'xml-apis'
41 all*.exclude group:'stax',module:'stax-api'
42 all*.exclude group:'org.slf4j',module:'slf4j-log4j12'
43 all*.exclude group:'commons-logging'
44}
45
46ext {
47 grpcVersion= '1.3.0'
48 logbackVersion = '1.2.2'
49 slf4jVersion='1.7.25'
50}
51
52dependencies {
53 compile "io.grpc:grpc-netty:${grpcVersion}"
54 compile "io.grpc:grpc-protobuf:${grpcVersion}"
55 compile "io.grpc:grpc-stub:${grpcVersion}"
56
57 compile "org.slf4j:slf4j-api:$slf4jVersion"
58 compile "ch.qos.logback:logback-classic:$logbackVersion"
59 runtime "org.slf4j:jcl-over-slf4j:$slf4jVersion"
60 runtime "org.slf4j:log4j-over-slf4j:$slf4jVersion"
61 runtime "org.slf4j:jul-to-slf4j:$slf4jVersion"
62 testCompile 'junit:junit:4.12'
63}
64
65protobuf {
66 protoc {
67 artifact = 'com.google.protobuf:protoc:3.2.0'
68 }
69 plugins {
70 grpc {
71 artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
72 }
73 }
74 generateProtoTasks {
75 all()*.plugins {
76 grpc {
77 option 'enable_deprecated=false'
78 }
79 }
80 }
81}
82
83tasks.eclipse.dependsOn compileJava
新建src/main/resources目录,在目录下创建logback的配置文件logback.xml:
1<?xml version="1.0" encoding="UTF-8"?>
2
3<configuration debug="false" scan="false" scanPeriod="30 seconds">
4
5 <contextName>grpc-java-demo</contextName>
6
7 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
8 <encoder>
9 <pattern>
10 <![CDATA[
11 [%date{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %level %logger - %message%n
12 ]]>
13 </pattern>
14 </encoder>
15 </appender>
16
17 <logger name="com.frognew.grpc.demo" level="INFO" additivity="false">
18 <appender-ref ref="STDOUT" />
19 </logger>
20
21 <root level="WARN">
22 <appender-ref ref="STDOUT" />
23 </root>
24
25</configuration>
删除src/main/java下的App.java文件和src/test/java下的AppTest.java文件。 在src下新建名称为proto的源码目录,在此目录中新建helloworld.proto文件内容如下:
1syntax = "proto3";
2
3option java_multiple_files = true;
4option java_package = "com.frognew.grpc.demo.helloworld";
5option java_outer_classname = "HelloWorldProto";
6option objc_class_prefix = "HLW";
7
8package helloworld;
9
10service Greeter {
11 rpc SayHello (HelloRequest) returns (HelloReply) {}
12}
13
14message HelloRequest {
15 string name = 1;
16}
17
18message HelloReply {
19 string message = 1;
20}
运行gradle eclipse
生成eclipse项目,此过程中会调用gradle的com.google.protobuf
插件基于src/main/proto中的PB文件生成Java代码。
将生成的项目导入eclipse中。
后续开发过程中,当PB文件发生修改时,只需要执行
gradle eclipse
再刷新eclipse项目即可。
HelloWorldServer和HelloWorldClient #
基于官方demo中的代码进行修改:
com.frognew.grpc.demo.helloworld.HelloWorldServer.java:
1package com.frognew.grpc.demo.helloworld;
2
3import java.io.IOException;
4
5import org.slf4j.Logger;
6import org.slf4j.LoggerFactory;
7
8import io.grpc.Server;
9import io.grpc.ServerBuilder;
10import io.grpc.stub.StreamObserver;
11
12public class HelloWorldServer {
13
14 private static final Logger LOGGER = LoggerFactory.getLogger(HelloWorldServer.class);
15
16 private Server server;
17
18 private void start() throws IOException {
19 /* The port on which the server should run */
20 int port = 50051;
21 server = ServerBuilder.forPort(port)
22 .addService(new GreeterImpl())
23 .build()
24 .start();
25 LOGGER.info("Server started, listening on " + port);
26 Runtime.getRuntime().addShutdownHook(new Thread() {
27 @Override
28 public void run() {
29 // Use stderr here since the logger may have been reset by its JVM shutdown hook.
30 System.err.println("*** shutting down gRPC server since JVM is shutting down");
31 HelloWorldServer.this.stop();
32 System.err.println("*** server shut down");
33 }
34 });
35 }
36
37 private void stop() {
38 if (server != null) {
39 server.shutdown();
40 }
41 }
42
43 /**
44 * Await termination on the main thread since the grpc library uses daemon threads.
45 */
46 private void blockUntilShutdown() throws InterruptedException {
47 if (server != null) {
48 server.awaitTermination();
49 }
50 }
51
52 /**
53 * Main launches the server from the command line.
54 */
55 public static void main(String[] args) throws IOException, InterruptedException {
56 final HelloWorldServer server = new HelloWorldServer();
57 server.start();
58 server.blockUntilShutdown();
59 }
60
61 static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
62
63 @Override
64 public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
65 HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
66 responseObserver.onNext(reply);
67 responseObserver.onCompleted();
68 }
69 }
70}
com.frognew.grpc.demo.helloworld.HelloWorldClient.java:
1package com.frognew.grpc.demo.helloworld;
2
3import java.util.concurrent.TimeUnit;
4
5import org.slf4j.Logger;
6import org.slf4j.LoggerFactory;
7
8import io.grpc.ManagedChannel;
9import io.grpc.ManagedChannelBuilder;
10import io.grpc.StatusRuntimeException;
11
12public class HelloWorldClient {
13 private static final Logger LOGGER = LoggerFactory.getLogger(HelloWorldClient.class);
14
15 private final ManagedChannel channel;
16 private final GreeterGrpc.GreeterBlockingStub blockingStub;
17
18 /** Construct client connecting to HelloWorld server at {@code host:port}. */
19 public HelloWorldClient(String host, int port) {
20 this(ManagedChannelBuilder.forAddress(host, port)
21 // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
22 // needing certificates.
23 .usePlaintext(true));
24 }
25
26 /** Construct client for accessing RouteGuide server using the existing channel. */
27 HelloWorldClient(ManagedChannelBuilder<?> channelBuilder) {
28 channel = channelBuilder.build();
29 blockingStub = GreeterGrpc.newBlockingStub(channel);
30 }
31
32 public void shutdown() throws InterruptedException {
33 channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
34 }
35
36 /** Say hello to server. */
37 public void greet(String name) {
38 LOGGER.info("Will try to greet " + name + " ...");
39 HelloRequest request = HelloRequest.newBuilder().setName(name).build();
40 HelloReply response;
41 try {
42 response = blockingStub.sayHello(request);
43 } catch (StatusRuntimeException e) {
44 LOGGER.warn("RPC failed: {}", e.getStatus());
45 return;
46 }
47 LOGGER.info("Greeting: " + response.getMessage());
48 }
49
50 /**
51 * Greet server. If provided, the first element of {@code args} is the name to use in the
52 * greeting.
53 */
54 public static void main(String[] args) throws Exception {
55 HelloWorldClient client = new HelloWorldClient("localhost", 50051);
56 try {
57 /* Access a service running on the local machine on port 50051 */
58 String user = "world";
59 if (args.length > 0) {
60 user = args[0]; /* Use the arg as the name to greet if provided */
61 }
62 client.greet(user);
63 } finally {
64 client.shutdown();
65 }
66 }
67}
运行Server输出:
1Server started, listening on 50051
运行Client,向Server发送RPC请求,输出:
1Will try to greet world ...
2Greeting: Hello world
总结 #
本篇主要是根据gRPC Java官方示例代码演示怎么快速使用Gradle快速创建gRPC Java项目的框架原型,因为gRPC HelloWorld的代码十分简单,就不展开多写了。