Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

关于 Hprose 3.0 通信协议设计方案的讨论 #6

Open
andot opened this issue May 9, 2018 · 30 comments
Open

关于 Hprose 3.0 通信协议设计方案的讨论 #6

andot opened this issue May 9, 2018 · 30 comments

Comments

@andot
Copy link
Member

andot commented May 9, 2018

Hprose 从 2008 年开始设计开发,从最初仅支持 HTTP,到后来增加了 WebSocket、TCP、UnixSocket 支持,以及在 Hprose 2.0 中增加了服务器推送等功能。这些都是在最初通信协议的设计下完成的。并未对通信协议本身做以修改。因此,Hprose 2.0 实现可以完全兼容 Hprose 1.x 的实现。

虽然 Hprose 2.0 目前已经满足了大多数开发者的需求,但是本着可以做到更好的目的,特开此贴,希望能够汇总广大 Hprose 开发者、用户及爱好者的意见建议和新需求,如果可能,将会把它们添加到下一版的 Hprose 3.0 中去。

所以,如果你有什么新的需求和想法,请不要吝啬你的文字,你可以开一个新的 issue,把你想说的内容用最详尽的方式表达出来,包括需求描述,应用场景描述,现有协议和实现为何无法满足,具体的改进和实现的细节等等。之后,你可以将你开的 issue 发到本贴的回复中。具体做法可以参照二楼回复。

格式不要求统一,但是请不要一句话带过,不然除了你自己,谁也不知道你想要的是什么。

欢迎大家积极参与讨论。

@andot
Copy link
Member Author

andot commented May 9, 2018

@xiaoqingping
Copy link

可不可以内置客户端认证功能?

@orangeagain
Copy link

要是能把一段byte[]数据变成本地调用就好了,谢谢

@andot
Copy link
Member Author

andot commented May 10, 2018

@xiaoqingping @orangeagain 你们的需求描述太过简单,我无法从中捕获到可以理解的信息。

你们可以把需要和解决方案写的详细一些吗?可以参照上面几个连接中的方案来写。

@tang6723
Copy link

最好支持Angular。感觉十分好。还有能不能支持聊天功能。

@Leither
Copy link

Leither commented May 10, 2018

Angular还是不要支持的好,耦合过重

@MiyamuraMiyako
Copy link

@pavlelee
Copy link
Member

我觉得3.0的通信协议会是一个Hprose protocol,基于这个协议会有多个现实方式,比如刚才 @MiyamuraMiyako 提到的KCP或者tcp、mq等。Hprose protocol应该抽象比较高级的功能,比如双向流、限流等。

@Lao-liu
Copy link

Lao-liu commented May 10, 2018

@MiyamuraMiyako KCP 是用在 UDP 基础上实现了 TCP 吧,基于这种方式用 RPC 也太小众吧 -____- !!

@MiyamuraMiyako
Copy link

@Lao-liu 一方面是响应小马哥对于速度的追求,另一方面小众不小众其实无所谓,只要好用咱们就用咯。rpcx也用了kcp

@Lao-liu
Copy link

Lao-liu commented May 10, 2018

@MiyamuraMiyako 嗯,估计主要是 rpcx 是基于 golang 的并且已经有了 kcp-go, 所以比较容易吧。但是 Hprose 目前支持的语言比较多。如果只是基于 KCP 来跑 Hprose 服务的话,完全可以用 kcptun 架好内网,在它基础上去跑就行了。

@MiyamuraMiyako
Copy link

@Lao-liu 你这么一说,我发现这么做也不错。没必要再单独实现了。

@andot
Copy link
Member Author

andot commented May 10, 2018

@tang6723 Angular 本来就支持。已经有好多人用 Angular + hprose 开发项目了啊。

@Lao-liu
Copy link

Lao-liu commented May 10, 2018

能不能增加对 IDL 接口描述语言的支持。有点类似 WSDL 一样的,可以用来描述发布接口的入参、返回数据的类型等。这样便于开发 ^_^

@Lao-liu
Copy link

Lao-liu commented May 10, 2018

@tang6723 https://www.jianshu.com/p/9758f28d7ba2 Angular 下直接用就行。

@andot
Copy link
Member Author

andot commented May 10, 2018

@Lao-liu IDL 比较难,而且 Hprose 最初就是为了实现不需要 IDL 的 RPC。在类型映射上 IDL 不够自由灵活,还会引入不同版本的接口不兼容的问题。要设计一套可选的,可以自由定义类型映射的,不会产生不同版本接口不兼容问题的 IDL 目前我没有想到该怎么来做。你可以试着来设计一套。

@andot
Copy link
Member Author

andot commented May 10, 2018

@MiyamuraMiyako 这个可以有,不知道对 Hprose 通信协议部分需不需要做修改。最好补充一下。

@MiyamuraMiyako
Copy link

@andot 不需要改已有协议。只是底层协议的更换。

@andot
Copy link
Member Author

andot commented May 10, 2018

@MiyamuraMiyako 好的,那实现上就更方便了,基本上替换一下连接方式就行了。

@Lao-liu
Copy link

Lao-liu commented May 10, 2018

@andot 我想简单了,我的想法只是增加一个通过反射方式对当前对外发布接口进行描述的功能。没考虑你那么复杂。 ^_^

@orangeagain
Copy link

想用在游戏上就必须有个接口,input
向kcp那样纯算法,不关心底层协议,input数据,得出结果,而不关心底层什么协议,这样才有通用性.
就像车可以换轮子一样,能换自制轮胎的车才能当赛车,才有性能,才有自定义的余地

@andot
Copy link
Member Author

andot commented May 10, 2018

@orangeagain 底层的传输协议可以是 HTTP、TCP、WebSocket,也可以换成 KCP。这些底层的用于传输的协议就是可以换的轮子。hprose 的远程调用、推送、反向调用部分的协议并不是轮子,它是整个车的主体部分。

@OliverZou
Copy link

应强调一致性,虽然是跨语言的,但是文档却是各个语言分开的,反而模糊了跨语言一致性的优点(如果跨语言不一致是很糟糕的结果)。文档应只有一份,强调一致的部分,不一致的部分不必要强调,语言只在示例中体现不同。文档一份只是形式要求,实质希望让使用者在概念上理解不同语言有一致的实现,不会有意外,从而放心使用。如果现有各个语言实现有不一致的,可以通过同一份文档来规范一致性,达不到基本一致性要求的语言可暂时放弃。另,我现在用protobuf+hprose在项目中解决问题,使用protobuf是因为有可持续的跨语言规范维护要求,也就是说项目需要idl来维护各个语言一致性,因为维护起来方便,idl有其实际意义,当然这和hprose无冲突,但却可以说明无需idl不能说是个太大的优点。grpc能支持的语言已经涵盖主流,golang和javacript已经支持的很完善了,hprose如果能在工程角度给开发带来更多帮助,使用更方便简洁,才能具备更大优势。

@andot
Copy link
Member Author

andot commented Jan 4, 2019

@OliverZou,你的想法很好,我当初也是打算把文档只写一份的,所以当初建了这样一个项目:https://github.com/hprose/hprose-doc
但是后来在实现的过程中发现,每种语言其实都有自己的特色和风格,我没有办法对各种语言都采用同一种方式来实现。但是每种语言实现的 hprose 序列化协议和 RPC 协议是一致的,这保证了不同语言之间的互通性。在 API 实现上,虽然不同语言采用的实现方式不同,但是对用户提供的接口在不同语言之间,在相同的大版本号下,基本上保持了一致性或相似性。但是尽管如此,把不同语言的 API 接口文档不区分语言的放在一起,仍然是不合适的。毕竟不是所有的人都精通所有语言,在一份文档里把所有语言的例子都列举一遍,会给只使用某种或某几种语言的用户造成困惑和阅读障碍。而且在我所知道的跨语言 RPC 中,比如 GRPC 和 Thrift,它们的文档也是各个语言分开的,并没有不区分语言的写成一份。所以我觉得更合理的方案是,序列化协议和 RPC 协议只有一份,这个肯定不能每个语言一份,目前也是这样做的。各个语言具体实现的 API 文档,各自一份,但是可以汇总到一个入口处,比如 https://github.com/hprose/hprose-doc ,这个目前也是这样做的。最后就是关于不同语言之间在实现上共同的概念部分(比如序列化,反序列化,客户端,服务器,中间件等等),可以写一份共通的文档,也就是你说的那份一致性的文档,这部分目前还没有,我打算为 hprose 3.0 增加这部分内容。

@andot
Copy link
Member Author

andot commented Jan 4, 2019

hprose 3.0 除了最上面列举的那三个新功能以外,还会对之前的 RPC 协议部分做一些精简,取消一些使用率很低,但是实现却很复杂,甚至影响可扩展性的内容,比如目前已经决定要取消的包括“引用参数回传”和“批处理调用”。

Hprose 3.0 在实现中,还会将原来在核心中紧密耦合实现的功能,单独拿出来,作为中间件或编码器来实现。

比如 Hprose 协议编码解码部分在 hprose 3.0 中会作为单独的可替换编码器实现,这样可以通过替换编码器来直接将 Hprose 客户端、服务器变身成 JSONRPC 客户端、服务器,当然也可以替换成 protobuf、msgpack 等编码器,只要用户喜欢,用户可以替换成任何它们喜欢的编码器。

原来的单向调用,故障转移,推送等功能也单独做成了中间件,用户需要的时候,可以挂载它们,不需要则不需要挂载它们,这不但让 hprose 客户端和服务器的核心变得更小更快更稳定,而且更具有可扩展性。

当然,有得必有失,目前 Hprose 3.0 最大的问题是跟旧的版本(1.x 和 2.x 版本)不再完全兼容,因为增加了独立的头部,并取消了引用参数回传和批处理调用,所以在旧系统升级时,不能像原来一样可以单独升级客户端或服务器端,只能客户端和服务器一起升级。当然,这在实际使用中,应该也算不上一个太大的问题。

@OliverZou
Copy link

几个建议:

  1. 感觉3.0走偏了。我的建议是在2.0的基础上改善现有使用问题。做到解决核心用户需求。实现的美观问题,以及插件问题只是解决开发问题而不是rpc基本需求问题。
  2. hprose 3.0如果不能尽快实现大部分语言,而2.0的客户端没法完全调用3.0的服务,建议3.0另立山头和2.0严格区分开来,2.0作为LTS长期存在和维护。
  3. hprose的一个优势,不是效率,而是在客户端接口声明、定义再完成后,使用起来的自然性,便利性,和使用一个本地函数一样。grpc的缺点就是不自然。这个优势目前我只看到ZeroC Ice另有做到。
  4. 然而,客户端必须再声明和服务器一样的接口,这个问题是要命的,我想可能是假设了客户端和服务器的使用者是同样的团队。先不论大量接口的情况,rpc接口暴露出去,未必是给自己人用,即使是同一个公司内部。举个栗子,应用场景需要使用各种语言客户端,居然每种语言的客户端的rpc接口定义都得在客户端手工定义一遍,先不论每种语言的定义差异,就算定义出来了,要改得麻烦死了。所以,在没有idl生成一致接口定义的情况下,hprose难以使用。建议,以protobuf idl文件为中间类型和接口定义文件,实现工具,把各个语言下的hprose客户端所需引用的rpc接口本地定义文件生成出来,便于客户端直接使用。同时也能部分解决grpc的迁移问题。
  5. 推送方面,其实未必hprose自己实现,可以使用同样是支持多语言的nats等消息推送系统,当然,如果hprose有时间实现的不错也是可以的,现实是没太多时间。
  6. hprose的远期目标,应该是忽略网络存在感的rpc调用解决方案。不在网络问题中纠缠。

随笔,仅供参考。

@OliverZou
Copy link

另注:pb生成的各个语言类型和接口定义文件,在hprose中并不能直接适用,pb并不是简单生成pojo对象,命名也各有差异,需要做大量适配工作。

@andot
Copy link
Member Author

andot commented Jun 10, 2019

1、如果有些问题能够直接在 2.0 的基础上解决,就不会写 3.0 了。3.0 的目标就是把核心精简,把 2.0 里面太多不必要的东西从核心中去掉。保持核心的最简化,其它的功能都通过插件方式实现。

2、2.0 不能调用 3.0 的某些服务并不是单纯在 2.0 上升级就能解决的。比如 3.0 中,跟 TCP 绑定协议的修改,增加了 UDP 绑定协议,对推送协议的修改等等。这个即使在 2.0 上升级,也会造成老的 2.0 版本跟新的 2.0 版本不兼容。所以才会升级大版本号到 3.0,目的就是跟 2.0 严格区分开。

3、hprose 的设计目标是不需要定义服务接口。客户端接口的存在也仅仅是方便客户端调用,而不是为了跟服务器接口统一。这跟那些用 IDL 来统一接口的 RPC 服务从设计上就是不一样的。

4、对于 hprose 来说,不是所有的客户端语言都需要定义接口。事实上,大部分语言其实是不需要在客户端定义接口。需要定义接口才能方便使用的语言只有 Java、C#、Go 这类强类型语言。而对于 PHP、JavaScript、Dart、Ruby、Python、Lua 等等这些弱类型语言,客户端可以直接使用最自然的方式去调用,而不用定义什么接口。另外,hprose 的服务是不需要基于接口实现的,所以说『客户端必须再声明和服务器一样的接口』这是个伪问题,事实情况是,对于 hprose 来说,服务器端根本不需要专为 RPC 来定义任何接口,而客户端为方便调用而定义的接口也完全不需要跟服务器端的服务实现一样。即便是 C# 这种强类型语言,hprose 也强烈不推荐在客户端定义跟服务实现一致的接口,而是希望用户在客户端定义方便自己调用的接口,举个简单例子,比如 Hello 这样一个函数,服务器端实现可能是这样的:

public string Hello(string name, ServiceContext context) {
    return "Hello " + name + " from " + context. RemoteEndPoint.toString();
}

而客户端的接口方法定义最好是这样的:

Task<string> Hello(string name);

这个调用接口跟服务器的实现接口完全不一样,服务器端同步实现的接口,客户端可以异步调用,服务器端有一个环境变量参数,而客户端完全不需要关心它。

如果用 IDL 来强行统一客户端和服务器端的接口,只会让服务器端不方便实现,让客户端也不方便调用。

这就是 hprose 为什么不使用 IDL 来统一接口的原因。

5、推送在 3.0 中不属于核心的功能,是以插件方式提供的。

6、这个目标是一致的。

@OliverZou
Copy link

OliverZou commented Jun 11, 2019

谢谢回复。

关于服务接口,我也了解使用hprose的确是不需要定义服务接口的。”客户端必须再声明和服务器一样的接口“确实不准确,不能用’必须‘。这个问题应该这么来说:

1.客户端需要获取接口信息。客户端虽然可以不用定义接口,但需要获取接口信息。客户端和服务端开发协作中(不一定是一个团队或者公司,客户端甚至是客户),如果没有接口定义,客户端是如何知道服务端提供了哪些服务?如何了解具体api形态如何?即使是弱类型语言,依然也要知道有哪些什么样的服务吧。通过文档?文档不具备强制性,开发过程中难以保证客户端和服务端的一致性。(这种”一致性“,仅要求连接点和输入输出类型信息,未必要求调用方式的一致性,异步或者同步调用方式并不影响一致性),而idl本身就是语言无关的精确文档。

2.接口便利性是重要的,虽然不用定义接口,但如果使用不方便,不用定义接口就不能成为一个优势。而hprose的一个重要优势是接口使用便利。对于强类型语言,如果要自然方便的使用rpc接口,就需要在客户端定义接口。一般情况下没什么,但在客户端不是服务器团队甚至是客户的团队的情况下,接口易用性是要满足的,总不能让客户全调用invoke,从用户角度,invoke接口并不便利。总之,在接口多,语言多且持续维护的情况下,客户端为了便利性从而需要定义接口,但这样存在较大的不便性。

另外,以IDL来定义接口,未必就是强行统一客户端和服务端的接口的方方面面,idl的客户端代码生成完全可以存在多个版本的调用形式,定义相同的结构对象在不同语言的接口中,可以使用map或者object或者struct对象,依语言情况而用,hprose的io可以保证解析成功。所以idl编译生成的代码,只需要客户端能够访问到服务端的这个函数,服务端能获取参数即可,两端的调用参数形式上不要求一定完全一样,但接口信息类型信息总是要有的,这是idl编译工具的实现问题。必要的话在idl编译参数明确指定即可。

一家之言,仅供参考。

@andot
Copy link
Member Author

andot commented Jun 11, 2019

既然你的目的是通过 IDL 来生成不同语言的客户端调用接口。那么这个东西其实是独立于 Hprose 本身的一个附加工具,而不必作为 Hprose 的核心实现。后期如果有人有能力能够完成这个东西,就可以直接拿来用,并不需要对目前的 Hprose 做任何修改。

而且我不认为 IDL 可以有这种灵活的能力,如果有的话,那么早就有了,为什么这么多年,那么多大厂都没有做出这样的东西来?如果您认为这是一件很简单的事情,您完全可以按照您心目中最理想的方式来实现,然后把这个工具开源出来分享给大家,我觉得我没有这个能力。

我认为就算是客户端跟服务端不是一个团队的,那服务器的开发为了方便客户端调用,来为客户端定义一个可以方便使用的接口,也不是一件很难的事情了。这比起现在的 SDK 开发方式,要为每种语言都要单独编写客户端的 SDK 比起来,只要为几个强类型语言编写不同的调用接口定义不是已经方便很多了吗?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants