数据分片
分片对数据进行分片的策略,主要有三种:水平分片、垂直分片和混合分片,具体如下图所示。水平分片和垂直分片是通过数据切分的操作方向来区分的,而混合分片 是它们的组合体。
水平分片水平分片有点类似于负载均衡,从流量角度来看,是负载均衡,从数据存储角度来看,是水平分片。
水平分片算法有两个最关键的因素,一是,如何对数据进行划分,即数据划分,二是,分片是 否支持动态分裂与合并,即数据平衡。
数据划分数据划分主要有两种方案,一种是基于模运算,一种是基于范围划分。基于模运算比较简单,不再阐述。而基于范围划分,又分为基于关键词划分和基于关键词的 Hash 值划分两种方式。
这两种分片都是给每一个分片分配一个固定的范围,两者的不同区别在于一个是直接拿关键词进行划分,另一个是利用了关键词的Hash值进行划分。看似区别不大,但是会影响数据的分布。
基于关键词划分基于关键词划分的好处是,分片后数据的分布依然保留了关键词的顺序,我们可以方便地进行区间查询,因为某个关键词区间的数据都是连续存储的。
但是基于关键词划分也会带来问题,即数据分布不均匀和访问的热度不均匀。比如说按照地区进行划分,那么某些省份 ...
Spring的IoC
大致流程1、解析xml文件,将xml中读取到的内容利用ClassPathXmlResource来进行存储。
2、初始化一个工厂,该工厂可以自由选择,比如SimpleBeanFactory或者AutowireCapableBeanFactory,又或者是BeanFactory,取决于场景需要。
3、实例化一个XmlBeanDefinitionReader,该类需要传入第二步生成的工厂,它的主要作用就是解析存储在ClassPathXmlResource中的属性,将它封装为一个BeanDefinition,然后存储在一个map中,map的key为对象的名字,value就是BeanDefinition。用于后续创建bean时,根据名称取到BeanDefinition,然后BeanDefinition中取得属性。
4、到此为止,上面的三步是为了初始化并且存储一些对象的信息,这些信息都来自xml中的配置。之后,调用refresh()来进行具体的创建。
5、在单例模式下,refresh()会先从存放了所有bean实例的map中根据名字取该元素,如果不为空,则直接返回,如果为空,则从毛坯实例中尝试获取 ...
降级
降级上面的熔断,是为了在系统过载时不发生雪崩,限流是为了流量较大时,系统不发生过载。但是这两者都不会区分该服务是核心业务还是非核心业务。而降级,则是为了减少或者停掉一些非核心业务,来确保核心业务收到的影响最小。
为什么需要降级
降级机制能从全局角度对资源进行调配,通过牺牲非核心服务来保障核心服务的稳定性。降级是主动停掉一些非核心业务,而限流则是被动的将一些请求拒绝。
降级可以提高系统的用户体验性和可用性。在一些场景中,如果正常调用出现了非业务层错误后,我们可以不返回错误,而是执行接口的B计划,进行降级,虽然可能和正常流程不太一样,但是比直接返回错误要好。
如何实现降级手动降级手动降级是指在分布式系统中提前设置好降级开关,然后通过类似配置中心的集中式降级平 台,来管理降级开关的配置信息,在系统需要降级的时候,通过降级平台手动启动降级开关, 对系统进行降级处理。
该方案需要注意的是,往往服务有成千上百个,如果全部手动操作,则很麻烦。一个解决办法是:通过对降级分级,利用服务的等级信息和业务信息进行批量降级,比如一次直接把p1,p2,p3的服务全部降级。
自动降级自动降级是指在分布式系统中, ...
限流
如果系统只有熔断机制,当流量激增的时候,就相当于被动的等待熔断机制的触发,此时就需要另外的手段来防止系统负载过高,限流就是一个很好的方案,要主动出击,防止服务挂掉。
为什么需要限流
熔断处理的方式不够优雅。熔断是等到系统过载之后才触发的,即先发生过载,等系统故障后才会介入,让系统恢复。这样的处理方式会导致系统的不必要抖动。
熔断机制是最后的底线。虽然熔断可以解决雪崩问题,但是它应该作为系统稳定性保障的 最后一道防线,正确使用熔断的思路应该是,在其他方法用尽 之后,如果过载问题依旧存在,这时熔断才会被动触发。
在快速失败的时候,需要能考虑调用方的重要程度。熔断是调用方依据响应结果自适应来触发的,在被调用方出现过载的时候,所有的调用方都将受到影响。但是不同接口的重要程度不一样,需要保证有些接口优先处理。
在多租户的情况下,不能让一个租户的问题影响到其他的租户,我们需要对每一个租户分配一定的配额,谁超过了就对谁进行限流,保证租户之间的隔离性。
如何实现限流限流一般有固定的限流算法,有以下几种:
固定窗口和滑动窗口固定窗口就是定义一个“固定”的统计周期,比如 10 秒、30 秒或者 1 分钟 ...
服务熔断
熔断熔断机制:当服务之间发起调用的时候,如果被调用方返回的 指定错误码的比例超过一定的阈值,那么后续的请求将不会真正发起,而是由调用方直接返回错误。
首先是闭合状态,此时可以处理请求,但是需要一个计数器,来统计调用失败的次数,如果失败的次数达到阈值,则将状态改为闭合。
在闭合状态下,可以直接拒绝后续的请求,也可以对请求做一个降级(后续介绍)。此时会启动一个超时计时器,当计时器超时后,会转变为半打开状态。
在半打开状态下,允许一定数量的请求发往被调用的服务,如果这些调用正常,则就可以认为被调用服务已经恢复正常,此时熔断器切换为闭合状态,同时重置计数器。如果仍有部分调用失败的情况,则认为被调用方仍然没有恢复,熔断器会切换到断开状态,然后重置计数器。半打开状态是为了防止恢复中的服务被大量请求再次打垮的情况。
熔断的关键点有以下五个关键点:粒度控制、错误类型、存活与过载的区别、重试和熔断的关系和熔断机制的适应范围。
粒度控制该问题是指我们想将监控资源过载的粒度控制在一个什么样的范围内,这个范围可以由服务、实例和接口这三个维度的组合来得到。
建议使用基于实例接口的熔断,这样的粒度最小, ...
路由选择算法
路由选择算法是为了选出从一个点发出的数据报,该如何经过各个路由器,以最小的成本或最快的速度到达目的ip的一种算法,可以理解为图中的最短路径问题。
一般而言,路由选择算法的一种分类方式是根据该算法是集中式还是分散式来划分。
集中式还是分散式集中式路由选择算法该算法以所有节点的连通性以及所有链路的开销为输入,这就要求算法在开始之前获得这些信息。该算法可以在一个集中的控制器或者在每台路由器的路由选择组件中重复进行。
集中式算法具有关于连通性和链路开销方面的完整信息。具有全局状态信息的算法常被称作链路状态(Link State, LS)算法, 因为该算法必须知道网络中每条链路的开销。
分散式路由选择算法该算法中,路由器以迭代、分布式的方式计算出最低开销路径。没有节点拥有关于所有网络链路开销的完整信息。 相反,每个节点仅有与其直接相连链路的开销知识即可开始工作。
然后,通过迭代计算过程以及与相邻节点的信息交换,一个节点逐渐计算出到达某目的节点或一组目的节点的最低开销路径。
静态的还是动态路由选择算法的第二种分类是基于算法是静态的还是动态的进行分类。
在静态路由选择算法(static routin ...
分布式场景下的CAP理论
CAP 理论CAP理论是关于数据一致性( C:Consistency )、服务可用性( A:Availability )、分区容错性( P:Partition-tolerance )。
CAP 理论告诉我们,一个分布式系统不可能同时满足数据一致性、服务可用性和分区容错性 这三个基本需求,最多只能同时满足其中的两个。
一致性( C )这里的一致性是指强一致性,又叫线性一致性,它要求多节点组成的分布式系统,能像单节点一样运作,如果一个写操作返回成功,那么之后的读请求都必须读到这个新数据;如果返回失败,那么所有的读操作都不能读到这个数据。
一致性中除了强一致性之外,还有其他的一致性级别,比如序列一致性( Sequential Consistency )和最终一致性( Eventual Consistency )等。
可用性( A )可用性指的是要求系统提供的服务必须处于 100% 可用的状态,对于用 户的每一个操作请求,系统总能够在有限的时间内返回结果。
分区容错性( P )分区指的是在整个分布式系统中,因为网络原因,系统被分隔成多个单独的部分,这里,不同系统之间在正常情况下应该是一个整体, ...
分布式锁
分布式锁锁存在的意义是为了保证在多CPU,多个线程的环境中,某一个时间点上,只能由一个线程进入临界区代码,从而保证临界区操作数据的一致性。
进程内的锁,是操作系统直接提供的,对于同一台机器上的多进程,可以直接通过操作系统的锁来实现,只不过是协调了多个进程,需要将锁放在所有进程都可以访问的共享内存中,所有进程通过共享内存中的 锁来进行加锁和解锁。
但是分布式是在不同机器上,通过操作系统的锁已经无法实现。
怎么实现分布式锁实现分布式锁,需要满足以下几个特性:
互斥:保证不同节点、不同线程的互斥访问。
超时机制:即超时设置,防止死锁。因为锁服务和请求锁的服务分散在不同的机器上面,它们之间是通过网络来通信 的,所以我们需要用超时机制,来避免获得锁的节点故障或者网络异常,导致它持有的锁不能 归还,出现死锁的情况。
同时还要确保留有线程不断延长锁的时间,防止事务还没处理完,而时间到了,导致释放了锁。
完备的锁接口:比如说lock接口和trylock接口等。
可重入性:即一个节点的一个线程已经获取了锁,那么该节点持有锁的这个线程 可以再次成功获取锁。我们在加锁时,记录好当前获取锁的节点+线程 ...
负载均衡
负载均衡在分布式的环境下,单个模块往往会部署多个,当调用方通过注册发现组件获得了被调用方的网络地址时,会获得多个地址,这时候就要通过负载均衡组件来确定是调用哪一个具体的组件。
我们可以根据负载均衡策略是否关心请求中携带的信息,即请求是否有状态,将负载均衡策略分为无状态的负载均衡、半状态的负 载均衡和全状态的负载均衡。
负载均衡的关键点负载均衡需要考虑到各个实例性能差异的情况, 让每一个实例都能充分发挥它的能力,不要出现一些实例负载比较高,而另一些实例的负载却 非常低的情况,这样会造成资源浪费。所以负载均衡的第一个关键点是公平性。即要关注被调用服务组之间的公平性,不能旱的旱死,涝的涝死。
负载均衡需要确保外部对后端服务的请求,一定能被路由到可以提供正确服务的实例上。如果后端是有状态的,那么我们就要考虑在请求上携带状态信息,然后根据状态将请求发送到对应的路由上,所以第二个关键点是正确性,即对于有状态的服务来说,负载均 衡需要关心请求的状态,将请求调度到能处理它的后端实例上,不要出现不能处理和错误处理 的情况。
无状态的负载均衡无状态的负载均衡指的是所有后端实例都是对等的,不管请求发送到哪个 ...
路由器结构
结构路由器结构
路由器由输入端口,交换结构,路由选择处理器以及输出端口组成。输入端口负责接收数据报,并且在这里决定好要怎么进行转发,然后经过交换结构,到达输出端口。即在输入端口这里已经找到了他的下一跳的地址,然后传送给交换结构,交换结构只需要根据数据去找对应的输出端口即可,可以大大减小路由表的大小。
而具体怎么找到该送往那个端口,最简单的是做一个map,包含了所有的映射,但是由于映射数量达到上百亿,不可取。另一种方案是:根据最长前缀匹配,比如说0000发网接口0,0001发网接口1,以此类推。
这里,输入端口查找的是数据报目的的ip,可能需要经过多个路由器来进行转发,才能到达目地地址。
交换交换技术有三种,经内存的交换,经总线交换,互联网络交换。
经内存的交换这种方案下,输入端口和输出端口的功能有点像传统操作系统中的I/O,当一个分组到达端口,改端口通过中断向路由选择处理器发送信号,然后将分组复制到处理器内存当中,路由选择器从分组的首部提取到目的地址,在转发表找适当的输出端口,再将该分组复制到输出端口的缓存当中。
经总线交换这种方案下,输入端口经一根总线将分组直接发送到输出 ...