Dubbo(一)——Dubbo 集成于 Spring 的原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Dubbo(一)——Dubbo 集成于 Spring 的原理相关的知识,希望对你有一定的参考价值。

参考技术A

最近一直在看dubbo的源码部分。在阅读的时候,需要有一个入手点,才能一点一点的进行下去。自己在研究的时候,发现思绪比较乱,于是就以 芋道源码 为基础,一点一点的啃食。芋道源码是直接从dubbo的配置和一些核心的API开始讲起,是从dubbo已经启动的过程作为开始节点,而这些核心 API 与 Spring 的之间的关系被省略了,这些东西对我来说属于前置的知识点,所以花了比较长的时间又从 Dubbo 的核心 API 倒着往前看。

在阅读 Dubbo 时,发现前置知识越来越多,如:Spring 的 refresh 中的一些核心点,Spring 中 bean 的生命周期,BeanFactory 与 FactoryBean 的区别等。所以这些前置知识花了特别多的时间去补。所幸,虽然补前置知识虽然时间长,但是性价比还是可以的。Dubbo 是依赖于Spring 的上下文环境的框架,其他依赖于 Spring 的框架也是相同的道理。Spring 的一些对外的扩展点,读过之后也会心中有数。

1、本篇主要是描述了 Dubbo 在 Spring 创建上下文的时候,是如何从创建,到能完整提供一个RPC调用能力的一些相关点。
2、由于源码比较多,直接贴断点也太过臃肿,所以仅仅贴一些关键点来概括整个流程。
3、本文是依赖于前面的 dubbo 项目进行断点分析,项目结构可以参照这里。项目中 dubbo 的配置方式是 xml 文件,所以本篇主要说 xml 配置方式。其他方式道理相同,并不是问题的关键点。

4、项目启动的是 dubbo-user 服务,所以 UserService 为 dubbo:service,OrderService 为 dubbo:reference。

下图为Spring 启动时是如何加载 Dubbo 的,其中省略了大量过程,只保留了一些关键节点,省略的部分可以略微脑补一下。

整个流程的入口是 Spring 的 refresh 方法。每个方法都有比较深的调用栈。与 Dubbo 有关的入口是 refresh 中的 invokeBeanFactoryPostProcessors 方法

这个方法是执行 beanFactory 的一些后处理操作,其核心流程为在Spring容器中找出实现了BeanFactoryPostProcessor接口的processor并执行。Spring容器会委托给PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法执行。
ConfigurationClassPostProcessor 是比较核心的类,在这里我们关注一下这个类。它的作用是对项目中配置的类进行处理。具体处理可以分为几步:

在加载类信息时,spring 会去用各种方式扫到注册的 bean 信息。我们在 spring 中注册的 bean,逃不出这个方法的扫描方式。 核心方法是:

扫描之后,会将扫描到的 bean 注册到 beanDefinitionMap 中

首先是此处 org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#parseBeanDefinitions,可以看出方法会以配置文件根节点起,遍历所有子节点。

其次是这里 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement(org.w3c.dom.Element, org.springframework.beans.factory.config.BeanDefinition), 此方法会通过解析出来的节点,获取对应的 Spring 的 namespaceUri ,进而获取对应的配置文件处理器。
此处 ele 参数实际值为 <dubbo:service ... />,namespaceUri 为 http://code.alibabatech.com/schema/dubbo

我们看一下 resolve 方法中的细节。因为这个方法内部才是 Dubbo 依赖于 Spring 的关键点。

此处的 NamespaceHandler 为 DubboNamespaceHandler,再创建结束之后,进行 init 初始化。

可以看到,DubboNamespaceHandler 在初始化的时候,会创建所有 dubbo 标签对应的Config 类的 DubboBeanDefinitionParser。并将 DubboBeanDefinitionParser 和 对应的 dubbo 标签类注册到 NamespaceHandlerSupport 的 parsers 中。

最后,会在 com.alibaba.dubbo.config.spring.schema.DubboBeanDefinitionParser#parse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, java.lang.Class<?>, boolean) 方法中进行处理

Dubbo 服务比较特殊,beanDefinition 跟普通的 bean 不太一样。在向 beanDefinitionMap 注册时,普通的 beanDefinition 的 beanName 与 beanClass 是对应的;而 dubbo 服务的 beanDefinition 的 beanName 是dubbo 服务的名称,beanClass 为 dubbo 对应的 Bean。

普通的 beanDefinition:

dubbo 引用的服务的 beanDefinition:

这一步的核心流程是从 beanFactory 中获取所有的 ApplicationListener,然后注册到监听器集合中。它的关键点其实是 ServiceBean。因为 ServiceBean 是 ApplicationListener 的实现。

所以 beanFactory 中 ServiceBean 也会被注册到监听器集合中。项目中的 ServiceBean 的 beanClass 实际是 UserService。

这一步的核心点,主要是创建剩余的各类对象,并将其保存到 singletonObjects 中。其中关联的前置知识为 Spring 中 bean 的生命周期 。它的核心方法是:
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

它的具体流程为:

ps:此处并不是只有这一步才会跟 bean 生命周期相关,bean 生命周期贯穿在 refresh 的很多流程中,只要执行doGetBean 方法,都会走这个流程。此处仅仅借楼关联一下。

这一步的核心点,是通知所有的监听器上下文刷新结束的事件。在这一步执行时,会通知到 ServiceBean。

此处暴露的是 UserService。

Dubbo 的启动条件是完全依赖于 Spring 的启动流程,Spring 的启动流程中核心的点是 refresh 方法。所以只要搞懂 refresh 方法,其他的拓展框架流程也会明白。只不过关联的知识点太多了,还是需要时间的积累才能一点一点的搞懂。
如果本篇有描述不清,或者描述有误的地方,还望在下方留言,大家一起交流,一起学习,一起进步~

一分钟入门Dubbo

Dubbo是什么?能做什么?

Dubbo是一个分布式服务框架,以及SOA治理方案。其功能主要包括:高性能NIO通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等。

Dubbo适用于哪些场景?

当网站变大后,不可避免的需要拆分应用进行服务化,以提高开发效率,调优性能,节省关键竞争资源等。

当进一步发展,服务间依赖关系变得错综复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。

接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?等等

在遇到这些问题时,都可以用Dubbo来解决。
可参见以下链接http://dubbo.apache.org/books/dubbo-user-book/preface/background.html

Dubbo性能如何?

Dubbo通过长连接减少握手,通过NIO及线程池在单连接上并发包处理消息,通过二进制流压缩数据,比常规HTTP等短连接协议更快,在阿里巴巴内部,每天支撑2000多个服务,30多亿访问量,最大单机支撑每天近1亿访问量。

可参见以下链接http://dubbo.apache.org/books/dubbo-user-book/perf-test.html

Dubbo架构


节点角色说明


节点 角色说明
Provider 暴露服务的服务提供方
Consumer 调用远程服务的服务消费方
Registry 服务注册与发现的注册中心
Monitor 统计服务的调用次数和调用时间的监控中心
Container 服务运行容器


调用关系说明

1.服务容器负责启动、加载、运行服务提供者。

2.服务提供者在启动时,向注册中心注册自己提供的服务

3.服务消费者在启动时,向注册中心订阅自己所需的服务。

6.服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

快速入门案例

提供者(Provider)服务代码

新建maven项目引入相关jar

pom.xml 引入spring 和dubbo相关jar

 <properties>
   <spring.version>4.2.4.RELEASE</spring.version>
 </properties>
 <dependencies>
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context</artifactId>
     <version>${spring.version}</version>
   </dependency>
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-beans</artifactId>
     <version>${spring.version}</version>
   </dependency>
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>${spring.version}</version>
   </dependency>
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jdbc</artifactId>
     <version>${spring.version}</version>
   </dependency>
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-aspects</artifactId>
     <version>${spring.version}</version>
   </dependency>
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-jms</artifactId>
     <version>${spring.version}</version>
   </dependency>
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-context-support</artifactId>
     <version>${spring.version}</version>
   </dependency>
   <!--dubbo相关-->
   <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>dubbo</artifactId>
     <version>2.5.7</version>
   </dependency>
   <dependency>
     <groupId>org.apache.zookeeper</groupId>
     <artifactId>zookeeper</artifactId>
     <version>3.4.10</version>
   </dependency>
   <dependency>
     <groupId>com.github.sgroschupf</groupId>
     <artifactId>zkclient</artifactId>
     <version>0.1</version>
   </dependency>
   <dependency>
     <groupId>javassist</groupId>
     <artifactId>javassist</artifactId>
     <version>3.11.0.GA</version>
   </dependency>
 </dependencies>
 <build>
   <plugins>
     <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-compiler-plugin</artifactId>
       <version>2.3.2</version>
       <configuration>
         <source>1.7</source>
         <target>1.7</target>
       </configuration>
     </plugin>
     <plugin>
       <groupId>org.apache.tomcat.maven</groupId>
       <artifactId>tomcat7-maven-plugin</artifactId>
       <configuration>
         <port>8081</port>
         <path>/</path>
       </configuration>
     </plugin>
   </plugins>
 </build>


配置web.xml


   <context-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>classpath:applicationContext*.xml</param-value>
   </context-param>
     <listener>
       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
     </listener>


配置applicatinsContext.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-2.0.xsd
http://code.alibabatech.com/schema/dubbo  
http://code.alibabatech.com/schema/dubbo/dubbo.xsd"
>
   <!--dubbo应用中心 通常项目名-->
   <dubbo:application name="dubbo_service"/>
   <!--配置dubbo注册中心 这里zk注册中心,需要提前启动zk-->
   <dubbo:registry address="zookeeper://192.168.16.199:2181"/>
   <!--包扫描-->
   <dubbo:annotation package="com.muzidao.dubbo.service.impl"/>
</beans>


配置服务接口,这个接口需要提供给消费者,可以通过打成jar包或复制到消费者服务代码中

public interface HelloService {
   public String sysHello(String name);
}


配置实现接口类

import com.alibaba.dubbo.config.annotation.Service;
import com.muzidao.dubbo.service.HelloService;

/**
* 服务实现类
* @author: astali
*/

//特别需要注意的地方,这个@Service不再是Spring,而是dubbo
@Service
public class HelloServiceImpl implements HelloService {
   @Override
   public String sysHello(String name) {
       System.out.println("我是dubbo服务接口实现者。");
       return "Hello 木子道 ,欢迎关注" + name;
   }
}


配置消费者(Coosumer)服务代码

pom.xml跟提供者一样

配置web.xml

  <servlet>
       <servlet-name>springmvc</servlet-name>
       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
       <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath:springmvc.xml</param-value>
       </init-param>
 </servlet>
 <servlet-mapping>
   <servlet-name>springmvc</servlet-name>
   <url-pattern>*.action</url-pattern>
 </servlet-mapping>


配置springmvc.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:mvc="http://www.springframework.org/schema/mvc"
      xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd"
>
 <mvc:annotation-driven>
    <mvc:message-converters register-defaults="false">
       <bean class="org.springframework.http.converter.StringHttpMessageConverter">
            <constructor-arg value="UTF-8"/>
       </bean>
     </mvc:message-converters>
  </mvc:annotation-driven>

 <!--dubbo应用中心 通常项目名-->
 <dubbo:application name="dubbo_consumer"/>
 <!--配置dubbo注册中心  需提前启动zk-->
 <dubbo:registry address="zookeeper://192.168.16.199:2181"/>
 <!--包扫描 -->
 <dubbo:annotation package="com.muzidao.dubbo.controller"/>
</beans>

配置消费者消费代码

package com.muzidao.dubbo.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.muzidao.dubbo.service.HelloService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* @author: astali
* Created by Administrator on 2018/5/3 17:25
*/

@RestController
@RequestMapping("/dubbo")
public class HelloController {
   //需要注意这里是引用dubbo引入注解
   @Reference
   private HelloService helloService;  
   //这个HelloService其实就是提供者的那个接口,
   这里我这是把接口复制到这个项目中,其实是可以打成jar引入进来。

   @RequestMapping("/hello")
   public String hello(){
       System.out.println("--木子道--正在说 -- Hello" );
       return  helloService.sysHello("dubbo");
   }
}

现在配置基本完成,现在启动看看效果


相关阅读



▼长按以下二维码即可关注▼







以上是关于Dubbo(一)——Dubbo 集成于 Spring 的原理的主要内容,如果未能解决你的问题,请参考以下文章

架构实战篇:Spring Boot 集成 Dubbo

一分钟入门Dubbo

集成Dubbo服务(Spring)

如何使用Dubbo服务和集成Spring

Spring 集成 Dubbo

dubbo入门和springboot集成dubbo小例子