前言
忙碌了兩個月,這次開發終於要結束了,今天下午公司在重組集群機器,也沒辦法干活兒了,就寫一些東西,相當於,留住一些東西,來紀念這辛苦的兩個月吧。做一個紀念,也是為了方便以后自己去查看。在這次開發中,學習了不少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。