什么是Dubbo
Dubbo是一个分布式服务框架,提供了高性能以及透明化的RPC远程服务调用解决方法,以及SOA服务治理方案。
Dubbo的核心部分:
远程通信:提供了高性能以及透明化的RPC远程服务调用。
服务治理:提供了包括集群容错、服务自动发现、负载均衡等服务治理方案。
Dubbo的架构:
上图是dubbo的架构图,有三种连接线,绿色实线代表同步操作,绿色虚线代表异步操作,蓝色虚线代表初始化操作。
由图可知:共有5种角色:
Provider:暴露服务的服务提供方。
Consumer:调用(消费)服务的服务消费方。
Registry:服务注册与发现的服务注册中心。
Monitor:统计服务调用次数和调用时间的监控中心。
Container:服务提供方的服务运行容器,可以是spring容器、jetty容器等。
启动调用步骤(对着图):
- 服务容器负责启动、加载、运行服务提供者,服务提供者是运行在服务容器中的。
- 服务提供者在启动时,向注册中心注册(发布)自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,因为消费者与注册中心使用的是长连接,并且消费者订阅了对应的数据,所以当注册中心被订阅的数据(主题)变更时,消费者会收到通知,做出相应反应(比如把变更后的数据拉取到本地进行更新)。
- 服务消费者调用服务提供者,从服务提供者的地址列表中,基于软负载均衡算法,选择一台提供者进行调用。如果调用失败,再选择另一台提供者进行调用。
- 服务消费者和提供者,在内存中的累计调用次数和调用时间,定时每分钟发送一次数据给监控中心。
Dubbo架构的特点:
- 连通性:
- 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。
- 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示。
- 服务提供者向注册中心注册所提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销,也就是只包含服务提供方接收到消费端的请求后开始,直到执行完逻辑返回响应给消费端时的时间。
- 服务消费端向注册中心获取所需的服务地址列表,并根据负载均衡算法直接调用提供者,汇报调用时间到监控中心,此时间包含网络开销,也就是记录请求发起开始,直到接收到响应之间的时间。
- 注册中心、服务提供者、服务消费者三者之间的连接都是长连接,与监控中心的连接时短连接。
- 注册中心通过长连接感知服务提供者的状态,服务提供者发生宕机,注册中心立刻发送事件给消费者。
- 注册中心和监控中心全部宕机也不会影响已运行的服务提供者和服务消费者,因为消费者在本地缓存了服务提供者的服务地址列表。并通过订阅进行地址列表及时更新。
- 注册中心和监控中心都是可选的,也就是可以基于dubbo的分布式服务系统可以不需要注册中心和监控中心,消费端和服务端可以直连,并且不对服务进行监控。只是没有注册中心将难以维护复杂的地址列表和服务之间的依赖关系。
- 健壮性:
- 监控中心宕机后不会影响服务的使用,只是会丢失部分的采样数据。
- 注册中心对等集群,任意一台宕机后,自动切换另一台。
- 注册中心全部宕机后,服务提供者和消费者仍能通过本地缓存的地址列表进行通信。
- 服务提供者是无状态的,也就是不会保存服务状态,也就是说我们不应该在服务提供方的进程(JVM)里缓存任意数据,导致同一提供者的不同实例状态不一样。任意一台宕机不影响使用。
- 服务提供者全部宕机后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复。
- 伸缩性:
- 服务提供者无状态,可动态增加、缩减机器部署实例,注册中心将会把增加、减少后的地址信息推送给消费者。
- 升级性:
当服务集群规模进一步扩大,带动IT治理结构进一步升级,需要实现动态部署,进行流动计算,现有分布式服务架构不会带来阻力。额 这个 我还没到达那个层面。
搭建一个简易的Dubbo服务
首先搭建一个服务提供者工程:
Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可。
需要依赖:
<!-- dubbo依赖 2.7以后 maven仓库 变成了apache的 之前的是com.alibaba的 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.2</version>
<exclusions>
<!-- 排除dubbo默认的spring依赖 -->
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- 提供日志的实现,这里使用logback日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 提供日志的实现 这里使用logback日志-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- Zookeeper客户端。这里是curator-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<!-- Zookeeper客户端。这里是curator-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
因为dubbo默认依赖的spring的版本号都比较低,比如2.7.2的spring版本是4.3.16,我们可以通过配置maven依赖来引入更新的spring版本。
这里使用Zookeeper作为dubbo服务的注册中心。
注意:Zookeeper服务器使用的版本太低的话,使用太高版本的curator会报错。服务提供方工程结构:
有两个子模块order-api、order-provider
order-api:这个是服务接口工程,服务的接口、传输数据实体类、业务异常都在这个工程里面。用于打包给消费者端引用。
order-provider:服务提供者工程,是dubbo服务,提供服务实现类。
public class OrderServiceImpl implements OrderService {
public DoOrderResponseWrapper doOrder(DoOrderRequestWrapper param) {
System.out.println(param.getName() + "创建了新订单");
Integer orderId = 100;
DoOrderResponseWrapper responseWrapper = new DoOrderResponseWrapper();
responseWrapper.setCode("30000");
responseWrapper.setData(orderId);
responseWrapper.setMsg("新建订单成功");
return responseWrapper;
}
}
public class APP {
public static void main(String[] args) {
Main.main(args);
}
}
<?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://dubbo.apache.org/schema/dubbo"
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">
<!-- name : 分布式系统中节点位移Id owner :一些信息,这里的语义是维护的人 -->
<dubbo:application name="order-provider" owner="YeHaocong"/>
<!-- 配置注册中心 protocol=N/A表示不使用配置中心,使用点对点-->
<dubbo:registry protocol="zookeeper" address="192.168.18.137:2181" />
<!-- 用dubbo协议在20880端口暴露服务 配置协议和端口 -->
<dubbo:protocol name="dubbo" port="20880" />
<!-- 声明需要暴露(发布)的服务接口 interface:接口名 ref:bean id-->
<dubbo:service interface="study.dubbostudy.orderapi.service.OrderService" ref="orderService" />
<!-- 配置是spring bean -->
<bean id="orderService" class="study.dubbostudy.orderprovider.serviceimpl.OrderServiceImpl" />
</beans>
由于order-provider模块用到了order-api模块,所以要把依赖添加到order-provider模块中。
注意:
服务配置文件默认是配置在类路径的 resources/META-INF/spring 目录下。dubbo默认会去这个目录下找配置文件。
这段源码就可以看出,尝试获取系统变量dubbo.spring.config的值,值为空就使用默认classpath*:META-INF/spring/*.xml。
可以在启动项目是添加JVM参数 -Ddubbo.spring.config=xxxx 修改默认值。
搭建一个消费者工程
依赖:
<!-- 提供者对外暴露的服务接口,参数封装、返回类型封装、异常等 -->
<dependency>
<groupId>study.dubbostudy</groupId>
<artifactId>order-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- dubbo依赖 2.7以后 maven仓库 变成了apache的 之前的是com.alibaba的 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>2.7.2</version>
<exclusions>
<!-- 排除dubbo默认的spring依赖 -->
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- spring依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<!-- Zookeeper客户端。这里是curator-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.12.0</version>
</dependency>
<!-- Zookeeper客户端。这里是curator-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.12.0</version>
</dependency>
<!-- 提供日志的实现,这里使用logback日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 提供日志的实现,这里使用logback日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
工程结构:
public class OrderConsumer {
public static void main(String[] args) {
//使用order-consumer.xml配置文件创建容器并初始化
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("order-consumer.xml");
//获取orderService bean
OrderService orderService = (OrderService) context.getBean("orderService");
DoOrderRequestWrapper requestWrapper = new DoOrderRequestWrapper();
requestWrapper.setName("YeHaocong");
//调用远程服务
DoOrderResponseWrapper responseWrapper = orderService.doOrder(requestWrapper);
System.out.println(responseWrapper);
}
}
<?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://dubbo.apache.org/schema/dubbo"
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">
<!-- name : 分布式系统中节点位移Id owner :一些信息,这里的语义是维护的人 -->
<dubbo:application name="dubbo-user" owner="YeHaocong"/>
<!-- 配置注册中心 N/A表示不使用配置中心,使用点对点-->
<dubbo:registry protocol="zookeeper" address="192.168.18.137:2181"/>
<!-- 生成远程服务代理,可以和本地bean一样使用远程bean -->
<!-- id : 远程代理服务Id,interface 接口 -->
<dubbo:reference id="orderService" interface="study.dubbostudy.orderapi.service.OrderService"/>
</beans>
启动
先启动服务提供方,启动类的main方法。
代表启动成功。
dubbo是URL驱动的,每个服务都会生成一个或多个URL。
上面服务的URL如下:
这个URL就是服务的地址。
URL的组成:
协议://host:port/服务接口的全限定名?接着就是一堆参数,就是你服务的配置的参数,比如timeout啥的。启动服务消费者:
成功调用并返回。
服务方也打印了相应信息。