golang小技巧
用来记录golang使用过程中的一些技巧
技巧
- golang可以在函数定义的时候确定其返回值具体为
func foo(arg int)(res int){}
-
go env GOPATH
可以用来去查看当前的GOPATH -
golang
查找问题, 可以根据Gogland
和ack
工具一起结合查找 -
golang
中的init
函数用来初始化一些变量, 它总是会被运行, 只要导入了这个包, 初始化就会在run main之前被运行
-
golang
中的接口类型, 以接口类型传入参数, 如果该接口类型, 实现了方法则ok -
proto.String()
是一个helper方法, 分配一个新的空间, 并返回一个指向它的指针 -
golang
中首字母大写表示public
, 首字母小写则表示protect
或者private
-
golang
中defer
语句的执行顺序为逆序 -
golang
中有时候为了诊断方便, 可以使用printStack
即runtime.Stack(buf[:], false)
-
io.Writer
是golang中接口类型的一种典型的运用, Fprintf=>(Printf, SPrintf) -
Stringer
接口通常用于对于printf
或者println
等一些列的函数数输出 -
golang
中的数组array
为定长数组, 如两个数组相比较必须是comparable
即长度和类型必须一致 -
golang
语言定义变量或者结构体在编译期就已经复制, 在运行期赋值, 需要重新定义一个函数 -
golang
中flag
库需要使用flag.parse()
来传递参数 -
buffer channel
在channel
中的元素个数小于channel size
时为非阻塞的, 达到channel size
之后则阻塞当前线程直到元素被取出. -
0 buff channel
即为sync channel
. 生产者和消费者都会被阻塞. -
golang
中对比并行程序设计有: 设置返回channel, 以经典的生产者消费者模式进行处理. -
timer
主要是用来设置定时任务,tickers
主要是用来设置周期任务. - 使用
sync.Mutex
通过对全局变量的Lock
和UnLock
实现atomic加锁和解锁 - 对于
Atomic inc
也可以使用sync/atomic
库实现. -
CAS
:Compare And Swap
比较并交换, 在golang中是以共享内存方式实现的一种同步机制. -
Share memory by communicating; don't communicate by sharing memory.
- golang中更好的一种方式是通过sync包以及channel来实现同步机制.
-
time.Sleep
vstime.After
-
time.After
通常于select
搭配使用, 可以设置超时机制 -
time.Sleep
可读性更强, 暂停当前Goroutine, 直到达到时间. -
timer
通过AfterFunc
或NewTimer
来创建, 使用Reset
来重置, timer. 使用Stop
来停止timer
, 停止timer
之后, 相应的goroutine
会被阻塞. -
timer
使用时, 如果使用了timer.Stop()
, 则timer, 会被GC所回收, 但是timer对应的channel
不会关闭, 则channel
将会被永远的阻塞. -
timer.Stop
对应的timer
被GC所回收,timer.C
被阻塞, 如果再使用timer.Reset
,则重新给timer对应的时间赋值并放入timer.C
中 -
timer
使用的正确姿势是, 使用timer.Reset
, 而不是使用timer.Stop
. 如果使用指针并且使用对应的timer.Channel
. 重置timer
时, 应当使用Reset
而不是重新Stop
+NewTimer
来重置, 这样会导致内存泄漏, 阻塞goroutine
-
deadlock
死锁: 多个线程占有一部分资源, 去请求另一部分资源时阻塞. 这些线程等等其他的线程释放资源. (已请求到锁) -
livelock
:活锁, 不断请求锁, 但是无法请求到资源. (未加锁) -
select
与channel
搭配使用, 如果要实现类似listener
的模式, 则需要在外层加for {select ...}
. - 通常使用
select
实现函数超时机制. -
HTTP keep-alive
是保持TCP
连接open
, 即复用TCP连接的一种手段. 例如: 通过HTTP keep-alive
就可以实现不用每次都去重新建立HTTP
连接, 这样就不会重新去请求静态资源css
,html
等. -
TCP keep-alive
是TCP协议定时地发送空的ack
包来确认彼此的状态. -
TCP keep-alive
和HTTP keep-alive
是两个不同的技术, 不存在谁依赖谁的关系.TCP keep-alive
用来检测对端是否存在, 而HTTP keep-alive
用来协商以复用连接. -
context
上下文类型,带有deadline,取消信号,还有其他与请求相关的request-scoped的值 -
context
遵循的几个原则: - 不要将
Context
放入结构体,使用Context
应该作为第一个参数传入,并命名为ctx,func DoSomething(ctx context.Context, arg Arg) error {//use ctx}
- 即使函数允许,不要传入
nil
的Context.如果不知道用哪种Context,使用context.TODO()
- 使用context的
Value
相关的方法只应该用于在程序和接口中传递和请求相关元数据,不要用它传递可选参数 - 相同的Context可以传递给不同的
goroutine
, Context并发安全 -
sync.Map
go1.9支持的线程安全的map,通过cas实现 - reflect: go语言反射机制。reflact反射是一种机制,这种机制可以实现自我类型的检查和描述
-
init
函数在main函数之前执行,通常用作初始化 -
sync.Once
保证程序仅仅执行一次action -
panic
类似于抛出异常,即停止当前正确的程序流程, panic之后会执行defer函数 - recover类似于恢复异常,即panic会一直向上抛出阻碍正常流程,直到recover则恢复
- 具体的某个golang对象初始化时候,通常的一种写法是通过
OptionFunc
来初始化一些可选参数 -
// +build
:是golang的build tag
例如// +build linux,386 darwin,!cgo
等价于(linux AND 386) OR (darwin AND (NOT cgo)) -
go build -tags="linux 386"
进行编译, 并根据Build Constraints
的条件进行编译 - golang make初始化并分配内存
- golang
%#v
与%+v
为打印变量并打印其变量名 -
value receiver
和pointer receiver
-
value receiver
是并发安全的, 且不改变value本身的值, copy by value -
pointer receiver
不是并发安全的,且可以改变pointer本身的值 - golang elastic 使用Scorll可以滚动的获取所有请求的结果
- dep 为官网golang包管理工具
-
dep init
: 效率较低,初始化golang项目的独立环境,利用gps分析出当前代码包中的依赖关系,并将其约束写入项目中的,Gopkg.toml
文件中。将所有依赖的包最新的version/branch/revision信息写入Gopkg.lock
,创建vendor目录,并以Gopkg.lock为输入,将其中包下载到vendor目录下面。 -
dep status
: 查看当前项目下的status的状态 -
dep ensure
: 确保当前项目的vendor处于一个正确的状态 -
gorilla/mux
: golang 非常受欢迎的URL router dispatchergoconvey
: golang 带UI的单元测试框架,可以很好的显示单元覆盖度 - ACL: access control list 访问控制列表,是路由器和交换机接口的指令,用于控制端口进出的数据包,ACL提供了网络安全访问的基本手段。
-
map[string]*person{}
与map[string]person{}
的区别在于指针和实例,实例为重新copy构造,改变内容之后,不改变map本身的内容, 末尾的{}为初始化map
-
OLAP
:Online Analytical Processing
: 在线数据处理关注于数据分析,OLTP: Online Transaction Processing
在线事务处理,类似于增删查改,专注于查询的速度 - 一致性hash,为了解决弹性集群动态扩容的问题,通常的做法是构造
0~2^32-1
个hash环状空间,对于具体的某个key找到对应服务器上节点时,只需要顺序在环上转动找到第一个命中的节点即可。当出现负载不均的情况时,可以考虑使用引入虚拟节点达到负载均衡的效果。 -
GC
: Garbage collection 垃圾回收机制,常见的垃圾回收算法有: 引用计数,标记清除Mark-Sweep
法,三色标记法,分代收集法。 - 引用计数:最基础的垃圾回收算法,例如C++中的
std:shared_ptr
- 标记清除:分为两个步骤:1. Mark: 从程序的根节点开始,遍历所有的对象,对可达的节点进行标记。2. Sweep: 将所有未标记的节点对象作为垃圾进行回收清除操作。有一个问题是会有
STW(Stop The World)
的问题,算法进行标记时会暂停整个应用程序。当程序中的对象特别多时,遍历整个对象树会消耗很多的时间。go1.3 - 三色标记:是
Mark-Sweep
的一个改进,支持并发,即可以实现on-the-fly
即在程序执行的同时进行垃圾回收 go1.6. 步骤如下:
1. 首先创建三个集合:白、灰、黑。
2. 将所有对象放入白色集合中。
3. 然后从根节点开始遍历所有对象(注意这里**并不递归遍历**),把遍历到的对象从白色集合放入灰色集合。
4. 之后遍历灰色集合,将灰色对象引用的对象从白色集合放入灰色集合,之后将此灰色对象放入黑色集合
5. 重复 4 直到灰色中无任何对象
6. 通过write-barrier检测对象有变化,重复以上操作
7. 集所有白色对象(垃圾)
- 分代收集:也是
Mark-Sweep
的一个改进。该算法基于一个经验:绝大多数的对象生命周期都很短,按照对象生命周期的长短来分代。
一般分代GC,分为3代,新生代(Young Generation),年老代(Tenured Generation),永久代(Permanent Generation)
原理如下:
1. 新对象放入第 0 代
2. 当内存用量超过一个较小的阈值时,触发 0 代收集
3. 第 0 代幸存的对象(未被收集)放入第 1 代
4. 只有当内存用量超过一个较高的阈值时,才会触发 1 代收集,2 代同理
因为 0 代中的对象十分少,所以每次收集时遍历都会非常快(比 1 代收集快几个数量级)。只有内存消耗过于大的时候才会触发较慢的 1 代和 2 代收集。