GOPATH 指定了 Golang 项目的 Workspace,Golang 是支持多 GOPATH 的,也就是说:在同一个 Golang 项目中可以同时拥有多个运行环境。多 GOPATH 支持带来了一定的灵活度,但也会导致某些副作用,例如:软件版本的一致性。
诸如 Etcd 或 Camlistore 这样的大项目通常会使用 godep 类似的依赖包管理工具,将所有依赖都保存到某个目录中。也就是说,这些项目都会要求使用一个单一的 GOPATH,它们只能在这个目录内找到对应的版本。
简而言之,如果你认为项目需要一个独立的 GOPATH,那么就创建它,但不要尝试在一个项目中使用多个 GOPATH。
$ go get golang.org/x/tools/cmd/goimports
$ goimports -w=true hello.go
import (
"fmt"
)
import (
"encoding/json"
"strings"
"github.com/astaxie/beego"
"github.com/go-sql-driver/mysql"
"myproject/models"
"myproject/controller"
"myproject/utils"
)
import "../net"
import . " pubcode/api/broker"
Go 在项目的工程化上提供了良好的支持,这是 Go 能够在服务器领域有一席之地的重要原因。这里说的工程友好包括:
go fmt 工具可以尽力保持项目代码风格的一致性,有些 IDE 会在保存 *.go 文件时会自动执行 gofmt,否则需要手动运行指令 gofmt -w .
,可以将当前目录和子目录下的所有文件都格式化一遍。
需要注意的是,gofmt 不识别空行,因为 gofmt 不能理解空行的意义。这一点需要引起注意,空行是非常好的体现了逻辑关联的方式,所以空行不能随意,非常严重地影响可读性。
golint 检测举例:
var str string = "test"
会有警告,应该写成 var str = "test"
。x += 1
应该 x++
。golint 检查的是代码规范,而 go vet 则是静态分析源码中存在的各种问题,例如:多余的代码,提前 return 的逻辑,struct 的 tag 是否符合标准等。执行指令 go vet .
即可。
Context 是官方推荐的并发模式,主要用于调度 goroutine,在很多库和框架都有支持。
因为 goroutine 创建成本极低,一个请求处理的过程中往往会产生很多和这个请求相关的 goroutine,请求处理结束或者中断后,没能及时结束的 goroutine 会泄漏, goroutine 本质上是线程,会继续占用 CPU,并且容易进一步导致内存泄漏。
Context 是一种接口,相同请求范围内的 goroutine 需要主动检查 Context 的状态来进 行合适的处理:
Done() <-chan struct{}
:返回一个管道,当 Context 取消或者超时的时候会被关闭。Err() error
:返回 Done 管道关闭的原因。Deadline() (deadline time.Time, ok bool)
:返回将要超时的时间Value(key interface{}) interface{}
:返回 Context 的值。Context 是一个独立的变量,不能保存在结构体中,需要在第一个参数以名称 ctx 主动传递。
APIClient
。apiClient
。const APP_VER = "1.0"
var isExist bool
type Reader interface {
Read(p []byte) (n int, err error)
}
// 多个函数接口
type WriteFlusher interface {
Write([]byte) (int, error)
Flush() error
}
type error interface {
Error() string
}
package main
import (
"fmt"
"regexp"
)
func main() {
regex := `(\d` // should be `(\d)`
defer func() {
e := recover()
if e != nil {
fmt.Printf("compile error: %v\n", regex)
}
} ()
regexp.MustCompile(regex)
}
ch := make(chan int, 0)
Socket 编程的流程:
Golang 的 net 标准库对此流程进行了抽象和封装。无论我们期望使用什么协议建立什么形式的连接,都只需要调用 net.Dial() 即可。
net.Dial() 支持的协议类型:
// TCP
conn, err := net.Dial("tcp", "192.168.0.10:2100")
// UDP
conn, err := net.Dial("udp", "192.168.0.12:975")
// ICMP
conn, err := net.Dial("ip4:icmp", "www.baidu.com")
// or
conn, err := net.Dial("ip4:1", "10.0.0.3")
Golang 的 net/http 标准库封装了 HTTP 编程函数。net/http 的 Client 类型提供了如下几个方法:
func (c *Client) Get(url string) (r *Response, err error)
func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, err error)
func (c *Client) PostForm(url string, data url.Values) (r *Response, err error)
func (c *Client) Head(url string) (r *Response, err error)
func (c *Client) Do(req *Request) (resp *Response, err error)
RPC 采用 C/S 工作模式。当执行一个远程过程调用时,客户端程序首先发送一个带有参数的调用信息到服务端,然后等待服务端响应。在服务端,服务进程保持睡眠状态直到客户端的调用信息到达为止。当一个调用信息到达时,服务端获得进程参数,计算出结果,并向客户端发送应答信息,然后等待下一个调用。最后,客户端接收来自服务端的应答信息,获得进 程结果,然后调用执行并继续进行。
一个对象中只有满足如下这些条件的方法,才能被 RPC 服务端设置为可供远程访问:
示例:
func (t *T) MethodName(argType T1, replyType *T2) error
Golang 提供了的标准库 encoding/json 对 JSON 数据进行编解码,并且允许使用 map[string]interface{} 和 []interface{} 类型的值来分别存放未知结构的 JSON 对象或数组。
func Marshal(v interface{}) ([]byte, error)
示例:
type Book struct {
Title string
Authors []string
Publisher string
IsPublished bool
Price float
}
gobook := Book{
"Go 语言编程",
["XuShiwei", "HughLv", "Pandaman", "GuaguaSong", "HanTuo", "BertYuan", "XuDaoli"],
"xxx.com.cn",
true,
9.99
}
b, err := json.Marshal(gobook)
b == []byte(`{
"Title": "Go语言编程",
"Authors": ["XuShiwei", "HughLv", "Pandaman", "GuaguaSong", "HanTuo", "BertYuan", "XuDaoli"],
"Publisher": "xxx.com.cn",
"IsPublished": true,
"Price": 9.99
}`)
编码时的数据类型映射如下:
func Unmarshal(data []byte, v interface{}) error
解码时的数据类型映射:
import "database/sql"
func listTracks(db sql.DB, artist string, minYear, maxYear int) {
result, err := db.Exec(
"SELECT * FROM tracks WHERE artist = ? AND ? <= year AND year <= ?", artist, minYear, maxYear)
// ...
}
Exec() 方法使用 SQL 字面量替换在查询字符串中的每个 ‘?’。SQL 字面量表示相应参数的值,它有可能是一个布尔值,一个数字,一个字符串,或者 nil 空值。用这种方式构造查询可以帮助避免 SQL 注入攻击。