golang轻量级I/O事件通知库插件event的使用

Golang轻量级I/O事件通知库插件event的使用

event logo

event 是一个用于Go的网络I/O事件通知库。它使用epoll和kqueue来轮询I/O事件,速度快且内存占用低。它的工作方式类似于libevent。

event 的目标是为构建高性能网络应用程序提供基础工具。

特性

  • 支持读/写/超时事件
  • 灵活的定时器和定时器事件
  • 支持事件优先级
  • 简单的API
  • 低内存使用

安装

要开始使用event,只需运行go get

go get -u github.com/cheng-zhongliang/event

事件类型

  • EvRead:当文件描述符可读时触发
  • EvWrite:当文件描述符可写时触发
  • EvTimeout:当超时到期时触发

当事件被触发时,回调函数将被调用。

读/写/超时事件

这些事件可以组合使用:

base := event.NewBase()
ev := event.New(base, fd, event.EvRead|event.Timeout, callback, arg)
ev.Attach(time.Second)

当文件描述符可读或超时到期时,此事件将被触发。

选项

事件默认是一次性的。如果要持久化,可以设置EvPersist选项:

ev := event.New(base, fd, event.EvRead|event.EvPersist, callback, arg)

定时器

定时器是一个一次性事件,将在超时到期后触发:

base := event.NewBase()
ev := event.NewTimer(base, callback, arg)
ev.Attach(time.Second)

定时器

定时器是一个重复事件,每次超时到期时都会触发:

base := event.NewBase()
ev := event.NewTicker(base, callback, arg)
ev.Attach(time.Second)

优先级

当事件同时触发时,高优先级事件将优先分发:

ev := event.New(base, fd, event.EvRead, callback, arg)
ev.SetPriority(event.HP)

使用示例

以下是一个绑定到端口1246的回显服务器示例:

package main

import (
	"syscall"

	"github.com/cheng-zhongliang/event"
)

func main() {
	base, err := event.NewBase()
	if err != nil {
		panic(err)
	}
	fd := socket()
	ev := event.New(base, fd, event.EvRead|event.EvPersist, accept, base)
	if err := ev.Attach(0); err != nil {
		panic(err)
	}
	if err := base.Dispatch(); err != nil && err != syscall.EBADF {
		panic(err)
	}
	syscall.Close(fd)
}

func socket() int {
	addr := syscall.SockaddrInet4{Port: 1246, Addr: [4]byte{0, 0, 0, 0}}
	fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
	if err != nil {
		panic(err)
	}
	if err := syscall.Bind(fd, &addr); err != nil {
		panic(err)
	}
	err = syscall.Listen(fd, syscall.SOMAXCONN)
	if err != nil {
		panic(err)
	}
	return fd
}

func accept(fd int, events uint32, arg interface{}) {
	base := arg.(*event.EventBase)
	clientFd, _, err := syscall.Accept(fd)
	if err != nil {
		panic(err)
	}
	ev := event.New(base, clientFd, event.EvRead|event.EvPersist, echo, nil)
	if err := ev.Attach(0); err != nil {
		panic(err)
	}
}

func echo(fd int, events uint32, arg interface{}) {
	buf := make([]byte, 0xFFF)
	n, err := syscall.Read(fd, buf)
	if err != nil {
		panic(err)
	}
	if _, err := syscall.Write(fd, buf[:n]); err != nil {
		panic(err)
	}
}

连接到回显服务器:

telnet localhost 1246

更多关于golang轻量级I/O事件通知库插件event的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang轻量级I/O事件通知库插件event的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang轻量级I/O事件通知库event使用指南

event是一个轻量级的Go语言I/O事件通知库,它提供了一种简单的方式来监控文件描述符上的I/O事件。下面我将详细介绍如何使用这个库。

安装

首先安装event库:

go get github.com/smallnest/event

基本使用

1. 创建事件循环

package main

import (
	"fmt"
	"github.com/smallnest/event"
	"os"
	"time"
)

func main() {
    // 创建事件循环
    loop, err := event.NewEventLoop()
    if err != nil {
        fmt.Println("Failed to create event loop:", err)
        return
    }
    defer loop.Close()
    
    // 启动事件循环
    go loop.Run()
    
    // 在这里添加事件监控...
}

2. 监控文件描述符

func watchFile(loop *event.EventLoop) {
    // 打开文件
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("Failed to open file:", err)
        return
    }
    defer file.Close()
    
    // 添加读事件监控
    err = loop.AddRead(file.Fd(), func(fd uintptr, extra interface{}) {
        fmt.Printf("File %d is readable\n", fd)
        
        // 读取文件内容
        buf := make([]byte, 1024)
        n, err := file.Read(buf)
        if err != nil {
            fmt.Println("Read error:", err)
            return
        }
        fmt.Printf("Read %d bytes: %s\n", n, string(buf[:n]))
    }, nil)
    
    if err != nil {
        fmt.Println("Failed to add read event:", err)
    }
}

3. 监控定时器事件

func watchTimer(loop *event.EventLoop) {
    // 添加定时器
    timerID, err := loop.AddTimer(2*time.Second, true, func(fd uintptr, extra interface{}) {
        fmt.Println("Timer fired!")
    }, nil)
    
    if err != nil {
        fmt.Println("Failed to add timer:", err)
        return
    }
    
    // 5秒后删除定时器
    time.AfterFunc(5*time.Second, func() {
        loop.DeleteTimer(timerID)
        fmt.Println("Timer removed")
    })
}

高级特性

1. 监控网络socket

func watchSocket(loop *event.EventLoop) {
    // 创建TCP监听
    ln, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Listen error:", err)
        return
    }
    defer ln.Close()
    
    // 监控监听socket的可读事件(新连接)
    err = loop.AddRead(ln.(*net.TCPListener).Fd(), func(fd uintptr, extra interface{}) {
        conn, err := ln.Accept()
        if err != nil {
            fmt.Println("Accept error:", err)
            return
        }
        fmt.Println("New connection from:", conn.RemoteAddr())
        
        // 监控新连接的读事件
        go handleConnection(loop, conn.(*net.TCPConn))
    }, nil)
    
    if err != nil {
        fmt.Println("Failed to monitor listener:", err)
    }
}

func handleConnection(loop *event.EventLoop, conn *net.TCPConn) {
    defer conn.Close()
    
    err := loop.AddRead(conn.Fd(), func(fd uintptr, extra interface{}) {
        buf := make([]byte, 1024)
        n, err := conn.Read(buf)
        if err != nil {
            fmt.Println("Read error:", err)
            loop.Delete(fd) // 移除监控
            return
        }
        fmt.Printf("Received %d bytes: %s\n", n, string(buf[:n]))
    }, nil)
    
    if err != nil {
        fmt.Println("Failed to monitor connection:", err)
    }
}

2. 事件优先级

event库支持设置事件优先级:

// 添加高优先级读事件
err := loop.AddReadWithPriority(file.Fd(), event.HighPriority, func(fd uintptr, extra interface{}) {
    // 处理高优先级事件
}, nil)

注意事项

  1. 事件回调函数应该尽量快速执行,避免阻塞事件循环
  2. 在回调函数中删除当前事件是安全的
  3. 跨goroutine调用EventLoop的方法需要自己处理同步问题
  4. 文件描述符关闭后会自动从事件循环中移除

完整示例

package main

import (
	"fmt"
	"github.com/smallnest/event"
	"os"
	"time"
)

func main() {
    loop, err := event.NewEventLoop()
    if err != nil {
        fmt.Println("Failed to create event loop:", err)
        return
    }
    defer loop.Close()
    
    go loop.Run()
    
    // 监控文件
    watchFile(loop)
    
    // 监控定时器
    watchTimer(loop)
    
    // 运行10秒
    time.Sleep(10 * time.Second)
}

func watchFile(loop *event.EventLoop) {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("Failed to open file:", err)
        return
    }
    defer file.Close()
    
    err = loop.AddRead(file.Fd(), func(fd uintptr, extra interface{}) {
        buf := make([]byte, 1024)
        n, err := file.Read(buf)
        if err != nil {
            fmt.Println("Read error:", err)
            return
        }
        fmt.Printf("Read %d bytes: %s\n", n, string(buf[:n]))
    }, nil)
    
    if err != nil {
        fmt.Println("Failed to add read event:", err)
    }
}

func watchTimer(loop *event.EventLoop) {
    timerID, err := loop.AddTimer(2*time.Second, true, func(fd uintptr, extra interface{}) {
        fmt.Println("Timer fired!")
    }, nil)
    
    if err != nil {
        fmt.Println("Failed to add timer:", err)
        return
    }
    
    time.AfterFunc(5*time.Second, func() {
        loop.DeleteTimer(timerID)
        fmt.Println("Timer removed")
    })
}

event库是一个轻量级的解决方案,适合需要简单I/O事件通知的场景。对于更复杂的应用,可以考虑使用更全面的库如gnet或evio。

回到顶部