[TOC] ## 保持 go 自持运行 1. 监听一个端口 添加一个 端口监听 并且设置一个借口为 `/ping` 返回 pong 这样既可以保持运行.又可以检测是否运行 ``` http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) { io.WriteString(w,"pong") }) http.ListenAndServe(":9193",nil) ``` ## 遍历一个chan 通过一个 for 循环(不推荐) ``` for k,v :=range chan_anme{ //code } ``` 应该使用 select的方式 ``` select { case close := <-chan_name: //code } ```` ## slice,chan,map 无需进行引用,就可实现传值 map ``` // map 类型不需要引用传值 func demo(a map[int]string) { a[11] = "123" } func main() { a := make(map[int]string) a[21] = "asd" demo(a) fmt.Println(a) //map[21:asd 11:123] } ``` chan ``` func main() { person := make(chan int, 10) modify(person) fmt.Println(<-person) //12 time.Sleep(23 * time.Second) } func modify(p chan int) { p <- 12 } ``` slice ``` func demo(a []int){ a[0]=12 //a = append(a, 1) //错误,不会添加到a中 } func main() { a :=make([]int,2,3) demo(a) fmt.Println(a) } ``` ## 指针 ``` func main() { i := 10 ip := &i fmt.Printf("原始指针的内存地址是:%p\n", &ip) //0xc042072018 modify(ip) fmt.Println("int值被修改了,新值为:", i) //1 } func modify(ip *int) { fmt.Printf("函数里接收到的指针的内存地址是:%p\n", &ip) //0xc042072028 *ip = 1 } ``` ### 关于指针 > [参考](https://studygolang.com/articles/19386?utm_source=tuicool&utm_medium=referral) 1. 指针指向的数据都是在堆上分配的 2. 值的拷贝是昂贵的,所以用一个指针来代替 3. 如果可以证明它里面没有指针,垃圾回收器会直接越过这块内存 > 垃圾回收器回收一个变量时,要检查该类型里是否有指针。 如果有,要检查指针所指向的内存是否可被回收,进而才能决定这个变量能否被回收。如此递归下去。 如果被回收的变量里面没有指针, 就不需要进去递归扫描了,直接回收掉就行 4. 如果结构体中没有指针,则可编译时知道,降低 gc 压力的压力 ## 匿名嵌入的方式实现继承 ``` type person struct { Name string } type foo struct { person //匿名嵌入的方式实现继承 } func main() { Foo := foo{} Foo.Name="asd" } ``` ## 实现 String() 接口的结构体 实现了 String(),可在打印时,自定义输出的字符串`fmt.Printlln(foo)` ## 条件编译 目录结构 ``` ├─echo ├─echo1.go └─echo 2.go └─main.go ``` echo1.go ``` // +build !echo2 package echo func Echo() { fmt.Println("echo1.go") } ``` echo2.go ``` // +build echo2 package echo func Echo() { fmt.Println("echo1.go") } ``` main.go ``` func main() { echo.Echo() } ``` ``` go build -tags=echo2 main.go && main.exe //output echo2.go go build main.go && main.exe //output echo1.go ``` ## 只初始化一次变量,如链接数据库 定义全局指针变量,在 init 中进行赋值 ``` var ( dbConn *sql.DB err error ) func init(){ dbConn,err=sql.Open("mysql","root:12345678@tcp(localhost:3306)/video_server?charset=utf8") if err!=nil{ panic(err) } err =dbConn.Ping() if err!=nil { panic(err) } } ``` ## sync.map - 并发安全的 map ``` //存 func (m *Map) Store(key, value interface{}) //取 func (m *Map) Load(key interface{}) (value interface{}, ok bool //删 func (m *Map) Delete(key interface{}) ``` ## go .(type) 转为特定类型 ``` type user struct { Name string Age int } func main() { Cache := make(map[int]interface{}) Cache[1] = user{"cpj", 12} //格式化为 user 类型 fmt.Println(Cache[1].(user).Name) } ``` ## 构造函数 ``` type user struct { Name string Age int } func NewUser(name string, age int) *user { return &user{ Name: name, Age: age, } } func (u *user) getName() string { return u.Name } func main() { //创建构造函数 user := NewUser("cpj", 12) fmt.Println(user.getName()) } ``` ## 中间件制作 以 http 接口为例子 ``` type minddleWarehandle struct { r *httprouter.Router } func NewMiddleWareHandle(r *httprouter.Router) minddleWarehandle { m := minddleWarehandle{r: r} return m } // !!! 覆写 ServerHttp 接口 func (m minddleWarehandle) ServerHttp(w http.ResponseWriter, r *http.Request) { //check session ValidateUserSession(r) m.r.ServeHTTP(w, r) } func RegisetHandlers() *httprouter.Router { router := httprouter.New() router.POST("/user", CreateUser) router.POST("/user/:user_name", Login) return router } func main() { r := RegisetHandlers() mh := NewMiddleWareHandle(r)// 添加一层中间件 http.ListenAndServe(":8000", mh.r) } ``` ## 传送二进文件,如视频等 ``` video, e := os.Open(filepath) defer video.Close() if e != nil { log.Println("error") return } //把文件放入响应中 w.Header().Set("Content-Type", "video/mp4") http.ServeContent(w, r, "", time.Now(), video) ``` ## Read(p []byte) (n int, err error) 函数 获取全部数据 ``` //使用此函数 ioutil.ReadAll(r.Body.Read()) ``` ## 打印 log.print... 显示文件与行数 ``` log.SetFlags(log.Lshortfile | log.LstdFlags) // output 2012/07/24 19:27:55 X.cgo1.go:14: ... ``` ## 在返回值中声明 ``` func add(a,b int)(c,d int){ c=a //d 在返回值中声明,可以不使用 return } ``` ## 使用 close 关闭 chan 每当使用完chan后,需关闭 >[参考](https://blog.csdn.net/len_yue_mo_fu/article/details/80315799) ``` func write(ch1 chan int) { for i := 0; i < 10; i++ { ch1<-i } close(ch1) //当复制完close 时,需要关闭 } ``` ## 退出多个循环 ``` func main() { L1: for x := 0; x < 3; x++ { L2: for y := 0; y < 5; y++ { if y > 2 { continue L2 } if x > 1 { break L1 } //退出多个循环,与 goto 类似 print(x, ":", y, " ") } println() } } //output //0:0 0:1 0:2 //1:0 1:1 1:2 ``` ## defer 延迟调⽤通过闭包读取 ``` func add(x, y int) (z int) { defer func() { z += 100 }() z = x + y return } func main() { println(add(1, 2)) // 输出: 103 } ``` ## make 初始化时,指定长度,可避免多次分配内存 `a := make([]int, 0, 10)` 在初始化`slice`时,若事先知道`capacity`的长度,可优化性能,避免多次内存分配 ## 接口类型命名使用 er ``` type Stringer interface { String() string } ``` ## 接⼝转换 ``` type User struct { id int name string } func main() { var o interface{} = User{1, "Tom"} u := o.(User) //{1 Tom} fmt.Println(u.name) } ``` ## iota 创建 字节大小 ``` const ( _ = iota // iota = 0 KB int64 = 1 << (10 * iota) // iota = 1 MB // 与 KB 表达式相同,但 iota = 2 GB TB ) ``` ## ⽤ closed channel 发出退出通知 ``` quit := make(chan bool) go func() { for { select { case <-quit: // closed channel 不会阻塞,因此可⽤作退出通知。 println("he") return default: // 执⾏正常任务。 println(time.Now().Nanosecond()) time.Sleep(time.Second) } } }() time.Sleep(2 * time.Second) close(quit) // 发出退出通知。 time.Sleep(2 * time.Second) ``` ## 递归函数中传递切片 ``` func sliceModify(slice *[]int) { *slice = append(*slice, 6) } func main() { slice := []int{1, 2, 3, 4, 5} sliceModify(&slice) fmt.Println(slice) } ``` ## 自定义tag标签(reflect) ```golang type Student struct { Name string `demo:"user_name"` Age int Score float32 `demo:"sc"` } func test(a interface{}) { v := reflect.TypeOf(a) numFiled := v.NumField() //获取filed 数量 for i := 0; i < numFiled; i++ { value, ok := v.Field(i).Tag.Lookup("demo") fmt.Println(ok) // true false true fmt.Println(value) // user_name 空 sc } } func main() { var a = Student{ Name: "stu01", Age: 18, Score: 92, } test(a) } ``` ## 只初始化一次即单例模式,(考虑并发) 如果没有初始化条件, ``` var myOnlyInstance = newMyType() ``` 有初始化条件 bad ``` public class MyType { static MyType single; public static MyType getSingle() { if (single == null) { single = new MyType(); } return single; } } ``` good ``` // The zero value for Once is ready to use var oSingle sync.Once var single *myType func getSingle() *myType { oSingle.Do(func(){ single = newMyType() }) return single } ``` ## 用vendor处理公共配置包 当多个微服务有公告要处理的第三方包或配置,则在微服务同级目录下创建 `vendor` ``` ├── api ├── stream_server ├── templates │   ├── img │   └── scripts ├── testData ├── vendor //创建 vendor │   ├── foo │   │   ├── config │   │   │   ├── config.go │   ├── aliOss 第三方包 ├── videos └── web ``` 应用 config.go ``` import ( "foo/config" ) ``` ## 优化打印 %#v 与 %+v ``` type Website struct { Name string } // 定义结构体变量 var site = Website{Name: "studygolang"} fmt.Printf("%#v", site) //Website{Name:"studygolang"} fmt.Printf("%+v", site) //{Name:"studygolang"} ``` ## 按行读取文件 ``` file, e := os.Create("aaa.txt") if e != nil { panic(e) } file.WriteString("hello word\n adsasdsadaasd\nasdasdasdasd \nasdasdad") // 将文件 offset 设置到文件开头 file.Seek(0, io.SeekStart) scanner := bufio.NewScanner(file) //设置读取方式为行 scanner.Split(bufio.ScanLines) for scanner.Scan() { fmt.Println(scanner.Text()) } if e = scanner.Err(); e != nil { panic(e) } ``` ## struct 格式化为json 为空不显示 添加`omitempty`后,如果 data 为空,则不显示 data ``` type H struct{ Msg string `json:"msg"` Data interface{} `json:"data,omitempty"` } ``` ## 静态文件访问 ``` http.HandleFunc("/hello", hello) http.Handle("/",http.FileServer(http.Dir("."))) http.ListenAndServe(":9001", nil) ```