自从产业界发明机器联网的那一天就已经开始探索最优的远程通讯机制。操做系统如 UNIX、Windows 和 Linux 等都有实现远程通讯的内部协议,挑战在于如何向开发人员开放一个通讯框架。node
在20世纪90年代,当 TCP/IP 协议日臻成熟变成网络通讯的黄金标准时,焦点转移到跨平台通讯 —— 一台计算机能够经过某种类型网络在另外一台计算机上发起一个动做。例如如 CORBA、DCOM、Java RMI 技术,在核心网络基础设施之上创造了一个对开发者友好的抽象层。这些技术还试图发展出一套与开发语言无关的通讯框架,这一点对于客户机/服务器体系结构相当重要。python
随着本世纪初 Web 技术的演进,HTTP 逐渐演变为事实上的通讯标准。HTTP 结合 XML 提供了一种自我描述、不依赖语言、与平台无关的远程通讯框架。这种结合的成果是 SOAP 和 WSDL 标准,它们保证了在各类运行环境和平台之间实现互操做的标准化。git
下一个冲击互联网的浪潮是 Web 编程。许多开发人员发现定义 SOAP 标准的 HTTP 和 XML 的组合过于严格。这时 JavaScript 和 JSON 开始流行了。Web 2.0 现象(API 发挥了关键做用), JSON 替代 XML 成为首选的协议。HTTP 和 JSON 这对致命的组合,催生了一个新的非官方标准 REST 。SOAP 要求严格遵照标准和结构定义,仅局限于大型企业应用程序,而 REST 在当代开发人员中很受欢迎。github
归功于 JavaScript 框架,Node.js 以及文档数据库的发展,REST 在 Web 开发者中广受欢迎。许多应用程序开始基于 REST 实现 ,即便是内部序列化和通讯模式领域。但 HTTP 是最有效的消息交换协议吗?即便在同一上下文、同一网络,或者是同一台机器上运行的服务之间?HTTP 的便捷性与高性能之间须要做出权衡,这促使咱们回到问题的起点,寻找微服务架构中最优的通讯框架。数据库
进入 gRPC 时代 —— 来自谷歌,现代的轻量级通讯协议。这是一个高性能的、开源的通用远程过程调用(RPC) 框架,它能够在多种开发语言、任何操做系统上运行。npm
gRPC 在推出的第一年内就被 CoreOS,Netflix,Square 和 Cockroach Labs 等机构采用。 CoreOS 团队的 Etcd,是一种分布式键/值存储服务,采用 gRPC 实现端通讯。电信公司如 Cisco,Juniper 和 Arista 都使用 gRPC 实现数据流遥测和网络设备配置。编程
当我第一次遇到 gRPC,它使我想到 CORBA。两个框架都基于语言无关的接口定义语言(IDL) 声明服务,经过特定的语言绑定实现。 安全
CORBA 和 gRPC 两者的设计,都是为了使客户端相信服务器在同一台机器。客户机在桩(Stub)上调用一个方法(method),调用过程由底层协议透明地处理。bash
gRPC 的秘诀在于处理序列化的方式。gRPC 基于 Protocol Buffer,一个开源的用于结构化数据序列化机制,它是语言和平台无关的。Protocol Buffer 的描述很是详细,与 XML 相似。可是它们比其余的协议格式更小,更快,效率更高。任何须要序列化的自定义数据类型在 gRPC 被定义为一个 Protocol Buffer 。服务器
Protocol Buffer 的最新版本是 proto3,支持多种开发语言的代码生成,Java , C++,Python,Ruby , Java Lite , JavaScript,Objective-C 和 C # 。当一个 Protocol Buffer 编译为一个特定的语言,它的访问器(setter 和 getter)为每一个字段提供定义。
相比于 REST + JSON 组合 ,gRPC 提供更好的性能和安全性。它极大的促进了在客户端和服务器之间使用 SSL / TLS 进行身份验证和数据交换加密。
为何微服务开发者须要使用 gRPC ?gRPC 采用 HTTP / 2 以支持高性能的、可扩展的 API 。报文使用二进制而不是文本通讯能够保持载荷紧凑、高效。HTTP / 2 请求在一个 TCP 链接上可支持多路复用,容许多个消息并发传送而不影响网络资源利用率。gRPC 使用报头压缩(header compression )来减小请求和响应的大小。
Note:Node.js 客户端不须要生成存根(Stub),只要 Protocol Buffer 文件是可访问的,它就能够直接与服务端对话。
为了进一步熟悉 gRPC,咱们将用 Python 语言建立一个简单的计算服务。它将同时被一个 Python 客户端和一个 Node.js 客户端调用。如下测试示例运行在 Mac OS X 。
你能够从 GitHub 库 https://github.com/grpc/grpc/tree/master/examples 访问源代码,在本身的机器上运行示例。
// 配置 Python gRPC python -m pip install virtualenv virtualenv venv source venv/bin/activate python -m pip install --upgrade pip //安装 gRPC 和 gRPC Tools python -m pip install grpcio python -m pip install grpcio-tools // 配置 Node.js gRPC npm install grpc --global //建立目录 mkdir Proto mkdir Server mkdir -p Client/Python mkdir -p Client/Node
//Proto/Calc.proto syntax = "proto3"; package calc; service Calculator { rpc Add (AddRequest) returns (AddReply) {} rpc Substract (SubstractRequest) returns (SubstractReply) {} rpc Multiply (MultiplyRequest) returns (MultiplyReply) {} rpc Divide (DivideRequest) returns (DivideReply) {} } message AddRequest{ int32 n1=1; int32 n2=2; } message AddReply{ int32 n1=1; } message SubstractRequest{ int32 n1=1; int32 n2=2; } message SubstractReply{ int32 n1=1; } message MultiplyRequest{ int32 n1=1; int32 n2=2; } message MultiplyReply{ int32 n1=1; } message DivideRequest{ int32 n1=1; int32 n2=2; } message DivideReply{ float f1=1; }
$ python -m grpc.tools.protoc --python_out=. --grpc_python_out=. --proto_path=. Calc.proto $ cp Calc_pb2.py ../Server $ cp Calc_pb2.py ../Client/Python $ cp Calc.proto ../Client/Node
# Server/Calc_Server.py from concurrent import futures import time import grpc import Calc_pb2 import Calc_pb2_grpc _ONE_DAY_IN_SECONDS = 60 * 60 * 24 class Calculator(Calc_pb2.CalculatorServicer): def Add(self, request, context): return Calc_pb2.AddReply(n1=request.n1+request.n2) def Substract(self, request, context): return Calc_pb2.SubstractReply(n1=request.n1-request.n2) def Multiply(self, request, context): return Calc_pb2.MultiplyReply(n1=request.n1*request.n2) def Divide(self, request, context): return Calc_pb2.DivideReply(f1=request.n1/request.n2) def serve(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) Calc_pb2_grpc.add_CalculatorServicer_to_server(Calculator(), server) server.add_insecure_port('[::]:50050') server.start() try: while True: time.sleep(_ONE_DAY_IN_SECONDS) except KeyboardInterrupt: server.stop(0) if __name__ == '__main__': serve()
python Calc_Server.py
# Client/Python/Calc_Client.py from __future__ import print_function import grpc import Calc_pb2 import Calc_pb2_grpc def run(): channel = grpc.insecure_channel('localhost:50050') stub = Calc_pb2_grpc.CalculatorStub(channel) response = stub.Add(Calc_pb2.AddRequest(n1=20,n2=10)) print(response.n1) response = stub.Substract(Calc_pb2.SubstractRequest(n1=20,n2=10)) print(response.n1) response = stub.Multiply(Calc_pb2.MultiplyRequest(n1=20,n2=10)) print(response.n1) response = stub.Divide(Calc_pb2.DivideRequest(n1=20,n2=10)) print(response.f1) if __name__ == '__main__': run()
//Client/Node/Calc_Client.js var PROTO_PATH = 'Calc.proto'; var grpc = require('grpc'); var calc_proto = grpc.load(PROTO_PATH).calc; var params={n1:20, n2:10}; function main() { var client = new calc_proto.Calculator('localhost:50050', grpc.credentials.createInsecure()); client.divide(params, function(err, response) { console.log(response.f1); }); client.multiply(params, function(err, response) { console.log(response.n1); }); client.substract(params, function(err, response) { console.log(response.n1); }); client.add(params, function(err, response) { console.log(response.n1); }); } main();
$ python Calc_Client.py 30 10 200 2.0 $ node Calc_Client.js 30 10 200 2.0
更多精彩内容扫码关注公众号:RiboseYim's Blog:https://riboseyim.github.io/2017/10/30/Protocol-gRPC/