Go in Practice-Manning 2016(读书笔记)

Go in Practice

Getting into Go[编辑]

  1. Noteworthy aspects of Go
    1. 多个返回值
      1. 可以命名返回值??fk func Names() (first string, second string) { first = "Foo" }
    2. A modern standard library
      1. net --> net/http --> html/template --> ...
    3. Concurrency with goroutines and channels
      1. 备注:CSP的概念是否可以大大减少栈空间的占用?同步的函数调用 -> 并发的消息通信 ...
    4. Go the toolchain—more than a language
      1. $ go get ./... 解析代码中的import语句并下载依赖
      2. hello_test.go:
        import "testing"
        func TestXxx(t *testing.T) { ... t.Error("!") }
      3. 1.5+:$ go test -cover
  2. Go in the vast language landscape
    1. C and Go:cgo
    2. Java and Go:Java需要一个分离的单独安装的JVM及其JIT(当然,并发调度模型也不一样?)
    3. Python, PHP and Go
    4. JavaScript, Node.js, and Go
  3. Getting up and running in Go

A solid foundation[编辑]

  1. CLI
    1. 使用flag解析命令行参数:
      1. var name = flag.String("name", "World", "A name to say hello to.")
      2. flag.BoolVar(&spanish, "spanish", false, "Use Spanish language.")
      3. flag.Parse()
      4. flag.VisitAll(func(flag *flag.Flag) { ... fmt.Printf(format, flag.Name, flag.Usage, flag.DefValue) }
    2. launchpad.net/gnuflag ?
    3. github.com/jessevdk/go-flags ?
    4. https://github.com/urfave/cli 一个CLI应用框架?
  2. 处理配置文件
    1. JSON:
      decoder := json.NewDecoder(file) //file, _ := os.Open("conf.json")
      conf := configuration{} //自定义的struct,这里似乎没有加``标注?
      err := decoder.Decode(&conf)
    2. https://github.com/coreos/etcd 分布式配置和服务发现
    3. YAML: github.com/kylelemons/go-gypsy/yaml
      config, err := yaml.ReadFile("conf.yaml")
      fmt.Println(config.Get("path"))
    4. INI:gopkg.in/gcfg.v1
      err := gcfg.ReadFileInto(&config, "conf.ini") //注意,ini有个section分组的概念(无非就是个嵌套struct)
    5. 环境变量
      1. Twelve-factor apps(PaaS最佳实践?)
      2. os.Getenv("PORT")
  3. Working with real-world web servers
    1. 优雅地关闭?github.com/braintree/manners
      1. main:
        ch := make(chan os.Signal)
        signal.Notify(ch, os.Interrupt, os.Kill)
        go listenForShutdown(ch)
        manners.ListenAndServe(":8080", handler) //Hook API;
      2. func listenForShutdown(ch <-chan os.Signal) {
        <-ch
        manners.Close() //允许当前的request处理完成(但是这里还是有个问题:假如遇到pending io怎么办?)
    2. Routing web requests
      1. 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
      2. 利用regexp匹配url:略
      3. Faster routing (without the work)
        1. github.com/julienschmidt/httprouter 大小写敏感、可处理/../
        2. github.com/gorilla/mux
        3. github.com/bmizerany/pat Sinatra风格,如/user/:name

Concurrency in Go[编辑]

  1. sync.WaitGroup
    1. wg.Add(1) ==> go func() { ... wg.Done() }() ==> wg.Wait()
  2. $ go run --race race.go *.txt
    1. 利用sync.Mutex保护对共享数据的并发访问
  3. select模式:
    done := time.After(30 * time.Second)
    select { //还可以在外面再for{}包装一下?
    ...
    case <-done: fmt.Println("Timed out")
  4. a closed channel always returns the channel’s nil value //对chan bool就是false?
    1. 用一个额外的channel来代表当前channel已关闭?每个channel相当于一个单向的msg通道?
  5. using a channel as a lock //这种情况下当前goroutine既写也读;
    1. lock := make(chan bool, 1)

处理errors和panics[编辑]

  1. An error indicates that a particular task couldn't be completed successfully.
    1. panic indicates that a severe event occurred, probably as a result of a programmer error.(内核BUG_ON~)
  2. func Concat(parts ...string) (string, error) {
    if len(parts)==0 { return "", errors.New("No strings supplied") }
  3. args := os.Args[1:]
    if result, err := Concat(args...); err != nil { ... } else ... //注意,if的scope包括else分支
  4. 包级别的error变量:如io.EOF
  5. panic(errors.New("Something bad happened."))
    1. defer func() { if err := recover(); err != nil { ... } }()
      • 既然是推迟执行的函数,那么怎么表达“re-try”的逻辑呢?交给更上一层来处理?
  6. !注意:defer闭包无法引用后面声明的变量(这跟JS的函数作用域不一样)
  7. A panic on a goroutine can't jump to the call stack of the function that initiated the goroutine.

调试和测试[编辑]

  1. logger := log.New(logfile, "logprefix ", log.LstdFlags|log.Lshortfile)
  2. 把log重定向到网络端口:
    1. nc -lk 1902
    2. conn, err := net.Dial("tcp", "localhost:1902")
      defer conn.Close()
      logger := log.New(conn, "example ", flog.Ldate | log.Lshortfile)
  3. 处理back-pressure:本地缓存
    1. 改用UDP:conn, err := net.DialTimeout("udp", "localhost:1902", timeout) //但仍然会有数据丢失、传输失序的问题
    2. 3rd包:https://github.com/Sirupsen/logrus https://github.com/golang/glog
  4. 输出到syslog
    1. logger, err := syslog.NewLogger(priority, flags)
    2. Logging to a remote syslog(略)
  5. Accessing stack traces
    1. runtime/debug包:debug.PrintStack() => runtime.Stack(buf, false)
  6. Generative testing:testing/quick //自动生成测试数据?
  7. Using performance tests and benchmarks
    1. b *testing.B ?=> 循环迭代到b.N点?

HTML与email模板模式[编辑]

  1. ?<a href="/user?id={ {.Id | urlquery}}"> //{{会当作mediawiki格式的模板命令,下面省略只用1对{}表示
  2. {.Date | dateFormat "Jan 2, 2006"} // http://golang.org/pkg/time/#Time.Format
  3. var t = template.Must(template.ParseFiles("templates/simple.html")) //包变量
  4. err := t.Execute(&bytes_Buffer, params)
  5. 嵌入子模板:{template "header.html" .}
  6. 模板继承*
    1. {define "base"} ... {end}
    2. 1.6+:{block "styles" .} ... {end}
    3. temp := template.Must(template.ParseFiles("base.html", "user.html")) //?

Serving and receiving assets and forms[编辑]

  1. http.ListenAndServe(":8080", http.FileServer(http.Dir("./files")))
    1. 也可以针对单独的文件:http.ServeFile(res, req, "./files/readme.txt")
    2. 去除url请求中的path前缀:
      1. handler := http.StripPrefix("/static/", http.FileServer(dir))
      2. http.Handle("/static/", handler)
  2. github.com/Masterminds/go-fileserver 定制的错误页?(居然是专为这本书写的?节省pdf页数?靠)
  3. 将静态文件缓存到内存:
    1. r := bytes.NewReader(b.Bytes()) //io.ReadSeeker类型?
      ==> http.ServeContent(res, req, req.URL.Path, v.modTime, v.content)
  4. RWMutex
  5. groupcache?提供跨server的共享内存缓存
  6. 将二进制资源打包进binary:github.com/GeertJohan/go.rice
    1. box := rice.MustFindBox("../files/")
    2. httpbox := box.HTTPBox()
    3. ==> $ rice embed-go && go build(不错,这个工具看上去很神奇)
  7. name := r.FormValue("name") //获取表单请求参数
  8. FormValue、PostFormValue(不包含url查询参数):获取第一个值
  9. 迭代多个值:
    1. err := r.ParseMultipartForm(maxMemory)
    2. for k, v := range r.PostForm["names"] { ... }
  10. 处理文件上传
    1. f, h, err := r.FormFile("file") //f是解析完成的文件内容(在内存里?)还是说等实际read时JIT地解析?
      类型:multipart.File, *multipart.FileHeader
    2. defer f.Close()
    3. out, err := os.Create("/tmp/" + h.Filename)
    4. defer out.Close()
    5. io.Copy(out, f)
  11. Uploading multiple files(多个文件上传)
    1. <input type="file" name="files" id="files" multiple> //Wtf,这我还没注意到,一个input元素就可以上传多个文件
    2. 服务器端:
      err := r.ParseMultipartForm(16 << 20)
      data := r.MultipartForm
      files := data.File["files"]
      for _, fh := range files {
      f, err := fh.Open()
      defer f.Close()
      ... //下略
  12. 验证上传文件的mime类型(略)
  13. Working with raw multipart data
    1. *multipart.Reader:r.MultipartReader
  14. 增量保存上传的文件:?(看起来只是为了节省服务器端内存,不是断点续传)

Web服务[编辑]

  1. 客户端:
    1. c := &http.Client{Timeout: time.Second}
    2. ➥ res, err := c.Get("http://goinpracticebook.com")
  2. 检测超时错误:
    1. case *url.Error:
      if err, ok := err.Err.(net.Error); ok && err.Timeout() { ...
    2. case *net.OpError:
      if err.Timeout() { ...
  3. 超时后自动resume:
    1. req.Header.Set("Range", "bytes="+strconv.FormatInt(file.Stat().Size(), 10)+"-") //Size为什么是一个函数呢?
  4. Parsing and mapping JSON:略
  5. Versioning REST APIs:无聊!

Using the cloud[编辑]

  1. 交叉编译:$ GOOS=windows GOARCH=386 go build
  2. 运行时监控:
    1. m := &runtime.MemStats{} ==> r := runtime.NumGoroutine(); runtime.ReadMemStats(m) //总感觉这里的代码有点怪异?

云服务之间的通信[编辑]

  1. 重用连接:
    tr := &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: pool},
    DisableCompression: true,
    Dial: (&net.Dialer{
    Timeout: 30 * time.Second,
    KeepAlive: 30 * time.Second,
    }).Dial,
  2. HTTP/2 Pipelining
    1. 不要defer r.Body.Close()
  3. Faster JSON marshal and unmarshal(去掉了反射,因此速度更快)
    1. $ go get -u github.com/ugorji/go/codec/codecgen
    2. $ codecgen -o user_generated.go user.go //预先定义好struct...
  4. Moving beyond REST
    1. $ go get -u github.com/golang/protobuf/protoc-gen-go
    2. $ protoc -I=. --go_out=. ./user.proto
    3. gRPC:www.grpc.io

反射与代码生成[编辑]

  1. name, type, value
  2. 判断interface{}是否是特定类型:2'
    1. _, ok := v.(fmt.Stringer)
    2. 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 }
  3. Structs, tags, and annotations
    1. FirstName string `json:"first" xml:"firstName,attr"`
    2. ...
  4. Generating Go code with Go code
    1. 基于html模板类似语法?元编程?扯淡
    2. 基于go/ast?还是要编译的吧?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值