IDL,Interface description language,即接口描述语言。
IDL是一种很有用的工具,它提供了对接口的形容,约定了接口协议。使得通信单方通信时,无需再发送 scheme,无效进步了通信数据的荷载比。
但对于RPC框架而言,IDL又不仅仅是一个接口描述语言。对于市面上绝大多数的RPC框架而言,IDL还是一个工具和一种应用过程,专指依据 IDL 形容文件,用指定的开发语言,生成对应的服务端接口模块,和客户端程序。这样的益处是,便于开发者疾速开发。
初衷是好的,然而,问题随同而来。
首先,根据 IDL 生成对应的客户端和接口模块,这个实质是编译。
但对于编译和语言的惯例了解和意识,却导致了问题的复杂化:IDL成了一门“编译型”的语言,倒退出了一套简单的规定和语法。而且不同 RPC 框架的 IDL 语言不齐全一样。每用一个新的框架,就有一套新的IDL语法和规定,就得重新学习一遍。
其次,市面上绝大多数的IDL语法,对于可选参数和可变类型参数,以及不定长参数存在着不同水平的反对问题。
晚期的 IDL,所有的接口字段必须存在。即便是无用的,也须要赋予一个诸如 'nil' 一类的值。否则就是违反 IDL 的标准。随着技术的倒退,之后的 IDL,呈现了 optional 关键字,但仅仅是不得已的状况下,才被举荐应用。而此时,被强烈推荐应用的却是关键字 require 以及 required。比方 Thrift,ProtoBuf 1.0 都是典型的代表。直到技术一直倒退,RPC 畛域的教训积攒越来越多,关键字 require 以及 required 才不再被持续举荐应用,而关键字 optional 成为 IDL 的新宠。可选参数,到目前为止还没有遇到什么问题。直到 IDL 开始编译,生成对应的客户端和接口代码时,新的问题呈现。
接口代码的生成,确实不便了网络服务和接口调用的开发。但适度简单的接口代码,间接导致了接口的强耦合:所有的业务都依赖于 IDL 生成的客户端和服务端的接口,如果一个变动,其余的须要全副追随变动。如果一个接口被改变,与之关联的所有服务和客户端,必须全副从新编译,否则极有可能在 IDL 生成的代码中,呈现不兼容的问题。
最典型的就是,加了一个新的参数,但不在协定数据的开端,那绝大多数 RPC 框架原有的接口在解决新版接口数据时,便会呈现兼容性谬误。
当然,这在很长一段时间内被视为天经地义。
但视之“天经地义”,却不障碍开发者们对其导致的问题熟视无睹,于是不少IDL为了绕过这个问题,创造了“序号/字段编号”这种本不应该存在的语言标识。
此外,传统的 IDL 一旦遇到参数的缩小,或者参数类型的更改,IDL 所生成的 RPC 框架的接口代码,则往往须要两个独立的接口处理函数,能力同时解决新旧两个版本的数据。所以,为了更加“优雅”的解决参数的删除和类型的扭转,局部 IDL 加强了对“序号/字段编号”这种本不应该存在的语言标识的依赖。字段编号不可反复,一旦确定了,便不能批改。于是字段编号的保护,便成了开发者历史包袱的一部分。
而后对于不定类型的参数,反对该个性的 IDL 会选用 Oneof 或 Union 等来实现,而剩下的 IDL 则间接弃疗。
至于不定长参数,相似于 C 语言 printf 那样的参数,这对简直所有的 IDL 而言,均是噩梦般的存在。因而对于不定长参数的反对,简直所有的 IDL 都是以弃疗完结。
在通过漫长的摸索和泛滥我的项目的历练后,咱们发现,对 IDL 的应用,其实不用如此苦楚。
咱们抉择了 IDL-less的计划,让 IDL 回归到纯正的接口形容。此时,IDL-less 的计划,不仅能轻易反对可选参数、可变类型参数,也能很好的反对参数的增减,和不定长参数。
在这个前提下,咱们应用 IDL-less 设计了新的 RPC 框架。在这个新的框架下,咱们发现,其实咱们尽管也可能依据新框架的 IDL 生成客户端和服务器的框架代码,但在更换思路后,须要生成的代码其实异样的简略,甚至对于手写来说,也齐全不是累赘。此时,如果对于传统的 RPC 框架,IDL 曾经默化为依据 IDL 文件,生成接口和框架代码的话,咱们不仅做到了 IDL-less,甚至咱们能够称为:无 IDL。
咱们的新的 IDL-less 的 RPC 框架:FPNN:Fast Programmable Nexus Network
与动则生成上万行框架和接口代码的传统 RPC 框架和 IDL 而言,因为 FPNN 框架设计之初,便将竭力简化应用复杂度作为外围设计指标之一,所以 FPNN 框架简直没有什么代码须要依据 IDL 形容生成。
首先,是框架的代码。
参考 FPNN 服务端根底应用向导“1. 服务代码框架” 将发现,无论开发 TCP 服务还是 UDP 服务,算上括号与空行,规范框架一共也才 3 个文件总共 41 行代码。其中 “DemoServer.cpp” 24 行,“DemoServer.cpp” 15 行,“DemoQuestProcessor.cpp” 2 行。而参考 FPNN 客户端根底应用向导 就会发现,算上括号与空行,客户端残缺框架一共就 8 行代码,而以下 17 行代码(算上括号、空行、正文)曾经能间接运行,并向服务器发送申请,并接管应答:
#include <iostream>#include "TCPClient.h" using namespace std;using namespace fpnn; int main(){ std::shared_ptr<TCPClient> client = TCPClient::createClient("demo.example.com", 6789); //-- 生成申请数据 FPQWriter qw(1, "echo"); qw.param("feedback", "Example string."); FPAnswerPtr answer = client->sendQuest(qw.take()); return 0;}
所以,对于 FPNN 框架,简直没有什么代码是须要根据 IDL 形容文件生成的。
而后,是接口和对象的解决。
假如 IDL 中规定了一个接口 demoInterface 和一个构造体 DemoData,编译成 C++ 代码可能是以下模式:
struct DemoData{ int demoInt; double demoDouble; std::string demoString;};void demoInterface(int firstValue, const std::string& secondValue, const struct DemoData& demoData);
而在 FPNN 中,无论任何接口,接口均对立为以下模式:
FPAnswerPtr method_name(const FPReaderPtr args, const FPQuestPtr quest, const ConnectionInfo& ci);
那对于根本类型参数的获取,FPNN 仅只是多了一步:
int firstValue = args->getInt("firstValue");
和
std::string secondValue = args->getString("secondValue");
而对于构造体,也是仅多一步(但从 FPNN 的理念上,构造体间接做为接口参数的这种设计,因为对构造扩大的兼容性极差,十分不举荐):
DemoData demoData = args->get("demoData", Demodata());
而对于构造体成员的获取,应用 IDL 生成的框架代码:
double value = demoData.demoValue();
间接应用 FPNN 框架:
demoData.demoValue;
没有本质性差别。
所以,对于 FPNN 这一 IDL-less 的 RPC 框架而言,尽管能够依据 IDL 文件生成对应的框架和接口代码,但简直毫无意义。因而,FPNN 框架也不会提供依据 IDL 文件生成对应的框架和接口代码的工具。