zoukankan      html  css  js  c++  java
  • RPC框架之Dubbo

     

    问题1:为什么要把系统拆分成分布式的?为啥要用dubbo?

    1.为什么要将系统进行拆分?

    1. 要是不拆分系统,一个大系统几十万行代码,很多人共同维护一份代码,简直是悲剧;

    2. 拆分了以后,一个大系统拆分成很多小服务,平均每个系统也就几万行代码,每个服务部署到单独的机器

    2.如何进行服务拆分?

    ​ 大部分系统,是要进行多轮拆分的,第一次拆分就可能将原来的多个模块拆分开来。

    ​ 但是后来可能每个系统都变得很复杂了,每个模块拆分出来的服务又要继续拆分。

    3.拆分后可以不用dubbo吗?

    ​ 当然可以,大不了各个系统之间,直接基于springmvc,就通过纯httpj接口互相通信。但是这里肯定是由问题的,因为HTTP接口通信维护起来成本很高,要考虑超时重试,负载均衡等各种问题。

    所以dubbo说白了,就是一个rpc框架,就是本地就是进行接口调用,但是dubbo会代理这个调用请求,跟远程机器网络通信,处理负载均衡,服务上下线自动感知,超时重试等问题,就不用我们自己做,交给dubbo。

    问题2:说一下dubbo的工作原理,注册中心挂了可以继续通信吗?说一下一次rpc请求的流程?

    1.dubbo工作原理

    2.dubbo分层

    • 第一层:service层,需要服务提供方和消费方来实现

    • 第二层:config层,配置层,主要是对dubbo的各种配置

    • 第三层:proxy层,服务代理层,透明生成客户端的stub和服务端的skeleton

    • 第四层:registry层,服务端的注册与发现

    • 第五层:cluster层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务

    • 第六层:monitor层,监控层,对rpc接口的调用次数和调用时间进行监控

    • 第七层:protocol层,远程调用层,封装rpc调用

    • 第八层:exchange层,信息交换层,封装请求响应模式,同步转异步

    • 第九层:transport层,网络传输层,抽象mina和netty为统一接口

    • 第十层:serilize层,数据序列化层

    2.注册中心挂了可以继续通信吗?

    是可以的,因为客户端第一次从注册中心获取服务端的服务后,会缓存在自己本地,下一次调用服务端不用去请求注册中心,因此注册中心挂了是可以继续通信的

    问题3:dubbo都支持哪些通信协议和序列化协议

    1.dubbo支持不同的协议

    1. dubbo协议

      默认就是走dubbo协议,单一长链接,NIO异步通信

      (NIO通信原理:NIO采用了Reactor模式(类似于观察者模式,不同之处在于Reactor模式可以监听多个主题),通过一个多路复用器来监听多个客户端的网络句柄,一旦监听到客户端的请求消息,将对应的请求消息转发给对应的Handler(业务处理类))

      适合场景:传输数据量较小,但是并发很高

    2. rmi协议

      走java二进制序列化,多个短连接,适合消费者和提供者数量差不多,适用于文件的传输,一般较少

    3. hessian协议

      走hessian序列化协议,多个短连接,适用于提供者数量和消费者数量还多,适用于文件传输,一般较少使用

    4. http协议

      走json序列化

    5. webservice

      走soap文本序列化

    问题4:dobbo负载均衡策略和集群容错策略?动态代理策略?

    dobbo负载均衡策略

    1.random loadbalance

    默认情况下,dubbo是random load balcance 随机调用实现负载均衡,可以对provider不同的实例设置不同的权限,会按照权重来负载均衡,权重越大分配流量越高,一般就用这个默认的就可以了。

    2.roundrobin loadbalance

    这个默认就是均匀地将流量打到各个机器上去,但是如果各个机器的性能都不一样,容易导致性能差的机器负载过高而宕机。此时需要调整权重,让性能差一点的机器承载权重少一些

    3.leastactive loadbalance

    这个就是自动感知一下,如果某个机器性能差,那么接收的请求越少,约不活跃,此时就会给不活跃的性能差的机器更少的请求

    4.consistanthash loadbalance

    一致性hash算法,相同的参数的请求一定分配到一个provider上去,provider挂掉的时候,会基于虚拟节点均匀分配剩余的流量,抖动不会太大。如果需要的不是随机负载均衡,是要一类请求都到这个节点上,那就走这个一致性hash策略

    dobbo集群容错策略

    1.failover cluster模式

    ​ 失败自动切换,自动重试其他机器,dubbo默认使用这种策略

    2.failfast cluster模式

    一次失败就立即失败,常见于写操作

    3.failsafe cluster模式

    出现异常就忽略,常用于不重要的接口调用,比如日志记录

    4.failbackc cluster模式

    失败了后台自动记录请求,然后定时发送,比较适合消息队列这种情况

    5.focking cluster模式

    并行的调用多个provider,只要一个成功就立即返回

    6.broadcast cluster模式

    逐个调用所有的provider

    dubbo的动态代理策略

    默认使用 javassist动态字节码生成,创建代理类

    问题5:你了不了解spi机制呢?如何基于spi机制对dubbo进行扩展?

    SPI机制

    • 简单来说,就是service provider interface;比如有一个接口A,现在这个接口A有三个实现类,那么在运行的时候对接口到底用哪个实现类呢?这就需要SPI,需要根据指定的配置和默认的配置,去找到对应的实现类加载进来,然后用这个实现类的对象

      例如:接口A --> 实现类A1 实现类A2 实现类A3

      配置一下,接口A= 实现类A2

      在系统运行的时候,会加载这个配置,用实现类A2实例化出对象来提供服务

      • 比如说你有一个工程Demo ,里面有一个接口A,接口A在工程里是没有实现的--> 系统在运行的时候,怎么给接口A选择一个实现类呢?

      • 你就可以自己搞一个jar包,META-INF/services/,上放一个文件,文件名就是接口名,接口A,接口A的实现类=com.ultrapower.service.实现类A2.让工程A来依赖这个jar包,然后在系统运行的时候,工程跑起来,对接口A,就会扫描自己依赖的所有jar包,在每个jar包里找找,有没有 META-INF/service 文件夹?如果有,在里面找,有没有接口A这个名字的文件?如果有,在里面找找有没有接口A的实现类是你的jar包里的那个类

    Dubbo的SPI机制

    dubbo也用了spi的思想,不过没有用到jdk的spi机制,是自己实现的一套SPI机制

       1 Protocol protocol=ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension() 
    • Protocol接口,dubbo需要判断一下,在系统运行的时候,应该选用这个Protocol接口的哪个实现类来实例化对象来使用呢?

    • 他会去找一个你配置的Protocol,它就会将你配置的Protocol实现类,加载到 jvm中来,然后会实例化对象,就会用到你提供的哪个Protocol接口实现类

    • 微内核,可插拔,大量的组件,Protocol负责RPC调用,你可以实现自己的rpc调用组件,实现Protocol接口,给自己一个实现类即可

    • 这行代码就是Dubbo中大量使用的,就是对很多组件,都保留一个接口和多个实现,然后在运行的时候动态的根据配置去找对应的实现类,如果没有配置,那就走默认的实现类。

     1  
     2   @SPI("dubbo")
     3   public interface Protocol{
     4        /**
     5        * 获取缺省端口,当用户没有配置端口时使用。
     6        *
     7        * @return 缺省端口
     8        */
     9       int getDefaultPort();
    10 11       /**
    12        * 暴露远程服务:<br>
    13        * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
    14        * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>
    15        * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>
    16        *
    17        * @param <T>     服务的类型
    18        * @param invoker 服务的执行体
    19        * @return exporter 暴露服务的引用,用于取消暴露
    20        * @throws RpcException 当暴露服务出错时抛出,比如端口已占用
    21        */
    22       @Adaptive
    23       <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
    24 25       /**
    26        * 引用远程服务:<br>
    27        * 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。<br>
    28        * 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。<br>
    29        * 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。<br>
    30        *
    31        * @param <T>  服务的类型
    32        * @param type 服务的类型
    33        * @param url  远程服务的URL地址
    34        * @return invoker 服务的本地代理
    35        * @throws RpcException 当连接服务提供方失败时抛出
    36        */
    37       @Adaptive
    38       <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
    39 40       /**
    41        * 释放协议:<br>
    42        * 1. 取消该协议所有已经暴露和引用的服务。<br>
    43        * 2. 释放协议所占用的所有资源,比如连接和端口。<br>
    44        * 3. 协议在释放后,依然能暴露和引用新的服务。<br>
    45        */
    46       void destroy();
    47 48   }

    在dubbo自己的jar里,在META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件中

    dubbo=com.alibaba.dubbo.rpc.DubboProtocol

    http=com.alibaba.dubbo.rpc.HttpProtocol

    hessian=com.alibaba.dubbo.rpc.hessianProtocol

    所有说这里就是dubbo默认的配置,其实就是一个Protocol接口,@SPI("dubbo")说的就是,通过SPI机制来提供实现类,实现类是通过dubbo作为默认key去配置文件里找的,配置文件的名称和接口名的全路径名时一样的,通过dubbo作为key 可以找到默认的实现类就是 com.alibaba.dubbo.rpc.DubboProtocol

    dubbo默认的网络通信协议,就是dubbo协议,用的 DubboProtocol

    如果想要动态替换默认的实现类,需要使用@Adapter接口,Protocol接口中,有两个方法加了@Adapter注解,就是说那俩接口会被代理实现。

    比如:这个Protocol接口搞了俩@Adapter 注解标注了方法,在运行的时候,就会针对Protocol生成代理类,这个代理类的俩方法里面会代理代码,代理代码会在运行时候动态根据url中的protocol来获取那个key,默认是dubbo,你也可以自己指定别的key,那么就会获取别的实现类的实例了。

    通过这个URL的参数不同,就可以控制动态的使用不同的组件实现类

    问题6:如何基于dubbo做服务治理,服务降级,失败重试以及超时重试?

    1.服务治理

    1.调用链路生成

    一个大型的分布式系统,由大量的服务组成。这些服务之间是如何调用的呢?调用链路是啥?

    那就需要基于dubbo做的分布式系统中,对各个服务之间的调用自动记录下来,然后自动将各个服务的依赖关系和调用链路生成出来,做成一张图,大家可以看到的

    2.服务访问压力以及时长统计

    需要自动统计各个接口之间的访问次数和调用延迟,而且要分成两个级别。一个级别是接口粒度,就是每个服务额每个接口每天被调用的次数,TP50 ,TP90,TP99,三个档次的请求延迟分别是多少;第二个级别是从源头入口,一个完整的请求链路经过几十个服务之后,完成一次请求,每天全链路走多少次,全链路请求延迟的TP50,TP90,TP99

    分别是多少?

    这些东西都搞定以后,后面才看当前系统的压力到底在哪里?如何来扩容和优化?

    3.其他的

    服务分层(避免循环依赖),调用链路失败监控和报警,服务鉴权,每个服务 的可用性的监控

    2.服务降级

    比如服务A调用服务B,结果服务B挂掉了,服务A重试几次调用服务B,还是不行,直接降级,走一个备用逻辑,给用户返回响应

    问题7:分布式服务接口的幂等性如何设计?

    所谓幂等性,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款,不能多插入一条数据,不能将统计值多加1。这就是幂等性。

    保证幂等性的手段如下:

    • 对于每次请求必须有一个全局唯一的标识

    • 每次处理完请求后,必须有一个记录标识这个请求处理完了,比如常见的方案是在mysql 中记录个啥状态,比如支付之前记录一条这个订单的支付流水,而且支付流水采用orderid 作为 唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。

    • 每次接收到了请求需要进行判断之前是否处理过的逻辑处理,比如说吗,如果有一个订单已经支付了,就已经有一个支付流水了,那么如果重复发送请求,则此时先插入支付流水,然而orderid已经存在了,唯一键约束生效,报错插入失败,然后你就不用扣款了

    • 一般生产上可以用redis中的 set来保证幂等

    问题8:如何设计一个类似于Dubbo的rpc框架

    简单梳理一下思路:

    1. 上来就要把服务到注册中心注册,就应该有一个注册中心,保留各个服务的地址信息,就可以用zookeeper来做。

    2. 然后消费者要去注册中心去拿服务信息,每个服务可能会存在于有多台机器

    3. 接着你就该发起一次请求,如何发呢? 基于动态代理,面向接口获取到一个动态代理,这个动态代理就是本地接口的一个代理,然后这个代理会找到服务对应的机器地址

    4. 然后找哪台机器发送请求? 这里会有一个负载均衡算法,比如可以使用随机轮训的算法

    5. 找到一台机器,如何发送给他? 可以用netty,nio的方式,第二个问题发送什么格式?可以是序列化格式或者json格式等

    6. 服务起那边一样,需要针对你的服务生成一个动态代理,监听某个网络端口,然后代理你本地的服务代码,接收到请求,就代理本地的服务方法。

  • 相关阅读:
    Apache Shiro 使用手册(二)Shiro 认证
    jdk 环境变量
    IDEA 相关整理
    mysql 相关命令
    hbase 迁库移库步骤
    Linux 常用指令整理
    springboot aop + logback + 统一异常处理 打印日志
    查看jar包的jdk版本
    maven 将jar包推送到自己本机的maven库
    jar包内的文件导出的注意点
  • 原文地址:https://www.cnblogs.com/ft-greate/p/12355290.html
Copyright © 2011-2022 走看看