首先要知道v2ray(下面简写“v2”)的配置文件格式为json,形式如下:

{
  "log": {},
  "api": {},
  "dns": {},
  "stats": {},
  "routing": {},
  "policy": {},
  "reverse": {},
  "inbounds": [],
  "outbounds": [],
  "transport": {}
}

v2配置很多但总体就上面10类,把握这个有助于你写配置。简单解释下这几个含义:

  • log:日志配置
  • api:配置远程控制api
  • dns:配置内置的dns服务器
  • stats:配置数据统计
  • routing:配置路由
  • policy:配置本地策略
  • reverse:配置反向代理
  • inbounds:配置入站连接
  • outbounds:配置出站连接
  • transport:配置如何与其它服务器建立和使用网络连接

这里我们要说的是api,实现用户流量的统计和用户的动态增删。

配置文件

我们先要配置v2文件。要实现流量统计功能,配置内需要确保存在以下配置:

  • "stats":{} 对象的存在 "policy" 中的统计开关为 true。
  • 全局统计的开关在 "system"下,用户统计的开关在 "levels" 下
  • 全局统计在相应的入站出站要有 tag
  • 用户统计在 "clients" 里面要有 email

要使用 api 查询流量,配置内需要确保存在以下配置:

  • "api" 配置对象里面有 StatsService
  • 专用的 dokodemo-door协议的入口,tag 为
  • api routing 里面有 inboundTag:api outboundTag:api 的规则

配置示例

{
    "stats": {},
    "api": {
        "tag": "api",
        "services": [
            "HandlerService",
            "StatsService"
        ]
    },
    "policy": {
        "levels": {
            "0": {
                "statsUserUplink": true, //开启用户流量上传统计
                "statsUserDownlink": true //开启用户流量下载统计
            }
        },
        "system": {
            "statsInboundUplink": true, //开启入站方向的流量上传
            "statsInboundDownlink": true, //开启入站方向的流量下载
            "statsOutboundUplink": true, //开启出站方向的流量上传
            "statsOutboundDownlink": true //开启出站方向的流量下载
        }
    },
    "inbounds": [
        {
            "tag": "tcp",
            "port": 3307,
            "protocol": "vmess",
            "settings": {
                "clients": [
                    {
                        "email": "auser",  //定义一个auser用户
                        "id": "e731f153-4f31-49d3-9e8f-ff8f396135ef",
                        "level": 0,
                        "alterId": 64
                    },
                    {
                        "email": "buser",  //定义一个buser用户
                        "id": "e731f153-4f31-49d3-9e8f-ff8f396135ee",
                        "level": 0,
                        "alterId": 64
                    }
                ]
            }
        },
        {
            "listen": "127.0.0.1",
            "port": 10085,
            "protocol": "dokodemo-door",
            "settings": {
                "address": "127.0.0.1"
            },
            "tag": "api"
        }
    ],
    "outbounds": [
        {
            "tag": "direct",
            "protocol": "freedom",
            "settings": {}
        }
    ],
    "routing": {
        "settings": {
            "rules": [
                {
                    "inboundTag": [
                        "api"
                    ],
                    "outboundTag": "api",
                    "type": "field"
                }
            ]
        },
        "strategy": "rules"
    }
}

v2ctl工具

v2会附带安装v2ctl工具,v2ctl支持多个参数其中一个参数是api,借此我们可以实现流量统计。server参数端口为dokodemo-door的端口。

v2ctl api --server=127.0.0.1:10085 StatsService.QueryStats 'pattern: "" reset: false'
    v2ctl api --server=127.0.0.1:10085 StatsService.GetStats 'name: "inbound>>>statin>>>traffic>>>downlink" reset: false'

虽然有v2ctl这个现成工具,但我们还是需要自己写脚本,因为v2ctl只能流量统计,无法实现用户的动态增删。
v2的api其实是打开了一个 grpc 协议的查询接口,我们只要借助任何一个支持grpc协议的语言就能调用。支持grpc协议的有很多,只要安装中间件,php都可跑。我们这里选择官方支持内存占用小的go语言。

go语言实现

先安装go语言,把go放入系统环境。然后下载v2和grpc的包。

go get -insecure v2ray.com/core
go get google.golang.org/grpc

开始写脚本,下面的代码参考了文章:调用 V2Ray 提供的 API 接口进行用户增删及流量统计查询操作。这里为了适合我的v2和go运行做了一点修改,增加了部分代码实现用户流量查询。

package main
import (
        "context"
        "fmt"
        "log"
        "google.golang.org/grpc"
        "v2ray.com/core/app/proxyman/command"
        statsService "v2ray.com/core/app/stats/command"
        "v2ray.com/core/common/protocol"
        "v2ray.com/core/common/serial"
        "v2ray.com/core/proxy/vmess"
)
const (
        API_ADDRESS = "127.0.0.1"
        API_PORT    = 10085
        INBOUND_TAG = "tcp"
        LEVEL   = 0
        EMAIL   = "123@gmail.com"
        UUID    = "2601070b-ab53-4352-a290-1d44414581ee"
        ALTERID = 32
)
func addUser(c command.HandlerServiceClient) {
        resp, err := c.AlterInbound(context.Background(), &command.AlterInboundRequest{
                Tag: INBOUND_TAG,
                Operation: serial.ToTypedMessage(&command.AddUserOperation{
                        User: &protocol.User{
                                Level: LEVEL,
                                Email: EMAIL,
                                Account: serial.ToTypedMessage(&vmess.Account{
                                        Id:               UUID,
                                        AlterId:          ALTERID,
                                        SecuritySettings: &protocol.SecurityConfig{Type: protocol.SecurityType_AUTO},
                                }),
                        },
                }),
        })
        if err != nil {
                log.Printf("failed to call grpc command: %v", err)
        } else {
                log.Printf("ok: %v", resp)
        }
}
func removeUser(c command.HandlerServiceClient) {
        resp, err := c.AlterInbound(context.Background(), &command.AlterInboundRequest{
                Tag: INBOUND_TAG,
                Operation: serial.ToTypedMessage(&command.RemoveUserOperation{
                        Email: EMAIL,
                }),
        })
        if err != nil {
                log.Printf("failed to call grpc command: %v", err)
        } else {
                log.Printf("ok: %v", resp)
        }
}
func queryUserTraffic(c statsService.StatsServiceClient) {
        resp, err := c.QueryStats(context.Background(), &statsService.QueryStatsRequest{
                Pattern: "user>>>123@gmail.com>>>traffic>>>uplink", // 筛选用户表达式
                Reset_:  false,  // 查询完成后是否重置流量
        })
        if err != nil {
                log.Printf("failed to call grpc command: %v", err)
        }
        // 获取返回值中的流量信息
        stat := resp.GetStat()
        // 返回的是一个数组,对其进行遍历输出
        for _, e := range stat {
                fmt.Println(e)
        }
}

func main() {
        cmdConn, err := grpc.Dial(fmt.Sprintf("%s:%d", API_ADDRESS, API_PORT), grpc.WithInsecure())
        if err != nil {
                panic(err)
        }
        //hsClient := command.NewHandlerServiceClient(cmdConn)
        hsClient_traffic := statsService.NewStatsServiceClient(cmdConn)
        //addUser(hsClient)
        //removeUser(hsClient)
        queryUserTraffic(hsClient_traffic)
}

其他

动手能力强的就可以参考sspanel、v2board等前端面板提供的网页api实现v2后端接入。sspanel面板可以直接参考官方提供的python版ss后端代码写:sspanel配套ss后端
目前v2后端市面上均收费,价格在60-80刀/年,200-300刀/永久,小机场还是有经济压力的,而免费版不到100个用户又是不够用。上面代码实现了流量统计和用户增删,这两个功能其实可以当做一个简单的后端运行了。上报用户ip、审计、限速等功能如果能再完善,基本就算是个完整功能的后端了。

Last modification:January 7th, 2021 at 10:43 am
如果觉得我的文章对你有用,请随意赞赏