漫谈gRPC

0. 三分钟科普

或许,除了写技术内容,咱们还须要一点别的吧,好比天然科学。html

因此今天咱们来了解一下 Kuiper Belt,关于 柯伊伯带 如下摘自百度百科:java

20世纪50年代,一位名叫吉纳德·柯伊伯的科学家首先提出在海王星轨道外存在一个小行星带,其中的星体被称为KBO(Kuiper Belt Objects)。1992年人类发现了第一个KBO,今天,咱们知道KBO地带有大约10万颗直径超过100千米的星体。此后天文学界就以纳德·柯伊伯名字命名此小行星带。node

柯伊伯带天体,是太阳系造成时遗留下来的一些团块。在45亿年前,有许多这样的团块在更接近太阳的地方绕着太阳转动,它们互相碰撞,有的就结合在一块儿,造成地球和其余类地行星,以及气体巨行星的固体核。在远离太阳的地方,那里的团块处在深度的冰冻之中,就一直原样保存了下来。柯伊伯带天体也许就是这样的一些遗留物,它们在太阳系刚开始造成的时候就已经在那里了。python

柯伊伯带是所知的太阳系的边界,是太阳系大多数彗星来源地。有天文学家认为,因为冥王星的大小和柯伊伯带的小行星的大小相约,因此冥王星应该排除在九大行星之列,而纳入柯伊伯带小行星的行列当中;而冥王星的卫星则应被看成是冥王星的伴星。ios

来放一张NASA Science的计算机模拟图片,毕竟一图胜千言,先睹为快:
c++

图片来源:
https://solarsystem.nasa.gov/solar-system/kuiper-belt/overview/
git

3分钟科普就先到这里,因此开始正题。github

1. 前言

以前一篇文章简单介绍了RPC框架的一些背景和组成,今天继续来深刻学习一下gRPC框架,后面计划把 脸书的Thrift百度的bRPC腾讯的Tars三个框架都进行学习,敬请期待。面试

经过本文你将了解到如下内容:算法

  • gRPC起源和简介

  • gRPC的基本组成和原理

  • gRPC的demo和优缺点

图片来自grpc官网

2. RPC和微服务化架构

在聊RPC以前,有必要简单了解一下互联网工程架构的演进之路。

2.1 互联网工程架构演进简介

我始终信奉 体量决定架构。

随着流量接入和并发业务量的快速增加,互联网工程架构经历了单体结构->服务化SOA架构->微服务化架构->服务网格架构等演进过程,如图:

特别提一下服务网格化架构 Service Mesh,我大概是几个月前听在阿里云的好友提到这个新名词,后来查了一下Service Mesh在17年就被提出了,想一想我确实是孤陋寡闻井底之蛙了,那就简单看一下这究竟是个什么吧。

可查阅原始外文资料:
《Pattern: Service Mesh》
https://philcalcado.com/2017/08/03/pattern_service_mesh.html

做者Phil Calçado的行文风格让我以为内容温馨,做者把Service Mesh的前因后果都说的简洁清晰,十分推荐阅读。

那么问题来,Servie Mesh是个啥?看下Service Mesh名词的提出者Buoyant的CEO William Morgan给的基本定义(译文):

服务网格是一个基础设施层,用于处理服务间通讯。云原生应用有着复杂的服务拓扑,服务网格保证请求在这些拓扑中可靠地穿梭。在实际应用当中,服务网格一般是由一系列轻量级的网络代理组成的,它们与应用程序部署在一块儿,但对应用程序透明。

Service Mesh的目标是作微服务时代的TCP/IP协议,屏蔽分布式底层复杂细节,让开发者回归到业务,就像咱们开发业务时不须要关心TCP/IP这样的基础网络通讯链路同样,在进行微服务开发时也无需关心负载均衡、服务发现、认证受权、监控追踪、流量控制等细节问题。

Service Mesh野心勃勃,咱们拭目以待

2.2 RPC和微服务化

从新回到RPC和分布式微服务化的话题,可是其中涉及不少范畴模糊的概念,咱们对此不作文字上的争辩。

RPC(Remote Procedure Call)远程过程调用,通俗来说RPC调用过程,就是 Server 端实现了一个函数,客户端使用 RPC 框架提供的接口,调用这个函数并获取返回值的过程,这里的C/S是相对而言的,提供功能的一方称为服务端,调用功能的一方称为客户端。

微服务化框架风格:大服务拆分为单个小服务,子服务专一特定业务、高内聚低耦合,集群化部署独立开发/测试,服务彼此物理独立但又相互调用。

分布式微服务化的体系之下,各个子服务之间必然存在相互调用的关系,随着微服务数量和多样性的增长,运维成本也在增大,RPC就显得很是重要,至于为何这么说,感兴趣的读者可自行查阅RPC和微服务化的联系就能够了。

有的文章说RPC是微服务化系统的水电燃气,好像也蛮形象,总之RPC之于微服务化就是基础且重要的角色。

尝试一句话归纳:互联网业务体量的增长促使工程架构的演进,微服务化的时代,传统的通讯方式并非很适用,因此咱们须要一种更加高效、可运维、便捷的数据通讯和服务管理框架,这就是RPC框架。

2.3 RPC阵营

生活缺乏统一,RPC也是。

从根本上来讲,RPC就是为了解决远程数据通讯问题,由框架来屏蔽底层细节从而让通讯双方专一于业务。

从广义角度来看,面对复杂的微服务化环境就会出现服务治理、服务发现、服务注册、负载均衡、监控警报、日志体系等附加功能,把工做作的更细致便捷。

一个优秀的RPC框架除了解决数据通讯问题之外完整可把控、易用可扩展、高效可运维也十分重要,根据RPC框架的侧重点不一样,能够分为两大阵营

  • 侧重跨语言调用
    在一个内部系统中可能存在Java/C++/Python/Go等多种语言编写的程序,因此跨语言调用在多语言系统中显得很重要,谷歌的gRPC、Facebook的Thrift、轻量级框架Hessian等

  • 侧重服务治理运维
    像阿里的dubbo和微博的Motan都是侧重服务治理类的典型代码,不过对于跨语言支持性差一些,腾讯的Tars算是兼顾了跨语言和服务治理的相关功能。

图片来自网络:多种rpc框架对比

3. gRPC和Stubby

罗马不是一天建成的,gRPC也不是。

在谷歌内部有一个称为Stubby的内部项目,Stubby是一个通用的RPC框架用来解决谷歌内部数据中心之间以及数据中心之中的微服务链接问题,这个项目在谷歌内部使用了好久,而且有很好的效果。

可是 Stubby 并不基于任何标准,而且与谷歌内部基础设施紧密耦合,不适合公开发布。随着 SPDY、HTTP/2 和 QUIC 的出现,公共标准中出现了许多Stubby 没有提供的其余特性。面对新标准和新特性的出现,谷歌决定对Stubby进行改造,以利用新标准化,并将其适用性扩展到移动、物联网和云,成为一个外界可用的通用远程过程调用框架。

可是这样的目标设定并不意味着 gRPC 比 Stubby 更强大,由于 Stubby 是在谷歌内部使用多年的系统,涉及不少内部框架基础,多是服务治理/运维/监控等。

咱们知道 gRPC 是侧重多语言场景的,可是在服务治理/运维/监控等方面并非很完善,这也就是 gRPC 不能等同于 Stubby 的缘由

画外音:像Stubby这样的系统在谷歌使用的很棒,可是这并不意味着Stubby就能够直接开源,相反很难直接开源。

由于优秀的系统框架每每依赖不少内部的其余组件,要想开源就只能裁剪或者重写通用化,这样也就意味着咱们拿到的多是个半成品的开源项目。

为了能让这个开源项目在本身业务中真正run起来,要走的路仍是不少,因此找一个通用完善的开源轮子是很是重要的,像阿里的dubbo和腾讯的tars对完整性和易用性都下了不少功夫,是很优秀的开源项目。

4. gRPC官方简介

gRPC的官方网站的一些介绍,为了不失真,仍是老规矩 中英双版:

gRPC is a modern open source high performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.

翻译一下:

gRPC是一个现代的开源高性能RPC框架,能够在任何环境下运行。它能够高效地链接数据中心内和数据中心之间的服务,并支持负载平衡、跟踪、运行情况检查和身份验证。它也适用于分布式计算的最后一英里,将设备、移动应用程序和浏览器链接到后端服务。

gRPC的适用场景和核心特征:

The main usage scenarios:
1.Efficiently connecting polyglot services in microservices style architecture
2.Connecting mobile devices, browser clients to backend services 3.Generating efficient client libraries

Core features that make it awesome
1.Idiomatic client libraries in 10 languages
2.Highly efficient on wire and with a simple service definition framework
3.Bi-directional streaming with http/2 based transport
4.Pluggable auth, tracing, load balancing and health checking

翻译一下:

在微服务风格的架构中高效链接多语言服务,能够将移动设备、浏览器客户端链接到后端服务,支持10多种语言的客户端,高效简单的服务定义框架,基于http/2传输双向流,可插拔认证、跟踪、负载平衡和健康检查。

图片来自网络:rpc官网给出的语言支持列表

简单来讲,gRPC是个支持多种语言,能够应用在微服务框架和移动场景而且基于http2传输,具有认证/追踪/负载均衡等综合功能的RPC框架。

官网上列出的gRPC的使用者包括但不限于:

图片来自grpc官网

5. gRPC的基础组成和原理

通用的RPC框架大概能够是这个样子的:

图片来自网络

基础的也是最重要的,因此咱们先了解一下gRPC是如何进行数据通讯的:

  • 数据内容交互格式
    常见的json、xml等是常见的文本类数据交换格式,json/xml 也都有各自的优缺点,后来又出现了二进制格式,好比 protobuf 等,二进制化传输效率更高更安全,同时也须要遵循一些规范来进行序列化和反序列化,gRPC就是使用protobuf 进行数据内容交换,固然 protobuf 不只仅是一直数据交互格式语言而是一套规则和工具,protobuf 也是谷歌出品。

  • 数据传输协议
    常见的传输协议有不少:TCP、UDP、Http等,RPC的传输协议也不外乎这几种,固然还有使用消息队列 MQ 进行数据传输的状况,大部分RPC协议是基于TCP协议进行传输的,gRPC则是基于HTTP协议传输的,http协议有1.0、1.一、2.0、3.0等多个版本,不一样版本之间的性能差别仍是挺大的,gRPC则使用http2进行数据传输。

5.1 gRPC和HTTP2

咱们知道了gRPC使用protobuf进行数据封装(序列化和反序列化),同时使用http2进行数据传输,那么仍是想几个问题吧。

使用protobuf并不难理解,毕竟是自家产品,并且还很不错。

可是不由要问gRPC为啥不使用TCP进行数据传输呢,emmm... http2也是基于TCP的啊!所以问题能够改成gRPC为啥没有直接使用TCP而是http,而且仍是http2呢?

咱们试着去探究一下缘由:

tcp协议做为传输层协议,相比http更加底层,咱们能够说tcp协议在传输数据时支持定制、减小网络开销提供并发能力,这确实是优势,同时也是缺点,协议的统一和受众很重要,因此才会出现http这种应用层协议,让动做变得统一,可是代价多是开销更大。

http也有不少版本,之因此选择了http2和gRPC的目标设定有很大关系,gRPC的一个重要场景是移动场景,http2最早在移动场景应用同时是通过实践检验的标准,而且http2的前身SPDY也和谷歌有很是大的关系,另外支持http2的客户端语言也很是多,所以对于数据包的处理方案也更加成熟和通用。

以前写给一篇关于HTTP2技术特征的文章,能够进行阅读:理解HTTP2协议

整体来讲,gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 链接上的多复用请求等特性。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

6. gRPC的demo

在github上grpc的相关example有多个版本,看了下c++和python,代码基本差很少,不过要想运行起来,还要装一些依赖包,须要折腾一下才行。

好在github上的相关说明写的很详细(这也是相对的emmm...),试一下就知道了。

粘个python的demo工程,主体就三部分:proto文件、client、server。

图片来自网络:基于gRPC的C/S交互简图

咱们经过修改.proto文件,而后使用grpc工具来生成client和service调用的文件:

这里简单提一下protobuf,这是一种序列化和反序列化工具,同时也是一门idl语言,protobuf有本身特定的语法,咱们能够在其中进行修改增长功能,以后使用proto的相关工具编译器生成对应语言的代码,好比c++或者python,从而作到语言无关,可扩展性确实好了不少,常被看成一种跨语言的序列化/反序列化方案。

环境安装和代码执行详细步骤可参考:

gRPC快速入门-python版
https://grpc.io/docs/quickstart/python/

greeter_server.py

from concurrent import futures
import logging

import grpc

import helloworld_pb2
import helloworld_pb2_grpc


class Greeter(helloworld_pb2_grpc.GreeterServicer):

    def SayHello(self, request, context):
        return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    server.wait_for_termination()


if __name__ == '__main__':
    logging.basicConfig()
    serve()

greeter_client.py

from __future__ import print_function
import logging

import grpc

import helloworld_pb2
import helloworld_pb2_grpc


def run():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.
    with grpc.insecure_channel('localhost:50051') as channel:
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    logging.basicConfig()
    run()

helloworld.proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

References

  • https://grpc.io/blog/principles/

  • https://www.infoq.cn/article/2016/09/gRPC1-0-Google-RPC

  • http://www.kancloud.cn:8080/dargon/stack_development/1134466

  • https://juejin.im/post/5cff855c518825612f412526

  • https://www.jianshu.com/p/4e0633a1a37a

  • https://www.sohu.com/a/271150652_468635

  • https://zhuanlan.zhihu.com/p/100709206

  • https://tech.meituan.com/2019/08/08/large-scale-microservice-communication-framework.html

  • https://cloud.tencent.com/developer/article/1461684

  • http://ddrv.cn/a/180301

  • https://docs.microsoft.com/zh-cn/aspnet/core/grpc/comparison?view=aspnetcore-3.0&viewFallbackFrom=aspnetcore-1.1

  • https://www.codercto.com/a/26880.html

  • https://blog.csdn.net/zhougb3/article/details/80403125

  • https://zhuanlan.zhihu.com/p/61901608

  • https://www.jianshu.com/p/e23e3e74538e

  • https://ketao1989.github.io/2016/12/10/rpc-theory-in-action/

写在最后

最近有一些工做上的调整,为了在新岗位快速适应并站稳脚跟,写文章的时间愈来愈少了,差很少天天早上写1个小时晚上写1个小时,可是仍然以为有点黑白颠倒,不是很适应目前的做息节奏,么得办法干就完了,加油整。

工程类的文章比较宽泛可是也很差写,以前尝试过写leetcode的题目,会比较快大约半天能够写一篇(固然不存在动画之类的,不擅长也不打算尝试)。

工程能力和算法能力一样重要,可是目前两方面的知识储备和能力都相差不少,因此这个号会持续更新,虽然慢但不会断。

最后依然是:感谢各位读者的阅读,秉承 若批评不自由则赞美无心义 的观点,欢迎指出存疑或者明显错误的地方,在此表示感谢。

Learning by doing 完结

一、抛弃jenkins,如何用node从零搭建自动化部署管理平台

二、【每日算法Day 72】谷歌面试题:又双叒叕是位运算,最详细的自动机推导过程

三、LeetCode专题 - 小岛题

四、刷遍Leetcode面试题系列连载(1) - 入门与工具简介(C#篇)

五、一文带你AC十道题【滑动窗口】

六、DDoS攻击:无限战争

若是以为文章不错,帮忙点个在看呗