mirror of
https://github.com/fish2018/pansou.git
synced 2025-11-25 11:29:30 +08:00
/api/health返回当前加载的插件和频道
This commit is contained in:
@@ -1,667 +0,0 @@
|
|||||||
# 异步插件超时动态配置设计方案
|
|
||||||
|
|
||||||
## 1. 背景与需求
|
|
||||||
|
|
||||||
当前PanSou系统的异步插件响应超时时间(ASYNC_RESPONSE_TIMEOUT)通过环境变量配置,在系统启动时加载,所有用户请求共享同一个超时设置。这种方式无法满足不同用户对响应时间的个性化需求:有些用户希望更快得到结果(即使不完整),而有些用户愿意等待更长时间以获取更完整的结果。
|
|
||||||
|
|
||||||
因此,需要实现一种机制,允许通过HTTP请求动态设置异步插件的响应超时时间,使不同用户可以根据自己的网络环境和需求调整超时设置。
|
|
||||||
|
|
||||||
## 2. 设计理念
|
|
||||||
|
|
||||||
### 2.1 核心原则
|
|
||||||
|
|
||||||
1. **请求隔离**:每个请求应有独立的超时设置,互不影响
|
|
||||||
2. **无状态设计**:不修改全局配置状态,确保线程安全
|
|
||||||
3. **向后兼容**:不破坏现有功能,默认行为保持不变
|
|
||||||
4. **易于扩展**:设计应便于未来添加更多可动态配置的参数
|
|
||||||
5. **最小修改**:尽量减少对现有代码的修改,降低引入bug的风险
|
|
||||||
|
|
||||||
### 2.2 技术选型
|
|
||||||
|
|
||||||
采用**上下文传递**模式,通过请求上下文(Context)传递超时设置,而不是修改全局配置。这种模式有以下优势:
|
|
||||||
|
|
||||||
1. **线程安全**:每个请求有独立的上下文,不存在并发问题
|
|
||||||
2. **灵活性高**:可以轻松扩展传递更多参数
|
|
||||||
3. **符合Go语言习惯**:与Go标准库的context包设计理念一致
|
|
||||||
4. **易于测试**:上下文可以在测试中轻松模拟和替换
|
|
||||||
|
|
||||||
## 3. 详细设计
|
|
||||||
|
|
||||||
### 3.1 整体架构
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────┐
|
|
||||||
│ API层 │
|
|
||||||
│ (解析请求参数,创建上下文) │
|
|
||||||
└───────────┬─────────────┘
|
|
||||||
│
|
|
||||||
┌───────────▼─────────────┐
|
|
||||||
│ 服务层 │
|
|
||||||
│ (传递上下文到插件系统) │
|
|
||||||
└───────────┬─────────────┘
|
|
||||||
│
|
|
||||||
┌───────────▼─────────────┐
|
|
||||||
│ 插件系统 │
|
|
||||||
│ (从上下文获取超时设置) │
|
|
||||||
└─────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 数据结构设计
|
|
||||||
|
|
||||||
#### 3.2.1 搜索上下文
|
|
||||||
|
|
||||||
```go
|
|
||||||
// SearchContext 搜索上下文,包含请求特定的配置
|
|
||||||
type SearchContext struct {
|
|
||||||
// 异步插件响应超时时间
|
|
||||||
AsyncResponseTimeout time.Duration
|
|
||||||
|
|
||||||
// 其他可能的配置参数(未来扩展)
|
|
||||||
// PluginTimeout time.Duration
|
|
||||||
// MaxResults int
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSearchContext 创建默认的搜索上下文
|
|
||||||
func NewSearchContext() *SearchContext {
|
|
||||||
return &SearchContext{
|
|
||||||
AsyncResponseTimeout: 0, // 0表示使用系统默认值
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithAsyncResponseTimeout 设置异步响应超时时间并返回上下文
|
|
||||||
func (ctx *SearchContext) WithAsyncResponseTimeout(timeout int) *SearchContext {
|
|
||||||
if timeout > 0 {
|
|
||||||
ctx.AsyncResponseTimeout = time.Duration(timeout) * time.Second
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAsyncResponseTimeout 获取有效的异步响应超时时间
|
|
||||||
// 如果上下文中未设置,则返回系统默认值
|
|
||||||
func (ctx *SearchContext) GetAsyncResponseTimeout() time.Duration {
|
|
||||||
if ctx.AsyncResponseTimeout > 0 {
|
|
||||||
return ctx.AsyncResponseTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用系统默认值
|
|
||||||
if config.AppConfig != nil {
|
|
||||||
return config.AppConfig.AsyncResponseTimeoutDur
|
|
||||||
}
|
|
||||||
|
|
||||||
// 系统配置不可用时的备用值
|
|
||||||
return 4 * time.Second
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.2.2 扩展请求模型
|
|
||||||
|
|
||||||
```go
|
|
||||||
// SearchRequest 搜索请求参数
|
|
||||||
type SearchRequest struct {
|
|
||||||
Keyword string `json:"kw" binding:"required"` // 搜索关键词
|
|
||||||
Channels []string `json:"channels"` // 搜索的频道列表
|
|
||||||
Concurrency int `json:"conc"` // 并发搜索数量
|
|
||||||
ForceRefresh bool `json:"refresh"` // 强制刷新,不使用缓存
|
|
||||||
ResultType string `json:"res"` // 结果类型:all、results、merge
|
|
||||||
SourceType string `json:"src"` // 数据来源类型:all、tg、plugin
|
|
||||||
Plugins []string `json:"plugins"` // 指定搜索的插件列表
|
|
||||||
AsyncTimeout int `json:"async_timeout"` // 异步响应超时时间(秒)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.3 接口设计
|
|
||||||
|
|
||||||
#### 3.3.1 修改插件接口
|
|
||||||
|
|
||||||
```go
|
|
||||||
// SearchPlugin 搜索插件接口
|
|
||||||
type SearchPlugin interface {
|
|
||||||
// Name 返回插件名称
|
|
||||||
Name() string
|
|
||||||
|
|
||||||
// Search 执行搜索并返回结果
|
|
||||||
// 添加ctx参数,传递搜索上下文
|
|
||||||
Search(ctx *SearchContext, keyword string) ([]model.SearchResult, error)
|
|
||||||
|
|
||||||
// Priority 返回插件优先级
|
|
||||||
Priority() int
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.3.2 修改异步插件基类
|
|
||||||
|
|
||||||
```go
|
|
||||||
// BaseAsyncPlugin 基础异步插件结构
|
|
||||||
type BaseAsyncPlugin struct {
|
|
||||||
name string
|
|
||||||
priority int
|
|
||||||
client *http.Client
|
|
||||||
backgroundClient *http.Client
|
|
||||||
cacheTTL time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// AsyncSearch 异步搜索基础方法
|
|
||||||
func (p *BaseAsyncPlugin) AsyncSearch(
|
|
||||||
ctx *SearchContext,
|
|
||||||
keyword string,
|
|
||||||
cacheKey string,
|
|
||||||
searchFunc func(*http.Client, string) ([]model.SearchResult, error),
|
|
||||||
) ([]model.SearchResult, error) {
|
|
||||||
// ...现有代码...
|
|
||||||
|
|
||||||
// 获取响应超时时间
|
|
||||||
// 从上下文获取,如果上下文中未设置,则使用系统默认值
|
|
||||||
responseTimeout := ctx.GetAsyncResponseTimeout()
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3.3.3 修改服务层接口
|
|
||||||
|
|
||||||
```go
|
|
||||||
// SearchService 搜索服务
|
|
||||||
type SearchService struct{
|
|
||||||
pluginManager *plugin.PluginManager
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search 执行搜索
|
|
||||||
// 添加ctx参数,传递搜索上下文
|
|
||||||
func (s *SearchService) Search(
|
|
||||||
ctx *SearchContext,
|
|
||||||
keyword string,
|
|
||||||
channels []string,
|
|
||||||
concurrency int,
|
|
||||||
forceRefresh bool,
|
|
||||||
resultType string,
|
|
||||||
sourceType string,
|
|
||||||
plugins []string,
|
|
||||||
) (model.SearchResponse, error) {
|
|
||||||
// ...现有代码...
|
|
||||||
|
|
||||||
// 如果需要搜索插件
|
|
||||||
if sourceType == "all" || sourceType == "plugin" {
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
// 传递上下文到searchPlugins方法
|
|
||||||
pluginResults, pluginErr = s.searchPlugins(ctx, keyword, plugins, forceRefresh, concurrency)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
}
|
|
||||||
|
|
||||||
// searchPlugins 搜索插件
|
|
||||||
// 添加ctx参数,传递搜索上下文
|
|
||||||
func (s *SearchService) searchPlugins(
|
|
||||||
ctx *SearchContext,
|
|
||||||
keyword string,
|
|
||||||
plugins []string,
|
|
||||||
forceRefresh bool,
|
|
||||||
concurrency int,
|
|
||||||
) ([]model.SearchResult, error) {
|
|
||||||
// ...现有代码...
|
|
||||||
|
|
||||||
// 执行搜索任务时传递上下文
|
|
||||||
tasks := make([]pool.Task, 0, len(availablePlugins))
|
|
||||||
for _, p := range availablePlugins {
|
|
||||||
plugin := p // 创建副本,避免闭包问题
|
|
||||||
tasks = append(tasks, func() interface{} {
|
|
||||||
// 传递上下文到插件的Search方法
|
|
||||||
results, err := plugin.Search(ctx, keyword)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.4 API层实现
|
|
||||||
|
|
||||||
```go
|
|
||||||
// SearchHandler 搜索处理函数
|
|
||||||
func SearchHandler(c *gin.Context) {
|
|
||||||
var req model.SearchRequest
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
|
|
||||||
// GET方式处理异步超时参数
|
|
||||||
asyncTimeout := 0
|
|
||||||
asyncTimeoutStr := c.Query("async_timeout")
|
|
||||||
if asyncTimeoutStr != "" && asyncTimeoutStr != " " {
|
|
||||||
asyncTimeout = util.StringToInt(asyncTimeoutStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
req = model.SearchRequest{
|
|
||||||
// ...现有字段...
|
|
||||||
AsyncTimeout: asyncTimeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
|
|
||||||
// 创建搜索上下文
|
|
||||||
searchCtx := model.NewSearchContext().WithAsyncResponseTimeout(req.AsyncTimeout)
|
|
||||||
|
|
||||||
// 执行搜索,传递上下文
|
|
||||||
result, err := searchService.Search(
|
|
||||||
searchCtx,
|
|
||||||
req.Keyword,
|
|
||||||
req.Channels,
|
|
||||||
req.Concurrency,
|
|
||||||
req.ForceRefresh,
|
|
||||||
req.ResultType,
|
|
||||||
req.SourceType,
|
|
||||||
req.Plugins,
|
|
||||||
)
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 4. 具体代码修改
|
|
||||||
|
|
||||||
### 4.1 创建搜索上下文(model/context.go)
|
|
||||||
|
|
||||||
```go
|
|
||||||
package model
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"pansou/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SearchContext 搜索上下文,包含请求特定的配置
|
|
||||||
type SearchContext struct {
|
|
||||||
// 异步插件响应超时时间
|
|
||||||
AsyncResponseTimeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewSearchContext 创建默认的搜索上下文
|
|
||||||
func NewSearchContext() *SearchContext {
|
|
||||||
return &SearchContext{
|
|
||||||
AsyncResponseTimeout: 0, // 0表示使用系统默认值
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithAsyncResponseTimeout 设置异步响应超时时间并返回上下文
|
|
||||||
func (ctx *SearchContext) WithAsyncResponseTimeout(timeout int) *SearchContext {
|
|
||||||
if timeout > 0 {
|
|
||||||
ctx.AsyncResponseTimeout = time.Duration(timeout) * time.Second
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAsyncResponseTimeout 获取有效的异步响应超时时间
|
|
||||||
// 如果上下文中未设置,则返回系统默认值
|
|
||||||
func (ctx *SearchContext) GetAsyncResponseTimeout() time.Duration {
|
|
||||||
if ctx.AsyncResponseTimeout > 0 {
|
|
||||||
return ctx.AsyncResponseTimeout
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用系统默认值
|
|
||||||
if config.AppConfig != nil {
|
|
||||||
return config.AppConfig.AsyncResponseTimeoutDur
|
|
||||||
}
|
|
||||||
|
|
||||||
// 系统配置不可用时的备用值
|
|
||||||
return 4 * time.Second
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.2 修改请求模型(model/request.go)
|
|
||||||
|
|
||||||
```go
|
|
||||||
package model
|
|
||||||
|
|
||||||
// SearchRequest 搜索请求参数
|
|
||||||
type SearchRequest struct {
|
|
||||||
Keyword string `json:"kw" binding:"required"` // 搜索关键词
|
|
||||||
Channels []string `json:"channels"` // 搜索的频道列表
|
|
||||||
Concurrency int `json:"conc"` // 并发搜索数量
|
|
||||||
ForceRefresh bool `json:"refresh"` // 强制刷新,不使用缓存
|
|
||||||
ResultType string `json:"res"` // 结果类型:all、results、merge
|
|
||||||
SourceType string `json:"src"` // 数据来源类型:all、tg、plugin
|
|
||||||
Plugins []string `json:"plugins"` // 指定搜索的插件列表
|
|
||||||
AsyncTimeout int `json:"async_timeout"` // 异步响应超时时间(秒)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.3 修改插件接口(plugin/plugin.go)
|
|
||||||
|
|
||||||
```go
|
|
||||||
package plugin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"pansou/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
// SearchPlugin 搜索插件接口
|
|
||||||
type SearchPlugin interface {
|
|
||||||
// Name 返回插件名称
|
|
||||||
Name() string
|
|
||||||
|
|
||||||
// Search 执行搜索并返回结果
|
|
||||||
// 添加ctx参数,传递搜索上下文
|
|
||||||
Search(ctx *model.SearchContext, keyword string) ([]model.SearchResult, error)
|
|
||||||
|
|
||||||
// Priority 返回插件优先级
|
|
||||||
Priority() int
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容层,用于支持旧插件
|
|
||||||
type legacyPluginAdapter struct {
|
|
||||||
plugin LegacySearchPlugin
|
|
||||||
}
|
|
||||||
|
|
||||||
// LegacySearchPlugin 旧版插件接口
|
|
||||||
type LegacySearchPlugin interface {
|
|
||||||
Name() string
|
|
||||||
Search(keyword string) ([]model.SearchResult, error)
|
|
||||||
Priority() int
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建兼容适配器
|
|
||||||
func NewLegacyPluginAdapter(plugin LegacySearchPlugin) SearchPlugin {
|
|
||||||
return &legacyPluginAdapter{plugin: plugin}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 实现新接口
|
|
||||||
func (a *legacyPluginAdapter) Name() string {
|
|
||||||
return a.plugin.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *legacyPluginAdapter) Search(ctx *model.SearchContext, keyword string) ([]model.SearchResult, error) {
|
|
||||||
// 忽略上下文,调用旧方法
|
|
||||||
return a.plugin.Search(keyword)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *legacyPluginAdapter) Priority() int {
|
|
||||||
return a.plugin.Priority()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.4 修改异步插件基类(plugin/baseasyncplugin.go)
|
|
||||||
|
|
||||||
```go
|
|
||||||
// AsyncSearch 异步搜索基础方法
|
|
||||||
func (p *BaseAsyncPlugin) AsyncSearch(
|
|
||||||
ctx *model.SearchContext,
|
|
||||||
keyword string,
|
|
||||||
cacheKey string,
|
|
||||||
searchFunc func(*http.Client, string) ([]model.SearchResult, error),
|
|
||||||
) ([]model.SearchResult, error) {
|
|
||||||
// ...现有代码...
|
|
||||||
|
|
||||||
// 获取响应超时时间
|
|
||||||
// 从上下文获取,如果上下文中未设置,则使用系统默认值
|
|
||||||
responseTimeout := defaultAsyncResponseTimeout
|
|
||||||
if ctx != nil {
|
|
||||||
responseTimeout = ctx.GetAsyncResponseTimeout()
|
|
||||||
} else if config.AppConfig != nil {
|
|
||||||
responseTimeout = config.AppConfig.AsyncResponseTimeoutDur
|
|
||||||
}
|
|
||||||
|
|
||||||
// 等待响应超时或结果
|
|
||||||
select {
|
|
||||||
case results := <-resultChan:
|
|
||||||
close(doneChan)
|
|
||||||
return results, nil
|
|
||||||
case err := <-errorChan:
|
|
||||||
close(doneChan)
|
|
||||||
return nil, err
|
|
||||||
case <-time.After(responseTimeout):
|
|
||||||
// 响应超时,返回空结果,后台继续处理
|
|
||||||
// ...现有代码...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.5 修改服务层(service/search_service.go)
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Search 执行搜索
|
|
||||||
// 添加ctx参数,传递搜索上下文
|
|
||||||
func (s *SearchService) Search(
|
|
||||||
ctx *model.SearchContext,
|
|
||||||
keyword string,
|
|
||||||
channels []string,
|
|
||||||
concurrency int,
|
|
||||||
forceRefresh bool,
|
|
||||||
resultType string,
|
|
||||||
sourceType string,
|
|
||||||
plugins []string,
|
|
||||||
) (model.SearchResponse, error) {
|
|
||||||
// 如果未提供上下文,创建默认上下文
|
|
||||||
if ctx == nil {
|
|
||||||
ctx = model.NewSearchContext()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
|
|
||||||
// 如果需要搜索TG
|
|
||||||
if sourceType == "all" || sourceType == "tg" {
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
tgResults, tgErr = s.searchTG(keyword, channels, forceRefresh)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果需要搜索插件
|
|
||||||
if sourceType == "all" || sourceType == "plugin" {
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
// 传递上下文到searchPlugins方法
|
|
||||||
pluginResults, pluginErr = s.searchPlugins(ctx, keyword, plugins, forceRefresh, concurrency)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
}
|
|
||||||
|
|
||||||
// searchPlugins 搜索插件
|
|
||||||
// 添加ctx参数,传递搜索上下文
|
|
||||||
func (s *SearchService) searchPlugins(
|
|
||||||
ctx *model.SearchContext,
|
|
||||||
keyword string,
|
|
||||||
plugins []string,
|
|
||||||
forceRefresh bool,
|
|
||||||
concurrency int,
|
|
||||||
) ([]model.SearchResult, error) {
|
|
||||||
// ...现有代码...
|
|
||||||
|
|
||||||
// 执行搜索任务时传递上下文
|
|
||||||
tasks := make([]pool.Task, 0, len(availablePlugins))
|
|
||||||
for _, p := range availablePlugins {
|
|
||||||
plugin := p // 创建副本,避免闭包问题
|
|
||||||
tasks = append(tasks, func() interface{} {
|
|
||||||
// 传递上下文到插件的Search方法
|
|
||||||
results, err := plugin.Search(ctx, keyword)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return results
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.6 修改API层(api/handler.go)
|
|
||||||
|
|
||||||
```go
|
|
||||||
// SearchHandler 搜索处理函数
|
|
||||||
func SearchHandler(c *gin.Context) {
|
|
||||||
var req model.SearchRequest
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// 根据请求方法不同处理参数
|
|
||||||
if c.Request.Method == http.MethodGet {
|
|
||||||
// ...现有代码...
|
|
||||||
|
|
||||||
// 处理异步超时参数
|
|
||||||
asyncTimeout := 0
|
|
||||||
asyncTimeoutStr := c.Query("async_timeout")
|
|
||||||
if asyncTimeoutStr != "" && asyncTimeoutStr != " " {
|
|
||||||
asyncTimeout = util.StringToInt(asyncTimeoutStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
req = model.SearchRequest{
|
|
||||||
Keyword: keyword,
|
|
||||||
Channels: channels,
|
|
||||||
Concurrency: concurrency,
|
|
||||||
ForceRefresh: forceRefresh,
|
|
||||||
ResultType: resultType,
|
|
||||||
SourceType: sourceType,
|
|
||||||
Plugins: plugins,
|
|
||||||
AsyncTimeout: asyncTimeout,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// ...现有代码...
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
|
|
||||||
// 创建搜索上下文
|
|
||||||
searchCtx := model.NewSearchContext().WithAsyncResponseTimeout(req.AsyncTimeout)
|
|
||||||
|
|
||||||
// 执行搜索,传递上下文
|
|
||||||
result, err := searchService.Search(
|
|
||||||
searchCtx,
|
|
||||||
req.Keyword,
|
|
||||||
req.Channels,
|
|
||||||
req.Concurrency,
|
|
||||||
req.ForceRefresh,
|
|
||||||
req.ResultType,
|
|
||||||
req.SourceType,
|
|
||||||
req.Plugins,
|
|
||||||
)
|
|
||||||
|
|
||||||
// ...现有代码...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.7 修改具体插件实现
|
|
||||||
|
|
||||||
以JikePanPlugin为例:
|
|
||||||
|
|
||||||
```go
|
|
||||||
// Search 实现搜索接口
|
|
||||||
func (p *JikePanPlugin) Search(ctx *model.SearchContext, keyword string) ([]model.SearchResult, error) {
|
|
||||||
// 生成缓存键
|
|
||||||
cacheKey := generateCacheKey(keyword)
|
|
||||||
|
|
||||||
// 使用异步搜索,传递上下文
|
|
||||||
return p.BaseAsyncPlugin.AsyncSearch(ctx, keyword, cacheKey, p.doSearch)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 5. 分阶段实施计划
|
|
||||||
|
|
||||||
### 5.1 第一阶段:基础架构改造
|
|
||||||
|
|
||||||
**目标**:创建搜索上下文,修改核心接口,但保持向后兼容
|
|
||||||
|
|
||||||
**具体任务**:
|
|
||||||
|
|
||||||
1. 创建`model/context.go`文件,实现`SearchContext`结构体
|
|
||||||
2. 修改`model/request.go`,添加`AsyncTimeout`字段
|
|
||||||
3. 修改`plugin/plugin.go`,扩展插件接口并添加兼容层
|
|
||||||
4. 修改`plugin/baseasyncplugin.go`,支持从上下文获取超时设置
|
|
||||||
5. 编写单元测试,确保基础架构正常工作
|
|
||||||
|
|
||||||
**预计工作量**:2人天
|
|
||||||
|
|
||||||
### 5.2 第二阶段:服务层改造
|
|
||||||
|
|
||||||
**目标**:修改服务层,支持传递和使用搜索上下文
|
|
||||||
|
|
||||||
**具体任务**:
|
|
||||||
|
|
||||||
1. 修改`service/search_service.go`中的`Search`方法,添加上下文参数
|
|
||||||
2. 修改`searchPlugins`方法,支持传递上下文到插件
|
|
||||||
3. 确保服务层改动不影响现有功能
|
|
||||||
4. 编写单元测试,验证服务层正常工作
|
|
||||||
|
|
||||||
**预计工作量**:1人天
|
|
||||||
|
|
||||||
### 5.3 第三阶段:API层改造
|
|
||||||
|
|
||||||
**目标**:修改API层,支持从请求中解析超时参数并创建上下文
|
|
||||||
|
|
||||||
**具体任务**:
|
|
||||||
|
|
||||||
1. 修改`api/handler.go`中的`SearchHandler`函数,解析超时参数
|
|
||||||
2. 创建搜索上下文并传递到服务层
|
|
||||||
3. 确保API层改动不影响现有功能
|
|
||||||
4. 编写单元测试,验证API层正常工作
|
|
||||||
|
|
||||||
**预计工作量**:1人天
|
|
||||||
|
|
||||||
### 5.4 第四阶段:插件适配
|
|
||||||
|
|
||||||
**目标**:适配所有现有插件,支持上下文传递
|
|
||||||
|
|
||||||
**具体任务**:
|
|
||||||
|
|
||||||
1. 修改所有异步插件实现,支持上下文参数
|
|
||||||
2. 对于同步插件,使用适配器包装
|
|
||||||
3. 确保所有插件正常工作
|
|
||||||
4. 编写集成测试,验证整个系统正常工作
|
|
||||||
|
|
||||||
**预计工作量**:2人天
|
|
||||||
|
|
||||||
### 5.5 第五阶段:测试与部署
|
|
||||||
|
|
||||||
**目标**:全面测试新功能,确保系统稳定运行
|
|
||||||
|
|
||||||
**具体任务**:
|
|
||||||
|
|
||||||
1. 进行全面的功能测试
|
|
||||||
2. 进行性能测试,确保改动不影响系统性能
|
|
||||||
3. 更新API文档,添加新参数说明
|
|
||||||
4. 部署到测试环境进行验证
|
|
||||||
5. 部署到生产环境
|
|
||||||
|
|
||||||
**预计工作量**:2人天
|
|
||||||
|
|
||||||
## 6. 风险评估与缓解措施
|
|
||||||
|
|
||||||
### 6.1 潜在风险
|
|
||||||
|
|
||||||
1. **接口变更影响**:插件接口变更可能导致现有插件无法正常工作
|
|
||||||
- 缓解措施:提供兼容层,确保旧插件仍能正常工作
|
|
||||||
|
|
||||||
2. **性能影响**:上下文传递可能增加系统开销
|
|
||||||
- 缓解措施:确保上下文结构轻量,避免不必要的数据复制
|
|
||||||
|
|
||||||
3. **测试覆盖不足**:接口变更涉及面广,可能难以全面测试
|
|
||||||
- 缓解措施:编写全面的单元测试和集成测试,覆盖各种场景
|
|
||||||
|
|
||||||
4. **用户误用**:用户可能设置不合理的超时值
|
|
||||||
- 缓解措施:添加参数验证,限制超时值在合理范围内
|
|
||||||
|
|
||||||
### 6.2 回滚计划
|
|
||||||
|
|
||||||
如果部署后发现严重问题,可以采取以下回滚措施:
|
|
||||||
|
|
||||||
1. 立即回滚到上一个稳定版本
|
|
||||||
2. 临时禁用动态超时功能,强制使用系统默认值
|
|
||||||
3. 修复问题后重新部署
|
|
||||||
|
|
||||||
## 7. 总结
|
|
||||||
|
|
||||||
本设计方案通过上下文传递模式,实现了异步插件响应超时时间的动态配置功能。这种方式不仅满足了不同用户对响应时间的个性化需求,还保持了良好的线程安全性和扩展性。
|
|
||||||
|
|
||||||
通过分阶段实施计划,可以平稳地完成系统改造,同时最大限度地降低对现有功能的影响。完成改造后,系统将具备更高的灵活性和用户体验。
|
|
||||||
|
|
||||||
未来,可以基于此架构轻松扩展更多可动态配置的参数,进一步提升系统的可定制性。
|
|
||||||
Reference in New Issue
Block a user