5 9月 2022
来源:thinkcmf
  • go
  • 笔记

GO语言学习笔记

基础开发环境配置

  1. 官网下载go:https://golang.org/dl/ 推荐1.13-1.15

  2. goproxy配置,使用go mod

  3. git 配置: git config — global


包依赖管理:

  1. go get xxx

  2. 使用命令 go mod vendor 配合go build -mod=vendor即可快速方便构建项目。


Go mod, vendor, gopath几种用法和优劣,最新推荐的方式要了解下


三方工具库:

redis:https://github.com/go-redis/redis



性能工具:

  1. Go单测工具中的benchmark:

    1. 编写方法BenchmarkAppendFloat

    2. 测试:go test -run=main_test.go -bench=BenchmarkAppendFloat -cpuprofile=cpu.file ./

  2. Go工具包自带pprof

  3. 使用Uber的go-torch

性能优化:

  1. GO反射带来的性能消耗

    1. tp := reflect.TypeOf(Abc{}),数据结构的类型编译耗时会比较长

    2. 大部分的性能消耗都在反射创建和反射赋值上,反射赋值我们可以尝试使用unsafe包直接操作内存:


var tp = reflect.TypeOf(Abc{})

val := reflect.New(tp)


val.Elem().Field(0).SetString("hello")

val.Elem().Field(1).SetInt(200)

val.Elem().Field(2).SetBool(true)


->


tp = reflect.TypeOf(Abc{})

of1 = tp.Field(1).Offset

of2 = tp.Field(2).Offset


val := reflect.New(tp).Interface()

of0 := (*emptyInterface)(unsafe.Pointer(&val)).word

*(*string)(of0) = "hello"

*((*int64)(unsafe.Pointer(uintptr(of0) + of1))) = 200

*((*bool)(unsafe.Pointer(uintptr(of0) + of2))) = true


  1. 优化官方hash包的案例来说明怎么使用工具优化内存的使用,包名hash/fnv

    1. 主要的性能消耗是在对象创建和write数据上,接下来分析一下源码,发现两次对象创建都没有必要使用堆内存,write的时候官方直接使用range操作[]byte,我们知道range操作每次迭代都是一次内存拷贝,尝试进行优化:

      1. 直接干掉对象的创建,使用函数代替

      2. 优化迭代方法,减少拷贝次数


const (

offset64 = uint64(14695981039346656037)

prime64 = uint64(1099511628211)


Init64 = offset64

)


func HashBytes64(b []byteuint64 {

return AddBytes64(Init64, b)

}


func AddBytes64(h uint64, b []byteuint64 {

for len(b) >= 8 {

h = (h * prime64) ^ uint64(b[0])

h = (h * prime64) ^ uint64(b[1])

h = (h * prime64) ^ uint64(b[2])

h = (h * prime64) ^ uint64(b[3])

h = (h * prime64) ^ uint64(b[4])

h = (h * prime64) ^ uint64(b[5])

h = (h * prime64) ^ uint64(b[6])

h = (h * prime64) ^ uint64(b[7])

b = b[8:]

}


if len(b) >= 4 {

h = (h * prime64) ^ uint64(b[0])

h = (h * prime64) ^ uint64(b[1])

h = (h * prime64) ^ uint64(b[2])

h = (h * prime64) ^ uint64(b[3])

b = b[4:]

}


if len(b) >= 2 {

h = (h * prime64) ^ uint64(b[0])

h = (h * prime64) ^ uint64(b[1])

b = b[2:]

}


if len(b) > 0 {

h = (h * prime64) ^ uint64(b[0])

}


return h

}



稳定性建设:

"github.com/gin-gonic/gin”?

事前-压测、事中-流量控制、事后-日志监控

  1. 流量控制

    1. Sentinel-go : https://sentinelguard.io/zh-cn/docs/golang/basic-api-usage.html?spm=ata.21736010.0.0.456468d2T3HG8C

  2. 日志监控

    1. https://gitee.com/phachon/go-logger

  3. 调用追踪trace

  4. 全链路压测:

    1. 压测标识别

    2. 压测标透传:go context传递

    3. 压测流量/数据隔离:

      1. Db:影子表

      2. Redis:路由到redis压测集群



基本概念:


错误和异常处理:

defer, panic, reoover — 类似java里的try-catch-finally,注意下recover只有定义在defer函数里才会生效,注意哪些错误能recover(比如slice越界,nul指针被引用,除零,写关闭的chan),thread limit不能revocer,concurrentmap竞争写不能recover;recover最佳实践:一般recover后会判断是否为err,有可能需要处理特殊的error,一般也需要打印日志或者告警


避免程序无法recover被干掉


错误处理的模型一般有两种,一般是错误码模型比如C/C++和Go,还有异常模型比如Java和C#。

推荐用github.com/pkg/errors这个错误处理的库


日志:

推荐阅读Kafka对于Log的定义,广义日志是可以理解的消息

https://engineering.linkedin.com/distributed-systems/log-what-every-software-engineer-should-know-about-real-time-datas-unifying?spm=ata.21736010.0.0.56fb10ffQB6EfS


日志要素:

traceid — 塞到context里,通过logger的通用函数打印(函数具体见使用的logger类)

耗时 — 类似java里的profiler


Go并没有支持类继承体系和多态,使用抽象函数并且在继承类里实现抽象方法是不可行的。推荐使用组合和接口

举例理解:

跨平台编译器,Go Composition: Compiler,代码如下所示:

package main


import (

"fmt"

)


// core structure design start

type SourceCollector interface {

collectSource()

}


type TargetCompiler interface {

compileToTarget()

}


type CrossCompiler struct {

collector SourceCollector

compiler TargetCompiler

}


func (v CrossCompiler) crossCompile() {

v.collector.collectSource()

v.compiler.compileToTarget()

}


// core structure design end


type IPhoneCompiler struct {

}


func (v IPhoneCompiler) collectSource() {

fmt.Println("IPhoneCompiler.collectSource")

}

热门文章

没有设置文章分类,请在后台设置。

最新发布

没有设置文章分类,请在后台设置。