蘇州培訓(xùn)網(wǎng) > 蘇州web前端培訓(xùn)機(jī)構(gòu) > 蘇州其然軟件開發(fā)培訓(xùn)
首頁 培訓(xùn)網(wǎng) 最新資訊 熱門問答

蘇州其然軟件開發(fā)培訓(xùn)

免費(fèi)試聽

您當(dāng)前的位置: 蘇州軟件系統(tǒng)培訓(xùn) > 蘇州web前端培訓(xùn) > 蘇州java程序員業(yè)余班

蘇州java程序員業(yè)余班_JAVA培訓(xùn)

¥詳詢

班制:周末班

蘇州其然軟件開發(fā)
上課(咨詢)地址:蘇州市昆山市震川西路111號名仕大廈
報(bào)名咨詢 預(yù)約試聽
課程介紹
蘇州java程序員業(yè)余班
其然IT 教育師資

韓奇峰高級講師

多年實(shí)戰(zhàn)工作經(jīng)驗(yàn)曾參與制作寶馬Usage Training項(xiàng)目、DMS項(xiàng)目,奧迪全 息投影項(xiàng)目,奔馳等多家汽車門戶行業(yè)大型項(xiàng)目,負(fù)責(zé)UI設(shè)計(jì)、界面設(shè)計(jì)、3D模型制作、前端開發(fā)等職務(wù)。

從事設(shè)計(jì)行業(yè)多年,精通PhotoShop、UI設(shè)計(jì)、AfterEffects、Flash、 Actionscript、HTML、CSS、JavaScript、jQuery、資深動(dòng)畫設(shè)計(jì)師,設(shè)計(jì)作品曾獲得全國動(dòng)畫設(shè)計(jì)三等獎(jiǎng)。

課程講解注重實(shí)戰(zhàn)應(yīng)用,對講述知識點(diǎn)穿插案例制作,使課程內(nèi)容更加接近 工作中實(shí)際的項(xiàng)目。授課風(fēng)格注重實(shí)戰(zhàn)經(jīng)驗(yàn)分析,深受學(xué)生喜歡。

蘇州java程序員業(yè)余班

自學(xué)java與java培訓(xùn)該如何選擇

蘇州java程序員業(yè)余班

從事IT培訓(xùn)行業(yè)這些年,身邊經(jīng)常有朋友來咨詢,問現(xiàn)在是否可以加入這個(gè)高薪行業(yè),Java學(xué)習(xí)難不難,需要哪些條件?

在軟件開發(fā)設(shè)計(jì)行業(yè)當(dāng)中,使用**多的編程語言就屬Java軟件開發(fā)了,很多人都想在Java編程開發(fā)領(lǐng)域?qū)で笠环莨ぷ鳌?/span>

Java是當(dāng)前世界上比較流行的計(jì)算機(jī)編程語言,Java徹底改變了人們的生活,國內(nèi)的開發(fā)人才需求量仍然在不斷的增長,主要原因還是在于移動(dòng)互聯(lián)網(wǎng)的快速發(fā)展,衍生出一大批的新生企業(yè),創(chuàng)業(yè)公司,預(yù)計(jì)在未來5年內(nèi)Java軟件開發(fā)人才的需求將會(huì)遠(yuǎn)大于供給,其中java程序員、java工程師**為缺乏。

自學(xué)java與java培訓(xùn)是兩個(gè)繞不開的話題,兩者的區(qū)別,前者知識是自己搜尋的,買書看或者網(wǎng)上找視頻看,而后者,技術(shù)知識是老師教的,作為一個(gè)普通大學(xué)生實(shí)際上任何人都能夠自學(xué),只不過聰明程度和堅(jiān)持的時(shí)間長短不同,花費(fèi)的時(shí)間長一點(diǎn)短一點(diǎn)而已。

學(xué)習(xí)關(guān)乎到時(shí)間和金錢,沒有效果和質(zhì)量的培訓(xùn),其實(shí)是沒有任何意義的。Java培訓(xùn)班有哪些呢?市面上的Java培訓(xùn)良莠不齊,選擇時(shí)應(yīng)該慎重些。Java致力于打造符合企業(yè)需求的全能型人才。課程定位定位中高級JAVA開發(fā)工程師,課程內(nèi)容涉及面廣,內(nèi)容深。課程涵蓋了Java、Web,數(shù)據(jù)庫,企業(yè)流行框架,Java等企業(yè)**佳實(shí)踐、云計(jì)算大數(shù)據(jù)課程、內(nèi)容由淺入深,剖析原理,讓學(xué)員掌握實(shí)用技術(shù),做企業(yè)和社會(huì)需要的稀缺人才。

此外,Java培訓(xùn)將為你提供就業(yè)保障,開設(shè)有就業(yè)指導(dǎo)課,設(shè)有專門的就業(yè)指導(dǎo)老師,在畢業(yè)前期,畢業(yè)之際,就業(yè)老師會(huì)為你做專門的就業(yè)指導(dǎo),更有職業(yè)素養(yǎng)課程,不僅將你打造成Java開發(fā)技術(shù)達(dá)人,更是一位懂得職場之道的合格就業(yè)者。還有豐富的Java學(xué)習(xí)視頻免費(fèi)為你提供學(xué)習(xí)。

Java開發(fā)體系結(jié)構(gòu)介紹

蘇州java程序員業(yè)余班

Java開發(fā)體系結(jié)構(gòu)介紹

1、類加載器:為程序的執(zhí)行加載所需要的全部類。類加載器將本地文件系 統(tǒng)的類名空間與來自遠(yuǎn)程網(wǎng)絡(luò)源的類名空間相分離,本地類總是首先被加載,以增加安全性。當(dāng)全部類被加載后,可執(zhí)行文件的存儲(chǔ)器格式被確定。這 時(shí),特定的存儲(chǔ)器地址被分配給符號引用并創(chuàng)建檢索表格。由于存儲(chǔ)器格式在運(yùn)行時(shí)出現(xiàn),因而Java解釋器增加了保護(hù)以防止對限制代碼區(qū)的非法進(jìn)入 。

2、字節(jié)代碼校驗(yàn)器:基于代碼的規(guī)范包括語法語義的檢查以及如上所述的 安全性檢查。

3、Java運(yùn)行時(shí)解釋器:它是JVM的核心內(nèi)容,實(shí)現(xiàn)把抽象的字節(jié)碼指令映射 到本地系統(tǒng)平臺(tái)下的庫引用或指令。

4、API類庫:實(shí)現(xiàn)標(biāo)準(zhǔn)Java平臺(tái)API的一系列可執(zhí)行代碼。

5、硬件本地平臺(tái)接口:提供對底層系統(tǒng)平臺(tái)資源庫調(diào)用的接口。

memcached源碼分析-----item過期失效處理以及LRU爬蟲


>

轉(zhuǎn)載:http://blog.csdn.net/luotuo44/article/details/42963793


   溫馨提示:本文用到了一些可以在啟動(dòng)memcached設(shè)置的全局變量。關(guān)于這些全局變量的含義可以參考《memcached啟動(dòng)參數(shù)詳解》。對于這些全局變量,處理方式就像《如何閱讀memcached源代碼》所說的那樣直接取其默認(rèn)值。另外, 本文會(huì)提及LRU隊(duì)列,關(guān)于LRU隊(duì)列的介紹可以參考《LRU隊(duì)列與item結(jié)構(gòu)體》。


過期失效處理:

        一個(gè)item在兩種情況下會(huì)過期失效:1.item的exptime時(shí)間戳到了。2.用戶使用flush_all命令將全部item變成過期失效的。讀者可能會(huì)說touch命令也可以使得一個(gè)item過期失效,其實(shí)這也屬于前面說的**種情況。


超時(shí)失效:

        對于**種過期失效,memcached的使用懶惰處理:不主動(dòng)檢測一個(gè)item是否過期失效。當(dāng)worker線程訪問這個(gè)item時(shí),才檢測這個(gè)item的exptime時(shí)間戳是否到了。比較簡單,這里就先不貼代碼,后面會(huì)貼。


flush_all命令:


        第二種過期失效是用戶flush_all命令設(shè)置的。flush_all會(huì)將所有item都變成過期失效。所有item是指哪些item?因?yàn)槎鄠€(gè)客戶端會(huì)不斷地往memcached插入item,所以必須要明白所有item是指哪些。是以worker線程接收到這個(gè)命令那一刻為界?還是以刪除那一刻為界?

        當(dāng)worker線程接收到flush_all命令后,會(huì)用全局變量settings的oldest_live成員存儲(chǔ)接收到這個(gè)命令那一刻的時(shí)間(準(zhǔn)確地說,是worker線程解析得知這是一個(gè)flush_all命令那一刻再減一),代碼為settings.oldest_live =current_time - 1;然后調(diào)用item_flush_expired函數(shù)鎖上cache_lock,然后調(diào)用do_item_flush_expired函數(shù)完成工作。

[cpp] view plain copy   void do_item_flush_expired(void) {       int i;       item *iter, *next;       if (settings.oldest_live == 0)           return;       for (i = 0; i < LARGEST_ID; i ) {           for (iter = heads[i]; iter != NULL; iter = next) {               if (iter->time != 0 && iter->time >= settings.oldest_live) {                   next = iter->next;                   if ((iter->it_flags & ITEM_SLABBED) == 0) {                       do_item_unlink_nolock(iter, hash(ITEM_key(iter), iter->nkey));                   }               } else {                   /* We ve hit the first old item. Continue to the next queue. */                   break;               }           }       }   }           do_item_flush_expired函數(shù)內(nèi)部會(huì)遍歷所有LRU隊(duì)列,檢測每一個(gè)item的time成員。檢測time成員是合理的。如果time成員小于settings.oldest_live就說明該item在worker線程接收到flush_all命令的時(shí)候就已經(jīng)存在了(time成員表示該item的**后一次訪問時(shí)間)。那么就該刪除這個(gè)item。


        這樣看來memcached是以worker線程接收到flush_all命令那一刻為界的。等等等等,看清楚一點(diǎn)??!在do_item_flush_expired函數(shù)里面,不是當(dāng)item的time成員小于settings.oldest_live時(shí)刪除這個(gè)item,而是大于的時(shí)候才刪除。從time成員變量的意義來說,大于代表什么啊?有大于的嗎?奇怪!@#@&¥


        實(shí)際上memcached是以刪除那一刻為界的。那settings.oldest_live為什么要存儲(chǔ)worker線程接收到flush_all命令的時(shí)間戳?為什么又要判斷iter->time是否大于settings.oldest_live呢?

        按照一般的做法,在do_item_flush_expired函數(shù)中直接把哈希表和LRU上的所有item統(tǒng)統(tǒng)刪除即可。這樣確實(shí)是可以達(dá)到目標(biāo)。但在本worker線程處理期間,其他worker線程完全不能工作(因?yàn)閐o_item_flush_expired的調(diào)用者已經(jīng)鎖上了cache_lock)。而LRU隊(duì)列里面可能有大量的數(shù)據(jù),這個(gè)過期處理過程可能會(huì)很長。其他worker線程完全不能工作是難于接受的。

        memcached的作者肯定也意識到這個(gè)問題,所以他就寫了一個(gè)奇怪的do_item_flush_expired函數(shù),用來加速。do_item_flush_expired只會(huì)刪除少量特殊的item。如何特殊法,在后面代碼注釋中會(huì)解釋。對于其他大量的item,memcached采用懶惰方式處理。只有當(dāng)worker線程試圖訪問該item,才檢測item是否已經(jīng)被設(shè)置為過期的了。事實(shí)上,無需對item進(jìn)行任何設(shè)置就能檢測該item是否為過期的,**settings.oldest_live變量即可。這種懶惰和前面**種item過期失效的處理是一樣的。


        現(xiàn)在再看一下do_item_flush_expired函數(shù),看一下特殊的item。

[cpp] view plain copy   void do_item_flush_expired(void) {       int i;       item *iter, *next;       if (settings.oldest_live == 0)           return;       for (i = 0; i < LARGEST_ID; i ) {           for (iter = heads[i]; iter != NULL; iter = next) {               //iter->time == 0的是lru爬蟲item,直接忽略               //一般情況下iter->time是小于settings.oldest_live的。但在這種情況下               //就有可能出現(xiàn)iter->time >= settings.oldest_live :  worker1接收到               //flush_all命令,并給settings.oldest_live賦值為current_time-1。               //worker1線程還沒來得及調(diào)用item_flush_expired函數(shù),就被worker2               //搶占了cpu,然后worker2往lru隊(duì)列插入了一個(gè)item。這個(gè)item的time               //成員就會(huì)滿足iter->time >= settings.oldest_live               if (iter->time != 0 && iter->time >= settings.oldest_live) {                   next = iter->next;                   if ((iter->it_flags & ITEM_SLABBED) == 0) {                       //雖然調(diào)用的是nolock,但本函數(shù)的調(diào)用者item_flush_expired                       //已經(jīng)鎖上了cache_lock,才調(diào)用本函數(shù)的                       do_item_unlink_nolock(iter, hash(ITEM_key(iter), iter->nkey));                   }               } else {                   //因?yàn)閘ru隊(duì)列里面的item是根據(jù)time降序排序的,所以當(dāng)存在一個(gè)item的time成員                   //小于settings.oldest_live,剩下的item都不需要再比較了                   break;               }           }       }   }  

懶惰刪除:

        現(xiàn)在閱讀item的懶惰刪除。注意代碼中的注釋。


[cpp] view plain copy   item *do_item_get(const char *key, const size_t nkey, const uint32_t hv) {       item *it = assoc_find(key, nkey, hv);       ...          if (it != NULL) {           //settings.oldest_live初始化值為0           //檢測用戶是否使用過flush_all命令,刪除所有item。           //it->time <= settings.oldest_live就說明用戶在使用flush_all命令的時(shí)候           //就已經(jīng)存在該item了。那么該item是要?jiǎng)h除的。           //flush_all命令可以有參數(shù),用來設(shè)定在未來的某個(gè)時(shí)刻把所有的item都設(shè)置           //為過期失效,此時(shí)settings.oldest_live是一個(gè)比worker接收到flush_all           //命令的那一刻大的時(shí)間,所以要判斷settings.oldest_live <= current_time           if (settings.oldest_live != 0 && settings.oldest_live <= current_time &&               it->time <= settings.oldest_live) {               do_item_unlink(it, hv);               do_item_remove(it);               it = NULL;               } else if (it->exptime != 0 && it->exptime <= current_time) {//該item已經(jīng)過期失效了               do_item_unlink(it, hv);//引用數(shù)會(huì)減一               do_item_remove(it);//引用數(shù)減一,如果引用數(shù)等于0,就刪除               it = NULL;              } else {               it->it_flags |= ITEM_FETCHED;           }       }             return it;   }  


        可以看到,在查找到一個(gè)item后就要檢測它是否過期失效了。失效了就要?jiǎng)h除之。

        除了do_item_get函數(shù)外,do_item_alloc函數(shù)也是會(huì)處理過期失效item的。do_item_alloc函數(shù)不是刪除這個(gè)過期失效item,而是占為己用。因?yàn)檫@個(gè)函數(shù)的功能是申請一個(gè)item,如果一個(gè)item過期了那么就直接霸占這個(gè)item的那塊內(nèi)存。下面看一下代碼。


[cpp] view plain copy   item *do_item_alloc(char *key, const size_t nkey, const int flags,                       const rel_time_t exptime, const int nbytes,                       const uint32_t cur_hv) {       uint8_t nsuffix;       item *it = NULL;       char suffix[40];       //要存儲(chǔ)這個(gè)item需要的總空間       size_t ntotal = item_make_header(nkey   1, flags, nbytes, suffix, &nsuffix);       if (settings.use_cas) {           ntotal  = sizeof(uint64_t);       }          //根據(jù)大小判斷從屬于哪個(gè)slab       unsigned int id = slabs_clsid(ntotal);          /* do a quick check if we have any expired items in the tail.. */       int tries = 5;       item *search;       item *next_it;       rel_time_t oldest_live = settings.oldest_live;          search = tails[id];       for (; tries > 0 && search != NULL; tries--, search=next_it) {           next_it = search->PRev;           ...              if (refcount_incr(&search->refcount) != 2) {//引用數(shù),還有其他線程在引用,不能霸占這個(gè)item               //刷新這個(gè)item的訪問時(shí)間以及在LRU隊(duì)列中的位置               do_item_update_nolock(search);               tries ;               refcount_decr(&search->refcount);               //此時(shí)引用數(shù)>=2                      continue;           }              //search指向的item的refcount等于2,這說明此時(shí)這個(gè)item除了本worker           //線程外,沒有其他任何worker線程索引其??梢苑判尼尫挪⒅赜眠@個(gè)item                       //因?yàn)檫@個(gè)循環(huán)是從lru鏈表的后面開始遍歷的。所以一開始search就指向            //了**不常用的item,如果這個(gè)item都沒有過期。那么其他的比其更常用           //的item就不要?jiǎng)h除了(即使它們過期了)。此時(shí)只能向slabs申請內(nèi)存           if ((search->exptime != 0 && search->exptime < current_time)               || (search->time <= oldest_live && oldest_live <= current_time)) {               //search指向的item是一個(gè)過期失效的item,可以使用之               it = search;               //重新計(jì)算一下這個(gè)slabclass_t分配出去的內(nèi)存大小               //直接霸占舊的item就需要重新計(jì)算               slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);               do_item_unlink_nolock(it, hv);//從哈希表和lru鏈表中刪除               /* Initialize the item block: */               it->slabs_clsid = 0;           }                         //引用計(jì)數(shù)減一。此時(shí)該item已經(jīng)沒有任何worker線程索引其,并且哈希表也           //不再索引其           refcount_decr(&search->refcount);           break;       }          ...       return it;   }         //新的item直接霸占舊的item就會(huì)調(diào)用這個(gè)函數(shù)   void slabs_adjust_mem_requested(unsigned int id, size_t old, size_t ntotal)   {       pthread_mutex_lock(&slabs_lock);       slabclass_t *p;       if (id < POWER_SMALLEST || id > power_largest) {           fprintf(stderr, "Internal error! Invalid slab class\n");           abort();       }          p = &slabclass[id];       //重新計(jì)算一下這個(gè)slabclass_t分配出去的內(nèi)存大小       p->requested = p->requested - old   ntotal;       pthread_mutex_unlock(&slabs_lock);   }  


        flush_all命令是可以有時(shí)間參數(shù)的。這個(gè)時(shí)間和其他時(shí)間一樣取值范圍是 1到REALTIME_MAXDELTA(30天)。如果命令為flush_all 100,那么99秒后所有的item失效。此時(shí)settings.oldest_live的值為current_time 100-1,do_item_flush_expired函數(shù)也沒有什么用了(總不會(huì)被搶占CPU99秒吧)。也正是這個(gè)原因,需要在do_item_get里面,加入settings.oldest_live<= current_time這個(gè)判斷,防止過早刪除了item。

        這里明顯有一個(gè)bug。假如客戶端A向服務(wù)器提交了flush_all10命令。過了5秒后,客戶端B向服務(wù)器提交命令flush_all100。那么客戶端A的命令將失效,沒有起到任何作用。


LRU爬蟲:

        前面說到,memcached是懶惰刪除過期失效item的。所以即使用戶在客戶端使用了flush_all命令使得全部item都過期失效了,但這些item還是占據(jù)者哈希表和LRU隊(duì)列并沒有歸還給slab分配器。


LRU爬蟲線程:


        有沒有辦法強(qiáng)制清除這些過期失效的item,不再占據(jù)哈希表和LRU隊(duì)列的空間并歸還給slabs呢?當(dāng)然是有的。memcached提供了LRU爬蟲可以實(shí)現(xiàn)這個(gè)功能。

        要使用LRU爬蟲就必須在客戶端使用lru_crawler命令。memcached服務(wù)器根據(jù)具體的命令參數(shù)進(jìn)行處理。

        memcached是用一個(gè)專門的線程負(fù)責(zé)清除這些過期失效item的,本文將稱這個(gè)線程為LRU爬蟲線程。默認(rèn)情況下memcached是不啟動(dòng)這個(gè)線程的,但可以在啟動(dòng)memcached的時(shí)候添加參數(shù)-o lru_crawler啟動(dòng)這個(gè)線程。也可以**客戶端命令啟動(dòng)。即使啟動(dòng)了這個(gè)LRU爬蟲線程,該線程還是不會(huì)工作。需要另外發(fā)送命令,指明要對哪個(gè)LRU隊(duì)列進(jìn)行清除處理?,F(xiàn)在看一下lru_crawler有哪些參數(shù)。


LRU爬蟲命令:



lru_crawler  <enable|disable>  啟動(dòng)或者停止一個(gè)LRU爬蟲線程。任何時(shí)刻,**多只有一個(gè)LRU爬蟲線程。該命令對settings.lru_crawler進(jìn)行賦值為true或者false lru_crawler crawl <classid,classid,classid|all>  可以使用2,3,6這樣的列表指明要對哪個(gè)LRU隊(duì)列進(jìn)行清除處理。也可以使用all對所有的LRU隊(duì)列進(jìn)行處理 lru_crawler sleep <microseconds>  LRU爬蟲線程在清除item的時(shí)候會(huì)占用鎖,會(huì)妨礙worker線程的正常業(yè)務(wù)。所以LRU爬蟲在處理的時(shí)候需要時(shí)不時(shí)休眠一下。默認(rèn)休眠時(shí)間為100微秒。該命令對settings.lru_crawler_sleep進(jìn)行賦值 lru_crawler tocrawl <32u>  一個(gè)LRU隊(duì)列可能會(huì)有很多過期失效的item。如果一直檢查和清除下去,勢必會(huì)妨礙worker線程的正常業(yè)務(wù)。這個(gè)參數(shù)用來指明**多只檢查每一條LRU隊(duì)列的多少個(gè)item。默認(rèn)值為0,所以如果不指定那么就不會(huì)工作。該命令對settings.lru_crawler_tocrawl進(jìn)行賦值



        如果要啟動(dòng)LRU爬蟲主動(dòng)刪除過期的item,需要這樣做:首先使用lru_crawler enable命令啟動(dòng)一個(gè)LRU爬蟲線程。然后使用lru_crawler tocrawl num命令確定每一個(gè)LRU隊(duì)列**多檢查num-1個(gè)item。**后使用命令lru_crawler crawl <classid,classid,classid|all> 指定要處理的LRU隊(duì)列。lru_crawler sleep可以不設(shè)置,如果要設(shè)置那么可以在lru_crawler crawl命令之前設(shè)置即可。

啟動(dòng)LRU爬蟲線程:

        現(xiàn)在來看一下LRU爬蟲是怎么工作的。先來看一下memcached為LRU爬蟲定義了哪些全局變量。


[cpp] view plain copy   static volatile int do_run_lru_crawler_thread = 0;   static int lru_crawler_initialized = 0;   static pthread_mutex_t lru_crawler_lock = PTHREAD_MUTEX_INITIALIZER;   static pthread_cond_t  lru_crawler_cond = PTHREAD_COND_INITIALIZER;         int init_lru_crawler(void) {//main函數(shù)會(huì)調(diào)用該函數(shù)       if (lru_crawler_initialized == 0) {           if (pthread_cond_init(&lru_crawler_cond, NULL) != 0) {               fprintf(stderr, "Can t initialize lru crawler condition\n");               return -1;           }           pthread_mutex_init(&lru_crawler_lock, NULL);           lru_crawler_initialized = 1;       }       return 0;   }           代碼比較簡單,這里就不說了。下面看一下lru_crawler enable和disable命令。enable命令會(huì)啟動(dòng)一個(gè)LRU爬蟲線程,而disable會(huì)停止這個(gè)LRU爬蟲線程,當(dāng)然不是直接調(diào)用pthread_exit停止線程。pthread_exit函數(shù)是一個(gè)危險(xiǎn)函數(shù),不應(yīng)該在代碼出現(xiàn)。



[cpp] view plain copy   static pthread_t item_crawler_tid;      //worker線程接收到"lru_crawler enable"命令后會(huì)調(diào)用本函數(shù)   //啟動(dòng)memcached時(shí)如果有-o lru_crawler參數(shù)也是會(huì)調(diào)用本函數(shù)   int start_item_crawler_thread(void) {       int ret;          //在stop_item_crawler_thread函數(shù)可以看到pthread_join函數(shù)       //在pthread_join返回后,才會(huì)把settings.lru_crawler設(shè)置為false。       //所以不會(huì)出現(xiàn)同時(shí)出現(xiàn)兩個(gè)crawler線程       if (settings.lru_crawler)           return -1;              pthread_mutex_lock(&lru_crawler_lock);       do_run_lru_crawler_thread = 1;       settings.lru_crawler = true;       //創(chuàng)建一個(gè)LRU爬蟲線程,線程函數(shù)為item_crawler_thread。LRU爬蟲線程在進(jìn)入       //item_crawler_thread函數(shù)后,會(huì)調(diào)用pthread_cond_wait,等待worker線程指定       //要處理的LRU隊(duì)列       if ((ret = pthread_create(&item_crawler_tid, NULL,           item_crawler_thread, NULL)) != 0) {           fprintf(stderr, "Can t create LRU crawler thread: %s\n",               strerror(ret));           pthread_mutex_unlock(&lru_crawler_lock);           return -1;       }       pthread_mutex_unlock(&lru_crawler_lock);          return 0;   }         //worker線程在接收到"lru_crawler disable"命令會(huì)執(zhí)行這個(gè)函數(shù)   int stop_item_crawler_thread(void) {       int ret;       pthread_mutex_lock(&lru_crawler_lock);       do_run_lru_crawler_thread = 0;//停止LRU線程       //LRU爬蟲線程可能休眠于等待條件變量,需要喚醒才能停止LRU爬蟲線程       pthread_cond_signal(&lru_crawler_cond);       pthread_mutex_unlock(&lru_crawler_lock);       if ((ret = pthread_join(item_crawler_tid, NULL)) != 0) {           fprintf(stderr, "Failed to stop LRU crawler thread: %s\n", strerror(ret));           return -1;       }       settings.lru_crawler = false;       return 0;   }  


        可以看到worker線程在接收到” lru_crawler enable”命令后會(huì)啟動(dòng)一個(gè)LRU爬蟲線程。這個(gè)LRU爬蟲線程還沒去執(zhí)行任務(wù),因?yàn)檫€沒有指定任務(wù)。命令"lru_crawler tocrawl num"并不是啟動(dòng)一個(gè)任務(wù)。對于這個(gè)命令,worker線程只是簡單地把settings.lru_crawler_tocrawl賦值為num。

清除失效item:


        命令”lru_crawler crawl<classid,classid,classid|all>”才是指定任務(wù)的。該命令指明了要對哪個(gè)LRU隊(duì)列進(jìn)行清理。如果使用all那么就是對所有的LRU隊(duì)列進(jìn)行清理。

        在看memcached的清理代碼之前,先考慮一個(gè)問題:怎么對一條LRU隊(duì)列進(jìn)行清理?

        **直觀的做法是先加鎖(鎖上cache_lock),然后遍歷一整條LRU隊(duì)列。直接判斷LRU隊(duì)列里面的每一個(gè)item即可。明顯這種方法有問題。如果memcached有大量的item,那么遍歷一個(gè)LRU隊(duì)列耗時(shí)將太久。這樣會(huì)妨礙worker線程的正常業(yè)務(wù)。當(dāng)然我們可以考慮使用分而治之的方法,每次只處理幾個(gè)item,多次進(jìn)行,**終達(dá)到處理整個(gè)LRU隊(duì)列的目標(biāo)。但LRU隊(duì)列是一個(gè)鏈表,不支持隨機(jī)訪問。處理隊(duì)列中間的某個(gè)item,需要從鏈表頭或者尾依次訪問,時(shí)間復(fù)雜度還是O(n)。

偽item:


        memcached為了實(shí)現(xiàn)隨機(jī)訪問,使用了一個(gè)很巧妙的方法。它在LRU隊(duì)列尾部插入一個(gè)偽item,然后驅(qū)動(dòng)這個(gè)偽item向隊(duì)列頭部前進(jìn),每次前進(jìn)一位。

        這個(gè)偽item是全局變量,LRU爬蟲線程無須從LRU隊(duì)列頭部或者尾部遍歷就可以直接訪問這個(gè)偽item。**這個(gè)偽item的next和prev指針,就可以訪問真正的item。于是,LRU爬蟲線程無需遍歷就可以直接訪問LRU隊(duì)列中間的某一個(gè)item。

        下面看一下lru_crawler_crawl函數(shù),memcached會(huì)在這個(gè)函數(shù)會(huì)把偽item插入到LRU隊(duì)列尾部的。當(dāng)worker線程接收到lru_crawler crawl<classid,classid,classid|all>命令時(shí)就會(huì)調(diào)用這個(gè)函數(shù)。因?yàn)橛脩艨赡芤驦RU爬蟲線程清理多個(gè)LRU隊(duì)列的過期失效item,所以需要一個(gè)偽item數(shù)組。偽item數(shù)組的大小等于LRU隊(duì)列的個(gè)數(shù),它們是一一對應(yīng)的。

[cpp] view plain copy   //這個(gè)結(jié)構(gòu)體和item結(jié)構(gòu)體長得很像,是偽item結(jié)構(gòu)體,用于LRU爬蟲   typedef struct {       struct _stritem *next;       struct _stritem *prev;       struct _stritem *h_next;    /* hash chain next */       rel_time_t      time;       /* least recent access */       rel_time_t      exptime;    /* expire time */       int             nbytes;     /* size of data */       unsigned short  refcount;       uint8_t         nsuffix;    /* length of flags-and-length string */       uint8_t         it_flags;   /* ITEM_* above */       uint8_t         slabs_clsid;/* which slab class we re in */       uint8_t         nkey;       /* key length, w/terminating null and padding */       uint32_t        remaining;  /* Max keys to crawl per slab per invocation */   } crawler;            static crawler crawlers[LARGEST_ID];   static int crawler_count = 0;//本次任務(wù)要處理多少個(gè)LRU隊(duì)列         //當(dāng)客戶端使用命令lru_crawler crawl <classid,classid,classid|all>時(shí),   //worker線程就會(huì)調(diào)用本函數(shù),并將命令的第二個(gè)參數(shù)作為本函數(shù)的參數(shù)   enum crawler_result_type lru_crawler_crawl(char *slabs) {       char *b = NULL;       uint32_t sid = 0;       uint8_t tocrawl[POWER_LARGEST];          //LRU爬蟲線程進(jìn)行清理的時(shí)候,會(huì)鎖上lru_crawler_lock。直到完成所有       //的清理任務(wù)才會(huì)解鎖。所以客戶端的前一個(gè)清理任務(wù)還沒結(jié)束前,不能       //再提交另外一個(gè)清理任務(wù)          if (pthread_mutex_trylock(&lru_crawler_lock) != 0) {           return CRAWLER_RUNNING;       }       pthread_mutex_lock(&cache_lock);          //解析命令,如果命令要求對某一個(gè)LRU隊(duì)列進(jìn)行清理,那么就在tocrawl數(shù)組       //對應(yīng)元素賦值1作為標(biāo)志       if (strcmp(slabs, "all") == 0) {//處理全部lru隊(duì)列           for (sid = 0; sid < LARGEST_ID; sid ) {               tocrawl[sid] = 1;           }       } else {           for (char *p = strtok_r(slabs, ",", &b);                p != NULL;                p = strtok_r(NULL, ",", &b)) {                  //解析出一個(gè)個(gè)的sid               if (!safe_strtoul(p, &sid) || sid < POWER_SMALLEST                       || sid > POWER_LARGEST) {//sid越界                   pthread_mutex_unlock(&cache_lock);                   pthread_mutex_unlock(&lru_crawler_lock);                   return CRAWLER_BADCLASS;               }               tocrawl[sid] = 1;           }       }          //crawlers是一個(gè)偽item類型數(shù)組。如果用戶要清理某一個(gè)LRU隊(duì)列,那么       //就在這個(gè)LRU隊(duì)列中插入一個(gè)偽item       for (sid = 0; sid < LARGEST_ID; sid ) {           if (tocrawl[sid] != 0 && tails[sid] != NULL) {                  //對于偽item和真正的item,可以用nkey、time這兩個(gè)成員的值區(qū)別               crawlers[sid].nbytes = 0;               crawlers[sid].nkey = 0;               crawlers[sid].it_flags = 1; /* For a crawler, this means enabled. */               crawlers[sid].next = 0;               crawlers[sid].prev = 0;               crawlers[sid].time = 0;               crawlers[sid].remaining = settings.lru_crawler_tocrawl;               crawlers[sid].slabs_clsid = sid;               //將這個(gè)偽item插入到對應(yīng)的lru隊(duì)列的尾部               crawler_link_q((item *)&crawlers[sid]);               crawler_count ;//要處理的LRU隊(duì)列數(shù)加一           }       }       pthread_mutex_unlock(&cache_lock);       //有任務(wù)了,喚醒LRU爬蟲線程,讓其執(zhí)行清理任務(wù)       pthread_cond_signal(&lru_crawler_cond);       STATS_LOCK();       stats.lru_crawler_running = true;       STATS_UNLOCK();       pthread_mutex_unlock(&lru_crawler_lock);       return CRAWLER_OK;   }  


        現(xiàn)在再來看一下偽item是怎么在LRU隊(duì)列中前進(jìn)的。先看一個(gè)偽item前進(jìn)圖。

        



        從上面的圖可以看到,偽item**與前驅(qū)節(jié)點(diǎn)交換位置實(shí)現(xiàn)前進(jìn)。如果偽item是LRU隊(duì)列的頭節(jié)點(diǎn),那么就將這個(gè)偽item移出LRU隊(duì)列。函數(shù)crawler_crawl_q完成這個(gè)交換操作,并且返回交換前偽item的前驅(qū)節(jié)點(diǎn)(當(dāng)然在交換后就變成偽item的后驅(qū)節(jié)點(diǎn)了)。如果偽item處于LRU隊(duì)列的頭部,那么就返回NULL(此時(shí)沒有前驅(qū)節(jié)點(diǎn)了)。crawler_crawl_q函數(shù)里面那些指針滿天飛,這里就不貼出代碼了。

        上面的圖,雖然偽item遍歷了LRU隊(duì)列,但并沒有刪除某個(gè)item。這樣畫,一來是為了好看,二來遍歷LRU隊(duì)列不一定會(huì)刪除item的(item不過期失效就不會(huì)刪除)。

清理item:


        前面說到,可以用命令lru_crawler tocrawl num指定每個(gè)LRU隊(duì)列**多只檢查num-1個(gè)item??辞宄c(diǎn),是檢查數(shù),不是刪除數(shù),而且是num-1個(gè)。首先要調(diào)用item_crawler_evaluate函數(shù)檢查一個(gè)item是否過期,是的話就刪除。如果檢查完num-1個(gè),偽item都還沒有到達(dá)LRU隊(duì)列的頭部,那么就直接將這個(gè)偽item從LRU隊(duì)列中刪除。下面看一下item_crawler_thread函數(shù)吧。

[cpp] view plain copy   static void *item_crawler_thread(void *arg) {       int i;          pthread_mutex_lock(&lru_crawler_lock);       while (do_run_lru_crawler_thread) {       //lru_crawler_crawl函數(shù)和stop_item_crawler_thread函數(shù)會(huì)signal這個(gè)條件變量       pthread_cond_wait(&lru_crawler_cond, &lru_crawler_lock);          while (crawler_count) {//crawler_count表明要處理多少個(gè)LRU隊(duì)列           item *search = NULL;           void *hold_lock = NULL;              for (i = 0; i < LARGEST_ID; i ) {               if (crawlers[i].it_flags != 1) {                   continue;               }               pthread_mutex_lock(&cache_lock);               //返回crawlers[i]的前驅(qū)節(jié)點(diǎn),并交換crawlers[i]和前驅(qū)節(jié)點(diǎn)的位置               search = crawler_crawl_q((item *)&crawlers[i]);               if (search == NULL || //crawlers[i]是頭節(jié)點(diǎn),沒有前驅(qū)節(jié)點(diǎn)                   //remaining的值為settings.lru_crawler_tocrawl。每次啟動(dòng)lru                   //爬蟲線程,檢查每一個(gè)lru隊(duì)列的多少個(gè)item。                   (crawlers[i].remaining && --crawlers[i].remaining < 1)) {                      //檢查了足夠多次,退出檢查這個(gè)lru隊(duì)列                   crawlers[i].it_flags = 0;                   crawler_count--;//清理完一個(gè)LRU隊(duì)列,任務(wù)數(shù)減一                   crawler_unlink_q((item *)&crawlers[i]);//將這個(gè)偽item從LRU隊(duì)列中刪除                   pthread_mutex_unlock(&cache_lock);                   continue;               }               uint32_t hv = hash(ITEM_key(search), search->nkey);               //嘗試鎖住控制這個(gè)item的哈希表段級別鎖               if ((hold_lock = item_trylock(hv)) == NULL) {                   pthread_mutex_unlock(&cache_lock);                   continue;               }&nb

體驗(yàn)課預(yù)約試聽

倒計(jì)時(shí)

12:00:00

課程熱線:

在線咨詢

客服在線時(shí)間:早上9點(diǎn)~下午6點(diǎn),其他時(shí)間請?jiān)诰€預(yù)約報(bào)名或留言,謝謝!

蘇州web前端

免費(fèi)體驗(yàn)課開班倒計(jì)時(shí)

11: 41: 09

稍后會(huì)有專業(yè)老師給您回電,請保持電話暢通

咨詢電話:
推薦機(jī)構(gòu) 全國分站 更多課程

本周僅剩 個(gè)試聽名額

請鍵入信息,稍后系統(tǒng)將會(huì)把領(lǐng)獎(jiǎng)短信發(fā)至您的手機(jī)

申請?jiān)嚶犆~

QQ:
加盟合作:0755-83654572