received event “button/power PWRF 00000080 00000001”

今天其中一台服務器無緣無故重啓了。問了機房的人,沒有人去操作。只有清潔工進過機房。

無耐,只好查看系統日誌。/var/log/acpid 發現以下內容。時間跟服務器重啓時間敏合。初步推薦是電源按扭被按了。

[Wed Mar 23 10:44:38 2011] received event “button/power PWRF 00000080 00000001”

[Wed Mar 23 10:44:38 2011] notifying client 4187[68:68]

[Wed Mar 23 10:44:38 2011] notifying client 4380[0:0]

[Wed Mar 23 10:44:38 2011] executing action “/bin/ps awwux | /bin/grep gnome-power-manager | /bin/grep -qv grep || /sbin/shutdown -h now”[Wed Mar 23 10:44:38 2011] BEGIN HANDLER MESSAGES

[Wed Mar 23 10:44:38 2011] END HANDLER MESSAGES

[Wed Mar 23 10:44:38 2011] action exited with status 0

[Wed Mar 23 10:44:38 2011] completed event “button/power PWRF 00000080 00000001”

 

不知道是誰。還是把這個電源按扭事件屏蔽比較安全一些。因爲不知道什麽時候又被“按”了。

# vi /etc/acpi/events/power.conf

# ACPID config to power down machine if powerbutton is pressed, but only if
# no gnome-power-manager is running

event=button/power.*
action=/bin/ps awwux | /bin/grep gnome-power-manager | /bin/grep -qv grep || /sbin/shutdown -h now

看到這裏,已經很肯定是電源按扭被按了。

把最後2行注釋掉:

# ACPID config to power down machine if powerbutton is pressed, but only if
# no gnome-power-manager is running

#event=button/power.*
#action=/bin/ps awwux | /bin/grep gnome-power-manager | /bin/grep -qv grep || /sbin/shutdown -h now

 

Rapid Development with Rails ( OSDC 演講)

這是我在 OSDC 2011 的前言草稿。本來是打算週六才打算放出來的,不過目前對岸正在 Twitter 上演 Rails 與 PHP 之爭(詳情請看 @robbinfan@fenng 的大戰),看了手癢。 想了一下,還是把當天的這前十分鐘講稿先放出來好了…

我的主題是如何善用 Rails 特性達到 Rapid Development。這場 talk 不會有太多的 code,一些觀念上的闡釋和搭配的架構介紹會講比較多。時間是 3/26(六)的下午 2:00 在中研院

====

簡單自我介紹一下,我是 xdite,在 T 客邦工作。我目前的角色是 Lead Developer 與 Manager。中間是有去「出國深造」一陣子 ….不過目前回來了 XD

我寫 Rails 大約有快四年的時間,之前待過和多PIXNETHTC

講題概念 ( 澄清關於對 Rails 的一些錯誤認知)

大概講一下這次會講這個主題的原因:Ruby on Rails 一直以來是一個非常優秀的網頁框架。應該是有始以來最棒的網頁框架,開發者不僅可以從中跟上世界頂尖開發者的研究進度,滿足自己的技術癮之外,又可以穩穩 的兼顧快速的商業需求。很可惜因為種種因素,讓世人對人一直對它停留在非常糟糕的印象(「市場上找不到開發者」且「只適合滿足技術狂人癮頭」的「效率糟 糕」「不穩定」框架)。

Rails 對於生意與開發上的幫助,絕非只有「Rapid Development」可言。Rails 如果如同大家所說的,只是這種糟糕的玩具,相信也活不到今天,也絕不會有越來越多的開發者、商業網站投入採用。如果各位正在或曾經開發網站,深入把玩過幾 個框架,應該知道 Rails 的架構和想法先進超出其他框架幾個世代之多。

Rails 的批評者最常對它的指責,總結起來大概就是這句:「市場上找不到開發者」且「只適合滿足技術狂人癮頭」的「效率糟糕」「不穩定」框架。

以前當我還是很菜的開發者時,聽到這句話會很生氣,但又不知道從何反駁起。但現在再聽到這種批評,我會心理暗笑對方是沒經營過商業網站的外行人。

事實上:世人的誤解,和真正現實的社會是這樣的:

PHP 的 Developer 好找,Rails 的 Developer 不好找

1. 挖來的「有經驗」的開發者,幾乎是不能對現有的團隊帶來戰力的。多數的情況,他只是用一門語言,埋頭重複寫了好幾年,從未自我進修與跟上現有開發技術潮流 的開發者。他寫的 code 通常又會帶著自己的性格(無論是好的或壞的)。在一個團隊中,大家作事如果沒有建立起共同的 convention,越補人往往越只是災難。更或者,有些人寫 code 其實只是寫自己開心寫拿來炫耀的,從來不想考慮商業上的需求和穩定…

別懷疑,如果你希望有好的開發團隊,請自己養,別去挖別去找。要挖也要挖能幫你建立制度的人,自己也要努力幫忙貢與獻維持制度,也不需要害怕這個 Leader 跑掉,公司建立制度就是為了要保證沒有了誰還不會完蛋。(我出國深造,Team 也沒倒…)

而且,培養幾個 Rails Developer 的成本,真的比挖一大堆 PHP Developer 的成本便宜很多。

Rails 只是炫,對商業無貢獻價值

2. 現實的商業變動需求是很大的。Rails 並非只是五分鐘 CRUD 這種炫,沒辦法成事的東西。Rails 天生的良好的架構可以讓你可以不需要重造輪子,快速達成商業需求。剩下來的時間和力氣,可以用在把產品做的更好更棒。甚至把時間花在把原先的站修整成更好 的架構。

當開發者不是處於事情多到做不完,每天寫的要死還是 deliver 出一個爛貨的狀態時。他將會有更多時間、精力甚至是心情,學習把事情做得更好更對。

(事實上,最近公司正在開發的產品,進度遠超過預期,提早三週寫完,RD 剩下來的時間都用在 refactor code,SEO,performance tuning,開心的 tune UI 等等…)

Rails 效能糟糕透頂,所以使用它會陷入麻煩

3. Rails 效能是很不錯的,不要被那些謠言騙了。早在 Rails 2.x 時代,效能評測遠遠就幹掉了類似性質(full stack 型)的 framework。而且在公司裡,如果你想跟老闆說你想花時間 tune 效能,它只會回你一句,為什麼不買機器解決就好?Code 乾淨度和效能永遠不是商業運營需要考量的第一重點,除非它嚴重影響了你的商業營運與拓展需求(技術部主管要自己懂得拿捏這一點分寸)。

而且現實的狀況是,通常買機器的成本會遠比雇用 RD tunning 的成本低。如果還在草創階段,當然還可以咬點牙自己擠一點效能來調度。但別忘了,做生意就是要搞大,搞大了還要賺錢,如果你搞很大還不賺錢,沒有錢買機器,那是你這門生意有問題。不是 Framework 的問題

永遠都不會是,別本末倒置了。

Rails 變動過大,跟著升級網站會爛光,成本過高

4. 「變動太快導致不穩定,維護成本代價太大,所以不想用。」就更暴露了講這句話的人,原本寫的 code 可能本來就很髒、沒有寫 test、沒有用版本控制。更可能的是,根本不知道世界有這些東西的存在 XD

回頭想想,如果「一個框架」都「不變動」而且「更新不大」,那才令人害怕。那表示這個專案一點價值都沒有,沒人想貢獻。follow 一個沒有價值的專案,那才可怕…

另外,框架本身變動就需要更新嗎?那也未必,一門生意可以穩穩的跑還可以賺錢,為何要活生生搞爛它?沒事找事做,搞爛它把生意砸掉是你自己個人的愚蠢,從來就不是框架的錯

採用 Rails 從來不是什麼辦家家酒的決定。如果想讓自己的公司業務快速拓展、自己團隊的開發者能夠迅速的跟上世界領先開發者的腳步、讓自己的開發團隊在寫程式之外,還有餘裕把產品做得更好更棒,你才更該採用 Rails。

Rails 在坊間的新手教學,基本就可以讓一個初上手的開發者達到一般人眼中的 Rapid Development 了。如果是這種快速 CRUD,很多人當然會說 XXX 也可以,為何一定要選 Rails?

但這種初階特性,及其開發速度。從來不是 Rails 開發者眼中真正 Rails 的價值,也不是所謂真正的 Rapid Development。今天這個 Talk 的主體,就是深入介紹一些只有進階開發者才知道的 Rails 之美、配合實務務經驗以及商業經驗,來達到真正恐怖的 Rapid Development。

Rapid Development,其實與 Rails 沒有絕對的正相關。只是 Rails 本身及其生態圈的發展與風氣,對於幫助發展 Rapid Development 有相當強大的正向力量。

一個專案的成敗,當然不只有開發工具暴力就行,還需要搭配其他的因素,如:團隊管理技巧、專案管理技巧、風險控制、商業運營 …etc.

不過,如果好的開發工具可以大量節省在開發產品本身時消耗的時間,那麼整個團隊將會有更多精力去專注在其他方向上,整體來說會提昇不小的專案成功機率。

 

node.js调研与服务性能测试

这几天对nodejs进行了一下简单的调研

主要关注这几个方面

  1. socket服务性能,
  2. socket客户端性能
  3. http服务性能.
  4. 服务的稳定性与资源占用
  5. 开发成本

考虑到今后的应用场景, 实现了一个简单的memcache代理服务.

内部维护了一个50连接的简单连接池, 通过长连接与memcache服务器相连.
同时对外提供socket代理服务与http restful服务

测试环境

测试使用编译安装的node.js v0.3.1,未使用任何第三方modules
代理服务与memcache部署在不同的服务器中.
系统均为rhel 5.2, cpu: AMD Opteron 2200, mem: 4g

测试用例

通过此代理程序, 分别使用memcached协议与http协议从memcache服务中取出一个长度为100bytes的值, 并检查最终输出是否正确

压力工具

socket: 由于没有找到合适的socket压力工具.用node.js实现了一个简单的socket压力工具
http: siege 2.70

测试结论

服务启动与空载资源占用


程序启动20秒后,系统资源占用达到稳定状态, 内存消耗13m, 堆尺寸8m
由堆使用变化可知v8每隔7~8秒会进行一次gc操作

100并发100秒socket长连接压力


压力启动后内存占用迅速提高至30m, v8堆也基本维持在22m的水平, 使用率在20%到50%之间波动
此时v8的gc操作频率降低到约20秒一次.
qps曲线比较平稳,在16700左右波动,幅度在400左右,v8的gc操作对性能没有明显影响
压力过程中cpu占用基本维持在95%左右,处于满载状态.
另, 测试结束后20秒左右, 所占用资源被释放,内存与v8堆均回复至空载水平.

250并发100秒http长连接压力


与socket相比, http消耗的系统资源约多出30%,且8v的gc操作也要更频繁
qps值为4392, gc操作对qps的影响也不明显
压力过程中cpu占用基本维持在95%左右,处于满载状态.
与socket时类似, 测试结束后20秒左右, 所占用资源被释放,内存与v8堆均回复至空载水平.

一些结论

性能:单cpu, socket 17000 qps, http 4400 qps, 内存消耗30~40m, cpu基本满载
用作中间层服务时,性能瓶颈基本应位于cpu运算性能.
v8引擎gc操作带来的性能影响已经可以基本忽略.
系统的健壮性不错,测试过程中qps与负载曲线基本都处于水平状态.且成功率均为100%
快速开发, 代理服务与压力工具总计开发时间3~4小时左右, 且最终性能与编译型语言差距不大,但开发时间节省很多
开发模式上与传统服务器端动态语言区别较大,不熟悉的开发人员需要一些上手时间.

另,由于时间因素,仅进行了单进程模式下的性能,使用web-worker模型的多进程模式下的性能没有进行测试
不过由单进程性能可以基本推断,在普通8核服务器下应能做到10万以上的socket, qps, 3万以上的http qps

总体来说, 非阻塞模式的io处理给nodejs带来在相对低系统资源耗用下的高性能与出众的负载能力, 非常适合用作依赖其它io资源的中间层服务.

相关源码下载:

http://nodejs-memcache-proxy-performance-test.googlecode.com/files/nodejs-text.tar.gz
包括测试程序与socket压力工具.

 

让代码取代你的配置文件吧

最近, 在编写一个专门压测NameNode的工具(以下简称s4nn), 它有两个难点 :

  1. s4nn需要可以模拟上万个DataNode ;
  2. s4nn 需要灵活的支持对NameNode访问行为的定义.

后者导致了本文的思考.

命令行参数和配置文件是最常用来配置系统的方法, 前者用于配置项较少, 后者则适合配置复杂情况. 这两种方式都有共同令人痛苦的地方:

  1. 编写代码去载入->解析->转换, 通常如同处理协议般无聊(要是有个什么变更, KMN!!);
  2. 对于复杂的配置文件编写而言, 总是没有顺手的编辑器支持, 写起来既累又易错.

要是用代码取代配置文件呢?

呃… 这会很麻烦吧, 像java这样的改了代码那还不要重新编译啊?

嗯, 确实, 但并没有想象中那么麻烦, 一些技巧可以让它变得简单(至少java是这样), 如Btrace.

用代码取代独立的配置文件不是新鲜的做法, 像GuiceGant 等已经以Internal DSL的代码代替了XML. 好处很明显:

  1. 良好的DSL风格, 简洁易懂 ;
  2. 免去对配置文件的解析转换 ;
  3. 最好的编辑器支持, 语法高亮, 一键格式化, 提示补全, 重构 ;
  4. 编译器帮助查错;
  5. 与代码无缝结合, 能够容易在变化中保持一致性.

简言之两个字, “高效”! 这倒是挺适合s4nn的, 不妨一试!

从需求的角度出发, 配置应该能够完成:

  1. 定义一组Client RPC调用行为, 其调用参数, 次数;
  2. 定义DataNode的运行时特征, 个数.

为满足这两点, 代码设计上可能会有:

class ClientDriver {
  void addRpc(times, name, args)
  void setNameNode(address)
}

class DataNodeSimulator {
  void setCapacity(size)
  void setHeartbeatInterval(sec)
  void setBlockReportInterval(sec)
  void setNameNode(address)
}

那么其配置文件会是:

 1 <configuration>
 2   <client>
 3     <rpc times="100000" name="getFileInfo">
 4       <arguments>
 5         <param name="src">/foo</param>
 6       </arguments>
 7     </rpc>
 8   </client>
 9   <namenode>
10     <address>host:port</address>
11   </namenode>
12   <datanode>
13     <simulator num="10000">
14       <capacity unit="GB">200</capacity>
15       <heartbeatInterval unit="SECOND">1</heartbeatInterval>
16       <blockReportInterval unit="HOUR">1</blockReportInterval>
17     </simulator>
18   </datanode>
19 </configuration>

简单吗? 简单, 那是因为这只是最基本的, 实际的配置应考虑到:

  1. 有30种RPC方法, 其中参数个数最多的有6,  参数类型并非都是基本类型, 参数值可能需要按照某种规则随即生成;
  2. DataNode模拟器也会有可能需要支持多种选项组合, 如实际集群种不是所有的机器的容量都一样的等.

这样的XML会臃肿到什么程度…

好吧, 看看用代码的效果:

 1 import com.taobao.s4nn.*;
 2
 3 public class Main extends Bootstrap {
 5
 6   protected void config() {
 7     setConcurrency(16);
 8     setMaxRandomPathDepth(10);
 9
10     /*
11      * DataNode simulator config
12      */
13     simulate(3, new DataNodeSimulatorGenerator() {
14       protected void config() {
15         setCapacity(gibibyte(1));
16         setTickPeriodSecond(1);
17         setHeartbeatInterval(1);
18         setBlockReportInterval(3);
19         setNameNode(proxyOfNameNodeAt("localhost:10001"));
20       }
21     });
22
23     /*
24      * NameNode Status pre-setting config
25      */
26     parallel(1, new StatusPresetterBuilder() {
27       protected void config() {
28         setName(client("InitalStatus"));
29         setNamenode(proxyOfNameNodeAt("localhost:10001"));
30         times(1, create(randomSrc, defaultFsPermission, overwrite(true), replication((short) 3), blocks(1)));
31       }
32     });
33
34     /*
35      * Client RPC driver config
36      */
37     parallel(10, new ClientDriverBuilder() {
38       protected void config() {
39         setName(seqNameWith("client"));
40         setNamenode(proxyOfNameNodeAt("localhost:10001"));
41
42         times(1, getBlockLocations(src("/foo"), randomOffsetIn(0, 1024), randomLengthIn(0, 2048)));
43         times(2, getListing(src("/foo")));
44         times(3, getLocatedListing(src("/foo")));
45         times(4, getStats());
46         times(5, getDatanodeReport(all));
47         times(6, getPreferriedBlockSize(src("/foo")));
48         times(7, getFileInfo(src("/foo")));
49         times(8, getContentSummary(src("/foo")));
50         times(9, setOwner(src("/foo"), username("jushi"), groupname("dwbasis")));
51         times(10, setReplication(src("/foo"), replication((short) 4)));
52         times(11, setPermission(src("/foo"), defaultFsPermission));
53         times(12, rename(src("/foo"), src("/bar")));
54         times(13, mkdirs(src("/bar"), defaultFsPermission));
55         times(14, setQuota(src("/foo"), randomNamespaceQuotaIn(1024, 2048), randomDiskspaceQuotaIn(1024, 4096)));
56         times(15, setTime(src("/foo"), currentTimeMillis, unchanged));
57       }
58     });
59   }
60 }

嗯,  既保持易读性又更为简洁,  重点是不用再写那些额外处理XML的代码了.

这里效仿了Guice中AbstractModule的DSL做法, 提供了setXxx, times等内置方法, 用Java IDE编写起来那是相当轻快啊~~

要是改用Scala或Groovy来实现, 那么还要简化, 使得代码味更少些, 而且直接运行简单到一条命令就搞定了:)

 

bash下利用trap捕捉信号

我在之前的文章里写了myisam读数据压缩的情况,最近决定把它用在生产环境上,所以避免不了写一个“安全”的处理脚本放在DB服务器上,这就引入了本文所讨论的话题。

我希望这个bash脚本在退出的时候做一些事情,包括:

  1. 它启动的切到后台的job需要被杀死;
  2. 一些临时文件的清理。

在这个脚本里我用到了trap这个命令,关于它,你可以man一下,我这里就不啰嗦了。直接上示例代码:

$ cat test_trap.sh

declare -i run_terminate=0

trap "run_terminate=1" SIGINT SIGTERM

# 启动io监控,IO较大时不进行压缩
vmstat 1 &gt;&gt; ./a.log &amp;

while [ ${run_terminate} -eq 0 ]
do
    # 核心代码
    sleep 30
done

for pid in $(ps -ef | awk -v p=${$} '{if ($3 == p){print $2}}')
do
    kill -9 ${pid} &gt; /dev/null
done

rm -f ./a.log
echo "Terminated."

在上面的代码中,我们捕捉INT信号(CTRL+C)和TERM信号(kill产生)。运行程序:

$ /bin/bash test_trap.sh

^C

按照我们预期的,当我CTRL+C退出程序,或者kill进程时,上面的脚本应该停掉vmstat进程,并且删除a.log,输出“Terminated”后退出。

但是,CTRL+C确实按照我们的设想进行了。可kill之后程序并没有任何反应,这是为何?

Google了一遍,天下文章一大抄,相似的例子,却没有人抛出这个问题。思索了半天,幡然醒悟:

  1. kill(killall)命令只是发出一个TERM信号(15),至于这个信号是否被对应进程捕捉并处理了,它并不管;
  2. kill发出的时候,test_trap.sh正在sleep——这个时间有点长(30秒)。

也就是说,等它“睡醒”了,它自然会处理TERM信号的。不信,你多等一会。

 

mysql数据压缩性能对比(二)

在上一篇文章中,我们用生产环境的真实数据与真实SQL测试了archive和myisampack两种方式下的性能对比情况。我们得到一个对这个测试case有效的结论,那就是在240万数据量的情况下,采用archive引擎将使得某些查询慢得无法忍受!

那么,原因是什么呢?

我们知道,mysql提供archive这种存储引擎是为了降低磁盘开销,但还有一个前提,那就是被归档的数据不需要或者很少被在线查询,偶尔的查询慢一些也是没关系的。鉴于上述原因,archive表是不允许建立自增列之外的索引的。

有了这个共识,我们拿一条测试SQL来分析一下不用索引前后的查询性能差别为什么这么大。在我们的测试SQL中有这么一条:

SELECT c1,c2,...,cn FROM  mysqlslap.rpt_topranks_v3
WHERE ... AND partition_by1 = '50008090'
ORDER BY added_quantity3 DESC
LIMIT 500

我们前边说过,测试的这个表在partition_by1这个字段上建立了索引,那么,我们初步判断在基准表和myisampack表上,这个查询应该用到了partition_by1的索引;EXPLAIN一下:

mysql&gt; EXPLAIN
    -&gt; SELECT ... FROM  mysqlslap.rpt_topranks_v3
    -&gt; WHERE ... AND partition_by1 = '50008090'
    -&gt; ORDER BY added_quantity3 DESC
    -&gt; LIMIT 500\G
*************************** 1. ROW ***************************
           id: 1
  select_type: SIMPLE
        TABLE: rpt_topranks_v3
         TYPE: REF
possible_keys: idx_toprank_pid,idx_toprank_chg
          KEY: idx_toprank_pid
      key_len: 99
          REF: const
         ROWS: 2477
        Extra: USING WHERE; USING filesort
1 ROW IN SET (0.00 sec)

正如我们所料,这个查询用到了建立在partition_by1这个字段上的索引,匹配的目标行数为2477,然后还有一个在added_quantity3字段上的排序。由于added_quantity3没有索引,所以用到了filesort。

我们再看一下这条SQL在归档表上的EXPLAIN结果:

mysql&gt; EXPLAIN
    -&gt; SELECT ... FROM  mysqlslap.rpt_topranks_v3_<strong>archive</strong>
    -&gt; WHERE ... AND partition_by1 = '50008090'
    -&gt; ORDER BY added_quantity3 DESC
    -&gt; LIMIT 500\G
*************************** 1. ROW ***************************
           id: 1
  select_type: SIMPLE
        TABLE: rpt_topranks_v3_archive
         TYPE: ALL
possible_keys: NULL
          KEY: NULL
      key_len: NULL
          REF: NULL
         ROWS: 2424753
        Extra: USING WHERE; USING filesort
1 ROW IN SET (0.00 sec)

EXPLAIN说:“我没有索引可用,所以只能全表扫描2424753行记录,然后再来个filesort。”你要追求性能,那显然是委屈MySQL了。

扩展阅读:

关于MYSQL的ORDER BY实现原理,请参考 http://isky000.com/database/mysql_order_by_implement

 

mysql的数据压缩性能对比(一)

数据魔方需要的数据,一旦写入就很少或者根本不会更新。这种数据非常适合压缩以降低磁盘占用。MySQL本身提供了两种压缩方式——archive引擎以及针对MyISAM引擎的myisampack方式。今天对这两种方式分别进行了测试,对比了二者在磁盘占用以及查询性能方面各自的优劣。至于为什么做这个,你们应该懂的,我后文还会介绍。且看正文:

1. 测试环境:

  1. 软硬件
  2. 一台 64位 2.6.18-92 内核Linux开发机,4G内存,4个2800Mhz Dual-Core AMD Opteron(tm) Processor 2220 CPU。

    MySQL放在一块7200转SAT硬盘,未做raid;

    MySQL未做任何优化,关闭了query cache,目的在于避免query cache对测试结果造成干扰。

  3. 表结构
  4. 2424753条记录,生产环境某一个分片的实际数据;

    分别建立了(partition_by1,idx_rank) 和 (partition_by1,chg_idx)的联合索引,其中partition_by1为32长度的varchar类型,用于检索;其余两个字段均为浮点数,多用于排序;

    autokid作为子增列,充当PRIMARY KEY,仅作为数据装载时原子性保证用,无实际意义。

2. 测试目的:

  1. 压缩空间对比
  2. 压缩率越大,占用的磁盘空间越小,直接降低数据的存储成本;

  3. 查询性能对比
  4. 压缩后查询性能不应该有显著降低。Archive是不支持索引的,因此性能降低是必然的,那么我们也应该心里有个谱,到底降低了多少,能不能接受。

3. 测试工具:

  1. mysqlslap
  2. 官方的工具当然是不二之选。关于mysqlslap的介绍请参考 官方文档

  3. 测试query
  4. 截取生产环境访问topranks_v3表的实际SQL共9973条,从中抽取访问量较大的7条,并发50,重复执行10次。命令如下:

    ./mysqlslap --defaults-file=../etc/my.cnf -u**** -p**** -c50 -i10 -q ../t.sql --debug-info

4.测试结论

比较项 磁盘空间 耗时(秒) CPU Idle LOAD 并发
基准表(MyISAM) 403956004 2.308 30 15 50
ARCHIVE 75630745 >300 75 4 1
PACK 99302109 2.596 30 22 50

根据上面的表格给出的测试数据,我们简单得出以下结论:

  1. 针对测试表,Archive表占用空间约为之前的18.7%,myisampack后空间占用约为之前的24.6%;二者相差不多,单纯从空间利用情况来看,我们似乎需要选择archive表;
  2. 我们再看查询性能,与基准表进行对比。无论在总耗时还是系统负载方面,50并发下的pack表查询性能与基准表相当;而archive表在单并发情况下耗时超过了5分钟(实在等不了了,kill之)!

那么,我们似乎可以得出结论,针对需要在线查询的表,ARCHIVE引擎基本上可以不考虑了。

在下一篇文章中,我将会详细为什么这个测试过程中ARCHIVE引擎如此地慢。

 

nginx+resin 使用中文域名解决方案。

xxx中文域名.中国

这样的域名在resin下会出错:

[15:34:31.628] {hmux-127.0.0.1:6801-5} java.lang.StringIndexOutOfBoundsException: String index out of range: 9[15:34:31.628] {hmux-127.0.0.1:6801-5}  at java.lang.String.charAt(String.java:687)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.host.DomainName.decode(DomainName.java:205)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.host.DomainName.fromAscii(DomainName.java:86)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.host.HostContainer.buildInvocation(HostContainer.java:305)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.cluster.Server.buildInvocation(Server.java:915)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.dispatch.DispatchServer.buildInvocation(DispatchServer.java:209)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.hmux.HmuxRequest.handleRequest(HmuxRequest.java:427)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.port.TcpConnection.run(TcpConnection.java:603)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.util.ThreadPool$Item.runTasks(ThreadPool.java:721)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.util.ThreadPool$Item.run(ThreadPool.java:643)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at java.lang.Thread.run(Thread.java:619)[15:34:31.628] {hmux-127.0.0.1:6801-5} java.lang.RuntimeException: java.lang.StringIndexOutOfBoundsException: String index out of range: 9[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.host.DomainName.fromAscii(DomainName.java:109)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.host.HostContainer.buildInvocation(HostContainer.java:305)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.cluster.Server.buildInvocation(Server.java:915)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.dispatch.DispatchServer.buildInvocation(DispatchServer.java:209)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.hmux.HmuxRequest.handleRequest(HmuxRequest.java:427)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.port.TcpConnection.run(TcpConnection.java:603)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.util.ThreadPool$Item.runTasks(ThreadPool.java:721)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.util.ThreadPool$Item.run(ThreadPool.java:643)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at java.lang.Thread.run(Thread.java:619)[15:34:31.628] {hmux-127.0.0.1:6801-5} Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: 9[15:34:31.628] {hmux-127.0.0.1:6801-5}  at java.lang.String.charAt(String.java:687)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.host.DomainName.decode(DomainName.java:205)[15:34:31.628] {hmux-127.0.0.1:6801-5}  at com.caucho.server.host.DomainName.fromAscii(DomainName.java:86)[15:34:31.628] {hmux-127.0.0.1:6801-5}  … 8 more

 

 

原因是中文域名做了“转码”,不知道是什么鬼东西。反正就出错了。要解决。

分析一下中文域名转码后是怎样的?

例如 中文域名.中国   转码后   xn--wlqpfy8in9c9zd3hc50a.xn--io0a7i

原来这样:当resin接收 到host时,用正常的域名去解释这个地址,由于这个地址不符合正规的域名格式,所以出错。

解决思路,能不能在nginx转发时,改变host呢?查一查nginx说明文档,有一条这样写:

proxy_set_header Host $host;

应该就是这条了。将后面的$host改为 一个英文域名

proxy_set_header Host www.youdomain.com;

 

问题解决了。哈。。。大家加油。

 

 

用Drupal 创建的8种网站

Drupal没有像WordPress那样普及,或许是因为它稍有难度来学习。但是如果你创建大的网站,使用Drupal,高度灵活的开源CMS,可以得到你想要的更漂亮、更强大的各种类型的网站。

下面是8种类型的网站,使用Drupal来创建,希望帮你对Drupal有更深刻的认识。

1. 文件存储分享站点

使用Drupal创建文件分享,你可以使用 CCK 和 Views ,也包括一些模块,比如Media MoverFilebrowser 或者Web File Manager。看看Box.net,你会非常感兴趣的^_^。

2. 社交网站

在社交网络能力方面,Drupal可能是最好的CMS。Drupal提供了强大的用户管理和权限管理系统。但是如果你想创建强大的社交网站,就需要一些模块,见http://drupal.org/node/206724

你想看一些案例?Imbee 或者 GoingOn

3. Twitter Clone

建议你不要尝试利用Drupal创建Twitter竞争产品,但是,如果你想整合Twitter功能到你的站点,Drupal的微博模块可以帮到你。

4. 新闻News portal

如果你想创建新闻站点或杂志站点,Drupal的完美的选择。使用CCK 和 Views ,你可以创建所有的发布内容类型,并且可灵活列表。这样的新闻站点非常之多,比如New York Observer

5. 博客网络

用Drupal创建博客网站,很轻松,甚至无需额外模块。看看 Wisebread吧。

6. 视频分享站点

这类站点太耗带宽了,如果你决定创建,那么Drupal来帮你实现吧。FlashVideo 模块提供了创建Youtube克隆的强大能力,它整合了CCK,转换视频到FLV,并有分享代码。另外你也可以尝试Media Mover 和 SWF ToolsMTV UK 站点就是Drupal创建的。

7. 图片分享站点

Image module ,这个模块将派上用场,可让你创建类Flickr站点,很好很强大。MyFinePix 就是Drupal创建的照片分享站点。

8. 类Digg-like news site

感谢 Drigg module, 这个模块可帮助你快速建立Digg克隆站点。流行的设计社交新闻网Designbump在使用Drupal。

英文原文:http://www.designer-daily.com/8-types-of-sites-you-can-build-with-drupal-13924

Nginx源码分析-内存池

Nginx的内存池实现得很精巧,代码也很简洁。总的来说,所有的内存池基本都一个宗旨:申请大块内存,避免“细水长流”。

一、创建一个内存池

nginx内存池主要有下面两个结构来维护,他们分别维护了内存池的头部和数据部。此处数据部就是供用户分配小块内存的地方。

//该结构用来维护内存池的数据块,供用户分配之用。
typedef struct {
    u_char               *last; 	//当前内存分配结束位置,即下一段可分配内存的起始位置
    u_char               *end;  	//内存池结束位置
    ngx_pool_t           *next; 	//链接到下一个内存池
    ngx_uint_t            failed; 	//统计该内存池不能满足分配请求的次数
} ngx_pool_data_t;
//该结构维护整个内存池的头部信息。
struct ngx_pool_s {
    ngx_pool_data_t       d; 		//数据块
    size_t                max;		//数据块的大小,即小块内存的最大值
    ngx_pool_t           *current;	//保存当前内存池
    ngx_chain_t          *chain;	//可以挂一个chain结构
    ngx_pool_large_t     *large;	//分配大块内存用,即超过max的内存请求
    ngx_pool_cleanup_t   *cleanup;	//挂载一些内存池释放的时候,同时释放的资源。
    ngx_log_t            *log;
};

有了上面的两个结构,就可以创建一个内存池了,nginx用来创建一个内存池的接口是:ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log)(位于src/core/ngx_palloc.c中);调用这个函数就可以创建一个大小为size的内存池了。这里,我用内存池的结构图来展示,就不做具体的代码分析了。

ngx_create_pool接口函数就是分配上图这样的一大块内存,然后初始化好各个头部字段(上图中的彩色部分)。红色表示的四个字段就是来自于上述的第一个结构,维护数据部分,由图可知:last是用户从内存池分配新内存的开始位置,end是这块内存池的结束位置,所有分配的内存都不能超过end。蓝色表示的max字段的值等于整个数据部分的长度,用户请求的内存大于max时,就认为用户请求的是一个大内存,此时需要在紫色表示的large字段下面单独分配;用户请求的内存不大于max的话,就是小内存申请,直接在数据部分分配,此时将会移动last指针。

二、分配小块内存(size <= max)

上面创建好了一个可用的内存池了,也提到了小块内存的分配问题。nginx提供给用户使用的内存分配接口有:
void *ngx_palloc(ngx_pool_t *pool, size_t size);
void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);

ngx_palloc和ngx_pnalloc都是从内存池里分配size大小内存,至于分得的是小块内存还是大块内存,将取决于size的大小;他们的不同之处在于,palloc取得的内存是对齐的,pnalloc则否。ngx_pcalloc是直接调用palloc分配好内存,然后进行一次0初始化操作。ngx_pmemalign将在分配size大小的内存并按alignment对齐,然后挂到large字段下,当做大块内存处理。下面用图形展示一下分配小块内存的模型:

上图这个内存池模型是由上3个小内存池构成的,由于第一个内存池上剩余的内存不够分配了,于是就创建了第二个新的内存池,第三个内存池是由于前面两个内存池的剩余部分都不够分配,所以创建了第三个内存池来满足用户的需求。由图可见:所有的小内存池是由一个单向链表维护在一起的。这里还有两个字段需要关注,failed和current字段。failed表示的是当前这个内存池的剩余可用内存不能满足用户分配请求的次数,即是说:一个分配请求到来后,在这个内存池上分配不到想要的内存,那么就failed就会增加1;这个分配请求将会递交给下一个内存池去处理,如果下一个内存池也不能满足,那么它的failed也会加1,然后将请求继续往下传递,直到满足请求为止(如果没有现成的内存池来满足,会再创建一个新的内存池)。current字段会随着failed的增加而发生改变,如果current指向的内存池的failed达到了4的话,current就指向下一个内存池了。猜测:4这个值应该是作者的经验值,或者是一个统计值。

三、大块内存的分配(size > max)

大块内存的分配请求不会直接在内存池上分配内存来满足,而是直接向操作系统申请这么一块内存(就像直接使用malloc分配内存一样),然后将这块内存挂到内存池头部的large字段下。内存池的作用在于解决小块内存池的频繁申请问题,对于这种大块内存,是可以忍受直接申请的。同样,用图形展示大块内存申请模型:

注意每块大内存都对应有一个头部结构(next&alloc),这个头部结构是用来将所有大内存串成一个链表用的。这个头部结构不是直接向操作系统申请的,而是当做小块内存(头部结构没几个字节)直接在内存池里申请的。这样的大块内存在使用完后,可能需要第一时间释放,节省内存空间,因此nginx提供了接口函数:ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);此函数专门用来释放某个内存池上的某个大块内存,p就是大内存的地址。ngx_pfree只会释放大内存,不会释放其对应的头部结构,毕竟头部结构是当做小内存在内存池里申请的;遗留下来的头部结构会作下一次申请大内存之用。

四、cleanup资源


可以看到所有挂载在内存池上的资源将形成一个循环链表,一路走来,发现链表这种看似简单的数据结构却被频繁使用。由图可知,每个需要清理的资源都对应有一个头部结构,这个结构中有一个关键的字段handler,handler是一个函数指针,在挂载一个资源到内存池上的时候,同时也会注册一个清理资源的函数到这个handler上。即是说,内存池在清理cleanup的时候,就是调用这个handler来清理对应的资源。比如:我们可以将一个开打的文件描述符作为资源挂载到内存池上,同时提供一个关闭文件描述的函数注册到handler上,那么内存池在释放的时候,就会调用我们提供的关闭文件函数来处理文件描述符资源了。

五、内存的释放

nginx只提供给了用户申请内存的接口,却没有释放内存的接口,那么nginx是如何完成内存释放的呢?总不能一直申请,用不释放啊。针对这个问题,nginx利用了web server应用的特殊场景来完成;一个web server总是不停的接受connection和request,所以nginx就将内存池分了不同的等级,有进程级的内存池、connection级的内存池、request级的内存池。也就是说,创建好一个worker进程的时候,同时为这个worker进程创建一个内存池,待有新的连接到来后,就在worker进程的内存池上为该连接创建起一个内存池;连接上到来一个request后,又在连接的内存池上为request创建起一个内存池。这样,在request被处理完后,就会释放request的整个内存池,连接断开后,就会释放连接的内存池。因而,就保证了内存有分配也有释放。

总结:通过内存的分配和释放可以看出,nginx只是将小块内存的申请聚集到一起申请,然后一起释放。避免了频繁申请小内存,降低内存碎片的产生等问题