因业务需要,需要使用rpc协议通信,java平台使用Google发布的GRPC框架应该是最合适的了。本篇主要讲解GPRC框架Android客户端的使用,关于RPC协议本篇不做说明,自行百度和Google;关于grpc框架高级应用和具体原理后面补上。RPC协议将通信数据映射成方法和接口调用。
本篇客户端使用android平台,服务器端为java控制台程序。废话不多说,直接上代码:
1.Android客户端
首先定义通信协议,grpc采用protobuf协议序列化,创建通信协议文件ReqAndRes.proto文件。具体协议规则请自行百度和Google。内容如下:
syntax="proto3";
option java_package = "com.dawson.grpc";
message ReqInfo{
string msg=1;
}
message ResInfo{
string msg=1;
}
service UserService{
rpc request(ReqInfo) returns(ResInfo){}
rpc requestServerStream(ReqInfo) returns(stream ResInfo){}
rpc requestClientStream(stream ReqInfo) returns(ResInfo){}
rpc requestBothStream(stream ReqInfo) returns(stream ResInfo){}
}
在src/main目录下面创建proto文件夹存放协议文件,然后使用protobuf插件将协议自动生成java文件。工程build.gradle配置的dependencies节点添加插件依赖:
classpath ‘com.google.protobuf:protobuf-gradle-plugin:0.8.5’
module 下的build.gradle配置如下:
apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.dawson.grpc"
minSdkVersion 19
targetSdkVersion 27
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.4.0"
}
plugins {
javalite {
artifact = "com.google.protobuf:protoc-gen-javalite:3.0.0"
}
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.7.0'
}
}
generateProtoTasks {
all().each { task ->
task.plugins {
javalite {}
grpc {
// Options added to --grpc_out
option 'lite'
}
}
}
}
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'io.grpc:grpc-okhttp:1.15.1'
implementation 'io.grpc:grpc-protobuf-lite:1.15.1'
implementation 'io.grpc:grpc-stub:1.15.1'
implementation 'javax.annotation:javax.annotation-api:1.2'
}
然后build整个工程项目,会自动生成对应java文件。然后直接在页面调用,代码如下:
final ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 5, 1, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(10));
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
request();
requestServerStream();
requestClientStream();
requestBothStream();
}
});
init();
}
Channel channel;
private void init() {
try {
channel = ManagedChannelBuilder.forAddress("192.168.31.147", 6655)
.usePlaintext()
.build();
} catch (Exception ex) {
showRes(ex.getMessage());
}
}
private void request() {
executor.execute(new Runnable() {
@Override
public void run() {
try {
ReqAndRes.ReqInfo info = ReqAndRes.ReqInfo.newBuilder().setMsg("hello server").build();
UserServiceGrpc.UserServiceBlockingStub stub = UserServiceGrpc.newBlockingStub(channel);
ReqAndRes.ResInfo res = stub.request(info);
showRes(String.valueOf(res.getMsg()));
} catch (Exception ex) {
showRes(ex.getMessage());
}
}
});
}
private void requestServerStream() {
executor.execute(new Runnable() {
@Override
public void run() {
try {
ReqAndRes.ReqInfo info = ReqAndRes.ReqInfo.newBuilder().setMsg("hello server").build();
UserServiceGrpc.UserServiceBlockingStub stub = UserServiceGrpc.newBlockingStub(channel);
Iterator<ReqAndRes.ResInfo> ress = stub.requestServerStream(info);
while (ress.hasNext()) {
showRes(String.valueOf(ress.next().getMsg()));
}
} catch (Exception ex) {
showRes(ex.getMessage());
}
}
});
}
private void requestClientStream() {
executor.execute(new Runnable() {
@Override
public void run() {
try {
ReqAndRes.ReqInfo reqInfo1 = ReqAndRes.ReqInfo.newBuilder().setMsg("hello server 1").build();
ReqAndRes.ReqInfo reqInfo2 = ReqAndRes.ReqInfo.newBuilder().setMsg("hello server 2").build();
UserServiceGrpc.UserServiceStub stub = UserServiceGrpc.newStub(channel);
StreamObserver<ReqAndRes.ResInfo> response = new StreamObserver<ReqAndRes.ResInfo>() {
@Override
public void onNext(ReqAndRes.ResInfo resInfo) {
showRes(String.valueOf(resInfo.getMsg()));
}
@Override
public void onError(Throwable t) {
showRes(t.getMessage());
}
@Override
public void onCompleted() {
showRes("onCompleted");
}
};
StreamObserver<ReqAndRes.ReqInfo> request = stub.requestClientStream(response);
request.onNext(reqInfo1);
request.onNext(reqInfo2);
request.onCompleted();
} catch (Exception ex) {
showRes(ex.getMessage());
}
}
});
}
private Thread bsThread;
StreamObserver<ReqAndRes.ReqInfo> request;
private void requestBothStream() {
if (bsThread == null || request == null) {
bsThread = new Thread() {
@Override
public void run() {
try {
UserServiceGrpc.UserServiceStub stub = UserServiceGrpc.newStub(channel);
StreamObserver<ReqAndRes.ResInfo> response = new StreamObserver<ReqAndRes.ResInfo>() {
@Override
public void onNext(ReqAndRes.ResInfo res) {
showRes(String.valueOf(res.getMsg()));
}
@Override
public void onError(Throwable t) {
showRes(t.getMessage());
}
@Override
public void onCompleted() {
showRes("onCompleted");
}
};
request = stub.requestBothStream(response);
} catch (Exception ex) {
showRes(ex.getMessage());
}
}
};
bsThread.start();
}
if (bsThread == null || request == null) {
return;
}
ReqAndRes.ReqInfo info = ReqAndRes.ReqInfo.newBuilder().setMsg("hello server" + System.currentTimeMillis() % 1000).build();
request.onNext(info);
}
private void showRes(final String msg) {
Log.e("show_res", msg);
}
rpc支持四种模式调用:普通阻塞,客户端流式rpc ,服务端流式rpc,双向流式rpc。分别对应以上代码的的四个方法。其中在创建channel时一定要调用.usePlaintext(),否则会自动加密导致服务端报错:无法找到http2头。
2.服务端代码
服务端创建java控制台程序,使用maven构建,同理需要使用插件将协议文件生成java文件。pom.xml文件gprc相关配置如下:
<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.0</modelVersion>
<groupId>com.dawson</groupId>
<artifactId>maven_java_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>maven_java_demo</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<grpc.version>1.15.1</grpc.version>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.15.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.15.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.15.1</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-core</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-all</artifactId>
<version>1.15.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
</dependencies>
<build>
<finalName>com.ytf.rpc.demo</finalName>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.15.1:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
在src/main/目录下面创建proto文件夹用于存放协议文件,然后使用maven构建,会自动生成Java文件,生成的Java文件在target/generated-source/protobuf/grpc-java和target/generated-source/protobuf/Java目录下面。然后在main/java目录中创建相同的package,再将生成的文件从target中拷贝到main/java对应包中。服务端需要实现服务UserServiceGrpc.UserServiceImplBase,代码如下:
package com.dawson.maven_java_demo;
import com.dawson.grpc.UserServiceGrpc;
import com.dawson.grpc.ReqAndRes;
import com.dawson.grpc.ReqAndRes.ReqInfo;
import com.dawson.grpc.ReqAndRes.ResInfo;
import io.grpc.stub.StreamObserver;
public class UserImpl extends UserServiceGrpc.UserServiceImplBase {
@Override
public void request(ReqInfo request, StreamObserver<ResInfo> responseObserver) {
ReqAndRes.ResInfo res = ReqAndRes.ResInfo.newBuilder().setMsg("hello client").build();
responseObserver.onNext(res);
responseObserver.onCompleted();
}
@Override
public void requestServerStream(ReqInfo request, StreamObserver<ResInfo> responseObserver) {
ResInfo res1 = ResInfo.newBuilder().setMsg("hello client 1").build();
ResInfo res2 = ResInfo.newBuilder().setMsg("hello client 1").build();
responseObserver.onNext(res1);
responseObserver.onNext(res2);
responseObserver.onCompleted();
}
@Override
public StreamObserver<ReqInfo> requestClientStream(StreamObserver<ResInfo> responseObserver) {
return new StreamObserver<ReqInfo>() {
StringBuilder stringBuilder = new StringBuilder();
@Override
public void onNext(ReqInfo res) {
stringBuilder.append(res.getMsg());
stringBuilder.append(",");
}
@Override
public void onError(Throwable t) {
}
@Override
public void onCompleted() {
ResInfo resInfo = ResInfo.newBuilder().setMsg("hello client (" + stringBuilder.toString() + ")")
.build();
responseObserver.onNext(resInfo);
responseObserver.onCompleted();
}
};
}
@Override
public StreamObserver<ReqInfo> requestBothStream(StreamObserver<ResInfo> responseObserver) {
Thread backThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
ResInfo res = ResInfo.newBuilder().setMsg("hello client:" + System.currentTimeMillis() % 1000)
.build();
responseObserver.onNext(res);
}
}
});
backThread.start();
return new StreamObserver<ReqInfo>() {
@Override
public void onNext(ReqInfo value) {
ResInfo.Builder res = ResInfo.newBuilder().setMsg("hello client (" + value.getMsg() + ")");
responseObserver.onNext(res.build());
}
@Override
public void onError(Throwable t) {
}
@Override
public void onCompleted() {
}
};
}
}
服务端启动grpc服务代码如下:
public static void main(String[] args) {
System.out.println("Hello World!");
UserServiceGrpc.UserServiceImplBase userService = new UserImpl();
io.grpc.Server server = ServerBuilder.forPort(6655).addService(userService.bindService()).build();
try {
server.start();
System.out.println("Server start success on port:6655");
server.awaitTermination();
} catch (Exception e) {
e.printStackTrace();
}
}