Go

来自tomtalk
Tom讨论 | 贡献2017年11月30日 (四) 01:57的版本 取完整域名

跳转至: 导航搜索

语言相关

When is the init() function in Go (Golang) run?

var WhatIsThe = AnswerToLife()
 
func AnswerToLife() int {
    return 42
}
 
func init() {
    WhatIsThe = 0
}
 
func main() {
    if WhatIsThe == 0 {
        fmt.Println("It's all a lie.")
    }
}

AnswerToLife() is guaranteed to run before init() is called, and init() is guaranteed to run before main() is called.

Keep in mind that init() is always called, regardless if there's main or not, so if you import a package that has an init function, it will be executed.

Go examples

package main
 
import "fmt"
 
func main() {
    f()
    fmt.Println("Returned normally from f.")
}
 
func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}
 
func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i+1)
}

Array、Slice、Map和Set使用详解

http://www.jb51.net/article/56828.htm

  1. 数组是 slice 和 map 的底层结构。
  2. slice 是 Go 里面惯用的集合数据的方法,map 则是用来存储键值对。
  3. 内建函数 make 用来创建 slice 和 map,并且为它们指定长度和容量等等。slice 和 map 字面值也可以做同样的事。
  4. slice 有容量的约束,不过可以通过内建函数 append 来增加元素。
  5. map 没有容量一说,所以也没有任何增长限制。
  6. 内建函数 len 可以用来获得 slice 和 map 的长度。
  7. 内建函数 cap 只能作用在 slice 上。
  8. 可以通过组合方式来创建多维数组和 slice。map 的值可以是 slice 或者另一个 map。slice 不能作为 map 的键。
  9. 在函数之间传递 slice 和 map 是相当廉价的,因为他们不会传递底层数组的拷贝。

How to print struct variables in console?

fmt.Printf("%+v\n", users)

From the fmt package:

when printing structs, the plus flag (%+v) adds field names.

Go语言天生支持Unicode,那我产生一个问题:Unicode字符的长度是多少?

这个问题的问法有没有问题?其实仔细想想,这样问是有问题的。首先,Unicode的基础是一个有编号的字符集,在字符集之上又规定了模块化的编码等等技术层次,各种具体的编码形式并不一致。因此,严格来说,Unicode是没有“长度”这一说的,它是抽象的字符,只有Unicode的编码才有具体的字节长度。而且不同的编码实现,长度也不一样。

Unicode 目前规划的总空间是17个平面(平面0至16),每个平面有 65536 个码点。我们常用的平面0(「Basic Multilingual Plane」,即「BMP」)码点范围为0x0000 至 0xFFFF,这并不是 Unicode 的全部。

BMP 的字符是 Unicode 中最基础和最常用的一部分,以 UTF-16 编码时使用2字节,以 UTF-8编码时使用1至3字节。超出 BMP 的字符以 UTF-16 或 UTF-8 编码都需要4字节。另外还有一个比较少用的编码形式,UTF-32,它编码任何 Unicode 字符都需要4个字节。

命名返回值

给返回值取名,在编码方面来讲不具有任何实用意义。

这么做的主要目的是为了使代码清晰,返回值的名称应当具有一定的意义,可以作为文档使用。因为Go支持多个返回值,比如一个函数返回5个值,这样写方法:

func cal(a int, b int) (int, int, int, int, int)

使用时要搞清楚这5个值代表什么含义比较费劲,且容易出错,如果给参数起了名字,使用就比较清晰方便了。

func add() (result int) {
	result = 1 + 4
	return
}

没有参数的 return 语句返回各个返回变量的当前值。这种用法被称作“裸”返回。

直接返回语句仅应当用在像下面这样的短函数中。在长的函数中它们会影响代码的可读性。

生成json数据

beego

func (c *ApiController) Heroes() {
	heroes := [...]interface{}{
		map[string]interface{}{"id": 11, "name": "Tom"},
		map[string]interface{}{"id": 12, "name": "Jack"},
	}
 
	c.Data["json"] = heroes
 
	c.ServeJSON()
}

原生

package main
 
import (
	"encoding/json"
	"fmt"
	"os"
)
 
func main() {
	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)
 
	user := map[string]interface{}{
		"Name":    "Tom",
		"Old":     34,
		"IsMarry": false,
	}
 
	json, _ := json.Marshal(user)
	fmt.Printf("%s", json)
}

解析json数据

var result_json map[string]interface{}
err = json.Unmarshal(str, &result_json)
if err != nil {
	return mila_session, err
}

MD5加密

m := md5.New()
io.WriteString(m, `Illuminate\Auth\Guard`)
keyMd5 := hex.EncodeToString(m.Sum(nil))

计算两个之差

t0 := time.Now()
expensiveCall()
t1 := time.Now()
fmt.Printf("The call took %v to run.\n", t1.Sub(t0))

取完整域名

func index(w http.ResponseWriter, r *http.Request) {
    fmt.Println(r.Proto)
    // output:HTTP/1.1
    fmt.Println(r.TLS)
    // output: <nil>
    fmt.Println(r.Host)
    // output: localhost:9090
    fmt.Println(r.RequestURI)
    // output: /index?id=1
 
    scheme := "http://"
    if r.TLS != nil {
        scheme = "https://"
    }
    fmt.Println(strings.Join([]string{scheme, r.Host, r.RequestURI}, ""))
    // output: http://localhost:9090/index?id=1
}

输出xml

var plist = "..."
c.Ctx.ResponseWriter.Header().Set("Content-Type", "application/xml; charset=utf-8")
c.Ctx.ResponseWriter.Write([]byte(plist))

Golang新开发者要注意的陷阱和常见错误

简式的变量声明仅可以在函数内部使用
不使用显式类型,无法使用“nil”来初始化变量

nil标志符用于表示interface、函数、maps、slices和channels的“零值”。如果你不指定变量的类型,编译器将无法编译你的代码,因为它猜不出具体的类型。

package main
 
func main() {  
    var x = nil //error
    ...
}
Strings无法修改

尝试使用索引操作来更新字符串变量中的单个字符将会失败。string是只读的byte slice(和一些额外的属性)。如果你确实需要更新一个字符串,那么使用byte slice,并在需要时把它转换为string类型。

func main() {  
    x := "text"
    //x[0] = 'T' error
    xbytes := []byte(x)
    xbytes[0] = 'T'
    fmt.Println(string(xbytes)) //prints Text
}
在多行的Slice、Array和Map语句中遗漏逗号
func main() {  
    x := []int{
    1,
    2 //error
    }
    _ = x
}

使用

安装

#1、下载安装包
wget https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz
 
#2、解压后把文件放到/usr/local/go
tar zxf go1.8.1.linux-amd64.tar.gz
 
#3、设置环境变量
export GOROOT=/usr/local/go
export PATH=$PATH:$GOROOT/bin
export GOPATH=$HOME/tom_go

第一个程序

package main
 
import (
	"fmt"
	"os"
	"strings"
)
 
func main() {
	who := "world!"
	if len(os.Args) > 1 {
		who = strings.Join(os.Args[1:], " ")
	}
 
	fmt.Println("Hello", who)
}

构建本地程序

需求满足三个条件:

  1. Go的bin目录($GOROOT/bin或者%GOROOT%\bin)必须在环境变量中。
  2. 必须有一个包含src目录的目录树,其中包含了本地程序和本地包的源代码。
  3. src目录的上一级目录必须在环境变量GOPATH中。
D:\github_projects\revel\src>go run hello.go tom
D:\github_projects\revel\src>go fmt hello.go 
D:\github_projects\revel\src>go build hello.go 
D:\github_projects\revel\src>hello tom

一个简单的web服务器

package main
 
import (
	"fmt"
	"log"
	"net/http"
)
 
const (
	html = `<html>homePage</home>`
)
 
var count int
 
func main() {
	http.HandleFunc("/", homePage)
	http.HandleFunc("/showCount", showCount)
 
	if err := http.ListenAndServe(":9001", nil); err != nil {
		log.Fatal("failed to start server", err)
	}
}
 
func homePage(writer http.ResponseWriter, request *http.Request) {
	//root路由类似/*,会响应所有无匹配请求。加个判断,拦截一下。
	if request.URL.Path != "/" {
		http.NotFound(writer, request)
		return
	}
 
	count++
	fmt.Fprint(writer, html)
}
 
func showCount(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprint(writer, count)
}

Why Handler registered by http.HandleFunc is called twice?

Your browser is requesting /favicon.ico, this matches the catchall handler, "/" so you get called twice.

How to cross compile from Windows to Linux?

go env                 #查看运行环境,其中有GOARCH值
set GOARCH=amd64
set GOOS=linux         #设置编译目标
go install -v -a std
go build complete.go   #编译执行文件

编译后会生成一个complete文件,上传到Linux服务器,设置为可执行文件,就可以运行了。

tomtalk.net部署

go env                 #查看运行环境,其中有GOARCH值
set GOARCH=386
set GOOS=linux         #设置编译目标
go build monitor.go   #编译执行文件
nohup ./monitor &

编译后会生成一个complete文件,上传到Linux服务器,设置为可执行文件,就可以运行了。

无法import github的包

$ go get github.com/go-sql-driver/mysql

go get获得golang.org的项目

国内由于墙,我们会收到 unrecognized import path 的错误,这时候我们如何通过命令行来执行 go get 呢?

go get -u -v golang.org/x/oauth2

go get的参数说明:

-d 只下载不安装

-f 只有在你包含了-u参数的时候才有效,不让-u去验证import中的每一个都已经获取了,这对于本地fork的包特别有用

-fix 在获取源码之后先运行fix,然后再去做其他的事情

-t 同时也下载需要为运行测试所需要的包

-u 强制使用网络去更新包和它的依赖包

-v 显示执行的命令

注意,这里的 –v 参数对我们分析问题很有帮助。

常用开发包

github.com/tealeg/xlsx

IDE

  • idea安装go插件(推荐)
  • sublime + gosublime

周边使用

what should be the values of GOPATH and GOROOT?

Here is a my simple setup:

directory for go related things: ~/programming/go
directory for go compiler/tools: ~/programming/go/go-1.4
directory for go software      : ~/programming/go/packages

GOROOT, GOPATH, PATH are set as following:

export GOROOT=/home/user/programming/go/go-1.4
export GOPATH=/home/user/programming/go/packages
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin

So, in short:

GOROOT is for compiler/tools that comes from go installation.

GOPATH is for your own go projects / 3rd party libraries (downloaded with "go get").

What's the recommended way to connect to MySQL from Go?

A few drivers are available but you should only consider those that implement the database/sql API as

  • it provides a clean and efficient syntax,
  • it ensures you can later change the driver without changing your code, apart the import and connection.

Two fast and reliable drivers are available for MySQL :

I've used both of them in production, programs are running for months with connection numbers in the millions without failure.

beego

自带工具

$ bee run
$ bee pack

自动编译工具

$ gin -a 8080

nginx部署

server {
    listen       80;
    server_name  oauthv2.local.com;
 
    charset utf-8;
    access_log  /home/log/oauthv2.local.com.access.log que360;
 
    location / {
        try_files /_not_exists_ @backend;
    }
 
    location @backend {
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host            $http_host;
 
        proxy_pass http://192.168.1.138:8080;
    }
}

Revel

  1. 路由、控制器、模板概览
  2. 控制器里的变量传给模板
  3. 以输出json:array、struct、slice操作
  4. 连接数据库、把数据库数据整理为struct
  5. 在控制台显示带键值的struct,fmt.Printf()
  6. 输出json,键值首字母要大写,否则内容为空。
  7. 在模板显示数据库记录。
  8. 用生产模式运行项目

其它疑问

  1. 如何编译linux版本?
  2. 如何部署生产?
  3. 如何配置虚拟站点?使用nginx。

启动程序

D:\github_projects\revel\src>revel run github.com/tecshuttle/revel