hyperledger fabric v0.6pbft源碼分析(一)requeststore.go

閱讀fabric源碼的共識機制部分,感受源碼難度仍是有的,因此先從最簡單的requeststore開始吧。golang

在閱讀了部分超級帳本的源碼後,有一個經驗就是,在閱讀源碼特別是大項目的源碼時,可能會感到無所適從,其實這也是很正常的,個人經驗是能夠先從一條線開始理清代碼的執行流。好比像 hyperledger 這樣的平臺,能夠從鏈碼的執行這條線來看源碼,跟着調試一步步走,相信會簡單很多。app

可是對於那些不是很好調試的代碼來講,還有一個簡單的方法,就是看代碼的單元測試的程序,體會它是怎麼使用的,這其實也是一個比較好的方法,下面分析pbft的實現源碼,就是使用這種方法來分析的。函數

pbft實現起來不容易,這裏從它最簡單的部分入手,話很少說,看代碼吧:單元測試

// consensus/pbft/requeststore_test.go
func TestOrderedRequests(t *testing.T) {
    or := &orderedRequests{}
    or.empty()

    r1 := createPbftReq(2, 1)
    r2 := createPbftReq(2, 2)
    r3 := createPbftReq(19, 1)
    if or.has(or.wrapRequest(r1).key) {
        t.Errorf("should not have req")
    }
    or.add(r1)
    if !or.has(or.wrapRequest(r1).key) {
        t.Errorf("should have req")
    }
    if or.has(or.wrapRequest(r2).key) {
        t.Errorf("should not have req")
    }
    if or.remove(r2) {
        t.Errorf("should not have removed req")
    }
    if !or.remove(r1) {
        t.Errorf("should have removed req")
    }
    if or.remove(r1) {
        t.Errorf("should not have removed req")
    }
    if or.order.Len() != 0 || len(or.presence) != 0 {
        t.Errorf("should have 0 len")
    }
    or.adds([]*Request{r1, r2, r3})

    if or.order.Back().Value.(requestContainer).req != r3 {
        t.Errorf("incorrect order")
    }
}

func BenchmarkOrderedRequests(b *testing.B) {
    or := &orderedRequests{}
    or.empty()

    Nreq := 100000

    reqs := make(map[string]*Request)
    for i := 0; i < Nreq; i++ {
        rc := or.wrapRequest(createPbftReq(int64(i), 0))
        reqs[rc.key] = rc.req
    }
    b.ResetTimer()

    b.N = 100;
    fmt.Printf("N is %d\n", b.N)
    for i := 0; i < b.N; i++ {

        for _, r := range reqs {
            or.add(r)
        }

        for k := range reqs {
            _ = or.has(k)
        }

        for _, r := range reqs {
            or.remove(r)
        }
    }
}

requeststore_test.go開始看,它測試了兩個函數:測試

  • TestOrderedRequests(t *testing.T)
  • BenchmarkOrderedRequests(b *testing.B)

這裏的第一個測試函數是普通的測試函數,第二個是benchmark測試函數(注意*testing.B)ui

先看createPbftReq這個函數:調試

// consensus/pbft/mock_utilities_test.go
func createPbftReq(tag int64, replica uint64) (req *Request) {
    tx := createTx(tag)
    txPacked := marshalTx(tx)
    req = &Request{
        Timestamp: tx.GetTimestamp(),
        ReplicaId: replica,
        Payload:   txPacked,
    }
    return
}

這裏就是使用傳過來的tag與replica構造了Request對象,其中tx的時間屬性(Seconds)與tag有關,在createTx還給定了tx的type,這些不是很重要,咱們只要知道是經過tag和replica構造了一個請求就好了。code

繼續看orderedRequests:對象

type orderedRequests struct {
    order    list.List
    presence map[string]*list.Element
}

它保存着一個列表,還有一個map,這裏的map鍵是list元素的hash,值對應於list的元素。rem

繼續看:wrapRequest函數

func (a *orderedRequests) wrapRequest(req *Request) requestContainer {
    return requestContainer{
        key: hash(req),
        req: req,
    }
}

就是把req變成 (hash(req), req)對,是否是很簡單。。
後面的測試邏輯就很簡單了,因此總的邏輯是:

建立空的orderedRequests並初始化
建立3個req
判斷or有沒有req1(此時爲空,固然沒有)
添加req1
判斷or有沒有req1(剛添加上,固然有)
判斷or有沒有req2(固然沒有)
刪掉r2(因此這個時候就能夠獲得源碼裏remove的做用,刪除成功返回true,不然返回false)
刪除r1(刪除成功)
再刪除r1(已爲空,刪除不成功)
檢查or是否爲空(爲空)
添加r1,r2,r3到or
看最後一個是否是r3(這也體現出requeststore是順序表)

第一個測試邏輯很是簡單,可是可讓咱們快速對源代碼文件有了必定了解。

下一個測試函數是一個benchmark函數,自己很是的簡單,我接觸golang不久,要提的主要是b.N是函數執行的次數,golang會使用不一樣的N來調用函數,屢次測試取平均嘛,這個挺方便的。

另外測試結束後會提示:

100 1031034650 ns/op

它表示函數執行了100次,平次一次執行時間是1031034650納秒。

測試到此就結束了,再看源碼文件,測試沒覆蓋到的主要是:

type requestStore struct {
    outstandingRequests *orderedRequests
    pendingRequests     *orderedRequests
}

其餘的函數基本上都是很是簡單的,除了:

// getNextNonPending returns up to the next n outstanding, but not pending requests
func (rs *requestStore) getNextNonPending(n int) (result []*Request) {
    for oreqc := rs.outstandingRequests.order.Front(); oreqc != nil; oreqc = oreqc.Next() {
        oreq := oreqc.Value.(requestContainer)
        if rs.pendingRequests.has(oreq.key) {
            continue
        }
        result = append(result, oreq.req)
        if len(result) == n {
            break
        }
    }

    return result
}

這個函數主要是從outstandingRequests拿出不在pendingRequests的n個request。

上面就是這部分的內容,很是簡單的代碼,關鍵是看代碼的一個思路,後面會對pbft其餘部分進行分析。

相關文章
相關標籤/搜索