漫谈gRPC
扫描二维码
随时随地手机看文章
0. 三分钟科普
或许,除了写技术内容,我们还需要一点别的吧,比如自然科学。
所以今天我们来了解一下 Kuiper Belt,关于 柯伊伯带 以下摘自百度百科:
20世纪50年代,一位名叫吉纳德·柯伊伯的科学家首先提出在海王星轨道外存在一个小行星带,其中的星体被称为KBO(Kuiper Belt Objects)。1992年人类发现了第一个KBO,今天,我们知道KBO地带有大约10万颗直径超过100公里的星体。此后天文学界就以纳德·柯伊伯名字命名此小行星带。
柯伊伯带天体,是太阳系形成时遗留下来的一些团块。在45亿年前,有许多这样的团块在更接近太阳的地方绕着太阳转动,它们互相碰撞,有的就结合在一起,形成地球和其他类地行星,以及气体巨行星的固体核。在远离太阳的地方,那里的团块处在深度的冰冻之中,就一直原样保存了下来。柯伊伯带天体也许就是这样的一些遗留物,它们在太阳系刚开始形成的时候就已经在那里了。
柯伊伯带是所知的太阳系的边界,是太阳系大多数彗星来源地。有天文学家认为,由于冥王星的大小和柯伊伯带的小行星的大小相约,所以冥王星应该排除在九大行星之列,而归入柯伊伯带小行星的行列当中;而冥王星的卫星则应被当作是冥王星的伴星。
来放一张NASA Science的计算机模拟图片,毕竟一图胜千言,先睹为快:
图片来源:
https://solarsystem.nasa.gov/solar-system/kuiper-belt/overview/
3分钟科普就先到这里,所以开始正题。
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.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 完结。
免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!