SOFA-源码分析-—-泛化调用
通常 RPC 调用需要客户端使用服务端提供的接口,而具体的形式则是使用 jar 包,通过引用 jar 包获取接口的的具体信息,例如接口名称,方法名称,参数类型,返回值类型。
但也存在一些情况,例如客户端没有 jar 包,或者是跨语言的调用,这个时候,就需要客户端使用字符串进行泛化调用。
如何使用
还是根据官方的例子来看一下:
1 | ConsumerConfig<GenericService> consumerConfig = new ConsumerConfig<GenericService>() |
我们看到,实际上,设置接口 ID 是和普通的调用时类似的,只是需要增加一个 Generic 属性为 true。
然后就返回了一个 GenericService 类型的代理对象,通过这个对象,就可以对服务发起调用,而调用的方式,则是使用 GenericService 的 $invoke 方法,需要传递方法名称,参数类型,参数值。并指定返回值。
SOFA 是如何实现的呢?
源码实现
既然和普通的调用只是变化了 Generic 属性,那么,我们就看看这个属性在哪些地方使用。
我们很快便找到了一个过滤器: ConsumerGenericFilter,该过滤器的生效条件则是 “客户端是否配置了泛型”。如果设置泛型,则添加到过滤链中。
而该过滤器的 invoke 方法肯定是对调用进行了一些特殊的操作,具体如下:
- 根据方法名称得到序列化的类型,例如普通序列化,泛型序列化,混合序列化。我们这里的例子返回的值是 0(普通序列化),然后设置序列化工厂类型,即普通序列化(根据方法名不同而不同)。
- 从 Request 对象中拿到方法名称,参数类型的字符串,方法参数。并重新设置到 Request 对象中,相当于重新整理了一遍。
- 然后根据刚刚设置的方法名重新设置调用类型。
这样就将泛型的调用修改成和普通调用一样了。
同时注意:发起调用时,该过滤器的默认 order 是 -18000,因此他会在大部分(除了异常处理和上下文)之前执行。
在使用 Bolt 的 RpcClient 进行调用的时候,会根据序列化类型决定是否进行泛型的序列化。
具体过程是,当调用时,会创建 InvokeContext 上下文,会在 Map 中存储自定义的序列化器,其中 key 是 SerializeFactoryType
,value 是 0
(我们这里),在 RpcRemoting 的 invokeXXX 方法中,会创建一个 RemotingCommand 对象,即执行 toRemotingCommand
方法,根据 InvokeContext 中的 SerializeFactoryType
获取到序列化工厂的枚举值,并设置到 RemotingCommand 对象中。
在 SofaRpcSerialization 类中,会根据 invokeContext 中存储的序列化枚举值得到序列化器,具体代码如下:
1 | // 根据SerializeType信息决定序列化器 |
根据SerializeType信息决定序列化器。而泛型的具体序列化器工厂则是 GenericMultipleClassLoaderSofaSerializerFactory 类,该类的会生成序列化器和反序列化器。并在 Bolt 中使用。
总结
从 SOFA 的设计中可以看出,泛化调用主要依赖于 GenericService 这个类和对应的 ConsumerGenericFilter 过滤器,如果一个客户端设置泛化了,那么调用过程中则会启用这个过滤器。
这个过滤器会将请求的数据重新整理。并修改成普通调用的样子。
同时也会设置一个泛型调用的序列化枚举放置在上下文中,上下文在 Bolt 中会根据枚举值动态获取不同的序列化器和反序列化器,对输出参数和返回值经进行处理。