Test 3rd-party API connectivity

content :None

Web.js MVC between client and server

Web.js 是一个 为简化 HTTP 开发而设计的 Web Framework,它致力于以最简单的语法进行开发高性能的应用。

Web.js between client and server 是指 web.js 这个文件可以同时在客户端和服务端使用吗?

不是的。。这个先不管。。先来看看其他“无关”的。。

以下是 Wikipedia 对 MVC 的解释:

MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

 


先来说说前端 MVC

现在国内来说,相对优秀的前端 MVC 架构是来自豆瓣说的 [(Backbone.js + Underscore.js) + Mustache.js + jQuery]

M – Model

Backbone.js 和 Underscore.js 是暂时来说国内公认的最好的一组 Model 框架

  1. 高性能
  2. 强自定义度
  3. 高灵活性
  4. …………
Backbone.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Model of Backbone
var Person = Backbone.Model.extent({
    sayHello: function () {
        alert('Hey, I`m ' + this.get('name') + '.');
        return this;
    },
    setName: function (name) {
        this.set({'name': name});
        alert('My name is ' + this.get('name') + '.');
        return this;
    }
});

var Will = new Person;

Will.setName('Will Wen Gunn')    // --> My name is Will Wen Gunn.
    .sayHello();                // --> Hey, I`m Will Wen Gunn.

其中 Backbone.Model 可以理解为经过封装的 Class 类型,然后 Person 是一个经过 extend 拓展的自定义 Class 类型,

Will 则是继承了 Person 的一个 Model 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Model of Underscore
$.getJSON(location.origin + '/persons')
    .done(function (data) {
        sessionStorage.persons = data;
        var persons = JSON.parse(sessionStorage.persons);
        // --> [{name: 'Will Wen Gunn'}, {name: 'Foo'}, {name: 'Bar'}]
        $('body').append(
            JSON.stringify(
                _.map(persons, function (person) {
                    if (person.name == 'Will Wen Gunn' || person.name !== 'Bar') {
                        return true;
                    }
                })
            )
        );
    });
// --> [{"name": "Will Wen Gunn"}, {"name": "Foo"}]

与其说 Underscore.js 是 Model 框架,还不如说是 Model 操作库,Underscore.js 并没有封装 Class ,而是使用 JavaScript 原有的 Array, Object, Function 类型进行操作 (其中人们比较常用 Object 和 Function 来模拟 Class)

这种 Model 会比 利用 Object 或者 Function 来定义 Class 要实际,因为这样可以很好地对对象和对象集 (Collection) 进行管理和操作。


V – View

View 更直接点地说就是我们平时所构建的 HTML ,而 MVC 其实源自于 DHTML ,W3C 的解释是动态页面(Dynamic HTML),而我更认为是 Data to HTML。

而这个正是Mustache.js诞生的原因

{{Mustache}}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
var person,
    tmpl-persons,
    proxy = new Eventproxy(),
    persons = function (data, tmpl) {
        $('body').append(
            Mustache.to_html(tmpl, data)
        );
    };
proxy.assign('data', 'tmpl', persons);
$.getJSON(location.origin + '/persons')
    .done(function (data) {
        persons = JSON.parse(data);
        /*
         * [ {Name: "Will Wen Gunn", Age: 15, Sex: "Man"},
         *   {Name: "Foo", Age: 15, Sex: "Man"},
         *   {Name: "Bar", Age: 15, Sex: "Man"} ]
         */
        proxy.trigger('data', data);
    });
$.get(location.origin + '/tmpls/persons.html')
    .done(function (data) {
        tmpl-persons = data;
        /*
         * {{#peoples}}
         *     {{Name}}
         *     {{Age}}
         *     {{Sex}}
         * {{/peoples}}
         */
        proxy.trigger('tmpl', tmpl);
    });
/*
 * Will Wen Gunn
 * 15
 * Man
 * Foo
 * 15
 * Man
 * Bar
 * 15
 * Woman
 */

这里使用到了朴灵小田同学的 EventProxy.js,相当好玩的一个小工具,但是发挥出来的作用很强阿。

它能让几个异步请求并行处理,最后集中处理。

https://github.com/JacksonTian/eventproxy

另外也还有一个来自国外的,类似的东西,叫Step,它也让异步函数分开,但是是串列的队列式,所以会产生阻塞,小问并不建议使用。


C – Controller

这个恐怕是争议最大的一块了,有人推崇jQuery,有人推崇YUI,有人推崇MooTools……

其实这个并没有什么太大关系,只要是有这样的能力的,哪种 JavaScript Library 都没所谓的。

我这里用jQuery把上面的这些M,V整合起来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
var Person = Backbone.Model.extent({
        sayHello: function () {
            alert('Hey, I`m ' + this.get('name') + '.');
            return this;
        },
        setName: function (name) {
            this.set({'name': name});
            alert('My name is ' + this.get('name') + '.');
            return this;
        }
    }),
    Persons  = Backbone.Collection.extend({
        model: Person,
        sayHello: function () {
            this.each(function (Person) {
                $('body').append('Hey, I`m ' + Person.get('name') + '.');
            });
        },
        sayName: function () {
            this.each(function (Person) {
                $('body').append('My name is ' + Person.get('name') + '.');
            });
        }
    });
var CNodejs = new Persons;
var persons,
    tmpl-persons;
$.get(location.origin + '/persons')
    .done(function (data) {
        persons = JSON.parse(data);
    })
    .get(location.origin + '/tmpls/persons.html')
    .done(function () {
        tmpl-persons = JSON.parse(data);
    });
CNodejs.add(persons.peoples);
$('body').append(
    Mustache.to_html(
        tmpl-persons,
        { peoples: CNodejs.toJSON() }
    )
);

Node.js MVC

M – Model, V – View

其实 Backbone.js, Underscore.js 和 Mustache.js 在 Node.js 上的用法是和前端一模一样的,所以我就不多介绍了。

C – Controller

来看看 Web.js 的 Router :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
var urlRouter = {
        '^(\d{4})\/(\d{2})\/(\d{2})\/(.*)\.jpg': '$1-$2-$3-$4.jpg',
        'google': 'http://www.google.com',
        'iwillwen': 'http://www.iwillwen.com'
    },
    getRouter = {
        '^getsomthing': function(req, res, qs) {
            res.sendJSON(qs);
        },
        '^car': function(req, res, qs) {
            switch (qs.action) {
                case 'foo':
                    res.send('Your action is foo');
                    break;
                case 'bar':
                    res.send('Your action is bar');
                    break;
            }
        }
    },
    postRouter = {
        '^postsomthing': function(req, res, data) {
            res.sendJSON(qs);
        },
        '^car': function(req, res, data) {
            switch (data.action) {
                case 'foo':
                    res.send('Your action is foo');
                    break;
                case 'bar':
                    res.send('Your action is bar');
                    break;
            }
        }
    };

web.run(urlRouter, 80)
    .get(getRouter)
    .post(postRouter);

console.log('The app is running on http://localhost');

这个和Express有点区别。

如果结合数据库的 Node.js MVC,Web.js该怎么写呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
var web =require('webjs'),
    mongo = require('mongoskin'),
    Backbone = require('backbone'),
    Underscore = require('underscore'),
    eventproxy = require('EventProxy.js'),
    db = mongo.db('localhost:27017/blog'),
    posts = db.collection('posts'),
    metas = db.collection('metas'),

var urlRouter = {
        '^page/(\d)': 'page.html',
        '^(.*)': 'post.html'
    },
    getRouter = {
        'init': function (req, res, qs) {
            var proxy = new eventproxy.EventProxy(),
            init = function (title, description, posts) {
                    var obj = {
                        title: title,
                        description: description,
                        posts: posts
                    };
                    res.sendJSON(obj);
                };
            proxy.assign('title', 'description', 'posts', init);
            metas.findOne({type: 'title'}, function (err, title) {
                proxy.trigger('title', title);
            });
            metas.findOne({type: 'description'}, function (err, description) {
                proxy.trigger('description', description);
            });
            posts.findTop10(function (err, posts) {
                proxy.trigger('posts', posts);
            });
        },
        'getPost': function (req, res, qs) {
            posts.findOne(qs, function (err, post) {
                res.sendJSON(post);
            });
        }
    },
    postRouter = {
        'setMeta': function (req, res, data) {
            metas.update(
                {type: data.type},
                data,
                {upsert: true},
                function (err) {
                    if (err) return res.send('Set failed.
' + err);
                    res.send('Set successed.');
                }
            );
        },
        'post': function (req, res, data) {
            posts.update(
                {title: data.title},
                data,
                {upsert: true},
                function (err) {
                    if (err) return res.send('Post failed.
' + err);
                    res.send('Post successed');
                }
            );
        }
    };
web.run(urlRouter, 80)
    .get(getRouter)
    .post(postRouter);

最后来讲讲

Web.js between client and server

来看看 Web.js for client (仍在编写中) 的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
(function ($) {
    //loaded /js/web-client.js
    var getRouter = {
        'getsomething' : function () {
            var proxy = new Eventproxy(),
                post = function (data, tmpl) {
                    $('body').append(
                        Mustache.to_html(tmpl, data)
                    );
                };
            proxy.assign('data', 'tmpl', init);
            web.getData('post', {}, function (data) {
                proxy.trigger('data', data);
            });
            web.getTmpl('post', function (tmpl) {
                proxy.trigger('tmpl', tmpl);
            });
        }
    };
    web.conn(location.origin)
        .get(getRouter);
})(jQuery);

这时候大家可能会问了,Web.js 的 MVC 究竟异样在哪里呢?

前端 MVC 的 M 其实和后端 MVC 的 M 本来是不可能一起使用的,因为如果由后端进行模板渲染,前端就没有渲染的必要,那么前端 MVC就不成立了。同理,如果让前端进行模板渲染,后端也就不存在 MVC 的概念。

如果要前后端的 MVC 同时存在要怎么做呢,其实很简单,就是让客户端进行性能·评估。

比如说 IE6、7 这样的低性能浏览器,Web.js for client 会让服务器先进行 Data to HTML 渲染,然后传输再到客户端,如果是Chrome,FireFox,Safari 和 Opera 等高性能浏览器,则选择在客户端进行渲染,减轻传输荷载。

另外 Web.js 默认会开启缓存应用加速机制 (DOM Storage,Cookies,Buffer,Object……),让一部分数据先存入缓存,让短时间内再次发出的请求从缓存中获取。减少 LAN 资源和数据库资源的损耗。

JavaScript 是一门建立在静态页面上的动态脚本语言,Ajax的普及和发展,使它完全可以完成一些静态页面做不到的事情,比如像PHP自身的文件响应机制 (相比这也是 PHP 吸引人的一个终于优点)。

上面的这一段代码就演示了一个静态页面通过 Web.js 进行 URL action router 识别,并向服务器请求数据和模板,然后在 DOM 中插入渲染得到的 HTML。

两个 Web.js 之间是可以无缝对接的,开发者无须设置太多。

当然 Web.js for client 也是支持 Express 等其他 Server-side 开发框架的。


好,最后来放一个完整的 Web.js MVC Router 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//server.js
var web =require('webjs'),
    mongo = require('mongoskin'),
    Backbone = require('backbone'),
    Underscore = require('underscore'),
    eventproxy = require('EventProxy.js'),
    db = mongo.db('localhost:27017/blog'),
    posts = db.collection('posts'),
    metas = db.collection('metas'),

var urlRouter = {
        '^(.*)': 'page.html'
    },
    getRouter = {
        'init': function (req, res, qs) {
            var proxy = new eventproxy.EventProxy(),
                init = function (title, description, posts) {
                    var obj = {
                        title: title,
                        description: description,
                        posts: posts
                    };
                    if (qs.render) {
                        res.send(
                            web.render('init', obj)
                        );
                    } else {
                        res.sendJSON(obj);
                    }
                };
            proxy.assign('title', 'description', 'posts', init);
            metas.findOne({type: 'title'}, function (err, title) {
                proxy.trigger('title', title);
            });
            metas.findOne({type: 'description'}, function (err, description) {
                proxy.trigger('description', description);
            });
            posts.findTop10(function (err, posts) {
                proxy.trigger('posts', posts);
            });
        },
        'getpost': function (req, res, qs) {
            posts.findOne(qs ,function (err, post) {
                if (qs.render) {
                    res.send(
                        web.render('post', post)
                    );
                } else {
                    res.sendJSON(post);
                }
            });
        }
    };
web.run(urlRouter, 80)
    .get(getRouter)
    .set('tmplDir', 'tmpls');

//page.html - client.js
(function ($) {
    var getRouter = {
        '/' : function () {    //init
            var proxy = new Eventproxy(),
                init = function (data, tmpl) {
                    $('body').append(
                        Mustache.to_html(tmpl, data)
                    );
                };
            proxy.assign('data', 'tmpl', init);
            web.getData('init', {}, function (data) {
                proxy.trigger('data', data);
            });
            web.getTmpl('init', function (tmpl) {
                proxy.trigger('tmpl', tmpl);
            });
        }
        '^(.*)' : function (action) {    // action --> (.*)
            var proxy = new Eventproxy(),
                post = function (data, tmpl) {
                    $('body').append(
                        Mustache.to_html(tmpl, data)
                    );
                };
            proxy.assign('data', 'tmpl', init);
            web.getData('post', {title: action}, function (data) {
                proxy.trigger('data', data);
            });
            web.getTmpl('post', function (tmpl) {
                proxy.trigger('tmpl', tmpl);
            });
        }
    };
    web.conn(location.origin)
        .get(getRouter)
})(jQuery);

你确信你了解时间吗?

你还记得“软件真的好难做”中的那个有意思的例子吗?那个例子告诉我们软件开发中假设可能会是致命的事。今天,我又在StackOverflow上看到一个关于时间的问题——为什么1927年12月31日的午夜时间这么奇怪?提问题的这个人给了下面的一段java代码(我做一些修改,保证让你可以copy过去就可以编译运行)

我在其中高亮了几行,这个程序就是想比较一下“1927-12-31 23:54:07”  和  “1927-12-31 23:54:08” 差几秒,很明显,是差一秒。但是程序的输出却不是这样的。

  1. import java.text.SimpleDateFormat;
  2. import java.text.ParseException;
  3. import java.util.Date;
  4. import java.util.TimeZone;
  5. class time{
  6.     public static void main(String[] args) throws ParseException {
  7.         SimpleDateFormat sf = new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”);
  8.         sf.setTimeZone(TimeZone.getTimeZone(“Asia/shanghai”));
  9.         String str3 = “1927-12-31 23:54:07”;
  10.         String str4 = “1927-12-31 23:54:08”;
  11.         Date sDt3 = sf.parse(str3);
  12.         Date sDt4 = sf.parse(str4);
  13.         long ld3 = sDt3.getTime() /1000;
  14.         long ld4 = sDt4.getTime() /1000;
  15.         System.out.println(ld3);
  16.         System.out.println(ld4);
  17.         System.out.println(ld4-ld3);
  18.     }
  19. }

复制代码

下面,让我们来看看程序的输出:(是的,差出353秒钟来)

-1325491905
-1325491552
353

产品管理:用流程规避风险

最近一直在跟的一些项目,过程中出现了一些反复,原因有很多。深入分析一下问题根结,希望以后能避免。

  关键词:产品负责制、确认层级机制、产品脊梁、靠谱

  问题:
  • 1、项目初期,产品原则已经开会统一、皆大欢喜,但中期却在产品原则上出现分歧。甚至在产品面临上线时,还有人为该产品赋予新的定位。
  • 2、产品设计环节中,在产品需求和定位已确认的情况下,纠结在“不符合需求、偏离定位”的设计细节,僵持不下、浪费时间、拖垮精力。
  • 3、跨部门合作的项目,设计部门提交的工作结果,经常被他们自己的上层或左右推翻重来,即使对产品有利,却伤害整个项目,伤害自己人。
  根据这些问题,需要问很多为什么。

  为什么初期统一过产品原则,中期却出现原则上的分歧?“发起分歧者”所具备的必要条件是什么?

  首先需要问公司:产品负责制,到底有没有明确和落实?其实,产品人员已经也应该,把对产品的长期责任,承担在自己身上。但是公司层面和总监层面如果没有强有力的确认产品负责制的归属,我们这种自我承担的产品负责制,永远不会在关键时刻发挥作用,而只会左顾右盼。

  在产品负责制尚不明晰的情况下,即使团队核心成员开会确认了产品原则,也永远不会有一个强有力的角色把产品原则贯彻到每一个阶段,初期产品定位和产品原则的会议确认,是没有力度的,只是表面上的统一而已。

  原因很简单,当一切顺风顺水、条条大路通罗马的时候,旅游团众大佬必然跟着美女导游就OK了。当路遇挫折时,导游就算熟悉路途中的险恶,也仍然不是无法把自己伪装成团队的菩萨,左右不了各位大佬的想法。(默契,可以克服这一问题。但在大公司的营盘里面,想培养一个默契而完整的项目团队,一般是可遇不可求的。但默契的产品小组还是有的。)

  在以上这种情况下,发起分歧者有什么必要条件?一是长期跟进产品,了解最初的产品原则和定位;二是了解整个团队的工作重心和产品市场情况,而不是只熟悉自己所管部门的情况。可悲的是,熟悉了以上两点的人,往往不会做出不靠谱的决策,而去伤害整个项目。

  既然已经确定了产品需求,为什么会出现“不符合需求、偏离定位”的设计细节?为什么会在这种问题上浪费时间?

  这种问题出现非常正常,当设计人员没有做充足的竞品分析,也没有充分理解产品的情况下,总会在设计时出现偏差。只要产品人员说明需求和修改原因,说服设计人员回归需求即可。可怕的是“僵持不下”,设计人员坚持己见,认为设计细节是正确的、靠谱的、符合产品需求的,产品人员却无法认同。僵持不下时,应该搁置问题,继续推进项目,而不是在这种问题上纠结一个下午,还把各种用户调研、应用实例搬出来说理。

  最终做决定时,如果依旧僵持不下,那么谁对产品长期负责,谁做决定。谁对产品长期负责?回归的上一个问题。

  为什么“工作结果”可以推翻重来?谁赋予了提交者“提交的权利”,又是谁赋予了推翻者“推翻的权利”?

  首先要问的问题是:是不是根本没有明确“确认层级”的工作流程,或者只是口头明确、表面默契而已?

  比如一个设计稿,当然要由直接负责人A确认,没有问题了,再交到下一步去执行。那么这个负责人A需要评估:自己是否能够保证这一个确认结果是最后结果。如果不能保证,那么再提交上一层的负责人,让他确认。但如果上层负责人曾经授权了负责人A的确认权限,就不应该在A已经确认之后,轻率的推翻确认结果。

  在这种确认过程中,应该在每一个项目实施之前明确“相应的设计工作,需要有相应的确认层级”,这一点至关重要!“确认层级”都不明晰的情况下,首先应该检讨的不是设计质量没有达到要求,而是为什么会出现这种流程上的漏洞,让不符合设计质量的东西推进到下一步骤。漏洞不堵住,就没有资格反复插手,这样既伤害团队,也拖垮自己。

  相反,当指定了项目设计工作的确认层级之后,再出现提交设计稿不合格、推翻设计工作不符合流程的情况,就可以明确的质疑“提交的权利”和“推翻的权利”,是否符合“确认层级”的流程。这个时候,是能力问题的就调整确认层级或更换负责人、是流程问题的就贯彻流程,比大家互相装无辜更加务实。

  最后感叹一下,产品经理是妥妥的炮灰,哪个环节出问题,都可以揭出产品规划和项目管理的漏洞和伤疤

  一个产品项目,从立项、交互、视觉、开发、测试等各个阶段,总会因为各种问题而反复伤害。尤其是跨部门合作、多环节配合的流程,伤害问题发生的隐患比比皆是。这个过程期间,产品经理的职责,就是想尽办法,避免各种伤害的发生、避免各种伤害打击到核心成员、伤及团队重心。

  可悲的是,在大部分环境下,卑微的产品人员不仅无法预估各种隐患问题,对于已经发生的伤害问题也没有足够的防备资源和职权。这个时候,产品经理,就是妥妥的炮灰,众口一铄,灰飞烟灭。最后,产品经理仍然必须站起来,拖着被各种问题所累的核心成员,继续把项目推进下去。

  不知道这种情况,在其他公司是否常见。当没有固化的产品负责制和确认层级管理机制的时候,深埋问题的跨部门团队开发产品,然后暴露出一堆问题,一遍又一遍的伤害每一个产品。所以,这种公司总是出现一堆一堆的打折产品,承担着一堆又一堆不给力的数据和市场境况,最后担责到产品人员身上,该调走的调走、该跳槽的跳槽,再招进下一批未知轻重的欣欣向荣者,如此反复、以至无穷。

  关键角色位置的靠谱牛人,是产品的脊梁

  产品团队,碰到这种复杂的情况,唯一的救命稻草,就是产品各环节的关键位置,有靠谱的关键角色把持,然后这类关键角色需要保持持续的头脑清醒、精神集中,在任何阶段都能回溯最初的产品原则、评估当前的团队重心和高层要求,统一麾下执行者的行动,并说服高层。从这个角度讲,没有那么多流程管理和部门配合的小团队产品和创业公司产品,要更加安全、更加靠谱。近期组里的一个小众产品,没有大资源投入、没有多部门合作、没有高层压力、没有战略意义,虽然选择了一条难路,但整个产品从设计到运营,让外人看来,显得张弛有度、节奏明确,这就是一个明显的例子。

  只要人靠谱,做什么都是靠谱的。如果人本身不靠谱,在任何角色上,都会出问题。至于什么问题:细节不到位、文档及版本控制逻辑不清楚、决策前后矛盾、协调成员配合乏力、工作重心聚焦不正确、沟通不主动……比比皆是。

  希望自己靠谱一点。

杜旬华你是怎么看到这个网站的?

没了,rt

Facebook如何管代码

【原文首发于《FrameThink》(英文版,需翻墙),感谢老崔的推荐(译文在此),推荐者说:“看完之后终于明白为什么优秀的工程师都去了或想去facebook,因为那里是工程师们的天堂。】

我对facebook的运转着迷,这是一个很独特的环境,不容易被复制(他们的体系并不适合所有的公司,即使他们努力尝试过)。下面是我和facebook的朋友们关于他们如何开发和管理项目的记录。

现在距离我收集的这些信息又过去6个月了,我相信facebook肯定又对他们的项目开发实践进行了改进。所以这些记录可能会有点过时。同时facebook的工程师驱动文化也越来越为大众所知。非常感谢那些帮助我整理这篇文章的facebook的朋友们。

记录:

    • 截止到2010年6月,facebook有将近2000名员工,10个月前只有1100名,一年之间差不多翻了一番。
    • 两个最大的部门是工程师和运维,每个部门大概都是400-500人。这两个部门人数大约占了公司的一半。
    • 产品经理与工程师的比例大约为1-7到1-10。
    • 每个工程师入职时,都要接收4-6周的培训,通过修补bugs和听高级开发工程师的课程来熟悉facebook。
    • 培训结束后,每个工程师都可以接触线上的数据库(更大的权力意味着更大的责任,也有一份”勿做清单”,不然可能会被开,比如共享用户的隐私数据)。
    • 有非常牢靠的安全体系,以免有人不小心/故意做了些不好的事。
    • 每个工程师可以修改facebook的任何代码,随时可以迁入。
    • 浓厚的工程师驱动文化。”产品经理基本可以被忽略”,这是facebook一名员工的话。工程师可以修改流程的细节,重新安排工作任务,随时植入自己的想法。
    • 在每月的跨部门会议上,由工程师来汇报工作进度,市场部和产品经理会出席会议,也可以做些简短的发言,但如果说得太多,很可能就会被打小报告。他们确实想让工程师来主导产品的开发,对自己的产品负责。
    • 项目需要的资源都是自愿的。
    • 一个产品经理把工程师们召集到一起,让他们对他的想法产生兴趣。
    • 工程师们决定开发那些让他们感兴趣的特性。
    • 工程师跟他们的经理说:”我下周想开发这5个新特性”。
    • 经理会让工程师独立开发,可能有时会让他优先完成一些特性。

facebook

  • 工程师独立完成所有的特性——前端/后端/数据库,等等所有相关的部分。如果需要得到设计人员的帮助,需要先让设计人员对你的想法产生兴趣。其他如架构之类的也一样。但总体来说,工程师要独立完成所有的任务。
  • 对于某个特性是否值得开发的争论,通常是这么解决的:花一个星期的时间完成他,并在小部分人群中(如1%)进行测试。
  • 工程师常常希望解决难题,这能获得声望和尊敬。他们很难对前端项目或UI设计产生太大的兴趣。这跟其他公司可能正好相反。在facebook,后端任务,比如新的feed算法,广告投放算法,memcache优化等等,是工程师真正感兴趣的。
  • 所有的代码修改都要进行审核(通过一个或多个工程师),但News Feed是个例外,因为太重要了,Zuckerberg会亲自review。
  • 所有的修改至少要被一个人审核,而且这个系统可以让任何人很方便地审核其他人的代码,即使你没有邀请他
  • 工程师负责测试,代码修复,和维护自己的项目。
  • 每个办公室或通过VPN连接的员工会使用下一版的facebook,这个版本的facebook会经常更新,通常比公开的早1-12小时。所有的员工被强烈建议提交bugs,而且通常会很快被修复。
  • 很奇怪只有很少的QA或自动测试——”大部分工程师都能写出基本没有bug的代码,只是在其他公司他们不需要这么做。如果有QA部门,他们只要把代码写完,扔给他们就行了”
  • [针对上一条]我们有自动测试,代码发布前必须要通过测试。我们不相信”所有的工程师都能写出没有bug的代码”,毕竟这是一个商业公司。
  • 很奇怪,缺少产品经理的影响和控制——产品经理是很独立的和自由的。产生影响力的关键是与工程师和工程师的领导们们搞好关系。需要大致了解技术,不要提一些愚蠢的想法。
  • 所有提交的代码每周二打包一次。
  • 只要多一分努力,终于一天会发生改变。
  • 星期二的代码发布,需要所有的提交过代码的工程师在场。
  • 代码打包前,工程师必须在一个特殊的IRC channel上。
  • 运维执行打包过程。
  • facebook有大约60000台服务器
  • 有9个代码发布级别
  • 最小的级别只有6台服务器
  • 星期二的代码发布会先发布到6台服务器上,运维组会检测这6台服务器的反应,保证代码正常工作,然后再提交到下一级
  • 如果发布出现了一些问题(如报错等等),那么就停止下一级的部署,提交出错代码的工程师负责修复问题,然后从头继续发布。
  • 所以一次发布可能会经历几次重复:1-2-3-fix. 回到1. 1-2-3-4-5-fix. 回到1. 1-2-3-4-5-6-7-8-9
  • 运维组是受过严格训练,倍受尊敬,而且有商业意识的。他们的工作包括分析错误日志,负载和内存状态等等。还包括用户行为。
  • 代码发布期间,运维组使用IRC-based页面系统,可以通过facebook/email/irc/im/sms ping每一个工程师,如果需要他们注意的话。对运维组不做回应是一件很羞愧的事。
  • 代码一旦发布到第9级,并且稳定运行,就算发布成功了。
  • 如果一个特性没有按时完成,也没什么大不了的,下次完成时一并发布即可。
  • 如果被svn-blamed,public shamed或工作经常疏忽就很可能被开除。”这是一个高效的文化”。不够高效或者不够聪明的员工会被剔除。管理层会在6个月的时间里观察你表现,如果不合格,只能说再见。每一级都是这个待遇,即使是C级别和VP级别,如果不够高效,也会被开除。
  • 被责骂不会导致解雇。我们特别尊重别人,原谅别人。大部分高级工程师都或多或少犯过一些严重的错误,包括我。但没有人因此被解雇。
  • 我也没有遇到过因为上面提到过的犯错误而被解雇。有些人犯了错,他们会非常努力地去修复,也让其他人得到了学习。

facebook与人人网的网站后台架构对比

要问时下什么类型的站点人气最旺?答案当属SNS网站。短短几年的时间,SNS便迅速确立了Web 2.0核心的地位。用户的增长量之快更是让人咂舌,而Facebook访问量首次超越Google的消息也让人觉得SNS的前途不可限量。当然,面对庞大 且繁琐的数据量、面对应用的高复杂度及用户体验,大型SNS站点的后台架构成为了近期受关注的重点之一。

在连续报道了《世界最大的PHP站点 Facebook后台技术探秘》及《专访人人网黄晶:SNS网站后台架构探秘》之 后,除了看到SNS网站后台的独特风景之外,我们也看到了作为国内外SNS站点的代表,Facebook和人人网在后台技术应用上还是有很多不同的地方, 当然,造成这种不同的原因有很多。这里我们将对比Facebook和人人网的后台架构,在找出这些差异的同时,我们也能够看出,跟国外相比,国内SNS网 站的后台技术差距还有多大?差距在哪里?

  后台语言的选择

作为一个大型站点,后台语言的选择意味着不同的架构路线、以及不同的开发框架。考虑到SNS网站后台架构的复杂性,可选择的语言并不 多,Facebook作为一个大型LAMP网站,选择了PHP;而人人网则使用Java。当然,PHP和Java各具优势,PHP+MySQL的黄金搭档 被无数站点所使用;在评价Java的优势时,黄晶老师说道,“而当项目日渐复杂的时候,Java则能通过其良好的OO特性,保持非常好的模块性,也有益于 网站重构。”

后台语言的选择有很多因素,选择哪种语言也并不重要,关键是要适合相应的生产环境,这里比较PHP与Java的优劣并没有太大的意义。但要说明 的是,每种语言都有它的劣势,如何进行有效的优化才是开发者们需要思考的,就像Facebook为PHP量身打造了HipHop那样。

  数据库

在后台架构中,数据库一直是我们关心的重点。曾经日壮山河的关系型数据库,在NoSQL运动下,仿佛显得日薄西山,这句话用在SNS站点中再合适不过了。没错,由于SNS站点的高复杂性,其对数据库的要求非常高,高性能、可扩展性以及可用性,缺一不可。

Facebook并不是一个传统意义上的LAMP站点,MySQL也主要作为一个Key-value的持久性存储使用,而它的存储系统则是 NoSQL运动的一个重要组成部分——Cassandra,它的特点也正是SNS站点所需求的,尽管很多人认为NoSQL还不够成熟,缺乏可靠性,但 Facebook的成功却是一个活生生的例子。

facebook架构图
Facebook数据库架构图,请点击原图查看

通过黄晶老师的介绍我们了解到,其实人人网也不只是在使用MySQL。Nuclear是人人网团队自主研发的分布式KV存储系统,目前已经在逐步的试用。关于Nuclear的详细介绍,可以参考人人网UGC团队博客的介绍:http://ugc.renren.com/2010/01/21/ugc-nuclear-guide-use/

从中我们不难看出,关系型数据库已经不能满足大型SNS站点的需求,在外国大胆使用NoSQL的同时(包括Facebook、Twitter 等),国内对NoSQL的使用还是显得比较谨慎,技术水平的差距,在这点上确实有所体现。从长远角度讲,特别是在SNS领域,NoSQL必将会逐步代替传 统的关系型数据库。

  缓存

缓存技术在后台架构中扮演着重要的角色。对于SNS站点来说,仅仅依靠数据库、Web服务器等软件的自身缓存机制是绝对不能满足要求的。 Memcached是人人网和Facebook都用到的一个分布式内存缓存系统,其已成为互联网最有名气的软件之一了。当然,缓存的手段是多种多样的,仅 仅保证日常后台的稳定运行也是不够的。面对一些突发事件,缓存机制更是尤为重要,特别是在数据库服务器与Web服务器上。

  图片存储架构

图片会给Web服务器带来不小的压力,特别是Facebook处理的图片量(每个月超过30亿张照片被上传),非常惊人!对此,Facebook专门开发了一套图片存储架构——Haystack。

相比于Facebook,人人网并没有一套完整的图片存储架构体系。当然,作为人人网的用户,能够感觉到进行上传图片等一些操作,还是很流畅 的。说明以目前的人人网图片处理技术,能够满足目前用户的需求,以及后台的流畅运行。但架构总要着眼于未来,开发一套完整的、高效的架构体系,才是最佳的 解决方案。

  SNS后台技术未来发展趋势

相比于其他类型的站点,SNS网站后台架构更为复杂。关于SNS后台技术未来发展趋势问题,黄晶也表示,鉴于SNS网站的一些业务特点,在后台 技术中,人人网最关注的仍然是高性能,可扩展性,高可用性,所以SNS网站架构中特别需要一个能灵活应对业务变化的一套健壮的分布式系统。

在对比过人人网与Facebook的后台架构之后,我们不难看出,除了整体架构的系统性能之外,对于其中每个细节的把握,更是我们国内架构师们需要仔细思考的,这同时也需要我们的技术人员提高自身能力、敢于使用先进的技术及应用、更要勇于创新。