RPC泛化調用

前言

廣泛RPC在客戶端須要提供接口,若是不提供則沒法進行調用。同時,由於客戶端也依賴提供的接口,服務端的升級、優化所帶來的更新,客戶端也要及時的更新API,不然會帶來影響。這樣,就帶來了依賴接口,經常更新API(接口)的麻煩。爲了解決這個問題,須要進行泛化調用。java

參考RPC

Sofa例子: 具體以下:api

  • 根據方法名稱獲得序列化的類型,例如普通序列化,泛型序列化,混合序列化。咱們這裏的例子返回的值是 0(普通序列化),而後設置序列化工廠類型,即普通序列化(根據方法名不一樣而不一樣)。
  • 從 Request 對象中拿到方法名稱,參數類型的字符串,方法參數。並從新設置到 Request 對象中,至關於從新整理了一遍。
  • 而後根據剛剛設置的方法名從新設置調用類型。
ConsumerConfig<GenericService> consumerConfig = new ConsumerConfig<GenericService>()
             .setApplication(applicationConfig)
             .setInterfaceId(TestGenericService.class.getName())
             .setGeneric(true)
             .setTimeout(50000)
             .setDirectUrl("bolt://127.0.0.1:22222?appName=generic-server");
         GenericService testService = consumerConfig.refer();
           // 上面這行中,生成GenericService的動態代理類
 
         LOGGER.warn("started at pid {}", RpcRuntimeContext.PID);

         while (true) {
             try {
                 String s1 = (String) testService.$invoke("echoStr", new String[] { "java.lang.String" },
                     new Object[] { "1111" });
					 
					 public GenericService assembleSofaRPCGenericService(String packageName, String rpcServer) {
        ConsumerConfig<GenericService> consumerConfig = new ConsumerConfig<GenericService>()
                .setInterfaceId(packageName)
                .setGeneric(true)
                .setDirectUrl(rpcServer)
                .setTimeout(3000);
        GenericService proxy = consumerConfig.refer();
        return proxy;
    }

String packageName1 = "com.test.endpoint.facade.SampleService";
GenericService genericService1 = assembleSofaRPCGenericService(packageName1, "bolt://10.167.24.122:12200");
SofaRPCDataSource sofaRPCDataSource = new SofaRPCDataSource();
sofaRPCDataSource.setGenericBean(genericService1);
pool.put(packageName1, sofaRPCDataSource);
String packageName2 = "com.test.endpoint.facade.SamplePeopleService";
GenericService genericService2 = assembleSofaRPCGenericService(packageName2, "bolt://10.167.24.122:12200");
SofaRPCDataSource sofaRPCDataSource2 = new SofaRPCDataSource();
sofaRPCDataSource2.setGenericBean(genericService2);
pool.put(packageName2, sofaRPCDataSource2);

public Object callSync() {
        Object result = genericService.$genericInvoke(${目標方法名}, ${參數類型String串}, ${參數值object串}, Response.class).getContent();
        return result;
    }

Dubbo例子:數組

如下幾種場景能夠考慮使用泛化調用:網絡

  • 服務測試平臺
  • API 服務網關

泛化調用主要用於消費端沒有 API 接口的狀況;不須要引入接口 jar 包,而是直接經過 GenericService 接口來發起服務調用,參數及返回值中的全部 POJO 均用 Map 表示。泛化調用對於服務端無需關注,按正常服務進行暴露便可。app

<dubbo:reference id="userService" interface="com.alibaba.dubbo.samples.generic.api.IUserService" generic="true"/>
// 須要使用的地方,經過強制類型轉化爲 GenericService 進行調用:
GenericService userService = (GenericService) context.getBean("userService");
// primary param and return value
String name = (String) userService.$invoke("delete", new String[]{int.class.getName()}, new Object[]{1});
System.out.println(name);

其中:框架

  1. GenericService 這個接口只有一個方法,名爲 $invoke,它接受三個參數,分別爲方法名、方法參數類型數組和參數值數組;
  2. 對於方法參數類型數組
  • 若是是基本類型,如 int 或 long,可使用 int.class.getName()獲取其類型;
  • 若是是基本類型數組,如 int[],則可使用 int[].class.getName();
  • 若是是 POJO,則直接使用全類名,如 com.alibaba.dubbo.samples.generic.api.Params。

Dubbo 框架會自動將 POJO 的返回值轉換成 Map。能夠看到,返回值 user 是一個 HashMap,裏面分別存放了 name、id、class 三個 k/v。測試

原理

原理無非就是將泛化調用轉化爲普通調用,關鍵在於對象的表示和序列化。優化

首先,Client會經過動態代理建立GenericService的代理類; 而後,會通過一系列過濾鏈(優先級排序,默認不須要,大的優先級高)代理

ConsumerExceptionFilter order = -20000 RpcReferenceContextFilter order = -19500 ConsumerGenericFilter order = -18000 ConsumerTracerFilter order = -10000 ConsumerInvoker 這裏會進行真正的業務的調用code

在泛化調用過濾器(ConsumerGenericFilter)中,會進行下列操做:

★設置序列化工廠類型爲普通序列化(序列化反序列化均使用SofaSerializerFactory,值爲0),並設置到Request參數中。

★進行調用參數($invoke參數)的修正,變成普通的調用參數(調用方法,調用參數類型,調用參數值)

★設置調用類型

最後,使用SOFABolt協議進行進行網絡調用。

相關文章
相關標籤/搜索