[MOOCS][Golang]MIT6_824 Distributed Systems Week2(Lec2/Lab2B)

http://i.stack.imgur.com/MUtmL.png

(圖片: FDS的優化示意圖,由這裡來的)

##前言:

到了第二個禮拜,其實卡了很久在Lab2b裡面.主要不是因為要判斷的case太多,而是希望把每次的test case找到在論文中的理由或是整個學術上的依據.

MIT 6.824 分散式系統 系列文章

6.824: Distributed Systems

##論文-Flat Datacenter Storage

這是微軟在2012發表的新的分散式計算系統FDS,號稱可以在60秒處理141GB的資料,打敗了當初Yahoo所創下的紀錄.

FDS組成

FDS為了要達到速度快,把繁瑣的檔案系統移除.整個組成相當的簡單 (Blob 與 Tract)

Blob:

Blob 是FDS系統裡面的構成元件.每一個Blob由數個Tract所組成的.

  • 一個Blob 可以包含若干個Tract
  • 一個Blob 擁有一個唯一的GUID

其中Blob 提供以下的API:

  • CreateBlob
  • OpenBlob
  • DeleteBlob
  • CloeBlob

Tract:

FDS裡面最基本的元素.

  • Tract的檔案大小是固定的(預設是8MB)

所有的Tracts 是透過 TractServer來管理,以下為Tract 相關的API:

  • GetBlobSize
  • ExtendBlobSize
  • WriteTract
  • ReadTract
  • WriteTract
  • GetSimultaneousLimit

實際運作上:

  • Blob 並不需要放在同一台電腦上
  • 為了達到速度快,可以把tract放在不同的儲存媒體上.達到類似Raid-0的效果.
  • 用來索引各個tract位置的是TLT(Tract Locator Table)而Metadata Server就是管理TLT的伺服器.

這一篇對於組成的部分有更詳細的解釋

FDS的備份

##第二週下半課程: ##關於Lab2 Part B:he primary/backup key/value service

這一次的作業相當的有趣,由於上一次要識做一個Server與Client間的分派者或是說控制者的角色(稱為Viewserver). 而這次要做的就是另外兩個角色 PBServer 與 Client.

###幾個注意地方:

以下列出幾個比較基礎的地方:

  • PBServer 與 Client 與 ViewServer三方的溝通都必須透過RPC call
  • RPC call rpcname必須注意是要Class.Api. 舉例而言: ViewServer.Ping
  • Client 每個動作都需要先去問ViewServer目前的primary

###開始流程:

  • 完成Server的Get()Put()Ping() (Ping必須在Tick裡面)

  • 每個transaction 必須要透過 primary 把結果傳給 backup ~
  • primary/backup要有傳遞備份資料與互相溝通的RPC 要新增不少個
  • Server需要判別自己的角色是primary或是backup (可以使用 ViewServer.Getme來判別)
  • Server與Client溝通原則:
    • 每一個Client的command,在得到reply == OK 前,必須要不斷地送.
    • 相反的,Server必須要遵循at-most-once原則,要過濾掉重複的request
    • client 不能在每次Get/Put去跟ViewServer詢問目前誰是primary,除非你認為primary 已經死了.
      • 時間內沒回覆?
  • 角色的準則裡面:
    • Primary:
    • Backup:
      • 不能直接回覆client需求,而要回復錯誤

Lab2A 的Client (也就是身為 PBServer的角色) 需要知道自己的所扮演的角色 (Primary/Backup)

  • Get Arg 增加欄位判別需求是來自 server/client 是 primary 才需要ack back
  • 這樣可以避免寫Lab2B 的時候,造成Lab2A的Go test failed.

有趣的思維是:

  • 你必須同時考慮三個角色 (Client/PbServer/Viewserver) 交互關係
  • 必須確保 Lab2A / Lab2B 的測試不能有誤… (相互干擾是一定會的,寫完要回頭看……回頭測試)

資料的同步:

  • Backup剛起來的時候(或是每個一段時間) 需要從Primary備份所有資料庫
  • 每一次的資料變動 (在這個例子裡面是 Put發生的時候),需要馬上把資料傳給Backup

TestBasicFail 經驗分享

你需要完成以下:

image

  1. Remus的Fig1 流程(參考以上):
    1. 定期需要從 Primary -> Backup 所有資料庫 (自行新增RPC)
    2. 每個command 在Primary 完成後,需要先把結果傳給Backup,然後才傳給Client
  2. PBServer 必須知道自己是 Primary 還是 Backup 並且做相關的 Init
  3. Client需要做Repease Request 直到結果是正確的.(注意 每次RPC request 最好有間隔)

你還不需要做:

  1. Hash Put
  2. Repeat AtOnce (後面的測試會有)

TestAtMostOnce passed, 經驗分享:

  1. Client 需要不斷的send request 如果沒有收到result 或是收到空的result
  2. Server 需要過濾重複的指令,並且傳送之前的結果.
    • 所以你需要記住之前的指令與參數確保指令重複.
    • 需要儲存上一次結果.重複指令就回傳
    • 此外,對於重複指令不需要傳到Backup
  3. 忘了提一下,關於Hash的部分演算法是: Hash_Value = hash(Previous_Value_String + Currently_Value_String )

TestFailPut/TestConcurrentSame/TestConcurrentSameUnreliable passed, 經驗分享:

  1. 重點部分, 當client 與primary 溝通,如果發現疑似primary掛掉的狀態.需要跟viewserver詢問新的primary.

心得:

對於FDS的理解還算粗淺,希望隨著課程的了解能夠瞭解更多的資料.而Lab2的作業,整個作業還沒寫完,會持續更新相關的資料.

##參考文章:

[Golang]跟效能優化有關的視覺化工具(gcvis與pprof)

前言:

架設好主機後,接下來開始就是要優化效能.其實有很多人建議了不少的優化與調整工具,不過這裡還是使用兩個視覺化可以很快調整的工具.

  • gcvis: 由Dave Cheney所開發的工具,可以很清楚的觀察GC(Garbage Collection)發生的時間點與Heap的大小,讓你可以了解系統由於GC消耗的多少的系統資源.
  • pprof:這個系統build-in的工具,可以讓你產生每個function所產生的方塊圖可以讓你清楚找出系統裡面的bottleneck.

這裏只是簡單的使用流程與心得,不過應該會有更多的資料補充.

關於gcvis

image

安裝與使用

  • go get github.com/davecheney/gcvis
  • 使用相當簡單 gcvis YOUR_APP -index -http=:6060

就會打開一個網頁顯示目前的heap與GC狀態

關於pprof

必須要先安裝好Graphviz

  • 執行go tool pprof http://localhost:YOUR_WEB_PORT/debug/pprof/profile
  • (pprof) top10
  • (pprof) web

會出現一個圖類似以下:

image

###問題: Go-martini會將handler預設是沒有開放的,所以會得到404 page not found:

這邊有個討論串,我覺得Patrick這個解法挺好的.

 import "net/http/pprof"
 
 //Some martini init here.
 m.Group("/debug/pprof", func(r martini.Router) {
        r.Any("/", pprof.Index)
        r.Any("/cmdline", pprof.Cmdline)
        r.Any("/profile", pprof.Profile)
        r.Any("/symbol", pprof.Symbol)
        r.Any("/block", pprof.Handler("block").ServeHTTP)
        r.Any("/heap", pprof.Handler("heap").ServeHTTP)
        r.Any("/goroutine", pprof.Handler("goroutine").ServeHTTP)
        r.Any("/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
    })

心得:

  • 使用gcvis可以幫助你瞭解目前Heap的數量,記憶體使用數量與GC發生的次數.記住,越多的GC代表著系統資源消耗在回收與釋放記憶體.
  • pprof可以完成一個樹狀圖.不過,目前在Web Application這裏可能還需要有更多的技巧找出真正卡住系統資源的地方.
  • Go 1.5 有出新的tracer, 等 1.5出來可以試著用用看….

相關文件:

[MOOCS][Golang]MIT6_824 Distributed Systems Week2(Lec2/Lab2A)

##前言:

本週進入第二個禮拜,開始有一些分散式系統的備份與替換的問題還有論文. 加油吧!!

MIT 6.824 分散式系統 系列文章

6.824: Distributed Systems

##第二週課程:

##關於筆記 Primary/Backup Replication (Remus)

盡量用理解後的中文紀錄,原始筆記在這裡:

  • 關於容錯(Fault Tolerance):
    • 希望能繼續服務儘管有問題發生. 特性如下:
      • 可見性: 即使有問題發生,也希望服務能夠繼續.
      • 正確性: 希望發生問題的時候,服務也跟單機一樣(無錯誤時)正確.
    • 核心想法: 備份,透過兩個或是以上的備份伺服器來預防任何的問題產生.
      • 重要問題:
        • 備份機的狀態?
        • 工作機的狀態如何轉移?
        • 何時要切開與備份機的連接?
        • 異常發生在切開之後?
        • 如何修復與重新連接備份機?
    • 兩個關於備份機的重要動作:
      • State Transfer (狀態轉移)
      • Replicated State Machine: (備份狀態機)

##論文 Remus: High Availability via Asynchronous Virtual Machine Replication

這裏有網頁版本

Remus 的系統特色:

  • 整個系統備份
  • 對於Application與Client而言是透明的.
  • 當運行成功的時候,備份與切換機器就運作順暢

image

圖片元素解釋:

  • Primary: 具有資料,會執行OS與App,並且負責跟Client溝通.
  • Backup: 只有資料,但是不執行OS與App.只有跑一些Remus的code

流程簡介:

  • (1) Primary 跑完 Completed Execution (E1),並且先不回傳E1結果回client.
  • (2) Primary把結果備份到Backup (背景執行)
  • (3) Backup同步所有目前資料跟Primary一樣.
  • (4) Primary 執行Speculative Exection (E2),並且先不回傳給client
  • (5) Backup 複製所有結果
  • (6) Primary放出E1結果
  • (7) Backup 把結果存入RAM/Disk.

Remus 特性與檢討:

Remus系統的估計

  • 速度為單機系統的1/2~1/4
  • Check point時間過久
  • 為了要能夠與client溝通,保留結果的時間不能太久

缺點

  • 速度不快:
    • 由於每個Exection都必須等待Backup 複製好相關資料。
    • Check point 至少等花上100ms

如何改善:

  • 減少主機儲存資料
  • 減少狀態的資料量,或是優化資料格式
  • 試著傳送指令而非狀態

##關於Lab2 Part A:ViewService

這一篇有點困難,所以稍微做點筆記:

###題目解釋: 主要要implement 一個具有faul-toerant的key/values services- Viewservice.這個Service有以下的幾個功能:

  • 監控 Primary (指的是原始service) 與 Backup(備份用的Service)是否是正常工作.
  • 正常而言,如果Primary壞了,會自動地把Backup promoted成 primary.此時如果有空的server就會拉近來變成新的Backup.
  • 當新的Primary與Backup上線後,Primary還有一個工作:
    • 備份所有資料庫給Backup
    • 所有的command set傳給Backup去確保Primary與Backup state machine是一致的.

關於ViewService的Primary/Backup轉移範例如下:

  • Primary (Server 1: S1) 發生問題,而Client 送給 S1 一個需求OP1.
  • Primary 轉發OP1 給 Backup(Server 2: S2), 這個動作是之前提到為了同步的關係.S1會把每個收到的指令透過forward轉給S2
  • 但是由於S2已經被promote成新的Primary.而這時尚未有新的Backup上線.
  • S2 回傳一個Error 給 S1 並且Forward給 Client 說已經沒有Backup Server.
  • ViewService會把S2設定成新的Primary target address 並且把新的指令都直接轉給 S2.

###開始解答:

打開viewsource這個資料夾,主要有幾個需要注意的:

  • server.go :主要code的部分.裡面主要有以下幾個部分需要思考:
    • Ping(): 主要負責的是接受Server的Ping,在其中包含著Server本身的資訊與現在資料的View number
    • Get(): 取得資料
    • Tick(): 這個也就是所謂的定期Heartbeat 也可以說經過一段時間會自動啟動的timer,如果有任何Server掛掉,必須要在這裡處理.
  • test_test.go:本次作業的測試程式,需要好好思考其中代表的涵意

####進階與Server有關的邏輯與含意

這邊可以參考它提供的圖片: image

在這裡也整理一下幾個需要注意的地方:

  • 其中Viewnum代表的涵意:
    • Viewnum代表Server所直接到的一個state或是第幾個View.他應該是一個會慢慢增加的部分.
  • 如何判定Server已經掛了:
    • Viewnum忽然變成0,如果原先的 viewnum已經>0,但是在某些狀況下忽然收到viewnum=0.那麼就是代表Server已經掛掉又重新執行.
    • 每一次Server的Ping,需要紀錄當時的時間.然後在tick裡面去檢查 DeadPings * PingInterval來確認Server是否掛了,但是某些狀況下可以當作是Server 繼續活著 (請看Ack).
  • 當Server掛掉的處理流程:
    • 如果是Primary掛掉,檢查是否有Backup.如果Backup已經正常在運作了,就把他切換成Primary. 並且把第三台主機當作Backup.
    • 如果是Backup掛掉,一樣會檢查是否有第三台主機,並且把它掛起來.
  • 回應(acknowledgement):
    • 在這裏一次的Get()Ping()被視為是一次的回應.並且注意Ping()viewnum必須要大於0,才能表示他已經開始運作.
    • 如果Primary主機有做Get(),但是卻沒有回應Ping(),在這次的處理必須等待而不能直接把主機關閉.(倒數第二個測試)
  • 伺服器初始化(Initialization):
    • 身為Backup主機,我們必須要確認Backup已經上線了(Online),唯一的方式就是確保他已經有Ping()一個數值超過0的viewnum
    • 如果Backup沒有初始化,是不能夠當作Primary的(最後的測試)

####心得與改進:

透過這些test case來開始這部分的作業,其實相當的有趣.一開始最令人困擾的是三個function不知道要拿來做什麼.其實題目不算是相當的清楚來解釋每一個步驟. 只能不斷地透過go test一步步來瞭解到底每一個流程,每一個原則會是什麼.這邊算是比較有趣也比較好玩的部分.

此外,由於流程裡要代表的狀態相當的多.一下子要確認主機是否有ack,一下又要確認有沒有init.簡單的做法可以透過許多個flag來代表個別的狀態. 當然,也可以使用狀態機(state machine)的方式方式來開發這次的作業. 只是要有可能會打掉重練的打算.

幾種重練的做法:

寫完作業後,對於論文Remus: High Availability via Asynchronous Virtual Machine Replication也比較容易了解了.

##參考文章:

[MOOCS][Golang]MIT6_824 Distributed Systems Week1

image

##前言:

主要是Golang.Tw上面有人在問,加上這門課程的作業本身是使用Golang來開發.就還蠻有興趣玩玩看.

課程鏈結在這裡.6.824: Distributed Systems

MIT 6.824 分散式系統 系列文章

6.824: Distributed Systems

##第一週課程:

###Paper1: 鼎鼎大名的Google Mapreduce 論文: 課程內容主要是要讀鼎鼎大名的mapreduce系統論文.

###Lab1: 寫一個簡單的Map跟Reduce 然後來寫一個Lab1 Word Count的小程式.裡面主要要完成兩個functions:

  • Map: 把文章裡面的字拆出來後,放入一個List. List 裡面的內容是文字與出現的次數.
  • Reduce: 就每個檔案裡面的所以出現單字找出來,並且回傳它的加總.

課程算很簡單,主要是要對於mapreduce有基本的了解.我一開始不是很了解,比對了參考文章內的內容跟論文演算法就看懂了. 其實還挺有趣的. 寫完裡面還有go test 可以去驗證你的想法有沒有問題.

###簡單介紹RPC

第一週後半段其實還有一部分是RPC的部分,還好最近有點研究.所以也很快地讀完筆記.

主要講解內容如下:

  • 什麼是RPC Server.
  • RPC 架構下會有什麼問題? (斷線,命令未收到,命令未完成)
  • 如何解決:
    • “最少一次”:
      • 優點: 可以確認server有執行到.
      • 使用範圍:
        • 可以用: 重複性質的讀取
        • 不能使用: 存款
    • “最多一次” (Go RPC就是這種)
      • 會把重複的需求挑出來,直接回答上一次的結果而不是再跑一次.
      • 怎麼分辨是不是重複: 透過unique ID.
      • Lab2 要玩這部分.
    • “剛好一次”:
      • 也就是最多一次的系統加上unbounded retries跟容錯設計.Lab3要玩這個.
  • Go thread:
    • 何時使用Go channel 何時使用 shared memory + lock:
      • Channels: 確定要兩個thread互相溝通,一個等待另一個回傳.
      • Shared memory + locks: 需要共用資料,但是不需要等待另一個結果.

##參考文章:

[iOS/Golang]Server-Side In-App-Purchase Verification in Apple Store

Steps

Connect to Apple iTune

// iOS Http code
NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
[parameters addEntriesFromDictionary:[credentials dictionary]];

// receipt is an object of my own making, but base64String just returns an
// NSString representation of the receipt data.
parameters[PURCHASE_RECEIPT] = [receipt base64String];

NSURLRequest *request =
    [[AFHTTPRequestSerializer serializer]
        requestWithMethod:@"POST"
                URLString:urlString
                parameters:parameters];

AFHTTPRequestOperation *operation =
    [[AFHTTPRequestOperation alloc]
        initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];

<snip>

[operation start];

// Json data
{"status":0,
    "environment":"Sandbox",
    "receipt":
    {"receipt_type":"ProductionSandbox",
        "adam_id":0,
        "bundle_id":"<snip>",
        "application_version":"1.0",
        "download_id":0,
        "request_date":"2013-11-12 01:43:06 Etc\/GMT",
        "request_date_ms":"1384220586352",
        "request_date_pst":"2013-11-11 17:43:06 America\/Los_Angeles",
        "in_app":[
                  {"quantity":"1",
                      "product_id":"<snip>",
                      "transaction_id":"1000000092978110",
                      "original_transaction_id":"1000000092978110",
                      "purchase_date":"2013-11-12 01:36:49 Etc\/GMT",
                      "purchase_date_ms":"1384220209000",
                      "purchase_date_pst":"2013-11-11 17:36:49 America\/Los_Angeles",
                      "original_purchase_date":"2013-11-12 01:36:49 Etc\/GMT",
                      "original_purchase_date_ms":"1384220209000",
                      "original_purchase_date_pst":"2013-11-11 17:36:49 America\/Los_Angeles",
                      "is_trial_period":"false"}
                  ]
    }
}

Reference

[Golang] 利用build tags達到不同的build config

##前言

image

為了要寫一個Web Service但是有可能會發生以下的需求:

  • 不同Server,有不同的設定參數
  • 不同的OS有不同的command set
  • 想要快速的開啟或是關閉debug log

在有以上的需求的時候,一開始都是使用OS偵測或是configuration file來區隔.但是到後其實是希望能透過不同的build config能夠產生不同的binary.

決定研究了一下: go build 有以下兩種方式可以達到部分的效果.

##Go build -ldflags

這可以在go build的時候,先設定一些變數的名稱.通常我自己比較習慣透過OS環境變數來設定,然後程式裡面再去讀取.

在你的主程式裡面,可以先定義一個變數flagString:

package main

import (
	"fmt"
)

var flagString string

func main() {
	fmt.Println("This build with ldflag:", flagString)
}

透過外在go build來設定這個變數 go build -ldflags '-X main.flagString "test"' 這樣你的結果會是

>> This build with ldflag: test

這個方式可以直接設定參數,讓initialize value透過外部設定來跑.

##Go build -tags

透過go build -tags 可以達到加入不同的檔案在compiling time.由於這樣,你能夠放在這個檔案裡面的東西就有很多.可以是:

  • 不同系列的define (達到ifdef的效果)
  • 不同的function implement (達到同一個function 在不同設定下有不同實現)

以下增加一個簡單的範例,來達到不同的build config可以載入不同的define value.

file: debug_config.go

//+build debug

package main

var TestString string = "test debug"
var TestString2 string = " and it will run every module with debug tag."

func GetConfigString() string {
	return "it is debug....."
}

請注意: //+build debug 前後需要一個空行(除非你在第一行)

另外,我們也有一般的設定檔 release_config.go

//+build !debug

package main

var TestString string = "test release"
var TestString2 string = " and it will run every module as no debug tag."

func GetConfigString() string {
	return "it is release....."
}

最後在主要的main裡面,可以直接去參考這兩個define value file: main.go

package main

import (
	"fmt"
)

func main() {
	fmt.Println("This build is ", TestString, TestString2, GetConfigString())
}

在這裡如果你是跑 go build -tags debug 那麼執行結果會是:

>> This build is  test debug  and it will run every module with debug tag. it is debug.....

如果跑的是 go build會預設去讀取!debug,那麼結果會是:

>> This build is  test release  and it will run every module as no debug tag. it is release.....

可以看到他不只可以加入define參數,也可以把不同function 有著不同的implement.

##使用時機討論與心得

使用上差異:

  • ldflags 可以加入一些參數,就跟gcc的ldflags 一樣-
  • tags 很像是 gcc -D 不過由於在檔案裡面要定義 //+build XXXX,感覺有點繁瑣. 不過由於可以以檔案來區隔,你可以加入多個定義值跟function

ldflags 使用時機:

個人認為可能可以拿來改變初始值得設定,或是去改變一些程式內的設定. **比如說: **

  • buffer value: 透過build 來改變buffer size,來做不同的測試與應用.
  • log flag: 決定要不要印log

tags 使用時機:

tags的使用時機相當的多,列出幾個我看到的:

  • debug log/encrypt : 定義debug func 然後再release/debug有不同的implement (印或是不印log),也可以在某些狀況下開啟或是關閉encrypt來測試.
  • 跨平台部分: 不同OS平台需要不同的設定與implement

其他更多的部分,可以看到在逐漸加上去.

##參考鏈結