分布式业务系统就是把原来的Java 开发的一个大块系统拆分成做个子系统,多个子系统支之间互相调用,形成一个整体。

    Dubbo 是一款高性能Java RPC 框架。它实现了面向接口代理的RPC 调用、服务注册和发现,负载均衡,容错,扩展性等功能;
    Dubbo 大致分为三层:
    业务层
    RPC 层
    Remoting 层

    dubbo 工作原理(核心组件):
    第一层:service 层,接口层,给服务提供者和消费者来实现的。
    第二层:config 层,配置层,对dubbo 进行各种配置。
    第三层:proxy 层,服务代理层,生成代理,代理之间进行通信。
    第四层:registry 层,服务注册层,负责服务注册与发现。
    第五层:cluster 层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务。
    第六层:monitor 层,监控层,对RPC 接口的调用次数和时间进行监控。
    第七层:protocal 层,远程调用层,封装RPC 接口调用。
    第八层:exchange 层,信息交换层,封装请求响应模式,同步转异步。
    第九层:transport 层,网络传输层,抽象main 和netty 为统一接口。
    第十层:serialize 层,数据序列化层。

    工作流程:

    1. provider(提供者)向注册中心去注册。
    2. consumer(消费者)从注册中心订阅服务,注册中心通知消费者注册好了的服务。
    3. 消费者调用提供者。
    4. 消费者和提供者异步通知监控中心。

    刚开始初始化的时候,消费者会将提供者的地址等的信息拉取到本地缓存,所以即使注册中心挂了也可以继续通信。

    注册中心:
    主要作用:
    动态载入服务,服务提供者通过注册中心把自己暴露给消费者,不需要消费者逐个更新配置文件。
    动态发现服务,消费者动态感知新的配置、路由规则和新的服务提供者。
    参数动态调整,支持参数的动态调整,新参数会自动更新到所有的服务节点。
    服务统一配置,统一连接到注册中心的服务配置。

    注册中心工作原理:
    Dubbo 有四种注册中心的实现,分别是ZooKeeper,Redis,Simple,Multicast
    Zookeper 是通过树形文件存储的ZNode 在/dubbo/service 下建立四个目录:
    Providers 目录:存放服务提供者URL 和元数据
    Consumers 目录:存放消费者URL 和元数据
    Routers 目录:存放消费者的理由策略
    Configurators 目录:存放多个用于服务提供者的动态配置URL 和元数据
    客户端第一次链接注册中心时会获取全量的服务元数据;根据Zookeeper 客户端的特性,会在对应的ZNode 的目录上注册一个Watcher ,同时让客户端和注册中心保持长连接。如果服务端的元数据发生改变,客户端就会接收到变更通知,然后去注册中心更新元数据信息。

    Dubbo 通信协议:
    dubbo 协议:
    默认走的协议,单一长连接,长连接:就是建立连接过后可以持续发送请求,不需要在建立连接。dubbo 协议进行的是NIO 异步通信,基于hassian 协议作为序列化的协议,一般用于传输数据量小,但是并发量很高的场景。NIO 异步通信可以支撑高并发请求,长连接是最适合高并发场景的。
    rmi 协议:
    走Java 二进制序列化,多个短连接,适合消费者和提供者差不多的情况,适用于文件传输,较少用。
    hessian 协议:
    hessian 序列化协议,多个短连接,是用户提供者比消费者多的情况,适用于文件传输,较少用。
    数据结构:
    原始二进制数据
    boolean
    64-bit date(64 位毫秒值的日期)
    64-bit double
    32-bit int
    64-bit long
    null
    UTF-8 编码的 string
    http 协议:
    json 序列化
    webservice :
    SOAP 文本序列化。

    PB(Protocol Buffer):
    PB 是Google 出品的轻量且高效的结构化数据存储格式,性能比JSON,xml 要高很多。第一:PB 使用proto 编译器,自动进行序列化和反序列化,速度非常快;第二:PB 的数据压缩效果好,它序列化后的数据量体积小,传输起来带宽和速度上会有优化。

    Dubbo 负载均衡策略:
    random loadbalance :
    默认情况下的均衡策略,就是随机调用实现负载均衡,可以对提供者不同实例设置不同的权重,会按照权重来负载均衡,权重越大分配的流量越高。
    roundrobin loadbalance:
    它默认均匀地将流量分配到各个机器上,机器的性能不一样,容易导致性能差的机器负载过高,所以需要调整权重,让性能差的机器承载权重小一点,流量小一点。
    leastactive loadbalance :
    会自动感知,如果机器的性能越差,接收的请求越少,越不活跃,此时就会给不活跃的性能差的机器更少的请求。
    consistanthash loadbalance :
    一致性hash 算法,相同参数的请求一定分发到一个提供者上,提供者挂掉时,会基于虚拟节点自动分配剩余流量,抖动不会太大。如果需要的不是随机负载均衡,是要一类请求都到一个节点那就走这个一致性hash 策略。

    Dubbo 集群容错策略:
    failover cluster 模式:
    失败自动切换,会自动重试其他机器,默认的模式,常见于读操作。
    failfast cluster 模式:
    一次调用失败就立即失败,常见与非幂等性的写操作,比如新增一条记录。
    failsafe cluster 模式:
    出现异常时忽略掉,常用于不重要的借口调用,比如记录日志。
    failback cluster 模式:
    失败后会自动记录请求,然后定时重发,比较适用于写消息队列这种情况。
    forking cluster 模式:
    并行调用多个提供者,只要一个成功就立即返回,常用于实时性要求比较高的操作,但是会浪费很多的服务资源,可设置最大并行数。
    broadcacst cluster 模式:
    逐个调用所有的提供者,任何一个提供者出错立即报错,通常用于通知提供者更新缓存或日志等本地资源信息。

    Dubbo 动态代理策略:
    默认使用javassist 动态字节码生成,创建代理类。但是可以通过spi 扩展机制配制自己的动态代理策略。

    spi 思想:
    spi(service provider interface ),根据指定的配置或默认配置,取找到对应的实现类加载进来。然后用这个实现类的实例对象。一般用在插件扩展的场景。
    Dubbo 用的是Protocol 接口,在系统运行的时候,dubbo 会判断应该选用protocol 接口的那个实现类来实例化对象,它会找到配置的protocol ,将配置的实现类加载到jvm 中然后实例化对象。

    Dubbo 的服务治理,服务降级,失败重试,超时重试:
    服务治理:
    1.调用链路自动生成
    一个大型的分布式系统或者现在流行的为服务框架,分布式系统都是由大量的服务组成。服务器之间的调用就需要基于dubbo 的分布式系统中对各个服务之间的调用自动记录下来,然后将各个服务之间的以来关系和调用链路生成出来。
    2.服务访问压力以及时长统计
    这就需要自动统计各个接口和服务之间的调用次数以及访问延时,分两个级别:

    • 接口粒度,就是每个服务的每个接口每天被调用多少次,请求延时分别是多少
    • 从源头入口开始,一个完整的请求链路经过几十个服务之后完成一次请求,每天链路走多少次,全链路请求延时分别是多少。

    知道这些东西之后才取考虑如何扩容、如何优化。
    3.其他
    服务分层(避免循环依赖)
    调用链路失败监控和报警
    服务鉴权
    每个服务的可用性的监控

    服务降级:
    服务降级是当服务器压力剧增的情况下,根据当前业务情况以及流量对一些服务和页面进行由策略的降级,以此释放服务器资源以保证核心任务的正常运行。
    降级的方式:
    服务接口拒绝服务:页面能访问,但是添加删除会提示服务器繁忙。
    页面拒绝访问:页面提示服务器繁忙,跳转到其他静态页面。
    延迟持久化:页面照常访问,记录变更会稍晚看到结果,将数据记录到异步队列或log ,服务恢复后执行。
    随机拒绝服务:服务接口随机拒绝服务,让用户重试,会导致用户体验不佳。
    调用接口失败的时候可以通过mock 同意返回null。mock 的值也可以改为true,然后跟接口同一个路径下实现Mock 类,然后在mock 类里实现自己的降级逻辑。

    失败重试和超时重试:
    失败重试就是消费者调用提供者失败,比如抛出异常,此时就可以重试,或者调用超时也可以重试
    参数设置:
    timeout:超时时间,超过设置时间则调用超时
    retries:一般是在读请求的时候设置,如果没有读到会报错,重试指定的次数。

    分布式服务接口的幂等性:
    所谓幂等性就是说一个接口多次发起同一个请求,要确保这个请求是准确的,保证幂等性应该结合实际的业务去考虑。
    主要考虑以下三点:
    1、对于每个请求必须有一个唯一的标识,
    2、每次处理完请求之后必须有一个记录标识表示已经处理了这个请求,常见的方法就是在mysql 中记录状态
    3、每次接收请求之前要进行判断之前是否有处理过这个请求,
    也就是:全局唯一ID、去重表,多版本并发控制
    但是我们通常只需要对新增请求和更新请求做幂等性保证。

    分布式服务接口请求的顺序性:
    在使用之前应该从业务逻辑上考虑是否需要顺序性保证;比如,使用分布式锁会导致系统复杂性提高,可能会带来效率低下,热数据压力过大等的问题。
    首先得用dubbo 的一致性hash 负载均衡策略,比如将某个请求分发个某个机器,然后机器可能因为多线程并发执行,就需要把对应的请求扔到一个内存队列中去,强制排队,以此来保证请求的顺序性。最好能将多个操作合并成一个操作。

    为什么要将系统进行拆分?
    1、不拆分的话系统的代码太多,多个人维护一份代码容易引起代码冲突,还要解决代码合并问题,耗时耗力。不容易进行再次开发和维护
    2、拆分后的系统每个服务器上的代码量就少很多,每个服务器部署到单独的机器上,不会代码冲突,容易维护。

    如何拆分?
    大部分的系统是会随着系统的复杂度的提升进行多次拆分的。第一次拆分可能就是根据情况将多个模块拆分出来。随着复杂度的提升,后面会将每个模块又进行拆分。

    如何设计一个类似Dubbo 的RPC 框架?
    考虑以下问题:

    • 首先是注册中心,保留各个服务的信息。
    • 当消费者去注册中心拿对应的服务时可能服务会存在多台机器中
    • 基于动态代理发送请求,面向接口获取动态代理,动态代理找到本地代理,然后找到服务器对应的机器地址。
    • 负载均衡算法。
    • 找到机器发送的请求
    • 服务器需要生成一个动态代理,监听网络端口代理本地服务代码进行调用。