前面提到進(jìn)程的文件鎖,實際上Run也用到了,可以試想下以下的場景。
用戶A執(zhí)行run pt-summary,由于本地已經(jīng)緩存了所以會直接運行本地的腳本。同時用戶B執(zhí)行run -u pt-summary,加上-u或者--update參數(shù)后Run會從遠(yuǎn)端下載并運行最新的腳本。如果不加文件鎖的話,用戶A的行為就不可預(yù)測了,而文件鎖很好得解決了這個問題。
具體使用方法如下,我們封裝了以下的接口。
var lockFile *os.File
// Lock the file.
func Flock(path string) error {
return fcntlFlock(syscall.F_WRLCK, path)
}
// Unlock the file.
func Funlock(path string) error {
err := fcntlFlock(syscall.F_UNLCK)
if err != nil {
return err
} else {
return lockFile.Close()
}
}
// Control the lock of file.
func fcntlFlock(lockType int16, path ...string) error {
var err error
if lockType != syscall.F_UNLCK {
mode := syscall.O_CREAT | syscall.O_WRONLY
mask := syscall.Umask(0)
lockFile, err = os.OpenFile(path[0], mode, 0666)
syscall.Umask(mask)
if err != nil {
return err
}
}
lock := syscall.Flock_t{
Start: 0,
Len: 1,
Type: lockType,
Whence: int16(os.SEEK_SET),
}
return syscall.FcntlFlock(lockFile.Fd(), syscall.F_SETLK, &lock)
}
在運行腳本前就調(diào)用鎖進(jìn)程的方法。
// Lock the script.
lockPath := cacheDir + ".lock"
err = flock.Flock(lockPath)
if err != nil {
utils.LogError("%s: %v\n", lockPath, err)
os.Exit(1)
}
使用Run時它會自動從網(wǎng)上下載腳本,走的HTTP協(xié)議,具體實現(xiàn)方法如下。
// Retrieve a file via HTTP GET.
func Fetch(url string, path string) error {
response, err := http.Get(url)
if err != nil {
return err
}
if response.StatusCode != 200 {
return Errorf("%s: %s", response.Status, url)
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
if strings.HasPrefix(url, MASTER_URL) {
// When fetching run.conf, etc.
return ioutil.WriteFile(path, body, 0644)
} else {
// When fetching scripts.
return ioutil.WriteFile(path, body, 0777)
}
}
Run的總體代碼是很簡單的,主要是通過解析run.conf下載相應(yīng)的腳本并執(zhí)行。