├── codes ├── init_trace │ ├── go.mod │ ├── trace │ │ └── trace.go │ ├── pkg2 │ │ └── pkg2.go │ ├── pkg1 │ │ └── pkg1.go │ └── main.go ├── proconsumer │ ├── go.mod │ ├── go.sum │ ├── consumer.go │ ├── producer.go │ ├── task_scheduler.go │ └── tests │ │ ├── testjob.go │ │ └── file.txt ├── practices │ └── crosscompile │ │ ├── rewrite_panics_windows.go │ │ └── rewrite_panics_unix.go ├── tag_picker │ ├── piker.go │ ├── order_num.go │ └── query_builer.go ├── slice_util │ └── code.go ├── distributed_lock │ ├── main.go │ ├── go.mod │ ├── etcd │ │ └── etcd_lock.go │ └── go.sum ├── unsafe │ ├── swtich_pointer_type.go │ ├── access_unexported_field.go │ ├── slice_switch_string_zero_copy.go │ └── use_sizeof.go ├── order_util │ └── gen_order_no.go ├── json │ └── encoder │ │ └── noescapehtml.go ├── ctx-usage-demo │ ├── http-timeout-demo.go │ ├── demo1.go │ └── demo2.go ├── atomic │ ├── value_demo.go │ └── add_load_demo.go ├── context_demo │ ├── ctx_listen_http_client_cancel.go │ ├── no_ctx_demo.go │ └── ctx_demo.go ├── slice │ ├── remove_duplicated_elements.go │ └── efficient_append.go ├── latency-codes │ └── req-body-not-close │ │ └── code.go ├── flag_library │ ├── boolean │ │ └── boolean.go │ ├── head_command │ │ └── head.go │ └── subcommand │ │ └── subcommand.go ├── gbkdetect │ └── code.go ├── copy_properties │ ├── copier_demo.go │ └── copy_with_option.go ├── synccond │ └── learn_sync_cond.go ├── singleflight │ └── anti_cache_break.go ├── password_complexity │ └── code.go ├── random_util │ └── code.go ├── password │ └── password.go ├── errorgroup │ └── errorgroup.go ├── mask_util │ └── code.go ├── reqeust_sign_generate │ └── request_sign.go ├── crypto_utils │ ├── aes_easy.go │ └── aes.go ├── trace_span │ └── main.go ├── http_client_with_rate │ └── http_rl_client.go ├── byte_order │ └── main.go ├── learntime │ └── code.go ├── cyclic_barrier │ └── h2o_factory.go ├── aes-go-java-convertor │ ├── aes-go │ │ └── ase.go │ └── aes-java │ │ └── EncryptDecryptDemo.java ├── httptool │ └── http_util.go ├── gen_token │ └── main.go ├── prevent_over_concurrency │ └── main.go ├── holmes_notes │ └── get_process_info.go ├── ali_tts_oss │ └── tts │ │ └── tts.go └── img │ └── base.go ├── cmd └── es │ ├── clear_doc_in_index.shell │ ├── query_with_must_not.shell │ └── create_index_with_mapping.shell ├── .gitignore ├── lang-basic ├── README.md └── content │ ├── chapter1.md │ ├── chapter3.md │ ├── chapter5.md │ ├── chapter4.md │ └── chapter2.md ├── LICENSE └── README.md /codes/init_trace/go.mod: -------------------------------------------------------------------------------- 1 | module example.com/init_trace 2 | 3 | go 1.13 4 | -------------------------------------------------------------------------------- /codes/proconsumer/go.mod: -------------------------------------------------------------------------------- 1 | module example.com/proconsumer 2 | 3 | go 1.13 4 | 5 | require golang.org/x/sync v0.0.0-20210220032951-036812b2e83c 6 | -------------------------------------------------------------------------------- /codes/practices/crosscompile/rewrite_panics_windows.go: -------------------------------------------------------------------------------- 1 | package crosscompile 2 | 3 | func RewritePanicsToFile(topic string) error { 4 | return nil 5 | } 6 | -------------------------------------------------------------------------------- /codes/init_trace/trace/trace.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import "fmt" 4 | 5 | func Trace(t string, v int) int { 6 | fmt.Println(t, ":", v) 7 | return v 8 | } 9 | -------------------------------------------------------------------------------- /cmd/es/clear_doc_in_index.shell: -------------------------------------------------------------------------------- 1 | curl -H 'Content-Type: application/json' -XPOST '{$es_server_host}[:{$port}]/{$index_name}/_delete_by_query' -d '{ 2 | "query" : { 3 | "match_all" : {} 4 | } 5 | }' 6 | -------------------------------------------------------------------------------- /codes/tag_picker/piker.go: -------------------------------------------------------------------------------- 1 | package tag_picker 2 | // TagPicker 接口定义 3 | type Picker interface { 4 | // 用于查询用户的标签值 5 | PickTagValueForUser (userId int64, args ... interface{}) 6 | // 通知查询到的标签值 7 | Notify () <-chan interface{} 8 | } 9 | -------------------------------------------------------------------------------- /codes/proconsumer/go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= 2 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /codes/init_trace/pkg2/pkg2.go: -------------------------------------------------------------------------------- 1 | package pkg2 2 | 3 | 4 | import ( 5 | "example.com/init_trace/trace" 6 | "fmt" 7 | ) 8 | 9 | var P2_v1 = trace.Trace("init P2_v1", 20) 10 | var P2_v2 = trace.Trace("init P2_v2", 30) 11 | 12 | func init() { 13 | fmt.Println("init func in pkg2") 14 | } 15 | -------------------------------------------------------------------------------- /codes/init_trace/pkg1/pkg1.go: -------------------------------------------------------------------------------- 1 | package pkg1 2 | 3 | import ( 4 | "example.com/init_trace/pkg2" 5 | "example.com/init_trace/trace" 6 | "fmt" 7 | ) 8 | 9 | var P1_v1 = trace.Trace("init P1_v1", pkg2.P2_v1 + 10) 10 | var P1_v2 = trace.Trace("init P1_v2", pkg2.P2_v2 + 10) 11 | 12 | func init() { 13 | fmt.Println("init func in pkg1") 14 | } 15 | -------------------------------------------------------------------------------- /codes/slice_util/code.go: -------------------------------------------------------------------------------- 1 | package slice_util 2 | 3 | func StringInSlice(search string, arr []string) bool { 4 | for _, v := range arr { 5 | if v == search { 6 | return true 7 | } 8 | } 9 | return false 10 | } 11 | 12 | func FilterStringSlice(ss []string, test func(string) bool) (ret []string) { 13 | for _, s := range ss { 14 | if test(s) { 15 | ret = append(ret, s) 16 | } 17 | } 18 | return 19 | } 20 | -------------------------------------------------------------------------------- /codes/init_trace/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "example.com/init_trace/pkg1" 5 | "example.com/init_trace/pkg2" 6 | 7 | "example.com/init_trace/trace" 8 | "fmt" 9 | ) 10 | 11 | func init() { 12 | fmt.Println("init1 func in main") 13 | } 14 | 15 | func init() { 16 | fmt.Println("init2 func in main") 17 | } 18 | 19 | var M_v1 = trace.Trace("init M_v1", pkg1.P1_v2 + 10) 20 | var M_v2 = trace.Trace("init M_v2", pkg2.P2_v2 + 10) 21 | 22 | 23 | func main() { 24 | fmt.Println("main func in main") 25 | } 26 | -------------------------------------------------------------------------------- /codes/proconsumer/consumer.go: -------------------------------------------------------------------------------- 1 | package proconsumer 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type Consumer struct { 8 | DataCh <- chan interface{} 9 | closure ConsumerCallerFunc 10 | CancelCtx context.Context 11 | } 12 | 13 | func (cs *Consumer) RegisterConsumeFunc(consumer ConsumerCallerFunc) error { 14 | cs.closure = consumer 15 | return nil 16 | } 17 | 18 | func (cs *Consumer) Run () error { 19 | err := cs.closure(cs) 20 | return err 21 | } 22 | 23 | type ConsumerCallerFunc func (consumer *Consumer) error -------------------------------------------------------------------------------- /codes/distributed_lock/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "example.com/distributed_lock/etcd" 5 | "flag" 6 | "log" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | addr = flag.String("addr", "192.168.64.4:30453", "etcd addresses") 12 | name = flag.String("name", "", "give a name") 13 | ) 14 | 15 | func main() { 16 | flag.Parse() 17 | endpoints := strings.Split(*addr, ",") 18 | cli, err := etcd.NewClient(endpoints) 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | defer cli.Close() 23 | 24 | etcd.UseLock(cli, *name) 25 | } 26 | -------------------------------------------------------------------------------- /codes/proconsumer/producer.go: -------------------------------------------------------------------------------- 1 | package proconsumer 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | type Producer struct { 8 | DataCh chan <- interface{} 9 | closure ProducerCallerFunc 10 | CancelCtx context.Context 11 | } 12 | 13 | 14 | func (pr *Producer) RegisterProduceFunc(closure ProducerCallerFunc) error { 15 | pr.closure = closure 16 | return nil 17 | } 18 | 19 | func (pr *Producer) Run() error { 20 | err := pr.closure(pr) 21 | return err 22 | } 23 | 24 | 25 | // 生产者要调用的方法 26 | type ProducerCallerFunc func(producer *Producer) error -------------------------------------------------------------------------------- /codes/unsafe/swtich_pointer_type.go: -------------------------------------------------------------------------------- 1 | import ( 2 | "fmt" 3 | "reflect" 4 | "unsafe" 5 | ) 6 | 7 | func main() { 8 | 9 | v1 := uint(12) 10 | v2 := int(13) 11 | 12 | fmt.Println(reflect.TypeOf(v1)) //uint 13 | fmt.Println(reflect.TypeOf(v2)) //int 14 | 15 | fmt.Println(reflect.TypeOf(&v1)) //*uint 16 | fmt.Println(reflect.TypeOf(&v2)) //*int 17 | 18 | p := &v1 19 | p = (*uint)(unsafe.Pointer(&v2)) //使用unsafe.Pointer进行类型的转换 20 | 21 | fmt.Println(reflect.TypeOf(p)) // *unit 22 | fmt.Println(*p) //13 23 | } 24 | -------------------------------------------------------------------------------- /codes/unsafe/access_unexported_field.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | ) 7 | 8 | func main() { 9 | 10 | var x struct { 11 | a int 12 | b int 13 | c []int 14 | } 15 | 16 | // unsafe.Offsetof 函数的参数必须是一个字段, 比如 x.b, 方法会返回 b 字段相对于 x 起始地址的偏移量, 包括可能的空洞。 17 | 18 | // 指针运算 uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)。 19 | 20 | // 和 pb := &x.b 等价 21 | pb := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b))) 22 | 23 | *pb = 42 24 | fmt.Println(x.b) // "42" 25 | } 26 | -------------------------------------------------------------------------------- /codes/order_util/gen_order_no.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | fmt.Println(GenOrderNo(12344566256)) 11 | } 12 | 13 | func GenOrderNo(userId int64) string { 14 | const TimeFormatPlainDate = "20060102" 15 | day := time.Now().Format(TimeFormatPlainDate) 16 | 17 | rand.Seed(time.Now().UnixNano()) 18 | seqStr := fmt.Sprintf("%014d", rand.Intn(99999999999999)) 19 | 20 | subId := fmt.Sprintf("%04d", 12344566) 21 | if len(subId) > 4 { 22 | subId = subId[len(subId)-5 : len(subId)-1] 23 | } 24 | return day + seqStr + subId 25 | } 26 | -------------------------------------------------------------------------------- /codes/json/encoder/noescapehtml.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | ) 8 | 9 | func main() { 10 | data := struct { 11 | Name string 12 | Url string 13 | }{ 14 | Name: "Jesper", 15 | Url: "https://yourexample.com/add?type=1&sku=iphone", 16 | } 17 | enc, _ := MarshalWithNoEscape(&data) 18 | fmt.Println(string(enc)) 19 | } 20 | 21 | func MarshalWithNoEscape(i interface{}) ([]byte, error) { 22 | buffer := &bytes.Buffer{} 23 | encoder := json.NewEncoder(buffer) 24 | encoder.SetEscapeHTML(false) 25 | err := encoder.Encode(i) 26 | return bytes.TrimRight(buffer.Bytes(), "\n"), err 27 | } 28 | -------------------------------------------------------------------------------- /codes/ctx-usage-demo/http-timeout-demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | // 创建一个监听8000端口的HTTP Server 12 | http.ListenAndServe(":8000", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 | ctx := r.Context() 14 | // 开始处理请求 15 | fmt.Fprint(os.Stdout, "processing request\n") 16 | // select 接收定时和ctx.Done两个channel,哪个先来执行哪个。 17 | select { 18 | case <-time.After(2 * time.Second): 19 | // 用定时器模拟请求处理成功,如果2s后接收到了channel里的信息,返回请求成功 20 | w.Write([]byte("request processed")) 21 | case <-ctx.Done(): 22 | // 如果请求被取消了,返回请求被取消 23 | fmt.Fprint(os.Stderr, "request cancelled\n") 24 | } 25 | })) 26 | } 27 | -------------------------------------------------------------------------------- /codes/atomic/value_demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | type Rectangle struct { 10 | length int 11 | width int 12 | } 13 | 14 | var rect atomic.Value 15 | 16 | func update(width, length int) { 17 | rectLocal := new(Rectangle) 18 | rectLocal.width = width 19 | rectLocal.length = length 20 | rect.Store(rectLocal) 21 | } 22 | 23 | func main() { 24 | wg := sync.WaitGroup{} 25 | wg.Add(10) 26 | // 10 个协程并发更新 27 | for i := 0; i < 10; i++ { 28 | go func() { 29 | defer wg.Done() 30 | update(i, i+5) 31 | }() 32 | } 33 | wg.Wait() 34 | _r := rect.Load().(*Rectangle) 35 | fmt.Printf("rect.width=%d\nrect.length=%d\n", _r.width, _r.length) 36 | } 37 | -------------------------------------------------------------------------------- /codes/context_demo/ctx_listen_http_client_cancel.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | // 创建一个监听8000端口的HTTP Server 12 | http.ListenAndServe(":8000", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 | ctx := r.Context() 14 | // 开始处理请求 15 | fmt.Fprint(os.Stdout, "processing request\n") 16 | // select 接收定时和ctx.Done两个channel,哪个先来执行哪个。 17 | select { 18 | case <-time.After(2 * time.Second): 19 | // 用定时器模拟请求处理成功,如果2s后接收到了channel里的信息,返回请求成功 20 | w.Write([]byte("request processed")) 21 | case <-ctx.Done(): 22 | // 如果请求被取消了,返回请求被取消 23 | fmt.Fprint(os.Stderr, "request cancelled\n") 24 | } 25 | })) 26 | } 27 | -------------------------------------------------------------------------------- /codes/ctx-usage-demo/demo1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | messages := make(chan int, 10) 10 | done := make(chan bool) 11 | 12 | defer close(messages) 13 | // consumer 14 | go func() { 15 | ticker := time.NewTicker(1 * time.Second) 16 | for _ = range ticker.C { 17 | select { 18 | case <-done: 19 | fmt.Println("child process interrupt...") 20 | return 21 | default: 22 | fmt.Printf("received message: %d\n", <-messages) 23 | } 24 | } 25 | }() 26 | 27 | // producer 28 | for i := 0; i < 10; i++ { 29 | messages <- i 30 | } 31 | time.Sleep(5 * time.Second) 32 | close(done) 33 | time.Sleep(1 * time.Second) 34 | fmt.Println("main process exit!") 35 | } 36 | -------------------------------------------------------------------------------- /codes/context_demo/no_ctx_demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | messages := make(chan int, 10) 10 | done := make(chan bool) 11 | 12 | defer close(messages) 13 | // consumer 14 | go func() { 15 | ticker := time.NewTicker(1 * time.Second) 16 | for _ = range ticker.C { 17 | select { 18 | case <-done: 19 | fmt.Println("child process interrupt...") 20 | return 21 | default: 22 | fmt.Printf("received message: %d\n", <-messages) 23 | } 24 | } 25 | }() 26 | 27 | // producer 28 | for i := 0; i < 10; i++ { 29 | messages <- i 30 | } 31 | time.Sleep(5 * time.Second) 32 | close(done) 33 | time.Sleep(1 * time.Second) 34 | fmt.Println("main process exit!") 35 | } 36 | -------------------------------------------------------------------------------- /codes/slice/remove_duplicated_elements.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func main() { 8 | s := []string{"hello", "world", "hello", "golang", "hello", "ruby", "php", "java"} 9 | 10 | fmt.Println(removeDuplicateElement(s)) //output: hello world golang ruby php java 11 | } 12 | 13 | func removeDuplicateElement(languages []string) []string { 14 | result := make([]string, 0, len(languages)) 15 | temp := map[string]struct{}{} 16 | for _, item := range languages { 17 | if _, ok := temp[item]; !ok { 18 | temp[item] = struct{}{} 19 | result = append(result, item) 20 | } 21 | } 22 | return result 23 | } 24 | 25 | // output: ["hello", "world", "golang", "ruby", "php", "java"] 26 | -------------------------------------------------------------------------------- /codes/distributed_lock/go.mod: -------------------------------------------------------------------------------- 1 | module example.com/distributed_lock 2 | 3 | go 1.13 4 | 5 | replace ( 6 | github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.5 7 | google.golang.org/grpc v1.38.0 => google.golang.org/grpc v1.26.0 8 | ) 9 | 10 | require ( 11 | github.com/coreos/bbolt v0.0.0-00010101000000-000000000000 // indirect 12 | github.com/coreos/etcd v3.3.25+incompatible 13 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect 14 | github.com/google/uuid v1.2.0 // indirect 15 | github.com/prometheus/client_golang v1.10.0 // indirect 16 | go.etcd.io/etcd v3.3.25+incompatible 17 | go.uber.org/zap v1.17.0 // indirect 18 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect 19 | google.golang.org/grpc v1.38.0 // indirect 20 | ) 21 | -------------------------------------------------------------------------------- /codes/latency-codes/req-body-not-close/code.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // request body不进行 body.Close() 操作导致移除的先决条件 4 | // 既不执行 ioutil.ReadAll(resp.Body) 也不执行resp.Body.Close(),并且不设置http.Client内timeout的时候,就会导致协程泄露 5 | func main() { 6 | tr := &http.Transport{ 7 | MaxIdleConns: 100, 8 | IdleConnTimeout: 3 * time.Second, 9 | } 10 | 11 | n := 5 12 | for i := 0; i < n; i++ { 13 | req, _ := http.NewRequest("POST", "https://www.baidu.com", nil) 14 | req.Header.Add("content-type", "application/json") 15 | client := &http.Client{ 16 | Transport: tr, 17 | } 18 | resp, _ := client.Do(req) 19 | _ = resp 20 | } 21 | time.Sleep(time.Second * 5) 22 | fmt.Printf("goroutine num is %d\n", runtime.NumGoroutine()) 23 | } 24 | -------------------------------------------------------------------------------- /codes/flag_library/boolean/boolean.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | ) 7 | 8 | type Color string 9 | 10 | const ( 11 | ColorBlack Color = "\u001b[30m" 12 | ColorRed = "\u001b[31m" 13 | ColorGreen = "\u001b[32m" 14 | ColorYellow = "\u001b[33m" 15 | ColorBlue = "\u001b[34m" 16 | ColorReset = "\u001b[0m" 17 | ) 18 | 19 | func colorize(color Color, message string) { 20 | fmt.Println(string(color), message, string(ColorReset)) 21 | } 22 | 23 | func main() { 24 | useColor := flag.Bool("color", false, "display colorized output") 25 | flag.Parse() 26 | 27 | if *useColor { 28 | colorize(ColorBlue, "Hello, DigitalOcean!") 29 | return 30 | } 31 | fmt.Println("Hello, DigitalOcean!") 32 | } 33 | -------------------------------------------------------------------------------- /codes/unsafe/slice_switch_string_zero_copy.go: -------------------------------------------------------------------------------- 1 | import ( 2 | "fmt" 3 | "reflect" 4 | "unsafe" 5 | ) 6 | 7 | func main() { 8 | s := "Hello World" 9 | b := string2bytes(s) 10 | fmt.Println(b) 11 | s = bytes2string(b) 12 | fmt.Println(s) 13 | 14 | } 15 | 16 | func string2bytes(s string) []byte { 17 | stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&s)) 18 | 19 | bh := reflect.SliceHeader{ 20 | Data: stringHeader.Data, 21 | Len: stringHeader.Len, 22 | Cap: stringHeader.Len, 23 | } 24 | 25 | return *(*[]byte)(unsafe.Pointer(&bh)) 26 | } 27 | 28 | func bytes2string(b []byte) string { 29 | sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 30 | 31 | sh := reflect.StringHeader{ 32 | Data: sliceHeader.Data, 33 | Len: sliceHeader.Len, 34 | } 35 | 36 | return *(*string)(unsafe.Pointer(&sh)) 37 | } 38 | -------------------------------------------------------------------------------- /codes/gbkdetect/code.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "golang.org/x/text/encoding/simplifiedchinese" 6 | "golang.org/x/text/transform" 7 | "io" 8 | "unicode/utf8" 9 | ) 10 | 11 | func convertGBKToUTF8(input string) (string, error) { 12 | // 检测是否为 GBK 编码(Windows 中文文件名常用) 13 | if detectEncoding([]byte(input)) != "GBK" { 14 | return input, nil 15 | } 16 | // GBK → UTF-8 转换 17 | decoder := simplifiedchinese.GBK.NewDecoder() 18 | reader := transform.NewReader(bytes.NewReader([]byte(input)), decoder) 19 | result, err := io.ReadAll(reader) 20 | return string(result), err 21 | } 22 | 23 | func detectEncoding(data []byte) string { 24 | if utf8.Valid(data) { 25 | return "UTF-8" 26 | } 27 | decoder := simplifiedchinese.GBK.NewDecoder() 28 | if _, _, err := transform.Bytes(decoder, data); err == nil { 29 | return "GBK" 30 | } 31 | return "Unknown" 32 | } 33 | -------------------------------------------------------------------------------- /codes/practices/crosscompile/rewrite_panics_unix.go: -------------------------------------------------------------------------------- 1 | //+build darwin linux unix 2 | 3 | package crosscompile 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "runtime" 9 | "syscall" 10 | ) 11 | 12 | var stdErrFileHandler *os.File 13 | // go的运行时错误panic默认写到标准错误中, 由于无法持久化容器重启后无法排查引起panic的原因 14 | // 使用此方法将标准错误重写到持久化的日志文件里 15 | func RewritePanicsToFile(topic string) error { 16 | errFile := "/home/golanger/log/" + topic + "_stdErr.log" 17 | errFileHandler, err := os.OpenFile(errFile, os.O_RDWR|os.O_CREATE | os.O_APPEND, 0666) 18 | if err != nil { 19 | fmt.Println(err) 20 | return err 21 | } 22 | stdErrFileHandler = errFileHandler //把文件句柄保存到全局变量,避免被GC回收 23 | 24 | if err = syscall.Dup2(int(errFileHandler.Fd()), int(os.Stderr.Fd())); err != nil { 25 | return err 26 | } 27 | // GC前关闭文件描述符 28 | runtime.SetFinalizer(stdErrFileHandler, func(fd *os.File) { 29 | fd.Close() 30 | }) 31 | 32 | return nil 33 | } 34 | 35 | -------------------------------------------------------------------------------- /codes/copy_properties/copier_demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/jinzhu/copier" 6 | ) 7 | 8 | type RoleA struct { 9 | RoleId int 10 | RoleName string 11 | } 12 | 13 | type RoleB struct { 14 | RoleId int 15 | RoleName string 16 | } 17 | 18 | type User struct { 19 | Name string 20 | Role RoleA 21 | Age int32 22 | EmployeeCode int64 23 | Salary int 24 | } 25 | 26 | type Employee struct { 27 | Name string 28 | Role RoleB 29 | Age int32 30 | EmployeeCode int64 31 | Salary int 32 | } 33 | 34 | func CopyProperties() { 35 | user := User{Name: "KevinYan", Age: 18, Salary: 200000} 36 | user.Role = RoleA{ 37 | RoleId: 2, 38 | RoleName: "Admin", 39 | } 40 | employee := new(Employee) 41 | 42 | 43 | copier.Copy(employee, &user) 44 | 45 | fmt.Printf( "%v\n", employee) 46 | fmt.Printf("%v\n", user) 47 | } 48 | 49 | func main() { 50 | CopyProperties() 51 | } 52 | -------------------------------------------------------------------------------- /codes/tag_picker/order_num.go: -------------------------------------------------------------------------------- 1 | package tag_picker 2 | 3 | 4 | type OrderNumTagPicker struct { 5 | TagName string 6 | ValueCh chan interface{} 7 | } 8 | 9 | type TradeNoInfo struct { 10 | TradeNo string `json:"trade_no"` 11 | } 12 | 13 | func (picker *OrderNumTagPicker) PickTagValueForUser(userId int64, args ...interface{}) { 14 | // 用类型转换得到交易号 15 | // tradeNo, ok := args[0].(string) 16 | extInfoJson, _ := args[0].(string) 17 | 18 | if err := json.Unmarshal([]byte(extInfoJson), &TradeNoInfo); err != nil { 19 | // log.Error自己实现 20 | log.Error("PayTotalTagPickerError", "Invalid arg", args[0]) 21 | // 结束执行并通知外部 22 | picker.ValueCh <- nil 23 | return 24 | } 25 | // 这里就打印下参数值,标签查询的具体逻辑自己实现 26 | fmt.Println(userId) 27 | fmt.Println(TradeNo) 28 | // 查询到的用户的标签 (假设交易号下有10个订单) 29 | picker.ValueCh <- 10 30 | } 31 | 32 | func (picker *OrderNumTagPicker) Notify () <-chan interface{} { 33 | return picker.ValueCh 34 | } 35 | -------------------------------------------------------------------------------- /codes/flag_library/head_command/head.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "flag" 6 | "fmt" 7 | "io" 8 | "os" 9 | ) 10 | 11 | func main() { 12 | var count int 13 | flag.IntVar(&count, "n", 5, "number of lines to read from the file") 14 | flag.Parse() 15 | 16 | var in io.Reader 17 | if filename := flag.Arg(0); filename != "" { 18 | f, err := os.Open(filename) 19 | if err != nil { 20 | fmt.Println("error opening file: err:", err) 21 | os.Exit(1) 22 | } 23 | defer f.Close() 24 | 25 | in = f 26 | } else { 27 | in = os.Stdin 28 | } 29 | 30 | buf := bufio.NewScanner(in) 31 | 32 | for i := 0; i < count; i++ { 33 | if !buf.Scan() { 34 | break 35 | } 36 | fmt.Println(buf.Text()) 37 | } 38 | 39 | if err := buf.Err(); err != nil { 40 | fmt.Fprintln(os.Stderr, "error reading: err:", err) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lang-basic/README.md: -------------------------------------------------------------------------------- 1 | 我维护 GoCookBook-- Go开发实战参考书的目的是为了记录做Go开发期间用过、学过、被坑过的各种经历,让自己也让偶然看到这个项目的人能更高效的完成生产级的程序开发。 2 | 3 | 不过我发现一些基础语法、概念之类的东西,只要不用,过不了两周就会忘或者是记忆模糊了,如果同时写两种编程语言也有类似的症状。所以这里再把以前记得一些基础语法、概念整理到项目里。这些都是我以前刚学Go时记得一些读书笔记,几乎没有什么废话, 4 | 全是各种语法、概念的代码示例,如果正好你也跟我一样健忘,可以常来翻翻,文章主要涉及五块基础内容,高阶和实战内容还是参考项目的其他章节吧。 5 | 6 | ### Go 语言基础 7 | - [基础类型和复合类型](https://github.com/kevinyan815/gocookbook/blob/master/lang-basic/content/chapter1.md) 8 | - [函数](https://github.com/kevinyan815/gocookbook/blob/master/lang-basic/content/chapter2.md) 9 | - [方法](https://github.com/kevinyan815/gocookbook/blob/master/lang-basic/content/chapter3.md) 10 | - [接口 -- interface](https://github.com/kevinyan815/gocookbook/blob/master/lang-basic/content/chapter4.md) 11 | - [goroutine和channel](https://github.com/kevinyan815/gocookbook/blob/master/lang-basic/content/chapter5.md) 12 | 13 | 14 | **实战内容请查看其他章节, 更多时下流行技术的应用和实战教程,可通过我公众号「网管叨bi叨」每周的推文来学习** 15 | 16 | ![](https://cdn.learnku.com/uploads/images/202001/01/6964/cuN9WI2NBT.png!large) 17 | -------------------------------------------------------------------------------- /codes/slice/efficient_append.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func VariableLenNoInitialSlice(size int) []int { 4 | slice := make([]int, 0) 5 | for i := 0; i < size; i++ { 6 | slice = append(slice, i) 7 | } 8 | return slice 9 | } 10 | 11 | func VariableLenShorterSlice(size int) []int { 12 | slice := make([]int, 100) 13 | for i := 0; i < size; i++ { 14 | slice = append(slice, i) 15 | } 16 | return slice 17 | } 18 | 19 | // Recommanded append in this way 20 | // first predfine slice capability to parameter specified size 21 | // then make it length to 0 at the beginning 22 | // and append item into it in iteration. 23 | // This would not cause the array underlying this slice to expend 24 | // that means not memory copy and paste opertion 25 | // that's why its more efficient then other ways 26 | func VariableLenLargerSlice(size int) []int { 27 | slice := make([]int, 0, size) 28 | for i := 0; i < size; i++ { 29 | slice = append(slice, i) 30 | } 31 | return slice 32 | } 33 | -------------------------------------------------------------------------------- /codes/synccond/learn_sync_cond.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | var queue []struct{} 11 | 12 | func main() { 13 | var wg sync.WaitGroup 14 | c := sync.NewCond(&sync.Mutex{}) 15 | 16 | for i := 0; i < 2; i ++ { 17 | wg.Add(1) 18 | 19 | go func(i int) { 20 | // this go routine wait for changes to the sharedRsc 21 | c.L.Lock() 22 | for len(queue) <= 0 { 23 | fmt.Println("goroutine" + strconv.Itoa(i) +" wait") 24 | c.Wait() 25 | } 26 | fmt.Println("goroutine" + strconv.Itoa(i), "pop data") 27 | queue = queue[1:] 28 | c.L.Unlock() 29 | wg.Done() 30 | }(i) 31 | 32 | } 33 | 34 | 35 | for i := 0; i < 2; i ++ { 36 | // 主goroutine延迟两秒准备好后把变量设置为true 37 | time.Sleep(2 * time.Second) 38 | c.L.Lock() 39 | fmt.Println("main goroutine push data") 40 | queue= append(queue, struct{}{}) 41 | c.Broadcast() 42 | fmt.Println("main goroutine broadcast") 43 | c.L.Unlock() 44 | 45 | } 46 | 47 | wg.Wait() 48 | } 49 | -------------------------------------------------------------------------------- /codes/atomic/add_load_demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | mutexAdd() 12 | AtomicAdd() 13 | } 14 | 15 | func mutexAdd() { 16 | var a int32 = 0 17 | var wg sync.WaitGroup 18 | var mu sync.Mutex 19 | start := time.Now() 20 | for i := 0; i < 1000000; i++ { 21 | wg.Add(1) 22 | go func() { 23 | defer wg.Done() 24 | mu.Lock() 25 | a += 1 26 | mu.Unlock() 27 | }() 28 | } 29 | wg.Wait() 30 | timeSpends := time.Now().Sub(start).Nanoseconds() 31 | fmt.Printf("use mutex a is %d, spend time: %v\n", a, timeSpends) 32 | } 33 | 34 | func AtomicAdd() { 35 | var a int32 = 0 36 | var wg sync.WaitGroup 37 | start := time.Now() 38 | for i := 0; i < 1000000; i++ { 39 | wg.Add(1) 40 | go func() { 41 | defer wg.Done() 42 | atomic.AddInt32(&a, 1) 43 | }() 44 | } 45 | wg.Wait() 46 | timeSpends := time.Now().Sub(start).Nanoseconds() 47 | fmt.Printf("use atomic a is %d, spend time: %v\n", atomic.LoadInt32(&a), timeSpends) 48 | } 49 | -------------------------------------------------------------------------------- /codes/singleflight/anti_cache_break.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/sync/singleflight" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | // 模拟一个Redis客户端 11 | type client struct { 12 | // ... 其他的配置省略 13 | requestGroup singleflight.Group 14 | } 15 | 16 | // 普通查询 17 | func (c *client) Get(key string) (interface{}, error) { 18 | fmt.Println("Querying Database") 19 | time.Sleep(time.Second) 20 | v := "Content of key" + key 21 | return v, nil 22 | } 23 | 24 | // SingleFlight查询 25 | func (c *client) SingleFlightGet(key string) (interface{}, error) { 26 | v, err, _ := c.requestGroup.Do(key, func() (interface{}, error) { 27 | return c.Get(key) 28 | 29 | }) 30 | if err != nil { 31 | return nil, err 32 | } 33 | return v, err 34 | } 35 | 36 | func main() { 37 | redisClient := new(client) 38 | wg := sync.WaitGroup{} 39 | wg.Add(10) 40 | for i := 0; i < 10; i++ { 41 | 42 | go func() { 43 | defer wg.Done() 44 | v, _ := redisClient.SingleFlightGet("Cyberpunk2077!!!") 45 | fmt.Println(v) 46 | }() 47 | } 48 | wg.Wait() 49 | } 50 | -------------------------------------------------------------------------------- /codes/password_complexity/code.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "unicode" 6 | ) 7 | 8 | func verifyPasswordComplexity(s string) bool { 9 | var ( 10 | hasMinLen = false 11 | hasUpper = false 12 | hasLower = false 13 | hasNumber = false 14 | hasSpecial = false 15 | ) 16 | if len(s) >= 8 { 17 | hasMinLen = true 18 | } 19 | for _, char := range s { 20 | switch { 21 | case unicode.IsUpper(char): 22 | hasUpper = true 23 | case unicode.IsLower(char): 24 | hasLower = true 25 | case unicode.IsNumber(char): 26 | hasNumber = true 27 | case unicode.IsPunct(char) || unicode.IsSymbol(char): 28 | hasSpecial = true 29 | } 30 | } 31 | return hasMinLen && hasUpper && hasLower && hasNumber && hasSpecial 32 | } 33 | 34 | func main() { 35 | fmt.Println(verifyPasswordComplexity("pass")) // false 36 | fmt.Println(verifyPasswordComplexity("password")) // false 37 | fmt.Println(verifyPasswordComplexity("Password")) // false 38 | fmt.Println(verifyPasswordComplexity("P@ssword")) // false 39 | fmt.Println(verifyPasswordComplexity("P@ssw0rd")) // true 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kevin Yan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /codes/random_util/code.go: -------------------------------------------------------------------------------- 1 | package random_util 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | type ( 9 | Random struct { 10 | charset Charset 11 | } 12 | 13 | Charset string 14 | ) 15 | 16 | const ( 17 | Alphanumeric Charset = Alphabetic + Numeric 18 | Alphabetic Charset = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 19 | Numeric Charset = "0123456789" 20 | Hex Charset = Numeric + "abcdef" 21 | ) 22 | 23 | var ( 24 | globalRandom = NewRandom() 25 | ) 26 | 27 | func NewRandom() *Random { 28 | rand.Seed(time.Now().UnixNano()) 29 | return &Random{ 30 | charset: Alphanumeric, 31 | } 32 | } 33 | 34 | func (r *Random) SetCharset(c Charset) { 35 | r.charset = c 36 | } 37 | 38 | func (r *Random) String(length uint8) string { 39 | b := make([]byte, length) 40 | for i := range b { 41 | b[i] = r.charset[rand.Int63()%int64(len(r.charset))] 42 | } 43 | return string(b) 44 | } 45 | 46 | func SetCharset(c Charset) { 47 | globalRandom.SetCharset(c) 48 | } 49 | 50 | func RandomString(length uint8) string { 51 | return globalRandom.String(length) 52 | } 53 | 54 | func RandNumStr(len uint8) string { 55 | r := NewRandom() 56 | r.SetCharset(Numeric) 57 | return r.String(len) 58 | } 59 | -------------------------------------------------------------------------------- /codes/password/password.go: -------------------------------------------------------------------------------- 1 | package password 2 | 3 | import ( 4 | "golang.org/x/crypto/bcrypt" 5 | "unicode" 6 | ) 7 | 8 | func BcryptPassword(plainPassword string) (string, error) { 9 | bytes, err := bcrypt.GenerateFromPassword([]byte(plainPassword), 11) 10 | return string(bytes), err 11 | } 12 | 13 | func BcryptCompare(passwordHash, plainPassword string) bool { 14 | err := bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(plainPassword)) 15 | return err == nil 16 | } 17 | 18 | func BcryptGetCost(passwordHash string) int { 19 | cost, _ := bcrypt.Cost([]byte(passwordHash)) 20 | return cost 21 | } 22 | 23 | func PasswordComplexityVerify(s string) bool { 24 | var ( 25 | hasMinLen = false 26 | hasUpper = false 27 | hasLower = false 28 | hasNumber = false 29 | hasSpecial = false 30 | ) 31 | if len(s) >= 8 { 32 | hasMinLen = true 33 | } 34 | for _, char := range s { 35 | switch { 36 | case unicode.IsUpper(char): 37 | hasUpper = true 38 | case unicode.IsLower(char): 39 | hasLower = true 40 | case unicode.IsNumber(char): 41 | hasNumber = true 42 | case unicode.IsPunct(char) || unicode.IsSymbol(char): 43 | hasSpecial = true 44 | } 45 | } 46 | return hasMinLen && hasUpper && hasLower && hasNumber && hasSpecial 47 | } 48 | -------------------------------------------------------------------------------- /codes/errorgroup/errorgroup.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "golang.org/x/sync/errgroup" 10 | ) 11 | 12 | func ErrorGroupDemo() { 13 | var eg errgroup.Group 14 | for i := 0; i < 100; i++ { 15 | i := i 16 | eg.Go(func() error { 17 | time.Sleep(2 * time.Second) // 長い処理 18 | if i > 90 { 19 | fmt.Println("Error:", i) 20 | return fmt.Errorf("Error occurred: %d", i) 21 | } 22 | fmt.Println("End:", i) 23 | return nil 24 | }) 25 | } 26 | if err := eg.Wait(); err != nil { 27 | log.Fatal(err) 28 | } 29 | } 30 | 31 | func ErrorCancelGroupDemo() { 32 | eg, ctx := errgroup.WithContext(context.Background()) 33 | 34 | for i := 0; i < 100; i++ { 35 | i := i 36 | eg.Go(func() error { 37 | time.Sleep(2 * time.Second) 38 | 39 | select { 40 | case <-ctx.Done(): 41 | fmt.Println("Canceled:", i) 42 | return nil 43 | default: 44 | if i > 90 { 45 | fmt.Println("Error:", i) 46 | return fmt.Errorf("Error: %d", i) 47 | } 48 | fmt.Println("End:", i) 49 | return nil 50 | } 51 | }) 52 | } 53 | if err := eg.Wait(); err != nil { 54 | log.Fatal(err) 55 | } 56 | } 57 | 58 | func main() { 59 | 60 | // ErrorGroupDemo() 61 | 62 | ErrorCancelGroupDemo() 63 | } 64 | -------------------------------------------------------------------------------- /codes/copy_properties/copy_with_option.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "github.com/jinzhu/copier" 6 | "regexp" 7 | "time" 8 | ) 9 | 10 | func CopyProperties(dst, src interface{}) error { 11 | err := copier.CopyWithOption(dst, src, copier.Option{ 12 | IgnoreEmpty: true, 13 | DeepCopy: true, 14 | Converters: []copier.TypeConverter{ 15 | { // time.Time 转换成字符串 16 | SrcType: time.Time{}, 17 | DstType: copier.String, 18 | Fn: func(src interface{}) (dst interface{}, err error) { 19 | s, ok := src.(time.Time) 20 | if !ok { 21 | return nil, errors.New("src type is not time.Time") 22 | } 23 | return s.Format("2006-01-02 15:04:05"), nil 24 | }, 25 | }, 26 | { // 字符串转成time.Time 27 | SrcType: copier.String, 28 | DstType: time.Time{}, 29 | Fn: func(src interface{}) (dst interface{}, err error) { 30 | s, ok := src.(string) 31 | 32 | if !ok { 33 | return nil, errors.New("src type is not time format string") 34 | } 35 | pattern := `^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$` // YYYY-MM-DD HH:MM:SS 36 | matched, _ := regexp.MatchString(pattern, s) 37 | if matched { 38 | return time.Parse("2006-01-02 15:04:05", s) 39 | } 40 | return nil, errors.New("src type is not time format string") 41 | }, 42 | }, 43 | }, 44 | 45 | }) 46 | 47 | return err 48 | } 49 | -------------------------------------------------------------------------------- /codes/context_demo/ctx_demo.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | messages := make(chan int, 10) 11 | 12 | // producer 13 | for i := 0; i < 10; i++ { 14 | messages <- i 15 | } 16 | valCtx := context.WithValue(context.Background(), "client-ip", "10.96.130.167") 17 | ctx, cancel := context.WithTimeout(valCtx, 5*time.Second) 18 | 19 | // consumer 20 | go func(ctx context.Context) { 21 | ticker := time.NewTicker(1 * time.Second) 22 | for _ = range ticker.C { 23 | select { 24 | case <-ctx.Done(): 25 | fmt.Println("child process interrupt...") 26 | return 27 | default: 28 | i := <-messages 29 | timeoutCtx, _ := context.WithTimeout(ctx, 10 * time.Second) 30 | fmt.Printf("child receive message: %d\n", 1) 31 | go anotherFunc(timeoutCtx, i) 32 | } 33 | } 34 | }(ctx) 35 | 36 | defer close(messages) 37 | defer cancel() 38 | 39 | select { 40 | case <-ctx.Done(): 41 | time.Sleep(1 * time.Second) 42 | fmt.Println("main process exit!") 43 | } 44 | } 45 | 46 | func anotherFunc(ctx context.Context, message int) { 47 | ticker := time.NewTicker(1 * time.Second) 48 | for _ = range ticker.C { 49 | select { 50 | case <-ctx.Done(): 51 | fmt.Println("descendants process interrupt...") 52 | return 53 | default: 54 | fmt.Printf("host-ip: %s, send message %d\n", ctx.Value("client-ip"), message) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /codes/ctx-usage-demo/demo2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | messages := make(chan int, 10) 11 | 12 | // producer 13 | for i := 0; i < 10; i++ { 14 | messages <- i 15 | } 16 | valCtx := context.WithValue(context.Background(), "client-ip", "10.96.130.167") 17 | ctx, cancel := context.WithTimeout(valCtx, 5*time.Second) 18 | 19 | // consumer 20 | go func(ctx context.Context) { 21 | ticker := time.NewTicker(1 * time.Second) 22 | for _ = range ticker.C { 23 | select { 24 | case <-ctx.Done(): 25 | fmt.Println("child process interrupt...") 26 | return 27 | default: 28 | i := <-messages 29 | timeoutCtx, _ := context.WithTimeout(ctx, 10 * time.Second) 30 | fmt.Printf("child receive message: %d\n", 1) 31 | go anotherFunc(timeoutCtx, i) 32 | } 33 | } 34 | }(ctx) 35 | 36 | defer close(messages) 37 | defer cancel() 38 | 39 | select { 40 | case <-ctx.Done(): 41 | time.Sleep(1 * time.Second) 42 | fmt.Println("main process exit!") 43 | } 44 | } 45 | 46 | func anotherFunc(ctx context.Context, message int) { 47 | ticker := time.NewTicker(1 * time.Second) 48 | for _ = range ticker.C { 49 | select { 50 | case <-ctx.Done(): 51 | fmt.Println("descendants process interrupt...") 52 | return 53 | default: 54 | fmt.Printf("host-ip: %s, send message %d\n", ctx.Value("client-ip"), message) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /codes/proconsumer/task_scheduler.go: -------------------------------------------------------------------------------- 1 | package proconsumer 2 | 3 | import ( 4 | "context" 5 | "golang.org/x/sync/errgroup" 6 | ) 7 | 8 | type TaskScheduler struct { 9 | errGroup *errgroup.Group 10 | producer *Producer 11 | consumer *Consumer 12 | consumerNum int 13 | } 14 | 15 | func NewTaskScheduler (pFunc ProducerCallerFunc, cFunc ConsumerCallerFunc, consumerNum int) (ts *TaskScheduler) { 16 | 17 | eg, cancelCtx := errgroup.WithContext(context.Background()) 18 | 19 | dataCh := make(chan interface{}, 1000) 20 | producer := &Producer{ 21 | DataCh: dataCh, 22 | } 23 | consumer := &Consumer{ 24 | DataCh: dataCh, 25 | } 26 | producer.RegisterProduceFunc(pFunc) 27 | consumer.RegisterConsumeFunc(cFunc) 28 | pCancelCtx, _ := context.WithCancel(cancelCtx) 29 | cCancelCtx, _ := context.WithCancel(cancelCtx) 30 | producer.CancelCtx = pCancelCtx 31 | consumer.CancelCtx = cCancelCtx 32 | ts = &TaskScheduler{ 33 | errGroup: eg, 34 | producer: producer, 35 | consumer: consumer, 36 | consumerNum: consumerNum, 37 | } 38 | 39 | return 40 | } 41 | 42 | func (ts *TaskScheduler) Execute () (err error) { 43 | 44 | ts.errGroup.Go(ts.producer.Run) 45 | 46 | for i := 0; i < ts.consumerNum; i++ { 47 | ts.errGroup.Go(ts.consumer.Run) 48 | } 49 | 50 | err = ts.errGroup.Wait() 51 | if err != nil { 52 | // 如果有goroutine执行发生错误, 等待2秒尽可能保证其他goroutine完成收尾工作。 53 | time.Sleep(time.Second * 2) 54 | } 55 | return err 56 | } 57 | -------------------------------------------------------------------------------- /codes/mask_util/code.go: -------------------------------------------------------------------------------- 1 | package mask_util 2 | 3 | import "strings" 4 | 5 | 6 | // MaskPhone 隐去手机号中间 4 位地区码, 如 155****8888 7 | func MaskPhone(phone string) string { 8 | if n := len(phone); n >= 8 { 9 | return phone[:n-8] + "****" + phone[n-4:] 10 | } 11 | return phone 12 | } 13 | 14 | // MaskEmail 隐藏邮箱ID的中间部分 zhang@go-mall.com ---> z***g@go-mall.com 15 | func MaskEmail(address string) string { 16 | strings.LastIndex(address, "@") 17 | id := address[0:strings.LastIndex(address, "@")] 18 | domain := address[strings.LastIndex(address, "@"):] 19 | 20 | if len(id) <= 1 { 21 | return address 22 | } 23 | switch len(id) { 24 | case 2: 25 | id = id[0:1] + "*" 26 | case 3: 27 | id = id[0:1] + "*" + id[2:] 28 | case 4: 29 | id = id[0:1] + "**" + id[3:] 30 | default: 31 | masks := strings.Repeat("*", len(id)-4) 32 | id = id[0:2] + masks + id[len(id)-2:] 33 | } 34 | 35 | return id + domain 36 | } 37 | 38 | // MaskRealName 保留姓名首末位 如:张三--->张* 赵丽颖--->赵*颖 欧阳娜娜--->欧**娜 39 | func MaskRealName(realName string) string { 40 | runeRealName := []rune(realName) 41 | if n := len(runeRealName); n >= 2 { 42 | if n == 2 { 43 | return string(append(runeRealName[0:1], rune('*'))) 44 | } else { 45 | count := n - 2 46 | newRealName := runeRealName[0:1] 47 | for temp := 1; temp <= count; temp++ { 48 | newRealName = append(newRealName, rune('*')) 49 | } 50 | return string(append(newRealName, runeRealName[n-1])) 51 | } 52 | } 53 | return realName 54 | } 55 | -------------------------------------------------------------------------------- /codes/reqeust_sign_generate/request_sign.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/md5" 6 | "encoding/hex" 7 | "fmt" 8 | "sort" 9 | "strconv" 10 | ) 11 | 12 | func GenRequestSign(request map[string]interface{}) (string, string) { 13 | 14 | delete(request, "sign") 15 | 16 | var keys []string 17 | for k := range request { 18 | keys = append(keys, k) 19 | } 20 | 21 | sort.Strings(keys) 22 | 23 | var reString bytes.Buffer 24 | for _, kk := range keys { 25 | if kk == "" { 26 | continue 27 | } 28 | if reString.Len() > 0 { 29 | reString.WriteByte('&') 30 | } 31 | 32 | reString.WriteString(kk) 33 | reString.WriteByte('=') 34 | switch vv := request[kk].(type) { 35 | case string: 36 | reString.WriteString(vv) 37 | case int, int8, int16, int32, int64: 38 | reString.WriteString(fmt.Sprintf("%d", vv)) 39 | case float64: 40 | reString.WriteString(strconv.FormatInt(int64(vv), 10)) 41 | default: 42 | continue 43 | } 44 | } 45 | secret := "37y4uxXZXeWtCDRq3z14dEhUhCawb2tM" 46 | reString.WriteString(secret) 47 | 48 | return reString.String(), Md5(reString.String()) 49 | } 50 | 51 | func Md5(str string) string { 52 | md5ctx := md5.New() 53 | md5ctx.Write([]byte(str)) 54 | return hex.EncodeToString(md5ctx.Sum(nil)) 55 | } 56 | 57 | func main() { 58 | req := map[string]interface{} { 59 | "request_no":"230621175012994051431130267648serial", 60 | "company_sign":"aaa", 61 | } 62 | 63 | _, b := GenRequestSign(req) 64 | fmt.Println(b) 65 | 66 | } 67 | -------------------------------------------------------------------------------- /codes/crypto_utils/aes_easy.go: -------------------------------------------------------------------------------- 1 | package crypto_utils 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | ) 8 | 9 | // AesEncrypt AES加密 | key长度为 16 字节才能加密成功 10 | func AesEncrypt(origData, key []byte) ([]byte, error) { 11 | block, err := aes.NewCipher(key) 12 | if err != nil { 13 | return nil, err 14 | } 15 | blockSize := block.BlockSize() 16 | origData = PKCS5Padding(origData, blockSize) 17 | blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) 18 | encrypted := make([]byte, len(origData)) 19 | blockMode.CryptBlocks(encrypted, origData) 20 | return encrypted, nil 21 | } 22 | 23 | func PKCS5Padding(ciphertext []byte, blockSize int) []byte { 24 | padding := blockSize - len(ciphertext)%blockSize 25 | padText := bytes.Repeat([]byte{byte(padding)}, padding) 26 | return append(ciphertext, padText...) 27 | } 28 | 29 | func AesDecrypt(crypted, key []byte) ([]byte, error) { 30 | block, err := aes.NewCipher(key) 31 | if err != nil { 32 | return nil, err 33 | } 34 | blockSize := block.BlockSize() 35 | blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) 36 | origData := make([]byte, len(crypted)) 37 | blockMode.CryptBlocks(origData, crypted) 38 | origData = PKCS5UnPadding(origData) 39 | return origData, nil 40 | } 41 | 42 | func PKCS5UnPadding(origData []byte) []byte { 43 | length := len(origData) 44 | // 去掉最后一个字节 unPadding 次 45 | unPadding := int(origData[length-1]) 46 | if unPadding < 1 || unPadding > 32 { 47 | unPadding = 0 48 | } 49 | return origData[:(length - unPadding)] 50 | } 51 | -------------------------------------------------------------------------------- /codes/flag_library/subcommand/subcommand.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "flag" 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | func NewGreetCommand() *GreetCommand { 11 | gc := &GreetCommand{ 12 | fs: flag.NewFlagSet("greet", flag.ContinueOnError), 13 | } 14 | 15 | gc.fs.StringVar(&gc.name, "name", "World", "name of the person to be greeted") 16 | 17 | return gc 18 | } 19 | 20 | type GreetCommand struct { 21 | fs *flag.FlagSet 22 | 23 | name string 24 | } 25 | 26 | func (g *GreetCommand) Name() string { 27 | return g.fs.Name() 28 | } 29 | 30 | func (g *GreetCommand) Init(args []string) error { 31 | return g.fs.Parse(args) 32 | } 33 | 34 | func (g *GreetCommand) Run() error { 35 | fmt.Println("Hello", g.name, "!") 36 | return nil 37 | } 38 | 39 | type Runner interface { 40 | Init([]string) error 41 | Run() error 42 | Name() string 43 | } 44 | 45 | func root(args []string) error { 46 | if len(args) < 1 { 47 | return errors.New("You must pass a sub-command") 48 | } 49 | 50 | cmds := []Runner{ 51 | NewGreetCommand(), 52 | } 53 | 54 | subcommand := os.Args[1] 55 | 56 | for _, cmd := range cmds { 57 | if cmd.Name() == subcommand { 58 | cmd.Init(os.Args[2:]) 59 | return cmd.Run() 60 | } 61 | } 62 | 63 | return fmt.Errorf("Unknown subcommand: %s", subcommand) 64 | } 65 | 66 | func main() { 67 | if err := root(os.Args[1:]); err != nil { 68 | fmt.Println(err) 69 | os.Exit(1) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /codes/trace_span/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "math/rand" 7 | "net" 8 | "strconv" 9 | "strings" 10 | "time" 11 | ) 12 | 13 | func main() { 14 | spanId := GenerateSpanID(GetLocalIP().String()) 15 | traceId := GenerateTraceID(GetLocalIP().String()) 16 | fmt.Println("traceId: " + traceId, "spanId: " + spanId) 17 | 18 | } 19 | 20 | 21 | 22 | func GenerateSpanID(addr string) string { 23 | strAddr := strings.Split(addr, ":") 24 | ip := strAddr[0] 25 | ipLong, _ := Ip2Long(ip) 26 | times := uint64(time.Now().UnixNano()) 27 | spanId := ((times ^ uint64(ipLong)) << 32) | uint64(rand.Int31()) 28 | return strconv.FormatUint(spanId, 16) 29 | } 30 | 31 | func GenerateTraceID(addr string) string { 32 | strAddr := strings.Split(addr, ":") 33 | ip := strAddr[0] 34 | ipLong, _ := Ip2Long(ip) 35 | times := uint64(time.Now().UnixNano()) 36 | traceId := ((times ^ uint64(ipLong)) << 32) | uint64(rand.Int31()) 37 | return strconv.FormatUint(traceId, 16) 38 | } 39 | 40 | func Ip2Long(ip string) (uint32, error) { 41 | ipAddr, err := net.ResolveIPAddr("ip", ip) 42 | if err != nil { 43 | return 0, err 44 | } 45 | return binary.BigEndian.Uint32(ipAddr.IP.To4()), nil 46 | } 47 | 48 | func GetLocalIP() net.IP { 49 | addrs, err := net.InterfaceAddrs() 50 | if err != nil { 51 | return net.IPv4zero 52 | } 53 | for _, a := range addrs { 54 | if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { 55 | if ip := ipnet.IP.To4(); ip != nil { 56 | return ipnet.IP 57 | } 58 | } 59 | } 60 | return net.IPv4zero 61 | } 62 | -------------------------------------------------------------------------------- /cmd/es/query_with_must_not.shell: -------------------------------------------------------------------------------- 1 | curl -XGET --header 'Content-Type: application/json' {$es_server_host[:$port]}/{$index_name}/_search -d '{ 2 | "from":0, 3 | "query":{ 4 | "bool":{ 5 | "must":[ 6 | { 7 | "terms":{ 8 | "businessId":[ 9 | 1, 10 | 2, 11 | 3 12 | ] 13 | } 14 | }, 15 | { 16 | "terms":{ 17 | "departmentId":[ 18 | 0, 19 | 1, 20 | 2, 21 | 3, 22 | 4, 23 | 5, 24 | 6, 25 | 7, 26 | 8, 27 | 9, 28 | 10 29 | ] 30 | } 31 | }, 32 | { 33 | "term":{ 34 | "performanceType":1 35 | } 36 | }, 37 | { 38 | "bool":{ 39 | "must_not":{ 40 | "match":{ 41 | "contactId":"" 42 | } 43 | } 44 | } 45 | } 46 | ] 47 | } 48 | }, 49 | "size":10 50 | }' 51 | -------------------------------------------------------------------------------- /codes/http_client_with_rate/http_rl_client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "time" 8 | 9 | "golang.org/x/time/rate" 10 | ) 11 | 12 | //RLHTTPClient Rate Limited HTTP Client 13 | type RLHTTPClient struct { 14 | client *http.Client 15 | Ratelimiter *rate.Limiter 16 | } 17 | 18 | //Do dispatches the HTTP request to the network 19 | func (c *RLHTTPClient) Do(req *http.Request) (*http.Response, error) { 20 | // Comment out the below 5 lines to turn off ratelimiting 21 | ctx := context.Background() 22 | err := c.Ratelimiter.Wait(ctx) // This is a blocking call. Honors the rate limit 23 | if err != nil { 24 | return nil, err 25 | } 26 | resp, err := c.client.Do(req) 27 | if err != nil { 28 | return nil, err 29 | } 30 | return resp, nil 31 | } 32 | 33 | //NewClient return http client with a ratelimiter 34 | func NewClient(rl *rate.Limiter) *RLHTTPClient { 35 | c := &RLHTTPClient{ 36 | client: http.DefaultClient, 37 | Ratelimiter: rl, 38 | } 39 | return c 40 | } 41 | 42 | func main() { 43 | rl := rate.NewLimiter(rate.Every(10*time.Second), 50) // 50 request every 10 seconds 44 | c := NewClient(rl) 45 | reqURL := "https://api.btcmarkets.net/v3/markets/BTC-AUD/ticker" 46 | req, _ := http.NewRequest("GET", reqURL, nil) 47 | for i := 0; i < 300; i++ { 48 | resp, err := c.Do(req) 49 | if err != nil { 50 | fmt.Println(err.Error()) 51 | fmt.Println(resp.StatusCode) 52 | return 53 | } 54 | if resp.StatusCode == 429 { 55 | fmt.Printf("Rate limit reached after %d requests", i) 56 | return 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /codes/unsafe/use_sizeof.go: -------------------------------------------------------------------------------- 1 | 2 | 3 | // ClientConn in other package 4 | //type ClientConn struct { 5 | // ctx context.Context 6 | // cancel context.CancelFunc 7 | // 8 | // target string 9 | // parsedTarget resolver.Target 10 | // authority string 11 | // dopts dialOptions 12 | // csMgr *connectivityStateManager 13 | // 14 | // balancerBuildOpts balancer.BuildOptions 15 | // blockingpicker *pickerWrapper 16 | // 17 | // mu sync.RWMutex 18 | // resolverWrapper *ccResolverWrapper 19 | // sc *ServiceConfig 20 | // conns map[*addrConn]struct{} 21 | // // Keepalive parameter can be updated if a GoAway is received. 22 | // mkp keepalive.ClientParameters 23 | // curBalancerName string 24 | // balancerWrapper *ccBalancerWrapper 25 | // retryThrottler atomic.Value 26 | // 27 | // firstResolveEvent *grpcsync.Event 28 | // 29 | // channelzID int64 // channelz unique identification number 30 | // czData *channelzData 31 | //} 32 | 33 | 34 | var ( 35 | Target *string 36 | ) 37 | 38 | func (cm *ClientConnManager) Call(method string, in interface{}, out interface{}) error { 39 | ctx, cancel := context.WithTimeout(context.Background(), 6*time.Second) 40 | defer cancel() 41 | cc, err := cm.getClientConn() 42 | if err != nil { 43 | return err 44 | } 45 | // use unsafe.Pointer and uintptr get unexported field parsedTarget 46 | Target = (* string)(unsafe.Pointer(uintptr(unsafe.Pointer(cc)) + unsafe.Sizeof(context.TODO()) + 47 | unsafe.Sizeof(* new(context.CancelFunc)) + unsafe.Sizeof("1"))) 48 | 49 | return cc.Invoke(ctx, method, in, out) 50 | } 51 | -------------------------------------------------------------------------------- /codes/byte_order/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "unsafe" 7 | ) 8 | 9 | const INT_SIZE = int(unsafe.Sizeof(0)) //64位操作系统,8 bytes 10 | 11 | //判断我们系统中的字节序类型 12 | func systemEdian() { 13 | 14 | var i = 0x01020304 15 | fmt.Println("&i:",&i) 16 | bs := (*[INT_SIZE]byte)(unsafe.Pointer(&i)) 17 | 18 | if bs[0] == 0x04 { 19 | fmt.Println("system edian is little endian") 20 | } else { 21 | fmt.Println("system edian is big endian") 22 | } 23 | fmt.Printf("temp: 0x%x,%v\n",bs[0],&bs[0]) 24 | fmt.Printf("temp: 0x%x,%v\n",bs[1],&bs[1]) 25 | fmt.Printf("temp: 0x%x,%v\n",bs[2],&bs[2]) 26 | fmt.Printf("temp: 0x%x,%v\n",bs[3],&bs[3]) 27 | 28 | } 29 | 30 | 31 | func testBigEndian() { 32 | 33 | var testInt int32 = 0x01020304 34 | fmt.Printf("%d use big endian: \n", testInt) 35 | testBytes := make([]byte, 4) 36 | binary.BigEndian.PutUint32(testBytes, uint32(testInt)) 37 | fmt.Println("int32 to bytes:", testBytes) 38 | fmt.Printf("int32 to bytes: %x \n", testBytes) 39 | 40 | convInt := binary.BigEndian.Uint32(testBytes) 41 | fmt.Printf("bytes to int32: %d\n\n", convInt) 42 | } 43 | 44 | func testLittleEndian() { 45 | 46 | var testInt int32 = 0x01020304 47 | fmt.Printf("%x use little endian: \n", testInt) 48 | var testBytes []byte = make([]byte, 4) 49 | binary.LittleEndian.PutUint32(testBytes, uint32(testInt)) 50 | fmt.Printf("int32 to bytes: %x \n", testBytes) 51 | 52 | convInt := binary.LittleEndian.Uint32(testBytes) 53 | fmt.Printf("bytes to int32: %d\n\n", convInt) 54 | } 55 | 56 | func main() { 57 | systemEdian() 58 | fmt.Println("") 59 | testBigEndian() 60 | testLittleEndian() 61 | } 62 | -------------------------------------------------------------------------------- /codes/learntime/code.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func main() { 9 | timeStr := "2023-10-11 23:34:45" 10 | comparedTime, _ := parseInLocalLocation(timeStr, TimeLayoutDefault) 11 | fmt.Printf("%s晚于当前时间: %v\n", timeStr, isAfterNow(comparedTime)) 12 | originTime := comparedTime 13 | aWeekLaterTime := weekLater(originTime, 1) 14 | fmt.Printf("初始时间: %v, 一周后的时间: %v\n", originTime, aWeekLaterTime) 15 | } 16 | 17 | const TimeLayoutDefault = "2006-01-02 15:04:05" 18 | const TimeLayoutDate = "20060102" 19 | const TimeLayoutDateWithHyphen = "2006-01-02" 20 | 21 | 22 | // parseFromString 解析字符串时间 23 | func parseFromString(timeStr, layout string) (timeObj time.Time, err error) { 24 | timeObj, err = time.Parse(layout, timeStr) 25 | return 26 | } 27 | 28 | // parseInLocalLocation 用本地时区解析字符串时间 29 | func parseInLocalLocation(timeStr, layout string) (timeObj time.Time, err error) { 30 | timeObj, err = time.ParseInLocation(layout, timeStr, time.Local) 31 | return 32 | } 33 | 34 | // parseInSpainLocation 用西班牙时区解析字符串时间 35 | func parseInSpainLocation(timeStr, layout string) (timeObj time.Time, err error) { 36 | loc, _ := time.LoadLocation("Europe/Madrid") 37 | timeObj, err = time.ParseInLocation(layout, timeStr, loc) 38 | return 39 | } 40 | 41 | func isBeforeNow(comparedTime time.Time) bool { 42 | return comparedTime.Before(time.Now()) 43 | } 44 | 45 | func isAfterNow(comparedTime time.Time) bool { 46 | return comparedTime.After(time.Now()) 47 | } 48 | 49 | func weekLater(originTime time.Time, w int) time.Time { 50 | return originTime.AddDate(0, 0, w * 7) 51 | } 52 | 53 | func daysEarlier(originTime time.Time, d int) time.Time { 54 | return originTime.AddDate(0, 0, -d) 55 | } 56 | -------------------------------------------------------------------------------- /codes/proconsumer/tests/testjob.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "example.com/proconsumer" 6 | "fmt" 7 | "os" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | ts := proconsumer.NewTaskScheduler(ProducerFunc, ConsumerFunc, 10) 14 | err := ts.Execute() 15 | fmt.Println(err, "11111") 16 | } 17 | 18 | func ProducerFunc(producer *proconsumer.Producer) error { 19 | dir, _ := os.Getwd() 20 | fmt.Println(dir) 21 | // 自己试一下故意写错文件路径 22 | file, err := os.Open(dir + "/tests/file.txt") 23 | if err != nil { 24 | return err 25 | } 26 | defer file.Close() 27 | 28 | scanner := bufio.NewScanner(file) 29 | i := 0 30 | for scanner.Scan() { 31 | 32 | select { 33 | case <-producer.CancelCtx.Done(): 34 | fmt.Println("producer end") 35 | return nil 36 | default: 37 | } 38 | // TODO 制造消息 39 | str := scanner.Text() 40 | i += 1 41 | if i % 100 == 0 { 42 | // 模拟耗时查询 43 | time.Sleep(time.Second * 3) 44 | } 45 | // TODO 制造错误情况, 测试下退出 46 | //if strings.HasPrefix(str, "rt") { 47 | // err = fmt.Errorf("producer Error") 48 | // return err 49 | //} 50 | // 发送数据给consumer 51 | producer.DataCh <- str 52 | } 53 | 54 | close(producer.DataCh) //查询完成关闭通道 55 | 56 | return nil 57 | } 58 | 59 | func ConsumerFunc(consumer *proconsumer.Consumer) error { 60 | for { 61 | 62 | select { 63 | case <-consumer.CancelCtx.Done(): 64 | fmt.Println("consumer cancel end") 65 | return nil 66 | case v, ok := <-consumer.DataCh: 67 | if !ok { 68 | fmt.Println("consumer execution end") 69 | return nil 70 | } 71 | err := Do(v) 72 | if err != nil { 73 | return err 74 | } 75 | } 76 | 77 | } 78 | 79 | return nil 80 | } 81 | 82 | func Do(v interface{}) error { 83 | val, _ := v.(string) 84 | // TODO 制造错误情况, 测试下退出 85 | if strings.HasPrefix(val, "rt") { 86 | return fmt.Errorf("worker error") 87 | } 88 | fmt.Println(strings.ToUpper(val)) 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /codes/distributed_lock/etcd/etcd_lock.go: -------------------------------------------------------------------------------- 1 | package etcd 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "github.com/coreos/etcd/clientv3" 10 | "go.etcd.io/etcd/clientv3/concurrency" 11 | ) 12 | 13 | func NewClient(endpoints []string) (*clientv3.Client, error){ 14 | ctx, cancel := context.WithCancel(context.Background()) 15 | cli, err := clientv3.New(clientv3.Config{ 16 | Endpoints: endpoints, 17 | DialTimeout: 200 * time.Millisecond, 18 | Context: ctx, 19 | }) 20 | if err != nil { 21 | cancel() 22 | return nil, err 23 | } 24 | 25 | return cli, err 26 | } 27 | 28 | // 测试锁 29 | func UseLock(cli *clientv3.Client, name string) { 30 | // 创建一个用于获取锁的Session 31 | s, err:= concurrency.NewSession(cli) 32 | if err != nil { 33 | log.Fatal(err) 34 | } 35 | defer s.Close() 36 | // 在指定Key上创建一个互斥锁 37 | l := concurrency.NewMutex(s, "/distributed-lock/") 38 | ctx := context.TODO() // 也可以通过 ctx 指定获取锁等待的超时时间,实现LockWithTimeout 39 | // 获得锁 / 阻塞等待知道获得锁 40 | if err := l.Lock(ctx); err != nil { 41 | log.Fatal(err) 42 | } 43 | 44 | fmt.Println("Acquired lock for ", name) 45 | fmt.Println("Do some work in", name) 46 | time.Sleep(5 * time.Second) 47 | if err := l.Unlock(ctx); err != nil { 48 | log.Fatal(err) 49 | } 50 | fmt.Println("Released lock for ", name) 51 | } 52 | 53 | // 使用TryLock, 获取不到则退出 54 | func UseTryLock(cli *clientv3.Client, name string) { 55 | // 创建一个用于获取锁的Session 56 | s, err:= concurrency.NewSession(cli) 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | defer s.Close() 61 | // 在指定Key上创建一个互斥锁 62 | l := concurrency.NewMutex(s, "/distributed-lock/") 63 | ctx := context.TODO() // 也可以通过 ctx 指定获取锁等待的超时时间,实现LockWithTimeout 64 | // 获得锁 / 阻塞等待知道获得锁 65 | if err := l.Lock(ctx); err != nil { 66 | log.Fatal(err) 67 | } 68 | 69 | fmt.Println("Acquired lock for ", name) 70 | fmt.Println("Do some work in", name) 71 | time.Sleep(5 * time.Second) 72 | if err := l.Unlock(ctx); err != nil { 73 | log.Fatal(err) 74 | } 75 | fmt.Println("Released lock for ", name) 76 | } 77 | -------------------------------------------------------------------------------- /codes/tag_picker/query_builer.go: -------------------------------------------------------------------------------- 1 | package tag_picker 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | const ( 10 | TAG_ORDER_NUM = "order_num" 11 | ) 12 | 13 | // 对外提供的批量查询用户标签值的方法 14 | func BulkQueryUserTagValue(tagNames []string, userId int64, queryArgs ...interface{}) (tagValuePairs []*TagValuePair) { 15 | tagCount := len(tagNames) 16 | if tagCount < 1 { 17 | return 18 | } 19 | wg := &sync.WaitGroup{} 20 | wg.Add(tagCount) 21 | tagValueCh := make(chan *TagValuePair, tagCount) // 用于接收所有Picker查到的标签值的Channel 22 | ctx, _ := context.WithTimeout(context.Background(), time.Minute) // 设置执行标签值查找的超时时间 23 | for _, tagName := range tagNames { 24 | go queryTagValue(ctx, wg, tagName, userId, tagValueCh, queryArgs...) 25 | } 26 | wg.Wait() 27 | close(tagValueCh) // 先关闭通道 方便下面for range不发生阻塞, 从channel中读完值即退出 28 | tagValuePairs = make([]*TagValuePair, 0) 29 | for tagValue := range tagValueCh { 30 | if tagValue.Value != nil { 31 | tagValuePairs = append(tagValuePairs, tagValue) 32 | } 33 | } 34 | 35 | return tagValuePairs 36 | } 37 | 38 | type TagValuePair struct { 39 | Name string `json:"tag_name"` 40 | Value interface{} `json:"tag_value"` 41 | } 42 | 43 | 44 | func queryTagValue(ctx context.Context, wg *sync.WaitGroup, tagName string, userId int64, tagValueCh chan *TagValuePair, queryArgs ...interface{}) { 45 | defer wg.Done() 46 | tagPicker := resolveTagPicker(tagName) 47 | if tagPicker == nil { 48 | log.Error("未识别的业务标签", common.ErrUnknownBusinessTag) 49 | return 50 | } 51 | go tagPicker.PickTagValueForUser(userId, queryArgs...) 52 | select { 53 | case <- ctx.Done(): // 超时返回 54 | return 55 | case tagValue := <- tagPicker.Notify(): // 接收标签值 56 | TagValuePair := &TagValuePair{ 57 | Name: tagName, 58 | Value: tagValue, 59 | } 60 | tagValueCh <- TagValuePair 61 | 62 | return 63 | } 64 | } 65 | 66 | // 根据标签名解析出对应的TagPicker 67 | func resolveTagPicker (tagName string) Picker { 68 | switch tagName { 69 | case TAG_ORDER_NUM: 70 | return &OrderNumTagPicker{ 71 | TagName: tagName, 72 | ValueCh: make(chan interface{}), 73 | } 74 | default: 75 | return nil 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /codes/cyclic_barrier/h2o_factory.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/marusama/cyclicbarrier" 7 | "golang.org/x/sync/semaphore" 8 | "math/rand" 9 | "sort" 10 | "sync" 11 | "time" 12 | ) 13 | 14 | // 定义水分子合成的辅助数据结构 15 | type H2O struct { 16 | semaH *semaphore.Weighted // 氢原子的信号量 17 | semaO *semaphore.Weighted // 氧原子的信号量 18 | b cyclicbarrier.CyclicBarrier // 循环栅栏,用来控制合成 19 | } 20 | 21 | 22 | func New() *H2O { 23 | return &H2O{ 24 | semaH: semaphore.NewWeighted(2), //氢原子需要两个 25 | semaO: semaphore.NewWeighted(1), // 氧原子需要一个 26 | b: cyclicbarrier.New(3), // 需要三个原子才能合成 27 | } 28 | } 29 | 30 | // 用来存放水分子结果的channel 31 | var ch chan string 32 | 33 | // 往channel里存放一个H原子 34 | func releaseHydrogen() { 35 | ch <- "H" 36 | } 37 | 38 | // 往channel里存放一个O原子 39 | func releaseOxygen() { 40 | ch <- "O" 41 | } 42 | 43 | func (h2o *H2O) hydrogen(releaseHydrogen func()) { 44 | h2o.semaH.Acquire(context.Background(), 1) 45 | 46 | releaseHydrogen() // 输出H 47 | h2o.b.Await(context.Background()) //等待栅栏放行 48 | h2o.semaH.Release(1) // 释放氢原子空槽 49 | } 50 | 51 | 52 | func (h2o *H2O) oxygen(releaseOxygen func()) { 53 | h2o.semaO.Acquire(context.Background(), 1) 54 | 55 | releaseOxygen() // 输出O 56 | h2o.b.Await(context.Background()) //等待栅栏放行 57 | h2o.semaO.Release(1) // 释放氢原子空槽 58 | } 59 | 60 | 61 | func main() { 62 | // 300个原子,300个goroutine,每个goroutine并发的产生一个原子 63 | var N = 100 64 | ch = make(chan string, N*3) 65 | 66 | h2o := New() 67 | 68 | // 用来等待所有的goroutine完成 69 | var wg sync.WaitGroup 70 | wg.Add(N * 3) 71 | 72 | // 200个生产氢原子的goroutine 73 | for i := 0; i < 2*N; i++ { 74 | go func() { 75 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 76 | h2o.hydrogen(releaseHydrogen) 77 | wg.Done() 78 | }() 79 | } 80 | // 100个生产氧原子的goroutine 81 | for i := 0; i < N; i++ { 82 | go func() { 83 | time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond) 84 | h2o.oxygen(releaseOxygen) 85 | wg.Done() 86 | }() 87 | } 88 | 89 | //等待所有的goroutine执行完 90 | wg.Wait() 91 | 92 | // 结果中肯定是300个原子 93 | if len(ch) != N*3 { 94 | fmt.Println(fmt.Errorf("expect %d atom but got %d", N*3, len(ch))) 95 | } 96 | 97 | // 每三个原子一组,分别进行检查。要求这一组原子中必须包含两个氢原子和一个氧原子,这样才能正确组成一个水分子。 98 | var s = make([]string, 3) 99 | for i := 0; i < N; i++ { 100 | s[0] = <-ch 101 | s[1] = <-ch 102 | s[2] = <-ch 103 | sort.Strings(s) 104 | 105 | 106 | water := s[0] + s[1] + s[2] 107 | if water != "HHO" { 108 | fmt.Println(fmt.Errorf("expect a water molecule but got %s", water)) 109 | } 110 | 111 | fmt.Println(water) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /codes/aes-go-java-convertor/aes-go/ase.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "encoding/base64" 8 | "errors" 9 | "fmt" 10 | ) 11 | 12 | func Encrypt(key string, val string) (string, error) { 13 | origData := []byte(val) 14 | crypted, err := encrypt(key, origData) 15 | if err != nil { 16 | return "", err 17 | } 18 | return base64.RawURLEncoding.EncodeToString(crypted), nil 19 | } 20 | 21 | func Decrypt(key string, val string) (string, error) { 22 | // base64 decode 23 | decodeBytes, _ := base64.RawURLEncoding.DecodeString(val) 24 | origData, err := decrpt(key, decodeBytes) 25 | if err != nil { 26 | return "", err 27 | } 28 | return string(origData), nil 29 | } 30 | 31 | 32 | func encrypt(key string, origData []byte) ([]byte, error) { 33 | if len(origData) <= 0 { 34 | return nil, errors.New("crypted len is zero") 35 | } 36 | keyBytes := GetKeyBytes(key) 37 | block, err := aes.NewCipher(keyBytes) 38 | if err != nil { 39 | return nil, err 40 | } 41 | blockSize := block.BlockSize() 42 | origData = PKCS5Padding(origData, blockSize) 43 | blockMode := cipher.NewCBCEncrypter(block, keyBytes[:blockSize]) 44 | crypted := make([]byte, len(origData)) 45 | blockMode.CryptBlocks(crypted, origData) 46 | return crypted, nil 47 | } 48 | 49 | func decrpt(key string, crypted []byte) ([]byte, error) { 50 | if len(crypted) <= 0 { 51 | return nil, errors.New("crypted len is zero") 52 | } 53 | keyBytes := GetKeyBytes(key) 54 | block, err := aes.NewCipher(keyBytes) 55 | if err != nil { 56 | return nil, err 57 | } 58 | blockSize := block.BlockSize() 59 | blockMode := cipher.NewCBCDecrypter(block, keyBytes[:blockSize]) 60 | origData := make([]byte, len(crypted)) 61 | blockMode.CryptBlocks(origData, crypted) 62 | origData = PKCS5UnPadding(origData) 63 | return origData, nil 64 | } 65 | 66 | func GetKeyBytes(key string) []byte { 67 | keyBytes := []byte(key) 68 | switch l := len(keyBytes); { 69 | case l < 16: 70 | keyBytes = append(keyBytes, make([]byte, 16-l)...) 71 | case l > 16: 72 | keyBytes = keyBytes[:16] 73 | } 74 | return keyBytes 75 | } 76 | 77 | func PKCS5Padding(ciphertext []byte, blockSize int) []byte { 78 | padding := blockSize - len(ciphertext)%blockSize 79 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 80 | return append(ciphertext, padtext...) 81 | } 82 | 83 | func PKCS5UnPadding(origData []byte) []byte { 84 | length := len(origData) 85 | unpadding := int(origData[length-1]) 86 | return origData[:(length - unpadding)] 87 | } 88 | 89 | func main() { 90 | encryptKey := "3wjyxqDPNyrd4QrhxTycRMU4dFN2lCm4" 91 | sign, _ := Encrypt(encryptKey, "37b63ec62ebf8b2e790b8d9829da2ec26f1fad67") 92 | fmt.Println(sign) 93 | 94 | pureString, _ := Decrypt(encryptKey, sign) 95 | fmt.Println(pureString) 96 | } 97 | -------------------------------------------------------------------------------- /codes/aes-go-java-convertor/aes-java/EncryptDecryptDemo.java: -------------------------------------------------------------------------------- 1 | package com.learnciper; 2 | 3 | import javax.crypto.Cipher; 4 | import javax.crypto.spec.IvParameterSpec; 5 | import javax.crypto.spec.SecretKeySpec; 6 | import java.security.GeneralSecurityException; 7 | import java.util.Arrays; 8 | import java.util.Base64; 9 | 10 | public class EncryptDecryptDemo { 11 | 12 | public byte[] decrypt(String key, byte[] crypted) throws GeneralSecurityException { 13 | byte[] keyBytes = getKeyBytes(key); 14 | byte[] buf = new byte[16]; 15 | System.arraycopy(keyBytes, 0, buf, 0, Math.max(keyBytes.length, buf.length)); 16 | Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 17 | cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(buf, "AES"), new IvParameterSpec(keyBytes)); 18 | return cipher.doFinal(crypted); 19 | } 20 | 21 | private byte[] getKeyBytes(String key) { 22 | byte[] bytes = key.getBytes(); 23 | return bytes.length == 16 ? bytes : Arrays.copyOf(bytes, 16); 24 | } 25 | 26 | //加密 27 | public String encrypt(String key, String val) { 28 | try { 29 | byte[] origData = val.getBytes(); 30 | byte[] crafted = encrypt(key, origData); 31 | return Base64.getUrlEncoder().withoutPadding().encodeToString(crafted); 32 | } catch (Exception e) { 33 | return ""; 34 | } 35 | } 36 | 37 | public String decrypt(String key, String val) throws GeneralSecurityException { 38 | byte[] crypted = Base64.getUrlDecoder().decode(val); 39 | byte[] origData = decrypt(key, crypted); 40 | return new String(origData); 41 | } 42 | 43 | public byte[] encrypt(String key, byte[] origData) throws GeneralSecurityException { 44 | byte[] keyBytes = getKeyBytes(key); 45 | byte[] buf = new byte[16]; 46 | System.arraycopy(keyBytes, 0, buf, 0, Math.max(keyBytes.length, buf.length)); 47 | Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); 48 | cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(buf, "AES"), new IvParameterSpec(keyBytes)); 49 | return cipher.doFinal(origData); 50 | 51 | } 52 | 53 | public static void main(String[] args) throws GeneralSecurityException { 54 | String key = "3wjyxqDPNyrd4QrhxTycRMU4dFN2lCm4"; 55 | DecryptDemo decryptClz = new DecryptDemo(); 56 | String encryptedStr = decryptClz.encrypt(key, "37b63ec62ebf8b2e790b8d9829da2ec26f1fad67"); 57 | // 加密前的纯串 37b63ec62ebf8b2e790b8d9829da2ec26f1fad67 58 | String pureStr = decryptClz.decrypt(key, "2D_24XH_IMxtYLPGdgZ37X_5_a4eFTpqT7v1aNwq-74Ko6XDpE54LRz4e5s4ehMc"); 59 | 60 | String es = String.format("加密后的串: %s", encryptedStr); 61 | String ds = String.format("解密后的串: %s", pureStr); 62 | System.out.println(es); 63 | System.out.println(ds); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /codes/httptool/http_util.go: -------------------------------------------------------------------------------- 1 | package httptool 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "net/http" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | func httpRequest(method string, url string, options ...Option) (code int, content string, err error) { 12 | start := time.Now() 13 | 14 | reqOpts := defaultRequestOptions() // 默认的请求选项 15 | for _, opt := range options { // 在reqOpts上应用通过options设置的选项 16 | err = opt.apply(reqOpts); if err != nil { 17 | return 18 | } 19 | } 20 | // 创建请求对象 21 | req, err := http.NewRequest(method, url, strings.NewReader(reqOpts.data)) 22 | // 记录请求日志 23 | defer func() { 24 | dur := int64(time.Since(start) / time.Second) 25 | if dur >= 500 { 26 | log.Println("Http"+method, url, "in", reqOpts.data, "out", content, "err", err, "dur/ms", dur) 27 | } else { 28 | log.Println("Http"+method, url, "in", reqOpts.data, "out", content, "err", err, "dur/ms", dur) 29 | } 30 | }() 31 | defer req.Body.Close() 32 | 33 | if len(reqOpts.headers) != 0 { // 设置请求头 34 | for key, value := range reqOpts.headers { 35 | req.Header.Add(key, value) 36 | } 37 | } 38 | if err != nil { 39 | return 40 | } 41 | // 发起请求 42 | client := &http.Client{Timeout: reqOpts.timeout} 43 | resp, error := client.Do(req) 44 | if error != nil { 45 | return 0, "", error 46 | } 47 | // 解析响应 48 | defer resp.Body.Close() 49 | code = resp.StatusCode 50 | result, _ := ioutil.ReadAll(resp.Body) 51 | content = string(result) 52 | 53 | return 54 | } 55 | 56 | // 发起GET请求 57 | func HttpGet(url string, options ...Option) (code int, content string, err error) { 58 | return httpRequest("GET", url, options...) 59 | } 60 | 61 | // 发起POST请求,请求头指定Content-Type: application/json 62 | func HttpJsonPost(url string, data string, timeout time.Duration) (code int, content string, err error) { 63 | headers := map[string]string{ 64 | "Content-Type": "application/json", 65 | } 66 | code, content, err = httpRequest( 67 | "POST", url, WithTimeout(timeout), WithHeaders(headers), WithData(data)) 68 | 69 | return 70 | } 71 | 72 | // 针对可选的HTTP请求配置项,模仿gRPC使用的Options设计模式实现 73 | type requestOption struct { 74 | timeout time.Duration 75 | data string 76 | headers map[string]string 77 | } 78 | 79 | type Option interface { 80 | apply(option *requestOption) error 81 | } 82 | 83 | type optionFunc func(option *requestOption) error 84 | 85 | func (f optionFunc) apply(opts *requestOption) error { 86 | return f(opts) 87 | } 88 | 89 | 90 | func defaultRequestOptions() *requestOption { 91 | return &requestOption{ // 默认请求选项 92 | timeout: 5 * time.Second, 93 | data: "", 94 | headers: nil, 95 | } 96 | } 97 | 98 | func WithTimeout(timeout time.Duration) Option { 99 | return optionFunc(func(opts *requestOption) (err error) { 100 | opts.timeout, err = timeout, nil 101 | return 102 | }) 103 | } 104 | 105 | func WithHeaders(headers map[string]string) Option { 106 | return optionFunc(func(opts *requestOption) (err error) { 107 | for k, v := range headers { 108 | opts.headers[k] = v 109 | } 110 | return 111 | }) 112 | } 113 | 114 | func WithData(data string) Option { 115 | return optionFunc(func(opts *requestOption) (err error) { 116 | opts.data, err = data, nil 117 | return 118 | }) 119 | } 120 | -------------------------------------------------------------------------------- /codes/gen_token/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/md5" 8 | "encoding/binary" 9 | "encoding/hex" 10 | "fmt" 11 | "time" 12 | ) 13 | 14 | const ( 15 | aesKEY = "justTest!@#$test" // must be 16 characters 16 | md5Len = 4 //MD5 part size in access token 17 | aesLen = 16 //bytes after being encrypted by aes 18 | ) 19 | 20 | func main() { 21 | token := genAccessToken(1234567) 22 | println(token) 23 | 24 | println(ParseAccessToken(token)) 25 | } 26 | 27 | // generate access token 28 | // + means concatenate 29 | // Token = Hex(MD5(uid + time)[0:4] + AES(uid + time)) 30 | // a string with length of 40 31 | func genAccessToken(uid int64) string { 32 | byteInfo := make([]byte, 12) 33 | binary.BigEndian.PutUint64(byteInfo, uint64(uid)) 34 | binary.BigEndian.PutUint32(byteInfo[8:], uint32(time.Now().Unix())) 35 | encodeByte, err := AesEncrypt(byteInfo, []byte(aesKEY)) //returned slice's size is 16 36 | if err != nil { 37 | fmt.Println("desc: ", "genAccessToken-AesEncrypt", "error: ", err, "byteInfo: ", byteInfo) 38 | } 39 | md5Byte := md5.Sum(byteInfo) 40 | data := append(md5Byte[0:md5Len], encodeByte...) 41 | 42 | return hex.EncodeToString(data) 43 | } 44 | 45 | // parse uid from access token 46 | func ParseAccessToken(accessToken string) (uid uint64) { 47 | if len(accessToken) != 2*(md5Len+aesLen) { 48 | fmt.Println("log_desc", "len(accessToken)", "length", len(accessToken)) 49 | return 50 | } 51 | encodeStr := accessToken[md5Len*2:] 52 | data, e := hex.DecodeString(encodeStr) 53 | if e != nil { 54 | fmt.Println("log_desc", "ParseAccessToken-DecodeString)", "error", e, "accessToken", encodeStr) 55 | return 56 | } 57 | decodeByte, e := AesDecrypt(data, []byte(aesKEY)) //忽略错误 58 | if e != nil { 59 | fmt.Println("log_desc", "ParseAccessToken-AesDecrypt)", "error", e, "data", data) 60 | } 61 | uid = binary.BigEndian.Uint64(decodeByte) 62 | 63 | return uid 64 | } 65 | 66 | //key is 16 bytes 67 | func AesEncrypt(origData, key []byte) ([]byte, error) { 68 | block, err := aes.NewCipher(key) 69 | if err != nil { 70 | return nil, err 71 | } 72 | blockSize := block.BlockSize() 73 | origData = PKCS5Padding(origData, blockSize) 74 | blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) 75 | crypted := make([]byte, len(origData)) 76 | 77 | blockMode.CryptBlocks(crypted, origData) 78 | return crypted, nil 79 | } 80 | 81 | func PKCS5Padding(ciphertext []byte, blockSize int) []byte { 82 | padding := blockSize - len(ciphertext)%blockSize 83 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 84 | return append(ciphertext, padtext...) 85 | } 86 | 87 | func AesDecrypt(crypted, key []byte) ([]byte, error) { 88 | block, err := aes.NewCipher(key) 89 | if err != nil { 90 | return nil, err 91 | } 92 | blockSize := block.BlockSize() 93 | blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) 94 | origData := make([]byte, len(crypted)) 95 | blockMode.CryptBlocks(origData, crypted) 96 | origData = PKCS5UnPadding(origData) 97 | 98 | return origData, nil 99 | } 100 | 101 | func PKCS5UnPadding(origData []byte) []byte { 102 | length := len(origData) 103 | 104 | unPadding := int(origData[length-1]) 105 | if unPadding < 1 || unPadding > 32 { 106 | unPadding = 0 107 | } 108 | return origData[:(length - unPadding)] 109 | } 110 | -------------------------------------------------------------------------------- /codes/prevent_over_concurrency/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "golang.org/x/sync/semaphore" 7 | "golang.org/x/time/rate" 8 | "math/rand" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | func main() { 14 | // 错误的并发控制 15 | badConcurrency() 16 | // 使用WaitGroup防止并发超限 17 | //useWaitGroup() 18 | // 使用Semaphore防止并发超限 19 | //useSemaphore() 20 | // 使用golang标准库的限流器 21 | //useRateLimit() 22 | // 使用通道实现的生产者和消费者队列 23 | useChannel() 24 | } 25 | 26 | func useWaitGroup() { 27 | 28 | batchSize := 50 29 | for { 30 | data, _ := queryDataWithSizeN(batchSize) 31 | if len(data) == 0 { 32 | fmt.Println("End of all data") 33 | break 34 | } 35 | var wg sync.WaitGroup 36 | for _, item := range data { 37 | wg.Add(1) 38 | go func(i int) { 39 | doSomething(i) 40 | wg.Done() 41 | }(item) 42 | } 43 | wg.Wait() 44 | 45 | fmt.Println("Next bunch of data") 46 | } 47 | } 48 | 49 | func useSemaphore() { 50 | var concurrentNum int64 = 10 51 | var weight int64 = 1 52 | var batchSize int = 50 53 | s := semaphore.NewWeighted(concurrentNum) 54 | for { 55 | data, _ := queryDataWithSizeN(batchSize) 56 | if len(data) == 0 { 57 | fmt.Println("End of all data") 58 | break 59 | } 60 | 61 | for _, item := range data { 62 | s.Acquire(context.Background(), weight) 63 | go func(i int) { 64 | doSomething(i) 65 | s.Release(weight) 66 | }(item) 67 | } 68 | 69 | } 70 | } 71 | 72 | func useRateLimit() { 73 | limiter := rate.NewLimiter(rate.Every(1*time.Second), 50) 74 | batchSize := 50 75 | for { 76 | data, _ :=queryDataWithSizeN(batchSize) 77 | if len(data) == 0 { 78 | fmt.Println("End of all data") 79 | break 80 | } 81 | 82 | for _, item := range data { 83 | // blocking until the bucket have sufficient token 84 | err := limiter.Wait(context.Background()) 85 | if err != nil { 86 | fmt.Println("Error: ", err) 87 | return 88 | } 89 | go func(i int) { 90 | doSomething(i) 91 | }(item) 92 | } 93 | } 94 | } 95 | 96 | func useChannel() { 97 | batchSize := 50 98 | dataChan := make(chan int) 99 | var wg sync.WaitGroup 100 | wg.Add(batchSize + 1) 101 | // 生产者 102 | go func() { 103 | for { 104 | data, _ := queryDataWithSizeN(batchSize) 105 | if len(data) == 0 { 106 | break 107 | } 108 | for _, item := range data { 109 | dataChan <- item 110 | } 111 | } 112 | close(dataChan) 113 | wg.Done() 114 | }() 115 | // 消费者 116 | go func() { 117 | for i := 0; i < 50; i++ { 118 | go func() { 119 | for { 120 | select { 121 | case v, ok := <- dataChan: 122 | if !ok { 123 | wg.Done() 124 | return 125 | } 126 | doSomething(v) 127 | } 128 | } 129 | }() 130 | } 131 | }() 132 | 133 | wg.Wait() 134 | } 135 | 136 | func badConcurrency() { 137 | batchSize := 50 138 | for { 139 | data, _ := queryDataWithSizeN(batchSize) 140 | if len(data) == 0 { 141 | break 142 | } 143 | 144 | for _, item := range data { 145 | go func(i int) { 146 | doSomething(i) 147 | }(item) 148 | } 149 | 150 | time.Sleep(time.Second * 1) 151 | } 152 | } 153 | 154 | func doSomething(i int) { 155 | time.Sleep(2 * time.Second) 156 | fmt.Println("End:", i) 157 | } 158 | 159 | func queryDataWithSizeN(size int) (dataList []int, err error) { 160 | rand.Seed(time.Now().Unix()) 161 | dataList = rand.Perm(size) 162 | return 163 | } 164 | -------------------------------------------------------------------------------- /codes/holmes_notes/get_process_info.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | 4 | func getUsageNormal() (float64, float64, int, int, error) { 5 | p, err := process.NewProcess(int32(os.Getpid())) 6 | if err != nil { 7 | return 0, 0, 0, 0, err 8 | } 9 | 10 | cpuPercent, err := p.Percent(time.Second) 11 | if err != nil { 12 | return 0, 0, 0, 0, err 13 | } 14 | 15 | // The default percent is from all cores, multiply by runtime.NumCPU() 16 | // but it's inconvenient to calculate the proper percent 17 | // here we divide by core number, so we can set a percent bar more intuitively 18 | cpuPercent = cpuPercent / float64(runtime.NumCPU()) 19 | 20 | mem, err := p.MemoryPercent() 21 | if err != nil { 22 | return 0, 0, 0, 0, err 23 | } 24 | 25 | gNum := runtime.NumGoroutine() 26 | 27 | tNum := getThreadNum() 28 | 29 | return cpuPercent, float64(mem), gNum, tNum, nil 30 | } 31 | 32 | func getUsageCGroup() (float64, float64, int, int, error) { 33 | p, err := process.NewProcess(int32(os.Getpid())) 34 | if err != nil { 35 | return 0, 0, 0, 0, err 36 | } 37 | 38 | cpuPercent, err := p.Percent(time.Second) 39 | if err != nil { 40 | return 0, 0, 0, 0, err 41 | } 42 | 43 | cpuPeriod, err := readUint("/sys/fs/cgroup/cpu/cpu.cfs_period_us") 44 | if err != nil { 45 | return 0, 0, 0, 0, err 46 | } 47 | 48 | cpuQuota, err := readUint("/sys/fs/cgroup/cpu/cpu.cfs_quota_us") 49 | if err != nil { 50 | return 0, 0, 0, 0, err 51 | } 52 | cpuCore := float64(cpuQuota) / float64(cpuPeriod) 53 | 54 | // the same with physical machine 55 | // need to divide by core number 56 | cpuPercent = cpuPercent / cpuCore 57 | mem, err := p.MemoryInfo() 58 | p.MemoryPercent() 59 | if err != nil { 60 | return 0, 0, 0, 0, err 61 | } 62 | 63 | memLimit, err := getCGroupMemoryLimit() 64 | if err != nil { 65 | return 0, 0, 0, 0, err 66 | } 67 | // mem.RSS / cgroup limit in bytes 68 | memPercent := float64(mem.RSS) * 100 / float64(memLimit) 69 | 70 | gNum := runtime.NumGoroutine() 71 | 72 | tNum := getThreadNum() 73 | 74 | return cpuPercent, memPercent, gNum, tNum, nil 75 | } 76 | 77 | func getCGroupMemoryLimit() (uint64, error) { 78 | usage, err := readUint("/sys/fs/cgroup/memory/memory.limit_in_bytes") 79 | if err != nil { 80 | return 0, err 81 | } 82 | machineMemory, err := mem.VirtualMemory() 83 | if err != nil { 84 | return 0, err 85 | } 86 | limit := uint64(math.Min(float64(usage), float64(machineMemory.Total))) 87 | return limit, nil 88 | } 89 | 90 | func readUint(path string) (uint64, error) { 91 | v, err := ioutil.ReadFile(path) 92 | if err != nil { 93 | return 0, err 94 | } 95 | return parseUint(strings.TrimSpace(string(v)), 10, 64) 96 | } 97 | 98 | func parseUint(s string, base, bitSize int) (uint64, error) { 99 | v, err := strconv.ParseUint(s, base, bitSize) 100 | if err != nil { 101 | intValue, intErr := strconv.ParseInt(s, base, bitSize) 102 | // 1. Handle negative values greater than MinInt64 (and) 103 | // 2. Handle negative values lesser than MinInt64 104 | if intErr == nil && intValue < 0 { 105 | return 0, nil 106 | } else if intErr != nil && 107 | intErr.(*strconv.NumError).Err == strconv.ErrRange && 108 | intValue < 0 { 109 | return 0, nil 110 | } 111 | return 0, err 112 | } 113 | return v, nil 114 | } 115 | 116 | func getThreadNum() int { 117 | return pprof.Lookup("threadcreate").Count() 118 | } 119 | 120 | func main() { 121 | // in machine use getUsageNormal 122 | fmt.Println(getUsageNormal()) 123 | // in docker us getUsageCGroup 124 | fmt.Println(getUsageCGroup()) 125 | 126 | } 127 | -------------------------------------------------------------------------------- /codes/ali_tts_oss/tts/tts.go: -------------------------------------------------------------------------------- 1 | package tts 2 | 3 | import ( 4 | "bytes" 5 | "xxx/oss" 6 | "crypto/md5" 7 | "encoding/hex" 8 | "errors" 9 | "fmt" 10 | "log" 11 | "os" 12 | "time" 13 | nls "github.com/aliyun/alibabacloud-nls-go-sdk" 14 | ) 15 | 16 | 17 | var ( 18 | accessKeyId = "xxxxx" 19 | accessKeySecret = "xxx" 20 | appKey = "xxxxxxx" 21 | 22 | OSSEndpoint = "xxxx" 23 | OSSAccessKey = "xxx" 24 | OSSKeySecrete = "xxx" 25 | 26 | ) 27 | 28 | var clientConfig *nls.ConnectionConfig 29 | 30 | func getClient() *nls.ConnectionConfig { 31 | return clientConfig 32 | } 33 | 34 | type userParam struct { 35 | audioStream bytes.Buffer //转换成功回调时把音频流保存在这里 36 | } 37 | 38 | func init() { 39 | 40 | var ak, as string 41 | if os.Getenv("env") == "prod" { 42 | ak = accessKeyIdProd 43 | as = accessKeySecretProd 44 | } else { 45 | ak = accessKeyId 46 | as = accessKeySecret 47 | } 48 | cf, err := nls.NewConnectionConfigWithAKInfoDefault(nls.DEFAULT_URL, appKey, ak, as) 49 | if err != nil { 50 | panic(err) 51 | } 52 | clientConfig = cf 53 | } 54 | 55 | // 转换任务错误执行 56 | func onTaskFailed(text string, param interface{}) { 57 | log.Print("TtsTextToVoiceTaskFailedError", text) 58 | } 59 | 60 | // 转换任务成功回调音频流 61 | func onSynthesisResult(data []byte, param interface{}) { 62 | p, ok := param.(*userParam) 63 | if !ok { 64 | log.Print("TtsTextToVoiceResultUserParamUnExpected", param) 65 | return 66 | } 67 | p.audioStream.Write(data) 68 | } 69 | 70 | // 执行完毕 71 | func onCompleted(text string, param interface{}) { 72 | log.Print("TtsTextToVoiceOnCompleted:", text) 73 | } 74 | 75 | func onClose(param interface{}) { 76 | log.Print("TtsTextToVoiceOnClosed") 77 | } 78 | 79 | func waitReady(ch chan bool, logger *nls.NlsLogger) error { 80 | select { 81 | case done := <-ch: 82 | { 83 | if !done { 84 | logger.Println("Wait failed") 85 | return errors.New("wait failed") 86 | } 87 | logger.Println("Wait done") 88 | } 89 | case <-time.After(60 * time.Second): 90 | { 91 | logger.Println("Wait timeout") 92 | return errors.New("wait timeout") 93 | } 94 | } 95 | return nil 96 | } 97 | 98 | func TextToVoice(text string) (audioFileUrl string, err error) { 99 | clientConf := getClient() 100 | param := nls.DefaultSpeechSynthesisParam() 101 | param.Voice = "yuer" // 儿童剧女声 102 | strId := "ai-" // tts日志的前缀 103 | logger := nls.NewNlsLogger(os.Stderr, strId, log.LstdFlags|log.Lmicroseconds) 104 | logger.SetLogSil(false) 105 | logger.SetDebug(true) 106 | userParam := new(userParam) 107 | //第三个参数控制是否请求长文本语音合成,false为短文本语音合成 108 | tts, err := nls.NewSpeechSynthesis(clientConf, logger, false, 109 | onTaskFailed, onSynthesisResult, nil, 110 | onCompleted, onClose, userParam) 111 | if err != nil { 112 | logger.Fatalln(err) 113 | return 114 | } 115 | ch, err := tts.Start(text, param, nil) 116 | if err != nil { 117 | tts.Shutdown() 118 | dlog.Error("TtsTextToVoiceStartError", err) 119 | return 120 | } 121 | err = waitReady(ch, logger) 122 | if err != nil { 123 | tts.Shutdown() 124 | dlog.Error("TtsTextToVoiceWaitError", err) 125 | return 126 | } 127 | tts.Shutdown() //tts关闭后再把文件流上传到OSS 128 | bucket, err := oss.GetBucket("qschou", OSSEndpoint, OSSAccessKey, OSSKeySecrete) 129 | if err != nil { 130 | log.Print("func", "SaveImgToOSS", "msg", "GetBucket Error", "err", err) 131 | return 132 | } 133 | uniqId := Md5(fmt.Sprintf("%d", time.Now().UnixNano())) 134 | fileName := fmt.Sprintf("files/ai-toolbox/text-to-audio/%s.%s", uniqId, "wav") 135 | err = bucket.PutObject(fileName, bytes.NewReader(userParam.audioStream.Bytes())) 136 | if err != nil { 137 | log.Print("func", "SaveImgToOSS", "msg", "PutObject Error", "err", err) 138 | return "", err 139 | } 140 | 141 | return "https://thumb.qschou.com" + "/" + fileName, nil 142 | //本地测试用 143 | //fname := "ttsdump.wav" 144 | //fout, err := os.OpenFile(fname, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0666) 145 | //fout.Write(userParam.audioStream.Bytes()) 146 | //return "", nil 147 | } 148 | 149 | 150 | func Md5(str string) string { 151 | tmp := md5.Sum([]byte(str)) 152 | return hex.EncodeToString(tmp[:]) 153 | } 154 | -------------------------------------------------------------------------------- /codes/img/base.go: -------------------------------------------------------------------------------- 1 | package img 2 | 3 | import ( 4 | "bytes" 5 | "image" 6 | "image/color" 7 | "image/draw" 8 | "image/png" 9 | ) 10 | 11 | 12 | 13 | // SubImage 裁剪图片 14 | func SubImage(imgByte []byte) (subImgList [][]byte, err error) { 15 | reader := bytes.NewReader(imgByte) 16 | 17 | img, _, err := image.Decode(reader) 18 | if err != nil { 19 | return 20 | } 21 | 22 | var ( 23 | // 获取图片的宽度和高度 24 | bounds = img.Bounds() 25 | width = bounds.Max.X 26 | height = bounds.Max.Y 27 | 28 | // 保存剪切后的四个图片 29 | topLeftBuf = new(bytes.Buffer) 30 | topRightBuf = new(bytes.Buffer) 31 | bottomLeftBuf = new(bytes.Buffer) 32 | bottomRightBuf = new(bytes.Buffer) 33 | ) 34 | 35 | // 剪切四个角落的图片 36 | topLeft := image.NewRGBA(image.Rect(0, 0, width/2, height/2)) 37 | draw.Draw(topLeft, topLeft.Bounds(), img, image.Point{}, draw.Src) 38 | if err = png.Encode(topLeftBuf, topLeft); err != nil { 39 | return 40 | } 41 | subImgList = append(subImgList, topLeftBuf.Bytes()) 42 | 43 | topRight := image.NewRGBA(image.Rect(0, 0, width/2, height/2)) 44 | draw.Draw(topRight, topRight.Bounds(), img, image.Point{X: width / 2}, draw.Src) 45 | if err = png.Encode(topRightBuf, topRight); err != nil { 46 | return 47 | } 48 | subImgList = append(subImgList, topRightBuf.Bytes()) 49 | 50 | bottomLeft := image.NewRGBA(image.Rect(0, 0, width/2, height/2)) 51 | draw.Draw(bottomLeft, bottomLeft.Bounds(), img, image.Point{Y: height / 2}, draw.Src) 52 | if err = png.Encode(bottomLeftBuf, bottomLeft); err != nil { 53 | return 54 | } 55 | subImgList = append(subImgList, bottomLeftBuf.Bytes()) 56 | 57 | bottomRight := image.NewRGBA(image.Rect(0, 0, width/2, height/2)) 58 | draw.Draw(bottomRight, bottomRight.Bounds(), img, image.Point{X: width / 2, Y: height / 2}, draw.Src) 59 | if err = png.Encode(bottomRightBuf, bottomRight); err != nil { 60 | return 61 | } 62 | subImgList = append(subImgList, bottomRightBuf.Bytes()) 63 | return 64 | } 65 | 66 | // SubImageByGap 根据间隔裁剪图片 67 | func SubImageByGap(imgByte []byte) (subImgList [][]byte, err error) { 68 | reader := bytes.NewReader(imgByte) 69 | 70 | imgBg, _, err := image.Decode(reader) 71 | if err != nil { 72 | return 73 | } 74 | 75 | var ( 76 | // 获取图片的宽度和高度 77 | bounds = imgBg.Bounds() 78 | bgWidth = bounds.Max.X 79 | bgHeight = bounds.Max.Y 80 | ) 81 | 82 | // 识别小图的水平间隔 83 | var ( 84 | lastGapY int 85 | rawImages []*image.RGBA // 裁剪出的小图 86 | ) 87 | for y := 0; y < bgHeight; y++ { // 遍历图片的高度 88 | gapWidth := 0 // 间隔宽度 89 | for x := 0; x < bgWidth; x++ { // 遍历图片的宽度 90 | at := imgBg.At(x, y) // 获取像素点的颜色 91 | if !isWhite(at) { // 判断是否是白色 92 | break 93 | } 94 | gapWidth++ // 间隔宽度+1 95 | } 96 | if gapWidth == bgWidth { // 间隔宽度等于图片宽度,说明是白色间隔 97 | if y-lastGapY <= 1 { // 排除连续的白色间隔 98 | lastGapY = y 99 | continue 100 | } 101 | // 裁剪出小图 102 | smallImg := image.NewRGBA(image.Rect(0, 0, bgWidth, y-lastGapY)) 103 | draw.Draw(smallImg, smallImg.Bounds(), imgBg, image.Point{Y: lastGapY}, draw.Src) 104 | rawImages = append(rawImages, smallImg) 105 | lastGapY = y 106 | } 107 | } 108 | 109 | var smallImages []*image.RGBA // 裁剪出的小图 110 | for _, img := range rawImages { // 遍历竖行图 111 | var lastGapX int 112 | // 重新获取图片的宽度和高度 113 | bgWidth = img.Bounds().Max.X 114 | bgHeight = img.Bounds().Max.Y 115 | for x := 0; x < bgWidth; x++ { // 遍历图片的宽度 116 | gapHeight := 0 // 间隔高度 117 | for y := 0; y < bgHeight; y++ { // 遍历图片的高度 118 | at := img.At(x, y) // 获取像素点的颜色 119 | if !isWhite(at) { // 判断是否是白色 120 | break 121 | } 122 | gapHeight++ // 间隔高度+1 123 | } 124 | if gapHeight == bgHeight { // 间隔高度等于图片高度,说明是白色间隔 125 | if x-lastGapX == 1 { // 排除连续的白色间隔 126 | lastGapX = x 127 | continue 128 | } 129 | // 裁剪出竖行图 130 | smallImg := image.NewRGBA(image.Rect(0, 0, x-lastGapX, bgHeight)) 131 | draw.Draw(smallImg, smallImg.Bounds(), img, image.Point{X: lastGapX}, draw.Src) 132 | smallImages = append(smallImages, smallImg) 133 | lastGapX = x 134 | } 135 | } 136 | // 添加最后一个列图 137 | if lastGapX < bgWidth { 138 | smallImg := image.NewRGBA(image.Rect(0, 0, bgWidth-lastGapX, bgHeight)) 139 | draw.Draw(smallImg, smallImg.Bounds(), img, image.Point{X: lastGapX}, draw.Src) 140 | smallImages = append(smallImages, smallImg) 141 | } 142 | } 143 | // 保存裁剪出的小图 144 | for _, img := range smallImages { 145 | if fullWhiteImg(img) { 146 | continue 147 | } 148 | imgBuf := new(bytes.Buffer) 149 | if err = png.Encode(imgBuf, img); err != nil { 150 | continue 151 | } 152 | subImgList = append(subImgList, imgBuf.Bytes()) 153 | } 154 | return 155 | } 156 | 157 | // fullWhiteImg 判断是否是全白图片 158 | // img 图片 159 | // is 是否是全白图片: true 是, false 否 160 | func fullWhiteImg(img *image.RGBA) (is bool) { 161 | bounds := img.Bounds() 162 | bgWidth := bounds.Max.X 163 | bgHeight := bounds.Max.Y 164 | for y := 0; y < bgHeight; y++ { // 遍历图片的高度 165 | for x := 0; x < bgWidth; x++ { // 遍历图片的宽度 166 | at := img.At(x, y) // 获取像素点的颜色 167 | if !isWhite(at) { // 判断是否是白色 168 | return 169 | } 170 | } 171 | } 172 | is = true 173 | return 174 | } 175 | 176 | // isWhite 判断是否是白色 177 | func isWhite(c color.Color) bool { 178 | maxColor := color.RGBA{R: 255, G: 255, B: 255, A: 255} // 白色的最大值 179 | minColor := color.RGBA{R: 240, G: 240, B: 240, A: 240} // 白色的最小值 180 | 181 | r, g, b, a := c.RGBA() 182 | var rBool, gBool, bBool, aBool bool 183 | //fmt.Printf("byte(r): %d, byte(g): %d, byte(b): %d, byte(a): %d\n", byte(r), byte(g), byte(b), byte(a)) 184 | if byte(r) <= maxColor.R && byte(r) >= minColor.R { 185 | rBool = true 186 | } 187 | if byte(g) <= maxColor.G && byte(g) >= minColor.G { 188 | gBool = true 189 | } 190 | if byte(b) <= maxColor.B && byte(b) >= minColor.B { 191 | bBool = true 192 | } 193 | if byte(a) <= maxColor.A && byte(a) >= minColor.A { 194 | aBool = true 195 | } 196 | return rBool && gBool && bBool && aBool 197 | } 198 | -------------------------------------------------------------------------------- /codes/proconsumer/tests/file.txt: -------------------------------------------------------------------------------- 1 | sssssssssss 2 | dsssssssssss 3 | dssssssssssssss 4 | fdddddddddddd 5 | eeeeeetr 6 | rtttttttttttt 7 | sssssssssss 8 | dsssssssssss 9 | dssssssssssssss 10 | fdddddddddddd 11 | eeeeeetr 12 | rtttttttttttt 13 | sssssssssss 14 | dsssssssssss 15 | dssssssssssssss 16 | fdddddddddddd 17 | eeeeeetr 18 | rtttttttttttt 19 | sssssssssss 20 | dsssssssssss 21 | dssssssssssssss 22 | fdddddddddddd 23 | eeeeeetr 24 | rtttttttttttt 25 | sssssssssss 26 | dsssssssssss 27 | dssssssssssssss 28 | fdddddddddddd 29 | eeeeeetr 30 | rtttttttttttt 31 | sssssssssss 32 | dsssssssssss 33 | dssssssssssssss 34 | fdddddddddddd 35 | eeeeeetr 36 | rtttttttttttt 37 | sssssssssss 38 | dsssssssssss 39 | dssssssssssssss 40 | fdddddddddddd 41 | eeeeeetr 42 | rtttttttttttt 43 | sssssssssss 44 | dsssssssssss 45 | dssssssssssssss 46 | fdddddddddddd 47 | eeeeeetr 48 | rtttttttttttt 49 | sssssssssss 50 | dsssssssssss 51 | dssssssssssssss 52 | fdddddddddddd 53 | eeeeeetr 54 | rtttttttttttt 55 | sssssssssss 56 | dsssssssssss 57 | dssssssssssssss 58 | fdddddddddddd 59 | eeeeeetr 60 | rtttttttttttt 61 | sssssssssss 62 | dsssssssssss 63 | dssssssssssssss 64 | fdddddddddddd 65 | eeeeeetr 66 | rtttttttttttt 67 | sssssssssss 68 | dsssssssssss 69 | dssssssssssssss 70 | fdddddddddddd 71 | eeeeeetr 72 | rtttttttttttt 73 | sssssssssss 74 | dsssssssssss 75 | dssssssssssssss 76 | fdddddddddddd 77 | eeeeeetr 78 | rtttttttttttt 79 | sssssssssss 80 | dsssssssssss 81 | dssssssssssssss 82 | fdddddddddddd 83 | eeeeeetr 84 | rtttttttttttt 85 | sssssssssss 86 | dsssssssssss 87 | dssssssssssssss 88 | fdddddddddddd 89 | eeeeeetr 90 | rtttttttttttt 91 | sssssssssss 92 | dsssssssssss 93 | dssssssssssssss 94 | fdddddddddddd 95 | eeeeeetr 96 | rtttttttttttt 97 | sssssssssss 98 | dsssssssssss 99 | dssssssssssssss 100 | fdddddddddddd 101 | eeeeeetr 102 | rtttttttttttt 103 | sssssssssss 104 | dsssssssssss 105 | dssssssssssssss 106 | fdddddddddddd 107 | eeeeeetr 108 | rtttttttttttt 109 | sssssssssss 110 | dsssssssssss 111 | dssssssssssssss 112 | fdddddddddddd 113 | eeeeeetr 114 | rtttttttttttt 115 | sssssssssss 116 | dsssssssssss 117 | dssssssssssssss 118 | fdddddddddddd 119 | eeeeeetr 120 | rtttttttttttt 121 | sssssssssss 122 | dsssssssssss 123 | dssssssssssssss 124 | fdddddddddddd 125 | eeeeeetr 126 | rtttttttttttt 127 | sssssssssss 128 | dsssssssssss 129 | dssssssssssssss 130 | fdddddddddddd 131 | eeeeeetr 132 | rtttttttttttt 133 | sssssssssss 134 | dsssssssssss 135 | dssssssssssssss 136 | fdddddddddddd 137 | eeeeeetr 138 | rtttttttttttt 139 | sssssssssss 140 | dsssssssssss 141 | dssssssssssssss 142 | fdddddddddddd 143 | eeeeeetr 144 | rtttttttttttt 145 | sssssssssss 146 | dsssssssssss 147 | dssssssssssssss 148 | fdddddddddddd 149 | eeeeeetr 150 | rtttttttttttt 151 | sssssssssss 152 | dsssssssssss 153 | dssssssssssssss 154 | fdddddddddddd 155 | eeeeeetr 156 | rtttttttttttt 157 | sssssssssss 158 | dsssssssssss 159 | dssssssssssssss 160 | fdddddddddddd 161 | eeeeeetr 162 | rtttttttttttt 163 | sssssssssss 164 | dsssssssssss 165 | dssssssssssssss 166 | fdddddddddddd 167 | eeeeeetr 168 | rtttttttttttt 169 | sssssssssss 170 | dsssssssssss 171 | dssssssssssssss 172 | fdddddddddddd 173 | eeeeeetr 174 | rtttttttttttt 175 | sssssssssss 176 | dsssssssssss 177 | dssssssssssssss 178 | fdddddddddddd 179 | eeeeeetr 180 | rtttttttttttt 181 | sssssssssss 182 | dsssssssssss 183 | dssssssssssssss 184 | fdddddddddddd 185 | eeeeeetr 186 | rtttttttttttt 187 | sssssssssss 188 | dsssssssssss 189 | dssssssssssssss 190 | fdddddddddddd 191 | eeeeeetr 192 | rtttttttttttt 193 | sssssssssss 194 | dsssssssssss 195 | dssssssssssssss 196 | fdddddddddddd 197 | eeeeeetr 198 | rtttttttttttt 199 | sssssssssss 200 | dsssssssssss 201 | dssssssssssssss 202 | fdddddddddddd 203 | eeeeeetr 204 | rtttttttttttt 205 | sssssssssss 206 | dsssssssssss 207 | dssssssssssssss 208 | fdddddddddddd 209 | eeeeeetr 210 | rtttttttttttt 211 | sssssssssss 212 | dsssssssssss 213 | dssssssssssssss 214 | fdddddddddddd 215 | eeeeeetr 216 | rtttttttttttt 217 | sssssssssss 218 | dsssssssssss 219 | dssssssssssssss 220 | fdddddddddddd 221 | eeeeeetr 222 | rtttttttttttt 223 | sssssssssss 224 | dsssssssssss 225 | dssssssssssssss 226 | fdddddddddddd 227 | eeeeeetr 228 | rtttttttttttt 229 | sssssssssss 230 | dsssssssssss 231 | dssssssssssssss 232 | fdddddddddddd 233 | eeeeeetr 234 | rtttttttttttt 235 | sssssssssss 236 | dsssssssssss 237 | dssssssssssssss 238 | fdddddddddddd 239 | eeeeeetr 240 | rtttttttttttt 241 | sssssssssss 242 | dsssssssssss 243 | dssssssssssssss 244 | fdddddddddddd 245 | eeeeeetr 246 | rtttttttttttt 247 | sssssssssss 248 | dsssssssssss 249 | dssssssssssssss 250 | fdddddddddddd 251 | eeeeeetr 252 | rtttttttttttt 253 | sssssssssss 254 | dsssssssssss 255 | dssssssssssssss 256 | fdddddddddddd 257 | eeeeeetr 258 | rtttttttttttt 259 | sssssssssss 260 | dsssssssssss 261 | dssssssssssssss 262 | fdddddddddddd 263 | eeeeeetr 264 | rtttttttttttt 265 | sssssssssss 266 | dsssssssssss 267 | dssssssssssssss 268 | fdddddddddddd 269 | eeeeeetr 270 | rtttttttttttt 271 | sssssssssss 272 | dsssssssssss 273 | dssssssssssssss 274 | fdddddddddddd 275 | eeeeeetr 276 | rtttttttttttt 277 | sssssssssss 278 | dsssssssssss 279 | dssssssssssssss 280 | fdddddddddddd 281 | eeeeeetr 282 | rtttttttttttt 283 | sssssssssss 284 | dsssssssssss 285 | dssssssssssssss 286 | fdddddddddddd 287 | eeeeeetr 288 | rtttttttttttt 289 | sssssssssss 290 | dsssssssssss 291 | dssssssssssssss 292 | fdddddddddddd 293 | eeeeeetr 294 | rtttttttttttt 295 | sssssssssss 296 | dsssssssssss 297 | dssssssssssssss 298 | fdddddddddddd 299 | eeeeeetr 300 | rtttttttttttt 301 | sssssssssss 302 | dsssssssssss 303 | dssssssssssssss 304 | fdddddddddddd 305 | eeeeeetr 306 | rtttttttttttt 307 | sssssssssss 308 | dsssssssssss 309 | dssssssssssssss 310 | fdddddddddddd 311 | eeeeeetr 312 | rtttttttttttt 313 | sssssssssss 314 | dsssssssssss 315 | dssssssssssssss 316 | fdddddddddddd 317 | eeeeeetr 318 | rtttttttttttt 319 | sssssssssss 320 | dsssssssssss 321 | dssssssssssssss 322 | fdddddddddddd 323 | eeeeeetr 324 | rtttttttttttt 325 | sssssssssss 326 | dsssssssssss 327 | dssssssssssssss 328 | fdddddddddddd 329 | eeeeeetr 330 | rtttttttttttt 331 | sssssssssss 332 | dsssssssssss 333 | dssssssssssssss 334 | fdddddddddddd 335 | eeeeeetr 336 | rtttttttttttt 337 | -------------------------------------------------------------------------------- /lang-basic/content/chapter1.md: -------------------------------------------------------------------------------- 1 | ### 前言 2 | 3 | 以下内容为几年前学Go语言时记录的,发布在这里让这个项目对更多人能起到参考作用。 4 | 更多时下流行技术的应用和实战教程,可通过我公众号「网管叨bi叨」每周的推文来学习 5 | 6 | -------- 7 | 最近在读《Go 语言程序设计》这本书想通过看书巩固一下自己的基础知识,把已经积累的点通过看书学习再编织成一个网,这样看别人写的优秀代码时才能更好理解。当初工作中需要使用 Go开发项目时看了网上不少教程,比如 uknown 翻译的《the way to go》看完基本上每次使用遇到不会的时候还会再去翻阅,这次把书中的重点还有一些平时容易忽视的Go语言中各种内部结构(类型、函数、方法)的一些行为整理成读书笔记。 8 | 9 | 因为《Go 语言程序设计》不是针对初学者的,所以我只摘选最重要的部分并适当补充和调换描述顺序力求用最少的篇幅描述清楚每个知识点。 10 | 11 | 《Go 语言程序设计》在线阅读地址:https://yar999.gitbooks.io/gopl-zh/content/ 12 | 13 | 如果刚接触 Go建议先去读 《the-way-to-go》在线阅读地址:https://github.com/unknwon/the-way-to-go_ZH_CN/blob/master/eBook/directory.md 14 | 15 | ### 命名: 16 | 17 | - 函数名、变量名、常量名、类型名、包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。 18 | 19 | - 大写字母和小写字母是不同的:heapSort和Heapsort是两个不同的名字。 20 | 21 | - 关键字不可用于命名 22 | 23 | ``` 24 | break default func interface select 25 | case defer go map struct 26 | chan else goto package switch 27 | const fallthrough if range type 28 | continue for import return var 29 | ``` 30 | 31 | - 推荐驼峰式命名 32 | 33 | - 名字的开头字母的大小写决定了名字在包外的可见性。如果一个名字是大写字母开头的,那么它可以被外部的包访问,包本身的名字一般总是用小写字母。 34 | 35 | ### 声明: 36 | 37 | - Go语言主要有四种类型的声明语句:var、const、type和func,分别对应变量、常量、类型和函数。 38 | 39 | ### 变量: 40 | 41 | - var声明语句可以创建一个特定类型的变量,然后给变量附加一个名字,并且设置变量的初始值。变量声明的一般语法如下: 42 | 43 | ```Go 44 | var 变量名字 类型 = 表达式 45 | ``` 46 | 47 | 其中“*类型*”或“*= 表达式*”两个部分可以省略其中的一个。如果省略的是类型信息,那么将 根据初始化表达式来推导变量的类型信息。如果初始化表达式被省略,那么将用零值初始化该变量。 数值类型变量对应的零值是0,布尔类型变量对应的零值是false,字符串类型对应的零值是空字符串,接口或引用类型(包括slice、map、chan和函数)变量对应的零值是nil。数组或结构体等聚合类型对应的零值是每个元素或字段都是对应该类型的零值。 48 | 49 | 零值初始化机制可以确保每个声明的变量总是有一个良好定义的值,因此在Go语言中不存在未初始化的变量。这个特性可以简化很多代码,而且可以在没有增加额外工作的前提下确保边界条件下的合理行为。例如: 50 | 51 | ```Go 52 | var s string 53 | fmt.Println(s) // "" 54 | ``` 55 | 56 | 57 | 58 | ### 字符串: 59 | 60 | - 文本字符串通常被解释为采用UTF8编码的Unicode码点(rune)序列。 61 | 62 | - 内置的len函数可以返回一个字符串中的字节数目(不是rune字符数目),索引操作s[i]返回第i个字节的字节值,i必须满足0 ≤ i< len(s)条件约束。 63 | 64 | - 字符串的值是不可变的:一个字符串包含的字节序列永远不会被改变,当然我们也可以给一个字符串变量分配一个新字符串值。可以像下面这样将一个字符串追加到另一个字符串: 65 | 66 | ```Go 67 | s := "left foot" 68 | t := s 69 | s += ", right foot" 70 | ``` 71 | 72 | 这并不会导致原始的字符串值被改变,但是变量s将因为+=语句持有一个新的字符串值,但是t依然是包含原先的字符串值。 73 | 74 | 因为字符串是不可修改的,因此尝试修改字符串内部数据的操作也是被禁止的: 75 | 76 | ```Go 77 | s[0] = 'L' // compile error: cannot assign to s[0] 78 | ``` 79 | 80 | - 每一个UTF8字符解码,不管是显式地调用utf8.DecodeRuneInString解码或是在range循环中隐式地解码,如果遇到一个错误的UTF8编码输入,将生成一个特别的Unicode字符'\uFFFD',在印刷中这个符号通常是一个黑色六角或钻石形状,里面包含一个白色的问号"�"。当程序遇到这样的一个字符,通常是一个危险信号,说明输入并不是一个完美没有错误的UTF8字符串。 81 | 82 | - 字符串的各种转换: 83 | 84 | string接受到[]rune的类型转换,可以将一个UTF8编码的字符串解码为Unicode字符序列: 85 | 86 | ```Go 87 | // "program" in Japanese katakana 88 | s := "プログラム" 89 | fmt.Printf("% x\n", s) // "e3 83 97 e3 83 ad e3 82 b0 e3 83 a9 e3 83 a0" 90 | r := []rune(s) 91 | fmt.Printf("%x\n", r) // "[30d7 30ed 30b0 30e9 30e0]" 92 | ``` 93 | 94 | (在第一个Printf中的`% x`参数用于在每个十六进制数字前插入一个空格。) 95 | 96 | 如果是将一个[]rune类型的Unicode字符slice或数组转为string,则对它们进行UTF8编码: 97 | 98 | ```Go 99 | fmt.Println(string(r)) // "プログラム" 100 | ``` 101 | 102 | 将一个整数转型为字符串意思是生成以只包含对应Unicode码点字符的UTF8字符串: 103 | 104 | ```Go 105 | fmt.Println(string(65)) // "A", not "65" 106 | fmt.Println(string(0x4eac)) // "京" 107 | ``` 108 | 109 | 如果对应码点的字符是无效的,则用'\uFFFD'无效字符作为替换: 110 | 111 | ```Go 112 | fmt.Println(string(1234567)) // "�" 113 | ``` 114 | 115 | ### 复合数据类型: 116 | 117 | - 基本数据类型,它们可以用于构建程序中数据结构,是Go语言的世界的原子。以不同的方式组合基本类型可以构造出复合数据类型。我们主要讨论四种类型——数组、slice、map和结构体,数组和结构体都是有固定内存大小的数据结构。相比之下,slice和map则是动态的数据结构,它们将根据需要动态增长。 118 | 119 | ### 数组: 120 | 121 | - 数组的长度是数组类型的一个组成部分,因此[3]int和[4]int是两种不同的数组类型。数组的长度必须是常量表达式,因为数组的长度需要在编译阶段确定。 122 | 123 | ```Go 124 | q := [3]int{1, 2, 3} 125 | q = [4]int{1, 2, 3, 4} // compile error: cannot assign [4]int to [3]int 126 | ``` 127 | 128 | ### Slice: 129 | 130 | - 长度对应slice中元素的数目;长度不能超过容量,容量一般是从slice的开始位置到底层数据的结尾位置。内置的len和cap函数分别返回slice的长度和容量。 131 | 132 | - x[m:n]切片操作对于字符串则生成一个新字符串,如果x是[]byte的话则生成一个新的[]byte。 133 | 134 | - slice并不是一个纯粹的引用类型,它实际上是一个类似下面结构体的聚合类型: 135 | 136 | ```Go 137 | type IntSlice struct { 138 | ptr *int 139 | len, cap int 140 | } 141 | ``` 142 | 143 | ### Map: 144 | 145 | - 在Go语言中,一个map就是一个哈希表的引用,map类型可以写为map[K]V,其中K和V分别对应key和value。map中所有的key都有相同的类型,所有的value也有着相同的类型,但是key和value之间可以是不同的数据类型。 146 | 147 | - map中的元素并不是一个变量,因此我们不能对map的元素进行取址操作: 148 | 149 | ```Go 150 | _ = &ages["bob"] // compile error: cannot take address of map element 151 | ``` 152 | 153 | 禁止对map元素取址的原因是map可能随着元素数量的增长而重新分配更大的内存空间,从而可能导致之前的地址无效。 154 | 155 | - map上的大部分操作,包括查找、删除、len和range循环都可以安全工作在nil值的map上,它们的行为和一个空的map类似。但是向一个nil值的map存入元素将导致一个panic异常: 156 | 157 | ```Go 158 | ages["carol"] = 21 // panic: assignment to entry in nil map 159 | ``` 160 | 161 | 在向map存数据前必须先创建map。 162 | 163 | - 和slice一样,map之间也不能进行相等比较;唯一的例外是和nil进行比较。要判断两个map是否包含相同的key和value,我们必须通过一个循环实现。 164 | 165 | ### 结构体: 166 | 167 | - 下面两个语句声明了一个叫Employee的命名的结构体类型,并且声明了一个Employee类型的变量dilbert: 168 | 169 | ```Go 170 | type Employee struct { 171 | ID int 172 | Name string 173 | Address string 174 | DoB time.Time 175 | Position string 176 | Salary int 177 | ManagerID int 178 | } 179 | 180 | var dilbert Employee 181 | ``` 182 | 183 | dilbert结构体变量的成员可以通过点操作符访问,比如dilbert.Name和dilbert.DoB。因为dilbert是一个变量,它所有的成员也同样是变量,我们可以直接对每个成员赋值: 184 | 185 | ```Go 186 | dilbert.Salary -= 5000 // demoted, for writing too few lines of code 187 | ``` 188 | 189 | 或者是对成员取地址,然后通过指针访问: 190 | 191 | ```Go 192 | position := &dilbert.Position 193 | *position = "Senior " + *position // promoted, for outsourcing to Elbonia 194 | ``` 195 | 196 | - 如果结构体成员名字是以大写字母开头的,那么该成员就是导出的;这是Go语言导出规则决定的。一个结构体可能同时包含导出和未导出的成员。未导出的成员只能在包内部访问,在外部包不可访问。 197 | 198 | - 结构体类型的零值中每个成员其类型的是零值。通常会将零值作为最合理的默认值。例如,对于bytes.Buffer类型,结构体初始值就是一个随时可用的空缓存,还有sync.Mutex的零值也是有效的未锁定状态。有时候这种零值可用的特性是自然获得的,但是也有些类型需要一些额外的工作。 199 | 200 | - 因为结构体通常通过指针处理,可以用下面的写法来创建并初始化一个结构体变量,并返回结构体的地址: 201 | 202 | ```Go 203 | pp := &Point{1, 2} 204 | ``` 205 | 206 | - Go语言有一个特性让我们只声明一个成员对应的数据类型而不指名成员的名字;这类成员就叫匿名成员。匿名成员的数据类型必须是命名的类型或指向一个命名的类型的指针。下面的代码中,Circle和Wheel各自都有一个匿名成员。我们可以说Point类型被嵌入到了Circle结构体,同时Circle类型被嵌入到了Wheel结构体。 207 | 208 | ```Go 209 | type Circle struct { 210 | Point 211 | Radius int 212 | } 213 | 214 | type Wheel struct { 215 | Circle 216 | Spokes int 217 | } 218 | ``` 219 | 220 | 得益于匿名嵌入的特性,我们可以直接访问叶子属性而不需要给出完整的路径: 221 | 222 | ```Go 223 | var w Wheel 224 | w.X = 8 // equivalent to w.Circle.Point.X = 8 225 | w.Y = 8 // equivalent to w.Circle.Point.Y = 8 226 | w.Radius = 5 // equivalent to w.Circle.Radius = 5 227 | w.Spokes = 20 228 | ``` 229 | 230 | - 外层的结构体不仅仅是获得了匿名成员类型的所有成员,而且也获得了该类型导出的全部的方法。这个机制可以用于将一个有简单行为的对象组合成有复杂行为的对象。 231 | -------------------------------------------------------------------------------- /cmd/es/create_index_with_mapping.shell: -------------------------------------------------------------------------------- 1 | curl -H 'Content-Type: application/json' -X PUT {$es_server_host[:port]}/{$index_name} -v -d ' 2 | { 3 | "mappings": { 4 | "_doc": { 5 | "properties": { 6 | "account": { 7 | "type": "text", 8 | "fields": { 9 | "keyword": { 10 | "type": "keyword", 11 | "ignore_above": 256 12 | } 13 | } 14 | }, 15 | "performanceType": { 16 | "type": "long" 17 | }, 18 | "orderNo": { 19 | "type": "keyword" 20 | }, 21 | "projectId": { 22 | "type": "long" 23 | }, 24 | "asrState": { 25 | "type": "long" 26 | }, 27 | "businessId": { 28 | "type": "long" 29 | }, 30 | "callDuration": { 31 | "type": "long" 32 | }, 33 | "callType": { 34 | "type": "long" 35 | }, 36 | "chat": { 37 | "properties": { 38 | "content": { 39 | "type": "text", 40 | "fields": { 41 | "keyword": { 42 | "type": "keyword", 43 | "ignore_above": 256 44 | } 45 | } 46 | }, 47 | "end_time": { 48 | "type": "long" 49 | }, 50 | "id": { 51 | "type": "long" 52 | }, 53 | "side": { 54 | "type": "long" 55 | }, 56 | "start_time": { 57 | "type": "long" 58 | } 59 | } 60 | }, 61 | "checkAccount": { 62 | "type": "text", 63 | "fields": { 64 | "keyword": { 65 | "type": "keyword", 66 | "ignore_above": 256 67 | } 68 | } 69 | }, 70 | "checkDate": { 71 | "type": "date", 72 | "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" 73 | }, 74 | "bpoCheckAt": { 75 | "type": "date", 76 | "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" 77 | }, 78 | "checkMethod": { 79 | "type": "long" 80 | }, 81 | "checkResult": { 82 | "type": "long" 83 | }, 84 | "checkState": { 85 | "type": "long" 86 | }, 87 | "checkStateV2": { 88 | "type": "long" 89 | }, 90 | "checkType": { 91 | "type": "long" 92 | }, 93 | "contactId": { 94 | "type": "keyword" 95 | }, 96 | "userPhone": { 97 | "type": "keyword" 98 | }, 99 | "contactTime": { 100 | "type": "date", 101 | "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" 102 | }, 103 | "contain": { 104 | "type": "long" 105 | }, 106 | "createdAt": { 107 | "type": "date", 108 | "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" 109 | }, 110 | "departmentId": { 111 | "type": "long" 112 | }, 113 | "endDate": { 114 | "type": "date", 115 | "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" 116 | }, 117 | "facedId": { 118 | "type": "long" 119 | }, 120 | "facedName": { 121 | "type": "text", 122 | "fields": { 123 | "keyword": { 124 | "type": "keyword", 125 | "ignore_above": 256 126 | } 127 | } 128 | }, 129 | "firstCheckAt": { 130 | "type": "date", 131 | "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" 132 | }, 133 | "id": { 134 | "type": "long" 135 | }, 136 | "investigation": { 137 | "type": "long" 138 | }, 139 | "leadsUuid": { 140 | "type": "keyword", 141 | "ignore_above": 256 142 | }, 143 | "lotNumber": { 144 | "type": "keyword", 145 | "ignore_above": 256 146 | }, 147 | "taskType": { 148 | "type": "long" 149 | }, 150 | "platformId": { 151 | "type": "long" 152 | }, 153 | "policys": { 154 | "properties": { 155 | "isSyncBx": { 156 | "type": "long" 157 | }, 158 | "policyNo": { 159 | "type": "keyword", 160 | "ignore_above": 256 161 | }, 162 | "skuName": { 163 | "type": "text", 164 | "fields": { 165 | "keyword": { 166 | "type": "keyword", 167 | "ignore_above": 256 168 | } 169 | } 170 | }, 171 | "skutitle": { 172 | "type": "text", 173 | "fields": { 174 | "keyword": { 175 | "type": "keyword", 176 | "ignore_above": 256 177 | } 178 | } 179 | }, 180 | "source": { 181 | "type": "long" 182 | } 183 | } 184 | }, 185 | "reExamine": { 186 | "type": "long" 187 | }, 188 | "recordUuid": { 189 | "type": "keyword", 190 | "ignore_above": 256 191 | }, 192 | "remarks": { 193 | "type": "text", 194 | "fields": { 195 | "keyword": { 196 | "type": "keyword", 197 | "ignore_above": 256 198 | } 199 | } 200 | }, 201 | "saasKeyword": { 202 | "properties": { 203 | "violationWord": { 204 | "type": "keyword" 205 | }, 206 | "standardWord": { 207 | "type": "keyword" 208 | }, 209 | "sensitiveWord": { 210 | "type": "keyword" 211 | } 212 | } 213 | }, 214 | "tagId": { 215 | "type": "long" 216 | }, 217 | "tagName": { 218 | "type": "text", 219 | "fields": { 220 | "keyword": { 221 | "type": "keyword", 222 | "ignore_above": 256 223 | } 224 | } 225 | }, 226 | "translationPlatform": { 227 | "type": "long" 228 | }, 229 | "updatedAt": { 230 | "type": "date", 231 | "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" 232 | } 233 | } 234 | } 235 | } 236 | }' 237 | -------------------------------------------------------------------------------- /lang-basic/content/chapter3.md: -------------------------------------------------------------------------------- 1 | > 以下内容为几年前学Go语言时记录的,发布在这里让这个项目对更多人能起到参考作用。 更多时下流行技术的应用和实战教程,可通过我公众号「网管叨bi叨」每周的推文来学习 2 | 3 | ## 方法 4 | 5 | ### 方法声明 6 | 7 | 在函数声明时,在其名字之前放上一个变量,即是一个方法。这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法。 8 | 9 | ```go 10 | package geometry 11 | 12 | import "math" 13 | 14 | type Point struct{ X, Y float64 } 15 | 16 | // traditional function 17 | func Distance(p, q Point) float64 { 18 | return math.Hypot(q.X-p.X, q.Y-p.Y) 19 | } 20 | 21 | // same thing, but as a method of the Point type 22 | func (p Point) Distance(q Point) float64 { 23 | return math.Hypot(q.X-p.X, q.Y-p.Y) 24 | } 25 | ``` 26 | 27 | 上面的代码里那个附加的参数p,叫做方法的接收器(receiver)。在Go语言中,我们并不会像其它语言那样用this或者self作为接收器;我们可以任意的选择接收器的名字。建议是可以使用其类型的第一个字母,比如这里使用了Point的首字母p。 28 | 29 | 在方法调用过程中,接收器参数一般会在方法名之前出现。这和方法声明是一样的,都是接收器参数在方法名字之前。下面是例子: 30 | 31 | ```Go 32 | p := Point{1, 2} 33 | q := Point{4, 6} 34 | fmt.Println(Distance(p, q)) // "5", function call 35 | fmt.Println(p.Distance(q)) // "5", method call 36 | ``` 37 | 38 | 可以看到,上面的两个函数调用都是Distance,但是却没有发生冲突。第一个Distance的调用实际上用的是包级别的函数geometry.Distance,而第二个则是使用刚刚声明的Point,调用的是Point类下声明的Point.Distance方法。这种`p.Distance`的表达式叫做选择器,因为他会选择合适的对应p这个对象的`Distance`方法来执行。 39 | 40 | 因为每种类型都有其方法的命名空间,我们在用Distance这个名字的时候,不同的Distance调用指向了不同类型里的Distance方法。 41 | 42 | ```Go 43 | // A Path is a journey connecting the points with straight lines. 44 | type Path []Point 45 | // Distance returns the distance traveled along the path. 46 | func (path Path) Distance() float64 { 47 | sum := 0.0 48 | for i := range path { 49 | if i > 0 { 50 | sum += path[i-1].Distance(path[i]) 51 | } 52 | } 53 | return sum 54 | } 55 | ``` 56 | 57 | Path是一个命名的slice类型,而不是Point那样的struct类型,然而我们依然可以为它定义方法。两个Distance方法有不同的类型。他们两个方法之间没有任何关系,尽管Path的Distance方法会在内部调用`Point.Distance`方法来计算每个连接邻接点的线段的长度。 58 | 59 | Go和很多其它的面向对象的语言不太一样。在Go语言里,我们可以为一些简单的数值、字符串、slice、map来定义一些附加行为很方便。方法可以被声明到任意类型,只要不是一个指针或者一个interface(接收者不能是一个指针类型,但是它可以是任何其他允许类型的指针)。 60 | 61 | 对于一个给定的类型,其内部的方法都必须有唯一的方法名,但是不同的类型却可以有同样的方法名,比如我们这里Point和Path就都有Distance这个名字的方法;所以我们没有必要非在方法名之前加类型名来消除歧义,比如PathDistance。在上面两个对Distance名字的方法的调用中,编译器会根据方法的名字以及接收器来决定具体调用的是哪一个函数。 62 | 63 | ### 指针对象的方法 64 | 65 | 当调用一个函数时,会对其每一个参数值进行拷贝,如果一个函数需要更新一个变量,或者函数的其中一个参数实在太大我们希望能够避免进行这种默认的拷贝,这种情况下我们就需要用到指针了。对应到我们这里用来更新接收器的对象的方法,当这个接受者变量本身比较大时,我们就可以用其指针而不是对象来声明方法,如下: 66 | 67 | ```go 68 | func (p *Point) ScaleBy(factor float64) { 69 | p.X *= factor 70 | p.Y *= factor 71 | } 72 | ``` 73 | 74 | 这个方法的名字是`(*Point).ScaleBy`。这里的括号是必须的;没有括号的话这个表达式可能会被理解为`*(Point.ScaleBy)`。 75 | 76 | - 在现实的程序里,一般会约定如果Point这个类有一个指针作为接收器的方法,那么所有Point的方法都必须有一个指针接收器,即使是那些并不需要这个指针接收器的函数。我们在这里打破了这个约定只是为了展示一下两种方法的异同而已。 77 | 78 | - 不管你的method的receiver是指针类型还是非指针类型,都是可以通过指针/非指针类型进行调用的,编译器会帮你做类型转换。 79 | 80 | ```go 81 | p := Point{1, 2} 82 | pptr := &p 83 | p.ScaleBy(2) // implicit (&p) 84 | pptr.Distance(q) // implicit (*pptr) 85 | ``` 86 | 87 | - 在声明一个method的receiver是指针还是非指针类型时,你需要考虑两方面的内部,第一方面是这个对象本身是不是特别大,如果声明为非指针变量时,调用会产生一次拷贝;第二方面是如果你用指针类型作为receiver,那么你一定要注意,这种指针类型指向的始终是一块内存地址,就算你对其进行了拷贝(指针调用时也是值拷贝,只不过指针的值是一个内存地址,所以在函数里的指针与调用方的指针变量是两个不同的指针但是指向了相同的内存地址)。 88 | 89 | ### Nil也是一个合法的接收器类型 90 | 91 | - 就像一些函数允许nil指针作为参数一样,方法理论上也可以用nil指针作为其接收器,尤其当nil对于对象来说是合法的零值时,比如map或者slice。在下面的简单int链表的例子里,nil代表的是空链表: 92 | 93 | ```go 94 | // An IntList is a linked list of integers. 95 | // A nil *IntList represents the empty list. 96 | type IntList struct { 97 | Value int 98 | Tail *IntList 99 | } 100 | // Sum returns the sum of the list elements. 101 | func (list *IntList) Sum() int { 102 | if list == nil { 103 | return 0 104 | } 105 | return list.Value + list.Tail.Sum() 106 | } 107 | ``` 108 | 109 | 当你定义一个允许nil作为接收器的方法的类型时,在类型前面的注释中指出nil变量代表的意义是很有必要的,就像我们上面例子里做的这样。 110 | 111 | ### 通过嵌入结构体来扩展类型 112 | 113 | - 下面的ColoredPoint类型 114 | 115 | ```go 116 | import "image/color" 117 | 118 | type Point struct{ X, Y float64 } 119 | 120 | type ColoredPoint struct { 121 | Point 122 | Color color.RGBA 123 | } 124 | ``` 125 | 126 | 内嵌可以使我们在定义ColoredPoint时得到一种句法上的简写形式,并使其包含Point类型所具有的一切字段和方法。 127 | 128 | ```go 129 | var cp ColoredPoint 130 | cp.X = 1 131 | fmt.Println(cp.Point.X) // "1" 132 | cp.Point.Y = 2 133 | fmt.Println(cp.Y) // "2" 134 | 135 | red := color.RGBA{255, 0, 0, 255} 136 | blue := color.RGBA{0, 0, 255, 255} 137 | var p = ColoredPoint{Point{1, 1}, red} 138 | var q = ColoredPoint{Point{5, 4}, blue} 139 | fmt.Println(p.Distance(q.Point)) // "5" 140 | p.ScaleBy(2) 141 | q.ScaleBy(2) 142 | fmt.Println(p.Distance(q.Point)) // "10" 143 | ``` 144 | 145 | 通过内嵌结构体可以使我们定义字段特别多的复杂类型,我们可以将字段先按小类型分组,然后定义小类型的方法,之后再把它们组合起来。 146 | 147 | - 内嵌字段会指导编译器去生成额外的包装方法来委托已经声明好的方法,和下面的形式是等价的: 148 | 149 | ```go 150 | func (p ColoredPoint) Distance(q Point) float64 { 151 | return p.Point.Distance(q) 152 | } 153 | 154 | func (p *ColoredPoint) ScaleBy(factor float64) { 155 | p.Point.ScaleBy(factor) 156 | } 157 | ``` 158 | 159 | 当Point.Distance被第一个包装方法调用时,它的接收器值是p.Point,而不是p,当然了,在Point类的方法里,你是访问不到ColoredPoint的任何字段的。 160 | 161 | - 方法只能在命名类型(像Point)或者指向类型的指针上定义,但是多亏了内嵌,我们给匿名struct类型来定义方法也有了手段。这个例子中我们为变量起了一个更具表达性的名字:cache。因为sync.Mutex类型被嵌入到了这个struct里,其Lock和Unlock方法也就都被引入到了这个匿名结构中了,这让我们能够以一个简单明了的语法来对其进行加锁解锁操作。 162 | 163 | ```go 164 | var cache = struct { 165 | sync.Mutex 166 | mapping map[string]string 167 | }{ 168 | mapping: make(map[string]string), 169 | } 170 | 171 | 172 | func Lookup(key string) string { 173 | cache.Lock() 174 | v := cache.mapping[key] 175 | cache.Unlock() 176 | return v 177 | } 178 | ``` 179 | 180 | ### 方法值和方法表达式 181 | 182 | - 我们经常选择一个方法,并且在同一个表达式里执行,比如常见的p.Distance()形式,实际上将其分成两步来执行也是可能的。p.Distance叫作“选择器”,选择器会返回一个方法"值"->一个将方法(Point.Distance)绑定到特定接收器变量的函数。因为已经在前文中指定过了,这个函数可以不通过指定其接收器即可被调用,只要传入函数的参数即可: 183 | 184 | ```go 185 | p := Point{1, 2} 186 | q := Point{4, 6} 187 | 188 | distanceFromP := p.Distance // method value 189 | fmt.Println(distanceFromP(q)) // "5" 190 | var origin Point // {0, 0} 191 | fmt.Println(distanceFromP(origin)) // "2.23606797749979", sqrt(5) 192 | 193 | scaleP := p.ScaleBy // method value 194 | scaleP(2) // p becomes (2, 4) 195 | scaleP(3) // then (6, 12) 196 | scaleP(10) // then (60, 120) 197 | ``` 198 | 199 | - 当T是一个类型时,方法表达式可能会写作T.f或者(*T).f,会返回一个函数"值",这种函数会将其第一个参数用作接收器,所以可以用通常(译注:不写选择器)的方式来对其进行调用: 200 | 201 | ```go 202 | p := Point{1, 2} 203 | q := Point{4, 6} 204 | 205 | distance := Point.Distance // method expression 206 | fmt.Println(distance(p, q)) // "5" 207 | fmt.Printf("%T\n", distance) // "func(Point, Point) float64" 208 | 209 | scale := (*Point).ScaleBy 210 | scale(&p, 2) 211 | fmt.Println(p) // "{2 4}" 212 | fmt.Printf("%T\n", scale) // "func(*Point, float64)" 213 | // 译注:这个Distance实际上是指定了Point对象为接收器的一个方法func (p Point) Distance(), 214 | // 但通过Point.Distance得到的函数需要比实际的Distance方法多一个参数, 215 | // 即其需要用第一个额外参数指定接收器,后面排列Distance方法的参数。 216 | ``` 217 | 218 | - 当你根据一个变量来决定调用同一个类型的哪个函数时,方法表达式就显得很有用了。你可以根据选择来调用接收器各不相同的方法。下面的例子,变量op代表Point类型的addition或者subtraction方法,Path.TranslateBy方法会为其Path数组中的每一个Point来调用对应的方法: 219 | 220 | ```go 221 | type Point struct{ X, Y float64 } 222 | 223 | func (p Point) Add(q Point) Point { return Point{p.X + q.X, p.Y + q.Y} } 224 | func (p Point) Sub(q Point) Point { return Point{p.X - q.X, p.Y - q.Y} } 225 | 226 | type Path []Point 227 | 228 | func (path Path) TranslateBy(offset Point, add bool) { 229 | var op func(p, q Point) Point 230 | if add { 231 | op = Point.Add 232 | } else { 233 | op = Point.Sub 234 | } 235 | for i := range path { 236 | // Call either path[i].Add(offset) or path[i].Sub(offset). 237 | path[i] = op(path[i], offset) 238 | } 239 | } 240 | ``` 241 | 242 | ### 封装 243 | 244 | - 一个对象的变量或者方法如果对调用方是不可见的话,一般就被定义为“封装”。封装有时候也被叫做信息隐藏,同时也是面向对象编程最关键的一个方面。 245 | - Go语言只有一种控制可见性的手段:大写首字母的标识符会从定义它们的包中被导出,小写字母的则不会。 246 | - 这种基于名字的手段使得在语言中最小的封装单元是package,而不是像其它语言一样的`Class`。一个struct类型的字段对同一个包的所有代码都有可见性,无论你的代码是写在一个函数还是一个方法里。 247 | 248 | - 封装提供了三方面的优点。首先,因为调用方不能直接修改对象的变量值,其只需要关注少量的语句并且只要弄懂少量变量的可能的值即可。 249 | 250 | 第二,隐藏实现的细节,可以防止调用方依赖那些可能变化的具体实现,这样使设计包的程序员在不破坏对外的api情况下能得到更大的自由。 251 | 252 | 封装的第三个优点也是最重要的优点,是阻止了外部调用方对对象内部的值任意地进行修改。因为对象内部变量只可以被同一个包内的函数修改,所以包的作者可以让这些函数确保对象内部的一些值的不变性。比如下面的Counter类型允许调用方来增加counter变量的值,并且允许将这个值reset为0,但是不允许随便设置这个值(译注:因为压根就访问不到): 253 | 254 | ```go 255 | type Counter struct { n int } 256 | func (c *Counter) N() int { return c.n } 257 | func (c *Counter) Increment() { c.n++ } 258 | func (c *Counter) Reset() { c.n = 0 } 259 | ``` 260 | 261 | - 只用来访问或修改内部变量的函数被称为setter或者getter,例子如下,比如log包里的Logger类型对应的一些函数。在命名一个getter方法时,我们通常会省略掉前面的Get前缀。这种简洁上的偏好也可以推广到各种类型的前缀比如Fetch,Find或者Lookup。 262 | 263 | ```go 264 | package log 265 | type Logger struct { 266 | flags int 267 | prefix string 268 | // ... 269 | } 270 | func (l *Logger) Flags() int 271 | func (l *Logger) SetFlags(flag int) 272 | func (l *Logger) Prefix() string 273 | func (l *Logger) SetPrefix(prefix string) 274 | ``` 275 | 276 | - Go的编码风格不禁止直接导出字段。当然,一旦进行了导出,就没有办法在保证API兼容的情况下去除对其的导出,所以在一开始的选择一定要经过深思熟虑并且要考虑到包内部的一些不变量的保证,还有未来可能的变化。 277 | -------------------------------------------------------------------------------- /codes/crypto_utils/aes.go: -------------------------------------------------------------------------------- 1 | package crypto_utils 2 | 3 | import ( 4 | "bytes" 5 | "crypto/aes" 6 | "crypto/cipher" 7 | "crypto/hmac" 8 | "crypto/rand" 9 | "crypto/sha1" 10 | "crypto/sha256" 11 | "encoding/base64" 12 | "encoding/hex" 13 | "encoding/json" 14 | "errors" 15 | "fmt" 16 | "io" 17 | "strings" 18 | "time" 19 | ) 20 | 21 | // AES KEY either 16, 24, or 32 bytes to select AES-128, AES-192, or AES-256 22 | // var aseKey = "b3c65d06a1cf4dbda57af0af5c63e85f" 23 | 24 | func Base64URLDecode(data string) ([]byte, error) { 25 | var missing = (4 - len(data)%4) % 4 26 | data += strings.Repeat("=", missing) 27 | res, err := base64.URLEncoding.DecodeString(data) 28 | fmt.Println(" decodebase64urlsafe is :", string(res), err) 29 | return base64.URLEncoding.DecodeString(data) 30 | } 31 | 32 | func Base64UrlSafeEncode(source []byte) string { 33 | // Base64 Url Safe is the same as Base64 but does not contain '/' and '+' (replaced by '_' and '-') and trailing '=' are removed. 34 | bytearr := base64.StdEncoding.EncodeToString(source) 35 | safeurl := strings.Replace(string(bytearr), "/", "_", -1) 36 | safeurl = strings.Replace(safeurl, "+", "-", -1) 37 | safeurl = strings.Replace(safeurl, "=", "", -1) 38 | return safeurl 39 | } 40 | 41 | /* 42 | // keyStr 密钥 43 | // value 消息内容 44 | */ 45 | func HMACSHA1(value, keyStr string) string { 46 | key := []byte(keyStr) 47 | mac := hmac.New(sha1.New, key) 48 | mac.Write([]byte(value)) 49 | //进行base64编码 50 | res := base64.StdEncoding.EncodeToString(mac.Sum(nil)) 51 | return res 52 | } 53 | 54 | func HmacEqual(signingString, signature string, key) error { 55 | originalSig, err := base64.StdEncoding.DecodeString(signature) 56 | mac := hmac.New(sha1.New, key) 57 | mac.Write([]byte(value)) 58 | if !hmac.Equal(originalSig, mac.Sum(nil)) { 59 | return errors.New("signature is invalid") 60 | } 61 | 62 | return nil 63 | } 64 | 65 | // key is 32 bytes 66 | func AesEcbPkcs5Decrypt(crypted, key []byte) (origData []byte, err error) { 67 | block, err := aes.NewCipher(key) 68 | if err != nil { 69 | return 70 | } 71 | blockMode := NewECBDecrypter(block) 72 | origData = make([]byte, len(crypted)) 73 | blockMode.CryptBlocks(origData, crypted) 74 | origData = PKCS5UnPadding(origData) 75 | return 76 | } 77 | 78 | func AesEcbPkcs5Encrypt(src string, key []byte) (crypted []byte, err error) { 79 | block, err := aes.NewCipher(key) 80 | if err != nil { 81 | return 82 | } 83 | ecb := NewECBEncrypter(block) 84 | content := []byte(src) 85 | content = PKCS5Padding(content, block.BlockSize()) 86 | crypted = make([]byte, len(content)) 87 | ecb.CryptBlocks(crypted, content) 88 | return 89 | } 90 | 91 | func PKCS5Padding(ciphertext []byte, blockSize int) []byte { 92 | padding := blockSize - len(ciphertext)%blockSize 93 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 94 | return append(ciphertext, padtext...) 95 | } 96 | 97 | func PKCS5UnPadding(origData []byte) []byte { 98 | length := len(origData) 99 | unpadding := int(origData[length-1]) 100 | return origData[:(length - unpadding)] 101 | } 102 | 103 | type ecb struct { 104 | b cipher.Block 105 | blockSize int 106 | } 107 | 108 | func newECB(b cipher.Block) *ecb { 109 | return &ecb{ 110 | b: b, 111 | blockSize: b.BlockSize(), 112 | } 113 | } 114 | 115 | type ecbEncrypter ecb 116 | 117 | // NewECBEncrypter returns a BlockMode which encrypts in electronic code book 118 | // mode, using the given Block. 119 | func NewECBEncrypter(b cipher.Block) cipher.BlockMode { 120 | return (*ecbEncrypter)(newECB(b)) 121 | } 122 | func (x *ecbEncrypter) BlockSize() int { return x.blockSize } 123 | func (x *ecbEncrypter) CryptBlocks(dst, src []byte) { 124 | if len(src)%x.blockSize != 0 { 125 | panic("crypto/cipher: input not full blocks") 126 | } 127 | if len(dst) < len(src) { 128 | panic("crypto/cipher: output smaller than input") 129 | } 130 | for len(src) > 0 { 131 | x.b.Encrypt(dst, src[:x.blockSize]) 132 | src = src[x.blockSize:] 133 | dst = dst[x.blockSize:] 134 | } 135 | } 136 | 137 | type ecbDecrypter ecb 138 | 139 | // NewECBDecrypter returns a BlockMode which decrypts in electronic code book 140 | // mode, using the given Block. 141 | func NewECBDecrypter(b cipher.Block) cipher.BlockMode { 142 | return (*ecbDecrypter)(newECB(b)) 143 | } 144 | func (x *ecbDecrypter) BlockSize() int { return x.blockSize } 145 | func (x *ecbDecrypter) CryptBlocks(dst, src []byte) { 146 | if len(src)%x.blockSize != 0 { 147 | panic("crypto/cipher: input not full blocks") 148 | } 149 | if len(dst) < len(src) { 150 | panic("crypto/cipher: output smaller than input") 151 | } 152 | for len(src) > 0 { 153 | x.b.Decrypt(dst, src[:x.blockSize]) 154 | src = src[x.blockSize:] 155 | dst = dst[x.blockSize:] 156 | } 157 | } 158 | 159 | func PKCS7Padding(ciphertext []byte) []byte { 160 | padding := aes.BlockSize - len(ciphertext)%aes.BlockSize 161 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 162 | return append(ciphertext, padtext...) 163 | } 164 | 165 | func PKCS7UnPadding(plantText []byte) []byte { 166 | length := len(plantText) 167 | unpadding := int(plantText[length-1]) 168 | return plantText[:(length - unpadding)] 169 | } 170 | 171 | func AES256CBCEncrypt(plantText string, key string, iv []byte) (value string, err error) { 172 | plaintext := PKCS7Padding([]byte(plantText)) 173 | ciphertext := make([]byte, len(plaintext)) 174 | block, err := aes.NewCipher([]byte(key)) 175 | if err != nil { 176 | return 177 | } 178 | mode := cipher.NewCBCEncrypter(block, iv) 179 | mode.CryptBlocks(ciphertext, plaintext) 180 | return base64.StdEncoding.EncodeToString(ciphertext), nil 181 | } 182 | 183 | func AES256CBCDecrypt(plantText string, key string, iv string) (value string, err error) { 184 | var block cipher.Block 185 | if block, err = aes.NewCipher([]byte(key)); err != nil { 186 | return 187 | } 188 | var iiv []byte 189 | if iiv, err = base64.StdEncoding.DecodeString(iv); err != nil { 190 | return 191 | } 192 | 193 | var cipherText []byte 194 | if cipherText, err = base64.StdEncoding.DecodeString(plantText); err != nil { 195 | return 196 | } 197 | 198 | mode := cipher.NewCBCDecrypter(block, iiv) 199 | mode.CryptBlocks(cipherText, cipherText) 200 | 201 | cipherText = PKCS7UnPadding(cipherText) 202 | return string(cipherText), nil 203 | } 204 | 205 | //key is 16 bytes 206 | func AesCbcPkcs5Encrypt(origData, key []byte) ([]byte, error) { 207 | block, err := aes.NewCipher(key) 208 | if err != nil { 209 | return nil, err 210 | } 211 | blockSize := block.BlockSize() 212 | origData = PKCS5Padding(origData, blockSize) 213 | blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) 214 | crypted := make([]byte, len(origData)) 215 | 216 | blockMode.CryptBlocks(crypted, origData) 217 | return crypted, nil 218 | } 219 | 220 | func AesCbcPkcs5Decrypt(crypted, key []byte) ([]byte, error) { 221 | block, err := aes.NewCipher(key) 222 | if err != nil { 223 | return nil, err 224 | } 225 | blockSize := block.BlockSize() 226 | blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) 227 | origData := make([]byte, len(crypted)) 228 | blockMode.CryptBlocks(origData, crypted) 229 | origData = PKCS5UnPadding(origData) 230 | 231 | return origData, nil 232 | } 233 | 234 | func getHmacCode(s string, key []byte) string { 235 | h := hmac.New(sha256.New, key) 236 | _, _ = io.WriteString(h, s) 237 | return fmt.Sprintf("%x", h.Sum(nil)) 238 | } 239 | 240 | type Crypt struct { 241 | Iv string `json:"iv"` 242 | Value string `json:"value"` 243 | Mac string `json:"mac"` 244 | } 245 | 246 | func OpenSslAesEncrypt(value string, key string) (payload string, err error) { 247 | iv := make([]byte, 16) 248 | _, err = rand.Read(iv) 249 | 250 | var encode string 251 | if encode, err = AES256CBCEncrypt(value, key, iv); err != nil { 252 | return 253 | } 254 | 255 | ivv := base64.StdEncoding.EncodeToString(iv) 256 | 257 | crypt := Crypt{Iv: ivv, Value: encode, Mac: getHmacCode(ivv+encode, []byte(key))} 258 | 259 | var bs []byte 260 | if bs, err = json.Marshal(crypt); err != nil { 261 | return 262 | } 263 | 264 | return base64.StdEncoding.EncodeToString(bs), nil 265 | } 266 | 267 | func OpenSslAesDecrypt(payload string, key string) (value string, err error) { 268 | var bs []byte 269 | if bs, err = base64.StdEncoding.DecodeString(payload); err != nil { 270 | return 271 | } 272 | crypt := Crypt{} 273 | if err = json.Unmarshal(bs, &crypt); err != nil { 274 | return 275 | } 276 | 277 | return AES256CBCDecrypt(crypt.Value, key, crypt.Iv) 278 | } 279 | 280 | 281 | // Demo Application 282 | func aesEncryptDemo(content string) string { 283 | aesKey := "b3c65d06a1cf4dbda57af0af5c63e85f" 284 | aesKeyByte, _ := hex.DecodeString(aesKey) 285 | encrypt, _ := AesEcbPkcs5Encrypt(content, aesKeyByte) 286 | return hex.EncodeToString(encrypt) 287 | } 288 | 289 | func aesDecryptDemo(crypt string) (reply string, err error) { 290 | if crypt == "" { 291 | return 292 | } 293 | aesKey := "b3c65d06a1cf4dbda57af0af5c63e85f" 294 | aesKeyByte, _ := hex.DecodeString(aesKey) 295 | cryptByte, _ := hex.DecodeString(crypt) 296 | decryptByte, err := AesEcbPkcs5Decrypt(cryptByte, aesKeyByte) 297 | return string(decryptByte), err 298 | } 299 | 300 | // generate sign 301 | func genSignDemo(params string) (sign string) { 302 | sha1key := "564826" 303 | time := time.Now().Format("20060102150405") 304 | sourceData := "¶ms=" + params + "&time=" + time 305 | return HMACSHA1(sourceData, sha1key) 306 | } 307 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Golang开发手记 2 | 3 | 4 | 用Go语言做开发,在这个Repository里整理一些常用的案例,计划慢慢积累作为以后开发的CookBook。 5 | 6 | 仓库里所有知识点对应的代码示例都可正常运行,拿来直接应用到生产项目上也不会有问题。因为目的是积累Go语言开发的案头书,所以并不会讲源码分析之类的东西,如果想更多了解 Go 语言各种内部原理和源代码解读欢迎关注我的公众号 **「网管叨bi叨」** ,那里除了应用还会用大量的原理分析。 7 | 8 | **另外最近我推出了自己的Go实战专栏课程,专栏配套一个专属的私有项目,通过tag版本追踪记录每个章节代码的变更,让大家能轻松跟上学习**。 9 | 10 | **专栏分为五大部分**: 11 | 12 | image 13 | 14 | **访问:https://xiaobot.net/p/golang 或者扫码下方海报二维码可查看课程详情** 15 | 16 | image 17 | 18 | 19 | 20 | ## 目录 21 | - 前期准备 22 | - [环境安装](https://github.com/kevinyan815/gocookbook/issues/74) 23 | - [基础语法](https://github.com/kevinyan815/gocookbook/blob/master/lang-basic/README.md) 24 | - 初始化 25 | - [Go应用初始化工作的执行顺序](https://github.com/kevinyan815/gocookbook/issues/24) 26 | - [Go语言init函数的六个特征](https://mp.weixin.qq.com/s/P-BhuQy1Vd3lxlYgClDAJA) 27 | 28 | - 项目工程 29 | - [依赖管理工具GOMODULE](https://mp.weixin.qq.com/s/xtvTUl2IZFQ79dSR_m-b7A) 30 | - [GoModules 管理私有依赖模块](https://mp.weixin.qq.com/s/8E1PwnglrS18hZsUEvE-Qw) 31 | - [Go Modules 依赖的版本管理](https://mp.weixin.qq.com/s/ptJK7CDHCr6P4JCdsUXKdg) 32 | - [常用编码规范](https://github.com/kevinyan815/gocookbook/issues/61) 33 | - [Go 里面怎么实现枚举](https://github.com/kevinyan815/gocookbook/issues/73) 34 | - 字符串 35 | - [看透Go语言的字符串](https://github.com/kevinyan815/gocookbook/issues/40) 36 | - [操作中文字符串](https://github.com/kevinyan815/gocookbook/issues/11) 37 | - [常用字符串操作](https://yourbasic.org/golang/string-functions-reference-cheat-sheet/) 38 | - [string、int、int64 类型之间的相互转换](https://yourbasic.org/golang/convert-int-to-string/) 39 | - [高性能地拼接字符串](https://github.com/kevinyan815/gocookbook/issues/68) 40 | - 数组 41 | - [数组的上限推导和越界检查](https://github.com/kevinyan815/gocookbook/issues/37) 42 | - Slice切片 43 | - [声明和初始化](https://github.com/kevinyan815/gocookbook/issues/3) 44 | - [追加和删除元素](https://github.com/kevinyan815/gocookbook/issues/4) 45 | - [过滤重复元素](https://github.com/kevinyan815/gocookbook/issues/5) 46 | - [排序结构体切片](https://github.com/kevinyan815/gocookbook/issues/12) 47 | - [切片并非引用类型](https://github.com/kevinyan815/gocookbook/issues/38) 48 | - [使用切片时要注意的几个坑](https://mp.weixin.qq.com/s/ISLNTCo7Jr9XnqAEhDuYcw) 49 | - [实用的切片工具函数](https://github.com/kevinyan815/gocookbook/blob/master/codes/slice_util/code.go) 50 | - Map 51 | - [(通识概念)哈希表的设计原理](https://github.com/kevinyan815/gocookbook/issues/39) 52 | - [声明和初始化](https://github.com/kevinyan815/gocookbook/issues/6) 53 | - [不要向nil map写入键值](https://github.com/kevinyan815/gocookbook/issues/7) 54 | - [修改map](https://github.com/kevinyan815/gocookbook/issues/8) 55 | - [遍历map](https://github.com/kevinyan815/gocookbook/issues/15) 56 | - [make 和 new](https://github.com/kevinyan815/gocookbook/issues/53) 57 | - [Go 函数的 Map 型参数,会发生扩容后指向不同底层内存的事儿吗?](https://mp.weixin.qq.com/s/WfzeNWV1j0fSXUiVOLe5jw) 58 | - 结构体 59 | - [巧用匿名结构体](https://github.com/kevinyan815/gocookbook/issues/89) 60 | - 读写数据 61 | - [编码JSON](https://github.com/kevinyan815/gocookbook/issues/2) 62 | - [解码JSON](https://github.com/kevinyan815/gocookbook/issues/1) 63 | - [如何控制Go编码JSON数据时的行为](https://mp.weixin.qq.com/s/L45G42s0DMZhStszZnmTGQ) 64 | - [逐行读取文件](https://github.com/kevinyan815/gocookbook/issues/13) 65 | - [Go语言IO库使用方法汇总 (进行IO操作时到底应该用哪个库)](https://github.com/kevinyan815/gocookbook/issues/62) 66 | - [字节序:大端序和小端序](https://mp.weixin.qq.com/s/ri2tt4nvEJub-wEsh0WPPA) 67 | - [用Golang读写HTTP请求(附Options设计模式实现)](https://github.com/kevinyan815/gocookbook/issues/64) 68 | - 目录和文件操作 69 | - [Go语言文件操作大全](https://mp.weixin.qq.com/s/dQUEq0lJekEUH4CHEMwANw) 70 | - [加餐版--实用的目录和文件操作](https://github.com/kevinyan815/gocookbook/issues/84) 71 | 72 | - 指针 73 | - [用法和使用限制](https://github.com/kevinyan815/gocookbook/issues/41) 74 | - [uintptr 和 unsafer.Pointer](https://github.com/kevinyan815/gocookbook/issues/42) 75 | - [扩展阅读:内存对齐](https://github.com/kevinyan815/gocookbook/issues/43) 76 | - 接口 77 | - [认识Go的接口](https://github.com/kevinyan815/gocookbook/issues/45) 78 | - [Go接口的类型和方法的接收者](https://github.com/kevinyan815/gocookbook/issues/46) 79 | - [接口的类型转换和断言](https://github.com/kevinyan815/gocookbook/issues/47) 80 | - [接口调用时的动态派发](https://github.com/kevinyan815/gocookbook/issues/67) 81 | - [Range 迭代](https://github.com/kevinyan815/gocookbook/issues/15) 82 | - 函数 83 | - [调用惯例和参数传递](https://github.com/kevinyan815/gocookbook/issues/44) 84 | - [defer的用法和行为分析](https://github.com/kevinyan815/gocookbook/issues/51) 85 | - [panic和recover](https://github.com/kevinyan815/gocookbook/issues/52) 86 | 87 | - 错误处理 88 | - [关于Golang错误处理的一些建议](https://github.com/kevinyan815/gocookbook/issues/66) 89 | - [Go代码更优雅地错误处理](https://github.com/kevinyan815/gocookbook/issues/82) 90 | - [Go 1.13后的包装错误和相关接口](https://mp.weixin.qq.com/s/SFbSAGwQgQBVWpySYF-rkw) 91 | - 包 92 | - [内部包](https://github.com/kevinyan815/gocookbook/issues/58) 93 | - 标准库 94 | - [正则表达式](https://github.com/kevinyan815/gocookbook/issues/9) 95 | - [Time 常用基础操作](https://github.com/kevinyan815/gocookbook/issues/14) 96 | - [Time 的时区和时间计算操作汇总](https://github.com/kevinyan815/gocookbook/issues/85) 97 | - 数据库访问 98 | - [使用标准库 database/sql 访问数据库](https://mp.weixin.qq.com/s/bhsFCXTZ_TBP0EvyRM-bdA) 99 | - [使用ORM库 gorm 访问数据库](https://mp.weixin.qq.com/s/N-ZAgRrEu2FJBlApIhuVsg) 100 | - [GORM 入门指南](https://www.liwenzhou.com/posts/Go/gorm/) 101 | - [GORM CRUD指南](https://www.liwenzhou.com/posts/Go/gorm-crud/) 102 | - 系统编程 103 | - [命令行flag](https://github.com/kevinyan815/gocookbook/issues/36) 104 | - [监听系统信号](https://github.com/kevinyan815/gocookbook/issues/55) 105 | - 并发编程 106 | - [Context上下文](https://github.com/kevinyan815/gocookbook/issues/50) 107 | - [Context 使用示例](https://github.com/kevinyan815/gocookbook/issues/50) 108 | - [图解 Context 原理](https://mp.weixin.qq.com/s/NNYyBLOO949ElFriLVRWiA) 109 | - [Context 源码学习](https://mp.weixin.qq.com/s/SJna8UAoV9GTGCuRezC9Qw) 110 | - [Channel 基本概念和用法](https://github.com/kevinyan815/gocookbook/issues/54) 111 | - [互斥锁的典型用法和常见误区](https://github.com/kevinyan815/gocookbook/issues/88) 112 | - [用WaitGroup进行协同等待](https://github.com/kevinyan815/gocookbook/issues/34) 113 | - [ErrorGroup 兼顾协同等待和错误传递](https://github.com/kevinyan815/gocookbook/issues/35) 114 | - [Reset计时器的正确姿势](https://github.com/kevinyan815/gocookbook/issues/17) 115 | - [结合cancelCtx, Timer, Goroutine, Channel的一个例子](https://github.com/kevinyan815/gocookbook/issues/18) 116 | - [使用WaitGroup, Channel和Context打造一个并发用户标签查询器](https://github.com/kevinyan815/gocookbook/issues/21) 117 | - [使用sync.Cond实现一个有限容量的队列](https://github.com/kevinyan815/gocookbook/issues/22) 118 | - [使用信号量控制有限资源的并发访问](https://github.com/kevinyan815/gocookbook/issues/30) 119 | - [使用Chan扩展互斥锁的功能](https://github.com/kevinyan815/gocookbook/issues/25) 120 | - [用SingleFlight合并重复请求](https://github.com/kevinyan815/gocookbook/issues/31) 121 | - [CyclicBarrier 循环栅栏](https://github.com/kevinyan815/gocookbook/issues/32) 122 | - [原子操作的用法详解](https://github.com/kevinyan815/gocookbook/issues/65) 123 | - 反射 124 | - [Go反射的使用教程](https://github.com/kevinyan815/gocookbook/issues/69) 125 | - [反射最常见的应用--结构体标签](https://github.com/kevinyan815/gocookbook/issues/70) 126 | - 线上问题解决实录 127 | - [重定向运行时panic到日志文件](https://github.com/kevinyan815/gocookbook/issues/19) 128 | - [用Go的交叉编译和条件编译让自己的软件包运行在多平台上](https://github.com/kevinyan815/gocookbook/issues/20) 129 | - [在容器里怎么设置GOMAXPRCS](https://github.com/kevinyan815/gocookbook/issues/57) 130 | - [预防并发搞垮友军的几个方法](https://github.com/kevinyan815/gocookbook/issues/63) 131 | - 编译原理 132 | - [Go程序的编译原理](https://github.com/kevinyan815/gocookbook/issues/56) 133 | - 一些有意思的小程序 134 | - [一个简单的概率抽奖工具](https://github.com/kevinyan815/gocookbook/issues/23) 135 | - [限流算法之计数器](https://github.com/kevinyan815/gocookbook/issues/29) 136 | - [限流算法之滑动窗口](https://github.com/kevinyan815/gocookbook/issues/26) 137 | - [限流算法之漏桶](https://github.com/kevinyan815/gocookbook/issues/28) 138 | - [限流算法之令牌桶](https://github.com/kevinyan815/gocookbook/issues/27) 139 | - [并发趣题--H2O制造工厂](https://github.com/kevinyan815/gocookbook/issues/33) 140 | - [可以自解释的Token生成算法](https://github.com/kevinyan815/gocookbook/blob/master/codes/gen_token/main.go) 141 | - [生成分布式链路追踪traceid和spanid的算法](https://github.com/kevinyan815/gocookbook/blob/master/codes/trace_span/main.go) 142 | - [一个带阻塞限流器的HTTP客户端](https://github.com/kevinyan815/gocookbook/blob/master/codes/http_client_with_rate/http_rl_client.go) 143 | - [AES加解密,HMAC验签](https://github.com/kevinyan815/gocookbook/blob/master/codes/crypto_utils/aes.go) 144 | - [密码复杂度验证](https://github.com/kevinyan815/gocookbook/tree/master/codes/password_complexity) 145 | - gRPC应用实践 146 | - [interceptor拦截器--gRPC的Middleware](https://github.com/kevinyan815/gocookbook/issues/60) 147 | - Go服务治理 148 | - [让Go进程监控自己的资源使用情况](https://github.com/kevinyan815/gocookbook/issues/71) 149 | - [Go服务进行自动采样性能分析的方案设计思路](https://github.com/kevinyan815/gocookbook/issues/72) 150 | - [从Go log库到Zap,怎么打造出好用又实用的Logger](https://mp.weixin.qq.com/s/Jh2iFY5uGe0qCFdKZWjotA) 151 | - [分布式服务的日志该怎么串联起来](https://mp.weixin.qq.com/s/M2jNnLkYaearwyRERnt0tA) 152 | - Go 单元测试通关指南 153 | - [go test 工具集和表格测试](https://github.com/kevinyan815/gocookbook/issues/75) 154 | - [模拟网络请求和接口调用](https://github.com/kevinyan815/gocookbook/issues/76) 155 | - [用gock 拦截HTTP请求,Mock API调用](https://github.com/kevinyan815/gocookbook/issues/92) 156 | - [原生数据库查询的 Mock 测试](https://github.com/kevinyan815/gocookbook/issues/77) 157 | - [数据库ORM的Mock测试](https://github.com/kevinyan815/gocookbook/issues/80) 158 | - [Mock接口实现和对接口打桩](https://github.com/kevinyan815/gocookbook/issues/78) 159 | - [全能打桩工具Go Monkey的使用介绍](https://github.com/kevinyan815/gocookbook/issues/79) 160 | - [Go单元测试-Goconvey的使用](https://github.com/kevinyan815/gocookbook/issues/91) 161 | - [如何写出可测试的代码](https://github.com/kevinyan815/gocookbook/issues/81) 162 | - [用单元测试发现协程泄露隐患](https://mp.weixin.qq.com/s/XrhdU95CswLGwS0CLxqZmg) 163 | - [Go 1.18 模糊测试使用教程](https://mp.weixin.qq.com/s/7I0zB_AsltzDLmc9ew48Bg) 164 | -------------------------------------------------------------------------------- /lang-basic/content/chapter5.md: -------------------------------------------------------------------------------- 1 | > 以下内容为几年前学Go语言时记录的,发布在这里让这个项目对更多人能起到参考作用。 更多时下流行技术的应用和实战教程,可通过我公众号「网管叨bi叨」每周的推文来学习 2 | 3 | ### Goroutines 4 | 5 | - 在Go语言中,每一个并发的执行单元叫作goroutine。设想一个程序中有两个函数,假设两个函数没有相互之间的调用关系。一个线性的程序会先调用其中的一个函数,然后再调用另一个。如果程序中包含多个goroutine,对两个函数的调用则可能发生在同一时刻。 6 | - 当一个程序启动时,其main函数即在一个单独的goroutine中运行,我们叫它main goroutine。新的goroutine会用go语句来创建。在语法上,go语句是在一个普通的函数或方法调用前加上关键字go。go语句会使其语句中的函数在一个新创建的goroutine中运行。而go语句本身会迅速地完成。 7 | 8 | ```go 9 | f() // call f(); wait for it to return 10 | go f() // create a new goroutine that calls f(); don't wait 11 | ``` 12 | 13 | - 主函数返回时,所有的goroutine都会被直接打断,程序退出。除了从主函数退出或者直接终止程序之外,没有其它的编程方法能够让一个goroutine来打断另一个的执行,但是之后可以看到一种方式来实现这个目的,通过goroutine之间的通信来让一个goroutine请求其它的goroutine,并被请求的goroutine自行结束执行。 14 | 15 | ### Channel 16 | 17 | - 如果说goroutine是Go语言程序的并发体的话,那么channels它们之间的通信机制。它可以让一个goroutine通过它给另一个goroutine发送值信息。每个channel都有一个特殊的类型,也就是channel可发送数据的类型。一个可以发送int类型数据的channel一般写为chan int。 18 | - 使用内置的make函数,我们可以创建一个channel: 19 | 20 | ```Go 21 | ch := make(chan int) 22 | ``` 23 | 24 | - 和map类似,channel也一个对make函数创建的底层数据结构的引用。当我们复制一个channel或把 channel用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者和被调用者将引用同一个channel对象。和其它的引用类型一样,channel的零值也是nil。 25 | 26 | - channel有发送和接收两个主要操作,都是通信行为。一个发送语句将一个值从一个goroutine通过channel发送到另一个执行接收操作的goroutine。发送和接收两个操作都是用`<-`运算符。在发送语句中,`<-`运算符分割channel和要发送的值。在接收语句中,`<-`运算符写在channel对象之前。一个不使用接收结果的接收操作也是合法的。 27 | 28 | ```Go 29 | ch <- x // 发送消息 30 | x = <-ch // 从 channel 中接收消息 31 | <-ch // 从 channel 接收并丢弃消息 32 | ``` 33 | 34 | - Channel还支持close操作,用于关闭channel,随后对基于该channel的任何发送操作都将导致panic异常。对一个已经被close过的channel执行接收操作依然可以接收到之前已经成功发送的数据;如果channel中已经没有数据的话将产生一个零值的数据。 35 | 36 | 使用内置的close函数就可以关闭一个channel: 37 | 38 | ```Go 39 | close(ch) 40 | ``` 41 | 42 | 以最简单方式调用make函数创建的时一个无缓冲的channel,但是我们也可以指定第二个整形参数,对应channel的容量。如果channel的容量大于零,那么该channel就是带缓冲的channel。 43 | 44 | ```Go 45 | ch = make(chan int) // unbuffered channel 46 | ch = make(chan int, 0) // unbuffered channel 47 | ch = make(chan int, 3) // buffered channel with capacity 3 48 | ``` 49 | 50 | #### 无缓冲 channel 51 | 52 | - 一个基于无缓冲Channel的发送操作将导致发送者goroutine阻塞,直到另一个goroutine在相同的Channel上执行接收操作,当发送的值通过Channel成功传输之后,两个goroutine可以继续执行后面的语句。反之,如果接收操作先发生,那么接收者goroutine也将阻塞,直到有另一个goroutine在相同的Channel上执行发送操作。 53 | 54 | - 下面的程序在 main 函数的 goroutine 中将标准输入复制到server,因此当客户端程序关闭标准输入时,后台goroutine可能依然在工作。我们需要让主goroutine等待后台goroutine完成工作后再退出,我们使用了一个channel来同步两个goroutine,在后台goroutine返回之前,它先打印一个日志信息,然后向done对应的channel发送一个值。主goroutine在退出前先等待从done对应的channel接收一个值。因此,总是可以在程序退出前正确输出“done”消息。 55 | 56 | ```Go 57 | func main() { 58 | conn, err := net.Dial("tcp", "localhost:8000") 59 | if err != nil { 60 | log.Fatal(err) 61 | } 62 | done := make(chan struct{}) 63 | go func() { 64 | io.Copy(os.Stdout, conn) // NOTE: ignoring errors 65 | log.Println("done") 66 | done <- struct{}{} // signal the main goroutine 67 | }() 68 | mustCopy(conn, os.Stdin) 69 | conn.Close() 70 | <-done // wait for background goroutine to finish 71 | } 72 | ``` 73 | 74 | - 基于channel发送消息有两个重要方面。首先每个消息都有一个值,但是有时候通讯的事实和发生的时刻也同样重要。当我们更希望强调通讯发生的时刻时,我们将它称为**消息事件**。有些消息事件并不携带额外的信息,它仅仅是用作两个goroutine之间的同步,这时候我们可以用`struct{}`空结构体作为channels元素的类型,虽然也可以使用bool或int类型实现同样的功能,`done <- 1`语句也比`done <- struct{}{}`更短。 75 | 76 | - 如果发送者知道,没有更多的值需要发送到channel的话,那么让接收者也能及时知道没有多余的值可接收将是有用的,因为接收者可以停止不必要的接收等待。这可以通过内置的close函数来关闭channel实现: 77 | 78 | ```Go 79 | close(naturals) 80 | ``` 81 | 82 | - 当一个channel被关闭后,再向该channel发送数据将导致panic异常。当一个被关闭的channel中已经发送的数据都被成功接收后,后续的接收操作将不再阻塞,它们会立即返回一个零值。 83 | - 接收 channel 语句中可以额外增加第二个值,标识 chnnel 是否已经关闭 84 | 85 | ```Go 86 | x, ok := <-naturals 87 | ``` 88 | 89 | - Go语言的range循环可直接在channels上面迭代。使用range循环是上面处理模式的简洁语法,它依次从channel接收数据,当channel被关闭并且没有值可接收时跳出循环。 90 | 91 | 在下面的程序中,我们的计数器goroutine只生成100个含数字的序列,然后关闭naturals对应的channel,这将导致计算平方数的squarer对应的goroutine可以正常终止循环并关闭squares对应的channel。(在一个更复杂的程序中,可以通过defer语句关闭对应的channel。)最后,主goroutine也可以正常终止循环并退出程序。 92 | 93 | ```Go 94 | func main() { 95 | naturals := make(chan int) 96 | squares := make(chan int) 97 | 98 | // Counter 99 | go func() { 100 | for x := 0; x < 100; x++ { 101 | naturals <- x 102 | } 103 | close(naturals) 104 | }() 105 | 106 | // Squarer 107 | go func() { 108 | for x := range naturals { 109 | squares <- x * x 110 | } 111 | close(squares) 112 | }() 113 | 114 | // Printer (in main goroutine) 115 | for x := range squares { 116 | fmt.Println(x) 117 | } 118 | } 119 | ``` 120 | 121 | - 试图重复关闭一个channel将导致panic异常,试图关闭一个nil值的channel也将导致panic异常。关闭一个channels还会触发一个广播机制,我们将在后面讨论。 122 | 123 | #### 单方向的 channel 124 | 125 | - 当一个channel作为一个函数参数是,它一般总是被专门用于只发送或者只接收。 126 | 127 | 为了表明这种意图并防止被滥用,Go语言的类型系统提供了单方向的channel类型,分别用于只发送或只接收的channel。类型`chan<- int`表示一个只发送int的channel,只能发送不能接收。相反,类型`<-chan int`表示一个只接收int的channel,只能接收不能发送。(箭头`<-`和关键字chan的相对位置表明了channel的方向。)这种限制将在编译期检测。 128 | 129 | - 因为关闭操作只用于断言不再向channel发送新的数据,所以只有在发送者所在的goroutine才会调用close函数,因此对一个只接收的channel调用close将是一个编译错误。 130 | 131 | 这是改进的版本,这一次参数使用了单方向channel类型: 132 | 133 | ```Go 134 | func counter(out chan<- int) { 135 | for x := 0; x < 100; x++ { 136 | out <- x 137 | } 138 | close(out) 139 | } 140 | 141 | func squarer(out chan<- int, in <-chan int) { 142 | for v := range in { 143 | out <- v * v 144 | } 145 | close(out) 146 | } 147 | 148 | func printer(in <-chan int) { 149 | for v := range in { 150 | fmt.Println(v) 151 | } 152 | } 153 | 154 | func main() { 155 | naturals := make(chan int) 156 | squares := make(chan int) 157 | go counter(naturals) 158 | go squarer(squares, naturals) 159 | printer(squares) 160 | } 161 | ``` 162 | 163 | 调用counter(naturals)将导致将`chan int`类型的naturals隐式地转换为`chan<- int`类型只发送型的channel。调用printer(squares)也会导致相似的隐式转换,这一次是转换为`<-chan int`类型只接收型的channel。任何双向channel向单向channel变量的赋值操作都将导致该隐式转换。 164 | 165 | #### 带缓冲的 channel 166 | 167 | 带缓存的Channel内部持有一个元素队列。队列的最大容量是在调用make函数创建channel时通过第二个参数指定的。下面的语句创建了一个可以持有三个字符串元素的带缓存Channel。图8.2是ch变量对应的channel的图形表示形式。 168 | 169 | ```Go 170 | ch = make(chan string, 3) 171 | ``` 172 | 173 | ![img](https://cdn.learnku.com/uploads/images/202001/01/6964/wNKROJvnWk.png!large) 174 | 175 | 向缓存Channel的发送操作就是向内部缓存队列的尾部插入元素,接收操作则是从队列的头部删除元素。如果内部缓存队列是满的,那么发送操作将阻塞直到因另一个goroutine执行接收操作而释放了新的队列空间。相反,如果channel是空的,接收操作将阻塞直到有另一个goroutine执行发送操作而向队列插入元素。 176 | 177 | 我们可以在无阻塞的情况下连续向新创建的channel发送三个值: 178 | 179 | ```Go 180 | ch <- "A" 181 | ch <- "B" 182 | ch <- "C" 183 | ``` 184 | 185 | 此刻,channel的内部缓存队列将是满的(图8.3),如果有第四个发送操作将发生阻塞。 186 | 187 | ![img](https://cdn.learnku.com/uploads/images/202001/01/6964/OWLJMBBjQ7.png!large) 188 | 189 | 如果我们接收一个值, 190 | 191 | ```Go 192 | fmt.Println(<-ch) // "A" 193 | ``` 194 | 195 | 那么channel的缓存队列将不是满的也不是空的(图8.4),因此对该channel执行的发送或接收操作都不会发送阻塞。通过这种方式,channel的缓存队列解耦了接收和发送的goroutine。 196 | 197 | ![img](https://cdn.learnku.com/uploads/images/202001/01/6964/55ocnoXzqc.png!large) 198 | 199 | 在某些特殊情况下,程序可能需要知道channel内部缓存的容量,可以用内置的cap函数获取: 200 | 201 | ```Go 202 | fmt.Println(cap(ch)) // "3" 203 | ``` 204 | 205 | 同样,对于内置的len函数,如果传入的是channel,那么将返回channel内部缓存队列中有效元素的个数。因为在并发程序中该信息会随着接收操作而失效,但是它对某些故障诊断和性能优化会有帮助。 206 | 207 | ```Go 208 | fmt.Println(len(ch)) // "2" 209 | ``` 210 | 211 | 在继续执行两次接收操作后channel内部的缓存队列将又成为空的,如果有第四个接收操作将发生阻塞: 212 | 213 | ```Go 214 | fmt.Println(<-ch) // "B" 215 | fmt.Println(<-ch) // "C" 216 | ``` 217 | 218 | 219 | 220 | 下面的例子展示了一个使用了带缓存channel的应用。它并发地向三个镜像站点发出请求,三个镜像站点分散在不同的地理位置。它们分别将收到的响应发送到带缓存channel,最后接收者只接收第一个收到的响应,也就是最快的那个响应。因此mirroredQuery函数可能在另外两个响应慢的镜像站点响应之前就返回了结果。(顺便说一下,多个goroutines并发地向同一个channel发送数据,或从同一个channel接收数据都是常见的用法。) 221 | 222 | ```Go 223 | func mirroredQuery() string { 224 | responses := make(chan string, 3) 225 | go func() { responses <- request("asia.gopl.io") }() 226 | go func() { responses <- request("europe.gopl.io") }() 227 | go func() { responses <- request("americas.gopl.io") }() 228 | return <-responses // return the quickest response 229 | } 230 | 231 | func request(hostname string) (response string) { /* ... */ } 232 | ``` 233 | 234 | 如果我们使用了无缓存的channel,那么两个慢的goroutines将会因为没有人接收而被永远卡住。这种情况,称为goroutines泄漏,这将是一个BUG。和垃圾变量不同,泄漏的goroutines并不会被自动回收,因此确保每个不再需要的goroutine能正常退出是重要的。 235 | 236 | 关于无缓存或带缓存channel之间的选择,或者是带缓存channel的容量大小的选择,都可能影响程序的正确性。无缓存channel更强地保证了每个发送操作与相应的同步接收操作;但是对于带缓存channel,这些操作是解耦的。同样,即使我们知道将要发送到一个channel的信息的数量上限,创建一个对应容量大小带缓存channel也是不现实的,因为这要求在执行任何接收操作之前缓存所有已经发送的值。如果未能分配足够的缓冲将导致程序死锁。 237 | 238 | #### 用带缓冲的channel 控制并发数量 239 | 240 | 此外对于buffered channel,我们可以用一个有容量限制的buffered channel来控制并发,这类似于操作系统里的计数信号量概念。从概念上讲,channel里的n个空槽代表n个可以处理内容的token(通行证),从channel里接收一个值会释放其中的一个token,并且生成一个新的空槽位。这样保证了在没有接收介入时最多有n个发送操作。(这里可能我们拿channel里填充的槽来做token更直观一些,不过还是这样吧~)。由于channel里的元素类型并不重要,我们用一个零值的struct{}来作为其元素。 241 | 242 | 下面的`crawl`函数,将对`links.Extract`的调用操作用获取、释放token的操作包裹起来,来确保同一时间对其只有20个调用。信号量数量和其能操作的`IO`资源数量应保持接近。 243 | 244 | ```go 245 | // goroutine获取token后,可以进行抓取操作,如果满20了 246 | // 那么 goroutine 会等到有获取 token 后再去执行 247 | var tokens = make(chan struct{}, 20) 248 | 249 | func crawl(url string) []string { 250 | fmt.Println(url) 251 | tokens <- struct{}{} // 获取 token 252 | list, err := links.Extract(url) 253 | <-tokens // 释放 token 254 | if err != nil { 255 | log.Print(err) 256 | } 257 | return list 258 | } 259 | ``` 260 | 261 | ### 并发循环的一个典型示例 262 | 263 | 在并发循环中为了知道最后一个goroutine什么时候结束(最后一个结束并不一定是最后一个开始),我们需要一个递增的计数器,在每一个goroutine启动时加一,在goroutine退出时减一。这需要一种特殊的计数器,这个计数器需要在多个goroutine操作时做到安全并且提供在其减为零之前一直等待的一种方法。这种计数类型被称为sync.WaitGroup,下面的代码就用到了这种方法: 264 | 265 | ```go 266 | // makeThumbnails6为从通道中接收到的每个文件创建缩略图。 267 | // 返回每个创建的缩略图所占的自己数。 268 | func makeThumbnails6(filenames <-chan string) int64 { 269 | sizes := make(chan int64) 270 | var wg sync.WaitGroup // number of working goroutines 271 | for f := range filenames { 272 | wg.Add(1) 273 | // worker 274 | go func(f string) { 275 | defer wg.Done() 276 | thumb, err := thumbnail.ImageFile(f) 277 | if err != nil { 278 | log.Println(err) 279 | return 280 | } 281 | info, _ := os.Stat(thumb) // OK to ignore error 282 | sizes <- info.Size() 283 | }(f) 284 | } 285 | 286 | // closer 287 | go func() { 288 | wg.Wait() 289 | close(sizes) 290 | }() 291 | 292 | var total int64 293 | for size := range sizes { 294 | total += size 295 | } 296 | return total 297 | } 298 | ``` 299 | 300 | 注意Add和Done方法的不对策。Add是为计数器加一,必须在worker goroutine开始之前调用,而不是在goroutine中;否则的话我们没办法确定Add是在"closer" goroutine调用Wait之前被调用。并且Add还有一个参数,但Done却没有任何参数;其实它和Add(-1)是等价的。我们使用defer来确保计数器即使是在出错的情况下依然能够正确地被减掉。上面的程序代码结构是当我们使用并发循环,但又不知道迭代次数时很通常而且很地道的写法。 301 | 302 | ### select多通道复用 303 | 304 | select语句的一般形式,和switch语句稍微有点相似。也会有几个case和最后的default选择支。每一个case代表一个通信操作(在某个channel上进行发送或者接收)并且会包含一些语句组成的一个语句块。 305 | 306 | ```go 307 | select { 308 | case <-ch1: 309 | // ... 310 | case x := <-ch2: 311 | // ...use x... 312 | case ch3 <- y: 313 | // ... 314 | default: 315 | // ... 316 | } 317 | ``` 318 | 319 | 一个接收表达式可能只包含接收表达式自身(译注:不把接收到的值赋值给变量什么的),就像上面的第一个case,或者包含在一个简短的变量声明中,像第二个case里一样;第二种形式让你能够在当前 case 块中引用接收到的值。 320 | 321 | select会等待case中有能够执行的case时去执行。当条件满足时,select才会去通信并执行case之后的语句;这时候其它通信是不会执行的。一个没有任何case的select语句写作select{},会永远地等待下去。 322 | 323 | 下面这个例子更微秒。ch这个channel的buffer大小是1,所以会交替的为空或为满,所以只有一个case可以进行下去,无论i是奇数或者偶数,它都会打印0 2 4 6 8。 324 | 325 | ```go 326 | ch := make(chan int, 1) 327 | for i := 0; i < 10; i++ { 328 | select { 329 | case x := <-ch: 330 | fmt.Println(x) // "0" "2" "4" "6" "8" 331 | case ch <- i: 332 | } 333 | } 334 | ``` 335 | 336 | 如果多个case同时就绪时,select会随机地选择一个执行,这样来保证每一个channel都有平等的被select的机会。增加上面例子的buffer大小会使其输出变得不确定,因为当buffer既不为满也不为空时,select语句的执行情况就像是抛硬币的行为一样是随机的。 337 | -------------------------------------------------------------------------------- /lang-basic/content/chapter4.md: -------------------------------------------------------------------------------- 1 | > 以下内容为几年前学Go语言时记录的,发布在这里让这个项目对更多人能起到参考作用。 更多时下流行技术的应用和实战教程,可通过我公众号「网管叨bi叨」每周的推文来学习 2 | 3 | ### 接口概述 4 | - 一个具体的类型可以准确的描述它所代表的值并且展示出对类型本身的一些操作方式就像数字类型的算术操作,切片类型的索引、附加和取范围操作。总的来说,当你拿到一个具体的类型时你就知道它的本身是什么和你可以用它来做什么。 5 | 6 | - 在Go语言中还存在着另外一种类型:接口类型。接口类型是一种抽象的类型。它不会暴露出它所代表的对象的内部结构和这个对象支持的基础操作的集合;它只会展示出自己的方法。也就是说当你有看到一个接口类型的值时,你不知道它是什么,唯一知道的就是可以通过它的方法来做什么。 7 | 8 | - fmt.Printf它会把结果写到标准输出和fmt.Sprintf它会把结果以字符串的形式返回,实际上,这两个函数都使用了另一个函数fmt.Fprintf来进行封装。fmt.Fprintf这个函数对它的计算结果会被怎么使用是完全不知道的。 9 | 10 | ```go 11 | package fmt 12 | 13 | func Fprintf(w io.Writer, format string, args ...interface{}) (int, error) 14 | func Printf(format string, args ...interface{}) (int, error) { 15 | return Fprintf(os.Stdout, format, args...) 16 | } 17 | func Sprintf(format string, args ...interface{}) string { 18 | var buf bytes.Buffer 19 | Fprintf(&buf, format, args...) 20 | return buf.String() 21 | } 22 | ``` 23 | 24 | ​ Fprintf函数中的第一个参数也不是一个文件类型。它是io.Writer类型这是一个接口类型定义如下: 25 | 26 | ```go 27 | package io 28 | 29 | type Writer interface { 30 | Write(p []byte) (n int, err error) 31 | } 32 | ``` 33 | 34 | io.Writer类型定义了函数Fprintf和这个函数调用者之间的约定,只要是实现了`io.Writer`接口的类型都可以作为 Fprintf 函数的第一个参数。 35 | 36 | - 一个类型可以自由的使用另一个满足相同接口的类型来进行替换被称作可替换性(LSP里氏替换)。这是一个面向对象的特征。 37 | 38 | ### 接口定义 39 | - io.Writer类型是用的最广泛的接口之一,因为它提供了所有的类型写入bytes的抽象,包括文件类型,内存缓冲区,网络链接,HTTP客户端,压缩工具,哈希等等。io包中定义了很多其它有用的接口类型。Reader可以代表任意可以读取bytes的类型,Closer可以是任意可以关闭的值,例如一个文件或是网络链接。 40 | 41 | ```go 42 | package io 43 | type Reader interface { 44 | Read(p []byte) (n int, err error) 45 | } 46 | type Closer interface { 47 | Close() error 48 | } 49 | ``` 50 | 51 | - 可以通过组合已有接口类型来定义新的接口类型,比如 io 包中的 52 | 53 | ```go 54 | type ReadWriter interface { 55 | Reader 56 | Writer 57 | } 58 | type ReadWriteCloser interface { 59 | Reader 60 | Writer 61 | Closer 62 | } 63 | ``` 64 | 65 | 上面用到的语法和结构内嵌相似,我们可以用这种方式命名另一个接口,而不用声明它所有的方法。这种方式称为接口内嵌,我们可以像下面这样,不使用内嵌来声明`io.ReadWriter`接口。 66 | 67 | ```go 68 | type ReadWriter interface { 69 | Read(p []byte) (n int, err error) 70 | Write(p []byte) (n int, err error) 71 | } 72 | ``` 73 | 74 | 或者甚至使用种混合的风格: 75 | 76 | ```go 77 | type ReadWriter interface { 78 | Read(p []byte) (n int, err error) 79 | Writer 80 | } 81 | ``` 82 | 83 | 这三种方式定义的`io.ReadWriter`是完全一样的。 84 | 85 | ### 接口实现 86 | 87 | - 一个类型如果拥有一个接口需要的所有方法,那么这个类型就实现了这个接口。例如,*os.File类型实现了io.Reader,Writer,Closer,和ReadWriter接口。*bytes.Buffer实现了Reader,Writer,和ReadWriter这些接口,但是它没有实现Closer接口因为它不具有Close方法。Go的程序员经常会简要的把一个具体的类型描述成一个特定的接口类型。举个例子,*bytes.Buffer是io.Writer;*os.Files是io.ReadWriter。 88 | - 接口实现的规则非常简单:表达一个类型属于某个接口只要这个类型实现这个接口。 89 | 90 | ```go 91 | var w io.Writer 92 | w = os.Stdout // OK: *os.File has Write method 93 | w = new(bytes.Buffer) // OK: *bytes.Buffer has Write method 94 | w = time.Second // compile error: time.Duration lacks Write method 95 | 96 | var rwc io.ReadWriteCloser 97 | rwc = os.Stdout // OK: *os.File has Read, Write, Close methods 98 | rwc = new(bytes.Buffer) // compile error: *bytes.Buffer lacks Close method 99 | ``` 100 | 101 | - 这个规则甚至适用于等式右边本身也是一个接口类型 102 | 103 | ```go 104 | w = rwc // OK: io.ReadWriteCloser has Write method 105 | rwc = w // compile error: io.Writer lacks Close method 106 | ``` 107 | 108 | - 因为ReadWriter和ReadWriteCloser包含Writer的方法,所以任何实现了ReadWriter和ReadWriteCloser的类型必定也实现了Writer接口 109 | - 对于一些命名的具体类型T;它一些方法的接收者是类型T本身然而另一些则是一个`*T`的指针。在`T`类型的变量上调用一个`*T`的方法是合法的,编译器隐式的获取了它的地址。但这仅仅是一个语法糖:T类型的值不拥有所有*T指针的方法。 110 | - interface{}类型,它没有任何方法,但实际上interface{}被称为空接口类型是不可或缺的。因为空接口类型对实现它的类型没有要求,所以所有类型都实现了interface{},我们可以将任意一个值赋给空接口类型。 111 | 112 | ```go 113 | var any interface{} 114 | any = true 115 | any = 12.34 116 | any = "hello" 117 | any = map[string]int{"one": 1} 118 | any = new(bytes.Buffer) 119 | ``` 120 | 121 | ### 接口值 122 | 123 | - 接口值由两个部分组成,一个具体的类型和那个类型的值。它们被称为接口的动态类型和动态值。 124 | 125 | - 像Go语言这种静态类型的语言,类型是编译期的概念;因此一个类型不是一个值,提供每个类型信息的值被称为类型描述符。 126 | 127 | - 在Go语言中,变量总是被一个定义明确的值初始化,一个接口的零值就是它的类型和值的部分都是nil。 128 | 129 | ![img](https://cdn.learnku.com/uploads/images/201912/28/6964/TQyayLLPRk.png!large) 130 | 131 | - 在你非常确定接口值的动态类型是可比较类型时(比如基本类型)才可以使用`==`和`!=`对两个接口值进行比较。如果两个接口值的动态类型相同,但是这个动态类型是不可比较的(比如切片),将它们进行比较就会失败并且panic: 132 | 133 | ```go 134 | var x interface{} = []int{1, 2, 3} 135 | fmt.Println(x == x) // panic: comparing uncomparable type []int 136 | ``` 137 | 138 | - 下面4个语句中,变量w得到了3个不同的值。(开始和最后的值是相同的) 139 | 140 | ```go 141 | var w io.Writer 142 | w = os.Stdout 143 | w = new(bytes.Buffer) 144 | w = nil 145 | ``` 146 | 147 | 第一个语句定义了变量w: 148 | 149 | ```go 150 | var w io.Writer 151 | ``` 152 | 153 | 在Go语言中,变量总是被一个定义明确的值初始化,即使接口类型也不例外。对于一个接口的零值就是它的类型和值的部分都是nil,如图 7.1。 154 | 155 | 一个接口值基于它的动态类型被描述为空或非空,所以这是一个空的接口值。你可以通过使用w==nil或者w!=nil来判读接口值是否为空。调用一个空接口值上的任意方法都会产生panic: 156 | 157 | ```go 158 | w.Write([]byte("hello")) // panic: nil pointer dereference 159 | ``` 160 | 161 | 第二个语句将一个*os.File类型的值赋给变量w: 162 | 163 | ```go 164 | w = os.Stdout 165 | ``` 166 | 167 | 这个赋值过程调用了一个具体类型到接口类型的隐式转换,这和显式的使用io.Writer(os.Stdout)是等价的。这类转换不管是显式的还是隐式的,都会刻画出操作到的类型和值。这个接口值的动态类型被设为`*os.File`指针的类型描述符(os.Stdout 是指向 os.File 的指针),它的动态值持有os.Stdout的拷贝;这是一个指向处理标准输出的os.File类型变量的指针。 168 | 169 | ![img](https://cdn.learnku.com/uploads/images/201912/28/6964/COeLNISHi3.png!large) 170 | 171 | 调用一个包含`*os.File`类型指针的接口值的Write方法,使得`(*os.File).Write`方法被调用。这个调用输出“hello”。 172 | 173 | ```go 174 | w.Write([]byte("hello")) // "hello" 175 | ``` 176 | 177 | 第三个语句给接口值赋了一个*bytes.Buffer类型的值 178 | 179 | ```go 180 | w = new(bytes.Buffer) 181 | ``` 182 | 183 | 现在动态类型是*bytes.Buffer并且动态值是一个指向新分配的缓冲区的指针(图7.3)。 184 | 185 | ![img](https://cdn.learnku.com/uploads/images/201912/28/6964/jJf6DrVptY.png!large) 186 | 187 | Write方法的调用也使用了和之前一样的机制: 188 | 189 | ```go 190 | w.Write([]byte("hello")) // writes "hello" to the bytes.Buffers 191 | ``` 192 | 193 | 这次类型描述符是`*bytes.Buffer`,所以调用了`(*bytes.Buffer).Write`方法,并且接收者是该缓冲区的地址。这个调用把字符串“hello”添加到缓冲区中。 194 | 195 | 最后,第四个语句将nil赋给了接口值: 196 | 197 | ```go 198 | w = nil 199 | ``` 200 | 201 | 这个重置将它所有的部分都设为nil值,把变量w恢复到和它之前定义时相同的状态图,在图7.1中可以看到。 202 | 203 | ### 一个包含nil指针的接口不是nil接口 204 | 205 | 一个不包含任何值的nil接口值和一个刚好包含nil指针的接口值是不同的。这个细微区别产生了一个容易绊倒每个Go程序员的陷阱。 206 | 207 | 思考下面的程序。当debug变量设置为true时,main函数会将f函数的输出收集到一个bytes.Buffer类型中。 208 | 209 | ```go 210 | const debug = true 211 | 212 | func main() { 213 | var buf *bytes.Buffer 214 | if debug { 215 | buf = new(bytes.Buffer) // enable collection of output 216 | } 217 | f(buf) // NOTE: subtly incorrect! 218 | if debug { 219 | // ...use buf... 220 | } 221 | } 222 | 223 | // If out is non-nil, output will be written to it. 224 | func f(out io.Writer) { 225 | // ...do something... 226 | if out != nil { 227 | out.Write([]byte("done!\n")) 228 | } 229 | } 230 | ``` 231 | 232 | 我们可能会预计当把变量debug设置为false时可以禁止对输出的收集,但是实际上在out.Write方法调用时程序发生了panic: 233 | 234 | ```go 235 | if out != nil { 236 | out.Write([]byte("done!\n")) // panic: nil pointer dereference 237 | } 238 | ``` 239 | 240 | 当main函数调用函数f时,它给f函数的out参数赋了一个`*bytes.Buffer`的空指针,所以out的动值是nil。然而,它的动态类型是`*bytes.Buffer`,意思就是out变量是一个包含空指针值的非空接口(如图7.5),所以防御性检查out!=nil的结果依然是true。 241 | 242 | ![img](https://cdn.learnku.com/uploads/images/201912/28/6964/WlEzgXBzjA.png!large) 243 | 244 | 动态分配机制依然决定`(*bytes.Buffer).Write`的方法会被调用,但是这次的接收者的值是nil。对于一些如`*os.File`的类型,nil是一个有效的接收者(§6.2.1),但是`*bytes.Buffer`类型不在这些类型中。这个方法会被调用,但是当它尝试去获取缓冲区时会发生panic。 245 | 246 | 问题在于尽管一个nil的`*bytes.Buffer`指针有实现这个接口的方法,它也不满足这个接口具体的行为上的要求。特别是这个调用违反了`(*bytes.Buffer).Write`方法的接收者非空的隐含先觉条件,所以将nil指针赋给这个接口是错误的。解决方案就是将main函数中的变量buf声明的类型改为io.Writer,(它的零值动态类型和动态值都为 nil)因此可以避免一开始就将一个不完全的值赋值给这个接口: 247 | 248 | ```go 249 | var buf io.Writer 250 | if debug { 251 | buf = new(bytes.Buffer) // enable collection of output 252 | } 253 | f(buf) // OK 254 | ``` 255 | 256 | 257 | 258 | ### error 接口 259 | 260 | - 预定义的error类型实际上就是interface类型,这个类型有一个返回错误信息的单一方法: 261 | 262 | ```go 263 | type error interface { 264 | Error() string 265 | } 266 | ``` 267 | 268 | - 创建一个error最简单的方法就是调用errors.New函数,它会根据传入的错误信息返回一个新的error。整个errors包仅只有4行: 269 | 270 | ```go 271 | package errors 272 | 273 | func New(text string) error { return &errorString{text} } 274 | 275 | type errorString struct { text string } 276 | 277 | func (e *errorString) Error() string { return e.text } 278 | ``` 279 | 280 | 每个New函数的调用都分配了一个独特的和其他错误不相同的实例。我们也不想要重要的`error`例如`io.EOF`和一个刚好有相同错误消息的`error`比较后相等。 281 | 282 | ```go 283 | fmt.Println(errors.New("EOF") == errors.New("EOF")) // "false" 284 | ``` 285 | 286 | 调用`errors.New`函数是非常稀少的,因为有一个方便的封装函数`fmt.Errorf`,它还会处理字符串格式化。 287 | 288 | ```go 289 | package fmt 290 | 291 | import "errors" 292 | 293 | func Errorf(format string, args ...interface{}) error { 294 | return errors.New(Sprintf(format, args...)) 295 | } 296 | ``` 297 | 298 | ### 类型断言 299 | 300 | - 类型断言是一个使用在接口值上的操作。语法上它看起来像x.(T)被称为断言类型。这里x表示一个接口值,T表示一个类型(接口类型或者具体类型)。一个类型断言会检查操作对象的动态类型是否和断言类型匹配。 301 | 302 | - x.(T)中如果断言的类型T是一个具体类型,类型断言检查x的动态类型是否和T相同。如果是,类型断言的结果是x的动态值,当然它的类型是T。换句话说,具体类型的类型断言从它的操作对象中获得具体的值。如果x 的动态类型与 T 不相同,会抛出panic。 303 | 304 | ```go 305 | var w io.Writer 306 | w = os.Stdout 307 | f := w.(*os.File) // success: f == os.Stdout 308 | c := w.(*bytes.Buffer) // panic: interface holds *os.File, not *bytes.Buffer 309 | ``` 310 | 311 | - 相反断言的类型T是一个接口类型,然后类型断言检查是否x的动态类型满足T。如果这个检查成功了,这个结果仍然是一个有相同类型和值部分的接口值,但是结果接口值的动态类型为T。换句话说,对一个接口类型的类型断言改变了类型的表述方式,改变了可以获取的方法集合(通常更大),但是它保护了接口值内部的动态类型和值的部分。 312 | 313 | - 在下面的第一个类型断言后,w和rw都持有os.Stdout因为它们每个值的动态类型都是`*os.File`,但是变量的类型是io.Writer只对外公开出文件的Write方法,变量rw的类型为 io.ReadWriter,只对外公开文件的Read方法。 314 | 315 | ```go 316 | var w io.Writer 317 | w = os.Stdout 318 | rw := w.(io.ReadWriter) // success: *os.File has both Read and Write 319 | w = new(ByteCounter) 320 | rw = w.(io.ReadWriter) // panic: *ByteCounter has no Read method 321 | ``` 322 | 323 | - 如果断言操作的对象是一个nil接口值,那么不论被断言的类型是什么这个类型断言都会失败。 324 | - 经常地我们对一个接口值的动态类型是不确定的,并且我们更愿意去检验它是否是一些特定的类型。如果类型断言出现在一个有两个结果的赋值表达式中,例如如下的定义,这个类型断言不会在失败的时候发生panic,代替地返回的第二个返回值是一个标识类型断言是否成功的布尔值: 325 | 326 | ```go 327 | var w io.Writer = os.Stdout 328 | f, ok := w.(*os.File) // success: ok, f == os.Stdout 329 | b, ok := w.(*bytes.Buffer) // failure: !ok, b == nil 330 | ``` 331 | 332 | ### type switch 333 | 334 | 接口被以两种不同的方式使用。在第一个方式中,以io.Reader,io.Writer,fmt.Stringer,sort.Interface,http.Handler,和error为典型,一个接口的方法表达了实现这个接口的具体类型间的相似性,但是隐藏了代表的细节和这些具体类型本身的操作。重点在于方法上,而不是具体的类型上。 335 | 336 | 第二个方式利用一个接口值可以持有各种具体类型值的能力并且将这个接口认为是这些类型的union(联合)。类型断言用来动态地区别这些类型。在这个方式中,重点在于具体的类型满足这个接口,而不是在于接口的方法(如果它确实有一些的话),并且没有任何的信息隐藏。我们将以这种方式使用的接口描述为discriminated unions(可辨识联合)。 337 | 338 | 一个类型开关像普通的switch语句一样,它的运算对象是x.(type)-它使用了关键词字面量type-并且每个case有一到多个类型。一个类型开关基于这个接口值的动态类型使一个多路分支有效。这个nil的case和if x == nil匹配,并且这个default的case和如果其它case都不匹配的情况匹配。一个对sqlQuote的类型开关可能会有这些case 339 | 340 | ```go 341 | switch x.(type) { 342 | case nil: // ... 343 | case int, uint: // ... 344 | case bool: // ... 345 | case string: // ... 346 | default: // ... 347 | } 348 | ``` 349 | 350 | 类型开关语句有一个扩展的形式,它可以将提取的值绑定到一个在每个case范围内的新变量上。 351 | 352 | ```go 353 | switch x := x.(type) { /* ... */ } 354 | ``` 355 | 356 | 使用类型开关的扩展形式来重写sqlQuote函数会让这个函数更加的清晰: 357 | 358 | ```go 359 | func sqlQuote(x interface{}) string { 360 | switch x := x.(type) { 361 | case nil: 362 | return "NULL" 363 | case int, uint: 364 | return fmt.Sprintf("%d", x) // x has type interface{} here. 365 | case bool: 366 | if x { 367 | return "TRUE" 368 | } 369 | return "FALSE" 370 | case string: 371 | return sqlQuoteString(x) // (not shown) 372 | default: 373 | panic(fmt.Sprintf("unexpected type %T: %v", x, x)) 374 | } 375 | } 376 | ``` 377 | 378 | 尽管sqlQuote接受一个任意类型的参数,但是这个函数只会在它的参数匹配类型开关中的一个case时运行到结束;其它情况的它会panic出“unexpected type”消息。虽然x的类型是interface{},但是我们把它认为是一个int,uint,bool,string,和nil值的discriminated union(可识别联合) 379 | 380 | ### 使用建议 381 | 382 | - 接口只有当有两个或两个以上的具体类型必须以相同的方式进行处理时才需要。 383 | - 当一个接口只被一个单一的具体类型实现时有一个例外,就是由于它的依赖,这个具体类型不能和这个接口存在在一个相同的包中。这种情况下,一个接口是解耦这两个包的一个好的方式。 384 | 385 | - 因为在Go语言中只有当两个或更多的类型须以相同的方式进行处理时才有必要使用接口,它们必定会从任意特定的实现细节中抽象出来。结果就是有更少和更简单方法(经常和io.Writer或 fmt.Stringer一样只有一个)的更小的接口。当新的类型出现时,小的接口更容易满足。对于接口设计的一个好的标准就是 ask only for what you need(只考虑你需要的东西)。 386 | -------------------------------------------------------------------------------- /lang-basic/content/chapter2.md: -------------------------------------------------------------------------------- 1 | > 以下内容为几年前学Go语言时记录的,发布在这里让这个项目对更多人能起到参考作用。 2 | 更多时下流行技术的应用和实战教程,可通过我公众号「网管叨bi叨」每周的推文来学习 3 | 4 | ## 函数 5 | 6 | ### 函数声明 7 | 8 | - 函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。 9 | 10 | ```Go 11 | func name(parameter-list) (result-list) { 12 | body 13 | } 14 | ``` 15 | 形式参数列表描述了函数的参数名以及参数类型。这些参数作为局部变量,其值由参数调用者提供。返回值也可以像形式参数一样被命名,在这种情况下,每个返回值被声明成一个局部变量,并初始化为其类型的零值。 16 | 17 | - 用 _ 符号作为形参名可以强调某个参数未被使用。 18 | 19 | ```Go 20 | func first(x int, _ int) int { return x } 21 | ``` 22 | 23 | - 函数的类型被称为函数的标识符。如果两个函数形式参数列表和返回值列表中的变量类型一一对应,那么这两个函数被认为有相同的类型和标识符。 24 | - 在函数调用时,Go语言没有默认参数值,也没有任何方法可以通过参数名指定形参,因此形参和返回值的变量名对于函数调用者而言没有意义。 25 | - 实参通过值的方式传递,因此函数的形参是实参的拷贝。对形参进行修改不会影响实参。但是,如果实参包括引用类型,如指针,slice(切片)、map、function、channel等类型,实参可能会由于函数的引用而被修改。 26 | - golang.org/x/... 目录下存储了一些由Go团队设计、维护,对网络编程、国际化文件处理、移动平台、图像处理、加密解密、开发者工具提供支持的扩展包。未将这些扩展包加入到标准库原因有二,一是部分包仍在开发中,二是对大多数Go语言的开发者而言,扩展包提供的功能很少被使用。 27 | 28 | ### 递归调用 29 | 30 | - 大部分编程语言使用固定大小的函数调用栈,常见的大小从64KB到2MB不等。固定大小栈会限制递归的深度,当你用递归处理大量数据时,需要避免栈溢出;除此之外,还会导致安全性问题。与相反,Go语言使用可变栈,栈的大小按需增加(初始时很小)。这使得我们使用递归时不必考虑溢出和安全问题 31 | - 虽然Go的垃圾回收机制会回收不被使用的内存,但是这不包括操作系统层面的资源,比如打开的文件、网络连接。因此我们必须显式的释放这些资源。 32 | 33 | ### 多返回值函数 34 | 35 | - 调用多返回值函数时,返回给调用者的是一组值,调用者必须显式的将这些值分配给变量: 36 | 37 | ```Go 38 | links, err := findLinks(url) 39 | ``` 40 | 41 | 如果某个值不被使用,可以将其分配给blank identifier: 42 | 43 | ```Go 44 | links, _ := findLinks(url) // errors ignored 45 | ``` 46 | 47 | - 如果一个函数将所有的返回值都显示的变量名,那么该函数的return语句可以省略操作数。这称之为bare return。 48 | 49 | ```Go 50 | // CountWordsAndImages does an HTTP GET request for the HTML 51 | // document url and returns the number of words and images in it. 52 | func CountWordsAndImages(url string) (words, images int, err error) { 53 | resp, err := http.Get(url) 54 | if err != nil { 55 | return 56 | } 57 | doc, err := html.Parse(resp.Body) 58 | resp.Body.Close() 59 | if err != nil { 60 | err = fmt.Errorf("parsing HTML: %s", err) 61 | return 62 | } 63 | words, images = countWordsAndImages(doc) 64 | return 65 | } 66 | func countWordsAndImages(n *html.Node) (words, images int) { /* ... */ } 67 | ``` 68 | 69 | 按照函数声明中返回值列表的次序,返回所有的返回值,在上面的例子中,每一个return语句等价于: 70 | 71 | ```Go 72 | return words, images, err 73 | ``` 74 | 75 | - 当一个函数有多处return语句以及许多返回值时,bare return 可以减少代码的重复,但是使得代码难以被理解。如果你没有仔细的审查上面的代码,很难发现前2处return等价于 `return 0,0,err`(Go会将返回值 words和images在函数体的开始处,根据它们的类型,将其初始化为0),最后一处return等价于 `return words,image,nil`。基于以上原因,不宜过度使用bare return。 76 | 77 | ### 错误 78 | 79 | - 在Go的错误处理中,错误是软件包API和应用程序用户界面的一个重要组成部分,程序运行失败仅被认为是几个预期的结果之一。 80 | 81 | - 对于那些将运行失败看作是预期结果的函数,它们会返回一个额外的返回值,通常是最后一个,来传递错误信息。 82 | 83 | ```go 84 | resp, err := http.Get(url) 85 | ``` 86 | 87 | - 内置的error是接口类型。nil意味着函数运行成功,non-nil表示失败。对于non-nil的error类型,我们可以通过调用error的`Error`函数或者输出函数获得字符串类型的错误信息。 88 | 89 | ```Go 90 | fmt.Println(err) 91 | fmt.Printf("%v", err) 92 | ``` 93 | 94 | ### 函数值 95 | 96 | - 在Go中,函数被看作第一类值(first-class values):函数像其他值一样,拥有类型,可以被赋值给其他变量,传递给函数,从函数返回。对函数值(function value)的调用类似函数调用。例子如下: 97 | 98 | ```Go 99 | func square(n int) int { return n * n } 100 | func negative(n int) int { return -n } 101 | func product(m, n int) int { return m * n } 102 | 103 | f := square 104 | fmt.Println(f(3)) // "9" 105 | 106 | f = negative 107 | fmt.Println(f(3)) // "-3" 108 | fmt.Printf("%T\n", f) // "func(int) int" 109 | 110 | f = product // compile error: can't assign func(int, int) int to func(int) int 111 | ``` 112 | 113 | - 函数类型的零值是nil。调用值为nil的函数值会引起panic错误: 114 | 115 | ```Go 116 | var f func(int) int 117 | f(3) // 此处f的值为nil, 会引起panic错误 118 | ``` 119 | 120 | - 函数值可以与nil比较: 121 | 122 | ```Go 123 | var f func(int) int 124 | if f != nil { 125 | f(3) 126 | } 127 | ``` 128 | 129 | 但是函数值之间是不可比较的,也不能用函数值作为map的key。 130 | 131 | 132 | 133 | ### 匿名函数 134 | 135 | - 拥有函数名的函数只能在包级语法块中被声明,通过函数字面量(function literal),我们可绕过这一限制,在任何表达式中表示一个函数值。函数字面量的语法和函数声明相似,区别在于func关键字后没有函数名。函数值字面量是一种表达式,它的值被称为匿名函数(anonymous function)。 136 | 137 | 函数字面量允许我们在使用函数时,再定义它。通过这种技巧,我们可以改写之前对strings.Map的调用: 138 | 139 | ```Go 140 | strings.Map(func(r rune) rune { return r + 1 }, "HAL-9000") 141 | ``` 142 | 143 | 更为重要的是,通过这种方式定义的函数可以访问完整的词法环境(lexical environment),这意味着在函数中定义的内部函数可以引用该函数的变量。 144 | 145 | ```Go 146 | // squares返回一个匿名函数。 147 | // 该匿名函数每次被调用时都会返回下一个数的平方。 148 | func squares() func() int { 149 | var x int 150 | return func() int { 151 | x++ 152 | return x * x 153 | } 154 | } 155 | ``` 156 | 157 | 通过这个例子,我们看到变量的生命周期不由它的作用域决定:squares返回后,变量x仍然隐式的存在于f中。 158 | 159 | - 当匿名函数需要被递归调用时,我们必须首先声明一个变量,再将匿名函数赋值给这个变量。如果不分成两步,函数字面量无法与变量绑定,我们也无法递归调用该匿名函数,比如: 160 | 161 | ```Go 162 | var visitAll func(items []string) 163 | visitAll = func(items []string) { 164 | ...... 165 | visitAll(m[item]) 166 | ...... 167 | } 168 | 169 | ``` 170 | 否则会出现编译错误 171 | ```Go 172 | visitAll := func(items []string) { 173 | // ... 174 | visitAll(m[item]) // compile error: undefined: visitAll 175 | // ... 176 | } 177 | ``` 178 | 179 | ### 可变参数 180 | 181 | - 参数数量可变的函数称为为可变参数函数。典型的例子就是fmt.Printf和类似函数。Printf首先接收一个的必备参数,之后接收任意个数的后续参数。 182 | 183 | 在声明可变参数函数时,需要在参数列表的最后一个参数类型之前加上省略符号“...”,这表示该函数会接收任意数量的该类型参数。 184 | 185 | ```Go 186 | func sum(vals...int) int { 187 | total := 0 188 | for _, val := range vals { 189 | total += val 190 | } 191 | return total 192 | } 193 | ``` 194 | 195 | sum函数返回任意个int型参数的和。在函数体中,vals被看作是类型为`[] int`的切片。sum可以接收任意数量的int型参数: 196 | 197 | ```Go 198 | fmt.Println(sum()) // "0" 199 | fmt.Println(sum(3)) // "3" 200 | fmt.Println(sum(1, 2, 3, 4)) // "10" 201 | ``` 202 | 203 | - 在上面的代码中,调用者隐式的创建一个数组,并将原始参数复制到数组中,再把数组的一个切片作为参数传给被调函数。如果原始参数已经是切片类型,我们该如何传递给sum?只需在最后一个参数后加上省略符。下面的代码功能与上个例子中最后一条语句相同。 204 | 205 | ```Go 206 | values := []int{1, 2, 3, 4} 207 | fmt.Println(sum(values...)) // "10" 208 | // fmt.Println(sum(1, 2, 3, 4)) 209 | ``` 210 | 211 | - 虽然在可变参数函数内部,`...int` 型参数的行为看起来很像切片类型,但实际上,可变参数函数和以切片作为参数的函数是不同的。 212 | 213 | ```Go 214 | func f(...int) {} 215 | func g([]int) {} 216 | fmt.Printf("%T\n", f) // "func(...int)" 217 | fmt.Printf("%T\n", g) // "func([]int)" 218 | ``` 219 | 220 | - 可变参数函数经常被用于格式化字符串。下面的errorf函数构造了一个以行号开头的,经过格式化的错误信息。函数名的后缀f是一种通用的命名规范,代表该可变参数函数可以接收Printf风格的格式化字符串。 221 | 222 | ```Go 223 | func errorf(linenum int, format string, args ...interface{}) { 224 | fmt.Fprintf(os.Stderr, "Line %d: ", linenum) 225 | fmt.Fprintf(os.Stderr, format, args...) 226 | fmt.Fprintln(os.Stderr) 227 | } 228 | linenum, name := 12, "count" 229 | errorf(linenum, "undefined: %s", name) // "Line 12: undefined: count" 230 | ``` 231 | 232 | `...interfac{}`表示函数在`format`参数后可以接收任意个任意类型的参数。`interface{}`会在后面介绍。 233 | 234 | 235 | 236 | ### Deferred 函数 237 | 238 | - 你只需要在调用普通函数或方法前加上关键字defer,就完成了defer所需要的语法。当defer语句被执行时,跟在defer后面的函数会被延迟执行。直到包含该defer语句的函数执行完毕时,defer后的函数才会被执行,不论包含defer语句的函数是通过return正常结束,还是由于panic导致的异常结束。你可以在一个函数中执行多条defer语句,它们的执行顺序与声明顺序相反。 239 | 240 | - defer语句经常被用于处理成对的操作,如打开、关闭、连接、断开连接、加锁、释放锁。通过defer机制,不论函数逻辑多复杂,都能保证在任何执行路径下,资源被释放。释放资源的defer应该直接跟在请求资源的语句后。 241 | 242 | - 对文件的操作 243 | 244 | ```Go 245 | package ioutil 246 | func ReadFile(filename string) ([]byte, error) { 247 | f, err := os.Open(filename) 248 | if err != nil { 249 | return nil, err 250 | } 251 | defer f.Close() 252 | return ReadAll(f) 253 | } 254 | ``` 255 | 256 | - 处理互斥锁 257 | 258 | ```Go 259 | var mu sync.Mutex 260 | var m = make(map[string]int) 261 | func lookup(key string) int { 262 | mu.Lock() 263 | defer mu.Unlock() 264 | return m[key] 265 | } 266 | ``` 267 | 268 | - 调试复杂程序时,defer机制也常被用于记录何时进入和退出函数。下例中的bigSlowOperation函数,直接调用trace记录函数的被调情况。bigSlowOperation被调时,trace会返回一个函数值,该函数值会在bigSlowOperation退出时被调用。通过这种方式, 我们可以只通过一条语句控制函数的入口和所有的出口,甚至可以记录函数的运行时间,如例子中的start。需要注意一点:不要忘记defer语句后的圆括号,否则本该在进入时执行的操作会在退出时执行,而本该在退出时执行的,永远不会被执行。 269 | 270 | *gopl.io/ch5/trace* 271 | 272 | ```Go 273 | func bigSlowOperation() { 274 | defer trace("bigSlowOperation")() // don't forget the extra parentheses 275 | // ...lots of work… 276 | time.Sleep(10 * time.Second) // simulate slow 277 | operation by sleeping 278 | } 279 | func trace(msg string) func() { 280 | start := time.Now() 281 | log.Printf("enter %s", msg) 282 | return func() { 283 | log.Printf("exit %s (%s)", msg,time.Since(start)) 284 | } 285 | } 286 | ``` 287 | 288 | 每一次bigSlowOperation被调用,程序都会记录函数的进入,退出,持续时间。(我们用time.Sleep模拟一个耗时的操作) 289 | 290 | ``` 291 | $ go build gopl.io/ch5/trace 292 | $ ./trace 293 | 2015/11/18 09:53:26 enter bigSlowOperation 294 | 2015/11/18 09:53:36 exit bigSlowOperation (10.000589217s) 295 | ``` 296 | 297 | - 用 defer 函数记录返回值(需要是命名返回值才能记录) 298 | 299 | ```Go 300 | func double(x int) (result int) { 301 | defer func() { fmt.Printf("double(%d) = %d\n", x,result) }() 302 | return x + x 303 | } 304 | _ = double(4) 305 | // Output: 306 | // "double(4) = 8" 307 | ``` 308 | 309 | - 被延迟执行的匿名函数甚至可以修改函数返回给调用者的返回值: 310 | 311 | ```Go 312 | func triple(x int) (result int) { 313 | defer func() { result += x }() 314 | return double(x) 315 | } 316 | fmt.Println(triple(4)) // "12" 317 | ``` 318 | 319 | - 在循环体中的defer语句需要特别注意,因为只有在函数执行完毕后,这些被延迟的函数才会执行。下面的代码会导致系统的文件描述符耗尽,因为在所有文件都被处理之前,没有文件会被关闭。 320 | 321 | ```Go 322 | for _, filename := range filenames { 323 | f, err := os.Open(filename) 324 | if err != nil { 325 | return err 326 | } 327 | defer f.Close() // NOTE: risky; could run out of file 328 | descriptors 329 | // ...process f… 330 | } 331 | ``` 332 | 333 | 一种解决方法是将循环体中的文件操作和defer语句移至另外一个函数。在每次循环时,调用这个函数。 334 | 335 | ```Go 336 | for _, filename := range filenames { 337 | if err := doFile(filename); err != nil { 338 | return err 339 | } 340 | } 341 | func doFile(filename string) error { 342 | f, err := os.Open(filename) 343 | if err != nil { 344 | return err 345 | } 346 | defer f.Close() 347 | // ...process f… 348 | } 349 | ``` 350 | 351 | ### Panic 和 Recover 352 | 353 | - Go的类型系统会在编译时捕获很多错误,但有些错误只能在运行时检查,如数组访问越界、空指针引用等。这些运行时错误会引起painc异常。 354 | 355 | - 当panic异常发生时,程序会中断运行,并立即执行在该goroutine(可以先理解成线程,在第8章会详细介绍)中被延迟的函数(defer 机制)。随后,程序崩溃并输出日志信息。日志信息包括panic value和函数调用的堆栈跟踪信息。 356 | 357 | - 虽然Go的panic机制类似于其他语言的异常,但panic的适用场景有一些不同。由于panic会引起程序的崩溃,因此panic一般用于严重错误,如程序内部的逻辑不一致。 358 | 359 | - 通常来说,不应该对panic异常做任何处理,但有时,也许我们可以从异常中恢复,至少我们可以在程序崩溃前,做一些操作。举个例子,当web服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭;如果不做任何处理,会使得客户端一直处于等待状态。 360 | 361 | - 如果在deferred函数中调用了内置函数recover,并且定义该defer语句的函数发生了panic异常,recover会使程序从panic中恢复,并返回panic value。导致panic异常的函数不会继续运行,但能正常返回。在未发生panic时调用recover,recover会返回nil。 362 | 363 | - 例子中deferred函数帮助Parse从panic中恢复。在deferred函数内部,panic value被附加到错误信息中;并用err变量接收错误信息,返回给调用者。 364 | 365 | ```Go 366 | func Parse(input string) (s *Syntax, err error) { 367 | defer func() { 368 | if p := recover(); p != nil { 369 | err = fmt.Errorf("internal error: %v", p) 370 | } 371 | }() 372 | // ...parser... 373 | } 374 | ``` 375 | 376 | - 不加区分的恢复所有的panic异常,不是可取的做法。 377 | 378 | - 只恢复应该被恢复的panic异常,此外,这些异常所占的比例应该尽可能的低。为了标识某个panic是否应该被恢复,我们可以将panic value设置成特殊类型。在recover时对panic value进行检查,如果发现panic value是特殊类型,就将这个panic作为errror处理,如果不是,则按照正常的panic进行处理 379 | 380 | ```Go 381 | func soleTitle(doc *html.Node) (title string, err error) { 382 | type bailout struct{} 383 | defer func() { 384 | switch p := recover(); p { 385 | case nil: // no panic 386 | case bailout{}: // "expected" panic 387 | err = fmt.Errorf("multiple title elements") 388 | default: 389 | panic(p) 390 | } 391 | }() 392 | forEachNode(doc, func(n *html.Node) { 393 | if n.Type == html.ElementNode && n.Data == "title" && 394 | n.FirstChild != nil { 395 | if title != "" { 396 | panic(bailout{}) // multiple titleelements 397 | } 398 | title = n.FirstChild.Data 399 | } 400 | }, nil) 401 | if title == "" { 402 | return "", fmt.Errorf("no title element") 403 | } 404 | return title, nil 405 | } 406 | ``` 407 | -------------------------------------------------------------------------------- /codes/distributed_lock/go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 4 | github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 5 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= 6 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 7 | github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= 8 | github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= 9 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 10 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 11 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 12 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 13 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 14 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 15 | github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 16 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 17 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 18 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 19 | github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= 20 | github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= 21 | github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 22 | github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= 23 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 24 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 25 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 26 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 27 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 28 | github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= 29 | github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 30 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 31 | github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= 32 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 33 | github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 34 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 35 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 36 | github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= 37 | github.com/coreos/etcd v3.3.25+incompatible h1:0GQEw6h3YnuOVdtwygkIfJ+Omx0tZ8/QkVyXI4LkbeY= 38 | github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 39 | github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY= 40 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 41 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= 42 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 43 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 44 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= 45 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 46 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 47 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 48 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 49 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 50 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 51 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= 52 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 53 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs= 54 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 55 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 56 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 57 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 58 | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 59 | github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= 60 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 61 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 62 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 63 | github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= 64 | github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= 65 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 66 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 67 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 68 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 69 | github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= 70 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 71 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 72 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 73 | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 74 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 75 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= 76 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 77 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 78 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= 79 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 80 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 81 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 82 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 83 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 84 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 85 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 86 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 87 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 88 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 89 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 90 | github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= 91 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 92 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 93 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 94 | github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= 95 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 96 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 97 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 98 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 99 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 100 | github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= 101 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 102 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 103 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 104 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 105 | github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= 106 | github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 107 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 108 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 109 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 110 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 111 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c h1:Lh2aW+HnU2Nbe1gqD9SOJLJxW1jBMmQOktN2acDyJk8= 112 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 113 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= 114 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 115 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= 116 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 117 | github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= 118 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 119 | github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= 120 | github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 121 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 122 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 123 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 124 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 125 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 126 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 127 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 128 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 129 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 130 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 131 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 132 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 133 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 134 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 135 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 136 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 137 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 138 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 139 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 140 | github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= 141 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 142 | github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= 143 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 144 | github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= 145 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 146 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 147 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 148 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 149 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 150 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= 151 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 152 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 153 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 154 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 155 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 156 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 157 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 158 | github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= 159 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 160 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 161 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 162 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 163 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 164 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 165 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 166 | github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= 167 | github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= 168 | github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 169 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 170 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 171 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 172 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 173 | github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= 174 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 175 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 176 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 177 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 178 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 179 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 180 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 181 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 182 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 183 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 184 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 185 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 186 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 187 | github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= 188 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 189 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 190 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 191 | github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= 192 | github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= 193 | github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= 194 | github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= 195 | github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 196 | github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 197 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 198 | github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= 199 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 200 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 201 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 202 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 203 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 204 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 205 | github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= 206 | github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= 207 | github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 208 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 209 | github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= 210 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= 211 | github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 212 | github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 213 | github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= 214 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 215 | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= 216 | github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= 217 | github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= 218 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 219 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 220 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 221 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 222 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 223 | github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 224 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 225 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 226 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 227 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 228 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= 229 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 230 | github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 231 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 232 | github.com/prometheus/client_golang v1.10.0 h1:/o0BDeWzLWXNZ+4q5gXltUvaMpJqckTa+jTNoB+z4cg= 233 | github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= 234 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 235 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 236 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 237 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 238 | github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 239 | github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= 240 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 241 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 242 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 243 | github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 244 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 245 | github.com/prometheus/common v0.18.0 h1:WCVKW7aL6LEe1uryfI9dnEc2ZqNB1Fn0ok930v0iL1Y= 246 | github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= 247 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 248 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 249 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 250 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 251 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 252 | github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= 253 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 254 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 255 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 256 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 257 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 258 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 259 | github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= 260 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 261 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 262 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 263 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 264 | github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= 265 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 266 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 267 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 268 | github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= 269 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 270 | github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= 271 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 272 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 273 | github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 274 | github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 275 | github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 276 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 277 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 278 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 279 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 280 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 281 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 282 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 283 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8 h1:ndzgwNDnKIqyCvHTXaCqh9KlOWKvBry6nuXMJmonVsE= 284 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 285 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 286 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 287 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= 288 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 289 | go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= 290 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 291 | go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= 292 | go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= 293 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= 294 | go.etcd.io/etcd v3.3.25+incompatible h1:V1RzkZJj9LqsJRy+TUBgpWSbZXITLB819lstuTFoZOY= 295 | go.etcd.io/etcd v3.3.25+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= 296 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 297 | go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 298 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 299 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 300 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 301 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 302 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 303 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 304 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 305 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= 306 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 307 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 308 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 309 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 310 | go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= 311 | go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 312 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 313 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 314 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 315 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 316 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 317 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 318 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 319 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= 320 | golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= 321 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 322 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 323 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 324 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 325 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 326 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 327 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 328 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 329 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 330 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 331 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 332 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 333 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 334 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 335 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 336 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 337 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 338 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 339 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 340 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 341 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 342 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 343 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 344 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 345 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 346 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= 347 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 348 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 349 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 350 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 351 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 352 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 353 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 354 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 355 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 356 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 357 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 358 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 359 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 360 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 361 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 362 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 363 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 364 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 365 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 366 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 367 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 368 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 369 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 370 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 371 | golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 375 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 377 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 378 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 379 | golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY= 380 | golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 381 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 382 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 383 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 384 | golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= 385 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 386 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 387 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= 388 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 389 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 390 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 391 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 392 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 393 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 394 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 395 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 396 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 397 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 398 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 399 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 400 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 401 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 402 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 403 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 404 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= 405 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 406 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 407 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 408 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 409 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 410 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 411 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 412 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 413 | google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 414 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= 415 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 416 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 417 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 418 | google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= 419 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 420 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 421 | google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 422 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 423 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 424 | google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= 425 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 426 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 427 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 428 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 429 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 430 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 431 | google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= 432 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 433 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 434 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 435 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 436 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 437 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 438 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 439 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 440 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 441 | gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 442 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 443 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 444 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 445 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 446 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 447 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 448 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 449 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 450 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 451 | gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= 452 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 453 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 454 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 455 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 456 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 457 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 458 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 459 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 460 | sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= 461 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 462 | sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= 463 | --------------------------------------------------------------------------------