从 Python 到 Golang

作者: Hevienz | 来源:发表于2016-01-23 16:56 被阅读5810次

    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, 世界")
    }
    

    用golang写个mongodb的api出来。
    https://github.com/Hevienz/gokule
    main.go

    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
    

    继续看一个Kill MySQL慢查询的小工具。
    https://github.com/Hevienz/stareMySQL
    main.go

    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)
        <-c // 阻塞main goroutine
    }
    

    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, %#v\n", p, p)
             res = "error" //defer中修改函数的返回值
          }
       }()
       panic(input) //触发panic
       return "normal"
    }
    
    func main() {
       err := PanicAndRecover("ds")
       fmt.Println(err)
    }
    

    面向对象的例子。
    https://github.com/Hevienz/go-mongo
    mongo.go

    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, %s\n", 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 %s\n", t)
    
        for i := 0; i < v.NumMethod(); i++ {
            methType := v.Method(i).Type()
            fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name,
                strings.TrimPrefix(methType.String(), "func"))
        }
    }
    

    其它重要的特性包括:接口,类型断言,类型分支,channel,互斥锁等,此处不再介绍。

    相关文章

      网友评论

        本文标题:从 Python 到 Golang

        本文链接:https://www.haomeiwen.com/subject/dzckkttx.html