Dubbo+Zookeeper使用
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
说明Apache Dubbo 是一款 RPC 服务开发框架用于解决微服务架构下的服务治理与通信问题官方提供了 Java、Golang 等多语言 SDK 实现。
本文介绍Dubbo的简单使用及一些Dubbo功能特性注册中心使用的是ZooKeeper可在官网下载。
另外在阿里巴巴发出的《微服务治理技术白皮书》中各种注册中心的区别如下
环境搭建
首先搭建一个ZooKeeper + Dubbo的项目
1下载ZooKeeper
在上面提供的官网上下载ZooKeeper解压到一个没有中文的路径下
因为默认提供的配置文件是一个模板解压后需要手动设置一下将zookeeper默认的配置文件zoo_sample.cfg复制一份并将文件名改为"zoo.cfg"
另外修改zoo.cfg配置文件中的这行配置默认的是Linux文件路径修改为当前目录的上级目录…/data
启动选择安装目录下bin文件夹下的zkServer.cmd
文件可双击启动或者在该目录下打开CMD窗口执行
没有报错运行结果如下表示启动成功
如果启动失败提示此时不应有 jdk
参考http://t.csdn.cn/52O2K
2创建项目
创建一个Zookper+Dubbo的项目有两个微服务一个用户服务UserService一个订单服务OrderService
-
订单服务消费者Consumer没有Service层直接使用用户服务查询用户信息、用户名
-
用户服务提供者Provider没有Controller层提供查询用户信息、用户名的接口
订单服务OrderService
Controlle层两个接口一个用于查找用户名一个用于查找用户。实现暂时空着待会儿使用Dubbo调用用户服务对应的接口
@RestController
@RequestMapping("order")
public class OrderController {
@GetMapping("/name")
public String getUserName(Integer id){
return userService.getUserName(id);
}
@GetMapping("/user")
public User getUser(Integer id){
return userService.getUser(id);
}
}
用户服务UserService
UserService实现类实现两个接口分别是查询用户名返回固定值查询用户返回固定用户对象
@Service
public class UserServiceImpl implements UserService {
@Override
public String getUserName(Integer id) {
return "getUserName";
}
@Override
public User getUser(Integer id) {
User user = new User(30, "张三", "12456");
return user;
}
}
3配置
使用Dubbo前需先导入依赖我这里直接在父模块中添加如下
<?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>
<groupId>com.hzy</groupId>
<artifactId>user-provider</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>8</java.version>
<spring-boot.version>2.2.1.RELEASE</spring-boot.version>
<dubbo.version>2.7.5</dubbo.version>
<curator.version>2.12.0</curator.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--MVC框架-->
<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>${dubbo.version}</version>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>${curator.version}</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>${curator.version}</version>
</dependency>
</dependencies>
</project>
设置各个服务的配置两方面一方面是zookeeper一方面是dubbo的如下
OrderService
server:
port: 8081
spring:
application:
name: dubbo-consumer
dubbo:
registry:
address: zookeeper://localhost:2181 # 连接到注册中心
protocol:
name: dubbo # 指定的协议
port: 28081 # 指定的端口
scan:
basePackages: com.hzy.controller # 接口列表和接口中的方法列表
UserService
server:
port: 8082
spring:
application:
name: dubbo-provider
dubbo:
registry:
address: zookeeper://localhost:2181 # 连接到注册中心
protocol:
name: dubbo # 指定的协议
port: 28082 # 指定的端口
scan:
basePackages: com.hzy.controller # 接口列表和接口中的方法列表
4启动
依次 启动用户服务、订单服务没有报错说明项目注册到zookeeper没有问题
打开zookeeper客户端输入 ls /
可查看到dubbo项再输入ls /dubbo
可查看到注册的服务
5使用
使用Dubbo非常简单只需如下操作
- 第一步修改提供者Service注解改为Dubbo依赖提供的
- 第二步将提供者的UserService接口复制一份到消费者这边来注意结构要平行相同
-
第三步在消费者Controller层注入引用UserService接口
-
第四步直接调用UserService中的接口
6测试
重启两个服务注意先启动提供者使用fastRequest测试
获取用户名
获取用户信息
可以看到两个接口都可以正常返回说明zookeeper+dubbo的结合已完成
Dubbo特性
以下介绍Dubbo中的一些特性更多特性用法可在官网中查看
1启动检查
上面一直提到需要先启动提供者再启动消费者这是因为消费者服务依赖于提供者在启动时Dubbo会进行检查发现提供者找不到会报错程序启动失败。
可在消费者这边添加如下配置取消启动检查
# 取消消费者检查
dubbo:
consumer:
check: false
再次启动就没有问题了
2地址缓存
地址缓存是指消费者与提供者联系建立起来了服务消费者会把提供者的接口地址缓存一份此时注册中心宕机该请求仍可以正常访问
发送请求没得问题
关闭注册中心zookeeper
两个服务一直在打印未知错误
此时访问刚刚的那个请求仍然可以成功
换个重启服务后没发过的请求也可以访问
3对象传输
使用Dubbo可以传输自定义对象但需要注意对象需要实现序列号接口不然会报错
根据经验凡是涉及到传输对象都需要实现序列化接口并且最好添加全参构造、无参构造因为有些工具底层可能是通过对象的全参构造来帮助我们实例化对象的。
4连接超时
Dubbo默认设置的连接超时是1秒即当消费者发送的请求1秒未得到返回即为超时这个超时是可以设置的。
在提供者这边方法内设置2秒睡眠
消费者发送请求返回超时错误
连接超时可以在消费者、提供者设置并且可以在接口、方法上设置优先级是消费者大于提供者方法大于接口如下我在消费者@Reference、提供者@Service双方设置超时时长
消费者设置1500ms
提供者设置3000ms
重启服务发送请求依旧报错说明提供者设置的超时没有起作用超时是以消费者的1500ms为准
互调设置消费者设置3000ms提供者1500ms重启服务可以正常访问
5重试次数
重试次数在之前连接超时错误的信息可以看到一个“Tried 3 times”表示尝试了3次默认重试2次
同样这个也可以在消费者、提供者双方设置
提供者
// 设置重试10次
@Service(timeout = 3000, retries = 10)
消费者
// 设置重试5次
@Reference(timeout = 1500, retries = 5)
private UserService userService;
同样优先级是消费者大于提供者
注意
需要注意哦不是所有操作都可以设置重试次数的应该需要考虑当前请求所对应的操作是否为幂等性操作即执行一次或多次结果都相同不然会造成数据混乱。以数据库的增删改查操作为例
-
增每增加一条数据数据库表都是变化的所以为非幂等性
-
删与增同理是非幂等性
-
改视情况而定看SQL语句是怎么写的如果是"number + 1"这样的是非幂等性如果是“set name = #{name} where id = #{id}”这样的就是幂等性的
-
查多次查询没有其他操作参与数据库表都是一样的所以是幂等性操作
6多版本
多版本是指如果存在多个提供者、消费者时可以指定版本信息让提供者指服务指定的消费者消费者指消费指定的服务者
消费者不指定版本信息
@Reference(timeout = 3000, retries = 5)
private UserService userService;
提供者指定版本信息“V1.0”
@Service(timeout = 3000, retries = 10, version = "V1.0")
请求失败提示没有可用的提供者
修改消费者版本号为“V1.0”
@Reference(timeout = 3000, retries = 5,version = "V1.0")
请求访问成功
使用Dubbo的这种特性可以实现灰度发布即版本更新不是一下全部更新而是在系统中设置多个版本如新版本的接口设置10个旧版本的接口设置5个实现“缓更新慢发布”减少系统出现异常的风险比如一些金融、支付类的软件。
7负载均衡
可使用“loadbalance”属性设置接口的负载均衡策略支持以下四种策略
-
RandomLoadBalance加权随机默认算法默认权重相同
-
RoundRobinLoadBalance加权轮询默认权重相同
-
LeastActiveLoadBalance最少活跃优先+加权随机能者多劳
-
ConsistentHashLoadBalance一致性Hash确定入参确定提供者适用于有状态的请求
注意使用并不是直接new它们类而是进入到它们对应的类里面查看它们的这行代码中写的名称
将该名称作为负载均衡属性的值
@Reference(loadbalance = "random")
private UserService userService;
在代码中打印一行标识开启两个服务发送十几条请求可以看到两边服务均有访问数量不等
提供者消费者两边都可以设置都有效
8失败降级
失败降级是指访问失败时返回一个降级方案即“打了折扣”的响应结果使用mock属性设置如下
@Reference(mock = "return 请求错误")
private UserService userService;
另外还可以有其他几种用法
# 调用失败返回请求错误
mock = "return 请求错误"
# 不发起远程调用直接返回null不管请求成功与否都返回null
mock = "force:return null"
# 发起远程调用失败后返回null
mock = "fail:return null"
# 发起远程调用失败后抛异常
mock = "throw"
# 调用失败后使用自己实现的降级方案
mock = "自定义类的全限定类名"
其中最后一种方法最好用搞一下
降级降级方案实现接口就可以
public class UserServiceMock implements UserService {
@Override
public String getUserName(Integer id) {
return "降级方案";
}
@Override
public User getUser(Integer id) {
return new User(-1,"降级对象","降级密码");
}
}
mock设置为降级方案的全限定类名
@Reference(mock = "com.hzy.mock.UserServiceMock")
private UserService userService;
测试连接超时走了降级
9集群容错
使用场景官方多个服务器部署同一集群中运行同一应用程序如果一台服务器出现故障其他服务器将接管负载确保应用程序对用户仍然可用。
Failover Cluster默认设置失败自动切换当出现失败重试其它服务器。通常用于读操作但重试会带来更长延迟。可通过 retries=“2” 来设置重试次数(不含第一次)。
其他配置如下
使用如下
@Reference(cluster = "failover")
private UserService userService;
同样也是使用对应的策略就进入到对应的类中使用对应的名称作为属性值
Nacos+Dubbo
Nacos作为注册中心Dubbo使用与ZooKeeper基本相同在使用上不同的地方只有以下两点
-
导入的依赖配置不同
-
注解不同ZooKeeper使用@Service、@Reference注解Nacos使用@DubboService、@DubboReference注解
总结
总结成一句话Dubbo是替代Feign的
另外提一句Dubbo是使用自定义的协议所以如果需要考虑提高Dubbo效率可以从传输协议、对象序列化方式这些方面入手
文中代码的GitHub地址https://github.com/Heapfiy/dubbo-simple.git
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |