设计目标:
- 支持跨平台的RPC调用
- 服务提供者自动注册服务元数据
- 服务消费者依据服务元素据根据连接均衡原则选取服务提供者
- 服务消费者在提供者不可用时自动切换提供者源
- 仅支持局域网
- 以小型企业应用为设计目标,考虑并发性能,服务数量规模不作为核心考量对象
- 面向小数据量服务接口,不支持大于100k的服务通信
设计描述:
组件构成:(1)服务发布端插件(2)服务消费端插件(3)RPC服务监控服务
设计思路:
- 由于跨语言平台,底部通信协议需要独立于语言平台类似与RMI或者net remoteing特性话的能力不做主要实现考量,但是识别为同平台时作为优化服务协作手段
- 保持调用一致性,不同语言平台的命名与使用规范保持一致
- 基于NIO设计服务提供方,提供者与消费者相互通过长链接通信,考虑路由控制以及服务端口映射的问题采用软路由。
- 为保证服务的可用,在提供者和消费者均增加心跳探测,在消费者增加服务无缝更替,除非是单例服务,否则在消费者可能会有服务延时但不出现服务不可用情况。
实现项目:
地址:https://github.com/hayaaAI/RPC
状态:第一迭代完成
迭代笔记
第一迭代:
- 基于先实现后优化的路径,通信插件使用第三方软件。net原计划使用SuperSocket,java使用netty。消费者和服务提供者通过长链接保持通信,服务提供者以NIO模式提供服务。经过测试发现SuperSocket不支持netcore平台,只好net平台自己使用socket实现NIO服务端以及使用TCPClient实现客户端。
- 在RPC的消费端,net的服务对象代理使用emit生成,java则基于javassist动态生成代理对象,此对象需要在程序启动初期构建,所以会造成程序的启动延迟。
- 使用远程配置插件进行系统配置。
- 系统设计目标为小型规模服务集群,在保证高可用前提下服务尽力小型化轻量化,并且由于已有的服务管理框架具有和zookeeper类似功能,服务注册与服务列表拉取不引入zookeeper作为服务发现与管理中心。
- 由于不同语言平台的序列化方式以及数据格式有差异,需要进一步将通信数据抽象,这里通过json,不过有部分的性能损失,后续根据使用需求进行迭代优化。
- 通信队列的处理使用简单的消费者模式依据不同的硬件构成存在着代码实现逻辑上的差异。如果基于多核cpu架构,可以应用多线程提高程序响应速度,但是如果面对的是单核那么多线程的切换将是性能的灾难,一个线程中的一个长链接处理队列的速度优势甚至更好。
- 基于netty实现client时碰到一个问题,如果每一个服务都实现一边netty的client流程,那么需要很多的线程,资源和性能损耗太大。而如果都实现在一个过程中,如果其中一个服务的服务端不可用,那么会造成client无法启动,选择后一种实现。
- java是大端数据存储,C#则是小端数据存储,二者数字类型转换的byte数组首尾是反的。而基于UTF8编码的字符串在数据存储上不需要在网络通信上进行大小端转换。
- 通过在头位置设置长度解决粘包与分包问题。
- Javassist动态生成代理类的时候,在方法中引用其他jar或者系统的类时需要将包路径也包括进去,否则即使设置了ClassPool的路径会有no such class的错误,同时泛型表达会有语法错误,需要避免使用泛型表达的语句。
- 时间类型在java平台json序列化是长整型,而C#平台json序列化是字符串时间,二者反向序列化不兼容。
- 在单核硬件环境下netty会由于线程的上下文切换而比多核硬件有更多的无效时间损耗。
第二迭代:
- 语言技术类似的跨平台软件架构设计可以通用,甚至产生的问题也类似。