seelog源碼閱讀【筆記】

最近被後臺日誌弄的很煩,看到有個項目簡簡單單,又能知足須要,順便試下看看效果,作下記錄。只是記錄下一部份內容,就不所有讀了,關於源碼能夠去https://github.com/xmge/seeloggit

結構設計

//  websocket客戶端
type client struct {
    id     string
    socket *websocket.Conn
    send   chan []byte
}

// 客戶端管理
type clientManager struct {
    clients    map[*client]bool
    broadcast  chan []byte
    register   chan *client
    unregister chan *client
}

WebSocket 是 HTML5 開始提供的一種在單個 TCP 鏈接上進行全雙工通信的協議。github

WebSocket 使得客戶端和服務器之間的數據交換變得更加簡單,容許服務端主動向客戶端推送數據。在 WebSocket API 中,瀏覽器和服務器只須要完成一次握手,二者之間就直接能夠建立持久性的鏈接,並進行雙向數據傳輸。web

在 WebSocket API 中,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。數組

程序使用管道做爲通訊基礎瀏覽器

  1. clients 用來保存當前所有的Websocket
  2. broadcast 做爲廣播使用的管道,當收到消息,向全部的clients中的websocket進行傳輸信息
  3. register 當新的連接創建,將client指針放入註冊管道
  4. unregister 當連接斷開,將斷開的連接對象放入取消管道
  5. client結構體內的send管道,當broadcast收到,將信息發到每一個client的send管道中
func (manager *clientManager) start() {
    defer func() {
        if err := recover(); err != nil {
            log.Printf("[seelog] error:%+v", err)
        }
    }()

    for {
        select {
        case conn := <-manager.register:
            manager.clients[conn] = true
        case conn := <-manager.unregister:
            if _, ok := manager.clients[conn]; ok {
                close(conn.send)
                delete(manager.clients, conn)
            }
        case message := <-manager.broadcast:
            for conn := range manager.clients {
                select {
                case conn.send <- message:
                default:
                    close(conn.send)
                    delete(manager.clients, conn)
                }
            }
        }
    }
}

使用select-case進行管道的數據處理,外部加一個for循環保持輪詢的狀態。服務器

func (c *client) write() {
    defer func() {
        manager.unregister <- c
        c.socket.Close()
    }()

    for {
        select {
        case message, ok := <-c.send:
            if !ok {
                c.socket.WriteClose(1)
                return
            }
            c.socket.Write(message)
        }
    }
}

這個是在每一個websocket啓動的時候使用,每一個socket保持一個for循環,使用defer用於關閉操做,當for被打斷(即關閉網頁之類的操做),socket被關閉,則會插入到取消管道中,clients鍵值對會刪除這個鏈接的信息。websocket

監控文件的內容變化

經過os.Stat獲取文件信息,返回值爲fileInfo的接口socket

fileInfo, err = os.Stat(filePath)

func (f *File) Stat() (FileInfo, error)

type FileInfo interface {
        Name() string       // base name of the file 文件名
        Size() int64        // length in bytes for regular files; system-dependent for others 文件大小(byte長度)
        Mode() FileMode     // file mode bits   文件模式(只讀、只寫之類)
        ModTime() time.Time // modification time
        IsDir() bool        // abbreviation for Mode().IsDir()  是不是目錄
        Sys() interface{}   // underlying data source (can return nil)  基礎數據源(能夠返回nil)
}

獲取當前的文件的截止位置設計

offset := fileInfo.Size()

獲取新的文件大小,而後根據文件大小和以前的區別,構建一個新的byte數組,大小爲新的字節數減去舊的字節數指針

msg := make([]byte, newOffset-offset)

使用Open方法打開一個文件,Open方法是以只讀的方式讀取數據

file, err := os.Open(filePath)

func Open(name string) (*File, error)

能夠將文件讀取的起點設置到某個位置,在seelog中,將讀取起點設置到文件末尾,當文件的大小發生變化,則文件從上個起點開始讀取文件內容

_, err = file.Seek(offset, 0)

func (f *File) Seek(offset int64, whence int) (ret int64, err error)

whence 存在3個參數
0:文件頭的絕對位置偏移offset的距離
1:文件的相對位置,即當前位置偏移offset的距離
2:文件末尾的絕對位置偏移offset的距離

這個特性當文件以O_APPEND的模式打開是沒有效果的

msg是以前構造的字節數組,將新增的內容讀取到字節數組中

_, err = file.Read(msg)

使用管道做爲消息傳輸的方式,manager在這裏是一個全局的manager,當管道收到消息,就打印處理

manager.broadcast <- msg

最後記得將文件關閉,不然下次打開會出錯

file.Close()
相關文章
相關標籤/搜索