内存分布式消息中间件中间件,你看好哪个

百度知道 - 信息提示
知道宝贝找不到问题了&_&!!
该问题可能已经失效。
秒以后自动返回&p&简单且实用的当属Viewstamped Replication,图灵奖得主杰作,比paxos还要早,只是最近随着raft的红火,原作者重新翻版:&br&&a href=&//link.zhihu.com/?target=http%3A//pmg.csail.mit.edu/papers/vr-revisited.pdf& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&pmg.csail.mit.edu/paper&/span&&span class=&invisible&&s/vr-revisited.pdf&/span&&span class=&ellipsis&&&/span&&/a&&/p&
简单且实用的当属Viewstamped Replication,图灵奖得主杰作,比paxos还要早,只是最近随着raft的红火,原作者重新翻版:
&p&事务模型完全不同;&/p&&p&结构上 Highly layered (存储层和计算层彻底分离,连编程语言都分开),方便我们对接其他的计算平台;&/p&&p&SQL 这边优化器的设计和侧重点也完全不一样;&/p&&p&虽然同为 RocksDB + Multi-Raft,但是本质上还是不太一样的,比如处理分裂和合并时的一致性这类的实现方式都不太一样;&/p&&p&还有,我觉得我们稍微牛逼一点 LOL;&/p&&br&&p&当然,我们和友商的目标还是很一致也清晰的,分布式的关系型数据库是未来 RDBMS 的标准形态。&/p&
事务模型完全不同;结构上 Highly layered (存储层和计算层彻底分离,连编程语言都分开),方便我们对接其他的计算平台;SQL 这边优化器的设计和侧重点也完全不一样;虽然同为 RocksDB + Multi-Raft,但是本质上还是不太一样的,比如处理分裂和合并时的一致性…
这个时候发这篇文章难免有蹭热点的嫌疑。。但作为一个计算机体系结构的研究生,在这些名词满天飞的时候,我的好奇心是抑制不住的,想一探这几样技术的究竟。&b&本文不对某一特定事件进行点评,仅从技术角度分析对比一下这三种技术。&/b&就算是当做自己的技术储备+科普了&b&。&/b&&p&首先,这三种技术都是属于闪存(Flash Memory)的不同种类,区别主要在于控制器,接口标准以及更底层的 Flash 芯片标准。它们在电脑/手机等系统中的主要作用是作为存储设备(storage)/文件系统。(注意它们虽然也叫memory,但和运存的 memory是完全两回事儿)&/p&&br&&p&以前电脑系统中的主要的存储设备是机械式磁盘,访问速度慢,体积庞大,功耗高,而且对震动非常敏感,因此很难用于小型化的移动设备里。Flash Memory 出现后,由于没有移动部件,几乎完美解决了以上机械硬盘的各种问题,因此很快在各种移动设备中获得广泛应用。(当然,Flash的写操作和寿命的问题也比较复杂,但这不是本文的讨论重点) 而且基于Flash 的 SSD 硬盘性能普遍好于传统机械硬盘,因此也成为了PC/服务器的主流存储设备。&/p&&p&那么既然 SSD,eMMC,UFS 都是 Flash,它们的区别在哪呢?&/p&&p&想要了解它们的区别,首先要了解他们解决的问题。&/p&&p&SSD 主要作用是取代 PC/服务器 上的 HDD 硬盘,它需要:&/p&&ul&&li&超大容量(百GB~TB级别)&/li&&li&极高的并行性以提高性能&/li&&li&对功耗,体积等要求并不敏感&/li&&li&兼容已有接口技术 (SATA,PCI等)&/li&&/ul&&br&&p&而 eMMC 和 UFS主要都是针对移动设备发明的,它们需要:&/p&&ul&&li&适当的容量&/li&&li&适当的性能&/li&&li&对功耗 ,体积的要求极其敏感&/li&&li&仅需遵循一定的接口标准 (稍后解释)&/li&&/ul&&p&为了直观感受一下区别,我刚才特意找来了一个 PCIe 的 SSD,如下图,上面黑色的芯片就是Flash:&/p&&figure&&img src=&https://pic4.zhimg.com/v2-acd849bd3da262ffb9b3a2_b.jpg& data-rawwidth=&4640& data-rawheight=&2610& class=&origin_image zh-lightbox-thumb& width=&4640& data-original=&https://pic4.zhimg.com/v2-acd849bd3da262ffb9b3a2_r.jpg&&&/figure&&p&我又拆了一个手机,它里面的 Flash 芯片是这样的(中间那个最大的黑色芯片):&figure&&img src=&https://pic2.zhimg.com/v2-657f2a93ac99_b.jpg& data-rawwidth=&2246& data-rawheight=&1737& class=&origin_image zh-lightbox-thumb& width=&2246& data-original=&https://pic2.zhimg.com/v2-657f2a93ac99_r.jpg&&&/figure&&/p&&p&看到区别了吗? (之前写的文章各种被人盗转,只好给图打码了,见谅)&/p&&p&一个SSD,为了达到高并行高性能的要求,&b&有多个Flash 芯片&/b&,这样就可以在每个芯片上进行相互独立的读写操作,以并行性来提高硬盘吞吐量,还可以增加冗余备份。而手机中为了节省空间和功耗,通常只有一片密度较高的 Flash 芯片。&/p&&p&管理一个 Flash 芯片,和管理多个 Flash 芯片,策略肯定是不一样的,因此它们的控制器 (controller)就完全不同了。而且 PC 上需要兼容 SATA 或 PCIe 或 m2 接口,这样你电脑硬盘坏了的时候,可以拔下来换上另一块同样接口的硬盘能照样用。而手机上的 Flash 芯片大多是直接焊在主板上的,基本上不需要考虑更换的问题,所以只要遵从一个特定标准,能和CPU正常通讯就好了。因此接口的不同也是 SSD 和 eMMC,UFS 的重要区别之一。&/p&&p&好了,SSD 和 (eMMC, UFS)的区别说完了,下面说一下可能很多人会关心的 eMMC 和 UFS 的区别了。&/p&&p&eMMC 和 UFS 都是面向移动端 Flash 的标准,区别在于,二者的接口技术大相径庭。&br&&/p&&p&eMMC 是一个起源较早的技术,全称叫 embedded MultiMedia Card,为什么单单e是小写呢? 因为先有的MMC啊。所谓MMC,大家可能没听过但可能见过,相机中用得较多,和SD卡长得很像(之前的图片就搞错了,感谢评论中各位的提醒)。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-b12ab19a87732edf0b952f_b.jpg& data-rawwidth=&1200& data-rawheight=&744& class=&origin_image zh-lightbox-thumb& width=&1200& data-original=&https://pic3.zhimg.com/v2-b12ab19a87732edf0b952f_r.jpg&&&/figure&&br&&p&MMC前面加了个embedded,主要就是为了突出现在这个设备是embedded 在电路板上。eMMC 和 MMC一样,沿用了 8 bit 的并行接口。在传输速率不高的时代,这个接口够用了。但随着设备对接口的带宽要求越来越高,想把并行接口速率提高也越来越难。eMMC 的最新 5.1标准理论最高值最高可以达到400 MB/s,再往上提高频率也不是不行,但就未必划算了。&/p&&p&好在这几年接口串行化大潮轰轰烈烈。所谓接口串行化,简单来说就是工程师们发现:与其用一个比较宽的并行接口以较低的速率传输,用一个串行接口用非常高的速率传输似乎更划算一些(带宽,功率,成本各方面综合考虑)。所以这个时候 UFS 应运而生,用高速串行接口取代了并行接口,而且还是全双工的,也就是可以读写同时进行。所以相比 eMMC, UFS的理论性能提高不少,甚至可以达到一些SSD的水准。可以在下图直观感受一下,蓝色的是UFS,红色的是eMMC,当然是越高越好:&/p&&p&&figure&&img src=&https://pic2.zhimg.com/v2-bad335c4f29a30c049dc_b.jpg& data-rawwidth=&500& data-rawheight=&286& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&https://pic2.zhimg.com/v2-bad335c4f29a30c049dc_r.jpg&&&/figure&(图片引自[1] Micron 的文档)&/p&&p&最后,大家可能比较关心的一个问题:我设计好了一个使用UFS的系统,然后悲伤地发现没有UFS可以用了,那能不能直接换成eMMC呢?&/p&&p&答案是不行的,因为 UFS 和 eMMC 接口完全不兼容,控制器也不可通用。下面两个示意图分别是eMMC和UFS的接口(图片引自[2][3] JEDEC标准)。&/p&&p&&figure&&img src=&https://pic4.zhimg.com/v2-613f7f6da5dc82a3b0a09a_b.jpg& data-rawwidth=&929& data-rawheight=&561& class=&origin_image zh-lightbox-thumb& width=&929& data-original=&https://pic4.zhimg.com/v2-613f7f6da5dc82a3b0a09a_r.jpg&&&/figure&&figure&&img src=&https://pic4.zhimg.com/v2-63ac626aadfcb1d72cd017_b.jpg& data-rawwidth=&733& data-rawheight=&435& class=&origin_image zh-lightbox-thumb& width=&733& data-original=&https://pic4.zhimg.com/v2-63ac626aadfcb1d72cd017_r.jpg&&&/figure&即使是示意图,也能看出两者的明显差别。eMMC有两条总线,分别传输指令数据输入和输出,而且因为是并行总线还要有额外的data strobe。而UFS则是有两条差分的数据lane,指令和数据都是以packet的形式发送的。就更不要提二者的信号线的电气特性也有很大差别了。这些将直接导致控制侧(CPU那边)SoC 的控制器和电路设计会有很大不同。&/p&&p&所以一个系统的SoC以及电路板一定要经过重新设计,才能把 UFS 替换成 eMMC,这不是在生产线上换个 Flash 芯片那么简单的事儿,还得经过比较长时间的设计和测试才行。否则想要快速拿出替代方案的话,恐怕从一开始就要设计兼容两套方案了,嗯。&/p&&p&---------------------------------------------------&/p&&p&评论中有很多业内人士的讨论,我们大概可以得到以下新的结论:&/p&&p&一款SoC可以设计为兼容两种标准的。&/p&&p&相应地,电路板也可以用一套方案兼容两种标准,或者即使用两种不同方案,但成本都不高。&/p&&p&两套方案的软件驱动也不一样。&/p&&p&----------------------------------------------------&/p&&p&[1] &a href=&https://link.zhihu.com/?target=https%3A//www.micron.com/about/blogs/2016/november/ufs-is-here-and-its-very-fast& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&UFS is here, and it’s very fast!&/a&&/p&&p&[2] JEDEC-JESD84-B51&/p&&p&[3] JEDEC-JESD220C&/p&
这个时候发这篇文章难免有蹭热点的嫌疑。。但作为一个计算机体系结构的研究生,在这些名词满天飞的时候,我的好奇心是抑制不住的,想一探这几样技术的究竟。本文不对某一特定事件进行点评,仅从技术角度分析对比一下这三种技术。就算是当做自己的技术储备+…
&figure&&img src=&https://pic4.zhimg.com/v2-d45ad7a9fdb80bda8a71cd28ea077729_b.jpg& data-rawwidth=&950& data-rawheight=&402& class=&origin_image zh-lightbox-thumb& width=&950& data-original=&https://pic4.zhimg.com/v2-d45ad7a9fdb80bda8a71cd28ea077729_r.jpg&&&/figure&我前一段时间在忙一个纯 Rust 的分布式框架。尽管已经存在很多类似并且成熟的框架,但是没有一个是为了 Rust 设计的,有很多轮子要造。&p&&a href=&http://link.zhihu.com/?target=https%3A//github.com/shisoft/bifrost& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&bifrost&/a& 是一个用于建造健壮的分布式系统的框架。它不依赖任何第三方的软件比如 Zookeeper,etcd 或者 consul 来保证一致性。bifrost 具备自己的基于 raft 一致性算法的状态机实现作为多项功能的基石,因为目前开源社区并不存在类似的库。bifrost 包含易用的,可定制的并且快速的 RPC 框架,不需要制定任何协议文件和第三方程序生成客户端存根和服务端 trait。bifrost 实现了简单,同时灵活的 raft 状态机框架,可以用于创建从简单的诸如表数据结构,到检测客户端成员变化的系统。它也具备建造带主从复制的分布式存储引擎的潜力。&figure&&img src=&https://pic4.zhimg.com/v2-e2f73a85ffadf06657e8b_b.png& data-rawwidth=&1353& data-rawheight=&543& class=&origin_image zh-lightbox-thumb& width=&1353& data-original=&https://pic4.zhimg.com/v2-e2f73a85ffadf06657e8b_r.jpg&&&/figure&使用 raft 一致性算法的原因是为了保证高可用性,通过把日志同步到多数的从服务器上再返回给客户端结果。少数崩溃或者缓慢的服务器并不会对数据完整性造成影响。它的读性能理论上可以任意扩展但是写性能会被集群中单个服务器限制。&/p&&p&定义一个 raft 状态机,需要用户自己选择函数类别,决定了状态机和和客户端具体该如何执行函数。目前 bifrost 支持 3 种不同的函数类别:命令,查询和订阅。对于命令函数,客户端必须要把请求发送到主服务器,主服务器会把日志同步到多数从服务器上后执行该命令并返回结果给客户端。对于查询函数,客户端会把请求发送到集群中任意服务器上,服务器会直接执行请求并把结果连带最后一条提交的日志 ID 返回给客户端,客户端会将收到的日志 ID 和已经记录的最后日志 ID 作比较,如果收到的日志 ID 小于记录的最后一条,会查询其他服务器,否则就接受返回值。对于订阅函数,客户端会要求初始化一个接受订阅消息的服务,发送订阅请求到主服务器,主服务器会把订阅请求作为配置变化的日志同步到从服务器,当相应的事件触发时,只有主服务器会给客户端发送订阅消息。&/p&&p&比如,一个非常简单的状态机可以这样定义&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&raft_state_machine! {
def cmd set(v: $t);
def qry get() -& $t;
def sub on_changed() -& ($t, $t);
&/code&&/pre&&/div&&p&用户需要慎重考虑函数类型,因为在查询(qry)请求中,状态机是不可变的。如果需要状态机可变,就必须要定义命令(cmd)函数。&/p&&div class=&highlight&&&pre&&code class=&language-rust&&&span&&/span&&span class=&k&&impl&/span&&span class=&w&& &/span&&span class=&n&&StateMachineCmds&/span&&span class=&w&& &/span&&span class=&k&&for&/span&&span class=&w&& &/span&&span class=&n&&Value&/span&&span class=&w&& &/span&&span class=&p&&{&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&k&&fn&/span&&span class=&w&& &/span&&span class=&n&&set&/span&&span class=&p&&(&/span&&span class=&o&&&&/span&&span class=&k&&mut&/span&&span class=&w&& &/span&&span class=&bp&&self&/span&&span class=&p&&,&/span&&span class=&w&& &/span&&span class=&n&&v&/span&&span class=&o&&:&/span&&span class=&w&& &/span&&span class=&cp&&$t&/span&&span class=&p&&)&/span&&span class=&w&& &/span&&span class=&o&&-&&/span&&span class=&w&& &/span&&span class=&nb&&Result&/span&&span class=&o&&&&/span&&span class=&p&&(),()&/span&&span class=&o&&&&/span&&span class=&w&& &/span&&span class=&p&&{&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&k&&if&/span&&span class=&w&& &/span&&span class=&kd&&let&/span&&span class=&w&& &/span&&span class=&nb&&Some&/span&&span class=&p&&(&/span&&span class=&k&&ref&/span&&span class=&w&& &/span&&span class=&n&&callback&/span&&span class=&p&&)&/span&&span class=&w&& &/span&&span class=&o&&=&/span&&span class=&w&& &/span&&span class=&bp&&self&/span&&span class=&p&&.&/span&&span class=&n&&callback&/span&&span class=&w&& &/span&&span class=&p&&{&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&kd&&let&/span&&span class=&w&& &/span&&span class=&n&&old&/span&&span class=&w&& &/span&&span class=&o&&=&/span&&span class=&w&& &/span&&span class=&bp&&self&/span&&span class=&p&&.&/span&&span class=&n&&val&/span&&span class=&p&&.&/span&&span class=&n&&clone&/span&&span class=&p&&();&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&subs&/span&&span class=&p&&.&/span&&span class=&n&&notify&/span&&span class=&p&&(&/span&&span class=&o&&&&/span&&span class=&n&&commands&/span&&span class=&o&&::&/span&&span class=&n&&on_changed&/span&&span class=&p&&{},&/span&&span class=&w&& &/span&&span class=&nb&&Ok&/span&&span class=&p&&((&/span&&span class=&n&&old&/span&&span class=&p&&,&/span&&span class=&w&& &/span&&span class=&n&&v&/span&&span class=&p&&.&/span&&span class=&n&&clone&/span&&span class=&p&&())));&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&p&&}&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&bp&&self&/span&&span class=&p&&.&/span&&span class=&n&&val&/span&&span class=&w&& &/span&&span class=&o&&=&/span&&span class=&w&& &/span&&span class=&n&&v&/span&&span class=&p&&;&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&nb&&Ok&/span&&span class=&p&&(())&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&p&&}&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&k&&fn&/span&&span class=&w&& &/span&&span class=&n&&get&/span&&span class=&p&&(&/span&&span class=&o&&&&/span&&span class=&bp&&self&/span&&span class=&p&&)&/span&&span class=&w&& &/span&&span class=&o&&-&&/span&&span class=&w&& &/span&&span class=&nb&&Result&/span&&span class=&o&&&&/span&&span class=&cp&&$t&/span&&span class=&p&&,&/span&&span class=&w&& &/span&&span class=&p&&()&/span&&span class=&o&&&&/span&&span class=&w&& &/span&&span class=&p&&{&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&nb&&Ok&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&.&/span&&span class=&n&&val&/span&&span class=&p&&.&/span&&span class=&n&&clone&/span&&span class=&p&&())&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&p&&}&/span&&span class=&w&&&/span&
&span class=&p&&}&/span&&span class=&w&&&/span&
&/code&&/pre&&/div&&p&raft_state_machine 宏不会为了订阅(sub)生成特征函数。在状态机特征函数的实现中,订阅需要像上面前 4 行那样触发。你可以在&a href=&http://link.zhihu.com/?target=https%3A//github.com/shisoft/bifrost/blob/develop/src/store/value.rs& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&/a&看到完整的样例。&/p&&p&使用订阅,只需要传入接收消息的闭包和需要匹配的参数(参数可以留空)。比如一个订阅函数可以这样定义&/p&&div class=&highlight&&&pre&&code class=&language-rust&&&span&&/span&&span class=&n&&def&/span&&span class=&w&& &/span&&span class=&n&&sub&/span&&span class=&w&& &/span&&span class=&n&&on_inserted&/span&&span class=&p&&()&/span&&span class=&w&& &/span&&span class=&o&&-&&/span&&span class=&w&& &/span&&span class=&p&&(&/span&&span class=&cp&&$kt&/span&&span class=&p&&,&/span&&span class=&w&& &/span&&span class=&cp&&$vt&/span&&span class=&p&&);&/span&&span class=&w&&&/span&
&/code&&/pre&&/div&&p&而在客户端使用它,可以这样调用&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&sm_client.on_inserted(move |res| {
if let Ok((key, value)) = res {
println!(&GOT INSERT CALLBACK {:?} -& {:?}&, key, value);
assert_eq!(inserted_stash.get(&key).unwrap(), &value);
&/code&&/pre&&/div&&p&有些时候需要对订阅的函数做一些限制,bifrost 可以在订阅时指定参数。这类订阅函数可以通过给括号中加入参数定义&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&def sub on_key_inserted(k: $kt) -& $
&/code&&/pre&&/div&&p&在服务端状态机触发消息,需要带上参数&/p&&div class=&highlight&&&pre&&code class=&language-text&&&span&&/span&callback.notify(&commands::on_key_inserted{k: k.clone()}, Ok(v.clone()));
&/code&&/pre&&/div&&p&接收这些消息,客户端在订阅时需要带上要订阅的参数,比如下面的例子是 sk1 的一份副本&/p&&div class=&highlight&&&pre&&code class=&language-rust&&&span&&/span&&span class=&n&&sm_client&/span&&span class=&p&&.&/span&&span class=&n&&on_key_inserted&/span&&span class=&p&&(&/span&&span class=&o&&|&/span&&span class=&n&&res&/span&&span class=&o&&|&/span&&span class=&w&& &/span&&span class=&p&&{&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&k&&if&/span&&span class=&w&& &/span&&span class=&kd&&let&/span&&span class=&w&& &/span&&span class=&nb&&Ok&/span&&span class=&p&&(&/span&&span class=&n&&value&/span&&span class=&p&&)&/span&&span class=&w&& &/span&&span class=&o&&=&/span&&span class=&w&& &/span&&span class=&n&&res&/span&&span class=&w&& &/span&&span class=&p&&{&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&println&/span&&span class=&o&&!&/span&&span class=&p&&(&/span&&span class=&s&&&GOT K1 CALLBACK {:?}&&/span&&span class=&p&&,&/span&&span class=&w&& &/span&&span class=&n&&value&/span&&span class=&p&&);&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&assert_eq&/span&&span class=&o&&!&/span&&span class=&p&&(&/span&&span class=&o&&&&/span&&span class=&nb&&String&/span&&span class=&o&&::&/span&&span class=&n&&from&/span&&span class=&p&&(&/span&&span class=&s&&&v1&&/span&&span class=&p&&),&/span&&span class=&w&& &/span&&span class=&o&&&&/span&&span class=&n&&value&/span&&span class=&p&&);&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&p&&}&/span&&span class=&w&&&/span&
&span class=&p&&},&/span&&span class=&w&& &/span&&span class=&n&&sk1&/span&&span class=&p&&.&/span&&span class=&n&&clone&/span&&span class=&p&&());&/span&&span class=&w&&&/span&
&/code&&/pre&&/div&&p&RPC,raft 状态机框架都支持多路复用。RPC 的客户端和服务端可以在一个端口上运行多个网络服务,而 raft 状态机也支持多个子状态机。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-a55eaac7eb052e531bdbf_b.png& data-rawwidth=&300& data-rawheight=&212& class=&content_image& width=&300&&&/figure&&p&这项特性允许用户灵活地复用现有资源,用户需要自行组装服务器和状态机。&/p&&br&&div class=&highlight&&&pre&&code class=&language-rust&&&span&&/span&&span class=&kd&&let&/span&&span class=&w&& &/span&&span class=&n&&addr&/span&&span class=&w&& &/span&&span class=&o&&=&/span&&span class=&w&& &/span&&span class=&nb&&String&/span&&span class=&o&&::&/span&&span class=&n&&from&/span&&span class=&p&&(&/span&&span class=&s&&&127.0.0.1:2100&&/span&&span class=&p&&);&/span&&span class=&w&&&/span&
&span class=&kd&&let&/span&&span class=&w&& &/span&&span class=&n&&raft_service&/span&&span class=&w&& &/span&&span class=&o&&=&/span&&span class=&w&& &/span&&span class=&n&&RaftService&/span&&span class=&o&&::&/span&&span class=&n&&new&/span&&span class=&p&&(&/span&&span class=&n&&Options&/span&&span class=&w&& &/span&&span class=&p&&{&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&storage&/span&&span class=&o&&:&/span&&span class=&w&& &/span&&span class=&n&&Storage&/span&&span class=&o&&::&/span&&span class=&nb&&Default&/span&&span class=&p&&(),&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&address&/span&&span class=&o&&:&/span&&span class=&w&& &/span&&span class=&n&&addr&/span&&span class=&p&&.&/span&&span class=&n&&clone&/span&&span class=&p&&(),&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&service_id&/span&&span class=&o&&:&/span&&span class=&w&& &/span&&span class=&mi&&0&/span&&span class=&p&&,&/span&&span class=&w&&&/span&
&span class=&p&&});&/span&&span class=&w&&&/span&
&span class=&kd&&let&/span&&span class=&w&& &/span&&span class=&n&&server&/span&&span class=&w&& &/span&&span class=&o&&=&/span&&span class=&w&& &/span&&span class=&n&&Server&/span&&span class=&o&&::&/span&&span class=&n&&new&/span&&span class=&p&&(&/span&&span class=&n&&vec&/span&&span class=&o&&!&/span&&span class=&p&&((&/span&&span class=&mi&&0&/span&&span class=&p&&,&/span&&span class=&w&& &/span&&span class=&n&&raft_service&/span&&span class=&p&&.&/span&&span class=&n&&clone&/span&&span class=&p&&())));&/span&&span class=&w&&&/span&
&span class=&kd&&let&/span&&span class=&w&& &/span&&span class=&n&&heartbeat_service&/span&&span class=&w&& &/span&&span class=&o&&=&/span&&span class=&w&& &/span&&span class=&n&&Membership&/span&&span class=&o&&::&/span&&span class=&n&&new&/span&&span class=&p&&(&/span&&span class=&o&&&&/span&&span class=&n&&server&/span&&span class=&p&&,&/span&&span class=&w&& &/span&&span class=&o&&&&/span&&span class=&n&&raft_service&/span&&span class=&p&&);&/span&&span class=&w&&&/span&
&span class=&n&&Server&/span&&span class=&o&&::&/span&&span class=&n&&listen_and_resume&/span&&span class=&p&&(&/span&&span class=&n&&server&/span&&span class=&p&&.&/span&&span class=&n&&clone&/span&&span class=&p&&(),&/span&&span class=&w&& &/span&&span class=&o&&&&/span&&span class=&n&&addr&/span&&span class=&p&&);&/span&&span class=&w&&&/span&
&span class=&n&&RaftService&/span&&span class=&o&&::&/span&&span class=&n&&start&/span&&span class=&p&&(&/span&&span class=&o&&&&/span&&span class=&n&&raft_service&/span&&span class=&p&&);&/span&&span class=&w&&&/span&
&span class=&n&&raft_service&/span&&span class=&p&&.&/span&&span class=&n&&bootstrap&/span&&span class=&p&&();&/span&&span class=&w&&&/span&
&/code&&/pre&&/div&&p&用户需要先定义服务,比如上面的 raft_service 和 heartbeat_service,并使用一个或者多个服务初始化服务器,用户也可以使用 register_service 在服务器初始化后再注册其他服务,就像在 Membership::new 里做的那样。如果需要,用户可以在一台服务器上注册多个 raft 状态机或者其他服务。唯一需要特别照顾的是每个服务要使用不同的服务 ID。&/p&&p&raft 状态机和 RPC 采取相同的理念,用户可以注册多个子状态机到 RaftService 的引用上使得,raft 状态机具有一定的意义。在客户端成员变化服务中,Membership::new 初始化函数会通过一下方法为用户做这些事&/p&&div class=&highlight&&&pre&&code class=&language-rust&&&span&&/span&&span class=&n&&raft_service&/span&&span class=&p&&.&/span&&span class=&n&&register_state_machine&/span&&span class=&p&&(&/span&&span class=&nb&&Box&/span&&span class=&o&&::&/span&&span class=&n&&new&/span&&span class=&p&&(&/span&&span class=&n&&Membership&/span&&span class=&w&& &/span&&span class=&p&&{&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&heartbeat&/span&&span class=&o&&:&/span&&span class=&w&& &/span&&span class=&n&&heartbeat_service&/span&&span class=&p&&.&/span&&span class=&n&&clone&/span&&span class=&p&&(),&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&groups&/span&&span class=&o&&:&/span&&span class=&w&& &/span&&span class=&n&&HashMap&/span&&span class=&o&&::&/span&&span class=&n&&new&/span&&span class=&p&&(),&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&members&/span&&span class=&o&&:&/span&&span class=&w&& &/span&&span class=&n&&HashMap&/span&&span class=&o&&::&/span&&span class=&n&&new&/span&&span class=&p&&(),&/span&&span class=&w&&&/span&
&span class=&p&&}));&/span&&span class=&w&&&/span&
&span class=&n&&server&/span&&span class=&p&&.&/span&&span class=&n&&register_service&/span&&span class=&p&&(&/span&&span class=&n&&DEFAULT_SERVICE_ID&/span&&span class=&p&&,&/span&&span class=&w&& &/span&&span class=&n&&heartbeat_service&/span&&span class=&p&&);&/span&&span class=&w&&&/span&
&/code&&/pre&&/div&&p&我在春节完成了对多路复用的升级。它从一定程度上提高了使用复杂度,但是可以更有效地利用资源完成相同的事情。&/p&&p&bifrost 还附带了一些实用工具,其中值得一提的是从 Clojure 借来的绑定功能。用户可以定义一个全局绑定变量并赋予一个默认值。此变量可以在任意时刻重新绑定,在绑定块内一直生效,并且可以在内部任何地方访问。此绑定可是线程可见的,意味着同时在不同线程中绑定此变量并不会相互影响。比如&/p&&div class=&highlight&&&pre&&code class=&language-rust&&&span&&/span&&span class=&n&&def_bindings&/span&&span class=&o&&!&/span&&span class=&w&& &/span&&span class=&p&&{&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&bind&/span&&span class=&w&& &/span&&span class=&n&&val&/span&&span class=&w&& &/span&&span class=&n&&IS_LEADER&/span&&span class=&o&&:&/span&&span class=&w&& &/span&&span class=&kt&&bool&/span&&span class=&w&& &/span&&span class=&o&&=&/span&&span class=&w&& &/span&&span class=&kc&&false&/span&&span class=&p&&;&/span&&span class=&w&&&/span&
&span class=&p&&}&/span&&span class=&w&&&/span&
&/code&&/pre&&/div&&p&会定义一个称为 IS_LEADER 的绑定变量并且它的默认值是 false。它可以通过以下宏重新绑定。&/p&&div class=&highlight&&&pre&&code class=&language-rust&&&span&&/span&&span class=&n&&with_bindings&/span&&span class=&o&&!&/span&&span class=&p&&(&/span&&span class=&n&&IS_LEADER&/span&&span class=&o&&:&/span&&span class=&w&& &/span&&span class=&n&&is_leader&/span&&span class=&p&&(&/span&&span class=&n&&meta&/span&&span class=&p&&)&/span&&span class=&w&& &/span&&span class=&o&&=&&/span&&span class=&w&& &/span&&span class=&p&&{&/span&&span class=&w&&&/span&
&span class=&w&&
&/span&&span class=&n&&meta&/span&&span class=&p&&.&/span&&span class=&n&&state_machine&/span&&span class=&p&&.&/span&&span class=&n&&write&/span&&span class=&p&&().&/span&&span class=&n&&commit_cmd&/span&&span class=&p&&(&/span&&span class=&o&&&&/span&&span class=&n&&entry&/span&&span class=&p&&)&/span&&span class=&w&&&/span&
&span class=&p&&})&/span&&span class=&w&&&/span&
&/code&&/pre&&/div&&p&至此在 commit_cmd 的任何地方,IS_LEADER 一直可以通过调用 get 函数访问到新的绑定值。在 with_bindings 宏以外,或者其他没有绑定的线程中,绑定值始终为 false。&/p&&p&设计模式不期望通过函数传递某些参数时,这个功能会很实用。在上面的例子中,只有少数的子状态机会需要知道当前是否为 raft 主服务器,但一些例状态机如订阅功能需要用到这个变量(只有主服务器才能发送订阅消息),为给每个状态机函数传递这个参数毫无必要。绑定可以减少冗余定义,让代码更加干净。&/p&&p&bifrost 支持 2 种绑定。第一种是前面见过的值绑定,适合基本类型,比如 u64, i8, bool 等等。另一种是引用绑定,bifrost 会把默认或者绑定值套在 Arc 引用计数容器中,用户每次通过 get 获得的是它的一个引用。引用绑定适合用于对象,比如 String 和 HashMap。&/p&&p&有关 bifrost 还有很多可以介绍。现在它还主处于开发阶段,有很多工作需要做才能作为一个可靠的库发表。我会在后面会通过其他文章介绍 RPC 和 raft 状态及框架的架构。 &/p&&p&此文章内容翻译自本人的博客: &a href=&http://link.zhihu.com/?target=http%3A//www.shisoft.org/index.php//bifrost-distributed-system-framework-for-rust/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&bifrost : distributed system framework for rust&/a&&/p&
我前一段时间在忙一个纯 Rust 的分布式框架。尽管已经存在很多类似并且成熟的框架,但是没有一个是为了 Rust 设计的,有很多轮子要造。 是一个用于建造健壮的分布式系统的框架。它不依赖任何第三方的软件比如 Zookeeper,etcd 或者 consul 来保证一…
&figure&&img src=&https://pic2.zhimg.com/v2-bf86d57bf_b.jpg& data-rawwidth=&641& data-rawheight=&350& class=&origin_image zh-lightbox-thumb& width=&641& data-original=&https://pic2.zhimg.com/v2-bf86d57bf_r.jpg&&&/figure&&p&&b&&u&关于作者:&/u&&/b&&/p&&p&&i&张成远,京东云资深架构师。《Mariadb原理与实现》作者,开源项目speedy作者,毕业于东北大学,2012年加入京东数据库研发团队,曾负责京东分布式数据库系统的架构与研发工作,主导了京东分布式数据库系统在公司的落地及大规模推广,目前负责京东云数据库的架构与研发工作。擅长高性能服务器开发、分布式缓存/数据库/存储等大规模分布式系统架构。 &/i&&/p&&p&关于数据库的使用,在京东有几个趋势,早期在京东主要用SqlServer及Oracle也有少量采用MySQL,随着业务发展技术积累及使用成本等因素,很多业务都开始使用MySQL,包括早期使用SqlServer及Oracle的很多核心业务也都渐渐的开始迁移到MySQL,单机的MySQL往往无法支撑这类业务,需要考分布式的解决方案,另外原本使用MySQL的业务随着数据量及访问量的增加也会遇到瓶颈最终也会考虑采用分布式解决的方案,整个京东发展趋势如图1所示。&/p&&figure&&img src=&https://pic2.zhimg.com/v2-bf86d57bf_b.jpg& data-rawwidth=&641& data-rawheight=&350& class=&origin_image zh-lightbox-thumb& width=&641& data-original=&https://pic2.zhimg.com/v2-bf86d57bf_r.jpg&&&/figure&&strong&图1 业务使用数据库演变趋势&/strong&&p&分布式的数据库解决方案有很多种,在各个互联网公司使用得也是非常的普遍,本质上就是将数据拆开存储在多个节点上从而缓解单节点的压力,业务层面也可以根据业务特点自行进行拆分,如图2所示,假设有一张user表,以ID为拆分键,假设拆分成两份,最简单的就是奇数ID的数据落到一个存储节点上,偶数ID的数据落到另外一个存储节点上,实际部署示意图如图3所示。&br&&/p&&p&除了业务层面做拆分,也可以考虑采用较为通用的一些解决方案,主要分为两类,一类是客户端解决方案,这种方案是在业务应用中引入特定的客户端包,通过该客户端包完成数据的拆分查询及结果汇总等操作,这种方案对业务有一定侵入性,随着业务应用实例部署的数量比较大,数据库端可能会面临连接数压力比较大的问题,另外版本升级也比较困难,优点是链路较短,从应用实例直接到数据库。&/p&&strong&&figure&&img src=&https://pic1.zhimg.com/v2-1a292e39e1da8e7402599_b.jpg& data-rawwidth=&824& data-rawheight=&456& class=&origin_image zh-lightbox-thumb& width=&824& data-original=&https://pic1.zhimg.com/v2-1a292e39e1da8e7402599_r.jpg&&&/figure&图2 数据拆分示意图&/strong&&p&另一类是中间件的解决方案,这种方案是提供兼容数据库传输协议及语法规范的代理,业务在连接中间件的时候可以直接使用传统的JDBC等客户端,从而大大减轻了业务开发层面的负担,弊端是中间件的开发难度会比客户端方案稍微高一点,另外网络传输链路上多走了一段,理论上对性能略有影响,实际使用环境中这些系统都是在机房内网访问,这种网络上的影响完全可以忽略不计。&/p&&strong&&figure&&img src=&https://pic4.zhimg.com/v2-7d0c612b2fd8fa2c04b6a7f94e10059a_b.jpg& data-rawwidth=&798& data-rawheight=&517& class=&origin_image zh-lightbox-thumb& width=&798& data-original=&https://pic4.zhimg.com/v2-7d0c612b2fd8fa2c04b6a7f94e10059a_r.jpg&&&/figure&图3 系统部署示意图&/strong&&p&根据上述分析,为了更好的支撑京东大量的大规模数据量的业务,我们开发了一套兼容MySQL协议的分布式数据库的中间件解决方案,我们称之为JProxy,这套方案经过了多次的演变最终完成并支撑了京东全集团的去Oracle/Sqlserver任务。&/p&&p&JProxy第一个版本如图4所示,每个JProxy都会有一个配置文件,我们会在配置文件中配置相应业务的库表拆分信息及路由信息, JProxy接收到SQL以后会对SQL进行解析再根据路由信息决定SQL是否需要重写及该发往哪些节点,等各节点结果返回以后再将结果汇总按照MySQL传输协议返回给应用。&/p&&p&结合上文的例子,当用户查询user这张表时假设SQL语句是select * from user where id = 1 or id = 2,当收到这条SQL以后,JProxy会将SQL拆分为select * from user where id=1 及select * from user where id = 2, 再分别把这两条sql语句发往后端的节点上,最后将两个节点上获取到的两条记录一并返回给应用。&/p&&p&这种方案在业务库表比较少的时候是可行的,随着业务的发展库表的数量可能会不断增加,尤其是针对去Oracle的业务在切换数据库的时候可能是一次切换几张表,下一次再切换另外几张表,这就要求经常修改配置文件。另外JProxy在部署的时候至少需要部署两份甚至多份,如图5所示,此时面临一个问题是如何保证所有的配置文件在不断修改的过程中是完全一致的。在早期运维过程中,我们靠人工修改完一份配置文件,再将相应的配置文件拷贝给其他的JProxy,确保JProxy配置文件内容一致,这个过程心智负担较重且容易出错。&/p&&br&&strong&&figure&&img src=&https://pic4.zhimg.com/v2-bfb41ea58587bcce4ee504_b.jpg& data-rawwidth=&647& data-rawheight=&481& class=&origin_image zh-lightbox-thumb& width=&647& data-original=&https://pic4.zhimg.com/v2-bfb41ea58587bcce4ee504_r.jpg&&&/figure&图4 版本一&/strong&&figure&&img src=&https://pic3.zhimg.com/v2-b4d5af0e27b398adf82e_b.jpg& data-rawwidth=&626& data-rawheight=&305& class=&origin_image zh-lightbox-thumb& width=&626& data-original=&https://pic3.zhimg.com/v2-b4d5af0e27b398adf82e_r.jpg&&&/figure&&p&&strong&图5 配置文件&/strong&&/p&&p&在之后的版本中我们引入了JManager模块,这个模块负责的工作是管理配置文件中的路由元信息,如图6所示。JProxy的路由元信息都是通过JManager来统一获取,我们只需要通过JManager往元数据库里添加修改路由元数据,操作完成以后通知各个JProxy动态加载路由信息就可以保证每个JProxy的路由信息是完全一致的,从而解决维护路由元信息一致性的痛点。&/p&&strong&&figure&&img src=&https://pic4.zhimg.com/v2-94481d0ebeaf79a615f2_b.jpg& data-rawwidth=&720& data-rawheight=&374& class=&origin_image zh-lightbox-thumb& width=&720& data-original=&https://pic4.zhimg.com/v2-94481d0ebeaf79a615f2_r.jpg&&&/figure&图6 版本二&/strong&&p&在提到分布式数据库解决方案时一定会考虑的一个问题是扩容问题,扩容有两种方式,一种我们称之为re-sharding方案,简单的说就是一片拆两片,两片拆为四片,如图7所示,原本只有一个MySQL实例一个shard,之后拆分成shard1和shard2两个分片,之后再添加新的MySQL实例,将shard1拆分成shard11和shard12两个分片,将shard2拆分成shard21和shard22两个分片放到另外新加的MySQL实例上,这种扩容方式是最理想的,但具体实现的时候会略微麻烦一点,我们短期之内选择了另一种偏保守一点在合理预估前提下足以支撑业务发展的扩容模式,我们称之为pre-sharding方案,这种方案是预先拆分在一定时期内足够用的分片数,在前期数据量较少时这些分片可以放在一个或少量的几个MySQL实例上,等后期数据量增大以后可以往集群中加新的MySQL实例,将原本的分片迁移到新添加的MySQL实例上,如图8所示,我们在一开始就拆分成了shard1、shard2、shard3、shard4四个分片,这四个分片最初是在一个MySQL实例上,数据量增大以后我们可以添加新的MySQL实例,将shard3和shard4迁移新的MySQL实例上,整个集群分片数没有发生变化但是容量已经变成了原来的两倍。&/p&&strong&&figure&&img src=&https://pic4.zhimg.com/v2-d222dc85d74cf1ea349a428d92af927b_b.jpg& data-rawwidth=&782& data-rawheight=&300& class=&origin_image zh-lightbox-thumb& width=&782& data-original=&https://pic4.zhimg.com/v2-d222dc85d74cf1ea349a428d92af927b_r.jpg&&&/figure&图7 re-sharding方案&/strong&&figure&&img src=&https://pic1.zhimg.com/v2-36d0fed6af8a078d1184088_b.jpg& data-rawwidth=&616& data-rawheight=&311& class=&origin_image zh-lightbox-thumb& width=&616& data-original=&https://pic1.zhimg.com/v2-36d0fed6af8a078d1184088_r.jpg&&&/figure&&p&&strong&图8 pre-sharding方案&/strong&&/p&&p&Pre-sharding方案相当于通过迁移完成达到扩容的目的,分片位置的变动涉及到数据的迁移验证及路由元数据的变更等一系列变动,所以我们引入了JTransfer系统,如图9所示。JTransfer可以做到在线无缝迁移,迁移扩容时只需提交一条迁移计划,指定将某个分片从哪个源实例迁移到哪个目标实例,可以指定在何时开始迁移任务,等到了时间点系统会自动开始做迁移。整个迁移过程中涉及到迁移基础全量数据和迁移过程中业务访问产生的增量数据,一开始会将基础全量数据从源实例中dump出来到目标实例恢复,确认数据正确以后开始追赶增量数据,当增量数据追赶到一定程度系统预估可以快速追赶结束时,我们会做一个短暂的锁定操作,从而确保将最后的增量全部追赶完成,这个锁定时间也是在提交迁移任务时可以指定的一个参数,比如最多只能锁定20s,如果因为此时访问量突然增大等原因最终剩余的增量没能在20s内追赶完成,整个迁移任务将会放弃,确保对线上访问影响达到最小。迁移完成之后会将路由元信息进行修改,同时将路由元信息推送给所有的JProxy,最后再解除锁定,访问将根据路由打到分片所在的新位置。&/p&&strong&&figure&&img src=&https://pic4.zhimg.com/v2-9d57cb16a7b8a00c781549fc_b.jpg& data-rawwidth=&798& data-rawheight=&485& class=&origin_image zh-lightbox-thumb& width=&798& data-original=&https://pic4.zhimg.com/v2-9d57cb16a7b8a00c781549fc_r.jpg&&&/figure&图9 版本三&/strong&&p&系统在生产环境中使用的时候,除了考虑以上的介绍以外还需要考虑很多部署及运维的事情,首先要考虑的就是系统如何活下来,需要考虑系统的自我保护能力,要确保系统的稳定性,要做到性能能够满足业务需求。&/p&&p&在JProxy内部我们采用了基于事件驱动的网络IO模型同时考虑到多核等特点,将整个系统的性能发挥到极致,在压测时JProxy表现出来的性能随着MySQL实例的增加几乎是呈现线性增长的趋势,而且整个过程中JProxy所在机器毫无压力。&/p&&p&保证性能还不够,还需要考虑控制连接数、控制系统内存等,连接数主要是控制连接的数量这个比较好理解,控制内存主要是指控制系统在使用过程中对内存的需求量,比如在做数据抽数时候,sql语句是类似select * from table这种的全量查询,此时后端所有的MySQL数据会通过多条连接并发地往中间件发送数据,从中间件到应用只有一条连接,如果不对内存进行控制就会造成中间件OOM,在具体实现的时候我们通过将数据压在TCP栈中来控制中间件前后端连接的网络流速从而很好的保证了整个系统的内存是在可控范围内。&/p&&p&另外还需要考虑权限,哪些IP可以访问哪些IP不能访问都需要可以精确的控制,具体到某一张表还需要控制增删改查的权限,我们建议业务在写SQL的时候尽量都带有拆分字段保证SQL都可以落在某个分片上从而保证整个访问是足够的简单可控,我们为之提供了精细的权限控制,可以做到表级别的增删改查权限,包括是否要带有拆分字段,最大程度做到对SQL的控制,保证业务在测试阶段写出不满足期望的SQL都能及时发现,大大降低后期线上运行时的风险。&/p&&p&除了基本的稳定性之外,在整个系统全局上还需要考虑到服务高可用方案。JProxy是无状态的,一个业务在同一个机房内部署至少两个JProxy且必须跨机架的,保证在同一个机房里JProxy是高可用的。在另外的机房会部署再部署两个JProxy,做到跨机房的高可用。除了中间件自身的高可用以外还需要保证数据库层面的高可用,全链路的高可用才是真正的高可用。数据库层面在同一个机房里会按照一主一从部署,在备用机房会再部署一个备,如图10所示。JProxy访问MySQL时通过域名访问,如果MySQL的主出异常数据库会进行相应的主从切换操作,JProxy可以访问到切换以后新的主,如果整个机房的数据库异常可以直接将数据的域名切换到备用机房,保证JProxy可以访问到备用机房的数据库。业务访问JProxy时也是通过域名访问,如果一个机房的JProxy都出现了异常,和数据库类似直接将JProxy前端的域名切换到备用机房,从而保证业务始终都能正常访问JProxy。&/p&&p&数据高可靠也是非常关键的点,我们会这对数据库的数据进行定期备份,将备份数据存储到相应的存储系统中,从而保证数据库中的数据即使被删除依然是可以恢复的。&/p&&strong&&figure&&img src=&https://pic3.zhimg.com/v2-d5bad64b5f3db0c1f633_b.jpg& data-rawwidth=&791& data-rawheight=&411& class=&origin_image zh-lightbox-thumb& width=&791& data-original=&https://pic3.zhimg.com/v2-d5bad64b5f3db0c1f633_r.jpg&&&/figure&图10 部署示意图&/strong&&p&系统在线上运行时候监控报警是极其重要的,监控可以分多个层次,如图11所示,从主机和操作系统的信息到应用系统的信息到特定系统内部特定的信息的监控等,针对操作系统及主机的监控京东有MJDOS系统可以把系统的内存/cpu/磁/网卡/机器负载等各种信息都纳入监控系统,这些操作系统的基础信息对系统异常的诊断非常关键,比如因为网络丢包等引起的服务异常都可以在这个监控系统中及时找到根源。&/p&&p&京东还有统一的监控报警系统UMP,这个监控系统主要是给所有的应用系统服务,所有的应用系统按照一定的规则暴露接口,在UMP系统中注册以后,UMP系统就可以提供一整套监控报警服务,最基本的比如系统的存活监控以及是否有慢查询等。&/p&&p&除了这两个基本的监控系统以外,我们还针对整套中间件系统开发了定制的监控系统JMonitor,之所以开发这套监控系统是因为我们需要采集更多的定制的监控信息,在系统发生异常时能够第一时间定位问题,举个例子当业务发现TP99下降时往往伴随着有慢SQL,应用从发送SQL到收到结果这个过程中经过了JProxy到MySQL又从MySQL经过JProxy再回到应用,这条链路上任何一个环节都可能慢,不管是哪个阶段耗时,我们需要将这种慢SQL的记录精细化,精细到各个阶段都花了多少时间,做到出现慢SQL时能快速准确的找到问题根源快速解决问题。&/p&&p&另外在配合业务去Oracle/SqlServer时,我们不建议使用跨库的事务,但是会出现有一种情况,同一个事务里的SQL都是带有拆分字段的,每条SQL都是单节点的,同一个事务里有多条这种SQL,结果却出现这个事务是跨库的,这种事务我们都会有详细的记录,业务方可以直接通过JMonitor找到这种事务从而更好的进一步改进。除了这个以外,在测试环境时候业务系统一开始写的SQL没有考虑太多的优化可能会出现比较多的慢SQL,这些慢SQL我们都会统一采集在JMonitor系统上进行分析处理,帮助业务方快速迭代调整SQL语句。&/p&&strong&&figure&&img src=&https://pic1.zhimg.com/v2-f6b05fd8cf8e7cadbdd92ecb1a044b54_b.jpg& data-rawwidth=&723& data-rawheight=&487& class=&origin_image zh-lightbox-thumb& width=&723& data-original=&https://pic1.zhimg.com/v2-f6b05fd8cf8e7cadbdd92ecb1a044b54_r.jpg&&&/figure&图11 监控体系&/strong&&p&业务在使用这套系统的时候 要尽量出现避免跨库的SQL,有一个很重要的原因是当出现跨库SQL的时候会耗费MySQL较多的连接如图12所示,一条不带拆分字段的SQL将会发送到所有的分片上,如果在一个MySQL实例上有64个分片,那一条这样的SQL就会耗费这个MySQL实例上的64个连接,这个资源消耗是非常可观的,如果可以控制SQL落在单个分片上可以大大降低MySQL实例上的连接压力。&/p&&strong&&figure&&img src=&https://pic1.zhimg.com/v2-d149e4a6eb731d6d165cf_b.jpg& data-rawwidth=&710& data-rawheight=&305& class=&origin_image zh-lightbox-thumb& width=&710& data-original=&https://pic1.zhimg.com/v2-d149e4a6eb731d6d165cf_r.jpg&&&/figure&图12 连接数&/strong&&p&跨库的分布式事务要要尽量避免,一个是基于MySQL的分布式数据库中间件的方案无法保证严格的分布式事务语义,另一个即使可以做到严格的分布式事务语义支持依然是要尽量避免垮库事务的,多个跨库的分布式事务在某个分片上发生死锁将会造成其他分片上的事务也无法继续导致直接引起大面积的死锁,即使是单节点上的事务也要尽量控制事务小一点,降低死锁发生的概率。&/p&&p&具体的路由策略不同的业务可以特殊对待,以京东分拣中心为例,各个分拣中心的大小差异很大,北京上海等大城市的分拣中心数据量很大其他城市的分拣中心相对会小一点, 针对这种特点我们会给其定制路由策略,做到将大的分拣中心的数据落在特定的性能较好的MySQL实例上,其他小的分拣中心的数据可以按照普通的拆分方式处理。&/p&&p&在JProxy系统层面我们可以支持多租户模式,但考虑到去Oracle/SqlServer的业务往往都是非常重要且数据量巨大的业务,所以我们的系统都是不同的业务独立部署一套,在部署层面避免各个业务之间的互相影响。考虑到独立部署会造成一些资源浪费,我们引入了容器系统,将操作系统资源通过容器的方式进行隔离,从而保证系统资源的充分利用。很多问题没必要一定要在代码层面解决,代码层面解决起来比较麻烦或者不能做到百分之百把控的事情可以通过架构层面来解决,架构层面不好解决的事情可以通过部署的层面来解决,部署层面不好解决的事情可以通过产品层面来解决,解决问题的方式各式各样,需要从整个系统全局角度来综合考量,引用邓公的一句话“不管黑猫白猫,能抓老鼠的就是好猫”,同样的道理能支撑住业务发展的系统就是好的系统。&/p&&p&另外再简单讨论一下为什么基于MySQL的分布式数据库中间件系统无法保证严格的分布式事务语义支持。所谓分布式事务语义本质上就是事务的语义,包含了ACID属性,分别是原子性、一致性、持久性、隔离性。&/p&&p&原子性是指一个事务要么成功要么失败,不能存在中间状态。持久性是指一个事务一旦提交成功那么要做到系统崩溃以后再恢复依然是成功的。隔离性是指各个并发事务之间是隔离的,不可见的,在数据库具体实现上可能会分很多个隔离级别。事务的一致性是指要保证系统要处于一个一致的状态,比如从A账户转了500元到B账户,那么从整体系统来看系统的总金额是没有发生变化的,不能出现A的账户已经减去500元但是B账户却没有增加500元的情况。&/p&&strong&&figure&&img src=&https://pic3.zhimg.com/v2-9afdaeafda226afbe4f4_b.jpg& data-rawwidth=&826& data-rawheight=&397& class=&origin_image zh-lightbox-thumb& width=&826& data-original=&https://pic3.zhimg.com/v2-9afdaeafda226afbe4f4_r.jpg&&&/figure&图13 可串行化调度&/strong&&br&&p&事务在数据库系统中执行的时候有一个可串行化调度的问题,假设有T1、T2、T3三个事务,那么这三个事务的执行的效果应该和三个事务串行执行效果一样,也就是最终效果效果应该是{T1/T2/T3, T1/T3/T2, T2/T1/T3, T2/T3/T1, T3/T1/T2, T3/T2/T1}集合中的一个,当涉及到分布式事务时,每个子事务之间的调度要和全局的分布式事务的调度顺序一致才能满足可串行化调度的要求,如图13所示,T1/T2/T3的三个分布式事务,在一个库中的调度顺序是T1/T2/T3和全局的调度顺序一致,在另一个库中的调度顺序变成了T3/T2/T1,此时站在全局的角度来看就打破了可串行化调度,可串行化调度保证了隔离性的实现,当可串行化调度被打破时自然隔离性也就随之打破,在基于MySQL的分布式中间件方案实现上,因为同一个分布式事务的各个子事务的事务ID是在各个MySQL上生成的,并没有提供全局的事务ID来保证各个子事务的调度顺序和全局的分布式事务一致,导致隔离性是无法保证的,所以说当前基于MySQL的分布式事务是无法保证严格的分布式事务语义支持的。当然随着MySQL引入GR可以做到CAP理论中的强一致,再加强中间件的相关功能及定制MySQL相关功能也是有可能做到支持严格的分布式事务的。&/p&
关于作者:张成远,京东云资深架构师。《Mariadb原理与实现》作者,开源项目speedy作者,毕业于东北大学,2012年加入京东数据库研发团队,曾负责京东分布式数据库系统的架构与研发工作,主导了京东分布式数据库系统在公司的落地及大规模推广,目前负责京东…
&figure&&img src=&https://pic1.zhimg.com/daccf8acc7be84eade1a7fb54ab7c4cf_b.jpg& data-rawwidth=&500& data-rawheight=&188& class=&origin_image zh-lightbox-thumb& width=&500& data-original=&https://pic1.zhimg.com/daccf8acc7be84eade1a7fb54ab7c4cf_r.jpg&&&/figure&&p& Spanner要满足的external consistency是指:后开始的事务一定可以看到先提交的事务的修改。所有事务的读写都加锁可以解决这个问题,缺点是性能较差。特别是对于一些workload中只读事务占比较大的系统来说不可接受。为了让只读事务不加任何锁,需要引入多版本。在单机系统中,维护一个递增的时间戳作为版本号很好办。分布式系统中,机器和机器之间的时钟有误差,并且误差范围不确定,带来的问题就是很难判断事件(在本文,事件指分布式事务版本号)发生的前后关系。反应在Spanner中,就是很难给事务赋予一个时间戳作为版本号,以满足external consistency。在这样一个误差范围不确定的分布式系统时,通常,获得两个事件发生的先后关系主要通过在节点之间进行通信分析其中的因果关系(casual relationship),经典算法包括Lamport时钟等算法。然而,Spanner采用不同的思路,通过在数据中心配备原子钟和GPS接收器来解决这个误差范围不确定的问题,进而解决分布式事务时序这个问题。基于此,Spanner提供了TrueTime API,返回值实际为一个区间[t-ε,t+ε],ε为时间误差,毫秒级,保证当前的真实时间位于这个区间。&/p&&p&
Spanner是一个支持分布式读写事务,只读事务的分布式存储系统,只读事务不加任何锁。和其他分布式存储系统一样,通过维护多副本来提高系统的可用性。一份数据的多个副本组成一个paxos group,通过paxos协议维护副本之间的一致性。对于涉及到跨机的分布式事务,涉及到的每个paxos group中都会选出一个leader,来参与分布式事务的协调。这些个leader又会选出一个大leader,称为coordinator leader,作为两阶段提交的coordinator,记作coordinator leader。其他leader作为participant。&/p&&p&
数据库事务系统的核心挑战之一是并发控制协议。Spanner的读写事务使用两阶段锁来处理。详细流程下图所示。&/p&&p&
如第一段所述,给事务赋予一个时间戳版本号是这样一个分布式存储系统的核心。下面先说如何确定读写事务的版本号,再说只读事务。&/p&&p&
前面已经说了两阶段提交过程中两阶段锁的过程,这里就省略这些,只讨论两阶段提交过程中如何确定最后的读写事务的时间戳版本号。&/p&&p&&strong&读写事务&/strong&&/p&&br&&p&&figure&&img src=&https://pic2.zhimg.com/de97fb744c_b.png& data-rawwidth=&959& data-rawheight=&902& class=&origin_image zh-lightbox-thumb& width=&959& data-original=&https://pic2.zhimg.com/de97fb744c_r.jpg&&&/figure&简要时序图如下:&br&&/p&&figure&&img src=&https://pic2.zhimg.com/34daff622ea5fa_b.jpg& data-rawwidth=&2500& data-rawheight=&1856& class=&origin_image zh-lightbox-thumb& width=&2500& data-original=&https://pic2.zhimg.com/34daff622ea5fa_r.jpg&&&/figure&&p&图中,commit wait分为两段,第一阶段是论文中4.1.2节提到的Start,第二阶段是4.1.2节的Commit Wait &/p&&p&&strong&只读事务&/strong&&/p&&p&
调用TrueTime API,将右区间作为只读事务的版本号,记作Sread, 如果读事务涉及到的副本不够新,那么读会阻塞。每个副本会维护一个时间戳Tsafe, 如果Sread &= Tsafe ,则这个副本够新。Tsafe取两个时间戳的更小值,第一个是副本所在的paxos group中已经apply到状态机的时间戳,第二个是paxos group中leader目前正在参与的还没有commit的分布式事务在该paxos group的最小的prepared timestamp - 1。&/p&&p&
还有一种方法,可以不用客户端等,方法是Sread通过客户端和所有涉及到的paxos group的leader进行协商,每个leader返回他们的最后一次事务的commit时间戳给客户端,然后客户端取最小的版本号作为Sread即可。&/p&&p&下面说一下两阶段提交的错误处理。&/p&&p&
两阶段提交协议由于协调者和参与者的故障可能会有严重的可用性问题。Spanner的两阶段提交实现基于Paxos协议,每个participant和coordinator本身产生的日志都会通过paxos协议复制到自身的paxos group中,从而解决可用性问题。同样以A,B,C三份数据为例,他们分别有三个副本,记作(A1,A2,A3),(B1,B2,B3),(C1,C2,C3),每组作为一个paxos group,内部通过paxos协议保证一致性。假设,A1,B1,C1分别为各自paxos group的leader,A1为coordinator leader。&/p&&p&
Prepare阶段:A1给B1和C1发送prepare消息后,假设B1挂了,A1等待超时,A1给C1发送rollback。B1后续回滚分为两种情况:1. B1在持久化prepare消息之前挂了,B1恢复后可自行回滚 2. 如果B1持久化prepare消息之后挂了,B1自身可以回放日志得知事务未决,主动联系(A1,A2,A3)。A1给B1,C1发送prepare消息之后,自己挂了,同样,A1通过回放日志可以得知。实际上,A1本身挂了之后,A2和A3通过选主协议马上会选出一个新的leader,不至于影响到可用性。&/p&&p&
Commit阶段:A1给B1,C1发送commit消息,B1 commit成功,C1挂了,C1起来后,如果C1之前没有持久化commit消息,则A1主动要求C1继续commit。如果C1之前已经持久化了commit消息,则自己commit。如果C1由于某些原因,始终commit不成功,则由上层业务进行回补操作。&/p&&p&&strong&参考资料&/strong&&/p&&a href=&http://link.zhihu.com/?target=http%3A//static.googleusercontent.com/media/research.google.com/zh-CN//archive/spanner-osdi2012.pdf& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Spanner: Google’s Globally-Distributed Database&/a&&a href=&http://link.zhihu.com/?target=http%3A//www.slideshare.net/josemariafuster1/spanner-osdi3%3Fqid%3Dd84-46c1-a3a2-cad395338eaa%26v%3D%26b%3D%26from_search%3D12& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&presentation:Spanner: Google’s Globally-Distributed Database&/a&
Spanner要满足的external consistency是指:后开始的事务一定可以看到先提交的事务的修改。所有事务的读写都加锁可以解决这个问题,缺点是性能较差。特别是对于一些workload中只读事务占比较大的系统来说不可接受。为了让只读事务不加任何锁,需要引入多版…
&figure&&img src=&https://pic3.zhimg.com/v2-74bf308b81b0ffeaa98d23c992aa2d44_b.jpg& data-rawwidth=&1400& data-rawheight=&720& class=&origin_image zh-lightbox-thumb& width=&1400& data-original=&https://pic3.zhimg.com/v2-74bf308b81b0ffeaa98d23c992aa2d44_r.jpg&&&/figure&&p&转载自:&a href=&http://link.zhihu.com/?target=http%3A//www.cppblog.com/merlinfang/archive//209311.aspx& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&值得推荐的C/C++框架和库 - 蓝色理想 - C++博客&/a&&br&&/p&&h2&值得学习的C语言开源项目&/h2&&h2&- 1. Webbench &/h2&&p&Webbench是一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能,最多可以模拟3万个并发连接去测试网站的负载能力。Webbench使用C语言编写, 代码实在太简洁,源码加起来不到600行。&/p&&p&下载链接:&a href=&http://link.zhihu.com/?target=http%3A//home.tiscali.cz/%7Ecz210552/webbench.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Web Bench Homepage&/a&&/p&&h2&- 2. Tinyhttpd&/h2&&p&tinyhttpd是一个超轻量型Http Server,使用C语言开发,全部代码只有502行(包括注释),附带一个简单的Client,可以通过阅读这段代码理解一个 Http Server 的本质。&/p&&p&下载链接:&a href=&http://link.zhihu.com/?target=http%3A//sourceforge.net/projects/tinyhttpd/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&sourceforge.net/project&/span&&span class=&invisible&&s/tinyhttpd/&/span&&span class=&ellipsis&&&/span&&/a&&/p&&h2&- 3. cJSON&/h2&&p&cJSON是C语言中的一个JSON编解码器,非常轻量级,C文件只有500多行,速度也非常理想。&/p&&p&cJSON也存在几个弱点,虽然功能不是非常强大,但cJSON的小身板和速度是最值得赞赏的。其代码被非常好地维护着,结构也简单易懂,可以作为一个非常好的C语言项目进行学习。&/p&&p&项目主页:&a href=&http://link.zhihu.com/?target=http%3A//sourceforge.net/projects/cjson/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&cJSON&/a&&/p&&h2&- 4. CMockery&/h2&&p&cmockery是google发布的用于C单元测试的一个轻量级的框架。它很小巧,对其他开源包没有依赖,对被测试代码侵入性小。cmockery的源代码行数不到3K,你阅读一下will_return和mock的源代码就一目了然了。&/p&&p&主要特点:&/p&&ul&&li&免费且开源,google提供技术支持;&/li&&li&轻量级的框架,使测试更加快速简单;&/li&&li&避免使用复杂的编译器特性,对老版本的编译器来讲,兼容性好;&/li&&li&并不强制要求待测代码必须依赖C99标准,这一特性对许多嵌入式系统的开发很有用&/li&&/ul&&p&下载链接:&a href=&http://link.zhihu.com/?target=http%3A//code.google.com/p/cmockery/downloads/list& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&code.google.com/p/cmock&/span&&span class=&invisible&&ery/downloads/list&/span&&span class=&ellipsis&&&/span&&/a&&/p&&h2&- 5. Libev&/h2&&p&libev是一个开源的事件驱动库,基于epoll,kqueue等OS提供的基础设施。其以高效出名,它可以将IO事件,定时器,和信号统一起来,统一放在事件处理这一套框架下处理。基于Reactor模式,效率较高,并且代码精简(4.15版本8000多行),是学习事件驱动编程的很好的资源。&/p&&p&下载链接:&a href=&http://link.zhihu.com/?target=http%3A//software.schmorp.de/pkg/libev.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&software.schmorp.de/pkg&/span&&span class=&invisible&&/libev.html&/span&&span class=&ellipsis&&&/span&&/a&&/p&&h2&- 6. Memcached&/h2&&p&Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提供动态数据库驱动网站的速度。Memcached 基于一个存储键/值对的 hashmap。Memcached-1.4.7的代码量还是可以接受的,只有10K行左右。&/p&&p&下载地址:&a href=&http://link.zhihu.com/?target=http%3A//memcached.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&a distributed memory object caching system&/a&&/p&&h2&- 7. Lua&/h2&&p&Lua很棒,Lua是巴西人发明的,这些都令我不爽,但是还不至于脸红,最多眼红。&/p&&p&让我脸红的是Lua的源代码,百分之一百的ANSI C,一点都不掺杂。在任何支持ANSI C编译器的平台上都可以轻松编译通过。我试过,真是一点废话都没有。Lua的代码数量足够小,5.1.4仅仅1.5W行,去掉空白行和注释估计能到1W行。&/p&&p&下载地址:&a href=&http://link.zhihu.com/?target=http%3A//www.lua.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&The Programming Language Lua&/a&&/p&&h2&- 8. SQLite&/h2&&p&SQLite是一个开源的嵌入式关系数据库,实现自包容、零配置、支持事务的SQL数据库引擎。 其特点是高度便携、使用方便、结构紧凑、高效、可靠。足够小,大致3万行C代码,250K。&/p&&p&下载地址:&a href=&http://link.zhihu.com/?target=http%3A//www.sqlite.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SQLite Home Page&/a& 。&/p&&h2&- 9. UNIX v6&/h2&&p&UNIX V6 的内核源代码包括设备驱动程序在内 约有1 万行,这个数量的源代码,初学者是能够充分理解的。有一种说法是一个人所能理解的代码量上限为1 万行,UNIX V6的内核源代码从数量上看正好在这个范围之内。看到这里,大家是不是也有“如果只有1万行的话没准儿我也能学会”的想法呢?&/p&&p&另一方面,最近的操作系统,例如Linux 最新版的内核源代码据说超过了1000 万行。就算不是初学者,想完全理解全部代码基本上也是不可能的。&/p&&p&下载地址:&a href=&http://link.zhihu.com/?target=http%3A//minnie.tuhs.org/cgi-bin/utree.pl%3Ffile%3DV6& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&minnie.tuhs.org/cgi-bin&/span&&span class=&invisible&&/utree.pl?file=V6&/span&&span class=&ellipsis&&&/span&&/a&&/p&&h2&- 10. NETBSD&/h2&&p&NetBSD是一个免费的,具有高度移植性的 UNIX-like 操作系统,是现行可移植平台最多的操作系统,可以在许多平台上执行,从 64bit alpha 服务器到手持设备和嵌入式设备。NetBSD计划的口号是:”Of course it runs NetBSD”。它设计简洁,代码规范,拥有众多先进特性,使得它在业界和学术界广受好评。由于简洁的设计和先进的特征,使得它在生产和研究方面,都有卓越的表现,而且它也有受使用者支持的完整的源代码。许多程序都可以很容易地通过NetBSD Packages Collection获得。&/p&&p&下载地址:&a href=&http://link.zhihu.com/?target=http%3A//www.netbsd.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&The NetBSD Project&/a&&/p&&h2&C++ 资源大全&/h2&&p&关于 C++ 框架、库和资源的一些汇总列表,内容包括:标准库、Web应用框架、人工智能、数据库、图片处理、机器学习、日志、代码分析等。&/p&&h3&标准库&/h3&&p&C++标准库,包括了STL容器,算法和函数等。&/p&&ul&&li&&a href=&http://link.zhihu.com/?target=http%3A//en.wikipedia.org/wiki/C%252B%252B_Standard_Library& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&C++ Standard Library&/a&:是一系列类和函数的集合,使用核心语言编写,也是C++ISO自身标准的一部分。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//en.wikipedia.org/wiki/Standard_Template_Library& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Standard Template Library&/a&:标准模板库&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//en.wikipedia.org/wiki/C_POSIX_library& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&C POSIX library&/a& : POSIX系统的C标准库规范&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/cplusplus& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ISO C++ Standards Committee&/a& :C++标准委员会&/li&&/ul&&h3&框架&/h3&&p&C++通用框架和库&/p&&ul&&li&&a href=&http://link.zhihu.com/?target=http%3A//stdcxx.apache.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Apache C++ Standard Library&/a&:是一系列算法,容器,迭代器和其他基本组件的集合&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//stlab.adobe.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ASL&/a& :Adobe源代码库提供了同行的评审和可移植的C++源代码库。&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/boostorg& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Boost&/a& :大量通用C++库的集合。&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/bloomberg/bde& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&BDE&/a& :来自于彭博资讯实验室的开发环境。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//libcinder.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Cinder&/a&:提供专业品质创造性编码的开源开发社区。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//ryan.gulix.cl/fossil.cgi/cxxomfort/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Cxxomfort&/a&:轻量级的,只包含头文件的库,将C++ 11的一些新特性移植到C++03中。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//dlib.net/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Dlib&/a&:使用契约式编程和现代C++科技设计的通用的跨平台的C++库。&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/paulhodge/EASTL& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&EASTL&/a& :EA-STL公共部分&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/sumeetchhetri/ffead-cpp& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ffead-cpp&/a& :企业应用程序开发框架&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/facebook/folly& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Folly&/a&:由Facebook开发和使用的开源C++库&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/julianstorer/JUCE& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&JUCE&/a& :包罗万象的C++类库,用于开发跨平台软件&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/facebook/libphenom& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&libPhenom&/a&:用于构建高性能和高度可扩展性系统的事件框架。&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/sourcey/libsourcey& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&LibSourcey&/a& :用于实时的视频流和高性能网络应用程序的C++11 evented IO&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/koanlogic/libu& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&LibU&/a& : C语言写的多平台工具库&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//loki-lib.sourceforge.net/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Loki&/a& :C++库的设计,包括常见的设计模式和习语的实现。&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//code.google.com/p/mili/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&MiLi&/a& :只含头文件的小型C++库&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//www.openframeworks.cc/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&openFrameworks&/a& :开发C++工具包,用于创意性编码。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//qt-project.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Qt&/a& :跨平台的应用程序和用户界面框架&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//code.google.com/p/reason/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Reason&/a& :跨平台的框架,使开发者能够更容易地使用Java,.Net和Python,同时也满足了他们对C++性能和优势的需求。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//root.cern.ch/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&ROOT&/a& :具备所有功能的一系列面向对象的框架,能够非常高效地处理和分析大量的数据,为欧洲原子能研究机构所用。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//www.stlport.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&STLport&/a&:是STL具有代表性的版本&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//stxxl.sourceforge.net/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&STXXL&/a&:用于额外的大型数据集的标准模板库。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//www.ultimatepp.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Ultimate++&/a& :C++跨平台快速应用程序开发框架&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//sourceforge.net/projects/wtl/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Windows Template Library&/a&:用于开发Windows应用程序和UI组件的C++库&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/jll63/yomm11& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Yomm11&/a& :C++11的开放multi-methods.&/li&&/ul&&h3&人工智能&/h3&&ul&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/aigamedev/btsk& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&btsk&/a& :游戏行为树启动器工具&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//eodev.sourceforge.net/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Evolving Objects&/a&:基于模板的,ANSI C++演化计算库,能够帮助你非常快速地编写出自己的随机优化算法。&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/andrometa/neu& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Neu&/a&:C++11框架,编程语言集,用于创建人工智能应用程序的多用途软件系统。&/li&&/ul&&h3&异步事件循环&/h3&&ul&&li&&a href=&http://link.zhihu.com/?target=http%3A//think-async.com/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Boost.Asio&/a&:用于网络和底层I/O编程的跨平台的C++库。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//libev.schmorp.de/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&libev&/a& :功能齐全,高性能的时间循环,轻微地仿效libevent,但是不再像libevent一样有局限性,也修复了它的一些bug。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//libevent.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&libevent&/a& :事件通知库&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/joyent/libuv& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&libuv&/a& :跨平台异步I/O。&/li&&/ul&&h3&音频&/h3&&p&音频,声音,音乐,数字化音乐库&/p&&ul&&li&&a href=&http://link.zhihu.com/?target=http%3A//www.fmod.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&FMOD&/a& :易于使用的跨平台的音频引擎和音频内容的游戏创作工具。&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/micknoise/Maximilian& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Maximilian&/a& :C++音频和音乐数字信号处理库&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//www.openal.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&OpenAL&/a& :开源音频库—跨平台的音频API&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//opus-codec.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Opus&/a&:一个完全开放的,免版税的,高度通用的音频编解码器&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//www.speex.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Speex&/a&:免费编解码器,为Opus所废弃&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/TonicAudio/Tonic& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Tonic&/a&: C++易用和高效的音频合成&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//xiph.org/vorbis/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Vorbis&/a&: Ogg Vorbis是一种完全开放的,非专有的,免版税的通用压缩音频格式。&/li&&/ul&&h3&生态学&/h3&&p&生物信息,基因组学和生物技术&/p&&ul&&li&&a href=&http://link.zhihu.com/?target=http%3A//molpopgen.github.io/libsequence/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&libsequence&/a&:用于表示和分析群体遗传学数据的C++库。&/li&&li&&a href=&http://link.zhihu.com/?target=http%3A//www.seqan.de/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&SeqAn&/a&:专注于生物数据序列分析的算法和数据结构。&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/ekg/vcflib& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Vcflib&/a& :用于解析和处理VCF文件的C++库&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//github.com/jewmanchue/wham& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Wham&/a&:直接把联想测试应用到BAM文件的基因结构变异。&/li&&/ul&&h3&压缩&/h3&&p&压缩和归档库&/p&&ul&&li&&a href=&http://link.zhihu.com/?target=http%3A//www.bzip.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&bzip2&/a&:一个完全免费,免费专利和高质量的数据压缩&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//bitbucket.org/attila_afra/doboz/overview& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&doboz&/a&:能够快速解压缩的压缩库&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//icculus.org/physfs/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&PhysicsFS&/a&:对各种归档提供抽象访问的库,主要用于视频游戏,设计灵感部分来自于Quake3的文件子系统。&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//projects.kde.org/projects/frameworks/karchive& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&KArchive&/a&:用于创建,读写和操作文件档案(例如zip和 tar)的库,它通过QIODevice的一系列子类,使用gzip格式,提供了透明的压缩和解压缩的数据。&/li&&li&&a href=&http://link.zhihu.com/?target=https%3A//code.google.com/p/lz4/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&LZ4&/a& :非常快速的压缩算法&/li&&

我要回帖

更多关于 分布式系统中间件 的文章

 

随机推荐