移除不必要的 user_host 字段及相关索引;优化通道缓存方式,直接缓存通道授权信息

This commit is contained in:
2025-05-12 17:04:20 +08:00
parent 2c37dcc2be
commit 2be1a18e91
5 changed files with 43 additions and 68 deletions

View File

@@ -4,9 +4,6 @@
- 页面 提取记录 - 页面 提取记录
- 页面 使用记录 - 页面 使用记录
- 保存 session 到数据库
- 移除 PayloadType使用 Grant_Type
### 长效业务 ### 长效业务
- 支付回调处理 - 支付回调处理

View File

@@ -628,7 +628,6 @@ create table channel (
node_host varchar(255), node_host varchar(255),
protocol int, protocol int,
auth_ip bool not null default false, auth_ip bool not null default false,
user_host varchar(255),
auth_pass bool not null default false, auth_pass bool not null default false,
username varchar(255), username varchar(255),
password varchar(255), password varchar(255),
@@ -644,7 +643,6 @@ create index channel_proxy_host_index on channel (proxy_host);
create index channel_proxy_port_index on channel (proxy_port); create index channel_proxy_port_index on channel (proxy_port);
create index channel_node_host_index on channel (node_host); create index channel_node_host_index on channel (node_host);
create index channel_auth_ip_index on channel (auth_ip); create index channel_auth_ip_index on channel (auth_ip);
create index channel_user_host_index on channel (user_host);
create index channel_auth_pass_index on channel (auth_pass); create index channel_auth_pass_index on channel (auth_pass);
create index channel_username_index on channel (username); create index channel_username_index on channel (username);
create index channel_expiration_index on channel (expiration); create index channel_expiration_index on channel (expiration);
@@ -661,7 +659,6 @@ comment on column channel.proxy_port is '转发端口';
comment on column channel.node_host is '节点地址'; comment on column channel.node_host is '节点地址';
comment on column channel.protocol is '协议类型1-http2-https3-socks5'; comment on column channel.protocol is '协议类型1-http2-https3-socks5';
comment on column channel.auth_ip is 'IP认证'; comment on column channel.auth_ip is 'IP认证';
comment on column channel.user_host is '用户地址';
comment on column channel.auth_pass is '密码认证'; comment on column channel.auth_pass is '密码认证';
comment on column channel.username is '用户名'; comment on column channel.username is '用户名';
comment on column channel.password is '密码'; comment on column channel.password is '密码';

View File

@@ -23,7 +23,6 @@ type Channel struct {
NodeHost string `gorm:"column:node_host;comment:节点地址" json:"node_host"` // 节点地址 NodeHost string `gorm:"column:node_host;comment:节点地址" json:"node_host"` // 节点地址
Protocol int32 `gorm:"column:protocol;comment:协议类型1-http2-https3-socks5" json:"protocol"` // 协议类型1-http2-https3-socks5 Protocol int32 `gorm:"column:protocol;comment:协议类型1-http2-https3-socks5" json:"protocol"` // 协议类型1-http2-https3-socks5
AuthIP bool `gorm:"column:auth_ip;not null;comment:IP认证" json:"auth_ip"` // IP认证 AuthIP bool `gorm:"column:auth_ip;not null;comment:IP认证" json:"auth_ip"` // IP认证
UserHost string `gorm:"column:user_host;comment:用户地址" json:"user_host"` // 用户地址
AuthPass bool `gorm:"column:auth_pass;not null;comment:密码认证" json:"auth_pass"` // 密码认证 AuthPass bool `gorm:"column:auth_pass;not null;comment:密码认证" json:"auth_pass"` // 密码认证
Username string `gorm:"column:username;comment:用户名" json:"username"` // 用户名 Username string `gorm:"column:username;comment:用户名" json:"username"` // 用户名
Password string `gorm:"column:password;comment:密码" json:"password"` // 密码 Password string `gorm:"column:password;comment:密码" json:"password"` // 密码

View File

@@ -36,7 +36,6 @@ func newChannel(db *gorm.DB, opts ...gen.DOOption) channel {
_channel.NodeHost = field.NewString(tableName, "node_host") _channel.NodeHost = field.NewString(tableName, "node_host")
_channel.Protocol = field.NewInt32(tableName, "protocol") _channel.Protocol = field.NewInt32(tableName, "protocol")
_channel.AuthIP = field.NewBool(tableName, "auth_ip") _channel.AuthIP = field.NewBool(tableName, "auth_ip")
_channel.UserHost = field.NewString(tableName, "user_host")
_channel.AuthPass = field.NewBool(tableName, "auth_pass") _channel.AuthPass = field.NewBool(tableName, "auth_pass")
_channel.Username = field.NewString(tableName, "username") _channel.Username = field.NewString(tableName, "username")
_channel.Password = field.NewString(tableName, "password") _channel.Password = field.NewString(tableName, "password")
@@ -63,7 +62,6 @@ type channel struct {
NodeHost field.String // 节点地址 NodeHost field.String // 节点地址
Protocol field.Int32 // 协议类型1-http2-https3-socks5 Protocol field.Int32 // 协议类型1-http2-https3-socks5
AuthIP field.Bool // IP认证 AuthIP field.Bool // IP认证
UserHost field.String // 用户地址
AuthPass field.Bool // 密码认证 AuthPass field.Bool // 密码认证
Username field.String // 用户名 Username field.String // 用户名
Password field.String // 密码 Password field.String // 密码
@@ -96,7 +94,6 @@ func (c *channel) updateTableName(table string) *channel {
c.NodeHost = field.NewString(table, "node_host") c.NodeHost = field.NewString(table, "node_host")
c.Protocol = field.NewInt32(table, "protocol") c.Protocol = field.NewInt32(table, "protocol")
c.AuthIP = field.NewBool(table, "auth_ip") c.AuthIP = field.NewBool(table, "auth_ip")
c.UserHost = field.NewString(table, "user_host")
c.AuthPass = field.NewBool(table, "auth_pass") c.AuthPass = field.NewBool(table, "auth_pass")
c.Username = field.NewString(table, "username") c.Username = field.NewString(table, "username")
c.Password = field.NewString(table, "password") c.Password = field.NewString(table, "password")
@@ -120,7 +117,7 @@ func (c *channel) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
} }
func (c *channel) fillFieldMap() { func (c *channel) fillFieldMap() {
c.fieldMap = make(map[string]field.Expr, 17) c.fieldMap = make(map[string]field.Expr, 16)
c.fieldMap["id"] = c.ID c.fieldMap["id"] = c.ID
c.fieldMap["user_id"] = c.UserID c.fieldMap["user_id"] = c.UserID
c.fieldMap["proxy_id"] = c.ProxyID c.fieldMap["proxy_id"] = c.ProxyID
@@ -130,7 +127,6 @@ func (c *channel) fillFieldMap() {
c.fieldMap["node_host"] = c.NodeHost c.fieldMap["node_host"] = c.NodeHost
c.fieldMap["protocol"] = c.Protocol c.fieldMap["protocol"] = c.Protocol
c.fieldMap["auth_ip"] = c.AuthIP c.fieldMap["auth_ip"] = c.AuthIP
c.fieldMap["user_host"] = c.UserHost
c.fieldMap["auth_pass"] = c.AuthPass c.fieldMap["auth_pass"] = c.AuthPass
c.fieldMap["username"] = c.Username c.fieldMap["username"] = c.Username
c.fieldMap["password"] = c.Password c.fieldMap["password"] = c.Password

View File

@@ -3,7 +3,6 @@ package services
import ( import (
"context" "context"
"database/sql" "database/sql"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"log/slog" "log/slog"
@@ -97,12 +96,6 @@ func (s *channelService) RemoveChannels(ctx context.Context, authCtx *auth.Conte
return ChannelServiceErr("删除通道失败") return ChannelServiceErr("删除通道失败")
} }
// 删除缓存
err = deleteCache(ctx, channels)
if err != nil {
return err
}
// 禁用代理端口并下线用过的节点 // 禁用代理端口并下线用过的节点
if env.DebugExternalChange { if env.DebugExternalChange {
var step = time.Now() var step = time.Now()
@@ -276,7 +269,7 @@ func (s *channelService) CreateChannel(
} }
// 缓存通道数据 // 缓存通道数据
err = cacheChannels(ctx, rid, newChannels) err = cacheChannels(ctx, rid, newChannels, whitelist)
if err != nil { if err != nil {
return err return err
} }
@@ -612,8 +605,28 @@ func saveChannels(channels []*m.Channel) (err error) {
return nil return nil
} }
func cacheChannels(ctx context.Context, rid string, channels []*m.Channel) (err error) { func cacheChannels(ctx context.Context, rid string, channels []*m.Channel, whitelists *[]string) (err error) {
err = cache(ctx, channels) if len(channels) == 0 {
return nil
}
pipe := g.Redis.TxPipeline()
zList := make([]redis.Z, 0, len(channels))
for _, channel := range channels {
expiration := time.Time(channel.Expiration)
keys := chAuthItems(channel, whitelists)
for _, key := range keys {
pipe.Set(ctx, key, true, time.Since(expiration))
}
zList = append(zList, redis.Z{
Score: float64(expiration.Unix()),
Member: channel.ID,
})
}
pipe.ZAdd(ctx, "tasks:channel", zList...)
_, err = pipe.Exec(ctx)
if err != nil { if err != nil {
return err return err
} }
@@ -642,62 +655,35 @@ func genPassPair() (string, string) {
return string(username), string(password) return string(username), string(password)
} }
func chKey(channel *m.Channel) string { func chAuthItems(channel *m.Channel, whitelists *[]string) []string {
return fmt.Sprintf("channel:%d", channel.ID) var count = 1
} var ips = make([]string, 0)
if channel.AuthIP && whitelists != nil {
func cache(ctx context.Context, channels []*m.Channel) error { count = len(*whitelists)
if len(channels) == 0 { ips = *whitelists
return nil
} }
pipe := g.Redis.TxPipeline() var proxy = channel.ProxyHost + ":" + strconv.Itoa(int(channel.ProxyPort))
var sb = strings.Builder{}
zList := make([]redis.Z, 0, len(channels)) var items = make([]string, count)
for _, channel := range channels { for i := range items {
marshal, err := json.Marshal(channel) // 权限 key 格式:<proxy_host>:<proxy_port>:<user_ip>?:<username>?:<password>?
if err != nil { sb.WriteString(proxy)
return err if channel.AuthIP {
sb.WriteString(":" + ips[i])
}
if channel.AuthPass {
sb.WriteString(":" + channel.Username + ":" + channel.Password)
} }
expiration := time.Time(channel.Expiration)
pipe.Set(ctx, chKey(channel), string(marshal), time.Until(expiration))
zList = append(zList, redis.Z{
Score: float64(expiration.Unix()),
Member: channel.ID,
})
}
pipe.ZAdd(ctx, "tasks:channel", zList...)
_, err := pipe.Exec(ctx)
if err != nil {
return err
} }
return nil return items
}
func deleteCache(ctx context.Context, channels []*m.Channel) error {
if len(channels) == 0 {
return nil
}
keys := make([]string, len(channels))
for i := range channels {
keys[i] = chKey(channels[i])
}
_, err := g.Redis.Del(ctx, keys...).Result()
if err != nil {
return err
}
return nil
} }
type ChannelAuthType int type ChannelAuthType int
const ( const (
ChannelAuthTypeAll ChannelAuthType = iota ChannelAuthTypeIp ChannelAuthType = iota + 1
ChannelAuthTypeIp
ChannelAuthTypePass ChannelAuthTypePass
) )