关于 RPC 细节点, 可以阅读 聊聊 Node.js 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 文件, 根据脚本生成相应包的 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))}...}
网关的核心架构使用了 IoC 框架 inverify。在 IoC 架构下, 实例化的过程在容器内进行。
在上述代码点开 provideService
方法, 可以看到实例化的过程 new ServiceClass(reference)
, 代码如下:
// dubbo.tsimport { 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 方法的作用给包类的每个方法做了一层代理, 代理的具体作用是将传入参数和包名包装为 RPC 对象。
// dubbo.tsfunction 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}
class Test {constructor(private _ref) {}}
ts 转换为 js 的形式如下
function Test(_ref) {this._ref = _ref}