Golang 技术博客 http://beego.me Golang 技术相关的博客,推荐相关的Go开源库 Wed, 07 Nov 2012 01:30:23 +0000 zh-CN hourly 1 Golang import使用入门 http://beego.me/index.php/2012/11/06/golang-import%e4%bd%bf%e7%94%a8%e5%85%a5%e9%97%a8/ http://beego.me/index.php/2012/11/06/golang-import%e4%bd%bf%e7%94%a8%e5%85%a5%e9%97%a8/#comments Tue, 06 Nov 2012 13:59:33 +0000 admin http://beego.me/?p=54 Read more »]]> 我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下:

import(
    "fmt"
)

然后我们代码里面可以通过如下的方式调用

fmt.Println("hello world")

上面这个fmt是Go语言的标准库,他其实是去goroot下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:

1.相对路径

import “./model” //当前文件同一目录的model目录,但是不建议这种方式来import

2.绝对路径

import “shorturl/model” //加载gopath/src/shorturl/model模块

上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事

1.点操作

我们有时候会看到如下的方式导入包

import(
    . "fmt"
)

这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println("hello world")可以省略的写成Println("hello world")

2.别名操作

别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字

import(
    f "fmt"
)

别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println("hello world")

3._操作

这个操作经常是让很多人费解的一个操作符,请看下面这个import

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有一定的帮助。

]]>
http://beego.me/index.php/2012/11/06/golang-import%e4%bd%bf%e7%94%a8%e5%85%a5%e9%97%a8/feed/ 3
[转摘]用 Go 实现零停机升级 TCP 服务 http://beego.me/index.php/2012/10/26/%e8%bd%ac%e6%91%98%e7%94%a8-go-%e5%ae%9e%e7%8e%b0%e9%9b%b6%e5%81%9c%e6%9c%ba%e5%8d%87%e7%ba%a7-tcp-%e6%9c%8d%e5%8a%a1/ http://beego.me/index.php/2012/10/26/%e8%bd%ac%e6%91%98%e7%94%a8-go-%e5%ae%9e%e7%8e%b0%e9%9b%b6%e5%81%9c%e6%9c%ba%e5%8d%87%e7%ba%a7-tcp-%e6%9c%8d%e5%8a%a1/#comments Fri, 26 Oct 2012 08:21:36 +0000 admin http://beego.me/?p=46 Read more »]]> 零停机升级几乎是现代网络服务的标配,其实现原理并不复杂……blablabla……( 从文件描述符讲起,省略一万字)。现在有人确认 Go 也可实现零停机升级 TCP 服务或者更加简短的叫法——热更新。

原文在此:Zero Downtime upgrades of TCP servers in Go

用 Go 实现零停机升级 TCP 服务

最近在 golang-nuts 邮件列表上有篇帖子提到 Nginx 可以保持服务的时候进行升级,而无需停止它正在监听的 socket。秘诀是取消监听的 socket 上的 close-on-exec,然后 fork 并运行一个新的服务(用升级后的二进制文件),并用参数告诉它使用继承的文件描述符,而不是调用 scoket() 和 listen(s)。

于是我就想试试在 Go 中是否能做到同样的事情,以及对于标准库需要做什么样的修改来达到这个效果。最终我实现了这个功能,而且只需要很小的修改,接下来会解释一下是如何做到的。

 

相关的代码在这里

在这个程序里有许多有趣的东西,我会逐一介绍。然后,我使用了“接口注入”的模式。这在 Go 中是一个重要的模式,但我不认为这个模式被广泛接受并编写进入文档。

当我开始思考这个问题的时候,我意识到其中的一个问题是需要在 http.(Server).Serve 内部找到方法,当旧的服务器正确关闭的时候,让其停止调用 Accept()。问题是那里没有钩子;唯一的跳出循环(“Accept,开启一个 goroutine 来处理,然后重复这个过程”)的办法是 Accept 返回一个错误。但是如果你认为 Accept 是一个系统调用,你可能会想:“我不能进入其中,并插入一个错误”。但是 Accept() 不是一个系统调用:它是 net.Listener 的一个接口。这意味着如果创建一个实现了 net.Listener 的自有对象,就可以将其传递给 http.(Server).Serve 然后在 Accept() 中做想做的事情。

在第一次了解结构的内嵌类型时,我非常迷惑并失去了方向。而尝试的时候,得到了各种混合的指针,并且发生了许多无法解释的空指针错误。这次,我又重新阅读代码,并有了一个大致的概念。当想要注入某个接口的方法时,类型嵌入是必须的。这使得可以继承全部底层对象的实现,然后根据需要重新定义。参阅在 upgradable.go 中的 stoppableListener。net.Listener 接口需要三个方法,包括 Accept, Close 和 Addr。但是我只定义了其中的一个:Accept()。stoppableListener 为何能实现了 net.Listener 呢?因为另外两个方法通过嵌入的方式实现了。只有 Accept() 有更加明确的定义。当编写 Accept() 的时候,我需要明确指出如何同底层对象通信,以便传递 Accept() 的调用。这里的诀窍就是要理解嵌入类型是如何在结构体中使用类型名创建了一个新的字段。因此可以通过 stoppableListenersl 的 sl.Listener 来调用其中的 net.Listener,同时可以通过 sl.Listener.Accept() 调用内部的 Accept()。

接下来,考虑如何处理 Serve() 的“停止”错误。用 os.Exit(0) 立刻退出是不正确的,因为可能还有 goroutine 正在服务 HTTP 客户端。需要某种办法了解到所有的客户端都已经完成。再次利用注入,可以将 Accept() 返回的 net.Conn 进行封装,然后来检测当前运行的连接的数量。这个注入 net.Conn 对象的技术还会有一些其他有趣的应用。例如,通过捕获 Read() 或者 Write() 调用,可以对连接进行强制限速,而无需协议上的任何实现。甚至可以投机取巧地做一些加密之类的蠢事,同样,无需在协议上进行。

当我确信可以完美的关闭服务之后,就需要了解如何在正确的文件描述符上启动一个新的服务。这是通过对 net 包的一个相当简单的改动实现的。参阅补丁。由于我的懒惰,仅仅在 TCPListener 上实现了。理论上,对于使用其他 socket 的服务的零停机升级也是可能的,不过对于 net 包的修改仅仅适用 TCP 服务。Rog Peppe 向我指出了 net.FileListener 对象可以从 *os.File 创建(可以使用 os.NewFile 生成)。

最后一个问题是 net 总是会在其打开的 socket 文件描述符设置 close-on-exec 标识。因此需要在监听的 socket 上关闭它,这样文件描述符在新的进程中也可使用。这需要在 syscall 库上增加一些东西(关闭或不关闭)。

我手工测试了工作正常(在其他窗口用命令行 GET 调用)。同时使用 http_load 测试了负载。在 20 秒的基准负载测试中,得到了 3937 请求/秒的成绩;然后再次测试,添加 “GET http://localhost:8000/upgrade”,同时在负载测试的时候执行了若干次二进制文件的替换,这次得到了 3880 请求/秒的成绩!这很酷!

 

转摘来自(http://www.mikespook.com/2012/05/%E7%BF%BB%E8%AF%91%E7%94%A8-go-%E5%AE%9E%E7%8E%B0%E9%9B%B6%E5%81%9C%E6%9C%BA%E5%8D%87%E7%BA%A7-tcp-%E6%9C%8D%E5%8A%A1/)

]]>
http://beego.me/index.php/2012/10/26/%e8%bd%ac%e6%91%98%e7%94%a8-go-%e5%ae%9e%e7%8e%b0%e9%9b%b6%e5%81%9c%e6%9c%ba%e5%8d%87%e7%ba%a7-tcp-%e6%9c%8d%e5%8a%a1/feed/ 0