节 *** ,代码,修养,妹子和其他(Go语言版)

节 *** ,代码,修养,妹子和其他(Go语言版),第1张

概述Festival & Fuck, Coding, Inner depth, Sister and Others. 某些文章会提到《为什么Go语言这么不受待见》,《真的没必要浪费心思在 Go 语言上》,《我为什么放弃Go语言》,《Why worse is better》等话题。经常重温这些话题,每次都会有新发现。最忌手里有了一个语言,心里便容不下另一个语言。 忽略细节、语法或者设计,Go语言各种好用

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?

总结

以上是内存溢出为你收集整理的节 *** ,代码,修养,妹子和其他(Go语言版)全部内容,希望文章能够帮你解决节 *** ,代码,修养,妹子和其他(Go语言版)所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1282824.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-09
下一篇 2022-06-09

发表评论

登录后才能评论

评论列表(0条)

保存