Go语言系列四

使用buffered channel实现对象池

sync.Pool对象缓存

sync.Pool对象获取

  • 1> 尝试从私有对象获取

  • 2> 私有对象不存在, 尝试从当前Processor的共享池获取

  • 3> 如果当前Processor共享池也是空的,那么就尝试去其它Processor的共享池获取

  • 4> 如果所有子池都是空的,最后就用用户指定的New函数产生一个新的对象返回

  • 5> 如果私有对象不存在则保存为私有对象

  • 6> 如果私有对象存在,放入当前Processor子池的共享池中

    ​ 图

pool := &sync.Pool {
    New: func() interface{} {
        return 0
    },
}

array := pool.Get().(int)
...
pool.Put(10)

sync.Pool对象的生命周期

  • 1> GC会清除sync.pool缓存的对象
  • 2> 对象的缓存有效期为下一次GC之前

sync.Pool总结

  • 1> 适合于通过复用,降低复杂对象的创建和GC代价
  • 协程安全,会有锁的开销
  • 生命周期受GC影响,不适合于做连接池等,需自己管理生命周期的资源的池化

单元测试

内置单元测试框架

  • 1> Fail, Error: 该测试失败,该测试继续,其它测试继续执行

  • 2> FailNow, Fatal: 该测试失败,该测试中止,其它测试继续执行

  • 代码覆盖率

    • go test -v -cover
  • 断言

Benchmark

func BenchmarkConcatStringByAdd(b *testing.B) {
    //与性能测试无关的代码
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        //测试代码
    }
    b.StopTimer()
    	//与性能测试无关的代码
}

go test -bench=. -benchmem

-bench=<相关benchmark测试>

Windows下使用go test命令行时,-bench=.应写为-bench="."

BDD (Behavior Driven Development)

让业务领域的专家参与开发

用业务领域的语言来描述

BDD in Go

项目网站 https://github.com/smartystreets/goconvey

安装

go get -u github.com/smartystreets/goconvey/convey

启动WEB UI

$GOPATH/bin/goconvey

reflect.TypeOf vs reflect.ValueOf

  • 1> reflect.TypeOf返回类型(reflect.Type)
  • 2> reflect.ValueOf返回值(reflect.Value)
  • 3> 可以从reflect.Value获得类型
  • 4> 通过kind来判断类型

判断类型--Kind()

const (
	Invalid Kind = iota
    Bool
    Int
    Int8
    Int16
    Int32
    Int64
    Uint
    Uint8
    Uint16
    Uint32
    Uint64
	...)

利用反射编写灵活的代码

按名字访问结构的成员

reflect.ValueOf(*e).FieldByName("Name")

按名字访问结构的方法

reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(1)})

Struct Tag

访问StructTag

if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {
    t.Error("Failed to get 'Name' field.")
} else {
    t.Log("Tag:format", nameField.Tag.Get("format"))
}

Reflect.Type 和 Reflect.Value都有FieldByName方法,注意区别

DeepEqual

比较切片和map

关于反射

  • 1> 提高了程序的灵活性
  • 2> 降低了程序的可读性
  • 3> 降低了程序的性能

"不安全"行为的危险性

i := 10
f := *(*float64)(unsafe.Pointer(&i))

架构模式

Pipe-Filter架构

  • 1> 非常适合与数据处理及数据分析系统

  • 2> Filter封装数据处理的功能

  • 3> 松耦合: Filter只跟数据(格式) 耦合

  • 4> Pipe用于连接Filter传递数据或者在异步处理过程中缓冲数据流

    ​ 进程内同步调用时,pipe演变为数据在方法调用间传递

Filter和组合模式

示例

Micro Kernel

特点

  • 1> 易于扩展
  • 2> 错误隔离
  • 3> 保持架构一致性

要点

  • 1> 内核包含公共流程或通用逻辑

  • 2> 将可变或可扩展部分规划为扩展点

  • 3> 抽象扩展点行为,定义接口

  • 4> 利用插件进行扩展

    ​ 图

示例

内置的JSON解析

利用反射实现,通过FeildTag来标识对应的json值

type BasicInfo struct {
    Name string `json:"name"`
    Age int `json:"age"`
}
type JobInfo struct {
    Skills []string `json:"skills"`
}
type Employee struct {
    BasicInfo BasicInfo `json:"basic_info"`
    
}

更快的JSON解析

EasyJSON采用代码生成而非反射

安装

go get -u github.com/mailru/easyjson/...

使用

easyjson -all <结构定义>.go

Default Router

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux //使用缺省的Router
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    handler.ServeHTTP(rw, req)
}

路由规则

  • 1> URL分为两种, 末尾是/: 表示一个子树,后面可以跟其他子路径, 末尾不是/,表示一个叶子,固定的路径

    ​ 以/结尾的URL可以匹配它的任何子路径,eg: /images会匹配/images/cute-cat.jpg

  • 2> 它采用最长匹配规则,如果有多个匹配,一定采用匹配路径最长的那个进行处理

  • 3> 如果没有找到任何匹配项,会返回404错误

更好的Router

https://github.com/julienschmidt/httprouter

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}

func main() {
    router := httprouter.New()
    router.GET("/", Index)
    router.GET("/hello/:name", Hello)
    
    log.Fatal(http.ListenAndServe(":8080", router))
}

构建Restful服务

面向资源的架构(Resource Oriented Architecture)

面向资源的架构

性能分析工具

准备工作

1. 安装graphviz
	sudo apt install graphviz
2. 将$GOPATH/bin 加入 $PATH
	Mac OS:在.bash_profile中修改路径
	Linux
3. 安装go-torch
	go get github.com/uber/go-torch
	下载并复制flamegraph.pl(https://github.com/brendangeregg/FlameGraph)至$GOPATH/bin路径	  将$GOPATH/bin加入$PATH
	

通过文件方式输出Profile

  • 1> 灵活性高,适用于特定代码段的分析
  • 2> 通过手动调用runtime/pprof的API
  • 3> API相关文档https://studygolang.com/static/pkgdoc/pkg/runtime_pprof.htm
  • 4> go tool pprof [binary] [binary.prof]

通过HTTP方式输出Profile

  • 1> 简单,适合于持续性运行的应用
  • 2> 在应用程序中导入import _ "net/http/pprof", 并启动http server即可
  • 3> http://:/debug/pprof/
  • 4> go tool pprof _http://:/debug/pprof/profile?seconds=10(默认值为30秒)
  • 5> go-torch -seconds 10 https://:/debug/pprof/profile

性能调优过程

常见分析指标

  • Wall Time
  • CPU Time
  • Block Time
  • Memory allocation
  • GC times/time spent

别让性能被"锁"住

sync.Map

  • 1> 适合读多写少,且Key相对稳定的环境

  • 2> 采用空间换时间的方案,并且采用指针的方式间接实现值的映射,所以存储

    ​ 空间会较built-in map大

Concurrent Map

  • 1> 适用于读写都很频繁的情况

GC友好的代码

避免内存分配和复制

  • 复杂对象尽量传递引用
    • 数组传递
    • 结构体传递

打开GC日志

go tool trace

普通程序输出trace信息

pacakage main
import (
	"os"
    "runtime/trace"
)

func main() {
    f, err := os.Create("trace.out")
    if err != nil {
        panic(err)
    }
    defer f.Close()
    
    err = trace.Start(f)
    if err != nil {
        panic(err)
    }
    defer trace.Stop()
    //Your program here
}

测试程序输出trace信息

go test -trace trace.out

可视化trace信息

go tool trace trace.out

避免内存分配和复制

  • 1> 初始化至合适的大小
    • 自动扩容是有代价的
  • 复用内存

面向错误的设计

隔离

隔离错误-设计

隔离错误-部署

重用 vs 隔离

逻辑结构的重用 vs 部署结构的隔离

冗余

单点失效

限流

慢响应

不要无休止的等待

错误传递

断路器

面向恢复的设计

健康检查

  • 注意僵尸进程
    • 池化资源耗尽
    • 死锁

Let it Crash!

defer func() {
    if err := recover(); err != nil {
        log.Error("recovered panic", err)
    }
}()

构建可恢复的系统

  • 1> 拒绝单体系统
  • 2> 面向错误和恢复的设计
    • 在依赖服务不可用时,可以继续存活
    • 快速启动
    • 无状态

与客户端协商

​ 服务器:"我太忙了, 请慢点发送数据"

​ Client: "好, 我一分钟后再发送"

Chaos_engneering

"if something hurts, do it more often!"

* 如果问题经常发生人们就会学习和思考解决它的办法

Chaos Engineering 原则

  • Build a Hypothesis around Steady State Behavior

  • Vary Real-world Events

  • Run Experiments in Production

  • Automate Experiments to Run Continuously

  • Minimize Blast Radius

    https://principlesofchaos.org

相关开源项目

https://github.com/Netflix/chaosmonkey

https://github.com/easierway/service_decorators/blob/master/README.md

是结束, 更是开始!

The master has failed more times than the beginner has tried.

图书推荐

* Go程序设计语言
* 面向模式的软件架构
* 计算机程序的构造和解释

lisp 函数式编程

posted @ 2019-09-28 11:04  电院院长  阅读(276)  评论(0编辑  收藏  举报