Compare commits

..

4 Commits

Author SHA1 Message Date
ygxbnet 7f0e4fe607 perf(api): 提高并发请求限制并添加配置写入日志
构建上传工具 / build-tool (push) Successful in 1m40s
- 将并发请求限制从10提升到500
- 在配置写入时添加调试日志输出
2026-04-28 15:03:13 +08:00
ygxbnet 199bd43b00 feat(api): 优化HTTP连接池和并发控制
- 增加IdleConnTimeout从30秒到30分钟
- 添加并发请求限制通道,最大同时请求数为10
- 实现InitConn函数用于预创建连接池
- 在UploadDataToServer中添加请求限流控制
- 优化资源清理逻辑,使用defer确保响应体关闭
- 重命名runtime包别名以避免冲突
- 在uploader中添加连接池初始化日志
- 添加panic恢复机制和错误处理
2026-04-28 15:00:15 +08:00
ygxbnet 4addc29b2c refactor(config): 添加配置写入的并发安全锁机制
- 引入 sync.Mutex 确保配置访问的线程安全性
- 在 WriteConfig 函数中实现读写锁定机制
- 防止多协程同时修改配置导致的数据竞争问题
2026-04-28 14:59:21 +08:00
ygxbnet 7face117f3 refactor(App): 解决程序运行时重复写入配置
- 移除 watch 监听器,改用事件驱动方式保存配置
- 添加 writeServerUrl、writeToken 等配置保存方法
- 在表单项上绑定 change 事件触发配置保存
- 移除不再使用的 nextTick 导入
- 统一配置保存逻辑到独立函数中
2026-04-28 14:53:19 +08:00
5 changed files with 78 additions and 33 deletions
+1
View File
@@ -65,6 +65,7 @@ func (a *App) GetConfig() config.Config {
} }
func (a *App) WriteConfig(key string, value any) { func (a *App) WriteConfig(key string, value any) {
fmt.Println("写入配置:", key, value)
config.WriteConfig(key, value) config.WriteConfig(key, value)
} }
+26 -26
View File
@@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import {ref, nextTick, watch, computed} from 'vue' import {ref, nextTick, computed} from 'vue'
import {ElMessage} from 'element-plus' import {ElMessage} from 'element-plus'
import {ElMessageBox} from 'element-plus' import {ElMessageBox} from 'element-plus'
import {SelectPath, GetConfig, WriteConfig, StartUpload, StopUpload} from '../wailsjs/go/main/App'; import {SelectPath, GetConfig, WriteConfig, StartUpload, StopUpload} from '../wailsjs/go/main/App';
@@ -93,6 +93,25 @@ const clearLog = () => {
}) })
} }
const writeServerUrl =() => {
WriteConfig("url", serverUrl.value)
}
const writeToken =() => {
WriteConfig("token", token.value)
}
const writeCheckDir = () => {
WriteConfig("check-dir", checkDir.value)
}
const writeConcurrentFiles = () => {
WriteConfig("handle-file-count", concurrentFiles.value)
}
const writeUploadThreads =() => {
WriteConfig("thread-count", uploadThreads.value)
}
const writeAutoStart = () => {
WriteConfig("is-run-on-start", autoStart.value)
}
// 加载配置 // 加载配置
try { try {
GetConfig().then((config: Config) => { GetConfig().then((config: Config) => {
@@ -109,25 +128,6 @@ try {
console.log(e) console.log(e)
} }
watch(serverUrl, () => {
WriteConfig("url", serverUrl.value)
})
watch(token, () => {
WriteConfig("token", token.value)
})
watch(checkDir, () => {
WriteConfig("check-dir", checkDir.value)
})
watch(concurrentFiles, () => {
WriteConfig("handle-file-count", concurrentFiles.value)
})
watch(uploadThreads, () => {
WriteConfig("thread-count", uploadThreads.value)
})
watch(autoStart, () => {
WriteConfig("is-run-on-start", autoStart.value)
})
EventsOn("is-run", (run) => { EventsOn("is-run", (run) => {
isRunning.value = run isRunning.value = run
}) })
@@ -144,34 +144,34 @@ EventsOn("log", (msg) => {
<div class="left-panel"> <div class="left-panel">
<div class="form-item"> <div class="form-item">
<label>服务器地址</label> <label>服务器地址</label>
<el-input v-model="serverUrl" placeholder="请输入服务器地址" :disabled="isRunning"/> <el-input v-model="serverUrl" placeholder="请输入服务器地址" :disabled="isRunning" @change="writeServerUrl()"/>
</div> </div>
<div class="form-item"> <div class="form-item">
<label>Token</label> <label>Token</label>
<el-input v-model="token" placeholder="请输入Token" :disabled="isRunning"/> <el-input v-model="token" placeholder="请输入Token" :disabled="isRunning" @change="writeToken()"/>
</div> </div>
<div class="form-item"> <div class="form-item">
<label>检测目录</label> <label>检测目录</label>
<div class="dir-input"> <div class="dir-input">
<el-input v-model="checkDir" placeholder="请选择检测目录" :disabled="isRunning"/> <el-input v-model="checkDir" placeholder="请选择检测目录" :disabled="isRunning" @change="writeCheckDir()"/>
<el-button @click="selectDirectory" :disabled="isRunning">选择目录</el-button> <el-button @click="selectDirectory" :disabled="isRunning">选择目录</el-button>
</div> </div>
</div> </div>
<div class="form-item"> <div class="form-item">
<label>同时处理文件数</label> <label>同时处理文件数</label>
<el-input-number v-model="concurrentFiles" :min="1" :max="100" :disabled="isRunning"/> <el-input-number v-model="concurrentFiles" :min="1" :max="100" :disabled="isRunning" @change="writeConcurrentFiles()"/>
</div> </div>
<div class="form-item"> <div class="form-item">
<label>单文件上传线程</label> <label>单文件上传线程</label>
<el-input-number v-model="uploadThreads" :min="1" :max="100" :disabled="isRunning"/> <el-input-number v-model="uploadThreads" :min="1" :max="100" :disabled="isRunning" @change="writeUploadThreads()"/>
</div> </div>
<div class="form-item"> <div class="form-item">
<el-checkbox v-model="autoStart" label="运行时自动启动上传" size="large" :disabled="isRunning"/> <el-checkbox v-model="autoStart" label="运行时自动启动上传" size="large" :disabled="isRunning" @change="writeAutoStart()"/>
</div> </div>
<div class="form-item"> <div class="form-item">
+33 -3
View File
@@ -3,6 +3,7 @@ package api
import ( import (
"context" "context"
"dypid-client/internal/config" "dypid-client/internal/config"
"fmt"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
@@ -13,12 +14,39 @@ var httpClient = &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
MaxIdleConns: 500, MaxIdleConns: 500,
MaxIdleConnsPerHost: 500, MaxIdleConnsPerHost: 500,
IdleConnTimeout: 30 * time.Second, IdleConnTimeout: 30 * time.Minute,
}, },
Timeout: 30 * time.Second, Timeout: 30 * time.Second,
} }
var limit chan struct{}
func init() {
//限制同时请求数为500
limit = make(chan struct{}, 500)
}
// InitConn 创建连接池
func InitConn() {
for i := 0; i < 200; i++ {
resp, err := httpClient.Get(config.APPConfig.Url + "/api/test")
if err != nil {
fmt.Println(err)
return
}
defer func() {
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
}
}
func UploadDataToServer(ctx context.Context, data string) error { func UploadDataToServer(ctx context.Context, data string) error {
limit <- struct{}{}
defer func() {
<-limit
}()
params := url.Values{} params := url.Values{}
params.Set("token", config.APPConfig.Token) params.Set("token", config.APPConfig.Token)
params.Set("data", data) params.Set("data", data)
@@ -38,8 +66,10 @@ func UploadDataToServer(ctx context.Context, data string) error {
if err != nil { if err != nil {
return err return err
} }
io.Copy(io.Discard, resp.Body) defer func() {
resp.Body.Close() io.Copy(io.Discard, resp.Body)
resp.Body.Close()
}()
return err return err
} }
+4
View File
@@ -2,6 +2,7 @@ package config
import ( import (
"fmt" "fmt"
"sync"
"github.com/fsnotify/fsnotify" "github.com/fsnotify/fsnotify"
"github.com/spf13/viper" "github.com/spf13/viper"
@@ -17,6 +18,7 @@ type Config struct {
} }
var APPConfig Config var APPConfig Config
var configMu sync.Mutex
const ( const (
Url = "url" Url = "url"
@@ -70,6 +72,8 @@ func InitConfig() {
} }
func WriteConfig(key string, value any) { func WriteConfig(key string, value any) {
configMu.Lock()
viper.Set(key, value) viper.Set(key, value)
viper.WriteConfig() viper.WriteConfig()
configMu.Unlock()
} }
+14 -4
View File
@@ -9,13 +9,14 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"runtime"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/wailsapp/wails/v2/pkg/runtime" wailsruntime "github.com/wailsapp/wails/v2/pkg/runtime"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
) )
@@ -42,6 +43,10 @@ func StartLooking(ctx context.Context, logChan *chan string, lookingPath string)
AddLog(logChan, `单文件上传线程: `+strconv.Itoa(config.APPConfig.ThreadCount)) AddLog(logChan, `单文件上传线程: `+strconv.Itoa(config.APPConfig.ThreadCount))
AddLog(logChan, "===============================================") AddLog(logChan, "===============================================")
AddLog(logChan, "正在创建连接池(连接池可避免首次大量上传时出现网络错误)")
api.InitConn()
AddLog(logChan, "创建连接池完成,开始运行程序")
progress.Clear() progress.Clear()
//推送上传进度 //推送上传进度
go func() { go func() {
@@ -58,7 +63,7 @@ func StartLooking(ctx context.Context, logChan *chan string, lookingPath string)
pg = append(pg, p) pg = append(pg, p)
return true return true
}) })
runtime.EventsEmit(ctx, "progress", pg) wailsruntime.EventsEmit(ctx, "progress", pg)
} }
} }
}() }()
@@ -217,6 +222,7 @@ func processFile(ctx context.Context, logChan *chan string, filePath string, fil
// 创建行通道 // 创建行通道
lines := make(chan string, 100) lines := make(chan string, 100)
defer close(lines)
var countLine int32 = 0 var countLine int32 = 0
// 创建指定个worker同时处理文件上传 // 创建指定个worker同时处理文件上传
for i := 0; i < config.APPConfig.ThreadCount; i++ { for i := 0; i < config.APPConfig.ThreadCount; i++ {
@@ -233,6 +239,12 @@ func processFile(ctx context.Context, logChan *chan string, filePath string, fil
// 读取文件并发送到通道 // 读取文件并发送到通道
scanner := bufio.NewScanner(file) scanner := bufio.NewScanner(file)
go func() { go func() {
defer func() {
if r := recover(); r != nil {
_, f, l, _ := runtime.Caller(0)
fmt.Println("panic:", f+":"+strconv.Itoa(l), r)
}
}()
for scanner.Scan() { for scanner.Scan() {
select { select {
case <-ctx.Done(): case <-ctx.Done():
@@ -246,7 +258,6 @@ func processFile(ctx context.Context, logChan *chan string, filePath string, fil
for int(countLine) != fileLines { for int(countLine) != fileLines {
select { select {
case <-ctx.Done(): case <-ctx.Done():
close(lines)
wg.Wait() wg.Wait()
return return
default: default:
@@ -259,7 +270,6 @@ func processFile(ctx context.Context, logChan *chan string, filePath string, fil
} }
} }
close(lines)
wg.Wait() wg.Wait()
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {