内容简介:关于 RPC 细节点,可以阅读在项目刚启动阶段,网关项目会通过 zk(路由管理系统) 进行路由寻址(寻找相应后端组的服务),后续网关项目就能和相应的后端组的服务直接通讯了。在初始化阶段,网关项目会将项目信息、注册区域(异地多活)、所需服务等传递给
RPC 是什么
- RPC(Remote Procedure Call) 译为远端过程调用。即在一台机子上能调用到另外一台机子上的服务;
- RPC 可以基于 HTTP 调用也可以基于 TCP 调用。基于 TCP 调用性能更佳,但是实现也更为复杂;
- RPC 通常要实现两部分协议,一个是应用层协议(如 JSON),一个是用来传输数据的通讯层协议(如 Dubbo);
RPC 调用过程
- 调用方通过本地网关调用相应服务;
- 网关将服务名和参数封装为 RPC 对象传给客户端 RPC 框架(@dwd/noob-client);
- 客户端 RPC 框架(@dwd/noob-client)将数据转化成二进制形式,然后以 TCP 的形式传递给服务端 RPC 框架;
- 服务端 RPC 框架将二进制数据反序列化为 RPC 对象,并交由服务方处理,服务端处理后返回结果;
- 执行上述阶段的逆序操作;
关于 RPC 细节点,可以阅读 聊聊 Node.js RPC(一)— 协议
结合代码分析 Java 包的调用过程
初始化阶段 —— RPC 调用之前
在项目刚启动阶段,网关项目会通过 zk(路由管理系统) 进行路由寻址(寻找相应后端组的服务),后续网关项目就能和相应的后端组的服务直接通讯了。
在初始化阶段,网关项目会将项目信息、注册区域(异地多活)、所需服务等传递给 @dwd/noob-client ,相关代码如下:
import { Client } from '@dwd/noob-client' const client = new Client({ application: { // 项目信息 name: config.name, }, registry: config.registry, // 注册区域 reference: config.references, // 所需服务 routerServer: { address: routerAddress }, }) return client.init().then(() => { for (const r of config.references) { loadService(r.id) } })
根据 xml 文件生成相关的包文件
在项目伊始时,会通过后端给出的 xml 文件,根据脚本生成相应包的 ts 文件。比如以下代码为骑手服务组的一个包的 ts 文件。
import { Reference } from '@dwd/noob' import { provideService, javaType} from '../../util/dubbo' @provideService('com.dianwoba.rider.elastic.provider.RiderElasticProvider') export default class RiderElasticProvider implements com.dianwoba.rider.elastic.provider.RiderElasticProvider { constructor(private _ref: Reference<com.dianwoba.rider.elastic.provider.RiderElasticProvider> ) {} async pageSearch(@javaType({"name":"com.dianwoba.rider.elastic.domain.dto.param.RiderEsParamDTO","isPrimitive":false,"isArray":false,"isGeneric":false}) paramDTO: com.dianwoba.rider.elastic.domain.dto.param.RiderEsParamDTO) : Promise<com.dianwoba.dubbo.base.result.Pagination<com.dianwoba.rider.elastic.domain.dto.result.RiderEsDTO>> { return this._ref.invoke('pageSearch', Array.from(arguments)) } ... }
RiderElasticProvider 的实例化
网关的核心架构使用了 IoC 框架 inverify 。在 IoC 架构下,实例化的过程在容器内进行。
在上述代码点开 provideService
方法,可以看到实例化的过程 new ServiceClass(reference)
,代码如下:
// dubbo.ts import { Client } from '@dwd/noob-client' export const provideService = <T>(interfaceName: string) => (ServiceClass: interfaces.Newable<T>) => { const reference = Client.reference.get(interfaceName) // 获取远程引用资源 const service = proxyService(new ServiceClass(reference), interfaceName) // 代理实例对象,下文解析 moduleBind<T>(interfaceName).toConstantValue(service) // 依赖注入 }
reference
对象的 __proto__
属性上有 invoke 方法(继承自 @dwd/noob)
proxyService 代理
proxyService 方法的作用给包类的每个方法做了一层代理,代理的具体作用是将传入参数和包名包装为 RPC 对象。
// dubbo.ts function proxyService<T>(service:T, identifier: string) { let handler = { apply: function(target: Function, thisArgument: any, argumentsList: any[]) { let funcName = target.name // 获取前面代码 @javaType({}) 中声明的对象 let paramTypes: JavaType[] = Reflect.getMetadata(JAVATYPE_SYMBOL, service, funcName) // 工厂模式创建转化为 RPC 对象的方法 let transform = converter.methodParameterTransformerFactory(...paramTypes) // 将传入参数和包名转化为 RPC 对象 let args = transform(argumentsList) return target.apply(thisArgument, args) } } Object.getOwnPropertyNames(Object.getPrototypeOf(service)).filter(name => name !== 'constructor' && !name.startsWith('_')).forEach(methodName => { service[methodName] = new Proxy(service[methodName], handler) // 将原型链上 service[methodName] 赋值到 service[methodName] 上,并用 handler 进行代理 }) return service }
番外笔记
ts 的语法
class Test { constructor(private _ref) {} }
ts 转换为 js 的形式如下
function Test(_ref) { this._ref = _ref }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
阿里巴巴Java开发手册
杨冠宝 / 电子工业出版社 / 2018-1 / 35
《阿里巴巴Java开发手册》的愿景是码出高效,码出质量。它结合作者的开发经验和架构历程,提炼阿里巴巴集团技术团队的集体编程经验和软件设计智慧,浓缩成为立体的编程规范和最佳实践。众所周知,现代软件行业的高速发展对开发者的综合素质要求越来越高,因为不仅是编程相关的知识点,其他维度的知识点也会影响软件的最终交付质量,比如,数据库的表结构和索引设计缺陷可能带来软件的架构缺陷或性能风险;单元测试的失位导致集......一起来看看 《阿里巴巴Java开发手册》 这本书的介绍吧!
JS 压缩/解压工具
在线压缩/解压 JS 代码
随机密码生成器
多种字符组合密码