Go in Practice
目录
[隐藏]Getting into Go[编辑]
- Noteworthy aspects of Go
- 多个返回值
- 可以命名返回值??fk func Names() (first string, second string) { first = "Foo" }
- A modern standard library
- net --> net/http --> html/template --> ...
- Concurrency with goroutines and channels
- 备注:CSP的概念是否可以大大减少栈空间的占用?同步的函数调用 -> 并发的消息通信 ...
- Go the toolchain—more than a language
- $ go get ./... 解析代码中的import语句并下载依赖
- hello_test.go:
- import "testing"
- func TestXxx(t *testing.T) { ... t.Error("!") }
- 1.5+:$ go test -cover
- 多个返回值
- Go in the vast language landscape
- C and Go:cgo
- Java and Go:Java需要一个分离的单独安装的JVM及其JIT(当然,并发调度模型也不一样?)
- Python, PHP and Go
- JavaScript, Node.js, and Go
- Getting up and running in Go
A solid foundation[编辑]
- CLI
- 使用flag解析命令行参数:
- var name = flag.String("name", "World", "A name to say hello to.")
- flag.BoolVar(&spanish, "spanish", false, "Use Spanish language.")
- flag.Parse()
- flag.VisitAll(func(flag *flag.Flag) { ... fmt.Printf(format, flag.Name, flag.Usage, flag.DefValue) }
- launchpad.net/gnuflag ?
- github.com/jessevdk/go-flags ?
- https://github.com/urfave/cli 一个CLI应用框架?
- 使用flag解析命令行参数:
- 处理配置文件
- JSON:
- decoder := json.NewDecoder(file) //file, _ := os.Open("conf.json")
- conf := configuration{} //自定义的struct,这里似乎没有加``标注?
- err := decoder.Decode(&conf)
- https://github.com/coreos/etcd 分布式配置和服务发现
- YAML: github.com/kylelemons/go-gypsy/yaml
- config, err := yaml.ReadFile("conf.yaml")
- fmt.Println(config.Get("path"))
- INI:gopkg.in/gcfg.v1
- err := gcfg.ReadFileInto(&config, "conf.ini") //注意,ini有个section分组的概念(无非就是个嵌套struct)
- 环境变量
- Twelve-factor apps(PaaS最佳实践?)
- os.Getenv("PORT")
- JSON:
- Working with real-world web servers
- 优雅地关闭?github.com/braintree/manners
- main:
- ch := make(chan os.Signal)
- signal.Notify(ch, os.Interrupt, os.Kill)
- go listenForShutdown(ch)
- manners.ListenAndServe(":8080", handler) //Hook API;
- func listenForShutdown(ch <-chan os.Signal) {
- <-ch
- manners.Close() //允许当前的request处理完成(但是这里还是有个问题:假如遇到pending io怎么办?)
- main:
- Routing web requests
- pr := newPathResolver() //path包?==> path.Match(pattern, req.Method + " " + req.URL.Path)
- pr.Add("GET /hello", hello)
- pr.Add("* /goodbye/*", goodbye)
- 缺点:匹配foo/bar/baz需要写foo/*/*,而/goodbye/*不能匹配/goodbye
- 利用regexp匹配url:略
- Faster routing (without the work)
- github.com/julienschmidt/httprouter 大小写敏感、可处理/../
- github.com/gorilla/mux
- github.com/bmizerany/pat Sinatra风格,如/user/:name
- pr := newPathResolver() //path包?==> path.Match(pattern, req.Method + " " + req.URL.Path)
- 优雅地关闭?github.com/braintree/manners
Concurrency in Go[编辑]
- sync.WaitGroup
- wg.Add(1) ==> go func() { ... wg.Done() }() ==> wg.Wait()
- $ go run --race race.go *.txt
- 利用sync.Mutex保护对共享数据的并发访问
- select模式:
- done := time.After(30 * time.Second)
- select { //还可以在外面再for{}包装一下?
- ...
- case <-done: fmt.Println("Timed out")
- a closed channel always returns the channel’s nil value //对chan bool就是false?
- 用一个额外的channel来代表当前channel已关闭?每个channel相当于一个单向的msg通道?
- using a channel as a lock //这种情况下当前goroutine既写也读;
- lock := make(chan bool, 1)
处理errors和panics[编辑]
- An error indicates that a particular task couldn't be completed successfully.
- A panic indicates that a severe event occurred, probably as a result of a programmer error.(内核BUG_ON~)
- func Concat(parts ...string) (string, error) {
- if len(parts)==0 { return "", errors.New("No strings supplied") }
- args := os.Args[1:]
- if result, err := Concat(args...); err != nil { ... } else ... //注意,if的scope包括else分支
- 包级别的error变量:如io.EOF
- panic(errors.New("Something bad happened."))
- defer func() { if err := recover(); err != nil { ... } }()
- 既然是推迟执行的函数,那么怎么表达“re-try”的逻辑呢?交给更上一层来处理?
- defer func() { if err := recover(); err != nil { ... } }()
- !注意:defer闭包无法引用后面声明的变量(这跟JS的函数作用域不一样)
- A panic on a goroutine can't jump to the call stack of the function that initiated the goroutine.
调试和测试[编辑]
- logger := log.New(logfile, "logprefix ", log.LstdFlags|log.Lshortfile)
- 把log重定向到网络端口:
- nc -lk 1902
- conn, err := net.Dial("tcp", "localhost:1902")
- defer conn.Close()
- logger := log.New(conn, "example ", flog.Ldate | log.Lshortfile)
- 处理back-pressure:本地缓存
- 改用UDP:conn, err := net.DialTimeout("udp", "localhost:1902", timeout) //但仍然会有数据丢失、传输失序的问题
- 3rd包:https://github.com/Sirupsen/logrus https://github.com/golang/glog
- 输出到syslog
- logger, err := syslog.NewLogger(priority, flags)
- Logging to a remote syslog(略)
- Accessing stack traces
- runtime/debug包:debug.PrintStack() => runtime.Stack(buf, false)
- Generative testing:testing/quick //自动生成测试数据?
- Using performance tests and benchmarks
- b *testing.B ?=> 循环迭代到b.N点?
HTML与email模板模式[编辑]
- ?<a href="/user?id={ {.Id | urlquery}}"> //{{会当作mediawiki格式的模板命令,下面省略只用1对{}表示
- {.Date | dateFormat "Jan 2, 2006"} // http://golang.org/pkg/time/#Time.Format
- var t = template.Must(template.ParseFiles("templates/simple.html")) //包变量
- err := t.Execute(&bytes_Buffer, params)
- 嵌入子模板:{template "header.html" .}
- 模板继承*
- {define "base"} ... {end}
- 1.6+:{block "styles" .} ... {end}
- temp := template.Must(template.ParseFiles("base.html", "user.html")) //?
Serving and receiving assets and forms[编辑]
- http.ListenAndServe(":8080", http.FileServer(http.Dir("./files")))
- 也可以针对单独的文件:http.ServeFile(res, req, "./files/readme.txt")
- 去除url请求中的path前缀:
- handler := http.StripPrefix("/static/", http.FileServer(dir))
- http.Handle("/static/", handler)
- github.com/Masterminds/go-fileserver 定制的错误页?(居然是专为这本书写的?节省pdf页数?靠)
- 将静态文件缓存到内存:
- r := bytes.NewReader(b.Bytes()) //io.ReadSeeker类型?
- ==> http.ServeContent(res, req, req.URL.Path, v.modTime, v.content)
- r := bytes.NewReader(b.Bytes()) //io.ReadSeeker类型?
- RWMutex
- groupcache?提供跨server的共享内存缓存
- 将二进制资源打包进binary:github.com/GeertJohan/go.rice
- box := rice.MustFindBox("../files/")
- httpbox := box.HTTPBox()
- ==> $ rice embed-go && go build(不错,这个工具看上去很神奇)
- name := r.FormValue("name") //获取表单请求参数
- FormValue、PostFormValue(不包含url查询参数):获取第一个值
- 迭代多个值:
- err := r.ParseMultipartForm(maxMemory)
- for k, v := range r.PostForm["names"] { ... }
- 处理文件上传
- f, h, err := r.FormFile("file") //f是解析完成的文件内容(在内存里?)还是说等实际read时JIT地解析?
- 类型:multipart.File, *multipart.FileHeader
- defer f.Close()
- out, err := os.Create("/tmp/" + h.Filename)
- defer out.Close()
- io.Copy(out, f)
- f, h, err := r.FormFile("file") //f是解析完成的文件内容(在内存里?)还是说等实际read时JIT地解析?
- Uploading multiple files(多个文件上传)
- <input type="file" name="files" id="files" multiple> //Wtf,这我还没注意到,一个input元素就可以上传多个文件
- 服务器端:
- err := r.ParseMultipartForm(16 << 20)
- data := r.MultipartForm
- files := data.File["files"]
-
for _, fh := range files {
- f, err := fh.Open()
- defer f.Close()
- ... //下略
- 验证上传文件的mime类型(略)
- Working with raw multipart data
- *multipart.Reader:r.MultipartReader
- 增量保存上传的文件:?(看起来只是为了节省服务器端内存,不是断点续传)
Web服务[编辑]
- 客户端:
- c := &http.Client{Timeout: time.Second}
- ➥ res, err := c.Get("http://goinpracticebook.com")
- 检测超时错误:
- case *url.Error:
- if err, ok := err.Err.(net.Error); ok && err.Timeout() { ...
- case *net.OpError:
- if err.Timeout() { ...
- case *url.Error:
- 超时后自动resume:
- req.Header.Set("Range", "bytes="+strconv.FormatInt(file.Stat().Size(), 10)+"-") //Size为什么是一个函数呢?
- Parsing and mapping JSON:略
- Versioning REST APIs:无聊!
Using the cloud[编辑]
- 交叉编译:$ GOOS=windows GOARCH=386 go build
- 运行时监控:
- m := &runtime.MemStats{} ==> r := runtime.NumGoroutine(); runtime.ReadMemStats(m) //总感觉这里的代码有点怪异?
云服务之间的通信[编辑]
- 重用连接:
-
tr := &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: pool},
- DisableCompression: true,
-
Dial: (&net.Dialer{
- Timeout: 30 * time.Second,
- KeepAlive: 30 * time.Second,
- }).Dial,
-
tr := &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: pool},
- HTTP/2 Pipelining
- 不要defer r.Body.Close()
- Faster JSON marshal and unmarshal(去掉了反射,因此速度更快)
- $ go get -u github.com/ugorji/go/codec/codecgen
- $ codecgen -o user_generated.go user.go //预先定义好struct...
- Moving beyond REST
- $ go get -u github.com/golang/protobuf/protoc-gen-go
- $ protoc -I=. --go_out=. ./user.proto
- gRPC:www.grpc.io
反射与代码生成[编辑]
- name, type, value
- 判断interface{}是否是特定类型:2'
- _, ok := v.(fmt.Stringer)
- func implements(concrete interface{}, target interface{}) bool {
- iface := reflect.TypeOf(target).Elem()
- v := reflect.ValueOf(concrete)
- t := v.Type()
- if t.Implements(iface) { return true }
- Structs, tags, and annotations
- FirstName string `json:"first" xml:"firstName,attr"`
- ...
- Generating Go code with Go code
- 基于html模板类似语法?元编程?扯淡
- 基于go/ast?还是要编译的吧?