日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Kestrel中的Journal.scala類(lèi)詳解

本文是Scala代碼實(shí)例之Kestrel的第六部分,講述PersistentQueue中的Journal.scala類(lèi)。

我們提供的服務(wù)有:成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、微信公眾號(hào)開(kāi)發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、昌邑ssl等。為千余家企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的昌邑網(wǎng)站制作公司

在PersistentQueue之下,有一個(gè)Journal.scala的類(lèi),支撐了消息隊(duì)列的存儲(chǔ)問(wèn)題。這是Kestrel提供的另外一個(gè)特性:通過(guò)文件系統(tǒng)保存消息隊(duì)列,避免服務(wù)重啟的時(shí)候,Kestrel的隊(duì)列丟失。

通過(guò)前面一段時(shí)間的閱讀,我們對(duì)Scala的語(yǔ)法已經(jīng)有一個(gè)基本的把握,所以在閱讀Journal的時(shí)候,我們就更注重實(shí)現(xiàn)的方式,而不是語(yǔ)法細(xì)節(jié)了。當(dāng)然Journal也沒(méi)有太多的語(yǔ)法細(xì)節(jié)需要講的了。當(dāng)然出了還沒(méi)有詳細(xì)說(shuō)過(guò)的case class和case object。

在Journal.scala的開(kāi)始部分就定義了一個(gè)abstract class類(lèi)JournalItem,并且定義了它的許多子類(lèi),這些子類(lèi)是用來(lái)和PersistentQueue進(jìn)行消息傳遞的。case class/case object是一種特殊的class/object,其功能是在對(duì)象里面增加了幾個(gè)功能

1. 把所有的建構(gòu)函數(shù)的var變成val,也就是變成了不可變的常量

2.自動(dòng)實(shí)現(xiàn)了equal, hashCode和toString三個(gè)方法

3.當(dāng)對(duì)象出現(xiàn)在case之后的時(shí)候,會(huì)自動(dòng)apply出一個(gè)對(duì)象,對(duì)象的值和創(chuàng)建的時(shí)候一樣,這個(gè)功能保證了可以和match…case語(yǔ)法可以寫(xiě)得很簡(jiǎn)練。

關(guān)于Case class的具體說(shuō)明可以參考:CaseClasses和MatchingOnCaseClasses。關(guān)于第三條特性,還可以參考CompanionObjects。

簡(jiǎn)單的理解,我們就把case object/case class當(dāng)作消息傳遞中需要使用的對(duì)象類(lèi)型就可以了。

Journal使用了noi的FileChannel,來(lái)處理文件的讀取和存儲(chǔ)。核心的算法,可以只看readJournalEntry和replay兩個(gè)方法。readJournalEntry的功能是從文件中讀取數(shù)據(jù),并且根據(jù)格式組成各種case class/case object,并且同時(shí)返回字節(jié)數(shù)。而在上層的方法,比如replay,則根據(jù)得到的不同數(shù)據(jù)類(lèi)型,調(diào)用更上層的函數(shù)f(case class/case object)。

我們回到PersistentQueue中看replayJournal的時(shí)候,發(fā)現(xiàn)它將調(diào)用replay后得到的一系列的case class轉(zhuǎn)義成為在PersistentQueue中需要執(zhí)行的各種命令——所以這個(gè)方法的名字叫做replay!就是回放的意思。

當(dāng)系統(tǒng)重啟的時(shí)候,打開(kāi)每個(gè)queue之前都需要一段回放的時(shí)間,把文件系統(tǒng)中記錄的當(dāng)時(shí)的整個(gè)存取過(guò)程重新回放一次,通過(guò)回放來(lái)重建內(nèi)存中的隊(duì)列?;剡^(guò)來(lái)再看Journal.scala的時(shí)候,我們就更清晰的知道,文件存儲(chǔ)的不是當(dāng)時(shí)的隊(duì)列狀態(tài),而是每一次系統(tǒng)執(zhí)行的軌跡。所以,Journal對(duì)整個(gè)Kestrel消息隊(duì)列的開(kāi)銷(xiāo)才會(huì)很小。

但是另外一個(gè)問(wèn)題隨之而來(lái),如果記錄所有的操作過(guò)程,那么這個(gè)文件不是只會(huì)增大,不會(huì)縮小么?為了解決這個(gè)問(wèn)題,Journal.scala實(shí)現(xiàn)了一個(gè)叫做roll的機(jī)制。從PersisitentQueue中的add方法中,我們可以看到這樣的代碼:

 
 
 
  1. if (keepJournal() && !journal.inReadBehind) {  
  2.      if (journal.size > maxJournalSize() * maxJournalOverflow() && queueSize < maxJournalSize()) {  
  3.        // force re-creation of the journal.  
  4.        log.info("Rolling journal file for '%s' (qsize=%d)", name, queueSize)  
  5.        journal.roll(xidCounter, openTransactionIds map { openTransactions(_) }, queue)  
  6.      }  
  7.      if (queueSize >= maxMemorySize()) {  
  8.        log.info("Dropping to read-behind for queue '%s' (%d bytes)", name, queueSize)  
  9.        journal.startReadBehind  
  10.      }  
  11.    }  

如果發(fā)生了Journal文件的尺寸太大,但是實(shí)際的Queue尺寸也沒(méi)有滿的時(shí)候,就啟動(dòng)roll進(jìn)程來(lái)重新建立一個(gè)Journal文件。處理的方法也很簡(jiǎn)單,就是把當(dāng)前內(nèi)存中的隊(duì)列直接寫(xiě)入到Journal對(duì)應(yīng)的文件中,變成一連串的add。這么做,是不是一個(gè)很好的做法?只有一種意外的情況,那就是現(xiàn)存在消息隊(duì)列里面的數(shù)據(jù)很多,那么重建Journal的時(shí)間就需要很多。作者也考慮到了這個(gè)問(wèn)題,所有要求queue實(shí)際的size必須小于Journal所能存儲(chǔ)的量的時(shí)候,才會(huì)做roll的操作,也就是說(shuō),當(dāng)隊(duì)列里面有很多的事件沒(méi)有處理的時(shí)候,就算硬盤(pán)占用得再多,也不會(huì)啟動(dòng)roll方法。而解決的方法是,當(dāng)內(nèi)存中的Queue太大,大到超過(guò)了***的內(nèi)存使用限制的時(shí)候,啟動(dòng)readBehind模式。

當(dāng)readBehind模式啟動(dòng)之后,會(huì)對(duì)文件增加一個(gè)read句柄,每次從內(nèi)存里面remove掉消息的時(shí)候,就會(huì)嘗試從文件中讀取消息放到內(nèi)存里面。在這樣的模式下,內(nèi)存就一致保持著最滿的隊(duì)列。更多的消息就先直接存儲(chǔ)到文件中,直到文件中的read指針和write指針重合,也就是說(shuō)所有在文件系統(tǒng)中的消息都已經(jīng)被處理完畢了,系統(tǒng)就會(huì)重新切換回正常的模式。

在這種模式下,只要硬盤(pán)的數(shù)量足夠大,我們基本上可以把這個(gè)消息隊(duì)列理解為無(wú)限長(zhǎng)……但是在readbehind模式下,是不會(huì)進(jìn)行roll的操作的。所以——大家需要注意的是,在配置中,maxJournalSize必須要小于maxMemorySize,否則這兩個(gè)機(jī)制就會(huì)打架了。而maxJournalSize這個(gè)數(shù)值也不應(yīng)該很大,這樣就能保證每次roll的效率會(huì)很快(因?yàn)閞oll的效率是取決于事件占用內(nèi)存的數(shù)量,也就是maxJournalSize的),超過(guò)這個(gè)值,系統(tǒng)就不會(huì)roll。

決定是否roll。還有一個(gè)數(shù)值就是maxJournalOverflow,這是一個(gè)很好的設(shè)計(jì),相當(dāng)于Journal文件的利用率,比如說(shuō)Overflow設(shè)置為5,表示現(xiàn)在有效的消息隊(duì)列數(shù)據(jù),只有整個(gè) Journal 文件大小的 1/5。

假設(shè)我們平均每個(gè)消息的數(shù)據(jù)占有1K,那么其他的指令信息基本可以被忽略(因?yàn)槎贾挥袔讉€(gè)字節(jié)而已),所以O(shè)verflow的比例相當(dāng)于總消息數(shù)量 / 還沒(méi)有處理過(guò)的消息數(shù)量。所以這個(gè)數(shù)值的上限取決于replay的效率。也就是讀取文件的速度,比如說(shuō),我們覺(jué)得啟動(dòng)的時(shí)候,讀取100M的文件,大概需要10s,是我們可以接受的,而每個(gè)消息字節(jié)平均是1K,roll一次100個(gè)消息需要100ms,是可以接受的。那么 maxJournalOverflow = 100M / 100 * 1K = 1000。不過(guò)實(shí)際情況可能是roll的次數(shù)會(huì)更多一些,因?yàn)楫?dāng)內(nèi)存中的消息隊(duì)列只有10個(gè)的時(shí)候,硬盤(pán)超過(guò)10M,就會(huì)觸發(fā)roll操作了。

但是設(shè)置1000的目的是,在最壞的情況可以接受,而不是在一半的情況下效率更高。這是需要注意的。

【相關(guān)閱讀】

  1. 從Java走進(jìn)Scala(Scala經(jīng)典讀物)
  2. A Scala Tutorial for Java programmers
  3. 專(zhuān)題:Scala編程語(yǔ)言
  4. 從Scala看canEqual與正確的的equals實(shí)現(xiàn)
  5. Scala快速入門(mén):從下載安裝到定義方法

網(wǎng)站題目:Kestrel中的Journal.scala類(lèi)詳解
路徑分享:http://www.dlmjj.cn/article/ccdgdpe.html