
前言
通常 RPC 調用需要客戶端使用服務端提供的接口,而具體的形式則是使用 jar 包,通過引用 jar 包獲取接口的的具體信息,例如接口名稱,方法名稱,參數(shù)類型,返回值類型。
但也存在一些情況,例如客戶端沒有 jar 包,或者是跨語言的調用,這個時候,就需要客戶端使用字符串進行泛化調用。
如何使用
還是根據(jù)官方的例子來看一下:
ConsumerConfig<GenericService> consumerConfig = new ConsumerConfig<GenericService>()
.setInterfaceId("com.alipay.sofa.rpc.quickstart.HelloService")
.setGeneric(true);
GenericService testService = consumerConfig.refer();
String result = (String) testService.$invoke("sayHello", new String[] { "java.lang.String" },new Object[] { "1111" });
我們看到,實際上,設置接口 ID 是和普通的調用時類似的,只是需要增加一個 Generic 屬性為 true。
然后就返回了一個 GenericService 類型的代理對象,通過這個對象,就可以對服務發(fā)起調用,而調用的方式,則是使用 GenericService 的 $invoke 方法,需要傳遞方法名稱,參數(shù)類型,參數(shù)值。并指定返回值。
SOFA 是如何實現(xiàn)的呢?
源碼實現(xiàn)
既然和普通的調用只是變化了 Generic 屬性,那么,我們就看看這個屬性在哪些地方使用。
我們很快便找到了一個過濾器: ConsumerGenericFilter,該過濾器的生效條件則是 “客戶端是否配置了泛型”。如果設置泛型,則添加到過濾鏈中。
而該過濾器的 invoke 方法肯定是對調用進行了一些特殊的操作,具體如下:
- 根據(jù)方法名稱得到序列化的類型,例如普通序列化,泛型序列化,混合序列化。我們這里的例子返回的值是 0(普通序列化),然后設置序列化工廠類型,即普通序列化(根據(jù)方法名不同而不同)。
- 從 Request 對象中拿到方法名稱,參數(shù)類型的字符串,方法參數(shù)。并重新設置到 Request 對象中,相當于重新整理了一遍。
- 然后根據(jù)剛剛設置的方法名重新設置調用類型。
這樣就將泛型的調用修改成和普通調用一樣了。
同時注意:發(fā)起調用時,該過濾器的默認 order 是 -18000,因此他會在大部分(除了異常處理和上下文)之前執(zhí)行。
在使用 Bolt 的 RpcClient 進行調用的時候,會根據(jù)序列化類型決定是否進行泛型的序列化。
具體過程是,當調用時,會創(chuàng)建 InvokeContext 上下文,會在 Map 中存儲自定義的序列化器,其中 key 是 SerializeFactoryType,value 是 0(我們這里),在 RpcRemoting 的 invokeXXX 方法中,會創(chuàng)建一個 RemotingCommand 對象,即執(zhí)行 toRemotingCommand 方法,根據(jù) InvokeContext 中的 SerializeFactoryType 獲取到序列化工廠的枚舉值,并設置到 RemotingCommand 對象中。
在 SofaRpcSerialization 類中,會根據(jù) invokeContext 中存儲的序列化枚舉值得到序列化器,具體代碼如下:
// 根據(jù)SerializeType信息決定序列化器
boolean genericSerialize = genericSerializeRequest(invokeContext);
if (genericSerialize) {
output.setSerializerFactory(genericSerializerFactory);
} else {
output.setSerializerFactory(serializerFactory);
}
根據(jù)SerializeType信息決定序列化器。而泛型的具體序列化器工廠則是 GenericMultipleClassLoaderSofaSerializerFactory 類,該類的會生成序列化器和反序列化器。并在 Bolt 中使用。
總結
從 SOFA 的設計中可以看出,泛化調用主要依賴于 GenericService 這個類和對應的 ConsumerGenericFilter 過濾器,如果一個客戶端設置泛化了,那么調用過程中則會啟用這個過濾器。
這個過濾器會將請求的數(shù)據(jù)重新整理。并修改成普通調用的樣子。
同時也會設置一個泛型調用的序列化枚舉放置在上下文中,上下文在 Bolt 中會根據(jù)枚舉值動態(tài)獲取不同的序列化器和反序列化器,對輸出參數(shù)和返回值經進行處理。