一.Dubbo的常用属性配置
1. check -启动时检查
Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"。
可以通过 check="false" 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动。
开启配置:
消费者:
1. <dubbo:reference check="false"/> 默认是true
2. <dubbo:consumer check="false"/> //全局配置,配置所有的consumer关闭启动时检查
生产者:
1. <dubbo:service check="false"/>
2. <dubbo:provider check="false"/> //配置所有的provider关闭启动时检查
配置的查找顺序:
- 方法级优先,接口级次之,全局配置再次之
- 如果级别一样,则消费方优先,提供方次之
2. version -多版本
当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。
可以按照以下的步骤进行版本迁移:
0.在低压力时间段,先升级一半提供者为新版本
1.再将所有消费者升级为新版本
2.然后将剩下的一半提供者升级为新版本
老版本服务提供者配置:
<dubbo:service interface="com.qf.service.DemoService" version="1.0.0" />
新版本服务提供者配置:
<dubbo:service interface="com.qf.service.DemoService" version="2.0.0" />
老版本服务消费者配置:
<dubbo:reference id="demoService" interface="com.qf.service.DemoService" version="1.0.0" />
新版本服务消费者配置:
<dubbo:reference id="demoService" interface="com.qf.service.DemoService" version="2.0.0" />
如果不需要区分版本,可以按照以下的方式配置(随机匹配):
<dubbo:reference id="demoService" interface="com.qf.service.DemoService" version="*" />
测试:
生产者:
提供两个实现类模拟两个版本的服务
public class DemoServiceImpl implements DemoService {
public String sayHello(String name) {
return "hello1"+name;
}
}
public class DemoServiceImpl2 implements DemoService {
public String sayHello(String name) {
return "hello2"+name;
}
}
修改配置:
<?xml version="1.0" encoding="UTF-8"?>
<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.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- 1.给服务起个名-->
<dubbo:application name="dubbo-provider"/>
<!-- 2.将zookeeper路径指定-->
<dubbo:registry address="zookeeper://114.55.219.117:2181"/>
<!-- 3.指定建立socket链接的端口号-->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- 4.服务提供-->
<dubbo:service interface="com.qf.service.DemoService" ref="demoService" version="1.0.0"/>
<dubbo:service interface="com.qf.service.DemoService" ref="demoService2" version="2.0.0"/>
<!-- 5.将DemoServiceImpl配置到spring容器中去-->
<!--旧版本-->
<bean id="demoService" class="com.qf.service.impl.DemoServiceImpl"></bean>
<!--新版本-->
<bean id="demoService2" class="com.qf.service.impl.DemoServiceImpl2"></bean>
</beans>
消费者:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--取名-->
<dubbo:application name="dubbo-consumer"/>
<!--zookeeper地址-->
<dubbo:registry address="zookeeper://114.55.219.117:2181"/>
<!--生成远程服务代理,和生产者的service包名一致-->
<!--随机使用哪个版本-->
<dubbo:reference interface="com.qf.service.DemoService" id="demoService" version="*"/>
<!--将controller交给spring去管理-->
<bean class="com.qf.controller.DemoController" id="demoController">
<property name="demoService" ref="demoService"/>
</bean>
</beans>
3. timeout超时时间
指定服务被调用的超时时间
在method,reference,service,consumer,provider中添加timeout属性,并指定超时时间的毫秒值
测试:
生产者:
分别在两个实现类方法中让线程睡眠3秒
public class DemoServiceImpl implements DemoService {
public String sayHello(String name) {
try {
Thread.sleep(3000);
} catch (Exception e){
}
return "hello1"+name;
}
}
public class DemoServiceImpl2 implements DemoService {
public String sayHello(String name) {
try {
Thread.sleep(3000);
} catch (Exception e){
}
return "hello2"+name;
}
}
消费者:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!--取名-->
<dubbo:application name="dubbo-consumer"/>
<!--zookeeper地址-->
<dubbo:registry address="zookeeper://114.55.219.117:2181"/>
<!--生成远程服务代理,和生产者的service包名一致-->
<!--随机使用哪个版本-->
<!--
分别在reference,method和consumer里设置了timeout,method优先级最高,所有执行method中的timeout
-->
<dubbo:reference interface="com.qf.service.DemoService" id="demoService" version="*" timeout="4000">
<dubbo:method name="sayHello" timeout="2000"/>
</dubbo:reference>
<dubbo:consumer timeout="5000"/>
<!--将controller交给spring去管理-->
<bean class="com.qf.controller.DemoController" id="demoController">
<property name="demoService" ref="demoService"/>
</bean>
</beans>
4. retries重试次数
retries指定重试次数,当调用服务失败时,指定重试的次数,不包括第一次,默认值为2
幂等: 多次重复操作和一次操作产生的影响是一样的。
非幂等:多次重复操作和一次操作产生的影响是不一样的。
在开发中要根据服务具有以上哪个特性决定重试次数
<dubbo:method retries="2"/>
<dubbo:reference retries="2"/>
<dubbo:service retries="2"/>
<dubbo:consumer retries="2"/>
<dubb:provider retries="2"/>
5.本地存根
在controller调用远程服务的代理对象之前,先调用本地的stub方法,在方法中可以做参数的校验,异常的捕获,容错性,可以在调用远程服务前,就规避一些问题,避免远程调用后才发生
相当于,controller先调用了stub方法然后在stub中调用远程服务
创建一个服务接口的实现类
public class DemoServiceStup implements DemoService{
private DemoService demoService;
//dubbo会将真正的远程服务代理对象传递过来
public DemoServiceStup(DemoService demoService){
this.demoService = demoService;
}
public String sayHello(String name) {
if(name.contains("黄")){
return "不合法";
}
String result = demoService.sayHello(name);
return result;
}
}
开启配置:
<dubbo:reference interface="com.qf.service.DemoService" stub="com.qf.service.DemoServiceStup" id="demoService" version="*" >
<dubbo:method name="sayHello" timeout="4000"/>
</dubbo:reference>
二. SpringBoot配置Dubbo的属性
1. yml文件 + 注解
在启动类上添加注解
@EnableDubbo
全局级别,直接在yml文件中配置
服务级别,直接配置在注解中
使用yml文件 + 注解,无法细粒度到方法级别,只能配置到引用服务的级别
1.check
可以在yml中指定 dubbo.consumer.check=false
也可以在注解上指定 @Reference(check=false)
2.version
dubbo.consumer.version=1.0.0
@Reference(version="1.0.0")
3.timeout
dubbo.consumer.timeout=0
@Reference(timeout=xxx)
4.retries
dubbo.consumer.retries=xx
@Reference(retries=xxx)
5.stub
dubbo.consumer.stub=xxx
@Reference(stub="xxx")
2. xml文件方式
在启动类上添加注解,引入指定的xml配置文件
@ImportResource(locations={"xxx"})
当使用xml文件配置后,要采用spring的注解,从spring容器中获取代理对象的实例
采用这种方式spring boot可以细粒度化控制配置信息
测试:
@SpringBootApplication
//@EnableDubbo
@ImportResource(locations = {"classpath:application-dubbo.xml"})
public class DubboApplication {
public static void main(String[] args) {
SpringApplication.run(DubboApplication.class, args);
}
}
controller
@RestController
@RequestMapping("/demo")
public class DemoController {
// @Reference
// @Resource
@Autowired
private DemoService demoService;
@GetMapping(value = "/hhh",produces = "text/html;charset-utf-8")
public String hhh(){
String str = demoService.sayHaHaHa();
return str;
}
}
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<dubbo:application name="dubbo-springboot-consumer"/>
<dubbo:registry address="zookeeper://114.55.219.117:2181"/>
<dubbo:reference interface="com.qf.service.DemoService" id="demoService"/>
</beans>
测试成功:
3.采用Dubbo的API方式
基于最原生的方式,直接创建config类
查看Dubbo的DubboNameSpaceHandler类,查看需要使用到的config类
需要在启动类上,添加dubbo的注解扫描
@DubboComponentScan
在引用服务时,使用
(dubbo的注解)
@Reference -> 订阅服务
@Service -> 暴露服务
生产者:
@SpringBootApplication
//@EnableDubbo
@DubboComponentScan
public class DubboApplication {
public static void main(String[] args) {
SpringApplication.run(DubboApplication.class, args);
}
}
config配置文件
@SpringBootConfiguration
public class DemoServiceImplConfig {
//application.name
@Bean
public ApplicationConfig applicationConfig(){
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("dubbo-springboot-provider");
return applicationConfig;
}
//zookeeper的地址
@Bean
public RegistryConfig registryConfig(){
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://114.55.219.117:2181");
return registryConfig;
}
//socket的协议和端口
@Bean
public ProtocolConfig protocolConfig(){
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setPort(20880);
protocolConfig.setName("dubbo");
return protocolConfig;
}
/*@Bean
public ServiceBean<DemoService> demoServiceBean(){
ServiceBean<DemoService> demoServiceBean = new ServiceBean<>();
demoServiceBean.setInterface("com.qf.service.DemoService");
demoServiceBean.setId("demoService");
return demoServiceBean;
}*/
}
对外暴露的服务:
@Service //dubbo中的注解
public class DemoServiceImpl implements DemoService {
@Override
public String sayHaHaHa() {
return "哈哈哈哈哈哈哈哈哈额";
}
}
消费者:
@SpringBootApplication
//@EnableDubbo
//@ImportResource(locations = {"classpath:application-dubbo.xml"})
@DubboComponentScan
public class DubboApplication {
public static void main(String[] args) {
SpringApplication.run(DubboApplication.class, args);
}
}
config配置:
@SpringBootConfiguration
public class DemoServiceConfig {
//1.dubbo.application.name=xxx
@Bean
public ApplicationConfig applicationConfig(){
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("dubbo-springboot-consumer");
return applicationConfig;
}
//2.dubbo.registry.address=xxx
@Bean
public RegistryConfig registryConfig(){
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://114.55.219.117:2181");
return registryConfig;
}
//3.dubbo.reference interface=xxx
//ReferenceBean继承了ReferenceConfig了,所以这里两者都行
/*@Bean
public ReferenceConfig<DemoService> referenceConfig(){
ReferenceConfig<DemoService> demoServiceReferenceConfig = new ReferenceConfig<>();
demoServiceReferenceConfig.setInterface("com.qf.service.DemoService");
demoServiceReferenceConfig.setId("demoService");
return demoServiceReferenceConfig;
}*/
@Bean
public ReferenceBean<DemoService> referenceBean(){
ReferenceBean<DemoService> demoServiceReferenceBean = new ReferenceBean<>();
demoServiceReferenceBean.setInterface("com.qf.service.DemoService");
demoServiceReferenceBean.setId("demoService");
//方法级别的设置
List<MethodConfig> methodConfigs = new ArrayList<>();
MethodConfig methodConfig = new MethodConfig();
methodConfig.setName("sayHello");
//超时timeout
methodConfig.setTimeout(1000);
//重试次数retries
methodConfig.setRetries(0);
//设置到list
methodConfigs.add(methodConfig);
//设置到referenceBean
demoServiceReferenceBean.setMethods(methodConfigs);
return demoServiceReferenceBean;
}
//4.dubbo.consumer..
}
controller
@RestController
@RequestMapping("/demo")
public class DemoController {
@Reference //订阅服务,这里必须使用@Reference
private DemoService demoService;
@GetMapping(value = "/hhh",produces = "text/html;charset-utf-8")
public String hhh(){
String str = demoService.sayHaHaHa();
return str;
}
}
三.高可用
1.zookeeper宕机-dubbo直连
1.服务之间可以正常通讯,突然zookeeper宕机
->服务之间的调用还可以正常执行
->如果有服务之前从未调用过,由于本地没有缓存,无法调用
->即便服务之间可以正常调用,还是会丢失信息
->搭建zookeeper集群,避免zookeeper宕机引起的各种问题
2.如果舍弃zookeeper或者其他各种注册中心,dubbo提供了直连方式
->提供者将注册中心地址设置为N/A
如:dubbo.registry.address=N/A
->消费者在服务的订阅上直接指定提供者的协议IP和端口 protocol://host:post
如:@Reference(url = "dubbo://127.0.0.1:20880")
好处:
配置简单
可以舍弃zookeeper注册中心
缺点
1.没有一个集中的管理工具,去治理整个dubbo
2.没有基于zookeeper的注册中心,导致缺少负载均衡,等待一系列的功能
3.需要记住很多很多服务的ip和port,维护麻烦
2.dubbo的负载均衡
1.负载均衡的策略
Random LoadBalance
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance
轮询,按公约后的权重设置轮询比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance
一致性 Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
2.配置方式
1.@Reference | @Service 中配置loadBalance属性,指定四种负载均衡机制中的一种
random | roundrobin | leastactive | consistenthash
2.关于随机和轮询的权重,推荐在监控中心动态的修改
测试:
准备一台和生产者端口不一致的服务器如下:
在生产者的条件下复制一个tomcat端口8081和dubbo端口20881
在消费者@Reference中加上负载均衡的属性策略
@Reference(loadbalance = "consistenthash")
3.服务容错策略
1.默认的容错
failover,失败重试,指定retries重试次数
Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。针对幂等操作
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
配置方式:
<dubbo:service cluster="failsafe" />
<dubbo:reference cluster="failsafe" />
4.服务的降级
1.dubbo的降级方式
可以通过服务降级功能 [1] 临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。
向注册中心写入动态配置覆盖规则:
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
其中:
mock=force:return+null
表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。- 还可以改为
mock=fail:return+null
表示消费方对该服务的方法调用在失败后,再返回 null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
2.Hystrix
1.导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
2.在启动类上添加注解
@EnableHystrix
@SpringBootApplication
//@EnableDubbo
@DubboComponentScan
@EnableHystrix
public class DubboApplication {
public static void main(String[] args) {
SpringApplication.run(DubboApplication.class, args);
}
}
3.在提供者的服务类的方法上添加注解
@Override
@HystrixCommand
public String sayHaHaHa(String name) {
if(Math.random()>0.5){
throw new RuntimeException();
}
return "哈哈哈哈哈哈哈哈哈额"+port;
}
4.在消费者调用远程服务的方法上,添加注解
//@HystrixCommand(fallBackMethod="xxx")
//出现问题访问xxx方法
@HystrixCommand(fallbackMethod = "xxx")
@GetMapping(value = "/hhh",produces = "text/html;charset-utf-8")
public String hhh(String name){
String str = demoService.sayHaHaHa(name);
return str;
}
public String xxx(String name){
return "网络开小差了";
}