節操,代碼,修養,妹子和其他(Go語言版)

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

Festival & Fuck, Coding, Inner depth, Sister and Others.

某些文章會提到《為什麼Go語言這麼不受待見》,《真的沒必要浪費心思在 Go 語言上》,《我為什麼放棄Go語言》,《Why worse is better》等話題。經常重溫這些話題,每次都會有新發現。最忌手裡有了一個語言,心裡便容不下另一個語言。

忽略細節、文法或者設計,Go語言各種好用。考慮到這些因素,Go被噴出翔都不為過。

本文不打算在細節、文法或者設計上扯淡,只舉些例子,說一說如何用Go語言寫出還湊合的代碼。

類、對象、屬性,可能還夾雜著一點設計模式

//代碼來自 https://github.com/xgdapg/xconn/blob/master/xconn.go,已驗證//Conn 對應一個tcp串連type Conn struct {        //原生TCP串連    conn         net.Conn        //發送資料的channel(類似隊列)    send         chan *MsgData        //訊息處理方法    msgHandler   MsgHandler        //tcp緩衝區    recvBuffer   []byte        //訊息一次封裝,後續會進行二次封裝    msgPacker    MsgPacker        //健全狀態檢查周期    pingInterval uint        //健全狀態檢查方法    pingHandler  PingHandler        //停止健全狀態檢查channel    pingStop     chan bool        //自訂關閉串連時所調用方法    closeHandler CloseHandler}//MsgHandler 為自訂處理訊息type MsgHandler interface {    HandleMsg(*MsgData)}//MsgPacker 為自訂拆包解包方式,為了效能可以直接將此段與recvLoop端合并type MsgPacker interface {    PackMsg(*MsgData) []byte    UnpackMsg([]byte) *MsgData}//MsgData 自訂訊息類型type MsgData struct {    Data []byte    Ext  interface{}}//PingHandler 為自訂心跳命令type PingHandler interface {    HandlePing()}//CloseHandler 為串連斷開時的自訂動作type CloseHandler interface {    HandleClose()}//NewConn 為處理新串連方式:啟動兩個協程,一個只負責讀,一個只負責寫,//也可以認為開啟了三個協程,第三個協程負責進行定時ping操作func NewConn(conn net.Conn) *Conn {    c := &Conn{        conn:         conn,        send:         make(chan *MsgData, 64),        msgHandler:   nil,        recvBuffer:   []byte{},        msgPacker:    nil,        pingInterval: 0,        pingHandler:  nil,        pingStop:     nil,        closeHandler: nil,    }    go c.recvLoop()    go c.sendLoop()    return c}//recoverPanic 程式panic情況下的處理方法(例如向已經關閉的tcp串連寫資料會造成panic)func recoverPanic() {    if err := recover(); err != nil {        //fmt.Println(err)    }}//SetMsgHandler 為 Getter Setter 方法func (this *Conn) SetMsgHandler(hdlr MsgHandler) {    this.msgHandler = hdlr}//SetMsgPacker 為 Getter Setter 方法func (this *Conn) SetMsgPacker(packer MsgPacker) {    this.msgPacker = packer}//SetPing 為 Getter Setter 方法func (this *Conn) SetPing(sec uint, hdlr PingHandler) {    this.pingInterval = sec    this.pingHandler = hdlr    if this.pingStop == nil {        this.pingStop = make(chan bool)    }    if sec > 0 {        go this.pingLoop()    }}//SetCloseHandler 為 Getter Setter 方法func (this *Conn) SetCloseHandler(hdlr CloseHandler) {    this.closeHandler = hdlr}//pingLoop 為定時健全狀態檢查操作func (this *Conn) pingLoop() {    defer recoverPanic()    for {        select {        case <-this.pingStop:            return        case <-time.After(time.Duration(this.pingInterval) * time.Second):            this.Ping()        }    }}//RawConn 返回原始的tcp串連func (this *Conn) RawConn() net.Conn {    return this.conn}//recvLoop 用於處理接收到的tcp包,並進行拆包等操作,然後調用recvMsg方法進行處理func (this *Conn) recvLoop() {    defer recoverPanic()    defer this.Close()    buffer := make([]byte, 2048)        //一次封包協議:四個位元組(int32)表示包長度,根據包長度截取訊息長度作為包。    for {        bytesRead, err := this.conn.Read(buffer)        if err != nil {            return        }        this.recvBuffer = append(this.recvBuffer, buffer[0:bytesRead]...)        for len(this.recvBuffer) > 4 {            length := binary.BigEndian.Uint32(this.recvBuffer[0:4])            readToPtr := length + 4            if uint32(len(this.recvBuffer)) < readToPtr {                break            }            if length == 0 {                if this.pingHandler != nil {                    this.pingHandler.HandlePing()                }            } else {                buf := this.recvBuffer[4:readToPtr]                go this.recvMsg(buf)            }            this.recvBuffer = this.recvBuffer[readToPtr:]        }    }}//recvMsg 為代理,實際執行的是背景HandleMsg方法。func (this *Conn) recvMsg(data []byte) {    defer recoverPanic()    msg := &MsgData{        Data: data,        Ext:  nil,    }        //調用UnackMsg對資訊進行二次解包    if this.msgPacker != nil {        msg = this.msgPacker.UnpackMsg(data)    }    if this.msgHandler != nil {        this.msgHandler.HandleMsg(msg)    }}//sendLoop 用於發送資料包func (this *Conn) sendLoop() {    defer recoverPanic()    for {        msg, ok := <-this.send        if !ok {            break        }        go this.sendMsg(msg)    }}//sendMsg 用於發送資料包,實際先調用PackMsg進行資訊持久化,然後二次封包,轉換為本架構能接受的形式func (this *Conn) sendMsg(msg *MsgData) {    defer recoverPanic()    sendBytes := make([]byte, 4)    if msg != nil {        data := msg.Data        if this.msgPacker != nil {            data = this.msgPacker.PackMsg(msg)        }        length := len(data)        binary.BigEndian.PutUint32(sendBytes, uint32(length))        sendBytes = append(sendBytes, data...)    }    this.conn.Write(sendBytes)}//Close 關閉串連func (this *Conn) Close() {    defer recoverPanic()    this.conn.Close()    close(this.send)    if this.pingStop != nil {        close(this.pingStop)    }    if this.closeHandler != nil {        this.closeHandler.HandleClose()    }}//SendMsg 用於發送資料func (this *Conn) SendMsg(msg *MsgData) {    this.send <- msg}//SendData 用於發送資料func (this *Conn) SendData(data []byte) {    this.SendMsg(&MsgData{Data: data, Ext: nil})}//Ping 用於健康監測func (this *Conn) Ping() {    go this.sendMsg(nil)}

作為一個專用於處理TCP連結的架構,實際上xconn(上文中的代碼)進行了兩次封裝,連訊息發送、資訊拆包封包、甚至接收資訊都進行了二次封裝。

實際代碼中,可以進行簡化操作,將二次的部分簡化為一次。

將代碼寫得和上面一樣工整,便已經超越大部分猿了。

上個例子中作者用到了recover,用的很克制,卻又恰到好處。

至於defer和panic

Go語言的try catch?

import (    "fmt"    "github.com/manucorporat/try")func main() {    try.This(func() {        panic("my panic")    }).Finally(func() {        fmt.Println("this must be printed after the catch")    }).Catch(func(e try.E) {        // Print crash        fmt.Println(e)    })}

以上代碼純屬搞笑,個人不建議工程項目中使用如此寫法,但是這種做法可以借鑒。

工程代碼:(用於Go與資料庫Transaction)

//代碼來自《Go語言遊戲項目應用情況彙報》func (db *Database) Transaction(work func()) {    db.lock.Lock()    defer db.lock.UnLock()    //事務控制    defer func() {        if err := recover; err == nil {            db.commit(info)        } else {            db.rollback()            //選擇性拋出panic            panic(TransError{err})        }    }()    //執行傳入的函數    work()}

《Go語言遊戲項目應用情況彙報》是我所能找到的,為數不多的幾個敢開放部分工程代碼的分享。整體代碼比較整潔,適於新手學習。

以上對err的處理方法寫法,和《Errors are values》有異曲同工之妙。

err的一種處理方式

示範代碼:來自《Errors are vales》

//這種寫法強烈不推薦!!!!這就是許多人說的Go程式一大半都在check error_, err = fd.Write(p0[a:b])if err != nil {    return err}_, err = fd.Write(p1[c:d])if err != nil {    return err}_, err = fd.Write(p2[e:f])if err != nil {    return err}// and so on//重要的事情說兩遍:不推薦,但是個人小項目這樣寫,完全沒問題。

推薦如下寫法:

var err errorwrite := func(buf []byte) {    if err != nil {        return    }    _, err = w.Write(buf)}write(p0[a:b])write(p1[c:d])write(p2[e:f])// and so onif err != nil {    return err}

也推薦如下寫法:

func (ew *errWriter) write(buf []byte) {    if ew.err != nil {        return    }    _, ew.err = ew.w.Write(buf)}

其他:

正名:為什麼選擇Go語言。

答:因為簡單,並且也不會別的。

建議:盡量選擇Go1.6及以上版本,避免GC造成程式STW。

至於GC效能,可以參考 Go1.6中的gc pause已經完全超越JVM了嗎?。

原文禁止轉載,因此提煉出關鍵字:從效果看XXX,但XXX;並且,XXX,XXX。

So, Why worse is better?

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.