dubbo特性

基本使用

参数校验

pom

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.0.0.GA</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>4.2.0.Final</version>
</dependency>

使用场景与实例

服务端在向外提供接口服务时,解决各种接口参数校验问题

参数标注示例
import java.io.Serializable;
import java.util.Date;

import javax.validation.constraints.Future;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

public class ValidationParameter implements Serializable {
    private static final long serialVersionUID = 7158911668568000392L;

    @NotNull // 不允许为空
    @Size(min = 1, max = 20) // 长度或大小范围
    private String name;

    @NotNull(groups = ValidationService.Save.class) // 保存时不允许为空,更新时允许为空 ,表示不更新该字段
    @Pattern(regexp = "^\\s*\\w+(?:\\.{0,1}[\\w-]+)*@[a-zA-Z0-9]+(?:[-.][a-zA-Z0-9]+)*\\.[a-zA-Z]+\\s*$")
    private String email;

    @Min(18) // 最小值
    @Max(100) // 最大值
    private int age;

    @Past // 必须为一个过去的时间
    private Date loginDate;

    @Future // 必须为一个未来的时间
    private Date expiryDate;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Date getLoginDate() {
        return loginDate;
    }

    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }

    public Date getExpiryDate() {
        return expiryDate;
    }

    public void setExpiryDate(Date expiryDate) {
        this.expiryDate = expiryDate;
    }
}
参数验证示例
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
 
public interface ValidationService {
    void save(@NotNull ValidationParameter parameter); // 验证参数不为空
    void delete(@Min(1) int id); // 直接对基本类型参数验证
}

开启验证

客户端验证参数

@DubboReference(methods = {},validation = "true")
private GoodsService goodsService;

服务器端验证参数

@DubboService(validation = "true")
public class GoodsServiceImpl implements GoodsService { }

多版本控制version

提供者

@DubboService(version = "v1.0")
public class GoodsServiceImpl implements GoodsService {}

消费者

@DubboReference(version = "v1.0")
private GoodsService goodsService;

负载均衡

在这里插入图片描述

注解配置

@DubboService(loadbalance = "roundrobin",methods = {})
public class GoodsServiceImpl implements GoodsService {}

@DubboReference(loadbalance = "roundrobin",methods = {})
private GoodsService goodsService;

服务分组

使用场景

一个接口有多种实现时,可以用 group 区分

提供者

使用 @DubboService 注解,添加 group 参数

@DubboService(group = "demo")
public class DemoServiceImpl implements DemoService {
 ...
}

@DubboService(group = "demo2")
public class Demo2ServiceImpl implements DemoService {
 ...
}

消费者

使用 @DubboReference 注解,添加 group 参数

@DubboReference(group = "demo")
private DemoService demoService;

@DubboReference(group = "demo2")
private DemoService demoService2;

// group值为 *----> 标识 匹配任意服务分组
@DubboReference(group = "*")
private DemoService demoService2;

服务超时

超时后,进行重试,也就是接下来的容错—> 设为 1 s

// 还可以进行方法级别的 超时设置
// 谁定义 服务就大概知道什么情况,建议 设置在服务提供方
@DubboService(version = "v1.0",timeout = 1000)
public class GoodsServiceImpl implements GoodsService {}

服务重试

  • 设置超时时间,在这个时间段内,无法完成访问,自动断开连接
  • 如果出现网络抖动,则这一次请求就会失败
  • Dubbo提供重试机制避免类似问题发生
  • 通过retries 属性设置重试次数,默认为 2次
@DubboService(version = "v1.0",timeout = 1000,retries = 3)
public class GoodsServiceImpl implements GoodsService {}

集群容错

在这里插入图片描述

Failover Cluster

失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。
可通过 retries="2"设置重试次数(不含第一次)。

重试次数配置如下:

@DubboReference(version = "v1.0",methods = {},cluster="failover")
private GoodsService goodsService;

服务降级

在这里插入图片描述

服务降级是—>指服务在非正常情况下进行降级应急处理,一般配合容错

使用场景

  1. 某 服务或接口负荷 超出最大承载能力范围,需要进行降级应急处理,避免系统崩溃
  2. 调用的 某非关键服务或接口暂时不可用时,返回模拟数据或空,业务还能继续可用
  3. 降级非核心业务的服务或接口,腾出系统资源,尽量保证核心业务的正常运行
  4. 某上游基础服务超时或不可用时,执行能快速响应的降级预案,避免服务整体雪崩

使用方式

@DubboReference(version = "v1.0", methods = {}, retries = 2, mock = "force:return null")
private GoodsService goodsService;

异步调用

定义 CompletableFuture 签名的接口

服务接口定义

public interface AsyncService {
    CompletableFuture<String> sayHello(String name);
}

服务实现

import org.apache.dubbo.config.annotation.DubboService;


import java.util.concurrent.CompletableFuture;

@DubboService(version = "v1.0", timeout = 500000)
public class AsyncServiceImpl implements AsyncService {
    // 还可以整合线程池!!!
    @Override
    public CompletableFuture<String> sayHello(String name) {
        return CompletableFuture.supplyAsync(() -> {
            System.out.println(name);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //返回值为String,与定义时相同!
            return "async response from provider.";
        });
    }
}

测试

import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

@RestController
public class TestBean {

    @DubboReference(version = "v1.0")
    private AsyncService asyncService;

    @GetMapping("/test")

    public String test() throws InterruptedException, ExecutionException {

        CompletableFuture<String> str = asyncService.sayHello("xiaoding");

        return "success";
    }


}


访问 秒相应!!!

在这里插入图片描述

启动时检查

Dubbo 缺省会在启动时检查依赖的服务是否可用,** 不可用时 会抛出异常,阻止 Spring 初始化完成**
好处:以便上线时,能及早发现问题,默认 check="true"

可以通过 check="false" 关闭检查,比如,测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动

// 推荐设置为false!
@DubboReference(check = false)
private GoodsService goodsService;

check----->只用来 启动时检查,运行时没有相应的依赖仍然会报错!!!

诊断与调优

优雅停机

设置优雅停机超时时间缺省超时时间是 10 秒,如果超时则强制关闭。 该参数可在 dubbo.properties 文件里配置例如:配置为 30 秒

dubbo:
  application:
    name: dubbo-provider #dubbo提供者名称
    shutwait: 30

调用结果缓存

缓存类型

lru 基于最近最少使用原则删除多余缓存,保持最热的数据被缓存。
threadlocal 当前线程缓存,比如一个页面渲染,用到很多 portal,每个 portal 都要去查用户信息,通过线程缓存,可以减少这种多余访问。

使用场景

结果缓存,用于加速热门数据的访问速度Dubbo 提供声明式缓存,以减少用户加缓存的工作量

使用方式:可以指定方法或类进行缓存

@DubboReference(cache = "lru",methods = {})
private GoodsService goodsService;

注册信息 简化

背景

服务为维度注册进入注册中心,导致了数据量的膨胀,进而引发注册中心 (如 zookeeper) 的网络开销增大,性能降低

使用场景

数据量大导致注册中心的网络开销增大,性能降低,减少注册中心上服务的注册数据

使用方式: 用 Spring-Bean 配置zk,yaml中 不需要配置!

提供者

import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.context.annotation.Bean;

public class DubboConfig {
    @Bean
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("zookeeper://127.0.0.1:2181");
        registryConfig.setSimplified(true);
        registryConfig.setExtraKeys("retries,owner");
        return registryConfig;
    }


}

class Service {
    // 暴露服务
    @DubboService(version = "1.1.8", group = "d-test", executes = 4500, retries = 7, owner = "victanno", timeout = 5300)
    public class AnnotationServiceImpl implements AnnotationService {
    
        public String sayHello(String name) {
            System.out.println("async provider received: " + name);
            return "annotation: hello, " + name;
        }
    }
}

消费者


import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.annotation.DubboReference;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

public class DubboConfig {
    @Bean
    public RegistryConfig registryConfig() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("zookeeper://127.0.0.1:2181");
        registryConfig.setSimplified(true);
        return registryConfig;
    }


}

@Component("annotationAction")
public class AnnotationAction {

    @DubboReference(version = "1.1.8", group = "d-test", owner = "vvvanno", retries = 4, actives = 6, timeout = 4500)
    private AnnotationService annotationService;

    public String doSayHello(String name) {
        return annotationService.sayHello(name);
    }
}

注意

如果一个应用中既有 provider 又有 consumer,那么配置需要合并成:

@Bean
//注册中心配置
public RegistryConfig registryConfig() {
    RegistryConfig registryConfig = new RegistryConfig();
    registryConfig.setAddress("zookeeper://127.0.0.1:2181");
    registryConfig.setSimplified(true);
    //只对provider生效
    registryConfig.setExtraKeys("retries,owner");
    return registryConfig;
}

并发控制

服务器端并发执行(或占用线程池线程数)不能超过 10 个

@DubboService(loadbalance = "roundrobin",executes = 10)

客户端并发执行(或占用连接的请求数)不能超过 10 个

@DubboReference(actives=10,methods = {})
private GoodsService goodsService;

连接控制

Dubbo 中服务端和客户端的连接控制

服务端连接控制

限制服务器端接受的连接不能超过 10 个

dubbo:
  application:
    name: dubbo-provider # 注册的服务名
  registry:
    address: zookeeper://192.168.111.101:2181 # 注册地址 使用zookeeper
  protocol:
    name: dubbo # dubbo协议
    port: -1
    accepts: 10 # 连接控制

客户端连接控制

限制客户端 服务使用连接不能超过 10 个

@DubboReference(connections = 10)
private GoodsService goodsService;

粘滞连接

使用场景

粘滞连接 用于 有状态服务,尽可能让客户端总是向同一提供者发起调用,除非该提供者挂了,再连另一台

粘滞连接将自动开启 延迟连接,以减少长连接数

使用方式:类或方法级别

@DubboReference(sticky = true, methods = {})
private GoodsService goodsService;

线程模型

如何调整线程模型

在protocol下配置dispatcher: all

dubbo:
  application:
    name: dubbo-springboot-demo-provider
  protocol:
    name: dubbo
    port: -1
    #配置线程模型
    dispatcher: all
  registry:
    id: zk-registry
    address: zookeeper://127.0.0.1:2181
  config-center:
    address: zookeeper://127.0.0.1:2181
  metadata-report:
    address: zookeeper://127.0.0.1:2181

各线程模型的配置值:

  1. All Dispatcher all
  2. Direct Dispatcher direct
  3. Execution Dispatcher execution
  4. Message Only Dispatcher: message
  5. Connection Ordered Dispatcher: connection

配置注册服务

使用 Java Config 代替注解

注意: Java Config 是 DubboServiceDubboReference 的替代方式,对于有复杂配置需求的服务建议使用这种方式

@Configuration
public class ProviderConfiguration {
    @Bean
    public ServiceConfig demoService() {
        ServiceConfig service = new ServiceConfig();
        service.setInterface(DemoService.class);
        service.setRef(new DemoServiceImpl());
        service.setGroup("dev");
        service.setVersion("1.0.0");
        Map<String, String> parameters = new HashMap<>();
        service.setParameters(parameters);
        return service;
    }
} 

协议

dubbo

使用场景

适合大并发小数据量的服务调用,服务消费者远大于服务提供者的情景

使用方式

dubbo:
  application:
    name: dubbo-provider
  registry:
    address: zookeeper://192.168.111.101:2181 #注册地址 使用zookeeper
  protocol:
    name: dubbo # 使用dubbo协议
    port: -1

Triple

基于 http2上的协议、完全兼容grpc、更加高效

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-rpc-triple</artifactId>
    <version>${dubbo.version}</version>
</dependency>

yaml

dubbo:
  application:
    name: dubbo-provider #dubbo提供者名称
  registry:
    address: zookeeper://192.168.111.101:2181 #注册地址 使用zookeeper
  protocol:
    name: tri #使用triple协议
    port: -1

其他

配置中心

Nacos

dubbo:
  config-center:
    address: nacos://127.0.0.1:8848

Zookeeper

dubbo:
  config-center:
    address: zookeeper://127.0.0.1:2181

应用级服务发现

应用 升级到 Dubbo 3.0 后,服务端自动开启接口级 + 应用级双注册功能,默认—>无需开发者修改任何配置

#  双注册
dubbo.application.register-mode=all
# 仅 应用级注册
dubbo.application.register-mode=instance
# 仅 接口级注册
dubbo.application.register-mode=interface

yaml文件

dubbo:
  application:
    name: dubbo-provider # dubbo提供者名称
    register-mode: instance
  registry:
    address: zookeeper://192.168.111.101:2181 #注册地址 使用zookeeper    
  protocol:
    name: dubbo #使用dubbo协议
    port: -1

延迟暴露

所有服务都将在 Spring 初始化完成后进行暴露,如果你不需要延迟暴露服务,无需配置 delay

延迟 5 秒暴露服务

@DubboService(version = "v1.0", delay = 5000)
public class ServiceImpl implements AsyncService {}

问题

线程池资源枯竭

服务端的 线程资源耗尽了。 默认情况下,Dubbo 服务端的业务线程数是 200 个。如果多个并发请求量超过了 200,就会拒绝新的请求,抛出此错误

dubbo:
  provider:
    threads: 300

可能的原因

  1. Consumer 的并发请求量太大导致 Provider 端创建的线程数量超限
  2. 可能 Provider 端在执行业务的时候,由于业务调用外部应用接口,导致线程出现阻塞,从而导致线程池回收不了线程

排查和解决步骤

  1. 开启 Dubbo 的访问日志功能,排查是否有短时间内大量调用 RPC 服务的情况
  2. 通过 jps 和 jstack 指令检查线程池中各个线程的状态,看下是否有业务调用外部应用接口造成阻塞
  3. 如果是 Consumer 的并发请求量太大,那么调整 Provider 端的 dubbo.provider.threads 参数,将 Dubbo 的线程池的数目调大
  4. 如果 Provider 业务的 QPS 实在太大,目前的服务器数目处理不完,那么增加 Provider 端服务器的数量,让更多的服务器分担压力
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值