从需求角度看NOSQL发展

 

早先当640kb就足够使用的观点流行的时候,数据处理规模很小,需求也不多,于是简单的文件存储即可满足需求,发展一段时间之后ISAM之类的简单存储就可满足需求,再之后sql流行,当sql为了适应各种需求变得越来越庞大的时候,效率也止步不前,在将缓存和多线程性能榨取完了之后,sql各项性能还只停留在满足常规应用的地步,难于处理1秒万次以上的读写操作,也难于解决万个以上的并发连接,一般的企业不可能动不动就上硬件,所以nosql发展是时代的需要是需求的推动。当然一般sql对传统企业还是足够满足的,所以我们在nosql的发展上没看到传统企业的身影,只看到当前发展最快的SNS公司积极推动nosql不断发展,著名的如:Facebook 推动Cassandra发展,Linkedin推动Voldemort发展,这都是最大的一类sns网站,这些网站都有几千万以上的用户,巨量数据读写,所以这些数据库都是极其强调分布式应用的,并不单纯的强调每个点的读写性能。再看小一点的mixi推动的Tokyo CabinetTokoy Tyrantgreen.jp推动Flare的发展,这些数据库都满足于几千万条数据的高速访问,也没看到特别的强调并发性,只强调他们的速度,当然几千万条数据还是有可能全放在内存里面的,就算放不下全部数据也至少可完全放下全部索引,这样读写当然快了,据说tt到亿条之后写性能急剧下降,大概就是这个原因吧。纯内存式数据库也必须要提一下,典型的如LiveJournal开发的memcached以及另一个新秀Redis等,前面提到的tt也支持memcached的协议,虽然这类数据库有很多局限,但在某些场合的确又很适合,memcahced其实连持续存储都不支持,为了解决持续存储问题,又有人发展了一些,如tt其实就是一个支持存储的memcached,国内新浪团队也给memcached加上berkeleydb支持持续存储。

上面说了这么多,无非想说一句话,需求推动技术进步,每一个技术进步其实都是为了满足某种需求的结果,就如google的三大基石bigtablegfsmap/reduce都是为了解决它的巨量数据而折腾出来的东西,google也正是靠这几个核心技术把持了互联网近十年的风光。同理我们可以想见,虽然百度没有大力的宣传他们的底层技术,但我们很容易想到,他们一定也是需要这些技术的,而且他们内部就算没有这些技术,但一定有类似的接替代产品,否则支撑不了他们那么巨量的数据,虽然替代产品未必有google的产品那么好,但大概是略差一点或相当的水平吧。国内互联网巨头腾讯支持了国内最大的im应用 10亿级,最大的棋牌游戏近亿在线,加上他们布局网络门户,布局qzone等,都是巨量用户,可以想见他们一定有类似的方案,早先听说他们棋牌游戏是通过很多mysql + proxy来完成的,虽然这个方式现在看起来也不是很完美,但至少是一个可行的解决方案,臆测下可以这样使用,proxy有个巨大的hash表,每个qqid计算一下就知道在哪个区段,重定向到哪个区段读写数据即可,说起来容易做起来难啊,就算我玩种菜都不知道遇到多少次他们数据出故障了,说明他们的系统面对巨大数据压力的时候还是碰到了很多问题。国内还有个公司不得不提,阿里巴巴淘宝,马云团队发迹很快,淘宝每年不知道要成交多少笔,但他们的数据也是一个天量,看了下他们dba团队的主页,牛一点的dba都笼络了不少,就是自己开发能力稍弱了一点,纵观国内对巨量数据需求最迫切的也就这几家公司了,虽然之后的51、开心网、盛大等也有类似需求,但数据量总归还是没有超过前面几家公司。

在需求的推动下,国内的nosqlkey-value应用也慢慢发展了一些,如张宴在新浪搞的memcachedb,到金山之后搞的dbcached,豆瓣开发的beansdb等,还有一些没开源没介绍不大为外界知道的应该也有一些,但总的来看水平还是比较低,有点不成气候的样子,靠的大多是1-2个牛人支撑,离开了这么几个人就不行了,东西也没人维护,的确,离开了巨量数据的需求一般的企业用sql就能满足也不会去研究这些东西,少数小一点的互联网企业有这个需求又没有相应的人才有能力去研究,年轻一点的开发人员都在玩概念想做也做不出这些东西,毕竟做这些东西没有很深厚的数据结构知识,没有3-5年的深入编程磨练是不可能真正做好一个像样东西的,矛盾啊。

最后说下我最近在做的一个东西,分布式md5计算,这个东西网上随便查一下就知道做的人不少,提供网站服务的都不少,但搜了几篇文章,看了几个网站www.cmd5.com www.md5.com.cn就知道,水平之低下超出了我的想象,基本上还是停留在用sql数据库的层次上,根据这些网站写的时间节点感觉他们大多数时间就是在制造数据,速度大概是几个月制造几十亿条数据,都号称有几万亿条数据,但事实上提供公开查询的数据只有区区几亿条,其他都要收费才能查询,天知道到底有没有那几万亿条记录,看上很吸引人,其实用处不算很大,用我最近整的md5数据制造方法1秒制造100w条数据,1亿条数据也就在2分钟内搞定,几亿条数据也不过10分钟左右就生成好了,1亿条记录耗费空间1.5G左右,不过10G左右空间即可,技术含量可见并不是很高。 其实我做这个项目并不是想做个类似的网站,主要是觉得这个东西玩技术很有意思,可大可小,一台机器也可玩,1T硬盘放600亿数据没问题,1万台机器也不多,全字母遍历到10位就算是上1w台机器也不够用,分布式存储分布式计算典型云计算概念,clientp2p可不p2p,很多技术元素都可参与其中,很有玩性的一个程序,所以就较上劲了,也好,正好练练技术,玩玩nosql的概念。

各种新兴技术出来都看到国内有深入分析,就说nosql系列的吧,深入分析memcached,深入分析tt,深入分析Cassandra的文章不计其数,到底也没看到有几个国人能写类似的东西,分析得头头是道,做的时候白痴一样,就算是使用都难用好,更别说自己动手做个这方面的好产品了,国情如此,略感欣慰的是国内现在也有一些公司和一些高水平的人真正参与其中,未来还是有可能有所突破的,正入本文所说,需求会推动技术发展,但短期肯定还是国外为主,国内的产品最多是一丝点缀。

posted @ 2010-10-03 14:18 袁斌 阅读(406) | 评论 (0)编辑 收藏

Jsonajax领域很流行,记得当时看过它的介绍后很兴奋,网上找了一些解析jsonc

 

c++代码,不过没有找到特别好的,有的写得不错不过要依赖于boost,有的用c写的不大好用,好在json语法简单,参考了一些c/c++json解析代码做了一个json类,最近又把去年写的json类修改为unicode下使用,增了一些功能,现在CJsonw可以解析const char *型输入,也可解析const wchar_t *型输入,可解析ansi编码、unicode编码、utf8编码的json文件。看看我的CJsonw定义:

 

        JsonwType type;                     //Json类型, true false null直接由类型表示了

        DWORD tag;                            //tag,用户自用值,默认为0

        union

        {

                struct{

                        wchar_t *cstring;//字符串型值

                        int clen;            //cstring按照字符计算的分配个数,包括可能的0

                };

                double dvalue;          //double

                struct

                {

                        int ivalue;         //int

                        DWORD dwhigh;       //高部

                };

                __int64 i64value;        //int64

                OBJS *objs;                     //对象型值

                ARRAYS *arrays;             //数组型值

        };

Sizeof(CJsonw) == 16

OBJS类型描述无序的key-value型数据,ARRAYS描述 array型有序数据,定义如下:

        typedef CHashiW<CJsonw *, CJsonw *> OBJS;

        typedef std::vector<CJsonw *> ARRAYS;

支持以下构造函数:

        CJsonw(JsonwType t=json_null);

        CJsonw(int value);

        CJsonw(__int64 value);

        CJsonw(float value);

        CJsonw(double value);

        CJsonw(const wchar_t *value);

        //数组型构造函数

        CJsonw(int *numbers, int count);

        CJsonw(__int64 *numbers, int count);

        CJsonw(float *numbers, int count);

        CJsonw(double *numbers, int count);

        CJsonw(const wchar_t **strings, int count);

支持以下赋值函数:

        bool setnull();

        bool set(bool value);

        bool set(int value);

        bool set(__int64 value);

        bool set(float value);

        bool set(double value);

        bool set(const wchar_t *value);

        bool set(int *numbers, int count);

        bool set(__int64 *numbers, int count);

        bool set(float *numbers, int count);

        bool set(double *numbers, int count);

        bool set(const wchar_t **strings, int count);

       

        //修改常规值,就是true, false, int, real, string

        bool setbystring(const wchar_t *value);

 

支持以下输入:

        bool parse(const char *string);

        bool parse(const wchar_t *string);

        bool parsefile(FILE *fp);

        bool parsefile(LPCTSTR filename);

文件可以ansi编码、unicode编码、或者utf8编码

 

支持以下wchar_t型输出:

        //dump,默认为unicode编码

        //uunicode表示字符串中的unicode字符是否按照\uxxxx格式输出

        //bDisptrue按照友好格式显示,会输出\t换行等方便阅读

        //bDispfalse按照紧凑模式显示,没有多余字符,方便网络传输等场合

        bool dump(CBlockBuffer *pbk, bool bNameQuotes=true, bool uunicode=false, bool bDisp=true);

        //跟上面的dump一样,但如果成功会在buf里面插入一个'\0'字符

        bool dump0(CBlockBuffer *pbk, bool bNameQuotes=true, bool uunicode=false, bool bDisp=true);

        bool save(LPCTSTR filename);

        bool save(FILE *fp);

保存到文件默认为UNICODE格式

 

支持以下ANSI型输出:

        //a系列dumpansi编码输出

        //bDisptrue按照友好格式显示,会输出\t换行等方便阅读

        //bDispfalse按照紧凑模式显示,没有多余字符,方便网络传输等场合

        bool dumpa(CBlockBuffer *pbk, bool bNameQuotes=true, bool uunicode=true, bool bDisp=true);

        //跟上面的dump一样,但如果成功会在buf里面插入一个'\0'字符

        bool dumpa0(CBlockBuffer *pbk, bool bNameQuotes=true, bool uunicode=true, bool bDisp=true);

ANSI型输出主要用在节省网络带宽或者和其他系统交换数据的情况下。

 

支持以下类型判别函数:

        //类型判别函数

        bool IsArray() const { return type==json_array; }

        bool IsObject() const { return type==json_object; }

        bool IsInt() const { return type==json_int; }

        bool IsReal() const { return type==json_real; }

        bool IsNumber() const { return IsInt()||IsReal(); }

        bool IsNull() const { return type==json_null; }

        bool IsTrue() const { return type==json_true; }

        bool IsFalse() const { return type==json_false; }

        bool IsString() const { return type==json_string; }

 

支持以下一些直接取值函数:

        const wchar_t *getordef(const wchar_t *strdef)

        int getordef(int idef)

        __int64 getordef(__int64 idef)

        double getordef(double ddef)

        bool getordef(bool bdef)

        //array的元素

        CJsonw *get(int n)

        CJsonw *operator[](int n) const

        //obj的元素

        CJsonw *get(const wchar_t *name)

        CJsonw *operator[](const wchar_t *name) const

 

支持以下取array型子元素数据的函数:

        const wchar_t *getordef(int n, const wchar_t *strdef)

        int getordef(int n, int idef)

        __int64 getordef(int n, __int64 idef)

        double getordef(int n, double ddef)

        bool getordef(int n, bool bdef)

 

支持以下取obj型子元素数据的函数:

        const wchar_t *getordef(const wchar_t *name, const wchar_t *strdef)

        int getordef(const wchar_t *name, int idef)

        __int64 getordef(const wchar_t *name, __int64 idef)

        double getordef(const wchar_t *name, double ddef)

        bool getordef(const wchar_t *name, bool bdef)

 

支持以下obj遍历函数:

        //遍历函数,最常见写法:

        //for(CJsonw::OBJIT *p=xxx.FirstObj(); p; p=p->next())...

        OBJIT *firstobj()

 

        //arrayobject元素个数,string类型返回分配的字符个数(包括可能的),其他类型都返回

        int size() const

 

另外支持一些增加array子元素函数:

        bool add(CJsonw *pnode)

        bool del(int n, bool bfree=true)

        bool addnull(){ return add(new CJsonw(json_null)); }

        bool addtrue(){ return add(new CJsonw(json_true)); }

        bool addfalse(){ return add(new CJsonw(json_false)); }

        bool add(int value){ return add(new CJsonw(value)); }

        bool add(__int64 value){ return add(new CJsonw(value)); }

        bool add(float value){ return add(new CJsonw(value)); }

        bool add(double value){ return add(new CJsonw(value)); }

        bool add(const wchar_t *value){ return add(new CJsonw(value)); }

 

也支持obj增加删除子对象函数:

        bool addobj(const wchar_t *name, CJsonw *pnode)

        bool delobj(const wchar_t *name, bool bfree=true)

        bool addobjnull(const wchar_t *name){ return addobj(name, new CJsonw(json_null)); }

        bool addobjtrue(const wchar_t *name){ return addobj(name, new CJsonw(json_true)); }

        bool addobjfalse(const wchar_t *name){ return addobj(name, new CJsonw(json_false)); }

        bool addobj(const wchar_t *name, int value){ return addobj(name, new CJsonw(value)); }

        bool addobj(const wchar_t *name, __int64 value){ return addobj(name, new CJsonw(value)); }

        bool addobj(const wchar_t *name, float value){ return addobj(name, new CJsonw(value)); }

        bool addobj(const wchar_t *name, double value){ return addobj(name, new CJsonw(value)); }

        bool addobj(const wchar_t *name, const wchar_t *value){ return addobj(name, new CJsonw(value)); }

 

还有一些特殊函数:

        //计算整个树的crc

        DWORD calccrc();

        //计算整个树的md5,要求md5不少于个字节

        byte *calcmd5(byte *md5);

        //计算hex表示的md5,不写尾部,要求md5hex不少于个字符

        char *calcmd5hex(char *md5hex, const byte x='x');

        //计算hex表示的md5,写尾部,要求md5hex不少于个字符

        char *calcmd5hex0(char *md5hex, const byte x='x');

 

 

json类上花了较多时间,主要是觉得这个类很有用,可以描述任意对象,易保存易传输易构造,对速度要求不苛刻的应用程序用json作为基本数据结构很合适,配合json里面的data自定义字段理论上支持描述任意对象,配合使用一些cache可扩大json类的使用范围,用它取代ini xml等配置简直是大材小用,用json类来做stringtable简直就是小菜一碟,而且还是hash级高效率,丝毫不比手工做一个hash_map<,>表差。

在实际应用中,去年有一个网络验证的项目就大量使用了这个类,配置、帮助都是json格式,应用中管理大量对象的内存数据库也是用了json类,编码后直接网上传输,server内部也是一个json对象直接管理了大量用户的信息,非常方便,可以说用json类使得这个程序少写了大量代码,也使得整个项目只用了1个月时间就做出来了,还部署成master – master- slave(N)模式稳定运行了将近1年时间。

在另一些网络应用中,俺将json格式数据作为变长包使用,前面俺写过一篇短文介绍了变长包最常见的几种格式,分别是:

1、 key\0value\0…

2、 json格式

3、 xml格式

使用json格式作为变长包现在很流行,这也是大量ajax所采用所依赖的技术,qq种菜,google搜索预列表都是采用json格式的。

posted @ 2010-10-03 14:17 袁斌 阅读(920) | 评论 (0)编辑 收藏

HashCrack程序规划

 

最近几天在思考如何遍历md5找出原码字符串,但九十多个可见字符,96^6 782757789696,就算只遍历6位组合也是非常惊人的巨量数据,单机根本无法完成,根据常用密码合成规律,我们可以采用分而治之的方法解决,方法大致如下:

1、 使用很多机器,机器越多覆盖的范围越大。

2、 优先覆盖最常用的范围,如全数字组合,小写字母组合,小写数字混合组合,大写字母组合,夹杂一个非字母数字字符的组合,之后覆盖非常见组合,如所有可见字符的N个全排列。

3、 采用c/s结构,中心机管理区段分配策略,优先覆盖常用区段,所有slavemaster一起构成一个覆盖网。

4、 不仅支持md5,也支持sha等其他类似算法。

5、 Masterslave也支持添加字典,为了覆盖尽可能多的组合,可添加任何合适的字典组合。

 

实现难点:

1、 数据组织。

方案一、考虑过磁盘存储方案,研究了sqlite,写效率大概10w/s,加了索引大概5w/ssqlite存储效率也不是很高,比berkeley存储浪费很多空间,加了索引后空间放大了一倍。也尝试过berkeley方案,写效率大概只有4.5w/s,存储相对sqlite紧凑一些。但由于写效率太低,都难于实现我所希望的一个slave一下分配1亿数据量的模式(1亿数据大概要占磁盘5-10G,最经济模式大概也要占2.xG)。由于对btree+不太熟悉,暂时还没有找到高效的存储方案。

 

方案二、其实首先是考虑了内存方案,用内存相比磁盘其实要简单一些,不过受机器可用内存的限制以及客户接受程度的限制,难于让一台slave处理1亿以上的数据量,默认大概只能分配2000w-5000w左右的数据量(大概占用内存400M-1.2G)。

 

以上两种方案其实选择方案一更合适一点,因为一般的机器挤2G左右的磁盘基本都没有问题,甚至20G-200G可能都问题不大,但要持久占用1G内存可能不大好,毕竟大多数用户比容易接受。第一版暂时用了方案二,待高速写存储方案做好后替换一个dll即可(该dll已经留好了接口可直接替换)。

 

2、 区段划分

单纯的划分某一个区段也是比较容易的,如10个数字的8个组合,可用如下方法表示

Digits = “0123456789”

Range(Digits, 8, 0, 20000000) 表示digits 8个字母的排列 [0,20000000]

但将一些常用区段单独出来之后涉及到和更大范围字母全排列的重合情况就不大好扣出来,暂时没有找到较好的表示方法,好在小范围排列被冗余也问题不是很大,此表示方法待继续研究,暂时采用冗余方案。

 

3、 Master 发现机制

其实这是一个经典的问题,可考虑得很复杂,也可考虑得很简单,由于我名下几个域名都被禁止解析了,所以这个问题变得不是那么方便,不过暂时还是用个域名部署一下最为简单,存储空间也是个问题,待我联系几个朋友看看能不能找到一个合适的存储地点,待找到后第一版就这么部署出去吧。

 

 

实现方法:

由于要有一个中心提供调度方案,因此该系统肯定有一个中心(不管是一个点还是N个点),所以考虑采用c/s模式,第一版采用一个masterNslave的方案。

master主要实现以下功能:

1、 覆盖最常用区段排列,籍此提供最基本的查询服务。

2、 支持大量slave接入(暂采用iocp模式接入)。

3、 提供区段划分功能,slave接入断开自动分配区段,优先分配常用区段,其次分配非常用区段。

4、 查询转发功能,接收slave的查询请求,转发给其他slave

 

slave实现以下功能:

1、 master交互,接收分配的区段,处理区段内的数据并提供该区段数据查询服务(此功能由一个接口dll实现,可轻易替换)。

2、 支持查询功能。

3、 在客户端还计划支持apislaveapiproxyapi通过消息向slave发送查询请求,slave通过给master发送查询请求,在整个群落里面提供查询服务,此功能为该体系的一大亮点,暂时没考虑做什么限制,主要为吸引用户提供很主动的编程功能,第一版通过一个cdll提供api调用功能,在此api基础上用户应该可以很容易包装出其他语言的调用接口,如luapython的接口等。

 

 

进度说明:

由于该项目只是一个即兴性项目,没打算耗费很多时间,计划在一周内完成,已经过去了3天,所以在细节上暂时无法抽出很多时间仔细研究,待整体功能成型后看情况再斟酌算法,易改变部分暂时都用接口dll实现,该项目代码部分大概完成了一半左右,未来2-3天左右大概可以做完第一版。

posted @ 2010-10-03 14:17 袁斌 阅读(192) | 评论 (0)编辑 收藏

简单总结下asp.net状态维持方法如下:

client端状态
1、ViewState
2、ControlState
3、Hidden Field (post 模式)
4、Cookie
5、QueryString (get 模式)

server端状态
1、Application (全局)
2、Cache (全局)
3、Session (current session,配合后台驱动可将session持续化到很多地方,如xml,
或者db、或者memcached 等地方)
4、配置文件(asp.net有一个sqlprofileProvider类使您可将配置文件数据保存到sql数据库中)
5、db

对保持连接的c/s程序来说,client端状态我们不是特别关心,除了cookie也有一些参考意义,其他几种方法很难直接在普通cs程序里面对应,对server端程序开发来说,asp.net的server端数据持续方法对我们很有参考价值,对一般的游戏程序来说,db是必然会用的,配置文件型的也可参考使用,session型的很多程序都在使用,application型的缓存估计也有很多程序在用,更有价值的是cache型,我们一般的s程序基本都没有实现时效性cache和文件触发性cache,asp.net的cache对我们很有参考价值。当然我们不必生搬硬套的照搬web上的概念,只要学到精髓就可以了,总之,还是我坚持的那句话,侠义地说是从web架构学习server端程序设计,更广义地说是从web架构学习一般网络应用程序设计。

posted @ 2010-10-03 14:16 袁斌 阅读(171) | 评论 (0)编辑 收藏

写了tmpool之后又发现居然有tcmalloc,而且比牛逼的ptmalloc好很多,真感叹啊,世界上好东西很多,但需要智慧的眼光去发现。在linux下简单比较了一下malloc和tcmalloc,快的真不是一点点啊,看到好多人用tcmalloc去重编译mysql, squid,nginx等。简单看了下tcmalloc的原理,比我写的tmpool要多一些整理的功能,大概tmpool就是tcmalloc在win下的子集,俺的tmpool区区800行代码自然是不能去跟tcmalloc比的,不过对付一般的server端程序还是够用的,经过一段时间的使用效果也还可以,待有时间对比下俺的tmpool和tcmalloc。

posted @ 2010-10-03 14:16 袁斌 阅读(1101) | 评论 (0)编辑 收藏

常见cs程序自定义数据包描述

 

常见cs程序自定义包可分为块型包、非块型包,非块型包如http协议的,用\r\n\r\n结束,我们这里重点讨论块型包,块型包常见头部如下:

Struct PKHEAD

{

        Union

{

                DWORD type;

                Struct

{

        WORD mtype;

        WORD stype;

};

        };

        DWORD len;

        Char buf[0];

};

内容buf部分常见组织方式莫过于定长、变长两种。定长的很简单,如struct item{…};作为内容部分,如果不涉及到高低序问题很好办,涉及到序的时候转换一下。变长部分常见组织方式有这么一些经典方法:

1、 Content1 \0 content2 \0 content3 \0 …

2、 json描述。

3、 xml描述。

方法1是效率很高的,对ansi型字符串或者utf8型字符串都没有问题,对utf16型字符串不行。解析这类变长部分的时候特别要注意包尾部的0,或者在缓冲区外部总保证存在一个0,或者保证包内尾部总存在一个结尾0,总之最后一个string要保证有一个合法的0结束,当然可能涉及到一些数值型到string型的转换可能会导致效率下降,1的缺点在于只有一个层次。如果将内容看作key1\0value1\0key2\0value2\0…可解决无序问题,但终归只有一个层次,不方便表示树形数据。

方法2的效率介于13之间,2由于描述很简洁,解析也是比较快的,比1好在可以解析任意层数据,嵌套N层都无所谓,阅读也很方便,解析的时候如果原始缓冲不释放可让字符串不复制直接记录在原始缓冲中的指针,这样可进一步提高解析效率。QQ种菜的通讯协议就是json格式的,阅读很方便,解析效率也可以,总的效率适中。

方法3的解析效率是最低的,好在xml描述性也比较强,解析代码也很多,跟上层应用交互的时候也比较方便,作为一个支持变长的选择也是可行的,不过如果特别在意效率的情况下避免使用,xml的解析效率比 2低了近一个数量级,比1可能低近2个数量级。

 

当然自定义包还有更多格式选择,标准协议也有很多如im 里面常见的xmppgoogleprotocol buffers等,还有json的一些扩展协议,如bjson等,如果只是处理一般的网游数据可能用不上这么复杂的协议,用上面提到的定长struct+1 or 2 or 3基本可以解决所有协议需求。也见到一些朋友自定义类型信息描述格式,个人觉得或许没有必要,特别是json如此流行的情况下再自己定义似乎有重复发明轮子的嫌疑,当然具体情况具体对待,一切以满足需求为目标。

posted @ 2010-10-03 14:15 袁斌 阅读(325) | 评论 (0)编辑 收藏

我一直坚持一个观点,从web体系学习服务器端程序设计,web作为发展最早也是前景最广阔的internet模型,几乎支撑了网络世界一半以上的应用,其他email ftp等传统应用虽然产生时间也很早但几乎都很没落,只有web风采依旧,蓬勃发展,从apache到lighttp到nginx等,一个接一个高性能的web服务器程序分别被开发出来,上层应用cgi, isapi(nsapi), fastcgi助力,html协议也在不断发展,html5也要登堂亮相,各种脚本语言争相斗艳,python、php、java、asp.net等。web应用支持了目前最大规模的数据google,支持了最大量用户的访问,因此各种存储、云计算等都是首先在web领域获得应用,可以说web就是it领域的创新源泉,看下apache下面有多少项目就可见一般。web体系可谓博大精深,从web体系学习服务器端程序设计是学无止境的,从nginx等服务器程序设计上可学习server程序如何分块、如何高效,从isapi/nsapi模型上可学习如何用进程内模块扩展应用,从fastcgi可学习如何用进程外模块扩展应用,从各种cache方案中可学习如何为server端提速,从各种脚本应用上可学习如何为server端程序二次开发助力,web是源泉,web的各种这方式几乎概括了server端程序的各种架构模型。当然一般自定义协议服务器和web还是有些地方不同的,最显著的区别一般也就在于协议不一样,另外就是web是无状态一般不保持连接,因此web需要传递各种session等维持状态的数据,总之从web可学习的地方实在是太多。

简单类比下web领域开发和一般server的扩展

nginx模块/apache模块            server程序+plugin

cgi                                      fork子进程

fastcgi                                 server proxy模式,由于fastcgi可网络方式部署,所以很容易在一个局域网内部实现一个ioserver+N logicserver模型

简单类比就可知道,如果一个server程序只要能很方便的支持plugin+fastcgi就可适应几乎所有各种需求,可惜的是一般的server程序几乎都不支持这两个模式的扩展,大多数gameserver只支持lua等脚本,说实在的,支持个脚本跟支持fastcgi和plugin模块相比还是差很多,如果支持fastcgi或者plugin只要做个plugin就支持lua了。

posted @ 2010-10-03 14:15 袁斌 阅读(227) | 评论 (0)编辑 收藏

Server模块如果仅仅作为一个lib包装出来自然不是最好,虽然可在c++工程很容易使用,效率也比较高,但要使用到其他语言上去就没有办法,如果包装一下自然是最好的,但如何包装呢,包装成一个什么样子也是很重要的,最常见的包装自然是包装成一个dll,输出一些接口,其他上层模块在该接口的基础上使用。最简单的包装一般可输出如下一些接口,

 

Public IServer:

{

Virtual void StartServer(LPCTSTR ports, LPCTSTR udpoorts, int iothreads, int syncthreads, int asyntchreads);

Virtual void StopServer();

         Virtual IClient *CreateClient();

         Virtual void CloseClient(IClient *);

         Virtual void OnCloseClient(IClient *);

         Virtual void OnConnectClient(IClient *);

         Virtual void PostSyncEvent(DWORD event, WPARAM wParam, LPARAM lParam)

         Virtual void PostAsyncEvent(DWORD event, WPARAM wparam, LPARAM lParam);

         Virtual void OnAsyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void OnSyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void OnTimer(UINT uid);

         Virtual void AddTimer(UINT uid, DWORD tk1, DWORD duetk);

};

 

Public IClient

{

         Virtual bool SendData(void *pdata, int len);

         Virtual bool SendFile(LPCTSTR lpfilename);

         Virtual bool SendFile(HANDLE hFile);

         Virtual bool SendMsg(PKHEAD *ph);

         Virtual bool SendMsg(PKHEAD *ph, void *pdata, int len);

         Virtual bool SendMsg(PKHEAD *ph, void *pdata, int len, byte encrypt, byte compress);

         Virtual void DelayClose();

         Virtual void OnDelayClose();

         Virtual void OnClose();

         Virtual void OnConnect();

         Virtual void OnAsyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void OnSyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void PostSyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void PostAsyncEvent(DWORD event, WPARAM wParam, LPARAM lParam);

         Virtual void SetTag(DWORD wParam);

         Virtual DWORD GetTag();

         Virtual long GetId();

};

 

IServer *DllCreateObject();

 

 

通过结合LoadLibrary并提供一个类似DllCreateObject的形式可很容易的 模拟类com实现,上层可很容易的是复用dll的实现。

posted @ 2010-10-03 14:14 袁斌 阅读(163) | 评论 (0)编辑 收藏

我写东西一般是即兴性创作,一般在走路的时候很容易产生各种想法,高兴的时候到家或到工作的地方马上就开写,几分钟就将想法记录下来,由于写得快也不检查,所以看上去可能不是很流畅,或者说得不是很清楚,甚至还有很多错别字等,请见谅。如关于某个技术问题想和我深入探讨请和我直接联系,qq:345585946

posted @ 2010-10-03 14:14 袁斌 阅读(128) | 评论 (0)编辑 收藏

有些时候需要给资源分配一个唯一id(32bit or 64bit or CHAR[N]),这里主要说下分配方法问题。

首先我们有个基本前提,如果是单线程分配,那么我们无需下面的方法,直接++value即可(CHAR型无论几线程都可使用GUID)顺序产生不重复序列,下面讨论的方法都是多线程下的分配策略:

方法1 win下做简单的方法莫过于使用InterlocckedIncrement(or InterlockedIncrement64)了,这个调用也很简单,每次递增一个,多线程间保证顺序递增绝无重复。此方法只可在单一进程上使用。

 

方法2、区间法,每个线程一次申请一个id区间[m, n],用完了再申请下一个区段,申请的时候锁一次,其他时间都不用锁,效率比3略低,比1高。此方法也只可在一个进程上使用,当然如果申请的策略修改一下也可实现多个进程甚至不同机器上的进程之间独立分配id

 

方法3、方法1虽然简单但毕竟InterlockedXXX系列函数调用还是有些耗时的,大概50cpu周期级别,更简单的方法可以使用线程切分原理,如有3个线程参与id分配,我们这样分配:

线程1 base=1, step =3,序列1,4,7,10,…

线程2 base=2, step=3,序列2,5,8,11,…

线程3 base=3, step=3,序列3,6,9,12,…

绝无重复,调用非常简单每个线程id = base; base += step;即可。

此方法在单进程上使用很简单,如果要拓展到多个进程上使用要通过配置来实现,但也是不难的。

 

方法4、如果id可用GUID表示那么方法要简单一点,生成id直接调用guid生成算法,这个id生成算法即使在多个进程之间甚至不同机器之间也可以保证唯一,也有其价值。

posted @ 2010-10-03 14:13 袁斌 阅读(1090) | 评论 (0)编辑 收藏

仅列出标题
共4页: 1 2 3 4