開發中常用的Golang高級用法


前言

忙碌了兩個月,這次開發終於要結束了,今天下午公司在重組集群機器,也沒辦法干活兒了,就寫一些東西,相當於,留住一些東西,來紀念這辛苦的兩個月吧。做一個紀念,也是為了方便以后自己去查看。在這次開發中,學習了不少Golang的高級特性,並且付諸於實現,也踩了不少坑,留下這篇文字,也是方便其他人能夠查看,或者借鑒,如果幫到你,那么我也會很開心你。

開發常遇到的問題

Golang判斷一個元素在不在切片/列表當中

在Python中,我們可以直接用 in 的方式去判斷,例如

if "i" in lists

但是在Golang中,沒有這種語法糖或者是關鍵字可以幫助我們處理這種問題,所以還是只能靠循環去處理這種問題,為此我封裝了一個Golang函數,函數如下

//FindType 循環對比,匹配到返回true,不匹配返回false
func FindType(a string, typelist []string) bool {
	for _, b := range typelist {
		if b == a {
			return true
		}
	}
	return false
}

Golang獲取文件的詳細信息

Golang獲取文件信息的方法相對來說容易一些,都已經有了對應的package,我這里只是把怎么用展示出來

//timespecToTime 轉換
func timespecToTime(ts syscall.Timespec) time.Time {
	return time.Unix(int64(ts.Sec), int64(ts.Nsec))
}

//GetFileinfo 獲取文件信息
func GetFileinfo(path string) {
	fileInfo, err := os.Stat(path)
	if err != nil {
		return 0
    }
    //文件大小
    filesize := fileInfo.Size()
    //文件創建時間
    stat_ts := fileInfo.Sys().(*syscall.Stat_t)
    Ctime := timespecToTime(stat_ts.Ctim).Format("2006/01/02")
    //文件修改時間
    Mtime := timespecToTime(stat_ts.Mtim).Format("2006/01/02")
    //文件訪問時間
    Attim := timespecToTime(stat_ts.Atim).Format("2006/01/02")
    //獲取文件所有者
    stat_ts := fileInfo.Sys().(*syscall.Stat_t)
	uid := strconv.Itoa(int(stat_ts.Uid))
    usrs, err := user.LookupId(string(uid))
    username := usrs.Username
    //獲取文件名
    filename := fileInfo.Name()
}

Golang比較兩個list/切片的不同之處(差集)

在開發中,我需要比較兩個list,然后取出他們之中不同的部分,這里Golang也沒有合適的法子,一般的方法就是轉map進行處理

//difference 進行比對,輸出不同的[]string
func difference(slice1, slice2 []string) []string {
	m := make(map[string]int)
	nn := make([]string, 0)
	inter := intersect(slice1, slice2)
	for _, v := range inter {
		m[v]++
	}

	for _, value := range slice1 {
		times, _ := m[value]
		if times == 0 {
			nn = append(nn, value)
		}
	}
	return nn
}

//intersect 把兩個對比的列表進行map化處理
func intersect(slice1, slice2 []string) []string {
	m := make(map[string]int)
	nn := make([]string, 0)
	for _, v := range slice1 {
		m[v]++
	}

	for _, v := range slice2 {
		times, _ := m[v]
		if times == 1 {
			nn = append(nn, v)
		}
	}
	return nn
}

func main() {
    infinode := []string{"10.0.9.1","10.0.9.2"}
    syncnodes := []string{"10.0.9.1","10.0.9.2", "10.0.9.3"}
    ips := difference(infinode, syncnodes)
}

Golang格式化時間

golang 一般都可以通過Format的方法進行時間格式化,一般你可以直接調format,例如

fmt.Println(time.Now().Format("2006-01-02 15:04:05"))

有些時候是他娘的時間戳

fmt.Println(time.Unix(1389028339, 0).Format("2006-01-02 15:04:05"))

有些時候會讓你搞成別的樣子,比如"2006/01/02 15:04:05"

fmt.Println(time.Now().Format("2006/01/02 15:04:05"))

有些時候會讓你比較個時間大小,例如

//比對時間,看看它在不在開始時間/結束時間的范圍內
starttime := "2020-05-12"
endtime := "2020-05-20"
ctime := "2020-05-15"
ft, err := time.Parse("2006-01-02", ctime)
st, err := time.Parse("2006-01-02", starttime)
et, err := time.Parse("2006-01-02", endtime)
if ft.After(et) && ft.Before(st) {
    fmt.Println("在里面兒!")
} else {
     fmt.Println("不在里面兒!")
}

Golang並發循環的使用

我們處理循環的時候,在Python中,我舉個例子

lists = [1, 2, 3, 4]
for i in lists:
    print i

這里打印的話是

1
2
3
4

Golang有個黑科技是並發循環,簡單說,就是能一下給你把這四個都給你打印出來,不是一條條循環,也不用等上一個循環的結果返回,也可以選擇略過錯誤。我在這里簡單實現一個

直接並發循環(無需考慮錯誤)

這里不考慮error的情況,具體代碼如下

ips := []string{"10.0.9.1","10.0.9.2"}
ch := make(chan struct{})
//並發循環ip
for _, ip := range ips {
    go func(ip string) {
        //模擬一個展示ip
        fmt.Println(ip)
        ch <- struct{}{}
    }(ip)
}
for range ips {
    <-ch
}

輸出錯誤的並發循環(考慮錯誤)

ips := []string{"10.0.9.1","10.0.9.2"}
//定義一個error
errors := make(chan error)
for _, ip := range ips {
    go func(ip string) {
        _, err := test(ip)
        errors <- err
    }(ip)
}
for range ips {
    if err := <-errors; err != nil {
        return err
    }
}

Golang做一個不退出的無限循環

有些時候我們希望一個服務/腳本/函數不斷運行,或者每隔一段時間來一發(運行一次),這時候我們就需要定義無限循環

func main() {
    for {
        //每隔1s打印一次start work
        time.Sleep(time.Second * 1)
		fmt.Println("Start work......")
	}
}

Golang實現一個遇到錯誤的重試機制

當我們最開發的時候,有些時候遇到錯誤,需要進行重試,這時候我們就需要進行錯誤捕獲和函數重載

//Retry 重試邏輯
//傳入函數,如果捕獲到錯誤,則重載函數,重新來一次執行,直到沒錯誤為止
func Retry(fn func() error) error {
	if err := fn(); err != nil {
		if s, ok := err.(stop); ok {
			return s.error
		}
		return Retry(fn)
	}
	return nil
}
//TestWork 測試函數,無意義
func TestWork() error {
    //測試函數,這里是偽代碼。
    _, err := GetIpNode()
    if err != nil {
        return err
    }
    return nil
}
func main() {
    go Retry(TestWork)
}

Golang執行Linux命令

有些時候我們需要執行linux相關命令,Golang封裝了相應的包,但是少部分時候我們需要控制一些很久不返回結果的命令,所以我加了一個超時時間,代碼如下

var (
    //這里我寫死了,你可以自己定義,更靈活一些
	Timeout = 60 * time.Second
)
//Command 執行shell
func Command(arg string) ([]byte, error) {
	ctxt, cancel := context.WithTimeout(context.Background(), Timeout)
	defer cancel()

	cmd := exec.CommandContext(ctxt, "/bin/bash", "-c", arg)

	var buf bytes.Buffer
	cmd.Stdout = &buf
	cmd.Stderr = &buf

	if err := cmd.Start(); err != nil {
		return buf.Bytes(), err
	}

	if err := cmd.Wait(); err != nil {
		return buf.Bytes(), err
	}

	return buf.Bytes(), nil
}

func main() {
    _, err := Command("ls")
    if err != nil {
        fmt.Println(err)
    }
}

Golang 簡單的日志邏輯

這邊貢獻一個簡單的日志實現代碼,往文件里面寫,可以自己定義格式,代碼如下

import (
	"fmt"
	"os"

	"github.com/op/go-logging"
)

func Monlog() (logs *logging.Logger) {
    var log = logging.MustGetLogger("monlog")
    //定義格式 時間 go文件 行數 等級 錯誤信息
	var format = logging.MustStringFormatter(
		`%{time:2006-01-02T15:04:05} %{shortfile} %{shortfunc} %{level} %{message}`)
    //文件的寫入位置 追加模式/沒有自動創建
	logFile, err := os.OpenFile("/var/log/monlog.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		panic(fmt.Sprintf("Open faile error!"))
    }
    //頭相關, 具體意思就是支持追加,然后格式化,且按照那個格式往里面寫
	backend1 := logging.NewLogBackend(logFile, "", 0)
	backend2 := logging.NewLogBackend(os.Stderr, "", 0)

	backend2Formatter := logging.NewBackendFormatter(backend2, format)
	backend1Formatter := logging.NewBackendFormatter(backend1, format)
	backend1Leveled := logging.AddModuleLevel(backend1Formatter)
	backend1Leveled.SetLevel(logging.INFO, "")
	logging.SetBackend(backend1Leveled, backend2Formatter)
	return log
}

func main() {
    log := Monlog()
    log.Info("info")
 	log.Notice("notice")
 	log.Warning("warning")
}

Golang 使用 Aws-sdk 獲取到指定bucket中全部的item

我前陣子寫過一篇文章,是Golang 調用 s3對象存儲的,使用指定的api可以獲取其中的item,但是人家限定100條,估摸着怕撐爆內存,但是如果我們想要獲取到所有的item,這個該怎么做呢?其實循環獲取就好了,但是我還是徒手實現一下吧,大家抄抄得了。

func GetRgwItem3(buckets string) ([]*s3.Object, error) {
    var items []*s3.Object
    sess, err := session.NewSession(&aws.Config{
		Credentials:      credentials.NewStaticCredentials(ak, sk, ""),
		Endpoint:         aws.String(endpoint + ":7480"),
		Region:           aws.String("us-east-1"),
		DisableSSL:       aws.Bool(true),
		S3ForcePathStyle: aws.Bool(false), //virtual-host style方式,不要修改
	})
	if err != nil {
		return nil, err
	}
	svc := s3.New(sess)
	err := svc.ListObjectsPages(&s3.ListObjectsInput{
		Bucket: &buckets,
	}, func(p *s3.ListObjectsOutput, last bool) (shouldContinue bool) {
		for _, obj := range p.Contents {
			items = append(items, obj)
		}
		return false
	})
	if err != nil {
		return items, err
	}
	return items, nil
}

我這里沒毛病啊,萬一你有問題你就咨詢我

后記

暫時先更這么多,這也不少了,大部分代碼我都給出了具體實現,要是還不會就留言給我或者發郵件。

其實我特想對那些寫幾個爬蟲在那里爬我博客的人說,你偷博客沒所謂,復制也沒所謂,不加名字說轉載就很說不過去了,還TM標榜你是原創,臉呢?cnblog我會逐漸棄掉,因為我有我自己的博客了,我也在逐漸搞遷移,把我原來寫的博客都遷移到我自己的博客上去,未來我的cnblog博客將逐漸少更或者停更,就算更我也會用英語/日語雙更,我讓你TM抄我東西不說,fuck off bitch。


免責聲明!

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



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