简单来说,Dubbo用于给消费方提供调用接口 (将接口注册到注册中心中)
节点角色说明:
● Provider: 暴露服务的服务提供方
● Consumer: 调用远程服务的服务消费方
● Container: 服务运行容器
● Registry: 服务注册与发现的注册中心
● Monitor: 统计服务的调用次数和调用时间的监控中心
(1) 启动容器,加载运行服务提供方
(2) 提供方启动时,在注册中心注册自己提供的服务
(3) 消费方启动时,在注册中心订阅自己需要的服务
(4) 注册中心用于管理提供方提供的url;ZooKeeper是Dubbo推荐使用的注册中心
(5) 监控中心管理整个过程
(1) 注册中心返回提供方地址列表给消费方,如果有变更,注册中心推送变更数据给消费方
(2) 消费方从提供方地址列表中,选择一台提供者进行调用,如果调用失败,选择另一台
(3) 消费方和提供方在内存中记录调用次数和调用时间,每分钟发送一次统计数据给监控中心
创建两个模块,一个作为提供方,一个作为消费方,通过Dubbo来实现消费方远程调用服务方提供 的服务,分别启动双方,进行测试
注意:要求双方都是可独立运行的war项目,并不是通过maven依赖调用提供方的服务
将接口作为独立模块的原因:
(1) 提供方需要实现此接口定义具体的方法(服务),因而需要定义此接口
(2) 消费方需要定义此接口从而调用接口中的方法(使用此接口提供的服务)
导致双方对此接口的定义重复,一旦接口改动,双方修改繁琐,故将公用接口作为独立模块
package com.itheima.service;
public interface UserService {
public String sayHello();
}
//并且需要将此模块install成为jar包
(1) pom.xml文件中的内容
<groupId>com.itheimagroupId>
<artifactId>dubbo-serviceartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<properties>
<spring.version>5.1.9.RELEASEspring.version>
<dubbo.version>2.7.4.1dubbo.version>
<zookeeper.version>4.0.0zookeeper.version>
properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubboartifactId>
<version>${dubbo.version}version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>${zookeeper.version}version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>${zookeeper.version}version>
dependency>
<dependency>
<groupId>com.itheimagroupId>
<artifactId>dubbo-interfaceartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
dependencies>
(2) web.xml中的内容
<context-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath*:spring/applicationContext*.xmlparam-value>
context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
listener>
(3) UserServiceImpl中的内容
//导入公共接口所在类的包
/**
* 1. @Service注解是Dubbo包下的,并不是Spring包下的
* 2. 使用了此注解的类提供的方法将会对外发布,并将访问地址,端口等注册到注册中心中
*/
@Service
public class UserServiceImpl implements UserService {
@Override
public String sayHello() {
return "hello Dubbo !";
}
}
(4) applicationContext.xml中的内容
<dubbo:application name="dubbo-service"/>
<dubbo:registry address="zookeeper://192.168.200.130:2181"/>
<dubbo:annotation package="com.itheima.service.impl" />
(1) pom.xml中的内容
<groupId>com.itheimagroupId>
<artifactId>dubbo-webartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>warpackaging>
<properties>
<spring.version>5.1.9.RELEASEspring.version>
<dubbo.version>2.7.4.1dubbo.version>
<zookeeper.version>4.0.0zookeeper.version>
properties>
<dependencies>
<dependency>
<groupId>org.apache.dubbogroupId>
<artifactId>dubboartifactId>
<version>${dubbo.version}version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-frameworkartifactId>
<version>${zookeeper.version}version>
dependency>
<dependency>
<groupId>org.apache.curatorgroupId>
<artifactId>curator-recipesartifactId>
<version>${zookeeper.version}version>
dependency>
<dependency>
<groupId>com.itheimagroupId>
<artifactId>dubbo-interfaceartifactId>
<version>1.0-SNAPSHOTversion>
dependency>
dependencies>
(2) web.xml中的内容
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:spring/springmvc.xmlparam-value>
init-param>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
(3) UserController中的内容
//导入公共接口所在类的包
@RestController
@RequestMapping("/user")
public class UserController {
/**
* 1. @Reference注解导入的为Dubbo包
* 2. @Reference注解作用:
* (1) 从zookeeper注册中心获取提供方的访问url
* (2) 进行远程调用RPC
* (3) 将结果封装为一个代理对象,赋值给userService对象
*/
@Reference
private UserService userService;
@RequestMapping("/sayHello")
public String sayHello(){
return userService.sayHello();
}
}
(4) springmvc.xml中的内容
<mvc:annotation-driven/>
<context:component-scan base-package="com.itheima.controller"/>
<dubbo:application name="dubbo-web" >
<dubbo:parameter key="qos.port" value="33333"/>
dubbo:application>
<dubbo:registry address="zookeeper://192.168.200.130:2181"/>
<dubbo:annotation package="com.itheima.controller" />
(1) 对service模块、web模块分别运行此指令,作为提供方和消费方的两个war项目
(2) web模块运行之后的日志信息如下
(3) 运行效果如下
当提供方(生产者)与消费方(消费者)需要传递Pojo类型时,需要使Pojo类实现IO包下的Serializable 接口,成为二进制流进行传输
问:当注册中心挂了之后,服务是否可以正常访问
答:可以;因为消费者在第一次调用服务时,会将生产者的地址缓存到本地,之后访问此地址时 不会再经过注册中心;当生产者的地址发生变化之后,注册中心通过notify机制通知消费者 新地址;
注意:注册中心挂了之后,已经存在的消费者可以访问服务,但新的消费者将无法进行访问
消费者在调用生产者的服务时,如果发生了阻塞、等待等情况,消费者会一直等待下去
在某个峰值时刻,如果大量的请求阻塞,会造成雪崩现象
Dubbo使用超时机制解决此问题,设置一个超时时间,如果在此时间内无法完成服务的访问,则 自动断开连接,并报timeout错误
使用timeout属性配置超时时间,默认值1000,单位毫秒
//timeout属性设置超时时间,单位毫秒;
@Service(timeout = 3000)
@Reference
注解也可使用timeout属性,但一般建议使用在生产者中,因为在生产者中 定义服务,可以预估需要的时间,方便进行超时设置设置了超时时间,在此时间内如果无法完成服务的调用,会自动断开连接并报错
如果出现网络抖动(网络不稳定短暂中断但很快恢复),则此次请求就会失败
Dubbo使用重试机制避免此类问题的发生
通过retries属性设置重试次数,默认为两次(加上第一次的请求,共三次)
//retries属性设置重传次数
@Service(timeout = 3000, retries = 3)
当生产者的服务出现新版本时,并不会让所有的消费者访问此版本,而是会让一部分消费者先调 用此版本,确认没有问题之后再让所有消费者调用此版本,称为灰度发布
Dubbo中使用version属性设置和调用同一个接口的不同版本
生产者代码:
@Service(version = "v1.0", timeout = 3000)
public class UserServiceImpl1 implements UserService {
...}
@Service(version = "v2.0")
public class UserServiceImpl2 implements UserService {
...}
消费者代码:
@Reference(version = "v2.0")
private UserService userService;
//成功调用v2.0版本的服务
(1) Random:默认策略;按权重设置随机概率,权重越高,越容易被访问
(2) RoundRobin:按请求先后轮询
(3) LeastActive:响应时间越短越容易被访问
(4) ConsistentHash:相同的请求总是调用同一个服务
注意:四种策略与Nginx的负载均衡策略类似
@Service(weight = 200)
@Reference(loadbalance = "random")
@Reference(loadbalance = "roundrobin")
@Reference(loadbalance = "leastactive")
@Reference(loadbalance = "consistenthash")
(1) Failover Cluster: 默认模式;失败重试,默认重试两次,使用retries配置;当出现失败,报错, 并重试其它服务器两次,一般用于读操作,写操作可能由于延迟等导致写入数据重复
(2) Failfast Cluster : 快速失败;发起一次调用,如果失败立即报错,不会重试,通常用于写操作
(3) Failsafe Cluster: 失败安全,出现异常时,不报错,直接忽略,返回一个空结果
(4) Failback Cluster: 失败自动恢复;请求失败后,后台记录失败请求,定时重发
(5) Forking Cluster : 并行调用多个服务,只要一个成功即返回
(6) Broadcast Cluster: 广播调用所有提供者,逐个调用,任意一个调用失败则报错
@Reference(cluster = "failover")
当服务器压力较大时,根据实际情况,释放某些服务,仅保留核心服务以保证服务整体高效运作
服务降级方式有如下两种
(1) mock = force:return null:表示消费者对该服务的调用都是返回空值,相当于把服务屏蔽
(2) mock = fail:return null:表示消费方对该服务的调用在失败后(会重试),再返回空值,不报错
@Reference(mock = "force : return null")