官网:http://dubbo.apache.org/zh-cn/
Github:https://github.com/apache/dubbo
2018 年 2 月 15 日,阿里巴巴的服务治理框架 dubbo 通过投票,顺利成为 Apache 基金会孵化项目。
Apache Dubbo 是一款高性能、轻量级的开源 Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。
Dubbo 提供三个核心功能:基于接口的远程调用、容错和负载均衡,以及服务的自动注册与发现。Dubbo 框架广泛的在阿里巴巴内部使用,以及当当、去哪儿、网易考拉、滴滴等都在使用。
节点 | 角色说明 |
---|---|
Provider |
暴露服务的服务提供方 |
Consumer |
调用远程服务的服务消费方 |
Registry |
服务注册与发现的注册中心 |
Monitor |
统计服务的调用次数和调用时间的监控中心 |
Container |
服务运行容器 |
我们先通过一个简单的案例让大家理解一下 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。
<dependencies>
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubboartifactId>
<version>2.7.4.1version>
dependency>
dependencies>
dubbo-provider 和 dubbo-consumer 的 pom.xml 依赖 dubbo-api 服务接口。
<dependencies>
<dependency>
<groupId>org.examplegroupId>
<artifactId>dubbo-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
dependencies>
dubbo-api 中编写 HelloService.java
package org.example.service;
/**
* Hello服务
*/
public interface HelloService {
String sayHello(String name);
}
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
<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">
<dubbo:application name="hello-world-app"/>
<dubbo:registry address="multicast://224.5.6.7:1234"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="org.example.service.HelloService" ref="helloService"/>
<bean id="helloService" class="org.example.service.impl.HelloServiceImpl"/>
beans>
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(); // 按任意键退出
}
}
dubbo-consumer.xml
<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">
<dubbo:application name="consumer-of-helloworld-app" />
<dubbo:registry address="multicast://224.5.6.7:1234" />
<dubbo:reference id="helloService" interface="org.example.service.HelloService" />
beans>
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:application
:应用程序名称dubbo:registry
:连接注册中心信息(配置注册中心)dubbo:protocol
:服务提供者注册服务采用的协议
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 注册中心不需要启动任何中心节点,只要广播地址一样,就可以互相发现。
unicast=false
,则广播给订阅者组播受网络结构限制,只适合小规模应用或开发阶段使用。组播地址段: 224.0.0.0 - 239.255.255.255
<dubbo:registry address="multicast://224.5.6.7:1234" />
或
<dubbo:registry protocol="multicast" address="224.5.6.7:1234" />
为了减少广播量,Dubbo 缺省使用单播发送提供者地址信息给消费者,如果一个机器上同时启了多个消费者进程,消费者需声明 unicast=false
,否则只会有一个消费者能收到消息。
当服务者和消费者运行在同一台机器上,消费者同样需要声明unicast=false
,否则消费者无法收到消息,导致 No provider available for the service 异常。
<dubbo:registry address="multicast://224.5.6.7:1234?unicast=false" />
或
<dubbo:registry protocol="multicast" address="224.5.6.7:1234">
<dubbo:parameter key="unicast" value="false" />
dubbo:registry>
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 是 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
。
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubbo-registry-nacosartifactId>
<version>2.7.4.1version>
dependency>
<dependency>
<groupId>com.alibaba.nacosgroupId>
<artifactId>nacos-clientartifactId>
<version>1.3.0version>
dependency>
服务提供者和服务消费者只需要调整 address
属性配置即可。
单机配置:
<dubbo:registry address="nacos://127.0.0.1:8848"/>
<dubbo:registry protocol="nacos" address="127.0.0.1:2181"/>
或:
dubbo.register.address=nacos://192.168.10.101:8848
或:
dubbo.register.protocol=nacos
dubbo.register.address=192.168.10.101:8848
集群配置:
<dubbo:registry address="nacos://192.168.10.101:2181?backup=192.168.10.102:2181,192.168.10.103:2181"/>
<dubbo:registry protocol="nacos" address="192.168.10.101:2181,192.168.10.102:2181,192.168.10.103:2181"/>
或:
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 控制台中即可显示。
之前的文章中,无论我们学习 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。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>dubbo-demoartifactId>
<packaging>pompackaging>
<version>1.0-SNAPSHOTversion>
<modules>
<module>service-apimodule>
<module>product-servicemodule>
<module>order-servicemodule>
modules>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.3.0.RELEASEversion>
parent>
<properties>
<spring-cloud.version>Hoxton.SR5spring-cloud.version>
<spring-cloud-alibaba.version>2.1.0.RELEASEspring-cloud-alibaba.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>${spring-cloud-alibaba.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
project>
service-api 的 pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dubbo-demoartifactId>
<groupId>com.examplegroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>service-apiartifactId>
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
project>
product-service 需要依赖 Nacos 和 Dubbo 的依赖,还有 service-api 的依赖,完整依赖如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dubbo-demoartifactId>
<groupId>com.examplegroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>product-serviceartifactId>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dubboartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.examplegroupId>
<artifactId>service-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
project>
order-service 需要依赖 Nacos 和 Dubbo 的依赖,还有 service-api 的依赖,完整依赖如下:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dubbo-demoartifactId>
<groupId>com.examplegroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>order-serviceartifactId>
<dependencies>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dubboartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.examplegroupId>
<artifactId>service-apiartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
project>
我们在 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<Product> productList;
}
package com.example.product.service;
import com.example.product.pojo.Product;
import java.util.List;
/**
* 商品服务
*/
public interface ProductService {
/**
* 查询商品列表
*
* @return
*/
List<Product> 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<Product> 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 提供了多种均衡策略,缺省为 random
随机调用,也可以自行扩展负载均衡策略。
一般在项目中不会在代码层面指定权重,而是通过监控中心(dubbo-admin)对服务动态的指定权重,官方文档:http://dubbo.apache.org/zh-cn/docs/admin/introduction.html
服务端服务级别
<dubbo:service interface="..." loadbalance="roundrobin" weight="100" />
客户端服务级别
<dubbo:reference interface="..." loadbalance="roundrobin" />
服务端方法级别
<dubbo:service interface="..." weight="100">
<dubbo:method name="..." loadbalance="roundrobin"/>
dubbo:service>
客户端方法级别
<dubbo:reference interface="...">
<dubbo:method name="..." loadbalance="roundrobin"/>
dubbo:reference>
dubbo:
provider:
loadbalance: roundrobin
weight: 100
consumer:
loadbalance: roundrobin
@Service(loadbalance = "roundrobin", weight = 100)
@Reference(loadbalance = "roundrobin")
至此 Dubbo RPC 通信所有的知识点就讲解结束了。
本文采用 知识共享「署名-非商业性使用-禁止演绎 4.0 国际」许可协议
。
大家可以通过 分类
查看更多关于 Spring Cloud
的文章。
您的点赞
和转发
是对我最大的支持。
扫码关注 哈喽沃德先生
「文档 + 视频」每篇文章都配有专门视频讲解,学习更轻松噢 ~