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

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

孫振東在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行銷專家,教你從零開始的技巧

使用git暢遊代碼的海洋

如果把互聯網上的紛繁代碼比作一片海洋,那麼git就是在這片海洋上航行的船隻,正所謂“水可載舟,亦可覆舟”,git使用恰當可以遠征星辰,不然可能會墜入無窮無盡的代碼海洋無法自拔。書回正傳,我們的征途是星辰大海!

揚帆起航

git的下載安裝暫且不表,可參考網站https://git-scm.com/downloads。

git的安裝只是個入門條件,下面如何使用git命令控制代碼才是遠征的基礎。那麼從何開始呢?讓我們一步步說起。

要想揚帆起航,首先得有艘帆船吧。我們要在本地建立一個項目,之後將本地項目初始化為git倉庫,可以使用

git init [–bare]

初始化本地項目,執行後會在當前執行目錄下生成.git文件夾,這也就是我們的帆船了。[–bare]可選參數,可初始化裸倉庫,裸倉庫可以與源碼項目分離,此時在當前目錄下不會生成.git文件夾,而是直接生成.git文件夾下的包括hooks、info、objects、refs共四個文件夾和config、description、HEAD三個文件。

在初始化裸倉庫后若想關聯源碼,只需進入hooks目錄新建post-receive.sample文件,並添加如下內容:

git –work-tree=<project-dir> –git-dir=<local-url> checkout -f

其中<project-dir>為本地項目文件路徑,<local-url>為本地git倉庫路徑。

 

對於當前目錄下的.git文件夾所代表的本地倉庫,不同的帆船有不同的配置,我們使用

git config –list

显示當前git配置。如果內容過多可以使用上下鍵翻頁,查看結束按q退出即可。同時使用

git config –e [–global]

以vim形式編輯修改git配置文件。

git config [–global] user.name “name”

形式修改指定配置,其中[–global]可選,為全局配置,name為用戶名配置。

 

 

git儀錶盤

 

 

 

 

 

 

 

圖 1 Git指令關係圖

 

建立git倉庫作為帆船后,就可以在當前目錄下任意使用git指令遨遊了。我們需要先認識下git控制的命令儀錶盤。

如圖1所示,git整艘船主要分為四大區域,包括Remote遠程倉庫,Repository本地倉庫,Index/Stage暫存區,Workspace工作區,不同區域之間可以使用相關指令進行代碼操作。在上節使用git init創建的文件夾下,會生成名為.git的隱藏文件夾,四大區域的配置都儲存在該文件夾下。其中在遠程倉庫和本地倉庫中,儲存有不同的branch分支,工作區和暫存區的文件只能針對某一分支進行修改提交操作,當然也可以使用分支操作指令對不同的分支進行增刪合併等操作。

針對一些倉庫的操作指令,使用前請務必保持頭腦清醒,不然你的一個蝴蝶煽動翅膀似的操作,可能會引發倉庫里的一場代碼風暴,造成不可挽回的結果。

根據不同命令操作區域,可以將git命令大致分為全局显示信息、工作區與遠程倉庫交互、工作區與本地倉庫交互、工作區與暫存區交互、暫存區與本地倉庫交互、本地倉庫與遠程倉庫交互、以及倉庫內部分支操作。下面對這些命令進行了粗略的統計介紹,詳細使用方式可參考git官方文檔介紹,如有不足還請補充。

显示信息

git help [command]

獲取命令的幫助信息。

 

git status

显示所有變更文件

 

git log [–stat] [–graph]

显示當前分支的版本信息。[–stat]參數指定显示commit發生變更的文件。[–graph]參數以數據圖形式查看合併分支記錄。

 

git blame <file>

显示文件的每一行最後修改的版本和作者

 

git show [commit][:filename]

显示某次提交的變更內容。其中[commit]為某次提交版本,也可在:後邊加參數[filename]指定查看某個文件內容。

 

git diff [HEAD] [first-branch] [second-branch]

显示文件差異。無參時比較緩存區和上一次commit的差異;[HEAD]為工作區與當前分支最新commit的差異;或者兩個分支之間的差異。

 

git reflog

显示已執行過的所有git動作日誌。

 

工作區與遠程倉庫

git pull <remote> <branch>

拉取遠程倉庫的變化,並與本地分支合併。<remote>為遠程倉庫名,<branch>為遠程倉庫中的某一分支名。

工作區與本地倉庫

git checkout [–b] <branch> [tag]

將暫存區切換到分支名。其中[-b]參數可選,當分支不存在時則創建,<branch >必須,為本地倉庫分支,[tag]可選,指定切換到倉庫分支中的某條標籤,不標註則默認為切換分支的最近一次提交。

工作區與暫存區

git add <dir>

添加指定目錄到暫存區,<dir>為添加路徑,允許多個,包括子目錄都會添加到暫存區中等待提交。

 

git rm [–cached] <file>

刪除暫存區中的文件,<file>為暫存區中要刪除的文件全路徑,[–cache]可選參數,只會停止繼續追蹤指定文件,但該文件目前仍然保留在暫存區。

 

git mv <file-old> <file-new>

修改暫存區中的文件名,<file-old>為原文件全路徑,<file-new>為修改后的文件全路徑。

 

 

git tag

查看暫存區中的所有標籤信息。

 

git tag –a <tag> [commit]

新建一個標籤。[tag]為標籤名;[commit]為指定的一次從暫存區到本地倉庫的提交中,默認為最新一次提交。

 

git tag –d [tag]

刪除本地標籤。

暫存區與本地倉庫

git commit [–amend] [–m <message>] [file] [-a] [-v]

從暫存區提交到本地倉庫。其中[–amend]重做上次從本地項目到暫存區的commit,當代碼與上次提交相比無變化時使用,只修改上次commit的<message>內容;[-m]參數為提交信息,<message>必寫且詳寫,以區別提交代碼的修改內容;[file]為指定暫存區中的文件;[-a]可直接提交項目中的變化到本地倉庫,在沒有新增文件時不需每次先git add提交到暫存區再提交到本地倉庫;[-v]可以在提交時显示所有變化文件的diff信息。

 

git cherry-pick [commit]

選擇一個commit版本合併到當前分支,[commit]為暫存區中的commit版本。

 

本地倉庫與遠程倉庫

git remote [-v]

查看關聯的遠程倉庫信息。[-v]可以查看詳細信息。

 

git remote add <remote-name> <remote-url>

本地路徑關聯遠程倉庫。

<remote-name>必要參數,為遠程倉庫的名字,默認是origin。

<remote-url>必要參數,為遠程倉庫的地址,git服務器通常都是以.git結尾。

 

git remote remove <remote-name>

刪除關聯的遠程倉庫。其中<remote-name>為遠程倉庫的名字。

 

git push [remote] [branch] [–force]

推送本地指定分支到遠程倉庫。[remote]為遠程倉庫名,[branch]為本地分支名,[–force]為強制推送本地到遠程,如有衝突則覆蓋。

 

git fetch <remote>

將遠程倉庫拉到本地倉庫。<remote>為遠程倉庫名。

 

git clone <url> [name]

創建一個本地倉庫。<url>必須,可以是遠程git服務器上的倉庫,也可以是本地倉庫。[name]選填是創建的新倉庫名,默認與原倉庫名一致。

 

分支指令

git branch [-r] [-a]

查看分支信息,無參只會查看本地倉庫所有分支,[-r]是遠程倉庫所有分支,[-a]則是包括本地和遠程倉庫所有的所有分支。

git branch [branch-name] [commit]

在本地倉庫新建分支,但暫存區仍然指向當前分支。其中[branch-name]為新建的本地倉庫分支;[commit]可將分支指向指定commit版本。

 

git branch –track [local-branch-name] [remote-branch]

新建一個分支,並連接指定的遠程分支。其中[local-branch-name]為本地倉庫新建分支,[remote-branch]為遠程倉庫分支。

 

git branch –set-upstream [local-branch] [remote-branch]

連接本地倉庫分支與遠程倉庫分支,其中[local-branch]為本地倉庫已有分支,[remote-branch]為遠程倉庫分支。

 

git branch –d [branch]

刪除本地倉庫分支,[branch]為本地倉庫中的已有分支。

 

git branch -m [branch-old] [branch-new]

修改本地倉庫分支,其中[branch-old]為原分支,[branch-new]為改名后的分支。

 

git branch –dr [remote-branch]

刪除遠程倉庫分支,[remote-branch]為遠程倉庫中的分支。不推薦使用,如果遠程倉庫未更新,可能會執行失敗,推薦使用git push origin-delete [remote-branch]。

 

git merge <local-branch>

合併指定分支到當前分支,<local-branch>為本地倉庫中的已有分支。

 

git rebase <remote-branch>

將當前分支的提交複製到指定的遠程分支上,<remote-branch>為指定遠程倉庫中的已有分支。

 

git reset [–mixed|–soft|–hard] [commit]

重置倉庫索引,重置一旦清空后的內容不會在倉庫歷史版本中留下歷史記錄。[–mixed]為默認參數,重置后只在工作區保留原節點修改文件,清空暫存區和本地倉庫並均恢復到指定重置節點;[–soft]為軟重置,重置后在工作區和暫存區均保留原節點修改文件,清空本地倉庫並恢復到指定重置節點;[–hard]為硬重置,重置后均不會保留原節點修改文件。[commit]為要重置的節點號。

 

git revert [commit]

還原文件到之前修改提交節點時,會在倉庫歷史版本中留下歷史記錄。[commit]為要還原的節點號。

常規操作

git這艘大船雖然功能繁雜,但是用起來是有章可循的。入門之後就駕駛下這艘大船來試試吧。

一般git有三種工作流程,包括Git flow,項目存在兩個長期分支(主分支master和開發分支develop),適用於基於版本發布的普通項目;Github flow,只有一個長期分支(主分支master),適用於持續發布的小型項目;Gitlab flow,項目存在多個長期分支,其中主分支master是其他所有分支的上游,只有上游分支採納的代碼才能應用到其下游分支,適用於長期維護的大型項目。

圖2展示了一次項目git流程演變過程,在master主分支上有Tag1-Tag4四次代碼更新,其中基於Tag2對應的版本1號創建了新的branch1分支,新分支創建后自動生成了版本2號並打上了Tag2-1標籤,之後master主分支和branch1分支都同時進行了代碼演變,在branch1分支提交的版本4號及標籤Tag2-3之後,branch1分支合併到了master主分支,合併前master主分支位於版本5號,合併后可能重新生成版本6號,並打上新的tag4標籤,之後master主分支修改提交為版本7號,而branch1分支則停留在tag2-3標籤的位置處。

 

 

 

 

 圖 2 Git流程示意圖

 

在這份項目流程中,分支創建之後,可能在不同的階段修改提交文件,根據對文檔的讀取權限範圍,我們可以形象地將這些階段劃分為三種身份類型,船長、水手和遊客。船長身份,作為項目管理者,主要負責遠程倉庫和本地倉庫之間的分支操作,協調分支衝突;水手身份,作為項目貢獻者,主要負責某一分支的迭代更新;遊客身份,作為項目使用者,只是訪問使用倉庫及其分支內容,不能提交任何修改。同一人在項目的不同階段可以是其中任意一種身份,下面以這三種身份為維度簡單介紹下使用到的相關git指令步驟,並輔以示意圖方式直觀解釋git指令執行前後git項目變化。

 

 

項目使用者-遊客

        作為git項目的遊客,當然只能將項目從遠程倉庫拉取到本地使用,期間除了切換倉庫分支外不會涉及其他遠程操作。

 

git pull origin master

拉取遠程倉庫origin的master分支到本地。

 

git checkout branch1 tag1

切換到分支branch1。

 

 

項目貢獻者-水手

        作為項目的水手,除了可以使用遊客的功能指令外,還會涉及到修改工作區文件,並將工作區文件提交到暫存區和倉庫等任務。通常水手只需要維護倉庫中的某一條分支並只對該分支負責,因此水手更注重工作區的代碼文件修改工作。

 

git pull origin dev:branch1

拉取遠程倉庫origin的dev分支到本地,並與本地branch1分支合併。工作區中文件即显示branch1分支,可在工作區做文件修改操作。

 

git add .

在工作區的文件修改之後,可先添加當前目錄所有文件到暫存區,之後可繼續修改工作區其他文件,也可將暫存區文件提交到本地倉庫。

 

git rm –cached file

針對工作區編譯生成的配置file文件,一般不需提交到倉庫,可使用該命令將file文件從暫存區刪除並停止後續追蹤。另外一種添加忽略文件的方式,在.git文件夾的同級目錄下新建.gitignore文件,在該文件中根據規則增加要忽略的文件路徑,之後將該文件提交到本地倉庫中。

 

git commit –m “commit message 1”

在確保工作區的所有修改文件均已提交到暫存區后,便將暫存區的修改提交到本地倉庫,同時附帶當次提交信息。每次提交都會在本地倉庫生成一個新的提交commit版本號,由於提交的commit版本號是冗長的sha1碼,所以為了方便後期溯源,通常會在主要的commit號版本上再打一個鮮明的標籤以作標記。

 

 

 

git tag tag1 1

在提交的commit版本號為1的節點上打標籤,打上標籤后的commit號便可使用簡短的標籤名tag1訪問,以便後期對該節點溯源。

 

 

 

git push origin branch1:dev –tags

將本地branch1分支及相關標籤推送到遠程倉庫origin的dev分支。如果本地branch1分支已經與遠程dev分支建立追蹤關係,也可直接使用

git push origin branch1 –tags

指令。

 

git branch –set-upstream-to=origin/dev branch1

設置本地倉庫的branch1分支與遠程倉庫origin的dev分支的追蹤關係。通常從遠程分支pull到本地的分支都已經建立了追蹤關係,不需要手動修改。

 

git branch –vv

查看本地分支及追蹤的遠程分支信息。

 

項目管理者-船長

        作為項目的船長,自然擁有整個git這艘大船的項目所有權限,除了使用水手的操作指令外,另需完成分支增刪合併等任務。通常船長是項目倉庫的創建者,負責管理維護倉庫的各分支關係,對項目的整個倉庫負責,因此相較於水手,船長更注重倉庫的分支管理相關工作。

 

git clone https://github.com/xxx.git -b dev

克隆遠程倉庫的dev分支到本地,默認會將文件更新到本地倉庫建立的同名dev分支。

 

git checkout –b branch1 origin/dev

在本地倉庫新建branch1分支,與遠程倉庫origin中的dev分支對應,並在本地切換到branch1分支。如果不指定遠程倉庫及分支信息origin/dev,則默認從本地倉庫dev分支創建。至此可以切換為水手身份,從該分支更新代碼,並將修改文件提交到該branch1分支。

 

git add .

git commit –m “modify file commit”

在完成對工作區文件的修改之後,使用水手身份將工作區的修改提交到本地branch1分支。

 

git checkout dev

切換到本地倉庫的dev主分支,作為本地倉庫與遠程倉庫代碼合併的操作分支。

 

git fetch origin dev

拉取遠程倉庫origin中的dev分支到本地倉庫當前dev分支

 

git pull

將本地倉庫dev分支的文件修改合併到工作區。

 

git merge branch1

將本地倉庫的branch1分支合併到當前dev分支。如果當前dev分支與branch1分支有衝突,需要根據衝突文件提示分別修改,之後再重新合併該分支。

 

git checkout branch1 build/files

或者只將branch1分支的build/files目錄下所有文件合併到本地倉庫的當前dev分支。同樣需要做衝突處理。

 

git push origin dev:dev –tags

向遠程倉庫origin中的dev分支並推送本地倉庫dev分支。推送時如果確認以本地分支覆蓋遠程分支,則可使用

 

git push —force origin dev:dev –tags

強制推送。最後如果想刪除遠程分支,有以下兩條指令

 

git push origin –delete dev

git push origin :dev

這兩種方式都可以刪除指定的遠程倉庫origin中的dev分支。

 

應急預案

上面的圖2Git流程圖簡單涉及了一次版本演變過程中的一些git信息,包括分支切換,打標籤等,看上去簡單易懂,而實際我們工作中駕船航行時卻並不總是風平浪靜。

        通常出現的緊急情況需要修改分支版本,包括變基、還原、重置等操作,針對不同場景需要選擇不同的操作方式。

變基

git rebase master

變基會將當前分支的修改文件複製到master分支,同時創建新的commit版本號並修改項目的歷史記錄。當master分支已經更新,且確認當前分支與master分支沒有衝突,那可以使用變基以便當前分支獲取master分支的更新。

 

 

 

 

重置

git reset 1

將HEAD重置到歷史提交版本1的狀態,還原倉庫和暫存區的文件與提交版本1一致,工作區維持修改文件狀態。

 

 

 

 

還原

 

git revert 1

重新創建一次提交版本節點,文件狀態與歷史提交版本1一致,工作區、暫存區與倉庫均保持一致。提交版本2相對於提交版本1新增了file.txt文件,執行該指令后,在提交版本3中將恢復到提交版本1的狀態,因此提交版本3相對於提交版本2則刪除了file.txt文件。

 

 

強制遠程覆蓋本地

git fetch –all

拉取遠程所有倉庫到本地倉庫,工作區不會有任何合併更新。

 

git reset –hard origin/dev

把工作區HEAD指向最新的遠程倉庫origin中的dev版本。

待補充

除此之外在駕馭git這艘大船時肯定還會出現各種意外,屆時將酌情補充。

 

 

 

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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

戴姆勒與歐洲五集團合作 擬在德國大幅增設加氫站

戴姆勒集團日前宣佈,將與法國液化空氣集團、德國林德集團、奧地利石油天然氣集團(OMV)、荷蘭皇家殼牌集團、法國道達爾集團合作,啟動一項名為“H2 流動”的計畫,到2017年將德國現有的15座加氫站增加到100座,到2023年增加到400座。以效刺激氫燃料電池車市場。

除了在城市中心區大量設置加氫站之外,還將在德國主要高速公路沿線設置加氫站,兩座加氫站之間的距離不超過90公里。這項加氫站建設計畫耗資巨大。戴姆勒集團預計,為了修建400座加氫站,大約需要3.5億歐元。

加氫站是發展氫燃料電池車的必備條件,各國都在陸續興建,但此前一直進展緩慢,去年全球僅新增27座加氫站。根據美國能源部最新公佈的資料,目前美國境內只有10座公共加氫站。

由於氫燃料電池車實現了零排放,有利於保護環境,同時降低對石油的依賴,被稱為“終極環保車”。此外,氫燃料電池車續駛里程較長,且幾分鐘內就可以完成加氫工作。目前氫燃料電池車市場處於起步階段,大多採用租賃方式。

今年1月,戴姆勒與福特、日產簽署了戰略聯盟協定,在燃料電池車領域展開深度合作,聯合研發可以共用的燃料電池堆、燃料電池系統及其他燃料電池車部件,並計畫2017年推出各自的量產燃料電池車。

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

【其他文章推薦】

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

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

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

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

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

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

雷諾擬與LG Chem合作開發時速400公里的電動車

全球最大可充電電池製造商樂金化學公司(LG Chem),和歐洲電動車先驅雷諾汽車(Renault)聯手,希望未來幾年將電動車的最高時速加快一倍。

據韓國總統朴槿惠的首席經濟幕僚趙源東(Cho Won Dong)透露,這兩家公司正在考慮開發最快時速可達400公里的電動車。目前電動車的最高時速為每小時200公里。

為加強雙邊合作,朴槿惠4日還拜會了雷諾在巴黎南方設立的電動車測試中心。

據悉,兩家公司目前還不會簽署了解備忘錄(MOU),但原則上同意互相合作,正在協商細節上的歧異。由於要開發高速電動車,鋰電池是最重要的環節之一,因此對於雷諾來說,與樂金化學的合作十分重要。

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

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

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

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

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

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

※回頭車貨運收費標準