在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網頁設計為架站首選

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

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

※回頭車貨運收費標準

Model S銷量不如預期 Tesla三季度財測不佳

美國電動車製造商特斯拉(Tesla Motors)於美股5日收盤後公佈的上季汽車銷量不如預期、賣給其他車商的碳權抵換交易收入下滑,加上第4季盈餘財測欠佳,導致該公司盤後股價重挫逾12%。

特斯拉表示,Q3期間電動轎車每週已可生產550台、整季出貨量達創記錄的5,500台,其中歐洲地區的出貨量超過了1,000台。不過,Model S的Q3出貨量仍不如市場樂觀的預估,市場曾預期Model S的Q3銷售量有望上升至5,850台。

展望Q4,特斯拉預估Model S的出貨量有望接近6,000台,這會讓2013年一整年的出貨量達到21,500台,而當季的本業毛利率則可望上升至25%。此外,特斯拉已在Q3開始接受來自中國大陸客戶對Model S下達的訂單,預計可在明年Q1首度交貨。

特斯拉去年的Model S出貨量僅約2,650台,不如該公司原本設定的5,000台。

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

【其他文章推薦】

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

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

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

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

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

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

Model S爆出第三起電池起火事件 Tesla股價大跌

美國知名電動車大廠特斯拉 (Tesla)熱門車款 Model S,週三 (6 日) 傳出近日第三起電池起火事件,引發外界對於爭議性高的電動車輛安全再掀疑慮。Tesla 週三股價才因前日盤後發布第三季車輛銷售不如預期而下挫,週四股價再因上述消息大跌 7.53%,收 139.77 美元。

這起 Tesla 電池起火事件發生在美國田納西州的 Murfreesboro 市,駕駛者 Juris Shibayama 週三下午開著 Model S 駛過路上遺落的拖車掛鉤,因刺破了車輛的電池部位而造成車輛起火。所幸駕駛者在起火之前逃出,並未受傷。

據CNBC報導,這已是過去6 週 Model S 傳出的第 3 起電池起火事件。第 1 次事件發生在西雅圖,當時也是車輛駛過路上一大塊金屬物體,導致電池部分被刺穿而起火。美國國家公路交通安全管理局 (NHTSA)針對上述事件的立場是,他們正在監視情況發展,但並未展開正式調查。

而最新週三的這次車輛起火事件過後,NHTSA 仍表示目前處於搜集資料階段,將諮詢事發當局來確定事故是否與車輛安全相關。

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

【其他文章推薦】

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

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

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

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

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

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

中國新能源汽車補貼未來兩年將下調 地方政府伸出援手

據南方網報導,在針對私人市場新能源補貼新政《關於繼續開展新能源汽車推廣應用工作的通知》(以下簡稱《通知》)出台月餘之際,中國大陸地方政府終於按捺不住了。

按照《通知》中的目標,在2013年至2015年這3年間,特大型城市或重點區域新能源汽車累計推廣量不低於1萬輛,其他城市或區域累計推廣量不低於5,000輛。

此外,《通知》明確,2014年和2015年,純電動乘用車、插電式混合動力(含增程式)乘用車、純電動專用車、燃料電池汽車補助標準在2013年標準的基礎上分別下降10%和20%。在新政補貼標準降低的情況下,地方政府開始伸出援手。

深圳市政府已經著手出台新能源汽車補貼的相關細則,最早會在12月出台,具體的補貼細則為與中央補貼一比一的比例。此外,北京市政府於2013年10月30日出台了《北京市2013-2017年機動車排放污染控制工作方案》,其中亦提出了關於推廣新能源汽車的相關細則。

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

【其他文章推薦】

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

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

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

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

※超省錢租車方案

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

充電設施不足 雷諾日產150萬電動車銷量恐難達成

雷諾暨日產執行長高恩(Carlos Ghosn)在接受英國「金融時報」訪問時首度公開坦承,由於充電設施不足,雷諾日產無法達成在2016年全球電動車銷售150萬輛的目標,達到時間將較預期落後4年多。

雷諾與日產已在電動車上投資數十億美元,高恩也一向大力唱好電動車前景。但他受訪時承認,電動車市場不如他的預期。他預估,以目前銷售速度來看,要達成銷售150萬電動車目標,恐怕較預定2016年的時間,再往後延4年或5年。

雖然政府向汽車業施壓去降低廢氣排放量,但到目前為止,電動車生產成本高,難以銷售和行駛哩數有限。尤其主要市場缺乏充電設施,產生所謂「續航憂慮」現象。

雖然外界普遍認為電動車價格高昂,是讓其難以普及的主因,但高恩認為充電與支援等基礎設施不足,才是導致雷諾日產難以達成銷售目標原因。在挪威和美國加州,因為政府給電動車買家提供補貼,並廣泛建設充電據點,才刺激電動車買氣。

雷諾與日產過去5年共賣出逾12萬輛電動車,為業界最高。日產Leaf為全球賣最好的電動車,迄今賣出約8.5萬輛。此外,雷諾日本與三菱已同意合作全球銷售小電動車,外界看好能進一步降低技術成本。

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

【其他文章推薦】

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

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

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

※超省錢租車方案

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

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