RPC

RPC 原理

RPC 框架的目标就是让远程服务调用更加简单、透明,RPC 框架负责屏蔽底层的传输方式(TCP 或者 UDP)、序列化方式(XML/Json/二进制)和通信细节。服务调用者可以像调用本地接口一样调用远程的服务提供者,而不需要关心底层通信细节和调用过程。

RPC 框架的调用原理图:

主流 RPC 框架

  1. 支持多语言的 RPC 框架,如 Google 的 grpc、Apache 的 Thrift
  2. 只支持特定语言的 RPC 框架,例如新浪微博的 Motan
  3. 支持服务治理等服务化特性的分布式服务框架,其底层内核仍然是 RPC 框架,例如阿里的 Doubble

随着微服务的发展,基于语言中立性原则构建微服务,逐渐成为一种主流模式,例如对于后端并发处理要求高的微服务,比较适合采用 Go 语言构建,而对于前端的 Web 界面,更适合 Java 和 JavaScript。

因此,基于多语言的 RPC 框架来构建微服务,是一种比较好的技术选择。例如 Netflix,API 服务编编排层和后端的微服务之间就采用 grpc 进行通信。

gRPC

概述

gRPC 是由 Google 开发并开源的一种语言中立的 RPC 框架,当前支持 C、Java 和 Go 语言。

gRPC的调用示例如下所示:

特点

  1. 语言中立,支持多种语言;
  2. 基于 IDL 文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口以及客户端 Stub;
  3. 通信协议基于标准的 HTTP/2 设计,支持双向流、消息头压缩、单 TCP 的多路复用、服务端推送等特性,这些特性使得 gRPC 在移动端设备上更加省电和节省网络流量;
  4. 序列化支持 PB (Protocol Buffer)和 JSON,PB 是一种语言无关的高性能序列化框架,基于 HTTP/2 + PB,保障了 RPC 调用的高性能。

服务端创建

定义服务

一个 RPC 服务通过参数返回值类型来指定可以远程调用的方法。在这里我们使用 protocal buffers 接口定义语言来定义服务方法。客户端和服务端均使用服务定义生成的接口代码。

创建 helloworld.proto 文件:

在这里我们定义一个服务。Greeter 服务有一个方法 SayHello ,可以让服务端从远程客户端接收一个包含用户名的 HelloRequest 消息后,在一个 HelloReply 里发送回一个Greeter。

  1. syntax = "proto3";
  2. option java_package = "io.grpc.examples";
  3. package helloworld;
  4. // The greeter service definition.
  5. service Greeter {
  6. // Sends a greeting
  7. rpc SayHello (HelloRequest) returns (HelloReply) {}
  8. }
  9. // The request message containing the user's name.
  10. message HelloRequest {
  11. string name = 1;
  12. }
  13. // The response message containing the greetings
  14. message HelloReply {
  15. string message = 1;
  16. }

生成 gRPC 代码

添加依赖:

  1. <properties>
  2. <grpc.version>1.0.3</grpc.version>
  3. </properties>
  4. <!--grpc所依赖的包-->
  5. <dependencies>
  6. <dependency>
  7. <groupId>io.grpc</groupId>
  8. <artifactId>grpc-netty</artifactId>
  9. <version>${grpc.version}</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>io.grpc</groupId>
  13. <artifactId>grpc-protobuf</artifactId>
  14. <version>${grpc.version}</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>io.grpc</groupId>
  18. <artifactId>grpc-stub</artifactId>
  19. <version>${grpc.version}</version>
  20. </dependency>
  21. <!--客户端连接池-->
  22. <dependency>
  23. <groupId>org.apache.commons</groupId>
  24. <artifactId>commons-pool2</artifactId>
  25. <version>2.4.2</version>
  26. </dependency>
  27. </dependencies>
  28. <build>
  29. <!--解析proto的工具-->
  30. <extensions>
  31. <extension>
  32. <groupId>kr.motd.maven</groupId>
  33. <artifactId>os-maven-plugin</artifactId>
  34. <version>1.4.1.Final</version>
  35. </extension>
  36. </extensions>
  37. <plugins>
  38. <plugin>
  39. <groupId>org.xolstice.maven.plugins</groupId>
  40. <artifactId>protobuf-maven-plugin</artifactId>
  41. <version>0.5.0</version>
  42. <configuration>
  43. <protocArtifact>com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}</protocArtifact>
  44. <pluginId>grpc-java</pluginId>
  45. <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
  46. </configuration>
  47. <executions>
  48. <execution>
  49. <goals>
  50. <goal>compile</goal>
  51. <goal>compile-custom</goal>
  52. </goals>
  53. </execution>
  54. </executions>
  55. </plugin>
  56. <plugin>
  57. <groupId>org.apache.maven.plugins</groupId>
  58. <artifactId>maven-compiler-plugin</artifactId>
  59. <configuration>
  60. <source>1.8</source>
  61. <target>1.8</target>
  62. </configuration>
  63. </plugin>
  64. </plugins>
  65. </build>

添加完依赖后使用 IDEA Maven 的 compile :

生成代码,在如下图目录中:

以下类包含所有我们需要创建这个例子的所有代码:

  • HelloRequest.java,HelloResponse.java 和其它文件所包含的所有 protocol buffer 用来填充、序列化和提取 HelloRequestHelloReply 类型的代码。

  • GreeterGrpc.java, 包含 (还有其他有用的代码):

    Greeter 服务端需要实现的接口

    1. public static interface Greeter {
    2. public void sayHello(Helloworld.HelloRequest request,
    3. StreamObserver&lt;Helloworld.HelloReply> responseObserver);
    4. }

    客户端用来与 Greeter 服务端进行对话的 存根 类。就像你所看到的,异步存根也实现了 Greeter 接口。

    1. public static class GreeterStub extends AbstractStub&lt;GreeterStub>
    2. implements Greeter {
    3. ...
    4. }

服务端实现

  1. public class HelloWorldServer {
  2. private int port = 50051;
  3. private Server server;
  4. /**
  5. * 启动服务
  6. * @throws IOException
  7. */
  8. private void start() throws IOException {
  9. server = ServerBuilder.forPort(port)
  10. .addService(new GreeterImpl())
  11. .build()
  12. .start();
  13. System.out.println("service start...");
  14. Runtime.getRuntime().addShutdownHook(new Thread() {
  15. @Override
  16. public void run() {
  17. System.err.println("*** shutting down gRPC server" +
  18. "since JVM is shutting down");
  19. HelloWorldServer.this.stop();
  20. System.err.println("*** server shut down");
  21. }
  22. });
  23. }
  24. private void stop() {
  25. if (server != null) {
  26. server.shutdown();
  27. }
  28. }
  29. // block 一直到退出程序
  30. private void blockUntilShutdown() throws InterruptedException {
  31. if (server != null) {
  32. server.awaitTermination();
  33. }
  34. }
  35. public static void main(String[] args) throws IOException
  36. , InterruptedException {
  37. final HelloWorldServer server = new HelloWorldServer();
  38. server.start();
  39. server.blockUntilShutdown();
  40. }
  41. // 实现 定义一个实现服务接口的类
  42. private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
  43. public void sayHello(HelloRequest req,
  44. StreamObserver<HelloReply> responseObserver) {
  45. //获取参数
  46. System.out.println("收到的信息:" + req.getName());
  47. //这里可以放置具体业务处理代码 start
  48. //这里可以放置具体业务处理代码 end
  49. //构造返回
  50. HelloReply reply = HelloReply.newBuilder()
  51. .setMessage(("Hello: " + req.getName())).build();
  52. responseObserver.onNext(reply);
  53. responseObserver.onCompleted();
  54. }
  55. }
  56. }

客户端实现

  1. public class HelloWorldClient {
  2. private final ManagedChannel channel; //一个gRPC信道
  3. private final GreeterGrpc.GreeterBlockingStub blockingStub;//阻塞/同步 存根
  4. //初始化信道和存根
  5. public HelloWorldClient(String host,int port){
  6. this(ManagedChannelBuilder.forAddress(host, port)
  7. // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid
  8. // needing certificates.
  9. .usePlaintext(true));
  10. }
  11. /** Construct client for accessing RouteGuide server using the existing channel. */
  12. private HelloWorldClient(ManagedChannelBuilder<?> channelBuilder) {
  13. channel = channelBuilder.build();
  14. blockingStub = GreeterGrpc.newBlockingStub(channel);
  15. }
  16. public void shutdown() throws InterruptedException {
  17. channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
  18. }
  19. //客户端方法
  20. public void greet(String name){
  21. HelloRequest request = HelloRequest
  22. .newBuilder().setName(name).build();
  23. HelloReply response;
  24. try {
  25. response = blockingStub.sayHello(request);
  26. } catch (StatusRuntimeException e) {
  27. System.out.println("RPC调用失败:"+e.getMessage());
  28. return;
  29. }
  30. System.out.println("服务器返回信息:"+response.getMessage());
  31. }
  32. public static void main(String[] args) throws InterruptedException {
  33. HelloWorldClient client = new HelloWorldClient("127.0.0.1",50051);
  34. try {
  35. for(int i=0;i<5;i++){
  36. client.greet("world:"+i);
  37. }
  38. }finally {
  39. client.shutdown();
  40. }
  41. }
  42. }

先运行服务端再与运行客户端,结果:

  1. 客户端结果:
  2. 服务器返回信息:Hello: world:0
  3. 服务器返回信息:Hello: world:1
  4. 服务器返回信息:Hello: world:2
  5. 服务器返回信息:Hello: world:3
  6. 服务器返回信息:Hello: world:4
  7. 服务端结果:
  8. service start...
  9. 收到的信息:world:0
  10. 收到的信息:world:1
  11. 收到的信息:world:2
  12. 收到的信息:world:3
  13. 收到的信息:world:4

自此,一个简单的本地 RPC 调用就完成了。

踩坑记录

proto 文件必须放在 main 目录下,否则 compile 的时候不会生成 target 里面的代码。

源码

源码

GitHub 学习仓库

gRPC 本地服务搭建的更多相关文章

  1. [New learn] 网络基础-apache本地服务搭建(支持php)

    1.简介 无网不利,无网不胜.对于移动应用来说离开网络那和咸鱼有什么分别?所以对于开发者来说更要学习好网络开发的技术. 2.搭建apache本地服务器 1.在finder中显示影藏的用户文件夹 fin ...

  2. node最简单的本地服务搭建

    **1.首先需要安装node** [node下载链接](http://nodejs.cn/download/)**2.需要安装http的镜像文件** npm install http-server - ...

  3. webpack-dev-server 搭建本地服务以及浏览器实时刷新

    一.概述开发项目中为了保证上线,开发项目是都需要使用localhost进行开发,以前的做法就是本地搭建Apache或者Tomcat服务器.有的前端开发人员 对服务器的搭建和配置并不熟悉,这个时候需要后 ...

  4. Spring Boot 2+gRPC 学习系列1:搭建Spring Boot 2+gRPC本地项目

    Spring Boot 2+gRPC 学习系列1:搭建Spring Boot 2+gRPC本地项目 https://blog.csdn.net/alinyua/article/details/8303 ...

  5. 【2020-03-21】Dubbo本地环境搭建-实现服务注册和消费

    前言 本周主题:加班工作.本周内忙于CRUD不能自拔,基本每天都是九点半下班,下周上线,明天还要加班推进进度.今天是休息日,于是重拾起了dubbo,打算近期深入了解一下其使用和原理.之所以说是重拾,是 ...

  6. 12. Vue搭建本地服务

    一. 搭建本地服务器 本地服务可以提高开发效率. webpack不需要每次都打包, 就可以看到修改后的效果. 本地服务器基于node.js搭建, 内部使用二十express框架. 可以实现让浏览器自动 ...

  7. 用node搭建本地服务环境

    const express = require('express'); const path = require('path'); const request = require('request') ...

  8. 本地服务器搭建服务:svn

    SVN(使用VisualSVN-server)可视化SVN 服务搭建,适合小白:简单又快捷,深入了解命令行方式等高手请移步官网看教程 1.官网 :http://subversion.apache.or ...

  9. Git本地服务器搭建及使用详解

    Git本地服务器搭建及使用 Git是一款免费.开源的分布式版本控制系统.众所周知的Github便是基于Git的开源代码库以及版本控制系统,由于其远程托管服务仅对开源免费,所以搭建本地Git服务器也是个 ...

随机推荐

  1. [Luogu] 计算系数

    https://www.luogu.org/problemnew/show/P1313#sub Answer = a ^ n * b ^ m * C(k, min(n,  m)) 这里用费马小定理求逆 ...

  2. 【luoguP3959 宝藏】-状压DP

    题目描述: 参与考古挖掘的小明得到了一份藏宝图,藏宝图上标出了 n 个深埋在地下的宝藏屋, 也给出了这 n 个宝藏屋之间可供开发的m 条道路和它们的长度. 小明决心亲自前往挖掘所有宝藏屋中的宝藏.但是 ...

  3. Hadoop配置多个HDFS入口

    为了验证存在不同的hdfs之间的hive的互操作(归根结底还是为了解决BUG) 需要在两个不同的hadoop集群的HDFS  能够在Hiveserver2上进行路由转发绕过一些坑. 就需要将某hdfs ...

  4. maven上传jar包(oracle jdbc驱动)

    由于Oracle授权问题,Maven3不提供Oracle JDBC driver,为了在Maven项目中应用Oracle JDBC driver,必须手动添加到本地仓库.一.首先要得到Oracle J ...

  5. Spring AOP常见面试题

    一.AOP是什么? 与OOP对比,面向切面,传统的OOP开发中的代码逻辑是至上而下的过程中会长生一些横切性问题,这些横切性的问题和我们的主业务逻辑关系不会散落在代码的各个地方,造成难以维护,AOP的编 ...

  6. Leetcode题目94.二叉树的中序遍历(中等)

    题目描述: 给定一个二叉树,返回它的中序遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶: 递归算法很简单,你可以通过迭代算法完成吗? 思路解析: 1 ...

  7. Nginx之搭建反向代理实现tomcat分布式集群

    参考博文: Nginx反向代理实现Tomcat分布式集群 1. jdk 安装 jdk 下载网址: http://www.oracle.com/technetwork/java/javase/downl ...

  8. Qt子窗口QMidSubwindow全屏出现的问题总结

    我的需求:想全屏一个子窗口QMidSubwindow,禁止显示最大化最小化和关闭按钮. 我开始尝试的是网上介绍的方法,把结果展现给大家一下,最后再总结: 方法1:QMidSubwindow直接调用sh ...

  9. rsync+inotify 实时双向同步

    前言 在遇到需要 nginx 负载均衡,承受较高并发的情况,同时会有双机相同目录内容须保持一致的需求 rsync+inotify 是一个不错的解决方案,相较于 rsync+sersync 在处理大量文 ...

  10. 短信的内容提供者Uri和短信表结构

    * sms表 * address :手机号码 * date :收发短信的时间 * read :短信的阅读状态 1,已读 0,未读 * type :收发短信的类型 1,收到短信 2,发出短信 * bod ...