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

服務消費端泛化調用

前面咱們搭建了基於 Spring 和Dubbo API 方式簡單的簡單調用。服務消費端引入了一個 SDK 二方包(api.jar),裏面存放着服務提供端提供的全部接口類,之因此須要引入接口類是由於服務消費端通常是基於接口使用 JDK 代理實現遠程調用的。api

泛化接口調用方式主要在服務消費端沒有 API 接口類及模型類元(好比入參和出參的 POJO 類)的狀況下使用。其參數及返回值中沒有對應的 POJO 類,因此全部 POJO 均轉換爲 Map 表示。使用泛化調用時候服務消費模塊再也不須要引入 SDK 二方包。多線程

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

上面代碼中,因爲 sayHello 的參數是 String,沒有很好的體現參數轉換爲 Map,下面咱們具體來講下 POJO 參數轉換 Map 的含義。app

好比服務提供者提供的一個接口的 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 數據:dom

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

正常狀況下調用接口是使用:異步

servicePerson.testPojo(person);

泛化調用下須要首先轉換 person 爲 Map,以下表示:ide

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 類。this

服務消費端異步調用

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

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

在 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 的非阻塞實現並行調用,客戶端不須要啓動多線程便可完成並行調用多個遠程服務,相對調用不一樣的服務使用不一樣線程來講開銷較小。

相關文章
相關標籤/搜索