之前一直用 Go 来写gRPC的服务,简单而直接。最近工作涉及用 Java 开发几个gRPC的服务,我们知道Java中有很多框架和Magical,所以这几个服务还是希望以Java的风格来写,因此打算写几篇文章对此做个整理。本篇做为开篇 ,先简单从快速开始一个项目原型开始写起。
开发环境
JDK 1.8
gradle 3.5
gRPC 1.30
protobuf 3.2.0
初始化项目原型
新建项目目录 grpc-java-demo :
mkdir grpc-java-demo
使用gradle生成项目原型:
cd grpc-java-demo
gradle init --type=java-application
修改生成构建脚本build.gradle:
apply plugin: 'java'
apply plugin: 'com.google.protobuf'
apply plugin: 'eclipse'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.1'
}
}
repositories {
mavenCentral()
}
sourceSets {
main {
java {
srcDir 'build/generated/source/proto/main/java'
srcDir 'build/generated/source/proto/main/grpc'
}
}
}
compileJava {
sourceCompatibility=1.8
targetCompatibility=1.8
options.encoding='UTF-8'
}
compileTestJava {
sourceCompatibility=1.8
targetCompatibility=1.8
options.encoding='UTF-8'
}
configurations {
all*.exclude group:'com.sun.xml.bind',module:'jaxb-impl'
all*.exclude group:'xml-apis',module:'xml-apis'
all*.exclude group:'stax',module:'stax-api'
all*.exclude group:'org.slf4j',module:'slf4j-log4j12'
all*.exclude group:'commons-logging'
}
ext {
grpcVersion= '1.3.0'
logbackVersion = '1.2.2'
slf4jVersion='1.7.25'
}
dependencies {
compile "io.grpc:grpc-netty:${grpcVersion}"
compile "io.grpc:grpc-protobuf:${grpcVersion}"
compile "io.grpc:grpc-stub:${grpcVersion}"
compile "org.slf4j:slf4j-api:$slf4jVersion"
compile "ch.qos.logback:logback-classic:$logbackVersion"
runtime "org.slf4j:jcl-over-slf4j:$slf4jVersion"
runtime "org.slf4j:log4j-over-slf4j:$slf4jVersion"
runtime "org.slf4j:jul-to-slf4j:$slf4jVersion"
testCompile 'junit:junit:4.12'
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.2.0'
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all()*.plugins {
grpc {
option 'enable_deprecated=false'
}
}
}
}
tasks.eclipse.dependsOn compileJava
新建src/main/resources目录,在目录下创建logback的配置文件logback.xml:
grpc-java-demo
[%date{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] %level %logger - %message%n
]]>
删除src/main/java下的App.java文件和src/test/java下的AppTest.java文件。在src下新建名称为proto的源码目录,在此目录中新建helloworld.proto文件内容如下:
syntax = "proto3";
option java_multiple_files = true;
option java_package = "com.frognew.grpc.demo.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
运行 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:
package com.frognew.grpc.demo.helloworld;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
public class HelloWorldServer {
private static final Logger LOGGER = LoggerFactory.getLogger(HelloWorldServer.class);
private Server server;
private void start() throws IOException {
/* The port on which the server should run */
int port = 50051;
server = ServerBuilder.forPort(port)
.addService(new GreeterImpl())
.build()
.start();
LOGGER.info("Server started, listening on " + port);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
System.err.println("*** shutting down gRPC server since JVM is shutting down");
HelloWorldServer.this.stop();
System.err.println("*** server shut down");
}
});
}
private void stop() {
if (server != null) {
server.shutdown();
}
}
/**
* Await termination on the main thread since the grpc library uses daemon threads.
*/
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
/**
* Main launches the server from the command line.
*/
public static void main(String[] args) throws IOException, InterruptedException {
final HelloWorldServer server = new HelloWorldServer();
server.start();
server.blockUntilShutdown();
}
static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(HelloRequest req, StreamObserver responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
}
}
com.frognew.grpc.demo.helloworld.HelloWorldClient.java:
package com.frognew.grpc.demo.helloworld;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
public class HelloWorldClient {
private static final Logger LOGGER = LoggerFactory.getLogger(HelloWorldClient.class);
private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub blockingStub;
/** Construct client connecting to HelloWorld server at {@code host:port}. */
public HelloWorldClient(String host, int port) {
this(ManagedChannelBuilder.forAddress(host, port)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext(true));
}
/** Construct client for accessing RouteGuide server using the existing channel. */
HelloWorldClient(ManagedChannelBuilder> channelBuilder) {
channel = channelBuilder.build();
blockingStub = GreeterGrpc.newBlockingStub(channel);
}
public void shutdown() throws InterruptedException {
channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
}
/** Say hello to server. */
public void greet(String name) {
LOGGER.info("Will try to greet " + name + " ...");
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response;
try {
response = blockingStub.sayHello(request);
} catch (StatusRuntimeException e) {
LOGGER.warn("RPC failed: {}", e.getStatus());
return;
}
LOGGER.info("Greeting: " + response.getMessage());
}
/**
* Greet server. If provided, the first element of {@code args} is the name to use in the
* greeting.
*/
public static void main(String[] args) throws Exception {
HelloWorldClient client = new HelloWorldClient("localhost", 50051);
try {
/* Access a service running on the local machine on port 50051 */
String user = "world";
if (args.length > 0) {
user = args[0]; /* Use the arg as the name to greet if provided */
}
client.greet(user);
} finally {
client.shutdown();
}
}
}
运行Server输出:
Server started, listening on 50051
运行Client,向Server发送RPC请求,输出:
Will try to greet world ...
Greeting: Hello world
总结
本篇主要是根据gRPC Java官方示例代码演示怎么快速使用Gradle快速创建gRPC Java项目的框架原型,因为gRPC HelloWorld的代码十分简单,就不展开多写了。
参考