Files
sundynix-agentix/sundynix-mcp-go/cmd/server/main.go
T
Blizzard 2e3cb8105d fix(store): PG DSN 默认用 127.0.0.1 免每连接 DNS 解析;订正压测报告 C=256 归因
DSN 由 localhost 改 127.0.0.1(gateway + mcp-go):pgx 每新建连接解析一次主机名,
高并发连接池扩容时省掉这层 DNS 开销与一类失败模式。

诚实订正:复测发现 DSN 改完 C=256 仍 0 完成(错误从 lookup localhost 变 dial 127.0.0.1 canceled)。
故 C=256 的「停摆」根因不是 DNS,而是单节点在 256 并发下整体饱和(任务排队 + 每任务 3 次 PG
状态写 + 网关 handler 顶不住),属单节点容量上限、非 bug;C≤128 优雅可用,正解是横向扩。
LOAD_TEST_REPORT §4.3/§6 与 project_analysis 容量实测已据实更新。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 12:49:35 +08:00

109 lines
4.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Command server 启动 sundynix-mcp-go —— 第 5 层 Go I/O 型 MCP 工具微服务。
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
sharedbus "github.com/sundynix/sundynix-shared/bus"
"github.com/sundynix/sundynix-shared/contract"
"github.com/sundynix/sundynix-mcp-go/internal/history"
"github.com/sundynix/sundynix-mcp-go/internal/mcp"
"github.com/sundynix/sundynix-mcp-go/internal/memory"
"github.com/sundynix/sundynix-mcp-go/internal/rag"
"github.com/sundynix/sundynix-mcp-go/internal/search"
"github.com/sundynix/sundynix-shared/otelx"
"github.com/sundynix/sundynix-shared/secrets"
)
func main() {
secrets.MustHaveKeyInProd() // 生产须配 SUNDYNIX_SECRET_KEY 以解密下发的 api_key 密文
otelx.SetupSlog("sundynix-mcp-go") // 结构化日志 + 链路感知(工具日志带 trace_id)
// 链路追踪:工具服务端 span 续上 dispatcher 的 tracetool.call → tool.serve)。
shutdownTrace, _ := otelx.Init(context.Background(), "sundynix-mcp-go")
defer func() { _ = shutdownTrace(context.Background()) }()
natsURL := envOr("NATS_URL", "nats://localhost:4222")
// DSN 用 127.0.0.1 而非 localhost:免 pgx 每连接 DNS 解析,避免高并发连接池扩容时 DNS 雪崩
// (见 LOAD_TEST_REPORT §4.3)。
pgDSN := envOr("POSTGRES_DSN", "postgres://sundynix:sundynix@127.0.0.1:5432/sundynix?sslmode=disable")
redisAddr := envOr("REDIS_ADDR", "localhost:6379")
milvusAddr := envOr("MILVUS_ADDR", "localhost:19530")
embBase := envOr("EMBED_BASE_URL", "") // OpenAI 兼容 embeddings 端点(空=向量检索降级)
embKey := envOr("EMBED_API_KEY", "")
embModel := envOr("EMBED_MODEL", "")
rerankBase := envOr("RERANK_BASE_URL", "") // DashScope 文本重排端点(空=不启用 rerank)
rerankKey := envOr("RERANK_API_KEY", "")
rerankModel := envOr("RERANK_MODEL", "")
neo4jURI := envOr("NEO4J_URI", "neo4j://localhost:7687") // GraphRAG 图谱(连不上则降级)
neo4jUser := envOr("NEO4J_USER", "neo4j")
neo4jPass := envOr("NEO4J_PASS", "sundynix")
b, err := sharedbus.Connect(natsURL)
if err != nil {
log.Fatalf("[mcp_go] nats connect: %v", err)
}
defer b.Close()
log.Printf("[mcp_go] connected %s", natsURL)
engine := search.NewHybrid() // LLM Wiki 混合检索:Bleve + Milvus + Neo4j
mem := memory.Open(pgDSN) // 偏好记忆:sundynix_user_profile(连不上则降级)
defer mem.Close()
hist := history.Open(redisAddr) // 会话短期历史:Redis(连不上则降级)
defer hist.Close()
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
// RAG 核心链:embedding + Milvus(向量) + Bleve(全文) + Neo4j(图谱) + 可选 rerank
ragEngine := rag.Open(ctx, rag.Config{
MilvusAddr: milvusAddr,
EmbedBase: embBase, EmbedKey: embKey, EmbedModel: embModel,
RerankBase: rerankBase, RerankKey: rerankKey, RerankModel: rerankModel,
Neo4jURI: neo4jURI, Neo4jUser: neo4jUser, Neo4jPass: neo4jPass,
})
defer ragEngine.Close()
// 配置控制面:取激活 embedding(向量) + chat(图谱抽取) 配置并订阅热更新。
applyEmbed := func(cfg *contract.ModelConfig) {
if cfg != nil {
ragEngine.SetEmbedding(cfg.BaseURL, cfg.APIKey, cfg.Model)
}
}
applyChat := func(cfg *contract.ModelConfig) {
if cfg != nil {
ragEngine.SetChat(cfg.BaseURL, cfg.APIKey, cfg.Model)
}
}
// 先订阅热更新(控制台改配置即生效)。
if _, err := b.SubscribeConfigUpdated(contract.ConfigKindChat, applyChat); err != nil {
log.Printf("[mcp_go] subscribe chat config: %v", err)
}
if _, err := b.SubscribeConfigUpdated(contract.ConfigKindEmbedding, applyEmbed); err != nil {
log.Printf("[mcp_go] subscribe embedding config: %v", err)
}
// 后台重试拉初始配置:容忍 gateway 晚于本服务启动(避免一次性扑空致 RAG 长期降级)。
go b.RequestConfigWithRetry(ctx, contract.ConfigKindEmbedding, applyEmbed)
go b.RequestConfigWithRetry(ctx, contract.ConfigKindChat, applyChat)
gw := mcp.NewGateway(b, engine, mem, hist, ragEngine, pgDSN)
log.Println("[mcp_go] serving MCP over sundynix.tools.go.* (Ctrl-C to quit)")
if err := gw.Serve(ctx); err != nil && err != context.Canceled {
log.Fatalf("[mcp_go] exit: %v", err)
}
}
func envOr(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}