笋干太咸

笋干太咸 查看完整档案

杭州编辑  |  填写毕业院校  |  填写所在公司/组织 renkai.org 编辑
编辑

欢迎发简历给我!
sungan@fordeal.com

个人动态

笋干太咸 关注了标签 · 2019-11-06

rust

一门赋予每个人构建可靠且高效软件能力的语言。

关注 464

笋干太咸 收藏了文章 · 2019-02-25

HBase运维基础——元数据逆向修复原理

背景
鉴于上次一篇文章——“云HBase小组成功抢救某公司自建HBase集群,挽救30+T数据”的读者反馈,对HBase的逆向工程比较感兴趣,并咨询如何使用相应工具进行运维等等。总的来说,就是想更深层理解HBase运维原理,提高运维HBase生产环境的能力,应对各种常见异常现象。不同的读者对hbase的了解程度不同,本文不打算着重编写一个工具怎么使用,而是从HBase的运维基础知识介绍开始讲解。为了能帮助大部分读者提高HBase运维能力,后续会写个“HBase运维系列” 专题系列文章。

clipboard.png

介绍

相信很多自建HBase的企业会经常碰到各种各样的hbase运维问题。比如使用HBase的时候,HBase写入一段时间后开始RegionServer节点开始挂掉,重启RegionServer发现启动很慢,很多region出现RTI问题,导致读写某个region的业务hang住了 。还有一些人的HBase集群多次运维尝试后,直接HBase启动不了了,meta表上线就开始报错,导致最终业务不能正常上线运行等等系列问题。本文就HBase运维的原理基础开始入手,重点讲解数据完整性,以及元数据“逆向工程”恢复数据完整性的原理方法。开启后续一系列的HBase运维知识讲解。

HBase目录结构
本文就1.x版本进行讲解,不同版本大致相通。HBase在HDFS上会单独使用一个目录为HBase文件目录的根目录,通常为 “/hbase”。基于这个目录下,会有以下目录组织结构:

/hbase/archive (1)
/hbase/corrupt (2) 
/hbase/data/default/TestTable/.tabledesc/.tableinfo.0000000001 (3)
/hbase/data/default/TestTable/fc06f27a6c5bc2ff57ea38018b4dd399/info/2e58b3e274ba4d889408b05e526d4b7b (4)
/hbase/data/default/TestTable/fc06f27a6c5bc2ff57ea38018b4dd399/recovered.edits/340.seqid (5)
/hbase/data/default/TestTable/fc06f27a6c5bc2ff57ea38018b4dd399/.regioninfo (6)
/hbase/data/default/TestTable/fc06f27a6c5bc2ff57ea38018b4dd399/.tmp (7)
/hbase/data/default/TestTable/fc06f27a6c5bc2ff57ea38018b4dd399/.splits (8)
/hbase/data/default/TestTable/fc06f27a6c5bc2ff57ea38018b4dd399/.merges (9)
/hbase/data/hbase/acl (10)
/hbase/data/hbase/meta (11)
/hbase/hbase.id (12)
/hbase/hbase.version (13)
/hbase/MasterProcWALs (14)
/hbase/oldWALs (15)
/hbase/.tmp (16)
/hbase/.trashtables/data (17)
/hbase/WALs/tins-donot-rm-test-hb1-004.hbase.9b78df04-b.rds.aliyuncs.com,16020,1523502350378/tins-donot-rm-test-hb1-004.hbase.9b78df04-b.rds.aliyuncs.com%2C16020%2C1523502350378.default.1524538284034 (18)

(1) 进行snapshot或者升级的时候使用到的归档目录。compaction删除hfile的时候,也会把就的hfile归档到这里等。

(2) splitlog的corrupt目录,以及corrupt hfile的目录。

(3) 表的基本属性信息元文件tableinfo。

(4) 对应表下的hfile数据文件。
(5) 当splitlog发生时,一个RS的wal会按照region级别split WALs写到对应目录下的的recovered.edits目录上,使得此region再次被open的时候,回放这些recovered.edits 日志。

(6) regioninfo文件。

(7) compaction等的临时tmp目录。

(8) split时临时目录,如果上次region的split没有完成被中断了,这个region再open的时候会自动清理这个目录,一般不需要人工干预。

(9) merges时的临时目录,和split一样,如果没有正常完成的时候被中断了,那么他会在下次被open的时候自动清理。一般也不需要人工干预。

(10) acl 开启HBase权限控制时的权限记录系统表

(11) meta 元数据表,记录region相关信息

(12) hbase.id 集群启动初始化的时候,创建的集群唯一id。可以重新fix生成
(13) hbase.version hbase 软件版本文件,代码静态版本,现在都是8
(14) master执行过程程序的状态保存,用于中断恢复执行使用。

(15) oldWALs 历史wal,即wal记录的数据已经确认持久化了,那么这些wal就会被移到这里。splitlog完成的那些就日志,也会被放到这里。

(16) tmp 临时辅助目录,比如写一个hbase.id文件,在这里写成功后,rename到 /hbase/hbase.id

(17) /hbase/.trashtables/data 当truncate table或者delete table的时候,这些数据会临时放在这里,默认1小时内被清

(18) 记录着一台RegionServer上的WAL日志文件。可以看到它是regionserver名字是有时间的,即下一次启动时RS的wal目录就会使用新的目录结构存放wal,这个旧的RS wal 目录就会被splitlog过程拆分回放

HBase涉及的主要文件及用途

HDFS静态文件,HDFS上的HBase 数据完整性

  1. hfile文件:数据文件,目前最高版本也是默认常用版本为 3。 hfile文件结构细节可以参考官网http://hbase.apache.org/book....。我们这里逆向生成元数据主要使用到了HFile fileinfo的firstkey、lastkey信息。
  2. hfilelink文件: 在hbase snapshot时用到, migration upgrade 也会使用到。很少碰到这类文件的运维问题,这里不作过多介绍。
  3. reference文件:用来指定half hfile,一个region有reference时,这个region不能split。split/merge会创建这个。进行compaction后生成新的hfile后,会把这个reference删除。hfile的reference文件名格式一般是 hfile.parentEncRegion。如:/hbase/data/default/table/region-one/family/hfilename。其region-two有reference hfile文件名格式为:/hbase/data/default/table/region-two/family/hfile.region-one 通常无效引用就是 region-one的hfile不存在了,那么这个引用就会失效。他的修复方法一般是把reference无效的引用移除。
  4. ".regioninfo" 文件,保存着 endkey/offline标志/regionid/regionName/split标志/startkey/tablename等
  5. tableinfo文件这类文件保存着 tableName/table属性信息/table级别config信息/family信息。其中family信息保存着 famliyName/famiy属性/famliy级别config信息等。

通常,table属性有:REGION_MEMSTORE_REPLICATION,PRIORITY,IS_ROOT_KEY等,一般这些属性默认也是根据配置的一样。family属性有:BLOCKSIZE,TTL,REPLICATION_SCOPE等,一般属性是根据配置使用默认的。

  1. hbase:meta表数据内容格式

    regionname, info:regioninfo, regioninfo的encodeValue值

    regionname, info:seqnumDuringOpen, 序列号

    regionname, info:server, region所在的server名

    regionname, info:serverstartcode, regionserver 启动的timestamp

元数据逆向生成原理

上述介绍的数据文件中,HBase的主要的元数据主要由meta表、tableinfo、regioninfo构成。这里的逆向生成元数据指的是,根据数据hfile数据文件,反向生成regioninfo/tableinfo/meta表的过程。

1. 逆向生成tableinfo文件

case1. 通过从master进程内存中的tabledescritor cache 完整恢复tableinfo文件,此时恢复的tableinfo是完整的,和之前的完全一样。

case2. 当cache中没有加载过此表的tableinfo时,修复过程只能从表的目录结构list所有familyNames 来恢复tableinfo,这个时候只能得到的是列簇的名字,恢复tableinfo文件内容中,除了表名、列簇名一致,其他的属性均采用默认值。这个时候如果运维人员知道有什么属性是自定义进去的,那么就需要要手动再次添加进去。

2. 逆向生成regioninfo文件

hfile 中的fileinfo读取firstkey/lastkey 排好序,得到region下所有hfile的最大rowkey和最小rowkey,并根据tableinfo中的表名 完整恢复 regioninfo文件。主要这里只能恢复 表明/startkey/endkey, 其他属性如:offline标志,regionName,split标志,hashcode等均使用代码生成或者配置的默认值。

3. 逆向填充meta表行

regioninfo文件序列化,填入meta表 info:regioninfo 列,并同时写入默认的server,等它被再次open的时候,重新分配region到实际的regionserver上,并更新这里的数据行。

逆向工程除了上面的直接文件、数据内容修复外,还涉及到数据的完整性其他方面修复。一个表示由无穷小的rowkey到无穷大的rowkey范围组成,还可能会发生的问题如:region空洞、region重叠现象,如:

clipboard.png

如果有region空洞的时候,就会使用他们的空洞边界作为startkey/endkey,再修复创建一个region目录及目录下的regioninfo文件。如果是region重叠,则会把重叠的region进行合并,取所有region的最大最小rowkey作为merge后新region的最大最小rowkey。

元数据工具修复

元数据的缺少或者完整性有问题,会影响系统运行,甚至集群直接不可用。最常见的如 meta表上线失败,region 上线open失败等。这里介绍两个工具,​工具一: hbase hbck 在线修复完整性修复元数据信息,​工具二:OfflineMetaRepair 离线重建 hbase:meta 元数据表。

在线hbck修复:
​前提:HDFS fsck 确保 hbase跟目录下文件没有损坏丢失,如果有,则先进行corrupt block 移除。

​步骤1. hbase hbck 检查输出所以ERROR信息,每个ERROR都会说明错误信息。

​步骤2. hbase hbck -fixTableOrphones 先修复tableinfo缺失问题,根据内存cache或者hdfs table 目录结构,重新生成tableinfo文件。

​步骤3. hbase hbck -fixHdfsOrphones 修复regioninfo缺失问题,根据region目录下的hfile重新生成regioninfo文件

​步骤4. hbase hbck -fixHdfsOverlaps 修复region重叠问题,merge重叠的region为一个region目录,并从新生成一个regioninfo

​步骤5. hbase hbck -fixHdfsHoles 修复region缺失,利用缺失的rowkey范围边界,生成新的region目录以及regioninfo填补这个空洞。

​步骤6. hbase hbck -fixMeta 修复meta表信息,利用regioninfo信息,重新生成对应meta row填写到meta表中,并为其填写默认的分配regionserver

​步骤7. hbase hbck -fixAssignment 把这些offline的region触发上线,当region开始重新open 上线的时候,会被重新分配到真实的RegionServer上 , 并更新meta表上对应的行信息。

离线OfflineMetaRepair重建​:
前提:HDFS fsck 确保 hbase跟目录下文件没有损坏丢失,如果有,则先进行corrupt block 移除

​步骤1: 执行 hbase org.apache.hadoop.hbase.util.hbck.OfflineMetaRepair -fix

最后,两个工具使用说明都比较详细,经过上面的基础介绍,相信都会看的懂的。这里不对工具再细致说明,工具的说明可以参考官网或者工具提示。​题外话,有些开源组件设计的时候,向hbase元数据文件写入一些特有的信息,但是并没有修改到hbase工具的修复工具,或者它自己没有维护修复工具,如果这类文件损坏、丢失了,那么相应的组件就会运行不正常。使用这类组件的用户,应该不仅记录好你的表的基本结构,还要记录表的属性配置等,当发生修复运维行为的时候,主要再次核对确认。

​小结

本文介绍了运维hbase基础原理中的数据完整性以及逆向元数据修复原理,并举例介绍两个逆向修复元数据的工具和实用执行步骤。后续会出系列文章,说明更多hbase运维基础、运作原理等,希望对大家的运维和使用HBase有所帮助。
详情请阅读原文

查看原文

笋干太咸 赞了文章 · 2019-01-23

BPC电波授时信号的“零成本”伪造

时间都去哪了?!

——BPC电波授时信号的“零成本”伪造
阿里巴巴移动安全

工作日,上班路上,看一眼情人节女友刚送的六局电波表。咦,出门明明还早,怎么眼看要迟到!别慌,可能只是你被“黑”了。

什么,你戴iWatch?那可以看看这篇:GPS信号伪造

电波钟/表顾名思义就是通过接收电波授时信号实现自动对时的钟表。以“电波表”为关键字在淘宝搜索,可以看到相关产品很多。其中主要是几个日系的手表大厂,如卡西欧、西铁城、精工、东方双狮等,此外国产品牌也有几个,就不一一列举了。我们后面实验中用到的是一台挂钟,由国产品牌康巴丝生产。

授时电波一般由国家负责标准时间的专门机构进行播发。所广播的时间是国家标准时,由多台高精度原子钟组成的守时钟组产生。授时电波采用频率低于100千赫的长波波段,不易被遮挡,因此一个发射站就可以基本覆盖一国国土。中英美日德等国有各自标准的长波授时服务,不仅名称不一,所用的频率和编码也不同,也就是开头提到的所谓六局(日本面积虽小,但有两局)。

我国的长波授时服务BPC,由中科院国家授时中心与某企业合作建立,面向民用。BPC广播站设立在河南商丘,频率为68.5千赫。采用脉宽调制,码率1赫兹。每个编码脉冲宽度为0.1s,0.2s,0.3s或0.4s,分别代表四进制的 0,1,2,3。而这一串四进制的数字是由播发时刻的秒、时、分、星期、日、月、年插入几个校验位组成的,长度为20s,并无任何加密手段。也就是说20秒的信号才可以完整传达当前日期和时间。这一编码方式相比其他各国60秒一帧的方法,对时过程更快。另外需要提醒一下,BPC电波授时编码属于某企业的专利技术,不能私自用于商业盈利。既然是专利,就不妨再公开引用一次,编码示意图如下。

图片描述

说了这么多,同学们应该对电波授时和电波钟表也有了大概的了解。下面讲讲如何“黑”的问题。思路很简单:伪造授时电波信号,盖过真正的BPC电波,电波钟也就乖乖听咱的了。

如何产生信号呢,我们采用了一台安捷伦最新款PSG系列信号发生器——开玩笑的,我司怎会有这,只有笔记本电脑。好吧,就用笔记本。是的,就用笔记本!

笔记本电脑就位后,照着专利说明书写一个程序,将日期时间翻译成BPC编码,然后将编码通过电脑自带的音频输出播放出来。为了避免笔记本自身杂散电磁辐射造成干扰,我们利用耳机作为播放设备。为了增强信号强度,我们把耳机粘在钟表背后靠近接收天线的位置,把音量调到最大。编造一个错误的时间,运行程序开始发播信号,人耳可以听见脉冲通断的声音。按下电波钟背后的对时按钮三秒钟,表针暂停,进入对时模式。静待几分钟,电波钟从信号中获取错误的时间,表针快速旋转至指定时间,对时成功!
(对时过程有视频为证,点击这里查看视频

讲到这里,肯定有同学要说,声卡最高只能输出22千赫的声音,怎么能发出68.5千赫,还是电波信号,你不要骗我!且慢,其实是笔者刚有意漏掉一个关键点现在来讲。电脑所播放的音频信号实际上是有讲究的,我们选择了68.5千赫的5分频,13.7千赫,以此作为载波在上面加载BPC编码。耳机发声就是靠线圈产生变化的磁场推动振膜实现的,因此发出听得见的声音的同时,也在发出听不见的电磁波。当我们把输出音量调至最大,耳机的非线性效应显著起来,频率成分中不仅有基频成分(13.7千赫)还有倍频成分,其中也就包含13.7千赫的5倍频信号68.5千赫,也就是BPC的载波频率。这样也就实现了标题所说的“零成本”。

作为一名合格的白帽子,讲了“攻”怎么能不讲“防”?防范措施就是我们做了一个艰难的决定,还是不公布源代码了。这样好歹熊孩子恶作剧之前还得好好做上两晚上功课。实际上仅仅从BPC的编码上来说,由于其非常简单透明,是很难防止类似攻击行为的。好在现在获取时间信息的手段多种多样,通过移动网络、有线网络、GPS、北斗等都可以获得非常准确的时间,因此采用多个时间来源相互参照,能够大大降低因被攻击造成损失的概率。

查看原文

赞 1 收藏 0 评论 0

笋干太咸 赞了文章 · 2018-08-29

计算机网络时间同步技术原理介绍

1.前言

由计算机网络系统组成的分布式系统,若想协调一致进行:IT行业的“整点开拍”、“秒杀”、“Leader选举”,通信行业的“同步组网”之类业务处理,毫秒级甚至微秒级的时间同步是重要基础之一。

2.术语描述

2.0 世界时

世界时UT,可以简单理解为按照地球自转一周来计量24小时的时间标准,由于地球自转速率的变化,世界时的秒长会有微小的变化,每天的快慢可以达到千分之几秒。

2.1 TAI时间

世界时不准,因此国际组织定义了TAI时间,即国际原子时( International Atomic Time),其起点是1958年的开始(世界时UT),以铯原子钟走秒连续计时的时间。

2.2 UTC时间

计算机网络普遍使用的UTC时间(协调世界时),由国际计量局BIPM综合全世界多个守时实验室的钟组计算得到,为了使UTC时间与地球自转1天的时间(世界时UT)协调一致,每隔1到2年, BIPM会通告在UTC时间6月30日或12月31日最后一分钟“加一秒”或“减一秒”等闰秒调整。也就是说,UTC时间会出现60秒或少了59秒的情况。

最近一次闰秒是UTC时间2015年6月底:

2015 年6月30日,23时 59分 59秒
2015 年6月30日,23时 59分 60秒
2015 年7月 1日, 0时 0分 0秒

由于存在闰秒,UTC时间与TAI时间是有差别的,UTC = TAI - n,这个n现在(2016年1月)是36秒,也就是说UTC时间比TAI时间慢了36秒。

2.3 北京时间

北京时间也就是东八区时间,在UTC时间基础上加8小时,中国的北京标准时间由位于陕西的国家授时中心发播。

2.4 GPS时间

由GPS系统通过卫星信号发播的原子时间,GPS时间用自1980年1月6日零点(UTC时间)起的星期数和该星期内内的秒数来表示。

工程上,GPS接收机会根据闰秒数将GPS时间换算为我们通常使用的UTC时间。GPS时间的源头是美国海军天文台的守时原子钟组。

2.5 北斗(BDS)时间

由北斗卫星导航系统通过卫星信号发播的原子时间,同样,北斗接收机会根据闰秒数将北斗时间换算为我们通常使用的UTC时间。

北斗时间的时钟源是位于北京的解放军时频中心的守时原子钟组,陕西的国家授时中心好尴尬:(。

2.6 频率

时间的导数就是频率,机械发条、石英晶体振荡器、原子钟等各种时钟源通过产生频率信号,按照频率均匀打拍计数,模拟时间的等间隔流逝,就有了可见的“时间”。

2.7 频率准确度

手表有准和不准的,反映的就是频率准不准,时钟频率和标准频率的偏差可以用频率准确度来衡量。1E-9量级表示1秒会差1ns,我们使用的个人电脑,它的守时时钟是个32. 768kHz的石英晶振,准确度大概只有2E-5量级(20ppm),也就是说1秒会差0.02ms,1天会跑偏大概2秒。

2.8 时间同步

广义的“时间同步”包括的时间和频率的同步。上级时钟将时间频率信号通过各种有线(以太网、SDH数字网、同轴电缆、电话等)、无线(卫星、长波、电台、微波、WIFI、Zigbee等)链路传递给下级时钟,下级时钟接受时间频率信息后,与上级时钟保持相位、频率的一致。

3.时间同步原理

3.1 单向授时

上级时钟主动发播时间信息,下级用户端被动接受时间信息,并调整本地时钟使时差控制在一定范围内。

要想提高授时精度,用户端必须计算出时间信息在传播链路中的延时,GPS/北斗等卫星授时,可以通过用户端定位与卫星之间距离确定电磁传输延时,消除大部分误差,而电缆、网络等如果是单向授时方式就无法准确计算单向链路时延了。

3.2 双向授时

用户端将接受的时间信息原路返回给上级时钟服务端,服务端将往返时间除以二即得到单向链路时延,再把单向时延告诉客户端,在此基础上,客户端得到服务端更准确的时间信息。比如:北斗单向卫星授时精度100ns,双向卫星授时精度可做到20ns。

3.3 网络时间同步

网络时间同步,特指在计算机网络内的服务器与客户端之间利用网络报文交换实现的时间同步。

鉴于计算机网络传输路径的不确定性和中间路由交换设备转发报文时间的不确定性,通过单播或多播实现的单向网络授时是不可靠的。因此,前辈们发明的网络时间同步技术NTP/PTP等,基本原理都是通过对网络报文打时间戳(标记),往返交换报文计算传输时延和同步误差。

3.4 频率同步

频率同步指的是主从时钟的频率误差保持在一定范围内,频率同步有2种类型:

第1种是直接传递模拟频率信号,比如用电缆或光缆传递10MHz、5Mhz、2.048MHz等标准频率,或者传递bit位宽脉冲;

第2种是通过测量得到的主从时钟时差,通过锁定主从相差实现频率锁定(PLL),或者间接计算频率偏差,完成频率修正。

4.计算机网络时间同步

计算机网络时间同步只是时间同步的一种应用场景,其时间传递的链路可能是SDH网、以太网、WIFI无线网络等。

4.1 NTP

NTP(Network Time Protocol)从1985年诞生来,目前仍在在大部分的计算机网络中起着同步系统时间的作用。

  • 基本原理

服务器和客户端之间通过二次报文交换,确定主从时间误差,客户端校准本地计算机时间,完成时间同步,有条件的话进一步校准本地时钟频率。

  • 时间同步过程

服务器在UDP的132端口提供授时服务,客户端发送附带T1时间戳(Timestamp)的查询报文给服务器,服务器在该报文上添加到达时刻T2和响应报文发送时刻T3,客户端记录响应报到达时刻T4。

改个维基的图:

NTP时间同步

  • 时差计算

维基这个图中用蓝色标注了主从直接来回链路的时延Sigma
Sigma = (t4-t1)-(t3-t2)
因此,假设来回网络链路是对称的,即传输时延相等,那么可以计算客户端与服务器之间的时间误差Delta为:
Delta = t2-t1-Sigma/2=((t2-t1)+(t3-t4))/2

客户端调整自身的时间Delta,即可完成一次时间同步。

  • 计时方式

NTP采用UTC时间计时,NTP时间戳包括自1900-01-01 00:00:00开始的UTC总秒数,当前秒的亚秒数。

当正闰秒时,60秒和下一分钟的0秒的NTP总秒数一致的,因此NTP报文专门定义了闰秒指示域来提示。

  • 误差分析

局域网内计算机利用NTP协议进行时间同步,时间同步精度在5ms左右,主要误差包括:

1)计算机打时间戳的位置在应用层,受协议栈缓存、任务调度等影响,不能在网络报文到来时马上打戳;
2)各种中间网络传输设备带来的传输时延不确定性以及链路的不对称性,将进一步降低NTP时间同步精度。

4.2 PTP

为克服NTP的各种缺点,PTP(Precision Time Protocol,精确时间同步协议)应运而生,最新协议是IEEE1588v2,可实现亚微秒量级的时间同步精度。

  • 基本原理

主从节点在网络链路层打时间戳,利用支持IEEE1588协议的PHY片,精准记录时间同步网络报文接受或发送的时刻。交换机、路由器等网络中间节点准确记录时间同步报文在其中停留的时间,实现对链路时延的准确计算。

  • 时间同步过程

PTP默认使用组播协议,二层或四层UDP组播都可以,一般我们使用基于UDP组播,使用319和320两个端口。

PTP定义了三种角色:OC、BC和TC。我们一般接触的是OC:主时钟和从时钟,交换机、路由器一般是BC或TC。

由于硬件性能有限,网络报文发送时记录的时刻信息,可以在随后的Follow_Up跟随报文中发出,这就是PTP的双步模式(Two-step)。

下图是两OC主从时钟之间的同步过程:

ptp时间同步

  • a.主时钟向从时钟发送Sync报文,并在本地记录发送时间t1;从时钟收到该报文后,记录接收时间t2。

  • b.时钟发送Sync报文之后,紧接着发送一个携带有t1的Follow_Up报文。

  • c.从时钟向主时钟发送Delay_Req报文,用于发起反向传输延时的计算,并在本地记录发送时间t3;主时钟收到该报文后,记录接收时间t4。

  • d.主时钟收到Delay_Req报文之后,回复一个携带有t4的Delay_Resp报文。

  • 时差计算

与NTP一样的原理,从时钟根据拥有的t1~t4这四个时间戳,由此可计算出主、从时钟间的往返总延时为:
Sigma = (t4-t1)-(t3-t2)

假设网络是对称的,从时钟相对于主时钟的时钟偏差为:
Delta = t2-t1-Sigma/2=((t2-t1)+(t3-t4))/2

  • 计时方式

与NTP不同,PTP采用TAI世界原子时间计时,而且PTP计时的起点与unix时间一致,即UTC时间1970年1月1日0点。

PTP主钟会告知从钟,当前UTC相对于TAI的累计偏移量,从钟据此计算当前准确的UTC时间。

  • 误差分析

PTP能准确记录报文发送和接受的时间,也能计算中间链路的延时,剩下影响最大的就是网络链路的不对称性了。

在实际工程中,网络中间链路设备不支持PTP协议,大大降低了PTP的同步精度。目前,PTP主要应用在通信同步网、电力同步网等行业网络系统里。

  • 同步拓扑

PTP域中所有的时钟节点都按一定层次组织在一起,可通过BMC(Best Master Clock,最佳主时钟)协议动态选举出最优时钟,最优时钟的时间最终将被同步到整个PTP域中。

BMC算法与STP(Spaning Tree Protocl)生成树协议类似,最终形成无环路的树形网络拓扑,且都是动态选举,能适应最佳主时钟切换的变化。

  • 扩展应用——PTP over SDH

充分利用各行业已有的SDH通信网络,利用PTP-E1信号转换设备,架设PTP同步网络,除了需要考虑链路倒换问题之外,SDH网络的时延稳定性可大幅提升网络时间同步精度。

4.3 SyncE同步以太网

以太网最早只能传输数据信号,有另外独立的频率同步网络,随着以太网的快速发展,SyncE(Synchronized Ethernet)同步以太网技术诞生后,企业们有了新的选择。

  • 基本原理

时钟节点利用以太网(1000M、1G、10G等)物理层的空闲间隙,传递位宽时钟信号,实现时钟频率信号(25M、125M等)的自上而下传递。

  • 协议控制

类似于SDH网络等时间间隔传递的SSM同步状态信息,同步以太网(Sync-E)利用链路层ESMC协议封装传递SSM信息,SSM信息包含时钟质量信息,接收端据此选择合适的上级网络时钟。

  • 应用

一般商业PHY片提供SyncE功能选项,开启该功能模式,即可利用PHY恢复出来的频率信号,校准本地时钟频率或分频后用于本地计时。

修改记录

2016-5-25: 增加世界时,NTP和PTP计时方式。

查看原文

赞 17 收藏 16 评论 2

笋干太咸 分享了头条 · 2017-12-07

对于currentTimeMillis,有理有据令人信服的文章

赞 2 收藏 1 评论 0

笋干太咸 发布了文章 · 2016-12-26

Kafka Log Compaction 解析

最近查看Kafka文档, 发现 Kafka 有个 Log Compaction 功能是我们之前没有留意到的, 但是有着很高的潜在实用价值.

什么是Log Compaction

Kafka 中的每一条数据都有一对 Key 和 Value, 数据存放在磁盘上, 一般不会被永久保留, 而是在到达一定的量或者时间后对最早写入的数据进行删除. Log Compaction 在默认的删除规则之外提供了另一种删除过时数据(或者说保留有价值的数据)的方式, 就是对于有相同 Key 的不同数据, 只保留最后一条, 前面的数据在合适的情况下删除.

Log Compaction 的应用场景

Log Compaction 特性, 就实时计算而言, 可以在灾难恢复方面有很好地应用场景. 比如说我们在 Storm 里做计算时, 需要长期在内存里维护一些数据, 这些数据可能是通过聚合了一天或者一周的日志得到的, 这些数据一旦由于偶然的原因(磁盘,网络等)崩溃了, 从头开始计算需要漫长的时间.一个可行的应对方法是定时将内存里的数据备份到外部存储中, 比如 Redis 或者 Mysql 等, 当崩溃发生的时候再从外部存储读回来继续计算.

使用 Log Compaction 来代替这些外部存储有以下好处.

  1. Kafka 既是数据源又是存储工具, 可以简化技术栈, 降低维护成本.

  2. 使用 Mysql 或者 Redis 作为外部存储的话, 需要将存储的 Key 记录下来, 恢复时再用这些 Key 将数据取回, 实现起来有一定的工程复杂度. 用Log Compaction 特性的话只要把数据一股脑儿地写进 Kafka, 等灾难恢复的时候再读回内存就行了.

  3. Kafka 针对磁盘读写都有很高的顺序性, 相对于 Mysql 没有索引查询等工作量的负担, 可以实现高性能, 相对于 Redis 而言, 它可以充分利用廉价的磁盘而对内存要求很低, 在接近的性能下能实现非常高的性价比(仅仅针对灾难恢复这个场景而言).

实现方式的简要介绍

当 topic 的 cleanup.policy (默认为delete) 设置为 compact 时, Kafka 的后台线程会定时把 topic 遍历两次, 第一次把每个 key 的哈希值最后一次出现的 offset 都存下来, 第二次检查每个 offset 对应的 key 是否在更后面的日志中出现过,如果出现了就删除对应的日志.

Compaction前后的结果对比

源码解析

Log Compaction 的大部分功能由CleanerThread完成, 核心逻辑在 Cleaner 的 clean方法

/**
   * Clean the given log
   *
   * @param cleanable The log to be cleaned
   *
   * @return The first offset not cleaned and the statistics for this round of cleaning
   */
  private[log] def clean(cleanable: LogToClean): (Long, CleanerStats) = {
    val stats = new CleanerStats()

    info("Beginning cleaning of log %s.".format(cleanable.log.name))
    val log = cleanable.log

    // build the offset map
    info("Building offset map for %s...".format(cleanable.log.name))
    val upperBoundOffset = cleanable.firstUncleanableOffset
    buildOffsetMap(log, cleanable.firstDirtyOffset, upperBoundOffset, offsetMap, stats)
    // <----- 这里第一次遍历所有offset将key索引
    val endOffset = offsetMap.latestOffset + 1
    stats.indexDone()

    // figure out the timestamp below which it is safe to remove delete tombstones
    // this position is defined to be a configurable time beneath the last modified time of the last clean segment
    val deleteHorizonMs =
      log.logSegments(0, cleanable.firstDirtyOffset).lastOption match {
        case None => 0L
        case Some(seg) => seg.lastModified - log.config.deleteRetentionMs
    }

    // determine the timestamp up to which the log will be cleaned
    // this is the lower of the last active segment and the compaction lag
    val cleanableHorizonMs = log.logSegments(0, cleanable.firstUncleanableOffset).lastOption.map(_.lastModified).getOrElse(0L)

    // group the segments and clean the groups
    info("Cleaning log %s (cleaning prior to %s, discarding tombstones prior to %s)...".format(log.name, new Date(cleanableHorizonMs), new Date(deleteHorizonMs)))
    for (group <- groupSegmentsBySize(log.logSegments(0, endOffset), log.config.segmentSize, log.config.maxIndexSize))
      cleanSegments(log, group, offsetMap, deleteHorizonMs, stats)
    // <-- 这里第二次遍历所有offset,删除冗余的日志,并且将多个小的segment合并为一个
    // record buffer utilization
    stats.bufferUtilization = offsetMap.utilization

    stats.allDone()

    (endOffset, stats)
  }

log compaction 通过两次遍历所有数据来实现, 两次遍历之间交流的媒介就是一个
OffsetMap, 下面是OffsetMap的签名

trait OffsetMap {
  def slots: Int
  def put(key: ByteBuffer, offset: Long)
  def get(key: ByteBuffer): Long
  def clear()
  def size: Int
  def utilization: Double = size.toDouble / slots
  def latestOffset: Long
}

这基本就是个普通的mutable map, 在 Kafka 项目中,它的实现只有一个, 叫做SkimpyOffsetMap

put方法

put 方法会为每个 key 生成一份摘要,默认使用 md5 方法生成一个 16byte 的摘要, 根据这个摘要在 bytes 中哈希的到一个下标, 如果这个下标已经被别的摘要占据, 则线性查找到下个空余的下标为止, 然后在对应位置插入该 key 对应的 offset

/**
 * Associate this offset to the given key.
 * @param key The key
 * @param offset The offset
 */
override def put(key: ByteBuffer, offset: Long) {
  require(entries < slots, "Attempt to add a new entry to a full offset map.")
  lookups += 1
  hashInto(key, hash1)
  // probe until we find the first empty slot
  var attempt = 0
  var pos = positionOf(hash1, attempt)  
  while(!isEmpty(pos)) {
    bytes.position(pos)
    bytes.get(hash2)
    if(Arrays.equals(hash1, hash2)) {
      // we found an existing entry, overwrite it and return (size does not change)
      bytes.putLong(offset)
      lastOffset = offset
      return
    }
    attempt += 1
    pos = positionOf(hash1, attempt)
  }
  // found an empty slot, update it--size grows by 1
  bytes.position(pos)
  bytes.put(hash1)
  bytes.putLong(offset)
  lastOffset = offset
  entries += 1
}

get方法

get 方法使用和 put 同样的摘要算法获得 key 的摘要, 通过摘要获得 offset 的存储位置

/**
  * Get the offset associated with this key.
  * @param key The key
  * @return The offset associated with this key or -1 if the key is not found
  */
 override def get(key: ByteBuffer): Long = {
   lookups += 1
   hashInto(key, hash1)
   // search for the hash of this key by repeated probing until we find the hash we are looking for or we find an empty slot
   var attempt = 0
   var pos = 0
   //we need to guard against attempt integer overflow if the map is full
   //limit attempt to number of slots once positionOf(..) enters linear search mode
   val maxAttempts = slots + hashSize - 4
   do {
    if(attempt >= maxAttempts)
       return -1L
     pos = positionOf(hash1, attempt)
     bytes.position(pos)
     if(isEmpty(pos))
       return -1L
     bytes.get(hash2)
     attempt += 1
   } while(!Arrays.equals(hash1, hash2))
   bytes.getLong()
 }

可能的空间问题 性能问题 冲突问题

空间问题

默认情况下, Kafka 用 16 个 byte 存放key的摘要, 用 8 个 byte 存放摘要对应的 offset, 1GB 的空间可以保存 1024* 1024*1024 / 24 = 44,739,242.666... 个 key 对应的数据.

性能问题

这个 log compaction 的原理挺简单, 就是定期把所有日志读两遍,写一遍, cpu 的速度超过磁盘完全不是问题, 只要日志的量对应的读两遍写一遍的时间在可接受的范围内, 它的性能就是可以接受的.

冲突问题

现在的 OffsetMap 唯一的实现名字叫做 SkimpyOffsetMap, 相信你们已经从这个名字里看出端倪, 最初的作者本身也认为这样的实现不够严谨. 这个算法在两个 key 的 md5 值相同的情况下就判断 key 是相同的, 如果遇到了 key 不同而 md5 值相同的情况, 那两个 key 中其中一个的消息就丢失了. 虽然 md5 值相同的概率很低, 但如果真的碰上了, 那就是100%, 概率值再低也没用, 而且从网上的反映看似乎冲突还不少见.

我个人目前想到的处理方案是, 大部分的 key 总长度并不算长, 可以把这类 key 所有可能的情况都md5一遍看一下是否有冲突, 如果没有的话就放心用.

查看原文

赞 3 收藏 4 评论 0

笋干太咸 回答了问题 · 2016-12-15

如何在终端下下载sbt相关的所有依赖

来源 http://www.scala-sbt.org/0.12...

可以使用sbt update-classifierssbt update-sbt-classifiers
能把所有依赖下载完

关注 1 回答 1

笋干太咸 提出了问题 · 2016-12-15

如何在终端下下载sbt相关的所有依赖

因为IDEA导入SBT项目有许多依赖要下载,下载过程慢而不透明,设置代理也不方便,所以希望在终端中能下载完.

关注 1 回答 1

笋干太咸 回答了问题 · 2016-11-25

Java 如何在gc输出日志参数里加上pid和时间戳

自问自答,可以用 %p 和 %t
http://bugs.java.com/view_bug...

关注 1 回答 1

认证与成就

  • 获得 34 次点赞
  • 获得 23 枚徽章 获得 0 枚金徽章, 获得 9 枚银徽章, 获得 14 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2011-12-08
个人主页被 959 人浏览