為什么去使用gRPC
使用gRPC,我們可以在.proto文件中定義我們的服務(wù),并以任何gRPC支持的語(yǔ)言實(shí)現(xiàn)客戶(hù)端和服務(wù)器。 使用gRPC還具有protocol buffer的所有優(yōu)點(diǎn),包括高效的序列化,簡(jiǎn)單的IDL和簡(jiǎn)單的接口更新。
自己認(rèn)為rpc框架的優(yōu)點(diǎn)主要在于,性能好(使用高效的序列化框架,tcp傳輸協(xié)議),跨語(yǔ)言(服務(wù)端和客戶(hù)端可以使用不同的語(yǔ)言開(kāi)發(fā))。
引入依賴(lài)
使用maven構(gòu)建項(xiàng)目,maven依賴(lài):
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.4.0</version>
</dependency>
使用Gradle構(gòu)建項(xiàng)目,Gradle依賴(lài):
compile 'io.grpc:grpc-netty:1.4.0'
compile 'io.grpc:grpc-protobuf:1.4.0'
compile 'io.grpc:grpc-stub:1.4.0'
Android開(kāi)發(fā),使用grpc-okhttp代替grpc-netty,使用grpc-protobuf-lite,或者grpc-protobuf-nano代替grpc-protobuf
compile 'io.grpc:grpc-okhttp:1.4.0'
compile 'io.grpc:grpc-protobuf-lite:1.4.0'
compile 'io.grpc:grpc-stub:1.4.0'
使用插件根據(jù)指定的.proto文件生成代碼,要將.proto文件放在src/main/proto和src/test/proto目錄下,這樣插件就可以找到.proto文件并且生成代碼。
使用maven插件如下:
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.4.1.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
或者使用gradle插件:
apply plugin: 'java'
apply plugin: 'com.google.protobuf'
buildscript {
repositories {
mavenCentral()
}
dependencies {
// ASSUMES GRADLE 2.12 OR HIGHER. Use plugin version 0.7.5 with earlier
// gradle versions
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
}
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.2.0"
}
plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.4.0'
}
}
generateProtoTasks {
all()*.plugins {
grpc {}
}
}
}
在build.gradle中加入相應(yīng)的插件之后,idea出現(xiàn)gradle插件圖形顯示

如果要將.proto文件放在其他路徑,可以加入下面的指令:
sourceSets {
main {
proto {
// In addition to the default 'src/main/proto'
srcDir 'src/main/protobuf'
srcDir 'src/main/protocolbuffers'
// In addition to the default '**/*.proto' (use with caution).
// Using an extension other than 'proto' is NOT recommended,
// because when proto files are published along with class files, we can
// only tell the type of a file from its extension.
include '**/*.protodevel'
}
java {
...
}
}
test {
proto {
// In addition to the default 'src/test/proto'
srcDir 'src/test/protocolbuffers'
}
}
}
編寫(xiě).proto文件
在src/main/proto目錄下編寫(xiě)Student.proto文件
syntax = "proto3";
package com.zhihao.miao.proto;
option java_package = "com.zhihao.miao.proto";
option java_outer_classname ="StudentProto";
//將屬性生成多個(gè)文件,便于代碼管理
option java_multiple_files = true;
//定義rpc方法
service StudentService{
//一元RPC
rpc GetRealNameByUsername(MyRequest) returns (MyResponse){}
}
message MyRequest{
string username = 1;
}
message MyResponse{
string realname = 2;
}
使用gradle插件編譯
gradle generateProto

將生成的代碼copy到main/java的制定的包下,生成的類(lèi)有以下幾個(gè),MyRequest,MyRequestOrBuilder,MyResponse,MyResponseOrBuilder,StudentProto是GRPC消息傳輸?shù)腗essage,后面的一個(gè)StudentServiceGrpc類(lèi)是遠(yuǎn)程調(diào)用的接口,服務(wù)端要重寫(xiě).proto中的方法要實(shí)現(xiàn)其內(nèi)部類(lèi)StudentServiceImplBase是
編寫(xiě)服務(wù)器代碼
服務(wù)端業(yè)務(wù)接口實(shí)現(xiàn)
package com.zhihao.miao.gprc;
import com.zhihao.miao.proto.*;
import io.grpc.stub.StreamObserver;
public class StudentServiceImpl extends StudentServiceGrpc.StudentServiceImplBase{
//第一個(gè)對(duì)象是請(qǐng)求參數(shù)對(duì)象,第二個(gè)參數(shù)是向客戶(hù)端返回結(jié)果的對(duì)象,方法定義不返回值,而是通過(guò)responseObserver去返回值
@Override
public void getRealNameByUsername(MyRequest request, StreamObserver<MyResponse> responseObserver) {
System.out.println("接收到客戶(hù)端信息: "+request.getUsername());
//onNext,onError,onCompletedB表示方法都是回調(diào)方法
responseObserver.onNext(MyResponse.newBuilder().setRealname("王超").build());
//onCompletedB表示方法方法執(zhí)行完畢
responseObserver.onCompleted();
}
}
編寫(xiě)服務(wù)器代碼
編寫(xiě)服務(wù)器端代碼,參考快速入門(mén)的服務(wù)端代碼,HelloWorldServer.java類(lèi)
cd /Users/naeshihiroshi/study/studySummarize/netty/grpc-java/examples
cd src/main/java/io/grpc/examples/helloworld
參考
服務(wù)器端代碼:
import io.grpc.Server;
import io.grpc.ServerBuilder;
import java.io.IOException;
public class GrpcServer {
private Server server;
//編寫(xiě)服務(wù)啟動(dòng)方法
private void start() throws IOException{
//增加實(shí)際業(yè)務(wù)代碼的實(shí)列,如果使用spring的話(huà)可以直接將StudentServiceImpl對(duì)象注入進(jìn)來(lái)
this.server = ServerBuilder.forPort(8899).addService(new StudentServiceImpl()).build().start();
System.out.println("server start!");
//jvm回調(diào)鉤子的作用,Runtime.getRuntime()可以獲得java一些運(yùn)行期間的一些信息。
//不管程序是正常關(guān)閉還是異常終端,在jvm關(guān)閉的時(shí)候做一些清理工作
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("關(guān)閉jvm");
GrpcServer.this.stop();
System.out.println("服務(wù)關(guān)閉");
}));
System.out.println("執(zhí)行到這里");
}
//停止服務(wù)器
private void stop(){
if(null != this.server){
this.server.shutdown();
}
}
//服務(wù)器阻塞
private void blockUntilShutdown() throws InterruptedException {
if(null != this.server){
this.server.awaitTermination();
//this.server.awaitTermination(3000, TimeUnit.MILLISECONDS);
}
}
public static void main(String[] args) throws IOException,InterruptedException{
GrpcServer server = new GrpcServer();
server.start();;
server.blockUntilShutdown();
}
}
客戶(hù)端代碼:
import com.zhihao.miao.proto.*;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
import java.time.LocalDateTime;
import java.util.Iterator;
public class GrpcClient {
public static void main(String[] args) throws Exception{
ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost",8899)
//使用TLS來(lái)連接服務(wù)端
.usePlaintext(true).build();
//定義一個(gè)stub,用于實(shí)際和服務(wù)端連接的對(duì)象blockingStub
StudentServiceGrpc.StudentServiceBlockingStub blockingStub = StudentServiceGrpc.newBlockingStub(managedChannel);
MyResponse response =blockingStub.getRealNameByUsername(MyRequest.newBuilder()
.setUsername("chao.wang").build());
System.out.println(response.getRealname());
}
}
運(yùn)行客戶(hù)端與服務(wù)器端,服務(wù)器端控制臺(tái)打?。?/p>
server start!
執(zhí)行到這里
接收到客戶(hù)端信息: chao.wang
客戶(hù)端控制臺(tái)打?。?/p>
王超
Disconnected from the target VM, address: '127.0.0.1:63197', transport: 'socket'
Process finished with exit code 0
關(guān)閉服務(wù)器,服務(wù)器控制臺(tái)上打印,執(zhí)行jvm回調(diào)鉤子。:
關(guān)閉jvm
服務(wù)關(guān)閉