Go 语言初步

标签: 语言与设计 | 发表时间:2010-11-18 06:39 | 作者:云风 wei
出处:http://blog.codingnow.com/

这几天认真玩起了 Go。所谓认真玩,就是拿 Go 写点程序,前后大约两千行吧。

据说 Go 的最佳开发平台是 Mac OS ,我没有。其次应该是 Linux 。Windows 版还没全部搞定,但是也可以用了。如果你用 google 搜索,很容易去到一个叫 go-windows 的开源项目上。千万别上当,这是个废弃的项目。如果你用这个,很多库都没有,而且语法也是老的。我在 Windows 下甚至不能正确链接自己写的多个 package 。活跃的 Windows 版是 gomingw ,对于 Windows 用户,装一个 mingw32 以后就可以开始玩了。

就三天来实战经历,我喜欢上这门新语言有如下原因:

mix-in 的接口风格。非常接近于我在用 C 时惯用的面向对象风格。有语法上的支持要舒服多了。以平坦的方式编写函数,没有层次。而后用 interface 把需要的功能聚合在一起。没有继承层次,只有组合功能。

强类型系统。使得犯错误的机会大大降低。正确通过编译,几乎就没有什么 bug 了。而编写程序又有点使用 lua 这种动态语言的感觉,总之,写起来很舒服。

内置的 string / slice 类型,以及 gc 。这是我觉得现代编程必须的东西。手工管理未必有更高的效率,但一定有更多的出错机会。至少,我一直主张有一个方便的 string 不变量的基本类型的(参见这一篇)。

defer 是个有趣使用的东西,用它来实现 RAII 比 C++ 利用栈上对象的析构函数的 trick 方案让人塌实多了。go 在语言设计上是很吝啬新的关键字的。但多出一个关键字 defer ,并用内建函数 panic / recover 来解决许多看似应该用 exception 解决的问题要漂亮的多。

zero 初始化。我一直觉得 C++ 的构造函数特别多余。按我用 C 的惯例,一切数据结构都应该用 0 初始化。所以 C 里有 calloc 这个函数。go 把这点贯彻了。不会再有未定义的数据。

包系统特别的好。而且严格定义了包的初始化过程,即 init 函数。在我自己的 C 语言构建的项目中,实现了几乎一样的机制,甚至也叫 init 。但是有语言层面的支持就是好。对,只有 init 没有 exit 。正合我意。

goroutine 是个相当有用的设计。8 年前,我给 C 实现了 coroutine 库,并用在项目里,并坚信,程序就应该这么写。但是没有语言级的支持,用起来还是很麻烦。goroutine 不仅简化了许多业务逻辑的编写,而且天生就是为并发编程而生的。select/chan 可能是唯一正确的并发编程的模型。Erlang 还是太小众了,而 Go 可以延用 Erlang 的模型,却有着纯正的 C 语言血统,我想会被更多人接受的。虽然 Go 依然可以用共享状态加锁的方案,但不推荐使用。chan 用习惯了,还是相当方便的。

{ 要不要独立占一行的信仰之争终于结束了。还记得前段时间有位同学来 email 指责我开源的代码没有章法。程序写的太乱。他的理由就是,我的 { 都没有独占一行。好了,争论可以结束了。在 Go 里,如果你把 { 从 if/for 语言的行末去掉,放在下一行。编译器是不会让你通过的。(除非你再加一个 ; )我很欣慰 ;)

我发现我花了四年时间锤炼自己用 C 语言构建系统的能力,试图找到一个规范,可以更好的编写软件。结果发现只是对 Go 的模仿。缺乏语言层面的支持,只能是一个拙劣的模仿。


对于有 C 基础的同学,比如我,学习 Go 毫不费力。按这篇文章的指引即可。Rob Pike 的三日教程 PPT ,我心急,用了一个下午就看完了,并且做完了练习。

不过实战编写程序还是需要反复查阅文档的。学习一门新语言,就是在学习它的各种惯用法和库。而不是去模拟熟悉的语言。我在编写代码的时候,时刻问自己,在 Go 里,通常用什么手法来处理这个问题。接下来就是不断的查询文档了。从这个意义上讲,学习新东西还是很累的。好在 Go 的各种设计都非常切合我的本意,所以自然是越写越舒服了。

至于把变量类型申明都放在后面,按 Sean 同学的话说,有种真气逆行的感觉。对我来说倒是小问题,几个小时就习惯了。反而 C 语言那种亦前亦后的方式才是奇怪呢。


说一下我的练手项目。我用 Go 重新实现了处理多连接的服务器。当然,现在的设计方案和几年前写 blog 时的方案有略微的不同。

需求是这样的:

这个服务会监停一个端口,允许外部多个连接的接入,并可以把这个连接上的数据包汇总发到后端的一个连接上。简单的说,就是一个 N 对 1 的数据处理器。把 N 个 TCP 数据流合成一个数据流。

一个服务的处理上限是 64K 的连接,使用 2 字节的 id 号区分不同的外部连接。我定义了简单的协议,每个数据片段有 3 字节的数据头。分别是数据长度一字节和 2 字节的连接 id 号。

这个服务仅仅做数据流的合并,而不规定数据逻辑上的分包。对内的数据管道上看起来的数据流就是这样的:

len idlo idhi content ... len idlo idhi content ... len idlo idhi content ...

处理合并起来的数据流非常简单,只需要通过一个 IO 管道 (可以是 socket 也可以的 stdio/stdout ,对于 Go 来说,甚至可以是一个 in-memory Pipe )这方便后端的程序不再考虑多连接的问题。

后端服务需要可以控制连接服务器。最基本的功能就是可以强制断开某个外部连接。并且可以获得新的外部连接接入或离开的信号。

更进一步,应该由后端服务器来控制连接服务器对外监听端口的开启与关闭,以及外部连接的上限等。

为了简化设计,我选择在一个特殊的内部连接(0号连接)上收发内部的控制指令。并且使用 \r\n 分割的文本协议。


用 Go 来实现这个服务非常简洁。全部我只使用了 240 行左右的 Go 代码。所有的网络连接都使用独立的 goroutine 来控制。每个都以阻塞方式处理 socket 。主循环仅仅使用一个 select ,这类似 Erlang 的事件驱动模型。

对于控制指令流,创建一个 in-memory Pipe 即可。在对内的数据流上,过滤到 id 为 0 的数据包,转发到这个 Pipe 上。使用 bufio 把 Pipe 转换成一个 bufio.Reader 接口,就可以方便的使用 ReadString 方法去读以回车分割的文本行,进而排发到解析指令的 goroutine 中,把结构化指令利用 chan 发进消息 select 循环。整个只需要不到 10 行代码。

大多数 goroutine 内部都是一个 for 循环,结束条件是和它通讯的 chan 被外部关闭。

需要稍微考虑性能的地方是给外部连接的数据包加上三字节的头转发到内部通道上。这里如果每个包都用 make 创建一个新的 array slice 会有一些内部管理上的开销。我的做法是每次申请 16k 的 array ,再创建一个 slice 去组包,如果这个 16k 的 array 没用完,就会顺着用下去。如 Go 的教程中所言,slice 的创建是很廉价的,想来也是如此。它只是对 array 的部分引用。

Go 的引用和值分得很清楚,这使它更像 C 而不是 Java ,却又提供了 C++ 提供不了的安全性。

用 Go 写网络程序,真是非常舒心。socket 和 file 在 interface 上的统一,暗合 Unix 之道。程序嘛,就是处理输入,产生输出。Reader 和 Writer 接口让人愉快。

相关 [go 语言] 推荐:

Go 语言初步

- wei - 云风的 BLOG
所谓认真玩,就是拿 Go 写点程序,前后大约两千行吧. 据说 Go 的最佳开发平台是 Mac OS ,我没有. Windows 版还没全部搞定,但是也可以用了. 如果你用 google 搜索,很容易去到一个叫 go-windows 的开源项目上. 如果你用这个,很多库都没有,而且语法也是老的. 我在 Windows 下甚至不能正确链接自己写的多个 package.

采访:关于 Go 语言和《Go Web编程》

- - 开源中国社区最新新闻
最近,在网上出现了一本名为《Go Web编程》的书籍,里面详细地讲述了使用Go语言进行Web编程的各个方面. 很特别的是,这本书是在GitHub上以开源的方式撰写的. 日前,InfoQ采访了这本书的作者谢孟军先生,请他来和大家谈谈Go语言以及他撰写的开源书籍. InfoQ:请您先简单和大家介绍一下自己.

《学习Go语言》0.4 中文版

- way - python.cn(jobs, news)
鱼哥(https://twitter.com/#!/smallfishxy)上个月勒令我要完成 0.4 版的翻译. 之前公司重组的时候,没顾上看英文版本的更新,结果这老外不声不响的做了如此之多的改动……. 于是只好人工 diff,一条一条的对比 commit 内容. 总算是跟进到了 0.4 这个 tag.

文章: Go语言开发工具LiteIDE

- - InfoQ cn
Go语言最初在2009年11月对外公布,在2011年3月16日发布第一个release,第一个正式版本Go1于2012年3月28日推出. 在Go语言的正式版本推出后,Eclipse、IntelliJ IDEA、vim、emacs、gedit、SublimeText2、Textmate、Textpad、SciTE、Notepad++等IDE和编辑器开始纷纷有了各自的Go语言插件.

go语言编写Web程序

- - 博客园_首页
创建一个数据类型,含有load和save函数. 基于http包创建web程序. 基于template包的html模板技术. 使用regexp包验证用户输入. web程序的基础技术(HTTP, HTML). 首先,要有一个Linux, OS X, or FreeBSD系统,可以运行go程序. 如果没有的话,可以安装一个虚拟机(如VirtualBox)或者 Virtual Private Server.

为什么放弃Go语言【转载】

- - 编程语言 - ITeye博客
转载地址: http://blog.csdn.net/liigo/article/details/23699459. 有好几次,当我想起来的时候,总是会问自己:我为什么要放弃Go语言. 开门见山地说,我当初放弃Go语言(golang),就是因为两个“不爽”:第一,对Go语言本身不爽;第二,对Go语言社区里的某些人不爽.

Dropbox 开源其 Go 语言库 —— godropbox

- - 开源中国社区最新新闻
Dropbox的成功大部分归功于Python,这个语言可以使我们快速迭代开发. 然而,为了支持日益增长的用户量,我们的基础设施日渐成熟,这时 我们开始寻找一种更为高效的方式来改变系统规模. 大约在一年前,我们作出决定,把对于性能要求很苛刻的后台部分从Python迁移到了Go语言,以提供更 好的并发支持和更快的运行速度.

为什么要使用 Go 语言,Go 语言的优势在哪里?

- - 知乎每日精选
可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了. 静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问题,动态语言的感觉就是有很多的包可以使用,写起来的效率很高. 语言层面支持并发,这个就是Go最大的特色,天生的支持并发,我曾经说过一句话,天生的基因和整容是有区别的,大家一样美丽,但是你喜欢整容的还是天生基因的美丽呢.

GO 工具包安装方法 - Go语言中文网 - Golang中文社区

- -
go 安装依赖包一般会通过四种路径. 比如我们可以通过go get github.com/xxx来下载安装包. 下载好之后, 通过go install github.com/xxx来安装包. 安装包会下载到$GOPATH/src文件中. 安装后的执行文件在$GOPATH/bin文件. 当我们执行go get golang.org/x/tools/cmd/goimports会报错.