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()
相关文章
相关标签/搜索