dubbo

dubbo协议

在这里插入图片描述

dubbo负载均衡策略及实现方式
  • Random 随机

  • RoundRobin 轮循

    平均分布,但是存在请求累积的问题

  • LeastActive 最少活跃,处理能力慢的服务接收更少的请求

    每个服务维护一个int类型活跃数计数器,开始处理时+1处理完成时-1。当A服务开始处理请求,计数器加1,此时A还未处理完成。而B服务接受到请求后很快处理完毕。那么A、B的活跃数分别是1、0。当又产生了一个新的请求,则选择B服务(B活跃数最小),减少慢服务收的请求

  • ConstantHash 一致性Hash 相同参数请求总是发到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其他提供者

    基于TreeMap<Long, Invoker<T>> 及其tailMap方法实现(TreeMap是基于key做了排序,tailMap方法返回大于等于key的SortedMap类型元素集合),默认为每个节点生成160个虚拟节点,为每个节点包括虚拟节点计算出一个hash值作为key,获取时,默认根据方法的第一个参数(eg: find(String name, int age) -> 取name进行hash)进行hash,随后调用tailMap(key)方法获取,如果返回空则取第一个(已这种方式形成逻辑环)

    private static final class ConsistentHashSelector<T> {
        private final TreeMap<Long, Invoker<T>> virtualInvokers; // 虚拟结点
        private final int                       replicaNumber;   // 副本数 
        private final int                       identityHashCode;// hashCode
        private final int[]                     argumentIndex;   // 参数索引数组
    
        public ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
            // 【创建TreeMap来保存结点】
            this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
            // 生成调用结点HashCode
            this.identityHashCode = System.identityHashCode(invokers);
            // 获取Url dubbo://169.254.90.37:20880/service.DemoService?anyhost=true&application=srnt&check=false&dubbo=2.8
            URL url = invokers.get(0).getUrl();
            // 获取所配置的结点数,如没有设置则使用默认值160
            this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);
            // 获取需要进行hash的参数数组索引,【默认对第一个参数进行hash】
            String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0"));
            argumentIndex = new int[index.length];
            for (int i = 0; i < index.length; i ++) {
                argumentIndex[i] = Integer.parseInt(index[i]);
            }
            // 创建虚拟结点 对每个invoker生成replicaNumber个虚拟结点,并存放于TreeMap中
            for (Invoker<T> invoker : invokers) {
                for (int i = 0; i < replicaNumber / 4; i++) {
                    // 根据md5算法为每4个结点生成一个消息摘要,摘要长为16字节128位。
                    byte[] digest = md5(invoker.getUrl().toFullString() + i);
                    for (int h = 0; h < 4; h++) {
                        long m = hash(digest, h);
                        virtualInvokers.put(m, invoker);// 放入map
                    }
                }
            }
        }
        private Invoker<T> sekectForKey(long hash) {
            Invoker<T> invoker;
            Long key = hash;
            // 若HashCode直接与某个虚拟结点的key一样,则直接返回该结点
            if (!virtualInvokers.containsKey(key)) {
                // 若不在,找到一个最小上届的key所对应的结点。
                SortedMap<Long, Invoker<T>> tailMap = virtualInvokers.tailMap(key);
                // 若存在则返回,例如hashCode落在图中[1]的位置
                // 若不存在,例如hashCode落在[2]的位置,那么选择treeMap中第一个结点
                // 使用TreeMap的firstKey方法,来选择最小上界。
                if (tailMap.isEmpty()) {
                    key = virtualInvokers.firstKey();
                } else {
                    key = tailMap.firstKey();
                }
            }
            invoker = virtualInvokers.get(key);
            return invoker;
        }
    }
    
dubbo使用中遇到的问题
  1. provider抛自定义异常,customer捕捉到的有时候正确,有时候是RuntimeException

    dubbo异常处理机制

    ExceptionFilter#invoke

    1.如果是checked异常(Exception作为异常父类,是checked异常,要求必须try,RuntimeException作为其子类,是unchecked异常),直接抛出.很明显,我们的HelloExceptionRuntimeException,不符合

    2.在方法签名上有声明,直接抛出.很明显,我们接口并未声明该异常,不符合

    3.异常类和接口类在同一jar包里,直接抛出.很明显,我们的异常类是在common.jar的,接口是在api.jar的,不符合

    4.是JDK自带的异常,直接抛出.很明显,这个HelloException是我们自定义的,不符合

    5.是Dubbo本身的异常(RpcException),直接抛出.很明显,这个HelloException是我们自定义的,和RpcException几乎没有半毛钱关系.

    6.否则,包装成RuntimeException抛给客户端.因为以上5点均不满足,所以该异常会被包装成RuntimeException异常抛出(重要)

    为什么要这么设计

    可能是由于序列化的原因,因为如果服务器抛出一个自定义异常,直接扔给客户端,客户端可能解析不了,第1步判断是否是检查异常,检查异常是dubbo框架内部的,所以客户端可见。第2步判断是否在方法上有声明该异常,如果有说明客户端对这个异常也是可见的,以下的都一样。

    怎么解决问题

    API.jar方法加上异常声明

  2. 有些版本(2.5)并发数高一些的话会出现大量客户端超时

    dubbo有3种线程池,默认使用固定线程池,核心线程数是200,队列容量默认是0(JDK的是Integer.max),这就会导致超过200个任务并发执行的时候就会拒绝,2.5版本的拒绝策略是记录日志后抛异常,抛出的异常再次交给业务线程处理,此时业务线程池可能仍然是满的,再次拒绝,导致响应信息一直没有机会处理,客户端傻等到超时
    2.6版本会catch拒绝异常,判断当前请求是否有返回值,有返回值的直接把异常信息写入响应,客户端不用再傻等

dubbo管理平台admin、监控平台monitor
监控平台
  • 服务注册信息,包括提供者和消费者、注册中心信息
  • 调用数据统计,包括调用耗时、QPS、调用和响应的折线图

实现原理:基于dubbo过滤器MonitorFilter,invoke方法调用时会收集调用相关的监控统计信息,并且每隔1分钟上报监控平台,
最终交给DubboMonitor做上报操作,定时上报的实现方式是用
定时线程池
ScheduledExecutorService.scheduleWithFixedDelay

监控中心

https://www.jianshu.com/p/2062a91502ac?utm_source=oschina-app

管理平台
  1. 【提供者】【消费者】查看服务提供者、消费者列表(机器ip),可操作禁用、启用
  2. 【权重调节】修改服务权重,【负载均衡】修改负载策略
  3. 【路由】配置路由策略,可以启用禁用,会设置一些匹配条件,比如针对某个方法,消费者ip、消费者应用名、过滤条件:服务提供者ip、端口
    路由可以用来做应用隔离,即发版时把所有流量都打到一个节点上,等另一个节点发版完成,再切到新版上,再去发旧版
  4. 【动态配置】可以配置服务降级,非业务异常比如超时这种,可以设置返回结果 return null

管理平台

https://www.jianshu.com/p/6ce86645c462

dubbo注册了些什么
  1. 节点类型

    dubbo / 服务接口路径 永久节点

    ​ providers 临时节点

    ​ consumers

  2. 服务信息 服务提供和服务消费注册信息不同

    服务提供者:协议、服务器ip与端口、服务接口路径、接口方法名称集合、dubbo版本号、服务接口版本号

providers/dubbo://10.118.66.15:20880/org.spring.springboot.dubbo.DubboService?anyhost=true&application=provider&dubbo=2.5.3&interface=org.spring.springboot.dubbo.DubboService&methods=find&pid=13608&revision=1.0.0&side=provider&timestamp=1566348434768&version=1.0.0

consumers/consumer://10.118.66.15/org.spring.springboot.dubbo.DubboService?application=consumer&category=consumers&check=false&dubbo=2.5.3&interface=org.spring.springboot.dubbo.DubboService&methods=getCitys,find&pid=5148&revision=1.0.0&side=consumer&timestamp=1566375948555&version=1.0.0

SPI和API

SPI(Service Provider Interface)服务提供者接口,API应用程序接口

SPI是JDK接口和实现类绑定的一种机制,实现方式,在[资源目录] 的META-INF文件夹下提供一个以接口命名的文件,文件内容是接口实现类的路径,对应的文件加载器类(ServiceLoader)会到这个地方加载对应的文件

从概念上讲,API侧重于服务提供者,我有一个服务,对外暴露接口。SPI侧重服务调用者,我想要一个服务,我定义一个接口,具体实现有其他人定

个以接口命名的文件,文件内容是接口实现类的路径,对应的文件加载器类(ServiceLoader)会到这个地方加载对应的文件

从概念上讲,API侧重于服务提供者,我有一个服务,对外暴露接口。SPI侧重服务调用者,我想要一个服务,我定义一个接口,具体实现有其他人定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值