final save code

This commit is contained in:
xsl
2025-08-02 13:11:45 +08:00
commit ae68a9e201
37 changed files with 289714 additions and 0 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

+162
View File
@@ -0,0 +1,162 @@
import requests
import time
import random
import string
from concurrent.futures import ThreadPoolExecutor, as_completed
import argparse
from threading import Lock
# 全局计数器
class RequestCounter:
def __init__(self):
self.sent = 0
self.in_progress = 0
self.completed = 0
self.success = 0
self.failed = 0
self.lock = Lock()
def increment_sent(self):
with self.lock:
self.sent += 1
def increment_in_progress(self):
with self.lock:
self.in_progress += 1
def decrement_in_progress(self):
with self.lock:
self.in_progress -= 1
def increment_completed(self):
with self.lock:
self.completed += 1
def increment_success(self):
with self.lock:
self.success += 1
def increment_failed(self):
with self.lock:
self.failed += 1
def get_stats(self):
with self.lock:
return {
'sent': self.sent,
'in_progress': self.in_progress,
'completed': self.completed,
'success': self.success,
'failed': self.failed
}
counter = RequestCounter()
def send_request(url, headers, data):
"""发送单个请求"""
try:
# 更新计数器
counter.increment_sent()
counter.increment_in_progress()
response = requests.post(url, headers=headers, json=data)
# 根据响应状态更新计数器
if response.status_code == 200:
counter.increment_success()
else:
counter.increment_failed()
return response.status_code, response.text
except Exception as e:
counter.increment_failed()
return None, str(e)
finally:
counter.decrement_in_progress()
counter.increment_completed()
def print_stats():
"""打印实时统计信息"""
while True:
stats = counter.get_stats()
print(f"\rStats - Sent: {stats['sent']}, In Progress: {stats['in_progress']}, "
f"Completed: {stats['completed']} (Success: {stats['success']}, Failed: {stats['failed']})",
end="", flush=True)
time.sleep(10)
def worker(url, headers, data, qps, duration):
"""工作线程函数,控制QPS"""
requests_count = 0
start_time = time.time()
end_time = start_time + duration
while time.time() < end_time:
request_start = time.time()
status_code, response_text = send_request(url, headers, data)
requests_count += 1
# 控制QPS
elapsed = time.time() - request_start
sleep_time = max(0, (1.0 / qps) - elapsed)
if sleep_time > 0:
time.sleep(sleep_time)
return requests_count
def run_test(url, headers, data, qps, duration, concurrency):
"""运行测试"""
total_requests = 0
start_time = time.time()
# 启动统计信息打印线程
import threading
stats_thread = threading.Thread(target=print_stats, daemon=True)
stats_thread.start()
with ThreadPoolExecutor(max_workers=concurrency) as executor:
futures = [executor.submit(worker, url, headers, data, qps, duration)
for _ in range(concurrency)]
for future in as_completed(futures):
total_requests += future.result()
end_time = time.time()
actual_duration = end_time - start_time
actual_qps = total_requests / actual_duration
# 获取最终统计
stats = counter.get_stats()
print("\n\nTest Summary:")
print(f"Total requests: {total_requests}")
print(f" Success: {stats['success']}")
print(f" Failed: {stats['failed']}")
print(f"Success rate: {(stats['success']/total_requests)*100:.2f}%")
print(f"Test duration: {actual_duration:.2f} seconds")
print(f"Actual QPS: {actual_qps:.2f}")
print(f"Target QPS: {qps}")
print(f"Concurrency: {concurrency}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='API Load Test Tool')
parser.add_argument('--qps', type=float, default=1, help='Requests per second (per thread)')
parser.add_argument('--duration', type=float, default=10, help='Test duration in seconds')
parser.add_argument('--concurrency', type=int, default=2, help='Number of concurrent threads')
args = parser.parse_args()
# 配置请求参数
url = 'http://mq.aidigifi.meidaojia.com/hairColor/v2'
# url = 'https://692139771842565-http-8801.northwest1.gpugeek.com:8443/hairColor/v2'
headers = {'Content-Type': 'application/json'}
data = {
"img": "data:image/jpeg;base64,/9j/4AAQSkZJ....Euy+sn/oZrKn8CPlK2qR/9k=",
"userId": "18701620166",
"rgb":[77,99,0],
"ratio": 0.9,
"output_format": "base64"
}
print(f"Starting test with QPS={args.qps}, duration={args.duration}s, concurrency={args.concurrency}")
run_test(url, headers, data, args.qps, args.duration, args.concurrency)
+203
View File
@@ -0,0 +1,203 @@
import requests
import time
import base64
import random
import string
from concurrent.futures import ThreadPoolExecutor, as_completed
import argparse
from threading import Lock
# 全局计数器
class RequestCounter:
def __init__(self):
self.sent = 0
self.in_progress = 0
self.completed = 0
self.success = 0
self.failed = 0
self.lock = Lock()
def increment_sent(self):
with self.lock:
self.sent += 1
def increment_in_progress(self):
with self.lock:
self.in_progress += 1
def decrement_in_progress(self):
with self.lock:
self.in_progress -= 1
def increment_completed(self):
with self.lock:
self.completed += 1
def increment_success(self):
with self.lock:
self.success += 1
def increment_failed(self):
with self.lock:
self.failed += 1
def get_stats(self):
with self.lock:
return {
'sent': self.sent,
'in_progress': self.in_progress,
'completed': self.completed,
'success': self.success,
'failed': self.failed
}
counter = RequestCounter()
def generate_random_task_id(length=19):
"""生成随机task_id"""
digits = string.digits
return ''.join(random.choice(digits) for _ in range(length))
def send_request(url, headers, data):
"""发送单个请求"""
try:
# 更新计数器
counter.increment_sent()
counter.increment_in_progress()
# 每次请求生成新的随机task_id
data["task_id"] = generate_random_task_id()
response = requests.post(url, headers=headers, json=data)
# 根据响应状态更新计数器
if response.status_code == 200:
counter.increment_success()
else:
counter.increment_failed()
return response.status_code, response.text
except Exception as e:
counter.increment_failed()
return None, str(e)
finally:
counter.decrement_in_progress()
counter.increment_completed()
def print_stats():
"""打印实时统计信息"""
while True:
stats = counter.get_stats()
print(f"\rStats - Sent: {stats['sent']}, In Progress: {stats['in_progress']}, "
f"Completed: {stats['completed']} (Success: {stats['success']}, Failed: {stats['failed']})",
end="", flush=True)
time.sleep(10)
def worker(url, headers, data, qps, duration):
"""工作线程函数,控制QPS"""
requests_count = 0
start_time = time.time()
end_time = start_time + duration
while time.time() < end_time:
request_start = time.time()
status_code, response_text = send_request(url, headers, data)
requests_count += 1
# 控制QPS
elapsed = time.time() - request_start
sleep_time = max(0, (1.0 / qps) - elapsed)
if sleep_time > 0:
time.sleep(sleep_time)
return requests_count
def run_test(url, headers, data, qps, duration, concurrency):
"""运行测试"""
total_requests = 0
start_time = time.time()
# 启动统计信息打印线程
import threading
stats_thread = threading.Thread(target=print_stats, daemon=True)
stats_thread.start()
with ThreadPoolExecutor(max_workers=concurrency) as executor:
futures = [executor.submit(worker, url, headers, data, qps, duration)
for _ in range(concurrency)]
for future in as_completed(futures):
total_requests += future.result()
end_time = time.time()
actual_duration = end_time - start_time
actual_qps = total_requests / actual_duration
# 获取最终统计
stats = counter.get_stats()
print("\n\nTest Summary:")
print(f"Total requests: {total_requests}")
print(f" Success: {stats['success']}")
print(f" Failed: {stats['failed']}")
print(f"Success rate: {(stats['success']/total_requests)*100:.2f}%")
print(f"Test duration: {actual_duration:.2f} seconds")
print(f"Actual QPS: {actual_qps:.2f}")
print(f"Target QPS: {qps}")
print(f"Concurrency: {concurrency}")
def image_to_base64(file_path, mime_type=None):
"""
将图片文件转换为带Base64前缀的Data URI字符串
参数:
file_path (str): 图片文件路径
mime_type (str): 可选,指定MIME类型。如果为None,则根据文件扩展名自动判断
返回:
str: 带Base64前缀的Data URI字符串
"""
# 如果没有指定MIME类型,根据文件扩展名推断
if mime_type is None:
extension = file_path.split('.')[-1].lower()
mime_types = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'webp': 'image/webp',
'bmp': 'image/bmp'
}
mime_type = mime_types.get(extension, 'application/octet-stream')
# 读取文件内容并编码为Base64
with open(file_path, 'rb') as image_file:
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
# 组合成Data URI格式
return f"data:{mime_type};base64,{encoded_string}"
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='API Load Test Tool')
parser.add_argument('--qps', type=float, default=1, help='Requests per second (per thread)')
parser.add_argument('--duration', type=float, default=10, help='Test duration in seconds')
parser.add_argument('--concurrency', type=int, default=2, help='Number of concurrent threads')
args = parser.parse_args()
# 配置请求参数
url = 'http://mq.aidigifi.meidaojia.com/api/swapHair/v1'
headers = {'Content-Type': 'application/json'}
data = {
"hair_id": "1907651680352395265",
"task_id": "1907651680352395265", # 会被每次请求覆盖
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/user2_1_%E5%89%AF%E6%9C%AC.JPG",
"is_hr": "false",
"output_format": "base64"
}
img64_str = image_to_base64("aaa.jpg")
data['img'] = img64_str
print(f"Starting test with QPS={args.qps}, duration={args.duration}s, concurrency={args.concurrency}")
run_test(url, headers, data, args.qps, args.duration, args.concurrency)
Executable
+228
View File
@@ -0,0 +1,228 @@
import time
import logging
import redis
import uuid
import json
from flask import Flask, request, jsonify
from datetime import datetime, timedelta
from config import QUEUE_NAME, DEFAULT_TIMEOUT, KEY_QUEUE_LOCK_NAME, acquire_lock
# 创建 logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO) # 设置 logger 的级别
# 获取当前日期和时间
now = datetime.now()
# 格式化为字符串(例如:2023-10-25 14:30:45
date_time_str = now.strftime("%Y-%m-%d_%H:%M:%S")
# 创建文件 handler
file_handler = logging.FileHandler(f'/var/log/meidaojia/app_{date_time_str}.log')
file_handler.setLevel(logging.INFO)
file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)
# 创建控制台 handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(console_formatter)
# 添加 handlers 到 logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)
app = Flask(__name__)
# 配置
from config import REDIS_HOST
from config import REDIS_PORT
from config import REDIS_DB
# 创建 Redis 连接池
redis_pool = redis.ConnectionPool(
host=REDIS_HOST,
port=REDIS_PORT,
db=REDIS_DB,
max_connections=2000 # 根据实际情况调整
)
def get_redis_conn():
"""获取 Redis 连接"""
return redis.Redis(connection_pool=redis_pool)
get_redis_conn().set(QUEUE_NAME, "[]")
def pushStr2Queue(key, data_str):
redis_conn = get_redis_conn()
lock = acquire_lock(redis_conn, KEY_QUEUE_LOCK_NAME)
if lock:
try:
redis_conn.set(key, data_str, ex=60)
key_queue_str = redis_conn.get(QUEUE_NAME).decode("utf-8")
key_queue = json.loads(key_queue_str)
key_queue.append(key)
logger.info(f"pushStr2Queue queue len:{key_queue}")
key_queue_str = json.dumps(key_queue)
redis_conn.set(QUEUE_NAME, key_queue_str)
finally:
lock.release()
else:
logger.error(f"pushStr2Queue get lock error {data_str}")
@app.route('/hairColor/v2', methods=['POST'])
def api_hairColor_v2():
# 获取请求参数
data = request.get_json()
if not data or 'img' not in data:
return jsonify({'msg': 'Missing img parameter', "state":-1, "data":"" }), 400
if not data or 'rgb' not in data:
return jsonify({'msg': 'Missing "rgb": parameter', "state":-1, "data":""}), 400
if not data or 'ratio' not in data:
return jsonify({'msg': 'Missing "ratio": parameter', "state":-1, "data":""}), 400
if not data or 'output_format' not in data:
return jsonify({'msg': 'Missing "output_format": parameter', "state":-1, "data":""}), 400
key = str(uuid.uuid4())
redis_conn = get_redis_conn()
# if len(data['img']) > 256:
# redis_conn.set(f"img_{key}", data['img'], ex=60)
# data['img'] = "base64"
task_data = {}
task_data['key'] = key
task_data['api'] = "/hairColor/v2"
task_data['request'] = data
logger.info(f"request hairColor/v2 img:{data['img'][:64]}, rgb:{data['rgb']}, ratio:{data['ratio']} output_format:{data['output_format']}")
task_data_str = json.dumps(task_data)
pushStr2Queue(key, task_data_str)
timeout = DEFAULT_TIMEOUT
start_time = datetime.now()
result_key = f"result_{key}"
result_status_key = f"result_status_code_{key}"
while (datetime.now() - start_time).seconds < timeout:
if redis_conn.exists(result_key):
result_str = redis_conn.get(result_key)
return result_str, int(redis_conn.get(result_status_key)), {'Content-Type': 'application/json'}
time.sleep(0.1)
logger.error(f"request hairColor time out ")
return jsonify({
'msg': f'Timeout after {timeout} seconds, http time out'
, "state":-1, "data":""
}), 408
@app.route('/api/swapHair/v1', methods=['POST'])
def api_swapHair_v1():
if not request.is_json:
return jsonify({"error": "Request must be JSON"}), 400
# 获取请求参数
data = request.get_json()
if not data or 'hair_id' not in data:
return jsonify({'msg': 'Missing hair_id parameter', "state":-1, "data":"" }), 400
if not data or 'task_id' not in data:
return jsonify({'msg': 'Missing task_id parameter', "state":-1, "data":""}), 400
if not data or 'user_img_path' not in data:
return jsonify({'msg': 'Missing user_img_path parameter', "state":-1, "data":""}), 400
if not data or 'is_hr' not in data:
return jsonify({'msg': 'Missing is_hr parameter', "state":-1, "data":""}), 400
if not data or 'output_format' not in data:
return jsonify({'msg': 'Missing output_format parameter', "state":-1, "data":""}), 400
redis_conn = get_redis_conn()
key = str(uuid.uuid4())
# if len(data['user_img_path']) > 256:
# redis_conn.set(f"img_{key}", data['user_img_path'], ex=60)
# data['user_img_path'] = "base64"
task_data = {}
task_data['key'] = key
task_data['api'] = "/api/swapHair/v1"
task_data['request'] = data
logger.info(f"request /api/swapHair/v1 user_img_path:{data['user_img_path'][:64]} hair_id:{data['hair_id']}")
task_data_str = json.dumps(task_data)
pushStr2Queue(key, task_data_str)
timeout = DEFAULT_TIMEOUT
start_time = datetime.now()
result_key = f"result_{key}"
result_status_key = f"result_status_code_{key}"
while (datetime.now() - start_time).seconds < timeout:
if redis_conn.exists(result_key):
result_str = redis_conn.get(result_key)
return result_str, int(redis_conn.get(result_status_key)), {'Content-Type': 'application/json'}
time.sleep(0.1)
logger.error(f"request /api/swapHair/v1 time out")
return jsonify({
'msg': f'Timeout after {timeout} seconds, http time out'
, "state":-1, "data":""
}), 408
# @app.route('/api/uploadHair/v1', methods=['POST'])
# def api_uploadHair_v1():
# # 获取请求参数
# data = request.get_json()
# if not data or 'img_lists' not in data:
# return jsonify({'msg': 'Missing img_lists parameter', "state":-1, "data":"" }), 400
# if not data or '"hair_id": ' not in data:
# return jsonify({'msg': 'Missing "hair_id": parameter', "state":-1, "data":""}), 400
# if not data or 'output_format' not in data:
# logger.warning(f"api_swapHair_v1 no output_format task_id:{data['task_id']}")
# key = str(uuid.uuid4())
# redis_conn = get_redis_conn()
# task_data = {}
# task_data['key'] = key
# task_data['api'] = "/api/uploadHair/v1"
# task_data['request'] = data
# logger.info(f"request api_uploadHair_v1 task_data: {json.dumps(task_data)}")
# task_data_str = json.dumps(task_data)
# pushStr2Queue(task_data_str)
# timeout = DEFAULT_TIMEOUT
# start_time = datetime.now()
# result_key = f"result_{key}"
# while (datetime.now() - start_time).seconds < timeout:
# if redis_conn.exists(result_key):
# result_str = redis_conn.get(result_key)
# result = json.loads(result_str)
# if result['state'] == 0:
# return jsonify(result), 200
# else:
# return jsonify(result), 500
# time.sleep(0.1)
# logger.error(f"request api_uploadHair_v1 time out")
# return jsonify({
# 'msg': f'Timeout after {timeout} seconds, http time out'
# , "state":-1, "data":""
# }), 408
if __name__ == '__main__':
# 启动Flask应用,启用多线程处理
app.run(
host='0.0.0.0',
port=80,
threaded=True, # 启用多线程处理并发请求
debug=False # 生产环境应设置为False
)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Executable
+69
View File
@@ -0,0 +1,69 @@
#!/bin/bash
# 定义连接参数
HOST="northwest1.gpugeek.com"
PORT="55139"
USER="root"
PASSWORD="uaUMSzmAHyka2ZcBvQHZ5My9PUNRP78T"
TARGET_DIR="/root/project/hair_service_sd"
COMMAND="if ! pgrep -f 'run_copy_cost_colorb64.py'; then bash start_services.sh; fi"
# 使用expect工具自动化交互过程
/usr/bin/expect <<EOF
set timeout 30
# 启动ssh连接
spawn ssh -p $PORT $USER@$HOST
expect {
# 处理首次连接的确认提示
"you sure you want to continue connecting" {
send "yes\r"
exp_continue
}
# 匹配各种可能的密码提示形式
"*password:" {
send "$PASSWORD\r"
}
"Password:" {
send "$PASSWORD\r"
}
"*assword:" {
send "$PASSWORD\r"
}
}
# 等待登录成功提示
expect {
"*\$ " {}
"*# " {}
}
# 进入目标目录
send "cd $TARGET_DIR\r"
expect {
"*\$ " {}
"*# " {}
}
# 执行检查进程并启动服务的命令
send "$COMMAND\r"
# 等待命令执行完成
expect {
"*\$ " {
send "exit\r"
}
"*# " {
send "exit\r"
}
timeout {
send "exit\r"
}
}
# 等待ssh会话结束
expect eof
EOF
echo "自动化任务已完成"
Executable
+68
View File
@@ -0,0 +1,68 @@
import time
from redis.lock import Lock
import logging
QUEUE_NAME = 'task_queue'
# 配置
REDIS_HOST = 'r-2zevknj33pkqrrbzjp.redis.rds.aliyuncs.com'
REDIS_PORT = 6379
REDIS_DB = 0
DEFAULT_TIMEOUT = (60+10)
GPU_SERVER_TIME_OUT = 60
GPU_SERVER_LIST = 'gpu_server_list_key'
SERVER_LOG_ = "server_log"
SERVER_LOG_EVENT_LEN = 20
KEY_QUEUE_LOCK_NAME = 'KEY_QUEUE_LOCK_NAME'
SERVER_LIST_LOCK_NAME = 'SERVER_LIST_LOCK_NAME'
SERVER_LIST_LOG_LOCK_NAME = 'SERVER_LIST_LOG_LOCK_NAME'
# 创建 logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO) # 设置 logger 的级别
# 创建文件 handler
file_handler = logging.FileHandler('/var/log/meidaojia/config.log')
file_handler.setLevel(logging.INFO)
file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)
# 创建控制台 handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(console_formatter)
# 添加 handlers 到 logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)
def acquire_lock(redis_client, lock_name):
lock = Lock(
redis_client,
name=lock_name,
timeout=30,
blocking_timeout=10
)
acquired = lock.acquire()
if acquired:
return lock
return None
# def acquire_lock(r, acquire_timeout=10):
# """获取分布式锁"""
# end = time.time() + acquire_timeout
# while time.time() < end:
# # 使用 SETNX 尝试获取锁
# if r.setnx(TASK_REDIS_LOCK_NAME, 1):
# # 设置过期时间,防止死锁
# r.expire(TASK_REDIS_LOCK_NAME, 10)
# return True
# time.sleep(0.1)
# return False
# # return True
# def release_lock(r):
# """释放分布式锁"""
# r.delete(TASK_REDIS_LOCK_NAME)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

+84
View File
@@ -0,0 +1,84 @@
import time
import logging
import redis
import uuid
import json
from flask import Flask, request, jsonify, send_from_directory
from datetime import datetime, timedelta
from config import QUEUE_NAME, DEFAULT_TIMEOUT
from flask_cors import CORS
GPU_SERVER_LIST = 'gpu_server_list_key'
# 创建 logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO) # 设置 logger 的级别
# 创建文件 handler
file_handler = logging.FileHandler('/var/log/meidaojia/app.log')
file_handler.setLevel(logging.INFO)
file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)
# 创建控制台 handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(console_formatter)
# 添加 handlers 到 logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)
monitor = Flask(__name__)
CORS(monitor)
# 配置
from config import REDIS_HOST
from config import REDIS_PORT
from config import REDIS_DB
from config import SERVER_LOG_
# 创建 Redis 连接池
redis_pool = redis.ConnectionPool(
host=REDIS_HOST,
port=REDIS_PORT,
db=REDIS_DB,
max_connections=2000 # 根据实际情况调整
)
def get_redis_conn():
"""获取 Redis 连接"""
return redis.Redis(connection_pool=redis_pool)
@monitor.route('/get_server_state', methods=['GET', 'POST'])
def get_server_state():
monitor_data = []
redis_conn = get_redis_conn()
server_list_str = redis_conn.get(GPU_SERVER_LIST).decode("utf-8")
server_list = json.loads(server_list_str)
for name in server_list:
server_str = redis_conn.get(name).decode("utf-8")
server = json.loads(server_str)
server_log_name = f"{SERVER_LOG_}{name}"
server_log_str = redis_conn.get(server_log_name).decode("utf-8")
server_log = json.loads(server_log_str)
server_log['busy'] = not server['can_use']
monitor_data.append(server_log)
json_str = json.dumps(monitor_data)
print(len(json_str))
return json_str, 200
@monitor.route('/')
def serve_index():
return send_from_directory('static', 'index.html')
if __name__ == '__main__':
# 启动Flask应用,启用多线程处理
monitor.run(
host='0.0.0.0',
port=5001,
threaded=True, # 启用多线程处理并发请求
debug=False # 生产环境应设置为False
)
+85
View File
@@ -0,0 +1,85 @@
#!/bin/bash
# 定义目标目录和检查的进程名
TARGET_DIR="/root/project/hair_service_sd"
PROCESS_NAME="run_copy_cost_colorb64.py"
START_COMMAND="bash start_services.sh"
# 读取servers.csv文件并逐行处理
while IFS=, read -r name ssh_cmd password _; do
# 去除可能存在的空格和引号
name=$(echo "$name" | xargs)
ssh_cmd=$(echo "$ssh_cmd" | xargs)
password=$(echo "$password" | xargs)
echo "..................................正在处理服务器: $name ........................................"
# 从ssh命令中提取端口和主机
port=$(echo "$ssh_cmd" | grep -oP '\-p\s*\K\d+')
host=$(echo "$ssh_cmd" | grep -oP 'root@\K[^\s]+')
# 使用expect工具自动化交互过程
/usr/bin/expect <<EOF
set timeout 30
# 启动ssh连接
spawn $ssh_cmd
expect {
# 处理首次连接的确认提示
"you sure you want to continue connecting" {
send "yes\r"
exp_continue
}
# 匹配各种可能的密码提示形式
"*password:" {
send "$password\r"
}
"Password:" {
send "$password\r"
}
"*assword:" {
send "$password\r"
}
}
# 等待登录成功提示
expect {
"*\$ " {}
"*# " {}
}
# 进入目标目录
send "cd $TARGET_DIR\r"
expect {
"*\$ " {}
"*# " {}
}
# 检查进程并决定是否启动服务
send "if ! pgrep -f '$PROCESS_NAME'; then echo '启动服务...'; $START_COMMAND; else echo '进程已在运行,跳过启动'; fi\r"
# 等待命令执行完成
expect {
"*\$ " {
send "exit\r"
}
"*# " {
send "exit\r"
}
timeout {
send "exit\r"
}
}
# 等待ssh会话结束
expect eof
EOF
echo "已完成服务器: $name"
# 添加延时
echo "--------------------------------------------- 等待 ----------------------------------------------"
sleep 5
done < servers_test.csv
echo "所有服务器处理完成"
+287309
View File
File diff suppressed because one or more lines are too long
+126
View File
@@ -0,0 +1,126 @@
import threading
import requests
import json
import base64
url = 'http://mq.aidigifi.meidaojia.com/api/swapHair/v1'
# url = 'https://692139771842565-http-8801.northwest1.gpugeek.com:8443/api/swapHair/v1'
headers = {'Content-Type': 'application/json'}
data = {
"hair_id": "1907651680352395265",
"task_id": "1907651680352395265",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/user2_1_%E5%89%AF%E6%9C%AC.JPG",
# "user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_8.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data = []
more_data.append(data)
data1 = {
"hair_id": "1907651680352395265",
"task_id": "2345623451234",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_8.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data1)
data2 = {
"hair_id": "1907651680352395265",
"task_id": "3145645324",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_7.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data2)
data3 = {
"hair_id": "1907651680352395265",
"task_id": "456412345",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_6.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data3)
data4 = {
"hair_id": "1907651680352395265",
"task_id": "45623445",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_5.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data4)
data5 = {
"hair_id": "1907651680352395265",
"task_id": "2345867435",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_4.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data5)
data6 = {
"hair_id": "1907651680352395265",
"task_id": "86747535",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_3.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data6)
def image_to_base64(image_path):
"""读取图片文件并转换为Base64编码"""
with open(image_path, "rb") as image_file:
# 读取图片二进制数据
image_data = image_file.read()
# 转换为Base64编码的bytes
base64_bytes = base64.b64encode(image_data)
# 转换为字符串(如果需要)
base64_string = base64_bytes.decode('utf-8')
return base64_string
# img64_str = image_to_base64("test_image.jfif")
# data['user_img_path'] = img64_str
# 存储所有请求的响应
responses = []
# 线程锁,防止多线程写入冲突
lock = threading.Lock()
send_index = 0
def send_request():
try:
global send_index
send_data = more_data[send_index]
send_index = send_index + 1
response = requests.post(url, headers=headers, json=send_data)
with lock: # 加锁,防止多线程同时修改 responses
responses.append(response.json())
except Exception as e:
with lock:
responses.append({"error": str(e)})
# 创建并启动多个线程
threads = []
num_requests = 7 # 并发请求数量
for _ in range(num_requests):
t = threading.Thread(target=send_request)
t.start()
threads.append(t)
# 等待所有线程完成
for t in threads:
t.join()
# 打印所有响应
for i, resp in enumerate(responses, 1):
print(f"请求 {i} 结果:", resp)
+137
View File
@@ -0,0 +1,137 @@
import requests
import random
import time
import concurrent.futures
import base64
from collections import defaultdict
url = 'http://172.17.110.92/api/swapHair/v1'
# url = 'https://699520645652485-http-8801.northwest1.gpugeek.com:8443/api/swapHair/v1'
headers = {'Content-Type': 'application/json'}
data = {
"hair_id": "1907651680352395265",
"task_id": "1907651680352395265",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/user2_1_%E5%89%AF%E6%9C%AC.JPG",
"is_hr": "false",
"output_format": "base64"
}
def image_to_base64(file_path, mime_type=None):
"""
将图片文件转换为带Base64前缀的Data URI字符串
参数:
file_path (str): 图片文件路径
mime_type (str): 可选,指定MIME类型。如果为None,则根据文件扩展名自动判断
返回:
str: 带Base64前缀的Data URI字符串
"""
# 如果没有指定MIME类型,根据文件扩展名推断
if mime_type is None:
extension = file_path.split('.')[-1].lower()
mime_types = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'webp': 'image/webp',
'bmp': 'image/bmp'
}
mime_type = mime_types.get(extension, 'application/octet-stream')
# 读取文件内容并编码为Base64
with open(file_path, 'rb') as image_file:
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
# 组合成Data URI格式
return f"data:{mime_type};base64,{encoded_string}"
img64_str = image_to_base64("aaa.jpg")
data['user_img_path'] = img64_str
class RequestStats:
def __init__(self):
self.total_requests = 0
self.successful_requests = 0
self.failed_requests = 0
self.response_times = []
self.start_time = None
self.end_time = None
self.request_timestamps = defaultdict(int)
def record_success(self, response_time):
self.successful_requests += 1
self.response_times.append(response_time)
self.request_timestamps[int(time.time())] += 1
def record_failure(self):
self.failed_requests += 1
def calculate_qps(self):
if not self.request_timestamps:
return 0
timestamps = sorted(self.request_timestamps.keys())
if len(timestamps) < 2:
return self.successful_requests
total_time = timestamps[-1] - timestamps[0] + 1
return self.successful_requests / total_time
def send_request(stats):
try:
data['task_id'] = str(int(random.uniform(0, 10000000)))
start_time = time.time()
response = requests.post(url, headers=headers, json=data)
end_time = time.time()
response_time = end_time - start_time
if response.status_code == 200:
stats.record_success(response_time)
print(f"请求完成,响应时间: {response_time:.3f}")
return response_time
else:
print(f"请求失败,状态码: {response.status_code}")
stats.record_failure()
return None
except Exception as e:
print(f"请求发生错误: {e}")
stats.record_failure()
return None
def main():
total_requests = 100 #请求总数
concurrency = 25 # 并发数
stats = RequestStats()
stats.start_time = time.time()
stats.total_requests = total_requests
print(f"开始测试,总请求数: {total_requests},并发数: {concurrency}")
with concurrent.futures.ThreadPoolExecutor(max_workers=concurrency) as executor:
futures = [executor.submit(send_request, stats) for _ in range(total_requests)]
concurrent.futures.wait(futures)
stats.end_time = time.time()
print("\n统计结果:")
print(f"总请求数: {stats.total_requests}")
print(f"成功请求数: {stats.successful_requests}")
print(f"失败请求数: {stats.failed_requests}")
if stats.successful_requests > 0:
total_time = stats.end_time - stats.start_time
avg_response_time = sum(stats.response_times) / len(stats.response_times)
qps = stats.calculate_qps()
print(f"测试总时间: {total_time:.3f}")
print(f"平均响应时间: {avg_response_time:.3f}")
print(f"最大响应时间: {max(stats.response_times):.3f}")
print(f"最小响应时间: {min(stats.response_times):.3f}")
print(f"QPS (每秒查询率): {qps:.2f}")
else:
print("所有请求都失败了,无法计算统计信息")
if __name__ == "__main__":
main()
+93
View File
@@ -0,0 +1,93 @@
import requests
import random
import time
import base64
# url = 'https://693565319933957-http-8801.northwest1.gpugeek.com:8443/api/swapHair/v1'
url = 'http://172.17.110.92/api/swapHair/v1'
headers = {'Content-Type': 'application/json'}
data = {
"hair_id": "1907651680352395265",
"task_id": "1907651680352395265",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/user2_1_%E5%89%AF%E6%9C%AC.JPG",
"is_hr": "false",
"output_format": "base64"
}
def image_to_base64(file_path, mime_type=None):
"""
将图片文件转换为带Base64前缀的Data URI字符串
参数:
file_path (str): 图片文件路径
mime_type (str): 可选,指定MIME类型。如果为None,则根据文件扩展名自动判断
返回:
str: 带Base64前缀的Data URI字符串
"""
# 如果没有指定MIME类型,根据文件扩展名推断
if mime_type is None:
extension = file_path.split('.')[-1].lower()
mime_types = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'webp': 'image/webp',
'bmp': 'image/bmp'
}
mime_type = mime_types.get(extension, 'application/octet-stream')
# 读取文件内容并编码为Base64
with open(file_path, 'rb') as image_file:
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
# 组合成Data URI格式
return f"data:{mime_type};base64,{encoded_string}"
# img64_str = image_to_base64("aaa.jpg")
# data['user_img_path'] = img64_str
def send_request():
try:
data['task_id'] = str(int(random.uniform(0, 10000000)))
start_time = time.time()
response = requests.post(url, headers=headers, json=data)
end_time = time.time()
response_time = end_time - start_time
return response_time
except Exception as e:
print(f"请求发生错误: {e}")
return None
def main():
total_requests = 30
response_times = []
for i in range(total_requests):
print(f"正在发送第 {i+1} 个请求...")
rt = send_request()
if rt is not None:
response_times.append(rt)
print(f"{i+1} 个请求完成,响应时间: {rt:.3f}")
else:
print(f"{i+1} 个请求失败")
# 单路串行,不需要延迟,但如果需要可以添加
# time.sleep(1)
if response_times:
avg_response_time = sum(response_times) / len(response_times)
print("\n统计结果:")
print(f"总请求数: {total_requests}")
print(f"成功请求数: {len(response_times)}")
print(f"失败请求数: {total_requests - len(response_times)}")
print(f"平均响应时间: {avg_response_time:.3f}")
print(f"最大响应时间: {max(response_times):.3f}")
print(f"最小响应时间: {min(response_times):.3f}")
else:
print("所有请求都失败了,无法计算统计信息")
if __name__ == "__main__":
main()
+45
View File
@@ -0,0 +1,45 @@
693573774479365,ssh -p 51884 root@northwest1.gpugeek.com,VZxzyqDNbucAEdm5nZQFFQ67v6X7vQcK,,,,,,,,,,,,,,,,,
693573706682373,ssh -p 46901 root@northwest1.gpugeek.com,ZDdguaaQsNRb8qmMaTu7q95eu2vG5Qc3,,,,,,,,,,,,,,,,,
693570752139269,ssh -p 44853 root@northwest1.gpugeek.com,Wavu5Cnewm65p5ABXqXuSMFfF2c77ZuG,,,,,,,,,,,,,,,,,
693570664882181,ssh -p 49563 root@northwest1.gpugeek.com,4mz3pKZdVff9X3hFzcfVY4h4VkzSTpcb,,,,,,,,,,,,,,,,,
693565557084165,ssh -p 57192 root@northwest1.gpugeek.com,5ztvzCwtEaBG8CYaCbyxZVXmzmUxuD7b,,,,,,,,,,,,,,,,,
693565452951557,ssh -p 40188 root@northwest1.gpugeek.com,wWAsrQ5yGvahpF2zHXVFrRterMeYWuN3,,,,,,,,,,,,,,,,,
693565319933957,ssh -p 55139 root@northwest1.gpugeek.com,uaUMSzmAHyka2ZcBvQHZ5My9PUNRP78T,,,,,,,,,,,,,,,,,
693597199511557,ssh -p 54874 root@northwest1.gpugeek.com,zN2SB3DZxNpnvpQzC7PBHun3CNh2ydGV,,,,,,,,,,,,,,,,,
693597082546181,ssh -p 40885 root@northwest1.gpugeek.com,HdYQ3QtzKdRgNQZaXWDKYSqWPkE2w53h,,,,,,,,,,,,,,,,,
693596619751429,ssh -p 44684 root@northwest1.gpugeek.com,tFF9WKSe5XRSDR8PWcNweF2cZ6ZdYpaW,,,,,,,,,,,,,,,,,
693596213850117,ssh -p 44307 root@northwest1.gpugeek.com,YSMNFffKTMY37Ze83UfmSr5XF65RP7dT,,,,,,,,,,,,,,,,,
693595248599045,ssh -p 54178 root@northwest1.gpugeek.com,akZ3qyRtqUum553sMNXEVNY3qzxMA3sV,,,,,,,,,,,,,,,,,
693595168616453,ssh -p 59719 root@northwest1.gpugeek.com,XqQzT8qtfUWFQP8E3PPMb7FMPRUYBehS,,,,,,,,,,,,,,,,,
693594319818757,ssh -p 56408 root@northwest1.gpugeek.com,46SMhqxfqkyvVu6qKyr9UW7GfTnK5GCp,,,,,,,,,,,,,,,,,
693594143621125,ssh -p 47103 root@northwest1.gpugeek.com,ZvxQPDvKQeVdRMnm3VTMD5vfdg8VpqCE,,,,,,,,,,,,,,,,,
693579634159621,ssh -p 45208 root@northwest1.gpugeek.com,467nsPAHNgS5fvzNNvzCaebzs3Z4DB8s,,,,,,,,,,,,,,,,,
693579093471237,ssh -p 40600 root@northwest1.gpugeek.com,mRabksFkwYV8NWx2NmAufTevNrFUeVBw,,,,,,,,,,,,,,,,,
693578025705477,ssh -p 58639 root@northwest1.gpugeek.com,WSXHUssQSXTCWSDuVCdcvG8DQyFPU2er,,,,,,,,,,,,,,,,,
693577880313861,ssh -p 59280 root@northwest1.gpugeek.com,CywKAemA3b2z6VFG5TzgYzAKy9bnGvNM,,,,,,,,,,,,,,,,,
693577753862149,ssh -p 58493 root@northwest1.gpugeek.com,yDxpPQbyxEuNbPQPapUV72vK3s9CRu3F,,,,,,,,,,,,,,,,,
693577626185733,ssh -p 59071 root@northwest1.gpugeek.com,UFNpw9B48ahAU8RrmcGVmSENazGCSSm5,,,,,,,,,,,,,,,,,
693577527365637,ssh -p 57796 root@northwest1.gpugeek.com,2sZhwEmS7RdgQeuCpSnQqgpfR4BQnhvd,,,,,,,,,,,,,,,,,
693575032029189,ssh -p 44408 root@northwest1.gpugeek.com,yM6wR5g8VGXbb4XfvA9ghYWVk9maTht8,,,,,,,,,,,,,,,,,
693574955724805,ssh -p 43820 root@northwest1.gpugeek.com,csQbqbCAkdwTZZSVdmxMq3svKhdpEAtF,,,,,,,,,,,,,,,,,
699531043577861,ssh -p 53934 root@northwest1.gpugeek.com,WD3WcCNTuz5VxmbAdmWFSYZVfhEXf8NK,,,,,,,,,,,,,,,,,
699531676672005,ssh -p 53976 root@northwest1.gpugeek.com,p37A8HRPcAU8SP4ssha4VBS9phgrykF9,,,,,,,,,,,,,,,,,
699552291840005,ssh -p 52102 root@northwest1.gpugeek.com,S5G4zWPQ8WMZ89Tk3afTgQAAfpqXSuM3,,,,,,,,,,,,,,,,,
699552392802309,ssh -p 48055 root@northwest1.gpugeek.com,afXHTcaPMqbWnqCfWv6tzSFkBKFFf7vS,,,,,,,,,,,,,,,,,
699552766849029,ssh -p 51140 root@northwest1.gpugeek.com,9YFyTAruNZwr8K44ZXp9ET5aG5c4DTa4,,,,,,,,,,,,,,,,,
699554474184709,ssh -p 40280 root@northwest1.gpugeek.com,sbSfgFa8V4bhBDpbqtccCvXkHF6gSfAn,,,,,,,,,,,,,,,,,
699554413039621,ssh -p 49817 root@northwest1.gpugeek.com,dRB7CRtcdgGwFe4nbxaEh8D7nXMmuYDX,,,,,,,,,,,,,,,,,
699554332229637,ssh -p 40625 root@northwest1.gpugeek.com,HQz2nC7tbTTxGZhC7YN4YFCg5WfcMDD2,,,,,,,,,,,,,,,,,
699554276352005,ssh -p 43763 root@northwest1.gpugeek.com,bYFCDzzN2MKtWHF4qvyK9PtG9xqurA5z,,,,,,,,,,,,,,,,,
699554118586373,ssh -p 49257 root@northwest1.gpugeek.com,GNMmwCpB3NcGryqznhB2acDmtgFPBYXs,,,,,,,,,,,,,,,,,
699554021834757,ssh -p 47192 root@northwest1.gpugeek.com,wFxwYDD6KapUSYgAanEcYzDRusQCvPBT,,,,,,,,,,,,,,,,,
699553939038213,ssh -p 47118 root@northwest1.gpugeek.com,fXU3wEvUE4ckCfSXwkVW7dW8xG3VqRh2,,,,,,,,,,,,,,,,,
699553843216389,ssh -p 40918 root@northwest1.gpugeek.com,3EYpyAx7s5gbHQEXWNZ6Td3QdMCZU8Mu,,,,,,,,,,,,,,,,,
699553742393349,ssh -p 57046 root@northwest1.gpugeek.com,a6KeZhBPMWecTtvsP3kbgcV7MZseyerY,,,,,,,,,,,,,,,,,
699553618411525,ssh -p 52041 root@northwest1.gpugeek.com,tev3z7knqaaSP2T74QtYqtfb9HdNy9WW,,,,,,,,,,,,,,,,,
699553554767877,ssh -p 42691 root@northwest1.gpugeek.com,r3bFQs4uNFyUCDN99f7deH2pXqdmRYEV,,,,,,,,,,,,,,,,,
699553450074117,ssh -p 42199 root@northwest1.gpugeek.com,hYgx3rbWpmYSFs6pEq8umZqcgAZyZa9p,,,,,,,,,,,,,,,,,
699553329291269,ssh -p 49696 root@northwest1.gpugeek.com,EVnYtmqBZTtdfzXPX39CukVugrX6HzEW,,,,,,,,,,,,,,,,,
699553242570757,ssh -p 57669 root@northwest1.gpugeek.com,FU9UmrBvMeCGvvxfDdX5fWCn4E9AtTBg,,,,,,,,,,,,,,,,,
699552977174533,ssh -p 41179 root@northwest1.gpugeek.com,NFx6ahHDQKpqFEBg4nrPMerureYX5dMV,,,,,,,,,,,,,,,,,
699552838074373,ssh -p 46220 root@northwest1.gpugeek.com,qZTCYqU4KpB9GbZfgxXAwcxwMcKM9eFz,,,,,,,,,,,,,,,,,
1 693573774479365 ssh -p 51884 root@northwest1.gpugeek.com VZxzyqDNbucAEdm5nZQFFQ67v6X7vQcK
2 693573706682373 ssh -p 46901 root@northwest1.gpugeek.com ZDdguaaQsNRb8qmMaTu7q95eu2vG5Qc3
3 693570752139269 ssh -p 44853 root@northwest1.gpugeek.com Wavu5Cnewm65p5ABXqXuSMFfF2c77ZuG
4 693570664882181 ssh -p 49563 root@northwest1.gpugeek.com 4mz3pKZdVff9X3hFzcfVY4h4VkzSTpcb
5 693565557084165 ssh -p 57192 root@northwest1.gpugeek.com 5ztvzCwtEaBG8CYaCbyxZVXmzmUxuD7b
6 693565452951557 ssh -p 40188 root@northwest1.gpugeek.com wWAsrQ5yGvahpF2zHXVFrRterMeYWuN3
7 693565319933957 ssh -p 55139 root@northwest1.gpugeek.com uaUMSzmAHyka2ZcBvQHZ5My9PUNRP78T
8 693597199511557 ssh -p 54874 root@northwest1.gpugeek.com zN2SB3DZxNpnvpQzC7PBHun3CNh2ydGV
9 693597082546181 ssh -p 40885 root@northwest1.gpugeek.com HdYQ3QtzKdRgNQZaXWDKYSqWPkE2w53h
10 693596619751429 ssh -p 44684 root@northwest1.gpugeek.com tFF9WKSe5XRSDR8PWcNweF2cZ6ZdYpaW
11 693596213850117 ssh -p 44307 root@northwest1.gpugeek.com YSMNFffKTMY37Ze83UfmSr5XF65RP7dT
12 693595248599045 ssh -p 54178 root@northwest1.gpugeek.com akZ3qyRtqUum553sMNXEVNY3qzxMA3sV
13 693595168616453 ssh -p 59719 root@northwest1.gpugeek.com XqQzT8qtfUWFQP8E3PPMb7FMPRUYBehS
14 693594319818757 ssh -p 56408 root@northwest1.gpugeek.com 46SMhqxfqkyvVu6qKyr9UW7GfTnK5GCp
15 693594143621125 ssh -p 47103 root@northwest1.gpugeek.com ZvxQPDvKQeVdRMnm3VTMD5vfdg8VpqCE
16 693579634159621 ssh -p 45208 root@northwest1.gpugeek.com 467nsPAHNgS5fvzNNvzCaebzs3Z4DB8s
17 693579093471237 ssh -p 40600 root@northwest1.gpugeek.com mRabksFkwYV8NWx2NmAufTevNrFUeVBw
18 693578025705477 ssh -p 58639 root@northwest1.gpugeek.com WSXHUssQSXTCWSDuVCdcvG8DQyFPU2er
19 693577880313861 ssh -p 59280 root@northwest1.gpugeek.com CywKAemA3b2z6VFG5TzgYzAKy9bnGvNM
20 693577753862149 ssh -p 58493 root@northwest1.gpugeek.com yDxpPQbyxEuNbPQPapUV72vK3s9CRu3F
21 693577626185733 ssh -p 59071 root@northwest1.gpugeek.com UFNpw9B48ahAU8RrmcGVmSENazGCSSm5
22 693577527365637 ssh -p 57796 root@northwest1.gpugeek.com 2sZhwEmS7RdgQeuCpSnQqgpfR4BQnhvd
23 693575032029189 ssh -p 44408 root@northwest1.gpugeek.com yM6wR5g8VGXbb4XfvA9ghYWVk9maTht8
24 693574955724805 ssh -p 43820 root@northwest1.gpugeek.com csQbqbCAkdwTZZSVdmxMq3svKhdpEAtF
25 699531043577861 ssh -p 53934 root@northwest1.gpugeek.com WD3WcCNTuz5VxmbAdmWFSYZVfhEXf8NK
26 699531676672005 ssh -p 53976 root@northwest1.gpugeek.com p37A8HRPcAU8SP4ssha4VBS9phgrykF9
27 699552291840005 ssh -p 52102 root@northwest1.gpugeek.com S5G4zWPQ8WMZ89Tk3afTgQAAfpqXSuM3
28 699552392802309 ssh -p 48055 root@northwest1.gpugeek.com afXHTcaPMqbWnqCfWv6tzSFkBKFFf7vS
29 699552766849029 ssh -p 51140 root@northwest1.gpugeek.com 9YFyTAruNZwr8K44ZXp9ET5aG5c4DTa4
30 699554474184709 ssh -p 40280 root@northwest1.gpugeek.com sbSfgFa8V4bhBDpbqtccCvXkHF6gSfAn
31 699554413039621 ssh -p 49817 root@northwest1.gpugeek.com dRB7CRtcdgGwFe4nbxaEh8D7nXMmuYDX
32 699554332229637 ssh -p 40625 root@northwest1.gpugeek.com HQz2nC7tbTTxGZhC7YN4YFCg5WfcMDD2
33 699554276352005 ssh -p 43763 root@northwest1.gpugeek.com bYFCDzzN2MKtWHF4qvyK9PtG9xqurA5z
34 699554118586373 ssh -p 49257 root@northwest1.gpugeek.com GNMmwCpB3NcGryqznhB2acDmtgFPBYXs
35 699554021834757 ssh -p 47192 root@northwest1.gpugeek.com wFxwYDD6KapUSYgAanEcYzDRusQCvPBT
36 699553939038213 ssh -p 47118 root@northwest1.gpugeek.com fXU3wEvUE4ckCfSXwkVW7dW8xG3VqRh2
37 699553843216389 ssh -p 40918 root@northwest1.gpugeek.com 3EYpyAx7s5gbHQEXWNZ6Td3QdMCZU8Mu
38 699553742393349 ssh -p 57046 root@northwest1.gpugeek.com a6KeZhBPMWecTtvsP3kbgcV7MZseyerY
39 699553618411525 ssh -p 52041 root@northwest1.gpugeek.com tev3z7knqaaSP2T74QtYqtfb9HdNy9WW
40 699553554767877 ssh -p 42691 root@northwest1.gpugeek.com r3bFQs4uNFyUCDN99f7deH2pXqdmRYEV
41 699553450074117 ssh -p 42199 root@northwest1.gpugeek.com hYgx3rbWpmYSFs6pEq8umZqcgAZyZa9p
42 699553329291269 ssh -p 49696 root@northwest1.gpugeek.com EVnYtmqBZTtdfzXPX39CukVugrX6HzEW
43 699553242570757 ssh -p 57669 root@northwest1.gpugeek.com FU9UmrBvMeCGvvxfDdX5fWCn4E9AtTBg
44 699552977174533 ssh -p 41179 root@northwest1.gpugeek.com NFx6ahHDQKpqFEBg4nrPMerureYX5dMV
45 699552838074373 ssh -p 46220 root@northwest1.gpugeek.com qZTCYqU4KpB9GbZfgxXAwcxwMcKM9eFz
+7
View File
@@ -0,0 +1,7 @@
699553450074117,ssh -p 42199 root@northwest1.gpugeek.com,hYgx3rbWpmYSFs6pEq8umZqcgAZyZa9p,,,,,,,,,,,,,,,,,
699553742393349,ssh -p 57046 root@northwest1.gpugeek.com,a6KeZhBPMWecTtvsP3kbgcV7MZseyerY,,,,,,,,,,,,,,,,,
699553618411525,ssh -p 52041 root@northwest1.gpugeek.com,tev3z7knqaaSP2T74QtYqtfb9HdNy9WW,,,,,,,,,,,,,,,,,
699553242570757,ssh -p 57669 root@northwest1.gpugeek.com,FU9UmrBvMeCGvvxfDdX5fWCn4E9AtTBg,,,,,,,,,,,,,,,,,
699553554767877,ssh -p 42691 root@northwest1.gpugeek.com,r3bFQs4uNFyUCDN99f7deH2pXqdmRYEV,,,,,,,,,,,,,,,,,
699552977174533,ssh -p 41179 root@northwest1.gpugeek.com,NFx6ahHDQKpqFEBg4nrPMerureYX5dMV,,,,,,,,,,,,,,,,,
699552838074373,ssh -p 46220 root@northwest1.gpugeek.com,qZTCYqU4KpB9GbZfgxXAwcxwMcKM9eFz,,,,,,,,,,,,,,,,,
1 699553450074117 ssh -p 42199 root@northwest1.gpugeek.com hYgx3rbWpmYSFs6pEq8umZqcgAZyZa9p
2 699553742393349 ssh -p 57046 root@northwest1.gpugeek.com a6KeZhBPMWecTtvsP3kbgcV7MZseyerY
3 699553618411525 ssh -p 52041 root@northwest1.gpugeek.com tev3z7knqaaSP2T74QtYqtfb9HdNy9WW
4 699553242570757 ssh -p 57669 root@northwest1.gpugeek.com FU9UmrBvMeCGvvxfDdX5fWCn4E9AtTBg
5 699553554767877 ssh -p 42691 root@northwest1.gpugeek.com r3bFQs4uNFyUCDN99f7deH2pXqdmRYEV
6 699552977174533 ssh -p 41179 root@northwest1.gpugeek.com NFx6ahHDQKpqFEBg4nrPMerureYX5dMV
7 699552838074373 ssh -p 46220 root@northwest1.gpugeek.com qZTCYqU4KpB9GbZfgxXAwcxwMcKM9eFz
+382
View File
@@ -0,0 +1,382 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>服务器状态监控</title>
<style>
:root {
--color-busy: #f39c12;
--color-idle: #2ecc71;
--color-normal: #2ecc71;
--color-error: #e74c3c;
--color-action: #3498db;
--color-text: #333;
--color-light-text: #7f8c8d;
--color-bg: #f5f5f5;
--color-card-bg: white;
--color-event-bg: #fafafa;
--color-event-item: white;
}
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: var(--color-bg);
color: var(--color-text);
}
h1 {
text-align: center;
margin-bottom: 20px;
}
.dashboard {
max-width: 1200px;
margin: 0 auto;
}
.controls {
display: flex;
justify-content: center;
margin-bottom: 20px;
gap: 10px;
}
.refresh-btn {
padding: 8px 16px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s;
}
.refresh-btn:hover {
background-color: #2980b9;
}
.refresh-btn:active {
background-color: #1c6da8;
}
.refresh-info {
text-align: center;
margin: 0 auto 20px;
padding: 10px;
background-color: var(--color-card-bg);
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
max-width: 500px;
}
.server-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
}
.server-card {
background-color: var(--color-card-bg);
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
display: flex;
flex-direction: column;
height: 500px; /* 固定高度 */
}
.server-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.server-name {
font-size: 18px;
font-weight: bold;
}
.server-status {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 15px;
}
.status-item {
display: flex;
align-items: center;
padding: 5px 10px;
border-radius: 4px;
background-color: #f8f9fa;
}
.status-indicator {
width: 10px;
height: 10px;
border-radius: 50%;
margin-right: 8px;
}
.status-busy { background-color: var(--color-busy); }
.status-idle { background-color: var(--color-idle); }
.status-normal { background-color: var(--color-normal); }
.status-error { background-color: var(--color-error); }
.status-text {
font-weight: bold;
}
.last-update {
font-size: 12px;
color: var(--color-light-text);
}
.events-section {
margin-top: 10px;
flex-grow: 1;
display: flex;
flex-direction: column;
min-height: 0; /* 重要:允许内部滚动 */
}
.events-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.events-title {
font-weight: bold;
}
.event-count {
background-color: var(--color-action);
color: white;
padding: 2px 8px;
border-radius: 10px;
font-size: 12px;
}
.event-list {
flex-grow: 1;
overflow-y: auto;
border: 1px solid #eee;
border-radius: 4px;
padding: 10px;
background-color: var(--color-event-bg);
}
.event-item {
margin-bottom: 10px;
padding: 12px;
border-radius: 4px;
background-color: var(--color-event-item);
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.event-time {
font-size: 12px;
color: var(--color-light-text);
margin-bottom: 5px;
}
.event-action {
font-weight: bold;
margin: 5px 0;
color: var(--color-action);
display: flex;
justify-content: space-between;
}
.action-tag {
background-color: #e1f5fe;
color: #0288d1;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
.event-data {
font-size: 13px;
word-break: break-all;
white-space: pre-wrap;
background-color: #f9f9f9;
padding: 8px;
border-radius: 3px;
margin-top: 5px;
font-family: monospace;
max-height: 150px;
overflow-y: auto;
}
.status-badge {
display: inline-flex;
align-items: center;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
}
.badge-busy {
background-color: rgba(243, 156, 18, 0.1);
color: var(--color-busy);
}
.badge-idle {
background-color: rgba(46, 204, 113, 0.1);
color: var(--color-idle);
}
.badge-normal {
background-color: rgba(46, 204, 113, 0.1);
color: var(--color-normal);
}
.badge-error {
background-color: rgba(231, 76, 60, 0.1);
color: var(--color-error);
}
.badge-success {
background-color: rgba(46, 204, 113, 0.1);
color: var(--color-normal);
}
.badge-failure {
background-color: rgba(231, 76, 60, 0.1);
color: var(--color-error);
}
</style>
</head>
<body>
<div class="dashboard">
<h1>服务器状态监控</h1>
<div class="controls">
<button class="refresh-btn" id="refreshBtn">手动刷新数据</button>
</div>
<div class="refresh-info">
最后更新时间: <strong id="lastUpdateTime">-</strong>
</div>
<div class="server-grid" id="serverContainer">
<!-- 服务器卡片将在这里动态生成 -->
</div>
</div>
<script>
// 获取DOM元素
const refreshBtn = document.getElementById('refreshBtn');
const lastUpdateTime = document.getElementById('lastUpdateTime');
const serverContainer = document.getElementById('serverContainer');
// 获取服务器状态数据
async function fetchServerStatus() {
try {
const response = await fetch('http://182.92.195.47:5001/get_server_state');
if (!response.ok) throw new Error('网络响应不正常');
return await response.json();
} catch (error) {
console.error('获取服务器状态失败:', error);
alert('获取数据失败: ' + error.message);
return null;
}
}
// 格式化JSON数据
function formatData(data) {
if (typeof data === 'string') return data;
try {
return JSON.stringify(data, null, 2);
} catch {
return String(data);
}
}
// 判断服务器状态
function getServerStatus(server) {
const now = Date.now() / 1000;
const lastEventTime = server.last_event?.time || 0;
// 忙碌状态
const busyStatus = {
status: server.busy ? 'busy' : 'idle',
text: server.busy ? '忙碌' : '空闲',
indicatorClass: server.busy ? 'status-busy' : 'status-idle',
badgeClass: server.busy ? 'badge-busy' : 'badge-idle'
};
// 连接状态 (5分钟判断)
const isNormal = (now - lastEventTime) < (5*60 + 10);
const connectStatus = {
status: isNormal ? 'normal' : 'error',
text: isNormal ? '活跃' : '不活跃',
indicatorClass: isNormal ? 'status-normal' : 'status-error',
badgeClass: isNormal ? 'badge-normal' : 'badge-error'
};
// 上次运行状态
const hasLastCall = server.hasOwnProperty('last_call');
const lastCallStatus = {
status: hasLastCall && server.last_call === 'success' ? 'success' : 'failure',
text: hasLastCall && server.last_call === 'success' ? '成功' : '失败',
indicatorClass: hasLastCall && server.last_call === 'success' ? 'status-normal' : 'status-error',
badgeClass: hasLastCall && server.last_call === 'success' ? 'badge-success' : 'badge-failure'
};
return { busyStatus, connectStatus, lastCallStatus };
}
// 创建服务器卡片
function createServerCard(server) {
const status = getServerStatus(server);
const card = document.createElement('div');
card.className = 'server-card';
card.innerHTML = `
<div class="server-header">
<div class="server-name">${server.name}</div>
</div>
<div class="server-status">
<div class="status-item">
<span class="status-indicator ${status.busyStatus.indicatorClass}"></span>
<span class="status-text">运行状态: <span class="status-badge ${status.busyStatus.badgeClass}">${status.busyStatus.text}</span></span>
</div>
<div class="status-item">
<span class="status-indicator ${status.connectStatus.indicatorClass}"></span>
<span class="status-text">活跃状态: <span class="status-badge ${status.connectStatus.badgeClass}">${status.connectStatus.text}</span></span>
</div>
<div class="status-item">
<span class="status-indicator ${status.lastCallStatus.indicatorClass}"></span>
<span class="status-text">上次运行: <span class="status-badge ${status.lastCallStatus.badgeClass}">${status.lastCallStatus.text}</span></span>
</div>
<div class="status-item">
<span class="last-update">最后活动: ${server.last_event?.time_str || '无记录'}</span>
</div>
</div>
<div class="events-section">
<div class="events-header">
<div class="events-title">事件记录</div>
<div class="event-count">${server.events.length}条</div>
</div>
<div class="event-list">
${server.events.map(event => `
<div class="event-item">
<div class="event-time">${event.time_str}</div>
<div class="event-action">
<span>${event.action}</span>
<span class="action-tag">${event.action}</span>
</div>
<div class="event-data">${formatData(event.data)}</div>
</div>
`).join('')}
</div>
</div>
`;
return card;
}
// 更新整个UI
async function updateUI() {
refreshBtn.disabled = true;
refreshBtn.textContent = '加载中...';
const servers = await fetchServerStatus();
if (servers && servers.length > 0) {
serverContainer.innerHTML = '';
servers.forEach(server => {
const card = createServerCard(server);
serverContainer.appendChild(card);
});
const now = new Date();
lastUpdateTime.textContent = now.toLocaleString();
}
refreshBtn.disabled = false;
refreshBtn.textContent = '手动刷新数据';
}
// 添加事件监听
refreshBtn.addEventListener('click', updateUI);
// 初始加载
updateUI();
</script>
</body>
</html>
+124
View File
@@ -0,0 +1,124 @@
import threading
import requests
import json
import base64
import time
import random
# url = 'http://172.17.110.92/hairColor/v2'
url = 'https://693565557084165-http-8801.northwest1.gpugeek.com:8443/hairColor/v2'
headers = {'Content-Type': 'application/json'}
data = {
"img": "data:image/jpeg;base64,/9j/4AAQSkZJ....Euy+sn/oZrKn8CPlK2qR/9k=",
"userId": "18701620166",
"rgb":[77,99,0],
"ratio": 0.9,
"output_format": "base64"
}
def image_to_base64(file_path, mime_type=None):
"""
将图片文件转换为带Base64前缀的Data URI字符串
参数:
file_path (str): 图片文件路径
mime_type (str): 可选,指定MIME类型。如果为None,则根据文件扩展名自动判断
返回:
str: 带Base64前缀的Data URI字符串
"""
# 如果没有指定MIME类型,根据文件扩展名推断
if mime_type is None:
extension = file_path.split('.')[-1].lower()
mime_types = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'webp': 'image/webp',
'bmp': 'image/bmp'
}
mime_type = mime_types.get(extension, 'application/octet-stream')
# 读取文件内容并编码为Base64
with open(file_path, 'rb') as image_file:
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
# 组合成Data URI格式
return f"data:{mime_type};base64,{encoded_string}"
img64_str = image_to_base64("aaa.jpg")
data['img'] = img64_str
# 存储所有请求的响应
responses = []
responses_time = []
# 线程锁,防止多线程写入冲突
lock = threading.Lock()
def send_request():
try:
start_time = time.time()
response = requests.post(url, headers=headers, json=data)
end_time = time.time()
response_time = end_time - start_time
print(f"相应时间:{response_time}")
result = response.json()
if 'state' in result:
if result['state'] != 0:
print(f"请求失败: {result}")
time.sleep(5)
with lock: # 加锁,防止多线程同时修改 responses
responses.append(response)
responses_time.append(response_time)
except Exception as e:
with lock:
error_msg = {"error": str(e)}
responses.append(error_msg)
print(f"请求失败: {error_msg}") # 打印错误信息
time.sleep(5) # 延时5秒
# 创建并启动多个线程
threads = []
num_requests = 1 # 并发请求数量
test_rand_num = 1
time_start = time.time()
for _ in range(test_rand_num):
for _ in range(num_requests):
t = threading.Thread(target=send_request)
t.start()
threads.append(t)
# 等待所有线程完成
for t in threads:
t.join()
end_time = time.time()
total_time = (end_time - time_start)
qps = (num_requests*test_rand_num)/total_time
print(f"num_requests:{num_requests} total_time:{total_time} qps:{qps}")
totalReqTime = 0
for t in responses_time:
totalReqTime += t
avgTime = totalReqTime/len(responses_time)
print(f"平均相应时间:{avgTime}")
# 打印所有响应
for i, resp in enumerate(responses, 1):
print(f"请求 {i} 结果:", {resp.status_code, resp.text[:128]})
print(f"num_requests:{num_requests} total_time:{total_time} qps:{qps}")
print(f"平均相应时间:{avgTime}")
+164
View File
@@ -0,0 +1,164 @@
import threading
import requests
import json
import base64
import time
import random
url = 'http://mq.aidigifi.meidaojia.com/api/swapHair/v1'
# url = 'https://692524911165445-http-8801.northwest1.gpugeek.com:8443/api/swapHair/v1'
# url = 'https://692502023221253-http-8801.northwest1.gpugeek.com:8443/api/swapHair/v1'
# url = 'https://692493464571909-http-8801.northwest1.gpugeek.com:8443/api/swapHair/v1'
headers = {'Content-Type': 'application/json'}
data = {
"hair_id": "1907651680352395265",
"task_id": "1907651680352395265",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/user2_1_%E5%89%AF%E6%9C%AC.JPG",
# "user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_8.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data = []
more_data.append(data)
data1 = {
"hair_id": "1907651680352395265",
"task_id": "2345623451234",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_8.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data1)
data2 = {
"hair_id": "1907651680352395265",
"task_id": "3145645324",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_7.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data2)
data3 = {
"hair_id": "1907651680352395265",
"task_id": "456412345",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_6.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data3)
data4 = {
"hair_id": "1907651680352395265",
"task_id": "45623445",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_5.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data4)
data5 = {
"hair_id": "1907651680352395265",
"task_id": "2345867435",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_4.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data5)
data6 = {
"hair_id": "1907651680352395265",
"task_id": "86747535",
"user_img_path": "https://cdn.meidaojia.com/ZoeFiles/testuser_3.JPG",
"is_hr": "false",
"output_format": "base64"
}
more_data.append(data6)
def image_to_base64(file_path, mime_type=None):
"""
将图片文件转换为带Base64前缀的Data URI字符串
参数:
file_path (str): 图片文件路径
mime_type (str): 可选,指定MIME类型。如果为None,则根据文件扩展名自动判断
返回:
str: 带Base64前缀的Data URI字符串
"""
# 如果没有指定MIME类型,根据文件扩展名推断
if mime_type is None:
extension = file_path.split('.')[-1].lower()
mime_types = {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'webp': 'image/webp',
'bmp': 'image/bmp'
}
mime_type = mime_types.get(extension, 'application/octet-stream')
# 读取文件内容并编码为Base64
with open(file_path, 'rb') as image_file:
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
# 组合成Data URI格式
return f"data:{mime_type};base64,{encoded_string}"
img64_str = image_to_base64("aaa.jpg")
data['user_img_path'] = img64_str
# 存储所有请求的响应
responses = []
# 线程锁,防止多线程写入冲突
lock = threading.Lock()
send_index = 0
def send_request(start_time):
try:
sleep_time = start_time #random.uniform(0, 2) # 生成 [0, 2) 之间的随机浮点数
time.sleep(sleep_time)
global send_index
send_data = more_data[send_index]
# send_index = send_index + 1
send_data['task_id'] = str(int(random.uniform(0, 10000000)))
response = requests.post(url, headers=headers, json=send_data)
with lock: # 加锁,防止多线程同时修改 responses
responses.append(response)
except Exception as e:
with lock:
responses.append({"error": str(e)})
# 创建并启动多个线程
threads = []
num_requests = 10 # 并发请求数量
time_start = time.time()
start_time = 0
for _ in range(num_requests):
# start_time = start_time + 1
start_time = 0 # random.uniform(0, 1)
t = threading.Thread(target=send_request, args=(start_time,))
t.start()
threads.append(t)
# 等待所有线程完成
for t in threads:
t.join()
end_time = time.time()
total_time = (end_time - time_start)
qps = num_requests/total_time
print(f"num_requests:{num_requests} total_time:{total_time} qps:{qps}")
# 打印所有响应
for i, resp in enumerate(responses, 1):
print(f"请求 {i} 结果: {resp.status_code, resp.text[:128]}")
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Executable
+428
View File
@@ -0,0 +1,428 @@
import time
import datetime
import uuid
import redis
import logging
import requests
import threading
import json
from datetime import datetime, timedelta
from config import REDIS_HOST
from config import REDIS_PORT
from config import REDIS_DB
from config import QUEUE_NAME
from config import GPU_SERVER_LIST
from config import SERVER_LOG_EVENT_LEN
from config import SERVER_LOG_
from config import GPU_SERVER_TIME_OUT
from config import KEY_QUEUE_LOCK_NAME, acquire_lock, SERVER_LIST_LOCK_NAME, SERVER_LIST_LOG_LOCK_NAME
import copy
import logging
import csv
# 创建 logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO) # 设置 logger 的级别
# 获取当前日期和时间
now = datetime.now()
# 格式化为字符串(例如:2023-10-25 14:30:45
date_time_str = now.strftime("%Y-%m-%d_%H:%M:%S")
# 创建文件 handler
file_handler = logging.FileHandler(f'/var/log/meidaojia/worker_{date_time_str}.log')
file_handler.setLevel(logging.INFO)
file_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(file_formatter)
# 创建控制台 handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(console_formatter)
# 添加 handlers 到 logger
logger.addHandler(file_handler)
logger.addHandler(console_handler)
# 创建 Redis 连接池
redis_pool = redis.ConnectionPool(
host=REDIS_HOST,
port=REDIS_PORT,
db=REDIS_DB,
max_connections=2000 # 根据实际情况调整
)
def get_redis_conn():
"""获取 Redis 连接"""
return redis.Redis(connection_pool=redis_pool)
redis_conn = get_redis_conn()
def get_time(timestamp):
# 转换为UTC时间
utc_time = datetime.utcfromtimestamp(timestamp)
# 转换为北京时间 (UTC+8)
beijing_time = utc_time + timedelta(hours=8) # 正确
# 格式化输出
formatted_time = beijing_time.strftime("%Y-%m-%d %H:%M:%S")
return formatted_time
def server_log_event(name, action, in_data, success=True):
log_data = copy.deepcopy(in_data)
server_log_name = f"{SERVER_LOG_}{name}"
lock = acquire_lock(redis_conn, f"server_log_lock_{name}")
if lock:
try:
logger.info(f"server_log_event name:{name} action:{action} success:{success}")
server_log_str = redis_conn.get(server_log_name).decode("utf-8")
server_log = json.loads(server_log_str)
event = {}
t = time.time()
event['time'] = t
event['time_str'] = get_time(t)
event['action'] = action
server_log['last_call'] = "success"
if 'state' in log_data:
if log_data['state'] != 0:
server_log['last_call'] = "failure"
if not success:
server_log['last_call'] = "failure"
if 'data' in log_data:
imgstr = log_data['data']
imglen = len(imgstr)
if imglen > 256:
log_data['data'] = imgstr[:64]
if 'img' in log_data:
imgstr = log_data['img']
imglen = len(imgstr)
if imglen > 256:
log_data['img'] = imgstr[:64]
if 'result' in log_data:
imgstr = log_data['result']
imglen = len(imgstr)
if imglen > 256:
log_data['result'] = imgstr[:64]
if 'user_img_path' in log_data:
imgstr = log_data['user_img_path']
imglen = len(imgstr)
if imglen > 256:
log_data['user_img_path'] = imgstr[:32]
event['data'] = log_data
if(len(server_log['events']) > SERVER_LOG_EVENT_LEN):
server_log['events'].pop(0)
server_log['events'].append(event)
server_log['last_event'] = event
server_log_str = json.dumps(server_log)
redis_conn.set(f"{SERVER_LOG_}{name}", server_log_str)
finally:
lock.release()
else:
logger.error(f"get lock none {server_log_name}")
def registerGpuServer(name, url, can_use):
try:
server_list_str = redis_conn.get(GPU_SERVER_LIST).decode("utf-8")
server_list = json.loads(server_list_str)
if name not in server_list:
server_list.append(name)
server = {}
server['name'] = name
server['url'] = url
server['can_use'] = can_use
server_str = json.dumps(server)
logger.info(f"registerGpuServer, {server_str}")
redis_conn.set(name, server_str)
server_log = {}
server_log['name'] = name
event = {}
t = time.time()
event['time'] = t
event['time_str'] = get_time(t)
event['action'] = "register"
event['data'] = f"url {url}"
server_log['last_event'] = event
server_log['events'] = []
server_log['last_call'] = "success"
if(len(server_log['events']) > SERVER_LOG_EVENT_LEN):
server_log['events'].pop(0)
server_log['events'].append(event)
server_log_str = json.dumps(server_log)
redis_conn.set(f"{SERVER_LOG_}{name}", server_log_str)
redis_conn.set(GPU_SERVER_LIST, json.dumps(server_list))
except Exception as e:
logger.error(f"registerGpuServer{e.message}")
def get_remote_gpu_server():
start_time = datetime.now()
while (datetime.now() - start_time).seconds < GPU_SERVER_TIME_OUT:
lock = acquire_lock(redis_conn, SERVER_LIST_LOCK_NAME)
try:
server_list_str = redis_conn.get(GPU_SERVER_LIST).decode("utf-8")
server_list = json.loads(server_list_str)
for name in server_list:
server_str = redis_conn.get(name).decode("utf-8")
server = json.loads(server_str)
if server['can_use']:
return server
finally:
lock.release()
time.sleep(0.2)
return None
def call_remote_gpu_server(task_data_str, server=None):
task_data = json.loads(task_data_str)
key = task_data['key']
if server == None:
server = get_remote_gpu_server()
result_str = ''
result_status_code = '200'
if server:
headers = {
'Content-Type': 'application/json'
}
if 'hairColor' in task_data['api']:
data = {
"img": task_data['request']['img'],
"rgb": task_data['request']["rgb"],
"ratio": task_data['request']['ratio'],
"userId": task_data['request']['userId'],
"output_format":task_data['request']['output_format']
}
else:
data = {
"hair_id": task_data['request']['hair_id'],
"task_id": task_data['request']["task_id"],
"user_img_path": task_data['request']['user_img_path'],
"is_hr": task_data['request']['is_hr'],
"output_format":task_data['request']['output_format']
}
name = server['name']
# set server busy
server['can_use'] = False
server_str = json.dumps(server)
redis_conn.set(name, server_str)
server_log_event(server['name'], "Request_GpuServer", data, True)
url = server['url'] + task_data['api']
try:
logger.info(f"call get_remote_gpu_server {url}")
start_ms = int(time.time() * 1000) # 转换为毫秒级整数
base64 = False
if data['output_format'] == 'base64':
base64 = True
# if 'hairColor' in task_data['api']:
# if data['img'] == 'base64':
# imgstr = redis_conn.get(f"img_{key}").decode("utf-8")
# data['img'] = imgstr
# else:
# if data['user_img_path'] == 'base64':
# imgstr = redis_conn.get(f"img_{key}").decode("utf-8")
# data['user_img_path'] = imgstr
response = requests.post(url, headers=headers, json=data, timeout=30)
logger.info(f"call finshed {response.status_code} {response.headers} {response.text[:128]}")
if response.status_code == 200:
result = response.json()
end_ms = int(time.time() * 1000)
if 'hairColor' in task_data['api']:
if base64:
result['result'] = f"data:image/jpeg;base64,{result['result']}"
else:
if base64:
result['data'] = f"data:image/jpeg;base64,{result['data']}"
result['process_time_ms'] = (end_ms - start_ms)
server_log_event(server['name'], "GpuServer_Response", result, True)
result_str = json.dumps(result)
result_status_code = '200'
if 'state' in result:
if result['state'] != 0:
result_status_code = '500'
else:
try:
result = response.json()
except json.JSONDecodeError:
result = {
"state":-1,
"data":"",
'msg': f'status_code error{response.status_code} call_remote_gpu_server{url} {response.text}'
}
time.sleep(1)
result_str = json.dumps(result)
result_status_code = str(response.status_code)
server_log_event(server['name'], f"error GpuServer_Response ", result, False)
except Exception as e:
logger.info(f"End call Exception {url} {type(e).__name__}, 错误信息: {e}")
time.sleep(1)
result = {
"state":-1,
"data":"",
'msg': f'Exception call call_remote_gpu_server{url}'
}
result_str = json.dumps(result)
result_status_code = '500'
server_log_event(server['name'], f"error GpuServer_Response ", result, False)
logger.info(f"End call {url}")
# release server
server['can_use'] = True
server_str = json.dumps(server)
redis_conn.set(name, server_str)
else:
result_str = json.dumps({
"state":-1,
"data":"",
'msg': f'Timeout after all gpu server is busy'
})
result_status_code = '500'
result_key = f"result_{key}"
redis_conn.set(result_key, result_str, ex=60)
result_status_key = f"result_status_code_{key}"
redis_conn.set(result_status_key, result_status_code, ex=60)
def check_server_work_call(server):
task_data = {}
request = {}
request['hair_id'] = "1907651680352395265"
request['task_id'] = "1907651680352395265"
request['user_img_path'] = "https://cdn.meidaojia.com/ZoeFiles/user2_1_%E5%89%AF%E6%9C%AC.JPG"
request['output_format'] = "url"
request['is_hr'] = "false"
task_data['request'] = request
key = str(uuid.uuid4())
task_data['key'] = key
task_data['api'] = "/api/swapHair/v1"
task_data_str = json.dumps(task_data)
threading.Thread(target=call_remote_gpu_server, args=(task_data_str, server)).start()
def check_server_work():
server_list_str = redis_conn.get(GPU_SERVER_LIST).decode("utf-8")
server_list = json.loads(server_list_str)
for name in server_list:
server_str = redis_conn.get(name).decode("utf-8")
server = json.loads(server_str)
if not server['can_use']:
continue
server_log_name = f"{SERVER_LOG_}{name}"
server_log_str = redis_conn.get(server_log_name).decode("utf-8")
server_log = json.loads(server_log_str)
if time.time() - server_log['last_event']['time'] > (5*60):
logger.info("check_server_work and call name")
check_server_work_call(server)
def get_task_queue_key():
redis_conn = get_redis_conn()
lock = acquire_lock(redis_conn, KEY_QUEUE_LOCK_NAME)
if not lock:
logger.error("get_task_queue_key get lock error")
return
try:
key_queue_str = redis_conn.get(QUEUE_NAME).decode("utf-8")
key_queue = json.loads(key_queue_str)
queue_len = len(key_queue)
if(queue_len > 0):
logger.info(f"get_task_queue_key queue len:{queue_len}")
key = key_queue.pop(0)
key_queue_str = json.dumps(key_queue)
logger.info(f"get_task_queue_key queue len:{len(key_queue)}")
redis_conn.set(QUEUE_NAME, key_queue_str)
return key
else:
return None
finally:
lock.release()
def main_worker():
while True:
try:
key = get_task_queue_key()
if key:
task_data_str = redis_conn.get(key)
if task_data_str:
threading.Thread(target=call_remote_gpu_server, args=(task_data_str,)).start()
else:
logging.error(f"main_worker get task_data_str error with key{key}")
continue
except Exception as e:
logger.info(f"main_worker {e}")
# check_server_work()
time.sleep(0.1)
serverindex = 0
def regServer(instance):
global serverindex
registerGpuServer(f"s{serverindex}_1_{instance}", f"https://{instance}-http-8801.northwest1.gpugeek.com:8443", True)
# registerGpuServer(f"s{serverindex}_2_{instance}", f"https://{instance}-http-8801.northwest1.gpugeek.com:8443", True)
serverindex += 1
def get_server_names(csv_file='servers.csv'):
"""
从servers.csv文件中提取所有主机名称
参数:
csv_file (str): CSV文件路径,默认为'servers.csv'
返回:
list: 包含所有主机名称的列表
"""
server_names = []
try:
with open(csv_file, mode='r', encoding='utf-8') as file:
csv_reader = csv.reader(file)
for row in csv_reader:
if row: # 确保不是空行
# 取第一列并去除前后空格
server_name = row[0].strip()
if server_name: # 确保名称不为空
server_names.append(server_name)
except FileNotFoundError:
print(f"错误:文件 {csv_file} 未找到")
except Exception as e:
print(f"读取文件时出错: {e}")
return server_names
if __name__ == '__main__':
import os
print(f"[Worker] Starting with PID: {os.getpid()}")
redis_conn.set(GPU_SERVER_LIST, "[]")
# servers = get_server_names()
# for s in servers:
# regServer(s)
regServer("693570664882181")
regServer("693565557084165")
main_worker()