Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

157 строки
5.8 KiB

  1. #!/bin/bash
  2. # Master + Slave 并发扣费测试脚本
  3. # 测试场景:Master本地扣费 和 Slave同步扣费 同时对同一用户扣费
  4. # 用法: ./test_region_sync_concurrent.sh [USER_ID]
  5. MASTER="http://172.22.57.85:23000"
  6. SLAVE="http://172.22.57.85:13000"
  7. API_KEY="123456789"
  8. USER_ID=${1:-1}
  9. echo "=========================================="
  10. echo " Master/Slave 并发扣费冲突测试"
  11. echo "=========================================="
  12. echo "Master: $MASTER"
  13. echo "Slave: $SLAVE"
  14. echo "测试用户ID: $USER_ID"
  15. echo "=========================================="
  16. echo ""
  17. # 先查询当前余额
  18. echo "[1] 查询初始余额..."
  19. INITIAL=$(curl -s -X POST "$MASTER/api/internal/sync/quota/query" \
  20. -H "Content-Type: application/json" \
  21. -H "X-Sync-API-Key: $API_KEY" \
  22. -H "X-Sync-Node: $SLAVE" \
  23. -d "{\"user_id\": $USER_ID}")
  24. INITIAL_QUOTA=$(echo $INITIAL | grep -o '"quota":[0-9]*' | grep -o '[0-9]*')
  25. echo "初始余额: $INITIAL_QUOTA"
  26. echo ""
  27. # 定义扣费金额
  28. MASTER_DEDUCT=1000 # Master端扣费 1000
  29. SLAVE_DEDUCT=1000 # Slave端扣费 1000
  30. EXPECTED_FINAL=$((INITIAL_QUOTA - MASTER_DEDUCT - SLAVE_DEDUCT))
  31. echo "[2] 并发执行 Master本地扣费($MASTER_DEDUCT) + Slave同步扣费($SLAVE_DEDUCT)..."
  32. echo " 期望最终余额: $INITIAL_QUOTA - $MASTER_DEDUCT - $SLAVE_DEDUCT = $EXPECTED_FINAL"
  33. echo ""
  34. # 并发发送两个请求
  35. # 请求1: 模拟 Slave 同步扣费(调用 Master 的 batch-deduct 接口)
  36. curl -s -X POST "$MASTER/api/internal/sync/quota/batch-deduct" \
  37. -H "Content-Type: application/json" \
  38. -H "X-Sync-API-Key: $API_KEY" \
  39. -H "X-Sync-Node: $SLAVE" \
  40. -d "{\"records\": [{\"user_id\": $USER_ID, \"request_id\": \"req-slave-concurrent-$(date +%s)\", \"quota\": $SLAVE_DEDUCT}]}" > /tmp/slave_result.json &
  41. # 请求2: 模拟 Master 本地扣费(通过正常API调用,这里用 DecreaseUserQuota 对应的管理接口)
  42. # 直接用 batch-deduct 的不同 request_id 模拟第二个并发扣费
  43. curl -s -X POST "$MASTER/api/internal/sync/quota/batch-deduct" \
  44. -H "Content-Type: application/json" \
  45. -H "X-Sync-API-Key: $API_KEY" \
  46. -H "X-Sync-Node: $SLAVE" \
  47. -d "{\"records\": [{\"user_id\": $USER_ID, \"request_id\": \"req-master-concurrent-$(date +%s)\", \"quota\": $MASTER_DEDUCT}]}" > /tmp/master_result.json &
  48. wait
  49. echo "Slave扣费结果: $(cat /tmp/slave_result.json)"
  50. echo "Master扣费结果: $(cat /tmp/master_result.json)"
  51. echo ""
  52. # 查询最终余额
  53. echo "[3] 查询最终余额..."
  54. FINAL=$(curl -s -X POST "$MASTER/api/internal/sync/quota/query" \
  55. -H "Content-Type: application/json" \
  56. -H "X-Sync-API-Key: $API_KEY" \
  57. -H "X-Sync-Node: $SLAVE" \
  58. -d "{\"user_id\": $USER_ID}")
  59. FINAL_QUOTA=$(echo $FINAL | grep -o '"quota":[0-9]*' | grep -o '[0-9]*')
  60. echo "最终余额: $FINAL_QUOTA"
  61. echo ""
  62. # 分析结果
  63. echo "=========================================="
  64. echo " 结果分析"
  65. echo "=========================================="
  66. echo "初始余额: $INITIAL_QUOTA"
  67. echo "Master扣费: -$MASTER_DEDUCT"
  68. echo "Slave扣费: -$SLAVE_DEDUCT"
  69. echo "期望最终余额: $EXPECTED_FINAL"
  70. echo "实际最终余额: $FINAL_QUOTA"
  71. echo ""
  72. ACTUAL_DEDUCTED=$((INITIAL_QUOTA - FINAL_QUOTA))
  73. echo "实际总共扣除: $ACTUAL_DEDUCTED"
  74. echo "期望总共扣除: $((MASTER_DEDUCT + SLAVE_DEDUCT))"
  75. echo ""
  76. if [ "$FINAL_QUOTA" -eq "$EXPECTED_FINAL" ]; then
  77. echo "✅ 测试通过: 余额变化正确,并发扣费无丢失"
  78. elif [ "$ACTUAL_DEDUCTED" -lt "$((MASTER_DEDUCT + SLAVE_DEDUCT))" ]; then
  79. LOST=$((MASTER_DEDUCT + SLAVE_DEDUCT - ACTUAL_DEDUCTED))
  80. echo "❌ 测试失败: 扣费丢失 $LOST,并发扣费存在覆盖问题"
  81. echo " 可能原因: BatchDeductQuota 使用 FOR UPDATE + 设定新值,"
  82. echo " 而 DecreaseUserQuota 使用 gorm.Expr 原子减,两者混用会互相覆盖"
  83. else
  84. echo "❌ 测试失败: 余额异常(扣多了或变成负数)"
  85. fi
  86. echo ""
  87. # 高并发版本: 多次并发测试
  88. echo "=========================================="
  89. echo " 高并发压力测试 (10轮)"
  90. echo "=========================================="
  91. echo ""
  92. echo "[4] 查询当前余额..."
  93. BEFORE=$(curl -s -X POST "$MASTER/api/internal/sync/quota/query" \
  94. -H "Content-Type: application/json" \
  95. -H "X-Sync-API-Key: $API_KEY" \
  96. -H "X-Sync-Node: $SLAVE" \
  97. -d "{\"user_id\": $USER_ID}")
  98. BEFORE_QUOTA=$(echo $BEFORE | grep -o '"quota":[0-9]*' | grep -o '[0-9]*')
  99. echo "当前余额: $BEFORE_QUOTA"
  100. echo ""
  101. echo "[5] 并发发送20个扣费请求(每个100)..."
  102. for i in {1..20}; do
  103. curl -s -X POST "$MASTER/api/internal/sync/quota/batch-deduct" \
  104. -H "Content-Type: application/json" \
  105. -H "X-Sync-API-Key: $API_KEY" \
  106. -H "X-Sync-Node: $SLAVE" \
  107. -d "{\"records\": [{\"user_id\": $USER_ID, \"request_id\": \"req-stress-$i-$(date +%s%N)\", \"quota\": 100}]}" > /dev/null &
  108. done
  109. wait
  110. echo "全部请求发送完成"
  111. echo ""
  112. echo "[6] 查询最终余额..."
  113. AFTER=$(curl -s -X POST "$MASTER/api/internal/sync/quota/query" \
  114. -H "Content-Type: application/json" \
  115. -H "X-Sync-API-Key: $API_KEY" \
  116. -H "X-Sync-Node: $SLAVE" \
  117. -d "{\"user_id\": $USER_ID}")
  118. AFTER_QUOTA=$(echo $AFTER | grep -o '"quota":[0-9]*' | grep -o '[0-9]*')
  119. echo "最终余额: $AFTER_QUOTA"
  120. ACTUAL_TOTAL_DEDUCTED=$((BEFORE_QUOTA - AFTER_QUOTA))
  121. EXPECTED_TOTAL_DEDUCTED=$((20 * 100))
  122. echo ""
  123. echo "实际总共扣除: $ACTUAL_TOTAL_DEDUCTED"
  124. echo "期望总共扣除: $EXPECTED_TOTAL_DEDUCTED"
  125. if [ "$ACTUAL_TOTAL_DEDUCTED" -eq "$EXPECTED_TOTAL_DEDUCTED" ]; then
  126. echo "✅ 高并发测试通过: 所有扣费都正确"
  127. elif [ "$ACTUAL_TOTAL_DEDUCTED" -lt "$EXPECTED_TOTAL_DEDUCTED" ]; then
  128. echo "❌ 高并发测试失败: 少扣了 $((EXPECTED_TOTAL_DEDUCTED - ACTUAL_TOTAL_DEDUCTED))"
  129. else
  130. echo "❌ 高并发测试失败: 多扣了 $((ACTUAL_TOTAL_DEDUCTED - EXPECTED_TOTAL_DEDUCTED))"
  131. fi
  132. echo ""
  133. echo "=========================================="
  134. echo " 测试完成"
  135. echo "=========================================="