使用的导入的包
go get -u github.com/cihub/seelog
go 基础语法
http://www.runoob.com/go/go-tutorial.html
常量,变量 分组声明
分组声明
在Go语言中,同时声明多个常量、变量,或者导入多个包时,可采用分组的方式进行声明。
例如下面的代码:
import "fmt"
import "os"
const i = 100
const pi = 3.1415
const prefix = "Go_"
var i int
var pi float32
var prefix string
可以分组写成如下形式:
import(
"fmt"
45
"os"
) c
onst(
i = 100
pi = 3.1415
prefix = "Go_"
) v
ar(
i int
pi float32
prefix string
)
除非被显式设置为其它值或iota,每个const分组的第一个常量被默认设置为它的0值,第二及后续的常量被默认
设置为它前面那个常量的值,如果前面那个常量的值是iota,则它也被设置为iota。
iota枚举
Go里面有一个关键字iota,这个关键字用来声明enum的时候采用,它默认开始值是0,每调用一次加1:
const(
x = iota // x == 0
y = iota // y == 1
z = iota // z == 2
w // 常量声明省略值时,默认和之前一个值的字面相同。这里隐式地说w = iota,因此w == 3。其实上面y和z可同样不用
) c
onst v = iota // 每遇到一个const关键字,iota就会重置,此时v == 0
Go程序设计的一些规则
Go之所以会那么简洁,是因为它有一些默认的行为:
- 大写字母开头的变量是可导出的,也就是其它包可以读取的,是公用变量;小写字母开头的就是不可导出的,是私有变量。
- 大写字母开头的函数也是一样,相当于class中的带public关键词的公有函数;小写字母开头的就是有private关键词的私有函数。
反射
Go语言实现了反射,所谓反射就是动态运行时的状态。我们一般用到的包是reflect包。如何运用reflect包,官方的
这篇文章详细的讲解了reflect包的实现原理,laws of reflection
使用reflect一般分成三步,下面简要的讲解一下:
要去反射是一个类型的值(这些值都实现了空interface),
首先需
要把它转化成reflect对象(reflect.Type或者reflect.Value,根据不同的情况调用不同的函数)。这两种获取方式如
下:
t := reflect.TypeOf(i) //得到类型的元数据,通过t我们能获取类型定义里面的所有元素
v := reflect.ValueOf(i) //得到实际的值,通过v我们获取存储在里面的值,还可以去改变值
转化为reflect对象之后我们就可以进行一些操作了,也就是将reflect对象转化成相应的值,例如
tag := t.Elem().Field(0).Tag //获取定义在struct里面的标签
name := v.Elem().Field(0).String() //获取存储在第一个字段里面的值
获取反射值能返回相应的类型和数值
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
最后,反射的话,那么反射的字段必须是可修改的,我们前面学习过传值和传引用,这个里面也是一样的道理,反射
的字段必须是可读写的意思是,如果下面这样写,那么会发生错误
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)
如果要修改相应的值,必须这样写
var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
v.SetFloat(7.1)
上面只是对反射的简单介绍,更深入的理解还需要自己在编程中不断的实践
2.5 面向对象
前面两章我们介绍了函数和struct,那你是否想过函数当作struct的字段一样来处理呢?今天我们就讲解一下函数的
另一种形态,带有接收者的函数,我们称为method
值传递,引用传递
6.2.1 按值传递(call by value) 按引用传递(call by reference)
Go 默认使用按值传递来传递参数,也就是传递参数的副本。函数接收参数副本之后,在使用变量的过程中可能对副本的值进行更改,但不会影响到原来的变量,比如 Function(arg1)。
如果你希望函数可以直接修改参数的值,而不是对参数的副本进行操作,你需要将参数的地址(变量名前面添加&符号,比如 &variable)传递给函数,这就是按引用传递,比如 Function(&arg1),此时传递给函数的是一个指针。如果传递给函数的是一个指针,指针的值(一个地址)会被复制,但指针的值所指向的地址上的值不会被复制;我们可以通过这个指针的值来修改这个值所指向的地址上的值。(译者注:指针也是变量类型,有自己的地址和值,通常指针的值指向一个变量的地址。所以,按引用传递也是按值传递。)
几乎在任何情况下,传递指针(一个32位或者64位的值)的消耗都比传递副本来得少。
在函数调用时,像切片(slice)、字典(map)、接口(interface)、通道(channel)这样的引用类型都是默认使用引用传递(即使没有显式的指出指针)。
有些函数只是完成一个任务,并没有返回值。我们仅仅是利用了这种函数的副作用,就像输出文本到终端,发送一个邮件或者是记录一个错误等。
但是绝大部分的函数还是带有返回值的。
如下,simple_function.go 里的 MultiPly3Nums 函数带有三个形参,分别是 a、b、c,还有一个 int 类型的返回值(被注释的代码具有和未注释部分同样的功能,只是多引入了一个本地变量):
示例 6.2 simple_function.go
package main
import "fmt"
func main() {
fmt.Printf("Multiply 2 * 5 * 6 = %d\n", MultiPly3Nums(2, 5, 6))
// var i1 int = MultiPly3Nums(2, 5, 6)
// fmt.Printf("MultiPly 2 * 5 * 6 = %d\n", i1)
}
func MultiPly3Nums(a int, b int, c int) int {
// var product int = a * b * c
// return product
return a * b * c
}
输出显示:
Multiply 2 * 5 * 6 = 60
如果一个函数需要返回四到五个值,我们可以传递一个切片给函数(如果返回值具有相同类型)或者是传递一个结构体(如果返回值具有不同的类型)。因为传递一个指针允许直接修改变量的值,消耗也更少。
问题 6.2:
如下的两个函数调用有什么不同:
(A) func DoSomething(a *A) {
b = a
}
(B) func DoSomething(a A) {
b = &a
}
goroutine
goroutine是Go并行设计的核心。goroutine说到底其实就是线程,但是他比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。 goroutine比thread更易用、更高效、更轻便。 goroutine通过go关键字实现了,其实就是一个普通的函数。 Range和Close 记住应该在生产者的地方关闭channel,而不是消费的地方去关闭它,这样容易引起panic Select select默认是阻塞的,只有当监听的channel中有发送或接收可以进行时才会运行,当多个channel都准备好的时候,select是随机的选择一个执行的。
在select里面还有default语法,select其实就是类似switch的功能,default就是当监听的channel都没有准备好的时候,默认执行的(select不再阻塞等待channel)。
超时
runtime goroutine
|
2.8 总结
这一章我们主要介绍了Go语言的一些语法,通过语法我们可以发现Go是多么的简单,只有二十五个关键字。让我们再
来回顾一下这些关键字都是用来干什么的。
break | default | func | interface | select |
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
var和const参考2.2Go语言基础里面的变量和常量申明
package和import已经有过短暂的接触
func 用于定义函数和方法
return 用于从函数返回
defer 用于类似析构函数
go 用于并行
select 用于选择不同类型的通讯
interface 用于定义接口,参考2.6小节
struct 用于定义抽象数据类型,参考2.5小节
break、case、continue、for、fallthrough、else、if、switch、goto、default这些参考2.3流程介绍里面
chan用于channel通讯
type用于声明自定义类型
map用于声明map类型数据
range用于读取slice、map、channel数据
上面这二十五个关键字记住了,那么Go你也已经差不多学会了。
《Go语言标准库》The Golang Standard Library by Example
https://books.studygolang.com/The-Golang-Standard-Library-by-Example/
go环境配置
goIDEA环境配置
go helloworld
Go 基本命令
go build 命令主要是用于测试编译。在包的编译过程中,若有必要,会同时编译与之相关联的包。
go run hello.go
go get 命令主要是用来动态获取远程代码包的。
go get github.com/go-sql-driver/mysql
go run 命令主要用于编译并运行Go程序。
go run hello.go
go test 命令,会自动读取源码目录下面名为*_test.go的文件,生成并运行测试用的可执行文件
|
go api
https://studygolang.com/pkgdoc
go 小demo
https://blog.csdn.net/mhw828/article/details/80703706
iota
package main
import "fmt"
func main() {
//为 0 const a = iota const ( //每增加一行+1 b = iota c = iota d = iota
e, f = iota, iota // 同一行数值相等 g, h, i, j, k = iota, iota, iota, iota, iota ) fmt.Printf("a = %d , b = %d , c = %d , d = %d , e = %d , f = %d , g = %d , h = %d , i = %d , j = %d , k = %d ", a, b, c, d, e, f, g, h, i, j, k) //每次 const 出现时,都会让 iota 初始化为0. const l = iota fmt.Println(l)
}
|
const ( a = iota b c // 使用 "_" 可以跳过值 _ _ d ) |
函数作为返回值 package main
import "fmt"
func main() {
hello := func() { println("hello")
} hello() //打印类型 fmt.Printf(reflet.Typeof(hello)) }
|
Web服务
package main
import ( "fmt" "log" "net/http" )
func main() { http.HandleFunc("/", handler) // each request calls handler 不可以带/的 log.Fatal(http.ListenAndServe("localhost:9512", nil)) }
// handler echoes the Path component of the request URL r. func handler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "你是我的眼") } |
Http请求
func main() {
//生成请求的客户端
}
|
原子计数器 package main
import "fmt" import "time" import "sync/atomic" import "runtime"
func main() { // 我们使用一个无符号整型来代表一个永远为正整数的counter var ops uint64 = 0 // 为了模拟并行更新,我们使用50个协程来每隔1毫秒来 // 增加一下counter值,注意这里的50协程里面的for循环, // 也就是说如果主协程不退出,这些协程将永远运行下去 // 所以这个程序每次输出的值有可能不一样 for i := 0; i < 50; i++ { go func() { for { // 为了能够保证counter值增加的原子性,我们使用 // atomic包中的AddUint64方法,将counter的地址和 // 需要增加的值传递给函数即可 atomic.AddUint64(&ops, 1) // 允许其他的协程来处理 runtime.Gosched() } }() } //等待1秒中,让协程有时间运行一段时间 time.Sleep(time.Second) // 为了能够在counter仍被其他协程更新值的同时安全访问counter值, // 我们获取一个当前counter值的拷贝,这里就是opsFinal,需要把 // ops的地址传递给函数`LoadUint64` opsFinal := atomic.LoadUint64(&ops) fmt.Println("ops:", opsFinal) }
|
信号 package main import "fmt" import "os" import "os/signal" import "syscall" func main() { // Go信号通知通过向一个channel发送``os.Signal`来实现。 // 我们将创建一个channel来接受这些通知,同时我们还用 // 一个channel来在程序可以退出的时候通知我们 sigs := make(chan os.Signal, 1) done := make(chan bool, 1) // `signal.Notify`在给定的channel上面注册该channel // 可以接受的信号 signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) // 这个goroutine阻塞等待信号的到来,当信号到来的时候, // 输出该信号,然后通知程序可以结束了 go func() { sig := <-sigs fmt.Println() fmt.Println(sig) done <- true }() // 程序将等待接受信号,然后退出 fmt.Println("awaiting signal") <-done fmt.Println("exiting") }
|
chan package main
import ( "fmt" )
func main() {
str1 := make(chan int, 2) str2 := make(chan int, 2)
str1 <- 100 str2 <- 0xff
c1 := <-str1 c2 := <-str2
fmt.Printf("%d %d", c1, c2) }
|
空interface
空interface(interface{})不包含任何的method,正因为如此,所有的类型都实现了空interface。空interface对于描述起不到任何的作用(因为它不包含任何的method),但是空interface在我们需要存储任意类型的数值的时候相当有用,因为它可以存储任意类型的数值。它有点类似于C语言的void*类型。
// 定义a为空接口
vara
interface{}
vari
int=
5
s :=
"Hello world"
// a可以存储任意类型的数值
a = i
a = s
一个函数把interface{}作为参数,那么他可以接受任意类型的值作为参数,如果一个函数返回interface{},那么也就可以返回任意类型的值。是不是很有用啊!
go的mysql数据库驱动
MySQL驱动
Go中支持MySQL的驱动目前比较多,有如下几种,有些是支持database/sql标准,而有些是采用了自己的实现接口,常用的有如下几种:
- https://github.com/go-sql-driver/mysql 支持database/sql,全部采用go写。
- 这个驱动比较新,维护的比较好
- 完全支持database/sql接口
- 支持keepalive,保持长连接,虽然星星fork的mymysql也支持keepalive,但不是线程安全的,这个从底层就支持了keepalive。
示例代码接下来的几个小节里面我们都将采用同一个数据库表结构:数据库test,用户表userinfo,关联用户信息表userdetail。 CREATE
CREATE
如下示例将示范如何使用database/sql接口对数据库表进行增删改查操作 package main import ( _ "github.com/go-sql-driver/mysql" "database/sql" "fmt" //"time" ) func main()
func checkErr(err error)
通过上面的代码我们可以看出,Go操作Mysql数据库是很方便的。 关键的几个函数我解释一下: sql.Open()函数用来打开一个注册过的数据库驱动,go-sql-driver中注册了mysql这个数据库驱动,第二个参数是DSN(Data Source Name),它是go-sql-driver定义的一些数据库链接和配置信息。它支持如下格式: user
db.Prepare()函数用来返回准备要执行的sql操作,然后返回准备完毕的执行状态。 db.Query()函数用来直接执行Sql返回Rows结果。 stmt.Exec()函数用来执行stmt准备好的SQL语句
|
NOSQL
5.6 NOSQL数据库操作
由 YmKK 创建, 最后一次修改 2016-02-24
NoSQL(Not Only SQL),指的是非关系型的数据库。随着Web2.0的兴起,传统的关系数据库在应付Web2.0网站,特别是超大规模和高并发的SNS类型的Web2.0纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。
而Go语言作为21世纪的C语言,对NOSQL的支持也是很好,目前流行的NOSQL主要有redis、mongoDB、Cassandra和Membase等。这些数据库都有高性能、高并发读写等特点,目前已经广泛应用于各种应用中。我接下来主要讲解一下redis和mongoDB的操作。
redis
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集合)。
目前应用redis最广泛的应该是新浪微博平台,其次还有Facebook收购的图片社交网站instagram。以及其他一些有名的互联网企业
目前我fork了最后一个驱动,更新了一些bug,目前应用在我自己的短域名服务项目中(每天200W左右的PV值)
https://github.com/astaxie/goredis
接下来的以我自己fork的这个redis驱动为例来演示如何进行数据的操作 package main import ( "github.com/astaxie/goredis" "fmt" ) func main()
我们可以看到操作redis非常的方便,而且我实际项目中应用下来性能也很高。client的命令和redis的命令基本保持一致。所以和原生态操作redis非常类似。
|
第六章 session和数据存储
Web里面经典的解决方案是cookie和session,cookie机制是一种客户端机制,把用户数据保存在客户端,而session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构来保存信息,每一个网站访客都会被分配给一个唯一的标志符,即sessionID,它的存放形式无非两种:要么经过url传递,要么保存在客户端的cookies里.当然,你也可以将Session保存到数据库里,这样会更安全,但效率方面会有所下降
go main 文件的引用 Init
go增删改查
go models 实体与数据库的映射
https://blog.csdn.net/boss2967/article/details/82792076
beego 环境配置
Beego环境搭建和bee工具安装使用,以Windows环境为例。
首先,下载并安装好GO并配置好GOROOT和GOPATH环境变量(如果您是用msi包安装的go,那么这些环境变量已经设置好了)。并在Path环境变量中加入%GOPATH%\bin和%GOROOT%bin。
第二步,下载并安装好git bash工具。
第三步,打开gitbash,输入 go get github.com/astaxie/beego 。稍等片刻即可在GOPATH的src目录下看到有\github.com\astaxie\beego目录。
第四步,在gitbash中输入 go get github.com/beego/bee 。稍等片刻即可在GOPATH的src目录下看到有\github.com\beego\bee目录,同时有很多依赖包被下载添加到\github.com目录下。
测试bee是否安装成功,可在命令行中输入bee,得到如下结果就成功了。 第五步:使用bee工具生成beego框架工程代码。在这一步之前一定要确定在PATH环境变量里已经加入了%GOPATH%\bin,里面有bee.exe。然后在开始菜单中找到命令提示符,以管理员身份运行(或者在git bash工具中进入到GOPATH的src目录下,注意必须到这个目录下),再输入 bee new <工程名称>,这样一个beego框架的工程就生成成功了。
|
beego 创建 web 项目 ,api项目
然后在开始菜单中找到命令提示符,以管理员身份运行(或者在git bash工具中进入到GOPATH的src目录下,注意必须到这个目录下),再输入 bee new <工程名称>,这样一个beego框架的工程就生成成功了。 //web 项目 Bee new project // bee api 项目 Bee api project |
beego blog 项目
beego项目交叉编译 部署到linux环境运行
go 项目编译 linux 运行
在xx.go所在的的文件夹下按sheet+鼠标右键在dos下打开,执行下面的命令 SET CGO_ENABLED=0 set GOARCH=amd64 set GOOS=linux GOOS:目标平台的操作系统(darwin、freebsd、linux、windows) GOARCH:目标平台的体系架构(386、amd64、arm) 交叉编译不支持 CGO 所以要禁用它 上面的命令编译 64 位可执行程序,你当然应该也会使用 386 编译 32 位可执行程序 go build xx.go 会生成一个没有后缀的xx二进制文件 并把项目 conf,static,views文件夹一起打包 将该文件放入linux系统某个文件夹下 赋予权限 chmod 777 xx 执行 ./xx
|
beego项目编译运行linux环境
打开Terminal 定位到工程的
使用命令
打包成Windows 命令
|