从学习Go到自己做项目也经过了差不多两三年的时间,对于Go谈一些自己的看法:
学习Go的话欢迎大家通过我写的书来学习,我已经开源在github: astaxie/build-web-application-with-golang · GitHub 还有如果你用来做API开发或者网络开发,那么我做的开源框架beego也许适合你,可以适当的来学习一下: astaxie/beego · GitHub
- Go有什么优势
- 可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了。
- 静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问题,动态语言的感觉就是有很多的包可以使用,写起来的效率很高。
- 语言层面支持并发,这个就是Go最大的特色,天生的支持并发,我曾经说过一句话,天生的基因和整容是有区别的,大家一样美丽,但是你喜欢整容的还是天生基因的美丽呢?Go就是基因里面支持的并发,可以充分的利用多核,很容易的使用并发。
- 内置runtime,支持垃圾回收,这属于动态语言的特性之一吧,虽然目前来说GC不算完美,但是足以应付我们所能遇到的大多数情况,特别是Go1.1之后的GC。
- 简单易学,Go语言的作者都有C的基因,那么Go自然而然就有了C的基因,那么Go关键字是25个,但是表达能力很强大,几乎支持大多数你在其他语言见过的特性:继承、重载、对象等。
- 丰富的标准库,Go目前已经内置了大量的库,特别是网络库非常强大,我最爱的也是这部分。
- 内置强大的工具,Go语言里面内置了很多工具链,最好的应该是gofmt工具,自动化格式化代码,能够让团队review变得如此的简单,代码格式一模一样,想不一样都很困难。
- 跨平台编译,如果你写的Go代码不包含cgo,那么就可以做到window系统编译linux的应用,如何做到的呢?Go引用了plan9的代码,这就是不依赖系统的信息。
- 内嵌C支持,前面说了作者是C的作者,所以Go里面也可以直接包含c代码,利用现有的丰富的C库。
- Go适合用来做什么
- 服务器编程,以前你如果使用C或者C++做的那些事情,用Go来做很合适,例如处理日志、数据打包、虚拟机处理、文件系统等。
- 分布式系统,数据库代理器等
- 网络编程,这一块目前应用最广,包括Web应用、API应用、下载应用、
- 内存数据库,前一段时间google开发的groupcache,couchbase的部分组建
- 云平台,目前国外很多云平台在采用Go开发,CloudFoundy的部分组建,前VMare的技术总监自己出来搞的apcera云平台。
- Go成功的项目
- nsq:bitly开源的消息队列系统,性能非常高,目前他们每天处理数十亿条的消息
- docker:基于lxc的一个虚拟打包工具,能够实现PAAS平台的组建。
- packer:用来生成不同平台的镜像文件,例如VM、vbox、AWS等,作者是vagrant的作者
- skynet:分布式调度框架
- Doozer:分布式同步工具,类似ZooKeeper
- Heka:mazila开源的日志处理系统
- cbfs:couchbase开源的分布式文件系统
- tsuru:开源的PAAS平台,和SAE实现的功能一模一样
- groupcache:memcahe作者写的用于Google下载系统的缓存系统
- god:类似redis的缓存系统,但是支持分布式和扩展性
- gor:网络流量抓包和重放工具 以下是一些公司,只是一小部分:
- http://Apcera.com
- http://Stathat.com
- Juju at Canonical/Ubuntu, presentation
- http://Beachfront.iO at Beachfront Media
- CloudFlare
- Soundcloud
- Mozilla
- Disqus
- http://Bit.ly
- Heroku
- youtube 下面列出来了一些使用的用户
- Go还存在的缺点 以下缺点是我自己在项目开发中遇到的一些问题:
- Go的import包不支持版本,有时候升级容易导致项目不可运行,所以需要自己控制相应的版本信息
- Go的goroutine一旦启动之后,不同的goroutine之间切换不是受程序控制,runtime调度的时候,需要严谨的逻辑,不然goroutine休眠,过一段时间逻辑结束了,突然冒出来又执行了,会导致逻辑出错等情况。
- GC延迟有点大,我开发的日志系统伤过一次,同时并发很大的情况下,处理很大的日志,GC没有那么快,内存回收不给力,后来经过profile程序改进之后得到了改善。
- pkg下面的图片处理库很多bug,还是使用成熟产品好,调用这些成熟库imagemagick的接口比较靠谱
学习Go的话欢迎大家通过我写的书来学习,我已经开源在github: astaxie/build-web-application-with-golang · GitHub 还有如果你用来做API开发或者网络开发,那么我做的开源框架beego也许适合你,可以适当的来学习一下: astaxie/beego · GitHub
Golang性能调优入门
| 1 Comment如何利用golang自带的profile工具进行应用程序的性能调优,前一段时间我 做的日志分析系统在线上遇到了一个问题,就是分任务的系统down机了,日志处理延迟了10几个小时,这个时候任务分发系统重启之后开始分发任务,但是一 下子就承受了十几个并发任务,导致内存消耗过快,直接吃掉了16G的内存,这可急坏了我啊。所以赶紧开始做性能优化。
性能优化我主要从以下几个方面进行了测试和调优:
性能优化我主要从以下几个方面进行了测试和调优:
- CPU Profiling
- Mem Profiling
- GC & HEAP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | package main import ( “fmt” “log” “os” “runtime” “runtime/debug” “runtime/pprof” “strconv” “sync/atomic” “syscall” “time” ) var heapProfileCounter int32 var startTime = time.Now() var pid int func init() { pid = os.Getpid() } func StartCPUProfile() { f, err := os.Create(“cpu-” + strconv.Itoa(pid) + “.pprof”) if err != nil { log.Fatal(err) } pprof.StartCPUProfile(f) } func StopCPUProfile() { pprof.StopCPUProfile() } func StartBlockProfile(rate int) { runtime.SetBlockProfileRate(rate) } func StopBlockProfile() { filename := “block-” + strconv.Itoa(pid) + “.pprof” f, err := os.Create(filename) if err != nil { log.Fatal(err) } if err = pprof.Lookup(“block”).WriteTo(f, 0); err != nil { log.Fatalf(” can’t write %s: %s”, filename, err) } f.Close() } func SetMemProfileRate(rate int) { runtime.MemProfileRate = rate } func GC() { runtime.GC() } func DumpHeap() { filename := “heap-” + strconv.Itoa(pid) + “-” + strconv.Itoa(int(atomic.AddInt32(&heapProfileCounter, 1))) + “.pprof” f, err := os.Create(filename) if err != nil { fmt.Fprintf(os.Stderr, “testing: %s”, err) return } if err = pprof.WriteHeapProfile(f); err != nil { fmt.Fprintf(os.Stderr, “testing: can’t write %s: %s”, filename, err) } f.Close() } func showSystemStat(interval time.Duration, count int) { usage1 := &syscall.Rusage{} var lastUtime int64 var lastStime int64 counter := 0 for { //http://man7.org/linux/man-pages/man3/vtimes.3.html syscall.Getrusage(syscall.RUSAGE_SELF, usage1) utime := usage1.Utime.Sec*1000000000 + usage1.Utime.Usec stime := usage1.Stime.Sec*1000000000 + usage1.Stime.Usec userCPUUtil := float64(utime-lastUtime) * 100 / float64(interval) sysCPUUtil := float64(stime-lastStime) * 100 / float64(interval) memUtil := usage1.Maxrss * 1024 lastUtime = utime lastStime = stime if counter > 0 { fmt.Printf(“cpu: %3.2f%% us %3.2f%% sy, mem:%s \n”, userCPUUtil, sysCPUUtil, toH(uint64(memUtil))) } counter += 1 if count >= 1 && count < counter { return } time.Sleep(interval) } } func ShowSystemStat(seconds int) { go func() { interval := time.Duration(seconds) * time.Second showSystemStat(interval, 0) }() } func PrintSystemStats() { interval := time.Duration(1) * time.Second showSystemStat(interval, 1) } func ShowGCStat() { go func() { var numGC int64 interval := time.Duration(100) * time.Millisecond gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)} memStats := &runtime.MemStats{} for { debug.ReadGCStats(gcstats) if gcstats.NumGC > numGC { runtime.ReadMemStats(memStats) printGC(memStats, gcstats) numGC = gcstats.NumGC } time.Sleep(interval) } }() } func PrintGCSummary() { memStats := &runtime.MemStats{} runtime.ReadMemStats(memStats) gcstats := &debug.GCStats{PauseQuantiles: make([]time.Duration, 100)} debug.ReadGCStats(gcstats) printGC(memStats, gcstats) } func printGC(memStats *runtime.MemStats, gcstats *debug.GCStats) { if gcstats.NumGC > 0 { lastPause := gcstats.Pause[0] elapsed := time.Now().Sub(startTime) overhead := float64(gcstats.PauseTotal) / float64(elapsed) * 100 allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() fmt.Printf(“NumGC:%d Pause:%s Pause(Avg):%s Overhead:%3.2f%% Alloc:%s Sys:%s Alloc(Rate):%s/s Histogram:%s %s %s \n”, gcstats.NumGC, toS(lastPause), toS(avg(gcstats.Pause)), overhead, toH(memStats.Alloc), toH(memStats.Sys), toH(uint64(allocatedRate)), toS(gcstats.PauseQuantiles[94]), toS(gcstats.PauseQuantiles[98]), toS(gcstats.PauseQuantiles[99])) } else { // while GC has disabled elapsed := time.Now().Sub(startTime) allocatedRate := float64(memStats.TotalAlloc) / elapsed.Seconds() fmt.Printf(“Alloc:%s Sys:%s Alloc(Rate):%s/s\n”, toH(memStats.Alloc), toH(memStats.Sys), toH(uint64(allocatedRate))) } } func avg(items []time.Duration) time.Duration { var sum time.Duration for _, item := range items { sum += item } return time.Duration(int64(sum) / int64(len(items))) } // human readable format func toH(bytes uint64) string { switch { case bytes < 1024: return fmt.Sprintf(“%dB”, bytes) case bytes < 1024*1024: return fmt.Sprintf(“%.2fK”, float64(bytes)/1024) case bytes < 1024*1024*1024: return fmt.Sprintf(“%.2fM”, float64(bytes)/1024/1024) default: return fmt.Sprintf(“%.2fG”, float64(bytes)/1024/1024/1024) } } // short string format func toS(d time.Duration) string { u := uint64(d) if u < uint64(time.Second) { switch { case u == 0: return “0″ case u < uint64(time.Microsecond): return fmt.Sprintf(“%.2fns”, float64(u)) case u < uint64(time.Millisecond): return fmt.Sprintf(“%.2fus”, float64(u)/1000) default: return fmt.Sprintf(“%.2fms”, float64(u)/1000/1000) } } else { switch { case u < uint64(time.Minute): return fmt.Sprintf(“%.2fs”, float64(u)/1000/1000/1000) case u < uint64(time.Hour): return fmt.Sprintf(“%.2fm”, float64(u)/1000/1000/1000/60) default: return fmt.Sprintf(“%.2fh”, float64(u)/1000/1000/1000/60/60) } } } |
Golang Import使用入门
| 0 Commentsimport( "fmt" )
然后我们代码里面可以通过如下的方式调用fmt.Println("hello world")
上面这个fmt是Go语言的标准库,他其实是去goroot下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:- 相对路径 import “./model” //当前文件同一目录的model目录,但是不建议这种方式来import
- 绝对路径 import “shorturl/model” //加载gopath/src/shorturl/model模块
- 点操作
import( . “fmt” ) 这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println(“hello world”)可以省略的写成Println(“hello world”)
- 别名操作
import( f “fmt” ) 别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println(“hello world”)
- _操作
import ( “database/sql” _ “github.com/ziutek/mymysql/godrv” ) _操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数,要理解这个问题,需要看下面这个图,理解包是怎么按照顺序加载的:
程序的初始化和执行都起始于main包。如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它 只会被导入一次(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。当一个包被导入时,如果该包还导入了其它的包,那么会先 将其它包导入进来,然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。等所有被导入的包都加载完毕了,就会开 始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),最后执行main函数。下图详细地解释了整个执行过 程:
通过上面的介绍我们了解了import的时候其实是执行了该包里面的init函数,初始化了里面的变量,_操作只是说该包引入了,我只初始化里面的 init函数和一些变量,但是往往这些init函数里面是注册自己包里面的引擎,让外部可以方便的使用,就很多实现database/sql的引起,在 init函数里面都是调用了sql.Register(name string, driver driver.Driver)注册自己,然后外部就可以使用了。
这样我们就介绍完了全部import的情况,希望对你理解Go的import有一定的帮助.