最近由於項目須要寫了一段時間的 Go
,相對於 Java
來講語法簡單同時又有着一些 Python
之類的語法糖,讓人大呼」真香「。linux
但現階段相對來講仍是 Python
寫的多一些,偶爾還得回爐寫點 Java
;天然對 Go
也談不上多熟悉。git
因而便利用週末時間本身作個小項目來加深一些使用經驗。因而我便想到了以前利用 Java
寫的一個博客小工具。github
那段時間正值微博圖牀大量圖片禁止外鏈,致使許多我的博客中的圖片都不能查看。這個工具能夠將文章中的圖片備份到本地,還能將圖片直接替換到其餘圖牀。windows
我我的如今是一直在使用,一般是在碼字的時候利用 iPic
之類的工具將圖片上傳到微博圖牀(主要是方便+免費)。寫完以後再經過這個工具一鍵切換到 [SM.MS](http://sm.MS)
這類付費圖牀,同時也會將圖片備份到本地磁盤。api
改成用 Go
重寫爲 cli
工具後使用效果以下:bash
之因此選擇這個工具用 Go
來重寫;一個是功能比較簡單,但也正好能夠利用到 Go
的一些特色,好比網絡 IO、協程同步之類。markdown
同時修改成命令行工具後是否是感受更極客了呢。網絡
再開始以前仍是先爲不熟悉 Go
的 Javaer
介紹下大概會用到哪些知識點:架構
go mod
)下面開始具體操做,我以爲即使是沒怎麼接觸過 Go
的朋友看完以後也能快速上手實現一個小工具。app
首先介紹一下 Go 的依賴管理,在版本 1.11
以後官方就自帶了依賴管理模塊,因此在當下最新版 1.15
中已經強烈推薦使用。
它的目的和做用與 Java
中的 maven
,Python
中的 pip
相似,但使用起來比 maven
簡單許多。
根據它的使用參考,須要首先在項目目錄下執行 go mod init
用於初始化一個 go.mod
文件,固然若是你使用的是 GoLang
這樣的 IDE
,在新建項目時會自動幫咱們建立好目錄結構,固然也包含 go.mod
這個文件。
在這個文件中咱們引入咱們須要的第三方包:
module btb go 1.15 require ( github.com/cheggaaa/pb/v3 v3.0.5 github.com/fatih/color v1.10.0 github.com/urfave/cli/v2 v2.3.0 )
我這裏使用了三個包,分別是:
pb
: progress bar,用於在控制檯輸出進度條。color
: 用於在控制檯輸出不一樣顏色的文本。cli
: 命令行工具開發包。import ( "btb/constants" "btb/service" "github.com/urfave/cli/v2" "log" "os" ) func main() { var model string downloadPath := constants.DownloadPath markdownPath := constants.MarkdownPath app := &cli.App{ Flags: []cli.Flag{ &cli.StringFlag{ Name: "model", Usage: "operating mode; r:replace, b:backup", DefaultText: "b", Aliases: []string{"m"}, Required: true, Destination: &model, }, &cli.StringFlag{ Name: "download-path", Usage: "The path where the image is stored", Aliases: []string{"dp"}, Destination: &downloadPath, Required: true, Value: constants.DownloadPath, }, &cli.StringFlag{ Name: "markdown-path", Usage: "The path where the markdown file is stored", Aliases: []string{"mp"}, Destination: &markdownPath, Required: true, Value: constants.MarkdownPath, }, }, Action: func(c *cli.Context) error { service.DownLoadPic(markdownPath, downloadPath) return nil }, Name: "btb", Usage: "Help you backup and replace your blog's images", } err := app.Run(os.Args) if err != nil { log.Fatal(err) } }
代碼很是簡單,無非就是使用了 cli
所提供的 api 建立了幾個命令,將用戶輸入的 -dp
、-mp
參數映射到 downloadPath
、markdownPath
變量中。
以後便利用這兩個數據掃描全部的圖片,以及將圖片下載到對應的目錄中。
更多使用指南能夠直接參考官方文檔。
能夠看到部分語法與 Java
徹底不一樣,好比:
Java
也支持)緊接着命令執行處調用了 service.DownLoadPic(markdownPath, downloadPath)
處理業務邏輯。
這裏包含的文件掃描、圖片下載之類的代碼就不分析了;官方 SDK
寫的很清楚,也比較簡單。
重點看看 Go
裏的 goroutime
也就是協程。
我這裏使用的場景是每掃描到一個文件就利用一個協程去解析和下載圖片,從而能夠提升總體的運行效率。
func DownLoadPic(markdownPath, downloadPath string) { wg := sync.WaitGroup{} allFile, err := util.GetAllFile(markdownPath) wg.Add(len(*allFile)) if err != nil { log.Fatal("read file error") } for _, filePath := range *allFile { go func(filePath string) { allLine, err := util.ReadFileLine(filePath) if err != nil { log.Fatal(err) } availableImgs := util.MatchAvailableImg(allLine) bar := pb.ProgressBarTemplate(constants.PbTmpl).Start(len(*availableImgs)) bar.Set("fileName", filePath). SetWidth(120) for _, url := range *availableImgs { if err != nil { log.Fatal(err) } err := util.DownloadFile(url, *genFullFileName(downloadPath, filePath, &url)) if err != nil { log.Fatal(err) } bar.Increment() } bar.Finish() wg.Done() }(filePath) } wg.Wait() color.Green("Successful handling of [%v] files.\n", len(*allFile)) if err != nil { log.Fatal(err) } }
就代碼使用層面看起來是否是要比 Java
簡潔許多,咱們不用像 Java
那樣須要維護一個 executorService
,也不須要考慮這個線程池的大小,一切都交給 Go
本身去調度。
使用時只須要在調用函數以前加上 go
關鍵字,只不過這裏是一個匿名函數。
並且因爲 goroutime
很是輕量,與 Java
中的 thread
相比佔用很是少的內存,因此咱們也不須要精準的控制建立數量。
不過這裏也用到了一個和 Java
很是相似的東西:WaitGroup
。
它的用法與做用都與 Java
中的 CountDownLatch
很是類似;主要用於等待全部的 goroutime
執行完畢,在這裏天然是等待全部的圖片都下載完畢而後退出程序。
使用起來主要分爲三步:
goruntime
的數量:wg.Add(len(number)
goruntime
執行完畢調用 wg.Done()
讓計數減一。wg.Wait()
等待WaitGroup
的數量減爲0。對於協程 Go 推薦使用 chanel
來互相通訊,這點從此有機會再討論。
核心邏輯也就這麼多,下面來說講打包與運行;這點和 Java
的區別就比較大了。
衆所周知,Java
有一句名言:write once run anywhere
這是由於有了 JVM
虛擬機,因此咱們無論代碼最終運行於哪一個平臺都只須要打出一個包;但 Go
沒有虛擬機它是怎麼作到在個各平臺運行呢。
簡單來講 Go
能夠針對不一樣平臺打包出不一樣的二進制文件,這個文件包含了全部運行所須要的依賴,甚至都不須要在目標平臺安裝 Go
環境。
Java
運行環境。我在這裏編寫了一個 Makefile
用於執行打包:make release
# Binary name BINARY=btb GOBUILD=go build -ldflags "-s -w" -o ${BINARY} GOCLEAN=go clean RMTARGZ=rm -rf *.gz VERSION=0.0.1 release: # Clean $(GOCLEAN) $(RMTARGZ) # Build for mac CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 $(GOBUILD) tar czvf ${BINARY}-mac64-${VERSION}.tar.gz ./${BINARY} # Build for arm $(GOCLEAN) CGO_ENABLED=0 GOOS=linux GOARCH=arm64 $(GOBUILD) tar czvf ${BINARY}-arm64-${VERSION}.tar.gz ./${BINARY} # Build for linux $(GOCLEAN) CGO_ENABLED=0 GOOS=linux GOARCH=amd64 $(GOBUILD) tar czvf ${BINARY}-linux64-${VERSION}.tar.gz ./${BINARY} # Build for win $(GOCLEAN) CGO_ENABLED=0 GOOS=windows GOARCH=amd64 $(GOBUILD).exe tar czvf ${BINARY}-win64-${VERSION}.tar.gz ./${BINARY}.exe $(GOCLEAN)
能夠看到咱們只須要在 go build
以前指定系統變量便可打出不一樣平臺的包,好比咱們爲 Linux
系統的 arm64
架構打包文件:
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build main.go -o btb
即可以直接在目標平臺執行 ./btb
運行程序。
本文全部代碼都已上傳 Github
: https://github.com/crossoverJie/btb
感興趣的也能夠直接運行安裝腳本體驗。
curl -fsSL https://raw.githubusercontent.com/crossoverJie/btb/master/install.sh | bash
這段時間接觸 Go
以後給個人感觸頗深,對於年紀 25 歲的 Java
來講,Go
確實是後生可畏,更氣人的是還遇上了雲原生這個浪潮,就更惹不起了。
一些之前看來不那麼重要的小毛病也被重點放大,好比啓動慢、佔用內存多、語法囉嗦等;不過我依然對這位賞飯吃的祖師爺保持期待,重新版本的 Java
能夠看出也在積極改變,更不用說它還有無人撼動的龐大生態。
更多 Java
後續內容能夠參考周志明老師的文章:雲原生時代,Java危矣?