Update InfoGenie
This commit is contained in:
@@ -1,22 +1,22 @@
|
||||
APP_ENV=development
|
||||
APP_PORT=5002
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_NAME=infogenie-test
|
||||
DB_USER=infogenie-test
|
||||
DB_PASSWORD=
|
||||
JWT_SECRET=
|
||||
JWT_EXPIRE_DAYS=7
|
||||
MAIL_HOST=
|
||||
MAIL_PORT=465
|
||||
MAIL_USERNAME=
|
||||
MAIL_PASSWORD=
|
||||
AUTH_CENTER_API_URL=https://auth.api.shumengya.top
|
||||
AUTH_CENTER_ADMIN_TOKEN=
|
||||
INFOGENIE_SITE_ADMIN_TOKEN=
|
||||
# REDIS_ENABLED=false
|
||||
# REDIS_ADDR=127.0.0.1:6379
|
||||
# REDIS_PASSWORD=
|
||||
# REDIS_DB=10
|
||||
# REDIS_KEY_PREFIX=infogenie:go:v1:
|
||||
# REDIS_SITE_TTL=60
|
||||
APP_ENV=development
|
||||
APP_PORT=5002
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_NAME=infogenie-test
|
||||
DB_USER=infogenie-test
|
||||
DB_PASSWORD=
|
||||
JWT_SECRET=
|
||||
JWT_EXPIRE_DAYS=7
|
||||
MAIL_HOST=
|
||||
MAIL_PORT=465
|
||||
MAIL_USERNAME=
|
||||
MAIL_PASSWORD=
|
||||
AUTH_CENTER_API_URL=https://auth.api.shumengya.top
|
||||
AUTH_CENTER_ADMIN_TOKEN=
|
||||
INFOGENIE_SITE_ADMIN_TOKEN=
|
||||
# REDIS_ENABLED=false
|
||||
# REDIS_ADDR=127.0.0.1:6379
|
||||
# REDIS_PASSWORD=
|
||||
# REDIS_DB=10
|
||||
# REDIS_KEY_PREFIX=infogenie:go:v1:
|
||||
# REDIS_SITE_TTL=60
|
||||
|
||||
@@ -13,7 +13,6 @@ RUN apk --no-cache add ca-certificates tzdata
|
||||
ENV TZ=Asia/Shanghai
|
||||
WORKDIR /app
|
||||
COPY --from=builder /out/server .
|
||||
COPY ai_config.json ./
|
||||
EXPOSE 5002
|
||||
ENV APP_ENV=production
|
||||
ENV APP_PORT=5002
|
||||
|
||||
238
infogenie-backend-go/internal/cache/redis.go
vendored
238
infogenie-backend-go/internal/cache/redis.go
vendored
@@ -1,119 +1,119 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"infogenie-backend/config"
|
||||
)
|
||||
|
||||
// Key suffixes(完整 key = KeyPrefix + suffix)
|
||||
const (
|
||||
KeySite60sDisabled = "site:60s-disabled"
|
||||
KeySite60sSource = "site:60s-source"
|
||||
KeySiteAIModelDisabled = "site:ai-model-disabled"
|
||||
)
|
||||
|
||||
func KeySiteFeatureClicks(section string) string {
|
||||
return "site:feature-clicks:" + section
|
||||
}
|
||||
|
||||
var (
|
||||
rdb *redis.Client
|
||||
keyPrefix string
|
||||
siteCacheTTL time.Duration
|
||||
redisOn bool
|
||||
)
|
||||
|
||||
// Init 在 REDIS_ENABLED 时连接并 Ping;未启用时为空操作
|
||||
func Init(rc config.RedisConfig) error {
|
||||
if !rc.Enabled {
|
||||
redisOn = false
|
||||
rdb = nil
|
||||
return nil
|
||||
}
|
||||
redisOn = true
|
||||
keyPrefix = rc.KeyPrefix
|
||||
siteCacheTTL = rc.SiteTTL
|
||||
if siteCacheTTL <= 0 {
|
||||
siteCacheTTL = 60 * time.Second
|
||||
}
|
||||
rdb = redis.NewClient(&redis.Options{
|
||||
Addr: rc.Addr,
|
||||
Password: rc.Password,
|
||||
DB: rc.DB,
|
||||
})
|
||||
if err := rdb.Ping(context.Background()).Err(); err != nil {
|
||||
return fmt.Errorf("redis: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enabled 表示已初始化且客户端可用
|
||||
func Enabled() bool {
|
||||
return redisOn && rdb != nil
|
||||
}
|
||||
|
||||
func fullKey(suffix string) string {
|
||||
return keyPrefix + suffix
|
||||
}
|
||||
|
||||
// GetJSON 命中返回 true;未启用或未命中返回 false(err 仅表示 Redis/JSON 异常)
|
||||
func GetJSON(ctx context.Context, suffix string, dest interface{}) (bool, error) {
|
||||
if !Enabled() {
|
||||
return false, nil
|
||||
}
|
||||
val, err := rdb.Get(ctx, fullKey(suffix)).Bytes()
|
||||
if err == redis.Nil {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := json.Unmarshal(val, dest); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// SetJSON ttl<=0 时使用配置的 SiteTTL
|
||||
func SetJSON(ctx context.Context, suffix string, v interface{}, ttl time.Duration) error {
|
||||
if !Enabled() {
|
||||
return nil
|
||||
}
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ttl <= 0 {
|
||||
ttl = siteCacheTTL
|
||||
}
|
||||
return rdb.Set(ctx, fullKey(suffix), b, ttl).Err()
|
||||
}
|
||||
|
||||
// Delete 删除若干后缀对应的 key;失败仅打日志
|
||||
func Delete(ctx context.Context, suffixes ...string) {
|
||||
if !Enabled() || len(suffixes) == 0 {
|
||||
return
|
||||
}
|
||||
keys := make([]string, 0, len(suffixes))
|
||||
for _, s := range suffixes {
|
||||
keys = append(keys, fullKey(s))
|
||||
}
|
||||
if err := rdb.Del(ctx, keys...).Err(); err != nil {
|
||||
log.Printf("redis DEL 失败: %v keys=%v", err, keys)
|
||||
}
|
||||
}
|
||||
|
||||
// Ping 未启用时返回 nil
|
||||
func Ping(ctx context.Context) error {
|
||||
if !Enabled() {
|
||||
return nil
|
||||
}
|
||||
return rdb.Ping(ctx).Err()
|
||||
}
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"infogenie-backend/config"
|
||||
)
|
||||
|
||||
// Key suffixes(完整 key = KeyPrefix + suffix)
|
||||
const (
|
||||
KeySite60sDisabled = "site:60s-disabled"
|
||||
KeySite60sSource = "site:60s-source"
|
||||
KeySiteAIModelDisabled = "site:ai-model-disabled"
|
||||
)
|
||||
|
||||
func KeySiteFeatureClicks(section string) string {
|
||||
return "site:feature-clicks:" + section
|
||||
}
|
||||
|
||||
var (
|
||||
rdb *redis.Client
|
||||
keyPrefix string
|
||||
siteCacheTTL time.Duration
|
||||
redisOn bool
|
||||
)
|
||||
|
||||
// Init 在 REDIS_ENABLED 时连接并 Ping;未启用时为空操作
|
||||
func Init(rc config.RedisConfig) error {
|
||||
if !rc.Enabled {
|
||||
redisOn = false
|
||||
rdb = nil
|
||||
return nil
|
||||
}
|
||||
redisOn = true
|
||||
keyPrefix = rc.KeyPrefix
|
||||
siteCacheTTL = rc.SiteTTL
|
||||
if siteCacheTTL <= 0 {
|
||||
siteCacheTTL = 60 * time.Second
|
||||
}
|
||||
rdb = redis.NewClient(&redis.Options{
|
||||
Addr: rc.Addr,
|
||||
Password: rc.Password,
|
||||
DB: rc.DB,
|
||||
})
|
||||
if err := rdb.Ping(context.Background()).Err(); err != nil {
|
||||
return fmt.Errorf("redis: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enabled 表示已初始化且客户端可用
|
||||
func Enabled() bool {
|
||||
return redisOn && rdb != nil
|
||||
}
|
||||
|
||||
func fullKey(suffix string) string {
|
||||
return keyPrefix + suffix
|
||||
}
|
||||
|
||||
// GetJSON 命中返回 true;未启用或未命中返回 false(err 仅表示 Redis/JSON 异常)
|
||||
func GetJSON(ctx context.Context, suffix string, dest interface{}) (bool, error) {
|
||||
if !Enabled() {
|
||||
return false, nil
|
||||
}
|
||||
val, err := rdb.Get(ctx, fullKey(suffix)).Bytes()
|
||||
if err == redis.Nil {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := json.Unmarshal(val, dest); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// SetJSON ttl<=0 时使用配置的 SiteTTL
|
||||
func SetJSON(ctx context.Context, suffix string, v interface{}, ttl time.Duration) error {
|
||||
if !Enabled() {
|
||||
return nil
|
||||
}
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ttl <= 0 {
|
||||
ttl = siteCacheTTL
|
||||
}
|
||||
return rdb.Set(ctx, fullKey(suffix), b, ttl).Err()
|
||||
}
|
||||
|
||||
// Delete 删除若干后缀对应的 key;失败仅打日志
|
||||
func Delete(ctx context.Context, suffixes ...string) {
|
||||
if !Enabled() || len(suffixes) == 0 {
|
||||
return
|
||||
}
|
||||
keys := make([]string, 0, len(suffixes))
|
||||
for _, s := range suffixes {
|
||||
keys = append(keys, fullKey(s))
|
||||
}
|
||||
if err := rdb.Del(ctx, keys...).Err(); err != nil {
|
||||
log.Printf("redis DEL 失败: %v keys=%v", err, keys)
|
||||
}
|
||||
}
|
||||
|
||||
// Ping 未启用时返回 nil
|
||||
func Ping(ctx context.Context) error {
|
||||
if !Enabled() {
|
||||
return nil
|
||||
}
|
||||
return rdb.Ping(ctx).Err()
|
||||
}
|
||||
|
||||
@@ -1,84 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// 用法: go run . <host:port> [password]
|
||||
// 例: go run . 10.1.1.100:6379
|
||||
func main() {
|
||||
addr := "10.1.1.100:6379"
|
||||
pass := os.Getenv("REDIS_PASSWORD")
|
||||
if len(os.Args) >= 2 {
|
||||
addr = os.Args[1]
|
||||
}
|
||||
if len(os.Args) >= 3 {
|
||||
pass = os.Args[2]
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
|
||||
defer cancel()
|
||||
|
||||
r := redis.NewClient(&redis.Options{Addr: addr, Password: pass})
|
||||
defer r.Close()
|
||||
if err := r.Ping(ctx).Err(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "连接失败 %s: %v\n", addr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
s, err := r.Info(ctx, "keyspace").Result()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "INFO keyspace: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
used := make(map[int]int64)
|
||||
for _, line := range strings.Split(s, "\r\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(line, "db") {
|
||||
continue
|
||||
}
|
||||
colon := strings.IndexByte(line, ':')
|
||||
if colon <= 2 {
|
||||
continue
|
||||
}
|
||||
dbStr := line[2:colon]
|
||||
dbNum, err := strconv.Atoi(dbStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var keys int64
|
||||
for _, part := range strings.Split(line[colon+1:], ",") {
|
||||
part = strings.TrimSpace(part)
|
||||
if strings.HasPrefix(part, "keys=") {
|
||||
keys, _ = strconv.ParseInt(strings.TrimPrefix(part, "keys="), 10, 64)
|
||||
break
|
||||
}
|
||||
}
|
||||
used[dbNum] = keys
|
||||
}
|
||||
|
||||
fmt.Printf("Redis %s — 逻辑库 key 统计(INFO keyspace)\n\n", addr)
|
||||
for i := 0; i < 16; i++ {
|
||||
k, ok := used[i]
|
||||
if !ok {
|
||||
fmt.Printf(" db%-2d : 无记录(通常为 0 个 key / 未写入过)\n", i)
|
||||
} else if k == 0 {
|
||||
fmt.Printf(" db%-2d : 0 keys\n", i)
|
||||
} else {
|
||||
fmt.Printf(" db%-2d : %d keys (已占用)\n", i, k)
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println("说明: Redis 默认 16 个逻辑库(0-15);「无记录」一般可当空闲选用。")
|
||||
fmt.Println("若 redis.conf 里 databases=N,实际库数量可能更大,可用 redis-cli CONFIG GET databases 查看。")
|
||||
}
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// 用法: go run . <host:port> [password]
|
||||
// 例: go run . 10.1.1.100:6379
|
||||
func main() {
|
||||
addr := "10.1.1.100:6379"
|
||||
pass := os.Getenv("REDIS_PASSWORD")
|
||||
if len(os.Args) >= 2 {
|
||||
addr = os.Args[1]
|
||||
}
|
||||
if len(os.Args) >= 3 {
|
||||
pass = os.Args[2]
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
|
||||
defer cancel()
|
||||
|
||||
r := redis.NewClient(&redis.Options{Addr: addr, Password: pass})
|
||||
defer r.Close()
|
||||
if err := r.Ping(ctx).Err(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "连接失败 %s: %v\n", addr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
s, err := r.Info(ctx, "keyspace").Result()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "INFO keyspace: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
used := make(map[int]int64)
|
||||
for _, line := range strings.Split(s, "\r\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(line, "db") {
|
||||
continue
|
||||
}
|
||||
colon := strings.IndexByte(line, ':')
|
||||
if colon <= 2 {
|
||||
continue
|
||||
}
|
||||
dbStr := line[2:colon]
|
||||
dbNum, err := strconv.Atoi(dbStr)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var keys int64
|
||||
for _, part := range strings.Split(line[colon+1:], ",") {
|
||||
part = strings.TrimSpace(part)
|
||||
if strings.HasPrefix(part, "keys=") {
|
||||
keys, _ = strconv.ParseInt(strings.TrimPrefix(part, "keys="), 10, 64)
|
||||
break
|
||||
}
|
||||
}
|
||||
used[dbNum] = keys
|
||||
}
|
||||
|
||||
fmt.Printf("Redis %s — 逻辑库 key 统计(INFO keyspace)\n\n", addr)
|
||||
for i := 0; i < 16; i++ {
|
||||
k, ok := used[i]
|
||||
if !ok {
|
||||
fmt.Printf(" db%-2d : 无记录(通常为 0 个 key / 未写入过)\n", i)
|
||||
} else if k == 0 {
|
||||
fmt.Printf(" db%-2d : 0 keys\n", i)
|
||||
} else {
|
||||
fmt.Printf(" db%-2d : %d keys (已占用)\n", i, k)
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println("说明: Redis 默认 16 个逻辑库(0-15);「无记录」一般可当空闲选用。")
|
||||
fmt.Println("若 redis.conf 里 databases=N,实际库数量可能更大,可用 redis-cli CONFIG GET databases 查看。")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user