• <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>

            loop_in_codes

            低調(diào)做技術(shù)__歡迎移步我的獨(dú)立博客 codemaro.com 微博 kevinlynx

            磁力搜索第二版-dhtcrawler2

            上篇

            下載使用

            目前為止dhtcrawler2相對(duì)dhtcrawler而言,數(shù)據(jù)庫(kù)部分調(diào)整很大,DHT部分基本沿用之前。但單純作為一個(gè)爬資源的程序而言,DHT部分可以進(jìn)行大幅削減,這個(gè)以后再說(shuō)。這個(gè)版本更快、更穩(wěn)定。為了方便,我將編譯好的erlang二進(jìn)制文件作為git的主分支,我還添加了一些Windows下的批處理腳本,總之基本上下載源碼以后即可運(yùn)行。

            項(xiàng)目地址:https://github.com/kevinlynx/dhtcrawler2

            使用方法

            • 下載erlang,我測(cè)試的是R16B版本,確保erl等程序被加入Path環(huán)境變量
            • 下載mongodb,解壓即用:

                mongod --dbpath xxx --setParameter textSearchEnabled=true
              
            • 下載dhtcrawler2

                git clone https://github.com/kevinlynx/dhtcrawler2.git
              
            • 運(yùn)行win_start_crawler.bat

            • 運(yùn)行win_start_hash.bat
            • 運(yùn)行win_start_http.bat
            • 打開(kāi)localhost:8000查看stats

            爬蟲每次運(yùn)行都會(huì)保存DHT節(jié)點(diǎn)狀態(tài),早期運(yùn)行的時(shí)候收集速度會(huì)不夠。dhtcrawler2將程序分為3部分:

            • crawler,即DHT爬蟲部分,僅負(fù)責(zé)收集hash
            • hash,準(zhǔn)確來(lái)講叫hash reader,處理爬蟲收集的hash,處理過(guò)程主要涉及到下載種子文件
            • http,使用hash處理出來(lái)的數(shù)據(jù)庫(kù),以作為Web端接口

            我沒(méi)有服務(wù)器,但程序有被部署在別人的服務(wù)器上:bt.cmhttp://222.175.114.126:8000/

            其他工具

            為了提高資源索引速度,我陸續(xù)寫了一些工具,包括:

            • import_tors,用于導(dǎo)入本地種子文件到數(shù)據(jù)庫(kù)
            • tor_cache,用于下載種子到本地,僅僅提供下載的功能,hash_reader在需要種子文件時(shí),可以先從本地取
            • cache_indexer,目前hash_reader取種子都是從torrage.com之類的種子緩存站點(diǎn)取,這些站點(diǎn)提供了種子列表,cache_indexer將這些列表導(dǎo)入數(shù)據(jù)庫(kù),hash_reader在請(qǐng)求種子文件前可以通過(guò)該數(shù)據(jù)庫(kù)檢查torrage.com上有無(wú)此種子,從而減少多余的http請(qǐng)求

            這些工具的代碼都被放在dhtcrawler2中,可以查看對(duì)應(yīng)的啟動(dòng)腳本來(lái)查看具體如何啟動(dòng)。

            OS/Database

            根據(jù)實(shí)際的測(cè)試效果來(lái)看,當(dāng)收集的資源量過(guò)百萬(wàn)時(shí)(目前bt.cm錄入近160萬(wàn)資源),4G內(nèi)存的Windows平臺(tái),mongodb很容易就會(huì)掛掉。掛掉的原因全是1455,頁(yè)面文件太小。有人建議不要在Windows下使用mongodb,Linux下我自己沒(méi)做過(guò)測(cè)試。

            mongodb可以部署為集群形式(replica-set),當(dāng)初我想把http部分的查詢放在一個(gè)只讀的mongodb實(shí)例上,但因?yàn)榻⒓簳r(shí),要同步已有的10G數(shù)據(jù)庫(kù),而每次同步都以mongodb掛掉結(jié)束,遂放棄。在目前bt.cm的配置中,數(shù)據(jù)庫(kù)torrent的鎖比例(db lock)很容易上50%,這也讓http在搜索時(shí),經(jīng)常出現(xiàn)搜索超時(shí)的情況。

            技術(shù)信息

            dhtcrawler最早的版本有很多問(wèn)題,修復(fù)過(guò)的最大的一個(gè)問(wèn)題是關(guān)于erlang定時(shí)器的,在DHT實(shí)現(xiàn)中,需要對(duì)每個(gè)節(jié)點(diǎn)每個(gè)peer做超時(shí)處理,在erlang中的做法直接是針對(duì)每個(gè)節(jié)點(diǎn)注冊(cè)了一個(gè)定時(shí)器。這不是問(wèn)題,問(wèn)題在于定時(shí)器資源就像沒(méi)有GC的內(nèi)存資源一樣,是會(huì)由于程序員的代碼問(wèn)題而出現(xiàn)資源泄漏。所以,dhtcrawler第一個(gè)版本在節(jié)點(diǎn)數(shù)配置在100以上的情況下,用不了多久就會(huì)內(nèi)存耗盡,最終導(dǎo)致erlang虛擬機(jī)core dump。

            除了這個(gè)問(wèn)題以外,dhtcrawler的資源收錄速度也不是很快。這當(dāng)然跟數(shù)據(jù)庫(kù)和獲取種子的速度有直接關(guān)系。尤其是獲取種子,使用的是一些提供info-hash到種子映射的網(wǎng)站,通過(guò)HTTP請(qǐng)求來(lái)下載種子文件。我以為通過(guò)BT協(xié)議直接下載種子會(huì)快些,并且實(shí)時(shí)性也要高很多,因?yàn)檫@個(gè)種子可能未被這些緩存網(wǎng)站收錄,但卻可以直接向?qū)Ψ秸?qǐng)求得到。為此,我還特地翻閱了相關(guān)協(xié)議,并且用erlang實(shí)現(xiàn)了(以后的文章我會(huì)講到具體實(shí)現(xiàn)這個(gè)協(xié)議)。

            后來(lái)我懷疑get_peers的數(shù)量會(huì)不會(huì)比announce_peer多,但是理論上一般的客戶端在get_peers之后都是announce_peer,但是如果get_peers查詢的peers恰好不在線呢?這意味著很多資源雖然已經(jīng)存在,只不過(guò)你恰好暫時(shí)請(qǐng)求不到。實(shí)際測(cè)試時(shí),發(fā)現(xiàn)get_peers基本是announce_peer數(shù)量的10倍。

            將hash的獲取方式做了調(diào)整后,dhtcrawler在幾分鐘以內(nèi)以幾乎每秒上百個(gè)新增種子的速度工作。然后,程序掛掉。

            從dhtcrawler到今天為止的dhtcrawler2,中間間隔了剛好1個(gè)月。我的所有業(yè)余時(shí)間全部撲在這個(gè)項(xiàng)目上,面臨的問(wèn)題一直都是程序的內(nèi)存泄漏、資源收錄的速度不夠快,到后來(lái)又變?yōu)閿?shù)據(jù)庫(kù)壓力過(guò)大。每一天我都以為我將會(huì)完成一個(gè)穩(wěn)定版本,然后終于可以去干點(diǎn)別的事情,但總是干不完,目前完沒(méi)完都還在觀察。我始終明白在做優(yōu)化前需要進(jìn)行詳盡的數(shù)據(jù)收集和分析,從而真正地優(yōu)化到正確的點(diǎn)上,但也總是憑直覺(jué)和少量數(shù)據(jù)分析就開(kāi)始嘗試。

            這里談?wù)動(dòng)龅降囊恍﹩?wèn)題。

            erlang call timeout

            最開(kāi)始遇到erlang中gen_server:call出現(xiàn)timeout錯(cuò)誤時(shí),我還一直以為是進(jìn)程死鎖了。相關(guān)代碼讀來(lái)讀去,實(shí)在覺(jué)得不可能發(fā)生死鎖。后來(lái)發(fā)現(xiàn),當(dāng)erlang虛擬機(jī)壓力上去后,例如內(nèi)存太大,但沒(méi)大到耗盡系統(tǒng)所有內(nèi)存(耗進(jìn)所有內(nèi)存基本就core dump了),進(jìn)程間的調(diào)用就會(huì)出現(xiàn)timeout。

            當(dāng)然,內(nèi)存占用過(guò)大可能只是表象。其進(jìn)程過(guò)多,進(jìn)程消息隊(duì)列太長(zhǎng),也許才是導(dǎo)致出現(xiàn)timeout的根本原因。消息隊(duì)列過(guò)長(zhǎng),也可能是由于發(fā)生了消息泄漏的緣故。消息泄漏我指的是這樣一種情況,進(jìn)程自己給自己發(fā)消息(當(dāng)然是cast或info),這個(gè)消息被處理時(shí)又會(huì)發(fā)送相同的消息,正常情況下,gen_server處理了一個(gè)該消息,就會(huì)從消息隊(duì)列里移除它,然后再發(fā)送相同的消息,這不會(huì)出問(wèn)題。但是當(dāng)程序邏輯出問(wèn)題,每次處理該消息時(shí),都會(huì)發(fā)生多余一個(gè)的同類消息,那消息隊(duì)列自然就會(huì)一直增長(zhǎng)。

            保持進(jìn)程邏輯簡(jiǎn)單,以避免這種邏輯錯(cuò)誤。

            erlang gb_trees

            我在不少的地方使用了gb_trees,dht_crawler里就可能出現(xiàn)gb_trees:get(xxx, nil)這種錯(cuò)誤。乍一看,我以為我真的傳入了一個(gè)nil值進(jìn)去。然后我苦看代碼,以為在某個(gè)地方我會(huì)把這個(gè)gb_trees對(duì)象改成了nil。但事情不是這樣的,gb_tress使用一個(gè)tuple作為tree的節(jié)點(diǎn),當(dāng)某個(gè)節(jié)點(diǎn)沒(méi)有子節(jié)點(diǎn)時(shí),就會(huì)以nil表示。

            gb_trees:get(xxx, nil)類似的錯(cuò)誤,實(shí)際指的是xxx沒(méi)有在這個(gè)gb_trees中找到。

            erlang httpc

            dht_crawler通過(guò)http協(xié)議從torrage.com之類的緩存網(wǎng)站下載種子。最開(kāi)始我為了盡量少依賴第三方庫(kù),使用的是erlang自帶的httpc。后來(lái)發(fā)現(xiàn)程序有內(nèi)存泄漏,google發(fā)現(xiàn)erlang自帶的httpc早為人詬病,當(dāng)然也有大神說(shuō)在某個(gè)版本之后這個(gè)httpc已經(jīng)很不錯(cuò)。為了省事,我直接換了ibrowse,替換之后正常很多。但是由于沒(méi)有具體分析測(cè)試過(guò),加之時(shí)間有點(diǎn)遠(yuǎn)了,我也記不太清細(xì)節(jié)。因?yàn)樵缙诘膆ttp請(qǐng)求部分,沒(méi)有做數(shù)量限制,也可能是由于我的使用導(dǎo)致的問(wèn)題。

            某個(gè)版本后,我才將http部分嚴(yán)格地與hash處理部分區(qū)分開(kāi)來(lái)。相較數(shù)據(jù)庫(kù)操作而言,http請(qǐng)求部分慢了若干數(shù)量級(jí)。在hash_reader中將這兩塊分開(kāi),嚴(yán)格限制了提交給httpc的請(qǐng)求數(shù),以獲得穩(wěn)定性。

            對(duì)于一個(gè)復(fù)雜的網(wǎng)絡(luò)系統(tǒng)而言,分清哪些是耗時(shí)的哪些是不大耗時(shí)的,才可能獲得性能的提升。對(duì)于hash_reader而言,處理一個(gè)hash的速度,雖然很大程度取決于數(shù)據(jù)庫(kù),但相較http請(qǐng)求,已經(jīng)快很多。它在處理這些hash時(shí),會(huì)將數(shù)據(jù)庫(kù)已收錄的資源和待下載的資源分離開(kāi),以盡快的速度處理已存在的,而將待下載的處理速度交給httpc的響應(yīng)速度。

            erlang httpc ssl

            ibrowse處理https請(qǐng)求時(shí),默認(rèn)和erlang自帶的httpc使用相同的SSL實(shí)現(xiàn)。這經(jīng)常導(dǎo)致出現(xiàn)tls_connection進(jìn)程掛掉的錯(cuò)誤,具體原因不明。

            erlang調(diào)試

            首先合理的日志是任何系統(tǒng)調(diào)試的必備。

            我面臨的大部分問(wèn)題都是內(nèi)存泄漏相關(guān),所以依賴的erlang工具也是和內(nèi)存相關(guān)的:

            • 使用etop,可以檢查內(nèi)存占用多的進(jìn)程、消息隊(duì)列大的進(jìn)程、CPU消耗多的進(jìn)程等等:

                spawn(fun() -> etop:start([{output, text}, {interval, 10}, {lines, 20}, {sort, msg_q }]) end).
              
            • 使用erlang:system_info(allocated_areas).檢查內(nèi)存使用情況,其中會(huì)輸出系統(tǒng)timer數(shù)量

            • 使用erlang:process_info查看某個(gè)具體的進(jìn)程,這個(gè)甚至?xí)敵鱿㈥?duì)列里的消息

            hash_writer/crawler

            crawler本身僅收集hash,然后寫入數(shù)據(jù)庫(kù),所以可以稱crawler為hash_writer。這些hash里存在大量的重復(fù)。hash_reader從數(shù)據(jù)庫(kù)里取出這些hash然后做處理。處理過(guò)程會(huì)首先判定該hash對(duì)應(yīng)的資源是否被收錄,沒(méi)有收錄就先通過(guò)http獲取種子。

            在某個(gè)版本之后,crawler會(huì)簡(jiǎn)單地預(yù)先處理這些hash。它緩存一定數(shù)量的hash,接收到新hash時(shí),就合并到hash緩存里,以保證緩存里沒(méi)有重復(fù)的hash。這個(gè)重復(fù)率經(jīng)過(guò)實(shí)際數(shù)據(jù)分析,大概是50%左右,即收到的100個(gè)請(qǐng)求里,有50個(gè)是重復(fù)的。這樣的優(yōu)化,不僅會(huì)降低hash數(shù)據(jù)庫(kù)的壓力,hash_reader處理的hash數(shù)量少了,也會(huì)對(duì)torrent數(shù)據(jù)庫(kù)有很大提升。

            當(dāng)然進(jìn)一步的方案可以將crawler和hash_reader之間交互的這些hash直接放在內(nèi)存中處理,省去中間數(shù)據(jù)庫(kù)。但是由于mongodb大量使用虛擬內(nèi)存的緣故(內(nèi)存映射文件),經(jīng)常導(dǎo)致服務(wù)器內(nèi)存不夠(4G),內(nèi)存也就成了珍稀資源。當(dāng)然這個(gè)方案還有個(gè)弊端是難以權(quán)衡hash緩存的管理。crawler收到hash是一個(gè)不穩(wěn)定的過(guò)程,在某些時(shí)間點(diǎn)這些hash可能爆多,而hash_reader處理hash的速度也會(huì)不太穩(wěn)定,受限于收到的hash類別(是新增資源還是已存在資源)、種子請(qǐng)求速度、是否有效等。

            當(dāng)然,也可以限制緩存大小,以及對(duì)hash_reader/crawler處理速度建立關(guān)系來(lái)解決這些問(wèn)題。但另一方面,這里的優(yōu)化是否對(duì)目前的系統(tǒng)有提升,是否是目前系統(tǒng)面臨的最大問(wèn)題,卻是需要考究的事情。

            cache indexer

            dht_crawler是從torrage.com等網(wǎng)站獲取種子文件,這些網(wǎng)站看起來(lái)都是使用了相同的接口,其都有一個(gè)sync目錄,里面存放了每天每個(gè)月索引的種子hash,例如 http://torrage.com/sync/。這個(gè)網(wǎng)站上是否有某個(gè)hash對(duì)應(yīng)的種子,就可以從這些索引中檢查。

            hash_reader在處理新資源時(shí),請(qǐng)求種子的過(guò)程中發(fā)現(xiàn)大部分在這些服務(wù)器上都沒(méi)有找到,也就是發(fā)起的很多http請(qǐng)求都是404回應(yīng),這不但降低了系統(tǒng)的處理能力、帶寬,也降低了索引速度。所以我寫了一個(gè)工具,先手工將sync目錄下的所有文件下載到本地,然后通過(guò)這個(gè)工具 (cache indexer) 將這些索引文件里的hash全部導(dǎo)入數(shù)據(jù)庫(kù)。在以后的運(yùn)行過(guò)程中,該工具僅下載當(dāng)天的索引文件,以更新數(shù)據(jù)庫(kù)。 hash_reader 根據(jù)配置,會(huì)首先檢查某個(gè)hash是否存在該數(shù)據(jù)庫(kù)中,存在的hash才可能在torrage.com上下載得到。

            種子緩存

            hash_reader可以通過(guò)配置,將下載得到的種子保存在本地文件系統(tǒng)或數(shù)據(jù)庫(kù)中。這可以建立自己的種子緩存,但保存在數(shù)據(jù)庫(kù)中會(huì)對(duì)數(shù)據(jù)庫(kù)造成壓力,尤其在當(dāng)前測(cè)試服務(wù)器硬件環(huán)境下;而保存為本地文件,又特別占用硬盤空間。

            基于BT協(xié)議的種子下載

            通過(guò)http從種子緩存里取種子文件,可能會(huì)沒(méi)有直接從P2P網(wǎng)絡(luò)里取更實(shí)時(shí)。目前還沒(méi)來(lái)得及查看這些種子緩存網(wǎng)站的實(shí)現(xiàn)原理。但是通過(guò)BT協(xié)議獲取種子會(huì)有點(diǎn)麻煩,因?yàn)閐ht_crawler是根據(jù)get_peer請(qǐng)求索引資源的,所以如果要通過(guò)BT協(xié)議取種子,那么這里還得去DHT網(wǎng)絡(luò)里查詢?cè)摲N子,這個(gè)查詢過(guò)程可能會(huì)較長(zhǎng),相比之下會(huì)沒(méi)有http下載快。而如果通過(guò)announce_peer來(lái)索引新資源的話,其索引速度會(huì)大大降低,因?yàn)?code>announce_peer請(qǐng)求比get_peer請(qǐng)求少很多,幾乎10倍。

            所以,這里的方案可能會(huì)結(jié)合兩者,新開(kāi)一個(gè)服務(wù),建立自己的種子緩存。

            中文分詞

            mongodb的全文索引是不支持中文的。我在之前提到,為了支持搜索中文,我將字符串拆成了若干子串。這樣的后果就是字符串索引會(huì)稍稍偏大,而且目前這一塊的代碼還特別簡(jiǎn)單,會(huì)將很多非文字字符也算在內(nèi)。后來(lái)我加了個(gè)中文分詞庫(kù),使用的是rmmseg-cpp。我將其C++部分抽離出來(lái)編譯成erlang nif,這可以在我的github上找到。

            但是這個(gè)庫(kù)拆分中文句子依賴于詞庫(kù),而這個(gè)詞庫(kù)不太新,dhtcrawler爬到的大部分資源類型你們也懂,那些詞匯拆出來(lái)的比率不太高,這會(huì)導(dǎo)致搜索出來(lái)的結(jié)果沒(méi)你想的那么直白。當(dāng)然更新詞庫(kù)應(yīng)該是可以解決這個(gè)問(wèn)題的,目前還沒(méi)有時(shí)間顧這一塊。

            總結(jié)

            一個(gè)老外對(duì)我說(shuō)過(guò),”i have 2 children to feed, so i will not do this only for fun”。

            你的大部分編程知識(shí)來(lái)源于網(wǎng)絡(luò),所以稍稍回饋一下不會(huì)讓你丟了飯碗。

            我很窮,如果你能讓我收獲金錢和編程成就,還不會(huì)嫌我穿得太邋遢,that’s really kind of you。

            posted on 2013-07-20 16:37 Kevin Lynx 閱讀(5701) 評(píng)論(1)  編輯 收藏 引用 所屬分類: networkerlang

            評(píng)論

            # re: 磁力搜索第二版-dhtcrawler2 2014-04-25 12:34 vvke

            在WIN上面,一搜索就跪。。。500錯(cuò)誤。。。  回復(fù)  更多評(píng)論   

            欧美激情精品久久久久久久九九九 | A狠狠久久蜜臀婷色中文网| 久久精品aⅴ无码中文字字幕不卡 久久精品成人欧美大片 | 久久国产午夜精品一区二区三区| 很黄很污的网站久久mimi色| 久久久国产99久久国产一| 久久综合88熟人妻| 国产综合成人久久大片91| 久久人人爽爽爽人久久久| 国产精品成人99久久久久91gav| 香蕉久久夜色精品升级完成| 国产精品亚洲综合专区片高清久久久 | 久久青青草原国产精品免费| 欧美精品国产综合久久| 九九热久久免费视频| 久久精品国产亚洲av影院| 久久五月精品中文字幕| 国产精品欧美久久久天天影视| 中文字幕精品久久| 久久99久久成人免费播放| 久久66热人妻偷产精品9| 一本大道久久香蕉成人网| 精品多毛少妇人妻AV免费久久| 日韩精品久久久肉伦网站| 久久中文字幕人妻丝袜| 日本高清无卡码一区二区久久| 久久无码av三级| 国产成人精品久久免费动漫| 日产精品99久久久久久| 免费精品久久天干天干| 香蕉久久夜色精品国产2020| 人人狠狠综合久久亚洲| 欧美成人免费观看久久| 中文字幕精品久久久久人妻| 国产一区二区久久久| 久久婷婷五月综合97色直播| 伊人久久大香线蕉综合热线| 性做久久久久久久久老女人| 一本一本久久a久久精品综合麻豆| 麻豆久久| 精品久久人人爽天天玩人人妻|