分布式事务node
指事务的每一个操做步骤都位于不一样的节点上,须要保证事务的 AICD 特性。web
1. 产生缘由算法
数据库分库分表;数据库
SOA 架构,好比一个电商网站将订单业务和库存业务分离出来放到不一样的节点上。浏览器
2. 应用场景缓存
下单:减小库存同时更新订单状态。库存和订单不在不一样一个数据库,所以涉及分布式事务。tomcat
支付:买家帐户扣款同时卖家帐户入帐。买家和卖家帐户信息不在同一个数据库,所以涉及分布式事务。服务器
3. 解决方案网络
3.1 两阶段提交协议架构
两阶段提交协议能够很好得解决分布式事务问题,它可使用 XA 来实现,XA 它包含两个部分:事务管理器和本地资源管理器。其中本地资源管理器每每由数据库实现,好比 Oracle、DB2 这些商业数据库都实现了 XA 接口,而事务管理器做为全局的协调者,负责各个本地资源的提交和回滚。
3.2 消息中间件
消息中间件也可称做消息系统 (MQ),它本质上是一个暂存转发消息的一个中间件。在分布式应用当中,咱们能够把一个业务操做转换成一个消息,好比支付宝的余额转如余额宝操做,支付宝系统执行减小余额操做以后向消息系统发一个消息,余额宝系统订阅这条消息而后进行增长帐户金额操做。
3.2.1 消息处理模型
点对点
.
发布/订阅
.
3.2.2 消息的可靠性
消息的发送端的可靠性:发送端完成操做后必定能将消息成功发送到消息系统。
消息的接收端的可靠性:接收端仅且可以从消息中间件成功消费一次消息。
发送端的可靠性
在本地数据建一张消息表,将消息数据与业务数据保存在同一数据库实例里,这样就能够利用本地数据库的事务机制。事务提交成功后,将消息表中的消息转移到消息中间件,若转移消息成功则删除消息表中的数据,不然继续重传。
接收端的可靠性
保证接收端处理消息的业务逻辑具备幂等性:只要具备幂等性,那么消费多少次消息,最后处理的结果都是同样的。
保证消息具备惟一编号,并使用一张日志表来记录已经消费的消息编号。
负载均衡的算法与实现
1. 算法
1.1 轮询(Round Robin)
轮询算法把每一个请求轮流发送到每一个服务器上。下图中,一共有 6 个客户端产生了 6 个请求,这 6 个请求按 (1, 2, 3, 4, 5, 6) 的顺序发送。最后,(1, 3, 5) 的请求会被发送到服务器 1,(2, 4, 6) 的请求会被发送到服务器 2。
.
该算法比较适合每一个服务器的性能差很少的场景,若是有性能存在差别的状况下,那么性能较差的服务器可能没法承担多大的负载。下图中,服务器 2 的性能比服务器 1 差,那么服务器 2 可能没法承担多大的负载。
.
1.2 加权轮询(Weighted Round Robbin)
加权轮询是在轮询的基础上,根据服务器的性能差别,为服务器赋予必定的权值。例以下图中,服务器 1 被赋予的权值为 5,服务器 2 被赋予的权值为 1,那么 (1, 2, 3, 4, 5) 请求会被发送到服务器 1,(6) 请求会被发送到服务器 2。
.
1.3 最少链接(least Connections)
因为每一个请求的链接时间不同,使用轮询或者加权轮询算法的话,可能会让一台服务器当前链接数多大,而另外一台服务器的链接多小,形成负载不均衡。例以下图中,(1, 3, 5) 请求会被发送到服务器 1,可是 (1, 3) 很快就断开链接,此时只有 (5) 请求链接服务器 1;(2, 4, 6) 请求被发送到服务器 2,它们的链接都尚未断开,继续运行时,服务器 2 会承担多大的负载。
.
最少链接算法就是将请求发送给当前最少链接数的服务器上。例以下图中,服务器 1 当前链接数最小,那么请求 6 就会被发送到服务器 1 上。
.
1.4 加权最小链接(Weighted Least Connection)
在最小链接的基础上,根据服务器的性能为每台服务器分配权重,而后根据权重计算出每台服务器能处理的链接数。
.
1.5 随机算法(Random)
把请求随机发送到服务器上。和轮询算法相似,该算法比较适合服务器性能差很少的场景。
.
2. 实现
2.1 DNS 解析
使用 DNS 做为负载均衡器,会根据负载状况返回不一样服务器的 IP 地址。大型网站基本使用了这种方式最为第一级负载均衡手段,而后在内部在第二级负载均衡。
.
2.2 修改 MAC 地址
使用 LVS(Linux Virtual Server)这种链路层负载均衡器,根据负载状况修改请求的 MAC 地址。
.
2.3 修改 IP 地址
在网络层修改请求的目的 IP 地址。
.
2.4 HTTP 重定向
HTTP 重定向负载均衡服务器收到 HTTP 请求以后会返回服务器的地址,并将该地址写入 HTTP 重定向响应中返回给浏览器,浏览器收到后再次发送请求。
.
2.5 反向代理
正向代理:发生在客户端,是由用户主动发起的。好比FQ,客户端经过主动访问代理服务器,让代理服务器得到须要的外网数据,而后转发回客户端。
反向代理:发生在服务器端,用户不知道发生了代理。
.
分布式锁
Java 提供了两种内置的锁的实现,一种是由 JVM 实现的 synchronized 和 JDK 提供的 Lock,当你的应用是单机或者说单进程应用时,可使用 synchronized 或 Lock 来实现锁。当应用涉及到多机、多进程共同完成时,那么这时候就须要一个全局锁来实现多个进程之间的同步。
1. 使用场景
例如一个应用有手机 APP 端和 Web 端,若是在两个客户端同时进行一项操做时,那么就会致使这项操做重复进行。
2. 实现方式
2.1 数据库分布式锁
基于 MySQL 锁表
该实现方式彻底依靠数据库惟一索引来实现。当想要得到锁时,就向数据库中插入一条记录,释放锁时就删除这条记录。若是记录具备惟一索引,就不会同时插入同一条记录。这种方式存在如下几个问题:
锁没有失效时间,解锁失败会致使死锁,其余线程没法再得到锁。
只能是非阻塞锁,插入失败直接就报错了,没法重试。
不可重入,同一线程在没有释放锁以前没法再得到锁。
采用乐观锁增长版本号
根据版本号来判断更新以前有没有其余线程更新过,若是被更新过,则获取锁失败。
2.2 Redis 分布式锁
基于 SETNX、EXPIRE
使用 SETNX(set if not exist)命令插入一个键值对时,若是 Key 已经存在,那么会返回 False,不然插入成功并返回 True。所以客户端在尝试得到锁时,先使用 SETNX 向 Redis 中插入一个记录,若是返回 True 表示得到锁,返回 False 表示已经有客户端占用锁。
EXPIRE 能够为一个键值对设置一个过时时间,从而避免了死锁的发生。
RedLock 算法
ReadLock 算法使用了多个 Redis 实例来实现分布式锁,这是为了保证在发生单点故障时还可用。
尝试从 N 个相互独立 Redis 实例获取锁,若是一个实例不可用,应该尽快尝试下一个。
计算获取锁消耗的时间,只有当这个时间小于锁的过时时间,而且从大多数(N/2+1)实例上获取了锁,那么就认为锁获取成功了。
若是锁获取失败,会到每一个实例上释放锁。
2.3 Zookeeper 分布式锁
Zookeeper 是一个为分布式应用提供一致性服务的软件,例如配置管理、分布式协同以及命名的中心化等,这些都是分布式系统中很是底层并且是必不可少的基本功能,可是若是本身实现这些功能并且要达到高吞吐、低延迟同时还要保持一致性和可用性,实际上很是困难。
抽象模型
Zookeeper 提供了一种树形结构级的命名空间,/app1/p_1 节点表示它的父节点为 /app1。
.
节点类型
永久节点:不会由于会话结束或者超时而消失;
临时节点:若是会话结束或者超时就会消失;
有序节点:会在节点名的后面加一个数字后缀,而且是有序的,例如生成的有序节点为 /lock/node-0000000000,它的下一个有序节点则为 /lock/node-0000000001,依次类推。
监听器
为一个节点注册监听器,在节点状态发生改变时,会给客户端发送消息。
分布式锁实现
建立一个锁目录 /lock。
在 /lock 下建立临时的且有序的子节点,第一个客户端对应的子节点为 /lock/lock-0000000000,第二个为 /lock/lock-0000000001,以此类推。
客户端获取 /lock 下的子节点列表,判断本身建立的子节点是否为当前子节点列表中序号最小的子节点,若是是则认为得到锁,不然监听本身的前一个子节点,得到子节点的变动通知后重复此步骤直至得到锁;
执行业务代码,完成后,删除对应的子节点。
会话超时
若是一个已经得到锁的会话超时了,由于建立的是临时节点,所以该会话对应的临时节点会被删除,其它会话就能够得到锁了。能够看到,Zookeeper 分布式锁不会出现数据库分布式锁的死锁问题。
羊群效应
在步骤二,一个节点未得到锁,须要监听监听本身的前一个子节点,这是由于若是监听全部的子节点,那么任意一个子节点状态改变,其它全部子节点都会收到通知,而咱们只但愿它的下一个子节点收到通知。
分布式 Session
若是不作任何处理的话,用户将出现频繁登陆的现象,好比集群中存在 A、B 两台服务器,用户在第一次访问网站时,Nginx 经过其负载均衡机制将用户请求转发到 A 服务器,这时 A 服务器就会给用户建立一个 Session。当用户第二次发送请求时,Nginx 将其负载均衡到 B 服务器,而这时候 B 服务器并不存在 Session,因此就会将用户踢到登陆页面。这将大大下降用户体验度,致使用户的流失,这种状况是项目毫不应该出现的。
1. 粘性 Session
原理
粘性 Session 是指将用户锁定到某一个服务器上,好比上面说的例子,用户第一次请求时,负载均衡器将用户的请求转发到了 A 服务器上,若是负载均衡器设置了粘性 Session 的话,那么用户之后的每次请求都会转发到 A 服务器上,至关于把用户和 A 服务器粘到了一块,这就是粘性 Session 机制。
优势
简单,不须要对 Session 作任何处理。
缺点
缺少容错性,若是当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的 Session 信息都将失效。
适用场景
发生故障对客户产生的影响较小;
服务器发生故障是低几率事件。
2. 服务器 Session 复制
原理
任何一个服务器上的 Session 发生改变,该节点会把这个 Session 的全部内容序列化,而后广播给全部其它节点,无论其余服务器需不须要 Session,以此来保证 Session 同步。
优势
可容错,各个服务器间 Session 可以实时响应。
缺点
会对网络负荷形成必定压力,若是 Session 量大的话可能会形成网络堵塞,拖慢服务器性能。
实现方式
设置 Tomcat 的 server.xml 开启 tomcat 集群功能。
在应用里增长信息:通知应用当前处于集群环境中,支持分布式,即在 web.xml 中添加 选项。
3. Session 共享机制
使用分布式缓存方案好比 Memcached、Redis,可是要求 Memcached 或 Redis 必须是集群。
使用 Session 共享也分两种机制,两种状况以下:
3.1 粘性 Session 共享机制
和粘性 Session 同样,一个用户的 Session 会绑定到一个 Tomcat 上。Memcached 只是起到备份做用。
.
3.2 非粘性 Session 共享机制
原理
Tomcat 自己不存储 Session,而是存入 Memcached 中。Memcached 集群构建主从复制架构。
.
优势
可容错,Session 实时响应。
实现方式
用开源的 msm 插件解决 Tomcat 之间的 Session 共享:Memcached_Session_Manager(MSM)
4. Session 持久化到数据库
原理
拿出一个数据库,专门用来存储 Session 信息。保证 Session 的持久化。
优势
服务器出现问题,Session 不会丢失
缺点
若是网站的访问量很大,把 Session 存储到数据库中,会对数据库形成很大压力,还须要增长额外的开销维护数据库。
5. Terracotta 实现 Session 复制
原理
Terracotta 的基本原理是对于集群间共享的数据,当在一个节点发生变化的时候,Terracotta 只把变化的部分发送给 Terracotta 服务器,而后由服务器把它转发给真正须要这个数据的节点。它是服务器 Session 复制的优化。
.
优势
这样对网络的压力就很是小,各个节点也没必要浪费 CPU 时间和内存进行大量的序列化操做。把这种集群间数据共享的机制应用在 Session 同步上,既避免了对数据库的依赖,又能达到负载均衡和灾难恢复的效果。
分库与分表带来的分布式困境与应对之策
.
1. 事务问题
使用分布式事务。
2. 查询问题
使用汇总表。
3. ID 惟一性
使用全局惟一 ID:GUID;
为每一个分片指定一个 ID 范围。