深入RPC分布式原理

前面几节讲的都是单机 RPC 服务的模式,无论是多线程也好多进程也好,它们都只能算是单点的设计。现代企业的关键性 RPC 服务是绝不可以只使用单点部署的。本节我们要对 RPC 服务进行分布式化,使得服务可以容忍个别节点故障仍能继续对外提供服务。

客户端

当 RPC 服务部署在多个节点上时,客户端得到的是一个服务列表,有多个 IP 端口对。客户端的连接池可以随机地挑选任意的 RPC 服务节点进行连接。

每个服务节点应该有个权重值,当所有节点的权重值一样时,它们的流量分配就是均匀的。如果某个节点的相对权重值较小,它被客户端选中的概率也会相对比较小。

class RPCNode {
    String addr;  // 服务地址
    int weight; // 节点权重
}

class RPCCluster {
    RPCNode[] nodes; // 节点列表

    Node random(); // 按权重随机挑选节点
}

容灾Failover

当有一个服务节点挂掉时,客户端需要采取一定的策略避免请求失败。当请求失败时,客户端还要进行重试,但是也不可以无限重试,要有一定的重试策略。

比如当节点挂掉时,将失效节点摘除,放置到失效节点列表中。然后每隔一段时间检查失效节点是否恢复了,如果恢复了,那就从失效节点中移除,再将节点地址重新加入到有效节点列表中。那如何判断节点真的挂掉了呢,一般需要设置一个时间窗口,统计在一定时间窗口里出现的错误数量。如果这个数量过大,那就意味着失效了。这也是为了防止网络偶然抖动导致服务节点流量的大幅波动。

降权法

上面提到客户端会为每个节点赋予一个权值,改变权值就可以改变节点的相对流量。如果某个节点出现了一次调用错误,可以对该节点进行降权。如果错误次数过多,降权会降的很快,最终达到一个最小值。之所以不应该降到零,那是为了给节点提供一个恢复的机会。被降权的节点后来只要有一次调用成功,那么 weight 值就应该尽快被还原,这样节点就可以快速恢复为正常节点。

客户端一次调用失败会尝试重试。如果降权太慢,会导致重试次数太多,因为第二次随机挑选节点时还是很有可能再次挑选到失效节点。降权太快也不好,网络抖动会导致节点流量分配的快速抖动,瞬间从正常降到近零,又可以瞬间从近零恢复到正常。

一个简单的策略是权重减半法。错误一次权重减半,连续错误两次权重就降到 1/4,如此直到降到最小值。如果初始权重值是 1024,那么权重值就会逐渐衰减1024=>512=>256=>128=>64=>32=>16=>8=>4=>2=>1。如果节点恢复了,那么调用会成功,权重就可以直接恢复到正常值,也可以通过加倍法逐渐恢复到正常值1=>2=>4=>8=>16=>32=>64=>128=>256=>512=>1024。如果希望恢复的更快一点,可以通过乘 4 法,乘 8 法。

服务发现

健壮的服务应该是可以支持动态扩容的服务。比如 RPC 服务压力过大,希望通过增加节点的方式来减小单个 RPC 服务的压力。如果使用的是前面的静态 RPC 服务地址列表,那么当节点增加时,我们需要修改客户端的配置重启才能生效。

通过服务发现技术,当 RPC 服务节点增加或减少时,客户端可以动态快速收到服务列表的变更信息,从而可以实时调整连接配置,这样无需重启就可以完成服务的扩容和缩容。

class ServiceDiscovery(object):

    def register_service(self, name, addr):
        pass

    def get_services(self, name):
        pass

    def on_services_changed(self, name):
        pass

服务发现技术依赖于服务之间的特殊中间节点。这个节点的作用就是接受服务的注册,提供服务的查找,以及服务列表变更的实时通知功能。它一般使用支持高可用的分布式配置数据库作为解决方案,如 zookeeper/etcd 等。

  1. 服务注册——服务节点在启动时将自己的服务地址注册到中间节点
  2. 服务查找——客户端启动时去中间节点查询服务地址列表
  3. 服务变更通知——客户端在中间节点上订阅依赖服务列表的变更事件。当依赖的服务列表变更时,中间节点负责将变更信息实时通知给客户端。

小结

分布式的原理并没有它的名字听起来那样复杂,它在本质上也不过是将多个单机服务组合在一起对外提供服务。

原文地址:https://www.cnblogs.com/ExMan/p/12112604.html

时间: 2024-10-08 10:58:26

深入RPC分布式原理的相关文章

学习笔记TF061:分布式TensorFlow,分布式原理、最佳实践

分布式TensorFlow由高性能gRPC库底层技术支持.Martin Abadi.Ashish Agarwal.Paul Barham论文<TensorFlow:Large-Scale Machine Learning on Heterogeneous Distributed Systems>. 分布式原理.分布式集群 由多个服务器进程.客户端进程组成.部署方式,单机多卡.分布式(多机多卡).多机多卡TensorFlow分布式. 单机多卡,单台服务器多块GPU.训练过程:在单机单GPU训练,

RPC 的原理和简单使用

RPC 的原理和简单使用 RPC 的概念 RPC,Remote Procedure Call ,翻译成中文就是远程过程调用,是一种进程间通信方式.它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数.在调用的过程中,不用程序员显式编码这个远程调用的细节.即无论是调用本地的接口/服务还是远程的接口/服务,本质上编写的调用代码基本相同. 说起 RPC,就不能不提到分布式,这个促使RPC诞生的领域. 假设你有一个计算器接口,Calculator 模块,以及它的实现类 Calcula

网络编程 -- RPC实现原理 -- RPC -- 迭代版本V1 -- 本地方法调用

网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2--RPC -- 本地方法调用:不通过网络 入门 1. RPCObjectProxy rpcObjectProxy = new RPCObjectProxy(new LocalRPCClient()); : 绑定目标对象 2. IUserService userService = (IUserService) rpcObjectProxy.create(IUserService.class); :返回代理类 3. List<User> u

Python Scrapy分布式原理详解

本文和大家分享的主要是python爬虫的Scrapy分布式原理相关内容,一起来看看吧,希望对大家学习python爬虫有所帮助. 关于Scrapy工作流程回顾 Scrapy单机架构 上图的架构其实就是一种单机架构,只在本机维护一个爬取队列,Scheduler进行调度,而要实现多态服务器共同爬取数据关键就是共享爬取队列. 分布式架构 我将上图进行再次更改 这里重要的就是我的队列通过什么维护? 这里一般我们通过Redis为维护,Redis,非关系型数据库,Key-Value形式存储,结构灵活. 并且r

网络编程 -- RPC实现原理 -- Netty -- 迭代版本V4 -- 粘包拆包

网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2--Netty -- new LengthFieldPrepender(2) : 设置数据包 2 字节的特征码 new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2) :  65535 :数据包长度.0:分隔符偏移值.2:分隔符长度.0:数据包偏移值.2:数据包长度. Class : Server package lime.pri.limeNio.netty.netty04; import j

网络编程 -- RPC实现原理 -- Netty -- 迭代版本V3 -- 编码解码

网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2--Netty -- pipeline.addLast(io.netty.handler.codec.MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN>) 覆写编码解码方法. pipeline相当于拦截器.在pipeline中添加MessageToMessageCodec接口的实现类,该接口的实现类中的encode()方法自动将发送的Object对象转换为ByteBuf,decode()方法自动将

网络编程 -- RPC实现原理 -- Netty -- 迭代版本V2 -- 对象传输

网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2--Netty -- 使用序列化和反序列化在网络上传输对象 只能传输( ByteBuf, FileRegion )两种类型,因此必须将对象在发送之前进行序列化,放进ByteBuf中,客户端接收到ByteBuf时,将字节码取出,反序列化成对象. Class : Server package lime.pri.limeNio.netty.netty02.exercise; import java.net.InetSocketAddress; i

网络编程 -- RPC实现原理 -- RPC -- 迭代版本V2 -- 本地方法调用 整合 Spring

网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2--RPC -- 本地方法调用 + Spring 1. 配置applicationContext.xml文件 注入 bean 及 管理 bean 之间的依赖关系 2. RPCObjectProxy 类 实现 FactoryBean<Object> 接口,通过 public Object getObject() throws Exception 返回代理类 3. List<User> users = userService.qu

网络编程 -- RPC实现原理 -- NIO多线程 -- 迭代版本V2

网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2--增加WriteQueue队列,存放selectionKey.addWriteEventToQueue()添加selectionKey并唤醒阻塞的selector.等selector唤醒之后再注册OP_WRITE事件. ( selectionKey.cancel();清除key对应事件之后,由于多线程 main线程和对应的IO线程会抢夺selector资源. 在selector.select()和sc.register(selection