• Feeds

  • Archive for November, 2009


    CN Erlounge IV tweets

    前言

    本文整理了Erlounge IV Erlang杭州开发者大会现场的记录的Tweets列表,建议先到 http://ecug.org/agenda/ 下载演讲文档,以便理解上下文。
    Twitter的优势是小之美,整理成一篇大文章比较杂乱,仅希望能给不在现场的读者在阅读演讲稿时提供一些参考的信息。
    文中 @user 方式表示用户的 twitter id,下文中提到的部分讲师Twitter id为@litaocheng @eric33yu @zoomq @sj_mackenzie @qiezi @bobcui @xushiwei @houmingyuan @aimingoo 同时也欢迎follow我的twitter: @xmpp
    Erlounge, Hangzhou
    (Erlounge IV第一天参会人员合影)

    Day 1

    @litaocheng 的《Erlang开发实践》如果要做Erlang具体开发的话(而不是所谓“架构”),建议看看后续公布的视频,这个是非常好的教程。由于当时没抢到无线IP,所以没有twitter记录

    @eric33yu 的 《Erlang系统调优》开始了,放弃无线上网,开始用手机发,幸亏BB打字快

    Erlang process 调度原则 尽量让一个cpu忙

    讲的都是实战精华,比如斑马书中提到进程字典不是一个好的编程习惯,但yufeng从实战角度鼓励用进程字典,因为进程字典性能非常好。

    模式匹配相同的标签尽量放在一起 如tuple,以提高效率

    讲erlang集群 所有节点都需要相互维持一个tcp连接 并要相互维持一个90s一次的心跳 开销很大

    讲erlang大量数据不要用tuple,array,list来存 最好用ets

    linux systemtap 来观测程序(profiler)很有用 非侵入式 不需要修改程序

    Erlang发送binary时候最好大于256长度,否则每份数据都需要copy,比如一次广播发送多份下行数据。

    讲erlang上不要把ulimit -n 设太高 因为每个handler 需要占用约200字节开销 即使没用

    yufeng讲他每做一个服务器程序,都投入30%时间编写log代码记录重要参数 以便运行期间观察

    讲erlang中如需跨语言通讯 推荐asn.1(不少现场同学也倾向于protocol buffers)

    Erlang中config set/get不适合用独立进程去做 推荐ets

    发现大部分erlang programmer是c++ 出身, 特别关注IO效率, cpu利用, 多核, 上下文切换开销, 内存分配/回收,总而言之就是性能, 另外一方面也许是 @eric33yu 忽悠成功 :)

    恶狼战役的视频很震撼,这个是在ppt上看不到的 2:20 PM Nov 7th

    erlang由于没有共享变量,所以进程间通讯是用message还是ets(memory db)的使用技巧是语言的特色 2:22 PM Nov 7th

    恶狼战役的项目专门给初学者练手用,都是用基本语法,没用什么什么高级特性 2:38 PM Nov 7th

    现在来自香港的老外Stewart Mackenzie讲 An Erlang Implementation of Restms,中英文夹杂很幽默,引起笑声阵阵 3:12 PM Nov 7th

    @sj_mackenzie讲他选择erlang的故事,平时他是靠java make $ 3:14 PM Nov 7th

    @sj_mackenzie解释了message queue的作用,并对比了xmpp, AMQP, atompub的一些不足,所以他建议用RestMS 3:34 PM Nov 7th

    @sj_mackenzie对key value store 也很感兴趣,他推荐了另外一个erlang based riak, 也是一个dynamo克隆 http://riak.basho.com/ 3:47 PM Nov 7th

    @sj_mackenzie 开始介绍 dynamo 理论了,这个是网上ppt没有的,so yet another dynamo fans. :) 3:49 PM Nov 7th

    @sj_mackenzie 介绍 riak performance, 单机上write 18k/s, read 25k/s 3:53 PM Nov 7th
    @sj_mackenzie 介绍 riak 还有一个link的特性,就像html的link一样,又向老外了解了一些link的细节 3:59 PM Nov 7th

    @sj_mackenzie 现在是trustno1讲cuda变成,开场白是Parallel和concurrent的区别 4:27 PM Nov 7th

    trustno1讲concurrent is coarse-grained Parallelism 4:33 PM Nov 7th

    trustno1讲”premature optimization is the root of all evil”翻译成“过早优化是万恶之源”是不准确的,premature应该说是草率的优化更准确 4:45 PM Nov 7th

    trustno1今天继续走“让听众听不懂”的路线,说是如果大家都听懂了他就给领导交辞呈,现在正在讲直流电平位移转为有符号数,然后突然停下来问大家前面内容有没有疑问,没人回应,然后话题继续 5:17 PM Nov 7th

    Day 1, Evening

    现在是 @bobcui 和 @qiezi 来分享actor erlang进程模型在c++事件,用51.com好友接口服务来做业务场景说明 8:10 PM Nov 7th

    @bobcui 正在介绍51.com底层服务concurrent实现思路,coroutine协程每秒可以切换200万次,比context switch有很大优势 8:27 PM Nov 7th

    @bobcui 介绍完coroutine spawn之后,引起听众讨论协程的一些缺陷,比如调度器没法主动调度,没有隔离,单个process出了问题没法像erlang那样隔离保护,伪erlang粉丝与纯erlang粉丝展开了pk 8:51 PM Nov 7th
    c_coroutine
    (插图:51.com C++实现的轻量级进程模型)

    纯erlang粉丝Ery Lee质疑这套c++的协程模型ac_actor,“这套模型有商用吗”, @bobcui 急了,”我们51.com已经有一百多台服务器在跑这个” 9:05 PM Nov 7th

    @xushiwei 开讲各种服务器编程模型的区别,进程fork方式,Boost ASIO, 轻量process其实都很相似,不管由os还是应用来管理调度,核心问题是看哪个进程够轻量 9:27 PM Nov 7th

    @xushiwei 谈到Boost ASIO缺点不够优雅,业务代码被切分支离破碎。但轻量级进程实现简单自然,易于维护且高效,不可避免是服务器编程发展方向 9:35 PM Nov 7th

    Day 2

    御风行的 @houmingyuan 讲网络程序的压力测试,使用erlang/otp的client比c++/python一样的机器人简单方便很多(原话:舒服太多),一个erlang process一个机器人,代码约1500行,一个星期可以做得非常完善,包括流量监控支持 10:42 AM Nov 8th
    erlang_cm
    (插图:御风行的Erlang连接服务器)

    御风行的连接服务器/网关,由c++改成erlang之后,代码由7924减少到2000行,状态机状态及事件都减少到一半以下。以及模式匹配比c++优雅舒适很多,性能和c++差不多,而且erlang是新手写的,c++是老手 11:02 AM Nov 8th

    御风行的erlang连接服务器/网关,6500并发下,测试流量达到3w-4w packet/s 11:18 AM Nov 8th

    说到erlang连接服务器6500并发下,测试流量达到3w-4w packet/s,不少提问没找到缺陷,又伤害了现场不少c++, boost asio粉丝脆弱的心灵 11:26 AM Nov 8th

    @eric33yu 补充erlang内存分配的方式,erlang有200多种内存分配的方式,比一般同学自己写的内存池有不可比拟的优势。另外由于大部分erlang大牛都是c++/erlang双剑客,所以大部分c++粉丝的质疑都被轻松驳回 11:36 AM Nov 8th

    发现erlang缺少一本类似effective java/c++这样的书,现场工程师有很多类似问题    12:48 PM Nov 8th

    C, Erlang, Java and Go Web Server performance test

    I had tested a hello world web server in C, Erlang, Java and the Go programming language.
    * C, use the well-known high performance web server nginx, with a hello world nginx module
    * Erlang/OTP
    * Java, using the MINA 2.0 framework, now the JBoss Netty framework.
    * Go, http://golang.org/

    1. Test environment

    1.1 Hardware/OS

    2 Linux boxes in a gigabit ethernet LAN, 1 server and 1 test client
    Linux Centos 5.2 64bit
    Intel(R) Xeon(R) CPU E5410  @ 2.33GHz (L2 cache: 6M), Quad-Core * 2
    8G memory
    SCSI disk (standalone disk, no other access)

    1.2 Software version

    nginx, nginx-0.7.63.tar.gz
    Erlang, otp_src_R13B02-1.tar.gz
    Java, jdk-6u17-linux-x64.bin, mina-2.0.0-RC1.tar.gz, netty-3.2.0.ALPHA1-dist.tar.bz2
    Go, hg clone -r release https://go.googlecode.com/hg/ $GOROOT (Nov 12, 2009)

    1.3 Source code and configuration

    Linux, run sysctl -p

    net.ipv4.ip_forward = 0
    net.ipv4.conf.default.rp_filter = 1
    net.ipv4.conf.default.accept_source_route = 0
    kernel.sysrq = 0
    kernel.core_uses_pid = 1
    net.ipv4.tcp_syncookies = 1
    kernel.msgmnb = 65536
    kernel.msgmax = 65536
    kernel.shmmax = 68719476736
    kernel.shmall = 4294967296
    kernel.panic = 1
    net.ipv4.tcp_rmem = 8192	873800	8738000
    net.ipv4.tcp_wmem = 4096	655360	6553600
    net.ipv4.ip_local_port_range = 1024	65000
    net.core.rmem_max = 16777216
    net.core.wmem_max = 16777216

    # ulimit -n
    150000

    C: ngnix hello world module, copy the code ngx_http_hello_module.c from http://timyang.net/web/nginx-module/

    in nginx.conf, set “worker_processes  1; worker_connections 10240″ for 1 cpu test, set “worker_processes  4; worker_connections 2048″ for multi-core cpu test. Turn off all access or debug log in nginx.conf, as follows

    worker_processes  1;
    worker_rlimit_nofile 10240;
    events {
        worker_connections  10240;
    }
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  0;
        server {
            listen       8080;
            server_name  localhost;
            location / {
                root   html;
                index  index.html index.htm;
            }
              location /hello {
                ngx_hello_module;
                hello 1234;
            }
    
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }

    $ taskset -c 1 ./nginx or $ taskset -c 1-7 ./nginx

    Erlang hello world server
    The source code is available at yufeng’s blog, see http://blog.yufeng.info/archives/105
    Just copy the code after “cat ehttpd.erl”, and compile it.

    $ erlc ehttpd.erl
    $ taskset -c 1 erl +K true +h 99999 +P 99999 -smp enable +S 2:1 -s ehttpd
    $ taskset -c 1-7 erl +K true -s ehttpd
    We use taskset to limit erlang vm to use only 1 CPU/core or use all CPU cores. The 2nd line is run in single CPU mode, and the 3rd line is run in multi-core CPU mode.

    Java source code, save the 2 class as HttpServer.java and HttpProtocolHandler.java, and do necessary import.

    public class HttpServer {
        public static void main(String[] args) throws Exception {
            SocketAcceptor acceptor = new NioSocketAcceptor(4);
            acceptor.setReuseAddress( true );
    
    		int port = 8080;
    		String hostname = null;
    		if (args.length > 1) {
    			hostname = args[0];
    			port = Integer.parseInt(args[1]);
    		}
    
            // Bind
            acceptor.setHandler(new HttpProtocolHandler());
            if (hostname != null)
            	acceptor.bind(new InetSocketAddress(hostname, port));
            else
            	acceptor.bind(new InetSocketAddress(port));
    
            System.out.println("Listening on port " + port);
            Thread.currentThread().join();
        }
    }
    
    public class HttpProtocolHandler extends IoHandlerAdapter {
        public void sessionCreated(IoSession session) {
            session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
            session.setAttribute(SslFilter.USE_NOTIFICATION);
        }
    
        public void sessionClosed(IoSession session) throws Exception {}
        public void sessionOpened(IoSession session) throws Exception {}
        public void sessionIdle(IoSession session, IdleStatus status) {}
        public void exceptionCaught(IoSession session, Throwable cause) {
            session.close(true);
        }
    
        static IoBuffer RESULT = null;
    	public static String HTTP_200 = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\n" +
    			"hello world\r\n";
    	static {
        	RESULT = IoBuffer.allocate(32).setAutoExpand(true);
        	RESULT.put(HTTP_200.getBytes());
        	RESULT.flip();
        }
        public void messageReceived(IoSession session, Object message)
                throws Exception {
            if (message instanceof IoBuffer) {
            	IoBuffer buf = (IoBuffer) message;
            	int c = buf.get();
            	if (c == 'G' || c == 'g') {
            		session.write(RESULT.duplicate());
            	}
            	session.close(false);
            }
        }
    }

    Nov 24 update Because the above Mina code doesn’t parse HTTP request and handle the necessary HTTP protocol, replaced with org.jboss.netty.example.http.snoop.HttpServer from Netty example, but removed all the string builder code from HttpRequestHandler.messageReceived() and just return a “hello world” result in HttpRequestHandler.writeResponse(). Please read the source code and the Netty documentation for more information.

    $ taskset -c 1-7 \
    java -server -Xmx1024m -Xms1024m -XX:+UseConcMarkSweepGC -classpath . test.HttpServer 192.168.10.1 8080

    We use taskset to limit java only use cpu1-7, and not use cpu0, because we want cpu0 dedicate for system call(Linux use CPU0 for network interruptions).

    Go language, source code

    package main
    import (
       "http";
        "io";
    )
    func HelloServer(c *http.Conn, req *http.Request) {
        io.WriteString(c, "hello, world!\n");
    }
    func main() {
         runtime.GOMAXPROCS(8); // 8 cores
         http.Handle("/", http.HandlerFunc(HelloServer));
         err := http.ListenAndServe(":8080", nil);
        if err != nil {
            panic("ListenAndServe: ", err.String())
        }
    }

    $ 6g httpd2.go
    $ 6l httpd2.6
    $ taskset -c 1-7 ./6.out

    1.4 Performance test client

    ApacheBench client, for 30, 100, 1,000, 5,000 concurrent threads
    ab -c 30 -n 1000000 http://192.168.10.1:8080/
    ab -c 100 -n 1000000 http://192.168.10.1:8080/
    1000 thread, 334 from 3 different machine
    ab -c 334 -n 334000 http://192.168.10.1:8080/
    5000 thread, 1667 from 3 different machine
    ab -c 1667 -n 334000 http://192.168.10.1:8080/

    2. Test results

    2.1 request per second

    30 (threads) 100 1,000 5,000
    Nginx html(1C) 21,301 21,331 23,746 23,502
    Nginx module(1C) 25,809 25,735 30,380 29,667
    Nginx module(Multi-core) 25,057 24,507 31,544 33,274
    Erlang(1C) 11,585 12,367 12,852 12,815
    Erlang(Multi-Core) 15,101 20,255 26,468 25,865
    Java, Mina2(without HTTP parse)
    30,631 26,846 31,911 31,653
    Java, Netty 24,152 24,423 25,487 25,521
    Go 14,080 14,748 15,799 16,110

    c_erlang_java_go
    2.2 latency, 99% requests within(ms)

    30 100 1,000 5,000
    Nginx html(1C) 1 4 42 3,079
    Nginx module(1C) 1 4 32 3,047
    Nginx module(Multi-core) 1 6 205 3,036
    Erlang(1C) 3 8 629 6,337
    Erlang(Multi-Core) 2 7 223 3,084
    Java, Netty 1 3 3 3,084
    Go 26 33 47 9,005

    3. Notes

    * On large concurrent connections, C, Erlang, Java no big difference on their performance, results are very close.
    * Java runs better on small connections, but the code in this test doesn’t parse the HTTP request header (the MINA code).
    * Although Mr. Yu Feng (the Erlang guru in China) mentioned that Erlang performance better on single CPU(prevent context switch), but the result tells that Erlang has big latency(> 1S) under 1,000 or 5,000 connections.
    * Go language is very close to Erlang, but still not good under heavy load (5,000 threads)
    After redo 1,000 and 5,000 tests on Nov 18
    * Nginx module is the winner on 5,000 concurrent requests.
    * Although there is improvement space for Go, Go has the same performance from 30-5,000 threads.
    * Erlang process is impressive on large concurrent request, still as good as nginx (5,000 threads).

    4. Update Log

    Nov 12, change nginx.conf work_connections from 1024 to 10240
    Nov 13, add runtime.GOMAXPROCS(8); to go’s code, add sysctl -p env
    Nov 18, realized that ApacheBench itself is a bottleneck under 1,000 or 5,000 threads, so use 3 clients from 3 different machines to redo all tests of 1,000 and 5,000 concurrent tests.
    Nov 24, use Netty with full HTTP implementation to replace Mina 2 for the Java web server. Still very fast and low latency after added HTTP handle code.

    Twitter系统运维经验

    最近看到的另外一个介绍Twitter技术的视频[Slides] [Video (GFWed)],这是Twitter的John Adams在Velocity 2009的一个演讲,主要介绍了Twitter在系统运维方面一些经验。 本文大部分整理的观点都在Twitter(@xmpp)上发过,这里全部整理出来并补充完整。

    Twitter没有自己的硬件,都是由NTTA来提供,同时NTTA负责硬件相关的网络、带宽、负载均衡等业务,Twitter operations team只关注核心的业务,包括Performance,Availability,Capacity Planning容量规划,配置管理等,这个可能跟国内一般的互联网公司有所区别。

    1. 运维经验

    * Metrics

    Twitter的监控后台几乎都是图表(critical metrics),类似驾驶室的转速表,时速表,让操作者可以迅速的了解系统当前的运作状态。联想到我们做的类似监控后台,数据很多,但往往还需要浏览者做二次分析判断,像这样满屏都是图表的方法做得还不够,可以学习下这方面经验。 据John介绍可以从图表上看到系统的瓶颈-系统最弱的环节(web, mq, cache, db?)
    根据图表可以科学的制定系统容量规划,而不是事后救火。Twitter operation dashboard

    * 配置管理

    每个系统都需要一个自动配置管理系统,越早越好,这条一整理发到Twitter上去之后引起很多回应。

    * Darkmode

    配置界面可以enable/disable 高计算消耗或高I/O的功能,也相当于优雅降级,系统压力过大时取消一些非核心但消耗资源大的功能。

    * 进程管理

    Twitter做了一个”Seppaku” patch, 就是将Daemon在完成了n个requests之后主动kill掉,以保持健康的low memory状态,这种做法据了解国内也有不少公司是这样做。

    * 硬件

    Twitter将CPU由AMD换成Xeon之后,获得30%性能提升,将CPU由双核/4核换成8核之后,减少了40%的CPU, 不过John也说,这种升级不适合自己购买硬件的公司。

    2. 代码协同经验

    * Review制度

    Twitter有上百个模块,如果没有一个好的制度,容易引起代码修改冲突,并把问题带给最终用户。所以Twitter有一强制的source code review制度, 如果提交的代码的svn comment没有”reviewed by xxx”, 则pre-commit脚本会让提交失败, review过的代码提交后会通过自动配置管理系统应用到上百台服务器上。 有@xiaomics同学在Twitter上马上就问,时间成本能否接受?如果有紧急功能怎么办?个人认为紧急修改时有两人在场,一人修改一人review也不是什么难事。

    * 部署管理

    从部署图表可以看到每个发布版本的CPU及latency变化,如果某个新版本latency图表有明显的向上跳跃,则说明该发布版本存在问题。另外在监控首页列出各个模块最后deploy版本的时间,可以清楚的看到代码库的现状。

    * 团队沟通

    Campfire来协同工作,campfire有点像群,但是更适合协同工作。对于Campfire就不做更多介绍,可参考Campfire官方说明。

    3. cache

    • Memcache key hash, 使用FNV hash 代替 MD5 hash,因为FNV更快。
    • 开发了Cache Money plugin(Ruby), 给应用程序提供read-through, write-through cache, 就像一个db访问的钩子,当读写数据库的时候会自动更新cache, 避免了繁琐的cache更新代码。
    • “Evictions make the cache unreliable for important configuration data”,Twitter使用memcache的一条经验是,不同类型的数据需放在不同的mc,避免eviction,跟作者前文Memcached数据被踢(evictions>0)现象分析中的一些经验一致。
    • Memcached SEGVs, Memcached崩溃(cold cache problem)据称会给这种高度依赖Cache的Web 2.0系统带来灾难,不知道Twitter具体怎么解决。
    • 在Web层Twitter使用了Varnish作为反向代理,并对其评价较高。