北京年底前試營運純電動車租賃服務

據中國大陸北京市交通委及相關租車企業負責人日前透露, 該市正在進行試點推廣純電動車租賃舉措,預計在2013年內正式導入市場試水溫,以打開純電動汽車個人購買方面發展緩慢的局面。

對於純電動汽車,政府提出了多項政策,但這兩年的發展速度並不及預期,進行汽車租賃措施也許是解開目前僵局較好的出路。主因消費者當前對於純電動汽車的續航能力、貶值率還有顧慮,因此,改成汽車租賃後,不用加油,相應的充電站也在完成,預料消費者會更願意使用純電動車。

一輛純電動汽車和一輛普通汽車的日租金預計差不多,但是使用純電動車卻可以大大降低使用成本。以一輛日租金120元(人民幣,下同)的10萬元左右經濟型轎車為例,普通汽車每公里加油費用在7毛錢,而純電動車每公里充電費用僅需要6分錢。

而最近一兩年,部分消費者購車消費觀念開始轉變,由「我要擁有一輛車」,轉變為「我要使用一輛車」。政府和事業單位也開始用租車的方式解決公共服務,促使租車率上升幅度超過5%。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

東方電氣發佈半年報 新能源汽車未來是看點

東方電氣昨(28)日發佈2013年半年報,其在報告期內實現營業收入增加2.28%,達到204.67億元(人民幣,下同);但淨利潤則同比下降4.65%,為11.86億元。對於業績上升而淨利潤下滑的原因,公司稱是產品毛利率下跌所致。資料顯示,上半年,該公司的毛利率下跌了1.3%,為18.05%。

雖然淨利潤下滑,但東方電氣的產品銷路仍良好。據中報披露,報告期內,公司新增訂單202億元,在手訂單超過1400億元。其中,高效清潔能源占59.5%、新能源占14%、水能及環保占7.3%、工程及服務業占19.2%。

東方電氣的中央研究院新能源與發電技術研究所,主要負責新能源開發,該所副所長胡蘊成近日表示,東方電氣的電動機產品已經成熟,進入到了市場領域,目前國內新能源汽車大多數電動機都是由東方電氣生產的。同時,動力總成和儲能技術已取得重大突破,不久將投入市場。

相關人士分析稱,成都發展新能源汽車有較強優勢:第一,成都有強大的汽車工業基礎,形成了較為完備的汽車產業鏈;第二,成都汽車科研實力強大,川大、電子科大等高校都有涉及,同時還有中科等電池、電機等的研發與生產企業;第三,鋰礦是新能源汽車的主要能源,而四川鋰礦資源豐富;第四,成都正在建設世界現代田園城市,在低碳方面投入力度也較大。

另外,國家對新能源汽車的扶持力度也正在加大,《節能與新能源汽車產業發展規劃》已編制完成,正在向各部委徵求意見,根據規劃,未來10年,中央財政將拿出超過1000億元,扶持新能源汽車產業鏈。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

※教你寫出一流的銷售文案?

韓國發明可折疊電動汽車

全球不少大城市都面臨交通擁擠,停車位嚴重不足的問題。而韓國近日研發一款可折疊電動汽車,減少樂停車時佔據的空間。

據韓國科研人員介紹,這款稱為Armadillo-T的車子可搭載兩個人,最高速度為每小時60公裏,充電10分鐘可走100公裏。駕駛員還可利用智能手機應用程序,在車子外部發出折疊或展開指令。

據了解,Armadillo-T在停車時會以車體中心為軸抬起後輪,使得車子佔位長度從2.8公尺縮短到1.65公尺。一個普通停車位可放置三輛Armadillo-T汽車,而且它可在停車狀態下旋轉360度,停放在普通車輛不能停放的地方。

為安全起見,設計師還將傳統的後視鏡置替換為環繞視圖攝像頭。然而,由于Armadillo-T未達韓國公共交通安全準則,如無法承受猛烈撞擊,因此無法在街道上行駛。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

中國電動汽車後碰撞及安全氣囊標准有望于十二五期間建立

據中國汽車技術研究中心標准所高級工程師孫振東近日表示,到十二五末,國家爭取逐步建立起電動汽車後碰撞以及安全氣囊的標准體系。

孫振東在9月8日于天津舉行的中國汽車產業發展(泰達)國際論壇上表示,目前新能源汽車正處於快速發展中,它的安全得到了社會各界的重視。中國汽車技術研究中心標准所也會加緊研究和制定新能源汽車的安全標准。

孫振東介紹,被動安全是指當車輛發生交通事故的時候,車輛對成員提供的有效保護。目前整個中國汽車標准體系中,有關被動安全的共有32項標准。

對於乘用車,國家已經建立了完整有效的體系。但是對於商用車,包括客車和貨車,被動安全標准正在建立和完善的過程中,這也是中國汽車技術研究中心標准所今後的工作重點。目前江淮汽車、長安汽車、比亞迪、福田汽車等車企有涉足電動汽車業務。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

中國多部委正制定新一輪新能源汽車推廣方案實施細則

據中國證券報報導,中國人民銀行、科技部、工信部等多部委正在加緊「備戰」新一輪新能源汽車的推廣方案。有觀點認為,隨著近來油價的持續高企,中國正在迎來新能源汽車推廣的最佳窗口期。

科技部部長萬鋼日前表示,國務院已正式批復新一輪的新能源汽車示范推廣方案,科技部、工信部等四部委正在制定實施細則。全國汽車標準委電動車輛分委會副主任陳全世表示,新一輪推廣政策或將淡化試點城市,在所有地級以上城市推廣混合動力汽車。隨著太陽能、風能和智能電網的發展,建議繼續大力拓展混合動力客車尤其是插電式混合動力客車的推廣。

中國人民銀行研究局副局長易誠表示,要完成「十二五」節能環保發展目標,我國每年在發展可再生能源方面的投入資金或將達到5000億到1萬億元(人民幣)。今後除繼續加強金融對綠色環保領域的產業扶持政策引導之外,建議在個人金融消費領域出臺綠色消費信貸扶持政策,支持混合動力汽車、節能家電等產品的消費信貸,鼓勵綠色消費方式。

據悉,新一輪新能源汽車示范推廣方案將以新能源汽車為主開展示范,以試點城市為核心建立試點區域,擴大輻射范圍,加大電動汽車的推廣。同時,改變原有的財政資金補貼方式,加快補貼資金的落實力度。此外,還將對充電站的建設進行財政支持。

業內人士認為,對汽車產品而言,一是通過節能產品惠民工程補貼政策,鼓勵傳統節能汽車消費;二是通過節能與新能源汽車示范推廣試點補貼政策,鼓勵多個城市的公共服務領域和私人購買使用新能源汽車。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

Golang簡易入門教程——面向對象篇

本文始發於個人公眾號:TechFlow,原創不易,求個關注

今天是golang專題的第9篇文章,我們一起來看看golang當中的面向對象的部分。

在現在高級語言當中,面向對象幾乎是不可或缺也是一門語言最重要的部分之一。golang作為一門剛剛誕生十年的新興語言自然是支持面向對象的,但是golang當中面向對象的概念和特性與我們之前熟悉的大部分語言都不盡相同。比如Java、Python等,相比之下, golang這個部分的設計非常得簡潔和優雅(仁者見仁),所以即使你之前沒有系統地了解過面向對象,也沒有關係,也一定能夠看懂。

常見的面向對象的部分,比如繼承、構造函數、析構函數,這些內容在golang當中統統沒有,因此整體的學習成本和其他的語言比起來會更低一些。

struct

在golang當中沒有類的概念,代替的是結構體(struct)這個概念。我們可以給結構體類型定義方法,為了表明該方法的適用對象是當前結構體,我們需要在方法當中定義接收者,位於func關鍵字和方法名之間。

我們一起來看一個例子:

type Point struct {
 x int
 y int
}

func (p Point) Dis() float64 {
 return math.Sqrt(float64(p.x*p.x + p.y*p.y))
}

在上面這段代碼當中我們定義了一個叫做Point的結構體,以及一個面向這個結構體的方法Dis。我們一個一個來看它們的語法。

對於結構體來說,我們通過type關鍵字定義。在golang當中type關鍵字的含義是定義一個新的類型。比如我們也可以這樣使用type:

type Integer int

它的含義是從int類型定義了一個新的類型Integer,從此之後我們可以在後序的代碼當中使用Integer來代替int。它有些類似於C++當中的typedef,結合這個含義,我們再來看結構體的定義就很好理解了。其實是我們通過struct關鍵字構造了一個結構體,然後使用type關鍵字定義成了一個類型。

之後我們創建了一個面向結構體Point的函數Dis,這個函數和我們之前使用的函數看起來並沒有太多的不同,唯一的區別在於我們在func和函數名之間多了一個(p Point)的定義。這其實是定義這個函數的接收者,也就是說它接受一個結構體的調用。

不僅如此,我們可以給golang當中的任何類型添加方法,比如:

type Integer int

func (a Integer) Less(b Integer) bool {
 return a < b
}

在這個例子當中,我們給原生的int類型添加了Less這個方法,用來比較大小。我們在添加方法之前使用type給int起了一個別名,這是因為golang不允許給簡單的內置類型添加方法,並且接收者的類型定義和方法聲明必須在同一個包里,我們必須要使用type關鍵字臨時定義一個新的類型。這裏要注意的是,雖然我們定義出來的Integer和int的功能完全一樣,但是它們屬於不同的類型,不能互相賦值。

和別的語言比較起來,這樣的定義的一個好處就是清晰。舉個例子,比如在Java當中,同樣的功能會寫成不同的樣子:

class Integer {
    private int val;
    public boolean less(Integer b) {
        return this.val < b.val;
    }
}

對於初學者而言,可能會覺得困惑,less函數當中的這個this究竟是哪裡來的?其實這是因為Java的成員方法當中隱藏了this這個參數,這一點在Python當中要稍稍清晰一些,因為它將self參數明確地寫了出來:

class Integer:
    def __init__(self, val):
        self.val = val
    def less(self, val):
        return self.val < val.val

而golang明確了結構體函數的接收者以及參數,顯得更加清晰。

指針接收者

golang當中,我們也可以將函數的接收者定義成指針類型

比如我們可以將剛才的函數寫成這樣:

type Point struct {
 x int
 y int
}

func (p *Point) Dis() float64 {
 return math.Sqrt(float64(p.x*p.x + p.y*p.y))
}

指針接收者和類型接收者在使用上是一樣的,我們並不需要將結構體轉化成指針類型,可以直接進行調用。golang內部會自己完成這個轉化:

func main() {
 p := Point{3, 4}
 fmt.Print(p.Dis())
}

那麼這兩者的區別是什麼呢?我們既然可以定義成普通的結構體對象,為什麼還要有一個指針對象的接收者呢?

其實很好理解, 兩者的區別有些類似於C++當中的值傳遞和引用傳遞。在值傳遞當中,我們傳遞的是值的一個拷貝,我們在函數當中修改參數並不會影響函數外的結果。而引用傳遞則不然,傳遞的是參數的引用,我們在函數內部修改它的話,會影響函數外的值。

也就是說在golang當中,如果我們函數接收的是一個指針類型,我們可以在函數內部修改這個結構體的值。否則的話,傳入的是一個拷貝,我們在其中修改值並不會影響它本身。我們來看個例子:

func (p *Point) Modify() {
 p.x += 5
 p.y -= 3
}

func main() {
 p := Point{3, 4}
 p.Modify()
 fmt.Print(p)
}

上面這段代碼當中函數的接收者是一個指針,所以我們得到的結果會是{8, 1},如果我們把指針去掉,改成普通的值接收的話,那麼最後的結果仍然是{3, 4}。

總結

我們今天學的內容有些多,我們來簡單梳理一下。首先,我們了解了通過type和struct關鍵字來定義一個結構體,結構體是golang當中面向對象的載體,golang拋棄了傳統的面向對象的實現方式和特性,擁有自己的面向對象的理念。

對於結構體來說,我們可以把它當做是接受者傳遞給一個函數,使得我們可以以類似調用類當中方法的形式來調用一個函數。並且對於函數而言,接受者除了值以外還可以是一個指針。如果是指針的話,當我們對結構體值進行修改的時候,會影響到原值。即使我們定義的接收者類型是指針,我們在調用的時候也不必显示將它轉化成結構體指針,golang當中會自動替我們完成這樣的轉化。

面向對象部分可以說是golang這一門語言當中最大的創新之一,也正是因為拋棄了傳統的類以及繼承、派生的概念,使得golang當中的面向對象語法糖相對簡潔。也因此有人將golang稱為升級版的C語言。雖然我們啰啰嗦嗦寫了很多,但是實際談到的內容並不多,我想理解起來也不會特別困難。

今天的文章到這裏就結束了,如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

本文使用 mdnice 排版

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

新北清潔公司,居家、辦公、裝潢細清專業服務

※推薦評價好的iphone維修中心

強烈推薦 10 本我私藏的數據庫書單,附讀書方法

二哥有推薦的數據庫書單嗎?關於 MySQL 和 Oracle 的,謝謝了。

讀者小貓私信問了我上面這個問題,我覺得問題挺典型的,值得寫篇文章分享一下。因為對於 Java 程序員來說,幾乎不可避免地要和數據庫打交道,MySQL 和 Oracle 恰好又是兩個使用最廣泛的數據庫。

MySQL 和 Oracle 都屬於關係型數據庫,現在都隸屬於甲骨文公司,這家公司的產品很牛逼,CEO 拉里埃爾森也很牛逼,和史蒂夫喬布斯是鐵哥們。Oracle 相對 MySQL 更沉重一些,屬於企業級應用。而 MySQL 是開源的,性能又給力,所以近些年來市場佔用率已經飆升到了第一位,甩開 Oracle 兩條街。

(我用 Oracle 比較少,所以本篇就以 MySQL 為主,小夥伴們如果對 Oracle 了解得比較透徹,請在留言區推薦一下,拜謝。)

好了,接下來上書單,希望小夥伴們能夠喜歡,喜歡的話,就收藏,讓它吃灰去。不不不,喜歡的話,買幾本好好讀讀,學到就是賺到。

第一本、《SQL 必知必會》

SQL,全名為 Structured Query Language,也就是結構化查詢語言。經常有一些小夥伴抱怨說,“二哥,在公司乾的無非是一些 CURD 的工作,感覺沒啥進步。”這句話裏面的 CURD 指的就是創建(Create)、更新(Update)、讀取(Retrieve)和刪除(Delete)相關的業務操作,也就是 SQL 的範圍。

但說白了,再偉大的產品,其業務永遠也是圍繞着增刪改查轉啊。

《SQL 必知必會》這本書名副其實,能在最短時間內教會你實際工作環境中最常用和最必需的 SQL 知識,實用性極強。這本書是麻省理工學院、伊利諾伊大學等眾多大學的參考教材——是不是一下子就感覺檔次高了。

第二本、《SQL 學習指南》

封面上有一隻小動物,再帶上“O’REILLY”這個標識,就可以下一句肯定的結論了——這是一本經典書。

《SQL學習指南(第2版 修訂版)》這本書以 SQL92 標準為藍本,涵蓋了 MySQL 6.0、Oracle 11g。全面系統地介紹了 SQL 語言各方面的基礎知識以及一些高級特性,包括 SQL 數據語言、SQL 方案語言、數據集操作、子查詢以及內建函數與條件邏輯等內容。

經常和數據庫接觸的初學者可以常備在身邊,用到就去翻翻。

第三本、《MySQL 必知必會》

《MySQL 必知必會》這本書的英文原版名是《Sams Teach Yourself MySQL in 10 Minutes》,有沒有品到一絲絲標題黨的味道?不過,中文版名字就優雅多了。

這書非常適合想快速了解數據庫原理和 MySQL 的新手閱讀。快餐性質,簡潔明快,小開本,而且很薄,有點《了不起的蓋茨比》那本書的味道,讀起來很流暢。

第四本、《數據庫系統概念》

哇,黑皮書來了哦!黑皮書只有一個缺點,就是枯燥,但說良心話,黑皮書都特么是經典之作啊。尤其適合心靜的大學生來讀,反正大學生活除了談戀愛最重要外(嘿嘿),我覺得就剩下學習了。

數據庫領域的殿堂級作品;夯實數據庫理論基礎,增強數據庫技術內功的必備之選;對深入理解數據庫,深入研究數據庫,深入操作數據庫都具有極強的指導作用!

總之,有時間就讀,真的香就對了。

第五本、《MySQL技術內幕 : InnoDB存儲引擎 第2版》

眾所周知,MySQL 有兩種常見的存儲引擎,一種是 MyISAM,一種是 InnoDB。如果需要支持事務,就選擇 InnoDB,如果一個表絕大多數情況下只用來讀,可以選擇 MyISAM。從 MySQL 5.5 開始,InnoDB 已經成為 MySQL 的默認引擎,這說明它的優勢是有目共睹的。

《MySQL技術內幕:InnoDB存儲引擎(第2版)》這本書恰好從源代碼的角度深度解析了 InnoDB 的體繫結構、實現原理、工作機制,並給出了大量最佳實踐,能幫助你系統而深入地掌握 InnoDB,更重要的是,它能給你你設計高性能、高可用的數據庫系統提供絕佳的指導。

第六本、《高性能MySQL(第3版)》

這本書的封面是不是有點熟,風格和之前推薦的《 SQL 學習指南》高度相似,對吧?這本書的知名度非常高,就好像 MySQL 領域的周杰倫,幾乎所有要學習 MySQL 的開發人員都得買它。

我家裡這本書已經快被我翻爛了,說實話。這本書的內容非常豐富,幾乎涵蓋了 MySQL 所有方面:

  • 前兩章為準備階段,包含一些基本的概念與性能評測工具。
  • 第三章到第七章為優化部分,從架構、索引、查詢語句,到參數配置、軟硬件的優化。
  • 第八章到第十四章為服務的管理與維護,涵蓋了複製、備份、安全等等內容及相關工具。

第七本、《高可用MySQL(第2版)》

《高可用MySQL:構建健壯的數據中心》這本書主要講解真實環境下如何使用 MySQL 的複製、集群和監控特性,揭示 MySQL 可靠性和高可用性的方方面面。旨在解決 MySQL 數據庫的常見應用瓶頸,在保持 MySQL 的持續可用性的前提下,挖潛各種提高性能的解決方案。

這本書比較適合 MySQL 的專業人士看,否則很難駕馭得了。

第八本、《Oracle高效設計》

沒辦法,雖然 Oracle 用得不多,但對經典書還是有一些耳聞的。《Oracle高效設計》這本書的評價還是不錯的,作者 Thomas Kyte 是 Oracle 專業領域世界上最權威的專家之一,也是 Oracle 核心技術小組副主席。

這本書對 Oracle 及數據庫的知識進行了全面深入的講解,是一本關於 Oracle 的高級手冊。內容包括:性能工具包、體繫結構選擇、語句處理、故障排除、高效的管理、高效的設計模式、高效的 PL/SQL 程序設計等。

好了,就到這吧,小夥伴們,我覺得這八本書已經夠看上一段時間了。接下來,順帶分享一下我的一些讀書方法,我覺得這個價值可能比書單本身更有價值,希望能夠給小夥伴們的一些參考。

1)速讀

像入門書籍,要在最短時間內過一遍,比如說《SQL 必知必會》,不要覺得速讀一遍什麼也記不住,沒關係的,本身腦容量就是有限的。速讀的目的很單純,了解一本書的大綱,有沒有勾引你(感興趣)的內容。

或者說有沒有你不懂的內容,標記一下,對後面再讀做到一個提醒的作用。

2)精讀

有了速讀的基礎,再認真讀一遍的時候,你就會感覺完全不一樣了,就好像你去一個地方旅遊,總要先做個小攻略,在地圖上盤算一下,真正身處一個地方的時候,你就不會暈頭轉向,或者說有一種,“哦,原來是這樣啊”的感覺。

3)實戰

讀書最怕就是眼高手低,你以為你記住了,理解了,如果不動手記筆記或者敲代碼去實戰的話,基本上等於沒看,因為你是在讀技術書,又不是哲學書,對吧?

只靠大腦去思考是遠遠不夠的,還需要你的手去敲一敲,跑一跑,執行一下,看看結果是否符合預期,會不會出錯。

這三個步驟下來,一本書就再也不愁“讀了和沒讀一樣”了,小夥伴們有沒有 get 到?

我是沉默王二,一枚有趣的程序員。如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀。

本文 GitHub 已經收錄,有大廠面試完整考點,歡迎 Star。

原創不易,莫要白票,請你為本文點個贊吧,這將是我寫作更多優質文章的最強動力。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※幫你省時又省力,新北清潔一流服務好口碑

※回頭車貨運收費標準

在Java虛擬機上班是一種怎樣的體驗?

 228 人贊同了該回答

 

 利益相關,匿了!

JVM公司裏面線程眾多,派系林立,尤其是執行引擎那波人,因為是核心部門,經常diss別的部門。

 

 

 

  428 人贊同了該回答

 

 不請自來。

其實在JVM工作沒有你們想象的那麼辛苦,其他部門不清楚,就拿我所在的垃圾回收部(這名字不好聽,叫GC部門吧)來說說。

我的工作是負責執行對象的finalize方法,你們也知道,現在的程序員,很少實現類的這個方法了,所以我的工作大部分時間都可以摸魚。

——————–分割線——————–

評論里有人問我對象的finalize方法是如何被執行的,這裏統一回復一下。

JVM的ClassLoader部門在加載一個class的時候,會檢查它是否有實現finalize方法,具體細節我不太清楚,請 @AppClassLoader 同學來幫忙解答一下。

如果發現有finalize方法,以後創建這個類的所有對象都會附帶創建一個Finalizer對象。

這個Finalizer有兩個關鍵點:

  • 繼承自Reference類,本身也是一個引用,引用的正是跟它一起創建的那個對象
  • 裏面有一個名叫queue的成員,指向了一個隊列: ReferenceQueue,正是 Finalizer的一個靜態成員變量。

除此之外,Finalizer裏面還有一個靜態線程FinalizerThread,這個其實就是我了。我的工作就是不斷上面的隊列裏面取出Finalizer對象,然後執行它引用對象的finalize方法。

什麼?你問我Finalizer對象是什麼時候進入這個隊列里的?這我就不知道了,超出了我的工作範圍,可以請 @ReferenceHandler 幫忙解答一下。

以上。

 

 

 

   522 人贊同了該回答

 

  謝邀!

JVM公司整體來說還是挺不錯的,各方面條件都還不錯。辦公大廈有兩層,一樓是native層,一堆native層的線程員工在下面辦公。我在二樓的Java層,這一層都是Java線程。

我在JVM類加載部門工作,我的Leader是ExtClassLoader,他的Leader是公司高管BootstrapClassLoader

我們部門的工作就是把磁盤上的.class文件加載到內存中,變成一個個可以使用的類。工作嘛還算輕鬆。不過有一點讓我不爽的是部門的雙親委派制度。

圖源網絡

 

每次遇到新的類需要加載,按照規定都必須請示領導來加載,領導又去請示他的領導來加載。但是高管BootstrapClassLoader只負責加載Java的核心類,我的領導也只負責加載一些擴展類,所以大部分時間請示完了結果他們都加載不了,還得讓我去加載。

一來二去的花了不少時間在流程上,瞎耽誤工夫。我多次反應這個問題,能不能不請示我直接加載算了,不過每次都被駁回,說是為了安全考慮,他們必須過目。唉,領導不肯放權也是難辦!

——————–分割線——————–

評論區戾氣太重!說我不懂安全也是醉了。

回答一下 @FinalizerThread 同學的問題。

確實如他所說,我們ClassLoader會去檢查類有沒有實現finalize方法,檢查結果會保存在Klass結構中的AccessFlags里。

這是一個很重要的字段,記錄了類的很多屬性:

有了這些信息,創建對象的時候就可以檢查標記來決定是否創建Finalizer對象了。

 

以上。 

 

 

 

145 人贊同了該回答

 

 感謝 @FinalizerThread 同學邀請。

人在JVM,剛下晚班。

時間緊迫,簡單說幾句。

和這位同學一樣,我也是GC部門的員工,公司待遇確實不錯,這方面還是很有競爭力的。

至於我的工作嘛,跟垃圾回收密切相關!

你們也知道在Java中,除了基礎的強引用外,還有四種特殊的引用:

  • FinalReference
  • 軟引用(SoftReference)
  • 弱引用(WeakReference)
  • 虛引用(PhantomReference)

前面FinalizerThread同學提到的Finalizer其實就是FinalReference的子類。

我的工作就是在垃圾回收時,把這些個特殊引用一個個加入到它們各自對應的隊列裏面去。

拿上面FinalizerThread同學提到的Finalizer對象來說,就是我來把它加到它所指向的隊列中,再由FinalizerThread同學去從這個隊列裏面取出來處理的。

  

 

 

 898 人贊同了該回答

 

 這個問題我來簡單回答一下。

看了前面幾位的回答,真的是旱的旱死,澇的澇死。我一天天忙得氣都喘不過來,你們居然還有時間摸魚!

我算是JVM公司里每天到的最早的幾個了,跟隨Threads::create_vm就起來了。

和樓上兩位一樣的是我也有一個工作隊列,叫_vm_thread,其類型是VMOperationQueue

和樓上兩位不一樣的是他們工作在二樓Java層,而我工作在一樓native層。

工作節奏這個東西真的是不同部門差得很遠,我所在的部門就我一個人,是一個單例線程,我要乾的就是不斷從工作隊列裏面取出操作來執行。

這個隊列裏面裝的都是一個個封裝成VM_Operation的東西,這是它們的基類,具體來說,有幾十種操作,列舉一部分,你們隨意感受一下:

#define VM_OPS_DO(template)                       \
  template(None)                                  \
  template(ThreadStop)                            \
  template(ThreadDump)                            \
  template(PrintThreads)                          \
  template(FindDeadlocks)                         \
  template(ClearICs)                              \
  template(ForceSafepoint)                        \
  template(ForceAsyncSafepoint)                   \
  template(Deoptimize)                            \
  template(DeoptimizeFrame)                       \
  template(DeoptimizeAll)                         \
  template(ZombieAll)                             \
  template(Verify)                                \
  template(PrintJNI)                              \
  template(HeapDumper)                            \
  template(DeoptimizeTheWorld)                    \
  template(CollectForMetadataAllocation)          \
  template(GC_HeapInspection)                     \
  template(GenCollectFull)                        \
  template(GenCollectFullConcurrent)              \
  template(GenCollectForAllocation)               \
  template(ParallelGCFailedAllocation)            \
  template(ParallelGCSystemGC)                    \
  ······

 

其他就不說了,就拿你們最熟悉的垃圾回收來說,沒有了我,JVM的堆區內存恐怕早就垃圾堆成山了。

時間關係,先寫到這裏。

——————–分割線——————–

一覺醒來居然有這麼多贊,謝謝大家!

再補充幾句。

VM_Operation中還設置了一個模式,用來表示執行這個操作是否需要進入安全點,(比如垃圾回收就需要),是否需要加鎖執行。

enum Mode {
    _safepoint,       // blocking, safepoint
    _no_safepoint,    // blocking, no safepoint
    _concurrent,      // non-blocking, no safepoint
    _async_safepoint  // non-blocking, safepoint
 };

 

安全點的進入和退出都是我來發起的,執行的是SafepointSynchronizebegin()函數end()函數。

以上。 

 

 

 

本文用知乎體的風格簡單介紹了JVM中幾個內置線程的工作,希望對大家學習JVM有一點幫助。

 

如果喜歡本文歡迎幫忙轉發分享,也歡迎大家關注我的微信公眾號,更多有趣,更多風格的文章等你來看!

 

往期TOP5文章

真慘!連各大編程語言都擺起地攤了!

因為一個跨域請求,我差點丟了飯碗

完了!CPU一味求快出事兒了!

哈希表哪家強?幾大編程語言吵起來了!

一個HTTP數據包的奇幻之旅

 

 

 

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

南投搬家公司費用需注意的眉眉角角,別等搬了再說!

新北清潔公司,居家、辦公、裝潢細清專業服務

※教你寫出一流的銷售文案?

TensorFlow中讀取圖像數據的三種方式

  本文面對三種常常遇到的情況,總結三種讀取數據的方式,分別用於處理單張圖片、大量圖片,和TFRecorder讀取方式。並且還補充了功能相近的tf函數。

1、處理單張圖片

  我們訓練完模型之後,常常要用圖片測試,有的時候,我們並不需要對很多圖像做測試,可能就是幾張甚至一張。這種情況下沒有必要用隊列機制。

import tensorflow as tf
import matplotlib.pyplot as plt

def read_image(file_name):
    img = tf.read_file(filename=file_name)     # 默認讀取格式為uint8
    print("img 的類型是",type(img));
    img = tf.image.decode_jpeg(img,channels=0) # channels 為1得到的是灰度圖,為0則按照圖片格式來讀
    return img

def main( ):
    with tf.device("/cpu:0"):
         # img_path是文件所在地址包括文件名稱,地址用相對地址或者絕對地址都行 
            img_path='./1.jpg'
            img=read_image(img_path)
            with tf.Session() as sess:
            image_numpy=sess.run(img)
            print(image_numpy)
            print(image_numpy.dtype)
            print(image_numpy.shape)
            plt.imshow(image_numpy)
            plt.show()

if __name__=="__main__":
    main()

"""
輸出結果為:

img 的類型是 <class 'tensorflow.python.framework.ops.Tensor'>
[[[196 219 209]
  [196 219 209]
  [196 219 209]
  ...

 [[ 71 106  42]
  [ 59  89  39]
  [ 34  63  19]
  ...
  [ 21  52  46]
  [ 15  45  43]
  [ 22  50  53]]]
uint8
(675, 1200, 3)
"""

   和tf.read_file用法相似的函數還有tf.gfile.FastGFile  tf.gfile.GFile,只是要指定讀取方式是’r’ 還是’rb’ 。

2、需要讀取大量圖像用於訓練

  這種情況就需要使用Tensorflow隊列機制。首先是獲得每張圖片的路徑,把他們都放進一個list裏面,然後用string_input_producer創建隊列,再用tf.WholeFileReader讀取。具體請看下例:

def get_image_batch(data_file,batch_size):
    data_names=[os.path.join(data_file,k) for k in os.listdir(data_file)]
 
    #這個num_epochs函數在整個Graph是local Variable,所以在sess.run全局變量的時候也要加上局部變量。  
    filenames_queue=tf.train.string_input_producer(data_names,num_epochs=50,shuffle=True,capacity=512)
    reader=tf.WholeFileReader()
    _,img_bytes=reader.read(filenames_queue)
    image=tf.image.decode_png(img_bytes,channels=1)    #讀取的是什麼格式,就decode什麼格式
    #解碼成單通道的,並且獲得的結果的shape是[?, ?,1],也就是Graph不知道圖像的大小,需要set_shape
    image.set_shape([180,180,1])   #set到原本已知圖像的大小。或者直接通過tf.image.resize_images,tf.reshape()
    image=tf.image.convert_image_dtype(image,tf.float32)
    #預處理  下面的一句代碼可以換成自己想使用的預處理方式
    #image=tf.divide(image,255.0)   
    return tf.train.batch([image],batch_size) 

  這裏的date_file是指文件夾所在的路徑,不包括文件名。第一句是遍歷指定目錄下的文件名稱,存放到一個list中。當然這個做法有很多種方法,比如glob.glob,或者tf.train.match_filename_once

全部代碼如下:

import tensorflow as tf
import os
def read_image(data_file,batch_size):
    data_names=[os.path.join(data_file,k) for k in os.listdir(data_file)]
    filenames_queue=tf.train.string_input_producer(data_names,num_epochs=5,shuffle=True,capacity=30)
    reader=tf.WholeFileReader()
    _,img_bytes=reader.read(filenames_queue)
    image=tf.image.decode_jpeg(img_bytes,channels=1)
    image=tf.image.resize_images(image,(180,180))

    image=tf.image.convert_image_dtype(image,tf.float32)
    return tf.train.batch([image],batch_size)

def main( ):
    img_path=r'F:\dataSet\WIDER\WIDER_train\images\6--Funeral'  #本地的一個數據集目錄,有足夠的圖像
    img=read_image(img_path,batch_size=10)
    image=img[0]  #取出每個batch的第一個數據
    print(image)
    init=[tf.global_variables_initializer(),tf.local_variables_initializer()]
    with tf.Session() as sess:
        sess.run(init)
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess,coord=coord)
        try:
            while not coord.should_stop():
                print(image.shape)
        except tf.errors.OutOfRangeError:
            print('read done')
        finally:
            coord.request_stop()
        coord.join(threads)


if __name__=="__main__":
    main()

"""
輸出如下:
(180, 180, 1)
(180, 180, 1)
(180, 180, 1)
(180, 180, 1)
(180, 180, 1)
"""

  這段代碼可以說寫的很是規整了。注意到init裏面有對local變量的初始化,並且因為用到了隊列,當然要告訴電腦什麼時候隊列開始, tf.train.Coordinator 和 tf.train.start_queue_runners 就是兩個管理隊列的類,用法如程序所示。

  與 tf.train.string_input_producer相似的函數是 tf.train.slice_input_producer。 tf.train.slice_input_producer和tf.train.string_input_producer的第一個參數形式不一樣。等有時間再做一個二者比較的博客

 3、對TFRecorder解碼獲得圖像數據

  其實這塊和上一種方式差不多的,更重要的是怎麼生成TFRecorder文件,這一部分我會補充到另一篇博客上。

  仍然使用 tf.train.string_input_producer。

import tensorflow as tf
import matplotlib.pyplot as plt
import os
import cv2
import  numpy as np
import glob

def read_image(data_file,batch_size):
    files_path=glob.glob(data_file)
    queue=tf.train.string_input_producer(files_path,num_epochs=None)
    reader = tf.TFRecordReader()
    print(queue)
    _, serialized_example = reader.read(queue)
    features = tf.parse_single_example(
        serialized_example,
        features={
            'image_raw': tf.FixedLenFeature([], tf.string),
            'label_raw': tf.FixedLenFeature([], tf.string),
        })
    image = tf.decode_raw(features['image_raw'], tf.uint8)
    image = tf.cast(image, tf.float32)
    image.set_shape((12*12*3))
    label = tf.decode_raw(features['label_raw'], tf.float32)
    label.set_shape((2))
    # 預處理部分省略,大家可以自己根據需要添加
    return tf.train.batch([image,label],batch_size=batch_size,num_threads=4,capacity=5*batch_size)

def main( ):
    img_path=r'F:\python\MTCNN_by_myself\prepare_data\pnet*.tfrecords'  #本地的幾個tf文件
    img,label=read_image(img_path,batch_size=10)
    image=img[0]
    init=[tf.global_variables_initializer(),tf.local_variables_initializer()]
    with tf.Session() as sess:
        sess.run(init)
        coord = tf.train.Coordinator()
        threads = tf.train.start_queue_runners(sess=sess,coord=coord)
        try:
            while not coord.should_stop():
                print(image.shape)
        except tf.errors.OutOfRangeError:
            print('read done')
        finally:
            coord.request_stop()
        coord.join(threads)


if __name__=="__main__":
    main()

  在read_image函數中,先使用glob函數獲得了存放tfrecord文件的列表,然後根據TFRecord文件是如何存的就如何parse,再set_shape;這裡有必要提醒下parse的方式。我們看到這裏用的是tf.decode_raw ,因為做TFRecord是將圖像數據string化了,數據是串行的,丟失了空間結果。從features中取出image和label的數據,這時就要用 tf.decode_raw  解碼,得到的結果當然也是串行的了,所以set_shape 成一個串行的,再reshape。這種方式是取決於你的編碼TFRecord方式的。

再舉一種例子:

reader=tf.TFRecordReader()
_,serialized_example=reader.read(file_name_queue)
features = tf.parse_single_example(serialized_example, features={
    'data': tf.FixedLenFeature([256,256], tf.float32), ###
    'label': tf.FixedLenFeature([], tf.int64),
    'id': tf.FixedLenFeature([], tf.int64)
})
img = features['data']
label =features['label']
id = features['id']

  這個時候就不需要任何解碼了。因為做TFRecord的方式就是直接把圖像數據append進去了。

參考鏈接:

  https://blog.csdn.net/qq_34914551/article/details/86286184

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※幫你省時又省力,新北清潔一流服務好口碑

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

Raft共識算法

Raft共識算法在分佈式系統中是常用的共識算法之一,論文原文In Search of an Understandable Consensus Algorithm ,作者在論文中指出Poxas共識算法的兩大問題,其一是難懂,其二是應用到實際系統存在困難。針對Paxos存在的問題,作者的目的是提出一個易懂的共識算法,論文中有單獨一小節論述Raft是一個實用的、安全可用、有效易懂的共識算法。本文描述了Raft共識算法的細節,很多內容描述及引用圖片均摘自論文原文。

Raft概述

我們主要分以下三部分對Raft進行討論:

  • Leader election——a new leader must be chosen when
    an existing leader fails. (領導人選舉)
  • Log replication——the leader must accept log entries from clients and replicate them across the cluster,
    forcing the other logs to agree with its own.(日誌複製)
  • Safety——the key safety property for Raft. (安全性)

正常工作過程中,Raft分為兩部分,首先是leader選舉過程,然後在選舉出來的leader基礎上進行正常操作,比如日誌複製操作等。

一個Raft集群通常包含\(2N+1\)個服務器,允許系統有\(N\)個故障服務器。每個服務器處於3個狀態之一:leaderfollowercandidate。正常操作狀態下,僅有一個leader,其他的服務器均為follower。follower是被動的,不會對自身發出的請求而是對來自leader和candidate的請求做出響應。leader處理所有的client請求(若client聯繫follower,則該follower將轉發給leader)。candidate狀態用來選舉leader。狀態轉換如下圖所示:

為了進行領導人選舉和日誌複製等,需要服務器節點存儲如下狀態信息:

狀態 所有服務器上持久存在的
currentTerm 服務器最後一次知道的任期號(初始化為 0,持續遞增)
votedFor 在當前獲得選票的候選人的 Id
log[] 日誌條目集;每一個條目包含一個用戶狀態機執行的指令,和收到時的任期號
狀態 所有服務器上經常變的
commitIndex 已知的最大的已經被提交的日誌條目的索引值
lastApplied 最後被應用到狀態機的日誌條目索引值(初始化為 0,持續遞增)
狀態 在領導人里經常改變的 (選舉后重新初始化)
nextIndex[] 對於每一個服務器,需要發送給他的下一個日誌條目的索引值(初始化為領導人最後索引值加一)
matchIndex[] 對於每一個服務器,已經複製給他的日誌的最高索引值

Raft在任何時刻都滿足如下特性:

  • Election Safety:在一個任期中只能有一個leader;
  • Leader Append-Only:leader不會覆蓋或刪除日誌中的entry,只有添加entry(follower存在依據leader回滾日誌的情況);
  • Log Matching:如果兩個日誌包含了一條具有相同index和term的entry,那麼這兩個日誌在這個index之前的所有entry都相同;
  • Leader Completeness: 如果在某一任期一條entry被提交committed了,那麼在更高任期的leader中這條entry一定存在;(領導人選舉時會保證這一性質,後面會講到這個問題)
  • State Machine Safety:如果一個節點將一條entry應用到狀態機中,那麼任何節點也不會再次將該index的entry應用到狀態機里;

下面我們詳細討論這幾部分。

Leader選舉(Leader election)

一個節點初始狀態為follower,當follower在選舉超時時間內未收到leader的心跳消息,則轉換為candidate狀態。為了避免選舉衝突,這個超時時間是一個隨機數(一般為150~300ms)。超時成為candidate后,向其他節點發出RequestVoteRPC請求,假設有\(2N+1\)個節點,收到\(N+1\)個節點以上的同意回應,即被選舉為leader節點,開始下一階段的工作。如果在選舉期間接收到eader發來的心跳信息,則candidate轉為follower狀態。

在選舉期間,可能會出現多個candidate的情況,可能在一輪選舉過程中都沒有收到多數的同意票,此時再次隨機超時,進入第二輪選舉過程,直至選出leader或着重新收到leader心跳信息,轉為follower狀態。

正常狀態下,leader會不斷的廣播心跳信息,follower收到leader的心跳信息後會重置超時。當leader崩潰或者出現異常離線,此時網絡中follower節點接收不到心跳信息,超時再次進入選舉流程,選舉出一個leader。

這裏還有補充一些細節,每個leader可以理解為都是有自己的任期(term)的,每一期起始於選舉階段,直到因節點失效等原因任期結束。每一期選舉期間,每個follower節點只能投票一次。圖中t3可能是因為沒有獲得超半數票等造成選舉失敗,須進行下一輪選舉,此時follower可以再次對最先到達的candidate發出的RequestVote請求投票(先到先得)。

對所有的請求(RequestVote、AppendEntry等請求),如果發現其Term小於當前節點,則拒絕請求,如果是candidate選舉期間,收到不小於當前節點任期的leader節點發來的AppendEntry請求,則認可該leader,candidate轉換為follower。

日誌複製(Log replication)

leader選舉成功后,將進入有效工作階段,即日誌複製階段,其中日誌複製過程會分記錄日誌和提交數據兩個階段。

整個過程如下:

  1. 首先client向leader發出command指令;(每一次command指令都可以認為是一個entry,或者說是日誌項)
  2. leader收到client的command指令后,將這個command entry追加到本地日誌中,此時這個command是uncommitted狀態,因此並沒有更新節點的當前狀態;
  3. 之後,leader向所有follower發送這條entry,也就是通過日誌複製AppendEntries消息 (可以是一條也可以是多條日誌項) 將日誌項複製到集群其他節點上,follower接收到后 (這裡有判斷條件的,並不是所有leader發送來的日誌項都無條件接收,而且還可能存在本地與leader日誌不一致的情況,後面會詳細說明,這裏先看正常情況) 追加到本地日誌中,並回應leader成功或者失敗;
  4. leader收到大多數follower的確認回應后,此entry在leader節點由uncommitted變為committed狀態,此時按這條command更新leader狀態,或者說將該日誌項應用到狀態機,然後向client返回執行結果;
  5. 在下一心跳中(這裏也可以是或者說多數情況下是新的日誌複製AppendEntries消息,會帶有相關信息,後面后詳細的字段說明會講到),leader會通知所有follower更新確認的entry,follower收到后,更新狀態,這樣,所有節點都完成client指定command的狀態更新。

可以看到client每次提交command指令,服務節點都先將該指令entry追加記錄到日誌中,等leader確認大多數節點已追加記錄此條日誌后,在進行提交確認,更新節點狀態。如果還對這個過程有些模糊的話,可以參考Raft動畫演示,較為直觀的演示了領導人選舉及日誌複製的過程。

安全(Safety)

前面描述了Raft算法是如何選舉和複製日誌的。然而,到目前為止描述的機制並不能充分的保證每一個狀態機會按照相同的順序執行相同的指令。我們需要再繼續深入思考以下幾個問題:

  • 第一個問題,leader選舉時follower收到candidate發起的投票請求,如果同意就進行回應,但具體的規則是什麼呢?是所有的follower都有可能被選舉為領導人嗎?
  • 第二個問題,leader可能在任何時刻掛掉,新任期的leader怎麼提交之前任期的日誌條目呢?

選舉限制

針對第一個問題,之前並沒有細講,如果當前leader節點掛了,需要重新選舉一個新leader,此時follower節點的狀態可能是不同的,有的follower可能狀態與剛剛掛掉的leader相同,狀態較新,有的follower可能記錄的當前index比原leader節點的少很多,狀態更新相對滯后,此時,從系統最優的角度看,選狀態最新的candidate為佳,從正確性的角度看,要確保Leader Completeness,即如果在某一任期一條entry被提交成功了,那麼在更高任期的leader中這條entry一定存在,反過來講就是如果一個candidate的狀態舊於目前被committed的狀態,它一定不能被選為leader。具體到投票規則:
1) 節點只投給擁有不比自己日誌狀態舊的節點;
2)每個節點在一個term內只能投一次,在滿足1的條件下,先到先得;

我們看一下請求投票 RPC(由候選人負責調用用來徵集選票)的定義:

參數 解釋
term 候選人的任期號
candidateId 請求選票的候選人的 Id
lastLogIndex 候選人的最後日誌條目的索引值
lastLogTerm 候選人最後日誌條目的任期號
返回值 解釋
term 當前任期號,以便於候選人去更新自己的任期號
voteGranted 候選人贏得了此張選票時為真

接收者實現:

  1. 如果term < currentTerm返回 false
  2. 如果 votedFor 為空或者為 candidateId,並且候選人的日誌至少和自己一樣新,那麼就投票給他

可以看到RequestVote投票請求中包含了lastLogIndex和lastLogTerm用於比較日誌狀態。這樣,雖然不能保證最新狀態的candidate成為leader,但能夠保證被選為leader的節點一定擁有最新被committed的狀態,但不能保證擁有最新uncommitted狀態entries。

提交之前任期的日誌條目

領導人知道一條當前任期內的日誌記錄是可以被提交的,只要它被存儲到了大多數的服務器上。但是之前任期的未提交的日誌條目,即使已經被存儲到大多數節點上,也依然有可能會被後續任期的領導人覆蓋掉。下圖說明了這種情況:

如圖的時間序列展示了為什麼領導人無法決定對老任期號的日誌條目進行提交。在 (a) 中,S1 是領導者,部分的複製了索引位置 2 的日誌條目。在 (b) 中,S1崩潰了,然後S5在任期3里通過S3、S4和自己的選票贏得選舉,然後從客戶端接收了一條不一樣的日誌條目放在了索引 2 處。然後到 (c),S5又崩潰了;S1重新啟動,選舉成功,開始複製日誌。在這時,來自任期2的那條日誌已經被複制到了集群中的大多數機器上,但是還沒有被提交。如果S1在(d)中又崩潰了,S5可以重新被選舉成功(通過來自S2,S3和S4的選票),然後覆蓋了他們在索引 2 處的日誌。反之,如果在崩潰之前,S1 把自己主導的新任期里產生的日誌條目複製到了大多數機器上,就如 (e) 中那樣,那麼在後面任期裏面這些新的日誌條目就會被提交(因為S5 就不可能選舉成功)。 這樣在同一時刻就同時保證了,之前的所有老的日誌條目就會被提交。

為了消除上圖裡描述的情況,Raft永遠不會通過計算副本數目的方式去提交一個之前任期內的日誌條目。只有領導人當前任期里的日誌條目通過計算副本數目可以被提交;一旦當前任期的日誌條目以這種方式被提交,那麼由於日誌匹配特性,之前的日誌條目也都會被間接的提交。

當領導人複製之前任期里的日誌時,Raft 會為所有日誌保留原始的任期號。

對Raft中幾種情況的思考

follower節點與leader日誌內容不一致時怎麼處理?

我們先舉例說明:正常情況下,follower節點應該向B節點一樣與leader節點日誌內容一致,但也會出現A、C等情況,出現了不一致,以A、B節點為例,當leader節點向follower節點發送AppendEntries<prevLogIndex=7,prevLogTerm=3,entries=[x<-4]>,leaderCommit=7時,我們分析一下發生了什麼,B節點日誌與prevLogIndex=7,prevLogTerm=3相匹配,將index=7x<-5)這條entry提交committed,並在日誌中新加入entryx<-4,處於uncommitted狀態;A節點接收到時,當前日誌index<prevLogIndexprevLogIndex=7,prevLogTerm=3不相匹配,拒接該請求,不會將x<-4添加到日誌中,當leader知道A節點因日誌不一致拒接了該請求后,不斷遞減preLogIndex重新發送請求,直到A節點index,termprevLogIndex,prevLogTerm相匹配,將leader的entries複製到A節點中,達成日誌狀態一致。

我們看一下附加日誌AppendEntries RPC(由領導人負責調用複製日誌指令;也會用作heartbeat)的定義:

參數 解釋
term 領導人的任期號
leaderId 領導人的 Id,以便於跟隨者重定向請求
prevLogIndex 新的日誌條目緊隨之前的索引值
prevLogTerm prevLogIndex 條目的任期號
entries[] 準備存儲的日誌條目(表示心跳時為空;一次性發送多個是為了提高效率)
leaderCommit 領導人已經提交的日誌的索引值
返回值 解釋
term 當前的任期號,用於領導人去更新自己
success 跟隨者包含了匹配上 prevLogIndex 和 prevLogTerm 的日誌時為真

接收者實現:

  1. 如果 term < currentTerm 就返回 false;
  2. 如果日誌在 prevLogIndex 位置處的日誌條目的任期號和 prevLogTerm 不匹配,則返回 false;
  3. 如果已經存在的日誌條目和新的產生衝突(索引值相同但是任期號不同),刪除這一條和之後所有的;(raft中follower處理不一致的一個原則就是一切聽從leader)
  4. 附加日誌中尚未存在的任何新條目;
  5. 如果 leaderCommit > commitIndex,令 commitIndex 等於 leaderCommit 和 新日誌條目索引值中較小的一個;

簡單總結一下,出現不一致時核心的處理原則是一切遵從leader。當leader向follower發送AppendEntry請求,follower對AppendEntry進行一致性檢查,如果通過,則更新狀態信息,如果發現不一致,則拒絕請求,leader發現follower拒絕請求,出現了不一致,此時將遞減nextIndex,並重新給該follower節點發送日誌複製請求,直到找到日誌一致的地方為止。然後把follower節點的日誌覆蓋為leader節點的日誌內容。

leader掛掉了,怎麼處理?

前面可能斷斷續續的提到這種情況的處理方法,首要的就是選出新leader,選出新leader后,可能上一任期還有一些entries並沒有提交,處於uncommitted狀態,該怎麼辦呢?處理方法是新leader只處理提交新任期的entries,上一任期未提交的entries,如果在新leader選舉前已經被大多數節點記錄在日誌中,則新leader在提交最新entry時,之前處於未提交狀態的entries也被committed了,因為如果兩個日誌包含了一條具有相同index和term的entry,那麼這兩個日誌在這個index之前的所有entry都相同;如果在新leader選舉前沒有被大多數節點記錄在日誌中,則原有未提交的entries有可能被新leader的entries覆蓋掉。

出現網絡分區時怎麼處理?

分佈式系統中網絡分區的情況基本無法避免,出現網絡分區時,原有leader在分區的一側,此時如果客戶端發來指令,舊leader依舊在分區一測進行日誌複製的過程,但因收不到大多數節點的確認,客戶端所提交的指令entry只能記錄在日誌中,無法進行提交確認,處於uncommitted狀態。而在分區的另一側,此時收不到心跳信息,會進入選舉流程重新選舉一個leader,新leader負責分區零一側的請求,進行日誌複製等操作。因為新leader可以收到大多數follower確認,客戶端的指令entry可以被提交,並更新節點狀態,當網絡分區恢復時,此時兩個leader會收到彼此廣播的心跳信息,此時,舊leader發現更大term的leader,舊leader轉為follower,此時舊leader分區一側的所有操作都要回滾,接受新leader的更新。

成員變更

在分佈式系統中,節點數量或者說服務器數量不是一成不變的,我們有可能會隨時增減節點數量,當增加節點時,有可能會出現兩個leader選舉成功的情況,主要是新舊配置不一致造成的,怎麼處理呢?最簡單粗暴的就是把目前所有節點都停掉,更新配置,再重啟所有節點,但會造成一段時間服務不可用,很多情況下這是不能被允許的。raft的解決辦法原論文中是聯合共識(Joint Consensus)的辦法,後來又提出了單節點變更(single-server changes)的方法。我們下面詳細描述一下這個問題。

Raft要求,在任一任期內,只能有一個leader,而成員變更的麻煩就在於,成員變更時可能會出現兩個leader,以一個例子說明:原系統有3個節點,成員為[1,2,3],現新增成員4、5。假設在成員變更時,1、2與3發生分區,此時,[1,2]為一組,1通過1、2兩節點選舉為leader,而5通過3、4、5選舉為leader,就形成了2個leader並存的情況。

因為每個節點新舊配置更新的時間不同,造成了在某一時刻,可能會存在新舊配置的兩個大多數情況的存在,上圖中,舊配置的大多數是兩個節點,而新配置的大多數是三個節點,在圖中紅線頭的時刻存在兩個大多數的情況,如果此時出現網絡分區進行選舉時就會出現兩個leader的情況。

怎麼解決呢?用什麼辦法才能不讓上面兩個大多少情況的出現呢?可通過單節點變更解決,即通過一次變更一個節點實現成員變更。主要思想是利用“一次變更一個節點,不會同時存在舊配置和新配置的兩個大多數”的特性,實現成員變更。比如上面的情況,就可先將3節點集群[A,B,C]變更為4節點集群[A,B,C,D],再將4節點集群變更為5節點集群[A,B,C,D]。

為什麼單節點變更不會造成兩個大多數情況的出現呢?我們可以進行如下推理:假設原節點數為2n+1,則舊配置的大多數major_old=n+1,新加入1個節點,新配置節點數為2n+2,則新配置的大多數為major_new=n+2,同時存在兩個大多數所需節點數目為major=major_old+major_new=n+1+n+2=2n+3>2n+2,也就是兩個大多數所需節點數超出了節點總數,故不存在這種情況,如何是刪除成員,其推理過程類似,結論相同。

具體的,我們依舊以這個3節點集群變更為5節點集群為例進行說明。假設現3節點集群[A,B,C],節點A為leader,配置為[A,B,C],我們先向集群加入節點D,新的配置為[A,B,C,D],成員變更通過以下兩步實現:

  • 第一步,leader節點A向新節點D同步數據;
  • 第二步,leader將新配置[A,B,C,D]作為一個日誌項複製到新配置中的所有節點(A,B,C,D)上,然後將新配置的日誌項應用到本地狀態機,完成單節點變更。

在變更后,現有集群的配置項就是[A,B,C,D],添加E節點也是同樣的步驟。上面的描述如果理解的比較模糊的話,其實raft是採用將修改集群配置的命令放在日誌條目中來處理的,其修改配置項,就是一條日誌項,其流程與普通的日誌項相同,只不過最後狀態機執行的結果是配置變更。

日誌壓縮

日誌壓縮主要是為了解決無限增長的日誌與有限的存貯空間的矛盾,可以想一個問題:對於已經committed的日誌項,是否有必要一直保存下去?如果沒有必要的話,是否可以對部分已committed的日誌項刪減或壓縮呢?raft的主要的解決辦法是採用快照進行日誌壓縮。

如上圖所示,對於日誌索引5之前的日誌項可以刪除,只保留一個快照(保存有當前狀態以及一些任期索引號等元信息)即可。

具體工程實現時,一般每個節點獨立打快照,當日誌超過一定量會觸發快照操作,具體實現以及更多細節待以後深究。

Client Protocol

raft共識算法真正工作時還需有一個客戶端協議(client protocol),綜合解決一些列的問題。比如會遇到下面這些問題:client怎麼和集群交互呢?client如果知道leader節點的話,可以直接將command發給leader節點,如果不知道的話,可以隨意發給集群中已知的節點,節點會將client的請求轉給leader。其實上面還有個問題,client發送請求(或者command)給leader,但是leader遲遲不給回應怎麼辦?重試是一個辦法。連接的leader崩潰了client怎麼辦?如果client超時重發command,怎麼保證command不被狀態機執行兩次?client生成command的時候要給加上唯一ID,當server的日誌中已存在相同command時會忽略。

附錄

這裏附加一張論文中的截圖,裏面詳細講明了不同節點需要維護什麼信息,每個消息是怎麼定義的,以及消息該如何處理等,不包含日誌壓縮以及成員變更部分:

這裏補充一點,raft共識算法與pbft共識算法解決的是不同的問題,即raft節點不能存在惡意節點,節點消息可以延遲、丟失,但不能造假或作惡,即不能存在拜占庭節點。

本文對raft共識算法做了一個整體的梳理學習,可能會存在某些細節描述不清晰的地方,在真正工程代碼實現時,還會存在更多的細節問題,同時,這裏缺少證明為什麼raft算法是正確的證明,有待今後更深一步理解共識算法后再行補充。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

新北清潔公司,居家、辦公、裝潢細清專業服務

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧