1.springboot+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.wernch</groupId>
<artifactId>spring-dubbo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>spring-dubbo-interface</module>
<module>spring-dubbo-provider</module>
<module>spring-dubbo-consumer</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<dubbo-version>2.7.8</dubbo-version>
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
</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>
<!-- Apache Dubbo -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-dependencies-bom</artifactId>
<version>${dubbo-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
<version>${dubbo-version}</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-version}</version>
</dependency>
<!-- Dubbo核心组件 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<!--Spring Boot 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<!-- Zookeeper客户端框架 -->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
</dependency>
<!-- Zookeeper dependencies -->
<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>
</dependencies>
</project>
spring-dubbo-interface 通过dubbo需要远程调用的接口
/**
* @author xmz
*/
public interface OrderService {
/**
* 获取订单详情
* @param orderId
* @return
*/
String getOrder(Long orderId);
}
spring-dubbo-provider 服务提供方
<dependencies>
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-version}</version>
</dependency>
<!-- Dubbo 核心依赖 -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId>
</dependency>
<!-- 公用RPC接口依赖 -->
<dependency>
<groupId>com.wernch</groupId>
<artifactId>spring-dubbo-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
/**
* @author xmz
* @date 2022/11/28 13:02
*/
@SpringBootApplication
public class DubboProviderApplication {
public static void main(String[] args) {
SpringApplication.run(DubboProviderApplication.class,args);
}
}
@DubboService(
version = "${dubbo.spring.provider.version}"
)
public class OrderServiceImpl implements OrderService {
/**
* 服务端口
*/
@Value("${server.port}")
private String serverPort;
/**
* 获取订单详情
* @param orderId
* @return
*/
public String getOrder(Long orderId) {
System.out.println("Service:"+serverPort);
return "Get Order Detail, Id: " + orderId + ", serverPort: " + serverPort;
}
}
server:
port: 18090
dubbo:
protocol:
name: dubbo
port: -1
scan:
base-packages: com.wrench
## Dubbo 注册器配置信息
registry:
address: zookeeper://127.0.0.1:2181
file: ${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache
spring:
provider:
version: 1.0.0
application:
name: spring-dubbo-provider # 必须设置的属性
spring-dubbo-consumer 服务消费方
<dependencies>
<!-- Dubbo Spring Boot Starter -->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-version}</version>
</dependency>
<!-- 公用RPC接口依赖 -->
<dependency>
<groupId>com.wernch</groupId>
<artifactId>spring-dubbo-interface</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
@SpringBootApplication
public class DubboConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(DubboConsumerApplication.class, args);
}
}
@Controller
public class OrderController {
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* 订单服务接口
*/
@DubboReference(version = "${dubbo.spring.provider.version}")
private OrderService orderService;
/**
* 获取订单详情接口
* @param orderId
* @return
*/
@RequestMapping("/getOrder")
@ResponseBody
public String getOrder(Long orderId) {
String result = orderService.getOrder(orderId);
return result;
}
}
server:
# 服务端口
port: 18091
dubbo:
registry:
# 消费端注册器配置信息
address: zookeeper://127.0.0.1:2181
file: ${user.home}/dubbo-cache/${spring.application.name}/dubbo.cache
#服务版本号
spring:
provider:
version: 1.0.0
spring:
application:
#服务名称
name: spring-dubbo-consumer # 必须设置的属性
在未启动provider时候启动consumer,consumer启动报错解决方法
@DubboReference(
version = "${dubbo.spring.provider.version}",
lazy = true
)
2.Dubbo高阶配置运用
2.1 不同配置覆盖关系
配置规则:
- 方法级优先,接口级次之,全局配置再次之。
- 如果级别一样,则消费方优先,提供方次之。
2.1.1 服务端超时设定
服务端超时配置
@Configuration
public class DubboConfig {
/***
* 服务端全局配置
*/
@Bean
public ProviderConfig registryConfig(){
ProviderConfig config = new ProviderConfig();
//全局超时时间 1s
config.setTimeout(1000);
return config;
}
}
对某个接口
@DubboReference(
version = "${dubbo.spring.provider.version}"
,methods = {
@Method(name = "getOrder",timeout = 1000)
}
)
private OrderService orderService;
客户端超时配置
全局
@Configuration
public class DubboConfig {
/***
* 客户端全局配置
*/
@Bean
public ConsumerConfig config(){
ConsumerConfig config = new ConsumerConfig();
config.setTimeout(4000);
return config;
}
}
对某个方法
@DubboService(
version = "${dubbo.spring.provider.version}"
,timeout = 1000
,methods = {
//某个方法超时配置
@Method(name = "getOrder",timeout = 2000)
}
)
public class OrderServiceImpl implements OrderService {
/**
* 服务端口
*/
@Value("${server.port}")
private String serverPort;
/**
* 获取订单详情
* @param orderId
* @return
*/
public String getOrder(Long orderId) {
if(orderId==4){
try {
//TimeUnit.SECONDS.sleep(1500);
Thread.sleep(900);
} catch (Exception e) {
}
}else if(orderId==5){
throw new RuntimeException("哈哈哈");
}
System.out.println("Service:"+serverPort);
return "Get Order Detail, Id: " + orderId + ", serverPort: " + serverPort;
}
}
2.2 属性配置优先级
优先级规则
优先级从高到低:
- JVM -D 参数;(运行时为了改变对应的配置)
- XML(application.yml/application.properties)配置会重写 dubbo.properties 中的;(经常变动的属性)
- dubbo.properties默认配置,仅仅作用于以上两者没有配置时。 (全局属性)
2.3 重试与容错处理机制
- 容错机制:
- Failfast Cluster
- 快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录。
- Failsafe Cluster
- 失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作。
- Failback Cluster
- 失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
- Forking Cluster
- 并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks=“2” 来设置最大并行数。
- Broadcast Cluster
- 广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
调整客户端重试次数
默认情况下重试两次
修改重试次数(consumer)
@DubboReference(
version = "${dubbo.spring.provider.version}",
//重试次数
retries = 3
)
private OrderService orderService;
2.4 负载均衡
- 默认负载策略
Dubbo默认采用的是随机负载策略。
开启三个服务节点,通过消费端访问验证: http://127.0.0.1:18091/getOrder?orderId=123通过控制后台日志输出, 可以看到每个服务节点呈现不规则的调用。
- Dubbo 支持的负载均衡策略(默认的负载均衡策略为random)
- Random LoadBalance
随机,按权重设置随机概率。 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
- RoundRobin LoadBalance
轮询,按公约后的权重设置轮询比率。 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
- LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
- ConsistentHash LoadBalance
一致性 Hash,相同参数的请求总是发到同一提供者。 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
dubbo
在2.7.7以上支持最短时间算法(ShortestResponse LoadBalance)
上次响应的时间最短(最快),下次请求依然到那台机器去
源码实现: org.apache.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance
Dubbo提供了四种实现:
- 2.7.7以下
- 2.7.7以下
客户端配置负载均衡
方式一
/***
* 客户端全局配置
*/
@Bean
public ConsumerConfig config(){
ConsumerConfig config = new ConsumerConfig();
//config.setTimeout(4000);
//负载均衡
config.setLoadbalance("roundrobin"); //轮询
//config.setLoadbalance("random"); //随机
//config.setLoadbalance("consistenthash"); //Hash
//config.setLoadbalance("leastactive"); //平均处理效率最高的节点优先
return config;
}
方式二
@DubboReference(
version = "${dubbo.spring.provider.version}"
//随机,具有权重随机
,loadbalance = "random"
)
服务端
方式一
@Bean
public ProviderConfigconfig(){
ConsumerConfig config = new ConsumerConfig();
//config.setTimeout(4000);
//负载均衡
config.setLoadbalance("roundrobin"); //轮询
//config.setLoadbalance("random"); //随机
//config.setLoadbalance("consistenthash"); //Hash
//config.setLoadbalance("leastactive"); //平均处理效率最高的节点优先
return config;
}
方式二
@DubboService(
version = "${dubbo.spring.provider.version}",
loadbalance = "random"
)
- 四种配置方式:
优先级从下至上:
服务端的服务级别>客户端的服务级别>服务端方法级别>客户端方法级别:
- 调用验证
修改客户端的负载策略, 改为轮询策略:
@DubboReference(version = "${dubbo.spring.provider.version}",
retries = 3,
loadbalance = "roundrobin")
开启三个服务节点, 进行访问验证: http://127.0.0.1:18091/getOrder?orderId=123
会依次轮询进行调用。
- 动态权重调整验证
使用Dubbo admin 进行权重配置
管理后台地址:http://127.0.0.1:8080/#
通过管理后台修改服务的权重配置:
调整权重
调整后可以看到权重配置已经生效:
代码中设置权重
以客户端为例
@Bean
public ProviderConfig registryConfig(){
ProviderConfig config = new ProviderConfig();
config.setWeight(100);
return config;
}
2.5 多版本控制
- 启动三个服务端
第一个服务端版本为1.0.0, 第二个服务端版本为2.0.0。
修改application.yml配置:
dubbo:
spring:
provider:
version: 1.0.0
- 消费端指定版本号为2.0.0
修改application.properties配置:
#服务版本号
dubbo:
spring:
provider:
version: 2.0.0
在注解中指定调用的版本程序
@DubboReference(version = “2.0.0”)
2.6 本地存根调用
- 远程调用前执行本地存根类似拦截器
- 客户端存根实现(一般接口中进行编写)
public class OrderServiceStub implements OrderService {
//代理对象->Proxy->Remote($Proxy)
private final OrderService orderService;
public OrderServiceStub(OrderService orderService) {
this.orderService = orderService;
}
/***
* 订单查询
* @param orderId
* @return
*/
@Override
public String getOrder(Long orderId) {
if(orderId!=null){
//远程调用
return orderService.getOrder(orderId);
}
return "本地校验不合法....";
}
}
- 修改客户端调用配置(哪里调用哪里写对应的数据)
//本地存根使用
@DubboReference(
version = "${dubbo.spring.provider.version}",
//指定本地存根
stub = "com.wrench.dubbo.stub.OrderServiceStub"
)
private OrderService orderService;
注:stub要配置存根接口的完整路径。
2.7 服务降级运用
服务发生异常,进行友好提示
- 服务动态禁用
启动单个服务节点,进入Dubbo Admin, 创建动态配置规则:
将disabled属性设为true, 服务禁用
configVersion: v2.7
enabled: true
configs:
- side: provider
addresses:
- '0.0.0.0:20881'
parameters:
timeout: 6000
disabled: true
将disabled属性改为false
configVersion: v2.7
enabled: true
configs:
- side: provider
addresses:
- '0.0.0.0:20881'
parameters:
timeout: 6000
disabled: false
- 服务降级
https://dubbo.apache.org/zh/docsv2.7/user/examples/service-downgrade/
降级 Dubbo 服务
可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略。
向注册中心写入动态配置覆盖规则:
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 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。
@DubboReference(
version = "${dubbo.spring.provider.version}",mock = "force:return null"
)
@DubboService(
version = "${dubbo.spring.provider.version}",mock = "mock=fail:return null"
)
提示
2.2.0 以上版本支持
2.8 并发与连接控制
实际运用, 会碰到高并发与峰值场景, Dubbo是可以做到并发与连接数控制。
并发数控制
- 服务端控制
- 服务级别
//springboot配置方式 @DubboService( version = "${dubbo.spring.provider.version}" ,executes = 10 ) public class OrderServiceImpl implements OrderService { public String getOrder(Long orderId) { ........ } }
<!---xml配置方式--> <dubbo:service interface="com.xxx.OrderService " executes="10" />
服务器端并发执行(或占用线程池线程数)不能超过 10 个。
- 方法级别
//springboot配置方式 @DubboService( version = "${dubbo.spring.provider.version}", executes = 10, methods = { //指定某个方法并发数量 @Method(name = "getOrder",executes = 3) } )
<!---xml配置方式--> <dubbo:service interface="com.xxxx.OrderService "> <dubbo:method name="getOrder" executes="3" /> </dubbo:service>
限制具体的方法,服务器端并发执行(或占用线程池线程数)不能超过 3 个。
优先级:方法的并发数设置优先服务的设置
全局的并发数(服务端示例)/*** * 服务端全局配置 */ @Bean public ProviderConfig registryConfig(){ ProviderConfig config = new ProviderConfig(); //全局并发数量 //config.setExecutes(100); return config; }
- 客户端控制
- 调用的服务控制
//springboot配置方式 @DubboReference( version = "${dubbo.spring.provider.version}" ,actives = 10 ) private OrderService orderService;
<!---xml配置方式--> <dubbo:reference interface="com.xxx.BarService" actives="10" />
客户端并发执行(或占用线程池线程数)不能超过 10 个。
- 方法级别
//springboot配置方式 @DubboService( version = "${dubbo.spring.provider.version}", actives= 10, methods = { //指定某个方法并发数量 @Method(name = "getOrder",executes = 3) } )
<!---xml配置方式--> <dubbo:reference interface="com.xxx.OrderService "> <dubbo:method name="getOrder" executes="3" /> </dubbo:reference >
客户端并发执行(或占用线程池线程数)不能超过 3 个。
限制具体的方法,客户端并发执行(或占用线程池线程数)不能超过 3 个。
优先级:方法的并发数设置优先服务的设置
全局的并发数(服务端示例)/*** * 客户端全局配置 */ @Bean public ConsumerConfigregistryConfig(){ ConsumerConfigconfig = new ConsumerConfig(); //全局并发数量 config.setActives(1000); return config; }
客户端负载配置
@DubboReference(
version = "${dubbo.spring.provider.version}",
loadbalance = "leastactive"
)
private OrderService orderService;
<!--xml配置方式--->
<dubbo:reference interface="com.xxx.orderService" loadbalance="leastactive" />
负载策略为最小连接数时, Loadbalance 会调用并发数最小的 Provider。
连接数控制
- 服务端连接控制
@DubboService(
version = "${dubbo.spring.provider.version}",
connections= "10"
)
<dubbo:provider protocol="dubbo" accepts="10" />
限制服务器端接受的连接不能超过 10 个
- 客户端连接控制
//本地存根使用
@DubboReference(
version = "${dubbo.spring.provider.version}",
connections = 10
)
<dubbo:reference interface="com.xxx.OrderService " connections="10" />
限制客户端服务使用连接不能超过 10 个
如果 dubbo:service 和 dubbo:reference 都配了 connections,dubbo:reference 优先。
如果服务端和客户端都配置了连接数控制,那么客户端优先
dubbo官方文档:https://dubbo.apache.org