Dubbo配置
配置原则
首先,从Dubbo支持的配置来源说起,默认有四种配置来源:
JVM System Properties,-D 参数
Externalized Configuration,外部化配置(如application.yml)
ServiceConfig、ReferenceConfig 等编程接口采集的配置(通过编码形式,如写个配置类标注为@Component)
本地配置文件 dubbo.properties
覆盖关系
优先级顺序:
- JVM 启动-D参数优先,这样可以使用户在部署和启动时进行参数的重写,比如在启动时需要改变协议的端口时。
- XML次之。如果中有配置项,则dubbo.properties中相应配置项无效。
- Properties。 相当于缺省值,只有XML没有配置时,dubbo.properties的相应配置项才会生效。
重试次数 retries
retries 属性表示远程服务调用重试次数,不包括第一次调用,不需要重试请设为0。默认为2.
如果该服务名下有多台服务器,当出现失败时,会轮询自动切换尝试其他服务器。
注解形式配置
@DubboService 和@DubboReference都有重试次数属性。@DubboService标注在对应接口的实现类上,@DubboReference标注在要注入的远程调用对象上。
@Service // 放进springboot容器中
//服务提供者接口实现类需要被@DubboService 驱动
@DubboService(retries = 3) //默认重试次数是2次
public class UserServiceImpl implements UserService {
public List<UserAddress> getUserAddressList(String userId) {
System.out.println("UserServiceImpl..1.....");
UserAddress address1 = new UserAddress(1, "北京市朝阳区麦肯锡", "1", "李老师", "010-56253825", "Y");
UserAddress address2 = new UserAddress(2, "深圳市宝安区珠江工厂)", "1", "王老师", "010-56253825", "N");
try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
return Arrays.asList(address1,address2);
}
}
}
控制请求超时,打印结果:
UserServiceImpl..1.....
UserServiceImpl..1.....
UserServiceImpl..1.....
UserServiceImpl..1.....
使用注解形式无法实现方法级别的属性设置
Spring XML 配置
<dubbo:service retries="2" />
或
<dubbo:reference retries="2" />
或
<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
超时等待时间 timeout
由于网络或服务端不可靠,会导致出现一种不确定的中间状态(超时)。为了避免超时导致客户端资源挂起耗尽,必须设置超时时间。timeout该属性远程服务调用超时时间(毫秒),默认时间为1000ms。
配置的覆盖规则:
- 方法级配置别优于接口级别,即小Scope优先
- Consumer端配置 优于 Provider配置 优于 全局配置,
- 最后是Dubbo Hard Code的配置值(见配置文档)
配置的覆盖规则:
1、 方法级别的优于接口级别,接口级别的优于全局配置。
2、 Consumer端配置优于Provider
Spring XML 形式
consumer.xml
服务提供者
项目结构
主启动类
由于是使用xml暴露服务的,所以不用使用@EnableDubbo
DubboProviderMain20881.java
//@EnableDubbo //使用xml暴露服务时候不用@EnableDubbo注解
@SpringBootApplication
public class DubboProviderMain20881 {
public static void main(String[] args) {
SpringApplication.run(DubboProviderMain20881.class,args);
}
}
服务接口实现类之一UserServiceImpl
package com.johnny.dubbo.service.impl;
import com.johnny.dubbo.entity.UserAddress;
import com.johnny.dubbo.service.UserService;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author Johnny Lin
* @date 2021/7/8 17:42
*/
@Service // 放进springboot容器中
//服务提供者接口实现类需要被@DubboService 驱动
@DubboService // 一定一定要加上@DubboService注解暴露远程服务给消费者 一开始就是就没有加上
public class UserServiceImpl implements UserService {
public List<UserAddress> getUserAddressList(String userId) {
System.out.println("UserServiceImpl..v1.....");
UserAddress address1 = new UserAddress(1, "北京市朝阳区麦肯锡", "1", "李老师", "010-56253825", "Y");
UserAddress address2 = new UserAddress(2, "深圳市宝安区珠江工厂)", "1", "王老师", "010-56253825", "N");
// try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(address1.getId()+"\t"+address1.getUserAddress());
System.out.println(address2.getId()+"\t"+address2.getUserAddress());
return Arrays.asList(address1,address2);
}
}
服务接口实现类之一UserServiceImpl2.java
package com.johnny.dubbo.service.impl;
import com.johnny.dubbo.entity.UserAddress;
import com.johnny.dubbo.service.UserService;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @author Johnny Lin
* @date 2021/7/8 17:42
*/
@Service // 放进springboot容器中
//服务提供者接口实现类需要被@DubboService 驱动
@DubboService // 一定一定要加上@DubboService注解暴露远程服务给消费者 一开始就是就没有加上
public class UserServiceImpl2 implements UserService {
public List<UserAddress> getUserAddressList(String userId) {
System.out.println("UserServiceImpl..v2.....");
UserAddress address1 = new UserAddress(3, "北京市朝阳区麦肯锡", "1", "李老师", "010-56253825", "Y");
UserAddress address2 = new UserAddress(4, "深圳市宝安区珠江工厂)", "1", "王老师", "010-56253825", "N");
// try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(address1.getId()+"\t"+address1.getUserAddress());
System.out.println(address2.getId()+"\t"+address2.getUserAddress());
return Arrays.asList(address1,address2);
}
}
服务提供者配置文件provider.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://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 1、指定当前服务/应用的名字(同样的服务名字相同,不要和别的服务同名) -->
<dubbo:application name="user-service-dubbo-provider"></dubbo:application>
<!-- 2、指定注册中心的位置 -->
<!-- <dubbo:registry address="zookeeper://192.168.59.123:2181"></dubbo:registry> -->
<dubbo:registry protocol="zookeeper" address="192.168.59.123:2181"></dubbo:registry>
<!-- 3、指定通信规则(通信协议?通信端口) -->
<dubbo:protocol name="dubbo" port="20882"></dubbo:protocol>
<!-- 4、暴露服务 interface:面向的接口 ref:指向服务的真正的实现对象
version: 版本,通过指定版本可以实现灰度发布
-->
<dubbo:service interface="com.johnny.dubbo.service.UserService"
ref="userServiceImpl" timeout="1000" version="1.0.0">
<dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method>
</dubbo:service>
<!--统一设置服务提供方的规则 -->
<dubbo:provider timeout="1000"></dubbo:provider>
<!-- 服务的实现 -->
<bean id="userServiceImpl" class="com.johnny.dubbo.service.impl.UserServiceImpl"></bean>
<dubbo:service interface="com.johnny.dubbo.service.UserService"
ref="userServiceImpl2" timeout="1000" version="2.0.0">
<dubbo:method name="getUserAddressList" timeout="1000"></dubbo:method>
</dubbo:service>
<bean id="userServiceImpl2" class="com.johnny.dubbo.service.impl.UserServiceImpl2"></bean>
<!-- 连接监控中心 -->
<dubbo:monitor protocol="registry"></dubbo:monitor>
</beans>
消费者
项目结构
注意application
yaml 和 consumer0.xml 都没有用到
**主启动类 ConsumerMain8081 **
@SpringBootApplication
//@EnableDubbo开启Dubbo注解 该注解中有@DubboComponentScan 作用是扫描dubbo注解所在包
//由于使用xml文件暴露服务 所以不需要该注解
//@EnableDubbo
public class ConsumerMain8081 {
public static void main(String[] args) {
SpringApplication.run(ConsumerMain8081.class,args);
}
}
服务实现类 OrderServiceImpl
package com.johnny.dubbo.service.impl;
import com.johnny.dubbo.entity.UserAddress;
import com.johnny.dubbo.service.OrderService;
import com.johnny.dubbo.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* @author Johnny Lin
* @date 2021/7/8 0:18
*/
@Service
public class OrderServiceImpl implements OrderService {
//Reference注解引用服务远程调用
// @DubboReference
// UserService userService;
//通过xml配置文件暴露接口时,接口的调用使用@Autowired注解,不用 @DubboReference
// 相当于通过spring将这个接口对象注入了
@Resource
private UserService userService;
public List<UserAddress> initOrder(String userId) {
System.out.println("OrderService 1……");
return userService.getUserAddressList(userId);
}
}
**服务控制类 OrderController **
package com.johnny.dubbo.controller;
import com.johnny.dubbo.entity.UserAddress;
import com.johnny.dubbo.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* @author Johnny Lin
* @date 2021/7/8 23:50
*/
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/initOrder/{id}")
public List<UserAddress> initOrder(@PathVariable("id")String userId) {
return orderService.initOrder(userId);
}
}
配置文件consumer.xml
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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">
<dubbo:application name="order-service-dubbo-consumer"></dubbo:application>
<dubbo:registry id="register" protocol="zookeeper" address="192.168.59.123:2181" timeout="25000"></dubbo:registry>
<!-- retries 重试次数: 不包含第一次调用 0代表不重试
幂等操作(如查询、删除、修改)需要设置重试次数
非幂等操作(如新增) 不能设置重试次数
-->
<!-- timeout 超时等待时间: 为0 表示默认是1000ms-->
<!-- 配置当前消费者的统一规则 设置全局超时时间 check=false表示所有服务启动时都不检查是否有提供者-->
<dubbo:consumer timeout="5000" check="false" ></dubbo:consumer>
<!-- 指定接口以及特定方法超时配置 -->
<!-- 通过dubbo:reference 的timeout属性设置接口级的超时时间-->
<!-- 通过dubbo:reference 标签生成一个id为userService的远程代理对象 该对象的timeout属性 -->
<!-- register 属性从指定注册中心注册获取服务列表,在多个注册中心时使用,值为<dubbo:registry>的id属性,多个注册中心ID用逗号分隔 缺省向所有registry注册 -->
<!-- 通过dubbo:reference的version属性指定使用消费端的版本实现灰度发布,* 表示所有版本,也可以指定版本 -->
<dubbo:reference interface="com.johnny.dubbo.service.UserService" id="userService"
registry="register" timeout="6000"
version = "*">
<!-- 通过dubbo:method 的timeout属性设置方法级的超时时间 方法级别的起作用-->
<dubbo:method name="getUserAddressList" timeout="1000" ></dubbo:method>
</dubbo:reference>
<!-- 指定监控中心协议 会在注册中心寻找监控中心地址 否则直连监控中心 因此无需再填写详细地址-->
<dubbo:monitor protocol="registry" ></dubbo:monitor>
<!-- <dubbo:monitor address="127.0.0.1:7001"></dubbo:monitor> -->
</beans>
非幂等操作(如新增) 不能设置重试次数
-->
<!-- timeout 超时等待时间: 为0 表示默认是1000ms-->
<!-- 配置当前消费者的统一规则 设置全局超时时间 所有服务都不检查-->
<dubbo:consumer timeout="5000" check="false" ></dubbo:consumer>
<!-- 指定接口以及特定方法超时配置 -->
<!-- 设置接口级的超时时间-->
<!-- 通过dubbo:reference 标签生成一个id为userService的远程代理对象 该对象的timeout属性 -->
<dubbo:reference interface="com.johnny.dubbo.service.UserService" id="userService"
timeout="6000">
<!-- 设置方法级的超时时间 -->
<dubbo:method name="getUserAddressList" timeout="1000" ></dubbo:method>
</dubbo:reference>
<!-- 指定监控中心协议 会在注册中心寻找监控中心地址 否则直连监控中心 因此无需再填写详细地址-->
<dubbo:monitor protocol="registry"></dubbo:monitor>
<!-- <dubbo:monitor address="127.0.0.1:7001"></dubbo:monitor> -->
</beans>
bug
No provider available from registry 192.168.59.123:2181 for service com.johnny.dubbo.service.UserService on consumer 192.168.255.1 use dubbo version 2.7.8, please check status of providers(disabled, not registered or in blacklist)
困扰了老子一天 是在想不明白为什么 监控中心也显示该消费服务没有提供者
原来服务提供者对应的接口实现类上也需要加上@DubboService 注解才能暴露服务给消费者
版本号
当一个接口实现,出现不兼容升级时,可以使用版本号过渡。版本号不同的服务相互不引用。可以按照以下步骤进行版本迁移:
老版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="1.0.0" />
新版本服务提供者配置:
<dubbo:service interface="com.foo.BarService" version="2.0.0" />
老版本服务消费者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="1.0.0" />
新版本服务消费者配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="2.0.0" />
如果不需要区分版本,可以按照以下的方式配置:
<dubbo:reference id="barService" interface="com.foo.BarService" version="*" />
注意当provider只有一个版本时 如果消费者version写的是*,那么消费者会找不到对应服务。
不同粒度配置的覆盖关系
1、 精确优先(即官网所说的 方法级优先、接口级次之,全局配置再次之)
2、 消费者设置优先(同等级别下,消费则优先,提供者次之)。
Spring配置
consumer.xml
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
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">
<!-- retries 重试次数: 不包含第一次调用 0代表不重试
幂等操作(如查询、删除、修改)需要设置重试次数
非幂等操作(如新增) 不能设置重试次数
-->
<!-- timeout 超时等待时间: 为0 表示默认是1000ms-->
<!-- 设置全局超时时间-->
<dubbo:consumer timeout="5000" ></dubbo:consumer>
<!-- 指定接口以及特定方法超时配置 -->
<!-- 设置接口级的超时时间-->
<!-- 通过dubbo:reference 标签生成一个id为userService的远程代理对象 该对象的timeout属性 -->
<dubbo:reference interface="com.johnny.dubbo.service.UserService" id="userService"
timeout="6000">
<!-- 设置方法级的超时时间 -->
<dubbo:method name="getUserAddressList" timeout="5000" ></dubbo:method>
</dubbo:reference>
</beans>
SSM 方式直接调用时直接读取配置文件从容器中获取该bean
@SpringBootApplication
@EnableDubbo //开启Dubbo注解 该注解中有@DubboComponentScan 作用是扫描dubbo注解所在包
public class ConsumerMain8081 {
public static void main(String[] args) {
SpringApplication.run(ConsumerMain8081.class,args);
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("consumer.xml");
UserService userService = applicationContext.getBean(UserService.class);
System.out.println(111);
System.out.println(userService.getUserAddressList("36"));
}
}
本地存根
在 Dubbo 中利用本地存根在客户端执行部分逻辑
消费端:
UserServiceStub
package com.johnny.dubbo.service.impl;
import com.johnny.dubbo.entity.UserAddress;
import com.johnny.dubbo.service.UserService;
import org.apache.dubbo.common.utils.StringUtils;
import java.util.List;
/**
* @author Johnny Lin
* @date 2021/7/11 23:44
*/
public class UserServiceStub implements UserService {
private final UserService userService;
/**
* 传入的是userService远程的代理对象
* @param userService
*/
public UserServiceStub(UserService userService) {
super();
this.userService = userService;
}
@Override
public List<UserAddress> getUserAddressList(String userId) {
// TODO Auto-generated method stub
System.out.println("UserServiceStub stub.....");
if(!StringUtils.isEmpty(userId)) {
return userService.getUserAddressList(userId);
}
return null;
}
}
consumer.xml
<dubbo:reference interface="com.johnny.dubbo.service.UserService" id="userService"
registry="register" timeout="6000"
version = "1.0.0" stub="com.johnny.dubbo.service.impl.UserServiceStub">
参考文档:
Dubbo 中的端口使用情况
高可用配置
高可用,通过设计,减少系统不能提供服务的时间
绕过注册中心,直连本地服务提供者:
在consumer的服务实现类OrderServiceImpl上
@DubboReference(url = "dubbo://localhost:20885")
private UserService userService;
Zookeeper宕机,可以与dubbo直连
现象: zookeeper注册中心宕机,还可以消费dubbo暴露的服务
原因:
dubbo的健壮性
- 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
- 注册中心全部宕掉之后,服务提供者和消费者仍能通过本地缓存通讯
其他情况:
- 监控中心宕掉后不影响使用,只是丢失部分采样数据
- 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但是不能注册新服务
- 服务提供者无状态,任意一台宕掉后,不影响使用
- 服务提供者全部宕掉后,服务消费应用将无法使用,并无限次重连等待服务提供者恢复。
使用@Reference(url="")
集群下的dubbo负载均衡
Dubbo提供了多种负载均衡策略,缺省为random随机调用
Random LoadBalance
随机,按权重设置随机概率
RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
ConsistentHash LoadBalance
一致性 Hash,相同参数的请求总是发到同一提供者。
整合Hystrix 服务熔断与降级处理
服务降级
当服务器压力剧增时,根据业务情况及流量,为保证服务端的核心业务的正常运作和避免客户端的过长等待,对于一些服务和页面有策略的不处理或换种简单的方式处理(如提示用户系统繁忙)。
集群容错
在集群调用失败时,Dubbo提供了多种容错方案,缺省为failover重试
Failover Cluster
失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries=“2” 来设置重试次数(不含第一次)。
Failfast Cluster
快速失败,只发起一次调用,失败立即报错。通常用于非幂等性操作,比如新增记录。
Failsafe Cluster
失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
Failback Cluster
失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知等操作。
Forking Cluster
并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。
Broadcast Cluster
广播调用所有提供者,逐个调用,任意一台报错则报错 [2]。通常用于通知所有提供者更新缓存或日志等本地资源信息。
集群模式配置
按照以下示例在服务提供方和消费方配置集群模式
<dubbo:service cluster="failsafe" />
或
<dubbo:reference cluster="failsafe" />