```
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful

feat(config): 添加超时空地图配置和时间地图查询功能

新增IsTimeSpace字段用于标识地图是否为超时空地图,
添加TimeMap API接口支持查询超时空地图配置

perf(socket): 优化XORDecryptU解密函数减少内存分配

基于bytebufferpool实现缓冲区池化,大幅降低高频调用下的
内存分配和GC压力,提升性能表现

refactor(utils): 优化packVal序列化函数提升性能和稳定性

减少反射开销,
This commit is contained in:
昔念
2026-02-21 16:48:42 +08:00
parent b536f0974e
commit 31d9eb3f9e
6 changed files with 154 additions and 29 deletions

View File

@@ -13,6 +13,8 @@ go tool pprof -http :8081 "http://127.0.0.1:9909/debug/pprof/profile"
go tool pprof -http :8081 "http://202.189.15.67:62672/debug/pprof/profile" go tool pprof -http :8081 "http://202.189.15.67:62672/debug/pprof/profile"
go tool pprof -http :8081 "http://8.162.8.203:9909/debug/pprof//profile" go tool pprof -http :8081 "http://8.162.8.203:9909/debug/pprof//profile"
go tool pprof -http :8081 "http://8.162.23.87:9909/debug/pprof//profile"
详情查看 [文档](./docs) 详情查看 [文档](./docs)
- [战斗](./docs/battle.md) - [战斗](./docs/battle.md)

View File

@@ -19,6 +19,7 @@ import (
"github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/os/gtime"
"github.com/panjf2000/gnet/v2" "github.com/panjf2000/gnet/v2"
"github.com/valyala/bytebufferpool"
) )
func (s *Server) Boot(serverid, port uint16) error { func (s *Server) Boot(serverid, port uint16) error {
@@ -305,21 +306,41 @@ func (s *Server) onevent(c gnet.Conn, v []byte) {
} }
// XORDecryptU 优化后的异或解密:减少内存分配,支持复用缓冲区 // XORDecryptU 优化后的异或解密:减少内存分配,支持复用缓冲区
// XORDecryptU 基于bytebufferpool优化的异或解密函数
// 保留原有接口无侵入式优化高频调用下大幅减少内存分配和GC
func XORDecryptU(encryptedData []byte, key uint32) []byte { func XORDecryptU(encryptedData []byte, key uint32) []byte {
if len(encryptedData) == 0 { if len(encryptedData) == 0 {
return []byte{} return []byte{}
} }
// 分配密钥字节数组(可全局缓存,避免每次创建 // 1. 栈上分配密钥字节数组(无GC压力保留原优化
var keyBytes [4]byte // 栈上分配比make更高效 var keyBytes [4]byte
binary.BigEndian.PutUint32(keyBytes[:], key) binary.BigEndian.PutUint32(keyBytes[:], key)
keyLen := len(keyBytes) keyLen := len(keyBytes)
// 复用输出缓冲区:如果需要极致性能,可传入外部缓冲区 // 2. 从bytebufferpool获取池化缓冲区替代make分配
decrypted := make([]byte, len(encryptedData)) buf := bytebufferpool.Get()
defer bytebufferpool.Put(buf) // 函数结束自动归还缓冲区到池
// 3. 调整缓冲区长度,匹配待解密数据(避免扩容)
buf.B = buf.B[:0] // 清空原有数据,保留底层数组
if cap(buf.B) < len(encryptedData) {
// 若缓冲区容量不足直接扩容bytebufferpool会自动管理
buf.B = make([]byte, len(encryptedData))
} else {
// 容量足够,直接调整长度
buf.B = buf.B[:len(encryptedData)]
}
// 4. 核心异或解密逻辑直接操作buf.B无额外内存分配
decrypted := buf.B
for i, b := range encryptedData { for i, b := range encryptedData {
decrypted[i] = b ^ keyBytes[i%keyLen] decrypted[i] = b ^ keyBytes[i%keyLen]
} }
return decrypted // 5. 拷贝结果(关键:避免返回池化缓冲区,防止被后续调用覆盖)
result := make([]byte, len(decrypted))
copy(result, decrypted)
return result
} }

View File

@@ -6,6 +6,7 @@ import (
"fmt" "fmt"
"math" "math"
"reflect" "reflect"
"unsafe"
) )
type Field struct { type Field struct {
@@ -76,40 +77,60 @@ func (f *Field) Size(val reflect.Value, options *Options) int {
return size return size
} }
// 预定义常用的order避免重复判断
var defaultOrder = binary.BigEndian
// packVal 优化版:减少反射开销+优化内存拷贝+优雅错误处理
func (f *Field) packVal(buf []byte, val reflect.Value, length int, options *Options) (size int, err error) { func (f *Field) packVal(buf []byte, val reflect.Value, length int, options *Options) (size int, err error) {
// 1. 预缓存order避免重复判断
order := f.Order order := f.Order
if options.Order != nil { if options != nil && options.Order != nil {
order = options.Order order = options.Order
} }
if f.Ptr { if order == nil {
val = val.Elem() order = defaultOrder
} }
// 2. 处理指针类型:提前解引用,避免后续重复操作
if f.Ptr {
if !val.IsNil() {
val = val.Elem()
} else {
return 0, fmt.Errorf("field %s is nil pointer", f.Name)
}
}
// 3. 预解析类型避免重复Resolve
typ := f.Type.Resolve(options) typ := f.Type.Resolve(options)
kind := val.Kind()
// 4. 扁平化分支逻辑,减少嵌套
switch typ { switch typ {
case Struct: case Struct:
return f.Fields.Pack(buf, val, options) return f.Fields.Pack(buf, val, options)
case Bool, Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64: case Bool, Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64:
size = typ.Size() size = typ.Size()
if len(buf) < size {
return 0, fmt.Errorf("buf size %d < required %d", len(buf), size)
}
var n uint64 var n uint64
switch f.kind { switch kind {
case reflect.Bool: case reflect.Bool:
if val.Bool() { n = boolToUint64(val.Bool())
n = 1
} else {
n = 0
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n = uint64(val.Int()) n = uint64(val.Int())
default: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
n = val.Uint() n = val.Uint()
default:
return 0, fmt.Errorf("unsupported kind %s for numeric type %s", kind, typ)
} }
// 扁平化数值写入逻辑
switch typ { switch typ {
case Bool: case Bool:
if n != 0 { buf[0] = byte(n)
buf[0] = 1
} else {
buf[0] = 0
}
case Int8, Uint8: case Int8, Uint8:
buf[0] = byte(n) buf[0] = byte(n)
case Int16, Uint16: case Int16, Uint16:
@@ -117,33 +138,90 @@ func (f *Field) packVal(buf []byte, val reflect.Value, length int, options *Opti
case Int32, Uint32: case Int32, Uint32:
order.PutUint32(buf, uint32(n)) order.PutUint32(buf, uint32(n))
case Int64, Uint64: case Int64, Uint64:
order.PutUint64(buf, uint64(n)) order.PutUint64(buf, n)
} }
case Float32, Float64: case Float32, Float64:
size = typ.Size() size = typ.Size()
if len(buf) < size {
return 0, fmt.Errorf("buf size %d < required %d", len(buf), size)
}
if kind != reflect.Float32 && kind != reflect.Float64 {
return 0, fmt.Errorf("unsupported kind %s for float type %s", kind, typ)
}
n := val.Float() n := val.Float()
switch typ { switch typ {
case Float32: case Float32:
order.PutUint32(buf, math.Float32bits(float32(n))) order.PutUint32(buf, math.Float32bits(float32(n)))
case Float64: case Float64:
order.PutUint64(buf, math.Float64bits(n)) order.PutUint64(buf, math.Float64bits(n))
} }
case String: case String:
switch f.kind { // 优化String类型减少内存拷贝
switch kind {
case reflect.String: case reflect.String:
s := val.String()
size = len(s)
if len(buf) < size {
return 0, fmt.Errorf("buf size %d < string length %d", len(buf), size)
}
// 用unsafe直接拷贝字符串到buf避免[]byte(s)的内存分配
copyStringToBuf(buf, s)
case reflect.Slice:
if val.Type().Elem().Kind() != reflect.Uint8 {
return 0, fmt.Errorf("unsupported slice type %s for String field", val.Type())
}
size = val.Len() size = val.Len()
copy(buf, []byte(val.String())) if len(buf) < size {
default: return 0, fmt.Errorf("buf size %d < bytes length %d", len(buf), size)
// TODO: handle kind != bytes here }
size = val.Len() // 直接拷贝字节切片,避免冗余操作
copy(buf, val.Bytes()) copy(buf, val.Bytes())
default:
return 0, fmt.Errorf("unsupported kind %s for String type", kind)
} }
case CustomType: case CustomType:
return val.Addr().Interface().(Custom).Pack(buf, options) // 优化反射断言提前检查类型避免panic
if !val.CanAddr() {
return 0, fmt.Errorf("custom type %s cannot take address", val.Type())
}
custom, ok := val.Addr().Interface().(Custom)
if !ok {
return 0, fmt.Errorf("type %s does not implement Custom interface", val.Type())
}
return custom.Pack(buf, options)
default: default:
panic(fmt.Sprintf("no pack handler for type: %s", typ)) // 替换panic为error避免程序崩溃
return 0, fmt.Errorf("no pack handler for type: %s", typ)
} }
return
return size, nil
}
// 辅助函数bool转uint64减少inline重复代码
func boolToUint64(b bool) uint64 {
if b {
return 1
}
return 0
}
// 辅助函数unsafe拷贝字符串到buf避免[]byte(s)的内存分配
// 注意仅在确定buf长度足够时使用
func copyStringToBuf(buf []byte, s string) {
// unsafe转换字符串转字节切片无内存分配
src := *(*[]byte)(unsafe.Pointer(&struct {
string
cap int
}{s, len(s)}))
copy(buf, src)
} }
func (f *Field) Pack(buf []byte, val reflect.Value, length int, options *Options) (int, error) { func (f *Field) Pack(buf []byte, val reflect.Value, length int, options *Options) (int, error) {

View File

@@ -3,6 +3,9 @@ package admin
import ( import (
"blazing/cool" "blazing/cool"
"blazing/modules/config/service" "blazing/modules/config/service"
"context"
"github.com/gogf/gf/v2/frame/g"
) )
type MapController struct { type MapController struct {
@@ -20,3 +23,13 @@ func init() {
}, },
}) })
} }
type TimeMapReq struct {
g.Meta `path:"/timemap" method:"POST"`
}
func (this *MapController) TimeMap(ctx context.Context, req *TimeMapReq) (res *cool.BaseRes, err error) {
res = &cool.BaseRes{}
res.Data = service.NewMapService().GetTimeMap()
return res, nil
}

View File

@@ -16,6 +16,8 @@ type MapConfig struct {
MapID uint32 `gorm:"not null;primaryKey;comment:'地图唯一ID主键'" json:"map_id" description:"地图ID"` MapID uint32 `gorm:"not null;primaryKey;comment:'地图唯一ID主键'" json:"map_id" description:"地图ID"`
WeatherType []uint32 `gorm:"type:int[];comment:'天气类型( 1-雨天2-雪天)'" json:"weather_type"` WeatherType []uint32 `gorm:"type:int[];comment:'天气类型( 1-雨天2-雪天)'" json:"weather_type"`
//是否超时空
IsTimeSpace int `gorm:"type:int;default:0;comment:'是否超时空'" json:"is_time_space"`
// 掉落物配置 // 掉落物配置
DropItemIds []uint32 `gorm:"type:int[];comment:'掉落物IDs" json:"drop_item_ids"` DropItemIds []uint32 `gorm:"type:int[];comment:'掉落物IDs" json:"drop_item_ids"`

View File

@@ -29,3 +29,12 @@ func (s *MapService) GetData(p1 uint32) (ret *model.MapConfig) {
return return
} }
func (s *MapService) GetTimeMap() (ret []model.MapConfig) {
//cacheKey := strings.Join([]string{fmt.Sprintf("%d", p1), fmt.Sprintf("%d", p2)}, ":")
m := dbm_notenable(s.Model)
m.Where(`is_time_space`, 1).Scan(&ret)
return
}