多个子服务间远程调用协调工作
中心化 (k8s)
去中心化 有领导 只是由节点自由选取(zookeeper) 脑裂:由于网络原因 形成了两个单独的集群各自工作,严重数据冲突 解决办法:小的集群全员自杀
CAP 一致性 可用性(不保证是最新数据) 分区容错性(某网络分区出错时,仍提供一致性可用性服务,除非整个网络坏了) P一定要 3个不能同时满足
不是3选2 CA可以互相妥协 ----》 base理论 基本可用(查询时间0.5-》2或降级页面) 软状态 最终一致性
Ab两个数据库 一样的数据
A更新了数据 未同步到b a死了 查询请求来了b 1.返回老数据 可用性 2.等a好了后同步a再返回新数据 一致性
//
C++11:
auto 类型推导
using 别名=原名
for(auto i:a)
函数绑定:有个库提供了这种模板的,类似于 template
class function{}
function
cout<
其实调用了f1.operator()(10);然后在这个函数里面调用b
lambda表达式 局部作用域中定义的匿名函数
cout<<[a,b]{return a cout<<[](int x,int y)->int{return x
[=] [&] 引用捕获和值捕获
int a=c+d; 分配了个只读属性的临时变量存c+d的值 生命周期以分号结束
int const& s=a+b; 有引用的话 其生命周期会延长
右值引用 int &&e=a+c; 但++e仍然正确 ,因为右值引用本身是个左值
int(a) 是右值 它是把a转换为int后存在了一个临时空间的
左值引用只引用左值 右值引用只引用右值
move() 把左值变为右值
string(string &&str):m_str(str.m_str){str.m_str=NULL;}
string(string const& str):m_str(strcpy(new char[strlen(str.m_str)+1],str.m_str)){}
string& opreator=(sting const & str)
{
{string tmp=str;swap(m_str,tmp.s_str);}
return *this;
}
string& opreator=(sting && str)
{
{string tmp=move(str);swap(m_str,tmp.s_str);}
return *this;
}
移动构造:
return时把返回值给临时变量(将亡右值),临时变量在main()中再传到相应的变量,很麻烦,还不如直接让渡资源
unordered_map
/
Redolog 物理层(记录对某个数据页哪个地方修改)随时记 用于非同步至硬盘前宕机重做 缓存最热的数据或者索引页减少io(dirty page)bufferpool<-数据页
Binlog 逻辑层 对某表哪一行修改 提交时记
协调者 参与者 事务是否真正提交
2pc 1参与者执行事务内容 协等响应 YES NO 超时 2提交 成功返回ack 存在问题 同步阻塞 单点问题 数据不一致(部分参与者已提交 协死或者参死) 过于保守
3pc 多个第一阶段 询问参与者是否可以提交该事务(事务已经发过去了) 2pc没有参与者超时功能 2阶段完后 3阶段参与者如果超时自动提交
Paxos 多个节点间确定一个值 client1 set x=3 client2 set x=2 都提交一个值 abc三个服务端如何就一个值达成共识 最终只确定一个 提议者 接受者(abc 一般是master) 学习者(slave)
1发提案号AA 之前未通过任何提案 返回无提案 若之前通过 返回已通过的最大提案号的提案信息 承诺之后不再响应<=AA的1过程 不再通过 2客户端在收到大多数响应后 发送提案值(若返回无提案 就发自己的值 否则 发1中返回的所有的响应中最大提案编号对应的提案值作为本次提交的提案值 提案编号还是自己的)
若失败 即响应<=一半 重新生成个提案号重提
存在活锁问题
Raft算法 要选leader:leader不断向追随者发消息说我还活着 追随者发现leader死后 毛遂自荐 任期编号+1 让其他人投 若得到大多数选票就成功 省去了paxos的1过程,因为client直接找领导 (最开始没有leader 都有连接leader的超时时间(随机的) 谁的先过期谁先毛遂自荐) 先来先服务的原则进行投票
Leader负责日志复制管理 日志复制为保证每个节点执行相同的操作序列
接收client请求(即日志) 并复制给其他节点,通知他们提交日志
当Leader收到大多数(n/2+1)Follower的ACK信息后将该日志设置为已提交并追加到本地磁盘中,通知客户端,并在下个heartbeat中Leader将通知所有的Follower将该日志存储在自己的本地磁盘中
安全性是用于保证每个节点都执行相同序列的安全机制,如当某个Follower在当前Leader commit Log时变得不可用了,稍后可能该Follower又会被选举为Leader,这时新Leader可能会用新的Log覆盖先前已committed的Log,这就导致节点执行不同序列;Safety(安全性)就是用于保证选举出来的Leader一定包含先前commited Log的机制
Kafka 消息队列 有一对一 一对多模式(注意发布到Topic中的消息会被多个消费者消费,消费者消费数据之后,数据不会被清除,Kafka会默认保留一段时间,然后再删除) 我们下面说的全是一对一模式
生产者、Kafka集群Broker、消费者、注册消息Zookeeper、消费者组(内含多个消费者 可消费topic下不同分区的消息 某一个分区中的消息只能够一个消费者组中的一个消费者所消费) 一个非常大的Topic可以分布到多个Broker上,一个Topic可以分为多个Partition 每个分区是一个有序的队列(分区有序,不能保证全局有序) 一个Topic的每个分区有若干个副本,一个Leader和多个Follower 以Partition为单位进行读写,类似于多路。
Producer生产的数据会被不断的追加到该log文件的末端,且每条数据都有自己的offset,consumer组中的每个consumer,都会实时记录自己消费到了哪个offset,以便出错恢复的时候,可以从上次的位置继续消费
一个topic有多个分区 每个分区有多个segment 以1g为分割点 一个segment对应一个.LOG和.INDEX文件
.LOG是实际内容 .index是一个映射 offset -》 log文件中多少行 每个.index的offset从0开始,偏移=offset+当前.INDEX文件的名字
消息的内容是 key和value
生产者ISR
为保证producer发送的数据能够可靠的发送到指定的topic中,topic的每个partition收到producer发送的数据后,都需要向producer发送ack,如果producer收到ack就会进行下一轮的发送,否则重新发送数据。
kafka是全部的follower同步完成,才可以发送ack
但如果有个follower故障咋搞 迟迟不返回 用ISR 如果follower长时间没有向leader同步数据,则该follower将从ISR中被踢出。当leader发生故障之后,会从ISR中选举出新的leader。
Kafka为用户提供了三种可靠性级别,用户根据可靠性和延迟的要求进行权衡选择不同的配置。
1.broker接收到还没有写入磁盘就已经返回 会数据丢失 At Most Once
2.partition的leader落盘成功后返回ack 会数据丢失
3.ISR的follower全部落盘成功才返回ack 会数据重复 At Least Once
对于重要的数据,则要求数据不重复也不丢失,即Exactly Once即精确的一次。
在0.11版本的Kafka之前,只能保证数据不丢失,在下游对数据的重复进行去重操作,多余多个下游应用的情况,则分别进行全局去重,对性能有很大影响。
0.11版本的kafka,引入了一项重大特性:幂等性,幂等性指代Producer不论向Server发送了多少次重复数据,Server端都只会持久化一条数据。
Kafka的幂等性实现实际是将之前的去重操作放在了数据上游来做,开启幂等性的Producer在初始化的时候会被分配一个PID,发往同一个Partition的消息会附带Sequence Number,而Broker端会对
但PID在重启之后会发生变化,同时不同的Partition也具有不同的主键,所以幂等性无法保证跨分区跨会话的Exactly Once。
LEO:每个副本最后的一个offset
HW:指代消费者能见到的最大的offset,ISR队列中最小的LEO。
follower故障:follower发生故障后会被临时提出ISR,等待该follower恢复后,follower会读取本地磁盘记录的上次的HW,并将log文件高于HW的部分截取掉,从HW开始向leader进行同步,等待该follower的LEO大于等于该partition的HW,即follower追上leader之后,就可以重新加入ISR了
leader故障:leader发生故障之后,会从ISR中选出一个新的leader,为了保证多个副本之间的数据的一致性,其余的follower会先将各自的log文件高于HW的部分截掉,然后从新的leader中同步数据。
这只能保证副本之间的数据一致性,并不能保证数据不丢失或者不重复
消费者的分区策略 round-robin range
一个消费者组:
假设存在三个topic:t0/t1/t2,分别拥有1/2/3个分区,共有6个分区,分别为t0-0/t1-0/t1-1/t2-0/t2-1/t2-2,这里假设我们有三个Consumer,C0、C1、C2,订阅情况为C0:t0,C1:t0、t1,C2:t0/t1/t2。循环遍历,遇到自己订阅的则消费,否则向下轮询下一个消费者 range不用知道 不用学那么细
由于Consumer在消费过程中可能会出现断电宕机等故障,Consumer恢复以后,需要从故障前的位置继续消费,所以Consumer需要实时记录自己消费到了那个offset,以便故障恢复后继续消费。
Kafka0.9版本之前,consumer默认将offset保存在zookeeper中,从0.9版本之后,consumer默认将offset保存在kafka一个内置的topic中,该topic为__consumer_offsets
如果所有实列属于同一个Group,那么它实现的就是消息队列模型;如果所有实列分别属于不同的Group,那么他实现的就是发布/订阅模型,就是前面说的一对多模式。
顺序写磁盘
零复制技术 log的文件缓存区->socket缓冲区
Kafka集群中有一个broker会被选举为Controller,负责管理集群broker的上下线、所有topic的分区副本分配和leader的选举等工作。Controller的工作管理是依赖于zookeeper的。
需要引入一个全局唯一的Transaction ID,并将Producer获得的PID和Transaction ID进行绑定,这样当Producer重启之后就可以通过正在进行的Transaction ID获得原来的PID。
为了管理Transaction,Kafka引入了一个新的组件Transaction Coordinator,Producer就是通过有和Transaction Coordinator交互获得Transaction ID对应的任务状态,Transaction Coordinator还负责将事务信息写入内部的一个Topic中,这样即使整个服务重启,由于事务状态得到保存,进行中的事务状态可以恢复,从而继续进行。
Kafka的Producer发送消息采用的是异步发送的方式,在消息发送的过程中,设计到了两个线程main线程和Sender线程,以及一个线程共享变量RecordAccumulator,main线程将消息发送给RecordAccumulator,Sender线程不断从RecordAccumulator中拉取消息发送到Kafka broker中。
BATCH_SIZE_ConFIG = “batch.size”:消息为batch.size大小,生产者才发送消息
LINGER_MS_ConFIG = “linger.ms”:如果消息大小迟迟不为batch.size大小,则等待linger.ms时间后直接发送
消费者提交offset方式: 自动提交(可设间隔时间) 手动提交(分为同步和异步)
先提交offset后消费,有可能造成数据的漏消费,而先消费再提交offset,有可能会造成数据的重复消费。
当有新的消费者加入消费者组、已有的消费者退出消费者组或者订阅的主体分区发生了变化,会触发分区的重新分配操作,重新分配的过程称为Rebalance。
消费者发生Rebalace之后,每个消费者消费的分区就会发生变化,因此消费者需要先获取到重新分配到的分区,并且定位到每个分区最近提交的offset位置继续消费。
自己记录下需要提交的offset,利用Rebalance分区监听器监听rebalance事件,一旦发生rebalance,先将offset提交,分区之后则找到最新的offset位置继续消费即可
拦截器:
拦截链interceptor chain 重写方法 onSend(ProducerRecord record) onAcknowledgement(Recordmetadata metadata, Exception exception)
分区器Partitioner用来判断消息发送到哪一个分区。序列化器是对数据进行序列化和反序列化。拦截器对于消息发送进行一个提前处理和收尾处理,处理顺利首先通过拦截器=>序列化器=>分区器



