Tag Archives: .NET

[repost ]Simpler, Cheaper, Faster: Playtomic’s Move From .NET To Node And Heroku

original:http://highscalability.com/blog/2012/10/15/simpler-cheaper-faster-playtomics-move-from-net-to-node-and.html

This is a guest post by Ben Lowry, CEO of Playtomic. Playtomic is a game analytics service implemented in about 8000 mobile, web and downloadable games played by approximately 20 million people daily.

Here’s a good summary quote by Ben Lowry on Hacker News:

Just over 20,000,000 people hit my API yesterday 700,749,252 times, playing the ~8,000 games my analytics platform is integrated in for a bit under 600 years in total play time. That’s just yesterday. There are lots of different bottlenecks waiting for people operating at scale. Heroku and NodeJS, for my use case, eventually alleviated a whole bunch of them very cheaply.

Playtomic began with an almost exclusively Microsoft.NET and Windows architecture which held up for 3 years before being replaced with a complete rewrite using NodeJS.  During its lifetime the entire platform grew from shared space on a single server to a full dedicated, then spread to second dedicated, then the API server was offloaded to a VPS provider and 4 – 6 fairly large VPSs.   Eventually the API server settled on 8 dedicated servers at Hivelocity, each a quad core with hyperthreading + 8gb of ram + dual 500gb disks running 3 or 4 instances of the API stack.

These servers routinely serviced 30,000 to 60,000 concurrent game players and received up to 1500 requests per second, with load balancing done via DNS round robin.

In July the entire fleet of servers was replaced with a NodeJS rewrite hosted at Heroku for a significant saving.

 

Scaling Playtomic With NodeJS

There were two parts to the migration:

  1. Dedicated to PaaS:  Advantages include price, convenience, leveraging their load balancing and reducing overall complexity.  Disadvantages include no New Relic for NodeJS, very inelegant crashes, and a generally immature platform.
  2. .NET to NodeJS: Switching architecture from ASP.NET / C# with local MongoDB instances and a service preprocessing event data locally and sending it to centralized server to be completed; to NodeJS on Heroku + Redis and preprocessing on SoftLayer (see Catalyst program).

Dedicated To PaaS

The reduction in complexity is significant; we had 8 dedicated servers each running 3 or 4 instances of the API at our hosting partner Hivelocity.  Each ran a small suite of software including:

  • MongoDB instance
  • log pre-processing service
  • monitoring service
  • IIS with api sites

Deploying was done via an FTP script that uploaded new api site versions to all servers.  Services were more annoying to deploy but changed infrequently.

MongoDB was a poor choice for temporarily holding log data before it was pre-processed and sent off.  It offered a huge speed advantage of just writing to memory initially which meant write requests were “finished” almost instantly which was far superior to common message queues on Windows, but it never reclaimed space left from deleted data which meant the db size would balloon to 100+ gigabytes if it wasn’t compacted regularly.

The advantages of PaaS providers are pretty well known, they all seem quite similar although it’s easiest to have confidence in Heroku and Salesforce since they seem the most mature and have broad technology support.

The main challenges transitioning to PaaS was shaking the mentality that we could run assistive software alongside the website as we did on the dedicated servers.  Most platforms provide some sort of background worker threads you can leverage but that means you need to route data and tasks from the web threads through a 3rd party service or server which seems unnecessary.

We eventually settled on a large server at Softlayer running a dozen purpose-specfic Redis instances and some middleware rather than background workers.  Heroku doesn’t charge for outbound bandwidth and Softlayer doesn’t charge for inbound which neatly avoided the significant bandwidth involved.

Switching From .NET To NodeJS

Working with JavaScript on the serverside is a mixed experience.  On the one hand the lack of formality and boilerplate is liberating.  On the other hand there’s no New Relic and no compiler errors which makes everything harder than it needs to be.

There are two main advantages that make NodeJS spectacularly useful for our API.

  1. Background workers in the same thread and memory as the web server
  2. Persistant, shared connections to redis and mongodb (etc)

Background Workers

NodeJS has the very useful ability to continue working independently of requests, allowing you to prefetch data and other operations that allow you to terminate a request very early and then finish processing it.

It is particularly advantageous for us to replicate entire MongoDB collections in memory, periodically refreshed, so that entire classes of work had access to current data without having to go an external database or local/shared caching layer.

We collectively save 100s – 1000s of database queries per second using this in:

  • Game configuration data on our main api
  • API credentials on our data exporting api
  • GameVars which developers use to store configuration or other data to hotload into their games
  • Leaderboard score tables (excluding scores)

The basic model is:

var cache = {};module.exports = function(request, response) {
response.end(cache[“x”]);
}

function refresh() {

// fetch updated data from database, store in cache object
cache[“x”] = “foo”;
setTimeout(refresh, 30000);
}

refresh();

The advantages of this are a single connection (per dyno or instance) to your backend databases instead of per-user, and a very fast local memory cache that always has fresh data.

The caveats are your dataset must be small, and this is operating on the same thread as everything else so you need to be conscious of blocking the thread or doing too-heavy cpu work.

Persistent Connections

The other massive benefit NodeJS offers over .NET for our API is persistant database connections.  The traditional method of connecting in .NET (etc) is to open your connection, do your operation, after which your connection is returned to a pool to be re-used shortly or expired if it’s no longer needed.

This is very common and until you get to a very high concurrency it will Just Work.  At a high concurrency the connection pool can’t re-use the connections fast enough which means it generates new connections that your database servers will have to scale to handle.

At Playtomic we typically have several hundred thousand concurrent game players that are sending event data which needs to be pushed back to our Redis instances in a different datacenter which with .NET would require a massive volume of connections – which is why we ran MongoDB locally on each of our old dedicated servers.

With NodeJS we have a single connection per dyno/instance which is responsible for pushing all the event data that particular dyno receives.  It lives outside of the request model something like this:

var redisclient  = redis.createClient(….);module.exports = function(request, response) {

var eventdata = “etc”;

redisclient.lpush(“events”, eventdata);

}

The End Result

High load:

REQUESTS IN LAST MINUTE


_exceptions: 75 (0.01%)
_failures: 5 (0.00%)
_total: 537,151 (99.99%)
data.custommetric.success: 1,093 (0.20%)
data.levelaveragemetric.success: 2,466 (0.46%)
data.views.success: 105 (0.02%)
events.regular.invalid_or_deleted_game#2: 3,814 (0.71%)
events.regular.success: 527,837 (98.25%)
gamevars.load.success: 1,060 (0.20%)
geoip.lookup.success: 109 (0.02%)
leaderboards.list.success: 457 (0.09%)
leaderboards.save.missing_name_or_source#201: 3 (0.00%)
leaderboards.save.success: 30 (0.01%)
leaderboards.saveandlist.success: 102 (0.02%)
playerlevels.list.success: 62 (0.01%)
playerlevels.load.success: 13 (0.00%)


 

This data comes from some load monitoring that operates in the background on each instance, pushes counters to Redis where they’re then aggregated and stored in MongoDB,  you can see it in action at https://api.playtomic.com/load.html.

There are a few different classes of requests in that data:

  • Events that check the game configuration from MongoDB, perform a GeoIP lookup (opensourced very fast implementation at https://github.com/benlowry/node-geoip-native), and then push to Redis
  • GameVars, Leaderboards, Player Levels all check game configuration from MongoDB and then whatever relevant MongoDB database
  • Data lookups are proxied to a Windows server because of poor NodeJS support for stored procedures

The result is 100,000s of concurrent users causing spectactularly light Redis loads fo 500,000 – 700,000 lpush’s per minute (and being pulled out on the other end):

1  [||                                                                                      1.3%]     Tasks: 83; 4 running
2  [|||||||||||||||||||                                                                    19.0%]     Load average: 1.28 1.20 1.19
3  [||||||||||                                                                              9.2%]     Uptime: 12 days, 21:48:33
4  [||||||||||||                                                                           11.8%]
5  [||||||||||                                                                              9.9%]
6  [|||||||||||||||||                                                                      17.7%]
7  [|||||||||||||||                                                                        14.6%]
8  [|||||||||||||||||||||                                                                  21.6%]
9  [||||||||||||||||||                                                                     18.2%]
10 [|                                                                                       0.6%]
11 [                                                                                        0.0%]
12 [||||||||||                                                                              9.8%]
13 [||||||||||                                                                              9.3%]
14 [||||||                                                                                  4.6%]
15 [||||||||||||||||                                                                       16.6%]
16 [|||||||||                                                                               8.0%]
Mem[|||||||||||||||                                                                 2009/24020MB]
Swp[                                                                                    0/1023MB]

PID USER     PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
12518 redis     20   0 40048  7000   640 S  0.0  0.0  2:21.53  `- /usr/local/bin/redis-server /etc/redis/analytics.conf
12513 redis     20   0 72816 35776   736 S  3.0  0.1  4h06:40  `- /usr/local/bin/redis-server /etc/redis/log7.conf
12508 redis     20   0 72816 35776   736 S  2.0  0.1  4h07:31  `- /usr/local/bin/redis-server /etc/redis/log6.conf
12494 redis     20   0 72816 37824   736 S  1.0  0.2  4h06:08  `- /usr/local/bin/redis-server /etc/redis/log5.conf
12488 redis     20   0 72816 33728   736 S  2.0  0.1  4h09:36  `- /usr/local/bin/redis-server /etc/redis/log4.conf
12481 redis     20   0 72816 35776   736 S  2.0  0.1  4h02:17  `- /usr/local/bin/redis-server /etc/redis/log3.conf
12475 redis     20   0 72816 27588   736 S  2.0  0.1  4h03:07  `- /usr/local/bin/redis-server /etc/redis/log2.conf
12460 redis     20   0 72816 31680   736 S  2.0  0.1  4h10:23  `- /usr/local/bin/redis-server /etc/redis/log1.conf
12440 redis     20   0 72816 33236   736 S  3.0  0.1  4h09:57  `- /usr/local/bin/redis-server /etc/redis/log0.conf
12435 redis     20   0 40048  7044   684 S  0.0  0.0  2:21.71  `- /usr/local/bin/redis-server /etc/redis/redis-servicelog.conf
12429 redis     20   0  395M  115M   736 S 33.0  0.5 60h29:26  `- /usr/local/bin/redis-server /etc/redis/redis-pool.conf
12422 redis     20   0 40048  7096   728 S  0.0  0.0 26:17.38  `- /usr/local/bin/redis-server /etc/redis/redis-load.conf
12409 redis     20   0 40048  6912   560 S  0.0  0.0  2:21.50  `- /usr/local/bin/redis-server /etc/redis/redis-cache.conf

and very light MongoDB loads for 1800 – 2500 crud operations a minute:

insert  query update delete getmore command flushes mapped  vsize    res faults locked % idx miss %     qr|qw   ar|aw  netIn netOut  conn       time
2      9      5      2       0       8       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     3k     7k   116   01:11:12
1      1      5      2       0       6       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     2k     3k   116   01:11:13
0      3      6      2       0       8       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     3k     6k   114   01:11:14
0      5      5      2       0      12       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     3k     5k   113   01:11:15
1      9      7      2       0      12       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     4k     6k   112   01:11:16
1     10      6      2       0      15       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     1|0     4k    22k   111   01:11:17
1      5      6      2       0      11       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     3k    19k   111   01:11:18
1      5      5      2       0      14       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     3k     3k   111   01:11:19
1      2      6      2       0       8       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     3k     2k   111   01:11:20
1      7      5      2       0       9       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     3k     2k   111   01:11:21
insert  query update delete getmore command flushes mapped  vsize    res faults locked % idx miss %     qr|qw   ar|aw  netIn netOut  conn       time
2      9      8      2       0       8       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     4k     5k   111   01:11:22
3      8      7      2       0       9       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     4k     9k   110   01:11:23
2      6      6      2       0      10       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     3k     4k   110   01:11:24
2      8      6      2       0      21       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     4k    93k   112   01:11:25
1     10      7      2       3      16       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     4k     4m   112   01:11:26
3     15      7      2       3      24       0  6.67g  14.8g  1.23g      0      0.2          0       0|0     0|0     6k     1m   115   01:11:27
1      4      8      2       0      10       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     4k     2m   115   01:11:28
1      6      7      2       0      14       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     4k     3k   115   01:11:29
1      3      6      2       0      10       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     3k   103k   115   01:11:30
2      3      6      2       0       8       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     3k    12k   114   01:11:31
insert  query update delete getmore command flushes mapped  vsize    res faults locked % idx miss %     qr|qw   ar|aw  netIn netOut  conn       time
0     12      6      2       0       9       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     4k    31k   113   01:11:32
2      4      6      2       0       8       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     3k     9k   111   01:11:33
2      9      6      2       0       7       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     3k    21k   111   01:11:34
0      8      7      2       0      14       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     4k     9k   111   01:11:35
1      4      7      2       0      11       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     3k     5k   109   01:11:36
1     15      6      2       0      19       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     5k    11k   111   01:11:37
2     17      6      2       0      19       1  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     6k   189k   111   01:11:38
1     13      7      2       0      15       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     1|0     5k    42k   110   01:11:39
2      7      5      2       0      77       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     2|0    10k    14k   111   01:11:40
2     10      5      2       0     181       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0    21k    14k   112   01:11:41
insert  query update delete getmore command flushes mapped  vsize    res faults locked % idx miss %     qr|qw   ar|aw  netIn netOut  conn       time
1     11      5      2       0      12       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     0|0     4k    13k   116   01:11:42
1     11      5      2       1      33       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     3|0     6k     2m   119   01:11:43
0      9      5      2       0      17       0  6.67g  14.8g  1.22g      0      0.1          0       0|0     1|0     5k    42k   121   01:11:44
1      8      7      2       0      25       0  6.67g  14.8g  1.22g      0      0.2          0       0|0     0|0     6k    24k   125   01:11:45

Related Articles

[repost ]Perl、PHP、Python、Java和Ruby的比较

original:http://developer.51cto.com/art/201107/277684.htm

预览

◆ 语言的发展趋势一定是动静结合、刚柔并济

◆ Perl凝练晦涩,Python优雅明晰,Ruby精巧灵动,PHP简明单纯

◆ 或许优雅正是来自对细节和规范的重视

◆ (RoR)与Ruby结合之后,便如一只猱身而上灵猫,立刻衬托出Java和.NET大象般的身影

提问

◆ Perl、Python、Ruby和PHP各自有何特点?

◆ 为什么动态语言多作为轻量级的解决方案?

◆ LAMP为什么受欢迎?

◆ Ruby on Rails为什么会流行?

◆ 编程语言的发展趋势是什么?

讲解

“剩下四种动态语言,我们将之归为后台脚本语言。”冒号说着画了张图表——

比较Perl、PHP、Python、Java和Ruby

引号听得仔细:“我记得您开始是把这些语言划分为C族静态语言、非C族静态语言和动态语言三类的。”

冒号解释:“那是按语法来划分的,偏重理论;现在是按应用来划分,偏重实践。”

句号旋即联想到:“这种分法貌似三层架构——前台语言对应表现层;平台语言和后台脚本语言对应业务逻辑层;系统语言对应数据层。”

“的确有几分神似,但千万不可混淆。”冒号提醒道,“三层架构(three-layer architecture)是模块设计上的逻辑划分[1];而这里是按语言应用范围进行的物理划分——与用户交互的是前台语言,与机器交互的是系统语言,介于其中的为前台提供服务同时又需要底层系统服务的是后台语言。”

逗号询问:“后台语言又细分成平台语言与后台脚本语言?”

“这是基于程序(program)与脚本(script)、静态与动态而分的。”冒号进行说明,“其实Perl,PHP,Python和Ruby都有自己的虚拟机(virtual machine),从这种意义上说它们也可作为平台语言。但在实际应用中,它们没有Java平台和.NET平台那种整合凝聚力和核心作用,通常作为轻量级的解决方案。”

问号想探个究竟:“这是由于它们都是动态语言的缘故吗?”

冒号回答:“理论上动态语言同样能承担大型应用,但实践上它们多作为粘合语言或用于中小型应用。用句时髦的话来形容,暂时还是主流的配角或非主流的主角。毕竟在运行效率、类型安全、可用资源、开发工具、技术支持等方面,它们与Java、C#相比尚有一定差距。另外它们同属‘草根’语言,虽有开源社区的大力支持,在影响力上与后者未可同日而语。”

叹号揣测:“说不定在不久的将来,动态语言也会成为主流的主角。”

“世易时移,殊难逆料。但有一点可以肯定,语言的发展趋势一定是动静结合、刚柔并济。”冒号断言,“一方面以Java和C#为代表的静态语言中嫁接了动态语言的枝条;另一方面以Java和.NET为代表的平台与动态语言的交壤地带也在逐步扩大。比如JRuby允许Ruby与Java之间互相调用,类似的还有Jython、IronRuby、IronPython等等。此外值得一提的是,动态语言最活跃的舞台当数LAMP,L-A-M-P。”

引号接茬:“L是Linux,A是Apache,M是MySQL,P是PHP。这四大组件形成了一个完整的开源网络开发平台。”

冒号补充道:“P也可指Perl、Python,甚至Ruby。”

逗号调侃:“可惜Ruby的‘R’比‘P’多了一根尾巴。”

“有人为了自圆其说,干脆让P表示‘Programming language’,这下所有语言都囊括其中了。老外就喜欢玩这种首字母缩略(acronym)的文字游戏,尤其LAMP正好还有‘灯’的含义,寓意开源世界的一盏明灯,他们一定更得意了。”冒号语带调笑,“前面我们曾提及,网络应用是生长动态语言最肥沃的土壤,而LAMP就是这块土壤上搭建的平台。作为网络平台,LAMP以其开放灵活、开发迅速、部署方便、高可配置、安全可靠、成本低廉等特色而与Java平台和.NET平台鼎足三分,尤其受中小企业的欢迎。LAMP中Linux是操作系统,Apache是Web服务器,MySQL是数据库系统,而我们当下最关心的是‘P族语言’:PHP、Perl、Python还有Ruby。”

问号建议:“作为动态语言,它们的共性上节课已经谈了不少,能说说它们的个性吗?”

“它们的个性极为鲜明:Perl凝练晦涩,Python优雅明晰,Ruby精巧灵动,PHP简明单纯。先看老大哥Perl,它博采众家之长,综合了C语言的结构、sed的正则表达式、AWK的关联数组(associative array)、Lisp的表(list)和Unix Shell的命令,此外还有借鉴了一种语言,你们知道是哪种吗?”冒号忽然卖了个关子。

逗号猜想:“应该是某种OOP语言吧。”

“Perl中确有不少C++的影子,但它的对象模型在5.0以后才引入,典型的半路出家,远不如前面的特征那么自然。与其说是一种自然而然的发展,不如说是在OOP潮流裹挟下的一种身不由己的迎合。真正深入骨髓的借鉴是自然语言。”冒号给出了答案,“我们提过,Perl的发明者Larry Wall是一名语言学家,他认为程序语言应该与自然语言一样,简洁自然、易读易写、表达多样、不拘一格。Perl还有不少的格言或哲学,使得编程语言一改严谨刻板的面孔,散发出浓郁的人文气息。”

叹号幽了一默:“我见过Perl的代码,人文气息没闻出来,但我怀疑有乙醚气息——看一会就觉得晕晕乎乎的。”

众人大笑。

“有人仅用一行Perl代码就实现了RSA算法,你看了那还不得当场晕倒啊?”冒号打趣道,“Perl的各种魔符好似一把把锋利的剪刀,做起文本裁剪之类的工作来游刃有余。这是它最大的长处,当初Perl就是Wall用来做Unix系统管理的,以后在CGI上的广泛应用也得益于此。这也赋予Perl极强的粘合力,因而有‘internet上的胶带(duct tape)[2]’的说法。它又号称瑞士军刀,精练而复杂,实用而强大。但Perl过于灵活自由,缺乏规范,影响了程序的可读性、一致性、整洁性和可维护性。不熟悉该语言的固然如读天书,熟悉语言而不熟悉问题的也颇费思量。相比之下Python被认为是Perl有力的挑战者,不仅在于它天然的OO设计和丰富的类库,更重要的是它对程序员友好度大大超过Perl。Python也有一系列的被称为禅(Zen)的哲学,不少与Perl是针锋相对的。比如:Perl认为做一件事可以有多种方法,而Python认为一件事应该最好只有一种方法;Perl追求语言的表现力,Python追求简单优雅;Perl喜欢隐性暗示,Python强调显性明示;Perl强调紧凑,Python强调松散; Perl的语法和语义丰富,Python的语法和语义简单而类库丰富。或许Python最让人不习惯的是它对空白符敏感性。”

引号感到惊奇:“对空白符敏感?这个倒真怪异。”

冒号见惯不怪:“虽然有点违反习惯,但非常符合Python一贯的规范简洁的风格——一方面从语法上保证了良好的编码风格;另一方面,每个代码块不再需要起始的大括号或begin/end之类的,减少了的代码行数。顺便插一句,另外一种优雅的语言Haskell同样对空白符敏感,或许优雅正是来自对细节和规范的重视吧。此外许多人抱怨Python中的自引用self太多,殊不知这也是它倡导显式表达的一种体现。总的看来,Python主要的问题还是在性能效率上不尽如人意。”

叹号好奇地问:“Ruby怎么样?据说它将取代Java。”

“不要轻言‘取代’二字。”冒号规诫道,“Java没有取代C++,也不会被Ruby取代,至多只是一种再分配。不过Ruby的确是门很可爱的语言,兼具Perl的表现力和Python的可读性。Ruby背后最具特色的理念是:关注程序员使用语言时的感受超过语言本身的功能。通俗地说,兵器的称手比锋利更重要;文雅地说,应给予程序员更多的人文关怀。就拿代码块(block)和迭代器(iterator)来说,虽然均非Ruby首创,但其语法最为赏心悦目。类似的例子比比皆是。Ruby的元编程能力特别强,也是它高度灵活的一种体现,但并不是所有人都喜欢这种风格。Ruby的主要弱点有两个:一个与Python类似,在性能上还有待提高;另一个是它的线程由用户空间(user space)而不是内核空间(kernel space)来管理[3],不能充分利用多核或多CPU。真正让Ruby变得炙手可热的是web应用框架 Ruby on Rails(RoR)的成功,它们还催生了Java平台上的Groovy语言和Groovy on Grails框架。RoR奉行的CoC(Convention over Configuration)和DRY(Don’t repeat yourself )原则以及MVC架构看似了无新意,但与Ruby结合之后,便如一只猱身而上灵猫,立刻衬托出Java和.NET大象般的身影。”

逗号有些怀疑:“框架竟然捧红了语言,框架真有这么重要吗?”

“如果web应用中动态页面较少或业务逻辑不复杂,框架的价值并不大。以前CGI编程就是往Perl之类的代码中嵌入HTML代码,如同Java中的Servlet;PHP则单纯地在HTML代码中插入PHP代码,如同早期的JSP。没有MVC,也不管什么三层架构,更没有ORM。但是——”冒号拖了个转折音,“一旦业务逻辑变得复杂,开发人员增多,手工作坊式编程开始捉襟见肘,引入框架这个流水生产线来提高生产力便是大势所趋。”

句号不解:“我想Perl、Python和PHP一定也有不少框架,Java中的框架更是泛滥成灾,何以独独RoR脱颖而出?”

冒号作出分析:“正值web2.0和敏捷开发(agile development)的概念流行之际,RoR将AJAX与Ruby组合在一起成为绝佳的回应。以前各种web应用框架是不少,但在RoR之前轻量级套餐式解决方案并不多。Perl中的Catalyst、Python中的Pylon还有PHP中的CakePHP等应是效仿之作。因此RoR出现的时机可说是不早不晚,正当其时。此外,Perl和PHP由于过于流行,反而有不少的历史包袱,人们习惯了将表示逻辑和业务逻辑编织在一起。至于Java企业解决方案,框架太多,搭配组合更多,增加了选择的难度。即使采用最常见的轻量级SSH(Struts+Spring+Hibernate)组合,维护起来也比RoR繁杂得多。”

叹号愈发担忧:“听这意思,Java还是危险啊!”

“言之过早。”冒号不以为然,“首先RoR还有待进一步检验,目前无论是应用广度还是深度上尚无法与Java相提并论;其次Java在性能、安全等方面还是有不少优势,而这些对于大型和关键性的应用来说尤为重要。即使在中小型web应用中,RoR较之PHP还远为不及。”

问号接下话题:“PHP为何如此流行?”

“因为它简单、专一。”冒号答得很干脆,“与Python和Ruby一开始就定位通用语言不同,PHP是专为网络而生的。同早期的Perl相似,PHP起初主要起文本过滤器的作用,只不过Perl多处理文件流(file stream),而PHP多处理套接字流(socket stream)。PHP的语法简单,且为网络应用度身定造,受到网络开发人员的追捧当在情理之中。它虽很实用很流行,但并不完美。比如:变量名大小写敏感而函数名大小写不敏感;函数命名规则不一致;不支持namespace和unicode[4];与Perl一样,它的对象模型不是先天的,直到PHP 5才真正完善;对线程支持不足;相比Perl、Python和Ruby,它的功能稍显单薄等等。”

引号突然想起:“我记得您在第一堂课提到PHP还能用于桌面应用。”

“不仅PHP,Perl、Python还有Ruby,都能作为前台语言来开发命令行或图形界面的应用。同样地,VB、Delphi和JavaScript也能作为后台语言。现代的程序语言既有自己的专长,又向通用化和全能化发展,以争取更多的生存空间。试想一下,现代的程序员又何尝不是如此呢?”言及于此,冒号收住话题,“语言简评告一段落,还有不少既有趣又有用的语言,在此就不一一评说了。我们看到,每种编程语言都有其独特的惯例用法和哲学理念,它们与编程范式一道形成了语言的编程风格。体悟愈深者编程语感愈强,思维与语言愈交融无碍,渐从必然王国走向自由王国。”

逗号满怀憧憬:“那是不是一种人剑合一的境界?”

“或许人器合一更准确吧,程序员可不能只会一种兵器哟。”冒号故意抠他的字眼,“现在请大家每人写一句对本节课的感言。”

众人沉思片刻,齐齐挥笔而就——

叹号——没有最好的语言,只有最合适的语言。

逗号——没有糟糕的语言,只有糟糕的程序员。

问号——没有一种语言是万能的,只会一种语言是万万不能的。

引号——废除对语言的宗教信仰,建立对语言的哲学思维。

句号——编程就是在人脑和电脑之间寻找最佳平衡点的过程。

冒号读罢大悦,顺手一掌拍出五记马屁:“精彩之极!可谓字字珠玑、句句联璧啊。兹决定,给诸位的奖赏是——立时下课!”

众人欣然领赏而去。

插语

[1] 有两种三层架构,一种是three-layer architecture,一种是three-tier architecture。它们经常换用,但其实是有分别的:前者仅仅在逻辑进行划分,而后者在物理上也进行了划分——不同层次的模块运行在不同的主机上。

[2] 不少地方译作‘输送带’、‘传送带’,因为duct有‘输送管’、‘导管’之意,于是想当然地认为这表明Perl在internet上起着输送作用。殊不知‘duct type’专指一种万能的粘性极强的胶带,用以比喻Perl的粘合力。

[3] 这类线程被称为绿色线程(green thread),也称伪线程。据称Ruby2.0将支持原生线程(native thread)。

[4] PHP将在5.3.0支持namespace,将在6.0支持unicode。

总结

◆ 比起Java平台和.NET平台,动态语言轻便灵活、开发效率高,但整合凝聚力还不够,在运行效率、类型安全、可用资源、开发工具、技术支持以及影响力等方面也有一定差距,故通常作为轻量级的解决方案。

◆ LAMP是由Linux、Apache、MySQL和包括PHP、Perl、Python或Ruby在内的脚本语言组成的网络开发平台,具有开放灵活、开发迅速、部署方便、高可配置、安全可靠、成本低廉等优点。

◆ Perl精练、复杂、强大、灵活、自由、隐晦、表现力强,但规范性、可读性、一致性、整洁性和可维护性较差。

◆ Python优雅规范、简洁明晰、易学易用、类库丰富,但效率稍差,有些人不喜欢它对空白符敏感的特性。

◆ Ruby语法精巧、高度灵活,兼具Perl的表现力和Python的可读性,尤其注重程序员的感受,但其性能和线程模型尚有待改进。

◆ PHP简单、专一、实用、流行,在但相比其他三种语言,在语法和功能上稍有欠缺。

◆ RoR是一种轻量级套餐式的web应用解决方案,是由好的设计(MVC架构和CoC、DRY原则)加上好的语言(Ruby)在好的时机(web2.0和敏捷开发风行之际)打造出的好的框架。

◆ 静态语言与动态语言从语言特征到运行环境都在逐渐融合。

◆ 程序员应该与程序语言一样,既要有自己的专长,又要向通用化和全能化发展。

◆ 编程语言惯例用法、哲学理念和编程范式形成了语言的编程风格。

原文:http://levi.cg.am/?p=711

【编辑推荐】

  1. 9大理由戏说为什么Scrum不行
  2. Erlang语言作者告诉你什么才是编程最好的方法
  3. 我们离HTML 5还有多远?
  4. 关于国内前端和JS技术发展的乱想
  5. C++程序员必读:让你的代码更强大

 

[repost]MySpace Architecture

original:

MySpace Architecture

Update:Presentation: Behind the Scenes at MySpace.com. Dan Farino, Chief Systems Architect at MySpace shares details of some of MySpace’s cool internal operations tools.

MySpace.com is one of the fastest growing site on the Internet with 65 million subscribers and 260,000 new users registering each day. Often criticized for poor performance, MySpace has had to tackle scalability issues few other sites have faced. How did they do it?

Site: http://myspace.com

Information Sources

  • Presentation: Behind the Scenes at MySpace.com
  • Inside MySpace.com

    Platform

  • ASP.NET 2.0
  • Windows
  • IIS
  • SQL Server

    What’s Inside?

  • 300 million users.
  • Pushes 100 gigabits/second to the internet. 10Gb/sec is HTML content.
  • 4,500+ web servers windows 2003/IIS 6.0/APS.NET.
  • 1,200+ cache servers running 64-bit Windows 2003. 16GB of objects cached in RAM.
  • 500+ database servers running 64-bit Windows and SQL Server 2005.
  • MySpace processes 1.5 Billion page views per day and handles 2.3 million concurrent users during the day
  • Membership Milestones:
    – 500,000 Users: A Simple Architecture Stumbles
    – 1 Million Users:Vertical Partitioning Solves Scalability Woes
    – 3 Million Users: Scale-Out Wins Over Scale-Up
    – 9 Million Users: Site Migrates to ASP.NET, Adds Virtual Storage
    – 26 Million Users: MySpace Embraces 64-Bit Technology
  • 500,000 accounts was too much load for two web servers and a single database.
  • At 1-2 Million Accounts
    – They used a database architecture built around the concept of vertical partitioning, with separate databases for parts of the website that served different functions such as the log-in screen, user profiles and blogs.
    – The vertical partitioning scheme helped divide up the workload for database reads and writes alike, and when users demanded a new feature, MySpace would put a new database online to support it.
    – MySpace switched from using storage devices directly attached to its database servers to a storage area network (SAN), in which a pool of disk storage devices are tied together by a high-speed, specialized network, and the databases connect to the SAN. The change to a SAN boosted performance, uptime and reliability.
  • At 3 Million Accounts
    – the vertical partitioning solution didn’t last because they replicated some horizontal information like user accounts across all vertical slices. With so many replications one would fail and slow down the system.
    – individual applications like blogs on sub-sections of the Web site would grow too large for a single database server
    – Reorganized all the core data to be logically organized into one database
    – split its user base into chunks of 1 million accounts and put all the data keyed to those accounts in a separate instance of SQL Server
  • 9 Million–17 Million Accounts
    – Moved to ASP.NET which used less resources than their previous architecture. 150 servers running the new code were able to do the same work that had previously required 246.
    – Saw storage bottlenecks again. Implementing a SAN had solved some early performance problems, but now the Web site’s demands were starting to periodically overwhelm the SAN’s I/O capacity—the speed with which it could read and write data to and from disk storage.
    – Hit limits with the 1 million-accounts-per-database division approach as these limits were exceeded.
    – Moved to a virtualized storage architecture where the entire SAN is treated as one big pool of storage capacity, without requiring that specific disks be dedicated to serving specific applications. MySpace now standardized on equipment from a relatively new SAN vendor, 3PARdata
  • Added a caching tier—a layer of servers placed between the Web servers and the database servers whose sole job was to capture copies of frequently accessed data objects in memory and serve them to the Web application without the need for a database lookup.
  • 26 Million Accounts
    – Moved to 64-bit SQL server to work around their memory bottleneck issues. Their standard database server configuration uses 64 GB of RAM.
  • Horizontally Federated Database. Databases are partition by purpose. Have profile, email databases etc. Partition is based on user range. 1 Million users live in each database. So you have Profile1, Profile2 all the way up to Profile300 as they have 300 million users.
  • Doesn’t use ASP cache because they don’t have a high enough hit rate on the front-end. The middle tier cache does have a high hit rate.
  • Failure isolation. Segment requests into web server by database. Allow only 7 threads per database. So if the database is slow only those threads will slowdown and the traffic in the other threads will flow.

    Operations

  • PerfCollector. Centralized collection of performance data via UDP. More reliable than Windows and allows any client to connect and see stats.
  • Web Based Stack Dump Tool. Can right-click on a problem server and get stack dump of the .Net managed threads. Used to have to RDC into system and attach a debugger and 1/2 later get an answer. Slow, nonscalable, and tedious. Not just a stack dump, gives a lot of context about what the thread is doing. Troubleshooting is easier because you can see 90 threads are blocked on a database so the database may be down.
  • Web Base Heap Dump Tool. Dumps all memory allocations. Very useful for developers. Save hours of doing it by hand.
  • Profiler. Traces a request from start to finish and produces a report. See URL, methods, status, everything that will help you identify a slow request. Looks at lock contentions, are a lot of exceptions being thrown, anything that might be interesting. Very light weight. It’s running on one box in every VIP (group of 100 servers) in production. Samples 1 thread every 10 seconds. Always tracing in background.
  • Powershell. Microsoft’s new shell that runs in process and pass objects between commands versus parsing text output. MySpace develops a lot of commandlets to support operations.
  • Developed their own asynchronous communication technology to get around windows networking problems and treat servers as a group. Can ship a .cs file, compile it, run it, and ship the response back.
  • Codespew. Pushes code updates on their communication technology. Used to do 5 code pushes a day, now down to 1 a week.

    Lessons Learned

  • You can build big websites using Microsoft tech.
  • A cache should have been used from the beginning.
  • The cache is a better place to store transitory data that doesn’t need to be recorded in a database, such as temporary files created to track a particular user’s session on the Web site.
  • Built in OS features to detect denial of service attacks can cause inexplicable failures.
  • Distribute your data to geographically diverse data centers to handle power failures.
  • Consider using virtualized storage/clustered file systems from the start. It allows you to massively parallelize IO access while being able to add disk as needed without any reorganization needed.
  • Develop tools that work in a production environment. Can’t simulate everything in test environment. The scale and variety of uses APIs are put to can’t be simulated in QA during testing. Legitimate users and hackers will run into corner cases that weren’t hit in testing, though QA will find most of the problems.
  • Throw hardware at problems. Easier than changing their backend software to a new way of doing things. The example is they add a new database server for every million users. It might be more efficient to change their approach to more efficiently use the database hardware, but it’s easier just to add servers. For now.