当前位置: 首页 > 编程日记 > 正文

Zookeeper ZAB协议原理浅析

文章目录

    • 前言
    • 1. 基本角色和概念
    • 2. Leader Election
    • 3. Discovery
    • 4. Synchronization
    • 5. BroadCast
    • 后记


前言

DTCC 要在下周一到周三要在北京举办,身边有不少人都去参加了,领略中国最为领先的一些公司的自研存储技术。
阿里自研polardb,polardb-x(x-engine)相关,华为自研Gaussdb,开源TiDB 等,从SQL,到NoSQL,到NewSQL 都会将一些核心技术设计在大会上分享讨论,而且今年也是中国数据存储技术进入时间领先领域的一年(之前都是google,微软等巨头),那这场大会将是深入了解近年来中国存储最为前沿的技术盛会。

然后周五的一场技术分享却发现自己知识体系的严重漏洞, 分布式领域从上到下(分布式协调系统,分布式数据库(kv,table,graph,document),分布式存储(块,文件系统,对象),单机存储),除了最底层的单机存储引擎(现在做的)之外没有一个能够深入理解掌握并灵活应用的。仅仅为了准备一个分布式协调系统的分享,就发现了无数的知识漏洞(从基础的编码到上层的系统认知),那这样的基础去参加更高层次的技术集会岂不是被吊打。就像平时听大佬们分享一样,总是在感叹自己的无知,没有足够的知识基础,没有办法在大脑中快速打造属于自己的知识架构,最后反而适得其反。


回到我们要讨论的ZAB协议上,这是周内分享的一部分,当然仅仅是一些原理上的描述,并没有涉及zookeeper内部的代码实现。分享过程中将ZAB协议的演进也做了一个整体的分享,从paxos,multi paxos, raft/zab 都做了整体的描述,本篇文章主要讨论zab的协议原理,毕竟zookeeper的实现核心,当然要上干货。
之前有两篇相关的zookeeper 基础入门和运维相关的文章,能够有效节省大家的入门时间,先对 zookeeper有一个整体的了解。

1. 一文入门zookeeper
2. 一文运维zookeeper

关于zab协议内容的介绍能够回答如下几个问题:

  1. zookeeper 客户端接口为什么是wait-free的?即接口之间不会相互影响,所以不需要相互等待返回,可以并行调用接口。(并行更新数据)
  2. zookeeper 如何保证不出现双主?
  3. zookeeper如何保证请求顺序执行?

1. 基本角色和概念

ZAB 协议(zookeeper atomic broadcast) zookeeper原子广播协议,作为zookeeper实现分布式协调服务的核心,提供从leader 选举,到日志复制,到数据同步 以及 最后的数据广播 ,提供了一整套的实现算法。是一个值得学习研究的分布式系统,能够极大得帮助一些感兴趣的同学提升对分布式系统的理解和认知。

zookeeper 内部有三种角色,每一种角色可以用一个zookeeper server进程来表示:

  • leader: 整个集群只能有一个,主要处理写请求,zookeeper中所有的写请求都需要由leader负责处理。当然也能提供读服务。
  • follower: 整个集群可以有多个,只能提供读服务。在leader发生异常之后通过ZAB的leader election 以及后续的Discovery完成leader的重新选举,将一个follower 标记为leader,对外提供读写服务。
  • observer: 不参与leader选举和投票,仅仅提供读服务和接受leader变更的通知。可以作为zookeeper同城双机房,中的从机房的角色。既能够提供读服务,又能够有效得减少两个机房之间的rpc通信,从而提升整体的集群性能(当然,存在的问题也很明显,主从机房发生网络分区,从机房就不可用了)。

ZAB协议的主要是四个阶段:

  • Leader Election: 主要是节点之间进行信息同步,选择出一个leader
  • Discovery: leader 获取最新的history信息。(这里的history信息是整个集群最新的<v,zxid> 事务版本zxid以及其对应的数据)
  • Synchronization: leader将获取到的最新的数据同步到其他的从节点,并补全老数据,删除新数据
  • BroadCast: 之前的三个阶段都是集群不可用的状态。到了这个阶段,整个集群就可以对外提供读写服务,且zookeeper集群正常状态下处于该阶段。

ZAB 协议中有一些基本概念需要提前同步一下,如果感觉理解的还不很深刻,建议先看看前言推荐的两篇文章。

  • zxid: 唯一标识一个trasaction, 全局唯一递增的64位整数。zxid由 <epoch, count>
  • Epoch: 每个leader生命周期的一个标识。newEpoch = lastEpoch + 1
  • Count :表示每个Epoch期间发生的transaction id, 每个count 都是从0开始加一递增
  • zxid的比较; 我们称zxid <e, c> 大于 zxid’<e’, c’>,当满足 (e > e’ ) || (e = e’ & c > c’).

同时在ZAB中,我们前面说到的zookeeper内部的角色都会统称为peer, 一个peer代表一个角色进程;peer中有如下几个核心变量:

  • history: 被Peer提交的历史proposal<v,zxid>,也就是数据和事务id
  • acceptedEpoch,接受最新的NEWEPOCH的Epoch,主要是用来leader选举过程中follower判断是否接受leader的NEWEPOCH信息。
  • currentEpoch 接受最新的NEWLEADER的epoch, 当选举出来新的 leader之后,会将新leader的epoch更新到这个文件中。
  • lastZxid: 表示history 最近提交的proposal的zxid.

这么多概念第一次看肯定记不下, 实际讲解的过程中如果忘记了可以返回来查看。

2. Leader Election

顾名思义,这个阶段就是选举leader的阶段,集群不可用。
核心目的:通过投票完成leader选举,且集群每一个成员都会知道leader的epoch和leader id(myid文件)

时序图如下:
在这里插入图片描述
投票的信息主要是类似vote (zxid,id),当然实际更加详细(vote,id,state,round);id表示 唯一标识一个peer的id,也就是myid文件中的编号;state表示peer的所处的状态( leader,follower,election),round表示当前peer是第几轮投票。

上图中我们使用node(id,zxid)来简化选举过程,其中id就是myid, zxid就是当前peer最新的版本号。
图中有三条白色箭头,分别代表三个节点的时间,每一个节点在某一个时间会有三条线。
比如T1时刻的node1的三条黄色线,表示分别向自己投票,将自己的投票信息发送给其他两个节点,投票信息的大小比较如下规则:node1(id,zxid) > node2(id’ , zxid’) ,当满足 zxid > zxid’ || (zxid==zxid’ && id > id’)

T1 时刻 开始了leader选举,三个节点都将各自的node信息先发送给自己,再发送给其他两个节点。
T2 时刻, 其他两个节点都已经完成了投票信息的比较:

  • 比如 node1 会收到其他两个节点的投票信息,依次和自己的zxid进行比较,版本号高的成为leader;node1最高,不需要再发送消息投票(它开始已经投自己一票了)。
  • node2 会收到来自node3和node1的投票,进行投票信息的比较,发现node1 > node2,node1 > node3,投票给node1,并准备好 新的投票结果进行广播。
  • node3 类似,投票给node1。

T3 时刻,整个集群其实已经完成了选举,node1成为新leader, 不过还是准leader,后续需要进行一些更进一步的版本数据同步。

这个过程存在一些问题,比如node1 T1时刻发送给 node2节点的投票信息出现rpc延迟,在node2完成投票决策之后才到达node2。
在这里插入图片描述

在T1 时候 , node2只收到了node2自己和node3的投票,进行投票信息的比对,虽然zxid 相等,但是node3 id更大,且也满足大多数,则node2 会选择node3作为leader,而node3会选择node1进行投票(node1发送到node3的投票信息并没有延迟)也就是到T3时刻,node2认为node3是leader , 而node3和node1都认为自己是leader。当然实际情况node3并不会被标记为leader,因为node3只收到一个投票,不满足大多数,只是集群中会存在这样的冲突。

当然这种问题在后续的Dicovery阶段进行leader版本信息比较时就能够避免,发现leader的版本号比follower 版本号更低时会触发重新选举,这里说一下Leader Election这个阶段如何避免 某个peer出现 delay message的问题。
在这里插入图片描述

维护一个超时时间 Finalize Wait Time,当某一个peer收到投票信息后发送了一次投票结果,但是在这段时间内如果还收到其他的投票信息且需要变更投票结果,那么这个peer会重新发送一个新的决策结果给其他的peer。

也就是到这个Finalize Wait Time 结束后的集群leader才会是新的Leader。
T2时刻也处在FWT的时间段内,这个时候延迟的node1 的投票信息发送到了node2,发现之前的投票结果需要变更,则会重新发起一次投票,投票为node1作为leader。

当然这个 Finalize Wait Time 肯定也不能完全保证解决这个问题,它的数值设置多大也只是概率性的降低delay message 导致的投票信息延迟达到的问题。所以,还需要更加严谨的机制来保证不出现leader冲突的问题,我们继续来看后续阶段。

3. Discovery

Leader Election之后整个集群已经完成了选主,当然这个leader并不是真正的leader。之前它可能拥有最新的版本号,但现在已经改朝换代了,需要有自己的年号来向天下宣告自己的登基。所以它需要重新发布年号(版本号),同时需要掌控整个王朝最新的资源(最新的数据),只有完成这一些事情自己才能稳坐宝座,成为正王。

现在的集群拥有这个几个角色,其中有一个准leader(按照之前leader选举过程,也有可能出现双leader,然后进入这个阶段)
在这里插入图片描述

ZAB协议的角度先总体说一下这个阶段的核心目的:

  1. 确认/生成 一个新leader的Epoch
  2. 新的Epoch同步到所有的Follower
  3. Leader获取最新的history, 准备进行后续的Synchronization 阶段。history: <value, zxid>

具体过程如下:
在这里插入图片描述

  1. Follower节点知道准Leader节点之后,会发送一个FOLLOWERINFO的信息携带自己的f.acceptedEpoch内容

  2. 准leader节点收到超过半数的FOLLOWERINFO之后,会从中选择一个最大的,并在最大的基础上+1,即max{f.acceptedEpoch} + 1

  3. 准Leader将准备好的NEWEPOCH发送到follower, 表示自己的年号已经更新。等待quorum中的成员回复ACK

  4. follower 收到NEWEPOCH之后和自己本地epoch进行比对:

    a. leader 发送过来的epoch > acceptedEpoch,更新自己的acceptedEpoch 为新的epoch,并回复一个ACKEPOCH消息,这个消息中携带上个currentEpoch, history 和 lastZxid(history 最近提交的proposal的zxid)

    b. leader发送过来的epoch < acceptedEpoch ,则 回退到阶段0,重新进行leader选举(集群中存在节点异常)。
    c. leader发送过来的epoch 和 本地acceptedEpoch相等的场景论文并没有提到,感觉应该会需要重新选举,毕竟leader已经在收到大多数的FOLLOWERINFO 中最大的+1了。

  5. Leader收到所有quorum中follower的ACKEPOCH, 从所有的消息中找出currentEpoch最大的或者lastZxid最大的follower,然后把该follower的history 作为自己的history(pull history的过程)。当然,如果本地自己的currentEpoch 或者 lastZxid最大,那就用本地的history即可。

到现在,Leader 已经获取到最新的history, 并开始准备进行后续的Synchronization 阶段。

4. Synchronization

这个阶段的核心目的是:

  1. 同步history proposal。即将Leader获取到的最新的history 数据同步到follower节点,让整个集群数据对齐。
  2. 处理上个阶段遗留下来的proposal,follower节点中的数据 需要清理的可以清理,需要删除的可以删除。

大体过程如下:
在这里插入图片描述

Leader这个阶段刚开始的时候已经有了整个集群最新的history数据。

  1. Leader 想所有的follower发送NEWLEADER信息,其中包括leader自己最新的epoch 和 最新的history数据。

  2. follower 收到leader的消息之后判断当前轮次自己的acceptedEpoch和leader发送过来的epoch是否一样(discovery阶段已经对follower自己的acceptedEpoch进行了更新)

    1). follower的acceptedEpoch和新epoch相同,表示自己已经跟上了新的epoch, 那么做如下几个事情
    a. 更新自己的currentEpoch为新的epoch,表示进入新的朝代了
    b. 按照zxid的大小逐一进行本地proposed,此时这些transaction还未commit。
    c. 更新自己的history为最新的history
    d. 返回一个ACKNEWLEADER 给leader, 表示这个follower已经完成数据同步

    2). follower收到的epoch和本地的acceptedEpoch不同,那么回退到阶段0,重新选主(存在节点异常,当前主节点并不能包含所有的数据,不能随意更新,否则会丢数据)。

  3. Leader 收到follower节点的ACKNEWLEADER消息之后,对proposal的数据进行提交commit,所有的follower节点也会收到commit请求(落盘)

  4. follower节点收到leader的COMMIT请求,会对自己本地已经proposed但还未commit的事务,按照zxid进行从小到大的排序,优先commit zxid较小的节点。

  5. Leader 和 Follower都完成同步之后进入第四阶段。

从朝代更替来看,前面的几个阶段整个国家处于乱世:无君,君臣各有所思,各有所谋,可能的多君。。。。
到这个阶段,历经千难万险完成了国家统一,君强臣明,整个国家开始一致对外,共向繁荣的场景。
正如我们的春秋战国到秦,五代十国到宋,南宋到元,每一个帝国的崛起都历经无数次的尝试和磨难,但大一统的目标从秦遍成了唯一,只有集群大一统,才能够更好得施展每一个角色的才华。

5. BroadCast

这是一个稳定的时代, 之前三个阶段,zookeeper无法对外提供服务。到了这个阶段,整个集群即能够对外提供读写服务。

这个阶段如果发生集群成员变更,即加入了follower和observer。
整体过程如下:
·

  1. Leader收到一个写请求,会生成一个Proposal: <value, zxid>, zxid = lastZxid + 1,对quorum中的follower节点发起propose请求,并携带生成的Proposal。
  2. follower节点收到propose的proposal,将其加入到history队列,并向leader回复ACK,表示已经收到propsal。
  3. leader收到过半节点的ACK之后,认为可以进行commit,则向quorum发送COMMIT请求
  4. follower收到propose的commit 之后开始进行提交
    1). 为了满足zxid的全局一致性,这里会检查follower本地是否有未提交的proposal<v,z>,保证比当前zxid小的propose先提交
    2). 当所有小于zxid的propose都完成commit之后再提交当前的zxid。
  5. BroadCast阶段 也能够接受新的Follower或者Observer的加入,步骤如下:
    1). 新加入的节点会给Leader发送一个FOLLOWERINFO信息
    2). Leader收到后会回复给他一个NEWEPOCH 和 NEWLEADER, 告诉这个节点集群最新的epoch和history数据
    3). 新节点收到NEWLEADER后,如果正常逻辑处理完成后(将history中的数据并发propose),回一个ACKNEWLEADER给Leader
    4). Leader收到ACK回复之后告诉他可以进行本地proposal的提交了,会发送一个COMMIT 请求
    5). 新节点收到这个请求,对本地完成proposed的数据按照zxid从小到达进行commit(落盘)。
    6). 新节点完成commit之后,leader会将新的节点加入到自己的quorum列表中。

ps :
a. 以上过程不论是leader还是follower 节点在进行propose的过程都是可以并发进行的。对于leader来说,一个proposal的发起不会等待上一个commit完成之后才会发起,当前proposal和上一个proposal是可以并行处理,保证了zookeeper的更新接口可以提供wait-free 的能力。

b. commit 时需要保证本地比当前zxid更小的事务优先提交,从而保证zookeeper的Linearizable 特性

以上基本就是ZAB协议的每个阶段的细节,在我们实际zookeeper的实现中,会对以上四个阶段做优化。

我们能够看到从开始选主到能够提供服务,这个过程还是会有大量的rpc 和数据交互,zookeeper实际将leader election和discovery变更为 FLE(Fast Leader Election)阶段,在完成Leader 选举之后 leader 就已经拥有了最新的history数据。

将Synchronization 阶段变更为了Recovery 过程,整体上就是让单次rpc携带的数据量更大,能够在完成相互的交流通信之后进行更多更快的本地计算,而不是将较多的时间消耗在rpc和等待rpc数据的过程中。

后记

这是一个乱世的结束,但从历史的角度看,也会是一个乱世的开始。

居安思危很难,领导层 只能够在大多数场景做出正确的决策,但p9999和max之间差异还是太大。我们的系统 同样是一个复杂体系,大量的静默错误(硬件损耗,磁盘的bit位反转,还有大量的底层系统软件到上层应用软件的bug)无法保证一个分布式集群 每时每刻都正常运行。只能在有限的人力,有限的资源下最大化我们系统的可用性,创造足够的价值。正如那一些历史上的伟大帝国 在他们所在的时代创造了让后人敬仰的文明。

相关文章:

Java项目:仓库管理系统设计和实现(java+ssm+springboot+layui)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 主要功能模块 1.用户模块管理&#xff1a;用户登录、用户注册、用户的查询、添加、删除操作、 2.客户信息管理&#xff1a;.客户列表的展示、添加、修改、删除操作、 3.供应商管理&#xff1a;供应商详情…

Java Web 中的一些问题

http://localhost:8080/struts2demo/online/userLogin.jsp 请求模式 :// 主机名名称&#xff08;或者服务器名称&#xff09; : 端口 / Servlet容器的名称&#xff08;通常为项目名称&#xff09; / 自定义的网页文件夹名或者映射中的文件包名 / 网页名称及其后缀或者响应动作…

《零成本实现Web自动化测试--基于Selenium》第一章 自动化测试基础

第一篇 Selenium 和WebDriver工具篇 第一章 自动化测试基础 1.1 初识自动化测试 自动化测试有两种常见方式 1.1.1 代码驱动测试&#xff0c;又叫测试驱动开发&#xff08;TDD&#xff09; 1.1.2 图形用户接口测试: 测试框架产生用户接口事件&#xff08;例如键盘敲击&#x…

第11章 AOF持久化

AOF持久化在硬盘上保存的是对Redis进行的逻辑操作&#xff0c;类似InnoDB中的bin log。说白了就是你对一个Redis输入了哪些语句&#xff0c;AOF文件都会原封不动的保存起来&#xff0c;等到需要回复Redis的时候再把这些语句执行一遍。 11.1 AOF持久化的实现 AOF简单的理解是把执…

Go 语言实现字符串匹配算法 -- BF(Brute Force) 和 RK(Rabin Karp)

今天介绍两种基础的字符串匹配算法&#xff0c;当然核心还是熟悉一下Go的语法&#xff0c;巩固一下基础知识 BF(Brute Force)RK(Rabin Karp) 源字符串&#xff1a;src, 目标字符串:dest&#xff1b; 确认dest是否是src 的一部分。 BF算法很简单暴力&#xff0c;维护两个下标…

Java项目:前后端分离网上手机商城平台系统设计和实现(java+vue+redis+springboot+mysql+ssm)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 主要模块设计如下&#xff1a; 前后端主要技术&#xff1a;Java springboot springMVC mybatis mysql vue jquery node.js redis 1) 用户注册和登录功能&#xff1a;。 2) 用户信息的管理以及角色的…

利用AutoSPSourceBuilder和Autospinstaller自动安装SharePoint Server 2013图解教程——Part 1...

这是一篇对之前 《利用AutoSPSourceBuilder和Autospinstaller自动安装SharePoint Server 2013图解教程——Part 2》的补充。本篇博客将对AutoSPSourceBuilder的使用进行说明。 AutoSPSourceBuilder介绍 下载AutoSPSourceBuilder点击进入AutoSPSourceBuilder的官网&#xff0c;找…

Git 版本还原命令

转载&#xff1a;https://blog.csdn.net/yxlshk/article/details/79944535 1.需求场景&#xff1a; 在利用github实现多人协作开发项目的过程中&#xff0c;有时会出现错误提交的情况&#xff0c;此时我们希望能撤销提交操作&#xff0c;让当前版本回到提交前的样子或者某一个版…

NVME CLI -- nvme 命令查看NVME设备内部状态

文章目录NVME 和 AHCI 性能比较NVME-CLI nvme工具使用1. 安装2. 命令综述3. 基本命令演示4. NVME 固件设备升级近期在做一些rocksdb on 新硬件的性能测试&#xff08;flash ssd, nvme ssd , nvme optane ssd, optane persistent memory&#xff09;&#xff0c;由于底层一些设备…

Java项目:网上水果蔬菜项目系统设计和实现(java+springboot+mysql+ssm)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 主主要技术&#xff1a;java springmvc springboot mybatis mysql jquery layui 等技术要模块设计如下&#xff1a; 用户角色的功能&#xff1a; 登录、注册、浏览商品、修改个人信息&#xff08;上传…

POJ 1189 记忆化搜索

钉子和小球Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 7218 Accepted: 2164Description 有一个三角形木板,竖直立放&#xff0c;上面钉着n(n1)/2颗钉子&#xff0c;还有(n1)个格子&#xff08;当n5时如图1&#xff09;。每颗钉子和周围的钉子的距离都等于d&am…

Android短信管家视频播放器代码备份

自己保留备份&#xff0c;增强记忆 这是video的类 public class VideoActivity extends Activity {/*** 解析网络页面*/private WebView wv;/*** 进度条类*/private ProgressDialog pd;/*** 异步处理消息*/private Handler handler;private static final int SHOW 0;private s…

Python常用函数--文档字符串DocStrings

Python 有一个甚是优美的功能称作python文档字符串&#xff08;Documentation Strings&#xff09;&#xff0c;在称呼它时通常会使用另一个短一些的名字docstrings。DocStrings 是一款你应当使用的重要工具&#xff0c;它能够帮助你更好地记录程序并让其更加易于理解。令人惊叹…

Go 分布式学习利器(17)-- Go并发编程之协程机制:Grountine 原理及使用

文章目录1. Thread VS Groutine2. Groutine 调度原理3. Groutine 示例代码关于Go的底层实现还需要后续持续研究&#xff0c;文中如有一些原理描述有误&#xff0c;欢迎指证。 1. Thread VS Groutine 这里主要介绍一下Go的并发协程相比于传统的线程 的不同点&#xff1a; 创建…

Java项目:美食菜谱分享平台系统设计和实现(java+springboot+mysql+ssm)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 主要技术实现&#xff1a;spring、 springmvc、 springboot、mybatis 、session、 jquery 、 md5 、bootstarp.js tomcat、拦截器等。 具体主要功能模块如下&#xff1a; 1.用户模块管理&#xff1a;用户…

【leetcode】Roman to Integer

题目描述&#xff1a; Given a roman numeral, convert it to an integer. Input is guaranteed to be within the range from 1 to 3999. 解题思路&#xff1a; 首先我们要了解罗马数字怎么写的 个位数举例 I, 1 】II, 2】 III, 3】 IV, 4 】V, 5 】VI, 6】 VII, 7】 VIII,8 】…

Apache Traffic Server管理工具

Traffic Line是命令行程序&#xff0c;可以用来快速监视 Traffic Server 的性能和网络流量&#xff0c;也能配置 TS。Traffic Shell也是命令行工具&#xff0c;进入该 shell 后有自己一套语法&#xff0c;可代替 Traffic Line 完成监控、配置任务。通过 Traffic Line 和 Traffi…

npm使用记录

npm是一个 包管理工具。安装node之后就可以使用npm命令了&#xff0c;为了方便使用&#xff0c;通常我们还要装下 淘宝NPM镜像&#xff0c;之后就可以用cnpm命令了。 注意&#xff1a;以下提到的如-g --save等标签都可以放在 包名前面。 首先一个前端项目下载下来&#xff0c;需…

Go 分布式学习利器(18)-- Go并发编程之lock+WaitGroup实现线程安全

Go语言中通过Groutine 启动一个Go协程&#xff0c;不同协程之间是并发执行的&#xff0c;就像C/Java中线程之间线程安全是一个常见的问题。 如下Go 语言代码: func TestConcurrent(t *testing.T) {var counter int 0for i : 0;i < 5000; i {go func() { // 启动groutine 进…

Java项目:网上家具商城平台设计和实现(java+springboot+mysql+ssm)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 主要技术&#xff1a;springmvc springboot mybatis mysql jquery layui 等技术 具体功能模块&#xff1a; (1) 用户注册和登录登录功能&#xff1a; ①用户的注册功能 : 访问网站的人根据网站的提示注册…

Linux socket TIME_WAIT 优化

如发现系统存在大量TIME_WAIT状态的连接&#xff0c;通过调整内核参数解决&#xff0c;vim /etc/sysctl.conf编辑文件&#xff0c;加入以下内容&#xff1a;net.ipv4.tcp_syncookies 1net.ipv4.tcp_tw_reuse 1net.ipv4.tcp_tw_recycle 1net.ipv4.tcp_fin_timeout 30然后执行…

Android Handler的使用!!!

大家好我们这一节讲的是Android Handler的使用,在讲Handler之前&#xff0c;我们先提个小问题&#xff0c;就是如何让程序5秒钟更新一下Title.首先我们看一下习惯了Java编程的人&#xff0c;在不知道Handler的用法之前是怎么样写的程序,代码如下所示:view plaincopy to clipboa…

git之reset图解

https://blog.csdn.net/longintchar/article/details/81843048 1、三棵树。 此时如果我们运行 git status&#xff0c;会发现没有任何改动&#xff0c;因为现在三棵树完全相同。 修改文件 现在我们想要对文件进行修改然后提交它。我们将会经历同样的过程&#xff1b;首先在工作…

Go 分布式学习利器(19)-- Go并发编程 之 CSP(communicating sequential processes) 机制

文章目录前言CSP 特点CSP代码 演示1. 正常流程的代码2. CSP 未设置buffer 代码3. 设置指定大小的channel buffer总结前言 CSP 这个名词大家会比较陌生&#xff0c;但是说到future 熟悉C / JAVA 线程模型的伙伴可能就会很熟悉了&#xff0c; 通过future机制能够实现两个线程之间…

Java项目:学生学科竞赛管理管理系统设计和实现(java+springboot+ssm+maven)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; 主要技术、spring、 springmvc、 springboot、 mybatis 、 jquery 、 layUI、md5 、bootstarp.js tomcat、、拦截器等项目 主要功能:登录、用户、菜单管理、角色管理、权限管理、立项申请、报名、结、经费…

update 改写 merge into

update语句改写成merge into有时会提高运行速度 看两个案例 1.根据业务将两个嵌套子查询改写成max&#xff0c;速度有3min提升到3s UPDATE OPER_792.LL_SCB_YDKB_20120730 A SET A.DCP (SELECT B.PROD_OFFER_NAME FROM OPER_792.YD_TC B WHERE A.SERV_ID B.SERV_ID AND B.TC_…

CCControlSwitch 、CCControlSlider、CCControlButton

/**bool hasMoved(); 这里获取的不是开关是否正在被用户拨动&#xff0c;而是开关最终的状态是由用户手动拨动开关进行的&#xff0c;*还是用户点击开关进行的状态更改*/CCControlSwitch* pSwitch CCControlSwitch::create(CCSprite::create("switch-mask.png"),CCS…

bzoj2961 共点圆 (CDQ分治, 凸包)

/* 可以发现可行的圆心相对于我们要查询的点是在一个半平面上&#xff0c; 然后我们要做的就是动态维护凸壳然后用这个半平面去切它 看看是否是在合法的那一面然后cdq分治就可以了代码基本是抄的&#xff0c;*/#include<cstdio> #include<algorithm> #include<c…

Rocksdb Iterator实现:从DBIter 到 TwoLevelIter 的漫长链路

文章目录1. 迭代器简单介绍2. 迭代器用户态相关接口3. 迭代器内部架构4. 迭代器的入口实现4.1 DBIter4.2 MergingIterator4.3 Memtable系列Iterator4.4 LevelIterator 和 TwoLevelIteratorps&#xff1a;本文的基础迭代器设计 以及 相关代码 是基于rocksdb 6.4.6版本进行描述的…

Java项目:OA办公自动化系统设计和实现(java+springboot+freemarker+mysql+maven+mybatis+jpa)

源码获取&#xff1a;博客首页 "资源" 里下载&#xff01; java springbootOA办公自动化系统&#xff1a; 主要功能模块&#xff1a;系统、用户、角色、考勤、流程、公告、邮件、任务、日程、计划、文件、笔记、通讯录、讨论区等多个模块管理 使用Maven进行项目管理…