dubbo的调用原理及泛化调用

落日映苍穹つ 2023-02-19 12:27 65阅读 0赞

简单介绍

dubbo是阿里开源出来的一个rpc框架,主要是用于微服务分布式项目的远程调用,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现,下面是调用的原理图:
在这里插入图片描述
dubbo框架的整体设计:
在这里插入图片描述
图例说明:

1,图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。
2,图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
3,图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。
4,图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法

远程调用细节

服务提供者暴露一个服务的详细过程

在这里插入图片描述
上图是服务提供者暴露服务的主过程:

首先 ServiceConfig 类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个 AbstractProxyInvoker 实例,到这一步就完成具体服务到 Invoker 的转化。接下来就是 Invoker 转换到 Exporter 的过程。

Dubbo 处理服务暴露的关键就在 Invoker 转换到 Exporter 的过程,上图中的红色部分。下面我们以 Dubbo 和 RMI 这两种典型协议的实现来进行说明:

Dubbo 的实现
Dubbo 协议的 Invoker 转为 Exporter 发生在 DubboProtocol 类的 export 方法,它主要是打开 socket 侦听服务,并接收客户端发来的各种请求,通讯细节由 Dubbo 自己实现。

RMI 的实现
RMI 协议的 Invoker 转为 Exporter 发生在 RmiProtocol类的 export 方法,它通过 Spring 或 Dubbo 或 JDK 来实现 RMI 服务,通讯细节这一块由 JDK 底层来实现,这就省了不少工作量。

服务消费者消费一个服务的详细过程

在这里插入图片描述
上图是服务消费的主过程:

首先 ReferenceConfig 类的 init 方法调用 Protocol 的 refer 方法生成 Invoker 实例(如上图中的红色部分),这是服务消费的关键。接下来把 Invoker 转换为客户端需要的接口(如:HelloWorld)。

dubbo直接引用

1,xml形式

服务提供者:

  1. public interface DemoService {
  2. String sayHello(String name);
  3. }

具体实现:

  1. public class DemoServiceImpl implements DemoService {
  2. public String sayHello(String name) {
  3. return "Hello " + name;
  4. }
  5. }

spring声明暴露服务:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
  6. <!-- 提供方应用信息,用于计算依赖关系 -->
  7. <dubbo:application name="hello-world-app" />
  8. <!-- 使用multicast广播注册中心暴露服务地址 -->
  9. <dubbo:registry address="multicast://224.5.6.7:1234" />
  10. <!-- 用dubbo协议在20880端口暴露服务 -->
  11. <dubbo:protocol name="dubbo" port="20880" />
  12. <!-- 声明需要暴露的服务接口 -->
  13. <dubbo:service interface="org.apache.dubbo.demo.DemoService" ref="demoService" />
  14. <!-- 和本地bean一样实现服务 -->
  15. <bean id="demoService" class="org.apache.dubbo.demo.provider.DemoServiceImpl" />
  16. </beans>

服务消费者:
通过 Spring 配置引用远程服务:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
  6. <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
  7. <dubbo:application name="consumer-of-helloworld-app" />
  8. <!-- 使用multicast广播注册中心暴露发现服务地址 -->
  9. <dubbo:registry address="multicast://224.5.6.7:1234" />
  10. <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
  11. <dubbo:reference id="demoService" interface="org.apache.dubbo.demo.DemoService" />
  12. </beans>

远程调用:

  1. public class Consumer {
  2. public static void main(String[] args) throws Exception {
  3. ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {
  4. "http://10.20.160.198/wiki/display/dubbo/consumer.xml"});
  5. context.start();
  6. DemoService demoService = (DemoService)context.getBean("demoService"); // 获取远程服务代理
  7. String hello = demoService.sayHello("world"); // 执行远程方法
  8. System.out.println( hello ); // 显示调用结果
  9. }
  10. }
2,api形式

提供者暴露服务:

  1. /**
  2. * @author shihaowei
  3. * @date 2020-06-11 11:43
  4. */
  5. @Configuration
  6. //@EnableDubbo
  7. @DubboComponentScan("person.shw.dubbo.provider")
  8. public class DubboConfig {
  9. @Value("${nacos.data.id:f21d3b6b-20ed-4091-ab17-ac073abc3eba}")
  10. private String namespace;
  11. @Bean
  12. public ApplicationConfig applicationConfig(){
  13. ApplicationConfig applicationConfig = new ApplicationConfig();
  14. applicationConfig.setName("provider-nacos");
  15. return applicationConfig;
  16. }
  17. @Bean
  18. public RegistryConfig registryConfig(){
  19. RegistryConfig registryConfig = new RegistryConfig();
  20. registryConfig.setAddress("nacos://120.79.76.230:8848");
  21. registryConfig.setGroup("ONE_GROUP");
  22. Map<String,String> paramMap = new HashMap<String, String>();
  23. paramMap.put("namespace",namespace);
  24. registryConfig.setParameters(paramMap);
  25. return registryConfig;
  26. }
  27. @Bean
  28. public ProtocolConfig protocolConfig(){
  29. ProtocolConfig protocolConfig = new ProtocolConfig();
  30. protocolConfig.setName("dubbo");
  31. protocolConfig.setPort(12347);
  32. return protocolConfig;
  33. }
  34. }

暴露服务的接口实现:

  1. import org.apache.dubbo.config.annotation.Service;
  2. import person.shw.dubbo.api.DubboApi;
  3. /**
  4. * @author shihaowei
  5. * @date 2020-06-09 17:21
  6. */
  7. @Service
  8. public class ProviderServiceImpl implements DubboApi {
  9. public String getConsumerData() {
  10. return "333333333333333333";
  11. }
  12. }

org.apache.dubbo.config.annotation.Service是引用的dubbo的包,而不是Spring的@Service

消费者消费服务:

  1. @Configuration
  2. public class DubboNacosConfig {
  3. @Bean
  4. public ApplicationConfig applicationConfig(RegistryConfig registryConfig){
  5. ApplicationConfig applicationConfig = new ApplicationConfig();
  6. applicationConfig.setName("consumer-nacos");
  7. applicationConfig.setRegistry(registryConfig);
  8. return applicationConfig;
  9. }
  10. @Bean
  11. public RegistryConfig registryConfig(){
  12. RegistryConfig registryConfig = new RegistryConfig();
  13. registryConfig.setCheck(false);
  14. registryConfig.setAddress("nacos://120.79.76.230:8848");
  15. registryConfig.setUsername("nacos");
  16. registryConfig.setPassword("nacos");
  17. Map<String,String> paramMap = new HashMap<String, String>();
  18. paramMap.put("namespace","f21d3b6b-20ed-4091-ab17-ac073abc3eba");
  19. registryConfig.setParameters(paramMap);
  20. return registryConfig;
  21. }
  22. }

调用服务

  1. import org.apache.dubbo.config.annotation.Reference;
  2. import org.springframework.web.bind.annotation.GetMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4. import person.shw.dubbo.api.DubboApi;
  5. /**
  6. * @author shihaowei
  7. * @date 2020-06-09 17:22
  8. */
  9. @RestController
  10. public class ConsumerController {
  11. @Reference
  12. private DubboApi dubboApi;
  13. @GetMapping(value = "/consumer/getMessage")
  14. public String getMessage(){
  15. return "【收到发来的消息】---->"+dubboApi.getConsumerData();
  16. }
  17. }

dubbo的泛化调用

为什么要使用泛化调用?

一般使用dubbo,provider端需要暴露出接口和方法,consumer端要十分明确服务使用的接口定义和方法定义(还有入参返参类型等等信息,常常还需要基于provider端提供的API),两端才能正常通信调用。

然而存在一种使用场景,调用方并不关心要调用的接口的详细定义,它只关注我要调用哪个方法,需要传什么参数,我能接收到什么返回结果即可,这样可以大大降低consumer端和provider端的耦合性。

所以为了应对以上的需求,dubbo提供了泛化调用,也就是在consumer只知道一个接口全限定名以及入参和返参的情况下,就可以调用provider端的调用,而不需要传统的接口定义这些繁杂的结构。

比如微服务的网关就可以采用泛化调用,又不需要强引用众多需要调用的服务

例子

继续使用上面使用api的配置,当我调用的时候,不时用@reference注解,而是用genericservice泛化调用,参数及返回值中的所有 POJO 均用 Map 表示,通常用于框架集成

  1. @RestController
  2. public class ConsumerController {
  3. @Autowired
  4. private ApplicationConfig applicationConfig;
  5. @GetMapping(value = "/consumer/getMessage")
  6. public String getMessage(){
  7. /** dubbo的泛化调用 */
  8. ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();
  9. reference.setApplication(applicationConfig);
  10. reference.setInterface("person.shw.dubbo.api.DubboApi");
  11. reference.setTimeout(200000);
  12. reference.setGeneric(true);
  13. /** 添加缓存策略 */
  14. /*ReferenceConfigCache cache = ReferenceConfigCache.getCache();
  15. GenericService genericService = cache.get(reference);*/
  16. GenericService genericService = reference.get();
  17. /** 设置接口所需要的参数类型 */
  18. String[] parametertypes = new String[]{
  19. };
  20. String[] args = new String[]{
  21. };
  22. Object data = genericService.$invoke("getConsumerData", parametertypes, args);
  23. System.out.println(JSON.toJSONString(data));
  24. return "【收到consumer发来的消息】---->"+JSON.toJSONString(data);
  25. }
  26. }

调用服务,打印日志

  1. 2020-06-20 17:44:25.482 INFO 3033 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 924 ms
  2. 2020-06-20 17:44:25.664 WARN 3033 --- [ main] org.apache.dubbo.config.AbstractConfig : [DUBBO] There's no valid metadata config found, if you are using the simplified mode of registry url, please make sure you have a metadata address configured properly., dubbo version: 2.7.3, current host: 192.168.31.132
  3. 2020-06-20 17:44:25.669 INFO 3033 --- [ main] org.apache.dubbo.config.AbstractConfig : [DUBBO] There's no valid monitor config found, if you want to open monitor statistics for Dubbo, please make sure your monitor is configured properly., dubbo version: 2.7.3, current host: 192.168.31.132
  4. 2020-06-20 17:44:25.689 INFO 3033 --- [ main] o.a.d.qos.protocol.QosProtocolWrapper : [DUBBO] qos won't be started because it is disabled. Please check dubbo.application.qos.enable is configured either in system property, dubbo.properties or XML/spring-boot configuration., dubbo version: 2.7.3, current host: 192.168.31.132
  5. 2020-06-20 17:44:25.786 INFO 3033 --- [ main] o.a.dubbo.registry.nacos.NacosRegistry : [DUBBO] Load registry cache file /Users/edz/.dubbo/dubbo-registry-consumer-nacos-120.79.76.230:8848.cache, data: {person.shw.dubbo.api.DubboApi=dubbo://192.168.31.132:12347/person.shw.dubbo.api.DubboApi?anyhost=true&application=provider-nacos&bean.name=ServiceBean:person.shw.dubbo.api.DubboApi&category=providers&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=person.shw.dubbo.api.DubboApi&methods=getConsumerData&path=person.shw.dubbo.api.DubboApi&pid=2846&protocol=dubbo&register=true&release=2.7.3&side=provider&timestamp=1592646130152}, dubbo version: 2.7.3, current host: 192.168.31.132
  6. 2020-06-20 17:44:25.804 INFO 3033 --- [ main] o.a.dubbo.registry.nacos.NacosRegistry : [DUBBO] Register: consumer://192.168.31.132/org.apache.dubbo.rpc.service.GenericService?application=consumer-nacos&category=consumers&check=false&dubbo=2.0.2&generic=true&interface=person.shw.dubbo.api.DubboApi&lazy=false&pid=3033&qos.enable=false&release=2.7.3&side=consumer&sticky=false&timeout=200000&timestamp=1592646265664, dubbo version: 2.7.3, current host: 192.168.31.132
  7. 2020-06-20 17:44:25.967 INFO 3033 --- [ main] o.a.dubbo.registry.nacos.NacosRegistry : [DUBBO] Subscribe: consumer://192.168.31.132/org.apache.dubbo.rpc.service.GenericService?application=consumer-nacos&category=providers,configurators,routers&dubbo=2.0.2&generic=true&interface=person.shw.dubbo.api.DubboApi&lazy=false&pid=3033&qos.enable=false&release=2.7.3&side=consumer&sticky=false&timeout=200000&timestamp=1592646265664, dubbo version: 2.7.3, current host: 192.168.31.132
  8. 2020-06-20 17:44:26.009 INFO 3033 --- [ main] o.a.dubbo.registry.nacos.NacosRegistry : [DUBBO] Notify urls for subscribe url consumer://192.168.31.132/org.apache.dubbo.rpc.service.GenericService?application=consumer-nacos&category=providers,configurators,routers&dubbo=2.0.2&generic=true&interface=person.shw.dubbo.api.DubboApi&lazy=false&pid=3033&qos.enable=false&release=2.7.3&side=consumer&sticky=false&timeout=200000&timestamp=1592646265664, urls: [dubbo://192.168.31.132:12347/person.shw.dubbo.api.DubboApi?anyhost=true&application=provider-nacos&bean.name=ServiceBean:person.shw.dubbo.api.DubboApi&category=providers&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=person.shw.dubbo.api.DubboApi&methods=getConsumerData&path=person.shw.dubbo.api.DubboApi&pid=2846&protocol=dubbo&register=true&release=2.7.3&side=provider&timestamp=1592646130152], dubbo version: 2.7.3, current host: 192.168.31.132
  9. 2020-06-20 17:44:26.181 INFO 3033 --- [ main] o.a.d.remoting.transport.AbstractClient : [DUBBO] Succeed connect to server /192.168.31.132:12347 from NettyClient 192.168.31.132 using dubbo version 2.7.3, channel is NettyChannel [channel=[id: 0x5cf0891f, L:/192.168.31.132:55666 - R:/192.168.31.132:12347]], dubbo version: 2.7.3, current host: 192.168.31.132
  10. 2020-06-20 17:44:26.181 INFO 3033 --- [ main] o.a.d.remoting.transport.AbstractClient : [DUBBO] Start NettyClient /192.168.31.132 connect to the server /192.168.31.132:12347, dubbo version: 2.7.3, current host: 192.168.31.132
  11. 2020-06-20 17:44:26.216 INFO 3033 --- [client.listener] o.a.dubbo.registry.nacos.NacosRegistry : [DUBBO] Notify urls for subscribe url consumer://192.168.31.132/org.apache.dubbo.rpc.service.GenericService?application=consumer-nacos&category=providers,configurators,routers&dubbo=2.0.2&generic=true&interface=person.shw.dubbo.api.DubboApi&lazy=false&pid=3033&qos.enable=false&release=2.7.3&side=consumer&sticky=false&timeout=200000&timestamp=1592646265664, urls: [dubbo://192.168.31.132:12347/person.shw.dubbo.api.DubboApi?anyhost=true&application=provider-nacos&bean.name=ServiceBean:person.shw.dubbo.api.DubboApi&category=providers&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=person.shw.dubbo.api.DubboApi&methods=getConsumerData&path=person.shw.dubbo.api.DubboApi&pid=2846&protocol=dubbo&register=true&release=2.7.3&side=provider&timestamp=1592646130152], dubbo version: 2.7.3, current host: 192.168.31.132
  12. 2020-06-20 17:44:26.218 INFO 3033 --- [ main] org.apache.dubbo.config.AbstractConfig : [DUBBO] Refer dubbo service org.apache.dubbo.rpc.service.GenericService from url nacos://120.79.76.230:8848/org.apache.dubbo.registry.RegistryService?anyhost=true&application=consumer-nacos&bean.name=ServiceBean:person.shw.dubbo.api.DubboApi&category=providers&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=true&interface=person.shw.dubbo.api.DubboApi&lazy=false&methods=getConsumerData&path=person.shw.dubbo.api.DubboApi&pid=3033&protocol=dubbo&qos.enable=false&register=true&register.ip=192.168.31.132&release=2.7.3&remote.application=provider-nacos&side=consumer&sticky=false&timeout=200000&timestamp=1592646265664, dubbo version: 2.7.3, current host: 192.168.31.132
  13. [泛化调用成功]----->"333333333333333333"
  14. 2020-06-20 17:44:26.409 INFO 3033 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
  15. 2020-06-20 17:44:26.627 INFO 3033 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 10009 (http) with context path ''
  16. 2020-06-20 17:44:26.630 INFO 3033 --- [ main] p.s.dubbo.consumer.DubboConsumerLanuch : Started DubboConsumerLanuch in 2.383 seconds (JVM running for 2.875)

倒数第四行打印日志,泛化调用成功。

// TODO 后面补上一个网关gateway使用dubbo泛化调用的例子

发表评论

表情:
评论列表 (有 0 条评论,65人围观)

还没有评论,来说两句吧...

相关阅读

    相关 dubbo调用

    dubbo泛化调用 一、前言 > 泛接口调用方式主要用于客户端没有API接口及模型类元的情况,参数及返回值中的所有POJO均用Map表示,通常用于框架集成,比如:实