Duboo基础
1.相关概念
Dubbo是阿里巴巴公司开源的一个高性能、轻量级的Java RPC框架,
致力于提供高性能和透明化的RPC(远程服务调用方案),以及SOA服务治理方案,
官网:http://dubbo.apache.org
dubbo架构:
节点角色说明:
- Provider:暴露服务的服务提供方
- Container:服务运行容器
- Consumer:调用远程服务的服务消费方
- Registry:服务注册与发现的注册中心
- Monitor:统计服务的调用次数和调用时间的监控中心
2.Dubbo入门案例
注意:以下的项目中的springboot版本不要选择过高,选择过高会报错,选择2.7.8即可
(1)编写原始项目
编写两个模块,分别包含service层和Dao(controller)层的代码,我们需要运用controller层调用service的服务:
编写完成的项目目录如下:
同时,需要在controller模块中导入service层的依赖:
<dependency>
<groupId>com.example</groupId>
<artifactId>Dubboservice</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
代码如下:
@RestController
@RequestMapping("/user")
public class Hellocontroller {
@Autowired
private Helloservice helloservice;
@GetMapping("/hello")
public String sayHello(String name){
return helloservice.sayHello(name);
}
}
public interface Helloservice {
String sayHello();
}
@Service
public class Helloserviceimpl implements Helloservice {
@Override
public String sayHello(String name) {
System.out.println("Hello,Dubbo and "+name+"!!!!");
return "Hello,Dubbo and "+name+"!!!!";
}
}
至于为什么controller层能调用service的代码,明明他们在不同的模块下,@SpringBootAplication扫描的路径应不包含另一个模块的代码呀?
答:因为我们引入了另一个模块的依赖,在我们扫描的时候他同时也会扫描另一个模块的代码,也就会扫到那个那个Bean对象并装配上去
(2)进行改造
我们现在的项目依旧是单体项目,因为每个模块并不能单独的运行,我们需要进行一定的改造:
<1>在父工程pom文件导入依赖:
因为是springBoot编写的,所以需引入dubbo-spring-boot-starter,而不是:dubbo
<properties>
<dubbo.version>3.2.0-beta.4</dubbo.version>
<spring-boot.version>2.7.8</spring-boot.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-bom</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<version>${dubbo.version}</version>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<2>在controller模块和service模块导入依赖:
需要引入dubbo与springboot整合的依赖,以及springboot的依赖
<dependencies>
<!-- dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-zookeeper-curator5</artifactId>
<type>pom</type>
<exclusions>
<exclusion>
<artifactId>slf4j-reload4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- spring boot starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--公共接口模块-->
<dependency>
<groupId>org.example</groupId>
<artifactId>Dubbointerface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<3>添加dubbo注解
在service的实现类中,我们将@service注解进行替换:
@DubboService
public class Helloserviceimpl implements Helloservice {
@Override
public String sayHello(String name) {
System.out.println("Hello,Dubbo and "+name+"!!!!");
return "Hello,Dubbo and "+name+"!!!!";
}
}
@DubboService注解可以帮我们把该对象对外发布,也就是在我们配置的注册中心中注册一下
在controller模块中,我们将@Autowired注解进行替换
@RestController
@RequestMapping("/user")
public class Hellocontroller {
@DubboReference
private Helloservice helloservice;
@GetMapping("/hello")
public String sayHello(String name){
return helloservice.sayHello(name);
}
}
@DubboReference的作用包括:
- 1.从zookeeper注册中心获取userservice的访问url
- 2.进行远程调用RPC
- 3.将结果封装为一个代理对象。给变量赋值
@DubboReference注解是远程注入,而@Autowreid注解是本地注入
<4>配置yml文件
我们需要将Bean对象在注册中心注册以及需要从注册中心获取对象,就需要配置注册中心:
在service层中:
#服务端口
server:
port: 9000
dubbo:
#配置需要服务模块的名称
application:
name: Dubboservice
#配置注册中心的地址
registry:
address: zookeeper://192.168.37.136:2181
在controller层中:
dubbo:
application:
name: Dubbocontroller
registry:
address: zookeeper://192.168.37.136:2181
配置完成后我们就可以启动两个服务了
<5>测试
通过访问路径:localhost:8080/user/hello?name=zc
可以看到返回结果:
说明成功
(3)抽离公共接口
我们在上述操作中发现,在service层中和controller层中我们都需要都用Helloservice接口,那么我们就可以单独将其抽离成一个独立的模块:
我们新建一个Dubbointerface模块,其中就存放该接口方法,其他的模块通过引入该模块就可以获取该接口方法了:
新建模块:
其他模块引入对该模块的依赖:
<dependency>
<groupId>org.example</groupId>
<artifactId>Dubbointerface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
这样依旧测试成功
Dubbo高级配置
1.序列化
在两台机器传输对象时,需要将对象的数据进行序列化和反序列化操作才能进行传输
- dubbo内部已经将序列化和反序列化的过程内部封装了。
- 我们只需要在定义pojo类时实现serializable接口即可
- 一般会定义一个公共的pojo模块,让生产者和消费者都依赖该模块。
2.地址缓存
注册中心挂了,服务是否可以正常访问?
- 可以,因为dubbo服务消费者在第一次调用时,会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心。
- 当服务提供者地址发生变化时,注册中心会通知服务消费者。
3.超时与重试
<1>超时:
- 服务消费者在调用服务提供者的时候发生了阻塞、等待的情形,这个时候,服务消费者会一直等待下去
- 在某个峰值时刻,大量的请求都在同时请求服务消费者,会造成线程的大量堆积,势必会造成雪崩。
- dubbo利用超时机制来解决这个问题,设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。
- 可以使用timeout配置超时时间,默认值为1000,单位毫秒
通过@Dubboservice标签中的timeout标签或在yml配置文件的registry中指定
<2>重试:
- 设置了超时时间,在这个时间段内,无法完成服务访问,则自动断开连接
- 如果出现网络抖动,则这一次请求就会失败。
- Dubbo提供重试机制来避免类似问题的发生
- 通过retries属性来设置重试次数。默认为2次。
通过@Dubboservice标签中的retries标签或在service模块的yml配置文件consumer中指定
例如:
dubbo:
#配置需要服务模块的名称
application:
name: Dubboservice
#配置注册中心的地址
registry:
address: zookeeper://192.168.37.136:2181
#配置连接超时时间
timeout: 3000
#配置重试次数
consumer:
retries: 3
<3>多版本
-
灰度发布:当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。
-
dubbo中使用version属性来设置和调用同一个接口的不同版本
-
使用@Service注解中的version标签指定当前服务为那个版本,再通过@DubboReference注解中的version标签指定要注入的是哪个版本即可
-
或者在service模块的yml文件中配置provider的version(先配置id,也就是provider的类名)和consumer的version((先配置id,也就是consumer的类名))
例如有两个service的版本:
@DubboService(version = "v1.0")
public class Helloserviceimpl1 implements Helloservice {
private Integer i=1;
@Override
public String printHello(String name){
System.out.println(i);
return "Hello "+name+"!";
}
@Override
public User findUser(int id){
User user=new User(id,"小明",18);
return user;
}
}
@DubboService(version = "v2.0")
public class Helloserviceimpl2 implements Helloservice {
private Integer i=2;
@Override
public String printHello(String name){
System.out.println(i);
return "Hello "+name+"!";
}
@Override
public User findUser(int id){
User user=new User(id,"小明",18);
return user;
}
}
@RestController
@RequestMapping("/user")
public class Hellocontroller {
@DubboReference(version = "v2.0")
private Helloservice helloservice;
@GetMapping("/hello")
public String sayHello(String name){
return helloservice.printHello(name);
}
@GetMapping("/find")
public User findUser(int id){
return helloservice.findUser(id);
}
}
最后在控制台输出的就是:1
<4>负载均衡
负载均衡策略(4种):
1.Random:按权重随机,默认值。按权重设置随机概率。
- 通过@Service中的weight标签指定权重,通过@DubboReferance中的LoadBlance标签可以指定均衡策略:random(默认方案)
- 或者在service的yml文件的consumer中指定均衡策略
2.RoundRobin:按权重轮询。
3.LeastActive:最少活跃调用数,相同活跃数的随机。
4.ConsistentHash:一致性Hash,相同参数的请求总是发到同一提供者。
<5>集群容错
集群容错模式:
- Failover Cluster:失败重试。默认值。当出现失败,重试其它服务器,默认重试2次,使用retries配置。一般用于读操作
- Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于写操作。
- Failsafe Cluster:失败安全,出现异常时,直接忽略。返回一个空结果。
- Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。
- Forking Cluster :并行调用多个服务器,只要一个成功即返回。
- Broadcast Cluster :广播调用所有提供者,逐个调用,任意一台报错则报错。
配置方法都是在@DubboReferance中的cluster标签指定的,例如:
@DubboReferance(cluster="failover")
等
<6>服务降级
服务降级方式:
-
mock=force:return null表示消费方对该服务的方法调用都直接返回null值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。
(也就是不论怎么访问,访问都不会生效)
-
mock=fail:return null表示消费方对该服务的方法调用在失败后,再返回null值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
(也就是当访问失败时就让访问不再生效)
同样是在@DubboReferance中配置mock标签,例如:
@DubboReferance(mock="force:return null")
最后的yml文件呈现为:
#服务端口
server:
port: 9000
dubbo:
#配置需要服务模块的名称
application:
name: Dubboservice
#配置注册中心的地址
registry:
address: zookeeper://192.168.37.136:2181
#配置连接超时时间
timeout: 3000
#配置重试次数
consumer:
retries: 3
#指定负载均衡策略:随机访问
loadbalance: random
#指定集群容错策略:失败重试
cluster: failover
#指定服务降级策略
mock: force:return null