Dubbo系列<7>-服務消費端泛化調用與異步調用

服務消費端泛化調用

前面我們搭建了基於 Spring 和Dubbo API 方式簡單的簡單調用。服務消費端引入了一個 SDK 二方包(api.jar),裏面存放着服務提供端提供的所有接口類,之所以需要引入接口類是因爲服務消費端一般是基於接口使用 JDK 代理實現遠程調用的。

泛化接口調用方式主要在服務消費端沒有 API 接口類及模型類元(比如入參和出參的 POJO 類)的情況下使用。其參數及返回值中沒有對應的 POJO 類,所以所有 POJO 均轉換爲 Map 表示。使用泛化調用時候服務消費模塊不再需要引入 SDK 二方包。

下面基於 Dubbo API 實現異步調用,在 Consumer 模塊裏面 TestConsumerApiGeneric 是泛化調用的方式,代碼如下:

上面代碼中,由於 sayHello 的參數是 String,沒有很好的體現參數轉換爲 Map,下面我們具體來說下 POJO 參數轉換 Map 的含義。

比如服務提供者提供的一個接口的 testPojo(Person person) 方法的參數爲如下所示:

package com.test;public class PersonImpl implements Person {private String name;private String password;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}

則 POJO 數據:

Person person = new PersonImpl();person.setName("lawt");person.setPassword("password");

正常情況下調用接口是使用:

servicePerson.testPojo(person);

泛化調用下需要首先轉換 person 爲 Map,如下表示:

Map<String, Object> map = new HashMap<String, Object>();// 注意:如果參數類型是接口,或者List等丟失泛型,可通過class屬性指定類型。map.put("class", "com.test.PersonImpl");map.put("name", "lawt");map.put("password", "password");

然後使用下面方法進行泛化調用:

servicePerson.$invoke("testPojo", new String[]{"com.tian.dubbo.domain.Person"}, new Object[]{map});

泛化調用通常用於框架集成,比如:實現一個通用的服務測試框架,可通過 GenericService 調用所有服務實現,而不需要依賴服務實現方提供的接口類以及接口的入參和出參的 POJO 類。

服務消費端異步調用

無論前面我們講解的正常調用還是泛化調用,都是同步調用,也就是服務消費方發起一個遠程調用後,調用線程要被阻塞掛起,直到服務提供方返回。

本節講解下服務消費端異步調用,異步調用是指服務消費方發起一個遠程調用後,不等服務提供方返回結果,調用方法就返回了,也就是當前線程不會被阻塞,這就允許調用方同時調用多個遠程方法。

在 Consumer 模塊裏面 TestConsumerAsync 是泛化調用,代碼如下:

public class TestConsumerAsync {    public static void main(String[] args) throws InterruptedException, ExecutionException {        // 當前應用配置       ApplicationConfig application = new ApplicationConfig();       application.setName("dubboConsumer");        // 連接註冊中心配置       RegistryConfig registry = new RegistryConfig();       registry.setAddress("127.0.0.1:2181");       registry.setProtocol("zookeeper");        // 引用遠程服務       ReferenceConfig<UserServiceBo> reference = new ReferenceConfig<UserServiceBo>();       reference.setApplication(application);       reference.setRegistry(registry);       reference.setInterface(UserServiceBo.class);       reference.setVersion("1.0.0");       reference.setTimeout(3000);        //(1)設置爲異步調用       reference.setAsync(true);        // 和本地bean一樣使用xxxService       UserServiceBo userService = reference.get();      long startTime = System.currentTimeMillis() / 1000;        // (2)因爲異步調用,此處返回null       System.out.println(userService.sayHello("哈哈哈"));        // 拿到調用的Future引用,當結果返回後,會被通知和設置到此Future            Future<String> userServiceFutureOne = RpcContext.getContext().getFuture();        // (3)因爲異步調用,此處返回null          System.out.println(userService.sayHello2("哈哈哈2"));        // 拿到調用的Future引用,當結果返回後,會被通知和設置到此Future            Future<String> userServiceFutureTwo = RpcContext.getContext().getFuture();        // (4)阻塞到get方法,等待結果返回             System.out.println(userServiceFutureOne.get());       System.out.println(userServiceFutureTwo.get());              long endTime = System.currentTimeMillis() / 1000;       System.out.println("costs:" + (endTime - startTime));   }}

運行上面代碼,輸出如下圖所示:


其中代碼(2)(3)處輸出 null,說明開啓異步調用後調用方直接返回 null。

輸出 costs:2 說明異步調用生效了,因爲 sayHello 和 sayHello2 方法內都 sleep 了2s,如果是順序調用則會耗時至少4s,這裏耗時2s說明兩次調用是併發進行的。

異步調用是基於 NIO 的非阻塞實現並行調用,客戶端不需要啓動多線程即可完成並行調用多個遠程服務,相對調用不同的服務使用不同線程來說開銷較小。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章