zoukankan      html  css  js  c++  java
  • 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!
    ", 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 函数式编程

  • 相关阅读:
    python设计模式-单例模式
    bash脚本条件测试总结
    Python网络编程:IO多路复用
    Python面向对象高级编程:@property--把方法变为属性
    Sql Server存储过程基本语法
    接口幂等性
    [转载]使用消息队列实现分布式事务-公认较为理想的分布式事务解决方案
    C#分布式事务解决方案-TransactionScope
    SOA架构和微服务架构的区别
    Atlas实现数据库读写分离
  • 原文地址:https://www.cnblogs.com/xzpin/p/11602103.html
Copyright © 2011-2022 走看看