Vert.x 线程模型揭秘

Vert.x是一个在JVM开发reactive应用的框架,可用于开发异步、可伸缩、高并发的Web应用(虽然不限于web应用)。其目的在于为JVM提供一个Node.js的替代方案。开发者可以通过它使用JavaScript、Ruby、Groovy、Java,甚至是混合语言来编写应用。
使用Vertx.x框架,可以用JavaScript、CoffeeScript、Ruby、Python、Groovy或Java开发应用程序的组件,最终应用程序可以是混合语言构建的。

本文试图揭示Vert.x的线程模型的应用,通过源代码分析Vert.x如何使用线程池处理请求的,以及比较Netty和Vert.x使用线程池的异同。

也许你觉得奇怪,默认启动一个Vert.x Verticle实例,它只用一个线程处理事件,在多核的情况下你需要创建多个Verticle实例以充分利用多个CPU Core的性能。

阅读全文

超全的Go Http路由框架性能比较

使用Go开发Web应用非常方便,它自己的路由器default request multiplexer超级简单,但是功能也有限,所幸net/http库的设计非常好,很容易实现自己定义的路由器,所以你如果在github搜一下,会找到很多的第三方的路由库。

但是这些路由库良莠不齐,尤其是早期实现的路由器,有些实现了很差的路由算法,有些没有仔细考虑内存的分配,导致垃圾回收的问题。

Julien Schmidt在实现HttpRouter库的时候将测试代码抽象出一个测试框架,用来测试Go的各种的路由器,测试的库相当的全。这个测试框架放在了github上。

对于架构师和Go Web开发人员来说,这个测试确实是一份值得参考的资料,在选择一款路由框架的时候非常有帮助。

路由是Go Web框架的一个功能,它可以将不同的URL映射到相应的处理方法上。一些库只实现了路由的功能,也有一些库实现了完整的Web框架的特性,如上下文管理,Session的维护,模版的处理,ORM等。本文只比较路由的性能。

阅读全文

一个速度快内存占用小的一致性哈希算法

一致性哈希最早由 MIT的 Karger 提出,在发表于1997年的论文 Consistent Hashing and Random Trees: Distributed Caching Protocols for Relieving Hot Spots on the World Wide Web, Karger et al 和合作者们提出了一致性哈希的概念(consistent hash),用来解决分布式Cache的问题。这篇论文中提出在动态变化的Cache环境中,哈希算法应该满足的4个适应条件::Balance(均衡)、Monotonicity(单调性)、Spread(分散性)、Load(负载)。

在分布式缓存系统中使用一致性哈希算法时,某个节点的添加和移除不会重新分配全部的缓存,而只会影响小部分的缓存系统,如果均衡性做的好的话,当添加一个节点时,会均匀地从其它节点移一部分缓存到新的节点上;当删除一个节点的时候,这个节点上的缓存会均匀地分配到其它活着的节点上。

一致性哈希缓存还被扩展到分布式存储系统上。数据被分成一组Shard,每个Shard由一个节点管理,当需要扩容时,我们可以添加新的节点,然后将其它Shard的一部分数据移动到这个节点上。比如我们有10个Shard的分布式存储系统,当前存储了120个数据,每个Shard存储了12个数据。当扩容成12个Shard时,我们从每个Shard上拿走2个数据,存入到新的两个Shard上,这样每个Shard都存储了10个数据,而整个过程中我们只移动了20/120=1/6的数据。

Karger 一致性哈希算法将每个节点(bucket)关联一个圆环上的一些随机点,对于一个键值,将其映射到圆环中的一个点上,然后按照顺时针方向找到第一个关联bucket的点,将值放入到这个bucke中。因此你需要存储一组bucket和它们的关联点,当bucket以及每个bucket的关联点很多的时候,你就需要多一点的内存来记录它。这个你经常在网上看到的介绍一致性哈希的算法(有些文章将节点均匀地分布在环上,比如节点1节点2节点3节点4节点1节点2节点3节点4……, 这是不对的,在这种情况下节点2挂掉后它上面的缓存全部转移给节点3了)。

其它的一致性算法还有Rendezvous hashing, 计算一个key应该放入到哪个bucket时,它使用哈希函数h(key,bucket)计算每个候选bucket的值,然后返回值最大的bucket。buckets比较多的时候耗时也较长,有人也提出了一些改进的方法,比如将bucket组织成tree的结构,但是在reblance的时候花费时间又长了。

Java程序员熟悉的Memcached的客户端Spymemcached、Xmemcached以及Folsom都提供了Ketama算法。其实Ketama算法最早于2007年用c 实现(libketama),很多其它语言也实现了相同的算法,它是基于Karger 一致性哈希算法实现:

  • 建立一组服务器的列表 (如: 1.2.3.4:11211, 5.6.7.8:11211, 9.8.7.6:11211)
  • 为每个服务器节点计算一二百个哈希值
  • 从概念上讲,这些数值被放入一个环上(continuum). (想象一个刻度为 0 到 2^32的时钟,这个时钟上就会散落着一些数字)
  • 每一个数字关联一个服务器,所以服务器出现在这个环上的一些点上,它们是哈希分布的
  • 为了找个一个Key应该放入哪个服务器,先哈希你的key,得到一个无符号整数, 沿着圆环找到和它相邻的最大的数,这个数对应的服务器就是被选择的服务器
  • 对于靠近 2^32的 key, 因为没有超过它的数字点,按照圆环的原理,选择圆环中的第一个服务器。

以上两种算法可以处理节点增加和移除的情况。对于分布式存储系统,当一个节点失效时,我们并不期望它被移除,而是使用备份节点替换它,或者将它恢复起来,因为我们不期望丢掉它上面的数据。对于这种情况(节点可以扩容,但是不会移除节点),Google的 John Lamping, Eric Veach提供一个高效的几乎不占用持久内存的算法: Jump Consistent Hash

阅读全文

spymemcached vs. xmemcached vs. Folsom

我创建了一个项目,用来比较Java memcached client的性能:Java-Memcached-Clients-Benchmark

spymemcachedxmemcached是Java中常用的Memcached的客户端。

spymemcached最早是由Dustin Sallings创建,至少从2006年就开始开发spymemcached,作为共同创建者加入到Couchbase以后到现在spymemcached由Couchbase来维护,2014年2月他加入了Google,兴趣也似乎转为了Golang。

xmemcached是原淘宝员工killme2008(庄晓丹, 花名“伯岩”),现在在LeanCloud工作, killme2008的兴趣在于clojure。xmemcached也是一个历史悠久的项目了,1.2.5版发布于2010年。

Folsom是一个比较新的Java Memcached client,它是现在如日中天的正版流媒体音乐服务平台Spotify的工程师们创建的,并且应用于公司的平台中。因为新(2015年初才第一次提交),所以多扯几句。Folsom基于Netty实现,并在编程风格也更现代,异步,链式调用,metrics,代码简洁而有效。由有丰富经验的工程师维护,并且深度应用于他们公司高并发的产品应用中,没有遇到什么问题,值得信赖。

spymemcached和xmemcached开发比较早,代码容易带有历史的包袱,但是因为它们已经广泛地应用于许多实际的项目中,开发人员比较熟悉。

spymemcached的项目描述是这样的:"A simple, asynchronous, single-threaded memcached client written in java. ", 异步和单线程是它的特点,单个的client只使用单个的IO线程,这有一个陷阱,如果你的应用中有很多线程,那么分配给spymemcached client的线程的CPU时间片可能很少,这会降低spymemcached性能。这一点xmemcached也好很多。

我还遇到过spymemcached的一个

xmemcached我觉得有问题的是没有提供异步接口,尽管它有一些方法提供了xxxWithNoRely,但是实际并不是异步的编程方式,只是忽略了memcached的返回,因为你想想异步地处理get方法,没办法,只能自己再包装。而且我测试的时候发现setWithNoReply还不稳定,抛出异常。

Folsom API非常的现代,但是了解它的人不多,所以没有经过广大的人民群众的检验,或许还没有暴露问题,另外文档也缺乏,只能看它的API和单元测试了解它的使用。

我还增加了一个Go memcached客户端的测试 gomemcache, 它是目前最常用的Go的客户端库。因为Go语言编程风格的原因,它所有的操作都是同步的,业务访问可以通过goroutine的方式实现异步访问。

阅读全文

Golang序列化框架对决 - 为什么andyleap/gencode那么快?

我在github上创建了一个Go语言序列化/反序列化库的性能比较的项目gosercomp,用来比较常见的Go语言生态圈的序列化库。
性能是以Go官方库提供的JSON/XML序列化库为基准,比较一下第三库能带来多大的性能提升。
尽管一些第三方库会自动产生Struct的代码,我们还是都以下面的数据结构为例:

1
2
3
4
5
type ColorGroup struct {
Id int `json:"id" xml:"id,attr" msg:"id"`
Name string `json:"name" xml:"name" msg:"name"`
Colors []string `json:"colors" xml:"colors" msg:"colors"`
}

其中Colors是一个slice。我并没有测试Struct嵌套以及循环引用的情况。

目前本项目包含了以下几种序列化库的性能比较:

对于序列化库的实现来讲,如果在运行时通过反射的方式进行序列化和反序列化,性能不会太好,比如官方库的Json和Xml序列化方法,所以高性能的序列化库很多都是通过代码生成在编译的时候提供序列化和反序列化的方法,下面我会介绍MessagePackgencode两种性能较高的序列化库。

本项目受alecthomas/go_serialization_benchmarks项目的启发。

阅读全文

[转]各大互联网公司架构演进之路汇总

原文地址:各大互联网公司架构演进之路汇总 by HollisChuang
请转载时务必保留文章的上述原始出处。

Web


支付宝和蚂蚁花呗的技术架构及实践
支付宝的高可用与容灾架构演进
聚划算架构演进和系统优化 (视频+PPT)
淘宝交易系统演进之路 (专访)
淘宝数据魔方技术架构解析
淘宝技术发展历程和架构经验分享(视频+PPT)(2.3日更新)
高德——快速转型时期的稳定性架构实践(视频+PPT)(2.3日更新)
秒杀系统架构分析与实战
腾讯社区搜索架构演进(视频+PPT)
京东峰值系统设计
京东咚咚架构演进
新浪微博平台架构
微博图床架构揭秘
微博推荐架构的演进
当当网系统分级与海量信息动态发布实践
当当网架构演进及规划实现(视频+PPT)
LinkedIn架构这十年
Facebook’s software architecture(英文)
从0到100——知乎架构变迁史
豆瓣的基础架构
搜狗搜索广告检索系统-弹性架构演进之路(视频+PPT)
小米网抢购系统开发实践
小米抢购限流峰值系统「大秒」架构解密
海尔电商峰值系统架构设计最佳实践
唯品会峰值系统架构演变
1号店电商峰值与流式计算
蘑菇街如何在双11中创造99.99%的可用性
麦包包峰值架构实践
苏宁易购:商品详情系统架构设计
携程的技术演进之路
篱笆网技术架构性能演进(视频+PPT)
从技术细节看美团的架构(1.26日更新)
美团云的网络架构演进之路(2.3日更新)
百度开放云大数据技术演进历程(视频+PPT)(2.3日更新)
途牛供应链系统的架构演进(视频+PPT)(2.3日更新)
Airbnb架构要点分享(2.3日更新)
12306核心模型设计思路和架构设计(2.20日更新)

无线


阿里无线技术架构演进
支付宝钱包客户端技术架构(2.3日更新)
手机淘宝构架演化实践
手淘技术架构演进细节
手机淘宝移动端接入网关基础架构演进之路
微信后台系统的演进之路
微信红包的架构设计简介
微信Android客户端架构演进之路
Android QQ音乐架构演进(视频+PPT)
快的打车架构实践
Uber 四年时间增长近 40 倍,背后架构揭秘
Uber容错设计与多机房容灾方案
大众点评移动应用的架构演进(视频+PPT)
饿了么移动APP的架构演进

其他


魅族实时消息推送架构
魅族云端同步的架构实践和协议细节



欢迎补充!~

Java Stream 详解

Stream是 Java 8新增加的类,用来补充集合类。

Stream代表数据流,流中的数据元素的数量可能是有限的,也可能是无限的。

Stream和其它集合类的区别在于:其它集合类主要关注与有限数量的数据的访问和有效管理(增删改),而Stream并没有提供访问和管理元素的方式,而是通过声明数据源的方式,利用可计算的操作在数据源上执行,当然BaseStream.iterator()BaseStream.spliterator()操作提供了遍历元素的方法。

Java Stream提供了提供了串行和并行两种类型的流,保持一致的接口,提供函数式编程方式,以管道方式提供中间操作和最终执行操作,为Java语言的集合提供了现代语言提供的类似的高阶函数操作,简化和提高了Java集合的功能。

本文首先介绍Java Stream的特点,然后按照功能分类逐个介绍流的中间操作和终点操作,最后会介绍第三方为Java Stream做的扩展。

前年年底的时候我写了一些关于Java 8 Lambda和Stream的文章,本文应该在那个时候完成。后来忙于项目和写《Scala集合技术手册》(Scala Collections Cookbook)这本书,一直没来得及写Java Stream的文章,现在这篇文章算是对 Java Stream的一个总结吧。

阅读全文

Java CompletableFuture 详解

Future是Java 5添加的类,用来描述一个异步计算的结果。你可以使用isDone方法检查计算是否完成,或者使用get阻塞住调用线程,直到计算完成返回结果,你也可以使用cancel方法停止任务的执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class BasicFuture {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(10);
Future<Integer> f = es.submit(() ->{
// 长时间的异步计算
// ……
// 然后返回结果
return 100;
});
// while(!f.isDone())
// ;
f.get();
}
}

虽然Future以及相关使用方法提供了异步执行任务的能力,但是对于结果的获取却是很不方便,只能通过阻塞或者轮询的方式得到任务的结果。阻塞的方式显然和我们的异步编程的初衷相违背,轮询的方式又会耗费无谓的CPU资源,而且也不能及时地得到计算结果,为什么不能用观察者设计模式当计算结果完成及时通知监听者呢?

很多语言,比如Node.js,采用回调的方式实现异步编程。Java的一些框架,比如Netty,自己扩展了Java的 Future接口,提供了addListener等多个扩展方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
future.addListener(new ChannelFutureListener()
{
@Override
public void operationComplete(ChannelFuture future) throws Exception
{
if (future.isSuccess()) {
// SUCCESS
}
else {
// FAILURE
}
}
});

Google guava也提供了通用的扩展Future:ListenableFutureSettableFuture 以及辅助类Futures等,方便异步编程。

1
2
3
4
5
6
7
8
9
10
11
final String name = ...;
inFlight.add(name);
ListenableFuture<Result> future = service.query(name);
future.addListener(new Runnable() {
public void run() {
processedCount.incrementAndGet();
inFlight.remove(name);
lastProcessed.set(name);
logger.info("Done with {0}", name);
}
}, executor);

Scala也提供了简单易用且功能强大的Future/Promise异步编程模式

作为正统的Java类库,是不是应该做点什么,加强一下自身库的功能呢?

在Java 8中, 新增加了一个包含50个方法左右的类: CompletableFuture,提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,提供了函数式编程的能力,可以通过回调的方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。

下面我们就看一看它的功能吧。

阅读全文

Web框架性能基准测试 (Round 12)

以前我发布过 techempower的 第11轮的测试第9轮的测试,现在第12轮的测试出来了,结果肯定又会出人意料。

techempower的测试有好几个case,我们以每个request包含12个数据库的插入操作为例,看看各个web框架的性能,以TPS为指标排序(每秒返回的response多的在前面,性能越好)

go fasthttp + postgresql居然排到了第一,啧啧,以前前几名都是C/C++的框架排第一,这次C++ web框架wt排在了第二。

nodejs + Mysql表现不俗,排在了第6位。

undertow edge + Postgres排在了第8位

依然没有netty的测试,我期待Netty的表现,应该比undertow要高吧。

Go原生web框架的表现不好,排名很低。

Java生态圈的框架如Spring、dropwizard等表现的不温不火。

排名靠前的Web框架

异步编程模型的说明

前几日在看到Alexander Temerev在github上创建了一个项目skynet,用来比较各语言的并发编程的性能,当时觉得这个项目挺有趣,也就翻译整理了一下,写了一篇文章:1百万线程的性能,并且分享在开发者头条上。

本身这个项目涉及的语言很多,测试代码也不一定完全合理,因此这个项目的issue中也有很多讨论,也有很多贡献者提供了其它语言的测试代码,或者完善了现有的测试代码。

很多读者也对这个项目感兴趣,也在我的文章中留言表达了自己的观点,我很赞赏这些有意义的观点。

这个项目的描述是"Skynet 1M threads microbenchmark",我也采用了这样的描述作为文章的标题。当然大家都知道这个项目比较的并不是1百万线程的性能,
而是各个语言中为并发编程实现的类似线程的编程模型,这个各个语言为并发编程而做的努力。

当然,每个编程语言实现的并发编程模型也不尽相同,我们没办法用一个统一的概念称呼它们,姑且叫做"线程”吧。

各种语言的编程模型从内存实现上可以分为两类:

  1. 基于共享内存的模型:采用单一的统一的内存镜像,并发单元通过共享内存进行通讯,比如Java中的线程
  2. 基于消息传递的模型:每个并发单元包含自有的内存,并发单元通过消息交换进行通讯,比如go channel,Scala Actor等

从实现上来说,至少有三种模型实现:

  1. 基于线程的实现: 大部分的操作系统(轻量级的进程、内核级、用户级)、Java、C、C++都是这种实现
  2. 基于Actor的实现:Scala, Erlang等
  3. 基于Coroutine的实现:Haskell, Python等

当然,有些语言也不止一种实现,比如Python还有Pykka,它是一种actor的实现。 Java也可以使用Akka Actor实现Actor模型,比如Go实现了goroutine和CSP模型(channel)。

Paul Butcher写了一本书,叫七周七并发模型, 对并发编程想了解的不妨看看。

参考文档:

  1. https://en.wikipedia.org/wiki/Light-weight_process
  2. https://en.wikipedia.org/wiki/Coroutine
  3. https://en.wikipedia.org/wiki/Green_threads
  4. http://tutorials.jenkov.com/java-concurrency/concurrency-models.html
  5. https://en.wikipedia.org/wiki/Concurrency_%28computer_science%29#Models
  6. https://en.wikipedia.org/wiki/Communicating_sequential_processes
  7. http://www.amazon.com/Seven-Concurrency-Models-Weeks-Programmers/dp/1937785653
  8. http://berb.github.io/diploma-thesis/original/056_other.html
  9. http://grid.cs.gsu.edu/~tcpp/curriculum/sites/default/files/Programming%20with%20Concurrency%20-%20Threads%20Actors%20and%20Coroutines.pptx