Chanjet:基于Go Channel、Buffer Pool和零拷贝实现的高性能双缓冲通道
项目介绍
Chanjet 是一个基于 Go 语言实现的高性能双缓冲通道,它利用了 Go Channel 的强大功能和 Buffer Pool 的优势,以及零拷贝技术,实现了数据的高效处理和传输。Chanjet 通过设计精巧的数据结构和算法,提高了通道的读写性能,同时减少了内存分配和销毁的开销,从而在保证数据安全的前提下,提升了系统的整体性能。
项目技术分析
Chanjet 的核心功能是基于 Go Channel、Buffer Pool 和零拷贝技术实现的高性能双缓冲通道。Go Channel 是 Go 语言中用于并发编程的一种数据结构,它可以安全地在不同的协程之间传输数据。Buffer Pool 则是一种内存池技术,用于缓存和复用内存块,从而减少内存分配和销毁的开销。零拷贝技术则是指在数据传输过程中,尽量减少数据在内核空间和用户空间之间的复制,从而提高数据传输效率。
Chanjet 的设计理念是将数据写入和读取分离,采用双写缓冲区设计,active 通道用于实时接收数据,passive 通道用于异步处理数据。当 active 满足切换逻辑执行通道轮转后,passive 转换为 active 实时写入通道接收数据,active 转换成 passive 异步处理通道。这样,即使接收方消费受限,也不会明显的阻塞 passive 通道的使用。同时,Chanjet 还设计了统一的读取队列,将 passive 通道中的所有数据写入到统一的 readq 通道中,接收方只需从 readq 中获取数据即可。
项目及技术应用场景
Chanjet 的应用场景非常广泛,包括但不限于:
-
日志系统:Chanjet 可以作为日志收集和传输的中间件,将日志数据高效地写入到存储系统或分析系统中。
-
数据处理系统:Chanjet 可以作为数据处理的中间件,将数据从源端传输到处理端,并保证数据的安全和可靠性。
-
数据同步系统:Chanjet 可以作为数据同步的中间件,将数据从一个系统同步到另一个系统,并保证数据的完整性和一致性。
-
高并发系统:Chanjet 可以作为高并发系统的中间件,将数据在不同的协程之间高效地传输,从而提高系统的并发性能。
项目特点
Chanjet 的特点如下:
-
双写缓冲区设计:active 通道用于实时接收数据,passive 通道用于异步处理数据,当 active 满足切换逻辑执行通道轮转后,passive 转换为 active 实时写入通道接收数据,active 转换成 passive 异步处理通道。
-
统一读取队列设计:passive 异步缓冲通道需要快速异步的将所有数据写入到统一的 readq 通道中,接收方(比如文件写入)只从 readq 中获取即可,这样即使接收方消费受限也不会明显的阻塞 passive 通道的使用。
-
复合通道轮转策略:当 active 通道中数据大小触发一定的阈值,比如最大容量的 80%,立即进行轮转。当达到一定时间,比如 5 秒还没有进行通道轮转时,后台定时程序立即触发通道轮转,防止因长期没有新数据写入导致接收方无法获取通道内数据的问题,也尽可能减少数据丢失的风险。
-
无锁化设计:双缓冲通道不使用加锁保护通道切换,使用原子状态实现并发安全的通道切换,大大提升了性能。
-
缓冲池设计:使用缓冲池设计,通道切换时复用池中可用通道,防止出现频繁的通道创建和销毁的开销。
-
完善监控的设计:完善的监控指标设计,支持 Prometheus 和 OpenTelemetry,目前已支持 Prometheus 指标,抽象批量上报接口,上报指标数据定时批量刷新到底层指标收集器,3700000 条数据写入单条指标上报总耗时 1.5 秒,批量刷新指标总耗时 <1.1 秒,耗时减少 400 毫秒左右,大大提升了性能。
性能测试结果
Chanjet 在性能测试中表现出色,无论是在写入还是读取操作上,都展现出了高效的数据处理能力。以下是一些性能测试结果:
- 128B 测试结果:操作次数达到 9,593,606 次,单次耗时为 138.6 ns/op,内存分配为 157 B/op,分配次数为 0 次。
- 64KB 测试结果:操作次数达到 7,249,588 次,单次耗时为 171.6 ns/op,内存分配为 208 B/op,分配次数为 0 次。
- 1MB 测试结果:操作次数达到 8,231,407 次,单次耗时为 179.2 ns/op,内存分配为 489 B/op,分配次数为 0 次。
这些测试结果表明,Chanjet 在不同的数据大小下都能保持高效的性能,无论是小数据还是大数据,都能快速地完成读写操作。
监控指标说明
Chanjet 提供了完善的监控指标设计,支持 Prometheus 和 OpenTelemetry。这些监控指标可以帮助用户实时了解 Chanjet 的运行状态和性能表现。以下是一些监控指标说明:
- 写入相关指标:包括写入操作总数、已写入数据的总字节数、写入失败的次数等。
- 读取相关指标:包括读取操作总数、已读取数据的总字节数、读取失败的次数等。
- 缓冲区切换指标:包括缓冲区切换操作总次数、切换延迟分布、定时任务跳过切换的次数等。
- 异步处理指标:包括当前活跃的异步工作协程数量。
- 缓冲池指标:包括对象池内存分配次数。
- 通道状态指标:包括当前活跃通道中未处理的数据条目数量和未处理的数据总大小。
- 指标类型说明:包括 Counter、Gauge、Histogram、CounterVec 等指标类型的特性和适用场景。
Chanjet 的监控指标设计全面且易于理解,用户可以根据这些指标来监控和优化 Chanjet 的性能和稳定性。
安装和示例
Chanjet 的安装非常简单,只需要使用以下命令即可:
go get github.com/TimeWtr/Chanjet
安装完成后,用户可以根据自己的需求编写相应的代码来使用 Chanjet。以下是一个示例代码:
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"
cj "github.com/TimeWtr/Chanjet"
"github.com/TimeWtr/Chanjet/_const"
"github.com/TimeWtr/Chanjet/metrics"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
func main() {
ser := gin.Default()
bf, err := cj.NewBuffer(1025*1024*100,
cj.WithMetrics(_const.PrometheusCollector))
if err != nil {
panic(err)
}
ser.GET("/metrics", gin.WrapH(metrics.GetHandler()))
ch := bf.Register()
exitChan := make(chan struct{}, 1)
var wg sync.WaitGroup
wg.Add(3)
go func() {
defer wg.Done()
counter := 0
for data := range ch {
fmt.Println("[收到数据]: ", string(data))
counter++
}
fmt.Println("通道关闭")
fmt.Printf("接收到日志数据条数: %d", counter)
}()
go func() {
defer wg.Done()
defer bf.Close()
template := "2025-05-12 12:12:00 [Info] 日志写入测试,当前的序号为: %d\n"
for i := 0; i < 3100000000; i++ {
err := bf.Write([]byte(fmt.Sprintf(template, i)))
if err != nil {
fmt.Printf("写入日志失败,错误:%s\n", err.Error())
continue
}
}
fmt.Println("结束了")
}()
// HTTP 服务协程
go func() {
defer wg.Done()
srv := &http.Server{
Addr: ":8080",
Handler: ser,
}
// 优雅关闭处理
go func() {
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
fmt.Printf("服务关闭异常: %v\n", err)
}
exitChan <- struct{}{} // 发送退出信号
}()
if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
fmt.Printf("服务启动失败: %v\n", err)
exitChan <- struct{}{}
}
}()
wg.Wait()
fmt.Println("写入成功")
}
在这个示例中,我们创建了一个 Chanjet 实例,并通过 Chanjet 的 Register 方法获取了一个通道。然后,我们创建了三个协程,分别用于写入数据、读取数据和启动 HTTP 服务。最后,我们等待所有协程执行完毕,并输出 "写入成功"。
总结
Chanjet 是一个高性能的双缓冲通道,它基于 Go Channel、Buffer Pool 和零拷贝技术,实现了数据的高效处理和传输。Chanjet 的设计理念是将数据写入和读取分离,采用双写缓冲区设计和统一读取队列设计,以及无锁化设计和缓冲池设计,从而在保证数据安全的前提下,提升了系统的整体性能。Chanjet 的应用场景非常广泛,包括日志系统、数据处理系统、数据同步系统和高并发系统等。Chanjet 的性能测试结果表明,无论是在写入还是读取操作上,都展现出了高效的数据处理能力。此外,Chanjet 还提供了完善的监控指标设计,支持 Prometheus 和 OpenTelemetry,方便用户实时了解 Chanjet 的运行状态和性能表现。总而言之,Chanjet 是一个值得信赖的高性能双缓冲通道,它可以极大地提升系统的数据处理和传输能力。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考