加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

Golang编程经验总结

发布时间:2020-12-16 19:24:25 所属栏目:大数据 来源:网络整理
导读:所有分类 开发语言与工具 Google Go/Golang开发 Golang编程经验总结 您的评价 : 较差 收藏该经验 如何选择web框架: 首先Golang语言开发web项目不一定非要框架,本身已经提供了Web开发需要的一切必要技术。当然如果想要ruby里面Rail那种高层次全栈式的MVC框
所有分类> 开发语言与工具> Google Go/Golang开发

Golang编程经验总结

您的评价:
较差
收藏该经验

如何选择web框架:
首先Golang语言开发web项目不一定非要框架,本身已经提供了Web开发需要的一切必要技术。当然如果想要ruby里面Rail那种高层次全栈式的MVC框架, Golang里面暂时没有,但是不是所有人都喜欢这种复杂的框架。Golang里面一些应用层面的技术需要自己去组装,比如session,cache,log等等. 可选择的web框架有martini, goji等,都是轻量级的。


Golang的web项目中的keepalive

关于keepalive, 是比较复杂的, 注意以下几点:

  1. http1.1 默认支持keepalive, 但是不同浏览器对keepalive都有个超时时间, 比如firefox:
    默认超时时间115秒, 不同浏览器不一样;

  2. Nginx默认超时时间75秒;

  3. golang默认超时时间是无限的, 要控制golang中的keepalive可以设置读写超时, 举例如下:

?
1
2
3
4
5
6
7
8
server := &http.Server{
Addr: ":9999" ,
Handler: framework,
ReadTimeout: 32 * time.Second,
WriteTimeout: 32 * time.Second,
MaxHeaderBytes: 1 << 20,
}
server.ListenAndServe()


github.com/go-sql-driver/mysql使用主意事项:

这是使用率极高的一个库, 在用它进行事务处理的情况下, 要注意一个问题, 由于它内部使用了连接池, 使用事务的时候如果没有Rollback或者Commit, 这个取出的连接就不会放回到池子里面, 导致的后果就是连接数过多, 所以使用事务的时候要注意正确地使用。


github.com/garyburd/redigo/redis使用注意事项:

这也是一个使用率极高的库, 同样需要注意,它是支持连接池的, 所以最好使用连接池, 正确的用法是这样的:

8
9
10
11
12
13
14
15
16
17
18
19
20
21
func initRedis(host string) *redis.Pool {
return &redis.Pool{
MaxIdle: 64,182)!important; background:none!important">IdleTimeout: 60 * time.Second,182)!important; background:none!important">TestOnBorrow: func(c redis.Conn,t time.Time) error {
_,err := c.Do( "PING" )
err
},
Dial: func() (redis.Conn,error) {
c,err := redis.Dial( "tcp" if err != nil {
nil,err
}
"SELECT"
c,err
}
}


另外使用的时候也要把连接放回到池子里面, 否则也会导致连接数居高不下。用完之后调用rd.Close(), 这个Close并不是真的关闭连接,而是放回到池子里面。



如何全局捕获panic级别错误:

5
defer func() {
err := recover(); err != nil {
lib.Log4e( "Panic error" }
}()

1. 需要注意的是捕获到pannic之后, 程序的执行点不会回到触发pannic的地方,需要程序再次执行, 一些框架支持这一点,比如martini里面有c.Next()。


2. 如果程序main里启动了多个goroutine, 每个goroutine里面都应该捕获pannic级别错误, 否则某个goroutine触发panic级别错误之后,整个程序退出, 这是非常不合理的。


最容易出错的地方:

使用指针,但是没有判断指针是否为nil, Golang中array, struct是值语义, slice,map, chanel是引用传递。


如何获取程序执行栈:


21
22
var
st = func(all bool) string {
// Reserve 1K buffer at first
buf := make([]byte,512)
for {
size := runtime.Stack(buf,all)
// The size of the buffer may be not enough to hold the stacktrace,133)!important; background:none!important">// so double the buffer size
size == len(buf) {
buf = make([]byte,len(buf)<<1)
continue
}
break
}
string(buf)
}
"panic:" + toString(err) + "nstack:" + st( false ))
}
}()


具体方法就是调用 runtime.Stack。


如何执行异步任务:

比如用户提交email,给用户发邮件, 发邮件的步骤是比较耗时的, 这个场景适合可以使用异步任务:

17
result := global.ResponseResult{ErrorCode: 0,ErrorMsg: "GetInviteCode success!" }
render.JSON(200,&result)
go func() {
type data struct {
Url string
}
name := "beta_test"
subject := "We would like to invite you to the private beta of Screenshot."
url := config.HttpProto + r.Host + "/user/register/" + *uniqid
html := ParseMailTpl(&name,&beta_test_mail_content,data{url})
e := this .SendMail(mail,subject,html.String())
e != nil {
lib.Log4w( "GetInviteCode,SendMail faild" } else {
}
思路是启动一个goroutine执行异步的操作, 当前goroutine继续向下执行。特别需要注意的是新启动的个goroutine如果对全局变量有读写操作的话,需要注意避免发生竞态条件, 可能需要加锁。


如何使用定时器:

通常情况下, 写一些定时任务需要用到crontab, 在Golang里面是不需要的, 提供了非常好用的定时器。举例如下:

func Init() {
ticker := time.NewTicker(30 * time.Minute)
{
select {
case c := <-global.TaskCmdChannel:
switch *c {
case "a" :
//todo
}
c := <-global.TaskImageMessageChannel:
m := new (model.TaskModel)
m.Init()
m.CreateImageMessage(c)
m = nil
<-ticker.C:
(model.TaskModel)
m.Init()
m.CleanUserExpiredSessionKey()
m = nil
}
多goroutine执行如果避免发生竞态条件:

Data races are among the most common and hardest to debug types of bugs in concurrent systems. A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write. See the The Go Memory Model for details.

官方相关说明:

http://blog.golang.org/race-detector

http://golang.org/ref/mem


多goroutine执行,访问全局的变量,比如map,可能会发生竞态条件, 如何检查呢?首先在编译的时候指定 -race参数,指定这个参数之后,编译出来的程序体积大一倍以上, 另外cpu,内存消耗比较高,适合测试环境, 但是发生竞态条件的时候会panic,有详细的错误信息。go内置的数据结构array,slice, map都不是线程安全的


没有设置runtime.GOMAXPROCS会有竞态条件的问题吗?

答案是没有, 因为没有设置runtime.GOMAXPROCS的情况下, 所有的goroutine都是在一个原生的系统thread里面执行, 自然不会有竞态条件。


如何充分利用CPU多核:

runtime.GOMAXPROCS(runtime.NumCPU() * 2)以上是根据经验得出的比较合理的设置。


解决并发情况下的竞态条件的方法:

1. channel, 但是channel并不能解决所有的情况,channel的底层实现里面也有用到锁, 某些情况下channel还不一定有锁高效, 另外channel是Golang里面最强大也最难掌握的一个东西, 如果发生阻塞不好调试。

2. 加锁, 需要注意高并发情况下,锁竞争也是影响性能的一个重要因素, 使用读写锁,在很多情况下更高效, 举例如下:

mu sync.RWMutex
mu.RLock()
defer mu.RUnlock()
conns := h.all_connections[img_id]
_,c := range conns {
c == nil /*|| c.uid == uid */ {
select {
c.send <- []byte(message):
default :
h.conn_unregister(c)
使用锁有个主意的地方是避免死锁,比如循环加锁。3. 原子操作(CAS),Golang的atomic包对原子操作提供支持,Golang里面锁的实现也是用的原子操作。


获取程序绝对路径:

Golang编译出来之后是独立的可执行程序, 不过很多时候需要读取配置,由于执行目录有时候不在程序所在目录,路径的问题经常让人头疼,正确获取绝对路径非常重要, 方法如下:

7
func GetCurrPath() string {
file,_ := exec.LookPath(os.Args[0])
path,_ := filepath.Abs(file)
index := strings.LastIndex(path,string(os.PathSeparator))
ret := path[:index]
ret
}



Golang函数默认参数:

大家都知道Golang是一门简洁的语言, 不支持函数默认参数. 这个特性有些情况下确实是有用的,如果不支持,往往需要重写函数,或者多写一个函数。其实这个问题非常好解决, 举例如下:

?
10
func ( *ImageModel) GetImageListCount(project_id int64,paramter_optional ...int) int {
t int
expire_time := 600
len(paramter_optional) > 0 {
expire_time = paramter_optional[0]
}
...
性能监控:

11
go func() {
profServeMux := http.NewServeMux()
profServeMux.HandleFunc( "/debug/pprof/" "/debug/pprof/cmdline" "/debug/pprof/profile" "/debug/pprof/symbol" err := http.ListenAndServe( ":7789" err != nil {
panic(err)
}
接下来就可以使用go tool pprof分析。



如何进行程序调试:

对于调试,每个人理解不一样, 如果要调试程序功能, 重新编译即可, Golang的编译速度极快。如果在开发的时候调试程序逻辑, 一般用log即可, Golang里面最好用的log库是log4go, 支持log级别。如果要进行断点调试, GoEclipse之类的是支持的, 依赖Mingw和GDB, 我个人不习惯这种调试方法。


守护进程(daemon)

下面给出完整的真正可用的例子:

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
package main
import (
"fmt"
"log"
"os"
"runtime"
"syscall"
"time"
)
func daemon(nochdir,noclose int) int {
ret,ret2 uintptr
err syscall.Errno
darwin := runtime.GOOS == "darwin"
// already a daemon
syscall.Getppid() == 1 {
0
}
// fork off the parent process
ret,ret2,err = syscall.RawSyscall(syscall.SYS_FORK,0)
err != 0 {
-1
}
// failure
ret2 < 0 {
os.Exit(-1)
}
// handle exception for darwin
darwin && ret2 == 1 {
ret = 0
}
// if we got a good PID,then we call exit the parent process.
ret > 0 {
os.Exit(0)
}
/* Change the file mode mask */
_ = syscall.Umask(0)
// create a new SID for the child process
s_ret,s_errno := syscall.Setsid()
s_errno != nil {
log.Printf( "Error: syscall.Setsid errno: %d" }
s_ret < 0 {
-1
}
nochdir == 0 {
os.Chdir( "/" )
}
noclose == 0 {
f,e := os.OpenFile( "/dev/null" e == nil {
fd := f.Fd()
syscall.Dup2(int(fd),int(os.Stdin.Fd()))
}
}
0
}
func main() {
daemon(0,1)
{
fmt.Println( "hello" )
time.Sleep(1 * time.Second)
}
进程管理:

个人比较喜欢用supervisord来进行进程管理,支持进程自动重启,supervisord是一个python开发的工具, 用pip安装即可。


代码热更新:

代码热更新一直是解释型语言比较擅长的,Golang里面不是做不到,只是稍微麻烦一些, 就看必要性有多大。如果是线上在线人数很多, 业务非常重要的场景, 还是有必要, 一般情况下没有必要。

  1. 更新配置.因为配置文件一般是个json或者ini格式的文件,是不需要编译的, 在线更新配置还是相对比较容易的, 思路就是使用信号, 比如SIGUSER2, 程序在信号处理函数中重新加载配置即可。

  2. 热更新代码.目前网上有多种第三方库, 实现方法大同小异。先编译代码(这一步可以使用fsnotify做到监控代码变化,自动编译),关键是下一步graceful restart进程,实现方法可参考:http://grisha.org/blog/2014/06/03/graceful-restart-in-golang/也是创建子进程,杀死父进程的方法。

条件编译:

条件编译时一个非常有用的特性,一般一个项目编译出一个可执行文件,但是有些情况需要编译成多个可执行文件,执行不同的逻辑,这比通过命令行参数执行不同的逻辑更清晰.比如这样一个场景,一个web项目,是常驻进程的, 但是有时候需要执行一些程序步骤初始化数据库,导入数据,执行一个特定的一次性的任务等。假如项目中有一个main.go,里面定义了一个main函数,同目录下有一个task.go函数,里面也定义了一个main函数,正常情况下这是无法编译通过的, 会提示“main redeclared”。解决办法是使用go build 的-tags参数。步骤如下(以windows为例说明):

1.在main.go头部加上// +build main

2. 在task.go头部加上// +build task

3. 编译住程序:go build -tags 'main'

4. 编译task:go build -tags 'task' -o task.exe


官方说明:

Build Constraints


A build constraint is a line comment beginning with the directive +build that lists the conditions under which a file should be included in the package. Constraints may appear in any kind of source file (not just Go),but they must appear near the top of the file,preceded only by blank lines and other line comments.


To distinguish build constraints from package documentation,a series of build constraints must be followed by a blank line.



如果将项目有关资源文件打包进主程序:

使用go generate命令,参考godoc的实现。



与C/C++ 交互

1.Cgo,Cgo支持Golang和C/C++混编, 在Golang里面使用pthread,libuv之类的都不难,github上也有相关开源代码;

2.Swig, 很多库都用Swig实现了Golang的绑定,Swig也可以反向回调Golang代码。

3. syscall包, 该包让你以Golang的方式进行系统编程,不需要再使用C/C++syscall提供了很多系统接口,比如epoll,原始socket套接字编程接口等。


其他:

近几年最热门的技术之一Docker是用Golang开发的, 已经有相关的书出版, 对系统运维,云计算感兴趣的可以了解。

来自:http://blog.csdn.net/yxw2014/article/details/43451625

相关文档 —  更多
  • Dive into Golang.pdf
  • golang web开发.pdf
  • Go Web 编程.pdf
  • 一个Python老兵眼中的golang.pdf
  • Effective Go(中文版).pdf
  • Wide 用户指南.pdf
  • VC编程经验总结.chm
  • Ruby 经验总结.doc
  • LoadRunner 性能测试经验总结.pdf
  • DB2 调优经验总结.doc
  • OSCache使用经验总结.doc
  • SQL Sever2005 优化经验总结.pdf
  • hadoop2.2+hbase0.96+hive0.12 安装整合详细高可靠文档及经验总结.pdf
  • QTP 学习与实践经验总结.doc
  • qtp学习与实践经验总结1.doc
  • 大型信息系统工程建设经验总结.doc
  • 从学校出来一路狂奔一年多的工作对于SSH项目的一些经验总结.pdf
  • JDBC连接数据库经验总结 .doc
  • Hive开发经验问答式总结.pdf
  • C#经典项目经验总结.doc
经验标签
Golang
同类热门经验
  • Go语言的fmt包中文教程
    6953次浏览
  • Golang测试技术
    3374次浏览
  • Go语言(golang)开源项目大全
    13299次浏览
  • Go语言开发工具 LiteIDE
    6481次浏览
  • GO语言标准库概览
    5415次浏览
  • 顺手的Go语言编辑器
    8380次浏览
相关经验
  • Golang的演化历程
    0人评
  • Golang环境配置建议
    Docker+Golang+Postgres实战
    zimg的golang版本:Gimg
    Golang FTP 库:goftp
    Golang测试技术
    GoLang 的时间处理
    Golang Import使用入门
    用Swift写的Golang学习App
    Golang channels 教程
    Golang基础学习总结
    Golang通过http代理抓取页面代码
    Golang 环境配置建议(Atom)
    golang的web框架:wgf
    0人评
相关讨论 - 更多
  • 项目管理心得:一个项目经理的个人体会、经验总结
  • 编程趋势
  • Web编程是函数式编程
  • Javascript 面向对象编程
  • 编程范式,程序员的编程世界观
  • 各种流行的编程风格
  • 编程高手与调试高手
猜你在找
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!