64a1a64c3f
节点 sing-box 编入 with_v2ray_api 后,每个 dp_uuid 有独立计数器
user>>>{dp_uuid}>>>traffic>>>uplink|downlink。agent 用 QueryStats(reset=true)
取窗口 delta,按用户精确上报(替代旧 clash 节点总量分摊:单用户准、多用户近似)。
- render.go: experimental.v2ray_api(loopback :19091)+ stats.users 列全部 dp_uuid
- v2rayapi/: vendor sing-box stats.pb.go(消息)+ 手写 client(用 v2ray 规范
ServiceName 路径,绕开生成代码的 experimental.v2rayapi.* 误名)
- usage_v2ray.go: V2RayUsageSource,uplink/downlink 天然用户视角(无需 swap)
- agent.UseV2RayUsage() 取代 UseClashUsage();clash_api 保留作本地调试
节点需部署带 with_v2ray_api 的 sing-box(已编好 linux/amd64 二进制)。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
37 lines
1.3 KiB
Go
37 lines
1.3 KiB
Go
package v2rayapi
|
|
|
|
// 极简 StatsService 客户端。消息定义(stats.pb.go)直接从 sing-box
|
|
// experimental/v2rayapi 原样 vendor(wire 保证一致);此处只手写 QueryStats 调用。
|
|
//
|
|
// 注意:sing-box 服务端把 gRPC ServiceName 覆盖成了 v2ray 规范名
|
|
// "v2ray.core.app.stats.command.StatsService"(见 sing-box stats.go),
|
|
// 而生成代码里的常量是 "experimental.v2rayapi.StatsService"。直接抄生成的
|
|
// client 会调错方法路径,故这里硬编码服务端真正注册的规范全名。
|
|
|
|
import (
|
|
"context"
|
|
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
const queryStatsFullMethod = "/v2ray.core.app.stats.command.StatsService/QueryStats"
|
|
|
|
// StatsClient 是 v2ray StatsService 的最小客户端(只用 QueryStats)。
|
|
type StatsClient struct {
|
|
cc grpc.ClientConnInterface
|
|
}
|
|
|
|
// NewStatsClient 包一个已建立的 gRPC 连接。
|
|
func NewStatsClient(cc grpc.ClientConnInterface) *StatsClient {
|
|
return &StatsClient{cc: cc}
|
|
}
|
|
|
|
// QueryStats 按 pattern 取计数器;reset=true 时取值并清零(天然给出窗口 delta)。
|
|
func (c *StatsClient) QueryStats(ctx context.Context, in *QueryStatsRequest, opts ...grpc.CallOption) (*QueryStatsResponse, error) {
|
|
out := new(QueryStatsResponse)
|
|
if err := c.cc.Invoke(ctx, queryStatsFullMethod, in, out, opts...); err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|