CAP理论,BASE理论
- Consistency (一致性):
即更新操作成功并返回客户端后,所有节点在同一时间的数据完全一致。对于客户端来说,一致性指的是并发访问时更新过的数据如何获取的问题。从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终一致。
- Availability (可用性):
即服务一直可用,而且是正常响应时间。系统能够很好的为用户服务,不出现用户操作失败或者访问超 时等用户体验不好的情况。
- Partition Tolerance (分区容错性):
即分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。分 区容错性要求能够使应用虽然是一个分布式系统,而看上去却好像是在一个可以运转正常的整体。比如 现在的分布式系统中有某一个或者几个机器宕掉了,其他剩下的机器还能够正常运转满足系统需求,对 于用户而言并没有什么体验上的影响。CP和AP:分区容错是必须保证的,当发生网络分区的时候,如果要继续服务,那么强一致性和可用性 只能 2 选 1 BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致 性) BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以 根据自身业务特点,采用适当的方式来使系统达到最终一致性。基本可用:
- 响应时间上的损失: 正常情况下,处理用户请求需要 0.5s 返回结果,但是由于系统出现故障,处理 用户请求的时间变为 3 s。
- 系统功能上的损失:正常情况下,用户可以使用系统的全部功能,但是由于系统访问量突然剧增, 系统的部分非核心功能无法使用。
软状态:数据同步允许一定的延迟 最终一致性:系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态,不要 求实时
负载均衡算法、类型
算法:
- 轮询法 将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的 连接数和当前的系统负载。
- 随机法 通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。由概率统 计理论可以得知,随着客户端调用服务端的次数增多, 其实际效果越来越接近于平均分配调用量到后端的每一台服务器,也就是轮询的结果。
- 源地址哈希法 源地址哈希的思想是根据获取客户端的IP地址,通过哈希函数计算得到的一个数值,用该数值对服务器 列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。采用源地址哈希法进行负载均 衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。
- 加权轮询法 不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置 高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权 重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。
- 加权随机法 与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是 按照权重随机请求后端服务器,而非顺序。
- 最小连接数法 最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根 据后端服务器当前的连接情况,动态地选取其中当前 积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分 流到每一台服务器。
类型 :
- DNS 方式实现负载均衡
- 硬件负载均衡:F5 和 A10
- 软件负载均衡:Nginx 、 HAproxy 、 LVS 。其中的区别:
- Nginx :七层负载均衡,支持 HTTP、E-mail 协议,同时也支持 4 层负载均衡;
- HAproxy :支持七层规则的,性能也很不错。OpenStack 默认使用的负载均衡软件就是 HAproxy;
- LVS :运行在内核态,性能是软件负载均衡中最高的,严格来说工作在三层,所以更通用一些, 适用各种应用服务。
分布式架构下,Session 共享有什么方案
- 采用无状态服务,抛弃session
- 存入cookie(有安全风险)
- 服务器之间进行 Session 同步,这样可以保证每个服务器上都有全部的 Session 信息,不过当服务 器数量比较多的时候,同步是会有延迟甚至同步失败;
- IP 绑定策略 使用 Nginx (或其他复杂均衡软硬件)中的 IP 绑定策略,同一个 IP 只能在指定的同一个机器访问,但 是这样做失去了负载均衡的意义,当挂掉一台服务器的时候,会影响一批用户的使用,风险很大;
- 使用 Redis 存储 把 Session 放到 Redis 中存储,虽然架构上变得复杂,并且需要多访问一次 Redis ,但是这种方案带来 的好处也是很大的:实现了 Session 共享;可以水平扩展(增加 Redis 服务器);服务器重启 Session 不丢失(不过也要注意 Session 在 Redis 中的刷新/失效机制);不仅可以跨服务器 Session 共享,甚至可以跨平台(例如网页端和 APP 端)。
简述你对RPC、RMI的理解
- RPC:在本地调用远程的函数,远程过程调用,可以跨语言实现 httpClient
- RMI:远程方法调用,Java中用于实现RPC的一种机制,RPC的Java版本,是J2EE的网络调用机制,跨 JVM调用对象的方法,面向对象的思维方式 直接或间接实现接口 java.rmi.Remote 成为存在于服务器端的远程对象,供客户端访问并提供一定的 服务 远程对象必须实现java.rmi.server.UniCastRemoteObject类,这样才能保证客户端访问获得远程对象 时,该远程对象将会把自身的一个拷贝以Socket的形式传输给客户端,此时客户端所获得的这个拷贝称 为“存根”,而服务器端本身已存在的远程对象则称之为“骨架”。其实此时的存根是客户端的一个代理, 用于与服务器端的通信,而骨架也可认为是服务器端的一个代理,用于接收客户端的请求之后调用远程 方法来响应客户端的请求。
代碼如下:
代码语言:javascript复制
public interface IService extends Remote {
String service(String content) throws RemoteException;
}
public class ServiceImpl extends UnicastRemoteObject implements IService {
private String name;
public ServiceImpl(String name) throws RemoteException {
this.name = name;
}
@Override
public String service(String content) {
return "server >> " content;
}
}
public class Server {
public static void main(String[] args) {
try {
IService service02 = new ServiceImpl("service02");
Context namingContext = new InitialContext();
namingContext.rebind("rmi://127.0.0.1/service02", service02);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("000000!");
}
}
public class Client {
public static void main(String[] args) {
String url = "rmi://127.0.0.1/";
try {
Context namingContext = new InitialContext();
IService service02 = (IService) namingContext.lookup(url
"service02");
Class stubClass = service02.getClass();
System.out.println(service02 " is " stubClass.getName());
//com.sun.proxy.$Proxy0
Class[] interfaces = stubClass.getInterfaces();
for (Class c : interfaces) {
System.out.println("implement" c.getName() " interface");
}
System.out.println(service02.service("hello"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
分布式id生成方案
- uuid
- 当前日期和时间 时间戳
- 时钟序列。计数器
- 全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得。
优点:代码简单,性能好(本地生成,没有网络消耗),保证唯一(相对而言,重复概率极低可以忽 略)
缺点:每次生成的ID都是无序的,而且不是全数字,且无法保证趋势递增。UUID生成的是字符串,字符串存储性能差,查询效率慢,写的时候由于不能产生顺序的append 操作,需要进行insert操作,导致频繁的页分裂,这种操作在记录占用空间比较大的情况下,性 能下降比较大,还会增加读 取磁盘次数** UUID长度过长,不适用于存储,耗费数据库性能。 ID无一定业务含义,可读性差。 有信息安全问题,有可能泄露mac地址 **
- 数据库自增序列
单机模式:优点:
- 实现简单,依靠数据库即可,成本小。
- ID数字化,单调自增,满足数据库存储和查询性能。
- 具有一定的业务可读性。(结合业务code)
缺点:
- 强依赖DB,存在单点问题,如果数据库宕机,则业务不可用。
- DB生成ID性能有限,单点数据库压力大,无法扛高并发场景。
- 信息安全问题,比如暴露订单量,url查询改一下id查到别人的订单
数据库高可用:多主模式做负载,基于序列的起始值和步长设置,不同的初始值,相同的步长,步长大 于节点数
优点:解决了ID生成的单点问题,同时平衡了负载。缺点:
- 系统扩容困难:系统定义好步长之后,增加机器之后调整步长困难。
- 数据库压力大:每次获取一个ID都必须读写一次数据库。
- 主从同步的时候:电商下单->支付insert master db select数据 ,因为数据同步延迟导致 查不到这个数 据。加cache(不是最好的解决方式)数据要求比较严谨的话查master主库。
- Leaf-segment
采用每次获取一个ID区间段的方式来解决,区间段用完之后再去数据库获取新的号段,这样一来可以大 大减轻数据库的压力 核心字段:biz_tag,max_id,step biz_tag用来区分业务, max_id表示该biz_tag目前所被分配的ID号段的最大值, step表示每次分配 的号段长度,原来每次获取ID都要访问数据库,现在只需要把Step设置的足够合理如1000,那么现在 可以在1000个ID用完之后再去访问数据库 优点:
- 扩张灵活,性能强能够撑起大部分业务场景。
- ID号码是趋势递增的,满足数据库存储和查询性能要求。
- 可用性高,即使ID生成服务器不可用,也能够使得业务在短时间内可用,为排查问题争取时间。
缺点:可能存在多个节点同时请求ID区间的情况,依赖DB
双buffer:将获取一个号段的方式优化成获取两个号段,在一个号段用完之后不用立马去更新号段,还 有一个缓存号段备用,这样能够有效解决这种冲突问题,而且采用双buffer的方式,在当前号段消耗了 10%的时候就去检查下一个号段有没有准备好,如果没有准备好就去更新下一个号段,当当前号段用完了 就切换到下一个已经缓存好的号段去使用,同时在下一个号段消耗到10%的时候,又去检测下一个号段有 没有准备好,如此往复。优点:基于JVM存储双buffer的号段,减少了数据库查询,减少了网络依赖,效率更高。缺点:segment号段长度是固定的,业务量大时可能会频繁更新号段,因为原本分配的号段会一下用完 如果号段长度设置的过长,但凡缓存中有号段没有消耗完,其他节点重新获取的号段与之前相比可 能跨度会很大。动态调整Step
- 基于redis、mongodb、zk等中间件生成
- 雪花算法
生成一个64bit的整性数字 第一位符号位固定为0,41位时间戳,10位workId,12位序列号 位数可以有不同实现 优点:
- 每个毫秒值包含的ID值很多,不够可以变动位数来增加,性能佳(依赖workId的实现)。
- 时间戳值在高位,中间是固定的机器码,自增的序列在低位,整个ID是趋势递增的。
- 能够根据业务场景数据库节点布置灵活挑战bit位划分,灵活度高。
缺点:强依赖于机器时钟,如果时钟回拨,会导致重复的ID生成,所以一般基于此的算法发现时钟回拨, 都会抛异常处理,阻止ID生成,这可能导致服务不可用。