今天我跟几个RPC框架之间发生了一些事,情节跌宕起伏一波三折,不吐不快,以至于我这个从来不写博客的人也忍不住写下来分享一下。

背景

主系统部署在Windows上(.NET 4.5),子系统(.NET CORE)部署在各种Linux上(Ubuntu/CentOS/RHEL等),两者之间通信用RPC框架。由于涉及到文件的读写(目前最大是4M,不过不排除增大的可能),因此选用了基于Protocol Buffers的GRPC框架,毕竟PB的序列化效率在请求比较大的时候还是很重要的。在功能已经完成之后,发现如果项目中包含了Google.ProtocolBuffers这样的依赖的话,有可能会被公司内部的工具扫描到然后被challenge(之前我就觉得友商的名字好扎眼哈哈),为了避免不必要的麻烦,最好还是不要用它了。由于项目中别处已经采用了thrift,所以首选还是用thrift。有人要说了既然之前就用了thrift,你这次怎么还用GRPC你是不是傻?其实用GRPC是因为真的很适合这个场景,而且2个thrift又必须版本不同(历史遗留问题)会有一个很恶心的坑,就是为了避免这个坑才用的GRPC。结果现在还是要改回thrift,又要用一个workaround 去解决双thrift版本的坑……头疼!话不多说,LET'S GO。

THRIFT从入门到放弃

第一个拦路虎就是现在nuget上的THRIFT版本真是五花八门,官方那个package已经3年没更新了,不少热心网友自己编的版本不过都不支持NETCORE,只有一个叫 apache-thrift-netcore的版本可用,并且上传者是官方package的维护者之一,感觉是个尝鲜版。该版本的thrift compiler依然只支持-gen csharp而不是官网上的-gen netcore。其实不管哪个,生成出来的要么是同步方法,要么就是原始的BeginXXX EndXXX,因为他们压根就没打算支持async(这个issue开了4年了啊我的哥)。这样的话我得在调用的地方包上一层Task.Run,因为之前实现的代码都是异步,真是恶心……效率低点代码难看点,算了忍吧。

改了一会发现传输压缩没找到实现方式,心想没道理应该有的啊,查了下果然有TZlibTransport ,但是只支持Java Python等(貌似就C#没有),这个功能不是netcore版本阉割的而是从来就没有,没有压缩的话传输效率会低很多,只能先加个TODO,回头有空自己实现一个压缩的子类了。挺生气的感觉C#对于THRIFT真是二等公民,各种缺功能各种不更新。

其实server挺快就改完了,然后开始改client,虽然有上面说的不完美,但是都不是block issue。Client的话之前GRPC的client是thread safe的所以都是复用的,thrift的client查了下是线程不安全,只能给每次会话创建一个新的client……等等,每次创建一个新的?然后构造函数里每次还都创建一个socket?那调用方的端口还不爆炸?简单测试了一下,如果真的这么干的话会跟HttpClient不复用一样有端口TIME_WAIT问题(即使正确Dispose),在高峰时期端口会被用完然后崩盘(我估计现在项目里另外一个用thrift的地方就有这个问题)所以必须得用一个连接池……对不起C#又没有,只能自己写!掀桌!

再看GRPC

冷静了下来,要么还是用原来的方案算了,上面这么多问题就算都解决了或者忍了,还要经受稳定性考验。毕竟apache-thrift-netcore的package description里作者自己都写着 “use with caution…” 这省略号我真是无比的虚,检查了下GRPC的几台服务器已经run了好几个星期了一点问题都没。或者换个RPC框架吧,反正不是thrift就行。等等为什么要换RPC框架,我们只是不要protocol buffers而已,所以只要不用它做序列化就可以了。但是GRPC能脱离PB吗?确实有可能可以,一个是RPC,一个是序列化,完全可以把PB设计成RPC的一个功能模块或者说插件,而不是依赖绑定的关系。看了一眼GRPC,发现了新世界……GRPC居然不依赖PB!是的,我没有看错,GRPC不依赖PB!虽然跟我刚才想的一样,但是你怎么能不依赖呢,你对得起官网上的描述吗 Define your service using Protocol Buffers, a powerful binary serialization toolset and language?你对得起IDL compiler生成的文件吗,里面全是Google.Protobuf这个namespace下的类?那就把PB依赖删掉吧……果然编译不过,能过才见鬼了……可能,GRPC不依赖PB只是因为手抖搞错了?不应该啊,从设计角度来讲,他们也确实不应该绑定,不过移除了PB也确实会编译不过是事实……等等!编译不过的其实都是IDL compiler生成的文件,这些文件里包含了序列化的行为所以是依赖PB的,移除PB必然会编译错误!所以说如果有其他的序列化组件的话,应该有自己的IDL compiler才对(甚至有自己的IDL语法),这样的话就说的通了,GRPC应该是这种设计思路。问题是……谁没事去给你做一个序列化组件啊,你都已经有PB了啊!

Bond拯救世界

抱着不见棺材不落泪的心态我随便搜了下,我去还真有个叫 Bond 的项目,看了下IDL,简直良心!各种贴心小棉袄,简直就是为C#量身定做的!尝试了几个demo,立刻就爱上了它!不仅更新很及时昨天还发布了一版,而且到处都能感受到C#是被放在第一位的,之前thrift带来的不爽一扫而光!顺便说一下Bond 也带一个RPC框架叫 Bond Comm,不过官方说Comm已经废弃了推荐配合GRPC试用。所以我把原来PB的几个IDL文件用Bond的规范重写一遍,其他什么都不用改就可以了……完美啊完美!

结语

其实我主要是想说,thrift真心不适合C#,如果你还在用它,赶紧去看看有没有我上面说的那几个问题。就算没有,还是早日改成GRPC吧,从C#角度看GRPC各方面体验都好很多。序列化的话可以用PB,但是我知道Bond以后我都会选择Bond,因为它的IDL更适合C#(毕竟微软家产品肯定.NET优先)。光从IDL来说,thrift还是比PB更好用一些,尤其是PB现在都用proto3了,proto2正在淘汰中一些功能马上就被砍掉(比如optional field),PB的IDL是简单至上,大道至简的感觉(可能也正是这个原因它的多语言支持很好吧)。Bond的IDL光从C#的角度来讲是比PB和thrift要强大的(居然还支持attribute你敢信),但是有得必有失,可能因为这些它对一些其他语言的支持就未必友好(这个是我猜的)。

说完IDL再说说RPC,这方面GRPC真心完爆Thrift。Thrfit的各种 Transport/Protocol 以及你能看到的任何 Txxx 类,看起来可以高度自定义实则是过度设计,因为很少有人去实现自己的 Transport和Protocol,RPC框架应当把这类细节对使用者隐藏掉。GRPC这方面做的很好,HTTP2足够标准,足够效率,足够应付可见的未来出现的其他可能,使用者只要关注自己的实现就可以,传输协议什么的,RPC框架会帮我做的很好,至少肯定比我自己做的好,我不需要关心,我只要相信它就可以。还有一个我印象很深的就是Error handling。GRPC中只有一种Exception就是RpcException,用StatusCode进行分类。服务器端无论过程中是什么Exception,在出口处catch然后根据类型赋予不同的StatusCode和Message。就算请求根本没有到服务器端,client端也会有对应的RpcException,这样在client端handle起来就很舒服很统一。Thrift允许自定义Exception,看起来很强大,但是为了client端处理起来容易,通常还是得有一个GeneralException(或者间接达到这个效果),往往最终这个GeneralException就是GRPC里的RpcException的作用。当请求无法达到服务器时,Thrift client端的异常就是五花八门了,完全取决于transport,各种各样的花式Exception,然而client端其实只需要知道是connection failure就可以了。

总的来说thrift给我的用户体验很差,就如同几年前一样,这么多年一点长进都没(事实上确实也没有新版本,无奈~) GRPC+PB 是更好的选择,对于C#来说GRPC+Bond可能会更适合。以上结论只针对C#/.NET,对于其他语言的开发体验不做评价。

RPC的故事的更多相关文章

  1. Redola.Rpc 的一个小目标

    Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标:20000 tps. Concurrency level: 8 threads Complete requests: 20000 ...

  2. 你应该知道的 RPC 原理

    作者:伯乐在线 - meituanalibaba   网址:http://blog.jobbole.com/92290/ 在校期间大家都写过不少程序,比如写个hello world服务类,然后本地调用 ...

  3. 一个故事讲清楚NIO

    转载请引用:一个故事讲清楚NIO 假设某银行只有10个职员.该银行的业务流程分为以下4个步骤: 1) 顾客填申请表(5分钟): 2) 职员审核(1分钟): 3) 职员叫保安去金库取钱(3分钟): 4) ...

  4. 你应该知道的RPC原理

    你应该知道的RPC原理 在学校期间大家都写过不少程序,比如写个hello world服务类,然后本地调用下,如下所示.这些程序的特点是服务消费方和服务提供方是本地调用关系. 而一旦踏入公司尤其是大型互 ...

  5. 基于RPC原理的dubbo

    在校期间大家都写过不少程序,比如写个hello world服务类,然后本地调用下,如下所示.这些程序的特点是服务消费方和服务提供方是本地调用关系. 而一旦踏入公司尤其是大型互联网公司就会发现,公司的系 ...

  6. RPC 原理的前生今世

    (如果感觉有帮助,请帮忙点推荐,添加关注,谢谢!你的支持是我不断更新文章的动力.本博客会逐步推出一系列的关于大型网站架构.分布式应用.设计模式.架构模式等方面的系列文章) 在校期间大家都写过不少程序, ...

  7. C# 的轻量级 RPC 框架

    Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标 Redola.Rpc 的一个小目标:20000 tps. Concurrency level: 8 threads Comple ...

  8. 一个故事讲清楚NIO(转)

    转载请引用:一个故事讲清楚NIO 假设某银行只有10个职员.该银行的业务流程分为以下4个步骤: 1) 顾客填申请表(5分钟): 2) 职员审核(1分钟): 3) 职员叫保安去金库取钱(3分钟): 4) ...

  9. 那些年,我们追过的RPC

    1974年冬,互联网大师 Jon Postel发表了RFC674:“Procedure Call Protocol Documents,Version 2”,尝试定义一种在包含70个节点的网络中共享资 ...

随机推荐

  1. 【Android学习】Service&Boradcast初步

    Service初步 掌握Service概念 掌握Service分类 Service开发能力具备 了解Service和intentService类的区别 重点难点 StartService和BoundS ...

  2. KindEditor上传图片

    <script type="text/javascript"> KindEditor.ready(function(K) { var editor1 = K.creat ...

  3. C/C++ Pthread线程

    线程按照其调度者可以分为用户级线程和核心级线程两种 用户级线程主要解决的是上下文切换的问题,它的调度算法和调度过程全部由用户自行选择决定,在运行时不需要特定的内核支持: 我们常用基本就是用户级线程,所 ...

  4. 【转】生活中的OO智慧——大话面向对象五大原则

    原文地址:http://www.cnblogs.com/aoyeyuyan/p/4388110.html 一·单一职责原则(Single-Responsibility Principle) 定义:一个 ...

  5. Windows store app[Part 4]:深入WinRT的异步机制

    接上篇Windows store app[Part 3]:认识WinRT的异步机制 WinRT异步机制回顾: IAsyncInfo接口:WinRT下异步功能的核心,该接口提供所有异步操作的基本功能,如 ...

  6. SOLR企业搜索平台 二 (分词安装)

    标签:linux lucene 分词 solr 全文检索 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://3961409.blog ...

  7. C# 编写的SqlServer 数据库自动备份服务,带配置,功能强大

    数据库自动备份服务,带配置,还算可以吧 周末抽时间,编写了一个这样的工具,可以让,对数据库不了解或不熟悉的人,直接学会使用备份,省时省力,同样,我也将一份,通过脚本进行备份的,也奉献上来, 通过sql ...

  8. linux 磁盘保留空间设置

    1.查看保留空间大小 tune2fs -l /dev/sda8  reserved blocks 2.调整保留空间大小(系统默认5%) tune2fs -m 0.5 /dev/sda8 参考链接:ht ...

  9. 本地Windows环境下安装MySql

    Windows 上安装 MySQL Windows 上安装 MySQL 相对来说会较为简单,你需要在 MySQL 下载中下载 Windows 版本的 MySQL 安装包. Download Link: ...

  10. Logstash 收集 IIS 日志

    日志样例 查看 IIS 日志配置,选择格式为 W3C(默认字段设置)保存生效. 2016-02-25 01:27:04 112.74.74.124 GET /goods/list/0/1.html - ...