程序员实用硬件体系结构归纳

1. 硬件体系结构

硬件关注大致CPU、CPU Cache(L1, L2, L3)、内存、SSD、普通硬盘HDD、网络网卡这几个层次

2. CPU架构

CPU核内主要技术点包括Super pipeling、Superscalar
、Simulaneous Multi-Threading(SMT)、out-of-order等,目前CPU都使用多核架构,例如来自Intel Core i7的Nehalem架构Fetch to retire,

CPU使用IMC接口与内存互联,使用QPI连接多个处理器((processors 比如Nehalem Quadcore)

2.1 性能指标

参考性能指标包括:

  1. 主频: CPU的时钟频率,内核工作的时钟频率
  2. 外频: 系统总线的工作频率
  3. 倍频: CPU外频与主频相差的倍数
  4. 前端总线: 将CPU连接到北桥芯片的总线
  5. 总线频率: 与外频相同或者是外频的倍数
  6. 总线数据带宽: (总线频率 * 数据位宽) / 8

相关组件有L1,L2,L3 cache (缓存数据与指令)
L1,L2: core独占; 带宽:20-80GB/S;延时:1-5ns
L3: core之间共享; 带宽:10-20GB/S;延时:10ns
Cache line size: 64 Bytes

QPI接口
Intel中,连接一个CPU中的多个处理器,直接互联 QPI带宽:~20GB/s

2.2 程序优化

3. CPU Cache

Cache Line是Cache与内存Memory交换的最小单位,X86架构的CPU基本为64B,以组相联的形式组织

一种方案Write Invalidate写导致其他CPU L1/L2的同一Cache Line失效,另一种Write Update写同时更新其他CPU L1/L2的同一Cache Line

写策略通常有Write Back新脏数据先读到Cache然后修改,Write Through新脏数据直接写到Memory

主要问题是Cache Coherence算法,比如MESI, MOESI等

MESI协议,有四种状态:

  • Invalid 无数据,
  • Shared 与Memory一致数据且多节点共享,
  • Exclusive 与Memory一致数据且单节点持有,
  • Modified 最新修改数据且单节点持有。

有六种交互信息:

  • Read
  • Read Reponse
  • Invalidate
  • Invalidate Acknowledge
  • Read Invalidate
  • Writeback

MOESI协议,五种状态:owned 与Shared共存,持有最新修改且Memory过期

这些协议作用于CPU Cache L1/2和内存,与Register无关

4. Memory

正确理解Memory Ordering与并发的关系

4.1 Atomic

涉及到内存的读写指令,特别是非对齐的双字操作,可以使用专用指令:CMPXCHG, XCHG, XADD等,或使用lock指令(使用在指令之前表示当前指令操作的内存只被当前CPU所用)

4.2 Memory Barrier

程序优化

参考

  1. CPU架构浅析
  2. Memory Barriers: a Hardware View for Software Hackers

分布式锁实现问题讨论

分布式锁除了使用Zookeeper这个常用工具外,也有使用Redis这个KV内存存储进行实现的。某人介绍了一种基于Redis的分布式锁实现方法(命名为Redlock)并刊登到了redis官网:

Redlock实现

某人之后还发文详细讨论了实现方法,并说明自己感觉这个算法并不安全

分布式锁如何实现

接着Redlock算法的原作者开始就此事发文详细讨论,说明这个算法足够安全

Redlock实现安全吗?

与此同时观众们也在发帖论战

观众开帖论战

有人跟帖

论证RedLock问题确实存在

推荐还是运用理论证明一下吧

测试方法

Open Chain系统设计

OpenChain简介

openchain使用C#开发的分布式总账系统,有比较强的横向扩展能力,并利用比特币的区块链保证不可更改性。

常见问题

  1. openchain是一种区块链吗?
    答:不是,其不使用块这个概念,并直接使用事务链。
  2. openchain是一条侧链吗?
    答:提供了模块将openchain部署为侧链,但并非必要步骤。

部署方法

提供docker方法直接部署openchain的服务器,使用sqlite作为默认存储,运行在.NET Core和.NET Framework上面

系统架构

  1. 事务流
    openchain的服务节点可以分为两种:validator节点、observer节点:前者接收事务并进行验证,只有通过验证的才计入总账(验证规则由管理员配置);后者会下载所有通过验证的事务信息流(形成备份?)
  2. 利用区块链
    即利用公链提交事务形成hash(使用OP_RETURN)
  3. 数据结构
    使用Protocol Buffers进行客户端与服务器之间的编解码操作
  4. 总账结构
    kv结构的有层级的总账

Zookeeper系统的不足

之前总结过Zookeeper的各种设计优点,但是这个系统的缺陷与优点同样突出,本文就是结合自己的使用经验,业界给出的评价对ZK的缺点进行的归纳,一方面归纳使用表现上的不足,另一方面根据个人经验总结出系统本身功能设计时的就存在的缺陷。同时也思考了相应对策与改进的办法,算是本人对ZK设计的完整的思考总结吧。最后还关注了下etcd这个后起之秀的设计,看看它是否已经弥补了ZK的不足,能否担当后继者。

1.实际使用暴露的不足

尽管ZK已经在工业界大量使用,但实际使用ZK时就会发现自己仍然面临种种问题,这里总结一下实际使用中系统暴露出的种种不足。

  1. API使用复杂

    使用ZK最直观的感觉就是客户端API使用起来特别麻烦,因为ZK的实际逻辑模型是文件存储,其他逻辑都需要结合使用临时节点和回调机制来实现,API接口与实际需求之间差距非常大,说白了就是ZK官方提供的API太过底层,而且表现力不足。没有http访问接口,只能通过API访问,导致兼容性与表现各异。比如一个细节很容易坑人:

    >

    ZK不能确保任何客户端能够获取(即Read Request)到一样的数据,需要客户端自己同步:方法是客户端在获取数据之前调用org.apache.zookeeper.Async(Callback.VoidCallback, java.lang.Object) 完成sync操作.

  2. 实际客户端逻辑复杂

    因为ZK提供的是非常底层的API,所以要实现一个基础需求,开发者用户自己需要依靠这些底层API在客户端实现并维护一个异常复杂的逻辑,甚至官方自己都一度不能提供正确的逻辑。比如实现分布式锁时,需要开发者自己在客户端处理异步请求锁的时间判断与仲裁,很容易出错。因此实现了多种ZK常用功能的开源项目Curator取代了官方成为ZK事实标准上的客户端了,总算让开发者使用ZK时稍微轻松一些,能够专注到自己的业务逻辑上面。但是Curator方案仍然有问题无法避免。

    而 Curator 这类客户端的复杂性使得支持多语言环境较难,怎样保证两个语言的 recipe 的是行为一致的?基本没有办法通过自动化测试来保证正确性,只能不停地线上踩坑一个个排查。

  3. ZK异常状态判断

    比逻辑更复杂的是开发者总是需要自己同时处理ZK通信中的异常状态,需要正确处理CONNECTION LOSS(连接断开) 和 SESSION EXPIRED(Session 过期)两类连接异常。一个典型的难题就是session的超时机制的问题

    The client will stay in disconnected state until the TCP connection is re-established with the cluster, at which point the watcher of the expired session will receive the “session expired” notification.

    甚至需要考虑包括业务方系统load变高,或者发生长时间gc,导致ZK重连甚至session过期的问题。

  4. 回调次数限制

    ZK中所有Watch回调通知都是一次性的。同一个ZK客户端对某一个节点注册相同的watch,也只会收到一次通知。节点数据的版本变化会触发NodeDataChanged回调,这导致需要重复注册,而如果节点数据的更新频率很高的话,客户端肯定就无法收到所有回调通知了。

  5. Zab协议与全系统的有效性

    实际应用中ZK系统整体可靠性也不一定有保证,比如在跨机房部署方面,由于ZK集群只能有一个Leader,因此一旦机房之间连接出现故障,Leader就只能照顾一个机房,其他机房运行的业务模块由于没有Leader也都只能停掉。于是所有流量集中到有Leader的那个机房,很容易造成系统crash。

    同时ZK对于网络隔离极度敏感,导致系统对于网络的任何风吹草动都会做出激烈反应。这是Zab协议本身的设计,一旦出现网络隔离,ZK就要发起选举流程。悲催的是Zab协议的选举过程比较慢,期间集群没有Leader也不能提供服务。造成本来半秒一秒的网络隔离造成的不可用时间被放大为选举不可用时间。

  6. 性能

    ZK本身的性能比较有限。典型的ZK的tps大概是一万多,单个节点平均连接数是6K,watcher是30万,吞吐似乎还可以,但是时延就没那么乐观了。特别

响应时间、网络、缓存

  1. 服务极限与可扩展性

  2. 存储极限与可扩展性

2.需求满足缺陷

设计层面上的不足

  1. 事务API能力不足
  2. 中心无仲裁能力
  3. 回调次数
  4. 可扩展性
  5. Zab协议

3. 怎样更好的设计

  1. 更丰富有效的API

    支持多IP,自动处理重连与恢复,服务端可以动态加入移除

  2. 委托服务端处理能力
    script能力?像redis支持luaScript
    节点单独超时时间设计
  3. 横向可扩展性
  4. JVM

4. 继任者etcd?

  1. raft协议取代zab
  2. go GC
  3. restful API

5. 参考资料

  1. zookeeper节点数与watch的性能测试
  2. 对Zookeeper的一些分析
  3. ZooKeeper真的low吗?上千节点场景配置服务讨论
  4. Zookeeper常见问题整理
  5. ZAB问题总结
  6. 剖析etcd

Zookeeper系统的优点

Zookeeper系统应用越来越广泛,在同一领域内开源软件方面基本没有其他选择处于垄断地位,但是实际用过的人都会觉得这个软件属于可用但又不那么好用的类型。本文是本人结合自己的实际使用经验与思考,同时参考真正大牛对这个系统的分析与评价进行的总结,主要还是想归纳一下真正的使用需求并思考相应对策与改进的办法。

1.常见应用场景

先归纳一下工程应用中常见的Zookeeper使用场景(以下简称ZK),这里按照个人感觉应用的频率从高到低排序说明。

  1. 可靠存储

    在实际使用中可以表现为配置管理、名字服务,这种应用完全是因为ZK多备份的可靠性强。当然也可以利用回调机制在数据变更时可以进行全体通知。实现起来非常简单而且很有效,所以是应用最广的场景。

  2. 集群管理

    利用ZK的通讯与回调机制完成分布式集群的机器状态监视,甚至很多系统中做主从备份时都会在ZK中注册以方便做热备切换。

  3. 服务注册发现管理

    由可靠存储加上通知回调机制其实满足了服务注册发现的最基本需求,某些在本人看起来不那么靠谱的应用场景,居然也在采用ZK实现。大有一统天下之势,所有类似的需求都开始采用ZK方案,比较出名的系统比如国内的Dubbo和国外的Kafka(居然还把ZK用在了负载均衡上面)、jStorm、Heron(twitter)等等

  4. 选主服务

    选主服务是ZK参考的原始系统Chubby设计出来最初的应用需求,当时是满足BigTable的master选主。ZK最初也是用在HBase里面,而后所有需要选主服务的都在采用,很多KV系统用来方便从多节点中选择一个中心节点(但是本人还真没找到什么)。
    需要注意的是有时选主服务在讨论里也被称为分布式锁的一种,很容易混淆概念。的确使用ZK来实现选主服务(实现方法最好跟分布式锁的方法完全一样,这里官方文档都曾经犯过错误)客观上遵循了时间优先原则,但是实际需求并非一定要满足这条,只要保证关键的唯一性就可以了,因此与同步意义上的锁很是有不同的。

  5. 分布式同步机制

    即真正的分布式锁,但是实际应用并不常见。本人实现过几次,目前准备运用在表单提交的同步上。

  6. 负载均衡

2.特性设计与优势

ZK主要使用场景远不是满足最初设计时对一致性调解的需求,这么受欢迎是因为其灵活的特性设计,只要简单组合就能满足很多种需求,同样将特性的受欢迎程度按照个人感觉从高到低进行说明

  1. 通知回调机制

    通过创建节点与设置Watcher可以很方便的建立回调通知。ZK的所有应用都基于这个特性,没有这个机制那么机器监控相关的应用都不能处理,也就不会诞生后来在服务注册发现相关的使用方法。实际上为分布式系统提供节点间回调通知方法的系统真的很少,甚至可能只有ZK(大家可以提供一些其他答案?)

  2. 可靠存储

    系统设计最初的需求之一,也是ZK特性中实现最好的部分,作为可靠存储ZK基本没出现过问题,仅此一项就可保证其的流行。

  3. 连接状态维护

    ZK自动维护了客户端所在的应用与服务器通信连接状态的变化,可以比较简单地维护系统中的成员通信情况。主要是不需要自己再去处理麻烦的通信状态监控问题,比如断线后自动释放节点并产生回调。

  4. 文件系统模型

    提供文件接口模型而不是锁接口,更具通用性。文件系统模型中文件与目录的概念可以映射多种含有层级关系的其他模型

  5. 自增长序列

    这点包含了锁的本质,但是因为zk的模型设计导致判断与仲裁需在客户端进行

3.实现技术选择与优点

zk本身的系统特性设计很出色,同时选择的实现技术也比较扎实,可谓蕴含相当的分布式系统工程经验在其中。下面结合个人理解讨论下这些实现技术有哪些特别优点与选择时可能的设计思路。

  1. 通讯机制与状态的实现
    基于jute进行编解码处理保证通用性,服务器端通信使用nio或netty都是标准选择。

  2. Zab协议与Paxos
    zk使用Zab协议保证部署的多台机器间构成的整体系统的一致性与可靠性。这个分布式协议类似Paxos但是更加具体有效,实际上Paxos工程实现会碰到很多协议中没有定义的问题,G家员工为在Chubby中使用Paxos算法甚至专门发了一篇文章来说明Paxos工程化踩了多少坑。
    Zab协议中将选主阶段与正常运行之间的阶段用catch up方式进行弥补,而关键的选主阶段使用了一个极其工程化的算法“fast leader election”(这个算法似乎没有经过形式化证明),这个算法足够粗暴有效,实现起来很简单。
    最近Paxos的工程简化版算法Raft很火,所有考虑使用Paxos的系统都在实现Raft协议,其过程与Zab协议很类似,但是选主算法更加简单(可能实现结果是比Zab选主更慢)而且无法如Zab一样简单替换这个部分的算法。个人看法是Zab协议比Raft更容易理解,而且容易工程实现。(为何没有Raft火爆?值得研究,但是Raft目前还没有工业级的系统验证!)

  3. 使用JVM
    zk作为一个以稳定性与一致性为主的系统,性能上面肯定有一点损失。相信大家实现这种系统首先都会考虑要利用语言本身的速度优势尽量弥补系统的性能损失,于是我们就能看到很多c++实现的类zk系统(比如Chubby),但是这些新系统却没有zk的普及率。
    可以说zk的流行中很重要的一点就是牺牲部分可能的性能使用JVM作为底层。正是因为虚拟机的使用屏蔽了各种异构系统底层,让zk可以很容易的稳定部署在多台配置性能都可能各异的机器上。个人理解这也是为什么现在那么多分布式系统都基于JVM技术栈,分布式系统需求的机器多,不可能所有配置都一样,而且机器都需要很容易进行物理替换或是系统升级,目前还只有JVM可以非常简单的提供这种等级的虚拟化屏蔽。
    当然最近docker容器技术大放异彩,轻量级虚拟化方案以极快的速度兴起,让各种异构系统有了更简单可定制的底层虚拟化方式,也许有可能改变分布式系统的底层技术栈。

4. 参考资料

  1. 可靠消息队列浅谈
  2. 为什么不应该使用ZooKeeper做服务发现
  3. 持续可用与CAP理论 – 一个系统开发者的观点
  4. Kafka深度解析
  5. Zookeeper架构设计与应用要点
  6. Zab vs. Paxos
  7. 架构师需要了解的Paxos原理、历程及实战

Xshell及Vim中文乱码解决

首先要保证Linux本地语言设置,主要是LC_ALL的设置(该设置会覆盖其他设置),可以使用locale查看。临时修改使用export LC_ALL=”zh_CN.UTF-8”,永久修改需要修改文件/etc/profile增加或修改export LC_ALL=zh_CN.UTF-8,修改完后无需重启。

其次要保证Xshell的连接编码使用utf-8而非默认编码。最后设置vim修改~/.vimrc增加set fileencodings=utf-8,gbk一行

为何线程互斥锁优于读写锁

尽管确实是在读多写少的情况下使用,但系统在测试中确实出现线程互斥锁优于读写锁的情况,

muduo库中给出了多种互斥锁优于读写锁的原因:

1. 读锁加锁的开销不会比mutex小,因为它需要更新当前reader的数目,如果临界区很小,那么mutex往往更快。

实际上mutex是由整形变量及等待队列构成

2. 读锁可能提升为写锁,增大调试难度

实际上系统调试过程中的确出现过这样的问题,甚至导致死锁,当时是以这样顺序调用的:

写申请(获得写锁)->读申请(读申请被忽略)->释放(实际把写锁释放)->释放(实际获得读锁)->下次的写申请(卡死)

3. 写锁会阻塞后面的读锁,在要求读低延迟的情况下使用并不适当。

这条完全不明白。

至于spinlock与互斥锁的使用对比在这篇文章《Pthreads mutex vs Pthreads spinlock》介绍得十分清楚。

 

本站总访问量