mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-09-28 07:36:38 +08:00
修复WAL模式下丢失数据问题
This commit is contained in:
@@ -2,7 +2,7 @@ name: Build and Push Images Based on Version
|
|||||||
|
|
||||||
# 在这里定义统一版本号
|
# 在这里定义统一版本号
|
||||||
env:
|
env:
|
||||||
VERSION: "2.0.3-beta"
|
VERSION: "2.0.4-beta"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
image: bqlpfy/springboot-backend:2.0.3-beta
|
image: bqlpfy/springboot-backend:2.0.4-beta
|
||||||
container_name: springboot-backend
|
container_name: springboot-backend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
@@ -15,6 +15,8 @@ services:
|
|||||||
- sqlite_data:/app/data
|
- sqlite_data:/app/data
|
||||||
networks:
|
networks:
|
||||||
- gost-network
|
- gost-network
|
||||||
|
stop_grace_period: 30s
|
||||||
|
stop_signal: SIGTERM
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "sh", "-c", "wget --no-verbose --tries=1 --spider http://localhost:6365/flow/test || exit 1"]
|
test: ["CMD", "sh", "-c", "wget --no-verbose --tries=1 --spider http://localhost:6365/flow/test || exit 1"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
@@ -23,7 +25,7 @@ services:
|
|||||||
start_period: 60s
|
start_period: 60s
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: bqlpfy/vite-frontend:2.0.3-beta
|
image: bqlpfy/vite-frontend:2.0.4-beta
|
||||||
container_name: vite-frontend
|
container_name: vite-frontend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
image: bqlpfy/springboot-backend:2.0.3-beta
|
image: bqlpfy/springboot-backend:2.0.4-beta
|
||||||
container_name: springboot-backend
|
container_name: springboot-backend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
@@ -15,6 +15,8 @@ services:
|
|||||||
- sqlite_data:/app/data
|
- sqlite_data:/app/data
|
||||||
networks:
|
networks:
|
||||||
- gost-network
|
- gost-network
|
||||||
|
stop_grace_period: 30s
|
||||||
|
stop_signal: SIGTERM
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "sh", "-c", "wget --no-verbose --tries=1 --spider http://localhost:6365/flow/test || exit 1"]
|
test: ["CMD", "sh", "-c", "wget --no-verbose --tries=1 --spider http://localhost:6365/flow/test || exit 1"]
|
||||||
interval: 30s
|
interval: 30s
|
||||||
@@ -23,7 +25,7 @@ services:
|
|||||||
start_period: 60s
|
start_period: 60s
|
||||||
|
|
||||||
frontend:
|
frontend:
|
||||||
image: bqlpfy/vite-frontend:2.0.3-beta
|
image: bqlpfy/vite-frontend:2.0.4-beta
|
||||||
container_name: vite-frontend
|
container_name: vite-frontend
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ get_architecture() {
|
|||||||
# 构建下载地址
|
# 构建下载地址
|
||||||
build_download_url() {
|
build_download_url() {
|
||||||
local ARCH=$(get_architecture)
|
local ARCH=$(get_architecture)
|
||||||
echo "https://github.com/bqlpfy/flux-panel/releases/download/2.0.3-beta/gost-${ARCH}"
|
echo "https://github.com/bqlpfy/flux-panel/releases/download/2.0.4-beta/gost-${ARCH}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# 下载地址
|
# 下载地址
|
||||||
|
|||||||
+11
-3
@@ -8,8 +8,8 @@ export LC_ALL=C
|
|||||||
|
|
||||||
|
|
||||||
# 全局下载地址配置
|
# 全局下载地址配置
|
||||||
DOCKER_COMPOSEV4_URL="https://github.com/bqlpfy/flux-panel/releases/download/2.0.3-beta/docker-compose-v4.yml"
|
DOCKER_COMPOSEV4_URL="https://github.com/bqlpfy/flux-panel/releases/download/2.0.4-beta/docker-compose-v4.yml"
|
||||||
DOCKER_COMPOSEV6_URL="https://github.com/bqlpfy/flux-panel/releases/download/2.0.3-beta/docker-compose-v6.yml"
|
DOCKER_COMPOSEV6_URL="https://github.com/bqlpfy/flux-panel/releases/download/2.0.4-beta/docker-compose-v6.yml"
|
||||||
|
|
||||||
COUNTRY=$(curl -s https://ipinfo.io/country)
|
COUNTRY=$(curl -s https://ipinfo.io/country)
|
||||||
if [ "$COUNTRY" = "CN" ]; then
|
if [ "$COUNTRY" = "CN" ]; then
|
||||||
@@ -234,7 +234,15 @@ update_panel() {
|
|||||||
configure_docker_ipv6
|
configure_docker_ipv6
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "🛑 停止当前服务..."
|
# 先发送 SIGTERM 信号,让应用优雅关闭
|
||||||
|
docker stop -t 30 springboot-backend 2>/dev/null || true
|
||||||
|
docker stop -t 10 vite-frontend 2>/dev/null || true
|
||||||
|
|
||||||
|
# 等待 WAL 文件同步
|
||||||
|
echo "⏳ 等待数据同步..."
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# 然后再完全停止
|
||||||
$DOCKER_CMD down
|
$DOCKER_CMD down
|
||||||
|
|
||||||
echo "⬇️ 拉取最新镜像..."
|
echo "⬇️ 拉取最新镜像..."
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ ENV LC_ALL=en_US.UTF-8
|
|||||||
RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list \
|
RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list \
|
||||||
&& sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list \
|
&& sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list \
|
||||||
&& apt-get update \
|
&& apt-get update \
|
||||||
&& apt-get install -y --no-install-recommends fontconfig fonts-dejavu \
|
&& apt-get install -y --no-install-recommends fontconfig fonts-dejavu sqlite3 \
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|||||||
@@ -229,6 +229,7 @@ public class WebSocketServer extends TextWebSocketHandler {
|
|||||||
String socks = (String) session.getAttributes().get("socks");
|
String socks = (String) session.getAttributes().get("socks");
|
||||||
|
|
||||||
log.info("节点 {} 尝试连接,开始处理连接逻辑", nodeId);
|
log.info("节点 {} 尝试连接,开始处理连接逻辑", nodeId);
|
||||||
|
log.info("屏蔽协议数据:http->{}, tls->{}, socks->{}", http, tls, socks);
|
||||||
|
|
||||||
// 检查是否已有该节点的连接,如果有则记录日志但直接覆盖
|
// 检查是否已有该节点的连接,如果有则记录日志但直接覆盖
|
||||||
WebSocketSession existingSession = nodeSessions.get(nodeId);
|
WebSocketSession existingSession = nodeSessions.get(nodeId);
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ package com.admin.config;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.boot.ApplicationArguments;
|
import org.springframework.boot.ApplicationArguments;
|
||||||
import org.springframework.boot.ApplicationRunner;
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
@@ -12,9 +15,11 @@ import java.sql.Statement;
|
|||||||
/**
|
/**
|
||||||
* SQLite 数据库配置
|
* SQLite 数据库配置
|
||||||
* 启用 WAL (Write-Ahead Logging) 模式以提高并发性能
|
* 启用 WAL (Write-Ahead Logging) 模式以提高并发性能
|
||||||
|
* 添加定期 checkpoint 和优雅关闭处理
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
|
@EnableScheduling
|
||||||
public class SQLiteConfig implements ApplicationRunner {
|
public class SQLiteConfig implements ApplicationRunner {
|
||||||
|
|
||||||
private final DataSource dataSource;
|
private final DataSource dataSource;
|
||||||
@@ -33,10 +38,46 @@ public class SQLiteConfig implements ApplicationRunner {
|
|||||||
statement.execute("PRAGMA cache_size=-64000;"); // 64MB 缓存
|
statement.execute("PRAGMA cache_size=-64000;"); // 64MB 缓存
|
||||||
statement.execute("PRAGMA temp_store=MEMORY;");
|
statement.execute("PRAGMA temp_store=MEMORY;");
|
||||||
statement.execute("PRAGMA busy_timeout=5000;"); // 5秒超时
|
statement.execute("PRAGMA busy_timeout=5000;"); // 5秒超时
|
||||||
|
statement.execute("PRAGMA wal_autocheckpoint=1000;"); // 每1000页自动checkpoint
|
||||||
|
|
||||||
|
log.info("SQLite WAL mode configured successfully");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to configure SQLite database", e);
|
log.error("Failed to configure SQLite database", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定期执行 checkpoint,确保 WAL 文件内容写入主数据库
|
||||||
|
* 每5分钟执行一次
|
||||||
|
*/
|
||||||
|
@Scheduled(fixedDelay = 300000, initialDelay = 300000)
|
||||||
|
public void performCheckpoint() {
|
||||||
|
try (Connection connection = dataSource.getConnection();
|
||||||
|
Statement statement = connection.createStatement()) {
|
||||||
|
|
||||||
|
statement.execute("PRAGMA wal_checkpoint(TRUNCATE);");
|
||||||
|
log.debug("SQLite WAL checkpoint completed");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to perform SQLite checkpoint", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用关闭前执行最终的 checkpoint,确保所有数据都写入主数据库文件
|
||||||
|
*/
|
||||||
|
@PreDestroy
|
||||||
|
public void onShutdown() {
|
||||||
|
log.info("Performing final SQLite checkpoint before shutdown...");
|
||||||
|
try (Connection connection = dataSource.getConnection();
|
||||||
|
Statement statement = connection.createStatement()) {
|
||||||
|
|
||||||
|
// 强制执行 checkpoint,将所有 WAL 内容写入主数据库
|
||||||
|
statement.execute("PRAGMA wal_checkpoint(TRUNCATE);");
|
||||||
|
log.info("Final SQLite checkpoint completed successfully");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to perform final SQLite checkpoint", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ public class NodeServiceImpl extends ServiceImpl<NodeMapper, Node> implements No
|
|||||||
ViteConfig viteConfig = viteConfigService.getOne(new QueryWrapper<ViteConfig>().eq("name", "ip"));
|
ViteConfig viteConfig = viteConfigService.getOne(new QueryWrapper<ViteConfig>().eq("name", "ip"));
|
||||||
if (viteConfig == null) return R.err("请先前往网站配置中设置ip");
|
if (viteConfig == null) return R.err("请先前往网站配置中设置ip");
|
||||||
StringBuilder command = new StringBuilder();
|
StringBuilder command = new StringBuilder();
|
||||||
command.append("curl -L https://github.com/bqlpfy/flux-panel/releases/download/2.0.3-beta/install.sh")
|
command.append("curl -L https://github.com/bqlpfy/flux-panel/releases/download/2.0.4-beta/install.sh")
|
||||||
.append(" -o ./install.sh && chmod +x ./install.sh && ");
|
.append(" -o ./install.sh && chmod +x ./install.sh && ");
|
||||||
String processedServerAddr = GostUtil.processServerAddress(viteConfig.getValue());
|
String processedServerAddr = GostUtil.processServerAddress(viteConfig.getValue());
|
||||||
command.append("./install.sh")
|
command.append("./install.sh")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export type SiteConfig = typeof siteConfig;
|
|||||||
|
|
||||||
// 缓存相关常量
|
// 缓存相关常量
|
||||||
const CACHE_PREFIX = 'vite_config_';
|
const CACHE_PREFIX = 'vite_config_';
|
||||||
const VERSION = "2.0.3-beta";
|
const VERSION = "2.0.4-beta";
|
||||||
const APP_VERSION = "1.0.3";
|
const APP_VERSION = "1.0.3";
|
||||||
|
|
||||||
const getInitialConfig = () => {
|
const getInitialConfig = () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user