[Java复习] 分布式PRC

分布式RPC框架

dubbo常见问题:

1. 问dubbo的工作原理:服务注册,注册中心,服务生产者,消费者,代理通信,负载均衡

2. 问网络通信,序列化: dubbo协议,长连接,NIO,hessian序列化协议

3. 问负载均衡策略,集群容错策略,动态策略:dubbo跑起来后一些功能是如何运转,怎么做复制均衡,怎么做容错,怎么生成动态代理?

4. 问dubbo SPI机制:是否了解SPI机制?如何基于SPI机制对dubbo进行扩展?

5. dubbo服务治理,超时,降级,重试等。

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

  项目大了,代码多了,很多代码冲突,代码合并维护困难。改一个地方,回归测试工作量很大。

  大项目发布上线依赖很多外部东西,发布特别麻烦。

2. 如何进行拆分?

  系统拆分需要经过多轮拆分,不可能一次就拆分好了。考虑多少个人维护多少个服务。

3.拆分后为什么要用Dubbo, 不用Dubbo可以吗?DubboThrift有什么区别?

  可以不用。可以用纯基于Spring MVC,纯http接口相互通信,但是维护成本很高。

  Dubbo已经在RPC做得很好了,成熟的RPC框架,本地就是进行接口调用,Dubbo代理调用请求,跟远程网络通信,处理负载均衡,超时重试等等功能。

4. 说一下的Dubbo 的工作原理?注册中心挂了可以继续通信吗?说说发起一次RPC请求的流程?

  4.1. dubbo工作原理

    第一层: service层/接口层: provider和consumer接口,留给你来实现服务的生产者和消费者

    第二层:config层:提供配置文件,对dubbo进行各种配置

    第三层:proxy层:dubbo为provider和consumer生成代理,代理之间进行网络通信

    第四层:registry层:负责服务的注册与发现

    第五层:cluster层:provider可以部署在多台机器,组成集群

    第六层:monitor层:对RPC接口调用次数和调用时间进行监控

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

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

    第九层:transport层:网络传输

    第十层:serialize层:数据序列化

   工作流程:

     第一步:provider 向注册中心去注册

     第二步:consumer 从注册中心订阅服务,注册中心会通知 consumer 注册好的服务

     第三步:consumer 调用 provider

     第四步:consumer 和 provider 都异步通知监控中心

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

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

5. dubbo支持哪些通信协议?支持哪些序列化协议?

  1. dubbo支持不同的通信协议

    1). dubbo协议

        dubbo://192.168.0.1:20188

        默认dubbo协议,单一长连接,NIO异步通信,Hessian二进制序列化。

        适用场景:传输数据量小(每次请求100KB内),消费者比提供者个数多, 适合于小数据量大并发的服务调用。

  • 为什么要消费者比提供者个数多?

           假设网络为千兆网卡(1024Mbit=128MByte),根据网络测试,每条连接最多只能压满7MByte,理论上1个服务提供者需要20个消费者才能压满网卡。

  • 为什么不能传大包?

           假设网络为千兆网卡(1024Mbit=128MByte),每条连接最大7Mbyte,如果请求是大包(比如500KByte),

           则单个服务提供者的TPS最大:128Mbyte / 500KByte = 262。
           单个消费者调用单个服务提供者TPS最大:7MByte / 500KByte = 14。
           所有单个请求包越大,消费者调用的TPS越低,网络成为瓶颈。

  • 为什么异步单一长连接?

          大多数网络都是服务提供者少,消费者请求者多。通过单一连接,保证单一消费者不会压垮提供者,长连接可以减少握手次数,使用异步IO,复用线程池。

        

         2)  Hessian协议。用于集成Hessian的服务,Hessian底层采用Http通讯,采用Servlet暴露服务。

          适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。适用场景:页面传输,文件传输,或与原生hessian服务互操作.

        因此比较高效的做法是带上传下载文件的服务使用hessian协议,普通的服务使用dubbo协议。

         其他协议, 如rmi协议(java序列化),http协议(JSON序列化),webservice(SOAP序列化)

        为什么PB(protocol  buffer)效率最高?

         PB是google出品的轻量高效的结构化数据存储格式,性能比JSON,XML要高很多。

         性能高原因:1. 使用proto编译器,自动序列化和反序列化,速度非常快。2.数据压缩效果好,序列化后数据体积小,传输带宽和速度有优势。

  

6. dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?

  6.1 负载均衡策略:

    1. Random loadbalance

       dubbo默认是random load balance,随机负载均衡。可以对provider设置不同权重,按照权重来做负载均衡,权重越大分配到流量越高。

    2. LeastActive loadbalance

       自动感知,如果某机器性能越差,接收请求越少,越不活跃,就会给更少的请求。

    3. Consistent hash loadbalance

      一致性hash算法,相同参数请求分发到一个provider上,这个provider挂了的时候,会基于虚拟节点均匀分配剩余流量,减小抖动。

      如果你需要是要某一类请求都到在某一个节点处理,那就选这个一致性 Hash 策略。

   6.2 集群容错策略:

      failover cluster模式(默认):

         如果某台机器宕机,请求失败几次后重试其他机器。

      failfast cluster模式:

         一次调用失败就立即失败。比如新增一条记录。

      failsafe cluster模式:

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

      failback cluster模式:

         失败后记录请求,然后定时重发,适合于写消息队列。

      forking cluster模式:

         并行调用多个provider,只要一个成功就立即返回。适用于实时性比较要求比较高的读操作,但会浪费资源。通过forks=”2”来设置最大并行数。

    6.3 动态代理策略:

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

7. SPI是什么?DubboSPI是什么?

  SPI: Service Provider Interface。

   比如有个接口A,有3个实现类,实现类A1/A2/A3。那么在系统运行时候对这个接口到底选择哪个实现类?

   需要根据指定的配置,去找对应的实现类加载,用这个实现类实例对象。这个就是SPI。

    比如通过jar包方式给某个接口提供实现,在自己jar包的META-INF/services/,放一个和接口同名的文件a,里面指定接口的实现就是jar包里某个类。

    在调用时,通过jar的a文件找到这个接口应该用哪个实现类。

   SPI 机制适用场景?

       适用插件扩展的场景,比如说你开发了一个给别人使用的开源框架,如果你想让别人自己写个插件,插到你的开源框架里面,从而扩展某个功能。

       比如jdbc, java提供jdbc接口,没有提供实现类。实际根据具体适用的数据库,比如mysql的jar包,mysql-jdbc-connector.jar引入。

       系统运行时,遇到jdbc接口,会在底层适用引入的jar提供的实现类。

   dubbo的SPI思想:

Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

   Protocol 接口,在系统运行的时候,dubbo 会判断一下应该选用这个 Protocol 接口的哪个实现类来实例化对象来使用。

@SPI("dubbo")  
public interface Protocol {      
    int getDefaultPort();  
    @Adaptive  
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;  
    @Adaptive  
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;  
    void destroy();  
} 

  在 dubbo 自己的 jar 里,在/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件中:

dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol

  如何自己扩展 dubbo 中的组件:

   自己写个工程,要是那种可以打成 jar 包的,里面的 src/main/resources 目录下,搞一个 META-INF/services,

   里面放个文件叫:com.alibaba.dubbo.rpc.Protocol,文件里搞一个my=com.bingo.MyProtocol。自己把 jar 弄到 nexus 私服里去。

   然后自己搞一个 dubbo provider 工程,在这个工程里面依赖你自己搞的那个 jar,然后在 spring 配置文件里给个配置:

<dubbo:protocol name=”my” port=”20000” />

  provider 启动的时候,就会加载到我们 jar 包里的my=com.bingo.MyProtocol 这行配置里,接着会根据你的配置使用你定义好的 MyProtocol 了,这个就是简单说明一下,你通过上述方式,可以替换掉大量的 dubbo 内部的组件,就是用你自己的 jar 包,然后配置一下即可。

    

8. 如何基于dubbo进行服务治理、服务降级、失败重试及超时重试?

  服务治理

   1. 调用链路自动生成

     基于dubbo做分布式系统中,对各个服务之间的调用自动记录下,然后自动将各个服务之间的依赖关系和调用链路生成出来。

     服务分层,避免循环依赖。

     调用链路失败监控和报警。

     

   

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

       自动统计各个接口和服务之间的调用次数以及访问延时。后面才能看当前系统的压力在哪里,如何来扩容和优化。

  3. 服务调用权限和可用性

      只有某个服务有权利调用另外一个服务。

      可用性监控,接口调用成功率,几个9? 99%, 99.9%,99.99%

  服务降级

    服务A调用服务B , B挂了,服务A尝试几次都不行,就要服务降级,走备选逻辑,给用户返回响应。

    我们调用接口失败的时候,直接通过mock统一返回null。

    也可以设置mock=true,可以通过走mock Service进行降级逻辑处理。

  失败重试和超时重试

      失败重试,就是 consumer 调用 provider 要是失败了,比如抛异常了,此时应该是可以重试的,或者调用超时了也可以重试。配置如下:

<dubbo:reference id="xxxx" interface="xx" check="true" async="false" retries="3" timeout="2000"/>

       retries:失败重试次数

       timeout:超过xxx秒超时返回

9. 分布式服务接口的幂等性如何设计(比如不能重复扣款)?

   幂等性:同一个接口,多次发起同一个请求,保证接口结果正确,不能重复操作,多扣款或多插入数据等等。

   单机:JVM内存用map或set保存orderid, 记录订单已经支付过。

   分布式: 1. 设计一个标识来表示这个请求已经处理过。比如支付之前插入一条记录标识这个订单的支付流水。

                     重发请求,由于已经插入过这个orderid的流水,唯一键约束,插不进去。

                   2. 数据库里记录一个支付状态。比如未支付,已支付等等。

                   3. 利用Redis来保存一个是否处理过的标识。SETNX key。

   PS:没有通用解决方案,需要结合业务来保证幂等性。

10. 分布式服务接口请求的顺序性如何保证?

    解决方案:

       1. 业务逻辑上的设计,最好不要设计这种需要保证顺序性的。

           一旦引入,比如使用分布式锁(很重),会导致系统复杂度上升,效率降低,热点数据压力过大等问题。

       2. 使用dubbo的一致性hash负载均衡策略。

           将某个请求(某个订单id)对应的请求都要分发到同一个机器上,然后将对应的多个请求放入一个内存队列,强制排队,这样来保证顺序。

11. 如何自己设计一个类似 Dubbo RPC 框架?

  考察点:有没有对某个RPC框架原理有深入理解。能不能从整体上来考虑,考察系统设计能力。

  回答思路:

  整体包括哪些模块:注册中心,消费者,生产者和想对应的接口,动态代理,负载均衡,底层网络通信协议,序列化协议。RPC框架扩展-SPI机制,超时,降级。

   1. 设计注册中心。 保留各个服务的信息,可以用ZK。

   2. 服务在注册中心注册,自动生成服务代理,动态代理,自动监听网络请求,

       消费者的代理,调用别的服务,实现负载均衡算法,在部署服务的多台机器上,按负载均衡算法挑选服务,建立网络连接,发送网络请求。

   3. 网球请求怎么发?可以用netty,NIO等等。

   4.  请求中数据格式?序列化协议。可以用hessian序列化,或者别的,pb等等,通过字节流发送到服务端。

   5. 服务接收端要反序列化,反序列化为对象,在本地调用服务接口,处理完结果后序列化返回给消费者。

   6. RPC框架怎么扩展: SPI机制。

   7.  怎么做超时,降级。

参考资料:

  《互联网Java进阶面试训练营》的笔记 -- 中华石杉

原文地址:https://www.cnblogs.com/fyql/p/11956553.html