mirror of
https://github.com/alibaba/higress.git
synced 2026-05-23 20:27:29 +08:00
[feat] load balancing across different clusters and endpoints based on metrics (#3063)
This commit is contained in:
175
plugins/wasm-go/extensions/ai-load-balancer/utils/queue.go
Normal file
175
plugins/wasm-go/extensions/ai-load-balancer/utils/queue.go
Normal file
@@ -0,0 +1,175 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// FixedQueue 实现了一个固定容量的环形缓冲区队列
|
||||
// 当队列满时,新元素会覆盖最旧的元素
|
||||
type FixedQueue[T any] struct {
|
||||
data []T
|
||||
head int
|
||||
tail int
|
||||
size int
|
||||
cap int
|
||||
}
|
||||
|
||||
// NewFixed 创建一个指定容量的固定队列
|
||||
func NewFixedQueue[T any](capacity int) *FixedQueue[T] {
|
||||
if capacity <= 0 {
|
||||
capacity = 16
|
||||
}
|
||||
return &FixedQueue[T]{
|
||||
data: make([]T, capacity),
|
||||
head: 0,
|
||||
tail: 0,
|
||||
size: 0,
|
||||
cap: capacity,
|
||||
}
|
||||
}
|
||||
|
||||
// Enqueue 入队操作
|
||||
// 如果队列已满,会覆盖最旧的元素
|
||||
func (q *FixedQueue[T]) Enqueue(item T) {
|
||||
if q.size < q.cap {
|
||||
// 队列未满,正常插入
|
||||
q.data[q.tail] = item
|
||||
q.tail = (q.tail + 1) % q.cap
|
||||
q.size++
|
||||
} else {
|
||||
// 队列已满,覆盖最旧元素
|
||||
q.data[q.tail] = item
|
||||
q.head = (q.head + 1) % q.cap // 移动head,丢弃最旧元素
|
||||
q.tail = (q.tail + 1) % q.cap // tail正常移动
|
||||
// size保持不变(仍然是cap)
|
||||
}
|
||||
}
|
||||
|
||||
// Dequeue 出队操作
|
||||
func (q *FixedQueue[T]) Dequeue() (T, error) {
|
||||
var zero T
|
||||
if q.size == 0 {
|
||||
return zero, errors.New("queue is empty")
|
||||
}
|
||||
|
||||
item := q.data[q.head]
|
||||
// 清除引用,避免内存泄漏
|
||||
var zeroVal T
|
||||
q.data[q.head] = zeroVal
|
||||
|
||||
q.head = (q.head + 1) % q.cap
|
||||
q.size--
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
// Peek 查看队头元素但不移除
|
||||
func (q *FixedQueue[T]) Peek() (T, error) {
|
||||
var zero T
|
||||
if q.size == 0 {
|
||||
return zero, errors.New("queue is empty")
|
||||
}
|
||||
return q.data[q.head], nil
|
||||
}
|
||||
|
||||
// Size 返回队列中元素的数量
|
||||
func (q *FixedQueue[T]) Size() int {
|
||||
return q.size
|
||||
}
|
||||
|
||||
// Capacity 返回队列的固定容量
|
||||
func (q *FixedQueue[T]) Capacity() int {
|
||||
return q.cap
|
||||
}
|
||||
|
||||
// IsEmpty 判断队列是否为空
|
||||
func (q *FixedQueue[T]) IsEmpty() bool {
|
||||
return q.size == 0
|
||||
}
|
||||
|
||||
// IsFull 判断队列是否已满
|
||||
func (q *FixedQueue[T]) IsFull() bool {
|
||||
return q.size == q.cap
|
||||
}
|
||||
|
||||
// OverwriteCount 返回被覆盖的元素数量
|
||||
// 注意:这个实现中我们不直接跟踪覆盖次数,
|
||||
// 但可以通过其他方式计算(如果需要的话)
|
||||
func (q *FixedQueue[T]) OverwriteCount() int {
|
||||
// 如果需要跟踪覆盖次数,可以添加一个字段
|
||||
// 目前这个实现不提供此功能
|
||||
return 0
|
||||
}
|
||||
|
||||
// Clear 清空队列
|
||||
func (q *FixedQueue[T]) Clear() {
|
||||
// 清除所有引用
|
||||
for i := 0; i < q.size; i++ {
|
||||
idx := (q.head + i) % q.cap
|
||||
var zero T
|
||||
q.data[idx] = zero
|
||||
}
|
||||
q.head = 0
|
||||
q.tail = 0
|
||||
q.size = 0
|
||||
}
|
||||
|
||||
// ToSlice 返回队列元素的切片副本(按队列顺序,从最旧到最新)
|
||||
func (q *FixedQueue[T]) ToSlice() []T {
|
||||
if q.size == 0 {
|
||||
return []T{}
|
||||
}
|
||||
|
||||
result := make([]T, q.size)
|
||||
if q.head <= q.tail || q.size == q.cap {
|
||||
if q.head < q.tail {
|
||||
// 数据连续且未满
|
||||
copy(result, q.data[q.head:q.tail])
|
||||
} else {
|
||||
// 数据连续但已满(head == tail)
|
||||
// 或者数据跨越边界
|
||||
if q.head == q.tail && q.size == q.cap {
|
||||
// 已满且head == tail的情况
|
||||
copy(result, q.data[q.head:])
|
||||
if len(result) > q.cap-q.head {
|
||||
copy(result[q.cap-q.head:], q.data[:q.tail])
|
||||
}
|
||||
} else {
|
||||
// 跨越边界
|
||||
copy(result, q.data[q.head:])
|
||||
copy(result[q.cap-q.head:], q.data[:q.tail])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 跨越边界的情况
|
||||
copy(result, q.data[q.head:])
|
||||
copy(result[q.cap-q.head:], q.data[:q.tail])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Oldest 返回最旧的元素(队头)
|
||||
func (q *FixedQueue[T]) Oldest() (T, error) {
|
||||
return q.Peek()
|
||||
}
|
||||
|
||||
// Newest 返回最新的元素(队尾的前一个元素)
|
||||
func (q *FixedQueue[T]) Newest() (T, error) {
|
||||
var zero T
|
||||
if q.size == 0 {
|
||||
return zero, errors.New("queue is empty")
|
||||
}
|
||||
|
||||
// tail指向下一个插入位置,所以最新元素在 (tail - 1 + cap) % cap
|
||||
newestIndex := (q.tail - 1 + q.cap) % q.cap
|
||||
return q.data[newestIndex], nil
|
||||
}
|
||||
|
||||
// ForEach 对队列中的每个元素执行回调函数
|
||||
func (q *FixedQueue[T]) ForEach(fn func(index int, item T)) {
|
||||
for i := 0; i < q.size; i++ {
|
||||
idx := (q.head + i) % q.cap
|
||||
fn(i, q.data[idx])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user