解决什么问题?
首先来分析下业务对象,才能对解决的问题进行归纳和总结。它解决的事分布式应用的问题,那么分布式应用会存在哪里问题是由它的业务特性来决定的,这些问题已是为了解决业务的问题。分布式的业务特征有哪些?
分布式业务的特征
存在的意义(高并发低延迟)
分布式这个东西存在的意义是什么?分布式解决什么问题?为什么有分布式这个东西的出现?分布式不是一开始就有的,已不是所有的应用都适合用分布式的方式;
分布式是为了解决业务高并发低延迟;当传统的单体应用不能承载特别高的并发和低延迟的时候,这个才需要把系统拆分为多个,把并发分摊到多个实例上进行承载,减轻单实例的压力,从而减少延迟,满足业务的需求。
分布式是为了解决在大项目开发过程中开发的协作;当一个系统涉及特别多的功能、复杂的流程和特别多的开发人员时,就需要把一个系统拆分为多个系统进行功能、流程的拆分,同时已能让多个开发人员能够并行的进行项目开发。
分布式是为了提高业务的连续性,做容灾多活的;当之前的单体系统在一个地方,会由于单体系统自身,和由于系统所在操纵系统物理硬件数据中心城市等基础环境,的影响,系统出现问题而导致业务无法使用。所以要把一个单体系统拆分为多个,或者把一个单体系统重复多个,部署在多个环节中,而这多个其实是完成一个业务的,所以就是分布式。
总的来说就是业务是一个,但是系统和应用确实多个在多个环境中,来实现整体的业务目标(高并发低延迟),流程目标(多团体并行开发),管理目标(业务连续性)
沟通方式(通过网络通信)
分布式业务是一个业务系统由多个子模块或者系统,通过网络的方式通信的(相较于一个主机上多个进程是通过系统的管道和共享内存来实现通信),来实现整体目标的。网络通信的方式有http和tcp,ip直连都可以。
业务痛点
那么在这些特质下会遇到什么问题?把一个单独系统拆分为多个模块和子系统、或者多个相同实例组成一个大集群后,"多个"就会存在相互之间的通信,协作;这就跟多个人多个团体一起协作完成一件事情一样,会遇到沟通方式的落后,沟通信息的不准确,重复劳动,没有统一标准,请假,做错,冲突,进度延期等等问题。(误解,偏见,懒惰,偷奸耍滑,等情绪问题可能已有的)。
从这些业务痛点总结出来主要问题是
第一:多个应用和系统的信息的不一致的准确,没有统一的标准,这就是分布式的数据一致性问题。
第二:多个应用和系统沟通方式的落后,重复劳动,冲突,请假等等,这就是分布式的工作协作问题。
解决分布式的数据一致性问题
解决分布式应用的协作和数据一致性。协作就是多个应用同时对一个数据对象进行读写操作。这里的知识对象是数据。
场景举例
1.使用相同的配置、配置中心。分布式应用中使用一套配置,通过ZK实现对配置的读写,不冲突。
2.使用统一的命名空间、各个分布式应用维护自己的名字并在整个系统中唯一,已可以叫做服务发现。
3.作为分布式状态锁,多个应用访问一个资源对象的时候,通过zk实现这个对象的分布式锁。
解决分布式的协作问题
解决分布式应用中,每个应用之间的相互协作,
场景举例
1.大部分场景都是集群管理
怎么解决
zookeeper的两大作用
顺序性
顺序性,多个应用对一个数据的操作,要按照动作发出的顺序对数据进行操作,动作发出的顺序,和数据被修改的顺序应该是一样的。
这里的知识对象是zookeeper中的fifo的队列和zab协议,这两个对象都能保证顺序性。
举例:
一个应用向zookeeper的主节点写数据(会把写转换为事务,类似数据库中的sql)(同时会生成一个zxid,这个zxid是按照事务的顺序递增的),和从节点从主节点拿到的要写数据的事务,都会存入当前节点的一个fifo队列中,这个fifo队列就是先进先出,这个fifo就是保障所有执行的数据写操作(在所有节点上执行这些写操作),都是按照之前写发起时的顺序执行的。同时前面说的zxid已能保障从故障恢复后、事务执行已可以按照之前发起的顺序执行。
原子性
原子性,一个应用将数据给zookeeper写入时,要么是集群整个更新成功,要么就是整个更新失败,把整个集群做成了原子性更新。其实这个应该在zookeeper的集群高可用容灾的特性之后说。(因为要集群,所以才会出现要做原子性。没有集群其实就不需要原子性)
这里的知识对象是存数据的znode多副本。一个应用向主节点写入时,是通过将写入数据这个行为转换为一个事务,最后通过这个事务来更新znode中的数据。(就是数据库样,写数据,通过sql事务,最终是更新到数据表里面),多个副本的更新要么都成功,要么都失败。这就是实现原子性。
举例:
应用写数据到主节点(只有主节点接受写入数据,其他从节点都是从主同步),主节点会生成一个带zxid的事务,这个事务会写到事务日志中,这个事务日志一开始是在系统内存中,然后将事务日志写入fifo中,开始广播给其他从节点(这里系统内存中的事务日志数据,异步{跟写入fifo和广播 相比}刷新到磁盘中。从节点接受到事务会将事务计入自己的事务日志,已是先写入内存中(异步下刷到磁盘),然后把事务写入fifo队列后,就给主节点发送ack成功。所有过半的成才是事务提交成功,才开始在主节点中,把事务日志刷新到内存znode中(类似数据库sql提交到数据表中)
多活容灾
znode在每个从节点上都有副本,当主down后,从节点能够承担起主节点能力,又能保障每个副本的数据一致性。
例子就是整个串起来:
- 主从节点都有zonde数据,从节点zonde数据是主节点的副本。
- 主节点收到写请求,是把事务(类似SQL日志BINLOG)写到系统内存(事务并不是提交状态,丢了已没关系),通过ZAB协议完成事务的提交(这个事务提交状态只和ZAB协议有关,事务日志刷盘是异步的,当主节点坏了是不是提交的事务就丢了?{因为在内存中还没来得及刷到磁盘}{其实这个时候主坏了,就会有从节点来接,从节点内存中有提交的事务日志,完成异步的刷盘}),提交的事务的事务日志肯定在磁盘中。
- 主节点和从节点都有FIFO队列来维护事务。主节点将zxid和事务关联后写入本节点的FIFO队列,并异步广播给其他从节点。其他从节点已会将zxid和事务关联写入本节点的FIFO队列。保证了事务的顺序执行。
- 主节点中内znode数据的刷新和保存。内存ZNODE的数据刷新是在事务提交后,事务提交后才是把数据刷新到内存中zonde数据。
- 主节点生成内存znode数据快照 是两个时间点(周期性,当事务达到一定量),生成快照前把内存中的znode刷到磁盘,生成新的快照。(这个时候会暂停所有写操作)
- 从节点收到事务消息后写入本地的FIFO队列后,就立刻给主节点返回ACK事务成功(但不需要刷事务日志到磁盘之后,不需要把事务刷到内存znode之后)。
- 从节点内存znode数据更,从节点收到事务消息后写入本地的FIFO队列后,然后根据fifo顺序来更新内存中zonde数据。更新内存Znode数据 是在 给主节点发送ACK成功消息之前就 必须完成。
- 从节点内存znode数据不会快照。只有主节点会对内存中znode数据进行快照,从节点不会对内存中znode数据进行快照,而是使用从主节点同步过来的"数据快照".
- 主从同步znode数据快照和增量事务日志,是周期性的。两个时间点生成zonde快照和增量事务(相对上一次快照时的事务增量,周期性和事务操作达到一定量时,会同步znode数据快照和增量事务给从节点。)
- 从节点恢复数据,是通过从主节点同步过来的znode快照。
- 主从同步事务日志,写主时,会把事务日志同步到从节点,不会对zonde打快照,已不会同步znode的快照给从。
ZAB协议。
zxid是64位两部分组成,高32由纪元(主节点任期)低32由事务数 组成。
1.广播阶段
主收到写请求,主节点新建一个zxid,高32位当前主节点的任期,低32当前任期中处理的第几个事务。然后把这个zxid关联到当前的事务(把写请求转换为一个事务,例如数据库的SQL),写入当前节点的FIFO队列,并广播给其他从节点。从节点接受到事务后会写入本地的FIFO队列,然后通就直接返回给主节点ACK成功。然后异步的通过FIFO把事务应用到内存ZNODE数据上。
2.故障恢复
当主节点失败后,其他从节点开始选举主节点,通过比较自己的ZXID,谁的ZXID最大,就是谁来当主节点,这个相当于谁拥有最新的事务日志。