575 lines
13 KiB
Go
575 lines
13 KiB
Go
|
|
package binanceservice
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"sync"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/go-admin-team/go-admin-core/logger"
|
||
|
|
"github.com/go-redis/redis/v8"
|
||
|
|
)
|
||
|
|
|
||
|
|
// LockManagerConfig 锁管理器专用配置
|
||
|
|
type LockManagerConfig struct {
|
||
|
|
Type LockType `json:"type"`
|
||
|
|
Scope LockScope `json:"scope"`
|
||
|
|
Key string `json:"key"`
|
||
|
|
Expiration time.Duration `json:"expiration"`
|
||
|
|
Timeout time.Duration `json:"timeout"`
|
||
|
|
RetryDelay time.Duration `json:"retry_delay"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// LockType 锁类型
|
||
|
|
type LockType string
|
||
|
|
|
||
|
|
const (
|
||
|
|
LockTypeLocal LockType = "local"
|
||
|
|
LockTypeDistributed LockType = "distributed"
|
||
|
|
)
|
||
|
|
|
||
|
|
// LockScope 锁范围
|
||
|
|
type LockScope string
|
||
|
|
|
||
|
|
const (
|
||
|
|
ScopeOrder LockScope = "order"
|
||
|
|
ScopePosition LockScope = "position"
|
||
|
|
ScopeUser LockScope = "user"
|
||
|
|
ScopeSymbol LockScope = "symbol"
|
||
|
|
ScopeGlobal LockScope = "global"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Lock 锁接口
|
||
|
|
type Lock interface {
|
||
|
|
Lock(ctx context.Context) error
|
||
|
|
Unlock(ctx context.Context) error
|
||
|
|
TryLock(ctx context.Context) (bool, error)
|
||
|
|
IsLocked() bool
|
||
|
|
GetKey() string
|
||
|
|
GetExpiration() time.Duration
|
||
|
|
}
|
||
|
|
|
||
|
|
// LocalLock 本地锁
|
||
|
|
type LocalLock struct {
|
||
|
|
key string
|
||
|
|
mu *sync.RWMutex
|
||
|
|
locked bool
|
||
|
|
expiration time.Duration
|
||
|
|
acquiredAt time.Time
|
||
|
|
}
|
||
|
|
|
||
|
|
// DistributedLock 分布式锁
|
||
|
|
type DistributedLock struct {
|
||
|
|
key string
|
||
|
|
value string
|
||
|
|
expiration time.Duration
|
||
|
|
redisClient *redis.Client
|
||
|
|
locked bool
|
||
|
|
acquiredAt time.Time
|
||
|
|
refreshTicker *time.Ticker
|
||
|
|
stopRefresh chan struct{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// LockManager 锁管理器
|
||
|
|
type LockManager struct {
|
||
|
|
config *OptimizedConfig
|
||
|
|
redisClient *redis.Client
|
||
|
|
localLocks map[string]*LocalLock
|
||
|
|
localMutex sync.RWMutex
|
||
|
|
metrics *MetricsCollector
|
||
|
|
}
|
||
|
|
|
||
|
|
// LockConfig 已在 config_optimized.go 中定义
|
||
|
|
|
||
|
|
// NewLockManager 创建锁管理器
|
||
|
|
func NewLockManager(config *OptimizedConfig, redisClient *redis.Client, metrics *MetricsCollector) *LockManager {
|
||
|
|
return &LockManager{
|
||
|
|
config: config,
|
||
|
|
redisClient: redisClient,
|
||
|
|
localLocks: make(map[string]*LocalLock),
|
||
|
|
metrics: metrics,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// CreateLock 创建锁
|
||
|
|
func (lm *LockManager) CreateLock(config LockManagerConfig) Lock {
|
||
|
|
switch config.Type {
|
||
|
|
case LockTypeDistributed:
|
||
|
|
return lm.createDistributedLock(config)
|
||
|
|
case LockTypeLocal:
|
||
|
|
return lm.createLocalLock(config)
|
||
|
|
default:
|
||
|
|
// 默认使用分布式锁
|
||
|
|
return lm.createDistributedLock(config)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// createLocalLock 创建本地锁
|
||
|
|
func (lm *LockManager) createLocalLock(config LockManagerConfig) *LocalLock {
|
||
|
|
lm.localMutex.Lock()
|
||
|
|
defer lm.localMutex.Unlock()
|
||
|
|
|
||
|
|
key := lm.buildLockKey(config.Scope, config.Key)
|
||
|
|
if lock, exists := lm.localLocks[key]; exists {
|
||
|
|
return lock
|
||
|
|
}
|
||
|
|
|
||
|
|
lock := &LocalLock{
|
||
|
|
key: key,
|
||
|
|
mu: &sync.RWMutex{},
|
||
|
|
expiration: config.Expiration,
|
||
|
|
}
|
||
|
|
|
||
|
|
lm.localLocks[key] = lock
|
||
|
|
return lock
|
||
|
|
}
|
||
|
|
|
||
|
|
// createDistributedLock 创建分布式锁
|
||
|
|
func (lm *LockManager) createDistributedLock(config LockManagerConfig) *DistributedLock {
|
||
|
|
key := lm.buildLockKey(config.Scope, config.Key)
|
||
|
|
value := fmt.Sprintf("%d_%s", time.Now().UnixNano(), generateRandomString(8))
|
||
|
|
|
||
|
|
return &DistributedLock{
|
||
|
|
key: key,
|
||
|
|
value: value,
|
||
|
|
expiration: config.Expiration,
|
||
|
|
redisClient: lm.redisClient,
|
||
|
|
stopRefresh: make(chan struct{}),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// buildLockKey 构建锁键名
|
||
|
|
func (lm *LockManager) buildLockKey(scope LockScope, key string) string {
|
||
|
|
return fmt.Sprintf("lock:%s:%s", scope, key)
|
||
|
|
}
|
||
|
|
|
||
|
|
// AcquireLock 获取锁(带超时和重试)
|
||
|
|
func (lm *LockManager) AcquireLock(ctx context.Context, config LockManagerConfig) (Lock, error) {
|
||
|
|
lock := lm.CreateLock(config)
|
||
|
|
startTime := time.Now()
|
||
|
|
|
||
|
|
// 设置超时上下文
|
||
|
|
lockCtx := ctx
|
||
|
|
if config.Timeout > 0 {
|
||
|
|
var cancel context.CancelFunc
|
||
|
|
lockCtx, cancel = context.WithTimeout(ctx, config.Timeout)
|
||
|
|
defer cancel()
|
||
|
|
}
|
||
|
|
|
||
|
|
// 重试获取锁
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case <-lockCtx.Done():
|
||
|
|
waitTime := time.Since(startTime)
|
||
|
|
if lm.metrics != nil {
|
||
|
|
lm.metrics.RecordLockOperation(false, waitTime)
|
||
|
|
}
|
||
|
|
return nil, fmt.Errorf("获取锁超时: %s", config.Key)
|
||
|
|
|
||
|
|
default:
|
||
|
|
err := lock.Lock(lockCtx)
|
||
|
|
if err == nil {
|
||
|
|
waitTime := time.Since(startTime)
|
||
|
|
if lm.metrics != nil {
|
||
|
|
lm.metrics.RecordLockOperation(true, waitTime)
|
||
|
|
}
|
||
|
|
return lock, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// 等待重试
|
||
|
|
select {
|
||
|
|
case <-lockCtx.Done():
|
||
|
|
return nil, lockCtx.Err()
|
||
|
|
case <-time.After(config.RetryDelay):
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ReleaseLock 释放锁
|
||
|
|
func (lm *LockManager) ReleaseLock(ctx context.Context, lock Lock) error {
|
||
|
|
if lock == nil {
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return lock.Unlock(ctx)
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithLock 使用锁执行操作
|
||
|
|
func (lm *LockManager) WithLock(ctx context.Context, config LockManagerConfig, operation func() error) error {
|
||
|
|
lock, err := lm.AcquireLock(ctx, config)
|
||
|
|
if err != nil {
|
||
|
|
return fmt.Errorf("获取锁失败: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
defer func() {
|
||
|
|
if unlockErr := lm.ReleaseLock(ctx, lock); unlockErr != nil {
|
||
|
|
logger.Errorf("释放锁失败 [%s]: %v", config.Key, unlockErr)
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
return operation()
|
||
|
|
}
|
||
|
|
|
||
|
|
// CleanupExpiredLocks 清理过期的本地锁
|
||
|
|
func (lm *LockManager) CleanupExpiredLocks() {
|
||
|
|
lm.localMutex.Lock()
|
||
|
|
defer lm.localMutex.Unlock()
|
||
|
|
|
||
|
|
now := time.Now()
|
||
|
|
for key, lock := range lm.localLocks {
|
||
|
|
if lock.locked && lock.expiration > 0 && now.Sub(lock.acquiredAt) > lock.expiration {
|
||
|
|
lock.locked = false
|
||
|
|
logger.Warnf("本地锁已过期并被清理: %s", key)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetLockStatus 获取锁状态
|
||
|
|
func (lm *LockManager) GetLockStatus() map[string]interface{} {
|
||
|
|
lm.localMutex.RLock()
|
||
|
|
defer lm.localMutex.RUnlock()
|
||
|
|
|
||
|
|
status := make(map[string]interface{})
|
||
|
|
localLocks := make(map[string]interface{})
|
||
|
|
|
||
|
|
for key, lock := range lm.localLocks {
|
||
|
|
localLocks[key] = map[string]interface{}{
|
||
|
|
"locked": lock.locked,
|
||
|
|
"acquired_at": lock.acquiredAt,
|
||
|
|
"expiration": lock.expiration,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
status["local_locks"] = localLocks
|
||
|
|
status["total_local_locks"] = len(lm.localLocks)
|
||
|
|
|
||
|
|
return status
|
||
|
|
}
|
||
|
|
|
||
|
|
// LocalLock 实现
|
||
|
|
|
||
|
|
// Lock 获取本地锁
|
||
|
|
func (ll *LocalLock) Lock(ctx context.Context) error {
|
||
|
|
ll.mu.Lock()
|
||
|
|
defer ll.mu.Unlock()
|
||
|
|
|
||
|
|
if ll.locked {
|
||
|
|
return fmt.Errorf("锁已被占用: %s", ll.key)
|
||
|
|
}
|
||
|
|
|
||
|
|
ll.locked = true
|
||
|
|
ll.acquiredAt = time.Now()
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Unlock 释放本地锁
|
||
|
|
func (ll *LocalLock) Unlock(ctx context.Context) error {
|
||
|
|
ll.mu.Lock()
|
||
|
|
defer ll.mu.Unlock()
|
||
|
|
|
||
|
|
if !ll.locked {
|
||
|
|
return fmt.Errorf("锁未被占用: %s", ll.key)
|
||
|
|
}
|
||
|
|
|
||
|
|
ll.locked = false
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// TryLock 尝试获取本地锁
|
||
|
|
func (ll *LocalLock) TryLock(ctx context.Context) (bool, error) {
|
||
|
|
ll.mu.Lock()
|
||
|
|
defer ll.mu.Unlock()
|
||
|
|
|
||
|
|
if ll.locked {
|
||
|
|
return false, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
ll.locked = true
|
||
|
|
ll.acquiredAt = time.Now()
|
||
|
|
return true, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// IsLocked 检查本地锁是否被占用
|
||
|
|
func (ll *LocalLock) IsLocked() bool {
|
||
|
|
ll.mu.RLock()
|
||
|
|
defer ll.mu.RUnlock()
|
||
|
|
return ll.locked
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetKey 获取锁键名
|
||
|
|
func (ll *LocalLock) GetKey() string {
|
||
|
|
return ll.key
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetExpiration 获取过期时间
|
||
|
|
func (ll *LocalLock) GetExpiration() time.Duration {
|
||
|
|
return ll.expiration
|
||
|
|
}
|
||
|
|
|
||
|
|
// DistributedLock 实现
|
||
|
|
|
||
|
|
// Lock 获取分布式锁
|
||
|
|
func (dl *DistributedLock) Lock(ctx context.Context) error {
|
||
|
|
// 使用 SET key value EX seconds NX 命令
|
||
|
|
result := dl.redisClient.SetNX(ctx, dl.key, dl.value, dl.expiration)
|
||
|
|
if result.Err() != nil {
|
||
|
|
return fmt.Errorf("获取分布式锁失败: %w", result.Err())
|
||
|
|
}
|
||
|
|
|
||
|
|
if !result.Val() {
|
||
|
|
return fmt.Errorf("分布式锁已被占用: %s", dl.key)
|
||
|
|
}
|
||
|
|
|
||
|
|
dl.locked = true
|
||
|
|
dl.acquiredAt = time.Now()
|
||
|
|
|
||
|
|
// 启动锁续期
|
||
|
|
dl.startRefresh()
|
||
|
|
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Unlock 释放分布式锁
|
||
|
|
func (dl *DistributedLock) Unlock(ctx context.Context) error {
|
||
|
|
if !dl.locked {
|
||
|
|
return fmt.Errorf("分布式锁未被占用: %s", dl.key)
|
||
|
|
}
|
||
|
|
|
||
|
|
// 停止续期
|
||
|
|
dl.stopRefreshProcess()
|
||
|
|
|
||
|
|
// 使用 Lua 脚本确保只删除自己的锁
|
||
|
|
luaScript := `
|
||
|
|
if redis.call("get", KEYS[1]) == ARGV[1] then
|
||
|
|
return redis.call("del", KEYS[1])
|
||
|
|
else
|
||
|
|
return 0
|
||
|
|
end
|
||
|
|
`
|
||
|
|
|
||
|
|
result := dl.redisClient.Eval(ctx, luaScript, []string{dl.key}, dl.value)
|
||
|
|
if result.Err() != nil {
|
||
|
|
return fmt.Errorf("释放分布式锁失败: %w", result.Err())
|
||
|
|
}
|
||
|
|
|
||
|
|
dl.locked = false
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// TryLock 尝试获取分布式锁
|
||
|
|
func (dl *DistributedLock) TryLock(ctx context.Context) (bool, error) {
|
||
|
|
result := dl.redisClient.SetNX(ctx, dl.key, dl.value, dl.expiration)
|
||
|
|
if result.Err() != nil {
|
||
|
|
return false, fmt.Errorf("尝试获取分布式锁失败: %w", result.Err())
|
||
|
|
}
|
||
|
|
|
||
|
|
if result.Val() {
|
||
|
|
dl.locked = true
|
||
|
|
dl.acquiredAt = time.Now()
|
||
|
|
dl.startRefresh()
|
||
|
|
return true, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
return false, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// IsLocked 检查分布式锁是否被占用
|
||
|
|
func (dl *DistributedLock) IsLocked() bool {
|
||
|
|
return dl.locked
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetKey 获取锁键名
|
||
|
|
func (dl *DistributedLock) GetKey() string {
|
||
|
|
return dl.key
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetExpiration 获取过期时间
|
||
|
|
func (dl *DistributedLock) GetExpiration() time.Duration {
|
||
|
|
return dl.expiration
|
||
|
|
}
|
||
|
|
|
||
|
|
// startRefresh 启动锁续期
|
||
|
|
func (dl *DistributedLock) startRefresh() {
|
||
|
|
if dl.expiration <= 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// 每隔过期时间的1/3进行续期
|
||
|
|
refreshInterval := dl.expiration / 3
|
||
|
|
dl.refreshTicker = time.NewTicker(refreshInterval)
|
||
|
|
|
||
|
|
go func() {
|
||
|
|
defer dl.refreshTicker.Stop()
|
||
|
|
|
||
|
|
for {
|
||
|
|
select {
|
||
|
|
case <-dl.stopRefresh:
|
||
|
|
return
|
||
|
|
case <-dl.refreshTicker.C:
|
||
|
|
if !dl.locked {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// 使用 Lua 脚本续期
|
||
|
|
luaScript := `
|
||
|
|
if redis.call("get", KEYS[1]) == ARGV[1] then
|
||
|
|
return redis.call("expire", KEYS[1], ARGV[2])
|
||
|
|
else
|
||
|
|
return 0
|
||
|
|
end
|
||
|
|
`
|
||
|
|
|
||
|
|
ctx := context.Background()
|
||
|
|
result := dl.redisClient.Eval(ctx, luaScript, []string{dl.key}, dl.value, int(dl.expiration.Seconds()))
|
||
|
|
if result.Err() != nil {
|
||
|
|
logger.Errorf("分布式锁续期失败 [%s]: %v", dl.key, result.Err())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if result.Val().(int64) == 0 {
|
||
|
|
logger.Warnf("分布式锁已被其他进程占用,停止续期 [%s]", dl.key)
|
||
|
|
dl.locked = false
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
}
|
||
|
|
|
||
|
|
// stopRefreshProcess 停止续期进程
|
||
|
|
func (dl *DistributedLock) stopRefreshProcess() {
|
||
|
|
if dl.refreshTicker != nil {
|
||
|
|
close(dl.stopRefresh)
|
||
|
|
dl.refreshTicker.Stop()
|
||
|
|
dl.refreshTicker = nil
|
||
|
|
dl.stopRefresh = make(chan struct{})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 辅助函数
|
||
|
|
|
||
|
|
// generateRandomString 生成随机字符串
|
||
|
|
func generateRandomString(length int) string {
|
||
|
|
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||
|
|
b := make([]byte, length)
|
||
|
|
for i := range b {
|
||
|
|
b[i] = charset[time.Now().UnixNano()%int64(len(charset))]
|
||
|
|
}
|
||
|
|
return string(b)
|
||
|
|
}
|
||
|
|
|
||
|
|
// 预定义的锁配置
|
||
|
|
|
||
|
|
// GetOrderLockConfig 获取订单锁配置
|
||
|
|
func GetOrderLockConfig(orderID string) LockManagerConfig {
|
||
|
|
return LockManagerConfig{
|
||
|
|
Type: LockTypeDistributed,
|
||
|
|
Scope: ScopeOrder,
|
||
|
|
Key: orderID,
|
||
|
|
Expiration: 30 * time.Second,
|
||
|
|
Timeout: 5 * time.Second,
|
||
|
|
RetryDelay: 100 * time.Millisecond,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetPositionLockConfig 获取持仓锁配置
|
||
|
|
func GetPositionLockConfig(userID, symbol string) LockManagerConfig {
|
||
|
|
return LockManagerConfig{
|
||
|
|
Type: LockTypeDistributed,
|
||
|
|
Scope: ScopePosition,
|
||
|
|
Key: fmt.Sprintf("%s_%s", userID, symbol),
|
||
|
|
Expiration: 60 * time.Second,
|
||
|
|
Timeout: 10 * time.Second,
|
||
|
|
RetryDelay: 200 * time.Millisecond,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetUserLockConfig 获取用户锁配置
|
||
|
|
func GetUserLockConfig(userID string) LockManagerConfig {
|
||
|
|
return LockManagerConfig{
|
||
|
|
Type: LockTypeLocal,
|
||
|
|
Scope: ScopeUser,
|
||
|
|
Key: userID,
|
||
|
|
Expiration: 30 * time.Second,
|
||
|
|
Timeout: 3 * time.Second,
|
||
|
|
RetryDelay: 50 * time.Millisecond,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetSymbolLockConfig 获取交易对锁配置
|
||
|
|
func GetSymbolLockConfig(symbol string) LockManagerConfig {
|
||
|
|
return LockManagerConfig{
|
||
|
|
Type: LockTypeLocal,
|
||
|
|
Scope: ScopeSymbol,
|
||
|
|
Key: symbol,
|
||
|
|
Expiration: 15 * time.Second,
|
||
|
|
Timeout: 2 * time.Second,
|
||
|
|
RetryDelay: 25 * time.Millisecond,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetGlobalLockConfig 获取全局锁配置
|
||
|
|
func GetGlobalLockConfig(operation string) LockManagerConfig {
|
||
|
|
return LockManagerConfig{
|
||
|
|
Type: LockTypeDistributed,
|
||
|
|
Scope: ScopeGlobal,
|
||
|
|
Key: operation,
|
||
|
|
Expiration: 120 * time.Second,
|
||
|
|
Timeout: 30 * time.Second,
|
||
|
|
RetryDelay: 500 * time.Millisecond,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 全局锁管理器实例
|
||
|
|
var GlobalLockManager *LockManager
|
||
|
|
|
||
|
|
// InitLockManager 初始化锁管理器
|
||
|
|
func InitLockManager(config *OptimizedConfig, redisClient *redis.Client, metrics *MetricsCollector) {
|
||
|
|
GlobalLockManager = NewLockManager(config, redisClient, metrics)
|
||
|
|
|
||
|
|
// 启动清理过期锁的定时任务
|
||
|
|
go func() {
|
||
|
|
ticker := time.NewTicker(time.Minute)
|
||
|
|
defer ticker.Stop()
|
||
|
|
|
||
|
|
for range ticker.C {
|
||
|
|
GlobalLockManager.CleanupExpiredLocks()
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetLockManager 获取全局锁管理器
|
||
|
|
func GetLockManager() *LockManager {
|
||
|
|
return GlobalLockManager
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithOrderLock 使用订单锁执行操作
|
||
|
|
func WithOrderLock(ctx context.Context, orderID string, operation func() error) error {
|
||
|
|
if GlobalLockManager == nil {
|
||
|
|
return operation()
|
||
|
|
}
|
||
|
|
|
||
|
|
config := GetOrderLockConfig(orderID)
|
||
|
|
return GlobalLockManager.WithLock(ctx, config, operation)
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithPositionLock 使用持仓锁执行操作
|
||
|
|
func WithPositionLock(ctx context.Context, userID, symbol string, operation func() error) error {
|
||
|
|
if GlobalLockManager == nil {
|
||
|
|
return operation()
|
||
|
|
}
|
||
|
|
|
||
|
|
config := GetPositionLockConfig(userID, symbol)
|
||
|
|
return GlobalLockManager.WithLock(ctx, config, operation)
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithUserLock 使用用户锁执行操作
|
||
|
|
func WithUserLock(ctx context.Context, userID string, operation func() error) error {
|
||
|
|
if GlobalLockManager == nil {
|
||
|
|
return operation()
|
||
|
|
}
|
||
|
|
|
||
|
|
config := GetUserLockConfig(userID)
|
||
|
|
return GlobalLockManager.WithLock(ctx, config, operation)
|
||
|
|
}
|