SOFA 原始碼分析 — 泛化呼叫

莫那·魯道發表於2018-05-19

SOFA 原始碼分析 — 泛化呼叫

前言

通常 RPC 呼叫需要客戶端使用服務端提供的介面,而具體的形式則是使用 jar 包,通過引用 jar 包獲取介面的的具體資訊,例如介面名稱,方法名稱,引數型別,返回值型別。

但也存在一些情況,例如客戶端沒有 jar 包,或者是跨語言的呼叫,這個時候,就需要客戶端使用字串進行泛化呼叫。

如何使用

還是根據官方的例子來看一下:

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 型別的代理物件,通過這個物件,就可以對服務發起呼叫,而呼叫的方式,則是使用 GenericService 的 $invoke 方法,需要傳遞方法名稱,引數型別,引數值。並指定返回值。

SOFA 是如何實現的呢?

原始碼實現

既然和普通的呼叫只是變化了 Generic 屬性,那麼,我們就看看這個屬性在哪些地方使用。

我們很快便找到了一個過濾器: ConsumerGenericFilter,該過濾器的生效條件則是 “客戶端是否配置了泛型”。如果設定泛型,則新增到過濾鏈中。

而該過濾器的 invoke 方法肯定是對呼叫進行了一些特殊的操作,具體如下:

  1. 根據方法名稱得到序列化的型別,例如普通序列化,泛型序列化,混合序列化。我們這裡的例子返回的值是 0(普通序列化),然後設定序列化工廠型別,即普通序列化(根據方法名不同而不同)。
  2. 從 Request 物件中拿到方法名稱,引數型別的字串,方法引數。並重新設定到 Request 物件中,相當於重新整理了一遍。
  3. 然後根據剛剛設定的方法名重新設定呼叫型別。

這樣就將泛型的呼叫修改成和普通呼叫一樣了。 同時注意:發起呼叫時,該過濾器的預設 order 是 -18000,因此他會在大部分(除了異常處理和上下文)之前執行。

在使用 Bolt 的 RpcClient 進行呼叫的時候,會根據序列化型別決定是否進行泛型的序列化。

具體過程是,當呼叫時,會建立 InvokeContext 上下文,會在 Map 中儲存自定義的序列化器,其中 key 是 SerializeFactoryType,value 是 0(我們這裡),在 RpcRemoting 的 invokeXXX 方法中,會建立一個 RemotingCommand 物件,即執行 toRemotingCommand 方法,根據 InvokeContext 中的 SerializeFactoryType 獲取到序列化工廠的列舉值,並設定到 RemotingCommand 物件中。

在 SofaRpcSerialization 類中,會根據 invokeContext 中儲存的序列化列舉值得到序列化器,具體程式碼如下:

// 根據SerializeType資訊決定序列化器
boolean genericSerialize = genericSerializeRequest(invokeContext);
if (genericSerialize) {
    output.setSerializerFactory(genericSerializerFactory);
} else {
    output.setSerializerFactory(serializerFactory);
}
複製程式碼

根據SerializeType資訊決定序列化器。而泛型的具體序列化器工廠則是 GenericMultipleClassLoaderSofaSerializerFactory 類,該類的會生成序列化器和反序列化器。並在 Bolt 中使用。

總結

從 SOFA 的設計中可以看出,泛化呼叫主要依賴於 GenericService 這個類和對應的 ConsumerGenericFilter 過濾器,如果一個客戶端設定泛化了,那麼呼叫過程中則會啟用這個過濾器。

這個過濾器會將請求的資料重新整理。並修改成普通呼叫的樣子。

同時也會設定一個泛型呼叫的序列化列舉放置在上下文中,上下文在 Bolt 中會根據列舉值動態獲取不同的序列化器和反序列化器,對輸出引數和返回值經進行處理。

相關文章