使用cgo实现高性能WebSocket服务

Go 语言的便捷,协程的高效,GC以及自身的安全性,使得它成为后端业务开发的利器. 之前看过来自 Mail.Ru 工程师分享 Go的百万连接数的WebSocket服务优化 ,*中文翻译版.描述通过使用优化 Go 协程和零拷贝Http Header 实现了对内存的高效利用. 但其中自定义的 epoll 结构中依旧用了锁,个人觉得还是有优化的空间. 可以用高效的 uWebSockets 库配合,使得接入层用高效的 C++ 语言,而后端业务处理则用 Go 语言.这样的好处就是 C++ 可以最高效精准的把控接入层的逻辑处理和内存的使用,而后端的业务开发则是以高效安全为主,使用 Go 正好合适. 这里使用的 uWebSockets 为 0.14.8 tag 的版本,配合 libuv作为事件驱动,zlib 作为压缩算法库,openssl 作为加密库,四个编译为 .a类型的静态库,方便一同编译到程序中,避免运行时动态库缺失的问题. … “使用cgo实现高性能WebSocket服务”

Read More

Go的sync.Pool

sync.Pool是来自Go 1.3版本加入的特性,可以被看做是存放可被重用的值的容器,这个容器具有以下特性: 1. 可自动伸缩 2. 高效 3. 并发安全 因此这个类设计的目的是用来保存和复用临时对象,以减少内存分配,降低CG压力,但是由于这个原因,所以它并不可靠,因此每次GC,都会首先清楚缓冲区,假如一个slice是存放在 Pool 中,而又没有其他地方引用,则会被当成垃圾清理掉,因此切勿用sync.Pool创建socket连接. 通过sync.Pool的源码,可以看出Pool创建的时候是不能指定大小的,所有sync.Pool的缓存对象数量是没有限制的,它只受限于内存大小. 通过Get方法获取到的值是任意的.如果一个临时对象池的Put方法未被调用过,且它的New字段也未曾被赋予一个非nil的函数值,那么它的Get方法返回的结果值就一定会是nil. Get方法返回的不一定就是存在于池中的值.不过,如果这个结果值是池中的,那么在该方法返回它之前就一定会把它从池中删除掉. 一个例子: var bytePool = sync.Pool{ New: func() interface{} { b := make([]byte, 1024) return &b }, } func main() { a … “Go的sync.Pool”

Read More

Go的defer

defer⽤于延迟一个函数或者方法(或者当前所创建的匿名函数)的执行,并且defer语句只能出现在函数或方法的内部. defer语句经常被用于处理成对的操作,如文件的打开与关闭、连接的建立与断开、加锁、释放锁. 通过defer机制,不论函数逻辑多复杂都能保证在任何执行路径下资源被释放.释放资源的defer应该直接跟在请求资源的语句后. 如果一个函数中有多个defer语句,它们会以后进先出的顺序执行,哪怕函数或某个延迟调用发生错误,这些调用依旧保证会被执⾏. 多个defer: func test(x int) { fmt.Println(1 / x)//x为0时,产生异常 } func main() { defer fmt.Println(“aaaaaaaa”) defer test(0) defer fmt.Println(“bbbbbbbb”) defer fmt.Println(“cccccccc”) } 输出: cccccccc bbbbbbbb aaaaaaaa panic: runtime error: integer divide by … “Go的defer”

Read More

Go的slice

经常用C中的数组,就会知道数组存在一个不足,那就是一旦容量分配了,那就是固定长度,无法再更改. 而Golang中的slice并不是数组或数组指针,其本质上是一个数组的某个部分的引用。在源码runtime/slice.go源码中定义了slice的结构: type slice struct { array unsafe.Pointer len int cap int } 可以看到slice是一个包含3个字段的结构体: array: 指向slice第一个元素的指针 len: slice的长度,长度是索引操作的上届, s[i] i<len(s) cap: slice的容量,容量是分割操作的上届, s[i:j] j<=cap(s) slice的使用 slice和数组的区别: 声明数组时,方括号内写明了数组的长度或使用…自动计算长度;而声明slice时,方括号内没有任何字符. var s1 []int //声明切片和声明array一样,只是少了长度,此为空(nil)切片 s2 := []int{} //make([]T, … “Go的slice”

Read More

Golang的channel

Go推荐的并发模型为CSP(Communicating Sequential Process),Channel是CSP的核心。 CSP并发模型不同于传统的多线程并发通过共享内存来通信,CSP是以通信的方式来共享内存。 Go并发的核心哲学是尽量通过消息传递取代共享内存来尽可能的避免显示锁。 在Go中,channel要求操作双方必须明确数据类型,操作的一方并不关心另外一端操作者的数量。 可以把channel理解成一个队列,但有其特殊性,从同步和异步两种模式来理解: 同步模式:要求channel的发送方和接收方成对出现,如果一方没有就绪,则另一方将阻塞。 异步模式:异步模式下将抢夺channel的缓冲区,发送方要求有缓冲区有空位可写入,如果没有则发送方阻塞;接收方要求有缓存数据可读取,如果没有则接收方阻塞。 1.带缓冲区的channel c := make(chan string, 3) c <- “hello” c <- “world” //异步通道缓冲区未满,写入数据不会阻塞 fmt.Println(len(c), cap(c)) // 2 3 len和cap分别为已经缓冲数量和缓冲区大小 fmt.Println(<-c) fmt.Println(<-c) // 缓冲区中还有数据,读取数据不会阻塞 2.从channel中接收数据 第一种直接读取: msg … “Golang的channel”

Read More