Files
goproxy/examples/plugin/plugins/stats/stats_plugin.go
DarkiT b6bf2c5699 增强插件系统:引入插件辅助器和自动方法发现功能
- 在插件系统中添加 PluginHelper 结构体,简化插件方法的自动发现和注册。
- 更新 BasePluginImpl 以支持通过辅助器执行插件方法,增强 Execute 方法的灵活性。
- 统计插件和示例程序中实现新的方法调用方式,展示如何使用自动注册的方法。
- 通过结构体参数传递,简化插件调用过程,提升用户体验。

此更新提升了插件系统的可扩展性和易用性,便于开发者动态管理和执行插件功能。
2025-03-14 11:37:42 +08:00

252 lines
6.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"context"
"fmt"
"sync"
"time"
"github.com/darkit/goproxy/examples/plugin"
)
// StatsPlugin 统计插件
// 用于收集和记录系统运行时统计数据
type StatsPlugin struct {
*plugin.BasePluginImpl // 改为使用BasePluginImpl
stats map[string]int64
startTime time.Time
mu sync.RWMutex
tickerStop chan bool
ticker *time.Ticker
config map[string]interface{}
}
// StatsParams 统计请求参数结构体
// 允许通过结构体传递参数,简化调用
type StatsParams struct {
Name string `json:"name"` // 统计项名称
Value int64 `json:"value"` // 统计值
BytesReceived int64 `json:"bytesReceived"` // 接收字节数
BytesSent int64 `json:"bytesSent"` // 发送字节数
IsError bool `json:"isError"` // 是否为错误请求
}
// Plugin 导出的插件变量
var Plugin = &StatsPlugin{
// 使用默认构造函数,不指定插件类型,将默认为通用插件
BasePluginImpl: plugin.NewPluginWithDefaultType(
"StatsPlugin",
"1.0.0",
"系统运行时统计插件",
"开发者",
),
stats: make(map[string]int64),
tickerStop: make(chan bool),
}
// 为展示如何指定类型,我们也可以显式设置插件类型
// var Plugin = &StatsPlugin{
// BasePlugin: plugin.NewBasePlugin(
// "StatsPlugin",
// "1.0.0",
// "系统运行时统计插件",
// "开发者",
// plugin.PluginTypeUtils, // 明确指定为工具类插件
// ),
// stats: make(map[string]int64),
// tickerStop: make(chan bool),
// }
// Init 初始化插件
func (p *StatsPlugin) Init(ctx context.Context, config map[string]interface{}) error {
p.config = config
// 初始化统计指标
p.mu.Lock()
p.stats["requests"] = 0
p.stats["errors"] = 0
p.stats["bytes_sent"] = 0
p.stats["bytes_received"] = 0
p.mu.Unlock()
fmt.Println("统计插件初始化完成")
return nil
}
// Start 启动插件
func (p *StatsPlugin) Start(ctx context.Context) error {
p.startTime = time.Now()
// 启动定时统计任务
interval := 60 * time.Second // 默认60秒
// 从配置中获取统计间隔
if intervalSec, ok := p.config["interval_seconds"].(float64); ok {
interval = time.Duration(intervalSec) * time.Second
}
p.ticker = time.NewTicker(interval)
go func() {
for {
select {
case <-p.ticker.C:
p.logStats()
case <-p.tickerStop:
p.ticker.Stop()
return
case <-ctx.Done():
p.ticker.Stop()
return
}
}
}()
fmt.Println("统计插件已启动")
return nil
}
// Stop 停止插件
func (p *StatsPlugin) Stop(ctx context.Context) error {
if p.ticker != nil {
p.tickerStop <- true
}
// 输出最终统计信息
p.logStats()
fmt.Println("统计插件已停止")
return nil
}
// 以下方法将被自动注册为可通过Execute调用的操作
// IncrementStat 增加统计值
// 会被自动注册为"incrementstat"操作
func (p *StatsPlugin) IncrementStat(name string, value int64) error {
p.mu.Lock()
defer p.mu.Unlock()
if _, exists := p.stats[name]; exists {
p.stats[name] += value
} else {
p.stats[name] = value
}
return nil
}
// GetStat 获取统计值
// 会被自动注册为"getstat"操作
func (p *StatsPlugin) GetStat(name string) (int64, error) {
p.mu.RLock()
defer p.mu.RUnlock()
if value, exists := p.stats[name]; exists {
return value, nil
}
return 0, fmt.Errorf("统计项 %s 不存在", name)
}
// RecordRequest 记录请求
// 会被自动注册为"recordrequest"操作
func (p *StatsPlugin) RecordRequest(ctx context.Context, params StatsParams) error {
p.IncrementStat("requests", 1)
p.IncrementStat("bytes_received", params.BytesReceived)
p.IncrementStat("bytes_sent", params.BytesSent)
if params.IsError {
p.IncrementStat("errors", 1)
}
return nil
}
// ResetStats 重置统计数据
// 会被自动注册为"resetstats"操作
func (p *StatsPlugin) ResetStats() error {
p.mu.Lock()
defer p.mu.Unlock()
p.stats = make(map[string]int64)
p.stats["requests"] = 0
p.stats["errors"] = 0
p.stats["bytes_sent"] = 0
p.stats["bytes_received"] = 0
p.startTime = time.Now()
return nil
}
// GenerateStatsReport 生成统计报告
// 会被自动注册为"generatestatsreport"操作
func (p *StatsPlugin) GenerateStatsReport() (map[string]interface{}, error) {
p.mu.RLock()
defer p.mu.RUnlock()
uptime := time.Since(p.startTime).Seconds()
report := map[string]interface{}{
"uptime_seconds": uptime,
"stats": p.stats,
}
if uptime > 0 && p.stats["requests"] > 0 {
report["requests_per_second"] = float64(p.stats["requests"]) / uptime
report["error_rate"] = float64(p.stats["errors"]) * 100 / float64(p.stats["requests"])
}
return report, nil
}
// GetAllStats 获取所有统计数据
// 会被自动注册为"getallstats"操作
func (p *StatsPlugin) GetAllStats() (map[string]int64, error) {
p.mu.RLock()
defer p.mu.RUnlock()
// 创建一个副本
statsCopy := make(map[string]int64, len(p.stats))
for k, v := range p.stats {
statsCopy[k] = v
}
// 添加运行时间
statsCopy["uptime_seconds"] = int64(time.Since(p.startTime).Seconds())
return statsCopy, nil
}
// logStats 记录当前统计信息
// 不会被注册为操作,因为它是内部方法
func (p *StatsPlugin) logStats() {
p.mu.RLock()
defer p.mu.RUnlock()
uptime := time.Since(p.startTime).Seconds()
fmt.Printf("===== 系统统计信息 =====\n")
fmt.Printf("运行时间: %.2f 秒\n", uptime)
fmt.Printf("总请求数: %d\n", p.stats["requests"])
fmt.Printf("错误数: %d\n", p.stats["errors"])
fmt.Printf("发送字节: %d\n", p.stats["bytes_sent"])
fmt.Printf("接收字节: %d\n", p.stats["bytes_received"])
if uptime > 0 && p.stats["requests"] > 0 {
fmt.Printf("平均请求/秒: %.2f\n", float64(p.stats["requests"])/uptime)
fmt.Printf("错误率: %.2f%%\n", float64(p.stats["errors"])*100/float64(p.stats["requests"]))
}
fmt.Printf("=======================\n")
}
// GetAvailableOperations 获取可用操作列表
// 这是一个帮助方法列出所有可通过Execute调用的操作
func (p *StatsPlugin) GetAvailableOperations() []string {
return p.GetAvailableActions()
}
// main 函数是必须的,但不会被调用
func main() {
// 不会被执行,仅用于编译插件
}