Dubbo 介绍
官网:http://dubbo.apache.org/zh-cn/
Github:https://github.com/apache/dubbo
2018 年 2 月 15 日,阿里巴巴的服务治理框架 dubbo 通过投票,顺利成为 Apache 基金会孵化项目。
Apache Dubbo 是一款高性能、轻量级的开源 Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
Dubbo 架构
Dubbo 提供三个核心功能:基于接口的远程调用、容错和负载均衡,以及服务的自动注册与发现。Dubbo 框架广泛的在阿里巴巴内部使用,以及当当、去哪儿、网易考拉、滴滴等都在使用。
节点角色说明
节点 | 角色说明 |
---|---|
Provider |
暴露服务的服务提供方 |
Consumer |
调用远程服务的服务消费方 |
Registry |
服务注册与发现的注册中心 |
Monitor |
统计服务的调用次数和调用时间的监控中心 |
Container |
服务运行容器 |
调用关系说明
- 服务容器负责启动,加载,运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Dubbo 快速入门
我们先通过一个简单的案例让大家理解一下 Dubbo 的使用,然后基于 Spring Boot 和 Spring Cloud 环境整合 Dubbo。
Dubbo 采用全 Spring 配置方式,透明化接入应用,对应用没有任何 API 侵入,只需用 Spring 加载 Dubbo 的配置即可。
依赖
JDK 1.6 以上和 Maven 3.0 以上,采用 Maven 多模块聚合工程构建 api 模块,provider 模块以及 consumer 模块。
聚合工程
项目结构如下图,简单介绍一下:
-
dubbo-api
:服务接口 -
dubbo-provider
:依赖服务接口,具体的业务实现,服务提供者 -
dubbo-coonsumer
:依赖服务接口,远程调用服务,服务消费者
依赖关系
dubbo-parent 的 pom.xml 依赖 apache dubbo。
org.apache.dubbo
dubbo
2.7.4.1
dubbo-provider 和 dubbo-consumer 的 pom.xml 依赖 dubbo-api 服务接口。
org.example
dubbo-api
1.0-SNAPSHOT
定义服务接口
dubbo-api 中编写 HelloService.java
package org.example.service;
/**
* Hello服务
*/
public interface HelloService {
String sayHello(String name);
}
定义服务提供者
在 provider 模块中实现服务接口
dubbo-provider 中编写 HelloServiceImpl.java
package org.example.service.impl;
import org.example.service.HelloService;
/**
* 服务实现
*/
public class HelloServiceImpl implements HelloService {
public String sayHello(String name) {
return "hello " + name;
}
}
配置服务提供者
dubbo-provider.xml
加载 Spring 配置启动服务
package org.example;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 发布服务
*/
public class Provider {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:dubbo-provider.xml");
context.start();
System.out.println("服务注册成功!");
System.in.read(); // 按任意键退出
}
}
定义服务消费者
通过 Spring 配置引用远程服务
dubbo-consumer.xml
加载 Spring 配置并调用远程服务
package org.example;
import org.example.service.HelloService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* 调用远程服务
*/
public class Consumer {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:dubbo-consumer.xml");
context.start();
HelloService helloService = (HelloService) context.getBean("helloService"); // 获取远程服务代理
String result = helloService.sayHello("world"); // 执行远程方法
System.out.println(result); // 显示调用结果
}
}
Dubbo 常用标签
-
dubbo:application
:应用程序名称 -
dubbo:registry
:连接注册中心信息(配置注册中心) -
dubbo:protocol
:服务提供者注册服务采用的协议- Dubbo 协议,默认
- RMI 协议
- Hessian 协议
- HTTP 协议
- WebService 协议
- Thrift 协议
- Memcached 协议
- Redis 协议
- Rest 协议(RESTful)
- Grpc 协议
- 更多协议信息请参考:http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html
-
dubbo:service
:声明需要暴露的服务接口 -
dubbo:reference
:配置订阅的服务(生成远程服务代理)
更多配置信息请参考:http://dubbo.apache.org/zh-cn/docs/user/references/xml/introduction.html
注册中心
注册中心我们已经学习了不少,例如:ZooKeeper、Eureka、Consul、Nacos 等等,注册中心可以更高效的管理系统的服务:比如服务接口的发布、自动剔除无效的服务、自动恢复服务等。
Dubbo 支持五种注册中心:Multicast、Nacos(推荐)、ZooKeeper(推荐) 、Redis、Simple。本文重点介绍前两个,更多注册中心的信息请参考:http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html
Multicast 注册中心
Multicast 注册中心不需要启动任何中心节点,只要广播地址一样,就可以互相发现。
- 提供方启动时广播自己的地址
- 消费方启动时广播订阅请求
- 提供方收到订阅请求时,单播自己的地址给订阅者,如果设置了
unicast=false
,则广播给订阅者 - 消费方收到提供方地址时,连接该地址进行 RPC 调用。
组播受网络结构限制,只适合小规模应用或开发阶段使用。组播地址段: 224.0.0.0 - 239.255.255.255
配置
或
为了减少广播量,Dubbo 缺省使用单播发送提供者地址信息给消费者,如果一个机器上同时启了多个消费者进程,消费者需声明 unicast=false
,否则只会有一个消费者能收到消息。
当服务者和消费者运行在同一台机器上,消费者同样需要声明unicast=false
,否则消费者无法收到消息,导致 No provider available for the service 异常。
或
ZooKeeper 注册中心
Apache ZooKeeper 是一个开放源码的分布式应用程序协调组件,是 Hadoop 和 Hbase 的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,推荐使用。
在微服务项目开发中 ZooKeeper 主要的角色是当做服务注册中心存在,我们将编写好的服务注册至 ZooKeeper 即可。
流程说明:
- 服务提供者启动时: 向
/dubbo/com.foo.BarService/providers
目录下写入自己的 URL 地址。 - 服务消费者启动时: 订阅
/dubbo/com.foo.BarService/providers
目录下的提供者 URL 地址。并向/dubbo/com.foo.BarService/consumers
目录下写入自己的 URL 地址。 - 监控中心启动时: 订阅
/dubbo/com.foo.BarService
目录下的所有提供者和消费者 URL 地址。
支持以下功能:
- 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息;
- 当注册中心重启时,能自动恢复注册数据,以及订阅请求;
- 当会话过期时,能自动恢复注册数据,以及订阅请求;
- 当设置
时,记录失败注册和订阅请求,后台定时重试; - 可通过
设置 zookeeper 登录信息; - 可通过
设置 zookeeper 的根节点,不配置将使用默认的根节点; - 支持
*
号通配符
,可订阅服务的所有分组和所有版本的提供者。
作为 Dubbo 的老牌黄金搭档 ZooKeeper,我们在单独讲解 Dubbo 时已经给大家分享过如何使用了,本文系 Spring Cloud Alibaba 系列文章,重点对象是 Nacos,所以 ZooKeeper 这里就不过多赘述了。
Nacos 注册中心
Nacos 是 Alibaba 公司推出的开源工具,用于实现分布式系统的服务发现与配置管理。Nacos 是 Dubbo 生态系统中重要的注册中心实现,其中 dubbo-registry-nacos 则是 Dubbo 融合 Nacos 注册中心的实现,推荐使用。
Nacos 官网:https://nacos.io/zh-cn/
Github:https://github.com/alibaba/nacos
预备工作
当您将 Nacos 整合到您的 Dubbo 工程之前,请确保后台已经启动 Nacos 服务。关于 Nacos 的安装和其他详细内容可参考我之前的文章 Spring Cloud 系列之 Alibaba Nacos 注册中心(一)、Spring Cloud 系列之 Alibaba Nacos 注册中心(二)。
快速上手
Dubbo 融合 Nacos 成为注册中心的操作步骤非常简单,大致步骤可分为“增加 Maven 依赖”和“配置注册中心“。
依赖
核心依赖主要是 dubbo-registry-nacos
和 nacos-client
。
org.apache.dubbo
dubbo-registry-nacos
2.7.4.1
com.alibaba.nacos
nacos-client
1.3.0
配置注册中心
服务提供者和服务消费者只需要调整 address
属性配置即可。
单机配置:
或:
dubbo.register.address=nacos://192.168.10.101:8848
或:
dubbo.register.protocol=nacos
dubbo.register.address=192.168.10.101:8848
集群配置:
或:
dubbo.register.address=nacos://192.168.10.101:8848
或:
dubbo.register.protocol=nacos
dubbo.register.address=192.168.10.101:8848,192.168.10.102:8848,192.168.10.103:8848
随后,重启您的 Dubbo 应用,Dubbo 的服务提供和消费信息在 Nacos 控制台中即可显示。
Spring Cloud Alibaba Nacos 整合 Dubbo
之前的文章中,无论我们学习 Eureka、Consul 还是 Nacos,负责服务间通信的功能都是由 Ribbon 来完成的,接下来我们使用 Dubbo 来替换 Ribbon。
聚合工程
dubbo-demo
聚合工程。SpringBoot 2.3.0.RELEASE
、Spring Cloud Hoxton.SR5
。
项目结构如下图,简单介绍一下:
-
service-api
:服务接口 -
product-service
:商品服务,服务提供者,提供了/product/list
接口 -
order-service
:订单服务,服务消费者,远程调用商品服务
依赖关系
dubbo-demo 的 pom.xml。
4.0.0
com.example
dubbo-demo
pom
1.0-SNAPSHOT
service-api
product-service
order-service
org.springframework.boot
spring-boot-starter-parent
2.3.0.RELEASE
Hoxton.SR5
2.1.0.RELEASE
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
${spring-cloud-alibaba.version}
pom
import
service-api 的 pom.xml
dubbo-demo
com.example
1.0-SNAPSHOT
4.0.0
service-api
org.projectlombok
lombok
product-service 需要依赖 Nacos 和 Dubbo 的依赖,还有 service-api 的依赖,完整依赖如下:
dubbo-demo
com.example
1.0-SNAPSHOT
4.0.0
product-service
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-alibaba-dubbo
org.springframework.boot
spring-boot-starter-web
com.example
service-api
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
order-service 需要依赖 Nacos 和 Dubbo 的依赖,还有 service-api 的依赖,完整依赖如下:
dubbo-demo
com.example
1.0-SNAPSHOT
4.0.0
order-service
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
com.alibaba.cloud
spring-cloud-alibaba-dubbo
org.springframework.boot
spring-boot-starter-web
com.example
service-api
1.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
定义服务接口
我们在 service-api
模块中定义实体类和服务接口信息。
实体类
Product.java
package com.example.product.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product implements Serializable {
private Integer id;
private String productName;
private Integer productNum;
private Double productPrice;
}
Order.java
package com.example.product.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order implements Serializable {
private Integer id;
private String orderNo;
private String orderAddress;
private Double totalPrice;
private List productList;
}
服务接口
package com.example.product.service;
import com.example.product.pojo.Product;
import java.util.List;
/**
* 商品服务
*/
public interface ProductService {
/**
* 查询商品列表
*
* @return
*/
List selectProductList();
}
定义服务提供者
配置文件
配置文件需要配置 Nacos 注册中心和 Dubbo 相关信息,核心配置如下:
server:
port: 7070 # 端口
spring:
application:
name: product-service # 应用名称
# 配置 Nacos 注册中心
cloud:
nacos:
discovery:
enabled: true # 如果不想使用 Nacos 进行服务注册和发现,设置为 false 即可
server-addr: 127.0.0.1:8848 # Nacos 服务器地址,单机版
# Dubbo
dubbo:
# 提供方应用信息,用于计算依赖关系
application:
name: product-service
# 使用 nacos 注册中心暴露服务地址
registry:
protocol: nacos
address: spring-cloud://localhost
# 用 dubbo 协议在 20880 端口暴露服务
protocol:
name: dubbo
port: 20880
# 扫描需要暴露的服务,可以被 @EnableDubbo 注解替代
#scan:
# base-packages: com.example.service
服务提供者
product-service 的 ProductServiceImpl.java
package com.example.service.impl;
import com.example.product.pojo.Product;
import com.example.product.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Service;
import java.util.Arrays;
import java.util.List;
/**
* 商品服务
* timeout 调用该服务的超时时间
* version 为版本号
* group 为分组
* interface、group、version 三者确定一个服务
*/
@Slf4j
@Service(timeout = 5000, version = "1.0", group = "product-service")
public class ProductServiceImpl implements ProductService {
/**
* 查询商品列表
*
* @return
*/
@Override
public List selectProductList() {
log.info("商品服务查询商品信息...");
return Arrays.asList(
new Product(1, "华为手机", 1, 5800D),
new Product(2, "联想笔记本", 1, 6888D),
new Product(3, "小米平板", 5, 2020D)
);
}
}
值得注意的是 @Service
注解不是 Spring 的注解而是 Dubbo
的注释:
启动类
启动类通过 @EnableDubbo
注解扫描需要暴露的服务,如果配置文件中配置了该选项,那么这里可以省略。
package com.example;
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 扫描需要暴露的服务
@EnableDubbo(scanBasePackages = "com.example.service")
// 开启 @EnableDiscoveryClient 注解,当前版本默认会开启该注解
//@EnableDiscoveryClient
@SpringBootApplication
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}
定义服务消费者
配置文件
配置文件需要配置 Nacos 注册中心和 Dubbo 相关信息,核心配置如下:
server:
port: 9090 # 端口
spring:
application:
name: order-service # 应用名称
# 配置 Nacos 注册中心
cloud:
nacos:
discovery:
enabled: true # 如果不想使用 Nacos 进行服务注册和发现,设置为 false 即可
server-addr: 127.0.0.1:8848 # Nacos 服务器地址,单机版
# Dubbo
dubbo:
# 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样
application:
name: order-service
# 发现 nacos 注册中心暴露的服务
registry:
protocol: nacos
address: spring-cloud://localhost
cloud:
subscribed-services: product-service # 订阅服务,远程调用的服务名称
服务消费者
order-service 的 OrderServiceImpl.java
package com.example.service.impl;
import com.example.product.pojo.Order;
import com.example.product.service.ProductService;
import com.example.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
// dubbo 提供了 @Reference 注解,可替换 @Autowired 注解,用于引入远程服务
// 如果注册服务时设置了版本及分组信息,调用远程服务时也要设置对应的版本及分组信息
@Reference(timeout = 5000, version = "1.0", group = "product-service")
private ProductService productService;
/**
* 根据主键查询订单
*
* @param id
* @return
*/
@Override
public Order selectOrderById(Integer id) {
log.info("订单服务查询订单信息...");
return new Order(id, "order-001", "中国", 22788D,
productService.selectProductList());
}
}
启动类
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 开启 @EnableDiscoveryClient 注解,当前版本默认会开启该注解
//@EnableDiscoveryClient
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
测试
先启动 Nacos 服务器,然后启动服务提供者 product-service,访问:http://localhost:8848/nacos/ 控制台显示如下:
然后启动服务消费者,控制台显示如下:
订单服务调用远程商品服务,结果如下:
Dubbo 负载均衡
在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random
随机调用,也可以自行扩展负载均衡策略。
负载均衡策略
Random LoadBalance
- 随机,按权重设置随机概率。
- 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance
- 轮询,按公约后的权重设置轮询比率。
- 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance
- 最少活跃调用数,ping 值(延迟低)的调用,相同延迟的情况下随机。
- 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance
- 一致性 Hash,相同参数的请求总是发到同一提供者。
- 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
- 算法参见:http://en.wikipedia.org/wiki/Consistent_hashing
- 缺省只对第一个参数 Hash,如果要修改,请配置
- 缺省用 160 份虚拟节点,如果要修改,请配置
配置
一般在项目中不会在代码层面指定权重,而是通过监控中心(dubbo-admin)对服务动态的指定权重,官方文档:http://dubbo.apache.org/zh-cn/docs/admin/introduction.html
xml
服务端服务级别
客户端服务级别
服务端方法级别
客户端方法级别
yaml
dubbo:
provider:
loadbalance: roundrobin
weight: 100
consumer:
loadbalance: roundrobin
注解
@Service(loadbalance = "roundrobin", weight = 100)
@Reference(loadbalance = "roundrobin")
至此 Dubbo RPC 通信所有的知识点就讲解结束了。
本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议
。
大家可以通过 分类
查看更多关于 Spring Cloud
的文章。
您的点赞
和转发
是对我最大的支持。
关注公众号 哈喽沃德先生
「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~