Why Python
Python是一个很酷的编程语言,它极致的简洁,极易上手,我们可以用它来做云计算、大数据分析,运维自动化,当然还可以写网站、做爬虫。
Why not Python
但我们也清楚地知道它会受限于GIL,还好我们有gevent,我们可以很愉快地monkey.patch_all(),然后用greenlet来处理,但即使这样,在并发量极高的情况下,它的效率就有些令我们担心了。
Golang or OpenResty
当我们开始关注高性能高并发服务端实现的时候,我们自然而然地将目光转向一些现代化的技术,这就是Golang和OpenResty,当然他们的核心都是将IO复用与协程进行有机的融合,但却有着截然不同的表现风格。如果你要问我更喜欢哪个,我会说both。当然下文我会介绍Golang。
Golang
我的哲学是,要熟悉一个编程语言,就是要从最简单的代码开始写起来。百试不爽。
万年不变的hello world先放这。
hello.go
package main //包声明
import "fmt" //导入包
func main() {
fmt.Println("Hello, 世界")
}
package main
import (
"encoding/json"
"github.com/cloudflare/conf"
"gopkg.in/macaron.v1" //Web框架Macaron
"labix.org/v2/mgo"
"log"
"net/http"
"os"
"strconv"
)
// 声明包级别的变量
var (
MONGO_URL string
MONGO_DB string
)
// 包初始化函数init
func init() {
c, err := conf.ReadConfigFile("config.conf") //配置文件见config.conf
e(err, true)
MONGO_URL = c.GetString("MONGO_URL", "127.0.0.1:27017")
MONGO_DB = c.GetString("MONGO_DB", "test")
}
func e(err error, fatal bool) (ret bool) {
if err != nil {
log.Println(err.Error())
if fatal {
os.Exit(1)
}
return true
} else {
return false
}
}
func main() {
ms, err := mgo.Dial(MONGO_URL)
e(err, true)
defer ms.Close() //延迟到函数将要返回时执行
ms.SetMode(mgo.Monotonic, true)
mdb := ms.DB(MONGO_DB)
m := macaron.Classic()
m.Map(mdb)
m.Get("/", func(mdb *mgo.Database) string {
names, err := mdb.CollectionNames()
e(err, false)
body, err := json.Marshal(names)
e(err, false)
return string(body)
})
m.Get("/:col", func(mdb *mgo.Database, ctx *macaron.Context, req *http.Request) string {
req.ParseForm()
mcol := mdb.C(ctx.Params(":col"))
limit := 20
offset := 0
if t := req.FormValue("limit"); t != "" {
limit, err = strconv.Atoi(t)
e(err, false)
}
if t := req.FormValue("offset"); t != "" {
offset, err = strconv.Atoi(t)
e(err, false)
}
total_count, err := mcol.Find(nil).Count()
e(err, false)
items := []map[string]interface{}{}
iter := mcol.Find(nil).Skip(offset).Limit(limit).Iter()
for {
item := map[string]interface{}{}
if iter.Next(&item) {
// item := map[string]interface{}{} 不能写在这里,因为Map是引用类型
items = append(items, item)
} else {
break
}
}
dict := map[string]interface{}{
"meta": map[string]int{"total_count": total_count, "offset": offset, "limit": limit},
"data": items,
"msg": "success",
"code": 0,
}
body, err := json.Marshal(dict)
e(err, false)
return string(body)
})
m.Run()
}
config.conf
MONGO_URL=127.0.0.1:27017
MONGO_DB=devmgmt
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql" //匿名导入
"io/ioutil"
"log"
"os"
"regexp"
"strings"
"time"
)
func e(err error, fatal bool) (ret bool) {
if err != nil {
log.Println(err.Error())
if fatal {
os.Exit(1)
}
return true
} else {
return false
}
}
var (
DBS = map[string]string{}
dbs = map[string]*sql.DB{}
)
func init() {
log.SetFlags(log.Flags() | log.Lshortfile)
bytes, err := ioutil.ReadFile("config.conf") //见config.conf
e(err, true)
str := string(bytes)
lines := strings.Split(str, "n")
reg, err := regexp.Compile(`s+`)
e(err, true)
for num, line := range lines {
if line == "" {
continue
}
if strings.HasPrefix(line, "#") {
continue
}
fields := reg.Split(line, -1)
if len(fields) != 2 {
log.Printf("Line %d is not match the config format.", num+1)
continue
}
dbname := fields[0]
dburi := fields[1]
DBS[dbname] = dburi
dbs[dbname] = init_db(dburi)
}
}
func init_db(uri string) *sql.DB {
db, err := sql.Open("mysql", uri)
e(err, true)
return db
}
func task(dbname string, db *sql.DB) {
for {
rows, err := db.Query("select id,info,time from information_schema.PROCESSLIST where time > 5 and command = 'Query';")
if err != nil {
log.Println(err.Error())
dbs[dbname] = init_db(DBS[dbname])
task(dbname, dbs[dbname])
break
}
for rows.Next() {
var id string
var info string
var time string
err = rows.Scan(&id, &info, &time)
if err != nil {
log.Println(err.Error())
} else {
log.Printf("[FOUND SQL]: '%s'@'%s' (ConnectionId: %s, Time: %s)", info, dbname, id, time)
_, err := db.Exec(fmt.Sprintf("kill %s;", id))
if err != nil {
log.Println(err.Error())
} else {
log.Printf("[KILLED SQL]: '%s'@'%s' (ConnectionId: %s, Time: %s)", info, dbname, id, time)
}
}
}
time.Sleep(1 * time.Second)
}
}
func main() {
for dbname, db := range dbs {
go task(dbname, db) //用go关键字创建goroutine
}
c := make(chan int)
}
config.conf
# 配置文件以#开头的为注释
# 配置行 为 数据库昵称和数据库uri的组合,中间以若干空白为分隔
db1(127.0.0.1) root:root.com@tcp(127.0.0.1:3306)/mysql?charset=utf8
vhost1 ds:root.com@tcp(192.2.3.143:3306)/mysql?charset=utf8
看个panic和recover的代码片段。
package main
import (
"fmt"
)
func PanicAndRecover(input string) (res string) {
defer func() {
if p := recover(); p != nil { //从panic中恢复
fmt.Printf("%T, %#vn", p, p)
res = "error" //defer中修改函数的返回值
}
}()
panic(input) //触发panic
return "normal"
}
func main() {
err := PanicAndRecover("ds")
fmt.Println(err)
}
package go_mongo
import (
"gopkg.in/mgo.v2"
"log"
"os"
)
type Mongo struct{
session *mgo.Session
}
func New(murls string) *Mongo {
ms, err := mgo.Dial(murls)
if err != nil {
log.Printf("mgo.Dial, %sn", err)
os.Exit(1)
}
return &Mongo{session: ms}
}
func (self *Mongo) Insert(mdbs string, mcols string, docs ...interface{}) error { //*Mongo类型的方法,docs是可变参数
mdb := self.session.DB(mdbs)
mcol := mdb.C(mcols)
return mcol.Insert(docs...)
}
在main.go中使用包github.com/Hevienz/go-mongo。
main.go
package main
import (
"github.com/Hevienz/go-mongo"
"github.com/Hevienz/go-utils" //下文介绍
"log"
)
func main() {
doc := map[string]interface{}{"ds": "cc"}
m := go_mongo.New("127.0.0.1:27017")
go_utils.Dir(m) //调用go_utils包的Dir方法
err := m.Insert("test", "ds", doc)
if err != nil {
log.Println(err)
}
}
用反射实现类似Python中的build-in函数dir。
https://github.com/Hevienz/go-utils
dir.go
package go_utils
import (
"reflect" //导入反射包
"fmt"
"strings"
)
func Dir(x interface{}) {
v := reflect.ValueOf(x)
t := v.Type()
fmt.Printf("type %sn", t)
for i := 0; i < v.NumMethod(); i++ {
methType := v.Method(i).Type()
fmt.Printf("func (%s) %s%sn", t, t.Method(i).Name,
strings.TrimPrefix(methType.String(), "func"))
}
}
其它重要的特性包括:接口,类型断言,类型分支,channel,互斥锁等,此处不再介绍。