声明: CSDN 原创投稿,未经许可,禁止任何形式的转载。
作者:赵昌峻,码农、全栈工程师,长期关注云计算、大数据、移动互联网相关技术,从事 IT 行业十载,有 5 年的企业级应用开发经历、3 年的云平台(openstack)研发经历,目前从事移动 APP 的研发,坚持每天快乐的编程。
在知乎上有这样一个问题:谁能用通俗的语言解释一下什么是 RPC 框架?,各路大神讲的都很到位,这里我就不详细解释了,gRPC 就是其中的一种 RPC 框架。
如上图所示,在 gRPC 中,客户端应用程序可以就像调用本地对象方法一样直接调用不同服务器上的应用程序方法,使您更容易创建分布式应用程序和服务。与许多 RPC 系统一样,gRPC 基于定义服务的思想,定义可以远程调用的方法,包括方法的参数和返回类型。在服务器端,服务器实现此接口并运行一个 gRPC 服务器来处理客户端调用。在客户端,客户端有一个 "存根 stub"(简称为某些语言的客户端),提供与服务器相同的方法。所有的数据传输都使用 protobuf。
我在 Protocol buffers(protobuf) 入门简介及性能分析中所用到的 protobuf 数据结构里增加如下内容:
- syntax = "proto3";
- option go_package = "user";
- option java_package = "com.ylifegroup.protobuf";
- enum PhoneType {
- HOME = 0;
- WORK = 1;
- OTHER = 2;
- }
- message ProtobufUser {
- int32 id = 1;
- string name = 2;
- message Phone {
- PhoneType phoneType = 1;
- string phoneNumber = 2;
- }
- repeated Phone phones = 3;
- }
- message AddPhoneToUserRequest {
- int32 uid = 1;
- PhoneType phoneType = 2;
- string phoneNumber = 3;
- }
- message AddPhoneToUserResponse {
- bool result = 1;
- }
- service PhoneService {
- rpc addPhoneToUser(AddPhoneToUserRequest) returns(AddPhoneToUserResponse);
- }
增加的内容很简单,定义了一个关于电话本的服务 PhoneBookService,服务包括一个把电话增加到指定联系人的方法 addPhoneToUser,同时定义了方法的参数 AddPhoneToUserRequest 和返回值 AddPhoneToUserResponse。
为了方便我们后面讲解微服务架构,从本节开始我们使用 Java 作为我们的开发语言。
我们的 Demo 将使用 Gradle 自动化构建工具,这里不会深入讲解 Gradle 的用法,建议没有使用过 Gradle 的可以从官方网站开始:https://gradle.org
可以使用下面的命令生成 Java 项目的基本结构:
- mkdir gRPCDemo
- cd gRPCDemo
- gradle init --type=java-library
初始化后,目录结构如下所示:
- └── gRPCDemo
- ├── build.gradle
- ├── gradle
- │ └── wrapper
- │ ├── gradle-wrapper.jar
- │ └── gradle-wrapper.properties
- ├── gradlew
- ├── gradlew.bat
- ├── settings.gradle
- └── src
- ├── main
- │ └── java
- │ └── Library.java
- └── test
- └── java
- └── LibraryTest.java
先删除没用的
和
- Library.java
。
- LibraryTest.java
然后修改配置文件
,如下所示:
- build.gradle
- /*
- * This build file was auto generated by running the Gradle 'init' task
- * by 'ChangjunZhao' at '16-12-27 下午3:25' with Gradle 3.1
- *
- * This generated file contains a sample Java project to get you started.
- * For more details take a look at the Java Quickstart chapter in the Gradle
- * user guide available at https://docs.gradle.org/3.1/userguide/tutorial_java_projects.html
- */
- // Apply the java plugin to add support for Java
- apply plugin: 'java'apply plugin: 'eclipse'apply plugin: 'com.google.protobuf'
- repositories {
- // Use 'jcenter' for resolving your dependencies.
- // You can declare any Maven/Ivy/file repository here.
- jcenter()
- }
- buildscript {
- repositories {
- mavenCentral()
- }
- dependencies {
- classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
- }
- }
- sourceSets {
- main {
- java {
- srcDir 'gen/main/java'srcDir 'gen/main/grpc'
- }
- proto {
- srcDir 'src/main/proto'
- }
- }
- }
- jar {
- from {
- configurations.runtime.collect {
- it.isDirectory() ? it: zipTree(it)
- }
- configurations.compile.collect {
- it.isDirectory() ? it: zipTree(it)
- }
- }
- manifest {
- attributes 'Main-Class': 'com.ylifegroup.protobuf.server.GRpcServer'
- }
- }
- protobuf {
- protoc {
- artifact = "com.google.protobuf:protoc:3.1.0"
- }
- plugins {
- grpc {
- artifact = 'io.grpc:protoc-gen-grpc-java:1.0.3'
- }
- }
- generatedFilesBaseDir = "$projectDir/gen/"generateProtoTasks {
- all() * .plugins {
- grpc {}
- }
- }
- }
- dependencies {
- compile 'io.grpc:grpc-netty:1.0.3'compile 'io.grpc:grpc-protobuf:1.0.3'compile 'io.grpc:grpc-stub:1.0.3'
- // The production code uses the SLF4J logging API at compile time
- compile 'org.slf4j:slf4j-api:1.7.21'
- // Declare the dependency for your favourite test framework you want to use in your tests.
- // TestNG is also supported by the Gradle Test task. Just change the
- // testCompile dependency to testCompile 'org.testng:testng:6.8.1' and add
- // 'test.useTestNG()' to your build script.
- testCompile 'junit:junit:4.12'
- }
- eclipse {
- classpath {
- defaultOutputDir = file('build/eclipse/bin')
- }
- }
- clean {
- delete protobuf.generatedFilesBaseDir
- }
我们使用了 grpc 官方提供的 gradle 插件
,它可是相当牛 B,可以帮我们自动生成 grpc 相关的代码。
- com.google.protobuf
为了完成服务器端的开发,增加了 grpc 相关的三个依赖:
- io.grpc:grpc-netty:1.0.3
- io.grpc:grpc-protobuf:1.0.3
- io.grpc:grpc-stub:1.0.3
在 protobuf 的配置部分,我们使用 grpc 代码生成的插件,以便自动帮我们生成 grpc 服务器端的代码(接口),具体的实现还需要我们自己去搞定。
在源文件配置部分
,我们指定了 proto 文件的目录
- sourceSets
,同时把 protobuf 自动生成代码所在两个目录 (默认)
- src/main/proto
,
- gen/main/java
增加到 Java 的源文件目录,这样在编译的时候才会编译这两个目录下的 Java 类。
- gen/main/grpc
OK,基本配置就这些,把上一节的 gRPC 服务定义文件
放到我们项目的
- phonebook.proto
目录(如果不存在自己手动创建)下,执行如下命令:
- src/main/proto
- . / gradlew build
gradle 会帮你自动生了 protobuf 和 grpc 相关的文件到如下目录:
- ├── gen
- │ └── main
- │ ├── grpc
- │ │ └── com
- │ │ └── ylifegroup
- │ │ └── protobuf
- │ │ └── PhoneServiceGrpc.java
- │ └── java
- │ └── com
- │ └── ylifegroup
- │ └── protobuf
- │ └── Phonebook.java
OK,剩下的工作大部分需要手动完成了,执行如下命令,生成 Eclipse 相关的配置文件:
- . / gradlew eclipse
然后用 Eclipse 打开 gRPCDemo 项目。
我们新建三个 package:
- com.ylifegroup.protobuf.service //用于放gRPC服务的实现类
- com.ylifegroup.protobuf.server //用于放gRPC服务器的实现类
- com.ylifegroup.protobuf.client //用于放gRPC客户端demo的相关类。
首先我们需要写代码来实现将电话增加到用户的逻辑,我们在 com.ylifegroup.protobuf.service 包下新建一个类叫 PhoneServiceImp,它只需要继承 protoc-gen-grpc 插件帮我们自己生成的一个 grpc 实现类 PhoneServiceGrpc.PhoneServiceImplBase,并实现相关方法即可,所有代码如下所示:
- package com.ylifegroup.protobuf.service;
- import com.ylifegroup.protobuf.PhoneServiceGrpc;
- import com.ylifegroup.protobuf.Phonebook.AddPhoneToUserRequest;
- import com.ylifegroup.protobuf.Phonebook.AddPhoneToUserResponse;
- import io.grpc.stub.StreamObserver;
- public class PhoneServiceImp extends PhoneServiceGrpc.PhoneServiceImplBase {
- @Override public void addPhoneToUser(AddPhoneToUserRequest request, StreamObserver < AddPhoneToUserResponse > responseObserver) {
- // TODO Auto-generated method stub
- AddPhoneToUserResponse response = null;
- if (request.getPhoneNumber().length() == 11) {
- System.out.printf("uid = %s , phone type is %s, nubmer is %s\n", request.getUid(), request.getPhoneType(), request.getPhoneNumber());
- response = AddPhoneToUserResponse.newBuilder().setResult(true).build();
- } else {
- System.out.printf("The phone nubmer %s is wrong!\n", request.getPhoneNumber());
- response = AddPhoneToUserResponse.newBuilder().setResult(false).build();
- }
- responseObserver.onNext(response);
- responseObserver.onCompleted();
- }
- }
代码很简单,我们只是检查手机号是不是 11 位,如果是把客户端的请求参数打印出来,给客户端返回 true,如果不是 11 位,提示手机号错误,给客户端返回 false。
代码很简单,这里就不详细解释了。
接下来,我们需要将我们实现的服务发布成 grpc 服务,这里有很多实现方法,我们就使用 grpc 官方提供的用 netty 实现服务器代码,还记得在 gradle 的配置文件里我们增加了
依赖吗?就是用在这的。
- io.grpc:grpc-netty:1.0.3
在 com.ylifegroup.protobuf.server 包下新建 GRpcServer 类,代码如下所示(大部分代码参考 grpc 官方的 helloworld):
- package com.ylifegroup.protobuf.server;
- import io.grpc.Server;
- import io.grpc.ServerBuilder;
- import java.io.IOException;
- import java.util.logging.Logger;
- import com.ylifegroup.protobuf.service.PhoneServiceImp;
- public class GRpcServer {
- private static final Logger logger = Logger.getLogger(GRpcServer.class.getName());
- 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 PhoneServiceImp()).build().start();
- logger.info("Server started, listening on " + port);
- Runtime.getRuntime().addShutdownHook(new Thread() {@Override public void run() {
- System.err.println("*** shutting down gRPC server since JVM is shutting down");
- GRpcServer.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 GRpcServer server = new GRpcServer();
- server.start();
- server.blockUntilShutdown();
- }
- }
详细的我就不解释了,一看代码就能明白,只需要把我们的电话本服务 PhoneServiceImp 增加到 grpc 服务器,然后就能对外提供电话本的服务了。
客户端的实现也很简单,在 com.ylifegroup.protobuf.client 包下新建 GRpcClient 类,代码如下:
- package com.ylifegroup.protobuf.client;
- import io.grpc.ManagedChannel;
- import io.grpc.ManagedChannelBuilder;
- import io.grpc.StatusRuntimeException;
- import java.util.concurrent.TimeUnit;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import com.ylifegroup.protobuf.PhoneServiceGrpc;
- import com.ylifegroup.protobuf.Phonebook.AddPhoneToUserRequest;
- import com.ylifegroup.protobuf.Phonebook.AddPhoneToUserResponse;
- import com.ylifegroup.protobuf.Phonebook.PhoneType;
- public class GRpcClient {
- private static final Logger logger = Logger.getLogger(GRpcClient.class.getName());
- private final ManagedChannel channel;
- private final PhoneServiceGrpc.PhoneServiceBlockingStub blockingStub;
- /** Construct client connecting to gRPC server at {@code host:port}. */
- public GRpcClient(String host, int port) {
- ManagedChannelBuilder < ?>channelBuilder = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true);
- channel = channelBuilder.build();
- blockingStub = PhoneServiceGrpc.newBlockingStub(channel);
- }
- public void shutdown() throws InterruptedException {
- channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
- }
- /** add phone to user. */
- public void addPhoneToUser(int uid, PhoneType phoneType, String phoneNubmer) {
- logger.info("Will try to add phone to user " + uid);
- AddPhoneToUserRequest request = AddPhoneToUserRequest.newBuilder().setUid(uid).setPhoneType(phoneType).setPhoneNumber(phoneNubmer).build();
- AddPhoneToUserResponse response;
- try {
- response = blockingStub.addPhoneToUser(request);
- } catch(StatusRuntimeException e) {
- logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
- return;
- }
- logger.info("Result: " + response.getResult());
- }
- public static void main(String[] args) throws Exception {
- GRpcClient client = new GRpcClient("localhost", 50051);
- try {
- client.addPhoneToUser(1, PhoneType.WORK, "13888888888");
- } finally {
- client.shutdown();
- }
- }
- }
代码也很简单,自己看吧,总结下来调用 gRPC 服务包括以下几步:
这其中大部分的代码 protoc-gen-grpc 都已经帮您生成,使用起来很方便。
我们在项目根目录执行如下命令:
- . / gradlew jar
会在 build/libs / 目录下生成一个可直接运行的 jar 包:gRPCDemo.jar。
我们执行如下命令即可启动我们的 gRPC 服务:
- java - jar build / libs / gRPCDemo.jar
如下图所示:
我们在 eclipse 中运行 GRpcClient 类,您将看到:
看到了吗?服务器端返回 "true"。
把代码中的 8 去掉一个,再次执行,如下图所示:
服务器端返回了 false,说明我们的服务还是靠谱的,哈哈。
这只是我们的第一个 grpc 版本,以方便你更好的理解 grpc,后面我将结合 spring boot 讲解微服务架构,那就更牛 B 了。
OK,今天就到这了,本文所涉及的全部代码见:github:https://github.com/ChangjunZhao/gRPCDemo
百度搜索 "就爱阅读", 专业资料, 生活学习, 尽在就爱阅读网 92to.com, 您的在线图书馆!
来源: http://www.92to.com/bangong/2017/08-10/26678878.html