
我的博客即将入驻“云栖社区”,诚邀技术同仁一同入驻。
1.4 Go开发工具 本节我将介绍几个开发工具,它们都具有自动化提示,自动化fmt功能。因为它们都是跨平台的,所以安装步骤之类的都是通用的。 LiteIDE LiteIDE是一款专门为Go语言开发的跨平台轻量级集成开发环境(IDE),由visualfc编写。 图1.4 LiteIDE主界面 LiteIDE主要特点: 支持主流操作系统 Windows Linux MacOS X Go编译环境管理和切换 管理和切换多个Go编译环境 支持Go语言交叉编译 与Go标准一致的项目管理方式 基于GOPATH的包浏览器 基于GOPATH的编译系统 基于GOPATH的Api文档检索 Go语言的编辑支持 类浏览器和大纲显示 Gocode(代码自动完成工具)的完美支持 Go语言文档查看和Api快速检索 代码表达式信息显示F1 源代码定义跳转支持F2 Gdb断点和调试支持 gofmt自动格式化支持 其他特征 支持多国语言界面显示 完全插件体系结构 支持编辑器配色方案 基于Kate的语法显示支持 基于全文的单词自动完成 支持键盘快捷键绑定方案 Markdown文档编辑支持 实时预览和同步显示 自定义CSS显示 可导出HTML和PDF文档 批量转换/合并为HTML/PDF文档 LiteIDE安装配置 LiteIDE安装 下载地址 http://code.google.com/p/golangide 源码地址 https://github.com/visualfc/liteide 首先安装好Go语言环境,然后根据操作系统下载LiteIDE对应的压缩文件直接解压即可使用。 安装Gocode 启用Go语言的输入自动完成需要安装Gocode: go get -u github.com/nsf/gocode 编译环境设置 根据自身系统要求切换和配置LiteIDE当前使用的环境变量。 以Windows操作系统,64位Go语言为例, 工具栏的环境配置中选择win64,点编辑环境,进入LiteIDE编辑win64.env文件 GOROOT=c:\go GOBIN= GOARCH=amd64 GOOS=windows CGO_ENABLED=1 PATH=%GOBIN%;%GOROOT%\bin;%PATH% 。。。 将其中的GOROOT=c:\go修改为当前Go安装路径,存盘即可,如果有MinGW64,可以将c:\MinGW64\bin加入PATH中以便go调用gcc支持CGO编译。 以Linux操作系统,64位Go语言为例, 工具栏的环境配置中选择linux64,点编辑环境,进入LiteIDE编辑linux64.env文件 GOROOT=$HOME/go GOBIN= GOARCH=amd64 GOOS=linux CGO_ENABLED=1 PATH=$GOBIN:$GOROOT/bin:$PATH 。。。 将其中的GOROOT=$HOME/go修改为当前Go安装路径,存盘即可。 GOPATH设置 Go语言的工具链使用GOPATH设置,是Go语言开发的项目路径列表,在命令行中输入(在LiteIDE中也可以Ctrl+,直接输入)go help gopath快速查看GOPATH文档。 在LiteIDE中可以方便的查看和设置GOPATH。通过菜单-查看-GOPATH设置,可以查看系统中已存在的GOPATH列表, 同时可根据需要添加项目目录到自定义GOPATH列表中。 Sublime Text 这里将介绍Sublime Text 2(以下简称Sublime)+GoSublime+gocode+MarGo的组合,那么为什么选择这个组合呢? 自动化提示代码,如下图所示 图1.5 sublime自动化提示界面 保存的时候自动格式化代码,让您编写的代码更加美观,符合Go的标准。 支持项目管理 图1.6 sublime项目管理界面 支持语法高亮 Sublime Text 2可免费使用,只是保存次数达到一定数量之后就会提示是否购买,点击取消继续用,和正式注册版本没有任何区别。 接下来就开始讲如何安装,下载Sublime 根据自己相应的系统下载相应的版本,然后打开Sublime,对于不熟悉Sublime的同学可以先看一下这篇文章Sublime Text 2 入门及技巧 打开之后安装 Package Control:Ctrl+ 打开命令行,执行如下代码: import urllib2,os; pf='Package Control.sublime-package'; ipp=sublime.installed_packages_path(); os.makedirs(ipp) if not os.path.exists(ipp) else None; urllib2.install_opener(urllib2.build_opener(urllib2.ProxyHandler())); open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read()); print 'Please restart Sublime Text to finish installation' 这个时候重启一下Sublime,可以发现在在菜单栏多了一个如下的栏目,说明Package Control已经安装成功了。 图1.7 sublime包管理 接下来安装gocode和MarGo。 打开终端运行如下代码(需要git) go get github.com/nsf/gocode go get github.com/DisposaBoy/MarGo 这个时候我们会发现在$GOPATH/bin下面多了两个可执行文件,gocode和MarGo,这两个文件会在GoSublime加载时自动启动。 安装完之后就可以安装Sublime的插件了。需安装GoSublime、SidebarEnhancements和Go Build,安装插件之后记得重启Sublime生效,Ctrl+Shift+p打开Package Controll 输入pcip(即“Package Control: Install Package”的缩写)。 这个时候看左下角显示正在读取包数据,完成之后出现如下界面 图1.8 sublime安装插件界面 这个时候输入GoSublime,按确定就开始安装了。同理应用于SidebarEnhancements和Go Build。 验证是否安装成功,你可以打开Sublime,打开main.go,看看语法是不是高亮了,输入import是不是自动化提示了,import "fmt"之后,输入fmt.是不是自动化提示有函数了。 如果已经出现这个提示,那说明你已经安装完成了,并且完成了自动提示。 如果没有出现这样的提示,一般就是你的$PATH没有配置正确。你可以打开终端,输入gocode,是不是能够正确运行,如果不行就说明$PATH没有配置正确。 Vim Vim是从vi发展出来的一个文本编辑器, 代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。 图1.9 VIM编辑器自动化提示Go界面 配置vim高亮显示 cp -r $GOROOT/misc/vim/* ~/.vim/ 在~/.vimrc文件中增加语法高亮显示 filetype plugin indent on syntax on 安装Gocode go get -u github.com/nsf/gocode gocode默认安装到$GOBIN下面。 配置Gocode ~ cd $GOPATH/src/github.com/nsf/gocode/vim ~ ./update.bash ~ gocode set propose-builtins true propose-builtins true ~ gocode set lib-path "/home/border/gocode/pkg/linux_amd64" lib-path "/home/border/gocode/pkg/linux_amd64" ~ gocode set propose-builtins true lib-path "/home/border/gocode/pkg/linux_amd64" gocode set里面的两个参数的含意说明: propose-builtins:是否自动提示Go的内置函数、类型和常量,默认为false,不提示。 lib-path:默认情况下,gocode只会搜索$GOPATH/pkg/$GOOS_$GOARCH 和 $GOROOT/pkg/$GOOS_$GOARCH目录下的包,当然这个设置就是可以设置我们额外的lib能访问的路径 恭喜你,安装完成,你现在可以使用:e main.go体验一下开发Go的乐趣。 Emacs Emacs传说中的神器,她不仅仅是一个编辑器,它是一个整合环境,或可称它为集成开发环境,这些功能如让使用者置身于全功能的操作系统中。 图1.10 Emacs编辑Go主界面 配置Emacs高亮显示 cp $GOROOT/misc/emacs/* ~/.emacs.d/ 安装Gocode go get -u github.com/nsf/gocode gocode默认安装到$GOBIN里面下面。 配置Gocode ~ cd $GOPATH/src/github.com/nsf/gocode/emacs ~ cp go-autocomplete.el ~/.emacs.d/ ~ gocode set propose-builtins true propose-builtins true ~ gocode set lib-path "/home/border/gocode/pkg/linux_amd64" // 换为你自己的路径 lib-path "/home/border/gocode/pkg/linux_amd64" ~ gocode set propose-builtins true lib-path "/home/border/gocode/pkg/linux_amd64" 需要安装 Auto Completion 下载AutoComplete并解压 ~ make install DIR=$HOME/.emacs.d/auto-complete 配置~/.emacs文件 ;;auto-complete (require 'auto-complete-config) (add-to-list 'ac-dictionary-directories "~/.emacs.d/auto-complete/ac-dict") (ac-config-default) (local-set-key (kbd "M-/") 'semantic-complete-analyze-inline) (local-set-key "." 'semantic-complete-self-insert) (local-set-key ">" 'semantic-complete-self-insert) 详细信息参考: http://www.emacswiki.org/emacs/AutoComplete 配置.emacs ;; golang mode (require 'go-mode-load) (require 'go-autocomplete) ;; speedbar ;; (speedbar 1) (speedbar-add-supported-extension ".go") (add-hook 'go-mode-hook '(lambda () ;; gocode (auto-complete-mode 1) (setq ac-sources '(ac-source-go)) ;; Imenu & Speedbar (setq imenu-generic-expression '(("type" "^type *\\([^ \t\n\r\f]*\\)" 1) ("func" "^func *\\(.*\\) {" 1))) (imenu-add-to-menubar "Index") ;; Outline mode (make-local-variable 'outline-regexp) (setq outline-regexp "//\\.\\|//[^\r\n\f][^\r\n\f]\\|pack\\|func\\|impo\\|cons\\|var.\\|type\\|\t\t*....") (outline-minor-mode 1) (local-set-key "\M-a" 'outline-previous-visible-heading) (local-set-key "\M-e" 'outline-next-visible-heading) ;; Menu bar (require 'easymenu) (defconst go-hooked-menu '("Go tools" ["Go run buffer" go t] ["Go reformat buffer" go-fmt-buffer t] ["Go check buffer" go-fix-buffer t])) (easy-menu-define go-added-menu (current-local-map) "Go tools" go-hooked-menu) ;; Other (setq show-trailing-whitespace t) )) ;; helper function (defun go () "run current buffer" (interactive) (compile (concat "go run " (buffer-file-name)))) ;; helper function (defun go-fmt-buffer () "run gofmt on current buffer" (interactive) (if buffer-read-only (progn (ding) (message "Buffer is read only")) (let ((p (line-number-at-pos)) (filename (buffer-file-name)) (old-max-mini-window-height max-mini-window-height)) (show-all) (if (get-buffer "*Go Reformat Errors*") (progn (delete-windows-on "*Go Reformat Errors*") (kill-buffer "*Go Reformat Errors*"))) (setq max-mini-window-height 1) (if (= 0 (shell-command-on-region (point-min) (point-max) "gofmt" "*Go Reformat Output*" nil "*Go Reformat Errors*" t)) (progn (erase-buffer) (insert-buffer-substring "*Go Reformat Output*") (goto-char (point-min)) (forward-line (1- p))) (with-current-buffer "*Go Reformat Errors*" (progn (goto-char (point-min)) (while (re-search-forward "<standard input>" nil t) (replace-match filename)) (goto-char (point-min)) (compilation-mode)))) (setq max-mini-window-height old-max-mini-window-height) (delete-windows-on "*Go Reformat Output*") (kill-buffer "*Go Reformat Output*")))) ;; helper function (defun go-fix-buffer () "run gofix on current buffer" (interactive) (show-all) (shell-command-on-region (point-min) (point-max) "go tool fix -diff")) 恭喜你,你现在可以体验在神器中开发Go的乐趣。默认speedbar是关闭的,如果打开需要把 ;; (speedbar 1) 前面的注释去掉,或者也可以通过 M-x speedbar 手动开启。 Eclipse Eclipse也是非常常用的开发利器,以下介绍如何使用Eclipse来编写Go程序。 图1.11 Eclipse编辑Go的主界面 首先下载并安装好Eclipse 下载goeclipse插件 http://code.google.com/p/goclipse/wiki/InstallationInstructions 下载gocode,用于go的代码补全提示 gocode的github地址: https://github.com/nsf/gocode 在windows下要安装git,通常用msysgit 再在cmd下安装: go get -u github.com/nsf/gocode 也可以下载代码,直接用go build来编译,会生成gocode.exe 下载MinGW并按要求装好 配置插件 Windows->Reference->Go (1).配置Go的编译器 图1.12 设置Go的一些基础信息 (2).配置Gocode(可选,代码补全),设置Gocode路径为之前生成的gocode.exe文件 图1.13 设置gocode信息 (3).配置GDB(可选,做调试用),设置GDB路径为MingW安装目录下的gdb.exe文件 图1.14 设置GDB信息 测试是否成功 新建一个go工程,再建立一个hello.go。如下图: 图1.15 新建项目编辑文件 调试如下(要在console中用输入命令来调试): 图1.16 调试Go程序 IntelliJ IDEA 熟悉Java的读者应该对于idea不陌生,idea是通过一个插件来支持go语言的高亮语法,代码提示和重构实现。 先下载idea,idea支持多平台:win,mac,linux,如果有钱就买个正式版,如果不行就使用社区免费版,对于只是开发Go语言来说免费版足够用了 安装Go插件,点击菜单File中的Setting,找到Plugins,点击,Broswer repo按钮。国内的用户可能会报错,自己解决哈。 这时候会看见很多插件,搜索找到Golang,双击,download and install。等到golang那一行后面出现Downloaded标志后,点OK。 然后点 Apply .这时候IDE会要求你重启。 重启完毕后,创建新项目会发现已经可以创建golang项目了: 下一步,会要求你输入 go sdk的位置,一般都安装在C:\Go,linux和mac根据自己的安装目录设置,选中目录确定,就可以了。 links
近日,工作中突遇一需求:将一数据表分组,而后取出每组内按一定规则排列的前N条数据。乍想来,这本是寻常查询,无甚难处。可提笔写来,终究是困住了笔者好一会儿。冥思苦想,遍查网络,不曾想这竟然是SQL界的一个经典话题。今日将我得来的若干方法列出,抛砖引玉,以期与众位探讨。 正文之前,对示例表结构加以说明。 表SectionTransactionLog,用来记录各部门各项活动的日志表 SectionId,部门Id SectionTransactionType,活动类型 TotalTransactionValue,活动花费 TransactionDate,活动时间 我们设定的场景为:选出每部门(SectionId)最近两次举行的活动。 笔者用来测试的SectionTransactionLog表中数据超3,000,000。 一、 嵌套子查询方式 1 1 SELECT * FROM SectionTransactionLog mLog 2 where 3 (select COUNT(*) from SectionTransactionLog subLog 4 where subLog.SectionId = mLog.SectionId and subLog.TransactionDate >= mLog.TransactionDate)<=2 5 order by SectionId, TransactionDate desc 运行时间:34秒 该方式原理较简单,只是在子查询中确定该条记录是否是其Section中新近发生的2条之一。 2 1 SELECT * FROM SectionTransactionLog mLog 2 where mLog.Id in 3 (select top 2 Id 4 from SectionTransactionLog subLog 5 where subLog.SectionId = mLog.SectionId 6 order by TransactionDate desc) 7 order by SectionId, TransactionDate desc 运行时间:1分25秒 在子查询中使用TransactionDate排序,取top 2。并应用in关键字确定记录是否符合该子查询。 二、 自联接方式 1 select mLog.* from SectionTransactionLog mLog 2 inner join 3 (SELECT rankLeft.Id, COUNT(*) as rankNum FROM SectionTransactionLog rankLeft 4 inner join SectionTransactionLog rankRight 5 on rankLeft.SectionId = rankRight.SectionId and rankLeft.TransactionDate <= rankRight.TransactionDate 6 group by rankLeft.Id 7 having COUNT(*) <= 2) subLog on mLog.Id = subLog.Id 8 order by mLog.SectionId, mLog.TransactionDate desc 运行时间:56秒 该实现方式较为巧妙,但较之之前方法也稍显复杂。其中,以SectionTransactionLog表自联接为基础而构造出的subLog部分为每一活动(以Id标识)计算出其在Section内部的排序rankNum(按时间TransactionDate)。 在自联接条件rankLeft.SectionId = rankRight.SectionId and rankLeft.TransactionDate <= rankRight.TransactionDate的筛选下,查询结果中对于某一活动(以Id标识)而言,与其联接的只有同其在一Section并晚于或与其同时发生活动(当然包括其自身)。下图为Id=1的活动自联接示意: 从上图中一目了然可以看出,基于此结果的count计算,便为Id=1活动在Section 9022中的排次rankNum。 而后having COUNT(*) <= 2选出排次在2以内的,再做一次联接select出所需信息。 三、 应用ROW_NUMBER()(SQL SERVER 2005及之后) 1 select * from 2 ( 3 select *, ROW_NUMBER() over(partition by SectionId order by TransactionDate desc) as rowNum 4 from SectionTransactionLog 5 ) ranked 6 where ranked.rowNum <= 2 7 order by ranked.SectionId, ranked.TransactionDate desc 运行时间:20秒 这是截至目前效率最高的实现方式。ROW_NUMBER() over(partition by SectionId order by TransactionDate desc)完成了分组、排序、取行号的整个过程。 效率思考 下面我们对上述的4种方法做一个效率上的统计。 方法 耗时(秒) 排名 应用ROW_NUMBER() 20 1 嵌套子查询方式1 34 2 自联接方式 56 3 嵌套子查询方式2 85 4 4种方法中,嵌套子查询2所用时最长,其效率损耗在什么地方了呢?难道果真是使用了in关键字的缘故?下图为其执行计划(execute plan): 从图中,我们可以看出优化器将in解析为了Left Semi Join, 其损耗极低。而该查询绝大部分性能消耗在子查询的order by处(Top N Sort)。果然,若删掉子查询中的order by TransactionDate desc子句(当然结果不正确),其耗时仅为8秒。 添加有效索引可提高该查询方法的性能。
目前业务上需要选用合适的消息队列来做数据传输,因此特意调研了一下当下较主流的消息队列的各特点: 消息中间件三要素:生产者、消息、消费者。 衡量标准:生产者、消息、消费者三者的交互。 1.消息路由:消息如何经过消息中间件到达消费者。 2.消息可靠性: 2.1.不允许消息丢失 2.2.允许消息丢失,性能需求大于可靠性 3.消息重放:已经消费过的消息是否能设置某一时间间隔后重新被消费(适用于新系统导入旧数据,防数据丢失) 4.消息堆积:流量高峰期时,消息中间件在高并发投递消息时可能会出现问题,所以把消息暂存在中间件,等流量高峰过去,再投给下游业务 5.消息优先级: 5.1.消息投递顺序和消费顺序一致 5.2.可设置某些消息的消费优先级 6.性能和扩展 6.1.性能:主要指tps、qps以及并发连接数 6.2.扩展:通过添加消费者来提高消费速率、消息中间件的容量上限 鉴于目前我们业务是做数据传输,主要是需要保证数据的完整性,综合比较,选择了RabbitMQ。
加 Golang学习 QQ群共同学习进步成家立业工作 ^-^ 群号:96933959 strings import "strings" strings包实现了用于操作字符的简单函数。 strings.Index("suoning", "n") //3(子串第一次出现的位置,不存在则返回-1) strings.LastIndex("suoning", "n") //5(子串最后一次出现的位置,不存在则返回-1) strings.Replace("suoning", "ning", "nick", -1) //suonick(替换,n为替换次数,<0替换所有子串) strings.HasPrefix("suoning", "suo") //true(判断s是否有前缀字符串prefix) strings.HasSuffix("suoning", "ning") //true(判断s是否有后缀字符串suffix) strings.Contains("suoning", "suoni") //true(判断字符串s是否包含子串substr) strings.Count("nnnnnn", "n") //6(字符串中有几个不重复的子串) strings.Repeat("n", 6) //nnnnnn(返回count个s串联的字符串) strings.ToLower("SuoNing") //suoning(将所有字母都转为对应的小写版本的拷贝) strings.ToUpper("suoning") //SUONING(将所有字母都转为对应的大写版本的拷贝) strings.TrimSpace(" \nsuoning\n ") //suoning(去掉前后端所有空白) strings.Trim("!!suoning!!", "!") //suoning(将s前后端所有cutset包含的utf-8码值都去掉的字符串) strings.TrimLeft("!suoning!", "!") //suoning!(将s前端所有cutset包含的utf-8码值都去掉的字符串) strings.TrimRight("!suoning!", "!") //!suoning(将s后端所有cutset包含的utf-8码值都去掉的字符串) strings.Fields(" suo ning ") //[suo ning](按照空白分割的多个字符串) strings.Split(",suo,ning,", ",") //[ suo ning ](去掉s中出现的sep的方式进行分割) strings.Join([]string{"suo", "ning"}, " ") //suo ning(用sep来拼接为一个字符串) bytes import "bytes" bytes包实现了操作[]byte的常用函数。本包的函数和strings包的函数相当类似。 s1 := []byte("索宁") s2 := []byte("索") fmt.Println(bytes.Equal(s1, s2)) //false(切片的内容是否完全相同) //Runes函数返回和s等价的[]rune切片。(将utf-8编码的unicode码值分别写入单个rune) r1 := bytes.Runes(s1) r2 := bytes.Runes(s2) fmt.Println(r1) //[32034 23425] fmt.Println(r2) //[32034] fmt.Println(r1[0] == r2[0]) //true(可以判断中文) strconv import "strconv" strconv包实现了基本数据类型和其字符串表示的相互转换。 //返回字符串表示的整数值 fmt.Println(strconv.ParseInt("-999", 0, 0)) //-999 <nil> fmt.Println(strconv.ParseUint("999", 0, 0)) //999 <nil> fmt.Println(strconv.Atoi("999")) //999 <nil> //base指定进制(2到36),如果base为0,则会从字符串前置判断,"0x"是16进制,"0"是8进制,否则是10进制; //bitSize指定结果必须能无溢出赋值的整数类型,0、8、16、32、64 分别代表 int、int8、int16、int32、int64; //返回i的base进制的字符串表示 fmt.Println(strconv.FormatInt(-520, 10)) //-520 fmt.Println(strconv.FormatUint(520, 10)) //520 fmt.Println(strconv.Itoa(520)) //520 //base 必须在2到36之间,结果中会使用小写字母'a'到'z'表示大于10的数字。 //返回字符串表示的bool值 fmt.Println(strconv.ParseBool("false")) //false <nil> fmt.Println(strconv.FormatBool(true)) //true fmt.Println(strconv.AppendBool([]byte{}, true)) //[116 114 117 101] //它接受1、0、t、f、T、F、true、false、True、False、TRUE、FALSE;否则返回错误。 //返回字符串表示的bool值 fmt.Println(strconv.ParseFloat("520.1314", 10)) //520.1314 <nil> fmt.Println(strconv.FormatFloat(520.1314, 'f', 6, 32)) //520.131409 //fmt表示格式:'f'(-ddd.dddd)、'b'(-ddddp±ddd,指数为二进制)、'e'(-d.dddde±dd,十进制指数)、'E'(-d.ddddE±dd,十进制指数)、'g'(指数很大时用'e'格式,否则'f'格式)、'G'(指数很大时用'E'格式,否则'f'格式)。 //prec控制精度(排除指数部分):对'f'、'e'、'E',它表示小数点后的数字个数;对'g'、'G',它控制总的数字个数。如果prec 为-1,则代表使用最少数量的、但又必需的数字来表示f。 //bitSize表示f的来源类型(32:float32、64:float64),会据此进行舍入。 time import "time" time包提供了时间的显示和测量用的函数。日历的计算采用的是公历。 fmt.Println(time.Now()) //2017-08-03 22:35:36.45766459 +0800 CST fmt.Println(time.Now().Unix()) //时间戳1501771053 fmt.Println(time.Sunday) //Sunday fmt.Println(time.Stamp) //Jan _2 15:04:05 fmt.Println(time.Date(2008, time.November, 11, 23, 0, 0, 0, time.UTC)) //2008-11-11 23:00:00 +0000 UTC //格式化输出 now := time.Now() fmt.Println(now.Format("02/1/2006 15:04:05")) //03/8/2017 22:37:58 fmt.Println(now.Format("2006/02/1 15:04")) //2017/03/8 22:37 fmt.Println(now.Format("2006-1-02 15:04:05")) //2017-8-03 22:37:58 m, _ := time.ParseDuration("-10m") //10分钟前 fmt.Println(now.Add(m)) //2017-08-03 22:34:55.810202385 +0800 CST d, _ := time.ParseDuration("-24h") //1天前 fmt.Println(now.Add(d)) //2017-08-02 22:46:28.742383801 +0800 CST hh, _ := time.ParseDuration("1h") //1小时后 fmt.Println(now.Add(hh)) //2017-08-03 23:47:21.458974014 +0800 CST time.Sleep(time.Second) //睡一秒 //定时器 t := time.NewTicker(time.Second) for v := range t.C { //每一秒执行一次, 这是个死循环 fmt.Println(v) //2017-08-03 22:53:13.849203492 +0800 CST } t.Stop() math/rand import "math/rand" rand包实现了伪随机数生成器。 随机数从资源生成。包水平的函数都使用的默认的公共资源。该资源会在程序每次运行时都产生确定的序列。如果需要每次运行产生不同的序列,应使用Seed函数进行初始化。默认资源可以安全的用于多go程并发。 func init() { rand.Seed(time.Now().UnixNano()) //使用给定的seed来初始化生成器到一个确定的状态。 } func main() { fmt.Println(rand.Int()) //9153536921449289609(返回一个非负的伪随机int值) fmt.Println(rand.Int31()) //2037735069(返回一个int32类型的非负的31位伪随机数) fmt.Println(rand.Int63()) //1132646811815532030(返回一个int64类型的非负的63位伪随机数) fmt.Println(rand.Intn(100)) //95(取值范围在[0,n)的伪随机int值,n<=0会panic) fmt.Println(rand.Float32()) //0.09178123(取值范围在[0.0, 1.0)的伪随机float32值) fmt.Println(rand.Perm(5)) //[0 4 1 2 3](有n个元素的,[0,n)范围内整数的伪随机排列的切片) } os import "os" os包提供了操作系统函数的不依赖平台的接口。设计为Unix风格的,虽然错误处理是go风格的;失败的调用会返回错误值而非错误码。通常错误值里包含更多信息。 fmt.Println(os.Hostname()) //bogon <nil>(返回内核提供的主机名) fmt.Println(os.Getpagesize()) //4096(返回底层的系统内存页的尺寸) fmt.Println(os.Environ()) //(环境变量的格式为"key=value"的字符串的切片拷贝) fmt.Println(os.Getenv("GOPATH")) //(名为key的环境变量的值,不存在返回空) fmt.Println(os.Setenv("Name", "Nick")) //(设置名为key的环境变量) os.Clearenv() //(删除所有环境变量) os.Exit(0) //(以给出的状态码code退出,程序会立刻终止,defer的函数不会被执行) fmt.Println(os.Getuid()) //501(调用者的用户ID) fmt.Println(os.Geteuid()) //501(调用者的有效用户ID) fmt.Println(os.Getgid()) //20(调用者的组ID) fmt.Println(os.Getegid()) //20(调用者的有效组ID) fmt.Println(os.Getgroups()) //[20 701 12 61 79 80 81 ...](调用者所属的所有用户组的组ID) fmt.Println(os.Getpid()) //62027(调用者所在进程的进程ID) fmt.Println(os.Getppid()) //62020(调用者所在进程的父进程的进程ID) fmt.Println(os.Getwd()) //返回一个对应当前工作目录的根路径 os.Chdir("/tmp/") //将当前工作目录修改为dir指定的目录 os.Mkdir("/tmp/test", 0666) //用指定的权限和名称创建一个目录 os.MkdirAll("/tmp/test1/test2/test3", 0666) //使用指定的权限和名称创建一个目录,递归上级目录 os.Rename("/tmp/test", "/tmp/test1/testNew") //修改一个文件的名字,移动一个文件 os.Remove("/tmp/test1/test2/test3") //删除name指定的文件或目录 os.RemoveAll("/tmp/test1/") //删除path指定的文件 os.Create("/tmp/test") //0666,创建文件,存在会清空 os.Open("/tmp/test") //O_RDONLY,打开一个文件 os.OpenFile("/tmp/test", os.O_CREATE|os.O_WRONLY, 0666) Args保管了命令行参数,第一个是程序名。 fmt import "fmt" mt包实现了类似C语言printf和scanf的格式化I/O。格式化动作('verb')源自C语言但更简单。 通用: %v 值的默认格式表示 %+v 类似%v,但输出结构体时会添加字段名 %#v 值的Go语法表示 %T 值的类型的Go语法表示 %% 百分号 布尔值: %t 单词true或false 整数: %b 表示为二进制 %c 该值对应的unicode码值 %d 表示为十进制 %o 表示为八进制 %q 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示 %x 表示为十六进制,使用a-f %X 表示为十六进制,使用A-F %U 表示为Unicode格式:U+1234,等价于"U+%04X" 浮点数与复数的两个组分: %b 无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat %e 科学计数法,如-1234.456e+78 %E 科学计数法,如-1234.456E+78 %f 有小数部分但无指数部分,如123.456 %F 等价于%f %g 根据实际情况采用%e或%f格式(以获得更简洁、准确的输出) %G 根据实际情况采用%E或%F格式(以获得更简洁、准确的输出) %f: 默认宽度,默认精度 %9f 宽度9,默认精度 %.2f 默认宽度,精度2 %9.2f 宽度9,精度2 %9.f 宽度9,精度0 字符串和[]byte: %s 直接输出字符串或者[]byte %q 该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示 %x 每个字节用两字符十六进制数表示(使用a-f) %X 每个字节用两字符十六进制数表示(使用A-F) 指针: %p 表示为十六进制,并加上前导的0x errors import "errors" errors包实现了创建错误值的函数。 使用字符串创建一个错误,请类比fmt包的Errorf方法,差不多可以认为是New(fmt.Sprintf(...))。 err := errors.New("emit macho dwarf: elf header corrupted") if err != nil { fmt.Print(err) //emit macho dwarf: elf header corrupted } const name, id = "bimmler", 17 err := fmt.Errorf("user %q (id %d) not found", name, id) if err != nil { fmt.Print(err) //user "bimmler" (id 17) not found } json import "encoding/json" json包实现了json对象的编解码。Marshal、Unmarshal函数。 Marshal函数返回v的json编码: type ColorGroup struct { ID int Name string Colors []string } group := ColorGroup{ ID: 1, Name: "Reds", Colors: []string{"Crimson", "Red", "Ruby", "Maroon"}, } b, err := json.Marshal(group) if err != nil { fmt.Println("error:", err) } os.Stdout.Write(b) //{"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]} Unmarshal函数解析json编码的数据并将结果存入v指向的值: var jsonBlob = []byte(`[ {"Name": "Platypus", "Order": "Monotremata"}, {"Name": "Quoll", "Order": "Dasyuromorphia"} ]`) type Animal struct { Name string Order string } var animals []Animal err := json.Unmarshal(jsonBlob, &animals) if err != nil { fmt.Println("error:", err) } fmt.Printf("%+v", animals) //[{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}] package main import ( "encoding/json" "fmt" ) type User struct { Name string `json:"userName"` Age int } func main() { u1 := &User{Name: "nick", Age: 18} u1Str, err := json.Marshal(u1) fmt.Println(err) fmt.Println(u1Str) var u User err = json.Unmarshal([]byte(u1Str), &u) fmt.Println(err) fmt.Println(u) } struct tag 使用: // 字段被本包忽略 Field int `json:"-"` // 字段在json里的键为"myName" Field int `json:"myName"` // 字段在json里的键为"myName"且如果字段为空值将在对象中省略掉 Field int `json:"myName,omitempty"` // 字段在json里的键为"Field"(默认值),但如果字段为空值会跳过;注意前导的逗号 Field int `json:",omitempty"` reflect flag import "flag" flag包实现了命令行参数的解析。 func main() { var confPath string var port int var b bool flag.StringVar(&confPath, "conf", "", "input config path.") flag.IntVar(&port, "p", 8000, "input port.") flag.BoolVar(&b, "b", false, "input bool.") flag.Parse() //注册 fmt.Println("config path: ", confPath) fmt.Println("port: ", port) fmt.Println("bool: ", b) } $ go build -o bin/test go_dev/args/main $ bin/test -conf "/tmp/config" -p 9006 -b true config path: /tmp/config port: 9006 bool: true context 一、传递全局变量 func process(ctx context.Context) { ret, ok := ctx.Value("trace_id").(int) if !ok { ret = 123456 } fmt.Println(ret) //111111 s, _ := ctx.Value("name").(string) fmt.Println(s) //suoning } func main() { ctx := context.WithValue(context.Background(), "name", "suoning") ctx = context.WithValue(ctx, "trace_id", 111111) process(ctx) } func add(ctx context.Context, a, b int) int { traceId := ctx.Value("trace_id").(string) fmt.Printf("trace_id:%v\n", traceId) return a + b } func calc(ctx context.Context, a, b int) int { traceId := ctx.Value("trace_id").(string) fmt.Printf("trace_id:%v\n", traceId) return add(ctx, a, b) } func main() { ctx := context.WithValue(context.Background(), "trace_id", "123456") s := calc(ctx, 500, 20) fmt.Println(s) } 二、Goroutine Timeout package main import ( "net/http" "context" "time" "fmt" "io/ioutil" ) type Result struct { r *http.Response err error } func process() { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() tr := &http.Transport{} client := &http.Client{Transport: tr} c := make(chan Result, 1) req, err := http.NewRequest("GET", "http://www.google.com", nil) if err != nil { fmt.Println("http NewRequest failed, err:", err) return } go func() { resp, err := client.Do(req) pack := Result{r: resp, err: err} c <- pack }() select { case <-ctx.Done(): tr.CancelRequest(req) res := <-c fmt.Println("Timeout, err:", res.err) case res := <-c: defer res.r.Body.Close() out, _ := ioutil.ReadAll(res.r.Body) fmt.Printf("Server Response: %s", out) } return } func main() { process() } sync 等待一组Goroutine返回 package main import ( "sync" "fmt" "time" ) func calc(w *sync.WaitGroup, i int) { fmt.Println("calc: ", i) time.Sleep(time.Second) w.Done() } func main() { wg := sync.WaitGroup{} for i:=0; i<10; i++ { wg.Add(1) go calc(&wg, i) } wg.Wait() fmt.Println("all goroutine finish") }