概述
一 Dubbo 概述
1.1Dubbo 介绍
-
Dubbo是阿里巴巴公司开源的一个高性能、轻量级的 Java RPC 框架。
-
致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案。
-
2011年10月27日,阿里巴巴开源了自己的SOA服务化治理方案的核心框架Dubbo
-
2012年10月23日Dubbo2.5.3发布后,在Dubbo开源将满一周年之际,阿里基本停止了对Dubbo的主要升级。只在之后的2013年和2014年更新过2次对Dubbo2.4的维护版本,然后停止了所有维护工作。Dubbo对Spring 的支持也停留在了Spring 2.5.6版本。
-
阿里停止维护和升级Dubbo期间,当当网开始维护自己的Dubbo分支版本Dubbox,支持了新版本的Spring,并对外开源了Dubbox。同时,网易考拉也维护了自己的独立分支Dubbok,可惜并未对外开源。
-
随着微服务的火热兴起,在国内外开发者对阿里不再升级维护Dubbo的吐槽声中,阿里终于开始重新对Dubbo的升级和维护工作。在2017年9月7日,阿里发布了Dubbo的2.5.4版本,距离上一个版本2.5.3发布已经接近快5年时间了。在随后的几个月中,阿里Dubbo开发团队以差不多每月一版本的速度开始快速升级迭代,修补了Dubbo老版本多年来存在的诸多bug,并对Spring等组件的支持进行了全面升级。
-
2018年1月8日,Dubbo2.6.0版本发布,并将之前当当网开源的Dubbo分支Dubbox进行了合并,实现了Dubbo版本的统一整合。
-
官网:http://dubbo.apache.org
1.2架构
节点 | 角色说明 |
---|---|
Provider | 暴露服务的服务提供方 |
Consumer | 调用远程服务的服务消费方 |
Registry | 服务注册与发现的注册中心 |
Monitor | 统计服务的调用次数和调用时间的监控中心 |
Container | 服务运行容器 |
二 Dubbo 快速入门
2.1 zookeeper安装
Dubbo默认支持五种注册中心:推荐使用 Zookeeper 注册中心
Zookeeper 是 Apache Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。
安装步骤 :
1)上传jdk安装包jdk-8u171-linux-x64.tar.gz到linux系统并解压
# 进入/usr/local 目录
cd /usr/local
tar -zxvf jdk-8u171-linux-x64.tar.gz
2)设置环境变量:
vi /etc/profile
JAVA_HOME=/usr/local/jdk1.8.0_171
PATH=$JAVA_HOME/bin:$PATH
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export JAVA_HOME
export PATH
export CLASSPATH
3)使得环境变量生效
source /etc/profile
4)验证
java -version
5)安装zk
# 进入/usr/local
cd /usr/local
# 把压缩包上传到linux系统,解压
tar -zxvf zookeeper-3.4.6.tar.gz
# 进入zookeeper-3.4.6目录
cd zookeeper-3.4.6
# 进入conf目录
cd conf
# 把zoo_sample.cfg 改名为 zoo.cfg
mv zoo_sample.cfg zoo.cfg
# 打开zoo.cfg文件, 修改dataDir属性
vi zoo.cfg
# 设置zoo.cfg放置数据的目录
dataDir=/usr/local/zookeeper-3.4.6/data
# wq保存退出
./zkServer.sh stop # 停止服务
./zkServer.sh status # 查看服务状态
2.2 服务提供者
实现步骤
1)创建maven工程:dubbo-service-user,添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<!--指定SpringBoot继承的父项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>cn.itcast</groupId>
<artifactId>dubbo-service-user</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--web启动器,传递依赖了tomcat-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--dubbo的起步依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.5</version>
</dependency>
<!-- zookeeper的api管理依赖 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<!-- zookeeper依赖 -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
</dependencies>
</project>
注意事项:如果出现zookeeper not connected主要是 jdk版本和zookeeper版本不匹配,jdk1.8以后,pom中zookeeper依赖不能超过3.4+,不然就会报错。此外3.3+的版本依赖包阿里云中没有,所以建议使用3.4+版本的。
2)创建application.yml配置文件,并添加配置如下
# springboot项目对外暴漏的端口(web项目就是访问端口)
server:
port: 9001
dubbo:
application:
# 项目名称,用于计算服务的依赖关系。(区分不同项目的服务)
name: dubbo-service-user
# 指定zk注册中心地址
registry:
address: zookeeper://192.168.12.134:2181
protocol:
# 对外暴漏的服务使用的协议:dubbo协议, 官网推荐
name: dubbo
# 对外暴漏服务的端口
port: 20881
# dubbo包扫描,会扫描dubbo提供的@Service注解
scan:
base-packages: cn.itcast.api
3)编写Api接口
package cn.itcast.api;
/**
* dubbo对外提供的服务的接口
*/
public interface UserApi {
String sayHi(String name);
}
4)编写Api接口实现类
package cn.itcast.api.impl;
import cn.itcast.api.UserApi;
import org.apache.dubbo.config.annotation.Service;
/**
* 使用dubbo提供的@Service注解
* 1. 导入的包:org.apache.dubbo.config.annotation.Service
* 2. 注意:不要导入spring的包 org.springframework.stereotype.Service
*/
@Service
public class UserApiImpl implements UserApi {
@Override
public String sayHi(String name) {
return "hi," + name;
}
}
5)启动类
package cn.itcast;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
启动后的执行流程:
1、Spring容器启动
2、Dubbo自动配置类开始执行
3、读取yml中相关配置,扫描包路径:cn.itcast.api
4、找到加了@Service注解的实现类:UserApiImpl
5、将UserApiImpl作为一个提供的服务接口放到zk中,指定使用dubbo协议,20881端口,key为cn.itcast.api.UserApi
,
value: dubbo://localhost:20881/userApiImpl
2.3 服务消费者
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
</parent>
<groupId>cn.itcast</groupId>
<artifactId>order-web-manager</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--dubbo的起步依赖-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.5</version>
</dependency>
<!-- zookeeper的api管理依赖 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.2.0</version>
</dependency>
<!-- zookeeper依赖 -->
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency>
</dependencies>
</project>
2)编写application.yml
server:
port: 8080
dubbo:
# 项目名称,用于计算服务的依赖关系。(区分不同项目的服务)
application:
name: order-web-manager
# 注册中心地址
registry:
address: zookeeper://192.168.12.134:2181
3)编写api接口
注意:服务消费者的接口名称、接口方法名称、包路径必须要与服务提供者一致,否则生成代理对象调用报错。
所以:这里的接口最好从服务提供者那里拷贝,不要手动去编写。
package cn.itcast.api;
/**
* 服务消费者:
* 1、这里的接口一定要与服务提供者一样
* 2、接口包名称、接口名称、接口方法名称、方法参数个数类型、返回值都要一样。
*/
public interface UserApi {
String sayHi(String name);
}
4)编写控制器
package cn.itcast.controller;
import cn.itcast.api.UserApi;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("order")
public class OrderController {
/**
* @Reference
* 1、导入dubbo提供的注解:org.apache.dubbo.config.annotation.Reference
* 2、用于对指定的接口生成代理对象,通过代理封装远程调用相关代码,实现远程调用
*/
@Reference
private UserApi userApi;
@GetMapping
public String hello(){
// 执行RPC远程调用, 获取dubbo服务返回结果. 在浏览器显示
String result = userApi.sayHi("Tom");
return result;
}
}
5)启动类
package cn.itcast;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
6)访问http://localhost:8080/order
执行流程:
1、Spring容器启动
2、Dubbo自动配置类开始执行,加载相关配置。
3、初始化OrderController时,发现有个属性加了@Reference注解,处理该属性
4、根据加载的相关配置,从zk中获取已经注册上去的信息,生成代理对象,并设置到OrderController的属性中。
5、调用hello方法时,执行RPC远程调用服务提供端,得到返回结果。
2.4. 优化项目结构
创建Api接口工程:dubbo-service-api,抽取接口UserApi,将其放入dubbo-service-api
package cn.itcast.api;
public interface UserApi {
String sayHi(String name);
}
2)优化服务提供者
dubbo-service-user 工程删除UserApi, 然后添加依赖:
<!--依赖Api接口工程-->
<dependency>
<groupId>cn.itcast</groupId>
<artifactId>dubbo-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
3)优化服务消费者
order-web-manager 工程删除UserApi, 然后添加依赖:
<!--依赖Api接口工程-->
<dependency>
<groupId>cn.itcast</groupId>
<artifactId>dubbo-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
三 Dubbo-admin 管理平台(了解)
3.1介绍
我们在开发时,需要知道Zookeeper注册中心都注册了哪些服务,有哪些消费者来消费这些服务。我们可以通过部署一个管理中心来实现。其实管理中心就是一个web应用,部署到tomcat即可。
3.2 安装
安装步骤如下:
1)将资料中的dubbo-admin-2.6.0.war文件复制到tomcat的webapps目录下
2)启动tomcat,此war文件会自动解压。 注意:Tomcat不要放到中文目录!!!
3)修改WEB-INF下的dubbo.properties文件,注意dubbo.registry.address对应的值需要对应当前使用的Zookeeper的ip地址和端口号
dubbo.registry.address=zookeeper://192.168.134.129:2181
dubbo.admin.root.password=root
dubbo.admin.guest.password=guest
4)重启tomcat
3.3 使用
操作步骤:
1)访问http://localhost:6080/dubbo-admin-2.6.0/,输入用户名(root)和密码(root)
2)启动服务提供者工程和服务消费者工程,可以在查看到对应的信息
四 Dubbo 高级特性(重点)
4.1 序列化
在订单服务中,需要查询用户信息。现在需要在dubbo服务提供者中添加一个根据用户id查询用户的方法,并交给订单系统远程调用。这里需要注意:dubbo服务提供端与消费端之间传递的对象,必须要实现序列化接口。
4.1.2 dubbo-service-api 接口工程
1)创建实体类,注意: 这里的实体类必须实现序列化接口,否则报错。
package cn.itcast.pojo;
import java.io.Serializable;
/**
* user类
*/
public class User implements Serializable {
private Long id;
private String name;
private Integer age;
private Integer sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
2)编写接口,在接口添加一个方法:findById()
package cn.itcast.api;
import cn.itcast.pojo.User;
public interface UserApi {
String sayHi(String name);
User findById(Long id);
}
4.1.3 dubbo-service-user服务工程
1)改造UserApiImpl
package cn.itcast.api.impl;
import cn.itcast.api.UserApi;
import cn.itcast.pojo.User;
import org.apache.dubbo.config.annotation.Service;
/**
* dubbo对外提供的接口实现类
*/
@Service
public class UserApiImpl implements UserApi {
@Override
public User findById(Long id) {
User user = new User();
user.setId(1l);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
@Override
public String sayHi(String name) {
return "hello, " + name;
}
}
2)重启服务
4.1.4 order-web-manager消费者工程
1)改造UserApiImpl
package cn.itcast.api.impl;
import cn.itcast.api.UserApi;
import cn.itcast.pojo.User;
import org.apache.dubbo.config.annotation.Service;
/**
* dubbo对外提供的接口实现类
*/
@Service
public class UserApiImpl implements UserApi {
@Override
public User findById(Long id) {
User user = new User();
user.setId(1l);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
@Override
public String sayHi(String name) {
return "hello, " + name;
}
}
2)重启服务
6.1.4 order-web-manager消费者工程
1)修改控制器
package cn.itcast.controller;
import cn.itcast.api.UserApi;
import cn.itcast.pojo.User;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("order")
public class OrderController {
/**
* @Reference
* 1、导入dubbo提供的注解:org.apache.dubbo.config.annotation.Reference
* 2、用于对指定的接口生成代理对象,通过代理封装远程调用代理,实现远程调用
*/
@Reference
private UserApi userApi;
/**
* 根据用户id查询用户信息。
* http://localhost:8080/order/user/1
*/
@GetMapping("/user/{id}")
public User findUserById(@PathVariable("id") Long id){
User user = userApi.findById(id);
return user;
}
@GetMapping
public String hello(){
// 执行RPC远程调用, 获取dubbo服务返回结果, 在浏览器显示
String result = userApi.sayHi("Tom");
return result;
}
}
2)重启服务,访问http://localhost:8080/order/user/1
4.1.5 无序列化报错示例
cause:java.lang.IllegalStateException: Serialized class cn.itcast.pojo.User must implement java.io.Serializable
4.2 地址缓存
4.2.1 介绍
提问:注册中心挂了,服务是否可以正常访问?
-
可以,因为dubbo服务消费者在第一次调用时,会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心。
-
当服务提供者地址发生变化时,注册中心会通知服务消费者。
4.2.2 演示步骤
1)执行命令,关闭注册中心
[root@itcast bin]# ./zkServer.sh stop
2)测试访问order服务,依然可以调用服务
4.3 超时与重试
4.3.1 介绍
-
服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会一直等待下去,相关线程得不到释放。
-
在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,势必会造成雪崩。
-
dubbo 利用超时机制来解决这个问题,设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。 使用timeout属性配置超时时间,默认值1000,单位毫秒。
-
设置了超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。
-
如果出现网络延迟,则这一次请求就会失败。
-
Dubbo 提供重试机制来避免类似问题的发生。
-
通过 retries 属性来设置重试次数。默认为 2 次。
4.3.2 演示步骤
1)重启zk(之前关闭了)
2)服务提供端dubbo-service-user 中添加睡眠时间,再重启该服务
package cn.itcast.api.impl;
import cn.itcast.api.UserApi;
import cn.itcast.pojo.User;
import org.apache.dubbo.config.annotation.Service;
/**
* dubbo对外提供的接口实现类
*/
@Service
public class UserApiImpl implements UserApi {
@Override
public User findById(Long id) {
User user = new User();
user.setId(id);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
@Override
public String sayHi(String name) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("111");
return "hello, " + name;
}
}
3)重启服务消费端 order-web-manager
4)调用http://localhost:9002/hello,发现报错
5)服务提供端dubbo-service-user 服务提供端添加超时、重试配置
package cn.itcast.api.impl;
import cn.itcast.api.UserApi;
import cn.itcast.pojo.User;
import org.apache.dubbo.config.annotation.Service;
/**
* dubbo对外提供的接口实现类
*/
@Service(timeout = 30000, retries = 0) // 设置超时时间(默认1000),重试次数(默认2次)
public class UserApiImpl implements UserApi {
@Override
public User findById(Long id) {
User user = new User();
user.setId(id);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
@Override
public String sayHi(String name) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("111");
return "hello, " + name;
}
}
6)依次重启服务提供端、服务消费端,继续访问发现可以访问成功了。
注意事项
1)Debug时设置超时时间大一点,否则容易超时,因为debug中间停了下来。
2)Debug时重试次数设置为0次。
3)在服务提供端的@Service注解以及服务调用方的@Reference注解上都可以添加超时、重试配置(都加了以调用方为主)。
4)建议配置添加在服务提供端。
4.4 多版本
4.4.1 介绍
4.4.2 演示步骤
演示:启动2次服务,同时指定版本。消费者可以根据版本调用新老服务。
1)修改dubbo-service-user 工程的UserApiImpl
@Service(version = "1.0") // version指定服务版本
public class UserApiImpl implements UserApi {
@Override
public User findById(Long id) {
System.out.println("UserApiImpl服务版本1.0");
User user = new User();
user.setId(id);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
}
2)修改dubbo-service-user中的application.yml配置
server:
port: ${port:9001} # 表示端口默认为9001;如果运行时期通过VM Option传入port参数,以传入为主。
dubbo:
application:
name: dubbo-service-user
protocol:
name: dubbo
port: ${dport:20881} # 表示端口默认为20881;dport是动态传入的参数名称,可以随意定义
registry:
address: zookeeper://192.168.12.134:2181
scan:
base-packages: cn.itcast.api
3)启动dubbo-service-user工程,直接运行run方法即可(基于1.0版本代码)。
4)再次修改实现类,并指定版本与提示信息。
@Service(version = "2.0") // version指定服务版本
public class UserApiImpl implements UserApi {
@Override
public User findById(Long id) {
System.out.println("UserApiImpl服务版本2.0");
User user = new User();
user.setId(id);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
}
5)再次启动dubbo-service-user工程(基于2.0版本代码),注意:不要直接启动。直接启动没法传入port、dport两个参数,会报端口冲突的错误。启动前先修改VM Options参数:
启动后服务列表如下:
6)最后,消费者通过版本号来调用指定版本服务提供者(实际项目中可以将消费者做集群,部分服务去调用提供者1.0版本,部分服务去调用提供者2.0版本)
@RestController
@RequestMapping("order")
public class OrderController {
// @Reference(version = "1.0")
@Reference(version = "2.0") // 修改版本后重启
private UserApi userApi;
/**
* 访问地址: http://localhost:8080/order/user/100
*/
@GetMapping("/user/{id}")
public User findById(@PathVariable("id") Long id){
return userApi.findById(id);
}
}
7)测试,访问服务消费者,发现在9002(即2.0版本)的控制台中打印了文字,说明被调用了。
4.5 负载均衡
4.5.1 介绍
问题:当服务提供者搭建集群时,我们该调用哪个服务呢?这就需要负载均衡
负载均衡策略(4种):
-
random :按权重随机,默认值。按权重设置随机概率。
-
roundrobin :按权重轮询。
-
leastactive:最少活跃调用数,相同活跃数的随机。
-
consistenthash:一致性 Hash,相同参数的请求总是发到同一提供者。
演示:准备二个服务提供端,权重分别是100/200,测试负载均衡策略。本例使用random随机。
4.5.2 服务提供者一
1)设置权重 weight = 100
@Service(weight = 100)
public class UserApiImpl implements UserApi {
@Override
public User findById(Long id) {
System.out.println("服务提供端1:权重100");
User user = new User();
user.setId(id);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
...
}
2)重启服务
4.5.2 服务提供者二
1)重新修改服务提供者UserApiImpl,设置权重weight = 200
@Service(weight = 200)
public class UserApiImpl implements UserApi {
@Override
public User findById(Long id) {
System.out.println("服务提供端2:权重200");
User user = new User();
user.setId(id);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
...
}
2)启动服务启动者2: VM Option 需要配置 -Dport=9002 -Ddport=20882 (多版本时候已经配了)。
4.5.4 服务消费者
@RestController
@RequestMapping("order")
public class OrderController {
@Reference(loadbalance = "random") // 按照权重随机
private UserApi userApi;
/**
* 访问地址: http://localhost:8080/order/user/1
*/
@GetMapping("/user/{id}")
public User findById(@PathVariable("id") Long id){
return userApi.findById(id);
}
}
访问 http://localhost:8080/order/user/1 10次,运行结果如下,提供端2打印了6次,提供端1打印了4次,发现调用到提供端2的次数多一些(不一定是2:1,但调用非常多次时,大概是2:1的比例)
服务提供者1:
服务提供者2:
说明:负载均衡算法设置同样可以添加在提供端,建议加在消费端
4.6 集群容错
集群容错模式:
-
Failover Cluster:失败重试。默认值。当出现失败,重试其它服务器 ,默认重试2次,使用 retries 配置。一般用于读操作
-
Failfast Cluster :快速失败,只发起一次调用,失败立即报错。通常用于写操作。(不重试)
-
Failsafe Cluster :失败安全,出现异常时,直接忽略。返回一个空结果。(一般用于不太重要的操作,比如消息通知)
-
Failback Cluster :失败自动恢复,后台记录失败请求,定时重发。(一般用于非常重要的操作)
-
Forking Cluster :并行调用多个服务器,只要一个成功即返回。
-
Broadcast Cluster :广播调用所有提供者,逐个调用,任意一台报错则报错。
4.6.2 案例描述
测试集群容错模式:Failover Cluster:失败重试。
需求:
1)准备两个服务提供者,第一个的实现类中设置线程睡眠时间,模拟超时调用失败。第二个实现类中不设置睡眠时间。
2)如果来自消费者的请求第一次调用了第一个服务提供者会超时,自动进行失败重试,进行重试,可能找到第一个,也可能找到第二个,如果找到的还是第一个,又会重试。
3)如果来自消费者的请求第一次调用了第二个服务提供者,那直接成功了,不会有后续操作了。
6.6.3 演示步骤
1) 修改服务提供者一,然后重启
@Service
public class UserApiImpl implements UserApi {
@Override
public User findById(Long id) {
System.out.println("服务提供端1");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = new User();
user.setId(id);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
...
}
2)修改服务提供者二,然后重启
@Service
public class UserApiImpl implements UserApi {
@Autowired
private UserMapper userMapper;
@Override
public User findById(Long id) {
System.out.println("服务提供端2");
User user = new User();
user.setId(id);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
...
}
3)修改服务消费者集群容错模式为failover,失败重试
@RestController
@RequestMapping("order")
public class OrderController {
@Reference(cluster = "failover")
private UserApi userApi;
/**
* 访问地址: http://localhost:8080/order/user/1
*/
@GetMapping("/user/{id}")
public User findById(@PathVariable("id") Long id){
return userApi.findById(id);
}
}
4)测试,访问http://localhost:8080/order/user/1
查看控制台,可以发现它最开始可能是调用9001或9002,然后超时了进行重试,可能接下来继续调用9002,也可能9001,如果再调不通,又会重试一次。
4.7 服务降级
4.6.1 介绍
-
当服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,增加响应速度!
-
当下游的服务因为某种原因响应超时或不可用,上游主动调用本地的一些降级逻辑,避免等待或出错,迅速返回给用户!
-
参考官网:服务降级 | Apache Dubbo
-
降级 Dubbo 服务:可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。
其中:
-
mock=force:return null
表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。 -
mock=fail:return null
表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。 -
4.6.2 演示
演示1: mock=force:return null
测试后发现:dubbo服务提供端控制台没有任何输出,且服务消费端没有报错,说明服务被降级了,没有调用服务提供端。
@RestController
@RequestMapping("order")
public class OrderController {
// 表示不发起远程调用,直接返回null
@Reference(mock = "force:return null")
private UserApi userApi;
}
演示2:mock=fail:return null
@Service
public class UserApiImpl implements UserApi {
@Override
public User findById(Long id) {
System.out.println("服务提供端2");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
User user = new User();
user.setId(1l);
user.setName("张三");
user.setAge(20);
user.setSex(1);
return user;
}
}
步骤2:服务的方法调用超时后,再返回 null 值,不抛异常
@RestController
@RequestMapping("order")
public class OrderController {
// 服务的方法调用在失败后,再返回 null 值,不抛异常
@Reference(mock = "fail:return null")
private UserApi userApi;
}
步骤3:访问测试
因为调用服务超时,自动重试2次。最终调用服务失败,返回null
五 面试题(重点)
问题1: Dubbo只能从注册中心获取服务吗?
不是,也可以使用直连,直连方式是不需要从注册中心获取服务。
在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时可能需要点对点直连方式。@Reference(url = "dubbo://localhost:24567")
private IUserservice userservice;直连方式会失去负载均衡的能力,所以不适合生产环境。
问题2: Dubbo服务提供者一定要需要先开启动,消费者后启动吗?
不需要,如果在@Reference中添加check=false,可以先启动消费者,再启动提供者
问题3: Dubbo默认调用服务超时时长是多少?
服务超时时长是: 1000毫秒
问题4: Dubbo集群容错模式有哪些? 默认容错模式是什么?
Dubbo集群容错模式有6种:
Failover(失败重试、读操作)
Failfast(快速失败、写操作、不重试)
Failsafe(失败安全,忽略本次失败的请求)
Failback(失败自动恢复,记录失败请求,定时重发)
Forking(并行调用每个服务提供者,只要一个成功就返回)
Broadcast(广播模式,逐个调用,任意一个报错就报错)
默认容错模式是: Failover(失败重试)
问题5: Dubbo注册中心有哪些?
Dubbo默认支持五种注册中心:
1. Multicast
2. Zookeeper(推荐)
3. Nacos
4. Redis
5. Simple
问题6: Dubbo注册中心zookeeper挂掉,消费者还可以正常调用服务提供者吗?
可以正常调用,因为消费者从注册中心,获取服务地址后会在本地缓存。(地址缓存)
问题7: Dubbo如何实现服务降级?
心如死灰了, 不发起远程调用,直接返回null值。
mock=force:return null在远程调用失败后,返回null值,对消费者不产生调用影响。
mock=fail:return null
问题8: Dubbo负载均衡策略有哪些,默认负载均衡策略是什么?
负载均衡策略有4种:
1. random(随机,按照权重随机,相当于翻牌子)
2. roundrobin(轮询,按照权重逐个调用,挨个来)
3. leastactive(最少活跃数,性能好的机器能处理更多的请求)
4. consistanthash(一致性hash,相同参数的请求都发给同一个)默认负载均衡策略是: random (随机)
问题9: Dubbo项目灰度发布?
使用服务提供者的版本号隔离,调用时指定版本号。
最后
以上就是直率朋友为你收集整理的高性能服务框架Dubbo的全部内容,希望文章能够帮你解决高性能服务框架Dubbo所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复