• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            云計(jì)算實(shí)踐2

             

            上一篇《基于云計(jì)算的價(jià)格查詢實(shí)現(xiàn)》就算是云計(jì)算實(shí)踐1吧,所以這篇就叫《云計(jì)算實(shí)踐2》。其實(shí)今年開始研究云計(jì)算有一段時(shí)間了,約3個(gè)月前研究md5破解(http://www.shprog.com/HashCrack.aspx),那個(gè)項(xiàng)目就是選來玩云計(jì)算的,當(dāng)時(shí)覺得md5破解這個(gè)小項(xiàng)目好玩,邏輯很簡單,密碼字母組合可長可短,規(guī)模可大可小,1臺機(jī)器不嫌少,1萬臺不嫌多,所以就選中了它,沒想到第一個(gè)md5破解版本后來演變成了主要是密碼數(shù)據(jù)庫的制造,雖然第一版沒有做成標(biāo)準(zhǔn)云計(jì)算,但也算有個(gè)結(jié)果,而且存儲效率、制造速度還是令人滿意的,也就是說那個(gè)項(xiàng)目只是云計(jì)算研究的副產(chǎn)品,我的本意并不是想做一個(gè)md5破解或者qq密碼破解,但結(jié)果就產(chǎn)生了這么一個(gè)產(chǎn)品,也算是努力了一個(gè)多月的結(jié)果,期間對hash算法、存儲格式等絞盡腦汁思考了很久,也因此對云計(jì)算倒是考慮得不多,最終偏離了大目標(biāo)。

            好在后續(xù)研究基于云計(jì)算的價(jià)格查詢終于又回到云計(jì)算上來了,而且仿照googlemap/reduce做了一個(gè)標(biāo)準(zhǔn)的jobserver + tasknode形式的實(shí)現(xiàn),雖然兄弟們未必對價(jià)格查詢項(xiàng)目看好,但對這個(gè)基于windows實(shí)現(xiàn)的云計(jì)算框架還是一致看好的,價(jià)格查詢項(xiàng)目第一階段基本完成預(yù)定目標(biāo),所以昨天我又將以前md5破解的東西寫了一個(gè)在線版的dll,拿到云計(jì)算框架里面來試圖云破解,不過這個(gè)不是特別成功,主要是即時(shí)計(jì)算耗時(shí)有些多,平均1個(gè)task計(jì)算1億組合大約需要30秒,因此在我現(xiàn)在只有2個(gè)點(diǎn)參與運(yùn)算的情況下遍歷很大區(qū)間是很耗時(shí)的,也因此我沒有做一個(gè)在線云破解md5的頁面,這個(gè)工作作為研究性探索也只在我的控制端下了幾個(gè)云計(jì)算的任務(wù)就告一段落,今后將致力于其他更實(shí)用的云計(jì)算實(shí)踐。

            為了做這第二個(gè)云計(jì)算的dll,我將原來定義的jobtask接口(可參見《基于云計(jì)算的價(jià)格查詢實(shí)現(xiàn)》)修改了一下,不再使用原來的c風(fēng)格接口,直接改成c++風(fēng)格了,如下:

            interface IJobTask

            {

                    virtual HMODULE free() = 0;

                    //初始化函數(shù),部署環(huán)境等

                    virtual bool init(bool tasknode) = 0;

                    //分割函數(shù),分割輸入

                    virtual size_t split(const char *input, size_t len, std::vector<CAutoBuffer *> &vbuf) = 0;

                    //task執(zhí)行函數(shù)

                    virtual bool map(const char *cmdline, CAutoBuffer &buf, CAutoBuffer &ibuf) = 0;

                    //reduce打包輸出函數(shù)

                    virtual bool reduce(std::vector<CAutoBuffer *> &vbuf, CAutoBuffer &buf) = 0;

                    //獲取執(zhí)行錯(cuò)誤

                    virtual char *geterror() = 0;

            };

            有朋友批評我,說我的接口使用stl容器,使用自定義類CAutoBuffer等不好,我以前也是這么跟別人講的,接口不要使用這些東西,但看了googlemap/reduce實(shí)現(xiàn)用的都是MapInputReduceInput之后我改變了看法,暫時(shí)就這樣定義吧,大不了各個(gè)dll都用同一版本的vc編譯就是了,也沒有什么大不了的,如果不行整體升級一下總可以吧,為了短時(shí)間盯住主要目標(biāo),也只能大刀闊斧不考慮過多細(xì)節(jié)了,這也算是一個(gè)平衡的結(jié)果吧。

            這次修改除了修改了接口,簡化了實(shí)現(xiàn)之外,還實(shí)現(xiàn)了一些特性,動態(tài)卸載,上一個(gè)版本裝入之后就不卸載了,要關(guān)閉exe才能卸載這些dll,所以無法熱更新,這個(gè)版本實(shí)現(xiàn)動態(tài)卸載之后就支持熱更新了,關(guān)鍵就在那個(gè)free函數(shù),

            virtual HMODULE free() = 0;

            該函數(shù)實(shí)例如下:

                    virtual HMODULE free()

                    {

                            HMODULE h = m_hlib;

                            delete this;

                            return h;

                    //     if(h) FreeLibrary(h);                這里釋放是有問題的,所以不能這樣釋放

                    }

            在外部調(diào)用的地方

            FreeLibrary(jf->free());

            這樣就實(shí)現(xiàn)了動態(tài)卸載dll的功能

             

            用上云計(jì)算布局的價(jià)格查詢的這段時(shí)間,還是有一些經(jīng)驗(yàn)教訓(xùn)的,基于這種相隔很遠(yuǎn),網(wǎng)絡(luò)條件差別很大的機(jī)器布局的云計(jì)算環(huán)境,可靠性是很差的,大多數(shù)時(shí)間可能反應(yīng)還是比較快,但有的時(shí)候反應(yīng)就特別慢,可能網(wǎng)絡(luò)延時(shí)會相差200ms,或者500ms,或者更多,我特意記錄了每個(gè)task的實(shí)際執(zhí)行時(shí)間和包括網(wǎng)絡(luò)傳輸在內(nèi)的總時(shí)間,就是從這兩個(gè)時(shí)間看出差距的,所以如果要基于這種環(huán)境做實(shí)時(shí)性很高的計(jì)算還是不適合的,如果對節(jié)點(diǎn)反饋實(shí)時(shí)性要求很高,那一定要布置類似局域網(wǎng)形式的計(jì)算環(huán)境,點(diǎn)點(diǎn)反饋時(shí)間1ms內(nèi),而且響應(yīng)穩(wěn)定不易受到影響。此外磁盤Log時(shí)間是不定的,我記錄最后一個(gè)task完成到job完成之間調(diào)用了兩次WriteLog,對大多數(shù)job來說,最后一個(gè)完成的task的時(shí)間和job完成的時(shí)間一致,但偶爾有少數(shù)job時(shí)間和最后一個(gè)完成的task時(shí)間差別很大,甚至有超過1s的,原先沒有這么精細(xì)的測量,這次在jobserver寫了很多log,起初是為了找錯(cuò)誤,后來是為了追蹤jobtask執(zhí)行,倒是意外的發(fā)現(xiàn)了一些問題,也獲得了一些意外的收獲。

            云計(jì)算好啊,早年我做過一個(gè)遠(yuǎn)程控制的程序,當(dāng)時(shí)做了一條命令broadcast,可以廣播其他任意命令,當(dāng)時(shí)很得意于這個(gè)設(shè)計(jì),也有指揮千軍萬馬的感覺,但當(dāng)時(shí)各自執(zhí)行,結(jié)果并不匯總,各個(gè)任務(wù)完全獨(dú)立。現(xiàn)在給云計(jì)算環(huán)境下達(dá)一個(gè)任務(wù),也有同樣的感覺,可能對使用我的價(jià)格查詢(http://www.oldworm.com/pps.aspx)的用戶或者使用google查詢的用戶根本感覺不到,他這一個(gè)查詢提交下去后面有那么多機(jī)器聯(lián)動運(yùn)算,但作為開發(fā)人員,真真切切的看到后面那么多機(jī)器在執(zhí)行任務(wù),真的是很爽的一件事情,一起看下我兩臺機(jī)器聯(lián)動執(zhí)行任務(wù)的場面共勉吧:

             

             

             

            圖看得不是很清楚,實(shí)際上第一個(gè)taskmanager是一臺機(jī)器,另一個(gè)taskmanager是另一臺機(jī)器,那兩個(gè)都是在遠(yuǎn)程桌面里面運(yùn)行的,下面ie是我的網(wǎng)頁,可以看到我在網(wǎng)頁里面查詢nokia的時(shí)候,上面兩臺機(jī)器的tasknodeapp里面就接收到任務(wù)并執(zhí)行了任務(wù),那個(gè)tasknodeapp是我臨時(shí)用來演示的,事實(shí)上里面都是調(diào)用tasknode.dlltasknode的主要任務(wù)都是tasknode.dll執(zhí)行的,為這個(gè)dll做了好幾個(gè)不同的容器,有service的有普通mfc的還有console的,這也是我的得意設(shè)計(jì)哦。

            未來還將繼續(xù)云計(jì)算實(shí)踐,期待有相同興趣愛好的朋友一起交流。

            posted @ 2010-10-03 14:23 袁斌 閱讀(357) | 評論 (0)編輯 收藏

            之前的文章講過,我設(shè)計(jì)的網(wǎng)絡(luò)框架有幾組線程,分別是io、異步、同步、定時(shí)器,各個(gè)不同應(yīng)用server幾組線程組合形式不盡相同,簡單的可只有io線程,復(fù)雜一點(diǎn)的可io+同步,更復(fù)雜一點(diǎn)的也可io+同步+異步+定時(shí)器,總之我以幾組線程的自由組合方式應(yīng)付各種應(yīng)用,在我負(fù)責(zé)的server全是這一套框架實(shí)現(xiàn)的,不管是支持幾萬人連接的服務(wù)器,還是只有幾個(gè)用戶連接的內(nèi)部服務(wù)器,這套框架也算是久經(jīng)考驗(yàn),穩(wěn)定運(yùn)行多年,內(nèi)部使用也非常簡單,如給sync線程組發(fā)一個(gè)消息只要PostSyncEvent,如果要給異步線程發(fā)一個(gè)消息只要發(fā)PostAsyncEvent,雖然只能開發(fā)的時(shí)候確定哪個(gè)任務(wù)在哪組線程執(zhí)行,但修改還是非常方便的,執(zhí)行體就是一組這樣的函數(shù):

            OnSyncEvent(DWORD dwEvent, DWORD wParam, DWORD lParam);

            OnAsyncEvent(DWORD dwEvent, DWORD wParam, DWORD lParam);

            一眼就知道是在哪個(gè)線程組里面執(zhí)行,當(dāng)然有的線程組是一個(gè)線程,有的線程組是多個(gè),這涉及到有的資源是不是要加鎖,有經(jīng)驗(yàn)的開發(fā)人員很容易理解。

            說了一下框架才容易理解我的問題,之前定時(shí)器是一個(gè)獨(dú)立的線程組,同步線程組、異步線程組、io組都沒有定時(shí)器功能,定時(shí)器觸發(fā)后要發(fā)送消息到相應(yīng)線程組,有的要發(fā)給異步線程組,有的要發(fā)給同步線程組,這就會引起線程切換,這是問題之一,還有一個(gè)問題,之前的定時(shí)器是由windows的時(shí)鐘隊(duì)列實(shí)現(xiàn)的,這個(gè)定時(shí)器優(yōu)點(diǎn)是很明顯的,定時(shí)精確,功能強(qiáng)大,參數(shù)眾多,獨(dú)立線程組,但也有很明顯的問題,如果要刪除一個(gè)定時(shí)器則有線程依賴,就是要在定時(shí)器線程才能刪除定時(shí)器,這個(gè)依賴約束很大,也很容易引起問題,用起來很不方便,使得一些資源的釋放不能夠即時(shí)進(jìn)行。正因?yàn)橛羞@么些問題,也為了使得時(shí)鐘模塊更容易移植,我設(shè)計(jì)了一個(gè)新時(shí)鐘模塊,為實(shí)現(xiàn)以下目標(biāo):

            1、無線程依賴,隨便調(diào)用者在哪個(gè)線程調(diào)用都可刪除指定的定時(shí)器。

            2、和事件消息集成在一個(gè)線程內(nèi),實(shí)現(xiàn)無需切換的定時(shí)器功能,這樣主線程、同步線程組、異步線程組都可在內(nèi)部處理定時(shí)器消息,無需單獨(dú)的定時(shí)器線程輔助,方便很多。

            為實(shí)現(xiàn)以上目標(biāo),我引入了libevent里面的minheap管理定時(shí)器,并根據(jù)之前管理事件的處理辦法,繼續(xù)使用iocp隊(duì)列管理線程消息,在每個(gè)線程組用iocp管理事件,根據(jù)最短觸發(fā)的定時(shí)器計(jì)算wait時(shí)間,這樣就在同一組線程內(nèi)實(shí)現(xiàn)了定時(shí)器和事件合并處理,當(dāng)然實(shí)現(xiàn)方法有很多,也可用iocp+WaitableTimer等,也可用apc,但那些實(shí)現(xiàn)的windows烙印都太深刻,雖然精度更高,實(shí)現(xiàn)更容易,我用minheap+iocp隊(duì)列方式的實(shí)現(xiàn)相對來說對windows的依賴較少,因?yàn)樘鎿Q一個(gè)iocp隊(duì)列處理事件是很容易的,這樣也方便移植和復(fù)用代碼。經(jīng)這樣修改之后,各個(gè)線程組包括主線程都可處理定時(shí)器和事件消息,也使得以前雞肋式的主線程終于可當(dāng)同步線程發(fā)揮作用,以前的定時(shí)器線程組也不一定需要了,既減少了線程,也減少了切換,現(xiàn)在各個(gè)線程組(包括主線程)都有完全一致的消息處理和時(shí)鐘處理函數(shù)。

            事件函數(shù):

            OnTimerEvent(DWORD dwEvent, DWORD wParam, DWORD lParam);

            OnSyncEvent(DWORD dwEvent, DWORD wParam, DWORD lParam);

            OnAsyncEvent(DWORD dwEvent, DWORD wParam, DWORD lParam);

            OnServiceEvent(DWORD dwEvent, DWORD wParam, DWORD lParam);

            定時(shí)器函數(shù):

            OnTimerTimer(TlsInfo *ptls, EventTimer *et);

            OnSyncTimer(TlsInfo *ptls, EventTimer *et);

            OnAsyncTimer(TlsInfo *ptls, EventTimer *et);

            OnIoTimer(TlsInfo *ptls, EventTimer *et);

            OnServiceTimer(TlsInfo *ptls, EventTimer *et);

            可以給線程組增加定時(shí)器刪除定時(shí)器

            AddTimerAddSyncTimerAddAsyncTimerAddServiceTimerAddIoTimer

            DelTimerDelSyncTimerDelAsyncTimerDelServiceTimerDelIoTimer

            可給各線程組發(fā)消息

            PostTimerEventPostSyncEventPostAsyncEventPostServiceEvent

             

            這套框架是我多年服務(wù)器端開發(fā)的得意之作,體現(xiàn)了我簡潔實(shí)用的設(shè)計(jì)思想,用起來非常方便,可任意組合,適應(yīng)各種需求的應(yīng)用,由于除主線程之外的io線程組、同步線程組、異步線程組、定時(shí)器線程都是可以關(guān)、開1個(gè)、開多個(gè),所以組合非常靈活,開1個(gè)可當(dāng)同步線程,開多個(gè)可當(dāng)異步線程(內(nèi)部搶資源),關(guān)閉就不存在該組線程,即使是io線程組也是可關(guān)的,這樣就使得這套框架不僅僅用在標(biāo)準(zhǔn)server上,就算是當(dāng)作一般的消息隊(duì)列服務(wù)器也沒問題,高度的靈活性使得這套框架可適應(yīng)各種規(guī)模的應(yīng)用,這次對定時(shí)器的改造使得這種組合更靈活,雖然現(xiàn)在的實(shí)現(xiàn)方法定時(shí)器的精度有一些下降,但瑕不掩瑜,這樣改造之后功能無疑是更強(qiáng)大了。

            posted @ 2010-10-03 14:23 袁斌 閱讀(699) | 評論 (0)編輯 收藏

            實(shí)用云計(jì)算環(huán)境簡述

             

            如今it領(lǐng)域沒聽說過云計(jì)算的絕對是out了,雖然大家都知道云計(jì)算,雖然很多高校很多專業(yè)都開設(shè)了云計(jì)算專業(yè),雖然很多人都在討論云計(jì)算,雖然也有少數(shù)人走在了應(yīng)用云計(jì)算的前列,然而,可悲的是,大多數(shù)人對云計(jì)算的認(rèn)識僅限于amazongooglemicrosoftibm有能力架設(shè)云計(jì)算環(huán)境,其他公司都靠邊,甚至唯他們的云計(jì)算才叫云計(jì)算,別的企業(yè)根本不可能做云計(jì)算,各級政府部門最搞笑了,動不動花多少錢引進(jìn)某某云計(jì)算環(huán)境,填補(bǔ)某某空白,多少cpu多少機(jī)器每秒多少萬億次計(jì)算,最終是不是一堆浪費(fèi)電力的擺設(shè)也沒有人知道,也沒人去過問。

            略感欣慰的是,很多企業(yè)都在務(wù)實(shí)地部署自己的云計(jì)算環(huán)境,大如騰訊、淘寶、百度、小如我們這樣剛成立的小公司,其實(shí)要部署一個(gè)私有云計(jì)算環(huán)境并沒有那么難,以我個(gè)人的經(jīng)驗(yàn)來看,如果有一個(gè)精干的小團(tuán)隊(duì),幾個(gè)人一個(gè)月部署一個(gè)私有云計(jì)算環(huán)境是完全可能可行的。在我看來,所謂云計(jì)算就是分布式存儲+分布式計(jì)算,不局限于底下oswin還是*nix,也不局限于是局域網(wǎng)環(huán)境還是廣域網(wǎng)環(huán)境,也不管上面跑的是c++的程序還是javascript的程序,下面簡單介紹下我設(shè)計(jì)的一個(gè)即時(shí)查詢價(jià)格的云計(jì)算體系:

            我一直在win下開發(fā),win用得非常熟練,所以我把云計(jì)算環(huán)境部署在windows之上,當(dāng)然也考慮到windows的機(jī)器眾多,tasknode可輕易找到非常多的目標(biāo)機(jī)器,我部署的云計(jì)算環(huán)境主要分兩類節(jié)點(diǎn),jobservertasknodejobserver主管任務(wù)切割、任務(wù)調(diào)度,tasknode是計(jì)算節(jié)點(diǎn)。另外還有一些節(jié)點(diǎn),jobowner可連接jobserver并提交任務(wù),并可查詢該任務(wù)的執(zhí)行情況,admin可連接jobserver查詢jobserver的狀態(tài)。

             

            其實(shí)這些上篇博客已經(jīng)寫過,我再講的詳細(xì)一點(diǎn),看具體的執(zhí)行情況,首先jobownerjobserver提交package,這個(gè)package是一個(gè)zip文件,包含一組文件,jobowner提交package之后jobserver會根據(jù)約定的規(guī)則管理package,并在jobserver展開該package,如下:

             

             

            Jobowner連到jobserver之后,發(fā)出如下的命令到jobserver

            0x49 0x0 0x0 0x0 0x2 0x0 0xb 0x0 127.0.0.1 0x0 ppsget.dll 0x0

            {type:[0,1,2,3,4],rmax:5,wb:"pc",text:"諾基亞 e63"} 0x0

            上面是用我設(shè)計(jì)的一種混合顯示格式顯示的包數(shù)據(jù),可以看到里面帶上了ppsget.dll,這就是指定包內(nèi)部名,其實(shí)還可以這樣ppsget.dll:getpage,如此一個(gè)dll就可支持多個(gè)IJobTask輸出,getpage只是獲得其中一個(gè)IJobTask接口(關(guān)于IJobTask接口參考上一篇云計(jì)算實(shí)踐2的文章)。具體命令是json格式,主要是為了方便信息傳輸和解析。Jobserver接收到該命令之后,調(diào)用ppsget.dllIJobTask接口中的split函數(shù),將該任務(wù)分解,之后調(diào)度Tasknode執(zhí)行,tasknode收到jobserver發(fā)過來的任務(wù)之后,檢查包名稱,如果缺少就會主動向jobserver要求發(fā)送相應(yīng)的包,并進(jìn)行部署,待部署完成之后從包獲取指定的IJobTask接口,執(zhí)行該接口的map函數(shù),將結(jié)果按照約定的格式發(fā)給jobserver,最后由jobserver調(diào)用IJobTask中的reduce函數(shù)進(jìn)行打包,最后將結(jié)果發(fā)給jobowner并記錄相關(guān)Log

            上圖中還可看到一個(gè)HashCrackCloud.dll,這是另一個(gè)云計(jì)算環(huán)境下破解md5密碼的dll,這個(gè)上篇文章也寫了一下,這里就不詳述了。

             

            為使得tasknode可適應(yīng)各種機(jī)器環(huán)境,我把tasknode設(shè)計(jì)為一個(gè)dll,該dll內(nèi)部自己管理消息及任務(wù)執(zhí)行,該dll可被加載到各種容器進(jìn)程(如gui進(jìn)程、console進(jìn)程、service進(jìn)程)等執(zhí)行,看下我的tasknode和它的容器進(jìn)程:

             

            這也算是我的得意設(shè)計(jì)吧,這樣設(shè)計(jì)的tasknodewindows系統(tǒng)下的確具有很高的靈活性。

            這樣的tasknode甚至可直接加載在jobserver進(jìn)程,也可被任意win系列機(jī)器的任意進(jìn)程加載參與運(yùn)算,用主動加載或被動加載都很方便,極大的方便了云計(jì)算環(huán)境的部署,反正具體執(zhí)行的任務(wù)都由package完成,tasknode只要按照約定的規(guī)則部署 package即可,所以這種云計(jì)算環(huán)境是非常輕量級又非常靈活的,開發(fā)一個(gè)新的任務(wù)只要做一個(gè)新的IJobTask即可,目前我這套體系除了沒有考慮太多安全性之外,這個(gè)云計(jì)算環(huán)境的實(shí)施還是非常容易的,實(shí)際上我們這個(gè)價(jià)格查詢的后臺云計(jì)算環(huán)境只用了不到2周的時(shí)間就開發(fā)完成。

            再看下jobserver記錄的每個(gè)joblog

             

            log中可很容易的分析出一個(gè)job每個(gè)task的執(zhí)行情況,并可根據(jù)這些數(shù)據(jù)進(jìn)行相應(yīng)的優(yōu)化處理。

            之所以把jobservertasknode以及package都寫出來,主要是為了表達(dá)一個(gè)看法,要實(shí)現(xiàn)一個(gè)簡單的云計(jì)算環(huán)境其實(shí)并不難,有經(jīng)驗(yàn)的團(tuán)隊(duì)很容易就能做出來,參考下googlemap/reduce論文,按照自己的需要簡化實(shí)現(xiàn),真理在實(shí)踐中,如果只是仰望googleamazon,那就真的是在云中霧里,另一個(gè)想要表達(dá)的就是云的形式是多種多樣的,并不一定amazonegoogle的云計(jì)算環(huán)境才是標(biāo)準(zhǔn)的,對實(shí)用派來說,形式都是次要的,實(shí)用才是關(guān)鍵的。

            posted @ 2010-10-03 14:23 袁斌 閱讀(1816) | 評論 (1)編輯 收藏

            基于云計(jì)算的價(jià)格查詢實(shí)現(xiàn)

             

             

            上篇博客提到價(jià)格查詢功能,當(dāng)時(shí)正在考慮做成云計(jì)算模式,所以當(dāng)時(shí)連多線程都沒考慮,就是準(zhǔn)備將功能都交給云計(jì)算系統(tǒng)的,由云計(jì)算內(nèi)部管理線程和調(diào)度問題,所以當(dāng)時(shí)實(shí)現(xiàn)就根本不用考慮多線程,現(xiàn)在功能基本實(shí)現(xiàn),下面大致講講我的做法。

            國內(nèi)很多人談到全文檢索就必提lucene,提到云計(jì)算就必提googlemap/reduce、開源的hadoop、amazonec2,似乎只有那些東西才叫云計(jì)算,咱是實(shí)戰(zhàn)派,沒興趣口舌之爭,在俺看來分布式存儲+分布式計(jì)算就叫云計(jì)算,俺就看了看googlemap/reduce論文,照其思想在win下做了個(gè)簡單的job/task調(diào)度系統(tǒng),使其能支撐俺的第一個(gè)實(shí)戰(zhàn)應(yīng)用價(jià)格查詢,圖示如下:

             

             

             

             

                adminclient承擔(dān)管理功能,可查看任務(wù)及執(zhí)行情況,可查看Tasknode機(jī)器情況,如果需要可管理Task,目前只支持簡單的幾條命令,adminclient主動連jobserver登錄成功后可發(fā)送管理命令。

             

                JobOwner提交一個(gè)Job之后返回一個(gè)jobid,如果意外斷開可通過下次重連的時(shí)候提交jobid和一個(gè)sessionid可提取job結(jié)果數(shù)據(jù),job提交通過提交一個(gè)zip包即可,參數(shù)等文件都打在包里面,tasknode可直接解包執(zhí)行里面的dllJobowner主動連jobserver,登錄成功后可發(fā)job命令。

             

                TaskNode是執(zhí)行具體任務(wù)的客戶端,job包用zip打包后發(fā)布給tasknodetasknode參與計(jì)算并反饋結(jié)果。TaskNode設(shè)計(jì)成多線程模式,一個(gè)線程保持和jobserver的通信,其他線程參與運(yùn)算,Tasknode可同時(shí)執(zhí)行多個(gè)不同的任務(wù),如a線程執(zhí)行價(jià)格查詢,b線程執(zhí)行hash破解等。Tasknode主動連jobserver,登錄后可接受jobserver分派的任務(wù),由于tasknode是主動連jobserver的,所以即使是內(nèi)網(wǎng)機(jī)器或者任意有閑置資源的機(jī)器都可作為Tasknode,不管它是家里的、公司的、還是網(wǎng)吧的,這也是該系統(tǒng)基于windows實(shí)現(xiàn)的一個(gè)重要前提,因?yàn)?/span>win的機(jī)器是如此的多,在國內(nèi)win的機(jī)器無處不在。

             

            JobServerjob調(diào)度器,管理包分發(fā)以及任務(wù)分割、調(diào)度,典型的執(zhí)行流程是這樣,jobowner提交一個(gè)命名的包給jobserverjobserver將該包部署管理,之后jobowner 可給jobserver提交任務(wù),jobserver收到任務(wù)后根據(jù)任務(wù)指定的包配置執(zhí)行,如部署包后裝載dll并執(zhí)行任務(wù)分割操作,分割是將一個(gè)job分割為多個(gè)task,之后再將每個(gè)task提交給一個(gè)tasknode執(zhí)行,并管理tasknode的輸出以及可能的出錯(cuò),出錯(cuò)現(xiàn)在的處理是交給另一個(gè)tasknode執(zhí)行,當(dāng)剩下最后一個(gè)tasknode的時(shí)候會將該tsaknode同步叫給另一個(gè)不同的tasknode執(zhí)行,不管誰最后成功執(zhí)行這個(gè)tasknode,只要該task執(zhí)行成功立即結(jié)束整個(gè)job,并將結(jié)果反饋給jobownerjobowner也可在執(zhí)行中提交查詢命令,jobserver會將被查詢job當(dāng)前的輸出返回,這樣碰到需要長時(shí)間執(zhí)行的任務(wù)也能適用。

             

            從以上介紹可以看到,具體任務(wù)是由包執(zhí)行的,這個(gè)包實(shí)際上可能是一個(gè)dll,也可能是幾個(gè)dll加上一些配置文件組成,之所以設(shè)計(jì)成這種模式,主要是考慮整個(gè)系統(tǒng)在win上方便部署,主dll需要支持幾個(gè)固定的接口:

             

            //任務(wù)dll初始化函數(shù)

            typedef bool (*jobtask_init_)(jobtaskfunc *jtfunc, bool tasknode);

            //map分割函數(shù)

            typedef size_t (*jobtask_split_)(jobtaskfunc *jtfunc,

                                               const char *input, size_t len,

                                               std::vector<CAutoBuffer *> &vbuf);

            //reduce打包函數(shù)

            typedef size_t (*jobtask_reduce_)(jobtaskfunc *jtfunc,

                                                     std::vector<CAutoBuffer *> &vbuf,

                                                     CAutoBuffer &buf);

            //Task執(zhí)行函數(shù)

            typedef bool (*jobtask_map_)(jobtaskfunc *jtfunc, const char *cmdline, CAutoBuffer &outbuf);

            //釋放函數(shù)

            typedef bool (*jobtask_free_)(jobtaskfunc *jtfunc);

             

             

            上面init函數(shù)主要執(zhí)行線程相關(guān)的初始化,該函數(shù)典型的可能是空,或者是

            CoInitialize(NULL);

            Split函數(shù)是用來將job輸入分割為N個(gè)tasknode輸入的,該函數(shù)由jobserver調(diào)用,每個(gè)tasknode輸入就是map函數(shù)的輸入,tasknode的任務(wù)就是調(diào)用map函數(shù),并傳遞輸入,最后將輸出返回給jobserverjobserver在需要的時(shí)候調(diào)用reduce將各個(gè)tasknode的輸出打包返回,free函數(shù)是個(gè)輔助函數(shù),釋放資源的。

            熟悉googlemap/reduce的應(yīng)該知道,我的實(shí)現(xiàn)簡化了reduce,在我的實(shí)現(xiàn)里面并沒有獨(dú)立的reduce worker,該任務(wù)由jobserver自己做了,這一方面是簡化實(shí)現(xiàn),另方面也是適應(yīng)需求的結(jié)果,畢竟在我的需求里面輸入是很少的(一個(gè)典型任務(wù)100字節(jié)量級)tasknode的計(jì)算是很多的,輸出也是不多的(1k量級),所以由jobserver打包整個(gè)輸出也很輕松,用不著一組獨(dú)立的reduce來管理輸出。另外可以看到上面接口用了我的自定義類CAutoBuffer,這個(gè)類主要管理不定長數(shù)據(jù)的,其實(shí)用vector<char>也可,但考慮方便,我的實(shí)現(xiàn)內(nèi)部都用了CAutoBuffer。一個(gè)典型的分布式應(yīng)用只要做一個(gè)dll,有上面幾個(gè)函數(shù),并輸出一個(gè)

             

            struct jobtaskfunc

            {

                    //初始化函數(shù)

                    jobtask_init_ init;

                    //釋放函數(shù)

                    jobtask_free_ free;

             

                    //以下被tasknode調(diào)用

                    jobtask_map_ map;

             

                    //以下被jobserver調(diào)用

                    jobtask_split_ split;

                    jobtask_reduce_ reduce;

            };

            typedef jobtaskfunc *(WINAPI *create_jobtask_)();

            函數(shù)即可。

             

            學(xué)習(xí)map/reduce重要的是學(xué)習(xí)其思想,并不拘泥于實(shí)現(xiàn)形式,我想這大概正是國內(nèi)環(huán)境欠缺的,國內(nèi)能說得頭頭是道的人太多,能動手干出結(jié)果來的人很少,真正坐下來做實(shí)事的不多,只喜歡抄抄概念,拿別人的東西過來架設(shè)一下,就是這樣的人也能混成大拿。我從map/reduce思想出發(fā),學(xué)習(xí)其思想,簡化其實(shí)現(xiàn),為實(shí)際應(yīng)用服務(wù),雖然這個(gè)東西很簡單,甚至可以說有些簡陋,但實(shí)際效果不錯(cuò),雖然現(xiàn)在只部署了兩個(gè)點(diǎn),但總體上還是令人滿意的。

             

            實(shí)現(xiàn)這個(gè)jobserver/tasknode系統(tǒng)并部署價(jià)格查詢花了不到兩周時(shí)間,實(shí)際上花在jobservertasknode上的時(shí)間大概只有一周多一點(diǎn),ppsget.dll(具體干活的dll)用正則表達(dá)式分析網(wǎng)頁并提取輸出,該dll被應(yīng)用到多線程環(huán)境后也出了一些問題,用boost::reg的時(shí)候居然偶爾會出現(xiàn)異常,原以為boost::reg這樣的應(yīng)用應(yīng)該是非常明確的,要么找到,要么沒有找到,除此不應(yīng)該有第三態(tài),沒想到boost::reg這個(gè)不爭氣的東西不但不是二態(tài)的,還容易出現(xiàn)異常,試用了一下tr1::regex也是類似的問題,無奈只能在外面包了一層異常處理,雖然不再被異常搞死,但一旦出現(xiàn)異常就是很慢的,要10s左右才返回,現(xiàn)在也沒有特別好的辦法,只在異常的時(shí)候?qū)㈨撁姹4妫潞蠓治霾⒏膶懻齽t表達(dá)式,盡量將正則表達(dá)式做小,將非貪婪式查找用少一點(diǎn)。

            下面看看我們價(jià)格查詢網(wǎng)站 http://www.shprog.com/pps.aspx 的輸出:

             

             

            那個(gè)360的價(jià)格居然是圖片,ocr模塊是俺同事搞的,現(xiàn)在識別率能達(dá)到99%以上,還是很不錯(cuò)的。

            posted @ 2010-10-03 14:22 袁斌 閱讀(222) | 評論 (0)編輯 收藏

            一直想測試一下json的解析速度,前些天終于花了一點(diǎn)時(shí)間測了一下,在我的破筆記本上,解析一個(gè)包含10個(gè)元素(各種類型都有)的objectjson1秒鐘大概只能解析不到10w次,就算把內(nèi)存池用到極致也只能解析12.5w次左右,換用自己定義的一種bjson格式,速度快了一些,但也不超過20w次,想想工作量也的確很大,生成一個(gè)包含10個(gè)子元素的object,需要動態(tài)分配最少10次,還要做最少10hashinsert,還有各種格式的轉(zhuǎn)換工作,里面有arrayobject還要額外分配容器并處理子對象,這可都是耗時(shí)操作,終于明白了為什么webserver為何一秒鐘只能處理幾千個(gè)請求甚至只能處理幾百個(gè)請求了,看來要將游戲協(xié)議完全用json暫時(shí)還是不大可取,從效率上看折中點(diǎn)的做法依然是struct+jsonstruct+string\0string\0…,這些我以前的blog都寫過,只是現(xiàn)在找到了效率上的依據(jù),畢竟游戲服務(wù)器一秒都是要處理幾萬數(shù)據(jù)包的,要是全是json光解析json就把時(shí)間耗光了,更不用說去處理其他任務(wù)了。

            posted @ 2010-10-03 14:21 袁斌 閱讀(903) | 評論 (0)編輯 收藏

            花了四天寫了個(gè)價(jià)格查詢的web體驗(yàn)版,大致結(jié)構(gòu)是這樣的,前端web界面:

             

            web通過tcp連接后臺一個(gè)ppsserverppsserver調(diào)用一個(gè)ppsget.dll從一些配置好的網(wǎng)站現(xiàn)拉網(wǎng)頁分析產(chǎn)品價(jià)格等信息,說起來是很簡單的,要是畫出結(jié)構(gòu)圖來也是很簡單的,看看效果:

             

             

             

            為了寫這個(gè)東西查了比價(jià)網(wǎng)等很多資料,看來看去覺得現(xiàn)在的一些比價(jià)網(wǎng)都把自己當(dāng)購物門戶了,上面什么信息都有,數(shù)據(jù)都是緩存的,有的還隱藏原始鏈接,用戶點(diǎn)進(jìn)去也都是緩存的數(shù)據(jù),不再鏈接到原始出處,看了幾個(gè)網(wǎng)站數(shù)據(jù)誤差較大,有個(gè)網(wǎng)站排在最前面價(jià)格最低的鏈接點(diǎn)進(jìn)去之后發(fā)現(xiàn)根本沒有那個(gè)低價(jià)格,也不知道那個(gè)價(jià)格信息是什么時(shí)候的,或者根本就提取錯(cuò)了。看了那么多比價(jià)網(wǎng)站,時(shí)間誤差最小的也超過10個(gè)小時(shí),很令我失望,總之我的出發(fā)點(diǎn)和這些網(wǎng)站不同,我希望做一個(gè)界面很簡潔的、實(shí)時(shí)查詢的服務(wù),而且速度要求很快,一次查詢速度最好小于1秒,當(dāng)然我現(xiàn)在技術(shù)預(yù)覽版離這個(gè)目標(biāo)還差得很遠(yuǎn)。界面簡潔使得用戶即使是使用手機(jī)也能得到很好的輸出,也不占用多少帶寬,我還希望前端接上條碼掃描功能,這樣很多不會輸入的人就可直接對著條碼就能查詢網(wǎng)店價(jià)格,多方便啊,呵呵。不過做這個(gè)功能發(fā)現(xiàn)技術(shù)不是大問題,我4天除了布好了架構(gòu)還做了5家網(wǎng)店的網(wǎng)頁分析,可見這些基本技術(shù)都不太難,最大的矛盾是實(shí)時(shí)查詢數(shù)據(jù)量太大,就算只查詢一個(gè)產(chǎn)品,分析5個(gè)網(wǎng)站的數(shù)據(jù)加在一起估計(jì)接近1M,這要是每秒有個(gè)幾百幾千人訪問那還得了啊,得要多大的帶寬才能撐得住啊,難怪看了那么多比價(jià)網(wǎng)站沒有一家提供實(shí)時(shí)查詢的,不是他們做不了實(shí)時(shí)查詢,的確是因?yàn)閹捥螅晕蚁虢酉聛碜鲆惶追植际讲樵兡P停瑢⒑芏酂o固定ip的機(jī)器接入ppscontrolserver,一起參與為用戶提供查詢服務(wù),今天在看mapreduce,希望自己不要閉門造車,其實(shí)很多年前就想做這個(gè)功能了,只是一直沒有下手,加上那個(gè)時(shí)候也沒有一套穩(wěn)定的網(wǎng)絡(luò)庫,現(xiàn)在條件都具備了,希望最近可以做一個(gè)簡單的分布式計(jì)算框架出來,那樣以后要做類似功能就容易了,可能只要加入一個(gè)簡單的dll發(fā)布一個(gè)計(jì)算命令就可以了。這個(gè)分布式計(jì)算模型做出來之后,傳統(tǒng)的比價(jià)網(wǎng)站就只能望俺項(xiàng)背了。

            posted @ 2010-10-03 14:21 袁斌 閱讀(487) | 評論 (0)編輯 收藏

            HashCrack跑起來了一段時(shí)間,一直沒有寫架構(gòu)方面的總結(jié),今天在地鐵上畫了一張圖:

            照此架構(gòu)理論上是可以支持非常巨大的后端數(shù)據(jù)的,如果將web也弄成多個(gè),分別連不同的SN則可支持非常巨大的用戶量。

            posted @ 2010-10-03 14:20 袁斌 閱讀(226) | 評論 (0)編輯 收藏

            HashCrack程序數(shù)據(jù)及索引設(shè)計(jì)2

             

             

            上個(gè)月寫了《HashCrack程序數(shù)據(jù)及索引設(shè)計(jì)》里面已經(jīng)提到早期設(shè)計(jì)的幾種存儲方法,最后達(dá)到了每條記錄15個(gè)字節(jié)左右的水平,但這個(gè)存儲效果還是很差的,而且是單體文件,受制于內(nèi)存限制,后來又設(shè)計(jì)了幾種復(fù)合索引格式,支持1萬億記錄一個(gè)復(fù)合索引,下面簡單講講之后的研究成果。

            6、將內(nèi)容區(qū)和索引區(qū)合并,索引位置不再提供指向內(nèi)容區(qū)的size_t,內(nèi)容區(qū)不再需要,直接在索引區(qū),這樣索引區(qū)indexnode

            Struct indexnode

            {

                    Size_t nextoffset;

                    Char str[0];

            };

            經(jīng)過此修改之后稍微不好的地方就是如果一個(gè)文件里面要管理不同長度的字符串那么只能取最長的字符串長度,以便indexnode保持相同大小容易索引。

            這種方法雖然效果不錯(cuò),但平均下來一個(gè)字符串還是要占用11個(gè)左右的字節(jié),而且不同長度的字符串有一些浪費(fèi)的地方。

             

            7、以上的存儲方法雖然已經(jīng)比較緊湊,但還不是最緊湊的方法,如果不保存字符串只是保存字符串在序列中的位置,那么不同字符串也沒有長度不同,也可以用同樣的大小去保存,如果一個(gè)db保存42億以下的字符串,那么只要4個(gè)字節(jié)就可以了,如果一個(gè)db保存1萬億以下的數(shù)據(jù),那么只要5個(gè)字節(jié)就可以,這真是個(gè)非常有創(chuàng)意的想法,其實(shí)我當(dāng)初想到這個(gè)想法的時(shí)候很擔(dān)心計(jì)算效率,遲遲沒有動手代碼,但思考了幾天之后打消了我對效率的擔(dān)心,相反,只保存一個(gè)position比復(fù)制N個(gè)字符串可能還要快一點(diǎn),這樣我們就只要9個(gè)字節(jié)描述indexnode了,看定義:

            Struct indexnode

            {

                    Size_t lpos;

                    Byte hpos;

                    Size_t nextoffset;

            };

            精確到9個(gè)字節(jié)表示一條記錄,很不錯(cuò),也沒有更多的限制。事實(shí)上9字節(jié)版本的速度比方法6的確是要快一點(diǎn),還沒優(yōu)化的時(shí)候就比6方法要快一些了,當(dāng)然查詢的時(shí)候由于要多計(jì)算一些信息,理論上是要慢一點(diǎn)的,但由于都是內(nèi)存計(jì)算,其實(shí)影響不是很大。

             

            8、上述9個(gè)字節(jié)的方法雖然已經(jīng)很緊湊,但如果給nextoffset做一點(diǎn)限制,讓一個(gè)區(qū)段的數(shù)據(jù)為1667w以下,那么描述nextoffset 只需要3個(gè)字節(jié)即可,這樣indexnode總的長度就只需要8個(gè)字節(jié),這真是很好的想法,我為這個(gè)想法驕傲,看下indexnode8字節(jié)版本

            Struct indexnode

            {

                    Size_t lpos;

                    Size_t hpos:8;

                    Size_t nextoffset:24;

            };

            精確的8字節(jié)indexnode,如此我們最終實(shí)現(xiàn)了最緊湊的md5數(shù)據(jù)庫,每條記錄8個(gè)字節(jié),幾乎無法再減少了,期待哪天突然靈光閃現(xiàn)再創(chuàng)造出更緊湊的存儲方法吧,呵呵,這個(gè)實(shí)現(xiàn)其實(shí)已經(jīng)超越了我最初的估計(jì)了,我以為能減少到12個(gè)字節(jié)已經(jīng)到頂了,沒想到還能減少到8個(gè)字節(jié)。

            8字節(jié)的版本最初寫出來的時(shí)候效率下降得很厲害,因?yàn)橐郧?/span>nextoffset當(dāng)指針用,現(xiàn)在3個(gè)字節(jié)無法當(dāng)指針,只能轉(zhuǎn)換,多一個(gè)轉(zhuǎn)換函數(shù)效率下降了一些,其他地方剛寫的時(shí)候也是非優(yōu)化算法,所以第一個(gè)8字節(jié)版本效率比9字節(jié)降低了一半以上,但花了一個(gè)早上優(yōu)化之后效率又上去了,現(xiàn)在制造復(fù)合索引只需要82秒就可完成1億條記錄,速度比方法6快不少,方法6需要120秒左右。

             

            或許我講得比較簡單,如果不是深入研究這一塊的人或許看不明白,但精華我基本上講出來了,實(shí)現(xiàn)上其實(shí)有很多技巧,如果要做到象我一樣的速度其實(shí)是需要很深功力的,我測試用的機(jī)器是朋友的入門級服務(wù)器E5504 2.0cpu4G內(nèi)存,普通7200轉(zhuǎn)硬盤。

            posted @ 2010-10-03 14:19 袁斌 閱讀(177) | 評論 (0)編輯 收藏

            從開始研究HashCrack兩個(gè)多月了,雖然中間忙其他項(xiàng)目間斷了近一個(gè)月,但總的耗在HashCrack上的時(shí)間也有一個(gè)多月,最近幾天又把web部分完善了一下,順便做了其他幾種加密算法,現(xiàn)在HashCrack支持MD5SHA1MYSQL5HASHQQHASH四種算法,每種算法都制造了46億數(shù)據(jù),總共占磁盤34.2 * 3Gqqhashmd5復(fù)用同一份數(shù)據(jù)。好在之前架構(gòu)做得比較好,換一種加密算法只要換兩個(gè)函數(shù)即可,所以加后面三種算法只花了1天時(shí)間。為了讓界面更友好一點(diǎn),臨時(shí)學(xué)了下ajax,并學(xué)習(xí)了一下.net里面調(diào)用c++ dll,順便用c++做了一個(gè)dll提供四種算法的加密供web調(diào)用。新web頁面地址是 http://www.shprog.com/hashCrack.aspx,部分界面如下:

             

             

            看上去一個(gè)簡單頁面,背后2服務(wù)器程序(1web 1 hashcrackserver),103G數(shù)據(jù),3個(gè)dllhashencrypt.dll, page.dll, data.dll),一個(gè)制造數(shù)據(jù)的exe,還有一個(gè)client工具,那工具好久沒升級了,client工具支持一次多條查詢。Hashcrackserver支持分布,client端工具也支持?jǐn)?shù)據(jù)分布和運(yùn)算,總的是一個(gè)云計(jì)算系統(tǒng)。

            現(xiàn)在覺得我的這個(gè)頁面比www.cmd5.com www.md5.com.cn免費(fèi)版有價(jià)值一點(diǎn),他們雖然總的數(shù)據(jù)可能多一些,但開放的數(shù)據(jù)很少,特別mysql5 qqhash sha1要么沒有,要么沒開放或只開放了一點(diǎn)點(diǎn)數(shù)據(jù),對免費(fèi)用戶實(shí)際用處不大。

            posted @ 2010-10-03 14:19 袁斌 閱讀(242) | 評論 (0)編輯 收藏

            前文已經(jīng)講述,字母全排列是個(gè)驚人的數(shù)字,即使只遍歷小寫字母和數(shù)字6個(gè)全排列也有36^6 = 217678233621億多個(gè),7個(gè)排列36^7 = 78364164096783億多,8個(gè)排列36^8 = 28211099074562.8萬億多個(gè),數(shù)字非常驚人。Md5反查是個(gè)string-string的映射,16-N個(gè)字符的映射,如果考慮hex模式的md5那就是32-N的映射,考慮映射人們最先想到的可能都是數(shù)據(jù)庫存儲方式,我也首先想到了用數(shù)據(jù)庫存儲,分別考察了一下sqliteberkeleydb,但測試下來制造數(shù)據(jù)的速度很慢,sqlite加索引大概只能到5w條記錄/s,不加索引為10w/sberkeleydb用單條模式大概只能到4.5w/s,這個(gè)速度已經(jīng)很慢了,更難于接受的是如果寫1000wsqlite加索引來說不是耗時(shí)200s,而是2000s了,也就是說耗時(shí)隨單個(gè)數(shù)據(jù)文件記錄的條數(shù)增多幾乎成平方模式遞增,而不是簡單的線性遞增,這是很要命的,就算制造1億條數(shù)據(jù)耗時(shí)也是驚人,我的實(shí)測中沒有測試過用sqlite制造1000w條以上的數(shù)據(jù),在我心目中已經(jīng)否定了那種模式。雖然我知道很多號稱有多少億條數(shù)據(jù)的網(wǎng)站其實(shí)都是用的數(shù)據(jù)庫,我不知道他們花了多少時(shí)間制造數(shù)據(jù),或者幾天,或者幾個(gè)月,或者更長時(shí)間,反正我對采用普通數(shù)據(jù)庫模式制造數(shù)據(jù)完全持否定態(tài)度,嵌入式速度太慢,其他數(shù)據(jù)庫則不光速度慢而且也不適合分布式應(yīng)用,難道用戶每裝個(gè)點(diǎn)還要裝個(gè)mysql之類的數(shù)據(jù)庫,幾乎不可能啊。

            下面說說我的方法,我本來第一版本是計(jì)劃先不做文件式數(shù)據(jù)庫的,第一版本來只規(guī)劃了做內(nèi)存數(shù)據(jù),充分榨取每一個(gè)字節(jié),關(guān)于內(nèi)存數(shù)據(jù)庫我實(shí)現(xiàn)了好幾個(gè)版本,下面分別介紹一下:

            版本1hash模式

            char key[16];做鍵,char pass[n];做內(nèi)容,由于hash桶占用了一些字節(jié):

                            DWORD h, nKeyLen;               //hash鍵值, 字符串長度

                            DWORD tag;                           //私有值,默認(rèn)為0提供給外部使用

                            bucket *pListNext;           //hash表雙鏈的下一個(gè)節(jié)點(diǎn)

                            bucket *pListPrev;           //hash表雙鏈的上一個(gè)節(jié)點(diǎn)

                            bucket *pNext;                        //拉鏈的下一個(gè)節(jié)點(diǎn)

                            VALUE second;                        //具體數(shù)據(jù)

                            _Elem first[0];                 //first

            用這個(gè)hash模式大概存儲一個(gè)6個(gè)字符的串的md5信息花了50個(gè)字節(jié),花費(fèi)太多,結(jié)果自然存不了多少數(shù)據(jù),該方案作為第一驗(yàn)證方案,除了花費(fèi)內(nèi)存太多還是個(gè)能通過的方案。

             

            版本2hash簡化方案

            在上述版本基礎(chǔ)上簡化桶設(shè)計(jì),拋棄作為標(biāo)準(zhǔn)桶的一些字段,精簡之后如下:

                            DWORD h;                               //hash鍵值

                            bucket *pNext;                        //拉鏈的下一個(gè)節(jié)點(diǎn)

                            byte nKeyLen;                  //字符串長度

                            VALUE second;                        //具體數(shù)據(jù)

                            _Elem first[0];                 //first

            該版本存儲一個(gè)6個(gè)字符的串的md5信息需要31個(gè)字節(jié),比版本1少了很多,進(jìn)步一些了。

            方案1和方案2速度都很快。

             

            版本3vector方案

            考慮到hash占用內(nèi)存較多,采用vector方案,直接存儲

            Char mm[16];

            Char pass[n];

            存儲一個(gè)6個(gè)字符的串的md5信息需要22個(gè)字節(jié),該方案排序速度太慢,查找速度肯定也比不上版本1和版本2,之后還測試過將vector里面存儲指針,那種模式每個(gè)6個(gè)字符的串的md5信息占用內(nèi)存26個(gè),接近hash版本,排序速度比直接存儲數(shù)據(jù)的好一點(diǎn),但也還是很慢,總之這個(gè)方案作為一個(gè)過度方案最終也被放棄了。

             

             

            方案4:全文件Hash緊縮方案

            以上這些方案的特點(diǎn)是都存儲了char mm[16]; 也就是說存儲部分都有計(jì)算出來的md5,經(jīng)過思考之后覺得可以放棄存儲md5,不存儲md5是個(gè)很妙的想法,繼續(xù)發(fā)揮hash思想,也不保存根據(jù)md5計(jì)算出來的hash值本身,只將該md5和串的信息關(guān)聯(lián)到hash值的模所在的索引節(jié)點(diǎn),這樣就將索引節(jié)點(diǎn)信息減少到極致:

                    size_t coffset;                  //content offset low

                    unsigned short a:12;       //切分為12, 4

                    unsigned short b:4;         //4,為下一個(gè)沖突值的索引序數(shù),如果沒有就為0

                    size_t nextindex;             //沖突條目的存儲序號,為0表示沒有沖突

             

            使用該索引可讓單文件最多支持內(nèi)容16T,最多687億記錄,具體實(shí)現(xiàn)的時(shí)候由于全使用文件所以速度比較慢,速度退化到sqlite之類同一級別了,不過這個(gè)設(shè)計(jì)思想為方案5提供了借鑒,如果跟方案5一樣用大塊內(nèi)存輔助,速度大概可以上升一個(gè)級別,不過由于沒有具體實(shí)現(xiàn),待研究之后再做評估。

             

            方案5hash緊縮內(nèi)存方案

            學(xué)習(xí)方案4的設(shè)計(jì)思想,考慮僅在內(nèi)存里面實(shí)現(xiàn)一個(gè)緊湊型文件,由于只考慮內(nèi)存可表示的32位范圍,所以簡化索引節(jié)點(diǎn)定義如下:

            Size_t coffset;          pass相對于內(nèi)容區(qū)首的偏移

            Size_t nindex;          沖突節(jié)點(diǎn)下一個(gè)序,如果為0則表示沒有沖突

            內(nèi)容區(qū)存儲更簡單,每個(gè)字符串直接保存,最后的0也保存,這樣每個(gè)字符串自然分開,對一個(gè)6個(gè)字符長的串來說,保存一個(gè)信息只需要15個(gè)字節(jié),真的是省啊,1億個(gè)字符串也只要大約1.5g左右硬盤就夠了。此方案雖然很妙,但實(shí)現(xiàn)的時(shí)候卻費(fèi)了一些周折,具體做的時(shí)候也做過好幾個(gè)版本,由于考慮該方案的內(nèi)容和索引最后都可以直接保存到文件,所以該方案對位置的保存都用的是相對位置,也由于想讓索引節(jié)點(diǎn)信息簡單,最初是讓沖突索引采用線性步長跳躍方法,測試之后發(fā)現(xiàn)這個(gè)方法速度奇慢,而且還有個(gè)非常討厭的問題,隨著數(shù)據(jù)量的增多沖突擴(kuò)散越來越厲害,耗時(shí)非線性的陡峭增長。放棄這個(gè)實(shí)現(xiàn)之后還是回到了經(jīng)典的拉鏈法,拉鏈法速度就是快,但拉鏈法處理索引節(jié)點(diǎn)雖然容易,但要讓索引信息可直接保存卻要花一些腦子,最后采用先用內(nèi)存擴(kuò)展拉鏈,待全部索引構(gòu)造好之后再把拉鏈出來的部分重新填到原始索引區(qū)中的空區(qū),并修正對應(yīng)索引相對位置。這個(gè)方法的精妙之處在于既省空間又有速度,最令人興奮的是采用該方法耗時(shí)隨著數(shù)據(jù)量的增大是線性增長,最后的實(shí)現(xiàn)在我的筆記本上大概100w/s1億條記錄從字母組合到最終生成索引文件也只要不到2分鐘的時(shí)間,制造了一些數(shù)據(jù)之后統(tǒng)計(jì)了一下,沖突節(jié)點(diǎn)比例大概占26%-35%,也就是說有65%以上的數(shù)據(jù)只要一次hash就直接命中,平均拉鏈長度1.2左右,最長拉鏈10,總體還是很滿意的。

             

            原本第一版沒有考慮這個(gè)可存儲的方案,但花了幾天就搞定了一個(gè)基本可用的存儲方案還是很令人興奮的,雖然該存儲方案還有一些問題沒有徹底解決,但已經(jīng)有進(jìn)一步處理的辦法,待下一個(gè)相對空閑時(shí)間段再仔細(xì)研究一下,定會有更簡潔的實(shí)現(xiàn)做出來,至于待解決的是什么問題以及如何解決那些問題還是等我代碼寫好了再寫出來吧。

            posted @ 2010-10-03 14:18 袁斌 閱讀(198) | 評論 (0)編輯 收藏

            僅列出標(biāo)題
            共4頁: 1 2 3 4 
            欧美亚洲日本久久精品| 久久精品人妻中文系列| AV无码久久久久不卡蜜桃| 国内精品人妻无码久久久影院| 久久97精品久久久久久久不卡| 国产精品成人久久久久三级午夜电影| 久久久久久一区国产精品| 久久精品成人欧美大片| 色综合久久综精品| 久久天天婷婷五月俺也去| 久久久一本精品99久久精品88| 99久久99久久精品国产片果冻| 亚洲国产精品嫩草影院久久| 久久99精品久久久久久久久久| 无码任你躁久久久久久| 99久久精品国内| 日产精品久久久久久久性色| 久久国产视频99电影| 久久国产色AV免费看| 久久久久久曰本AV免费免费| 99久久精品免费看国产一区二区三区 | 国产亚洲婷婷香蕉久久精品| 亚洲AⅤ优女AV综合久久久| 久久亚洲精品人成综合网| 亚洲中文字幕伊人久久无码| 99re久久精品国产首页2020| 精品国产青草久久久久福利| 久久se这里只有精品| 99久久国产主播综合精品| 成人免费网站久久久| 精品久久人妻av中文字幕| 久久久久av无码免费网| 久久夜色精品国产噜噜亚洲a| 亚洲国产精品狼友中文久久久 | 国产Av激情久久无码天堂| 99久久99久久精品国产片果冻| 久久中文字幕精品| 亚洲国产成人久久笫一页| 色综合久久夜色精品国产| 久久99热这里只有精品66| 2021最新久久久视精品爱|