RSS 2.0 Feed

2008年2月11日

     前些天发现自己的网站无法访问,询问机房这边,说是机器最近常死机,我就把网站迁移到一个朋友的主机上, 结果没过几天机器又挂了,问朋友的机房那边说是硬件防火墙被攻击了而死掉了,详细情况不知。看来不是硬件问题,多半是被SYN FLOOD或者CC攻击了。恰好原来的机房说最近购买了新的防火墙,我又放了回去。

      既然不是硬件问题而可能是攻击,我就开始检查IIS log了,发现 IIS 里面很多Timer_ConnectionIdle和Timer_MinBytesPerSecond的错误,到网络上google了一下,常见说法是说错误是因为IIS的设置不当引起的,是因为连接超时时间设置太小,解决方法是设置连接超时为600秒,把MinFileBytesPerSec的设置从240修改到0(相当于关掉该设置)。觉得这些解决方法都有问题,假如车辆防盗警报经常响,正确的解决方法是看看有谁常来打你车子的主意,或者把车子放在更安全的地方,而绝对不是关掉警报。

      因为HTTP服务需要占用TCP连接,而TCP连接时是需要占用系统资源的,而且IIS为每个连接也需要分配相应的资源(至少一个FSM是要给的)。目前的主机能够处理上万的连接就可以说是软硬件设计都很不错了(可以参见C10K )。假如恶意人员通过一台或者多台机器发起大量的连接,而不请求内容(这样不需要消耗多少攻击机器的带宽),就可以大量消耗服务器资源而达到拒绝服务的目的。

     所以 IIS 需要关闭长时间非活动的连接,这个就是Timer_ConnectionIdle 的错误由来。

     既然盾牌改进了,当然矛也要发展一下,攻击者可以给服务器故意缓慢的发送和接收内容而消耗服务器的资源,这样可以避免服务器对于Timer_ConnectionIdle 的保护,相应的IIS的防范就是 MinFileBytesPerSec 设置,MinFileBytesPerSec 属性通过以最小的数据量保持连接,来禁止恶意的或软件工作不正常的客户端消耗资源。如果吞吐量低于 MinFileBytesPerSec 设置的值,则终止连接。LOG里面就会显示Timer_MinBytesPerSecond错误(一些Timer_MinBytesPerSecond错误是因为 windows 2003 的http.sys错误引起的,解决方式是打上最新 ServicePack : http://support.microsoft.com/kb/919797   http://support.microsoft.com/kb/919797/en-us )

      所以说这些设置都是用来保护IIS服务器的,可以一定程度上抵御一些恶意的行为消耗服务器的资源,所以我反而将IIS连接超时从原来的600秒改到了30秒,就让攻击来得更加猛烈些吧!!不过我还是很纳闷: 网站不大,所以我不可能去财消灾。无怨无仇的,谁这么无聊啊?!

 

最后发现结果挺搞笑的,欢迎看后继文章。(最近挺忙, 续集暂时不会出来。希望解决这种错误的同志:假如不是攻击的话,打上微软的补丁包就好了)

posted @ | Feedback (3) | Filed Under [ 多维技术 ]

2007年9月6日

看了蝈蝈俊.net的《理解缓存》,觉得真的是一个对于web applcation 缓存应用的好文,难得的是覆盖了冰山海面下的部分。我现在做的应用可以说和缓存打的交道也不少(不过不是web应用),也写些东西来分享给大家。

1.缓存是什么?

在我看来,缓存是通过存储中间结果,缩短访问路径来减少开销,提高性能的方法。这个概括未必最科学全面,也不够具体。我们来看看一个http 动态页面访问的例子:

访问路径是 : 数据库->应用数据集->内存对象->动态页面->HTTP服务器->用户浏览器

一个简单的访问,中间经过了多个环节,我们称这些环节为访问路径,我们来看看哪些地方可以采用缓存:

  1. HTTP服务器->用户浏览器,大家都知道浏览器都有本地缓存,浏览过的页面图片脚本等都会根据http header还有html的相关指令临时保存在本地硬盘里面,假如再次访问,访问路径就变成了"本地硬盘缓存->用户浏览器", 浏览的环节大幅度减少,性能也提高了。在这个环节,经常还使用带缓存的代理服务器来提高性能。
  2. 动态页面->HTTP服务器,这里有多种方式, 比如动态页面静态化,目前大量的大型网站使用这种方式。还有WEB服务器根据一定规则缓存整个动态页面,比如asp.net的Page Cache。这里的访问路径变成了"缓存页面->HTTP服务器->用户浏览器"
  3. 本地数据集->内存对象->动态页面。常见的就是缓存数据集还有对象,这个是ASP.NET cache里面相对浓墨重彩的部分,也是Memcached发力的侧重点, 也就不多说了。
  4. 数据库->应用数据集,不少数据库实现都有查询缓存

这里缓存都在访问路径中的环节存储了中间结果,用来减少相应的开销

2.缓存本身的开销

缓存本身也是有性能开销的,一种是将内存存储到缓存中开销,一种是将内容取出来的开销。另外,缓存往往还要付出空间上的开销。另外还要付出系统复杂度的开销,这增加了开发和维护成本。

大家也听说过IE缓存太大了或者是文件系统碎片太多以后,可能相反会拖累浏览速度,测试我倒是没有做过,但是的确是完全可能的。也就是说,缓存的开销可能会不缓存而是直接访问还要大,这就是大家不想看到的了。

3. 缓存的目的

其实前面已经说过缓存是为了减少开销,提高性能,这不就是缓存的目的吗?这倒是没错,但是也不尽然。

因为开销是一个很笼统的词,具体点有CPU开销,磁盘IO开销,网络开销,数据库访问开销等,缓存对于性能的优化,除了一些大众化的优化措施以外,还得有的放矢。

以前学习写程序,大家一定都听说过什么时间换空间,空间换时间,到底什么时候要拿空间换时间,什么时候要拿时间换空间,只能看具体应用了。前面说过缓存也有开销,其实缓存就是拿某些开销换取其他开销的下降而已。比如说动态页面静态化是一些大型网站常见的优化方法,他付出了磁盘的空间和读写开销,来换取更低的CPU消耗(不用解析动态页面)和数据库访问。有些网站每天访问量没有多少,却频繁生成和更新静态页面,同时还在服务器上做下载,本来磁盘就不堪重负,这下更加是雪上加霜,可以说是缓存优化的反面例子。或者是本来内存不大,磁盘swap很多影响性能,但是却使用大量内存做页面和对象缓存,也是反面的例子。

所以说不能盲目的进行缓存优化,一个系统,性能出现了问题,或者将来可能出现问题,性能总会有一个或者若干个瓶颈,我们要做的就是平衡或者削弱这个瓶颈,缓存是重要的手段。

所以缓存的目的是针对几个主要指标,兼顾若干个其他指标,来尽量实现低开销。

比如,数据库的CPU较高,那么一般是复杂的查询或者是存储过程导致的,在前面的各个环节进行缓存优化,比如缓存数据集和内存对象,都是好的解决方法,缓存整个页面也是个好方法,但是缓存页面要付出更多的空间开销,在某些情况下,缓存数据集或者内存对象已经够了。

假如WEB服务器的CPU较高,往往是因为动态页面处理造成的,找出开销大的处理,将处理的结果对象缓存,或者是页面静态化是不错的方法,而缓存数据库结果集往往收效不大。

4. 啥样的缓存才是好缓存?

蝈蝈俊认为是命中率最高的缓存最好。我做的领域是streaming server的磁盘IO缓存和CDN的网络边缘缓存,瓶颈就是磁盘或者网络IO,这种时候,命中率就是硬道理。

但是对于web服务器来讲,影响性能的因素很多,不同的内容访问开销相差很大,什么是好缓存,虽然命中率是极为重要的指标,但是还得要综合缓存的开销,原始的访问路径/开销和性能瓶颈来综合评价。也就是说不同的应用侧重不同,跑的硬件条件和瓶颈也不一样,很难有一个简单的指标。

比如缓存一个命中率稍低,但是原始访问开销很大的对象(比如要经过复杂查询和处理的对象) 比一个命中率较高,但是原始访问开销很小的对象要划得来。我觉得假如有一个"加权命中率"会更好,原始访问开销大的对象,要给与更高的权值,再进行命中率的计算。


假如一个缓存策略可以减少某个方面的性能开销,但是却带来了新的性能瓶颈,那么它也不是个好的缓存实现。比如磁盘IO紧张,页面更新非常频繁的情况下,静态页面缓存往往就不是一个好方法(好像没有用静态化页面做聊天室的吧:->)。

到底什么是好的缓存实现呢?前面也讲过了缓存的目的,我觉得充分利用了软硬件条件,消灭了性能瓶颈的就是好缓存。

5. 如何进行缓存优化

进行缓存优化,第一是要找到性能瓶颈,第二是找到瓶颈有关的应用部分。

性能瓶颈一般还是好找,系统有那么多性能计数器,数据库n多的调优工具,查询优化分析器。好好对照厂商的技术资料和google大法,很容易找到是CPU 内存还是IO限制了性能提高。

然后就是应用针对性能瓶颈的优化,有一个2/8定律,就是说80%的性能都消耗在20%的处理中,这20%也分为两种,一种是访问比例很高的,一种是开销贼大的,当然两个都占了,就更加没说的了。我们的任务就是集中火力提升这20%的性能,缓存往往是重要的方法。

寻找开销大的操作是个细活。我们在软件设计阶段可以预见瓶颈的部位,在出现问题的时候可以猜测出现瓶颈的部位,但是除非对行业模型,相关的架构性能还有一些性能细节非常了解(也就是说你实践过n次了),否则当初的预见和猜测都可能有较大偏差的,性能优化没有银弹,实践出真知。要多多使用工具进行分析,假如在系统里本身就有一些性能计数,可以在线或者离线提取就更好了。对于成熟的运营系统,性能统计和分析往往是其必不可少的功能。



在代码里寻找性能瓶颈太过细节,就不废话了,剩下的事情就是需要分析用户行为,根据用户对内容的访问频度实现不同的缓存策略。

6. 用户行为分析

用户行为主要有两种,一种是时间行为分布,就是一个内容的访问随时间变化的规律,不同的内容常常不同,比如新闻和音乐肯定是不一样的。
另外一种是空间分布,就是用户对于不同内容的偏好程度,大家常常说80/20规律,就是指大量的访问往往集中于少量内容,80/20只是一个定性的规律,这里一般适用的规律是zipf分布,我统计过一些系统的行为,和zipf分布的吻合度还是很好的,大家可以看看我的一篇文章:http://blog.joycode.com/peon/archive/2006/08/19/80885.aspx

大型的系统,缓存所有的内容肯定是不切实际的,缓存访问比率高的部分内容大概能达到多少缓存命中率呢?我根据zipf公式做了计算,结果可以看看下面的图:

y轴为命中率,x轴为缓存的内容占所有内容的比率,不同类型的内容有不同的a值(什么是a值, 请看http://blog.joycode.com/peon/archive/2006/08/19/80885.aspx),上面除了一条lg函数曲线外,其余的是各个a值对应的命中率曲线。可以看到a=0.95的时候,刚好符合20/80的定律。
光有理论的不够,进行实际统计永远是必须的,会议室里面的讨论永远代替不了实际的统计,log分析,性能计数器都是必要的技术。

7.关于分布式缓存

缓存会增加应用的复杂度,假如应用是分布式的而缓存不是分布式的,这个复杂度将会会平方。但是分布式缓存肯定比本机缓存效率低,所以是否选择分布式缓存实现,哪些内容使用分布式缓存哪些使用本机缓存,是第一步就得考虑的。

后记:
本文的着重点在性能,但是有些东西写的虚了点,有些想写的东西又没有写出来,算是薄积薄发献丑了:>
另外推荐一篇文章: 《从LiveJournal后台发展看大规模网站性能优化方法



posted @ | Feedback (10) | Filed Under [ 多维技术 ]

2007年7月23日

     发现很多情况下,msn传输文件比qq要慢,倒不是说msn没有快的时候,但是大部分的时候是真的比QQ慢,连我这种神经比较大条的人都注意到了,google了一下,早就有人做了解答,基本上就是说msn传输文件是使用TCP,而QQ使用UDP,剩下的事情就是论证TCP传输文件没有UDP快。大概的就是下面的几个观点:

1. TCP是可靠的,需要验证数据是否到达和是否正确,而UDP是不可靠的,少做了很多事情,所以MSN的文件传输比QQ慢。

我看了当时就想笑,UDP传输不可靠,但是应用层肯定会写代码作些和TCP的可靠传输差不多的事情。也用了QQ不少时日了,从来也没有发现传输文件有问题的,用UDP作协议也很久了,不做应用层验证重传的代码,我还真不敢写。这个理由,失败。

2. TCP建立连接需要3次握手,而UDP不需要,所以TCP慢。

3次握手这个事实倒是千真万确,还好我没有那么容易被忽悠,两个人谈话之前要握握手的确要稍微费上几秒钟,但是这个关谈话的语速啥事情?假如网络的ping值达到300ms,各位看官喜欢网络游戏的,估计都不会玩了,否则垂死的boss会高兴的发现你忽然变成了木偶可以随便殴打不还手,最后你只能骂骂电信网通然后复活几分钟后又是一条好汉。但是对于TCP,这样的ping值,3次握手一般都不需要1秒钟,把这个定为文件慢慢传的罪魁祸首,似乎太不靠谱了,这个理由还是失败。

3. TCP一旦建立链接,路由就确定了,而UDP是不确定的路由方式,谁速度快走谁的线路。

这样说就说明没有作者好好理解TCP/IP协议了,TCP的链路只是一个逻辑的,又没有建立物理链路,下面跑的还是IP包,这个包走这条路,那个包完全可能走另外的路,这点TCP和UDP没有两样。理由继续失败。

4. msn服务器在国外?

有些道理,但是我听一个美国的朋友说他也喜欢用QQ传文件的。

 

     那到底是怎么回事呢?是因为微软没有做好?(题外话,个人的确觉得MSN相比QQ的飞速进步而显得动作迟缓)QQ的Fans开始摩拳擦掌,一些不那么喜欢M$的估计就要开始丢板砖了。不管立场如何,事实还是要探寻一下,本着不求甚解,薄积薄发,浅入浅出的精神,我认为有几个可能原因:

1. 两个传文件客户端都在NAT后面的时候 (你不知道NAT啥意思?比如多个人通过路由器共享一个猫上网,那么你们一般就是在NAT后面了),从技术实现上讲,TCP在这种情况下穿越NAT比UDP麻烦得多。UDP只要开始几个穿越NAT的协商包需要服务器转一下,后面的文件数据可以两个客户端之间直接传输搞定,但是一般TCP就只能全程由服务器中转了,你说哪一个会比较快? 为什么TCP需要服务器中转?先看看NAT吧,听说有高人可以用raw sock搞定,反正我没有中间服务器搞不定。

 

2. 但是即使上面的条件不成立,msn还是一般比QQ慢的。问题还是在出在前面提到的验证数据可靠性上面,TCP是可靠的,UDP是不可靠的,但是用UDP做传输文件这档子事情,肯定要在应用层写一个验证的协议,否则传过来的文件缺胳膊少腿,会被用户骂死的。说是协议,其实也不难,打个比方吧:

    long long ago,贾宝玉在北京,林黛玉在长沙,怎么互诉衷肠呢,派家丁送信!路途遥远,怎么知道信收到没有?打电话问?那时候发明了这个就不用送信了,只能看家丁是否带了回信来了。假如发现一个家丁一个月还没有回,那就多半迷路堵车遭遇山贼或者开小差到扬州花差快活去了,再派一个人送吧! TCP就是这么做的,UDP在应用层协议一般也需要这么做,但是实现上有时候往往有区别。

    北京到长沙之间的路,并不是只有一个人跑的,常常会发生拥堵,假如发现家丁好久没有回了,TCP版的贾宝玉再派人送信是要的,但是他会比较识大体,他会少写信,降低发送速度,原来一天一封,现在可能一周一封了。他想,大家假如都这样,路就不会那么拥挤了。这做法很有道理,假如塞车了大家都想见缝插针,只能越来越塞,最后大家都动不了,还不如彼此容让慢慢排队。而UDP版本的贾宝玉假如也这么集体主义,那么他就叫做TCP友好流,就假如它只管自己拼命挤,就是非TCP友好的。

    所有的TCP协议实现假如发现拥塞,都会马上降低自己的发送速度。假如基于UDP的协议不这么做的话,原来拥塞的IP包加上重发的包,网路只会越来越拥塞,最后所有的坚持集体主义的TCP流都会做出牺牲,把带宽让给一些非TCP友好的UDP流。所以除非网络状况非常好,否则TCP是拼不过非TCP友好的协议的。

    我怀疑(仅仅是怀疑而已,我也没有条件和时间验证),QQ的文件传输机制是不那么TCP友好的,它抢带宽更加"我能",这样虽然对于整个网络负荷不是什么好事,但是对于单个用户,就见仁见智了,就好像大家看待多线程下载或者p2p一样。

posted @ | Feedback (30) | Filed Under [ 多维技术 ]

2007年5月19日

   无论你是否采用测试驱动开发(TDD),编写Unit test case都是重要的工作.在项目的各个阶段,我们都需要构建测试、运行测试、报告测试结果. 有很多框架为这个工作提供便利,比如大名鼎鼎的xUnit系列.今天,我准备介绍的是一个轻量级的C++ Unit Test框架:TUT (Template Unit Tests的缩写)

   大家第一个想问的问题估计就是:它和CppUnit有什么不同? 它有什么特点?
   我觉得最关键的地方在于TUT是一个轻量级的框架,TUT是使用template技术写的,完全没有macro. 除去例子,它的代码一共只有两个头文件.我们完全不用build这个框架然后设置编译连接那么多麻烦,只要在编写Unit test的时候,把这两个头文件拷贝到项目目录或者设置一下头文件的包含路径就可以了.

  TUT把Unit test case通过group-test的层次组织,用户可以把相似的test case放到同一个group,这个group有一个唯一的名字, group包含多个test, 包含的test数量取决于你的编译器允许的template嵌套深度.用户可以选择运行所有的测试或者只是部分group,甚至部分test. 在每个test里面,你可以使用TUT提供的一些方法来确认测试结果的正确性,告诉TUT这个测试是成功还是失败.

   闲话少说,估计大家喜欢眼见为实,让我们写个例子来看看:

   1. 首先包含必要的头文件
#include <stdio.h>
#include "tut.h"
#include "tut_reporter.h"


  2. 接着写了一个测试class

//测试类,做的事情很简单,记住给它的最大的数字,我们就测试这个类
class max_pool
{

int
m_max;
public
:
max_pool(int x=5) : m_max(x){ printf("%s init max num %d\n", __func__, x); }
void
try_set_value(int num) { if (num>m_max) m_max = num; }
int
get_max() { return m_max; }
};
   3. 写关于test group的代码 
/*这个类用在test_basic里面,
我们可以用它来完成一些每个test都需要做的初始化和清理工作,
不过这里只是为了说明, 只是输出点文字
*/

class
obj_init
{

public
:
obj_init() { printf( "obj_init for each test\n" ); }
~
obj_init() { printf( "~obj clean for each test\n" ); }
};

namespace tut
{

struct
test_basic
{

max_pool m;
obj_init xxx;
};
//每个测试用例,都会重新构造和析构test_basic里面的m 和 xxx,
//假如有什么需要每个测试都需要初始化和销毁的,放在这里

typedef test_group<test_basic> factory;
typedef factory::object object;

factory tf("max_poll_test_name");//这里设置了测试group的名字
}

4. 写两个test case
namespace tut
{

/**
* Checks insert operation
*/

template
<>
template
<>
void
object::test<1>()
{

printf("\trun test case <1>\n");
m.try_set_value(4);
ensure( m.get_max()==5 );//告诉TUT假如这里==5就是测试OK
}

template
<>
template
<>
void
object::test<2>()
{

printf("\trun test case <2>\n");
m.try_set_value(8);
ensure( m.get_max()==8 );//告诉TUT假如这里==8就是测试OK

}
}

5.终于写到main()了,运行所有测试:
using tut::reporter;
using tut::groupnames;

namespace tut
{
   test_runner_singleton runner;
}

int main()
{
	reporter visi;
	tut::runner.get().set_callback(&visi);
	tut::runner.get().run_tests();
    return 0;
}

编译...结果我用的EditPlus+cygwin组合编译出现了问题,说我的template嵌套层次太多。看看tut.h:
template <class Data, int MaxTestsInGroup = 50>
class test_group : public group_base { ...
 
原来是 test_group<test_basic> 缺省有50个test,也就是说编译器需要能支持50层的template嵌套, 
我修改了一下typedef test_group<test_basic,15> factory; 这样编译器只要能支持15层嵌套就可以通过了。
大家注意,VC6也是无法兼容TUT的,需要VS2003或者以上版本的支持.
 
最后运行的结果当然是测试通过了,结果如下, 大家可以注意到struct test_basic里面对象在每个test的输出:
 
max_pool init max num 5obj_init for each test
        run test case <1>~obj clean for each test
max_poll_test_name: .max_pool init max num 5
obj_init for each test
        run test case <2>
~obj clean for each test.max_pool init max num 5
obj_init for each test
~obj clean for each test

posted @ | Feedback (3) | Filed Under [ C/C++ ]

2007年5月13日

领导者/追随者(Leader/Followers)模型和半同步/半异步(half-sync/half-async)模型都是常用的客户-服务器编程模型.

这几天翻了些文章,发现对领导者/追随者模型说的比较少,下面就这个模型打个比方:

  1. 话说一个地方有一群有组织无纪律的人从事山贼这个很有前途的职业。
  2. 一般就是有一个山贼在山路口察看,其他人在林子里面睡觉。
  3. 假如发现有落单的过往客商,望风的山贼就会弄醒一个睡觉的山贼,然后自己去打劫。
  4. 醒来的山贼接替作望风的事情。
  5. 打劫的山贼搞定以后,就会去睡觉,直到被其他望风的山贼叫醒来望风为止。
  6. 有时候过往客商太多,而山贼数量不够,有些客商就能侥幸平安通过山岭(所有山贼都去打劫其他客商了)。

 

下面是这个模式的计算机版本:

 

  1. 有若干个线程(一般组成线程池)用来处理大量的事件
  2. 有一个线程作为领导者,等待事件的发生;其他的线程作为追随者,仅仅是睡眠。
  3. 假如有事件需要处理,领导者会从追随者中指定一个新的领导者,自己去处理事件。
  4. 唤醒的追随者作为新的领导者等待事件的发生。
  5. 处理事件的线程处理完毕以后,就会成为追随者的一员,直到被唤醒成为领导者。
  6. 假如需要处理的事件太多,而线程数量不够(能够动态创建线程处理另当别论),则有的事件可能会得不到处理。

这个模型其实并不难于理解,但是我想假如是中国人给起的名字的话,也许会叫作 "皇帝轮流做,今年到我家" 模型更加贴切,因为领导者追随者之间是一种平等的关系。这不符合大部分人对于"领导者-追随者"的通常意义的理解。说句实话,个人认为半同步/半异步模型叫做"领导者-追随者'更加适合,不相信可以看看例子:

  1. 话说一个地方有一群有组织无纪律的人从事山贼这个很有前途的职业。
  2. 他们有一个山贼头头,他专门负责望风,其他的喽罗待命。
  3. 假如发现有落单的过往客商,山贼头头会到路口拦路,让客商双手抱头蹲在地上,然后让一个小喽罗为这个倒霉鬼"服务"。
  4. 假如客商很多,山贼头头会让客商在地上蹲成一排(严肃点,排队啦,打劫啦)。 一群小喽罗挨个为大家"服务"。
  5. 头头的工作很重要,对于每个客商他都不会花费太多时间,拦路以后,他会让客商排队等待打劫。
  6. 过往客商太多而山贼数量不够,客商的排队可能需要等待较长的时间。

这个就是半同步/半异步模型的比喻,可以参考一下 http://www.javaeye.com/article/60414

大家可以看到这两个模式之间的区别,最显著的,就是半同步/半异步模型拥有一个显式的待处理事件队列,而领导者-追随者模型没有一个显式的队列(很多IO机制操作系统一般会有一个隐式的队列)。因为这个事件队列,半同步/半异步模型可以获得处理上的灵活性,但是因为上下文的切换,效率上却比领导者-追随者模型稍有不及。

BTW,昨晚试验live writer,结果这个软件自动post了一篇blog,而我一时半会没有发现,望大家海涵阿。说句实话,觉得这个软件虽然不错,但是不是太适应中国国情阿,国内大部分blog都没法支持,csdn的支持也不是很好,居然上传不了图片,本来想以后可以写文章同时发到多个blog,看来是不现实了。

原文地址: http://blog.joycode.com/peon/archive/2007/05/13/102457.aspx

posted @ | Feedback (12) | Filed Under [ 多维技术 ]

2007年4月15日

发现文章发在首页会干扰博客堂显示,所以只发了摘要,有兴趣的同学点文章标题看全文吧。
所谓原子操作,就是"不可中断的一个或一系列操作" , 在确认一个操作是原子的情况下,多线程环境里面,我们可以避免仅仅为保护这个操作在外围加上性能昂贵的锁,甚至借助于原子操作,我们可以实现互斥锁。

很多操作系统都为int类型提供了+-赋值的原子操作版本,比如 NT 提供了 InterlockedExchange 等API, Linux/UNIX也提供了atomic_set 等函数。

前两天有同学问我:在x86上,g_count++ (int类型) 是否是一个原子操作? 我的回答是"不是的, 多个CPU的机器(SMP)上面这就不是原子操作"。
今天想起,在单CPU上这个是否是原子操作呢,但是这个和编译器有关,编译器可能有两种编译方式:
A. 多条指令版本 , 这就不是原子的
MOV 寄存器 , g_count
ADD 寄存器, 1
MOV g_count , 寄存器...

posted @ | Feedback (15) | Filed Under [ C/C++ ]

2007年3月20日

windows media 有 simulator 进行压力测试, 现在介绍如何使用helixapp real simulator 测试helix server的并发能力

posted @ | Feedback (15) | Filed Under [ MediaTechnology ]

2007年3月15日

(本文带有一定行业性,为流媒体CDN内容分发网络的范畴)

http://blog.lmtw.com/b/peon/archives/2006/39714.html

虽然张翠山已死,但是江湖上的传言愈来愈烈,都说武当派已经得到了屠龙宝刀,而且已经参透了刀中的秘密。张三丰宋远桥对这种谣言自然是嗤之以鼻。来滋事的自然不少,以武当六侠之能全部轻松打发,可是慕名而来的求教者越来越多,有一些资质品德都算不错的人,六侠将之收为门徒,眼下门徒数量越来越多,别说授徒,六侠和宋青书的日常事务也日益繁忙了。

     张三丰为此专门开了个会,决定今后六侠处理日常事务和进行研发新的武艺,名义上,张三丰统一教授所有第三代和第4代弟子的武艺 ,弟子有什么问题,可以写纸条给张三丰,或者向www.张三丰.com查询,实际上的武艺教授由宋青书和一些第三代弟子中的佼佼者负责,成立一个武馆,专门负责解答武学问题。     每天,宋青书和其他教师处理给张三丰的纸条和WEB请求,每天都有大量的纸条和WEB查询,宋青书一个人是绝对处理不来的,宋青书检测所有教师的状态,把请求给懂得该问题(拥有请求内容)并且负载最轻的教师处理。     这里,宋青书和其他教师形成了一个本地负载均衡的集群。负载均衡(Load Balance)将大量的并发访问或数据流量分担到多台节点设备上分别处理,减少用户等待响应的时间提高处理能力,负载均衡建立在现有网络结构之上,它提供了一种廉价、有效、透明的方法,来扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。本地负载均衡是指对本地的服务器群做负载均衡,能有效地解决数据流量过大、网络负荷过重的问题,并且不需花费昂贵开支购置性能卓越的服务器,可充分利用现有设备,避免服务器单点故障造成数据流量的损失。    开始的时候,武当派的光大武学的事业进行的很好,随着武当山的弟子继续增加,武当山在山下开了一些别院,求教的弟子络绎不绝。现在不是宋青书他们的武馆忙不过来,反正学生多了多弄老师就行,而是山路变得拥挤起来,而且大多数学生抱怨来回就要一天,严重影响学武效率。

这里,通过的集群服务器(宋青书和许多老师)做本地负载平衡,很好的解决了大量请求的负载问题,但是出现了以下的问题:消耗大量的骨干带宽(山路拥挤不堪),用户请求网络距离太远,反应缓慢(请教个问题来回要一天)。        武当诸侠也意识到了这个问题,于是就在山下的别院成立了分馆,由别院的一些优秀弟子充当其他弟子的教师。这些别院的信息分中心直接就挂张三丰的名号,www.张三丰.com的牌子也是挂得相当响亮,相应的路标也指好了。山北的弟子顺着路标找张三丰,就自然跑到山北的武馆,山南的弟子则会找到山南的武馆。每个武馆都有门房,根据请教的内容,告知学武者应该找的老师的房间号。学武者自己去找该老师解答问题。

   这里为了武当派为了解决响应速度和骨干带宽的问题,引入了全局负载均衡(Global Server Load Balance 有时称为地域负载均衡),把各地的用户对于资源的访问,根据内容有无,服务器负载,网络带宽和速度,将请求导向到不同的服务器集群进行服务。    这里武当派采取的全局负载均衡策略相当于Internet的智能DNS+内容重定向的方法。    智能DNS: 对于资源访问,采用统一的域名,但是智能DNS根据地域,分别指向边缘服务器进行服务(山北的的路标指到山北武馆,山南的指到山南)。但是智能DNS有粒度太粗的问题,智能DNS服务器无法判断边缘服务器是否拥有该内容,边缘服务器是否健康是否有足够的能力服务。所以常常需要和其他方式配合,比如4/7层交换和内容重定向。
<    内容重定向(可以参考"武当学艺之反向代理"一文):对于访问请求,有一个内容路由服务器(相当于武馆的门房)信息通过一定的内容导向策略(一般是就近和负载最轻原则),将其分配给合适的缓存服务器进行服务。重定向需要应用层协议的支持,而且往往有一定的限制,但是可以做的非常的灵活,达到最好的效果。

(后面的文章应该到了介绍PUSH/PULL的分发技术了)

posted @ | Feedback (11) | Filed Under [ MediaTechnology ]

2007年2月21日

2月9日,Google中国正式推出Google文件( Google Docs & Spreadsheets )中文版。可以用来多人在线协作编辑文档和电子表格。
 
网址在 http://docs.google.com/   需要gmail帐户登陆


 
通过简单几次click, 我创建了一个在线文档 http://docs.google.com/View?docid=dcgxxtrr_0f3kvv5 

虽然有丰富的编辑和导入导出和历史版本功能, 但论文字功能,google文档当然比不上office,  但是他可以多人在网上在线编辑和浏览文档,相比大家书写word文档然后用邮件传来传去,这的确算是一个很"颠覆"的东西,个人觉得这是一些团队创建维护和共享文档的非常好的办法。从协作方面而言,它类似于wiki ,但是又有些和wiki不一样的特点 :

  • wiki 主要用于建立一个大而全或者针对某个领域的一些列文档, google 文件一般只针对某个主题。
  • wiki 一般互联网上每个人都可以修改和浏览,但是google文件可以进行相关的权限控制。
  • 为了防止恶意或者错误的修改。wiki一般都提供版本历史功能,可以自由回退到某个版本。google文件也有这个功能, 但是这个功能对于google文件虽然很重要,但是没有wiki那么严重.
  • wiki 需要大量用户才能不断丰富和发展,但是google文件哪怕是个人使用,也可以享受到随时随地修改的便利,更别说便于团队维护修改了。
  • google文件的相关搜索功能很强。

感觉互联网在不断地改变我们的生活,星期六和朋友聊天,感慨互联网从来没有像今天这么紧密过:每天,我们都要用它和人联络,用google查资料,在网上看小说看视频听音乐买东西。相信这种文档协作的方式,今后也会深深影响我们工作的习惯。

附:Google(谷歌)文件中文版上线 — 在线同步规划您的生活、工作

posted @ | Feedback (9) | Filed Under [ 信手涂鸦 ]

2007年2月10日

PetShop可以使用SQLServer或者Oracle作为数据库,基于SQLServer和Oracle的差异,PetShop作了一个中间层DAL(Data Access Layer),来实现这个功能。

DAL层供BLL(Business Logic Layer)层调用,封装和数据库有关的实现细节。

在DAL命名空间中有如下的interface:

IAccount  / IInventory / IItem  / IOrder / IProduct / IProfile

然后分别使用SQLServer和Oracle实现了以上的接口,以 IAccount  接口为例子:

IAccount  接口实现用户帐户的登录和注销等功能,以下是其接口方法:

  AccountInfo SignIn(string userId, string password);

  AddressInfo GetAddress(string userId);

  void Insert(AccountInfo account);

  void Update(AccountInfo Account);

以上的方法,大家都可以看出它们相应的功能。

PetShop.SQLServerDAL 命名空间中是这样实现

public class Account : IAccount{

...

public AccountInfo SignIn(string userId, string password) {

    SqlParameter[] signOnParms = GetSignOnParameters();

    signOnParms[0].Value = userId;
    signOnParms[1].Value = password;

    using (SqlDataReader rdr = SQLHelper.ExecuteReader(SQLHelper.CONN_STRING_NON_DTC, CommandType.Text, SQL_SELECT_ACCOUNT, signOnParms)) {
    if (rdr.Read()) {
    AddressInfo myAddress = new AddressInfo(rdr.GetString(1), rdr.GetString(2), rdr.GetString(3), rdr.GetString(4), rdr.GetString(5), rdr.GetString( 6 ), rdr.GetString(7), rdr.GetString( 8 ), rdr.GetString(9));
     return new AccountInfo(userId, password, rdr.GetString(0), myAddress, rdr.GetString(10), rdr.GetString(11), Convert.ToBoolean(rdr.GetInt32(12)), Convert.ToBoolean(rdr.GetInt32(13)));
    }
    return null;
   }
}

在这里面使用SqlDataReader ,SQLHelper(PetShop对SQLServer调用的封装)实现了用户登录功能。

在PetShop.OracleDAL命名空间中的IAccount实现和其类似,除了部分代码细节,主要是OracleDataReader/OraHelper的变化。

在BLL中,只要一个接口的引用,然后使用Factory根据配置文件创建出SQL或者Oracle的相应的DAL对象,就可以对上层屏蔽具体的数据库实现。

让我们看看namespace PetShop.BLL 中Account(这个不是实现IAccount接口的)的SignIn方法:

  public AccountInfo SignIn(string userId, string password) {

   // 对用户和密码的基本的非空验证
   if ((userId.Trim() == string.Empty) || (password.Trim() == string.Empty))
    return null;

   // 使用DALFactory根据配置创建IAccount 的 SQL 或者 Oracle 实例
   IAccount dal = PetShop.DALFactory.Account.Create();

   // 调用接口方法
   AccountInfo account = dal.SignIn(userId, password);

   // Return the account
   return account;
  }
待续...

其他推荐资料:

.Net PetShop 4.0的层次结构 (ZT)

对比.NET PetShop和Duwamish来探讨Ado.NET的数据库编程模式

浅析Microsoft .net PetShop程序中的购物车和订单处理模块(Profile技术,异步MSMQ消息)

《解剖PetShop》系列

posted @ | Feedback (25) | Filed Under [ DotNet ]

2007年2月9日

http://blog.joycode.com/sam1111/archive/2004/11/04/38040.aspx

提到google的高级搜索的site关键字只能把搜索范围限制在blog.joycode.com这个域名内,不能搜索

http://blog.joycode.com/sam1111这样的范围,得确是一个不方便的地方。

其实有一个替代的方法:使用 InUrl 关键字,表示搜索的页面URL必须包含某个关键词

比如 搜索 site:blog.joycode.com inurl:joy 兼职 就可以把开心发布的兼职信息都找出来:)

假如你想进一步缩小范围,只在随笔而不是文章中找,可以搜索 site:blog.joycode.com inurl:(joy archive) 兼职

可惜,这个关键字常常不能如你所愿,好像可以搜索到的东西不是很完全,可能一般的站点google作inurl索引不完全。就好像以前google搜索反向链接(使用link: ... )只能给出rank>=4的,现在能都给出来了,google也在不断的完善中 :-D

但是对于像 www.microsoft.com 的站点,inurl是非常有效的,特别是你已经知晓一个站点URL的一些规律的情况下。比如从经验我们知道微软howto文章的url里面总是包含howto的单词,我们可以这样使用google搜索关于安全的howto: site:www.microsoft.com inurl:howto security

另外,开心大哥提到我们写生活随想之类Post的问题,要求我们不要选中显示在首页的复选,但是发现仍然会显示在 http://blog.joycode.com 的首页,只是不显示在自己blog的首页罢了,合适的选项应该是Include in Aggregated Site

相关文章:

Google搜索技巧-入门篇

十大高明的Google搜索技巧

Google黑客搜索技术<

Google搜索技巧2005

Google桌面搜索使用心得

Google talk技巧十则

十四个方法提高博客的页面访问量

关于一个google搜索技巧-InUrl

百度搜索技巧

广告: HelixApp流媒体防盗链(WMS IIS HELIX)

HelixApp流媒体防盗链"

posted @ | Feedback (9) | Filed Under [ 多维技术 ]

    商业公司的流氓软件越来越像黑客程序;黑客攻击明码标价越来越像商业公司。
 
    看了文苗关于黑客的文章,不管黑客是否经历了古典现代后现代的变化,但是有些感觉不吐不快,前些年提到黑客,大家对他们还带着几分崇敬,但是现在则是无尽的苦恼。若干年前媒体上出现的黑客,大部分是发现了什么漏洞,窃取了政府的一些信息,最近媒体的报道则大多是QQ盗窃,网银盗窃和网络钓鱼之类的(姑且不论这些人是否是真正的黑客)。
 
    有个朋友msn的签名愁云惨淡,说他的网站被攻击了,前些日子有人还告诉我他被攻击以后还被勒索,说不汇钱就将一直攻击下去。我当时出于好奇到一个小有名气的黑客站点上看了一下,发现攻击工具都是明码标价,而且还有售后服务。假如用户自己不会使用,还可以收费代为攻击。
 
    感觉现在互联网上一些阴暗的东西已经成为一条产业链:无论是前些日子沸沸扬扬的流氓软件,还是垃圾邮件和BLOG上另开心拍案而起的无穷无尽的SPAM,还是这些不知是否能算作真正黑客的人有意无意的攻击。这些大家都讨厌至极的东西虽然人人喊打,但是实际上却日益繁荣,因为他们已经和太多的经济利益挂在了一起。一些网站流氓营销的最终成功,也给很多公司和个人打上了兴奋剂。黑了可以洗白,甚至还可以掉头打黑,社会只会以成败论英雄。
 
      反流氓软件厂商奔着巨大的经济利益,借着大众的舆论,这段时间流氓软件终于没有那么嚣张了。不知道什么时候,我们在上网的时候打开一个链接,不用再提心吊胆。

posted @ | Feedback (5) | Filed Under [ 信手涂鸦 ]

2006年8月19日

http://blog.lmtw.com/b/peon/archives/2006/39703.html

对于CDN的内容管理,有一个基本定律,就是大家常说对于内容的访问遵循80/20原则,也就是20%的内容,会占有80%的访问量。

这是一个定性的原则,定量来说,内容访问近似符合Zipf定律(Zipf's law), 这个定律是美国语言学家Zipf发现的,他在1932年研究英文单词的出现频率时,发现如果把单词频率从高到低的次序排列,每个单词出现频率和它的符号访问排名存在简单反比关系:

这里 r 表示一个单词的出现频率的排名,P(r)表示排名为r的单词的出现频率.

(单词频率分布中 C约等于0.1, a约等于1)

后人将这个分布称为齐夫分布,这个分布是一个统计型的经验规律,描述了这样一个定理:只有少数英文单词经常被使用,大部分的单词很少被使用。这个定理也在很多分布里面得到了验证,比如人们的收入,互联网的网站数量和访问比例,互联网内容和访问比例(其他分布两个常数有所不同,a越大,分布越密集,对于VOD来说某些时候符合双zipf分布)。

下面是某个系统VOD内容的访问分布,第一幅图是访问频率曲线,Y轴是内容的访问次数,X轴是内容根据访问次数的排名, 我们可以看到,多数访问集中于少量内容上:

第二幅图是对数轴的访问频率曲线,源数据和上图一致,可以看到近似为一条直线:

从曲线的斜率可以计算出,这里的内容访问频率分布,a约等于0.6(不同种类的内容a的大小也不一样)。

posted @ | Feedback (3) | Filed Under [ MediaTechnology ]

2006年6月4日

写了多年程序,转眼就28了,发现身边的大龄程序员也多了起来,就要到了盛传的"程序员30"的这道坎,想想这些年的酸甜苦辣,重新翻出mvm的这篇文章,看着那么同业的回复,不仅感触良多:

http://blog.joycode.com/mvm/archive/2004/03/23/16987.aspx

mvm针对大龄程序员一定要转市场,转管理的观点,提出程序员还可以做某方面精通(当然也可以是多方面精通)的特种兵。反对的意见也很多,我挑几个典型的:

# 回复: 做军官还是做特种兵?2004-3-23 20:00 | blackwhites
其实我也很困惑,其实30多岁不想干程序员的原因是有很多
1很多人怕吃苦,因为有了老婆和孩子要照顾家,所以都想换方向
2老板喜欢一些年轻点的开发人员,价格便宜,30多岁老程序员面邻压力
3。技术更新太快,而自己的学习速度比较迟钝,全靠吃老本
其实国外那些有作为的程序员恰恰都是30岁以上居多的,而在中国给人的感觉如果30岁还在写代码的话..

# 回复: 做军官还是做特种兵?2004-3-26 14:59 | king
在怎么特种兵,他也是去前线最危险的地方玩命去
军官可以没有那么辛苦,并且薪水也远多于那些在外面玩命的吧
老了,就玩不起了,但成熟了,心眼多了,可以考虑作管理。

# re: 做军官还是做特种兵?2005-9-1 12:08 | neoragex2002
国内软件行业对程序员的普遍认识不足的问题。行业本身的创新性和价值观念、人才评价就都有问题。试想在一个崇尚赚快钱、不注重人才培养的行业氛围中程序员的地位几何?能够得到几许尊重?中国公司并不是处处都是微软。中国的软件行业现状也不能与北美、欧洲相比。审时度势,因地制宜,比较的基础、环境都不一样,因此楼主的这种反思是没有太多意义的。


感触:
的确,我现在也没有以前那么愿意加班了,我得考虑身体,女友;我的薪水涨到了这个程度,经济压力那么重,估计再找工作,很多老板看到简历的薪水要求就会马上把我否决;技术更新那么快,我没有时间去一一追逐;同学都是什么经理,什么长了,我还是一个developer(我以developer为荣), 也许顶个leader的小头衔,老爸老妈是否还能像以前那样夸儿子有出息,女朋友会怎么想......我年纪继续大下去,还玩得起么?

但是我还是决定继续作程序员,因为我觉得还可以做下去. 程序世界不是只有VC/DELPHIC这些程序语言,也不是只有MFC/GLIBC这些库,NT/Linux这些操作系统, 也许有人觉得做特种兵就是要精通各种语言, 熟悉各种LIB和SDK, 才能玩得转, 这些都是耗费巨大体力和精力的事情, 老了就完完了. 人家一个密码学的专家可以干上一辈子, 为什么程序员甚至程序特种兵30就玩不动了? 假如一个程序员把自己的价值就定位到VC JAVA DotNet上, 的确就会疲于奔命, 渐渐就玩不动了.

你看招聘, 很多公司上来就要求各种语言,各种数据库, 甚至还要你精通PS,还能算帐顶个会计, 要求多得不得了. 你看了以后要么自卑,要么鄙视: 他们自己都不知道该做什么东西, 该要什么人.  那些招聘网站的首页,那些有名的外企,绝对不会show这种招聘广告: 丢人哪. 他们只会在基本的程序和平台外语技能后面加上: 精通xxx尤佳,最好该领域有xx年经验. 这里xxx可能是内核定制,可能是语音视频技术,可能是OA系统开发, 或者...... 这些才是他们想要的关键技能, 才是你的价值所在.

( BTW,我无意挑起语言之争,语言是很基础很重要的技能,它们就像厨师的菜刀和锅铲,它们是一门艺术,但是一个厨师不应该仅仅了解这些。)

常常看到有人问: 我精通这个语言,那个语言,大家看看我到底值多少钱哪? 假如你干了两年还这么问,你就麻烦了, 老板看的不是你单纯的程序语言技能, 他要的是你解决问题的能力, 这些更多的和你做过什么项目, 从事哪个领域或者行业的开发有关. 举个例子: 你给一家外包公司的简历, 说你精通VC或者其他什么的那行(不是不要你说), 远远比不上你说有在外包企业从事3年工作的那行文字重要.

很多人说外企好,别人老板尊重程序员,他们的程序员干到50还乐呵呵. 我觉得文化的原因当然有, 但是更加主要的原因, 是因为资本家觉得他们还有价值, 而且是比那些刚入行的青年有大得多的价值. 那么多的内部技术文献, 那么多年通过开发和维护系统换来的经验, 该领域里面那么多的细节, 系统该这么作,不该那么作, 只有这些老家伙了解, 而且几乎是直觉上的了解, 我不仅不能开掉他们, 还要让他们HAPPY, 不能让他们被别人挖走了.

不可能每个人都能转成市场和管理,每个人都能做首席架构师, 但是这不妨碍我们继续在一个自己精通的领域内作我们的特种兵: 我们比客户还清楚他们要什么; 我们不仅知道系统怎么做的, 而且知道为什么必须这样做. 有些地方, 架构师设计的时候, 必须通过我们的REVIEW评审他才放心;  而且有一点很重要: 这些知识比那些易变的语言甚至稳固的硬件更难过时.

(下面是看了评论写的,谢谢大家):

后记1:

我觉得不管大公司小公司,对于一些不管什么项目都做的公司,只想安心做事情的程序员是永远没有前途的,对于那些没有根,没有技术积淀,没有行业背景的公司,程序员假如不能转型成市场或者管理者,他不仅低人一头,而且对于公司永远只是一个可以随时炒掉的螺丝钉。

成为为资深技术人员是出路之一,但是需要合适的土壤,欧美这方面的确好很多,但是在中国找到类似的土壤并非不可能。另外做技术比不上管理风光是必然的,外国中国都一样,但是是否一直做技术,要看个人兴趣和能力。不要迷信外企的"技术管理双阶晋升",那是糊弄人的,但是他们会给技术人员一个起码的自尊,你起码可以说:在公司我比经理级别高(虽然你没有那么大的办公室)。

后记2:

我觉得这是一个一直做技术是否现实的问题,一个如何实现更高的自我价值的问题:有人说年纪大了学习能力并没有下降,但即使保持了学习能力,给你开那么多的薪水,你比刚入行的小伙子的优势在哪里?我可以说,对于新的语言的掌握甚至实战能力,我看到很多学校出来的新人非常不错甚至可以说精通,你难道和一批批的新人反反复复的拼这些?就算你样样比他们强,性价比呢?

posted @ | Feedback (27) | Filed Under [ 信手涂鸦 ]

2005年11月10日

大家都知道Linux是Linus Torvalds不满Minix不够实用,没办法,Andy Taonenbaum 写这个就是为了教学和研究,就写了Linux.

那么大家可以看看 http://www.minix3.org ,这个项目除了保持Minix的特点,比如微内核(内核4000行都不到,驱动都是用户态的),Minix3更加朝着实用性的方向发展,期望在一些嵌入式设备里面能有一席之地。

运行:大家在http://www.minix3.org/download/ 下载光盘映像,刻盘或者直接放在VMWARE里面开虚拟机运行就可以从光盘启动。

或者也可以从Bochs启动: http://www.osnews.com/story.php?news_id=7303

 

posted @ | Feedback (6) | Filed Under [ 多维技术 ]

2005年1月23日

2004年12月16日

在写一个新项目,做了几天,因为还没有成型,也因为懒,就没有把它丢到ClearCase里面 结果单元测试通过以后,忽然发现:最主要的一个文件是空白!!不知道为什么被清除了,顿时冷汗直下 想起google的desktop引擎,赶紧search一下,发现了一个几分钟前的版本, 看了一下,赶紧恢复了文件,不过里面的中文注释都变成了?号,总比全丢了强!

posted @ | Feedback (11) | Filed Under [ 信手涂鸦 ]

2004年12月9日

CDN是内容分发网络的缩写,主要解决网络骨干带宽耗用和用户访问Qos问题,大家使用Google搜索一把就可以找到大量的资料。

文章贴在我另外一个BLOG:

http://blog.liumeiti.org/peon/archive/2004/12/07/320.aspx

文章的几个人物都是相熟的朋友,调侃他们一把:D   今天晚上看ACE又泡汤了

另外,开心的CLR is everywhere 说明微软在数据库和企业开发方面的成功,另外windows2003也在服务器领域攻城略地,不仅联想到微软在IPTV领域近期发生的一些动向:

SBC同微软签订4亿美元 IPTV产品合同

微软与汤姆逊推出IPTV机顶盒在瑞士试播

微软在做了大量投入以后,开始大举进入这个领域,虽然有人说“跟着微软走没有错”,而且微软投入的力量(号称历时10年,100亿美元)和技术实力也非常惊人,不过这个领域毕竟不同于通用软件和开发者社区,结局如何,我们拭目以待;另外,不知道微软何时大举进军通信领域:D

posted @ | Feedback (2) | Filed Under [ MediaTechnology ]

2004年10月16日

http://webmessenger.msn.com/

很多人应该已经知道这个,意外发现这个webmessenger的浏览器兼容性也是不错的:


而且不错的地方是:它可以同时打开多个帐号 :D

posted @ | Feedback (7) | Filed Under [ 信手涂鸦 ]