Dubbo基本特性之泛化調用
Dubbo 是支持泛化調用的,什麽是泛化調用呢?泛化調用的好處是什麽呢,泛化調用說白一點就是服務消費者並沒有服務的接口。
在《Dubbo入門—搭建一個最簡單的Demo框架》一文中,我們已完成了最最基本的Dubbo的搭建及調用,我們的dubbo-provider、dubbo-consumer、dubbo-consumer2都是依賴dubbo-api,而dubbo-api其實什麽也沒做,就定義了一個接口,也就是說,在我們開發寫Demo的時候,必做的一件事情,就是在服務消費者和服務提供者兩端同路徑下有同樣的接口,只不過在服務提供者端會有該接口的具體實現,之所以在服務消費者有一個沒有任何具體實現的接口,是因為在設計RPC之初,設計者的最高理念就是你去面向接口編程,你在進行遠程調用的時候,並沒有意識到你在進行遠程調用,卻也能拿到接口一樣,相信你也感覺到了,服務消費者在調用服務的時候,與調用一個普通的接口是一樣的。
泛化調用就是服務消費者端因為某種原因並沒有該服務接口,這個原因有很多,比如是跨語言的,一個PHP工程師想調用某個java接口,他並不能按照你約定,去寫一個個的接口,Dubbo並不是跨語言的RPC框架,但並不是不能解決這個問題,這個PHP程序員搭建了一個簡單的java web項目,引入了dubbo的jar包,使用dubbo的泛化調用,然後利用web返回json,這樣也能完成跨語言的調用。泛化調用的好處之一就是這個了。
好了,簡而言之,泛化調用,最最直接的表現就是服務消費者不需要有任何接口的實現,就能完成服務的調用。
一.泛化調用入門
老規矩,我們現在服務提供者端定義一個簡單的接口,(與往常不一樣的地方就是不需要把該接口復制到服務提供者的同路徑下了,因為服務消費者不需要使用該接口進行調用),在這我們就繼續用《Dubbo入門—搭建一個最簡單的Demo框架》一文中的案例代碼。
定義一個簡單的接口DemoService.java
package com.alibaba.dubbo.demo; import java.util.List; public interface DemoService { List<String> getPermissions(Long id); }
該接口的具體實現DemoServiceImpl.java
package com.alibaba.dubbo.demo.impl; import com.alibaba.dubbo.demo.DemoService; import java.util.ArrayList;import java.util.List; public class DemoServiceImpl implements DemoService { public List<String> getPermissions(Long id) { List<String> demo = new ArrayList<String>(); demo.add(String.format("Permission_%d", id - 1)); demo.add(String.format("Permission_%d", id)); demo.add(String.format("Permission_%d", id + 1)); return demo; } }
服務提供者端的Spring的配置文件provider.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <!--定義了提供方應用信息,用於計算依賴關系;在 dubbo-admin 或 dubbo-monitor 會顯示這個名字,方便辨識--> <dubbo:application name="demotest-provider" owner="programmer" organization="dubbox"/> <!--使用 zookeeper 註冊中心暴露服務,註意要先開啟 zookeeper--> <dubbo:registry address="zookeeper://localhost:2181"/> <!-- 用dubbo協議在20880端口暴露服務 --> <dubbo:protocol name="dubbo" port="20880" /> <!--使用 dubbo 協議實現定義好的 api.PermissionService 接口--> <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" protocol="dubbo"/> <!--具體實現該接口的 bean--> <bean id="demoService" class="com.alibaba.dubbo.demo.impl.DemoServiceImpl"/> </beans>
服務提供者端再寫一個啟動類Provider.java:
package com.alibaba.dubbo.demo.impl; import org.springframework.context.support.ClassPathXmlApplicationContext; import java.io.IOException; public class Provider { public static void main(String[] args) throws IOException { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "provider.xml"); System.out.println(context.getDisplayName() + ": here"); context.start(); System.out.println("服務已經啟動..."); System.in.read(); } }
好了,到此為止,泛化調用服務提供者的配置文件就全部結束了,你可以看到從代碼和spring的配置文件都沒有任何特殊處理,與一般的服務提供者並沒有任何的不同,泛化調用,從這四個字看也知道是調用的方式不一樣而已,我們再看看服務消費者端的代碼編寫。
因為服務消費者沒有了接口,我們直接編寫消費者端spring的配置文件consumer.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"> <dubbo:application name="demotest-consumer" owner="programmer" organization="dubbox"/> <!--向 zookeeper 訂閱 provider 的地址,由 zookeeper 定時推送--> <dubbo:registry address="zookeeper://localhost:2181"/> <!--使用 dubbo 協議調用定義好的 api.PermissionService 接口--> <dubbo:reference id="permissionService" interface="com.alibaba.dubbo.demo.DemoService" generic="true"/> </beans>
可以看到上圖的配置文件中有兩個地方需要註意一下,第一個是interface,其實該接口在消費者端並不存在,這是與往常寫的不一樣的地方,第二個地方需要註意的地方就是generic=”true”這樣的標簽,表示該接口支持泛型調用。
好了,我們寫一個服務測試類,看看如何進行泛型調用Consumer.java
package com.alibaba.dubbo.consumer; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.alibaba.dubbo.rpc.service.GenericService; public class Consumer { public static void main(String[] args) { /////////////////Spring泛化調用///////// ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml"); context.start(); System.out.println("consumer start"); GenericService demoService = (GenericService) context.getBean("permissionService"); System.out.println("consumer"); Object result = demoService.$invoke("getPermissions", new String[] { "java.lang.Long" }, new Object[]{ 1L }); System.out.println(result); } }
看到Main函數中,從spring的上下文中讀到”permissionService”之後卻把它強轉為GenericService的類,然後調用GenericService的$invoke的方法,該方法有三個參數,第一個參數是你調用遠程接口的具體方法名,第二個參數是permissionService這個方法的入參的類型,最後一個參數是值。
我們測試一下,啟動dubbo-provider的main函數,然後啟動服務消費者的測試類Consumer.java,你會發現控制臺打印:
[22/04/18 05:06:36:036 CST] main INFO config.AbstractConfig: [DUBBO] Refer dubbo service com.alibaba.dubbo.rpc.service.GenericService from url zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=demotest-consumer&check=false&dubbo=2.5.3&generic=true&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=6604&side=consumer×tamp=1524387995725, dubbo version: 2.5.3, current host: 10.39.56.134 consumer [Permission_0, Permission_1, Permission_2]
可以看到能夠調通,沒有問題。
我們剛才說,一個PHP程序員想要搭建一個簡單的web項目,可是你卻叫他依賴於spring的配置文件,對他難度是不小的,dubbo也幫你想到了,泛型調用,服務消費端可以不依賴spring的配置文件,我們重新寫下測試類Consumer:
package com.alibaba.dubbo.consumer; import com.alibaba.dubbo.config.ApplicationConfig; import com.alibaba.dubbo.config.ReferenceConfig; import com.alibaba.dubbo.config.RegistryConfig; import com.alibaba.dubbo.config.utils.ReferenceConfigCache; import com.alibaba.dubbo.rpc.service.GenericService; public class Consumer { public static void main(String[] args) { // 普通編碼配置方式 ApplicationConfig application = new ApplicationConfig(); application.setName("dubbo-consumer"); // 連接註冊中心配置 RegistryConfig registry = new RegistryConfig(); registry.setAddress("zookeeper://127.0.0.1:2181"); ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>(); reference.setApplication(application); reference.setRegistry(registry); reference.setInterface("com.alibaba.dubbo.demo.DemoService"); reference.setGeneric(true); // 聲明為泛化接口 ReferenceConfigCache cache = ReferenceConfigCache.getCache(); GenericService genericService = cache.get(reference); // 基本類型以及Date,List,Map等不需要轉換,直接調用 Object result = genericService.$invoke("getPermissions", new String[] { "java.lang.Long" }, new Object[] { 1L }); System.out.println(result); } }
重新啟動一下該類。控制臺正常打印:
[22/04/18 05:09:09:009 CST] main INFO config.AbstractConfig: [DUBBO] Refer dubbo service com.alibaba.dubbo.rpc.service.GenericService from url zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=dubbo-consumer&check=false&dubbo=2.5.3&generic=true&interface=com.alibaba.dubbo.demo.DemoService&methods=getPermissions&organization=dubbox&owner=programmer&pid=21144&side=consumer×tamp=1524388149244, dubbo version: 2.5.3, current host: 10.39.56.134 [Permission_0, Permission_1, Permission_2]
也能正常進行調用。
二.泛化調用小結
泛化調用可以方便用戶對dubbo服務消費者端的擴展,可以方便,豐富了服務消費者的調用方式,甚至可以做變相的Rest調用,這些都是可以的,不過,它的缺點也是很明顯的,參數傳遞復雜,不方便使用。但是這種方式是不能缺失的。
參考文章:https://blog.csdn.net/linuu/article/details/54313560
實例代碼:http://bijian1013.iteye.com/blog/2419726
Dubbo基本特性之泛化調用