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调用,这些都是可以的,不过,它的缺点也是很明显的,参数传递复杂,不方便使用。但是这种方式是不能缺失的。