安装步骤略
下载grpc-java后,执行一下命令
cd grpc-java/compile
# 编译插件
../gradlew java_pluginExecutable
# 测试插件
../gradlew test
若看到BUILD SUCCESSFUL字样,则编译成功
syntax = "proto3";
option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
# java_package 用来指定生成的java类所在的包,如果该proto文件被指定编译为其他语言时,java参数失效,默认包为package
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";
package helloworld;
# 定义服务
service Greeter {
# 定义grpc方法,并且制订消息的请求类型和反回类型
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
# 消息类型都是protocol buffer的消息格式
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
grpc可以有四种服务类型
1. 简单的rcp模式,client端使用stub发送请求到服务端,然后进入等待模式,直到响应发送回client端
rpc GetFeature(Point) returns (Feature) {}
2. 服务端流式rpc,client端发送请求到服务端,返回消息序列的流数据(gets a stream to read a sequence of messages back),client从返回的流数据读取消息,直至不再有数据,如果指定服务端返回流数据类型,需使用stream关键字
rpc ListFeatures(Rectangle) returns (stream Feature) {}
3. 客户端流式rpc,client使用指定的stream方式写消息序列并发送给服务端,一旦客户端写消息完成,会等待服务端全部读取并做出响应
rpc RecordRoute(stream Point) returns (RouteSummary) {}
4. 双向流式rpc,双向流式操作独立,客户端和服务端可以以任何序列随意读写,比如,服务端可以在等待client端发送所有消息之前写响应消息,或者服务端可以选择读一个消息写一个消息或者其他的读写组合。消息的顺序都是被保存的
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
使用protocol编译器protoc和指定的grpc的一个java插件。
使用maven的protobuf插件生成相关代码
命令行:
protoc --java_out=./java/ ./proto/hello.proto
生成grpc通讯代码
protoc --plugin=protoc-gen-grpc-java=/Users/liuyu9/Documents/personal/golang/src/github.com/grpc-java/compiler/build/exe/java_plugin/protoc-gen-grpc-java --grpc-java_out=./java ./proto/hello.proto
文件列表如下:
# GreeterGrpc包含服务端和客户端要实现的一些基本类
-rw-r--r-- 1 root staff 7352 Oct 25 18:48 GreeterGrpc.java
# 包含了protocol buffer发送、序列化、反序列化我们请求和响应代码
-rw-r--r-- 1 root staff 16207 Oct 25 18:53 HelloReply.java
-rw-r--r-- 1 root staff 501 Oct 25 18:53 HelloReplyOrBuilder.java
-rw-r--r-- 1 root staff 16192 Oct 25 18:53 HelloRequest.java
-rw-r--r-- 1 root staff 493 Oct 25 18:53 HelloRequestOrBuilder.java
-rw-r--r-- 1 root staff 3011 Oct 25 18:53 HelloWorldProto.java
编写服务端文件的工作有两部分:
1. 重写proto文件定义的服务生成的服务基类,确定服务的工作内容
2. 运行grpc服务,监听客户端请求并做出响应
package com.weibo.dorteam.grpc;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.stub.StreamObserver;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import java.io.IOException;
import java.util.logging.Logger;
public class helloServer {
private static final Logger logger = Logger.getLogger(helloServer.class.getName());
private int port = 50051;
private Server server;
private void start() throws IOException {
// 使用ServerBuilder来构建和启动服务,通过使用forPort方法来指定监听的地址和端口
// 创建一个实现方法的服务GreeterImpl的实例,并通过addService方法将该实例纳入
// 调用build() start()方法构建和启动rpcserver
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");
helloServer.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 helloServer server = new helloServer();
server.start();
server.blockUntilShutdown();
}
// 我们的服务GreeterImpl继承了生成抽象类GreeterGrpc.GreeterImplBase,实现了服务的所有方法
private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
@Override
public void sayHello(HelloRequest req, StreamObserver responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
// 使用响应监视器的onNext方法返回HelloReply
responseObserver.onNext(reply);
// 使用onCompleted方法指定本次调用已经完成
responseObserver.onCompleted();
}
}
}
消息类型,StreamObserver一个响应监视器,是服务器调生成响应结果使用的特定接口
为了调用服务端的方法,首先需要创建一个或两个stub:
阻塞式stub:rpc调用会阻塞等待服务端响应后返回消息或异常信息
非阻塞式stub:非阻塞调用方式,响应异步返回
package com.weibo.dorteam.grpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import io.grpc.examples.helloworld.GreeterGrpc;
import io.grpc.examples.helloworld.HelloReply;
import io.grpc.examples.helloworld.HelloRequest;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
public class helloClient {
private static final Logger logger = Logger.getLogger(helloClient.class.getName());
private final ManagedChannel channel;
private final GreeterGrpc.GreeterBlockingStub blockingStub;
/** Construct client connecting to HelloWorld server at {@code host:port}. */
// 首先,我们需要为stub创建一个grpc的channel,指定我们连接服务端的地址和端口
// 使用ManagedChannelBuilder方法来创建channel
public helloClient(String host, int port) {
channel = ManagedChannelBuilder.forAddress(host, port)
// Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
// needing certificates.
.usePlaintext(true)
.build();
// 使用我们从proto文件生成的GreeterGrpc类提供的newBlockingStub方法指定channel创建stubs
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 + " ...");
// 创建并定制protocol buffer对象,使用该对象调用服务端的sayHello方法,获得response
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
HelloReply response;
try {
response = blockingStub.sayHello(request);
// 如果有异常发生,则异常被编码成Status,可以从StatusRuntimeException异常中捕获
} catch (StatusRuntimeException e) {
logger.log(Level.WARNING, "RPC failed: {0}", 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 {
helloClient client = new helloClient("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();
}
}
}
服务端运行:
# 不需要参数
mvn exec:java -Dexec.mainClass="com.weibo.dorteam.grpc.helloServer"
# 需要参数
mvn exec:java -Dexec.mainClass="com.weibo.dorteam.grpc.helloServer" -Dexec.args="arg0 arg1 arg2"
# 传递classpath
mvn exec:java -Dexec.mainClass="com.weibo.dorteam.grpc.helloServer" -Dexec.classpathScope=runtime
客户端运行:
mvn exec:java -Dexec.mainClass="com.weibo.dorteam.grpc.helloClient"
<dependency>
<groupId>io.grpcgroupId>
<artifactId>grpc-allartifactId>
<version>1.0.1version>
dependency>
<build>
<extensions>
<extension>
<groupId>kr.motd.mavengroupId>
<artifactId>os-maven-pluginartifactId>
<version>1.4.1.Finalversion>
extension>
extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.pluginsgroupId>
<artifactId>protobuf-maven-pluginartifactId>
<version>0.5.0version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.0.0:exe:${os.detected.classifier}protocArtifact>
<pluginId>grpc-javapluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:0.13.2:exe:${os.detected.classifier}pluginArtifact>
<protocExecutable>/Users/peng/protoc-3.0.0-beta-2-osx-x86_64/protocprotocExecutable>
configuration>
<executions>
<execution>
<goals>
<goal>compilegoal>
<goal>compile-customgoal>
goals>
execution>
executions>
plugin>
plugins>
build>
注:protoc的版本需要匹配protobuf-java,如果不是直接依赖protobuf-java,则依赖grpc时,间接依赖protobuf-java,版本为3.0.0
同上
mvn generated-sources
默认生成路径为${basedir}/target/generated-sources
同上
同上
准备一份proto文件
syntax = "proto3";
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
docker run -i -t -v /data0/liuyu9/test/grpc/python-grpc:/python-grpc grpc/python python -m grpc.tools.protoc -I /python-grpc --python_out=/python-grpc --grpc_python_out=/python-grpc /python-grpc/hello.proto
指定protos文件的路径,以及输出路径,并指定proto 文件
from __future__ import print_function
import grpc
import hello_pb2
def run():
channel = grpc.insecure_channel('**.**.**.**:50051')
stub = hello_pb2.GreeterStub(channel)
response = stub.SayHello(hello_pb2.HelloRequest(name='you'))
print("Greeter client received: " + response.message)
if __name__ == '__main__':
run()
docker run -i -t -v /data0/liuyu9/test/grpc/python-grpc:/python-grpc grpc/python python /python-grpc/helloClient.py
输出结果为:hello you
若不适用grpc/python的镜像,物理机的python需要安装grpc,安装过程中,版本及各种依赖包的冲突非常麻烦。。。没有耐心了
I. pom execution报错:
感觉像是包依赖冲突的问题。。。未解
Multiple annotations found at this line:
- Execution default of goal org.xolstice.maven.plugins:protobuf-maven-plugin:0.5.0:compile-custom failed: An API
incompatibility was encountered while executing org.xolstice.maven.plugins:protobuf-maven-plugin:0.5.0:compile-custom:
java.lang.NoSuchMethodError: org.codehaus.plexus.util.DirectoryScanner.setupMatchPatterns()V
----------------------------------------------------- realm = plugin>org.xolstice.maven.plugins:protobuf-maven-plugin:
0.5.0--726595060 strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy urls[0] = file:/Users/liuyu9/.m2/
repository/org/xolstice/maven/plugins/protobuf-maven-plugin/0.5.0/protobuf-maven-plugin-0.5.0.jar urls[1] = file:/Users/
liuyu9/.m2/repository/org/sonatype/sisu/sisu-inject-bean/1.4.2/sisu-inject-bean-1.4.2.jar urls[2] = file:/Users/liuyu9/.m2/
repository/org/sonatype/sisu/sisu-guice/2.1.7/sisu-guice-2.1.7-noaop.jar urls[3] = file:/Users/liuyu9/.m2/repository/com/
google/guava/guava/18.0/guava-18.0.jar urls[4] = file:/Users/liuyu9/.m2/repository/org/apache/maven/plugin-tools/
maven-plugin-annotations/3.4/maven-plugin-annotations-3.4.jar urls[5] = file:/Users/liuyu9/.m2/repository/org/codehaus/
plexus/plexus-utils/3.0.22/plexus-utils-3.0.22.jar urls[6] = file:/Users/liuyu9/.m2/repository/org/codehaus/plexus/plexus-
component-annotations/1.6/plexus-component-annotations-1.6.jar urls[7] = file:/Users/liuyu9/.m2/repository/org/
sonatype/aether/aether-util/1.7/aether-util-1.7.jar urls[8] = file:/Users/liuyu9/.m2/repository/org/codehaus/plexus/plexus-
interpolation/1.14/plexus-interpolation-1.14.jar urls[9] = file:/Users/liuyu9/.m2/repository/org/sonatype/plexus/plexus-
sec-dispatcher/1.3/plexus-sec-dispatcher-1.3.jar urls[10] = file:/Users/liuyu9/.m2/repository/org/sonatype/plexus/plexus-
cipher/1.4/plexus-cipher-1.4.jar Number of foreign imports: 4 import: Entry[import org.sonatype.plexus.build.incremental
from realm ClassRealm[plexus.core, parent: null]] import: Entry[import org.codehaus.plexus.util.Scanner from realm
ClassRealm[plexus.core, parent: null]] import: Entry[import org.codehaus.plexus.util.AbstractScanner from realm
ClassRealm[plexus.core, parent: null]] import: Entry[import from realm ClassRealm[project>grpcTest:grpcTest:0.0.1-
SNAPSHOT, parent: ClassRealm[maven.api, parent: null]]] -----------------------------------------------------
(org.xolstice.maven.plugins:protobuf-maven-plugin:0.5.0:compile-custom:default:generate-sources)
- Execution default of goal org.xolstice.maven.plugins:protobuf-maven-plugin:0.5.0:compile failed: An API
incompatibility was encountered while executing org.xolstice.maven.plugins:protobuf-maven-plugin:0.5.0:compile:
java.lang.NoSuchMethodError: org.codehaus.plexus.util.DirectoryScanner.setupMatchPatterns()V
----------------------------------------------------- realm = plugin>org.xolstice.maven.plugins:protobuf-maven-plugin:
0.5.0--726595060 strategy = org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy urls[0] = file:/Users/liuyu9/.m2/
repository/org/xolstice/maven/plugins/protobuf-maven-plugin/0.5.0/protobuf-maven-plugin-0.5.0.jar urls[1] = file:/Users/
liuyu9/.m2/repository/org/sonatype/sisu/sisu-inject-bean/1.4.2/sisu-inject-bean-1.4.2.jar urls[2] = file:/Users/liuyu9/.m2/
repository/org/sonatype/sisu/sisu-guice/2.1.7/sisu-guice-2.1.7-noaop.jar urls[3] = file:/Users/liuyu9/.m2/repository/com/
google/guava/guava/18.0/guava-18.0.jar urls[4] = file:/Users/liuyu9/.m2/repository/org/apache/maven/plugin-tools/
maven-plugin-annotations/3.4/maven-plugin-annotations-3.4.jar urls[5] = file:/Users/liuyu9/.m2/repository/org/codehaus/
plexus/plexus-utils/3.0.22/plexus-utils-3.0.22.jar urls[6] = file:/Users/liuyu9/.m2/repository/org/codehaus/plexus/plexus-
component-annotations/1.6/plexus-component-annotations-1.6.jar urls[7] = file:/Users/liuyu9/.m2/repository/org/
sonatype/aether/aether-util/1.7/aether-util-1.7.jar urls[8] = file:/Users/liuyu9/.m2/repository/org/codehaus/plexus/plexus-
interpolation/1.14/plexus-interpolation-1.14.jar urls[9] = file:/Users/liuyu9/.m2/repository/org/sonatype/plexus/plexus-
sec-dispatcher/1.3/plexus-sec-dispatcher-1.3.jar urls[10] = file:/Users/liuyu9/.m2/repository/org/sonatype/plexus/plexus-
cipher/1.4/plexus-cipher-1.4.jar Number of foreign imports: 4 import: Entry[import org.sonatype.plexus.build.incremental
from realm ClassRealm[plexus.core, parent: null]] import: Entry[import org.codehaus.plexus.util.Scanner from realm
ClassRealm[plexus.core, parent: null]] import: Entry[import org.codehaus.plexus.util.AbstractScanner from realm
ClassRealm[plexus.core, parent: null]] import: Entry[import from realm ClassRealm[project>grpcTest:grpcTest:0.0.1-
SNAPSHOT, parent: ClassRealm[maven.api, parent: null]]] -----------------------------------------------------
(org.xolstice.maven.plugins:protobuf-maven-plugin:0.5.0:compile:default:generate-sources)
原因:M2E的bug eclipse版本过低
https://github.com/sergei-ivanov/maven-protoc-plugin/issues/10
https://bugs.eclipse.org/bugs/show_bug.cgi?id=422106
更新eclipse为最新版,删除所有.project文件夹,重新更新project
II. 编译protoc-gen-grpc-java插件时报错:
fatal error: ‘google/protobuf/io/zero_copy_stream.h’ file not found
原因:找不到头文件,因为macos系统没有将/usr/local作为搜索头文件和库的默认路径
解决:export CXXFLAGS=”-I/usr/local/include” LDFLAGS=”-L/usr/local/lib”
III. 生成的grpc通讯代码GreeterGrpc编译报错
报错内容:
Multiple markers at this line
- Method breakpoint:GreeterGrpc$GreeterImplBase [entry] - bindService()
- The method bindService() of type GreeterGrpc.GreeterImplBase must override a superclass
method
- implements io.grpc.BindableService.bindService
Multiple markers at this line
- The method invoke(Req, StreamObserver) of type GreeterGrpc.MethodHandlers
must override a superclass method
- implements io.grpc.stub.ServerCalls.UnaryRequestMethod.invoke
Multiple markers at this line
- implements io.grpc.stub.ServerCalls.StreamingRequestMethod.invoke
- The method invoke(StreamObserver) of type GreeterGrpc.MethodHandlers must
override a superclass method
原因:同问题I,由于M2E插件版本过低有bug
解决办法:更新M2E,或者更新eclipse为最新版
https://github.com/grpc/grpc-java/tree/master/compiler
http://www.grpc.io/docs/guides/