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


閱讀fabric源碼的共識機制部分,感覺源碼難度還是有的,所以先從最簡單的requeststore開始吧。

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

但是對於那些不是很好調試的代碼來說,還有一個簡單的方法,就是看代碼的單元測試的程序,體會它是怎么使用的,這其實也是一個比較好的方法,下面分析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)

先看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構造了一個請求就行了。

繼續看orderedRequests:

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

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

繼續看: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其他部分進行分析。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM