perf: optimize system concurrency, I/O stability and fix WebSocket disconnects
This commit is contained in:
@@ -83,7 +83,7 @@ Before using the production scripts, ensure the runtime environment has:
|
||||
- a usable Python environment
|
||||
- backend dependencies installed from the checked-in Python package metadata in `pyproject.toml`
|
||||
- the package installed with `pip install -e .` or `uv pip install -e .`
|
||||
- frontend dependencies installed with `npm ci`
|
||||
- frontend dependencies installed with `npm install`
|
||||
- repo dependencies installed
|
||||
- required market/model API keys
|
||||
- any desired `TICKERS` override
|
||||
@@ -94,7 +94,7 @@ Recommended production install sequence:
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -e .
|
||||
cd frontend && npm ci && npm run build && cd ..
|
||||
cd frontend && npm install && npm run build && cd ..
|
||||
```
|
||||
|
||||
## Skill Sandbox Configuration
|
||||
|
||||
164
deploy/install-production.sh
Normal file → Executable file
164
deploy/install-production.sh
Normal file → Executable file
@@ -166,7 +166,7 @@ KillMode=mixed
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
ProtectHome=true
|
||||
ProtectHome=false
|
||||
LimitNOFILE=65535
|
||||
TasksMax=4096
|
||||
MemoryMax=${memory_max}
|
||||
@@ -477,7 +477,17 @@ main() {
|
||||
|
||||
SERVICE_GROUP="${SERVICE_GROUP:-$(ask_required 'systemd 运行用户组' "$(id -gn)")}"
|
||||
|
||||
DOMAIN="${DOMAIN:-$(ask_required '部署域名(可填写 IP 或 localhost)' 'localhost')}"
|
||||
# 自动尝试获取公网 IP 作为默认域名值
|
||||
local detected_ip=""
|
||||
if [[ -z "${DOMAIN:-}" ]]; then
|
||||
log "正在尝试自动获取公网 IP..."
|
||||
detected_ip=$(curl -s --connect-timeout 5 https://ifconfig.me || curl -s --connect-timeout 5 https://api.ipify.org || echo "")
|
||||
if [[ -n "${detected_ip}" ]]; then
|
||||
log "自动检测到公网 IP: ${detected_ip}"
|
||||
fi
|
||||
fi
|
||||
|
||||
DOMAIN="${DOMAIN:-$(ask_required '部署域名(可填写 IP 或 localhost)' "${detected_ip:-localhost}")}"
|
||||
validate_domain_like "${DOMAIN}" || warn "域名/IP 形态看起来不标准,请再次确认: ${DOMAIN}"
|
||||
|
||||
ENV_FILE="${ENV_FILE:-$(ask_required '环境变量文件路径' '/etc/bigtime/bigtime.env')}"
|
||||
@@ -486,53 +496,65 @@ main() {
|
||||
PYTHON_BIN="${PYTHON_BIN:-$(ask 'Python 可执行文件路径' "${APP_DIR}/.venv/bin/python")}"
|
||||
[[ -n "${PYTHON_BIN}" ]] || fail "Python 路径不能为空"
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}运行参数${NC}"
|
||||
TICKERS="${TICKERS:-$(ask '默认股票池(逗号分隔)' 'AAPL,MSFT,GOOGL,AMZN,NVDA,META,TSLA,AMD,NFLX,AVGO,PLTR,COIN')}"
|
||||
FIN_DATA_SOURCE="${FIN_DATA_SOURCE:-$(ask '行情数据源(finnhub/yfinance/financial_datasets)' 'finnhub')}"
|
||||
MODEL_NAME="${MODEL_NAME:-$(ask '默认模型名' 'qwen3-max')}"
|
||||
MAX_COMM_CYCLES="${MAX_COMM_CYCLES:-$(ask_required '最大讨论轮数' '2')}"
|
||||
validate_numeric "${MAX_COMM_CYCLES}" || fail "最大讨论轮数必须是数字: ${MAX_COMM_CYCLES}"
|
||||
MARGIN_REQUIREMENT="${MARGIN_REQUIREMENT:-$(ask_required '保证金比例' '0.5')}"
|
||||
validate_numeric "${MARGIN_REQUIREMENT}" || fail "保证金比例必须是数字: ${MARGIN_REQUIREMENT}"
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}密钥配置${NC}"
|
||||
FINANCIAL_DATASETS_API_KEY="${FINANCIAL_DATASETS_API_KEY:-$(ask 'FINANCIAL_DATASETS_API_KEY(可留空)' '')}"
|
||||
FINNHUB_API_KEY="${FINNHUB_API_KEY:-$(ask 'FINNHUB_API_KEY(live 模式建议填写)' '')}"
|
||||
POLYGON_API_KEY="${POLYGON_API_KEY:-$(ask 'POLYGON_API_KEY(可留空)' '')}"
|
||||
OPENAI_API_KEY="${OPENAI_API_KEY:-$(ask 'OPENAI_API_KEY(可留空)' '')}"
|
||||
OPENAI_BASE_URL="${OPENAI_BASE_URL:-$(ask 'OPENAI_BASE_URL(可留空)' '')}"
|
||||
DASHSCOPE_API_KEY="${DASHSCOPE_API_KEY:-$(ask 'DASHSCOPE_API_KEY(可留空)' '')}"
|
||||
MEMORY_API_KEY="${MEMORY_API_KEY:-$(ask 'MEMORY_API_KEY(可留空)' '')}"
|
||||
|
||||
if [[ "${FIN_DATA_SOURCE}" == "finnhub" && -z "${FINNHUB_API_KEY}" ]]; then
|
||||
warn "你选择了 finnhub 作为数据源,但 FINNHUB_API_KEY 为空。live 模式下通常会失败。"
|
||||
fi
|
||||
if [[ -z "${OPENAI_API_KEY}" && -z "${DASHSCOPE_API_KEY}" ]]; then
|
||||
warn "OPENAI_API_KEY 和 DASHSCOPE_API_KEY 都为空,模型调用可能无法工作。"
|
||||
local SKIP_ENV_CONFIG=false
|
||||
if [[ -f "${ENV_FILE}" ]]; then
|
||||
echo ""
|
||||
if confirm "检测到环境变量文件 ${ENV_FILE} 已存在,是否跳过详细参数配置并保留现有文件?" "Y"; then
|
||||
SKIP_ENV_CONFIG=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if confirm "使用 Docker 沙盒执行技能?" "N" "${AUTO_USE_DOCKER}"; then
|
||||
SKILL_SANDBOX_MODE="docker"
|
||||
if ! ${SKIP_ENV_CONFIG}; then
|
||||
echo ""
|
||||
echo -e "${CYAN}运行参数${NC}"
|
||||
TICKERS="${TICKERS:-$(ask '默认股票池(逗号分隔)' 'AAPL,MSFT,GOOGL,AMZN,NVDA,META,TSLA,AMD,NFLX,AVGO,PLTR,COIN')}"
|
||||
FIN_DATA_SOURCE="${FIN_DATA_SOURCE:-$(ask '行情数据源(finnhub/yfinance/financial_datasets)' 'finnhub')}"
|
||||
MODEL_NAME="${MODEL_NAME:-$(ask '默认模型名' 'qwen3-max')}"
|
||||
MAX_COMM_CYCLES="${MAX_COMM_CYCLES:-$(ask_required '最大讨论轮数' '2')}"
|
||||
validate_numeric "${MAX_COMM_CYCLES}" || fail "最大讨论轮数必须是数字: ${MAX_COMM_CYCLES}"
|
||||
MARGIN_REQUIREMENT="${MARGIN_REQUIREMENT:-$(ask_required '保证金比例' '0.5')}"
|
||||
validate_numeric "${MARGIN_REQUIREMENT}" || fail "保证金比例必须是数字: ${MARGIN_REQUIREMENT}"
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}密钥配置${NC}"
|
||||
FINANCIAL_DATASETS_API_KEY="${FINANCIAL_DATASETS_API_KEY:-$(ask 'FINANCIAL_DATASETS_API_KEY(可留空)' '')}"
|
||||
FINNHUB_API_KEY="${FINNHUB_API_KEY:-$(ask 'FINNHUB_API_KEY(live 模式建议填写)' '')}"
|
||||
POLYGON_API_KEY="${POLYGON_API_KEY:-$(ask 'POLYGON_API_KEY(可留空)' '')}"
|
||||
OPENAI_API_KEY="${OPENAI_API_KEY:-$(ask 'OPENAI_API_KEY(可留空)' '')}"
|
||||
OPENAI_BASE_URL="${OPENAI_BASE_URL:-$(ask 'OPENAI_BASE_URL(可留空)' '')}"
|
||||
DASHSCOPE_API_KEY="${DASHSCOPE_API_KEY:-$(ask 'DASHSCOPE_API_KEY(可留空)' '')}"
|
||||
MEMORY_API_KEY="${MEMORY_API_KEY:-$(ask 'MEMORY_API_KEY(可留空)' '')}"
|
||||
|
||||
if [[ "${FIN_DATA_SOURCE}" == "finnhub" && -z "${FINNHUB_API_KEY}" ]]; then
|
||||
warn "你选择了 finnhub 作为数据源,但 FINNHUB_API_KEY 为空。live 模式下通常会失败。"
|
||||
fi
|
||||
if [[ -z "${OPENAI_API_KEY}" && -z "${DASHSCOPE_API_KEY}" ]]; then
|
||||
warn "OPENAI_API_KEY 和 DASHSCOPE_API_KEY 都为空,模型调用可能无法工作。"
|
||||
fi
|
||||
|
||||
if confirm "使用 Docker 沙盒执行技能?" "N" "${AUTO_USE_DOCKER}"; then
|
||||
SKILL_SANDBOX_MODE="docker"
|
||||
else
|
||||
SKILL_SANDBOX_MODE="none"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}当前部署摘要${NC}"
|
||||
echo " 应用目录: ${APP_DIR}"
|
||||
echo " 运行用户: ${SERVICE_USER}:${SERVICE_GROUP}"
|
||||
echo " 域名: ${DOMAIN}"
|
||||
echo " 环境文件: ${ENV_FILE}"
|
||||
echo " Python: ${PYTHON_BIN}"
|
||||
echo " 数据源: ${FIN_DATA_SOURCE:-}"
|
||||
echo " 模型: ${MODEL_NAME:-}"
|
||||
echo " 沙盒模式: ${SKILL_SANDBOX_MODE:-none}"
|
||||
echo ""
|
||||
|
||||
if ! confirm "确认以上配置并继续写入系统文件?" "Y"; then
|
||||
fail "用户取消部署。"
|
||||
fi
|
||||
else
|
||||
SKILL_SANDBOX_MODE="none"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${CYAN}当前部署摘要${NC}"
|
||||
echo " 应用目录: ${APP_DIR}"
|
||||
echo " 运行用户: ${SERVICE_USER}:${SERVICE_GROUP}"
|
||||
echo " 域名: ${DOMAIN}"
|
||||
echo " 环境文件: ${ENV_FILE}"
|
||||
echo " Python: ${PYTHON_BIN}"
|
||||
echo " 数据源: ${FIN_DATA_SOURCE}"
|
||||
echo " 模型: ${MODEL_NAME}"
|
||||
echo " 沙盒模式: ${SKILL_SANDBOX_MODE}"
|
||||
echo ""
|
||||
|
||||
if ! confirm "确认以上配置并继续写入系统文件?" "Y"; then
|
||||
fail "用户取消部署。"
|
||||
echo -e "${GREEN}将使用现有的环境文件,跳过详细参数配置。${NC}"
|
||||
fi
|
||||
|
||||
if [[ ! -x "${PYTHON_BIN}" ]]; then
|
||||
@@ -546,10 +568,12 @@ main() {
|
||||
"${PYTHON_BIN}" -m pip install -e "${APP_DIR}"
|
||||
|
||||
log "构建前端"
|
||||
(cd "${APP_DIR}/frontend" && npm ci && npm run build)
|
||||
(cd "${APP_DIR}/frontend" && npm install && npm run build)
|
||||
|
||||
log "写入环境变量文件 ${ENV_FILE}"
|
||||
write_env_file
|
||||
if ! ${SKIP_ENV_CONFIG}; then
|
||||
log "写入环境变量文件 ${ENV_FILE}"
|
||||
write_env_file
|
||||
fi
|
||||
|
||||
if confirm "生成并安装 systemd unit?" "Y" "${AUTO_INSTALL_SYSTEMD}"; then
|
||||
render_systemd_unit "Agent Service" "backend.apps.agent_service:app" "8000" "1" "1024M" "/etc/systemd/system/bigtime-agent.service"
|
||||
@@ -568,20 +592,50 @@ main() {
|
||||
if confirm "生成并安装 nginx 配置?" "Y" "${AUTO_INSTALL_NGINX}"; then
|
||||
local use_tls="no"
|
||||
if confirm "使用 HTTPS/Let's Encrypt 证书路径?" "N" "${AUTO_USE_TLS}"; then
|
||||
use_tls="yes"
|
||||
SSL_CERT_PATH="${SSL_CERT_PATH:-$(ask_required 'SSL 证书 fullchain.pem 路径' "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem")}"
|
||||
SSL_KEY_PATH="${SSL_KEY_PATH:-$(ask_required 'SSL 私钥 privkey.pem 路径' "/etc/letsencrypt/live/${DOMAIN}/privkey.pem")}"
|
||||
[[ -f "${SSL_CERT_PATH}" ]] || warn "证书文件当前不存在: ${SSL_CERT_PATH}"
|
||||
[[ -f "${SSL_KEY_PATH}" ]] || warn "私钥文件当前不存在: ${SSL_KEY_PATH}"
|
||||
|
||||
local ssl_err=0
|
||||
[[ -f "${SSL_CERT_PATH}" ]] || { warn "SSL 证书文件不存在: ${SSL_CERT_PATH}"; ssl_err=1; }
|
||||
[[ -f "${SSL_KEY_PATH}" ]] || { warn "SSL 私钥文件不存在: ${SSL_KEY_PATH}"; ssl_err=1; }
|
||||
[[ -f "/etc/letsencrypt/options-ssl-nginx.conf" ]] || { warn "缺失 /etc/letsencrypt/options-ssl-nginx.conf,请检查 certbot 配置"; ssl_err=1; }
|
||||
[[ -f "/etc/letsencrypt/ssl-dhparams.pem" ]] || { warn "缺失 /etc/letsencrypt/ssl-dhparams.pem,请检查 certbot 配置"; ssl_err=1; }
|
||||
|
||||
if [[ ${ssl_err} -eq 0 ]]; then
|
||||
use_tls="yes"
|
||||
else
|
||||
warn "由于 SSL 关键文件缺失,将回退至 HTTP 模式,以确保 Nginx 能通过配置检查。"
|
||||
use_tls="no"
|
||||
fi
|
||||
else
|
||||
SSL_CERT_PATH=""
|
||||
SSL_KEY_PATH=""
|
||||
fi
|
||||
NGINX_TARGET="/etc/nginx/conf.d/bigtime.conf"
|
||||
render_nginx_conf "${NGINX_TARGET}" "${use_tls}"
|
||||
if confirm "立即执行 nginx -t 并 reload?" "Y" "${AUTO_RELOAD_NGINX}"; then
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
if confirm "立即执行 nginx -t 并生效配置?" "Y" "${AUTO_RELOAD_NGINX}"; then
|
||||
log "正在验证 Nginx 配置..."
|
||||
if ! sudo nginx -t; then
|
||||
fail "Nginx 配置检查失败!请根据上方报错信息调整。常见的错误包括:80/443 端口被占用,或 server_name 冲突。"
|
||||
fi
|
||||
|
||||
if systemctl is-active --quiet nginx; then
|
||||
log "Nginx 正在运行,执行 reload..."
|
||||
sudo systemctl reload nginx
|
||||
else
|
||||
log "Nginx 未运行,尝试启动..."
|
||||
sudo systemctl enable --now nginx
|
||||
fi
|
||||
|
||||
# 关键修复:确保 nginx 用户对 /root 路径有 x 权限
|
||||
if [[ "${APP_DIR}" == /root/* ]]; then
|
||||
log "检测到应用部署在 /root 下,正在修复父目录访问权限..."
|
||||
sudo chmod o+x /root 2>/dev/null || true
|
||||
sudo chmod o+x "$(dirname "${APP_DIR}")" 2>/dev/null || true
|
||||
sudo chmod -R o+rX "${APP_DIR}"
|
||||
fi
|
||||
|
||||
log "Nginx 配置已生效。"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ Recommended frontend mode:
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
npm ci
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ sudo systemctl enable --now bigtime-runtime.service
|
||||
|
||||
Recommended production frontend mode:
|
||||
|
||||
- build with `cd frontend && npm ci && npm run build`
|
||||
- build with `cd frontend && npm install && npm run build`
|
||||
- let `nginx` serve `frontend/dist` directly
|
||||
|
||||
The repository also contains `backend.apps.frontend_service`, but for
|
||||
|
||||
Reference in New Issue
Block a user